001/*
002 *  Copyright 2010 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.repositoryapp.jcr;
017
018import java.io.File;
019import java.io.FileInputStream;
020import java.io.IOException;
021import java.io.InputStream;
022import java.text.DateFormat;
023import java.text.SimpleDateFormat;
024import java.time.ZonedDateTime;
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.Calendar;
028import java.util.GregorianCalendar;
029import java.util.HashMap;
030import java.util.List;
031import java.util.Map;
032
033import javax.jcr.Binary;
034import javax.jcr.Node;
035import javax.jcr.RepositoryException;
036import javax.jcr.Session;
037import javax.jcr.Value;
038import javax.jcr.ValueFactory;
039
040import org.apache.avalon.framework.parameters.Parameters;
041import org.apache.avalon.framework.service.ServiceException;
042import org.apache.avalon.framework.service.ServiceManager;
043import org.apache.cocoon.acting.ServiceableAction;
044import org.apache.cocoon.environment.ObjectModelHelper;
045import org.apache.cocoon.environment.Redirector;
046import org.apache.cocoon.environment.Request;
047import org.apache.cocoon.environment.SourceResolver;
048import org.apache.cocoon.servlet.multipart.PartOnDisk;
049import org.apache.commons.lang3.StringUtils;
050
051import org.ametys.core.cocoon.JSonReader;
052import org.ametys.core.util.DateUtils;
053import org.ametys.plugins.repositoryapp.RepositoryProvider;
054import org.ametys.workspaces.repository.jcr.RepositoryDao;
055
056/**
057 * Set value for a property (if the property does not exist, create the property)
058 */
059public class SetPropertyAction extends ServiceableAction
060{
061    
062    /** The input date format. */
063    protected static final DateFormat _DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
064    
065    /** The repository provider. */
066    protected RepositoryProvider _repositoryProvider;
067    
068    @Override
069    public void service(ServiceManager serviceManager) throws ServiceException
070    {
071        super.service(serviceManager);
072        _repositoryProvider = (RepositoryProvider) serviceManager.lookup(RepositoryProvider.ROLE);
073    }
074    
075    public Map act(Redirector redirector, SourceResolver sourceResolver, Map objectModel, String source, Parameters parameters) throws Exception
076    {
077        Map<String, Object> result = new HashMap<>();
078        
079        Request request = ObjectModelHelper.getRequest(objectModel);
080        
081        String workspaceName = request.getParameter("workspace");
082        
083        Session session = _repositoryProvider.getSession(workspaceName);
084        
085        String nodePath = request.getParameter("path");
086        String propertyName = request.getParameter("name");
087        String[] values = request.getParameterValues("value");
088        String type = request.getParameter("type");
089        boolean multiple = Boolean.valueOf(request.getParameter("multiple"));
090        
091        if (getLogger().isDebugEnabled())
092        {
093            getLogger().debug("Trying to add property: '" + propertyName + "' to the node at path: '" + nodePath + "'");
094        }
095        
096        String relPath = RepositoryDao.removeLeadingSlash(nodePath);
097        
098        // Get the parent node
099        Node node = null;
100        if (StringUtils.isEmpty(relPath))
101        {
102            node = session.getRootNode();
103        }
104        else
105        {
106            node = session.getRootNode().getNode(relPath);
107        }
108        
109        // Get the type in jcr format (e.g. javax.jcr.PropertyType.BOOLEAN)
110        int jcrType = javax.jcr.PropertyType.valueFromName(StringUtils.capitalize(type));
111        
112        try
113        {
114            // Add or update property to the node
115            if (type.equalsIgnoreCase("Date"))
116            {
117                _setDate(node, propertyName, values);
118            }
119            else if (type.equalsIgnoreCase("Binary"))
120            {
121                _setBinary(session, request, node, propertyName);
122            }
123            else if (type.equalsIgnoreCase("Reference"))
124            {
125                _setReference(session, node, propertyName, values, multiple);
126            }
127            else
128            {
129                _setValue(node, propertyName, values, multiple, jcrType);
130            }
131            
132            result.put("success", true);
133        }
134        catch (Exception e)
135        {
136            getLogger().error("Unable to set value " + Arrays.toString(values) + " for property '" + propertyName + "'", e);
137            result.put("success", false);
138            result.put("message", e.toString());
139        }
140        
141        request.setAttribute(JSonReader.OBJECT_TO_READ, result);
142        return EMPTY_MAP;
143    }
144    
145    private void _setDate(Node node, String propertyName, String[] value) throws RepositoryException
146    {
147        //create calendar date from string value
148        ZonedDateTime zdt = DateUtils.parseZonedDateTime(value[0]);
149        Calendar cal = GregorianCalendar.from(zdt);
150        
151        node.setProperty(propertyName, cal);
152    }
153    
154    private void _setBinary(Session session, Request request, Node node, String propertyName) throws RepositoryException, IOException
155    {
156        // The upload was either sent as the property name (when modified) or as "fieldValue" (when added).
157        PartOnDisk uploadedFilePart = (PartOnDisk) request.get(propertyName);
158        if (uploadedFilePart == null)
159        {
160            uploadedFilePart = (PartOnDisk) request.get("fieldValue");
161        }
162        
163        File uploadedFile = (uploadedFilePart != null) ? uploadedFilePart.getFile() : null;
164        InputStream is = new FileInputStream(uploadedFile);
165        
166        Binary binary = session.getValueFactory().createBinary(is);
167        node.setProperty(propertyName, binary);
168    }
169    
170    private void _setReference(Session session, Node node, String propertyName, String[] value, boolean multiple) throws RepositoryException
171    {
172        List<Value> values = new ArrayList<>();
173        ValueFactory vf = session.getValueFactory();
174        for (String v : value)
175        {
176            values.add(vf.createValue(session.getNodeByIdentifier(v)));
177        }
178        
179        boolean isMultiple = multiple || node.hasProperty(propertyName) && node.getProperty(propertyName).getDefinition().isMultiple();
180        if (isMultiple)
181        {
182            node.setProperty(propertyName, values.toArray(new Value[values.size()]));
183        }
184        else
185        {
186            node.setProperty(propertyName, values.get(0));
187        }
188    }
189    
190    private void _setValue(Node node, String propertyName, String[] value, boolean multiple, int jcrType) throws RepositoryException
191    {
192        boolean isMultiple = multiple || node.hasProperty(propertyName) && node.getProperty(propertyName).getDefinition().isMultiple();
193        if (isMultiple)
194        {
195            node.setProperty(propertyName, value, jcrType);
196        }
197        else
198        {
199            node.setProperty(propertyName, value[0], jcrType);
200        }
201    }
202    
203}