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}