001/*
002 *  Copyright 2017 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.frontedition;
017
018import java.util.HashMap;
019import java.util.List;
020import java.util.Map;
021
022import javax.jcr.RepositoryException;
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.logger.AbstractLogEnabled;
029import org.apache.avalon.framework.service.ServiceException;
030import org.apache.avalon.framework.service.ServiceManager;
031import org.apache.avalon.framework.service.Serviceable;
032import org.apache.cocoon.components.ContextHelper;
033import org.apache.cocoon.environment.Request;
034
035import org.ametys.cms.repository.Content;
036import org.ametys.core.ui.Callable;
037import org.ametys.plugins.repository.AmetysObject;
038import org.ametys.plugins.repository.AmetysObjectResolver;
039import org.ametys.plugins.repository.AmetysRepositoryException;
040import org.ametys.plugins.repository.UnknownAmetysObjectException;
041import org.ametys.plugins.repository.jcr.DefaultTraversableAmetysObject;
042import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector;
043import org.ametys.web.repository.page.Page;
044import org.ametys.web.repository.page.ZoneDAO;
045import org.ametys.web.repository.page.ZoneItem;
046
047/**
048 * Various helpers for front-edition plugin
049 */
050public class FrontEditionHelper extends AbstractLogEnabled implements Serviceable, Component, Contextualizable
051{
052    /** Avalon Role */
053    public static final String ROLE = FrontEditionHelper.class.getName();
054    
055    /** The Ametys object resolver */
056    private AmetysObjectResolver _ametysObjectResolver;
057    private ZoneDAO _zoneDAO;
058    /** Context */
059    private Context _context;
060    
061    public void service(ServiceManager manager) throws ServiceException
062    {
063        _ametysObjectResolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
064        _zoneDAO = (ZoneDAO) manager.lookup(ZoneDAO.ROLE);
065    }
066
067    public void contextualize(Context context) throws ContextException
068    {
069        _context = context;
070    }
071    
072    /**
073     * Get the first available parent for the current page, in the live workspace
074     * @param pageId id of the page that need to be checked
075     * @return path of the parent (can be this page if available in live)
076     */
077    @Callable
078    public String firstAvailableParentInLivePath(String pageId)
079    {
080        AmetysObject resolveById = _ametysObjectResolver.resolveById(pageId);
081        if (resolveById instanceof Page)
082        {
083            Page page = (Page) resolveById;
084            return firstAvailableParentInLivePath(page);
085        }
086        else
087        {
088            return "";
089        }
090    }
091    /**
092     * Get the first available parent for the requested page, in the live version
093     * @param page page to check
094     * @return path of the parent (can be this page if available in live)
095     */
096    public String firstAvailableParentInLivePath(Page page)
097    {
098        Request request = ContextHelper.getRequest(_context);
099        if (page == null)
100        {
101            return "";
102        }
103        AmetysObject available = page;
104        Page availablePage = null;
105        boolean found = false;
106        String forcedWorkspace = RequestAttributeWorkspaceSelector.getForcedWorkspace(request);
107        try
108        {
109            RequestAttributeWorkspaceSelector.setForcedWorkspace(request, "live");
110    
111            while (!found)
112            {
113                try
114                {
115                    AmetysObject resolveById = _ametysObjectResolver.resolveById(available.getId());
116                    if (resolveById != null)
117                    {
118                        found = true;
119                    }
120                    else
121                    {
122                        available = available.getParent();
123                    }
124                }
125                catch (UnknownAmetysObjectException e)
126                {
127                    available = available.getParent();
128                }
129            }
130
131            if (available instanceof Page)
132            {
133                availablePage = (Page) available;
134            }
135        }
136        finally
137        {
138            //reset workspace
139            RequestAttributeWorkspaceSelector.setForcedWorkspace(request, forcedWorkspace);
140        }
141        return availablePage == null ? "" : availablePage.getPathInSitemap();
142    }
143    
144    /**
145     * Determines if the page exists
146     * @param pageId the page id
147     * @return true if the page exists
148     */
149    @Callable
150    public boolean pageExists(String pageId)
151    {
152        Request request = ContextHelper.getRequest(_context);
153        String currentWorkspace = RequestAttributeWorkspaceSelector.getForcedWorkspace(request);
154        try
155        {
156            RequestAttributeWorkspaceSelector.setForcedWorkspace(request, "live");
157            return _ametysObjectResolver.hasAmetysObjectForId(pageId);
158        }
159        finally
160        {
161            //reset workspace
162            RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWorkspace);
163        }
164    }
165    
166    /**
167     * Get the name of a workflow action by it's id
168     * @param contentId id of the content to check if action is available
169     * @param workflowName name of the workflow checked
170     * @param actionIds actions where the name needs to be retreived
171     * @return workwflow action name
172     */
173    @Callable
174    public Map<Integer, String> getWorkflowActionName(String contentId, String workflowName, List<Integer> actionIds)
175    {
176        Map<Integer, String> result = new HashMap<>();
177        for (Integer actionId : actionIds)
178        {
179            boolean hasWorkflowRight = AmetysFrontEditionHelper.hasWorkflowRight(actionId, contentId, false);
180            if (hasWorkflowRight)
181            {
182                String translatedName = AmetysFrontEditionHelper.getWorkflowName(workflowName, actionId);
183                result.put(actionId, translatedName);
184            }
185        }
186        return result;
187    }
188    
189    /**
190     * Check if contents are modifiable and check all attributes restriction
191     * @param actionId the edit action id
192     * @param contentIds the id of contents
193     * @param checkEditionMode Check if we are in edition mode or not
194     * @return A Map with content id as key. The value is null if content is unmodifiable or a map of content informations as an array with path of unmodifiable attributes otherwise
195     */
196    @Callable
197    public Map<String, Object> getModifiableContents(int actionId, List<String> contentIds, boolean checkEditionMode)
198    {
199        Map<String, Object> result = new HashMap<>();
200        for (String contentId : contentIds)
201        {
202            Content content = _ametysObjectResolver.resolveById(contentId);
203            
204            Map<String, Object> contentInfo = new HashMap<>();
205            contentInfo.put("unmodifiableAttributes", AmetysFrontEditionHelper.getUnmodifiableAttributes(content, List.of(actionId), checkEditionMode));
206            contentInfo.put("rights", AmetysFrontEditionHelper.getRightsForContent(content));
207            
208            result.put(contentId, contentInfo);
209        }
210        
211        return result;
212    }
213    
214    /**
215     * Move a zone item of a page before/after another zone item of the same page
216     * @param zoneItemId zone item to move
217     * @param zoneName name of the zone
218     * @param pageId page Id
219     * @param offset how many item back/forward to move ? (negative for up, positive to down)
220     * @return true if success
221     * @throws UnknownAmetysObjectException If an error occurred
222     * @throws AmetysRepositoryException If an error occurred
223     * @throws RepositoryException If an error occurred
224     */
225    @Callable
226    public boolean moveZoneItemId(String zoneItemId, String zoneName, String pageId, int offset) throws UnknownAmetysObjectException, AmetysRepositoryException, RepositoryException
227    {
228        ZoneItem zoneItem = _ametysObjectResolver.resolveById(zoneItemId);
229        AmetysObject parent = zoneItem.getParent();
230        if (parent instanceof DefaultTraversableAmetysObject)
231        {
232            DefaultTraversableAmetysObject traversableParent = (DefaultTraversableAmetysObject) parent;
233            long itemPosition = traversableParent.getChildPosition(zoneItem);
234            long targetPosition = itemPosition + offset;
235            ZoneItem targetZoneItem = traversableParent.getChildAt(targetPosition);
236            return _zoneDAO.moveZoneItemTo(zoneItemId, zoneName, offset < 0, targetZoneItem.getId(), pageId);
237        }
238        return false;
239    }
240}