001/*
002 *  Copyright 2012 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.odf.oai;
017
018import java.io.IOException;
019import java.text.ParseException;
020import java.text.SimpleDateFormat;
021import java.time.ZoneOffset;
022import java.time.ZonedDateTime;
023import java.util.ArrayList;
024import java.util.Arrays;
025import java.util.Collection;
026import java.util.Date;
027import java.util.TimeZone;
028
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.ProcessingException;
033import org.apache.cocoon.environment.ObjectModelHelper;
034import org.apache.cocoon.environment.Request;
035import org.apache.cocoon.xml.XMLUtils;
036import org.apache.commons.lang.StringUtils;
037import org.xml.sax.SAXException;
038
039import org.ametys.cms.CmsConstants;
040import org.ametys.cms.repository.Content;
041import org.ametys.core.util.DateUtils;
042import org.ametys.odf.ODFHelper;
043import org.ametys.odf.catalog.CatalogsManager;
044import org.ametys.odf.program.Program;
045import org.ametys.odf.program.ProgramFactory;
046import org.ametys.plugins.repository.AmetysObjectIterable;
047import org.ametys.plugins.repository.UnknownAmetysObjectException;
048import org.ametys.plugins.repository.query.SortCriteria;
049import org.ametys.plugins.repository.query.expression.AndExpression;
050import org.ametys.plugins.repository.query.expression.DateExpression;
051import org.ametys.plugins.repository.query.expression.Expression;
052import org.ametys.plugins.repository.query.expression.Expression.Operator;
053
054/**
055 * Generator for the <code>ListIdentifiers</code> verb.
056 */
057public class ListIdentifiersGenerator extends AbstractOAIVerbGenerator implements Serviceable
058{
059    /** The OAI-PMH Sets Extension Point*/
060    private OaiSetExtensionPoint _oaiSetEP;
061    
062    private CatalogsManager _catalogsManager;
063
064    private ODFHelper _odfHelper;
065    
066    @Override
067    public void service(ServiceManager serviceManager) throws ServiceException
068    {
069        _oaiSetEP = (OaiSetExtensionPoint) serviceManager.lookup(OaiSetExtensionPoint.ROLE);
070        _catalogsManager = (CatalogsManager) serviceManager.lookup(CatalogsManager.ROLE);
071        _odfHelper = (ODFHelper) serviceManager.lookup(ODFHelper.ROLE);
072    }
073    
074    @Override
075    protected Collection<String> getRequiredParameters()
076    {
077        return Arrays.asList("verb", "metadataPrefix");
078    }
079
080    @Override
081    protected Collection<String> getAllowedParameters()
082    {
083        return Arrays.asList("verb", "from", "until", "metadataPrefix", "resumptionToken", "set");
084    }
085
086    @Override
087    protected void generateVerb() throws IOException, SAXException, ProcessingException
088    {
089        Request request = ObjectModelHelper.getRequest(objectModel);
090
091        String token = request.getParameter("resumptionToken");
092        
093        if (StringUtils.isNotEmpty(token))
094        {
095            generateError("badResumptionToken ", "This repository does not support resumption tokens");
096            return;
097        }
098        
099        String metadataPrefix = request.getParameter("metadataPrefix");
100        
101        if (!metadataPrefix.equals("cdm") && !metadataPrefix.equals("oai_dc"))
102        {
103            generateError("cannotDisseminateFormat", "The value of the metadataPrefix argument is not supported by the repository.");
104            return;
105        }
106        
107        SimpleDateFormat fullFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
108        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
109        TimeZone tz = TimeZone.getTimeZone("UTC");
110        fullFormat.setTimeZone(tz);
111        format.setTimeZone(tz);
112
113        ArrayList<Expression> expressions = new ArrayList<>();
114        
115        try
116        {
117            String fromParam = request.getParameter("from");
118            
119            if (fromParam != null)
120            {
121                Date from;
122                if (fromParam.indexOf('T') == -1)
123                {
124                    from = format.parse(fromParam);
125                }
126                else
127                {
128                    from = fullFormat.parse(fromParam);
129                }
130                
131                expressions.add(new DateExpression("lastModified", Operator.GE, from));
132            }
133            
134            String untilParam = request.getParameter("until");
135            
136            if (untilParam != null)
137            {
138                Date until;
139                if (untilParam.indexOf('T') == -1)
140                {
141                    until = format.parse(untilParam);
142                }
143                else
144                {
145                    until = fullFormat.parse(untilParam);
146                }
147                
148                expressions.add(new DateExpression("lastModified", Operator.LE, until));
149            }
150        }
151        catch (ParseException ex)
152        {
153            generateError("badArgument", "One or more aguments are malformed.");
154            return;
155        }
156        
157        OaiSet oaiSet = null;
158        String set = request.getParameter("set");
159        
160        if (StringUtils.isNotEmpty(set))
161        {
162            oaiSet = _oaiSetEP.getExtension(set);
163            if (oaiSet == null)
164            {
165                generateError("noRecordsMatch", "The set specified as an argument does not exists.");
166                return;
167            }
168        }
169        
170        Expression expression = new AndExpression(expressions.toArray(new Expression[]{}));
171        
172        SortCriteria sortCriteria = new SortCriteria();
173        sortCriteria.addCriterion(Content.ATTRIBUTE_TITLE, true, true);
174        
175        try (AmetysObjectIterable<Program> programs = oaiSet != null
176                ? oaiSet.getRecords(expression, sortCriteria)
177                // FIXME Generate identifier for language "fr" and default catalog only
178                : _odfHelper.getProgramItems(ProgramFactory.PROGRAM_CONTENT_TYPE, null, _catalogsManager.getDefaultCatalogName(), "fr", expression, sortCriteria))
179        {
180        
181            XMLUtils.startElement(contentHandler, getTagName());
182            
183            for (Program program : programs)
184            {
185                try
186                {
187                    String[] labels = program.getAllLabels();
188                    
189                    if (Arrays.asList(labels).contains(CmsConstants.LIVE_LABEL))
190                    {
191                        program.switchToLabel(CmsConstants.LIVE_LABEL);
192                        saxProgram(program);
193                    }
194                }
195                catch (UnknownAmetysObjectException e)
196                {
197                    // no "Live" label, continue
198                }
199            }
200            
201            XMLUtils.endElement(contentHandler, getTagName());
202        }
203    }
204    
205    /**
206     * Returns the surrounding tag name.
207     * @return the surrounding tag name.
208     */
209    protected String getTagName()
210    {
211        return "ListIdentifiers";
212    }
213    
214    /**
215     * Generate the program information.
216     * @param program the program
217     * @throws SAXException if an error occurs
218     */
219    protected void saxProgram(Program program) throws SAXException
220    {
221        XMLUtils.startElement(contentHandler, "header");
222        
223        XMLUtils.createElement(contentHandler, "identifier", program.getCDMId());
224        
225        ZonedDateTime date = program.getLastModified();
226        XMLUtils.createElement(contentHandler, "datestamp", DateUtils.zonedDateTimeToString(date, ZoneOffset.UTC, "yyyy-MM-dd'T'HH:mm:ssXXX"));
227        
228        XMLUtils.endElement(contentHandler, "header");
229    }
230}