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            Set<String> subscribers = entry.getValue();
105            if (!subscribers.isEmpty()) {
106                Map<String, Serializable> propMap = new HashMap<>();
107                propMap.put(NOTIF_NAMEKEY, entry.getKey());
108                propMap.put(NOTIF_SUBSCRIBERSKEY, new ArrayList<String>(subscribers));
109                props.add(propMap);
110            }
111        }
112
113        if (!props.isEmpty()) {
114            if (!doc.hasFacet(SubscriptionAdapter.NOTIFIABLE_FACET)) {
115                doc.addFacet(SubscriptionAdapter.NOTIFIABLE_FACET);
116            }
117        }
118
119        doc.setPropertyValue(NOTIF_PROPERTY, (Serializable) props);
120    }
121
122    /**
123     * Return the list of subscribers name for a given notification.
124     *
125     * @param notification
126     * @return
127     */
128    public List<String> getNotificationSubscribers(String notification) {
129        Set<String> subscribers = getNotificationMap().get(notification);
130        return subscribers != null ? new ArrayList<>(subscribers) : Collections.emptyList();
131    }
132
133    /**
134     * Return the list of of subscriptions for a given user
135     *
136     * @return
137     */
138    public List<String> getUserSubscriptions(String username) {
139        List<String> result = new ArrayList<String>();
140        for (Entry<String, Set<String>> entry : getNotificationMap().entrySet()) {
141            if (entry.getValue().contains(username)) {
142                result.add(entry.getKey());
143            }
144        }
145        return result;
146    }
147
148    /**
149     * Add a subscription to a notification for a given user.
150     *
151     * @param username
152     * @param notification
153     */
154    public void addSubscription(String username, String notification) {
155        Map<String, Set<String>> notificationMap = getNotificationMap();
156        if (!notificationMap.containsKey(notification)) {
157            notificationMap.put(notification, new HashSet<>());
158        }
159        notificationMap.get(notification).add(username);
160        setNotificationMap(notificationMap);
161    }
162
163    /**
164     * Add a subscription to all notification for a given user
165     *
166     * @param username
167     */
168    public void addSubscriptionsToAll(String username) {
169
170        Set<String> notificationNames = new HashSet<String>();
171
172        NotificationManager ns = Framework.getLocalService(NotificationManager.class);
173
174        for (Notification notif : ns.getNotificationsForSubscriptions(doc.getType())) {
175            notificationNames.add(notif.getName());
176        }
177
178        CoreSession session = doc.getCoreSession();
179
180        if (session != null) {
181            for (DocumentModel parent : session.getParentDocuments(doc.getRef())) {
182                for (Notification notif : ns.getNotificationsForSubscriptions(parent.getType())) {
183                    notificationNames.add(notif.getName());
184                }
185            }
186        }
187
188        // add subscriptions to every relevant notification
189        for (String name : notificationNames) {
190            addSubscription(username, name);
191        }
192
193    }
194
195    /**
196     * Remove a subscription to a notification for a given user.
197     *
198     * @param username
199     * @param notification
200     */
201    public void removeUserNotificationSubscription(String username, String notification) {
202        Map<String, Set<String>> map = getNotificationMap();
203        if (map.containsKey(notification)) {
204            map.get(notification).remove(username);
205        }
206        setNotificationMap(map);
207    }
208
209    /**
210     * Copy the subscriptions of the current doc to the targetted document.
211     *
212     * @param targetDoc
213     */
214    public void copySubscriptionsTo(DocumentModel targetDoc) {
215        if (!targetDoc.hasFacet(NOTIFIABLE_FACET)) {
216            targetDoc.addFacet(NOTIFIABLE_FACET);
217        }
218        targetDoc.setPropertyValue(NOTIF_PROPERTY, doc.getPropertyValue(NOTIF_PROPERTY));
219    }
220
221}