001/* 002 * Copyright 2024 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.odf.cdmfr; 017 018import java.time.ZonedDateTime; 019import java.util.List; 020import java.util.Optional; 021import java.util.Set; 022import java.util.stream.Collectors; 023 024import org.apache.avalon.framework.component.Component; 025import org.apache.avalon.framework.context.Context; 026import org.apache.avalon.framework.context.ContextException; 027import org.apache.avalon.framework.context.Contextualizable; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.avalon.framework.service.Serviceable; 031import org.apache.cocoon.components.ContextHelper; 032import org.apache.cocoon.environment.Request; 033 034import org.ametys.cms.repository.Content; 035import org.ametys.cms.repository.ModifiableContent; 036import org.ametys.odf.ODFHelper; 037import org.ametys.odf.ProgramItem; 038import org.ametys.odf.orgunit.OrgUnit; 039import org.ametys.odf.person.Person; 040import org.ametys.odf.program.Program; 041import org.ametys.plugins.repository.AmetysObjectResolver; 042 043/** 044 * CDM-fr handler to deposit or send CDM-fr 045 */ 046public class CDMFRHandler implements Component, Serviceable, Contextualizable 047{ 048 /** The component role. */ 049 public static final String ROLE = CDMFRHandler.class.getName(); 050 051 /** Key to get if the CDM-fr observer is suspended */ 052 public static final String SUSPEND_CDMFR = "suspend-cdmfr"; 053 054 /** The Ametys object resolver */ 055 protected AmetysObjectResolver _resolver; 056 057 /** The ODF helper */ 058 protected ODFHelper _odfHelper; 059 060 /** The send CDM-fr processor */ 061 protected SendCDMfrProcessor _sendCDMfrProcessor; 062 063 /** The deposit CDM-fr processor */ 064 protected DepositCDMfrProcessor _depositCDMfrProcessor; 065 066 /** The context */ 067 protected Context _context; 068 069 @Override 070 public void service(ServiceManager manager) throws ServiceException 071 { 072 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 073 _odfHelper = (ODFHelper) manager.lookup(ODFHelper.ROLE); 074 _sendCDMfrProcessor = (SendCDMfrProcessor) manager.lookup(SendCDMfrProcessor.ROLE); 075 _depositCDMfrProcessor = (DepositCDMfrProcessor) manager.lookup(DepositCDMfrProcessor.ROLE); 076 } 077 078 public void contextualize(Context context) throws ContextException 079 { 080 _context = context; 081 } 082 083 /** 084 * Check if at least one of the external CDM-fr actions is active. 085 * @return <code>true</code> if one of the action is active and the handler is useful 086 */ 087 public boolean needToHandle() 088 { 089 return _sendCDMfrProcessor.isActive() || _depositCDMfrProcessor.isActive(); 090 } 091 092 /** 093 * Handle CDM-fr for the given contents 094 * @param contents the list of contents 095 */ 096 public void handleCDMFR(List<ModifiableContent> contents) 097 { 098 if (needToHandle()) 099 { 100 _handleCDMFR(contents); 101 } 102 } 103 104 private void _handleCDMFR(List<ModifiableContent> contents) 105 { 106 Set<Program> programs = contents.stream() 107 .map(this::getProgramParents) 108 .flatMap(Set::stream) 109 .collect(Collectors.toSet()); 110 111 _sendCDMfrProcessor.processPrograms(programs); 112 _depositCDMfrProcessor.processPrograms(programs); 113 } 114 115 /** 116 * Get the program parents for CDM-fr treatment 117 * @param content the content 118 * @return the set of program parents 119 */ 120 public Set<Program> getProgramParents(Content content) 121 { 122 return switch (content) 123 { 124 case OrgUnit orgunit -> _odfHelper.getProgramsReferencingOrgunit(orgunit); 125 case Person person -> _odfHelper.getProgramsReferencingPerson(person); 126 case ProgramItem programItem -> _odfHelper.getParentPrograms(programItem, true); 127 default -> Set.of(); 128 }; 129 } 130 131 /** 132 * <code>true</code> if the CDM-fr observer is suspended 133 * @return <code>true</code> if the CDM-fr observer is suspended 134 */ 135 public boolean isSuspended() 136 { 137 try 138 { 139 Request request = ContextHelper.getRequest(_context); 140 // Check if the suspend date is defined in request attributes 141 return Optional.ofNullable(request.getAttribute(SUSPEND_CDMFR)).isPresent(); 142 } 143 catch (Exception e) 144 { 145 // Exception can occurred if the context is not defined. 146 // In this case, we consider that the CDM-fr handler is not suspended, 147 // it is better to send to much than not enough 148 return false; 149 } 150 } 151 152 /** 153 * Suspend CDM-fr observers. If you call this method, the {@link #unsuspendCDMFRObserver(Set)} 154 * method should be call at the end of you treatments. 155 */ 156 public void suspendCDMFRObserver() 157 { 158 // The method should not be call twice until the unsuspend method has not been called. 159 if (isSuspended()) 160 { 161 throw new UnsupportedOperationException("The CDM-fr observers are already suspended, this method should not be called until the CDM-fr observers are unsuspended."); 162 } 163 164 Request request = ContextHelper.getRequest(_context); 165 // Add the suspend date from request attributes 166 request.setAttribute(SUSPEND_CDMFR, ZonedDateTime.now()); 167 } 168 169 /** 170 * Unsuspend CDM-fr observers. You can give content identifiers to the method to be handled. 171 * @param waitingContentIds The collection of content identifiers waiting to be handle, can be null 172 */ 173 public void unsuspendCDMFRObserver(Set<String> waitingContentIds) 174 { 175 Request request = ContextHelper.getRequest(_context); 176 177 // Do not do useless operations if actions are inactives 178 if (needToHandle() && waitingContentIds != null && isSuspended()) 179 { 180 // Get the suspend date from request attributes 181 ZonedDateTime suspendDate = (ZonedDateTime) request.getAttribute(SUSPEND_CDMFR); 182 183 // Get the validated contents into the collection from the begin to now to handle them 184 List<ModifiableContent> validatedContents = waitingContentIds.stream() 185 .map(_resolver::<ModifiableContent>resolveById) 186 .filter(c -> _hasBeenValidatedAfter(c, suspendDate)) 187 .toList(); 188 189 _handleCDMFR(validatedContents); 190 } 191 192 // Remove the suspend date from request attributes 193 request.removeAttribute(SUSPEND_CDMFR); 194 } 195 196 private boolean _hasBeenValidatedAfter(Content content, ZonedDateTime suspendDate) 197 { 198 return Optional.of(content) 199 .map(Content::getLastValidationDate) 200 .map(date -> date.isAfter(suspendDate)) 201 .orElse(false); 202 } 203}