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