com.sonicle.webtop.calendar.Service.java Source code

Java tutorial

Introduction

Here is the source code for com.sonicle.webtop.calendar.Service.java

Source

/* 
 * Copyright (C) 2014 Sonicle S.r.l.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY SONICLE, SONICLE DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact Sonicle S.r.l. at email address sonicle[at]sonicle[dot]com
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * Sonicle logo and Sonicle copyright notice. If the display of the logo is not
 * reasonably feasible for technical reasons, the Appropriate Legal Notices must
 * display the words "Copyright (C) 2014 Sonicle S.r.l.".
 */
package com.sonicle.webtop.calendar;

import com.sonicle.commons.EnumUtils;
import com.sonicle.commons.InternetAddressUtils;
import com.sonicle.commons.LangUtils;
import com.sonicle.commons.URIUtils;
import com.sonicle.commons.db.DbUtils;
import com.sonicle.commons.time.DateTimeRange;
import com.sonicle.commons.time.DateTimeUtils;
import com.sonicle.commons.web.Crud;
import com.sonicle.commons.web.DispositionType;
import com.sonicle.commons.web.json.PayloadAsList;
import com.sonicle.commons.web.json.Payload;
import com.sonicle.commons.web.ServletUtils;
import com.sonicle.commons.web.ServletUtils.StringArray;
import com.sonicle.commons.web.json.CompositeId;
import com.sonicle.commons.web.json.JsonResult;
import com.sonicle.commons.web.json.MapItem;
import com.sonicle.commons.web.json.bean.IntegerSet;
import com.sonicle.commons.web.json.bean.QueryObj;
import com.sonicle.commons.web.json.bean.StringSet;
import com.sonicle.commons.web.json.extjs.FieldMeta;
import com.sonicle.commons.web.json.extjs.GridColumnMeta;
import com.sonicle.commons.web.json.extjs.GridMetadata;
import com.sonicle.commons.web.json.extjs.ExtTreeNode;
import com.sonicle.webtop.calendar.bol.js.JsCalendar;
import com.sonicle.webtop.calendar.bol.js.JsCalendarLinks;
import com.sonicle.webtop.calendar.bol.js.JsSchedulerEvent;
import com.sonicle.webtop.calendar.bol.js.JsSchedulerEventDate;
import com.sonicle.webtop.calendar.bol.js.JsEvent;
import com.sonicle.webtop.calendar.bol.js.JsCalendarLkp;
import com.sonicle.webtop.calendar.bol.js.JsFolderNode;
import com.sonicle.webtop.calendar.bol.js.JsFolderNode.JsFolderNodeList;
import com.sonicle.webtop.calendar.bol.js.JsPletEvents;
import com.sonicle.webtop.calendar.bol.js.JsSharing;
import com.sonicle.webtop.calendar.model.ShareFolderCalendar;
import com.sonicle.webtop.calendar.model.ShareRootCalendar;
import com.sonicle.webtop.calendar.model.EventInstance;
import com.sonicle.webtop.calendar.bol.model.RBEventDetail;
import com.sonicle.webtop.calendar.model.EventKey;
import com.sonicle.webtop.calendar.bol.model.MyShareFolderCalendar;
import com.sonicle.webtop.calendar.bol.model.MyShareRootCalendar;
import com.sonicle.webtop.calendar.bol.model.SetupDataCalendarRemote;
import com.sonicle.webtop.calendar.io.EventICalFileReader;
import com.sonicle.webtop.calendar.model.Calendar;
import com.sonicle.webtop.calendar.model.CalendarPropSet;
import com.sonicle.webtop.calendar.model.EventAttachment;
import com.sonicle.webtop.calendar.model.EventAttachmentWithBytes;
import com.sonicle.webtop.calendar.model.EventAttachmentWithStream;
import com.sonicle.webtop.calendar.model.EventQuery;
import com.sonicle.webtop.calendar.model.FolderEventInstances;
import com.sonicle.webtop.calendar.model.UpdateEventTarget;
import com.sonicle.webtop.calendar.model.SchedEventInstance;
import com.sonicle.webtop.calendar.msg.RemoteSyncResult;
import com.sonicle.webtop.calendar.rpt.AbstractAgenda;
import com.sonicle.webtop.calendar.rpt.RptAgendaSummary;
import com.sonicle.webtop.calendar.rpt.RptEventsDetail;
import com.sonicle.webtop.calendar.rpt.RptAgendaWeek5;
import com.sonicle.webtop.calendar.rpt.RptAgendaWeek7;
import com.sonicle.webtop.core.CoreManager;
import com.sonicle.webtop.core.CoreUserSettings;
import com.sonicle.webtop.core.app.RunContext;
import com.sonicle.webtop.core.app.WT;
import com.sonicle.webtop.core.app.WebTopSession;
import com.sonicle.webtop.core.app.WebTopSession.UploadedFile;
import com.sonicle.webtop.core.bol.OUser;
import com.sonicle.webtop.core.bol.js.JsSimple;
import com.sonicle.webtop.core.bol.js.JsWizardData;
import com.sonicle.webtop.core.bol.model.Sharing;
import com.sonicle.webtop.core.model.SharePermsRoot;
import com.sonicle.webtop.core.dal.UserDAO;
import com.sonicle.webtop.core.io.output.AbstractReport;
import com.sonicle.webtop.core.io.output.ReportConfig;
import com.sonicle.webtop.core.sdk.AsyncActionCollection;
import com.sonicle.webtop.core.sdk.BaseService;
import com.sonicle.webtop.core.sdk.BaseServiceAsyncAction;
import com.sonicle.webtop.core.sdk.UserProfile;
import com.sonicle.webtop.core.sdk.UserProfileId;
import com.sonicle.webtop.core.sdk.WTException;
import com.sonicle.webtop.core.sdk.WTRuntimeException;
import com.sonicle.webtop.core.util.LogEntries;
import com.sonicle.webtop.core.util.LogEntry;
import com.sonicle.webtop.core.util.MessageLogEntry;
import com.sonicle.webtop.core.util.RRuleStringify;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.URI;
import java.sql.Connection;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.mail.internet.InternetAddress;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.joda.time.LocalTime;
import org.joda.time.format.DateTimeFormatter;
import org.slf4j.Logger;

/**
 *
 * @author malbinola
 */
public class Service extends BaseService {
    public static final Logger logger = WT.getLogger(Service.class);

    private CalendarManager manager;
    private CalendarServiceSettings ss;
    private CalendarUserSettings us;

    public static final String ERP_EXPORT_FILENAME = "events_{0}-{1}-{2}.{3}";

    private final LinkedHashMap<String, ShareRootCalendar> roots = new LinkedHashMap<>();
    private final LinkedHashMap<Integer, ShareFolderCalendar> folders = new LinkedHashMap<>();
    private final HashMap<Integer, CalendarPropSet> folderProps = new HashMap<>();
    private final HashMap<String, ArrayList<ShareFolderCalendar>> foldersByRoot = new HashMap<>();
    private final HashMap<Integer, ShareRootCalendar> rootByFolder = new HashMap<>();
    private final AsyncActionCollection<Integer, SyncRemoteCalendarAA> syncRemoteCalendarAAs = new AsyncActionCollection<>();
    private StringSet inactiveRoots = null;
    private IntegerSet inactiveFolders = null;
    private ErpExportWizard erpWizard = null;

    @Override
    public void initialize() throws Exception {
        UserProfile up = getEnv().getProfile();
        manager = (CalendarManager) WT.getServiceManager(SERVICE_ID, false, up.getId());
        ss = new CalendarServiceSettings(SERVICE_ID, up.getDomainId());
        us = new CalendarUserSettings(SERVICE_ID, up.getId());
        initFolders();
    }

    @Override
    public void cleanup() throws Exception {
        synchronized (syncRemoteCalendarAAs) {
            syncRemoteCalendarAAs.clear();
        }
        inactiveFolders.clear();
        inactiveFolders = null;
        inactiveRoots.clear();
        inactiveRoots = null;
        rootByFolder.clear();
        foldersByRoot.clear();
        folderProps.clear();
        folders.clear();
        roots.clear();
        us = null;
        ss = null;
        manager = null;
    }

    @Override
    public ServiceVars returnServiceVars() {
        DateTimeFormatter hmf = DateTimeUtils.createHmFormatter();
        ServiceVars co = new ServiceVars();
        co.put("calendarRemoteSyncEnabled", ss.getCalendarRemoteAutoSyncEnabled());
        co.put("eventStatFieldsVisible", ss.getEventStatisticFieldsVisible());
        co.put("defaultCalendarSync", EnumUtils.toSerializedName(ss.getDefaultCalendarSync()));
        co.put("view", us.getView());
        co.put("workdayStart", hmf.print(us.getWorkdayStart()));
        co.put("workdayEnd", hmf.print(us.getWorkdayEnd()));
        return co;
    }

    private WebTopSession getWts() {
        return getEnv().getWebTopSession();
    }

    private void runSyncRemoteCalendar(int calendarId, String calendarName, boolean full) throws WTException {
        synchronized (syncRemoteCalendarAAs) {
            if (syncRemoteCalendarAAs.containsKey(calendarId)) {
                logger.debug("SyncRemoteCalendar run skipped [{}]", calendarId);
                return;
            }
            final SyncRemoteCalendarAA aa = new SyncRemoteCalendarAA(calendarId, calendarName, full);
            syncRemoteCalendarAAs.put(calendarId, aa);
            aa.start(RunContext.getSubject(), RunContext.getRunProfileId());
        }
    }

    private void initFolders() throws Exception {
        synchronized (roots) {
            updateRootFoldersCache();
            updateFoldersCache();

            // HANDLE TRANSITION: cleanup code when process is completed!
            StringSet checkedRoots = us.getCheckedCalendarRoots();
            if (checkedRoots != null) { // Migration code... (remove after migration)
                List<String> toInactive = roots.keySet().stream().filter(shareId -> !checkedRoots.contains(shareId))
                        .collect(Collectors.toList());
                inactiveRoots = new StringSet(toInactive);
                us.setInactiveCalendarRoots(inactiveRoots);
                us.clearCheckedCalendarRoots();

            } else { // New code... (keep after migrarion)
                inactiveRoots = us.getInactiveCalendarRoots();
                // Clean-up orphans
                if (inactiveRoots.removeIf(shareId -> !roots.containsKey(shareId))) {
                    us.setInactiveCalendarRoots(inactiveRoots);
                }
            }

            IntegerSet checkedFolders = us.getCheckedCalendarFolders();
            if (checkedFolders != null) { // Migration code... (remove after migration)
                List<Integer> toInactive = folders.keySet().stream()
                        .filter(calendarId -> !checkedFolders.contains(calendarId)).collect(Collectors.toList());
                inactiveFolders = new IntegerSet(toInactive);
                us.setInactiveCalendarFolders(inactiveFolders);
                us.clearCheckedCalendarFolders();

            } else { // New code... (keep after migrarion)
                inactiveFolders = us.getInactiveCalendarFolders();
                // Clean-up orphans
                if (inactiveFolders.removeIf(calendarId -> !folders.containsKey(calendarId))) {
                    us.setInactiveCalendarFolders(inactiveFolders);
                }
            }
        }
    }

    private void updateRootFoldersCache() throws WTException {
        UserProfileId pid = getEnv().getProfile().getId();
        synchronized (roots) {
            roots.clear();
            roots.put(MyShareRootCalendar.SHARE_ID, new MyShareRootCalendar(pid));
            for (ShareRootCalendar root : manager.listIncomingCalendarRoots()) {
                roots.put(root.getShareId(), root);
            }
        }
    }

    private void updateFoldersCache() throws WTException {
        synchronized (roots) {
            foldersByRoot.clear();
            folders.clear();
            rootByFolder.clear();
            for (ShareRootCalendar root : roots.values()) {
                foldersByRoot.put(root.getShareId(), new ArrayList<ShareFolderCalendar>());
                if (root instanceof MyShareRootCalendar) {
                    for (Calendar cal : manager.listCalendars().values()) {
                        final MyShareFolderCalendar fold = new MyShareFolderCalendar(root.getShareId(), cal);
                        foldersByRoot.get(root.getShareId()).add(fold);
                        folders.put(cal.getCalendarId(), fold);
                        rootByFolder.put(cal.getCalendarId(), root);
                    }
                } else {
                    for (ShareFolderCalendar fold : manager.listIncomingCalendarFolders(root.getShareId())
                            .values()) {
                        final int calId = fold.getCalendar().getCalendarId();
                        foldersByRoot.get(root.getShareId()).add(fold);
                        folders.put(calId, fold);
                        folderProps.put(calId, manager.getCalendarCustomProps(calId));
                        rootByFolder.put(calId, root);
                    }
                }
            }
        }
    }

    public void processManageFoldersTree(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        ArrayList<ExtTreeNode> children = new ArrayList<>();

        try {
            String crud = ServletUtils.getStringParameter(request, "crud", true);
            if (crud.equals(Crud.READ)) {
                String node = ServletUtils.getStringParameter(request, "node", true);
                boolean chooser = ServletUtils.getBooleanParameter(request, "chooser", false);

                if (node.equals("root")) { // Node: root -> list roots
                    for (ShareRootCalendar root : roots.values()) {
                        children.add(createRootNode(chooser, root));
                    }
                } else { // Node: folder -> list folders (calendars)
                    boolean writableOnly = ServletUtils.getBooleanParameter(request, "writableOnly", false);
                    ShareRootCalendar root = roots.get(node);

                    if (root instanceof MyShareRootCalendar) {
                        for (Calendar cal : manager.listCalendars().values()) {
                            MyShareFolderCalendar folder = new MyShareFolderCalendar(node, cal);
                            if (writableOnly && !folder.getElementsPerms().implies("CREATE"))
                                continue;

                            children.add(createFolderNode(chooser, folder, null, root.getPerms()));
                        }
                    } else {
                        if (foldersByRoot.containsKey(root.getShareId())) {
                            for (ShareFolderCalendar folder : foldersByRoot.get(root.getShareId())) {
                                if (writableOnly && !folder.getElementsPerms().implies("CREATE"))
                                    continue;

                                final ExtTreeNode etn = createFolderNode(chooser, folder,
                                        folderProps.get(folder.getCalendar().getCalendarId()), root.getPerms());
                                if (etn != null)
                                    children.add(etn);
                            }
                        }
                    }
                }
                new JsonResult("children", children).printTo(out);

            } else if (crud.equals(Crud.UPDATE)) {
                PayloadAsList<JsFolderNodeList> pl = ServletUtils.getPayloadAsList(request, JsFolderNodeList.class);

                for (JsFolderNode node : pl.data) {
                    if (node._type.equals(JsFolderNode.TYPE_ROOT)) {
                        toggleActiveRoot(node.id, node._active);

                    } else if (node._type.equals(JsFolderNode.TYPE_FOLDER)) {
                        CompositeId cid = new CompositeId().parse(node.id);
                        toggleActiveFolder(Integer.valueOf(cid.getToken(1)), node._active);
                    }
                }
                new JsonResult().printTo(out);

            } else if (crud.equals(Crud.DELETE)) {
                PayloadAsList<JsFolderNodeList> pl = ServletUtils.getPayloadAsList(request, JsFolderNodeList.class);

                for (JsFolderNode node : pl.data) {
                    if (node._type.equals(JsFolderNode.TYPE_FOLDER)) {
                        CompositeId cid = new CompositeId().parse(node.id);
                        manager.deleteCalendar(Integer.valueOf(cid.getToken(1)));
                    }
                }
                new JsonResult().printTo(out);
            }

        } catch (Exception ex) {
            logger.error("Error in ManageCalendarsTree", ex);
        }
    }

    public void processLookupCalendarRoots(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        List<JsSimple> items = new ArrayList<>();

        try {
            Boolean writableOnly = ServletUtils.getBooleanParameter(request, "writableOnly", true);

            synchronized (roots) {
                for (ShareRootCalendar root : roots.values()) {
                    if (root instanceof MyShareRootCalendar) {
                        UserProfile up = getEnv().getProfile();
                        items.add(new JsSimple(up.getStringId(), up.getDisplayName()));
                    } else {
                        //TODO: se writableOnly verificare che il gruppo condiviso sia scrivibile
                        items.add(new JsSimple(root.getOwnerProfileId().toString(), root.getDescription()));
                    }
                }
            }

            new JsonResult("roots", items, items.size()).printTo(out);

        } catch (Exception ex) {
            logger.error("Error in LookupCalendarRoots", ex);
            new JsonResult(false, "Error").printTo(out);
        }
    }

    public void processLookupCalendarFolders(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        List<JsCalendarLkp> items = new ArrayList<>();

        try {
            synchronized (roots) {
                for (ShareRootCalendar root : roots.values()) {
                    if (foldersByRoot.containsKey(root.getShareId())) {
                        for (ShareFolderCalendar fold : foldersByRoot.get(root.getShareId())) {
                            items.add(new JsCalendarLkp(fold, folderProps.get(fold.getCalendar().getCalendarId())));
                        }
                    }
                }
            }
            new JsonResult("folders", items, items.size()).printTo(out);

        } catch (Exception ex) {
            logger.error("Error in LookupCalendarFolders", ex);
            new JsonResult(false, "Error").printTo(out);
        }
    }

    public void processManageSharing(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {

        try {
            String crud = ServletUtils.getStringParameter(request, "crud", true);
            if (crud.equals(Crud.READ)) {
                String id = ServletUtils.getStringParameter(request, "id", true);

                Sharing sharing = manager.getSharing(id);
                String description = buildSharingPath(sharing);
                new JsonResult(new JsSharing(sharing, description)).printTo(out);

            } else if (crud.equals(Crud.UPDATE)) {
                Payload<MapItem, Sharing> pl = ServletUtils.getPayload(request, Sharing.class);

                manager.updateSharing(pl.data);
                new JsonResult().printTo(out);
            }

        } catch (Exception ex) {
            logger.error("Error in ManageSharing", ex);
            new JsonResult(false, "Error").printTo(out);
        }
    }

    public void processManageCalendars(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        Calendar item = null;

        try {
            String crud = ServletUtils.getStringParameter(request, "crud", true);
            if (crud.equals(Crud.READ)) {
                int id = ServletUtils.getIntParameter(request, "id", true);

                item = manager.getCalendar(id);
                new JsonResult(new JsCalendar(item, getEnv().getProfile().getTimeZone())).printTo(out);

            } else if (crud.equals(Crud.CREATE)) {
                Payload<MapItem, JsCalendar> pl = ServletUtils.getPayload(request, JsCalendar.class);

                item = manager.addCalendar(JsCalendar.createCalendar(pl.data));
                updateFoldersCache();
                new JsonResult().printTo(out);

            } else if (crud.equals(Crud.UPDATE)) {
                Payload<MapItem, JsCalendar> pl = ServletUtils.getPayload(request, JsCalendar.class);

                manager.updateCalendar(JsCalendar.createCalendar(pl.data));
                updateFoldersCache();
                new JsonResult().printTo(out);

            } else if (crud.equals(Crud.DELETE)) {
                Payload<MapItem, JsCalendar> pl = ServletUtils.getPayload(request, JsCalendar.class);

                manager.deleteCalendar(pl.data.calendarId);
                updateFoldersCache();
                toggleActiveFolder(pl.data.calendarId, true); // forgets it by simply activating it
                new JsonResult().printTo(out);

            } else if (crud.equals("readLinks")) {
                int id = ServletUtils.getIntParameter(request, "id", true);

                ShareFolderCalendar fold = folders.get(id);
                if (fold == null)
                    throw new WTException("Calendar not found [{}]", id);
                Map<String, String> links = manager.getCalendarLinks(id);
                new JsonResult(new JsCalendarLinks(id, fold.getCalendar().getName(), links)).printTo(out);

            } else if (crud.equals("sync")) {
                int id = ServletUtils.getIntParameter(request, "id", true);
                boolean full = ServletUtils.getBooleanParameter(request, "full", false);

                item = manager.getCalendar(id);
                if (item == null)
                    throw new WTException("Calendar not found [{0}]", id);
                runSyncRemoteCalendar(id, item.getName(), full);

                new JsonResult().printTo(out);
            }

        } catch (Throwable t) {
            logger.error("Error in ManageCalendars", t);
            new JsonResult(false, "Error").printTo(out);
        }
    }

    public void processSetupCalendarRemote(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        WebTopSession wts = getEnv().getWebTopSession();
        final String PROPERTY_PREFIX = "setupremotecalendar-";

        try {
            String crud = ServletUtils.getStringParameter(request, "crud", true);
            if (crud.equals("s1")) {
                String tag = ServletUtils.getStringParameter(request, "tag", true);
                String profileId = ServletUtils.getStringParameter(request, "profileId", true);
                String provider = ServletUtils.getStringParameter(request, "provider", true);
                String url = ServletUtils.getStringParameter(request, "url", true);
                String username = ServletUtils.getStringParameter(request, "username", null);
                String password = ServletUtils.getStringParameter(request, "password", null);

                Calendar.Provider remoteProvider = EnumUtils.forSerializedName(provider, Calendar.Provider.class);
                URI uri = URIUtils.createURI(url);

                CalendarManager.ProbeCalendarRemoteUrlResult result = manager.probeCalendarRemoteUrl(remoteProvider,
                        uri, username, password);
                if (result == null)
                    throw new CalendarProbeException(this.lookupResource("setupCalendarRemote.error.probe"));

                SetupDataCalendarRemote setup = new SetupDataCalendarRemote();
                setup.setProfileId(profileId);
                setup.setProvider(remoteProvider);
                setup.setUrl(uri);
                setup.setUsername(username);
                setup.setPassword(password);
                setup.setName(result.displayName);
                wts.setProperty(SERVICE_ID, PROPERTY_PREFIX + tag, setup);

                new JsonResult(setup).printTo(out);

            } else if (crud.equals("s2")) {
                String tag = ServletUtils.getStringParameter(request, "tag", true);
                String name = ServletUtils.getStringParameter(request, "name", true);
                String color = ServletUtils.getStringParameter(request, "color", true);
                Short syncFrequency = ServletUtils.getShortParameter(request, "syncFrequency", null);

                wts.hasPropertyOrThrow(SERVICE_ID, PROPERTY_PREFIX + tag);
                SetupDataCalendarRemote setup = (SetupDataCalendarRemote) wts.getProperty(SERVICE_ID,
                        PROPERTY_PREFIX + tag);
                setup.setName(name);
                setup.setColor(color);
                setup.setSyncFrequency(syncFrequency);

                Calendar cal = manager.addCalendar(setup.toCalendar());
                wts.clearProperty(SERVICE_ID, PROPERTY_PREFIX + tag);
                updateFoldersCache();
                runSyncRemoteCalendar(cal.getCalendarId(), cal.getName(), true); // Starts a full-sync

                new JsonResult().printTo(out);
            }

        } catch (CalendarProbeException ex) {
            new JsonResult(ex).printTo(out);
        } catch (Exception ex) {
            logger.error("Error in SetupCalendarRemote", ex);
            new JsonResult(ex).printTo(out);
        }
    }

    public void processManageHiddenCalendars(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        try {
            String crud = ServletUtils.getStringParameter(request, "crud", true);
            if (crud.equals(Crud.READ)) {
                String rootId = ServletUtils.getStringParameter(request, "rootId", true);
                if (rootId.equals(MyShareRootCalendar.SHARE_ID))
                    throw new WTException();

                ArrayList<JsSimple> items = new ArrayList<>();
                synchronized (roots) {
                    for (ShareFolderCalendar folder : foldersByRoot.get(rootId)) {
                        CalendarPropSet pset = folderProps.get(folder.getCalendar().getCalendarId());
                        if ((pset != null) && pset.getHiddenOrDefault(false)) {
                            items.add(new JsSimple(folder.getCalendar().getCalendarId(),
                                    folder.getCalendar().getName()));
                        }
                    }
                }
                new JsonResult(items).printTo(out);

            } else if (crud.equals(Crud.UPDATE)) {
                Integer categoryId = ServletUtils.getIntParameter(request, "calendarId", true);
                Boolean hidden = ServletUtils.getBooleanParameter(request, "hidden", false);

                updateCalendarFolderVisibility(categoryId, hidden);
                new JsonResult().printTo(out);

            } else if (crud.equals(Crud.DELETE)) {
                ServletUtils.StringArray ids = ServletUtils.getObjectParameter(request, "ids",
                        ServletUtils.StringArray.class, true);

                HashSet<String> pids = new HashSet<>();
                synchronized (roots) {
                    for (String id : ids) {
                        int categoryId = Integer.valueOf(id);
                        ShareFolderCalendar fold = folders.get(categoryId);
                        if (fold != null) {
                            updateCalendarFolderVisibility(categoryId, null);
                            pids.add(fold.getCalendar().getProfileId().toString());
                        }
                    }
                }
                new JsonResult(pids).printTo(out);
            }

        } catch (Exception ex) {
            new JsonResult(ex).printTo(out);
        }
    }

    public void processSetCalendarColor(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        try {
            Integer id = ServletUtils.getIntParameter(request, "id", true);
            String color = ServletUtils.getStringParameter(request, "color", null);

            updateCalendarFolderColor(id, color);

            new JsonResult().printTo(out);

        } catch (Exception ex) {
            new JsonResult(ex).printTo(out);
        }
    }

    public void processSetCalendarSync(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        try {
            Integer id = ServletUtils.getIntParameter(request, "id", true);
            String sync = ServletUtils.getStringParameter(request, "sync", null);

            updateCalendarFolderSync(id, EnumUtils.forSerializedName(sync, Calendar.Sync.class));
            new JsonResult().printTo(out);

        } catch (Exception ex) {
            logger.error("Error in SetCategorySync", ex);
            new JsonResult(ex).printTo(out);
        }
    }

    public void processGetSchedulerDates(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        ArrayList<JsSchedulerEventDate> items = new ArrayList<>();

        try {
            UserProfile up = getEnv().getProfile();
            DateTimeZone utz = up.getTimeZone();
            //DateTimeFormatter ymdZoneFmt = DateTimeUtils.createYmdFormatter(utz);

            // Defines boundaries
            String start = ServletUtils.getStringParameter(request, "startDate", true);
            String end = ServletUtils.getStringParameter(request, "endDate", true);
            DateTime fromDate = DateTimeUtils.parseYmdHmsWithZone(start, "00:00:00", up.getTimeZone());
            DateTime toDate = DateTimeUtils.parseYmdHmsWithZone(end, "23:59:59", up.getTimeZone());

            Set<Integer> activeCalIds = getActiveFolderIds();
            List<LocalDate> dates = manager.listEventDates(activeCalIds, fromDate, toDate, utz);
            for (LocalDate date : dates) {
                items.add(new JsSchedulerEventDate(date.toString("yyyy-MM-dd")));
            }
            /*
            List<DateTime> dates = manager.listEventDates(activeCalIds, fromDate, toDate, utz);
            for (DateTime date : dates) {
               items.add(new JsSchedulerEventDate(ymdZoneFmt.print(date)));
            }
            */

            new JsonResult("dates", items).printTo(out);

        } catch (Exception ex) {
            logger.error("Error in GetSchedulerDates", ex);
            new JsonResult(false, "Error").printTo(out);

        }
    }

    public void processManageEventsScheduler(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        ArrayList<JsSchedulerEvent> items = new ArrayList<>();

        try {
            UserProfile up = getEnv().getProfile();
            DateTimeZone utz = up.getTimeZone();

            String crud = ServletUtils.getStringParameter(request, "crud", true);
            if (crud.equals(Crud.READ)) {
                String from = ServletUtils.getStringParameter(request, "startDate", true);
                String to = ServletUtils.getStringParameter(request, "endDate", true);

                // Defines view boundary 
                DateTime fromDate = DateTimeUtils.parseYmdHmsWithZone(from, "00:00:00", up.getTimeZone());
                DateTime toDate = DateTimeUtils.parseYmdHmsWithZone(to, "23:59:59", up.getTimeZone());

                Set<Integer> activeCalIds = getActiveFolderIds();
                List<SchedEventInstance> instances = manager.listEventInstances(activeCalIds,
                        new DateTimeRange(fromDate, toDate), utz, false);
                for (SchedEventInstance instance : instances) {
                    final ShareRootCalendar root = rootByFolder.get(instance.getCalendarId());
                    if (root == null)
                        continue;
                    final ShareFolderCalendar fold = folders.get(instance.getCalendarId());
                    if (fold == null)
                        continue;
                    CalendarPropSet pset = folderProps.get(instance.getCalendarId());

                    items.add(new JsSchedulerEvent(root, fold, pset, instance, up.getId(), utz));
                }

                new JsonResult("events", items).printTo(out);

            } /* else if (crud.equals(Crud.CREATE)) {
                Payload<MapItem, JsSchedulerEvent> pl = ServletUtils.getPayload(request, JsSchedulerEvent.class);
                    
                DateTimeZone etz = DateTimeZone.forID(pl.data.timezone);
                DateTime newStart = DateTimeUtils.parseYmdHmsWithZone(pl.data.startDate, etz);
                DateTime newEnd = DateTimeUtils.parseYmdHmsWithZone(pl.data.endDate, etz);
                manager.cloneEventInstance(EventKey.buildKey(pl.data.eventId, pl.data.originalEventId), newStart, newEnd);
                    
                new JsonResult().printTo(out);
                    
              } else if (crud.equals(Crud.UPDATE)) {
                Payload<MapItem, JsSchedulerEvent.Update> pl = ServletUtils.getPayload(request, JsSchedulerEvent.Update.class);
                    
                DateTimeZone etz = DateTimeZone.forID(pl.data.timezone);
                DateTime newStart = DateTimeUtils.parseYmdHmsWithZone(pl.data.startDate, etz);
                DateTime newEnd = DateTimeUtils.parseYmdHmsWithZone(pl.data.endDate, etz);
                manager.updateEventInstance(pl.data.id, newStart, newEnd, pl.data.title, false);
                    
                new JsonResult().printTo(out);
                    
              }*/ else if (crud.equals(Crud.UPDATE)) {
                String eventKey = ServletUtils.getStringParameter(request, "id", true);
                String newStartString = ServletUtils.getStringParameter(request, "newStart", null);
                String newEndString = ServletUtils.getStringParameter(request, "newEnd", null);
                String newTitle = ServletUtils.getStringParameter(request, "newTitle", null);
                UpdateEventTarget target = ServletUtils.getEnumParameter(request, "target",
                        UpdateEventTarget.THIS_INSTANCE, UpdateEventTarget.class);
                boolean notify = ServletUtils.getBooleanParameter(request, "notify", false);

                DateTime newStart = null, newEnd = null;
                if ((newStartString != null) || (newEndString != null)) {
                    newStart = DateTimeUtils.parseYmdHmsWithZone(newStartString, utz);
                    newEnd = DateTimeUtils.parseYmdHmsWithZone(newEndString, utz);
                }

                manager.updateEventInstance(target, new EventKey(eventKey), newStart, newEnd, newTitle, notify);
                new JsonResult().printTo(out);

            } else if (crud.equals(Crud.DELETE)) {
                String eventKey = ServletUtils.getStringParameter(request, "id", true);
                UpdateEventTarget target = ServletUtils.getEnumParameter(request, "target",
                        UpdateEventTarget.THIS_INSTANCE, UpdateEventTarget.class);
                boolean notify = ServletUtils.getBooleanParameter(request, "notify", false);

                manager.deleteEventInstance(target, new EventKey(eventKey), notify);
                new JsonResult().printTo(out);

            } else if (crud.equals(Crud.COPY)) {
                String eventKey = ServletUtils.getStringParameter(request, "id", true);
                String newStartString = ServletUtils.getStringParameter(request, "newStart", null);
                //UpdateEventTarget target = ServletUtils.getEnumParameter(request, "target", UpdateEventTarget.THIS_INSTANCE, UpdateEventTarget.class);
                boolean notify = ServletUtils.getBooleanParameter(request, "notify", false);

                DateTime newStart = null;
                if (newStartString != null) {
                    newStart = DateTimeUtils.parseYmdHmsWithZone(newStartString, utz);
                }

                manager.cloneEventInstance(new EventKey(eventKey), null, newStart, null, notify);
                new JsonResult().printTo(out);

            } else if (crud.equals(Crud.MOVE)) {
                boolean copy = ServletUtils.getBooleanParameter(request, "copy", false);
                String eventKey = ServletUtils.getStringParameter(request, "id", true);
                Integer calendarId = ServletUtils.getIntParameter(request, "targetCalendarId", true);

                if (copy) {
                    boolean notify = ServletUtils.getBooleanParameter(request, "notify", false);
                    manager.cloneEventInstance(new EventKey(eventKey), calendarId, null, null, notify);
                } else {
                    manager.moveEventInstance(new EventKey(eventKey), calendarId);
                }
                new JsonResult().printTo(out);

            } else if (crud.equals("restore")) {
                String eventKey = ServletUtils.getStringParameter(request, "id", true);

                manager.restoreEventInstance(new EventKey(eventKey));
                new JsonResult().printTo(out);
            }

        } catch (Exception ex) {
            logger.error("Error in ManageEventsScheduler", ex);
            new JsonResult(false, "Error").printTo(out);
        }
    }

    public void processManageEvents(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        JsEvent item = null;

        try {
            String crud = ServletUtils.getStringParameter(request, "crud", true);
            if (crud.equals(Crud.READ)) {
                String eventKey = ServletUtils.getStringParameter(request, "id", true);

                EventInstance evt = manager.getEventInstance(eventKey);
                UserProfileId ownerId = manager.getCalendarOwner(evt.getCalendarId());
                item = new JsEvent(evt, ownerId.toString());
                new JsonResult(item).printTo(out);

            } else if (crud.equals(Crud.CREATE)) {
                boolean notify = ServletUtils.getBooleanParameter(request, "notify", false);
                Payload<MapItem, JsEvent> pl = ServletUtils.getPayload(request, JsEvent.class);

                EventInstance event = JsEvent.buildEventInstance(pl.data);
                CoreManager core = WT.getCoreManager();
                event.setOrganizer(core.getUserData().getFullEmailAddress());

                for (JsEvent.Attachment jsa : pl.data.attachments) {
                    UploadedFile upFile = getUploadedFileOrThrow(jsa._uplId);
                    EventAttachmentWithStream att = new EventAttachmentWithStream(upFile.getFile());
                    att.setAttachmentId(jsa.id);
                    att.setFilename(upFile.getFilename());
                    att.setSize(upFile.getSize());
                    att.setMediaType(upFile.getMediaType());
                    event.getAttachments().add(att);
                }
                manager.addEvent(event, notify);

                new JsonResult().printTo(out);

            } else if (crud.equals(Crud.UPDATE)) {
                UpdateEventTarget target = ServletUtils.getEnumParameter(request, "target",
                        UpdateEventTarget.THIS_INSTANCE, UpdateEventTarget.class);
                boolean notify = ServletUtils.getBooleanParameter(request, "notify", false);
                Payload<MapItem, JsEvent> pl = ServletUtils.getPayload(request, JsEvent.class);

                EventInstance event = JsEvent.buildEventInstance(pl.data);

                for (JsEvent.Attachment jsa : pl.data.attachments) {
                    if (!StringUtils.isBlank(jsa._uplId)) {
                        UploadedFile upFile = getUploadedFileOrThrow(jsa._uplId);
                        EventAttachmentWithStream att = new EventAttachmentWithStream(upFile.getFile());
                        att.setAttachmentId(jsa.id);
                        att.setFilename(upFile.getFilename());
                        att.setSize(upFile.getSize());
                        att.setMediaType(upFile.getMediaType());
                        event.getAttachments().add(att);
                    } else {
                        EventAttachment att = new EventAttachment();
                        att.setAttachmentId(jsa.id);
                        att.setFilename(jsa.name);
                        att.setSize(jsa.size);
                        event.getAttachments().add(att);
                    }
                }
                manager.updateEventInstance(target, event, notify);

                new JsonResult().printTo(out);

            } /* else if(crud.equals(Crud.MOVE)) {
                String id = ServletUtils.getStringParameter(request, "id", true);
                Integer calendarId = ServletUtils.getIntParameter(request, "targetCalendarId", true);
                boolean copy = ServletUtils.getBooleanParameter(request, "copy", false);
                    
                manager.moveEventInstance(copy, id, calendarId);
                new JsonResult().printTo(out);
              }*/

        } catch (Exception ex) {
            logger.error("Error in ManageEvents", ex);
            new JsonResult(false, "Error").printTo(out);
        }
    }

    public void processDownloadEventAttachment(HttpServletRequest request, HttpServletResponse response) {

        try {
            boolean inline = ServletUtils.getBooleanParameter(request, "inline", false);
            String attachmentId = ServletUtils.getStringParameter(request, "attachmentId", null);

            if (!StringUtils.isBlank(attachmentId)) {
                Integer eventId = ServletUtils.getIntParameter(request, "eventId", true);

                EventAttachmentWithBytes attch = manager.getEventAttachment(eventId, attachmentId);
                InputStream is = null;
                try {
                    is = new ByteArrayInputStream(attch.getBytes());
                    ServletUtils.writeFileResponse(response, inline, attch.getFilename(), null, attch.getSize(),
                            is);
                } finally {
                    IOUtils.closeQuietly(is);
                }
            } else {
                String uploadId = ServletUtils.getStringParameter(request, "uploadId", true);

                UploadedFile uplFile = getUploadedFileOrThrow(uploadId);
                InputStream is = null;
                try {
                    is = new FileInputStream(uplFile.getFile());
                    ServletUtils.writeFileResponse(response, inline, uplFile.getFilename(), null, uplFile.getSize(),
                            is);
                } finally {
                    IOUtils.closeQuietly(is);
                }
            }

        } catch (Exception ex) {
            logger.error("Error in DownloadEventAttachment", ex);
            ServletUtils.writeErrorHandlingJs(response, ex.getMessage());
        }
    }

    public void processManageGridEvents(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        ArrayList<JsSchedulerEvent> items = new ArrayList<>();

        try {
            UserProfile up = getEnv().getProfile();
            DateTimeZone utz = up.getTimeZone();

            String crud = ServletUtils.getStringParameter(request, "crud", true);
            if (crud.equals(Crud.READ)) {
                QueryObj queryObj = ServletUtils.getObjectParameter(request, "query", new QueryObj(),
                        QueryObj.class);

                Set<Integer> activeCalIds = getActiveFolderIds();
                List<SchedEventInstance> instances = manager.listEventInstances(activeCalIds,
                        buildSearchRange(DateTimeUtils.now(), utz), EventQuery.toCondition(queryObj, utz), utz,
                        true);
                for (SchedEventInstance instance : instances) {
                    final ShareRootCalendar root = rootByFolder.get(instance.getCalendarId());
                    if (root == null)
                        continue;
                    final ShareFolderCalendar fold = folders.get(instance.getCalendarId());
                    if (fold == null)
                        continue;
                    CalendarPropSet pset = folderProps.get(instance.getCalendarId());

                    items.add(new JsSchedulerEvent(root, fold, pset, instance, up.getId(), utz));
                }

                new JsonResult("events", items).printTo(out);
            }

        } catch (Exception ex) {
            logger.error("Error in ManageGridEvents", ex);
            new JsonResult(false, "Error").printTo(out);

        }
    }

    public void processExportForErp(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        UserProfile up = getEnv().getProfile();

        try {
            String op = ServletUtils.getStringParameter(request, "op", true);
            if (op.equals("do")) {
                String fromDate = ServletUtils.getStringParameter(request, "fromDate", true);
                String toDate = ServletUtils.getStringParameter(request, "toDate", true);

                DateTimeFormatter ymd = DateTimeUtils.createYmdFormatter(up.getTimeZone());
                erpWizard = new ErpExportWizard();
                erpWizard.fromDate = ymd.parseDateTime(fromDate).withTimeAtStartOfDay();
                erpWizard.toDate = DateTimeUtils.withTimeAtEndOfDay(ymd.parseDateTime(toDate));

                LogEntries log = new LogEntries();
                File file = WT.createTempFile();

                try {
                    DateTimeFormatter ymd2 = DateTimeUtils.createFormatter("yyyyMMdd", up.getTimeZone());
                    DateTimeFormatter ymdhms = DateTimeUtils.createFormatter("yyyy-MM-dd HH:mm:ss",
                            up.getTimeZone());

                    try (FileOutputStream fos = new FileOutputStream(file)) {
                        log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "Started on {0}",
                                ymdhms.print(new DateTime())));
                        manager.exportEvents(log, erpWizard.fromDate, erpWizard.toDate, fos);
                        log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "Ended on {0}",
                                ymdhms.print(new DateTime())));
                        erpWizard.file = file;
                        erpWizard.filename = MessageFormat.format(ERP_EXPORT_FILENAME, up.getDomainId(),
                                ymd2.print(erpWizard.fromDate), ymd2.print(erpWizard.fromDate), "csv");
                        log.addMaster(
                                new MessageLogEntry(LogEntry.Level.INFO, "File ready: {0}", erpWizard.filename));
                        log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "Operation completed succesfully"));
                        new JsonResult(new JsWizardData(log.print())).printTo(out);
                    }
                } catch (Throwable t) {
                    logger.error("Error generating export", t);
                    file.delete();
                    new JsonResult(new JsWizardData(log.print())).setSuccess(false).printTo(out);
                }
            }

        } catch (Exception ex) {
            logger.error("Error in ExportForErp", ex);
            new JsonResult(false, ex.getMessage()).printTo(out);
        }
    }

    public void processExportForErp(HttpServletRequest request, HttpServletResponse response) {
        try {
            try (FileInputStream fis = new FileInputStream(erpWizard.file)) {
                ServletUtils.setFileStreamHeaders(response, "application/octet-stream", DispositionType.ATTACHMENT,
                        erpWizard.filename);
                ServletUtils.setContentLengthHeader(response, erpWizard.file.length());
                IOUtils.copy(fis, response.getOutputStream());
            }

        } catch (Exception ex) {
            logger.error("Error in ExportForErp", ex);
        } finally {
            erpWizard = null;
        }
    }

    public void processGetPlanning(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        CoreUserSettings cus = getEnv().getCoreUserSettings();
        CoreManager core = WT.getCoreManager();
        ArrayList<MapItem> items = new ArrayList<>();
        Connection con = null;

        try {
            String eventStartDate = ServletUtils.getStringParameter(request, "startDate", true);
            String eventEndDate = ServletUtils.getStringParameter(request, "endDate", true);
            String timezone = ServletUtils.getStringParameter(request, "timezone", true);
            JsEvent.Attendee.List attendees = ServletUtils.getObjectParameter(request, "attendees",
                    new JsEvent.Attendee.List(), JsEvent.Attendee.List.class);
            //JsAttendeeList attendees = ServletUtils.getObjectParameter(request, "attendees", new JsAttendeeList(), JsAttendeeList.class);

            // Parses string parameters
            DateTimeZone eventTz = DateTimeZone.forID(timezone);
            DateTime eventStartDt = DateTimeUtils.parseYmdHmsWithZone(eventStartDate, eventTz);
            DateTime eventEndDt = DateTimeUtils.parseYmdHmsWithZone(eventEndDate, eventTz);

            UserProfile up = getEnv().getProfile();
            DateTimeZone profileTz = up.getTimeZone();

            LocalTime localStartTime = eventStartDt.toLocalTime();
            LocalTime localEndTime = eventEndDt.toLocalTime();
            LocalTime fromTime = DateTimeUtils.min(localStartTime, us.getWorkdayStart());
            LocalTime toTime = DateTimeUtils.max(localEndTime, us.getWorkdayEnd());

            // Defines useful date/time formatters
            DateTimeFormatter ymdhmFmt = DateTimeUtils.createYmdHmFormatter();
            DateTimeFormatter tFmt = DateTimeUtils.createFormatter(cus.getShortTimeFormat());
            DateTimeFormatter dFmt = DateTimeUtils.createFormatter(cus.getShortDateFormat());

            ArrayList<String> spans = manager.generateTimeSpans(60, eventStartDt.toLocalDate(),
                    eventEndDt.toLocalDate(), us.getWorkdayStart(), us.getWorkdayEnd(), profileTz);

            // Generates fields and columnsInfo dynamically
            ArrayList<FieldMeta> fields = new ArrayList<>();
            ArrayList<GridColumnMeta> colsInfo = new ArrayList<>();

            GridColumnMeta col = null;
            fields.add(new FieldMeta("recipient"));
            colsInfo.add(new GridColumnMeta("recipient"));
            for (String spanKey : spans) {
                LocalDateTime ldt = ymdhmFmt.parseLocalDateTime(spanKey);
                fields.add(new FieldMeta(spanKey));
                col = new GridColumnMeta(spanKey, tFmt.print(ldt));
                col.put("date", dFmt.print(ldt));
                col.put("overlaps", (ldt.compareTo(eventStartDt.toLocalDateTime()) >= 0)
                        && (ldt.compareTo(eventEndDt.toLocalDateTime()) < 0));
                colsInfo.add(col);
            }

            // Collects attendees availability...
            OUser user = null;
            UserProfileId profileId = null;
            LinkedHashSet<String> busyHours = null;
            MapItem item = null;
            for (JsEvent.Attendee attendee : attendees) {
                item = new MapItem();
                item.put("recipient", attendee.recipient);

                user = guessUserByAttendee(core, attendee.recipient);
                if (user != null) {
                    profileId = new UserProfileId(user.getDomainId(), user.getUserId());
                    busyHours = manager.calculateAvailabilitySpans(60, profileId, eventStartDt.withTime(fromTime),
                            eventEndDt.withTime(toTime), eventTz, true);
                    for (String hourKey : spans) {
                        item.put(hourKey, busyHours.contains(hourKey) ? "busy" : "free");
                    }
                } else {
                    for (String spanKey : spans) {
                        item.put(spanKey, "unknown");
                    }
                }
                items.add(item);
            }

            GridMetadata meta = new GridMetadata(true);
            meta.setFields(fields);
            meta.setColumnsInfo(colsInfo);
            new JsonResult(items, meta, items.size()).printTo(out);

        } catch (Exception ex) {
            logger.error("Error in GetPlanning", ex);
            new JsonResult(false, "Error").printTo(out);

        } finally {
            DbUtils.closeQuietly(con);
        }
    }

    public void processImportEventsFromICal(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        UserProfile up = getEnv().getProfile();

        try {
            String uploadId = ServletUtils.getStringParameter(request, "uploadId", true);
            String op = ServletUtils.getStringParameter(request, "op", true);

            UploadedFile upl = getUploadedFile(uploadId);
            if (upl == null)
                throw new WTException("Uploaded file not found [{0}]", uploadId);
            File file = new File(WT.getTempFolder(), upl.getUploadId());

            EventICalFileReader rea = new EventICalFileReader(up.getTimeZone());

            if (op.equals("do")) {
                Integer calendarId = ServletUtils.getIntParameter(request, "calendarId", true);
                String mode = ServletUtils.getStringParameter(request, "importMode", true);

                LogEntries log = manager.importEvents(calendarId, rea, file, mode);
                removeUploadedFile(uploadId);
                new JsonResult(new JsWizardData(log.print())).printTo(out);
            }

        } catch (Exception ex) {
            logger.error("Error in ImportContactsFromICal", ex);
            new JsonResult(false, ex.getMessage()).printTo(out);
        }
    }

    public void processPrintScheduler(HttpServletRequest request, HttpServletResponse response) {
        ByteArrayOutputStream baos = null;
        UserProfile up = getEnv().getProfile();

        try {
            String filename = ServletUtils.getStringParameter(request, "filename", "print");
            String view = ServletUtils.getStringParameter(request, "view", "w5");
            String from = ServletUtils.getStringParameter(request, "startDate", true);

            DateTime startDate = DateTimeUtils.parseYmdHmsWithZone(from, "00:00:00", up.getTimeZone());

            ReportConfig.Builder builder = reportConfigBuilder();
            DateTime fromDate = null, toDate = null;
            AbstractAgenda rpt = null;
            if (view.equals("d")) {
                fromDate = startDate.withTimeAtStartOfDay();
                toDate = startDate.plusDays(1).withTimeAtStartOfDay();
                rpt = new RptAgendaSummary(builder.build(), 1);

            } else if (view.equals("w5")) {
                fromDate = startDate.withTimeAtStartOfDay();
                toDate = startDate.plusDays(5).withTimeAtStartOfDay();
                rpt = new RptAgendaWeek5(builder.build());

            } else if (view.equals("w")) {
                fromDate = startDate.withTimeAtStartOfDay();
                toDate = startDate.plusDays(7).withTimeAtStartOfDay();
                rpt = new RptAgendaWeek7(builder.build());

            } else if (view.equals("dw")) {
                fromDate = startDate.withTimeAtStartOfDay();
                toDate = startDate.plusDays(14).withTimeAtStartOfDay();
                rpt = new RptAgendaSummary(builder.build(), 14);

            } else if (view.equals("m")) {
                if (startDate.getDayOfMonth() == 1) {
                    fromDate = startDate.withTimeAtStartOfDay();
                } else {
                    fromDate = startDate.plusMonths(1).withDayOfMonth(1).withTimeAtStartOfDay();
                }
                int days = fromDate.dayOfMonth().getMaximumValue();
                toDate = fromDate.plusMonths(1).withDayOfMonth(1).withTimeAtStartOfDay();
                rpt = new RptAgendaSummary(builder.build(), days);

            } else {
                throw new WTException("View not supported [{0}]", view);
            }

            Set<Integer> activeCalIds = getActiveFolderIds();
            Map<Integer, Calendar> calendars = folders.entrySet().stream()
                    .filter(map -> activeCalIds.contains(map.getKey()))
                    .collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue().getCalendar()));

            List<SchedEventInstance> instances = manager.listEventInstances(activeCalIds,
                    new DateTimeRange(fromDate, toDate), up.getTimeZone(), true);
            rpt.setDataSource(manager, fromDate, toDate, up.getTimeZone(), calendars, instances);

            baos = new ByteArrayOutputStream();
            WT.generateReportToStream(rpt, AbstractReport.OutputType.PDF, baos);
            ServletUtils.setContentDispositionHeader(response, "inline", filename + ".pdf");
            ServletUtils.writeContent(response, baos, "application/pdf");

        } catch (Exception ex) {
            logger.error("Error in PrintScheduler", ex);
            ServletUtils.writeErrorHandlingJs(response, ex.getMessage());
        } finally {
            IOUtils.closeQuietly(baos);
        }
    }

    public void processPrintEventsDetail(HttpServletRequest request, HttpServletResponse response) {
        ArrayList<RBEventDetail> items = new ArrayList<>();
        ByteArrayOutputStream baos = null;
        CoreManager core = WT.getCoreManager();
        UserProfile up = getEnv().getProfile();

        try {
            String filename = ServletUtils.getStringParameter(request, "filename", "print");
            StringArray keys = ServletUtils.getObjectParameter(request, "keys", StringArray.class, true);

            RRuleStringify.Strings strings = WT.getRRuleStringifyStrings(up.getLocale());
            RRuleStringify rrs = new RRuleStringify(strings, up.getTimeZone());

            EventInstance event = null;
            Calendar calendar = null;
            for (String key : keys) {
                event = manager.getEventInstance(key);
                calendar = manager.getCalendar(event.getCalendarId());
                items.add(new RBEventDetail(core, rrs, calendar, event));
            }

            ReportConfig.Builder builder = reportConfigBuilder();
            RptEventsDetail rpt = new RptEventsDetail(builder.build());
            rpt.setDataSource(items);

            baos = new ByteArrayOutputStream();
            WT.generateReportToStream(rpt, AbstractReport.OutputType.PDF, baos);
            ServletUtils.setContentDispositionHeader(response, "inline", filename + ".pdf");
            ServletUtils.writeContent(response, baos, "application/pdf");

        } catch (Exception ex) {
            logger.error("Error in PrintContacts", ex);
            ServletUtils.writeErrorHandlingJs(response, ex.getMessage());
        } finally {
            IOUtils.closeQuietly(baos);
        }
    }

    private DateTimeRange buildSearchRange(DateTime now, DateTimeZone timezone) {
        return new DateTimeRange(now.withZone(timezone).minusMonths(6).withTimeAtStartOfDay(),
                now.withZone(timezone).plusMonths(6).withTimeAtStartOfDay());
    }

    public void processPortletEvents(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        ArrayList<JsPletEvents> items = new ArrayList<>();
        UserProfile up = getEnv().getProfile();
        DateTimeZone utz = up.getTimeZone();

        try {
            String query = ServletUtils.getStringParameter(request, "query", null);

            if (query == null) {
                final ShareRootCalendar root = roots.get(MyShareRootCalendar.SHARE_ID);
                final List<Integer> ids = manager.listCalendarIds();
                for (SchedEventInstance instance : manager.listUpcomingEventInstances(ids, DateTimeUtils.now(),
                        utz)) {
                    final ShareFolderCalendar folder = folders.get(instance.getCalendarId());
                    if (folder == null)
                        continue;

                    items.add(new JsPletEvents(root, folder, instance, utz));
                }
            } else {
                final Set<Integer> ids = folders.keySet();
                final String pattern = LangUtils.patternizeWords(query);
                List<SchedEventInstance> instances = manager.listEventInstances(ids,
                        buildSearchRange(DateTimeUtils.now(), utz), EventQuery.toCondition(pattern), utz, true);
                for (SchedEventInstance instance : instances) {
                    final ShareRootCalendar root = rootByFolder.get(instance.getCalendarId());
                    if (root == null)
                        continue;
                    final ShareFolderCalendar fold = folders.get(instance.getCalendarId());
                    if (fold == null)
                        continue;
                    //CalendarPropSet pset = folderProps.get(instance.getCalendarId());

                    items.add(new JsPletEvents(root, fold, instance, utz));
                }
            }
            new JsonResult(items).printTo(out);

        } catch (Exception ex) {
            logger.error("Error in PortletEvents", ex);
            new JsonResult(false, "Error").printTo(out);
        }
    }

    private ReportConfig.Builder reportConfigBuilder() {
        UserProfile.Data ud = getEnv().getProfile().getData();
        CoreUserSettings cus = getEnv().getCoreUserSettings();
        return new ReportConfig.Builder().useLocale(ud.getLocale()).useTimeZone(ud.getTimeZone().toTimeZone())
                .dateFormatShort(cus.getShortDateFormat()).dateFormatLong(cus.getLongDateFormat())
                .timeFormatShort(cus.getShortTimeFormat()).timeFormatLong(cus.getLongTimeFormat())
                .generatedBy(WT.getPlatformName() + " " + lookupResource(CalendarLocale.SERVICE_NAME))
                .printedBy(ud.getDisplayName());
    }

    private OUser guessUserByAttendee(CoreManager core, String recipient) {
        Connection con = null;

        try {
            InternetAddress ia = InternetAddressUtils.toInternetAddress(recipient);
            if (ia == null)
                return null;
            List<UserProfileId> pids = core.listUserIdsByEmail(ia.getAddress());
            if (pids.isEmpty())
                return null;
            UserProfileId pid = pids.get(0);

            con = WT.getCoreConnection();
            UserDAO udao = UserDAO.getInstance();
            return udao.selectByDomainUser(con, pid.getDomainId(), pid.getUserId());

        } catch (WTRuntimeException ex) {
            return null;
        } catch (Exception ex) {
            logger.error("Error guessing user from attendee", ex);
            return null;
        } finally {
            DbUtils.closeQuietly(con);
        }
    }

    private String buildSharingPath(Sharing sharing) throws WTException {
        StringBuilder sb = new StringBuilder();

        // Root description part
        CompositeId cid = new CompositeId().parse(sharing.getId());
        if (roots.containsKey(cid.getToken(0))) {
            ShareRootCalendar root = roots.get(cid.getToken(0));
            if (root instanceof MyShareRootCalendar) {
                sb.append(lookupResource(CalendarLocale.CALENDARS_MY));
            } else {
                sb.append(root.getDescription());
            }
        }

        // Folder description part
        if (sharing.getLevel() == 1) {
            int calId = Integer.valueOf(cid.getToken(1));
            Calendar calendar = manager.getCalendar(calId);
            sb.append("/");
            sb.append((calendar != null) ? calendar.getName() : cid.getToken(1));
        }

        return sb.toString();
    }

    private LinkedHashSet<Integer> getActiveFolderIds() {
        LinkedHashSet<Integer> ids = new LinkedHashSet<>();
        synchronized (roots) {
            for (ShareRootCalendar root : getActiveRoots()) {
                for (ShareFolderCalendar folder : foldersByRoot.get(root.getShareId())) {
                    if (inactiveFolders.contains(folder.getCalendar().getCalendarId()))
                        continue;
                    ids.add(folder.getCalendar().getCalendarId());
                }
            }
        }
        return ids;
    }

    private List<ShareRootCalendar> getActiveRoots() {
        return roots.values().stream().filter(root -> !inactiveRoots.contains(root.getShareId()))
                .collect(Collectors.toList());
    }

    private void toggleActiveRoot(String shareId, boolean active) {
        toggleActiveRoots(new String[] { shareId }, active);
    }

    private void toggleActiveRoots(String[] shareIds, boolean active) {
        synchronized (roots) {
            for (String shareId : shareIds) {
                if (active) {
                    inactiveRoots.remove(shareId);
                } else {
                    inactiveRoots.add(shareId);
                }
            }
            us.setInactiveCalendarRoots(inactiveRoots);
        }
    }

    private void toggleActiveFolder(Integer folderId, boolean active) {
        toggleActiveFolders(new Integer[] { folderId }, active);
    }

    private void toggleActiveFolders(Integer[] folderIds, boolean active) {
        synchronized (roots) {
            for (int folderId : folderIds) {
                if (active) {
                    inactiveFolders.remove(folderId);
                } else {
                    inactiveFolders.add(folderId);
                }
            }
            us.setInactiveCalendarFolders(inactiveFolders);
        }
    }

    private void updateCalendarFolderVisibility(int calendarId, Boolean hidden) {
        synchronized (roots) {
            try {
                CalendarPropSet pset = manager.getCalendarCustomProps(calendarId);
                pset.setHidden(hidden);
                manager.updateCalendarCustomProps(calendarId, pset);

                // Update internal cache
                ShareFolderCalendar folder = folders.get(calendarId);
                if (!(folder instanceof MyShareFolderCalendar)) {
                    folderProps.put(calendarId, pset);
                }
            } catch (WTException ex) {
                logger.error("Error saving custom calendar props", ex);
            }
        }
    }

    private void updateCalendarFolderColor(int calendarId, String color) throws WTException {
        synchronized (roots) {
            if (folders.get(calendarId) instanceof MyShareFolderCalendar) {
                Calendar cal = manager.getCalendar(calendarId);
                cal.setColor(color);
                manager.updateCalendar(cal);
                updateFoldersCache();
            } else {
                CalendarPropSet pset = manager.getCalendarCustomProps(calendarId);
                pset.setColor(color);
                manager.updateCalendarCustomProps(calendarId, pset);
                folderProps.put(calendarId, pset);
            }
        }
    }

    private void updateCalendarFolderSync(int calendarId, Calendar.Sync sync) throws WTException {
        synchronized (roots) {
            if (folders.get(calendarId) instanceof MyShareFolderCalendar) {
                Calendar cat = manager.getCalendar(calendarId);
                cat.setSync(sync);
                manager.updateCalendar(cat);
                updateFoldersCache();
            } else {
                CalendarPropSet pset = manager.getCalendarCustomProps(calendarId);
                pset.setSync(sync);
                manager.updateCalendarCustomProps(calendarId, pset);
                folderProps.put(calendarId, pset);
            }
        }
    }

    private ExtTreeNode createRootNode(boolean chooser, ShareRootCalendar root) {
        if (root instanceof MyShareRootCalendar) {
            return createRootNode(chooser, root.getShareId(), root.getOwnerProfileId().toString(),
                    root.getPerms().toString(), lookupResource(CalendarLocale.CALENDARS_MY), false,
                    "wtcal-icon-root-my-xs").setExpanded(true);
        } else {
            return createRootNode(chooser, root.getShareId(), root.getOwnerProfileId().toString(),
                    root.getPerms().toString(), root.getDescription(), false, "wtcal-icon-root-incoming-xs")
                            .setExpanded(true);
        }
    }

    private ExtTreeNode createRootNode(boolean chooser, String id, String pid, String rights, String text,
            boolean leaf, String iconClass) {
        boolean active = !inactiveRoots.contains(id);
        ExtTreeNode node = new ExtTreeNode(id, text, leaf);
        node.put("_type", JsFolderNode.TYPE_ROOT);
        node.put("_pid", pid);
        node.put("_rrights", rights);
        node.put("_active", active);
        node.setIconClass(iconClass);
        if (!chooser)
            node.setChecked(active);
        node.put("expandable", false);
        return node;
    }

    private ExtTreeNode createFolderNode(boolean chooser, ShareFolderCalendar folder, CalendarPropSet folderProps,
            SharePermsRoot rootPerms) {
        Calendar cal = folder.getCalendar();
        String id = new CompositeId().setTokens(folder.getShareId(), cal.getCalendarId()).toString();
        String color = cal.getColor();
        Calendar.Sync sync = Calendar.Sync.OFF;
        boolean active = !inactiveFolders.contains(cal.getCalendarId());

        if (folderProps != null) { // Props are not null only for incoming folders
            if (folderProps.getHiddenOrDefault(false))
                return null;
            color = folderProps.getColorOrDefault(color);
            sync = folderProps.getSyncOrDefault(sync);
        } else {
            sync = cal.getSync();
        }

        ExtTreeNode node = new ExtTreeNode(id, cal.getName(), true);
        node.put("_type", JsFolderNode.TYPE_FOLDER);
        node.put("_pid", cal.getProfileId().toString());
        node.put("_rrights", rootPerms.toString());
        node.put("_frights", folder.getPerms().toString());
        node.put("_erights", folder.getElementsPerms().toString());
        node.put("_calId", cal.getCalendarId());
        node.put("_builtIn", cal.getBuiltIn());
        node.put("_provider", EnumUtils.toSerializedName(cal.getProvider()));
        node.put("_color", Calendar.getHexColor(color));
        node.put("_sync", EnumUtils.toSerializedName(sync));
        node.put("_default", cal.getIsDefault());
        node.put("_active", active);
        node.put("_isPrivate", cal.getIsPrivate());
        node.put("_defBusy", cal.getDefaultBusy());
        node.put("_defReminder", cal.getDefaultReminder());
        if (!chooser)
            node.setChecked(active);
        return node;
    }

    private class SyncRemoteCalendarAA extends BaseServiceAsyncAction {
        private final int calendarId;
        private final String calendarName;
        private final boolean full;

        public SyncRemoteCalendarAA(int calendarId, String calendarName, boolean full) {
            super();
            setName(this.getClass().getSimpleName());
            this.calendarId = calendarId;
            this.calendarName = calendarName;
            this.full = full;
        }

        @Override
        public void executeAction() {
            getWts().notify(new RemoteSyncResult(true).setCalendarId(calendarId).setCalendarName(calendarName)
                    .setSuccess(true));
            try {
                manager.syncRemoteCalendar(calendarId, full);
                this.completed();
                getWts().notify(new RemoteSyncResult(false).setCalendarId(calendarId).setCalendarName(calendarName)
                        .setSuccess(true));

            } catch (WTException ex) {
                logger.error("Remote sync failed", ex);
                getWts().notify(new RemoteSyncResult(false).setCalendarId(calendarId).setCalendarName(calendarName)
                        .setThrowable(ex, true));
            } finally {
                synchronized (syncRemoteCalendarAAs) {
                    syncRemoteCalendarAAs.remove(calendarId);
                }
            }
        }
    }

    private static class CalendarProbeException extends WTException {
        public CalendarProbeException(String message, Object... arguments) {
            super(message, arguments);
        }
    }

    private static class ErpExportWizard {
        public DateTime fromDate;
        public DateTime toDate;
        public File file;
        public String filename;
    }
}