View Javadoc
1   /**
2    * This file Copyright (c) 2008-2016 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.templating.models.util;
35  
36  
37  import info.magnolia.context.MgnlContext;
38  
39  import java.util.Collection;
40  import java.util.List;
41  
42  import javax.jcr.Node;
43  import javax.jcr.RepositoryException;
44  
45  import org.apache.commons.lang3.StringEscapeUtils;
46  import org.apache.commons.lang3.StringUtils;
47  import org.slf4j.Logger;
48  import org.slf4j.LoggerFactory;
49  
50  /**
51   * Simple class providing pagination.
52   */
53  public class Pagination {
54  
55      private static final Logger log = LoggerFactory.getLogger(Pagination.class);
56  
57      public static final String PARAMETER_CURRENT_PAGE = "currentPage";
58  
59      public static final String PROPERTY_MAX_RESULTS = "maxResultsPerPage";
60      public static final String PROPERTY_POSITION = "position";
61  
62      public static final String POSITION_TOP = "top";
63      public static final String DEFAULT_POSITION = POSITION_TOP;
64      public static final int DEFAULT_MAX_RESULTS = 10000;
65  
66      private int count;
67      private String link;
68      private int maxResultsPerPage;
69      private Collection items;
70      private String position;
71  
72      public Pagination(String link, Collection items, Node content) {
73          this.count = items.size();
74          this.items = items;
75          this.link = link;
76  
77          getPaginationProperties(content);
78      }
79  
80      protected Pagination() {
81      }
82  
83      protected void setResults(int maxResultsPerPage) {
84          this.maxResultsPerPage = maxResultsPerPage;
85      }
86  
87      protected void setCount(int count) {
88          this.count = count;
89      }
90  
91      public int getCount() {
92          return count;
93      }
94  
95      /**
96       * Returns a sub-{@link java.util.List} of all items from offset (see {@link #getOffset()}) to limit (calculated
97       * from max results per page and the before mentioned offset).
98       */
99      public Collection getPageItems() {
100         Collection subList = items;
101         int offset = getOffset();
102         if (count > 0) {
103             int limit = maxResultsPerPage + offset;
104             if (items.size() < limit) {
105                 limit = count;
106             }
107             subList = ((List) items).subList(offset, limit);
108         }
109         return subList;
110     }
111 
112     /**
113      * Return the current offset.
114      */
115     protected int getOffset() {
116         int offset = ((getCurrentPage() - 1) * maxResultsPerPage);
117         if (offset > count) {
118             int pages = count / maxResultsPerPage;
119             offset = pages * maxResultsPerPage;
120         }
121         return offset;
122     }
123 
124     /**
125      * Returns the current page (according to the GET parameters value, see {@link #PARAMETER_CURRENT_PAGE}, or 1).
126      */
127     public int getCurrentPage() {
128         int currentPage = 1;
129         final String currentPageParameter = MgnlContext.getParameter(PARAMETER_CURRENT_PAGE);
130         if (currentPageParameter != null && (Integer.parseInt(currentPageParameter) > 1)) {
131             currentPage = Integer.parseInt(currentPageParameter);
132         }
133         return currentPage;
134     }
135 
136     /**
137      * Returns a link the n-th page.
138      */
139     public String getPageLink(int i) {
140         final String current = String.format("%s=", PARAMETER_CURRENT_PAGE);
141 
142         if (this.link.indexOf('?') > 0) {
143             final String queryString = StringUtils.substringAfter(this.link, "?");
144             final StringBuilder newQueryString = new StringBuilder("?");
145             final String[] params = queryString.split("&");
146             boolean pageSet = false;
147             int count = 0;
148 
149             for (String param : params) {
150                 if (param.startsWith(current)) {
151                     newQueryString.append(current).append(i);
152                     pageSet = true;
153                 } else {
154                     newQueryString.append(StringEscapeUtils.escapeHtml4(param)); // Without this escaping possible XSS
155                 }
156                 count++;
157                 if (count < params.length) {
158                     newQueryString.append("&");
159                 }
160             }
161 
162             if (!pageSet) {
163                 if (newQueryString.length() > 1) {
164                     newQueryString.append("&");
165                 }
166                 newQueryString.append(current).append(i);
167 
168             }
169 
170             return StringUtils.substringBefore(this.link, "?") + newQueryString.toString();
171         } else {
172             return this.link + "?" + current + i;
173         }
174     }
175 
176     /**
177      * Returns the number of pages.
178      */
179     public int getNumPages() {
180         int numPages = count / maxResultsPerPage;
181         if ((count % maxResultsPerPage) > 0) {
182             numPages++;
183         }
184         return numPages;
185     }
186 
187     /**
188      * Returns the index to be displayed before the current page.
189      */
190     public int getBeginIndex() {
191         if (getCurrentPage() - 2 <= 1) {
192             return 1;
193         } else {
194             return getCurrentPage() - 2;
195         }
196     }
197 
198     /**
199      * Returns the index to be displayed after the current page.
200      */
201     public int getEndIndex() {
202         if (getCurrentPage() + 2 >= getNumPages()) {
203             return getNumPages();
204         } else {
205             return getCurrentPage() + 2;
206         }
207     }
208 
209     /**
210      * Returns the current position of the pagination element (see {@link #DEFAULT_POSITION}, {@link #POSITION_TOP}).
211      */
212     public String getPosition() {
213         return position;
214     }
215 
216     /**
217      * Tries to retrieve pagination properties from the underlying content {@link javax.jcr.Node}, otherwise will use
218      * provided defaults.
219      *
220      * @see #PROPERTY_MAX_RESULTS
221      * @see #PROPERTY_POSITION
222      */
223     private void getPaginationProperties(final Node content) {
224         maxResultsPerPage = DEFAULT_MAX_RESULTS;
225         position = DEFAULT_POSITION;
226 
227         try {
228             if (content.hasProperty(PROPERTY_MAX_RESULTS)) {
229                 int max = (int) content.getProperty(PROPERTY_MAX_RESULTS).getLong();
230                 if (max > 0) {
231                     maxResultsPerPage = max;
232                 }
233             }
234 
235             if (content.hasProperty(PROPERTY_POSITION)) {
236                 position = content.getProperty(PROPERTY_POSITION).getString();
237             }
238         } catch (RepositoryException e) {
239             log.warn("Could not get pagination properties for content [{}]. Using defaults instead.", content, e);
240         }
241     }
242 
243 }