001/*
002 *  Copyright 2020 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.mobileapp.action;
017
018import java.time.Instant;
019import java.time.LocalDate;
020import java.time.ZonedDateTime;
021import java.util.ArrayList;
022import java.util.Comparator;
023import java.util.Date;
024import java.util.HashMap;
025import java.util.List;
026import java.util.Map;
027import java.util.stream.Collectors;
028
029import org.apache.avalon.framework.service.ServiceException;
030import org.apache.avalon.framework.service.ServiceManager;
031import org.apache.cocoon.environment.Request;
032
033import org.ametys.cms.repository.Content;
034import org.ametys.cms.search.Sort;
035import org.ametys.plugins.mobileapp.QueriesHelper;
036import org.ametys.plugins.queriesdirectory.Query;
037import org.ametys.plugins.repository.AmetysObjectIterable;
038
039/**
040 * Returns the list of feeds for a user
041 */
042public class GetFeedsContentsAction extends AbstractLoggedAction
043{
044    /** The Ametys object resolver */
045    protected QueriesHelper _queryHelper;
046    
047    @Override
048    public void service(ServiceManager smanager) throws ServiceException
049    {
050        super.service(smanager);
051        _queryHelper = (QueriesHelper) smanager.lookup(QueriesHelper.ROLE);
052    }
053    
054    @Override
055    protected Map<String, Object> doLoggedInAction(Request request, Map<String, Object> jsonParams)
056    {
057        Map<String, Object> result = new HashMap<>();
058        
059        List<Query> queries = _queryHelper.getQueries();
060        
061        List<QuerySearchresult> results = new ArrayList<>();
062        
063        for (Query query : queries)
064        {
065            List<Sort> sort = _queryHelper.getSortProperty(query, queries.size() > 1);
066            AmetysObjectIterable<Content> searchResults = _queryHelper.executeQuery(query, sort, true);
067            if (searchResults != null)
068            {
069                Sort firstSort = sort.get(0);
070                searchResults.stream()
071                    .forEach(content -> results.add(new QuerySearchresult(query, firstSort, content)));
072            }
073        }
074        
075        Comparator<QuerySearchresult> comparator = new Comparator<>()
076        {
077            public int compare(QuerySearchresult o1, QuerySearchresult o2)
078            {
079                Object o1Value = getDate(o1);
080                Object o2Value = getDate(o2);
081                return compareDates(o1Value, o2Value);
082            }
083            
084            private Object getDate(QuerySearchresult queryResult)
085            {
086                String field = queryResult.getSort().getField();
087                Content content = queryResult.getContent();
088                Object value;
089                if (content.hasValue(field))
090                {
091                    value = content.getValue(field, true, null);
092                }
093                else
094                {
095                    value = content.getLastValidationDate();
096                }
097                return value;
098            }
099        };
100        
101        List<Map<String, String>> jsonResults;
102        if (queries.size() > 1)
103        {
104            jsonResults = results.stream()
105                .sorted(comparator)
106                .map(searchResult -> contentToJson(searchResult))
107                .collect(Collectors.toList());
108        }
109        else
110        {
111            jsonResults = results.stream()
112                    .map(searchResult -> contentToJson(searchResult))
113                    .collect(Collectors.toList());
114        }
115        
116
117        result.put("items", jsonResults);
118        
119        return result;
120    }
121    
122    /**
123     * Transform a content into a json map
124     * @param searchResult the search result containing tho content
125     * @return a json map
126     */
127    protected Map<String, String> contentToJson(QuerySearchresult searchResult)
128    {
129        Content content = searchResult.getContent();
130        Map<String, String> result = _queryHelper.getDataForContent(content);
131
132        result.put("feed_id", searchResult.getQueryId());
133        result.put("category_name", searchResult.getQueryName());
134        
135        String sortField = searchResult.getSort().getField();
136        
137        String isoDate = _queryHelper.getContentFormattedDate(content, sortField);
138        result.put("date", isoDate);
139        
140        return result;
141    }
142    
143    /**
144     * Compare two dates (can be {@link ZonedDateTime} and/or {@link LocalDate} and/or {@link Date}
145     * @param o1 a {@link ZonedDateTime} and/or {@link LocalDate} and/or {@link Date}
146     * @param o2 a {@link ZonedDateTime} and/or {@link LocalDate} and/or {@link Date}
147     * @return -1 if o1 is before o2, +1 if o1 is after o2, 0 if same date (not equals)
148     * @throws ClassCastException one of the object is not a {@link ZonedDateTime} or {@link LocalDate} or {@link Date}
149     */
150    protected int compareDates(Object o1, Object o2) throws ClassCastException
151    {
152        if (o1 == null && o2 == null)
153        {
154            return 0;
155        }
156        else if (o1 == null)
157        {
158            return -1;
159        }
160        else if (o2 == null)
161        {
162            return 1;
163        }
164        else if (o1 instanceof Date && o2 instanceof Date)
165        {
166            return ((Date) o1).compareTo((Date) o2);
167        }
168        else if (o1 instanceof LocalDate && o2 instanceof LocalDate)
169        {
170            return ((LocalDate) o1).compareTo((LocalDate) o2);
171        }
172        else if (o1 instanceof ZonedDateTime && o2 instanceof ZonedDateTime)
173        {
174            return ((ZonedDateTime) o1).compareTo((ZonedDateTime) o2);
175        }
176        
177        Instant i1 = _queryHelper.toInstant(o1, o2);
178        Instant i2 = _queryHelper.toInstant(o2, o1);
179        if (i1 != null && i2 != null)
180        {
181            return i1.compareTo(i2);
182        }
183        
184        // One of them is not null and not a LocalDate or ZonedDateTime
185        throw new ClassCastException();
186    }
187    
188    /**
189     * Content fetched from a query
190     */
191    protected class QuerySearchresult
192    {
193        private Query _query;
194        private Sort _sort;
195        private Content _content;
196        
197        /**
198         * New QuerySearchresult
199         * @param query the query that found this content
200         * @param sort the sort used
201         * @param content the content itself
202         */
203        public QuerySearchresult(Query query, Sort sort, Content content)
204        {
205            _query = query;
206            _sort = sort;
207            _content = content;
208        }
209        
210        /**
211         * The query ID
212         * @return The query ID
213         */
214        public String getQueryId()
215        {
216            return _query.getId();
217        }
218        /**
219         * The query name
220         * @return The query name
221         */
222        public String getQueryName()
223        {
224            return _query.getTitle();
225        }
226
227        
228        /**
229         * The sort
230         * @return The sort
231         */
232        public Sort getSort()
233        {
234            return _sort;
235        }
236        
237        /**
238         * The content
239         * @return The content
240         */
241        public Content getContent()
242        {
243            return _content;
244        }
245        
246    }
247
248}