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.schedule.progression.ContainerProgressionTracker; 033import org.ametys.core.schedule.progression.SimpleProgressionTracker; 034import org.ametys.core.user.UserIdentity; 035import org.ametys.core.util.JSONUtils; 036import org.ametys.plugins.core.schedule.Scheduler; 037import org.ametys.plugins.repository.AmetysObjectIterable; 038import org.ametys.runtime.config.Config; 039import org.ametys.runtime.i18n.I18nizableText; 040import org.ametys.runtime.i18n.I18nizableTextParameter; 041import org.ametys.web.WebHelper; 042import org.ametys.web.repository.site.Site; 043import org.ametys.web.repository.site.SiteManager; 044 045/** 046 * Content consistency engine: generate consistency information for all contents. 047 * Sends a report e-mail if there are inconsistencies. 048 */ 049public class CheckContentConsistencySchedulable extends org.ametys.cms.content.consistency.CheckContentConsistencySchedulable 050{ 051 /** The key for the name of the site containing the contents to check */ 052 public static final String SITE_NAME_KEY = "siteName"; 053 /** Right to receive the consistency report of content without site */ 054 public static final String RECEIVE_REPORT_NO_SITE_RIGHT = "Web_Rights_ReceiveConsistencyReport_No_Site"; 055 056 private static final String __WITH_SITE_REPORT = "with-site-report"; 057 private static final String __NO_SITE_REPORT = "no-site-report"; 058 private static final String __JOBDATAMAP_SITE_NAME_KEY = Scheduler.PARAM_VALUES_PREFIX + SITE_NAME_KEY; 059 060 /** The utils for JSON */ 061 protected JSONUtils _jsonUtils; 062 /** The site manager. */ 063 protected SiteManager _siteManager; 064 /** Web version of the content consistency manager */ 065 private ContentConsistencyManager _webContentConsistencyManager; 066 067 @Override 068 public void service(ServiceManager manager) throws ServiceException 069 { 070 super.service(manager); 071 _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE); 072 _jsonUtils = (JSONUtils) manager.lookup(JSONUtils.ROLE); 073 _webContentConsistencyManager = (ContentConsistencyManager) manager.lookup(org.ametys.cms.content.consistency.ContentConsistencyManager.ROLE); 074 } 075 076 @Override 077 public void execute(JobExecutionContext context, ContainerProgressionTracker progressionTracker) throws Exception 078 { 079 JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); 080 String siteAsMap = (String) jobDataMap.get(__JOBDATAMAP_SITE_NAME_KEY); 081 List<String> siteNames = _getSiteName(siteAsMap); 082 083 ContainerProgressionTracker noSiteReportStep = progressionTracker.addContainerStep(__NO_SITE_REPORT, new I18nizableText("plugin.web", "PLUGINS_WEB_GLOBAL_CONTENT_CONSISTENCY_REPORT_STEP_NO_SITE_LABEL")); 084 noSiteReportStep.addSimpleStep(CHECK_CONTENTS, new I18nizableText("plugin.web", "PLUGINS_WEB_GLOBAL_CONTENT_CONSISTENCY_CHECK_CONTENTS_LABEL")); 085 noSiteReportStep.addSimpleStep(REMOVE_OUTDATED_RESULT, new I18nizableText("plugin.web", "PLUGINS_WEB_GLOBAL_CONTENT_CONSISTENCY_REMOVE_OUTDATED_RESULT_LABEL")); 086 ContainerProgressionTracker withSitesReportStep = progressionTracker.addContainerStep(__WITH_SITE_REPORT, new I18nizableText("plugin.web", "PLUGINS_WEB_GLOBAL_CONTENT_CONSISTENCY_REPORT_STEP_WITH_SITE_LABEL")); 087 088 // Start by checking if there is any content without site with error. 089 ConsistenciesReport noSiteReport = _webContentConsistencyManager.checkContentsWithoutSite(noSiteReportStep); 090 091 try (AmetysObjectIterable<Site> sites = _siteManager.getSites()) 092 { 093 for (Site currentSite : sites) 094 { 095 String currentStepId = __WITH_SITE_REPORT + "_" + currentSite.getId(); 096 Map<String, I18nizableTextParameter> param = Map.of("site", new I18nizableText(currentSite.getTitle())); 097 098 // Task weight was chosen arbitrarily because creating the report is way longer than the other tasks 099 ContainerProgressionTracker siteStep = withSitesReportStep.addContainerStep(currentStepId, new I18nizableText("plugin.web", "PLUGINS_WEB_GLOBAL_CONTENT_CONSISTENCY_REPORT_STEP_WITH_SITE_CONTENTS_LABEL", param)); 100 ContainerProgressionTracker reportStep = siteStep.addContainerStep(__CHECK_CONTENT_CONSISTENCY_REPORT, new I18nizableText("plugin.web", "PLUGINS_WEB_GLOBAL_CONTENT_CONSISTENCY_REPORT_STEP_LABEL"), 10); 101 reportStep.addSimpleStep(CHECK_CONTENTS, new I18nizableText("plugin.web", "PLUGINS_WEB_GLOBAL_CONTENT_CONSISTENCY_CHECK_CONTENTS_LABEL")); 102 reportStep.addSimpleStep(REMOVE_OUTDATED_RESULT, new I18nizableText("plugin.web", "PLUGINS_WEB_GLOBAL_CONTENT_CONSISTENCY_REMOVE_OUTDATED_RESULT_LABEL")); 103 siteStep.addSimpleStep(__CHECK_CONTENT_CONSISTENCY_NOTIFICATION, new I18nizableText("plugin.web", "PLUGINS_WEB_GLOBAL_CONTENT_CONSISTENCY_NOTIFICATION_STEPLABEL")); 104 } 105 // Iterate over sites to checks requested sites, or all of them if no requested site 106 for (Site currentSite : sites) 107 { 108 String currentStepId = __WITH_SITE_REPORT + "_" + currentSite.getId(); 109 String currentSiteName = currentSite.getName(); 110 111 if (siteNames.isEmpty() || siteNames.contains(currentSiteName)) 112 { 113 // Make the site "current" 114 Request request = ContextHelper.getRequest(_context); 115 request.setAttribute("siteName", currentSiteName); 116 117 // Check content for current site 118 ContainerProgressionTracker currentSiteStep = (ContainerProgressionTracker) withSitesReportStep.getStep(currentStepId); 119 ConsistenciesReport report = _webContentConsistencyManager.checkContentsFromSite(currentSiteName, currentSiteStep.getStep(__CHECK_CONTENT_CONSISTENCY_REPORT)); 120 121 SimpleProgressionTracker notificationProgressionTracker = currentSiteStep.getStep(__CHECK_CONTENT_CONSISTENCY_NOTIFICATION); 122 // One of the report is not empty, we need to compute user to notify 123 if (!report.isEmpty() || !noSiteReport.isEmpty()) 124 { 125 Set<UserIdentity> siteUsers = getUsersToNotify(); 126 // Notification right for content without site are assigned by site 127 // (because there is no other way to assign a right) 128 // use the fake context to determine the user to notify 129 Set<UserIdentity> noSiteUsers = _rightManager.getAllowedUsers(RECEIVE_REPORT_NO_SITE_RIGHT, "/cms").resolveAllowedUsers(Config.getInstance().getValue("runtime.mail.massive.sending")); 130 131 Set<UserIdentity> intersection = new HashSet<>(siteUsers); 132 intersection.retainAll(noSiteUsers); 133 134 siteUsers.removeAll(intersection); 135 noSiteUsers.removeAll(intersection); 136 137 notificationProgressionTracker.setSize(siteUsers.size() + noSiteUsers.size() + intersection.size()); 138 139 // Include only the site contents 140 if (!siteUsers.isEmpty() && !report.isEmpty()) 141 { 142 _sendEmail(report, siteUsers, notificationProgressionTracker); 143 } 144 145 // Include only content without site 146 if (!noSiteUsers.isEmpty() && !noSiteReport.isEmpty()) 147 { 148 _sendEmail(noSiteReport, noSiteUsers, notificationProgressionTracker); 149 } 150 151 // Include content from site and outside 152 if (!intersection.isEmpty()) 153 { 154 ConsistenciesReport mergedReport = new ConsistenciesReport( 155 ListUtils.sum(report.results(), noSiteReport.results()), 156 ListUtils.sum(report.unchecked(), noSiteReport.unchecked())); 157 _sendEmail(mergedReport, intersection, notificationProgressionTracker); 158 } 159 } 160 else 161 { 162 notificationProgressionTracker.setSize(0); 163 } 164 } 165 } 166 } 167 168 } 169 170 @Override 171 protected I18nizableText _getMailSubject(ConsistenciesReport report) 172 { 173 return new I18nizableText("plugin.web", "PLUGINS_WEB_GLOBAL_CONTENT_CONSISTENCY_MAIL_SUBJECT", List.of(_getSite().getTitle())); 174 } 175 176 private Site _getSite() 177 { 178 String siteName = _getCurrentSiteName(); 179 return _siteManager.getSite(siteName); 180 } 181 182 @Override 183 protected String _getReportUri() 184 { 185 String siteName = _getCurrentSiteName(); 186 187 StringBuilder url = new StringBuilder(_baseUrl); 188 if (!_baseUrl.endsWith("/")) 189 { 190 url.append('/'); 191 } 192 url.append(siteName).append("/index.html?uitool=uitool-global-consistency"); 193 194 return url.toString(); 195 } 196 197 /** 198 * Retrieves the names of the sites from given parameters 199 * @param siteAsMap JSON {@link Map} containing the sites names 200 * @return the names of the sites 201 */ 202 @SuppressWarnings("unchecked") 203 protected List<String> _getSiteName(String siteAsMap) 204 { 205 Map<String, Object> mapSite = _jsonUtils.convertJsonToMap(siteAsMap); 206 return mapSite.containsKey("sites") ? (List<String>) mapSite.get("sites") : List.of(); 207 } 208 209 /** 210 * Retrieves the current site name 211 * @return the current site name 212 */ 213 protected String _getCurrentSiteName() 214 { 215 Request request = ContextHelper.getRequest(_context); 216 return WebHelper.getSiteName(request); 217 } 218}