Java tutorial
/* * 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.contacts; import com.github.rutledgepaulv.qbuilders.conditions.Condition; import com.sonicle.commons.EnumUtils; import com.sonicle.commons.IdentifierUtils; import com.sonicle.commons.InternetAddressUtils; import com.sonicle.commons.LangUtils; import com.sonicle.commons.LangUtils.CollectionChangeSet; import com.sonicle.commons.PathUtils; import com.sonicle.commons.db.DbUtils; import com.sonicle.commons.time.DateTimeUtils; import com.sonicle.commons.web.json.CompositeId; import com.sonicle.dav.CardDav; import com.sonicle.dav.CardDavFactory; import com.sonicle.dav.DavSyncStatus; import com.sonicle.dav.DavUtil; import com.sonicle.dav.carddav.DavAddressbook; import com.sonicle.dav.carddav.DavAddressbookCard; import com.sonicle.dav.impl.DavException; import com.sonicle.webtop.contacts.bol.OCategory; import com.sonicle.webtop.contacts.bol.OCategoryPropSet; import com.sonicle.webtop.contacts.bol.OContact; import com.sonicle.webtop.contacts.bol.OContactAttachment; import com.sonicle.webtop.contacts.bol.OContactAttachmentData; import com.sonicle.webtop.contacts.bol.OContactPicture; import com.sonicle.webtop.contacts.bol.OContactPictureMetaOnly; import com.sonicle.webtop.contacts.bol.OContactVCard; import com.sonicle.webtop.contacts.bol.OListRecipient; import com.sonicle.webtop.contacts.bol.VContact; import com.sonicle.webtop.contacts.bol.VContactObject; import com.sonicle.webtop.contacts.bol.VContactObjectChanged; import com.sonicle.webtop.contacts.bol.VContactCompany; import com.sonicle.webtop.contacts.bol.VContactHrefSync; import com.sonicle.webtop.contacts.bol.VListRecipient; import com.sonicle.webtop.contacts.bol.VContactLookup; import com.sonicle.webtop.contacts.bol.model.MyShareRootCategory; import com.sonicle.webtop.contacts.model.ShareFolderCategory; import com.sonicle.webtop.contacts.model.ShareRootCategory; import com.sonicle.webtop.contacts.model.Contact; import com.sonicle.webtop.contacts.model.ContactPictureWithBytesOld; import com.sonicle.webtop.contacts.model.ContactsList; import com.sonicle.webtop.contacts.model.ContactsListRecipient; import com.sonicle.webtop.contacts.dal.CategoryDAO; import com.sonicle.webtop.contacts.dal.CategoryPropsDAO; import com.sonicle.webtop.contacts.dal.ContactAttachmentDAO; import com.sonicle.webtop.contacts.dal.ContactDAO; import com.sonicle.webtop.contacts.dal.ContactPictureDAO; import com.sonicle.webtop.contacts.dal.ContactVCardDAO; import com.sonicle.webtop.contacts.dal.ContactPredicateVisitor; import com.sonicle.webtop.contacts.dal.ListRecipientDAO; import com.sonicle.webtop.contacts.io.ContactInput; import com.sonicle.webtop.contacts.io.VCardInput; import com.sonicle.webtop.contacts.io.VCardOutput; import com.sonicle.webtop.contacts.io.input.ContactFileReader; import com.sonicle.webtop.contacts.model.BaseContact; import com.sonicle.webtop.contacts.model.Category; import com.sonicle.webtop.contacts.model.CategoryPropSet; import com.sonicle.webtop.contacts.model.CategoryRemoteParameters; import com.sonicle.webtop.contacts.model.ContactAttachment; import com.sonicle.webtop.contacts.model.ContactAttachmentWithBytes; import com.sonicle.webtop.contacts.model.ContactAttachmentWithStream; import com.sonicle.webtop.contacts.model.ContactObject; import com.sonicle.webtop.contacts.model.ContactObjectChanged; import com.sonicle.webtop.contacts.model.ContactObjectWithBean; import com.sonicle.webtop.contacts.model.ContactObjectWithVCard; import com.sonicle.webtop.contacts.model.ContactCompany; import com.sonicle.webtop.contacts.model.ContactLookup; import com.sonicle.webtop.contacts.model.ContactPicture; import com.sonicle.webtop.contacts.model.ContactPictureWithBytes; import com.sonicle.webtop.contacts.model.ContactQuery; import com.sonicle.webtop.contacts.model.ListContactsResult; import com.sonicle.webtop.contacts.model.Grouping; import com.sonicle.webtop.contacts.model.ShowBy; import com.sonicle.webtop.contacts.model.ContactType; import com.sonicle.webtop.core.CoreManager; import com.sonicle.webtop.core.app.RunContext; import com.sonicle.webtop.core.app.WT; import com.sonicle.webtop.core.app.provider.RecipientsProviderBase; import com.sonicle.webtop.core.bol.OShare; import com.sonicle.webtop.core.sdk.BaseManager; import com.sonicle.webtop.core.bol.Owner; import com.sonicle.webtop.core.model.Recipient; import com.sonicle.webtop.core.model.IncomingShareRoot; import com.sonicle.webtop.core.model.SharePermsFolder; import com.sonicle.webtop.core.model.SharePermsElements; import com.sonicle.webtop.core.model.SharePermsRoot; import com.sonicle.webtop.core.bol.model.Sharing; import com.sonicle.webtop.core.dal.BaseDAO; import com.sonicle.webtop.core.dal.DAOException; import com.sonicle.webtop.core.dal.DAOIntegrityViolationException; import com.sonicle.webtop.core.io.BatchBeanHandler; import com.sonicle.webtop.core.io.input.FileReaderException; import com.sonicle.webtop.core.model.BaseMasterData; import com.sonicle.webtop.core.model.MasterData; import com.sonicle.webtop.core.model.MasterDataLookup; import com.sonicle.webtop.core.model.RecipientFieldCategory; import com.sonicle.webtop.core.model.RecipientFieldType; import com.sonicle.webtop.core.sdk.AbstractMapCache; import com.sonicle.webtop.core.sdk.AbstractShareCache; import com.sonicle.webtop.core.sdk.AuthException; import com.sonicle.webtop.core.sdk.BaseReminder; import com.sonicle.webtop.core.sdk.ReminderEmail; import com.sonicle.webtop.core.sdk.ReminderInApp; 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.sdk.interfaces.IRecipientsProvidersSource; 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.NotificationHelper; import com.sonicle.webtop.core.util.VCardUtils; import eu.medsea.mimeutil.MimeType; import ezvcard.VCard; import freemarker.template.TemplateException; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.sql.BatchUpdateException; import java.sql.Connection; import java.sql.SQLException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import javax.imageio.ImageIO; import javax.mail.internet.InternetAddress; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.imgscalr.Scalr; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.LocalDate; import org.joda.time.LocalTime; import org.slf4j.Logger; /** * * @author malbinola */ public class ContactsManager extends BaseManager implements IContactsManager, IRecipientsProvidersSource { private static final Logger logger = WT.getLogger(ContactsManager.class); private static final String GROUPNAME_CATEGORY = "CATEGORY"; private final OwnerCache ownerCache = new OwnerCache(); private final ShareCache shareCache = new ShareCache(); private static final ConcurrentHashMap<String, UserProfileId> pendingRemoteCategorySyncs = new ConcurrentHashMap<>(); public ContactsManager(boolean fastInit, UserProfileId targetProfileId) { super(fastInit, targetProfileId); if (!fastInit) { shareCache.init(); } } private ContactsServiceSettings getServiceSettings() { return new ContactsServiceSettings(SERVICE_ID, getTargetProfileId().getDomainId()); } private CardDav getCardDav(String username, String password) { if (!StringUtils.isBlank(username) && !StringUtils.isBlank(username)) { return CardDavFactory.begin(username, password); } else { return CardDavFactory.begin(); } } private List<ShareRootCategory> internalListIncomingCategoryShareRoots() throws WTException { CoreManager coreMgr = WT.getCoreManager(getTargetProfileId()); List<ShareRootCategory> roots = new ArrayList(); HashSet<String> hs = new HashSet<>(); for (IncomingShareRoot share : coreMgr.listIncomingShareRoots(SERVICE_ID, GROUPNAME_CATEGORY)) { final SharePermsRoot perms = coreMgr.getShareRootPermissions(share.getShareId()); ShareRootCategory root = new ShareRootCategory(share, perms); if (hs.contains(root.getShareId())) continue; // Avoid duplicates ?????????????????????? hs.add(root.getShareId()); roots.add(root); } return roots; } @Override public List<RecipientsProviderBase> returnRecipientsProviders() { try { ArrayList<RecipientsProviderBase> providers = new ArrayList<>(); UserProfile.Data ud = WT.getUserData(getTargetProfileId()); providers.add(new RootRecipientsProvider(getTargetProfileId().toString(), ud.getDisplayName(), getTargetProfileId(), listCategoryIds())); for (ShareRootCategory root : shareCache.getShareRoots()) { final List<Integer> catIds = shareCache.getFolderIdsByShareRoot(root.getShareId()); providers.add(new RootRecipientsProvider(root.getOwnerProfileId().toString(), root.getDescription(), root.getOwnerProfileId(), catIds)); } return providers; } catch (WTException ex) { logger.error("Unable to return providers"); return null; } } private String getAnniversaryReminderDelivery(HashMap<UserProfileId, String> cache, UserProfileId pid) { if (!cache.containsKey(pid)) { ContactsUserSettings cus = new ContactsUserSettings(SERVICE_ID, pid); String value = cus.getAnniversaryReminderDelivery(); cache.put(pid, value); return value; } else { return cache.get(pid); } } private DateTime getAnniversaryReminderTime(HashMap<UserProfileId, DateTime> cache, UserProfileId pid, LocalDate date) { if (!cache.containsKey(pid)) { LocalTime time = new ContactsUserSettings(SERVICE_ID, pid).getAnniversaryReminderTime(); //TODO: valutare se uniformare i minuti a quelli consentiti (ai min 0 e 30), se errato non verr mai preso in considerazione UserProfile.Data ud = WT.getUserData(pid); DateTime value = new DateTime(ud.getTimeZone()).withDate(date).withTime(time); cache.put(pid, value); return value; } else { return cache.get(pid); } } public String buildSharingId(int categoryId) throws WTException { UserProfileId targetPid = getTargetProfileId(); // Skip rights check if running user is resource's owner UserProfileId owner = ownerCache.get(categoryId); if (owner == null) throw new WTException("owner({0}) -> null", categoryId); String rootShareId = null; if (owner.equals(targetPid)) { rootShareId = MyShareRootCategory.SHARE_ID; } else { rootShareId = shareCache.getShareRootIdByFolderId(categoryId); } if (rootShareId == null) throw new WTException("Unable to find a root share [{0}]", categoryId); return new CompositeId().setTokens(rootShareId, categoryId).toString(); } public Sharing getSharing(String shareId) throws WTException { CoreManager core = WT.getCoreManager(getTargetProfileId()); return core.getSharing(SERVICE_ID, GROUPNAME_CATEGORY, shareId); } public void updateSharing(Sharing sharing) throws WTException { CoreManager core = WT.getCoreManager(getTargetProfileId()); core.updateSharing(SERVICE_ID, GROUPNAME_CATEGORY, sharing); } public UserProfileId getCategoryOwner(int categoryId) throws WTException { return ownerCache.get(categoryId); } public String getIncomingCategoryShareRootId(int categoryId) throws WTException { return shareCache.getShareRootIdByFolderId(categoryId); } @Override public List<ShareRootCategory> listIncomingCategoryRoots() throws WTException { return shareCache.getShareRoots(); } @Override public Map<Integer, ShareFolderCategory> listIncomingCategoryFolders(String rootShareId) throws WTException { CoreManager coreMgr = WT.getCoreManager(getTargetProfileId()); LinkedHashMap<Integer, ShareFolderCategory> folders = new LinkedHashMap<>(); for (Integer folderId : shareCache.getFolderIdsByShareRoot(rootShareId)) { final String shareFolderId = shareCache.getShareFolderIdByFolderId(folderId); if (StringUtils.isBlank(shareFolderId)) continue; SharePermsFolder fperms = coreMgr.getShareFolderPermissions(shareFolderId); SharePermsElements eperms = coreMgr.getShareElementsPermissions(shareFolderId); if (folders.containsKey(folderId)) { final ShareFolderCategory shareFolder = folders.get(folderId); if (shareFolder == null) continue; shareFolder.getPerms().merge(fperms); shareFolder.getElementsPerms().merge(eperms); } else { final Category category = getCategory(folderId); if (category == null) continue; folders.put(folderId, new ShareFolderCategory(shareFolderId, fperms, eperms, category)); } } return folders; } @Override public List<Integer> listCategoryIds() throws WTException { return new ArrayList<>(listCategories().keySet()); } @Override public List<Integer> listIncomingCategoryIds() throws WTException { return shareCache.getFolderIds(); } @Override public Map<Integer, Category> listCategories() throws WTException { return listCategories(getTargetProfileId()); } private Map<Integer, Category> listCategories(UserProfileId pid) throws WTException { CategoryDAO catDao = CategoryDAO.getInstance(); LinkedHashMap<Integer, Category> items = new LinkedHashMap<>(); Connection con = null; try { con = WT.getConnection(SERVICE_ID); for (OCategory ocat : catDao.selectByProfile(con, pid.getDomainId(), pid.getUserId())) { items.put(ocat.getCategoryId(), ManagerUtils.createCategory(ocat)); } return items; } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } public List<Category> listRemoteCategoriesToBeSynchronized() throws WTException { CategoryDAO catDao = CategoryDAO.getInstance(); ArrayList<Category> items = new ArrayList<>(); Connection con = null; try { ensureSysAdmin(); con = WT.getConnection(SERVICE_ID); for (OCategory ocat : catDao.selectByProvider(con, Arrays.asList(Category.Provider.CARDDAV))) { items.add(ManagerUtils.createCategory(ocat)); } return items; } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public Map<Integer, DateTime> getCategoriesLastRevision(Collection<Integer> categoryIds) throws WTException { ContactDAO contDao = ContactDAO.getInstance(); Connection con = null; try { List<Integer> okCategoryIds = categoryIds.stream() .filter(categoryId -> quietlyCheckRightsOnCategoryFolder(categoryId, "READ")) .collect(Collectors.toList()); con = WT.getConnection(SERVICE_ID); return contDao.selectMaxRevTimestampByCategoriesType(con, okCategoryIds, false); } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public Category getCategory(int categoryId) throws WTException { CategoryDAO catDao = CategoryDAO.getInstance(); Connection con = null; try { checkRightsOnCategoryFolder(categoryId, "READ"); con = WT.getConnection(SERVICE_ID); OCategory ocat = catDao.selectById(con, categoryId); return ManagerUtils.createCategory(ocat); } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public Category getBuiltInCategory() throws WTException { CategoryDAO catdao = CategoryDAO.getInstance(); Connection con = null; try { con = WT.getConnection(SERVICE_ID); OCategory ocat = catdao.selectBuiltInByProfile(con, getTargetProfileId().getDomainId(), getTargetProfileId().getUserId()); if (ocat == null) return null; checkRightsOnCategoryFolder(ocat.getCategoryId(), "READ"); return ManagerUtils.createCategory(ocat); } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } public Map<String, String> getCategoryLinks(int categoryId) throws WTException { checkRightsOnCategoryFolder(categoryId, "READ"); UserProfile.Data ud = WT.getUserData(getTargetProfileId()); String davServerBaseUrl = WT.getDavServerBaseUrl(getTargetProfileId().getDomainId()); String categoryUid = ContactsUtils.encodeAsCategoryUid(categoryId); String addressbookUrl = MessageFormat.format(ContactsUtils.CARDDAV_ADDRESSBOOK_URL, ud.getProfileEmailAddress(), categoryUid); LinkedHashMap<String, String> links = new LinkedHashMap<>(); links.put(ContactsUtils.CATEGORY_LINK_CARDDAV, PathUtils.concatPathParts(davServerBaseUrl, addressbookUrl)); return links; } @Override public Category addCategory(Category category) throws WTException { Connection con = null; try { checkRightsOnCategoryRoot(category.getProfileId(), "MANAGE"); con = WT.getConnection(SERVICE_ID, false); category.setBuiltIn(false); category = doCategoryInsert(con, category); DbUtils.commitQuietly(con); writeLog("CATEGORY_INSERT", category.getCategoryId().toString()); return category; } catch (SQLException | DAOException | WTException ex) { DbUtils.rollbackQuietly(con); throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public Category addBuiltInCategory() throws WTException { CategoryDAO catdao = CategoryDAO.getInstance(); Connection con = null; try { checkRightsOnCategoryRoot(getTargetProfileId(), "MANAGE"); con = WT.getConnection(SERVICE_ID, false); OCategory ocat = catdao.selectBuiltInByProfile(con, getTargetProfileId().getDomainId(), getTargetProfileId().getUserId()); if (ocat != null) { logger.debug("Built-in category already present"); return null; } Category cat = new Category(); cat.setBuiltIn(true); cat.setName(WT.getPlatformName()); cat.setDescription(""); cat.setIsDefault(true); cat = doCategoryInsert(con, cat); DbUtils.commitQuietly(con); writeLog("CATEGORY_INSERT", cat.getCategoryId().toString()); return cat; } catch (SQLException | DAOException | WTException ex) { DbUtils.rollbackQuietly(con); throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public void updateCategory(Category category) throws WTException { Connection con = null; try { int categoryId = category.getCategoryId(); checkRightsOnCategoryFolder(categoryId, "UPDATE"); con = WT.getConnection(SERVICE_ID, false); boolean updated = doCategoryUpdate(con, category); if (!updated) throw new NotFoundException("Category not found [{}]", categoryId); DbUtils.commitQuietly(con); writeLog("CATEGORY_UPDATE", String.valueOf(categoryId)); } catch (SQLException | DAOException | WTException ex) { DbUtils.rollbackQuietly(con); throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public void deleteCategory(int categoryId) throws WTException { CategoryDAO catDao = CategoryDAO.getInstance(); CategoryPropsDAO psetDao = CategoryPropsDAO.getInstance(); Connection con = null; try { checkRightsOnCategoryFolder(categoryId, "DELETE"); // Retrieve sharing status (for later) String sharingId = buildSharingId(categoryId); Sharing sharing = getSharing(sharingId); con = WT.getConnection(SERVICE_ID, false); Category cat = ManagerUtils.createCategory(catDao.selectById(con, categoryId)); if (cat == null) throw new NotFoundException("Category not found [{}]", categoryId); int deleted = catDao.deleteById(con, categoryId); psetDao.deleteByCategory(con, categoryId); doContactsDeleteByCategory(con, categoryId, !cat.isProviderRemote()); // Cleanup sharing, if necessary if ((sharing != null) && !sharing.getRights().isEmpty()) { logger.debug("Removing {} active sharing [{}]", sharing.getRights().size(), sharing.getId()); sharing.getRights().clear(); updateSharing(sharing); } DbUtils.commitQuietly(con); final String ref = String.valueOf(categoryId); writeLog("CATEGORY_DELETE", ref); writeLog("CONTACT_DELETE", "*@" + ref); writeLog("CONTACTLIST_DELETE", "*@" + ref); } catch (SQLException | DAOException | WTException ex) { DbUtils.rollbackQuietly(con); throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public CategoryPropSet getCategoryCustomProps(int categoryId) throws WTException { return getCategoryCustomProps(getTargetProfileId(), categoryId); } private CategoryPropSet getCategoryCustomProps(UserProfileId profileId, int categoryId) throws WTException { CategoryPropsDAO psetDao = CategoryPropsDAO.getInstance(); Connection con = null; try { con = WT.getConnection(SERVICE_ID); OCategoryPropSet opset = psetDao.selectByProfileCategory(con, profileId.getDomainId(), profileId.getUserId(), categoryId); return (opset == null) ? new CategoryPropSet() : ManagerUtils.createCategoryPropSet(opset); } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public Map<Integer, CategoryPropSet> getCategoryCustomProps(Collection<Integer> categoryIds) throws WTException { return getCategoryCustomProps(getTargetProfileId(), categoryIds); } public Map<Integer, CategoryPropSet> getCategoryCustomProps(UserProfileId profileId, Collection<Integer> categoryIds) throws WTException { CategoryPropsDAO psetDao = CategoryPropsDAO.getInstance(); Connection con = null; try { con = WT.getConnection(SERVICE_ID); LinkedHashMap<Integer, CategoryPropSet> psets = new LinkedHashMap<>(categoryIds.size()); Map<Integer, OCategoryPropSet> map = psetDao.selectByProfileCategoryIn(con, profileId.getDomainId(), profileId.getUserId(), categoryIds); for (Integer categoryId : categoryIds) { OCategoryPropSet opset = map.get(categoryId); psets.put(categoryId, (opset == null) ? new CategoryPropSet() : ManagerUtils.createCategoryPropSet(opset)); } return psets; } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public CategoryPropSet updateCategoryCustomProps(int categoryId, CategoryPropSet propertySet) throws WTException { ensureUser(); return updateCategoryCustomProps(getTargetProfileId(), categoryId, propertySet); } private CategoryPropSet updateCategoryCustomProps(UserProfileId profileId, int categoryId, CategoryPropSet propertySet) throws WTException { CategoryPropsDAO psetDao = CategoryPropsDAO.getInstance(); Connection con = null; try { OCategoryPropSet opset = ManagerUtils.createOCategoryPropSet(propertySet); opset.setDomainId(profileId.getDomainId()); opset.setUserId(profileId.getUserId()); opset.setCategoryId(categoryId); con = WT.getConnection(SERVICE_ID); try { psetDao.insert(con, opset); } catch (DAOIntegrityViolationException ex1) { psetDao.update(con, opset); } return propertySet; } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public List<ContactObject> listContactObjects(int categoryId, ContactObjectOutputType outputType) throws WTException { ContactDAO contDao = ContactDAO.getInstance(); Connection con = null; try { con = WT.getConnection(SERVICE_ID); checkRightsOnCategoryFolder(categoryId, "READ"); ArrayList<ContactObject> items = new ArrayList<>(); Map<String, List<VContactObject>> map = contDao.viewContactObjectByCategory(con, categoryId); for (List<VContactObject> vconts : map.values()) { if (vconts.isEmpty()) continue; VContactObject vcont = vconts.get(vconts.size() - 1); if (vconts.size() > 1) { logger.trace("Many contacts ({}) found for same href [{} -> {}]", vconts.size(), vcont.getHref(), vcont.getContactId()); } items.add(doContactObjectPrepare(con, vcont, outputType)); } return items; } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public CollectionChangeSet<ContactObjectChanged> listContactObjectsChanges(int categoryId, DateTime since, Integer limit) throws WTException { ContactDAO contDao = ContactDAO.getInstance(); Connection con = null; try { con = WT.getConnection(SERVICE_ID); checkRightsOnCategoryFolder(categoryId, "READ"); ArrayList<ContactObjectChanged> inserted = new ArrayList<>(); ArrayList<ContactObjectChanged> updated = new ArrayList<>(); ArrayList<ContactObjectChanged> deleted = new ArrayList<>(); if (limit == null) limit = Integer.MAX_VALUE; if (since == null) { List<VContactObjectChanged> vconts = contDao.viewLiveContactObjectsChangedByCategory(con, categoryId, limit); for (VContactObjectChanged vcont : vconts) { inserted.add(new ContactObjectChanged(vcont.getContactId(), vcont.getRevisionTimestamp(), vcont.getHref())); } } else { List<VContactObjectChanged> vconts = contDao.viewChangedByCategorySince(con, categoryId, since, limit); for (VContactObjectChanged vcont : vconts) { Contact.RevisionStatus revStatus = EnumUtils.forSerializedName(vcont.getRevisionStatus(), Contact.RevisionStatus.class); if (Contact.RevisionStatus.DELETED.equals(revStatus)) { deleted.add(new ContactObjectChanged(vcont.getContactId(), vcont.getRevisionTimestamp(), vcont.getHref())); } else { if (Contact.RevisionStatus.NEW.equals(revStatus) || (vcont.getCreationTimestamp().compareTo(since) >= 0)) { inserted.add(new ContactObjectChanged(vcont.getContactId(), vcont.getRevisionTimestamp(), vcont.getHref())); } else { updated.add(new ContactObjectChanged(vcont.getContactId(), vcont.getRevisionTimestamp(), vcont.getHref())); } } } } return new CollectionChangeSet<>(inserted, updated, deleted); } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public ContactObjectWithVCard getContactObjectWithVCard(int categoryId, String href) throws WTException { List<ContactObjectWithVCard> ccs = getContactObjectsWithVCard(categoryId, Arrays.asList(href)); return ccs.isEmpty() ? null : ccs.get(0); } @Override public List<ContactObjectWithVCard> getContactObjectsWithVCard(int categoryId, Collection<String> hrefs) throws WTException { ContactDAO contDao = ContactDAO.getInstance(); Connection con = null; try { con = WT.getConnection(SERVICE_ID); checkRightsOnCategoryFolder(categoryId, "READ"); ArrayList<ContactObjectWithVCard> items = new ArrayList<>(); Map<String, List<VContactObject>> map = contDao.viewContactObjectByCategoryHrefs(con, categoryId, hrefs); for (String href : hrefs) { List<VContactObject> vconts = map.get(href); if (vconts == null) continue; if (vconts.isEmpty()) continue; VContactObject vcont = vconts.get(vconts.size() - 1); if (vconts.size() > 1) { logger.trace("Many contacts ({}) found for same href [{} -> {}]", vconts.size(), vcont.getHref(), vcont.getContactId()); } items.add( (ContactObjectWithVCard) doContactObjectPrepare(con, vcont, ContactObjectOutputType.VCARD)); } return items; } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public ContactObject getContactObject(int contactId, ContactObjectOutputType outputType) throws WTException { ContactDAO contDao = ContactDAO.getInstance(); Connection con = null; try { con = WT.getConnection(SERVICE_ID); VContactObject cobj = contDao.viewContactObjectById(con, contactId); if (cobj != null) { checkRightsOnCategoryFolder(cobj.getCategoryId(), "READ"); return doContactObjectPrepare(con, cobj, outputType); } else { return null; } } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public void addContactObject(int categoryId, String href, VCard vCard) throws WTException { VCardInput in = new VCardInput(); ContactInput ci = in.fromVCard(vCard, null); ci.contact.setCategoryId(categoryId); ci.contact.setHref(href); String rawData = null; if (vCard != null) { String prodId = VCardUtils.buildProdId(ManagerUtils.getProductName()); rawData = new VCardOutput(prodId).write(vCard); } addContact(ci.contact, rawData); } @Override public void updateContactObject(int categoryId, String href, VCard vCard) throws WTException { int contactId = getContactIdByCategoryHref(categoryId, href, true); VCardInput in = new VCardInput(); ContactInput ci = in.fromVCard(vCard, null); ci.contact.setContactId(contactId); ci.contact.setCategoryId(categoryId); // Due that in vcard we do not have separate information for company ID // and description, in order to not lose data during the update, we have // to apply some checks and then restore the original company in // particular cases: // - value as ID matches the company raw id // - value as DESCRIPTION matched the company linked description if (ci.contact.hasCompany()) { ContactCompany origCompany = getContactCompany(contactId); if (origCompany != null) { ContactCompany currCompany = ci.contact.getCompany(); if (StringUtils.equals(currCompany.getValueId(), origCompany.getCompanyId()) || StringUtils.equals(currCompany.getCompanyDescription(), origCompany.getValue())) { ci.contact.setCompany(origCompany); } } } updateContact(ci.contact, true); } @Override public void deleteContactObject(int categoryId, String href) throws WTException { int contactId = getContactIdByCategoryHref(categoryId, href, true); deleteContact(contactId); } private int getContactIdByCategoryHref(int categoryId, String href, boolean throwExIfManyMatchesFound) throws WTException { ContactDAO contDao = ContactDAO.getInstance(); Connection con = null; try { con = WT.getConnection(SERVICE_ID); List<Integer> ids = contDao.selectAliveIdsByCategoryHrefs(con, categoryId, href); if (ids.isEmpty()) throw new NotFoundException("Contact card not found [{}, {}]", categoryId, href); if (throwExIfManyMatchesFound && (ids.size() > 1)) throw new WTException("Many matches for href [{}]", href); return ids.get(ids.size() - 1); } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public boolean existContact(Collection<Integer> categoryIds, Condition<ContactQuery> conditionPredicate) throws WTException { ContactDAO contDao = ContactDAO.getInstance(); Connection con = null; try { List<Integer> okCategoryIds = categoryIds.stream() .filter(categoryId -> quietlyCheckRightsOnCategoryFolder(categoryId, "READ")) .collect(Collectors.toList()); org.jooq.Condition condition = BaseDAO.createCondition(conditionPredicate, new ContactPredicateVisitor(true)); con = WT.getConnection(SERVICE_ID); return contDao.existByCategoryTypeCondition(con, okCategoryIds, ContactType.CONTACT, condition); } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } /** * @deprecated */ @Deprecated @Override public ListContactsResult listContacts(Collection<Integer> categoryIds, boolean listOnly, Grouping groupBy, ShowBy showBy, String pattern, int page, int limit, boolean returnFullCount) throws WTException { ContactType type = listOnly ? ContactType.LIST : ContactType.ANY; return listContacts(categoryIds, type, groupBy, showBy, ContactQuery.toCondition(pattern), page, limit, returnFullCount); } @Override public ListContactsResult listContacts(Collection<Integer> categoryIds, ContactType type, Grouping groupBy, ShowBy showBy, String pattern) throws WTException { return listContacts(categoryIds, type, groupBy, showBy, ContactQuery.toCondition(pattern), 1, Integer.MAX_VALUE, false); } @Override public ListContactsResult listContacts(Collection<Integer> categoryIds, ContactType type, Grouping groupBy, ShowBy showBy, Condition<ContactQuery> conditionPredicate) throws WTException { return listContacts(categoryIds, type, groupBy, showBy, conditionPredicate, 1, Integer.MAX_VALUE, false); } @Override public ListContactsResult listContacts(Collection<Integer> categoryIds, ContactType type, Grouping groupBy, ShowBy showBy, Condition<ContactQuery> conditionPredicate, int page, int limit, boolean returnFullCount) throws WTException { ContactDAO contDao = ContactDAO.getInstance(); Connection con = null; try { List<Integer> okCategoryIds = categoryIds.stream() .filter(categoryId -> quietlyCheckRightsOnCategoryFolder(categoryId, "READ")) .collect(Collectors.toList()); org.jooq.Condition condition = BaseDAO.createCondition(conditionPredicate, new ContactPredicateVisitor(true)); int offset = ManagerUtils.toOffset(page, limit); Collection<ContactDAO.OrderField> orderFields = toContactDAOOrderFields(groupBy, showBy); con = WT.getConnection(SERVICE_ID); Integer fullCount = null; if (returnFullCount) fullCount = contDao.countByCategoryTypeCondition(con, okCategoryIds, type, condition); ArrayList<ContactLookup> items = new ArrayList<>(); for (VContactLookup vcont : contDao.viewByCategoryTypeCondition(con, orderFields, okCategoryIds, type, condition, limit, offset)) { items.add(ManagerUtils.fillContactLookup(new ContactLookup(), vcont)); } return new ListContactsResult(items, fullCount); } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } private Collection<ContactDAO.OrderField> toContactDAOOrderFields(Grouping groupBy, ShowBy showBy) { ArrayList<ContactDAO.OrderField> fields = new ArrayList<>(3); if (ShowBy.FIRST_LAST.equals(showBy)) { fields.add(ContactDAO.OrderField.FIRSTNAME); fields.add(ContactDAO.OrderField.LASTNAME); } else if (ShowBy.LAST_FIRST.equals(showBy)) { fields.add(ContactDAO.OrderField.LASTNAME); fields.add(ContactDAO.OrderField.FIRSTNAME); } else { fields.add(ContactDAO.OrderField.DISPLAYNAME); } if (Grouping.COMPANY.equals(groupBy)) { fields.add(0, ContactDAO.OrderField.COMPANY); } else { fields.add(ContactDAO.OrderField.COMPANY); } return fields; } @Override public Contact getContact(int contactId) throws WTException { return getContact(contactId, true, true); } public Contact getContact(int contactId, boolean processPicture, boolean processAttachments) throws WTException { Connection con = null; try { con = WT.getConnection(SERVICE_ID); Contact cont = doContactGet(con, contactId, processPicture, processAttachments); if (cont == null) return null; checkRightsOnCategoryFolder(cont.getCategoryId(), "READ"); return cont; } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public ContactAttachmentWithBytes getContactAttachment(int contactId, String attachmentId) throws WTException { ContactDAO contDao = ContactDAO.getInstance(); ContactAttachmentDAO attDao = ContactAttachmentDAO.getInstance(); Connection con = null; try { con = WT.getConnection(SERVICE_ID); Integer catId = contDao.selectCategoryId(con, contactId); if (catId == null) return null; checkRightsOnCategoryFolder(catId, "READ"); OContactAttachment oatt = attDao.selectByIdContact(con, attachmentId, contactId); if (oatt == null) return null; OContactAttachmentData oattData = attDao.selectBytes(con, attachmentId); return ManagerUtils.fillContactAttachment(new ContactAttachmentWithBytes(oattData.getBytes()), oatt); } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public ContactCompany getContactCompany(int contactId) throws WTException { ContactDAO contDao = ContactDAO.getInstance(); Connection con = null; try { con = WT.getConnection(SERVICE_ID); VContactCompany vcc = contDao.viewContactCompanyByContact(con, contactId); if (vcc == null) return null; checkRightsOnCategoryFolder(vcc.getCategoryId(), "READ"); return ManagerUtils.createContactCompany(vcc); } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public Contact addContact(Contact contact) throws WTException { return addContact(contact, null); } @Override public Contact addContact(Contact contact, String vCardRawData) throws WTException { CoreManager coreMgr = WT.getCoreManager(getTargetProfileId()); CategoryDAO catDao = CategoryDAO.getInstance(); Connection con = null; try { checkRightsOnCategoryElements(contact.getCategoryId(), "CREATE"); con = WT.getConnection(SERVICE_ID, false); String provider = catDao.selectProviderById(con, contact.getCategoryId()); if (Category.isProviderRemote(provider)) throw new WTException("Category is remote and therefore read-only [{}]", contact.getCategoryId()); ContactInsertResult result = doContactInsert(coreMgr, con, false, contact, vCardRawData, true, true); DbUtils.commitQuietly(con); writeLog("CONTACT_INSERT", String.valueOf(result.ocontact.getContactId())); Contact newContact = ManagerUtils.createContact(result.ocontact); newContact.setPicture(ManagerUtils.createContactPicture(result.opicture)); newContact.setAttachments(ManagerUtils.createContactAttachmentList(result.oattachments)); return newContact; } catch (SQLException | DAOException | IOException | WTException ex) { DbUtils.rollbackQuietly(con); throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public void updateContact(Contact contact) throws WTException { updateContact(contact, false); } @Override public void updateContact(Contact contact, boolean processPicture) throws WTException { updateContact(contact, processPicture, true); } public void updateContact(Contact contact, boolean processPicture, boolean processAttachments) throws WTException { CoreManager coreMgr = WT.getCoreManager(getTargetProfileId()); CategoryDAO catDao = CategoryDAO.getInstance(); Connection con = null; try { checkRightsOnCategoryElements(contact.getCategoryId(), "UPDATE"); con = WT.getConnection(SERVICE_ID, false); String provider = catDao.selectProviderById(con, contact.getCategoryId()); if (Category.isProviderRemote(provider)) throw new WTException("Category is remote and therefore read-only [{}]", contact.getCategoryId()); boolean updated = doContactUpdate(coreMgr, con, false, contact, processPicture, processAttachments); if (!updated) throw new WTException("Contact not updated [{}]", contact.getContactId()); DbUtils.commitQuietly(con); writeLog("CONTACT_UPDATE", String.valueOf(contact.getContactId())); } catch (SQLException | DAOException | IOException | WTException ex) { DbUtils.rollbackQuietly(con); throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public ContactPictureWithBytes getContactPicture(int contactId) throws WTException { ContactDAO contDao = ContactDAO.getInstance(); ContactPictureDAO cpicDao = ContactPictureDAO.getInstance(); Connection con = null; try { con = WT.getConnection(SERVICE_ID); Integer catId = contDao.selectCategoryId(con, contactId); if (catId == null) return null; checkRightsOnCategoryFolder(catId, "READ"); OContactPicture opic = cpicDao.select(con, contactId); if (opic == null) return null; return ManagerUtils.fillContactPicture(new ContactPictureWithBytes(opic.getBytes()), opic); } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public void updateContactPicture(int contactId, ContactPictureWithBytesOld picture) throws WTException { ContactDAO contDao = ContactDAO.getInstance(); Connection con = null; try { con = WT.getConnection(SERVICE_ID, false); if (picture == null) throw new WTException("Picture is null"); OContact ocont = contDao.selectById(con, contactId); if (ocont == null || ocont.getIsList()) throw new WTException("Unable to get contact [{0}]", contactId); checkRightsOnCategoryElements(ocont.getCategoryId(), "UPDATE"); contDao.updateRevision(con, contactId, BaseDAO.createRevisionTimestamp()); doContactPictureUpdate(con, contactId, picture); DbUtils.commitQuietly(con); writeLog("CONTACT_UPDATE", String.valueOf(contactId)); } catch (SQLException | DAOException | WTException ex) { DbUtils.rollbackQuietly(con); throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public void deleteContact(int contactId) throws WTException { ContactDAO contDao = ContactDAO.getInstance(); Connection con = null; try { con = WT.getConnection(SERVICE_ID, false); OContact cont = contDao.selectById(con, contactId); //TODO: Speed-up below query... if (cont == null || cont.getIsList()) throw new WTException("Contact not found [{0}]", contactId); checkRightsOnCategoryElements(cont.getCategoryId(), "DELETE"); doContactDelete(con, contactId, true); DbUtils.commitQuietly(con); writeLog("CONTACT_DELETE", String.valueOf(contactId)); } catch (SQLException | DAOException | WTException ex) { DbUtils.rollbackQuietly(con); throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public void deleteContact(Collection<Integer> contactIds) throws WTException { ContactDAO contDao = ContactDAO.getInstance(); Connection con = null; try { con = WT.getConnection(SERVICE_ID, false); for (Integer contactId : contactIds) { if (contactId == null) continue; OContact ocont = contDao.selectById(con, contactId); if (ocont == null || ocont.getIsList()) throw new WTException("Unable to get contact [{0}]", contactId); checkRightsOnCategoryElements(ocont.getCategoryId(), "DELETE"); doContactDelete(con, contactId, true); } DbUtils.commitQuietly(con); writeLog("CONTACT_DELETE", "*"); } catch (SQLException | DAOException | WTException ex) { DbUtils.rollbackQuietly(con); throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public void moveContacts(boolean copy, Collection<Integer> contactIds, int targetCategoryId) throws WTException { CoreManager coreMgr = WT.getCoreManager(getTargetProfileId()); ContactDAO contDao = ContactDAO.getInstance(); Connection con = null; try { con = WT.getConnection(SERVICE_ID, false); for (Integer contactId : contactIds) { OContact ocont = contDao.selectById(con, contactId); if (ocont == null || ocont.getIsList()) throw new WTException("Unable to get contact [{0}]", contactId); checkRightsOnCategoryFolder(ocont.getCategoryId(), "READ"); if (copy || (targetCategoryId != ocont.getCategoryId())) { checkRightsOnCategoryElements(targetCategoryId, "CREATE"); if (!copy) checkRightsOnCategoryElements(ocont.getCategoryId(), "DELETE"); Contact contact = ManagerUtils.fillContact(new Contact(), ocont); doContactMove(coreMgr, con, copy, contact, targetCategoryId); writeLog("CONTACT_UPDATE", String.valueOf(contact.getContactId())); } } DbUtils.commitQuietly(con); } catch (SQLException | DAOException | IOException | WTException ex) { DbUtils.rollbackQuietly(con); throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public ContactsList getContactsList(int contactId) throws WTException { ContactDAO contDao = ContactDAO.getInstance(); ListRecipientDAO lrecDao = ListRecipientDAO.getInstance(); Connection con = null; try { con = WT.getConnection(SERVICE_ID); OContact ocont = contDao.selectById(con, contactId); if ((ocont == null) || !ocont.getIsList()) return null; checkRightsOnCategoryFolder(ocont.getCategoryId(), "READ"); List<VListRecipient> vlrecs = lrecDao.viewByContact(con, contactId); return ManagerUtils.createContactsList(ocont, vlrecs); } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public void addContactsList(ContactsList list) throws WTException { CoreManager coreMgr = WT.getCoreManager(getTargetProfileId()); CategoryDAO catDao = CategoryDAO.getInstance(); Connection con = null; try { checkRightsOnCategoryElements(list.getCategoryId(), "CREATE"); con = WT.getConnection(SERVICE_ID, false); Category category = ManagerUtils.createCategory(catDao.selectById(con, list.getCategoryId())); if (category == null) throw new WTException("Unable to get category [{}]", list.getCategoryId()); if (category.isProviderRemote()) throw new WTException("Category is read only"); OContact result = doContactsListInsert(coreMgr, con, list); DbUtils.commitQuietly(con); writeLog("CONTACTLIST_INSERT", String.valueOf(result.getContactId())); } catch (SQLException | DAOException | IOException | WTException ex) { DbUtils.rollbackQuietly(con); throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public void addToContactsList(int contactsListId, ContactsList list) throws WTException { ContactDAO condao = ContactDAO.getInstance(); Connection con = null; try { con = WT.getConnection(SERVICE_ID, false); OContact cont = condao.selectById(con, contactsListId); if (cont == null) throw new NotFoundException("Contact list not found [{}]", contactsListId); if (!cont.getIsList()) throw new WTException("Not a contacts list"); checkRightsOnCategoryElements(cont.getCategoryId(), "UPDATE"); boolean updated = doContactsListAddTo(con, list); if (!updated) throw new WTException("Contacts list cannot be updated [{}]", list.getContactId()); DbUtils.commitQuietly(con); writeLog("CONTACTLIST_ADDTO", String.valueOf(contactsListId)); } catch (SQLException | DAOException | WTException ex) { DbUtils.rollbackQuietly(con); throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public void updateContactsList(ContactsList list) throws WTException { CoreManager coreMgr = WT.getCoreManager(getTargetProfileId()); Connection con = null; try { checkRightsOnCategoryElements(list.getCategoryId(), "UPDATE"); // Rights check! con = WT.getConnection(SERVICE_ID, false); boolean updated = doContactsListUpdate(coreMgr, con, list); if (!updated) throw new WTException("Contacts list not found [{}]", list.getContactId()); DbUtils.commitQuietly(con); writeLog("CONTACTLIST_UPDATE", String.valueOf(list.getContactId())); } catch (SQLException | DAOException | IOException | WTException ex) { DbUtils.rollbackQuietly(con); throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public void deleteContactsList(int contactsListId) throws WTException { ContactDAO condao = ContactDAO.getInstance(); Connection con = null; try { con = WT.getConnection(SERVICE_ID, false); OContact cont = condao.selectById(con, contactsListId); if (cont == null) throw new NotFoundException("Contact list not found [{}]", contactsListId); if (!cont.getIsList()) throw new WTException("Not a contacts list"); checkRightsOnCategoryElements(cont.getCategoryId(), "DELETE"); int deleted = doContactDelete(con, contactsListId, true); DbUtils.commitQuietly(con); writeLog("CONTACTLIST_DELETE", String.valueOf(contactsListId)); } catch (SQLException | DAOException | WTException ex) { DbUtils.rollbackQuietly(con); throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public void deleteContactsList(Collection<Integer> contactsListIds) throws WTException { ContactDAO condao = ContactDAO.getInstance(); Connection con = null; try { con = WT.getConnection(SERVICE_ID, false); for (Integer contactsListId : contactsListIds) { OContact cont = condao.selectById(con, contactsListId); if (cont == null) throw new NotFoundException("Contact list not found [{}]", contactsListId); if (!cont.getIsList()) throw new WTException("Not a contacts list"); checkRightsOnCategoryElements(cont.getCategoryId(), "DELETE"); doContactDelete(con, contactsListId, true); } DbUtils.commitQuietly(con); writeLog("CONTACTLIST_DELETE", "*"); } catch (SQLException | DAOException | WTException ex) { DbUtils.rollbackQuietly(con); throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } @Override public void moveContactsList(boolean copy, Collection<Integer> contactIds, int targetCategoryId) throws WTException { CoreManager coreMgr = WT.getCoreManager(getTargetProfileId()); ContactDAO contDao = ContactDAO.getInstance(); ListRecipientDAO lrecDao = ListRecipientDAO.getInstance(); Connection con = null; try { con = WT.getConnection(SERVICE_ID, false); for (Integer contactsListId : contactIds) { OContact ocont = contDao.selectById(con, contactsListId); if (ocont == null) throw new NotFoundException("Contact list not found [{}]", contactsListId); if (!ocont.getIsList()) throw new WTException("Not a contacts list"); checkRightsOnCategoryFolder(ocont.getCategoryId(), "READ"); if (copy || (targetCategoryId != ocont.getCategoryId())) { checkRightsOnCategoryElements(targetCategoryId, "CREATE"); if (!copy) checkRightsOnCategoryElements(ocont.getCategoryId(), "DELETE"); List<VListRecipient> recipients = lrecDao.viewByContact(con, contactsListId); ContactsList clist = ManagerUtils.createContactsList(ocont, recipients); doMoveContactsList(coreMgr, con, copy, clist, targetCategoryId); writeLog("CONTACTLIST_UPDATE", String.valueOf(contactsListId)); } } DbUtils.commitQuietly(con); } catch (SQLException | DAOException | IOException | WTException ex) { DbUtils.rollbackQuietly(con); throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } public void eraseData(boolean deep) throws WTException { CategoryDAO catDao = CategoryDAO.getInstance(); CategoryPropsDAO psetDao = CategoryPropsDAO.getInstance(); ContactDAO contDao = ContactDAO.getInstance(); //ContactPictureDAO cpicDao = ContactPictureDAO.getInstance(); //ContactVCardDAO vcaDao = ContactVCardDAO.getInstance(); //ListRecipientDAO lrecDao = ListRecipientDAO.getInstance(); Connection con = null; //TODO: controllo permessi try { con = WT.getConnection(SERVICE_ID, false); UserProfileId pid = getTargetProfileId(); // Erase contact and all related tables if (deep) { for (OCategory ocat : catDao.selectByProfile(con, pid.getDomainId(), pid.getUserId())) { //cpicDao.deleteByCategory(con, ocat.getCategoryId()); //vcaDao.deleteByCategory(con, ocat.getCategoryId()); //lrecDao.deleteByCategory(con, ocat.getCategoryId()); contDao.deleteByCategory(con, ocat.getCategoryId()); } } else { DateTime revTs = BaseDAO.createRevisionTimestamp(); for (OCategory ocat : catDao.selectByProfile(con, pid.getDomainId(), pid.getUserId())) { contDao.logicDeleteByCategory(con, ocat.getCategoryId(), revTs); } } // Erase categories psetDao.deleteByProfile(con, pid.getDomainId(), pid.getUserId()); catDao.deleteByProfile(con, pid.getDomainId(), pid.getUserId()); DbUtils.commitQuietly(con); } catch (SQLException | DAOException ex) { DbUtils.rollbackQuietly(con); throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } public LogEntries importContacts(int categoryId, ContactFileReader rea, File file, String mode) throws WTException { LogEntries log = new LogEntries(); Connection con = null; checkRightsOnCategoryElements(categoryId, "CREATE"); if (mode.equals("copy")) checkRightsOnCategoryElements(categoryId, "DELETE"); log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "Started at {0}", new DateTime())); try { con = WT.getConnection(SERVICE_ID, false); if (mode.equals("copy")) { log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "Cleaning contacts...")); int del = doContactsDeleteByCategory(con, categoryId, true); log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "{0} contact/s deleted!", del)); } ContactBatchImportBeanHandler handler = new ContactBatchImportBeanHandler(log, con, categoryId); try { rea.readContacts(file, handler); handler.flush(); Throwable lex = handler.getLastException(); if (lex != null) { if (lex.getCause() instanceof BatchUpdateException) { SQLException sex = ((BatchUpdateException) lex.getCause()).getNextException(); if (sex != null) { logger.error("DB error", lex); throw sex; } } throw new ImportException( MessageFormat.format("Unexpected error. Reason: {0}", lex.getMessage()), lex); } } catch (IOException | FileReaderException ex1) { throw new ImportException( MessageFormat.format("Problems while opening source file. Reason: {0}", ex1.getMessage()), ex1); } DbUtils.commitQuietly(con); log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "{0} contact/s read!", handler.handledCount)); log.addMaster( new MessageLogEntry(LogEntry.Level.INFO, "{0} contact/s imported!", handler.insertedCount)); } catch (ImportException ex) { DbUtils.rollbackQuietly(con); logger.error("Import error", ex.getCause()); log.addMaster(new MessageLogEntry(LogEntry.Level.WARN, "Problems encountered. No changes have been applied!")); log.addMaster(new MessageLogEntry(LogEntry.Level.ERROR, ex.getMessage())); } catch (SQLException | DAOException ex) { DbUtils.rollbackQuietly(con); logger.error("DB error", ex); log.addMaster(new MessageLogEntry(LogEntry.Level.WARN, "Problems encountered. No changes have been applied!")); log.addMaster( new MessageLogEntry(LogEntry.Level.ERROR, "Unexpected DB error. Reason: {0}", ex.getMessage())); } finally { DbUtils.closeQuietly(con); } log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "Ended at {0}", new DateTime())); return log; } public List<BaseReminder> getRemindersToBeNotified(DateTime now) { ArrayList<BaseReminder> alerts = new ArrayList<>(); HashMap<UserProfileId, Boolean> okCache = new HashMap<>(); HashMap<UserProfileId, DateTime> dateTimeCache = new HashMap<>(); HashMap<UserProfileId, String> deliveryCache = new HashMap<>(); ContactDAO cdao = ContactDAO.getInstance(); Connection con = null; // Valid reminder times (see getAnniversaryReminderTime in options) // are only at 0 and 30 min of each hour. So skip unuseful runs... if ((now.getMinuteOfHour() == 0) || (now.getMinuteOfHour() == 30)) { try { con = WT.getConnection(SERVICE_ID); LocalDate date = now.toLocalDate(); List<VContact> bdays = cdao.viewOnBirthdayByDate(con, date); for (VContact cont : bdays) { boolean ok = false; if (!okCache.containsKey(cont.getCategoryProfileId())) { ok = getAnniversaryReminderTime(dateTimeCache, cont.getCategoryProfileId(), date) .withZone(DateTimeZone.UTC).equals(now); okCache.put(cont.getCategoryProfileId(), ok); } if (ok) { DateTime dateTime = getAnniversaryReminderTime(dateTimeCache, cont.getCategoryProfileId(), date); String delivery = getAnniversaryReminderDelivery(deliveryCache, cont.getCategoryProfileId()); UserProfile.Data ud = WT.getUserData(cont.getCategoryProfileId()); if (delivery.equals(ContactsSettings.ANNIVERSARY_REMINDER_DELIVERY_EMAIL)) { alerts.add(createAnniversaryEmailReminder(ud.getLocale(), ud.getEmail(), true, cont, dateTime)); } else if (delivery.equals(ContactsSettings.ANNIVERSARY_REMINDER_DELIVERY_APP)) { alerts.add(createAnniversaryInAppReminder(ud.getLocale(), true, cont, dateTime)); } } } List<VContact> anns = cdao.viewOnAnniversaryByDate(con, date); for (VContact cont : anns) { boolean ok = false; if (!okCache.containsKey(cont.getCategoryProfileId())) { ok = getAnniversaryReminderTime(dateTimeCache, cont.getCategoryProfileId(), date) .withZone(DateTimeZone.UTC).equals(now); okCache.put(cont.getCategoryProfileId(), ok); } if (ok) { DateTime dateTime = getAnniversaryReminderTime(dateTimeCache, cont.getCategoryProfileId(), date); String delivery = getAnniversaryReminderDelivery(deliveryCache, cont.getCategoryProfileId()); UserProfile.Data ud = WT.getUserData(cont.getCategoryProfileId()); if (delivery.equals(ContactsSettings.ANNIVERSARY_REMINDER_DELIVERY_EMAIL)) { alerts.add(createAnniversaryEmailReminder(ud.getLocale(), ud.getEmail(), false, cont, dateTime)); } else if (delivery.equals(ContactsSettings.ANNIVERSARY_REMINDER_DELIVERY_APP)) { alerts.add(createAnniversaryInAppReminder(ud.getLocale(), false, cont, dateTime)); } } } } catch (Exception ex) { logger.error("Error collecting reminder alerts", ex); } finally { DbUtils.closeQuietly(con); } } return alerts; } public ProbeCategoryRemoteUrlResult probeCategoryRemoteUrl(Category.Provider provider, URI url, String username, String password) throws WTException { if (!Category.Provider.CARDDAV.equals(provider)) { throw new WTException("Provider is not valid or is not remote [{0}]", EnumUtils.toSerializedName(provider)); } if (Category.Provider.CARDDAV.equals(provider)) { CardDav dav = getCardDav(username, password); try { DavAddressbook dbook = dav.getAddressbook(url.toString()); return (dbook != null) ? new ProbeCategoryRemoteUrlResult(dbook.getDisplayName()) : null; } catch (DavException ex) { logger.error("DAV error", ex); return null; } } else { throw new WTException("Unsupported provider"); } } public void syncRemoteCategory(int categoryId, boolean full) throws WTException { CoreManager coreMgr = WT.getCoreManager(getTargetProfileId()); CategoryDAO catDao = CategoryDAO.getInstance(); final String PENDING_KEY = String.valueOf(categoryId); final VCardInput icalInput = new VCardInput(); Connection con = null; if (pendingRemoteCategorySyncs.putIfAbsent(PENDING_KEY, RunContext.getRunProfileId()) != null) { throw new ConcurrentSyncException("Sync activity is already running [{}, {}]", categoryId, RunContext.getRunProfileId()); } try { //checkRightsOnCategoryFolder(categoryId, "READ"); con = WT.getConnection(SERVICE_ID, false); Category cat = ManagerUtils.createCategory(catDao.selectById(con, categoryId)); if (cat == null) throw new WTException("Category not found [{}]", categoryId); if (!Category.Provider.CARDDAV.equals(cat.getProvider())) { throw new WTException("Specified category is not remote (CardDAV) [{}]", categoryId); } // Force a full update if last-sync date is null if (cat.getRemoteSyncTimestamp() == null) full = true; if (Category.Provider.CARDDAV.equals(cat.getProvider())) { CategoryRemoteParameters params = LangUtils.deserialize(cat.getParameters(), CategoryRemoteParameters.class); if (params == null) throw new WTException("Unable to deserialize remote parameters"); if (params.url == null) throw new WTException("Remote URL is undefined"); CardDav dav = getCardDav(params.username, params.password); try { DavAddressbook dbook = dav.getAddressbookSyncToken(params.url.toString()); if (dbook == null) throw new WTException("DAV addressbook not found"); final boolean syncIsSupported = !StringUtils.isBlank(dbook.getSyncToken()); final DateTime newLastSync = DateTimeUtils.now(); if (!full && (syncIsSupported && !StringUtils.isBlank(cat.getRemoteSyncTag()))) { // Partial update using SYNC mode String newSyncToken = dbook.getSyncToken(); logger.debug("Querying CardDAV endpoint for changes [{}, {}]", params.url.toString(), cat.getRemoteSyncTag()); List<DavSyncStatus> changes = dav.getAddressbookChanges(params.url.toString(), cat.getRemoteSyncTag()); logger.debug("Returned {} items", changes.size()); try { if (!changes.isEmpty()) { ContactDAO contDao = ContactDAO.getInstance(); Map<String, List<Integer>> contactIdsByHref = contDao.selectHrefsByByCategory(con, categoryId); // Process changes... logger.debug("Processing changes..."); HashSet<String> hrefs = new HashSet<>(); for (DavSyncStatus change : changes) { String href = FilenameUtils.getName(change.getPath()); //String href = change.getPath(); if (DavUtil.HTTP_SC_TEXT_OK.equals(change.getResponseStatus())) { hrefs.add(href); } else { // Card deleted List<Integer> contactIds = contactIdsByHref.get(href); Integer contactId = (contactIds != null) ? contactIds.get(contactIds.size() - 1) : null; if (contactId == null) { logger.warn("Deletion not possible. Card path not found [{}]", PathUtils .concatPaths(dbook.getPath(), FilenameUtils.getName(href))); continue; } doContactDelete(con, contactId, false); } } // Retrieves events list from DAV endpoint (using multiget) logger.debug("Retrieving inserted/updated cards [{}]", hrefs.size()); Collection<String> paths = hrefs.stream().map( href -> PathUtils.concatPaths(dbook.getPath(), FilenameUtils.getName(href))) .collect(Collectors.toList()); List<DavAddressbookCard> dcards = dav.listAddressbookCards(params.url.toString(), paths); //List<DavAddressbookCard> dcards = dav.listAddressbookCards(params.url.toString(), hrefs); // Inserts/Updates data... logger.debug("Inserting/Updating cards..."); for (DavAddressbookCard dcard : dcards) { String href = FilenameUtils.getName(dcard.getPath()); //String href = dcard.getPath(); if (logger.isTraceEnabled()) logger.trace("{}", VCardUtils.print(dcard.getCard())); List<Integer> contactIds = contactIdsByHref.get(href); Integer contactId = (contactIds != null) ? contactIds.get(contactIds.size() - 1) : null; if (contactId != null) { doContactDelete(con, contactId, false); } final ContactInput ci = icalInput.fromVCardFile(dcard.getCard(), null); ci.contact.setCategoryId(categoryId); ci.contact.setHref(href); ci.contact.setEtag(dcard.geteTag()); doContactInsert(coreMgr, con, false, ci.contact, null, true, false); } } catDao.updateRemoteSyncById(con, categoryId, newLastSync, newSyncToken); DbUtils.commitQuietly(con); } catch (Exception ex) { DbUtils.rollbackQuietly(con); throw new WTException(ex, "Error importing vCard"); } } else { // Full update or partial computing hashes String newSyncToken = null; if (syncIsSupported) { // If supported, saves last sync-token issued by the server newSyncToken = dbook.getSyncToken(); } // Retrieves cards from DAV endpoint logger.debug("Retrieving whole list [{}]", params.url.toString()); List<DavAddressbookCard> dcards = dav.listAddressbookCards(params.url.toString()); logger.debug("Endpoint returns {} items", dcards.size()); // Handles data... try { Map<String, VContactHrefSync> syncByHref = null; if (full) { doContactsDeleteByCategory(con, categoryId, false); } else if (!full && !syncIsSupported) { // This hash-map is only needed when syncing using hashes ContactDAO contDao = ContactDAO.getInstance(); syncByHref = contDao.viewHrefSyncDataByCategory(con, categoryId); } logger.debug("Processing results..."); // Define a simple map in order to check duplicates. // eg. SOGo passes same card twice :( HashSet<String> hrefs = new HashSet<>(); for (DavAddressbookCard dcard : dcards) { String href = FilenameUtils.getName(dcard.getPath()); //String href = dcard.getPath(); String etag = dcard.geteTag(); if (logger.isTraceEnabled()) logger.trace("{}", VCardUtils.print(dcard.getCard())); if (hrefs.contains(href)) { logger.trace("Card duplicated. Skipped! [{}]", href); continue; } boolean skip = false; Integer matchingContactId = null; if (syncByHref != null) { // Only if... (!full && !syncIsSupported) see above! String prodId = VCardUtils.buildProdId(ManagerUtils.getProductName()); String hash = DigestUtils .md5Hex(new VCardOutput(prodId).write(dcard.getCard(), true)); VContactHrefSync hrefSync = syncByHref.remove(href); if (hrefSync != null) { // Href found -> maybe updated item if (!StringUtils.equals(hrefSync.getEtag(), hash)) { matchingContactId = hrefSync.getContactId(); etag = hash; logger.trace("Card updated [{}, {}]", href, hash); } else { skip = true; logger.trace("Card not modified [{}, {}]", href, hash); } } else { // Href not found -> added item logger.trace("Card newly added [{}, {}]", href, hash); etag = hash; } } if (!skip) { final ContactInput ci = icalInput.fromVCardFile(dcard.getCard(), null); ci.contact.setCategoryId(categoryId); ci.contact.setHref(href); ci.contact.setEtag(etag); if (matchingContactId == null) { doContactInsert(coreMgr, con, false, ci.contact, null, true, false); } else { ci.contact.setContactId(matchingContactId); boolean updated = doContactUpdate(coreMgr, con, false, ci.contact, true, false); if (!updated) throw new WTException("Contact not found [{}]", ci.contact.getContactId()); } } hrefs.add(href); // Marks as processed! } if (syncByHref != null) { // Only if... (!full && !syncIsSupported) see above! // Remaining hrefs -> deleted items for (VContactHrefSync hrefSync : syncByHref.values()) { logger.trace("Card deleted [{}]", hrefSync.getHref()); doContactDelete(con, hrefSync.getContactId(), false); } } catDao.updateRemoteSyncById(con, categoryId, newLastSync, newSyncToken); DbUtils.commitQuietly(con); } catch (Exception ex) { DbUtils.rollbackQuietly(con); throw new WTException(ex, "Error importing vCard"); } } } catch (DavException ex) { throw new WTException(ex, "CardDAV error"); } } else { throw new WTException("Unsupported provider [{0}]", cat.getProvider()); } } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); pendingRemoteCategorySyncs.remove(PENDING_KEY); } } private Category doCategoryInsert(Connection con, Category cat) throws DAOException, WTException { CategoryDAO catDao = CategoryDAO.getInstance(); OCategory ocat = ManagerUtils.createOCategory(cat); ocat.setCategoryId(catDao.getSequence(con).intValue()); fillOCategoryWithDefaults(ocat); if (ocat.getIsDefault()) catDao.resetIsDefaultByProfile(con, ocat.getDomainId(), ocat.getUserId()); catDao.insert(con, ocat); return ManagerUtils.createCategory(ocat); } private boolean doCategoryUpdate(Connection con, Category cat) throws DAOException, WTException { CategoryDAO catDao = CategoryDAO.getInstance(); OCategory ocat = ManagerUtils.createOCategory(cat); fillOCategoryWithDefaults(ocat); if (ocat.getIsDefault()) catDao.resetIsDefaultByProfile(con, ocat.getDomainId(), ocat.getUserId()); return catDao.update(con, ocat) == 1; } private ContactObject doContactObjectPrepare(Connection con, VContactObject vcont, ContactObjectOutputType outputType) throws WTException { if (ContactObjectOutputType.STAT.equals(outputType)) { return ManagerUtils.fillContactCard(new ContactObject(), vcont); } else { ContactPictureDAO cpicDao = ContactPictureDAO.getInstance(); Contact cont = ManagerUtils.fillContact(new Contact(), vcont); if (vcont.getHasPicture()) { OContactPicture opic = cpicDao.select(con, vcont.getContactId()); if (opic != null) cont.setPicture( ManagerUtils.fillContactPicture(new ContactPictureWithBytes(opic.getBytes()), opic)); } if (ContactObjectOutputType.VCARD.equals(outputType)) { ContactObjectWithVCard cc = ManagerUtils.fillContactCard(new ContactObjectWithVCard(), vcont); VCardOutput out = new VCardOutput(VCardUtils.buildProdId(ManagerUtils.getProductName())); VCard vCard = out.toVCard(cont); if (vcont.getHasVcard()) { //TODO: in order to be fully compliant, merge generated vcard with the original one in db table! } cc.setVcard(out.write(vCard)); return cc; } else { ContactObjectWithBean cc = ManagerUtils.fillContactCard(new ContactObjectWithBean(), vcont); cc.setContact(cont); return cc; } } } private Contact doContactGet(Connection con, int contactId, boolean processPicture, boolean processAttachments) throws DAOException, WTException { ContactDAO contDao = ContactDAO.getInstance(); ContactPictureDAO cpicDao = ContactPictureDAO.getInstance(); ContactAttachmentDAO attDao = ContactAttachmentDAO.getInstance(); OContact ocont = contDao.selectById(con, contactId); if ((ocont == null) || (ocont.getIsList())) return null; Contact cont = ManagerUtils.createContact(ocont); if (processPicture) { OContactPictureMetaOnly opic = cpicDao.selectMeta(con, contactId); cont.setPicture(ManagerUtils.createContactPicture(opic)); } if (processAttachments) { List<OContactAttachment> oatts = attDao.selectByContact(con, contactId); cont.setAttachments(ManagerUtils.createContactAttachmentList(oatts)); } return cont; } private int doBatchInsertContacts(CoreManager coreMgr, Connection con, int categoryId, ArrayList<Contact> contacts) throws WTException { ContactDAO contDao = ContactDAO.getInstance(); ArrayList<OContact> ocontacts = new ArrayList<>(); //TODO: eventualmente introdurre supporto alle immagini ArrayList<String> masterDataIds = new ArrayList<>(); for (Contact contact : contacts) { if (contact.hasCompany() && !StringUtils.isBlank(contact.getCompany().getCompanyId())) { masterDataIds.add(contact.getCompany().getCompanyId()); } } Map<String, MasterDataLookup> masterDataMap = coreMgr.lookupMasterData(masterDataIds); for (Contact contact : contacts) { OContact ocont = ManagerUtils.createOContact(contact); ocont.setIsList(false); ocont.setCategoryId(categoryId); ocont.setContactId(contDao.getSequence(con).intValue()); fillDefaultsForInsert(ocont); if (contact.hasCompany()) { ocont.setCompanyData(lookupRealCompanyData(masterDataMap, contact.getCompany())); } ocontacts.add(ocont); } return contDao.batchInsert(con, ocontacts, BaseDAO.createRevisionTimestamp()); } private OContact.CompanyData lookupRealCompanyData(Map<String, ? extends BaseMasterData> masterDataMap, ContactCompany company) { OContact.CompanyData cd = new OContact.CompanyData(); String valueId = company.getValueId(); if (!StringUtils.isBlank(valueId)) { BaseMasterData md = masterDataMap.get(valueId); if (md != null) { cd.company = md.getDescription(); // Save hystoric data cd.companyMasterDataId = valueId; } else { cd.company = valueId; cd.companyMasterDataId = null; } } else { cd.company = company.getValue(); cd.companyMasterDataId = null; } return cd; } private OContact.CompanyData lookupRealCompanyData(CoreManager coreMgr, ContactCompany company) throws WTException { OContact.CompanyData cd = new OContact.CompanyData(); String valueId = company.getValueId(); if (!StringUtils.isBlank(valueId)) { MasterData md = coreMgr.getMasterData(valueId); if (md != null) { cd.company = md.getDescription(); // Save hystoric data cd.companyMasterDataId = valueId; } else { cd.company = valueId; cd.companyMasterDataId = null; } } else { cd.company = company.getValue(); cd.companyMasterDataId = null; } return cd; } private ContactInsertResult doContactInsert(CoreManager coreMgr, Connection con, boolean isList, Contact contact, String rawVCard, boolean processPicture, boolean processAttachments) throws DAOException, IOException { ContactDAO contDao = ContactDAO.getInstance(); OContact ocont = ManagerUtils.createOContact(contact); ocont.setContactId(contDao.getSequence(con).intValue()); ocont.setIsList(isList); fillDefaultsForInsert(ocont); if (!isList && contact.hasCompany()) { try { ocont.setCompanyData(lookupRealCompanyData(coreMgr, contact.getCompany())); } catch (WTException ex) { logger.error("Unable to lookup company data", ex); } } contDao.insert(con, ocont, BaseDAO.createRevisionTimestamp()); if (isList) { return new ContactInsertResult(ocont, null, null); } else { OContactPicture ocpic = null; ArrayList<OContactAttachment> oatts = null; if (!StringUtils.isBlank(rawVCard)) { doContactVCardInsert(con, ocont.getContactId(), rawVCard); } if (processPicture && (contact.getPicture() != null)) { ContactPicture pic = contact.getPicture(); if (!(pic instanceof ContactPictureWithBytes)) throw new IOException("Picture bytes not available"); ocpic = doContactPictureInsert(con, ocont.getContactId(), (ContactPictureWithBytes) pic); } if (processAttachments && (contact.getAttachments() != null)) { oatts = new ArrayList<>(); for (ContactAttachment att : contact.getAttachments()) { if (!(att instanceof ContactAttachmentWithStream)) throw new IOException("Attachment stream not available [" + att.getAttachmentId() + "]"); oatts.add(doContactAttachmentInsert(con, ocont.getContactId(), (ContactAttachmentWithStream) att)); } } return new ContactInsertResult(ocont, ocpic, oatts); } } private boolean doContactUpdate(CoreManager coreMgr, Connection con, boolean isList, Contact contact, boolean processPicture, boolean processAttachments) throws DAOException, IOException { ContactDAO contDao = ContactDAO.getInstance(); ContactAttachmentDAO attDao = ContactAttachmentDAO.getInstance(); OContact ocont = ManagerUtils.createOContact(contact); fillDefaultsForUpdate(ocont); if (!isList && contact.hasCompany()) { try { ocont.setCompanyData(lookupRealCompanyData(coreMgr, contact.getCompany())); } catch (WTException ex) { logger.error("Unable to lookup company data", ex); } } boolean ret = false; if (isList) { ret = contDao.updateList(con, ocont, BaseDAO.createRevisionTimestamp()) == 1; } else { ret = contDao.update(con, ocont, BaseDAO.createRevisionTimestamp()) == 1; } if (!isList) { if (processPicture) { ContactPicture pic = contact.getPicture(); if (pic != null) { if (!(pic instanceof ContactPictureWithBytes)) throw new IOException("Picture bytes not available"); doContactPictureUpdate(con, ocont.getContactId(), (ContactPictureWithBytes) pic); } else { doContactPictureDelete(con, ocont.getContactId()); } } if (processAttachments && (contact.getAttachments() != null)) { List<ContactAttachment> oldAtts = ManagerUtils .createContactAttachmentList(attDao.selectByContact(con, contact.getContactId())); CollectionChangeSet<ContactAttachment> changeSet = LangUtils.getCollectionChanges(oldAtts, contact.getAttachments()); for (ContactAttachment att : changeSet.inserted) { if (!(att instanceof ContactAttachmentWithStream)) throw new IOException("Attachment stream not available [" + att.getAttachmentId() + "]"); doContactAttachmentInsert(con, ocont.getContactId(), (ContactAttachmentWithStream) att); } for (ContactAttachment att : changeSet.updated) { if (!(att instanceof ContactAttachmentWithStream)) continue; doContactAttachmentUpdate(con, (ContactAttachmentWithStream) att); } for (ContactAttachment att : changeSet.deleted) { attDao.delete(con, att.getAttachmentId()); } } } return ret; } private int doContactDelete(Connection con, int contactId, boolean logicDelete) throws DAOException { ContactDAO contDao = ContactDAO.getInstance(); if (logicDelete) { return contDao.logicDeleteById(con, contactId, BaseDAO.createRevisionTimestamp()); } else { // List are not supported here //doContactPictureDelete(con, contactId); //doContactVCardDelete(con, contactId); return contDao.deleteById(con, contactId); } } private int doContactsDeleteByCategory(Connection con, int categoryId, boolean logicDelete) throws DAOException { ContactDAO contDao = ContactDAO.getInstance(); if (logicDelete) { return contDao.logicDeleteByCategory(con, categoryId, BaseDAO.createRevisionTimestamp()); } else { //ContactPictureDAO cpicDao = ContactPictureDAO.getInstance(); //ContactVCardDAO vcaDao = ContactVCardDAO.getInstance(); //ListRecipientDAO lrecDao = ListRecipientDAO.getInstance(); //cpicDao.deleteByCategory(con, categoryId); //vcaDao.deleteByCategory(con, categoryId); //lrecDao.deleteByCategory(con, categoryId); return contDao.deleteByCategory(con, categoryId); } } private void doContactMove(CoreManager coreMgr, Connection con, boolean copy, Contact contact, int targetCategoryId) throws DAOException, IOException, WTException { if (copy) { ContactPictureDAO cpicDao = ContactPictureDAO.getInstance(); ContactVCardDAO vcaDao = ContactVCardDAO.getInstance(); contact.setCategoryId(targetCategoryId); if (contact.hasPicture()) { OContactPicture opic = cpicDao.select(con, contact.getContactId()); if (opic != null) { contact.setPicture( ManagerUtils.fillContactPicture(new ContactPictureWithBytes(opic.getBytes()), opic)); } } OContactVCard ovca = vcaDao.selectById(con, contact.getContactId()); String rawVCard = (ovca != null) ? ovca.getRawData() : null; //TODO: maybe add support to attachments copy doContactInsert(coreMgr, con, false, contact, rawVCard, true, false); } else { ContactDAO contDao = ContactDAO.getInstance(); contDao.updateCategory(con, contact.getContactId(), targetCategoryId, BaseDAO.createRevisionTimestamp()); } } private boolean doContactVCardInsert(Connection con, int contactId, String rawVCard) throws DAOException { ContactVCardDAO vcaDao = ContactVCardDAO.getInstance(); OContactVCard ovca = new OContactVCard(); ovca.setContactId(contactId); ovca.setRawData(rawVCard); return vcaDao.insert(con, ovca) == 1; } private boolean doContactVCardDelete(Connection con, int contactId) throws DAOException { ContactVCardDAO vcaDao = ContactVCardDAO.getInstance(); return vcaDao.delete(con, contactId) == 1; } private OContactPicture doContactPictureInsert(Connection con, int contactId, ContactPictureWithBytes picture) throws DAOException { ContactPictureDAO cpicDao = ContactPictureDAO.getInstance(); OContactPicture ocpic = new OContactPicture(); ocpic.setContactId(contactId); ocpic.setMediaType(picture.getMediaType()); try { BufferedImage bi = ImageIO.read(new ByteArrayInputStream(picture.getBytes())); if ((bi.getWidth() > 720) || (bi.getHeight() > 720)) { bi = Scalr.resize(bi, Scalr.Method.QUALITY, Scalr.Mode.AUTOMATIC, 720); ocpic.setWidth(bi.getWidth()); ocpic.setHeight(bi.getHeight()); String formatName = new MimeType(picture.getMediaType()).getSubType(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { ImageIO.write(bi, formatName, baos); baos.flush(); ocpic.setBytes(baos.toByteArray()); } catch (IOException ex1) { logger.warn("Error resizing image", ex1); } finally { IOUtils.closeQuietly(baos); } } else { ocpic.setWidth(bi.getWidth()); ocpic.setHeight(bi.getHeight()); ocpic.setBytes(picture.getBytes()); } } catch (IOException ex) { throw new WTRuntimeException(ex, "Error handling picture"); } cpicDao.insert(con, ocpic); return ocpic; } private void doContactPictureUpdate(Connection con, int contactId, ContactPictureWithBytes picture) throws DAOException { doContactPictureDelete(con, contactId); doContactPictureInsert(con, contactId, picture); } private void doContactPictureInsert(Connection con, int contactId, ContactPictureWithBytesOld picture) throws DAOException { ContactPictureDAO cpicDao = ContactPictureDAO.getInstance(); OContactPicture ocpic = new OContactPicture(); ocpic.setContactId(contactId); ocpic.setMediaType(picture.getMediaType()); try { BufferedImage bi = ImageIO.read(new ByteArrayInputStream(picture.getBytes())); if ((bi.getWidth() > 720) || (bi.getHeight() > 720)) { bi = Scalr.resize(bi, Scalr.Method.QUALITY, Scalr.Mode.AUTOMATIC, 720); ocpic.setWidth(bi.getWidth()); ocpic.setHeight(bi.getHeight()); String formatName = new MimeType(picture.getMediaType()).getSubType(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { ImageIO.write(bi, formatName, baos); baos.flush(); ocpic.setBytes(baos.toByteArray()); } catch (IOException ex1) { logger.warn("Error resizing image", ex1); } finally { IOUtils.closeQuietly(baos); } } else { ocpic.setWidth(bi.getWidth()); ocpic.setHeight(bi.getHeight()); ocpic.setBytes(picture.getBytes()); } } catch (IOException ex) { throw new WTRuntimeException(ex, "Error handling picture"); } cpicDao.insert(con, ocpic); } private void doContactPictureUpdate(Connection con, int contactId, ContactPictureWithBytesOld picture) throws DAOException { doContactPictureDelete(con, contactId); doContactPictureInsert(con, contactId, picture); } private void doContactPictureDelete(Connection con, int contactId) throws DAOException { ContactPictureDAO cpicDao = ContactPictureDAO.getInstance(); cpicDao.delete(con, contactId); } private OContactAttachment doContactAttachmentInsert(Connection con, int contactId, ContactAttachmentWithStream attachment) throws DAOException, IOException { ContactAttachmentDAO attDao = ContactAttachmentDAO.getInstance(); OContactAttachment oatt = ManagerUtils.createOTaskAttachment(attachment); oatt.setContactAttachmentId(IdentifierUtils.getUUIDTimeBased()); oatt.setContactId(contactId); attDao.insert(con, oatt, BaseDAO.createRevisionTimestamp()); InputStream is = attachment.getStream(); try { attDao.insertBytes(con, oatt.getContactAttachmentId(), IOUtils.toByteArray(is)); } finally { IOUtils.closeQuietly(is); } return oatt; } private boolean doContactAttachmentUpdate(Connection con, ContactAttachmentWithStream attachment) throws DAOException, IOException { ContactAttachmentDAO attDao = ContactAttachmentDAO.getInstance(); OContactAttachment oatt = ManagerUtils.createOTaskAttachment(attachment); attDao.update(con, oatt, BaseDAO.createRevisionTimestamp()); InputStream is = attachment.getStream(); try { attDao.deleteBytes(con, oatt.getContactAttachmentId()); return attDao.insertBytes(con, oatt.getContactAttachmentId(), IOUtils.toByteArray(is)) == 1; } finally { IOUtils.closeQuietly(is); } } private OContact doContactsListInsert(CoreManager coreMgr, Connection con, ContactsList list) throws DAOException, IOException { ListRecipientDAO lrecDao = ListRecipientDAO.getInstance(); ContactInsertResult result = doContactInsert(coreMgr, con, true, createContact(list), null, false, false); for (ContactsListRecipient rcpt : list.getRecipients()) { OListRecipient olrec = new OListRecipient(rcpt); olrec.setContactId(result.ocontact.getContactId()); olrec.setListRecipientId(lrecDao.getSequence(con).intValue()); lrecDao.insert(con, olrec); } return result.ocontact; } private boolean doContactsListUpdate(CoreManager coreMgr, Connection con, ContactsList list) throws DAOException, IOException { ListRecipientDAO lrecDao = ListRecipientDAO.getInstance(); if (!doContactUpdate(coreMgr, con, true, createContact(list), false, false)) return false; //TODO: gestire la modifica determinando gli eliminati e gli aggiunti? lrecDao.deleteByContact(con, list.getContactId()); for (ContactsListRecipient rcpt : list.getRecipients()) { OListRecipient olrec = new OListRecipient(rcpt); olrec.setContactId(list.getContactId()); olrec.setListRecipientId(lrecDao.getSequence(con).intValue()); lrecDao.insert(con, olrec); } return true; } private boolean doContactsListAddTo(Connection con, ContactsList list) throws DAOException { ListRecipientDAO lrecDao = ListRecipientDAO.getInstance(); for (ContactsListRecipient rcpt : list.getRecipients()) { OListRecipient olrec = new OListRecipient(rcpt); olrec.setContactId(list.getContactId()); olrec.setListRecipientId(lrecDao.getSequence(con).intValue()); lrecDao.insert(con, olrec); } return true; } private boolean doMoveContactsList(CoreManager coreMgr, Connection con, boolean copy, ContactsList clist, int targetCategoryId) throws DAOException, IOException { clist.setCategoryId(targetCategoryId); if (copy) { doContactsListInsert(coreMgr, con, clist); return true; } else { return doContactsListUpdate(coreMgr, con, clist); } } private String lookupMasterDataDescription(CoreManager coreMgr, String masterDataId) throws WTException { if (masterDataId == null) return null; MasterData md = coreMgr.getMasterData(masterDataId); return (md != null) ? md.getDescription() : null; } private UserProfileId findCategoryOwner(int categoryId) throws WTException { Connection con = null; try { con = WT.getConnection(SERVICE_ID); CategoryDAO dao = CategoryDAO.getInstance(); Owner owner = dao.selectOwnerById(con, categoryId); return (owner == null) ? null : new UserProfileId(owner.getDomainId(), owner.getUserId()); } catch (SQLException | DAOException ex) { throw wrapException(ex); } finally { DbUtils.closeQuietly(con); } } private void checkRightsOnCategoryRoot(UserProfileId owner, String action) throws WTException { UserProfileId targetPid = getTargetProfileId(); if (RunContext.isWebTopAdmin()) return; if (owner.equals(targetPid)) return; String shareId = shareCache.getShareRootIdByOwner(owner); if (shareId == null) throw new WTException("ownerToRootShareId({0}) -> null", owner); CoreManager coreMgr = WT.getCoreManager(targetPid); if (coreMgr.isShareRootPermitted(shareId, action)) return; //if (core.isShareRootPermitted(SERVICE_ID, RESOURCE_CATEGORY, action, shareId)) return; throw new AuthException("Action not allowed on root share [{0}, {1}, {2}, {3}]", shareId, action, GROUPNAME_CATEGORY, targetPid.toString()); } private boolean quietlyCheckRightsOnCategoryFolder(int categoryId, String action) { try { checkRightsOnCategoryFolder(categoryId, action); return true; } catch (AuthException ex1) { return false; } catch (WTException ex1) { logger.warn("Unable to check rights [{}]", categoryId); return false; } } private void checkRightsOnCategoryFolder(int categoryId, String action) throws WTException { UserProfileId targetPid = getTargetProfileId(); if (RunContext.isWebTopAdmin()) return; // Skip rights check if running user is resource's owner UserProfileId owner = ownerCache.get(categoryId); if (owner == null) throw new WTException("categoryToOwner({0}) -> null", categoryId); if (owner.equals(targetPid)) return; // Checks rights on the wildcard instance (if present) CoreManager core = WT.getCoreManager(targetPid); String wildcardShareId = shareCache.getWildcardShareFolderIdByOwner(owner); if (wildcardShareId != null) { if (core.isShareFolderPermitted(wildcardShareId, action)) return; //if (core.isShareFolderPermitted(SERVICE_ID, RESOURCE_CATEGORY, action, wildcardShareId)) return; } // Checks rights on category instance String shareId = shareCache.getShareFolderIdByFolderId(categoryId); if (shareId == null) throw new WTException("categoryToLeafShareId({0}) -> null", categoryId); if (core.isShareFolderPermitted(shareId, action)) return; //if (core.isShareFolderPermitted(SERVICE_ID, RESOURCE_CATEGORY, action, shareId)) return; throw new AuthException("Action not allowed on folder share [{0}, {1}, {2}, {3}]", shareId, action, GROUPNAME_CATEGORY, targetPid.toString()); } private void checkRightsOnCategoryElements(int categoryId, String action) throws WTException { UserProfileId targetPid = getTargetProfileId(); if (RunContext.isWebTopAdmin()) return; // Skip rights check if running user is resource's owner UserProfileId owner = ownerCache.get(categoryId); if (owner == null) throw new WTException("categoryToOwner({0}) -> null", categoryId); if (owner.equals(targetPid)) return; // Checks rights on the wildcard instance (if present) CoreManager core = WT.getCoreManager(targetPid); String wildcardShareId = shareCache.getWildcardShareFolderIdByOwner(owner); if (wildcardShareId != null) { if (core.isShareElementsPermitted(wildcardShareId, action)) return; //if (core.isShareElementsPermitted(SERVICE_ID, RESOURCE_CATEGORY, action, wildcardShareId)) return; } // Checks rights on category instance String shareId = shareCache.getShareFolderIdByFolderId(categoryId); if (shareId == null) throw new WTException("categoryToLeafShareId({0}) -> null", categoryId); if (core.isShareElementsPermitted(shareId, action)) return; //if (core.isShareElementsPermitted(SERVICE_ID, RESOURCE_CATEGORY, action, shareId)) return; throw new AuthException("Action not allowed on folderEls share [{0}, {1}, {2}, {3}]", shareId, action, GROUPNAME_CATEGORY, targetPid.toString()); } private OCategory fillOCategoryWithDefaults(OCategory tgt) { if (tgt != null) { ContactsServiceSettings ss = getServiceSettings(); if (tgt.getDomainId() == null) tgt.setDomainId(getTargetProfileId().getDomainId()); if (tgt.getUserId() == null) tgt.setUserId(getTargetProfileId().getUserId()); if (tgt.getBuiltIn() == null) tgt.setBuiltIn(false); if (StringUtils.isBlank(tgt.getProvider())) tgt.setProvider(EnumUtils.toSerializedName(Category.Provider.LOCAL)); if (StringUtils.isBlank(tgt.getColor())) tgt.setColor("#FFFFFF"); if (StringUtils.isBlank(tgt.getSync())) tgt.setSync(EnumUtils.toSerializedName(ss.getDefaultCategorySync())); if (tgt.getIsDefault() == null) tgt.setIsDefault(false); //if (fill.getIsPrivate() == null) fill.setIsPrivate(false); Category.Provider provider = EnumUtils.forSerializedName(tgt.getProvider(), Category.Provider.class); if (Category.Provider.CARDDAV.equals(provider)) { tgt.setIsDefault(false); } } return tgt; } private Contact createContact(ContactsList src) { if (src == null) return null; Contact cont = new Contact(); cont.setContactId(src.getContactId()); cont.setCategoryId(src.getCategoryId()); cont.setDisplayName(src.getName()); cont.setFirstName(src.getName()); cont.setLastName(src.getName()); return cont; } private OContact fillDefaultsForInsert(OContact tgt) { if (tgt != null) { if (StringUtils.isBlank(tgt.getPublicUid())) { tgt.setPublicUid(ContactsUtils.buildContactUid(tgt.getContactId(), WT.getDomainInternetName(getTargetProfileId().getDomainId()))); } if (StringUtils.isBlank(tgt.getHref())) tgt.setHref(ContactsUtils.buildHref(tgt.getPublicUid())); if (!tgt.getIsList()) { if (StringUtils.isBlank(tgt.getDisplayName())) tgt.setDisplayName(BaseContact.buildFullName(tgt.getFirstname(), tgt.getLastname())); } else { // Compose list workEmail as: "list-{contactId}@{serviceId}" tgt.setWorkEmail(RCPT_ORIGIN_LIST + "-" + tgt.getContactId() + "@" + SERVICE_ID); } } return tgt; } private OContact fillDefaultsForUpdate(OContact tgt) { if (tgt != null) { if (!tgt.getIsList()) { if (StringUtils.isBlank(tgt.getDisplayName())) tgt.setDisplayName(BaseContact.buildFullName(tgt.getFirstname(), tgt.getLastname())); } else { // Compose list workEmail as: "list-{contactId}@{serviceId}" tgt.setWorkEmail(RCPT_ORIGIN_LIST + "-" + tgt.getContactId() + "@" + SERVICE_ID); } } return tgt; } /* private String buildSearchfield(OContact contact) { StringBuilder sb = new StringBuilder(); sb.append(StringUtils.defaultString(contact.getLastname())); sb.append(StringUtils.defaultString(contact.getFirstname())); return sb.toString().toLowerCase(); } private String buildSearchfield(CoreManager coreMgr, OContact contact) { StringBuilder sb = new StringBuilder(); sb.append(buildSearchfield(contact)); String masterData = null; if (!StringUtils.isEmpty(contact.getCompany())) { try { masterData = lookupMasterDataDescription(coreMgr, contact.getCompany()); } catch (WTException ex) { logger.warn("Problems looking-up master data description", ex); } } final String company = StringUtils.defaultIfBlank(masterData, contact.getCompany()); sb.append(StringUtils.defaultString(company).toLowerCase()); return sb.toString(); } */ private ReminderInApp createAnniversaryInAppReminder(Locale locale, boolean birthday, VContact contact, DateTime date) { String type = (birthday) ? "birthday" : "anniversary"; String resKey = (birthday) ? ContactsLocale.REMINDER_TITLE_BIRTHDAY : ContactsLocale.REMINDER_TITLE_ANNIVERSARY; String title = MessageFormat.format(lookupResource(locale, resKey), BaseContact.buildFullName(contact.getFirstname(), contact.getLastname())); ReminderInApp alert = new ReminderInApp(SERVICE_ID, contact.getCategoryProfileId(), type, contact.getContactId().toString()); alert.setTitle(title); alert.setDate(date); alert.setTimezone(date.getZone().getID()); return alert; } private ReminderEmail createAnniversaryEmailReminder(Locale locale, InternetAddress recipient, boolean birthday, VContact contact, DateTime date) { String type = (birthday) ? "birthday" : "anniversary"; String resKey = (birthday) ? ContactsLocale.REMINDER_TITLE_BIRTHDAY : ContactsLocale.REMINDER_TITLE_ANNIVERSARY; String fullName = BaseContact.buildFullName(contact.getFirstname(), contact.getLastname()); String title = MessageFormat.format(lookupResource(locale, resKey), StringUtils.trim(fullName)); String subject = NotificationHelper.buildSubject(locale, SERVICE_ID, title); String body = null; try { body = TplHelper.buildAnniversaryEmail(locale, birthday, recipient.getAddress(), fullName); } catch (IOException | TemplateException ex) { logger.error("Error building anniversary email", ex); } ReminderEmail alert = new ReminderEmail(SERVICE_ID, contact.getCategoryProfileId(), type, contact.getContactId().toString()); alert.setSubject(subject); alert.setBody(body); return alert; } public class RootRecipientsProvider extends RecipientsProviderBase { public final UserProfileId ownerId; private final Collection<Integer> categoryIds; public RootRecipientsProvider(String id, String description, UserProfileId ownerId, Collection<Integer> categoryIds) { super(id, description); this.ownerId = ownerId; this.categoryIds = categoryIds; } @Override public List<Recipient> getRecipients(RecipientFieldType fieldType, String queryText, int max) { ContactDAO dao = ContactDAO.getInstance(); ArrayList<Recipient> items = new ArrayList<>(); Connection con = null; boolean listsOnly = fieldType.equals(RecipientFieldType.LIST); if (listsOnly) fieldType = RecipientFieldType.EMAIL; try { con = WT.getConnection(SERVICE_ID); RecipientFieldCategory[] fieldCategories = new RecipientFieldCategory[] { RecipientFieldCategory.WORK, RecipientFieldCategory.HOME, RecipientFieldCategory.OTHER }; for (RecipientFieldCategory fieldCategory : fieldCategories) { if (!dao.hasTableFieldFor(fieldType, fieldCategory)) continue; final String origin = getContactOriginBy(fieldCategory); final List<VContact> vconts = dao.viewRecipientsByFieldCategoryQuery(con, fieldType, fieldCategory, categoryIds, queryText); for (VContact vcont : vconts) { final String value = vcont.getValueBy(fieldType, fieldCategory); final String recipientId = vcont.getContactId() != null ? vcont.getContactId().toString() : null; if (vcont.getIsList() && fieldCategory.equals(RecipientFieldCategory.WORK) && fieldType.equals(RecipientFieldType.EMAIL)) { items.add(new Recipient(this.getId(), this.getDescription(), RCPT_ORIGIN_LIST, vcont.getDisplayName(), value, Recipient.Type.TO, recipientId)); } else if (!listsOnly) { if (fieldType.equals(RecipientFieldType.EMAIL) && !InternetAddressUtils.isAddressValid(value)) continue; String personal = vcont.getDisplayName(); if (StringUtils.isBlank(personal)) personal = InternetAddressUtils.buildPersonal(vcont.getFirstname(), vcont.getLastname()); items.add(new Recipient(this.getId(), this.getDescription(), origin, personal, value, Recipient.Type.TO, recipientId)); } } } return items; } catch (Throwable t) { logger.error("Error listing recipients", t); return null; } finally { DbUtils.closeQuietly(con); } } @Override public List<Recipient> expandToRecipients(String virtualRecipient) { ListRecipientDAO dao = ListRecipientDAO.getInstance(); ArrayList<Recipient> items = new ArrayList<>(); Connection con = null; try { con = WT.getConnection(SERVICE_ID); int contactId = ContactsUtils.getListIdFromVirtualRecipient(virtualRecipient); if (contactId >= 0) { UserProfileId pid = new UserProfileId(getId()); List<VListRecipient> recipients = dao.selectByProfileContact(con, pid.getDomainId(), pid.getUserId(), contactId); for (VListRecipient recipient : recipients) { Recipient.Type rcptType = EnumUtils.forSerializedName(recipient.getRecipientType(), Recipient.Type.class); InternetAddress ia = InternetAddressUtils.toInternetAddress(recipient.getRecipient()); if (ia != null) { items.add(new Recipient(this.getId(), this.getDescription(), RCPT_ORIGIN_LISTITEM, ia.getPersonal(), ia.getAddress(), rcptType)); } } } else { throw new WTException("Bad key format [{0}]", virtualRecipient); } return items; } catch (Exception ex) { logger.error("Error listing recipients", ex); return null; } finally { DbUtils.closeQuietly(con); } } private String getContactOriginBy(RecipientFieldCategory fieldCategory) { if (fieldCategory.equals(RecipientFieldCategory.WORK)) { return RCPT_ORIGIN_CONTACT_WORK; } else if (fieldCategory.equals(RecipientFieldCategory.HOME)) { return RCPT_ORIGIN_CONTACT_HOME; } else if (fieldCategory.equals(RecipientFieldCategory.OTHER)) { return RCPT_ORIGIN_CONTACT_OTHER; } else { return null; } } } private class ContactBatchImportBeanHandler extends BatchBeanHandler<ContactInput> { private ArrayList<Contact> contacts = new ArrayList<>(); public Connection con; public int categoryId; public int insertedCount = 0; public ContactBatchImportBeanHandler(LogEntries log, Connection con, int categoryId) { super(log); this.con = con; this.categoryId = categoryId; } @Override protected int getCurrentBeanBufferSize() { return contacts.size(); } @Override protected void clearBeanBuffer() { contacts.clear(); } @Override protected void addBeanToBuffer(ContactInput bean) { contacts.add(bean.contact); } @Override public boolean handleBufferedBeans() { CoreManager coreMgr = WT.getCoreManager(getTargetProfileId()); try { insertedCount = insertedCount + doBatchInsertContacts(coreMgr, con, categoryId, contacts); return true; } catch (Throwable t) { lastException = t; return false; } } } private static class ImportException extends Exception { public ImportException(Throwable cause) { super(cause); } public ImportException(String message, Throwable cause) { super(message, cause); } } public static class CategoryContacts { public final OCategory folder; public final List<VContact> contacts; public CategoryContacts(OCategory folder, List<VContact> contacts) { this.folder = folder; this.contacts = contacts; } } public static class ProbeCategoryRemoteUrlResult { public final String displayName; public ProbeCategoryRemoteUrlResult(String displayName) { this.displayName = displayName; } } private static class ContactInsertResult { public final OContact ocontact; public final OContactPicture opicture; public final List<OContactAttachment> oattachments; public ContactInsertResult(OContact ocontact, OContactPicture opicture, List<OContactAttachment> oattachments) { this.ocontact = ocontact; this.opicture = opicture; this.oattachments = oattachments; } } private class OwnerCache extends AbstractMapCache<Integer, UserProfileId> { @Override protected void internalInitCache() { } @Override protected void internalMissKey(Integer key) { try { UserProfileId owner = findCategoryOwner(key); if (owner == null) throw new WTException("Owner not found [{0}]", key); put(key, owner); } catch (WTException ex) { throw new WTRuntimeException(ex.getMessage()); } } } private class ShareCache extends AbstractShareCache<Integer, ShareRootCategory> { @Override protected void internalInitCache() { final CoreManager coreMgr = WT.getCoreManager(getTargetProfileId()); try { for (ShareRootCategory root : internalListIncomingCategoryShareRoots()) { shareRoots.add(root); ownerToShareRoot.put(root.getOwnerProfileId(), root); for (OShare folder : coreMgr.listIncomingShareFolders(root.getShareId(), GROUPNAME_CATEGORY)) { if (folder.hasWildcard()) { final UserProfileId ownerPid = coreMgr.userUidToProfileId(folder.getUserUid()); ownerToWildcardShareFolder.put(ownerPid, folder.getShareId().toString()); for (Category category : listCategories(ownerPid).values()) { folderTo.add(category.getCategoryId()); rootShareToFolderShare.put(root.getShareId(), category.getCategoryId()); folderToWildcardShareFolder.put(category.getCategoryId(), folder.getShareId().toString()); } } else { int categoryId = Integer.valueOf(folder.getInstance()); folderTo.add(categoryId); rootShareToFolderShare.put(root.getShareId(), categoryId); folderToShareFolder.put(categoryId, folder.getShareId().toString()); } } } ready = true; } catch (WTException ex) { throw new WTRuntimeException(ex.getMessage()); } } } /* private static class ContactResultBeanHandler extends DefaultBeanHandler<ContactReadResult> { private LogEntries log; public ArrayList<ContactReadResult> parsed; public ContactResultBeanHandler(LogEntries log) { this.log = log; parsed = new ArrayList<>(); } @Override public void handle(ContactReadResult bean, LogEntries log) throws Exception { parsed.add(bean); log.addAll(log); } } public LogEntries importContacts22(int categoryId, ContactFileReader rea, File file, String mode) throws WTException { LogEntries log = new LogEntries(); Connection con = null; try { checkRightsOnCategoryElements(categoryId, "CREATE"); // Rights check! if(mode.equals("copy")) checkRightsOnCategoryElements(categoryId, "DELETE"); // Rights check! log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "Started at {0}", new DateTime())); log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "Reading source file...")); ContactResultBeanHandler handler = new ContactResultBeanHandler(log); try { rea.readContacts(file, handler); } catch(IOException | FileReaderException ex) { log.addMaster(new MessageLogEntry(LogEntry.Level.ERROR, "Unable to complete reading. Reason: {0}", ex.getMessage())); throw new WTException(ex); } log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "{0} contact/s found!", handler.parsed.size())); con = WT.getConnection(SERVICE_ID); con.setAutoCommit(false); if(mode.equals("copy")) { log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "Cleaning previous contacts...")); int del = doDeleteContactsByCategory(con, categoryId, false); log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "{0} contact/s deleted!", del)); } log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "Importing...")); int count = 0; for(ContactReadResult parse : handler.parsed) { parse.contact.setCategoryId(categoryId); try { doInsertContact(con, false, parse.contact, parse.picture); DbUtils.commitQuietly(con); count++; } catch(Exception ex) { logger.trace("Error inserting contact", ex); DbUtils.rollbackQuietly(con); log.addMaster(new MessageLogEntry(LogEntry.Level.ERROR, "Unable to import contact [{0}, {1}, {2}]. Reason: {3}", parse.contact.getFirstName(), parse.contact.getLastName(), parse.contact.getPublicUid(), ex.getMessage())); } } log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "{0} contact/s imported!", count)); } catch(SQLException | DAOException ex) { throw wrapThrowable(ex); } catch(WTException ex) { throw ex; } finally { DbUtils.closeQuietly(con); log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "Ended at {0}", new DateTime())); } return log; } */ /* public LogEntries importVCard(int categoryId, InputStream is, String mode) throws WTException { LogEntries log = new LogEntries(); Connection con = null; try { checkRightsOnCategoryElements(categoryId, "CREATE"); // Rights check! if(mode.equals("copy")) checkRightsOnCategoryElements(categoryId, "DELETE"); // Rights check! log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "Started at {0}", new DateTime())); log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "Reading vCard file...")); ArrayList<ParseResult> parsed = null; try { parsed = VCardHelper.parseVCard(log, is); } catch(IOException ex) { log.addMaster(new MessageLogEntry(LogEntry.Level.ERROR, "Unable to complete parsing. Reason: {0}", ex.getMessage())); throw new WTException(ex); } log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "{0} contacts/s found!", parsed.size())); con = WT.getConnection(SERVICE_ID); con.setAutoCommit(false); if(mode.equals("copy")) { log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "Cleaning previous contacts...")); int del = doDeleteContactsByCategory(con, categoryId, false); log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "{0} contact/s deleted!", del)); } log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "Importing...")); int count = 0; for(ParseResult parse : parsed) { parse.contact.setCategoryId(categoryId); try { doInsertContact(con, false, parse.contact, parse.picture); DbUtils.commitQuietly(con); count++; } catch(Exception ex) { logger.trace("Error inserting contact", ex); DbUtils.rollbackQuietly(con); log.addMaster(new MessageLogEntry(LogEntry.Level.ERROR, "Unable to import contact [{0}, {1}, {2}]. Reason: {3}", parse.contact.getFirstName(), parse.contact.getLastName(), parse.contact.getPublicUid(), ex.getMessage())); } } log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "{0} contact/s imported!", count)); } catch(SQLException | DAOException ex) { throw wrapThrowable(ex); } catch(WTException ex) { throw ex; } finally { DbUtils.closeQuietly(con); log.addMaster(new MessageLogEntry(LogEntry.Level.INFO, "Ended at {0}", new DateTime())); //TODO: inviare email report //for(LogEntry entry : log) { // logger.trace("{}", ((MessageLogEntry)entry).getMessage()); //} } return log; } */ }