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