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