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    protected static final String NOTIF_PROPERTY = "notif:notifications";
048
049    protected static final String NOTIF_SUBSCRIBERSKEY = "subscribers";
050
051    protected static final String NOTIF_NAMEKEY = "name";
052
053    protected 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    protected 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<>();
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<>();
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    protected void setNotificationMap(Map<String, Set<String>> map) {
100        List<Map<String, Serializable>> props = new ArrayList<>();
101        for (Entry<String, Set<String>> entry : map.entrySet()) {
102            Set<String> subscribers = entry.getValue();
103            if (!subscribers.isEmpty()) {
104                Map<String, Serializable> propMap = new HashMap<>();
105                propMap.put(NOTIF_NAMEKEY, entry.getKey());
106                propMap.put(NOTIF_SUBSCRIBERSKEY, new ArrayList<>(subscribers));
107                props.add(propMap);
108            }
109        }
110
111        if (!props.isEmpty()) {
112            if (!doc.hasFacet(SubscriptionAdapter.NOTIFIABLE_FACET)) {
113                doc.addFacet(SubscriptionAdapter.NOTIFIABLE_FACET);
114            }
115        }
116
117        doc.setPropertyValue(NOTIF_PROPERTY, (Serializable) props);
118    }
119
120    /**
121     * Clears the notification in the document's property.
122     *
123     * @since 10.2
124     */
125    protected void clearNotification() {
126        doc.setPropertyValue(NOTIF_PROPERTY, null);
127    }
128
129    /**
130     * Return the list of subscribers name for a given notification.
131     */
132    public List<String> getNotificationSubscribers(String notification) {
133        Set<String> subscribers = getNotificationMap().get(notification);
134        return subscribers != null ? new ArrayList<>(subscribers) : Collections.emptyList();
135    }
136
137    /**
138     * Return the list of of subscriptions for a given user
139     */
140    public List<String> getUserSubscriptions(String username) {
141        List<String> result = new ArrayList<>();
142        for (Entry<String, Set<String>> entry : getNotificationMap().entrySet()) {
143            if (entry.getValue().contains(username)) {
144                result.add(entry.getKey());
145            }
146        }
147        return result;
148    }
149
150    /**
151     * Add a subscription to a notification for a given user.
152     */
153    public void addSubscription(String username, String notification) {
154        Map<String, Set<String>> notificationMap = getNotificationMap();
155        if (!notificationMap.containsKey(notification)) {
156            notificationMap.put(notification, new HashSet<>());
157        }
158        notificationMap.get(notification).add(username);
159        setNotificationMap(notificationMap);
160    }
161
162    /**
163     * Add a subscription to all notification for a given user
164     */
165    public void addSubscriptionsToAll(String username) {
166
167        Set<String> notificationNames = new HashSet<>();
168
169        NotificationManager ns = Framework.getService(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    public void removeUserNotificationSubscription(String username, String notification) {
196        Map<String, Set<String>> map = getNotificationMap();
197        if (map.containsKey(notification)) {
198            map.get(notification).remove(username);
199        }
200        setNotificationMap(map);
201    }
202
203    /**
204     * Copy the subscriptions of the current doc to the targetted document.
205     */
206    public void copySubscriptionsTo(DocumentModel targetDoc) {
207        if (!targetDoc.hasFacet(NOTIFIABLE_FACET)) {
208            targetDoc.addFacet(NOTIFIABLE_FACET);
209        }
210        targetDoc.setPropertyValue(NOTIF_PROPERTY, doc.getPropertyValue(NOTIF_PROPERTY));
211    }
212
213}