001/*
002 * (C) Copyright 2006-2015 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     dmetzler
016 */
017package org.nuxeo.ecm.platform.ec.notification;
018
019import java.io.Serializable;
020import java.util.ArrayList;
021import java.util.Arrays;
022import java.util.Collections;
023import java.util.HashMap;
024import java.util.HashSet;
025import java.util.List;
026import java.util.Map;
027import java.util.Map.Entry;
028import java.util.Set;
029
030import org.nuxeo.ecm.core.api.CoreSession;
031import org.nuxeo.ecm.core.api.DocumentModel;
032import org.nuxeo.ecm.platform.notification.api.Notification;
033import org.nuxeo.ecm.platform.notification.api.NotificationManager;
034import org.nuxeo.runtime.api.Framework;
035
036/**
037 * Encapsulates all notification storage logic in the Notifiable facet.
038 *
039 * @since 7.3
040 */
041public class SubscriptionAdapter {
042
043    public static final String NOTIFIABLE_FACET = "Notifiable";
044
045    private static final String NOTIF_PROPERTY = "notif:notifications";
046
047    private static final String NOTIF_SUBSCRIBERSKEY = "subscribers";
048
049    private static final String NOTIF_NAMEKEY = "name";
050
051    private DocumentModel doc;
052
053    public SubscriptionAdapter(DocumentModel doc) {
054        this.doc = doc;
055    }
056
057    /**
058     * Take the document storage propery and put it in a map.
059     * <dl>
060     *   <dt>key</dt>
061     *   <dd>notificationName</dd>
062     *   <dt>value</dt>
063     *   <dd>list of subscribers</dd>
064     * </dl>
065     * After having modified the map, update the doc with {@link #setNotificationMap(Map)}
066     *
067     * @return
068     */
069    private Map<String, Set<String>> getNotificationMap() {
070
071        if (!doc.hasFacet(SubscriptionAdapter.NOTIFIABLE_FACET)) {
072            return new HashMap<>();
073        }
074
075        Map<String, Set<String>> result = new HashMap<String, Set<String>>();
076
077        @SuppressWarnings("unchecked")
078        List<Map<String, Serializable>> props = (List<Map<String, Serializable>>) doc.getPropertyValue(NOTIF_PROPERTY);
079        for (Map<String, Serializable> prop : props) {
080            String notificationName = (String) prop.get(NOTIF_NAMEKEY);
081            String[] subscribers = (String[]) prop.get(NOTIF_SUBSCRIBERSKEY);
082
083            if (subscribers != null && subscribers.length > 0) {
084                if (!result.containsKey(notificationName)) {
085                    Set<String> subscribersSet = new HashSet<String>();
086                    result.put(notificationName, subscribersSet);
087                }
088                result.get(notificationName).addAll(Arrays.asList(subscribers));
089            }
090        }
091        return result;
092
093    }
094
095    /**
096     * Take a map and store it in the document's notification property. To get the original map, use
097     * {@link #getNotificationMap()}
098     */
099    private void setNotificationMap(Map<String, Set<String>> map) {
100        List<Map<String, Serializable>> props = new ArrayList<Map<String, Serializable>>();
101        for (Entry<String, Set<String>> entry : map.entrySet()) {
102            Map<String, Serializable> propMap = new HashMap<>();
103            propMap.put(NOTIF_NAMEKEY, entry.getKey());
104            propMap.put(NOTIF_SUBSCRIBERSKEY, new ArrayList<String>(entry.getValue()));
105            props.add(propMap);
106        }
107
108        if (!props.isEmpty()) {
109            if (!doc.hasFacet(SubscriptionAdapter.NOTIFIABLE_FACET)) {
110                doc.addFacet(SubscriptionAdapter.NOTIFIABLE_FACET);
111            }
112
113            doc.setPropertyValue(NOTIF_PROPERTY, (Serializable) props);
114        }
115    }
116
117    /**
118     * Return the list of subscribers name for a given notification.
119     *
120     * @param notification
121     * @return
122     */
123    public List<String> getNotificationSubscribers(String notification) {
124        Set<String> subscribers = getNotificationMap().get(notification);
125        return subscribers != null ? new ArrayList<>(subscribers) : Collections.emptyList();
126    }
127
128    /**
129     * Return the list of of subscriptions for a given user
130     *
131     * @return
132     */
133    public List<String> getUserSubscriptions(String username) {
134        List<String> result = new ArrayList<String>();
135        for (Entry<String, Set<String>> entry : getNotificationMap().entrySet()) {
136            if (entry.getValue().contains(username)) {
137                result.add(entry.getKey());
138            }
139        }
140        return result;
141    }
142
143    /**
144     * Add a subscription to a notification for a given user.
145     *
146     * @param username
147     * @param notification
148     */
149    public void addSubscription(String username, String notification) {
150        Map<String, Set<String>> notificationMap = getNotificationMap();
151        if (!notificationMap.containsKey(notification)) {
152            notificationMap.put(notification, new HashSet<>());
153        }
154        notificationMap.get(notification).add(username);
155        setNotificationMap(notificationMap);
156    }
157
158    /**
159     * Add a subscription to all notification for a given user
160     *
161     * @param username
162     */
163    public void addSubscriptionsToAll(String username) {
164
165        Set<String> notificationNames = new HashSet<String>();
166
167        NotificationManager ns = Framework.getLocalService(NotificationManager.class);
168
169        for (Notification notif : ns.getNotificationsForSubscriptions(doc.getType())) {
170            notificationNames.add(notif.getName());
171        }
172
173        CoreSession session = doc.getCoreSession();
174
175        if (session != null) {
176            for (DocumentModel parent : session.getParentDocuments(doc.getRef())) {
177                for (Notification notif : ns.getNotificationsForSubscriptions(parent.getType())) {
178                    notificationNames.add(notif.getName());
179                }
180            }
181        }
182
183        // add subscriptions to every relevant notification
184        for (String name : notificationNames) {
185            addSubscription(username, name);
186        }
187
188    }
189
190    /**
191     * Remove a subscription to a notification for a given user.
192     *
193     * @param username
194     * @param notification
195     */
196    public void removeUserNotificationSubscription(String username, String notification) {
197        Map<String, Set<String>> map = getNotificationMap();
198        if (map.containsKey(notification)) {
199            map.get(notification).remove(username);
200        }
201        setNotificationMap(map);
202    }
203
204    /**
205     * Copy the subscriptions of the current doc to the targetted document.
206     *
207     * @param targetDoc
208     */
209    public void copySubscriptionsTo(DocumentModel targetDoc) {
210        if(!targetDoc.hasFacet(NOTIFIABLE_FACET)) {
211            targetDoc.addFacet(NOTIFIABLE_FACET);
212        }
213        targetDoc.setPropertyValue(NOTIF_PROPERTY, doc.getPropertyValue(NOTIF_PROPERTY));
214    }
215
216}