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.io.IOException;
019import java.util.Arrays;
020import java.util.HashMap;
021import java.util.Map;
022import java.util.Set;
023
024import javax.jcr.Node;
025import javax.jcr.RepositoryException;
026import javax.jcr.lock.LockManager;
027
028import org.apache.avalon.framework.parameters.Parameters;
029import org.apache.avalon.framework.service.ServiceException;
030import org.apache.avalon.framework.service.ServiceManager;
031import org.apache.cocoon.ProcessingException;
032import org.apache.cocoon.environment.ObjectModelHelper;
033import org.apache.cocoon.environment.Redirector;
034import org.apache.cocoon.environment.Request;
035import org.apache.cocoon.environment.SourceResolver;
036import org.apache.commons.lang.StringUtils;
037
038import org.ametys.cms.content.GetMetadataSetDefAction;
039import org.ametys.cms.content.external.ExternalizableMetadataProviderExtensionPoint;
040import org.ametys.cms.contenttype.ContentTypesHelper;
041import org.ametys.cms.contenttype.MetadataDefinition;
042import org.ametys.cms.contenttype.MetadataType;
043import org.ametys.cms.repository.Content;
044import org.ametys.cms.transformation.RichTextTransformer;
045import org.ametys.core.cocoon.JSonReader;
046import org.ametys.core.right.RightManager;
047import org.ametys.core.user.CurrentUserProvider;
048import org.ametys.plugins.repository.AmetysObject;
049import org.ametys.plugins.repository.AmetysObjectResolver;
050import org.ametys.plugins.repository.AmetysRepositoryException;
051import org.ametys.plugins.repository.jcr.JCRAmetysObject;
052import org.ametys.plugins.repository.lock.LockHelper;
053import org.ametys.plugins.repository.lock.LockableAmetysObject;
054import org.ametys.plugins.repository.metadata.CompositeMetadata;
055import org.ametys.plugins.repository.metadata.ModifiableRichText;
056import org.ametys.plugins.repository.version.VersionableAmetysObject;
057import org.ametys.web.renderingcontext.RenderingContext;
058import org.ametys.web.renderingcontext.RenderingContextHandler;
059
060/**
061 * Check if the content can be edited, and return the value
062 */
063public class GetServerValueAction extends GetMetadataSetDefAction 
064{
065    
066    /** The ametys object resolver */
067    protected AmetysObjectResolver _resolver;
068    /** The rendering context handler */
069    protected RenderingContextHandler _renderingContextHandler;
070    
071    @Override
072    public void service(ServiceManager serviceManager) throws ServiceException
073    {
074        super.service(serviceManager);
075        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
076        _rightManager = (RightManager) manager.lookup(RightManager.ROLE);
077        _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE);
078        _renderingContextHandler = (RenderingContextHandler) manager.lookup(RenderingContextHandler.ROLE);
079        _contentTypesHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE);
080        _externalizableMetaProvider = (ExternalizableMetadataProviderExtensionPoint) manager.lookup(ExternalizableMetadataProviderExtensionPoint.ROLE);
081    }
082    
083    @Override
084    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
085    {
086        Request request = ObjectModelHelper.getRequest(objectModel);
087        
088        String contentId = parameters.getParameter("contentId");
089        String metadataPath = parameters.getParameter("metadataPath");
090        boolean validateContent = parameters.getParameterAsBoolean("validateContent", false);
091        String metadataSetName = parameters.getParameter("metadataSetName", "main");
092
093        Map<String, Object> jsonObject = new HashMap<>();
094        boolean success = true;
095        
096        Content content = _resolver.resolveById(contentId);
097        if (isContentLocked(content))
098        {
099            success = false;
100            jsonObject.put("error", "locked");
101        }
102        else if (validateContent)
103        {
104            RenderingContext context = _renderingContextHandler.getRenderingContext();
105            if (context == RenderingContext.FRONT && content instanceof VersionableAmetysObject)
106            {
107                String[] labels = ((VersionableAmetysObject) content).getLabels();
108                if (!Arrays.asList(labels).contains("Live"))
109                {
110                    success = false;
111                    jsonObject.put("error", "draft");
112                }
113            }
114        }
115        
116        if (success)
117        {
118            jsonObject.putAll(_contentMetadata2Json(metadataSetName, content, metadataPath));
119        }
120        
121        request.setAttribute(JSonReader.OBJECT_TO_READ, jsonObject);
122        return EMPTY_MAP;
123    }
124
125    /**
126     * Check if the content is locked
127     * @param content The content
128     * @return True if the content is locked
129     */
130    protected boolean isContentLocked(Content content)
131    {
132        if (!(content instanceof JCRAmetysObject))
133        {
134            return false;
135        }
136        
137        try
138        {
139            Node node = ((JCRAmetysObject) content).getNode();
140            LockManager lockManager = node.getSession().getWorkspace().getLockManager();
141
142            if (lockManager.isLocked(node.getPath()))
143            {
144                Node lockHolder = lockManager.getLock(node.getPath()).getNode();
145
146                AmetysObject ao = _resolver.resolve(lockHolder, false);
147                if (ao instanceof LockableAmetysObject)
148                {
149                    LockableAmetysObject lockableAO = (LockableAmetysObject) ao;
150                    if (!LockHelper.isLockOwner(lockableAO, _currentUserProvider.getUser()))
151                    {
152                        return true;
153                    }
154                }
155            }
156        }
157        catch (RepositoryException e)
158        {
159            getLogger().error(String.format("Repository exception during lock checking for ametys object '%s'", content.getId()), e);
160            throw new AmetysRepositoryException(e);
161        }
162        
163        return false;
164    }
165
166    private Map<String, Object> _contentMetadata2Json(String metadataSetName, Content content, String metadataPath) throws ProcessingException
167    {
168        String[] metadataPathSplit = metadataPath.split("/");
169        MetadataDefinition metaDef = null;
170        CompositeMetadata metadataHolder = content.getMetadataHolder();
171        String lastPart = null;
172        for (String metadataPathPart : metadataPathSplit)
173        {
174            lastPart = metadataPathPart;
175            if (metaDef == null)
176            {
177                metaDef =  _contentTypesHelper.getMetadataDefinition(metadataPathPart, content.getTypes(), content.getMixinTypes());
178            }
179            else
180            {
181                metaDef = metaDef.getMetadataDefinition(metadataPathPart);
182            }
183            
184            if (metaDef == null)
185            {
186                throw new ProcessingException(String.format("Unknown metadata path '%s' in metadata set '%s' of type '%s' for content type(s) '%s'",
187                        metadataPath, metadataSetName, "edition", StringUtils.join(content.getTypes(), ',')));    
188            }
189            
190            if (MetadataType.COMPOSITE.equals(metaDef.getType()))
191            {
192                metadataHolder = metadataHolder.getCompositeMetadata(metadataPathPart);
193            }
194        }
195        
196        Set<String> externalAndLocalMetadata = _externalizableMetaProvider.getExternalAndLocalMetadata(content);
197        
198        Map<String, Object> jsonObject = metadataDefinition2JsonObject(content, null, metaDef, metadataPath, externalAndLocalMetadata);
199        if (metaDef != null)
200        {
201            if (MetadataType.RICH_TEXT.equals(metaDef.getType()))
202            {
203                RichTextTransformer richTextTransformer = metaDef.getRichTextTransformer();
204                StringBuilder result = new StringBuilder(2048);
205                if (metadataHolder.hasMetadata(lastPart))
206                {
207                    try
208                    {
209                        richTextTransformer.transformForEditing((ModifiableRichText) metadataHolder.getRichText(lastPart), result);
210                    }
211                    catch (IOException e)
212                    {
213                        throw new AmetysRepositoryException("Unable to transform a rich text into a string", e);
214                    }
215                    jsonObject.put("value", result.toString());
216                }
217                else
218                {
219                    jsonObject.put("value", "");
220                }
221            }
222            else
223            {
224                jsonObject.put("value", metaDef.isMultiple() ? metadataHolder.getStringArray(lastPart, null) : metadataHolder.getString(lastPart, null));    
225            }
226        }
227        return jsonObject;
228    }
229    
230}