001/*
002 *  Copyright 2018 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.ugc.actions;
017
018import java.io.IOException;
019import java.util.ArrayList;
020import java.util.HashMap;
021import java.util.List;
022import java.util.Map;
023
024import org.apache.avalon.framework.context.Context;
025import org.apache.avalon.framework.context.ContextException;
026import org.apache.avalon.framework.context.Contextualizable;
027import org.apache.avalon.framework.parameters.Parameters;
028import org.apache.avalon.framework.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030import org.apache.cocoon.acting.ServiceableAction;
031import org.apache.cocoon.components.ContextHelper;
032import org.apache.cocoon.environment.ObjectModelHelper;
033import org.apache.cocoon.environment.Redirector;
034import org.apache.cocoon.environment.Request;
035import org.apache.cocoon.environment.SourceResolver;
036import org.apache.commons.lang.StringUtils;
037
038import org.ametys.cms.contenttype.ContentType;
039import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
040import org.ametys.cms.contenttype.ContentTypesHelper;
041import org.ametys.cms.repository.Content;
042import org.ametys.cms.repository.WorkflowAwareContent;
043import org.ametys.core.captcha.CaptchaHelper;
044import org.ametys.core.cocoon.ActionResultGenerator;
045import org.ametys.core.ui.mail.StandardMailBodyHelper;
046import org.ametys.core.ui.mail.StandardMailBodyHelper.MailBodyBuilder;
047import org.ametys.core.user.CurrentUserProvider;
048import org.ametys.core.user.User;
049import org.ametys.core.user.UserIdentity;
050import org.ametys.core.user.UserManager;
051import org.ametys.core.util.I18nUtils;
052import org.ametys.core.util.JSONUtils;
053import org.ametys.core.util.URIUtils;
054import org.ametys.core.util.mail.SendMailHelper;
055import org.ametys.plugins.repository.AmetysObjectResolver;
056import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder;
057import org.ametys.plugins.ugc.UGCConstants;
058import org.ametys.runtime.config.Config;
059import org.ametys.runtime.i18n.I18nizableText;
060import org.ametys.web.WebHelper;
061import org.ametys.web.cache.PageHelper;
062import org.ametys.web.content.FOContentCreationHelper;
063import org.ametys.web.repository.content.WebContent;
064import org.ametys.web.repository.page.Page;
065import org.ametys.web.repository.page.ZoneItem;
066import org.ametys.web.repository.site.Site;
067
068import com.google.common.collect.ArrayListMultimap;
069import com.google.common.collect.Multimap;
070
071import jakarta.mail.MessagingException;
072
073/**
074 * Create content action
075 */
076public class ProposeContentAction extends ServiceableAction implements Contextualizable
077{  
078    private static final int _INITIAL_ACTION_ID = 1111;
079    
080    /** Ametys object resolver. */
081    protected AmetysObjectResolver _resolver;
082    
083    /** The content type extension point */
084    protected ContentTypeExtensionPoint _cTypeEP;
085    
086    /** The content type helper */
087    protected ContentTypesHelper _contentTypeHelper;
088    
089    /** The JSON Utils */
090    protected JSONUtils _jsonUtils;
091    
092    /** The current user provider */
093    protected CurrentUserProvider _currentUserProvider;
094    
095    /** The I18n utils */
096    protected I18nUtils _i18nUtils;
097
098    /** The user manager */
099    protected UserManager _userManager;
100
101    /** Helper for FO content creation */
102    protected FOContentCreationHelper _foContentCreationHelper;
103    
104    /** The page helper */
105    protected PageHelper _pageHelper;
106
107    private Context _context;
108    
109    @Override
110    public void service(ServiceManager serviceManager) throws ServiceException
111    {
112        super.service(serviceManager);
113        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
114        _cTypeEP = (ContentTypeExtensionPoint) serviceManager.lookup(ContentTypeExtensionPoint.ROLE);
115        _contentTypeHelper = (ContentTypesHelper) serviceManager.lookup(ContentTypesHelper.ROLE);
116        _jsonUtils = (JSONUtils) serviceManager.lookup(JSONUtils.ROLE);
117        _currentUserProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE);
118        _i18nUtils = (I18nUtils) serviceManager.lookup(I18nUtils.ROLE);
119        _userManager = (UserManager) serviceManager.lookup(UserManager.ROLE);
120        _foContentCreationHelper = (FOContentCreationHelper) serviceManager.lookup(FOContentCreationHelper.ROLE);
121        _pageHelper = (PageHelper) serviceManager.lookup(PageHelper.ROLE);
122    }
123    
124    public void contextualize(Context context) throws ContextException
125    {
126        _context = context;
127    }
128    
129    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
130    {
131        Map<String, Object> result = new HashMap<>();
132        
133        Multimap<String, I18nizableText> errors = ArrayListMultimap.create();
134
135        Request request = ObjectModelHelper.getRequest(objectModel);
136        
137        String zoneId = request.getParameter("zoneId");
138        ZoneItem zoneItem = _resolver.resolveById(zoneId);
139        Page page = zoneItem.getParent().getParent().getParent().getParent();
140        
141        ModelAwareDataHolder serviceParameters = zoneItem.getServiceParameters();
142        String workflowName = serviceParameters.getValue("workflow");
143        String contentTypeId = serviceParameters.getValue("content-type");
144        UserIdentity[] adminUsers = serviceParameters.getValue("users");
145        
146        String siteName = request.getParameter("site");
147        String language = request.getParameter("lang");
148        
149        ContentType ugcMixin = _cTypeEP.getExtension(UGCConstants.UGC_MIXIN_TYPE);
150        
151        ContentType contentType = _cTypeEP.getExtension(contentTypeId);
152        Map<String, Object> values = _foContentCreationHelper.getAndValidateFormValues(request, contentType, "main", errors);
153        values.putAll(_foContentCreationHelper.getAndValidateFormValues(request, ugcMixin, "main", errors));
154        
155        // Check captcha if needed
156        if (_pageHelper.isCaptchaRequired(page))
157        {   
158            String captchaValue = request.getParameter("captcha");
159            String captchaKey = request.getParameter("captcha-key");
160
161            // Validate the input
162            if (!CaptchaHelper.checkAndInvalidate(captchaKey, captchaValue))
163            {
164                errors.put("captcha", new I18nizableText("plugin.ugc", "PLUGINS_UGC_SERVICE_FORM_ERROR_INVALID_CAPTCHA"));
165            }
166        }
167        
168        // If there are no errors
169        if (errors.isEmpty())
170        {            
171            String title = request.getParameter("title");
172            
173            // we add a prefix to the title for the content name to prevent issue with numeric title
174            String prefix = StringUtils.substringAfterLast(contentTypeId, ".");
175            
176            result = _foContentCreationHelper.createAndEditContent(_INITIAL_ACTION_ID, new String[] {contentTypeId}, new String[] {UGCConstants.UGC_MIXIN_TYPE}, siteName, prefix + "-" + title, title, language, values, workflowName, null, new HashMap<>());
177            
178            if (result.containsKey(Content.class.getName()))
179            {
180                result.put("success", true);
181
182                // Send mail
183                WorkflowAwareContent content = (WorkflowAwareContent) result.get(Content.class.getName());
184                _sendMail(adminUsers, content);
185            }
186            else
187            {
188                result.put("success", false);
189            }
190        }
191        else
192        {
193            result.put("success", false);
194            result.put("errors", _jsonUtils.convertObjectToJson(errors.asMap()));
195        }
196        
197        request.setAttribute(ActionResultGenerator.MAP_REQUEST_ATTR, result);
198        return EMPTY_MAP;
199    }
200
201    /**
202     * Send mail to inform that a content is created
203     * @param adminUsers the admin users
204     * @param content the created content
205     */
206    protected void _sendMail(UserIdentity[] adminUsers, WorkflowAwareContent content)
207    {
208        if (adminUsers != null)
209        {
210            String creatorFullName = null;
211            UserIdentity creatorIdentity = _currentUserProvider.getUser();
212            if (creatorIdentity != null)
213            {
214                User creator = _userManager.getUser(creatorIdentity);
215                creatorFullName = creator.getFullName();
216            }
217            else
218            {
219                creatorFullName = content.getValue(UGCConstants.ATTRIBUTE_PATH_UGC_AUTHOR);
220            }
221            
222            Site site = _getSite(content);
223            
224            try
225            {
226                String htmlBody = _getI18nBody(content, creatorFullName, site);
227                
228                I18nizableText i18nSubject = _getI18nSubject(site);
229                String subject = _i18nUtils.translate(i18nSubject);
230                
231                for (UserIdentity adminUser : adminUsers)
232                {
233                    User user = _userManager.getUser(adminUser);
234                    if (user == null)
235                    {
236                        getLogger().error("Can not send an e-mail for the content creation to the unexisting user " + adminUser);
237                    }
238                    else
239                    {
240                        String email = user.getEmail();
241                        try
242                        {
243                            SendMailHelper.newMail()
244                                          .withSubject(subject)
245                                          .withHTMLBody(htmlBody)
246                                          .withRecipient(email)
247                                          .sendMail();
248                        }
249                        catch (MessagingException | IOException e)
250                        {
251                            getLogger().warn("Could not send an e-mail for the content creation to " + email, e);
252                        }
253                    }
254                }
255            }
256            catch (IOException e)
257            {
258                getLogger().warn("Fail to build HTML body for the content creation", e);
259            }
260        }
261    }
262
263    /**
264     * Get the i18n body
265     * @param content the content
266     * @param creatorFullName the creator full name
267     * @param site the site
268     * @return The i18n body
269     * @throws IOException if failed to build html body
270     */
271    protected String _getI18nBody(WorkflowAwareContent content, String creatorFullName, Site site) throws IOException
272    {
273        List<String> parametersBody = new ArrayList<>();
274        parametersBody.add(content.getTitle());
275        parametersBody.add(creatorFullName);
276        
277        MailBodyBuilder bodyBuilder = StandardMailBodyHelper.newHTMLBody()
278            .withTitle(_getI18nSubject(site));
279            
280        if (site != null)
281        {
282            parametersBody.add(_getContentUri(content));
283            parametersBody.add(site.getTitle());
284            parametersBody.add(site.getUrl());
285            
286            bodyBuilder.withMessage(new I18nizableText("plugin.ugc", "PLUGINS_UGC_SERVICE_FORM_CREATED_CONTENT_MAIL_BODY", parametersBody))
287                .withLink(_getContentUri(content), new I18nizableText("plugin.ugc", "PLUGINS_UGC_SERVICE_FORM_CREATED_CONTENT_MAIL_BODY_CONTENT_LINK"));
288        }
289        else
290        {
291            bodyBuilder.withMessage(new I18nizableText("plugin.ugc", "PLUGINS_UGC_SERVICE_FORM_CREATED_CONTENT_MAIL_BODY_NO_SITE", parametersBody));
292        }
293        return bodyBuilder.build();
294    }
295
296    /**
297     * Get the i18n subject
298     * @param site the site
299     * @return The i18n subject
300     */
301    protected I18nizableText _getI18nSubject(Site site)
302    {
303        I18nizableText i18nSubject = null;
304        if (site != null)
305        {
306            List<String> parametersSubject = new ArrayList<>();
307            parametersSubject.add(site.getTitle());
308            i18nSubject = new I18nizableText("plugin.ugc", "PLUGINS_UGC_SERVICE_FORM_CREATED_CONTENT_MAIL_SUBJECT", parametersSubject);
309        }
310        else
311        {
312            i18nSubject = new I18nizableText("plugin.ugc", "PLUGINS_UGC_SERVICE_FORM_CREATED_CONTENT_MAIL_SUBJECT_NO_SITE");
313        }
314        return i18nSubject;
315    }
316    
317    /**
318     * Get the site name
319     * @param content The content
320     * @return the site name
321     */
322    protected Site _getSite(WorkflowAwareContent content)
323    {
324        if (content instanceof WebContent)
325        {
326            return ((WebContent) content).getSite();
327        }
328        
329        return null;
330    }
331    
332    
333    /**
334     * Get the content uri
335     * @param content the content
336     * @return the content uri
337     */
338    protected String _getContentUri(WorkflowAwareContent content)
339    {
340        Request request = ContextHelper.getRequest(_context);
341        String siteName = WebHelper.getSiteName(request, content);
342        
343        return _getRequestUri() + "/" + siteName + "/index.html?uitool=uitool-content,id:%27" + URIUtils.encodeParameter(content.getId()) + "%27";
344    }
345    
346    /**
347     * Get the request URI.
348     * @return the full request URI.
349     */
350    protected String _getRequestUri()
351    {
352        return StringUtils.stripEnd(StringUtils.removeEndIgnoreCase(Config.getInstance().getValue("cms.url"), "index.html"), "/");
353    }
354}