001/*
002 *  Copyright 2010 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.web.content.consistency;
017
018import java.util.HashSet;
019import java.util.List;
020import java.util.Map;
021import java.util.Set;
022
023import org.apache.avalon.framework.service.ServiceException;
024import org.apache.avalon.framework.service.ServiceManager;
025import org.apache.cocoon.components.ContextHelper;
026import org.apache.cocoon.environment.Request;
027import org.apache.commons.collections4.ListUtils;
028import org.quartz.JobDataMap;
029import org.quartz.JobExecutionContext;
030
031import org.ametys.cms.content.consistency.ContentConsistencyManager.ConsistenciesReport;
032import org.ametys.core.cache.AbstractCacheManager;
033import org.ametys.core.user.UserIdentity;
034import org.ametys.core.util.JSONUtils;
035import org.ametys.plugins.core.schedule.Scheduler;
036import org.ametys.plugins.repository.AmetysObjectIterable;
037import org.ametys.runtime.config.Config;
038import org.ametys.runtime.i18n.I18nizableText;
039import org.ametys.web.repository.site.Site;
040import org.ametys.web.repository.site.SiteManager;
041
042/**
043 * Content consistency engine: generate consistency information for all contents.
044 * Sends a report e-mail if there are inconsistencies.
045 */
046public class CheckContentConsistencySchedulable extends org.ametys.cms.content.consistency.CheckContentConsistencySchedulable
047{
048    /** The key for the name of the site containing the contents to check */
049    public static final String SITE_NAME_KEY = "siteName";
050    /** Right to receive the consistency report of content without site */
051    public static final String RECEIVE_REPORT_NO_SITE_RIGHT = "Web_Rights_ReceiveConsistencyReport_No_Site";
052    
053    private static final String __JOBDATAMAP_SITE_NAME_KEY = Scheduler.PARAM_VALUES_PREFIX + SITE_NAME_KEY;
054    
055    /** The utils for JSON */
056    protected JSONUtils _jsonUtils;
057    /** The site manager. */
058    protected SiteManager _siteManager;
059    /** Web version of the content consistency manager */
060    private ContentConsistencyManager _webContentConsistencyManager;
061    /** The cache manager */
062    private AbstractCacheManager _cacheManager;
063    
064    @Override
065    public void service(ServiceManager manager) throws ServiceException
066    {
067        super.service(manager);
068        _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE);
069        _jsonUtils = (JSONUtils) manager.lookup(JSONUtils.ROLE);
070        _webContentConsistencyManager = (ContentConsistencyManager) manager.lookup(org.ametys.cms.content.consistency.ContentConsistencyManager.ROLE);
071        _cacheManager = (AbstractCacheManager) manager.lookup(AbstractCacheManager.ROLE);
072    }
073    
074    @Override
075    public void execute(JobExecutionContext context) throws Exception
076    {
077        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
078        String siteAsMap = (String) jobDataMap.get(__JOBDATAMAP_SITE_NAME_KEY);
079        List<String> siteNames = _getSiteName(siteAsMap);
080        
081        // Start by checking if there is any content without site with error.
082        ConsistenciesReport noSiteReport = _webContentConsistencyManager.checkContentsWithoutSite();
083        
084        try (AmetysObjectIterable<Site> sites = _siteManager.getSites())
085        {
086            // Iterate over sites to checks requested sites, or all of them if no requested site
087            for (Site currentSite : sites)
088            {
089                String currentSiteName = currentSite.getName();
090                
091                if (siteNames.isEmpty() || siteNames.contains(currentSiteName))
092                {
093                    // Make the site "current"
094                    Request request = ContextHelper.getRequest(_context);
095                    request.setAttribute("siteName", currentSiteName);
096                    
097                    // Check content for current site
098                    ConsistenciesReport report = _webContentConsistencyManager.checkContentsFromSite(currentSiteName);
099                    
100                    // One of the report is not empty, we need to compute user to notify
101                    if (!report.isEmpty() || !noSiteReport.isEmpty())
102                    {
103                        Set<UserIdentity> siteUsers = getUsersToNotify();
104                        // Notification right for content without site are assigned by site
105                        // (because there is no other way to assign a right)
106                        // use the fake context to determine the user to notify
107                        Set<UserIdentity> noSiteUsers = _rightManager.getAllowedUsers(RECEIVE_REPORT_NO_SITE_RIGHT, "/cms").resolveAllowedUsers(Config.getInstance().getValue("runtime.mail.massive.sending"));
108                        
109                        Set<UserIdentity> intersection = new HashSet<>(siteUsers);
110                        intersection.retainAll(noSiteUsers);
111                        
112                        siteUsers.removeAll(intersection);
113                        noSiteUsers.removeAll(intersection);
114                        
115                        // Include only the site contents
116                        if (!siteUsers.isEmpty() && !report.isEmpty())
117                        {
118                            _sendEmail(report, siteUsers);
119                        }
120                        
121                        // Include only content without site
122                        if (!noSiteUsers.isEmpty() && !noSiteReport.isEmpty())
123                        {
124                            _sendEmail(noSiteReport, noSiteUsers);
125                        }
126                        
127                        // Include content from site and outside
128                        if (!intersection.isEmpty())
129                        {
130                            ConsistenciesReport mergedReport = new ConsistenciesReport(
131                                    ListUtils.sum(report.results(), noSiteReport.results()),
132                                    ListUtils.sum(report.unchecked(), noSiteReport.unchecked()));
133                            _sendEmail(mergedReport, intersection);
134                        }
135                    }
136                }
137                
138                // As looping on sites, we should simulate several request
139                // Otherwise the email builder may cache a skin resolution
140                _cacheManager.resetAllNonDispatchableRequestCaches();
141            }
142        }
143        
144    }
145
146    @Override
147    protected I18nizableText _getMailSubject(ConsistenciesReport report)
148    {
149        return new I18nizableText("plugin.web", "PLUGINS_WEB_GLOBAL_CONTENT_CONSISTENCY_MAIL_SUBJECT", List.of(_getSite().getTitle()));
150    }
151    
152    private Site _getSite()
153    {
154        String siteName = _getCurrentSiteName();
155        return _siteManager.getSite(siteName);
156    }
157    
158    @Override
159    protected Map<String, String> _getEmailParams(List<String> errorResults, List<String> unchecked)
160    {
161        Map<String, String> params = super._getEmailParams(errorResults, unchecked);
162        
163        String siteName = _getCurrentSiteName();
164        
165        StringBuilder url = new StringBuilder(_baseUrl);
166        if (!_baseUrl.endsWith("/"))
167        {
168            url.append('/');
169        }
170        url.append(siteName).append("/index.html?uitool=uitool-global-consistency");
171        
172        params.put("url", url.toString());
173        
174        Site site = _siteManager.getSite(siteName);
175        String siteTitle = site.getTitle();
176        
177        params.put("siteTitle", siteTitle);
178        params.put("siteName", siteName);
179        
180        return params;
181    }
182    
183    /**
184     * Retrieves the names of the sites from given parameters
185     * @param siteAsMap JSON {@link Map} containing the sites names
186     * @return the names of the sites
187     */
188    @SuppressWarnings("unchecked")
189    protected List<String> _getSiteName(String siteAsMap)
190    {
191        Map<String, Object> mapSite = _jsonUtils.convertJsonToMap(siteAsMap);
192        return mapSite.containsKey("sites") ? (List<String>) mapSite.get("sites") : List.of();
193    }
194    
195    /**
196     * Retrieves the current site name
197     * @return the current site name
198     */
199    protected String _getCurrentSiteName()
200    {
201        Request request = ContextHelper.getRequest(_context);
202        return (String) request.getAttribute("siteName");
203    }
204}