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.plugins.pagesubscription.dao; 017 018import java.util.List; 019import java.util.Map; 020 021import org.apache.avalon.framework.component.Component; 022import org.apache.avalon.framework.context.Context; 023import org.apache.avalon.framework.context.ContextException; 024import org.apache.avalon.framework.context.Contextualizable; 025import org.apache.avalon.framework.service.ServiceException; 026import org.apache.avalon.framework.service.ServiceManager; 027import org.apache.avalon.framework.service.Serviceable; 028import org.apache.cocoon.components.ContextHelper; 029import org.apache.cocoon.environment.Request; 030import org.apache.commons.lang3.StringUtils; 031 032import org.ametys.cms.fo.ForceDefaultRepositoryWorkspaceCallableDecorator; 033import org.ametys.core.ui.Callable; 034import org.ametys.core.user.CurrentUserProvider; 035import org.ametys.core.user.UserIdentity; 036import org.ametys.plugins.pagesubscription.BroadcastChannelHelper.BroadcastChannel; 037import org.ametys.plugins.pagesubscription.FrequencyHelper.Frequency; 038import org.ametys.plugins.pagesubscription.Subscription; 039import org.ametys.plugins.pagesubscription.SubscriptionException; 040import org.ametys.plugins.pagesubscription.SubscriptionException.Type; 041import org.ametys.plugins.pagesubscription.type.SubscriptionType; 042import org.ametys.plugins.pagesubscription.type.SubscriptionType.FrequencyTiming; 043import org.ametys.plugins.repository.AmetysObjectResolver; 044import org.ametys.plugins.repository.RepositoryConstants; 045import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector; 046import org.ametys.runtime.plugin.component.AbstractLogEnabled; 047 048/** 049 * Abstract DAO for subscriptions 050 */ 051public abstract class AbstractSubscriptionsDAO extends AbstractLogEnabled implements Component, Serviceable, Contextualizable 052{ 053 /** The ametys object resolver */ 054 protected AmetysObjectResolver _resolver; 055 056 /** The current user provider */ 057 protected CurrentUserProvider _currentUserProvider; 058 059 /** the Avalon context */ 060 protected Context _context; 061 062 public void service(ServiceManager smanager) throws ServiceException 063 { 064 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 065 _currentUserProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE); 066 } 067 068 public void contextualize(Context context) throws ContextException 069 { 070 _context = context; 071 } 072 073 private void _checkSubscription(Subscription subscription) 074 { 075 UserIdentity user = _currentUserProvider.getUser(); 076 boolean isSubscriber = subscription.getSubscriber() 077 .filter(u -> u.equals(user)) 078 .isPresent(); 079 if (!isSubscriber) 080 { 081 throw new SubscriptionException("Can access to the subscription with id '" + subscription.getId() + "' because the subscriber is not the current user.", Type.NO_ACCESS); 082 } 083 } 084 085 /** 086 * Get the current user subscription from the given id 087 * @param subscriptionId the subscription id 088 * @return the subscription as JSON 089 */ 090 @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION, decorators = ForceDefaultRepositoryWorkspaceCallableDecorator.DECORATOR_ID) 091 public Map<String, Object> getSubscription(String subscriptionId) 092 { 093 return getSubscription(subscriptionId, true); 094 } 095 096 /** 097 * Get the subscription from the given id 098 * @param subscriptionId the subscription id 099 * @param checkCurrentUser <code>true</code> to check if the subscription belong to the current user 100 * @return the subscription as JSON 101 */ 102 public Map<String, Object> getSubscription(String subscriptionId, boolean checkCurrentUser) 103 { 104 try 105 { 106 Subscription subscription = _resolver.resolveById(subscriptionId); 107 if (checkCurrentUser) 108 { 109 _checkSubscription(subscription); 110 } 111 112 return Map.of("success", true, "subscription", subscription.getSubscriptionType().subscriptionToJSON(subscription)); 113 } 114 catch (Exception e) 115 { 116 getLogger().error("Fail to get subscription with id '{}'", subscriptionId, e); 117 return Map.of("success", false); 118 } 119 } 120 121 /** 122 * Edit the current user subscription with the given id 123 * @param subscriptionId the subscription id 124 * @param frequency the frequency 125 * @param broadcastChannels the broadcast channels 126 * @return the subscription as JSON 127 */ 128 @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION, decorators = ForceDefaultRepositoryWorkspaceCallableDecorator.DECORATOR_ID) 129 public Map<String, Object> editSubscription(String subscriptionId, String frequency, List<String> broadcastChannels) 130 { 131 return editSubscription(subscriptionId, frequency, null, broadcastChannels, true); 132 } 133 134 /** 135 * Edit the subscription with the given id 136 * @param subscriptionId the subscription id 137 * @param frequency the frequency 138 * @param forcedTiming the force frequency timing. Can be null if not forced 139 * @param broadcastChannels the broadcast channels 140 * @param checkCurrentUser <code>true</code> to check if the subscription belong to the current user 141 * @return a Map with success status and the subscription as JSON 142 */ 143 @SuppressWarnings("unchecked") 144 public Map<String, Object> editSubscription(String subscriptionId, String frequency, FrequencyTiming forcedTiming, List<String> broadcastChannels, boolean checkCurrentUser) 145 { 146 Request request = ContextHelper.getRequest(_context); 147 // Retrieve the current workspace. 148 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 149 150 try 151 { 152 // Force default the workspace. 153 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, RepositoryConstants.DEFAULT_WORKSPACE); 154 155 Subscription subscription = _resolver.resolveById(subscriptionId); 156 if (checkCurrentUser) 157 { 158 _checkSubscription(subscription); 159 } 160 161 SubscriptionType subscriptionType = subscription.getSubscriptionType(); 162 163 Frequency subFrequency = StringUtils.isNotBlank(frequency) 164 ? Frequency.valueOf(frequency) 165 : subscription.getFrequency(); 166 167 List<BroadcastChannel> subChannels = broadcastChannels != null 168 ? broadcastChannels.stream() 169 .map(BroadcastChannel::valueOf) 170 .toList() 171 : subscription.getBroadcastChannels(); 172 173 if (forcedTiming != null) 174 { 175 subscriptionType.editForceSubscription(subscription, subFrequency, subChannels, forcedTiming, null); 176 } 177 else 178 { 179 subscriptionType.editSubscription(subscription, subFrequency, subChannels, null); 180 } 181 182 return Map.of("success", true, "subscription", subscriptionType.subscriptionToJSON(subscription)); 183 } 184 catch (Exception e) 185 { 186 getLogger().error("Unable to edit subscription with id '{}'", subscriptionId, e); 187 return Map.of("success", false); 188 } 189 finally 190 { 191 // Restore current workspace 192 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 193 } 194 } 195 196 /** 197 * Remove the current user subscription with the given id 198 * @param subscriptionId the subscription id 199 * @return true if successfully subscribe, false otherwise 200 * @throws Exception if an error occurred 201 */ 202 @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION, decorators = ForceDefaultRepositoryWorkspaceCallableDecorator.DECORATOR_ID) 203 public boolean unsubscribe(String subscriptionId) throws Exception 204 { 205 return unsubscribe(subscriptionId, true); 206 } 207 208 /** 209 * Remove the subscription with the given id 210 * @param subscriptionId the subscription id 211 * @return true if successfully subscribe, false otherwise 212 * @param checkCurrentUser <code>true</code> to check if the subscription belong to the current user 213 * @throws Exception if an error occurred 214 */ 215 public boolean unsubscribe(String subscriptionId, boolean checkCurrentUser) throws Exception 216 { 217 Request request = ContextHelper.getRequest(_context); 218 // Retrieve the current workspace. 219 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 220 221 try 222 { 223 // Force default the workspace. 224 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, RepositoryConstants.DEFAULT_WORKSPACE); 225 226 Subscription subscription = _resolver.resolveById(subscriptionId); 227 if (checkCurrentUser) 228 { 229 _checkSubscription(subscription); 230 } 231 232 subscription.getSubscriptionType().unsubscribe(subscription); 233 return true; 234 } 235 catch (Exception e) 236 { 237 getLogger().error("Fail to unsubscribe to suscription with id {}", subscriptionId, e); 238 return false; 239 } 240 finally 241 { 242 // Restore current workspace 243 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 244 } 245 } 246}