001/* 002 * Copyright 2026 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.cms.workflow.schedule; 017 018import java.time.ZonedDateTime; 019import java.util.ArrayList; 020import java.util.List; 021import java.util.Map; 022 023import org.apache.avalon.framework.context.Context; 024import org.apache.avalon.framework.context.ContextException; 025import org.apache.avalon.framework.context.Contextualizable; 026import org.apache.cocoon.components.ContextHelper; 027import org.apache.cocoon.environment.Request; 028 029import org.ametys.cms.clientsideelement.ScheduleContentPublicationClientSideElement; 030import org.ametys.cms.repository.WorkflowAwareContent; 031import org.ametys.cms.workflow.AbstractContentWorkflowComponent; 032import org.ametys.plugins.repository.data.holder.ModifiableDataHolder; 033import org.ametys.plugins.repository.version.ModifiableDataAwareVersionableAmetysObject; 034import org.ametys.plugins.workflow.EnhancedCondition; 035import org.ametys.runtime.i18n.I18nizableText; 036 037import com.opensymphony.module.propertyset.PropertySet; 038import com.opensymphony.workflow.WorkflowException; 039 040/** 041 * Condition that check if a publication or unpublication is currently scheduled on the content. 042 * If so, the condition is invalid unless a parameter is provided to allow the scheduled action to process 043 */ 044public class ScheduledPublicationCondition extends AbstractContentWorkflowComponent implements EnhancedCondition, Contextualizable 045{ 046 /** Set a transientVars or request param to true with this key to ignore this condition */ 047 public static final String ALLOW_SCHEDULED_ACTION = ScheduledPublicationCondition.class.getName() + "$allowScheduled"; 048 /** The avalon context */ 049 protected Context _context; 050 051 public void contextualize(Context context) throws ContextException 052 { 053 _context = context; 054 } 055 056 public boolean passesCondition(Map transientVars, Map args, PropertySet ps) throws WorkflowException 057 { 058 WorkflowAwareContent content = getContent(transientVars); 059 ModifiableDataHolder unversionedDataHolder = ((ModifiableDataAwareVersionableAmetysObject) content).getUnversionedDataHolder(); 060 061 Long scheduledAction; 062 if (allowScheduledAction(transientVars)) 063 { 064 // Check any scheduled action because we don't know the current state. 065 Long currentAction = Long.valueOf((Integer) transientVars.get("actionId")); 066 scheduledAction = unversionedDataHolder.getValue(ScheduleContentPublicationClientSideElement.PUBLISH_ACTION_ID); 067 if (currentAction.equals(scheduledAction)) 068 { 069 return true; 070 } 071 scheduledAction = unversionedDataHolder.getValue(ScheduleContentPublicationClientSideElement.UNPUBLISH_ACTION_ID); 072 if (currentAction.equals(scheduledAction)) 073 { 074 return true; 075 } 076 } 077 078 // check the scheduled action are expired 079 scheduledAction = unversionedDataHolder.getValue(ScheduleContentPublicationClientSideElement.PUBLISH_ACTION_ID); 080 ZonedDateTime scheduledDate = unversionedDataHolder.getValue(ScheduleContentPublicationClientSideElement.PUBLISH_DATE); 081 // publication not planned or expired 082 if (scheduledAction == null || ZonedDateTime.now().isAfter(scheduledDate)) 083 { 084 // check the unpublish 085 scheduledAction = unversionedDataHolder.getValue(ScheduleContentPublicationClientSideElement.UNPUBLISH_ACTION_ID); 086 scheduledDate = unversionedDataHolder.getValue(ScheduleContentPublicationClientSideElement.UNPUBLISH_DATE); 087 088 // unpublication not planned or expired 089 if (scheduledAction == null || ZonedDateTime.now().isAfter(scheduledDate)) 090 { 091 return true; 092 } 093 } 094 095 _reportFailure(transientVars, content, scheduledAction); 096 097 return false; 098 } 099 100 private void _reportFailure(Map transientVars, WorkflowAwareContent content, Long scheduledAction) throws WorkflowException 101 { 102 List<ConditionFailure> conditionFailures = getConditionFailures(transientVars); 103 if (conditionFailures != null) 104 { 105 conditionFailures.add(new ConditionFailure( 106 String.format("Schedule condition failed : content: %s is schedule for action: %s", content.getId(), scheduledAction), 107 ScheduledPublicationCondition.class.getName() 108 )); 109 } 110 111 List<String> i18nParams = new ArrayList<>(); 112 i18nParams.add(_contentHelper.getTitle(content)); 113 114 addWorkflowError(transientVars, new I18nizableText("plugin.cms", "WORKFLOW_CONTENT_SCHEDULED_PUBLICATION_CONDITION_FAILED", i18nParams)); 115 116 if (_logger.isDebugEnabled()) 117 { 118 _logger.debug(String.format("Content: %s is schedule for action: %s, failing condition", content.getId(), scheduledAction)); 119 } 120 } 121 122 private boolean allowScheduledAction(Map transientVars) 123 { 124 Boolean allow = (Boolean) transientVars.get(ALLOW_SCHEDULED_ACTION); 125 if (allow == null) 126 { 127 Request request = ContextHelper.getRequest(_context); 128 allow = (Boolean) request.getAttribute(ALLOW_SCHEDULED_ACTION); 129 } 130 return Boolean.TRUE.equals(allow); 131 } 132 133 134 public I18nizableText getLabel() 135 { 136 return new I18nizableText("plugin.cms", "PLUGINS_CMS_SCHEDULED_PUBLICATION_CONDITION_LABEL"); 137 } 138 139}