001/*
002 *  Copyright 2014 Anyware Services
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 */
016package org.ametys.plugins.pagesubscription;
017
018import java.util.ArrayList;
019import java.util.HashMap;
020import java.util.HashSet;
021import java.util.List;
022import java.util.Map;
023import java.util.Set;
024import java.util.regex.Pattern;
025
026import javax.jcr.Node;
027import javax.jcr.RepositoryException;
028import javax.jcr.Value;
029
030import org.apache.avalon.framework.parameters.Parameters;
031import org.apache.avalon.framework.service.ServiceException;
032import org.apache.avalon.framework.service.ServiceManager;
033import org.apache.cocoon.ProcessingException;
034import org.apache.cocoon.environment.ObjectModelHelper;
035import org.apache.cocoon.environment.Redirector;
036import org.apache.cocoon.environment.Request;
037import org.apache.cocoon.environment.SourceResolver;
038import org.apache.commons.lang.StringUtils;
039
040import org.ametys.core.cocoon.ActionResultGenerator;
041import org.ametys.core.observation.AbstractNotifierAction;
042import org.ametys.core.observation.Event;
043import org.ametys.core.util.mail.SendMailHelper;
044import org.ametys.plugins.repository.AmetysObjectResolver;
045import org.ametys.plugins.repository.RepositoryConstants;
046import org.ametys.plugins.repository.jcr.JCRAmetysObject;
047import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector;
048import org.ametys.runtime.i18n.I18nizableText;
049import org.ametys.web.ObservationConstants;
050import org.ametys.web.repository.page.Page;
051import org.ametys.web.repository.page.jcr.DefaultPage;
052
053/**
054 * Action to subscribe to a page
055 */
056public class PageSubscribeAction extends AbstractNotifierAction
057{
058    /** Constant for the attachment node name. */
059    public static final String SUBSCRIBERS_PROPERTY_NAME = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":subscriberMails";
060    
061    /** The pattern to check emails */
062    protected static final Pattern EMAIL_VALIDATOR = SendMailHelper.EMAIL_VALIDATION;
063    
064    /** The pattern to check text input */
065    protected static final Pattern TEXT_VALIDATOR = Pattern.compile("^\\s*$");
066    
067    /** The ametys resolver */
068    protected AmetysObjectResolver _resolver;
069    
070    @Override
071    public void service(ServiceManager smanager) throws ServiceException
072    {
073        super.service(smanager);
074        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
075    }
076    
077    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
078    {
079        Map<String, Object> result = new HashMap<>();
080        List<I18nizableText> errors = new ArrayList<>();
081        
082        Request request = ObjectModelHelper.getRequest(objectModel);
083        String action = request.getParameter("page-subscribe-action");
084        result.put("action", action);
085        
086        String email = request.getParameter("email");
087        
088        if (email == null || !EMAIL_VALIDATOR.matcher(StringUtils.trimToEmpty(email.toLowerCase())).matches() || TEXT_VALIDATOR.matcher(StringUtils.trimToEmpty(email)).matches())
089        {
090            errors.add(new I18nizableText("plugin.page-subscription", "PLUGINS_PAGE_SUBSCRIBE_FORM_MAIL_ERROR_MAILBY"));
091        }
092        else
093        {
094            result.put("email", email);
095        }
096        
097        String pageId = request.getParameter("page-id");
098        if (pageId == null) 
099        {
100            throw new IllegalArgumentException("Unable to subscribe or unsubscribe to the page: cannot determine the current page");
101        }
102        else
103        {
104            // Force default workspace
105            String currentWorspace = RequestAttributeWorkspaceSelector.getForcedWorkspace(request);
106            RequestAttributeWorkspaceSelector.setForcedWorkspace(request, "default");
107            
108            try
109            {
110                Page page = _resolver.resolveById(pageId);
111                
112                if (page == null)
113                {
114                    throw new IllegalArgumentException("Unable to subscribe or unsubscribe to the page: the page of id " + pageId + " is unknown");
115                }
116                
117                if (page instanceof JCRAmetysObject)
118                {
119                    Set<String> subscribers = _getSubscribers((JCRAmetysObject) page);
120                    
121                    if (action.equals("subscribe"))
122                    {
123                        subscribers.add(email);
124                    }
125                    else if (action.equals("unsubscribe"))
126                    {
127                        subscribers.remove(email);
128                    }
129                    
130                    _setSubscribers((DefaultPage) page, subscribers);
131                    
132                    // Notify observers that page data has been changed.
133                    Map<String, Object> eventParams = new HashMap<>();
134                    eventParams.put(ObservationConstants.ARGS_PAGE, page);
135                    eventParams.put(ObservationConstants.ARGS_PAGE_ID, page.getId());
136                    _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_UPDATED, _getCurrentUser(), eventParams));
137                }
138                else
139                {
140                    throw new ProcessingException("Unable to subscribe or unsubscribe to the page: the page is not subscribable");
141                }
142            }
143            finally
144            {
145                RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWorspace);
146            }
147            
148        }
149         
150        if (!errors.isEmpty())
151        {
152            result.put("error", errors);
153        }
154        request.setAttribute(ActionResultGenerator.MAP_REQUEST_ATTR, result);
155        
156        return EMPTY_MAP;
157    }
158    
159    private Set<String> _getSubscribers (JCRAmetysObject page)
160    {
161        Set<String> subscriberMails = new HashSet<>();
162        
163        try
164        {
165            Node node = page.getNode();
166            if (node.hasProperty(SUBSCRIBERS_PROPERTY_NAME))
167            {
168                Value[] values = node.getProperty(SUBSCRIBERS_PROPERTY_NAME).getValues();
169                for (Value value : values)
170                {
171                    subscriberMails.add(value.getString());
172                }
173            }
174        }
175        catch (RepositoryException e)
176        {
177            getLogger().error("Unable to retrieve subscribers for page of id '" + page.getId() + "'", e);
178        }
179        
180        return subscriberMails;
181    }
182    
183    private void _setSubscribers (DefaultPage page, Set<String> subscribers)
184    {
185        try
186        {
187            Node node = page.getNode();
188            
189            String[] subscribersAsStringArray = subscribers.toArray(new String[subscribers.size()]);
190            node.setProperty(SUBSCRIBERS_PROPERTY_NAME, subscribersAsStringArray);
191            page.saveChanges();
192        }
193        catch (RepositoryException e)
194        {
195            getLogger().error("Unable to set subscribers for page of id '" + page.getId() + "'", e);
196        }
197    }
198
199}