All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bedework.appcommon.client.ROClientImpl Maven / Gradle / Ivy

The newest version!
/* ********************************************************************
    Licensed to Jasig under one or more contributor license
    agreements. See the NOTICE file distributed with this work
    for additional information regarding copyright ownership.
    Jasig licenses this file to you under the Apache License,
    Version 2.0 (the "License"); you may not use this file
    except in compliance with the License. You may obtain a
    copy of the License at:

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on
    an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied. See the License for the
    specific language governing permissions and limitations
    under the License.
*/
package org.bedework.appcommon.client;

import org.bedework.access.PrivilegeDefs;
import org.bedework.appcommon.BedeworkDefs;
import org.bedework.appcommon.CollectionCollator;
import org.bedework.appcommon.ConfigCommon;
import org.bedework.appcommon.EventFormatter;
import org.bedework.appcommon.EventKey;
import org.bedework.caldav.util.filter.FilterBase;
import org.bedework.calfacade.BwCalendar;
import org.bedework.calfacade.BwCategory;
import org.bedework.calfacade.BwContact;
import org.bedework.calfacade.BwDateTime;
import org.bedework.calfacade.BwFilterDef;
import org.bedework.calfacade.BwGroup;
import org.bedework.calfacade.BwLocation;
import org.bedework.calfacade.BwPrincipal;
import org.bedework.calfacade.BwProperty;
import org.bedework.calfacade.BwString;
import org.bedework.calfacade.DirectoryInfo;
import org.bedework.calfacade.RecurringRetrievalMode;
import org.bedework.calfacade.configs.AuthProperties;
import org.bedework.calfacade.configs.SystemProperties;
import org.bedework.calfacade.exc.CalFacadeException;
import org.bedework.calfacade.filter.BwCollectionFilter;
import org.bedework.calfacade.filter.SimpleFilterParser.ParseResult;
import org.bedework.calfacade.indexing.BwIndexer;
import org.bedework.calfacade.indexing.BwIndexer.DeletedState;
import org.bedework.calfacade.indexing.BwIndexer.Position;
import org.bedework.calfacade.indexing.SearchResult;
import org.bedework.calfacade.indexing.SearchResultEntry;
import org.bedework.calfacade.locale.BwLocale;
import org.bedework.calfacade.responses.GetFilterDefResponse;
import org.bedework.calfacade.svc.BwCalSuite;
import org.bedework.calfacade.svc.BwPreferences;
import org.bedework.calfacade.svc.BwView;
import org.bedework.calfacade.svc.CalSvcIPars;
import org.bedework.calfacade.svc.EventInfo;
import org.bedework.calfacade.svc.wrappers.BwCalSuiteWrapper;
import org.bedework.calsvci.CalSvcFactoryDefault;
import org.bedework.calsvci.CalSvcI;
import org.bedework.calsvci.CalendarsI.SynchStatusResponse;
import org.bedework.convert.IcalTranslator;
import org.bedework.sysevents.events.HttpEvent;
import org.bedework.sysevents.events.HttpOutEvent;
import org.bedework.sysevents.events.SysEventBase;
import org.bedework.util.caching.FlushMap;
import org.bedework.util.logging.BwLogger;
import org.bedework.util.logging.Logged;
import org.bedework.util.misc.Util;
import org.bedework.util.misc.response.GetEntitiesResponse;
import org.bedework.util.misc.response.GetEntityResponse;
import org.bedework.util.misc.response.Response;
import org.bedework.util.webaction.Request;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.servlet.http.HttpServletResponse;

import static org.bedework.calfacade.indexing.BwIndexer.DeletedState.includeDeleted;
import static org.bedework.calfacade.indexing.BwIndexer.DeletedState.noDeleted;
import static org.bedework.calfacade.indexing.BwIndexer.docTypeCollection;
import static org.bedework.calfacade.indexing.BwIndexer.docTypeEvent;
import static org.bedework.calsvci.CalSuitesI.ResourceClass;

/**
 * User: douglm Date: 6/27/13 Time: 2:03
 */
public class ROClientImpl implements Logged, Client {
  protected ConfigCommon conf;

  protected String id;

  protected CalSvcIPars pars;

  protected CalSvcI svci;

  protected ObjectMapper mapper = new ObjectMapper(); // create once, reuse

  protected boolean publicView;

  protected boolean superUser;

  protected boolean publicAdmin;

  protected BwPrincipal currentPrincipal;
  private String currentCalendarAddress;

  private String primaryPublicPath;

  private static final Object primaryPublicPathLocker = new Object();

  private CollectionsupportedLocales;

  private final ClientState cstate;

  private transient CollectionCollator calendarCollator;
  protected String appType;

  private final Map publicIndexers = new HashMap<>();
  private final Map userIndexers = new HashMap<>();
  private SearchResult lastSearch;
  private List lastSearchEntries;

  /* Set this whenever an update occurs. We may want to delay or flush
   */
  protected long lastUpdate;

  /* Don't delay or flush until after end of request in which we
     updated.
   */
  protected long requestEnd;
  private String viewMode;

  /* The list of cloned admin groups for the use of the user client
   */
  protected static Collection> adminGroupsInfo;

  protected static Collection> calsuiteAdminGroupsInfo;

  protected BwCalSuiteWrapper calSuite;
  protected BwPrincipal calSuiteOwner;
  protected String calSuiteName;

  protected static long lastAdminGroupsInfoRefresh;
  static long adminGroupsInfoRefreshInterval = 1000 * 60 * 5;

  private static final Object adminGroupLocker = new Object();

  /**
   *
   * @param conf client configuration
   * @param id identify the client - usually module name
   * @param authUser account
   * @param runAsUser account
   * @param calSuiteName the calendar suite
   * @param appType type of application: submit, admin etc
   * @param publicView true for the public RO client
   */
  public ROClientImpl(final ConfigCommon conf,
                      final String id,
                      final String authUser,
                      final String runAsUser,
                      final String calSuiteName,
                      final String appType,
                      final boolean publicView) {
    this(conf, id);

    reinit(authUser, runAsUser, calSuiteName, appType, publicView);
  }

  protected ROClientImpl(final ConfigCommon conf,
                         final String id) {
    this.id = id;
    this.conf = conf;
    cstate = new ClientState(this);

    if (debug()) {
      mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
    }

    final DateFormat df = new SimpleDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'");

    mapper.setDateFormat(df);

    mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
  }

  public void reinit(final String authUser,
                     final String runAsUser,
                     final String calSuiteName,
                     final String appType,
                     final boolean publicView) {
    currentPrincipal = null;
    this.appType = appType;

    pars = CalSvcIPars.getRoClientPars(id,
                                       authUser,
                                       runAsUser,
                                       calSuiteName,
                                       getPublicAuth());
    svci = new CalSvcFactoryDefault().getSvc(pars);
    this.publicView = publicView;
    resetIndexers();
    this.calSuiteName = calSuiteName;
  }
  
  protected void resetIndexers() {
    publicIndexers.clear();
    userIndexers.clear();
  }

  protected BwPrincipal getCurrentCalSuiteOwner() throws CalFacadeException {
    if (calSuiteOwner != null) {
      return calSuiteOwner;
    }

    if (calSuiteName == null) {
      return null;
    }

    calSuite = getCalSuite(calSuiteName);
    if (calSuite != null) {
      final String owner = calSuite.getGroup().getOwnerHref();

      calSuiteOwner = getPrincipal(owner);
    }

    return calSuiteOwner;
  }

  @Override
  public Client copy(final String id) {
    final ROClientImpl cl = new ROClientImpl(conf, id);

    copyCommon(id, cl);

    cl.publicView = publicView;

    return cl;
  }

  protected void copyCommon(final String id,
                            final ROClientImpl cl) {
    cl.pars = (CalSvcIPars)pars.clone();
    cl.pars.setLogId(id);

    cl.svci = new CalSvcFactoryDefault().getSvc(cl.pars);
    cl.appType = appType;
    cl.calSuiteName = calSuiteName;
  }

  @Override
  public void requestIn(final int conversationType)
          throws CalFacadeException {
    postNotification(new HttpEvent(SysEventBase.SysCode.WEB_IN));
    svci.setState("Request in");

    if (conversationType == Request.conversationTypeUnknown) {
      svci.open();
      svci.beginTransaction();
      return;
    }

    if (svci.isRolledback()) {
      svci.close();
    }

    if (conversationType == Request.conversationTypeOnly) {
              /* if a conversation is already started on entry, end it
                  with no processing of changes. */
      if (svci.isOpen()) {
        svci.setState("Request in - close");
        svci.endTransaction();
      }
    }

    if (conversationType == Request.conversationTypeProcessAndOnly) {
      if (svci.isOpen()) {
        svci.setState("Request in - flush");
        svci.flushAll();
        svci.endTransaction();
        svci.close();
      }
    }

    svci.open();
    svci.beginTransaction();
    svci.setState("Request in - started");
  }

  @Override
  public void requestOut(final int conversationType,
                         final int actionType,
                         final long reqTimeMillis)
          throws CalFacadeException {
    requestEnd = System.currentTimeMillis();
    postNotification(
            new HttpOutEvent(SysEventBase.SysCode.WEB_OUT,
                             reqTimeMillis));
    svci.setState("Request out");
    publicIndexers.clear();
    userIndexers.clear();

    if (!isOpen()) {
      return;
    }

    if (conversationType == Request.conversationTypeUnknown) {
      if (actionType != Request.actionTypeAction) {
        flushAll();
      }
    } else {
      if ((conversationType == Request.conversationTypeEnd) ||
              (conversationType == Request.conversationTypeOnly)) {
        flushAll();
      }
    }

    svci.endTransaction();
    svci.setState("Request out - ended");
  }

  @Override
  public boolean isOpen() {
    return svci.isOpen();
  }

  @Override
  public void close() throws CalFacadeException {
    svci.close();
  }

  @Override
  public ConfigCommon getConf() {
    return conf;
  }

  @Override
  public void flushAll() throws CalFacadeException {
    svci.flushAll();
  }

  @Override
  public void postNotification(final SysEventBase ev) {
    svci.postNotification(ev);
  }

  @Override
  public void endTransaction() throws CalFacadeException {
    svci.endTransaction();
  }

  @Override
  public String getCurrentChangeToken() throws CalFacadeException {
    var evChg = getIndexer(isDefaultIndexPublic(),
                      docTypeEvent).currentChangeToken();
    if (evChg == null) {
      evChg = "";
    }

    return evChg + getIndexer(isDefaultIndexPublic(),
                              docTypeCollection).currentChangeToken();
  }

  @Override
  public boolean getPublicAdmin() {
    return publicAdmin;
  }

  @Override
  public boolean getWebSubmit() {
    return BedeworkDefs.appTypeWebsubmit.equals(appType);
  }

  @Override
  public boolean getPublicAuth() {
    return BedeworkDefs.appTypeWebpublicauth.equals(appType);
  }

  @Override
  public String getAppType() {
    return appType;
  }

  @Override
  public void writeJson(final HttpServletResponse resp,
                        final Object val) throws CalFacadeException {
    try {
      mapper.writeValue(resp.getOutputStream(), val);
    } catch (final Throwable t) {
      throw new CalFacadeException(t);
    }
  }

  @Override
  public void outputJson(final HttpServletResponse resp,
                         final String etag,
                         final String[] header,
                         final Object val) {
    resp.setStatus(HttpServletResponse.SC_OK);

    if (etag != null) {
      resp.setHeader("etag", etag);
    }

    if (header != null) {
      resp.setHeader(header[0], header[1]);
    }

    resp.setContentType("application/json; charset=UTF-8");

    writeJson(resp, val);
    try {
      resp.getOutputStream().close();
    } catch (final IOException ioe) {
      throw new CalFacadeException(ioe);
    }
  }

  @Override
  public AuthProperties getAuthProperties() {
    return svci.getAuthProperties();
  }

  @Override
  public SystemProperties getSystemProperties() {
    return svci.getSystemProperties();
  }

  @Override
  public void rollback() {
    try {
      svci.rollbackTransaction();
    } catch (final Throwable ignored) {}

    try {
      svci.endTransaction();
    } catch (final Throwable ignored) {}
  }

  @Override
  public long getUserMaxEntitySize() {
    return svci.getUserMaxEntitySize();
  }

  @Override
  public boolean isDefaultIndexPublic() {
    return getWebSubmit() || getPublicAdmin() || isGuest();
  }

  @Override
  public boolean isPrincipal(final String val) {
    return svci.getDirectories().isPrincipal(val);
  }

  /* ------------------------------------------------------------
   *                     Directories
   * ------------------------------------------------------------ */

  @Override
  public DirectoryInfo getDirectoryInfo() {
    return svci.getDirectories().getDirectoryInfo();
  }

  @Override
  public String getCalendarAddress(final String user) {
    final BwPrincipal u = svci.getUsersHandler().getUser(user);
    if (u == null) {
      return null;
    }

    return svci.getDirectories().principalToCaladdr(u);
  }

  @Override
  public String getPrimaryPublicPath() {
    synchronized (primaryPublicPathLocker) {
      if (primaryPublicPath == null) {
        final var primaryCal = svci.getCalendarsHandler()
                                   .getPrimaryPublicPath();
        if (primaryCal == null) {
          throw new CalFacadeException("No primary calendar set");
        }

        primaryPublicPath = primaryCal.getPath();
      }

      return primaryPublicPath;
    }
  }

  @Override
  public String uriToCaladdr(final String val) {
    return svci.getDirectories().uriToCaladdr(val);
  }

  @Override
  public BwPrincipal calAddrToPrincipal(final String cua) {
    return svci.getDirectories().caladdrToPrincipal(cua);
  }

  /* ------------------------------------------------------------
   *                     Principals
   * ------------------------------------------------------------ */

  @Override
  public boolean isSuperUser() {
    return superUser;
  }

  @Override
  public boolean isGuest() {
    return true;
  }

  @Override
  public BwPrincipal getCurrentPrincipal() {
    if (currentPrincipal == null) {
      currentPrincipal = (BwPrincipal)svci.getPrincipal().clone();
    }

    return currentPrincipal;
  }

  @Override
  public BwPrincipal getAuthPrincipal() {
    return svci.getPrincipalInfo().getAuthPrincipal();
  }

  @Override
  public BwPrincipal getOwner() {
    return getCurrentPrincipal();
  }

  @Override
  public String getCurrentPrincipalHref() {
    return getCurrentPrincipal().getPrincipalRef();
  }

  @Override
  public String getCurrentCalendarAddress() {
    if (currentCalendarAddress == null) {
      currentCalendarAddress = svci.getDirectories().principalToCaladdr(getCurrentPrincipal());
    }

    return currentCalendarAddress;
  }

  @Override
  public BwPrincipal getUser(final String val) {
    return svci.getUsersHandler().getUser(val);
  }

  @Override
  public String makePrincipalUri(final String id,
                                 final int whoType) {
    return svci.getDirectories().makePrincipalUri(id, whoType);
  }

  @Override
  public BwPrincipal getPrincipal(final String href)
          throws CalFacadeException {
    return svci.getDirectories().getPrincipal(href);
  }

  @Override
  public boolean validPrincipal(final String href) {
    return svci.getDirectories().validPrincipal(href);
  }

  /* ------------------------------------------------------------
   *                     Admin Groups
   * ------------------------------------------------------------ */

  @Override
  public Collection> getAdminGroups()
          throws CalFacadeException {
    return refreshAdminGroupInfo();
  }

  /* ------------------------------------------------------------
   *                     Preferences
   * ------------------------------------------------------------ */

  @Override
  public BwPreferences getPreferences() {
    if (publicView) {
      final BwPreferences prefs = getCalsuitePreferences();
      if (prefs != null) {
        return prefs;
      }
    }

    return svci.getPrefsHandler().get();
  }

  public BwPreferences getCalsuitePreferences() {
    return getCalsuitePreferences(getCalSuite());
  }

  @Override
  public BwPreferences getPreferences(final String user) {
    return null;
  }

  @Override
  public void updatePreferences(final BwPreferences val)
          throws CalFacadeException {
    throw new CalFacadeException("org.bedework.read.only.client");
  }

  @Override
  public String getPreferredCollectionPath(final String compName)
          throws CalFacadeException {
    return svci.getCalendarsHandler().getPreferred(compName);
  }

  /** Set false to inhibit lastLocale stuff */
  public static boolean tryLastLocale = true;

  @Override
  public Locale getUserLocale(final Collection locales,
                              final Locale locale) {
    final Collection sysLocales = getSupportedLocales();

    if (locale != null) {
      /* See if it's acceptable */
      final Locale l = BwLocale.matchLocales(sysLocales, locale);
      if (l != null) {
        if (debug()) {
          debug("Setting locale to " + l);
        }
        return l;
      }
    }

    /* See if the user expressed a preference */
    final Collection properties = getPreferences().getProperties();
    String preferredLocaleStr = null;
    String lastLocaleStr = null;

    if (properties != null) {
      for (final BwProperty prop: properties) {
        if (preferredLocaleStr == null) {
          if (prop.getName().equals(BwPreferences.propertyPreferredLocale)) {
            preferredLocaleStr = prop.getValue();
            if (!tryLastLocale) {
              break;
            }
          }
        }

        if (tryLastLocale) {
          if (lastLocaleStr == null) {
            if (prop.getName().equals(BwPreferences.propertyLastLocale)) {
              lastLocaleStr = prop.getValue();
            }
          }
        }

        if ((preferredLocaleStr != null) &&
                (lastLocaleStr != null)) {
          break;
        }
      }
    }

    if (preferredLocaleStr != null) {
      final Locale l = BwLocale.matchLocales(sysLocales,
                                             makeLocale(preferredLocaleStr));
      if (l != null) {
        if (debug()) {
          debug("Setting locale to " + l);
        }
        return l;
      }
    }

    if (lastLocaleStr != null) {
      final Locale l = BwLocale.matchLocales(sysLocales,
                                             makeLocale(lastLocaleStr));
      if (l != null) {
        if (debug()) {
          debug("Setting locale to " + l);
        }
        return l;
      }
    }

    /* See if the supplied list has a match in the supported locales */

    if (locales != null) {
      // We had an ACCEPT-LANGUAGE header

      for (final Locale loc: locales) {
        final Locale l = BwLocale.matchLocales(sysLocales, loc);
        if (l != null) {
          if (debug()) {
            debug("Setting locale to " + l);
          }
          return l;
        }
      }
    }

    /* Use the first from supported locales -
     * there's always at least one in the collection */
    final Locale l = sysLocales.iterator().next();

    if (debug()) {
      debug("Setting locale to " + l);
    }
    return l;
  }

  /* ------------------------------------------------------------
   *                     Collections
   * ------------------------------------------------------------ */

  @Override
  public BwCalendar getHome() throws CalFacadeException {
    return svci.getCalendarsHandler().getHome();
  }

  @Override
  public BwCalendar getCollection(final String path) {
    checkUpdate();
    return svci.getCalendarsHandler().get(path);
  }

  @Override
  public boolean collectionExists(final String path)
          throws CalFacadeException {
    checkUpdate();
    return getCollection(path) != null;
  }

  @Override
  public BwCalendar getSpecial(final int calType,
                               final boolean create)
          throws CalFacadeException {
    checkUpdate();

    return svci.getCalendarsHandler().getSpecial(calType, create);
  }

  @Override
  public BwCalendar resolveAlias(final BwCalendar val,
                                 final boolean resolveSubAlias,
                                 final boolean freeBusy)
          throws CalFacadeException {
    checkUpdate();
    return svci.getCalendarsHandler().resolveAliasIdx(val,
                                                      resolveSubAlias,
                                                      freeBusy);
  }

  @Override
  public Collection getChildren(final BwCalendar col)
          throws CalFacadeException {
    checkUpdate();

    if (!col.getPublick()) {
      return svci.getCalendarsHandler().getChildrenIdx(col);
    }

    // This and its children need to be cached
    BwCalendar ourCopy;

    synchronized (publicCloned) {
      if (!col.unsaved()) {
        ourCopy = publicCloned.get(col.getPath());

        if (ourCopy == null) {
          ourCopy = col.shallowClone();
          publicCloned.put(col.getPath(), ourCopy);
        }
      } else {
        ourCopy = col;
      }

      Collection children = ourCopy.getChildren();
      if (children != null) {
        // Assume ok
        return children;
      }

      children = col.getChildren();
      if (children == null) {
        // Have to retrieve
        children = svci.getCalendarsHandler().getChildrenIdx(col);
      }

      // Assume we have to clone

      final Collection ourChildren = new ArrayList<>();

      for (final BwCalendar ch: children) {
        BwCalendar ourCh = ch;
        if (!ch.unsaved()) {
          ourCh = publicCloned.get(ch.getPath());

          if (ourCh == null) {
            ourCh = ch.shallowClone();
            publicCloned.put(ch.getPath(), ourCh);
          }
        }

        ourChildren.add(ourCh);
      }

      ourCopy.setChildren(ourChildren);

      return ourChildren;
    }
  }

  @Override
  public SynchStatusResponse getSynchStatus(final String path) throws CalFacadeException {
    return svci.getCalendarsHandler().getSynchStatus(path);
  }

  @Override
  public Collection decomposeVirtualPath(final String vpath)
          throws CalFacadeException {
    return svci.getCalendarsHandler().decomposeVirtualPath(vpath);
  }

  @Override
  public String getPublicCalendarsRootPath() {
    return svci.getCalendarsHandler().getPublicCalendarsRootPath();
  }

  @Override
  public BwCalendar getPublicCalendars() throws CalFacadeException {
    checkUpdate();

    final String path = svci.getCalendarsHandler().getPublicCalendarsRootPath();

    BwCalendar res = publicCloned.get(path);

    if (res != null) {
      return res;
    }

    res = svci.getCalendarsHandler().getPublicCalendars();
    
    if (res == null) {
      warn("*****************************************************" +
           "Unable to retrieve public calendar root " + path +
           "*****************************************************");
      return null;
    }

    synchronized (publicCloned) {
      if (publicCloned.get(path) != null) {
        // Somebody moved in.
        return publicCloned.get(path);
      }

      publicCloned.put(path, res);
    }

    return res;
  }

  @Override
  public BwCalendar getHome(final BwPrincipal principal,
                            final boolean freeBusy)
          throws CalFacadeException {
    return svci.getCalendarsHandler().getHome(principal, freeBusy);
  }

  public void flushCached() {
    synchronized (publicCloned) {
      publicCloned.clear();
    }
  }

  private final static Map publicCloned =
      new HashMap<>();

  /* ------------------------------------------------------------
   *                     Categories
   * ------------------------------------------------------------ */

  @Override
  public GetEntityResponse getCategoryByName(final BwString name) {
    checkUpdate();
    return svci.getCategoriesHandler().findPersistent(name);
  }

  @Override
  public BwCategory getCategoryByUid(final String uid) {
    checkUpdate();
    final var resp = svci.getCategoriesHandler().getByUid(uid);
    checkResponse(resp);
    return resp.getEntity();
  }

  @Override
  public BwCategory getCategory(final String href)
          throws CalFacadeException {
    checkUpdate();
    return svci.getCategoriesHandler().get(href);
  }

  @Override
  public Collection getCategories()
          throws CalFacadeException {
    checkUpdate();
    return svci.getCategoriesHandler().get();
  }

  @Override
  public Collection getPublicCategories()
          throws CalFacadeException {
    checkUpdate();
    return svci.getCategoriesHandler().getPublic();
  }

  @Override
  public Collection getEditableCategories()
          throws CalFacadeException {
    checkUpdate();
    return svci.getCategoriesHandler().getEditable();
  }

  @Override
  public Set getDefaultPublicCategoryUids()
          throws CalFacadeException {
    final Set catUids = new TreeSet<>();

    for (final BwCalSuite suite: svci.getCalSuitesHandler().getAll()) {
      final BwPreferences prefs = getCalsuitePreferences(suite);

      if ((prefs != null) && (prefs.getDefaultCategoryUids() != null)) {
        catUids.addAll(prefs.getDefaultCategoryUids());
      }
    }

    return catUids;
  }

  /* ------------------------------------------------------------
   *                     Contacts
   * ------------------------------------------------------------ */

  @Override
  public GetEntityResponse getContactByUid(final String uid) {
    checkUpdate();
    final var resp = svci.getContactsHandler().getByUid(uid);
    checkResponse(resp);
    return resp;
  }

  @Override
  public Collection getContacts()
          throws CalFacadeException {
    checkUpdate();
    return svci.getContactsHandler().get();
  }

  @Override
  public GetEntitiesResponse getContacts(final String fexpr,
                                                    final int from,
                                                    final int size) {
    return svci.getContactsHandler().find(fexpr, from, size);
  }

  @Override
  public Collection getPublicContacts()
          throws CalFacadeException {
    checkUpdate();
    return svci.getContactsHandler().getPublic();
  }

  @Override
  public Collection getEditableContacts()
          throws CalFacadeException {
    checkUpdate();
    return svci.getContactsHandler().getEditable();
  }

  @Override
  public GetEntityResponse findContact(final BwString val) {
    final var resp = svci.getContactsHandler().findPersistent(val);
    checkResponse(resp);
    return resp;
  }

  /* ------------------------------------------------------------
   *                     Locations
   * ------------------------------------------------------------ */

  @Override
  public GetEntityResponse getLocationByUid(final String uid) {
    checkUpdate();
    final var resp = svci.getLocationsHandler().getByUid(uid);
    checkResponse(resp);
    return resp;
  }

  @Override
  public Collection getLocations()
          throws CalFacadeException {
    checkUpdate();
    return svci.getLocationsHandler().get();
  }

  @Override
  public GetEntitiesResponse getLocations(final String fexpr,
                                                      final int from,
                                                      final int size) {
    return svci.getLocationsHandler().find(fexpr, from, size);
  }

  @Override
  public Collection getPublicLocations()
          throws CalFacadeException {
    checkUpdate();
    return svci.getLocationsHandler().getPublic();
  }

  @Override
  public Collection getEditableLocations()
          throws CalFacadeException {
    checkUpdate();
    return svci.getLocationsHandler().getEditable();
  }

  @Override
  public GetEntityResponse findLocation(final BwString address) {
    final var resp = svci.getLocationsHandler().findPersistent(address);
    checkResponse(resp);
    return resp;
  }

  @Override
  public GetEntityResponse fetchLocationByCombined(
          final String val,
          final boolean persisted) {
    return svci.getLocationsHandler().fetchLocationByCombined(val,
                                                              persisted);
  }

  @Override
  public GetEntityResponse fetchLocationByKey(
          final String name,
          final String val) {
    return svci.getLocationsHandler().fetchLocationByKey(name, val);
  }

  /* ------------------------------------------------------------
   *                     Events
   * ------------------------------------------------------------ */

  @Override
  public GetEntitiesResponse getEventByUid(final String path,
                                             final String guid,
                                             final String rid,
                                             final RecurringRetrievalMode recurRetrieval) {
    final GetEntitiesResponse resp = new GetEntitiesResponse<>();

    try {
      final var ents =
              svci.getEventsHandler()
                  .getByUid(path, guid,
                            rid,
                            recurRetrieval);
      if (Util.isEmpty(ents)) {
        resp.setStatus(Response.Status.notFound);
      } else {
        resp.setEntities(ents);
      }

      return resp;
    } catch (final Throwable t) {
      checkResponse(resp); // Will force an error
      return Response.error(resp, t); // fake return
    }
  }

  @Override
  public SearchParams getSearchParams() {
    return cstate.getSearchParams();
  }

  @Override
  public EventInfo getEvent(final String colPath,
                            final String name,
                            final String recurrenceId) {
    return svci.getEventsHandler().get(colPath, name, recurrenceId);
  }

  @Override
  public EventInfo getEvent(final String href)
          throws CalFacadeException {
    final EventKey key = new EventKey(href, false);
    return svci.getEventsHandler().get(key.getColPath(),
                                       key.getName(),
                                       key.getRecurrenceId());
  }

  @Override
  public Collection getEvents(final String filter,
                                         final BwDateTime startDate,
                                         final BwDateTime endDate,
                                         final boolean expand)
          throws CalFacadeException {
    if (filter == null) {
      return null;
    }

    checkUpdate();

    final BwFilterDef fd = new BwFilterDef();
    fd.setDefinition(filter);

    parseFilter(fd);

    final RecurringRetrievalMode rrm;
    if (expand) {
      rrm = RecurringRetrievalMode.expanded;
    } else {
      rrm = RecurringRetrievalMode.overrides;
    }

    return svci.getEventsHandler().getEvents(null,
                                             fd.getFilters(),
                                             startDate,
                                             endDate,
                                             null,
                                             noDeleted,
                                             rrm);
  }

  /* ------------------------------------------------------------
   *                     Views
   * ------------------------------------------------------------ */

  @Override
  public void setViewMode(final String val) {
    viewMode = val;
  }

  @Override
  public String getViewMode() {
    if (viewMode != null) {
      return viewMode;
    }

    viewMode = getPreferences().getDefaultViewMode();
    if (viewMode == null) {
      if (getPublicAdmin()) {
        viewMode = listViewMode;
      } else {
        viewMode = gridViewMode;
      }
    }

    return viewMode;
  }

  @Override
  public BwView getView(final String val) throws CalFacadeException {
    checkUpdate();
    return svci.getViewsHandler().find(val);
  }

  @Override
  public Collection getAllViews() throws CalFacadeException {
    if (getPublicAuth() && (getCurrentCalSuiteOwner() != null)) {
      return svci.getViewsHandler().getAll(getCurrentCalSuiteOwner());
    }
    return svci.getViewsHandler().getAll();
  }

  /* ------------------------------------------------------------
   *                     State of client
   * ------------------------------------------------------------ */

  @Override
  public void flushState() {
  }

  /* ------------------------------------------------------------
   *                     Search
   * ------------------------------------------------------------ */

  @Override
  public void clearSearch() {
    cstate.setSearchParams(null);
    lastSearch = null;
  }

  @Override
  public void clearSearchEntries() {
    lastSearchEntries = null;
  }

  @Override
  public SearchResult search(final SearchParams params) throws CalFacadeException {
    checkUpdate();
    cstate.setSearchParams(params);

    lastSearchEntries = null;

    String start = null;
    String end = null;

    if (params.getFromDate() != null) {
      start = params.getFromDate().getDate();
    }

    if (params.getToDate() != null) {
      end = params.getToDate().getDate();
    }

    boolean publicIndex = isDefaultIndexPublic();

    if (params.getPublicIndexRequested()) {
      publicIndex = true;
    }

    final DeletedState delState;

    if (getPublicAdmin() && isSuperUser()) {
      delState = includeDeleted;
    } else {
      delState = noDeleted;
    }

    lastSearch = getIndexer(publicIndex, docTypeEvent).search(
            params.getQuery(),
            params.getRelevance(),
            params.getFilter(),
            params.getSort(),
            getDefaultFilterContext(),
            start,
            end,
            params.getPageSize(),
            delState,
            params.getRecurMode());

    return lastSearch;
  }

  @Override
  public List getSearchResult(final Position pos) throws CalFacadeException {
    checkUpdate();
    if (lastSearch == null) {
      return null;
    }

    if ((pos == Position.current) && (lastSearchEntries != null)) {
      return lastSearchEntries;
    }

    lastSearchEntries = formatSearchResult(lastSearch.getIndexer().
            getSearchResult(lastSearch, pos, PrivilegeDefs.privAny));

    if ((lastSearch != null) && (cstate.getSearchParams() != null)) {
      cstate.getSearchParams().setCurOffset(lastSearch.getLastPageStart());
    }

    return lastSearchEntries;
  }

  @Override
  public List getSearchResult(final int start,
                                                 final int num) throws CalFacadeException {
    if (lastSearch == null) {
      return new ArrayList<>(0);
    }
    checkUpdate();

    return formatSearchResult(lastSearch.getIndexer().
            getSearchResult(lastSearch, start, num, PrivilegeDefs.privRead));
  }

  /* ------------------------------------------------------------
   *                   Calendar Suites
   * ------------------------------------------------------------ */

  @Override
  public BwCalSuiteWrapper getCalSuite() {
    try {
      return svci.getCalSuitesHandler().get();
    } catch (final CalFacadeException e) {
      throw new RuntimeException(e);
    }
  }

  private static Collection suites;

  @Override
  public Collection getContextCalSuites()
          throws CalFacadeException {
    refreshAdminGroupInfo();

    return suites;
  }

  @Override
  public BwCalSuiteWrapper getCalSuite(final String name)
          throws CalFacadeException {
    return svci.getCalSuitesHandler().get(name);
  }

  /* ------------------------------------------------------------
   *                   Filters
   * ------------------------------------------------------------ */

  @Override
  public GetFilterDefResponse getFilter(final String name) {
    return svci.getFiltersHandler().get(name);
  }

  @Override
  public ParseResult parseFilter(final BwFilterDef val) {
    return svci.getFiltersHandler().parse(val);
  }

  @Override
  public ParseResult parseSort(final String val) {
    return svci.getFiltersHandler().parseSort(val);
  }

  @Override
  public Collection getAllFilters()
          throws CalFacadeException {
    return svci.getFiltersHandler().getAll();
  }

  /* ------------------------------------------------------------
   *                   protected methods
   * ------------------------------------------------------------ */

  protected BwPreferences getCalsuitePreferences(final BwCalSuite cs) {
    if (cs == null) {
      return null;
    }

    final String csHref = cs.getGroup().getOwnerHref();

    final BwPrincipal p = getUser(csHref);
    if (p == null) {
      return null;
    }

    return svci.getPrefsHandler().get(p);
  }

  protected Collection> refreshAdminGroupInfo() {
    final var res = adminGroupsInfo; // Save in case adminGroupsInfo set to null
    if ((res != null) &&
            (System.currentTimeMillis() < (lastAdminGroupsInfoRefresh +
                                                   adminGroupsInfoRefreshInterval))) {
      return res;
    }

    synchronized (adminGroupLocker) {
      final Set groupHrefs = new TreeSet<>();

      suites = new ArrayList<>();

      for (final BwCalSuite suite: svci.getCalSuitesHandler().getAll()) {
        final BwCalSuite cs = (BwCalSuite)suite.clone();

        // For the moment we skip suites if the group description starts with "INACTIVE"
        final String desc = cs.getGroup().getDescription();

        if ((desc != null) && desc.startsWith("INACTIVE")) {
          continue;
        }

        groupHrefs.add(cs.getGroup().getPrincipalRef());

        cs.setContext(null);
        cs.setDefaultContext(false);

        suites.add(cs);
      }

      adminGroupsInfo = new ArrayList<>();
      calsuiteAdminGroupsInfo = new ArrayList<>();
      
      final Map> cloned = new HashMap<>();

      final var ags =
              svci.getAdminDirectories().getAll(true);

      for (final var g: ags) {
        final var cg = cloneGroup(g, cloned);

        if (groupHrefs.contains(cg.getPrincipalRef())) {
          calsuiteAdminGroupsInfo.add(cg);
        }

        // Set the memberships for this group.
        final var mgs = getAllAdminGroups(g);

        for (final var mg: mgs) {
          final var cmg = cloneGroup(mg, cloned);

          cg.addGroup(cmg);
        }

        adminGroupsInfo.add(cg);
      }

      lastAdminGroupsInfoRefresh = System.currentTimeMillis();

      return adminGroupsInfo;
    }
  }

  /** Return all groups of which the given principal is a member. Never returns null.
   *
   * 

This does check the groups for membership of other groups so the * returned collection gives the groups of which the principal is * directly or indirectly a member. * * @param val a principal * @return Collection of BwGroup * @throws CalFacadeException on fatal error */ private Collection> getAllAdminGroups(final BwPrincipal val) throws CalFacadeException { return svci.getAdminDirectories().getAllGroups(val); } private BwGroup cloneGroup(final BwGroup g, final Map> cloned) { var cg = (BwGroup)cloned.get(g.getPrincipalRef()); if (cg != null) { return cg; } cg = g.shallowClone(); cloned.put(g.getPrincipalRef(), cg); final var ms = g.getGroupMembers(); if (ms == null) { return cg; } for (final var mbr: ms) { BwPrincipal cmbr = cloned.get(mbr.getPrincipalRef()); if (cmbr == null) { if (mbr instanceof BwGroup) { cmbr = cloneGroup((BwGroup)mbr, cloned); } else { cmbr = (BwPrincipal)mbr.clone(); } cloned.put(mbr.getPrincipalRef(), cmbr); } cg.addGroupMember(cmbr); } return cg; } protected String getCSResourcesPath(final BwCalSuite suite, final String rc) throws CalFacadeException { final ResourceClass csRc = ResourceClass.valueOf(rc); return svci.getCalSuitesHandler().getResourcesPath(suite, csRc); /* if (rc.equals(CalSuiteResource.resourceClassGlobal)) { return Util.buildPath(false, getBasicSyspars().getGlobalResourcesPath()); } final BwPrincipal eventsOwner = getPrincipal(suite.getGroup().getOwnerHref()); final String home = svci.getPrincipalInfo().getCalendarHomePath(eventsOwner); final BwPreferences prefs; if (superUser) { prefs = getPreferences(eventsOwner.getPrincipalRef()); } else { prefs = getPreferences(); } String col = null; if (rc.equals(CalSuiteResource.resourceClassAdmin)) { col = prefs.getAdminResourcesDirectory(); if (col == null) { col = ".adminResources"; } } else if (rc.equals(CalSuiteResource.resourceClassCalSuite)) { col = prefs.getSuiteResourcesDirectory(); if (col == null) { col = ".csResources"; } } if (col != null) { return Util.buildPath(false, home, "/", col); } throw new RuntimeException("System error"); */ } /* protected BasicSystemProperties getBasicSyspars() throws CalFacadeException { return svci.getBasicSystemProperties(); } */ protected CollectionCollator getCalendarCollator() { if (calendarCollator == null) { calendarCollator = new CollectionCollator<>(); } return calendarCollator; } protected BwIndexer getIndexer(final boolean publick, final String docType) { if (publick) { BwIndexer idx = publicIndexers.get(docType); if (idx == null) { idx = svci.getIndexer(true, docType); publicIndexers.put(docType, idx); } return idx; } BwIndexer idx = userIndexers.get(docType); if (idx == null) { idx = svci.getIndexer(false, docType); userIndexers.put(docType, idx); } return idx; } private final static long indexerDelay = 1010; // Just over a sec for the indexer protected T update(final T val) { updated(); return val; } protected void updated() { lastUpdate = System.currentTimeMillis(); synchronized (adminGroupLocker) { adminGroupsInfo = null; } synchronized (primaryPublicPathLocker) { primaryPublicPath = null; } } protected void checkUpdate() { /* if (debug()) { debug("checkUpdate: \n" + " req=" + requestEnd + "\n" + "last=" + lastUpdate + "\n" + "wait=" + (indexerDelay - (System.currentTimeMillis() - lastUpdate))); }*/ if (lastUpdate == 0) { return; } if (requestEnd < lastUpdate) { // We don't wait if this conversation caused the update return; } final long toWait = indexerDelay - (System.currentTimeMillis() - lastUpdate); if (toWait > 0) { try { Thread.sleep(toWait); } catch (final InterruptedException ignored) { //throw new CalFacadeException(ie); // Assume we're shutting down. } lastUpdate = 0; // Only wait once } } protected Locale makeLocale(final String val) { return Util.makeLocale(val); } private final static FlushMap defaultFilters = new FlushMap<>(); protected FilterBase getDefaultFilterContext() throws CalFacadeException { final var pr = getCurrentPrincipal(); final String phref; if (pr == null) { phref = null; } else { phref = pr.getPrincipalRef(); } FilterBase tblVal = defaultFilters.get(phref); if (tblVal != null) { return tblVal; } final BwView preferred = getView(null); if (preferred == null) { tblVal = makeDefaultView(); } else { final String fexpr = "view=\"" + preferred.getName() + "\""; final BwFilterDef fd = new BwFilterDef(); fd.setDefinition(fexpr); parseFilter(fd); tblVal = fd.getFilters(); if (tblVal == null) { warn("Null filter for " + phref); } } synchronized (defaultFilters) { defaultFilters.put(phref, tblVal); } return tblVal; } /* ------------------------------------------------------------ * private methods * ------------------------------------------------------------ */ private FilterBase makeDefaultView() throws CalFacadeException { final Collection cols = new ArrayList<>(); findCollections(getHome(), cols); FilterBase flt = null; for (final BwCalendar col: cols) { flt = FilterBase.addOrChild(flt, new BwCollectionFilter(col.getName(), col)); } return flt; } private void findCollections(final BwCalendar root, final Collection cols) throws CalFacadeException { if (root.getCalendarCollection()) { cols.add(root); return; } for (final var ch: svci.getCalendarsHandler().getChildren(root)) { findCollections(ch, cols); } } private List formatSearchResult( final List entries) { final IcalTranslator trans = new IcalTranslator(new IcalCallbackcb(this)); for (final SearchResultEntry sre: entries) { final Object o = sre.getEntity(); if (!(o instanceof EventInfo)) { continue; } final EventFormatter ef = new EventFormatter(this, trans, (EventInfo)o); sre.setEntity(ef); } return entries; } private Collection getSupportedLocales() { if (supportedLocales != null) { return supportedLocales; } supportedLocales = new ArrayList<>(); final String ll = getSystemProperties().getLocaleList(); if (ll == null) { supportedLocales.add(BwLocale.getLocale()); return supportedLocales; } int pos = 0; while (pos < ll.length()) { final int nextPos = ll.indexOf(",", pos); if (nextPos < 0) { supportedLocales.add(makeLocale(ll.substring(pos))); break; } supportedLocales.add(makeLocale(ll.substring(pos, nextPos))); pos = nextPos + 1; } if (supportedLocales.isEmpty()) { supportedLocales.add(BwLocale.getLocale()); } return supportedLocales; } protected void checkResponse(final Response resp) { if (!resp.isError()) { return; } final var exc = resp.getException(); if (exc instanceof RuntimeException) { throw (RuntimeException)exc; } if (exc != null) { throw new RuntimeException(exc); } throw new RuntimeException(resp.toString()); } /* ==================================================================== * Logged methods * ==================================================================== */ private final BwLogger logger = new BwLogger(); @Override public BwLogger getLogger() { if ((logger.getLoggedClass() == null) && (logger.getLoggedName() == null)) { logger.setLoggedClass(getClass()); } return logger; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy