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