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