001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.theme;
016    
017    import com.liferay.portal.kernel.exception.PortalException;
018    import com.liferay.portal.kernel.util.HtmlUtil;
019    import com.liferay.portal.kernel.util.ListUtil;
020    import com.liferay.portal.kernel.util.PredicateFilter;
021    import com.liferay.portal.kernel.util.StringUtil;
022    import com.liferay.portal.kernel.util.Validator;
023    import com.liferay.portal.kernel.util.WebKeys;
024    import com.liferay.portal.model.Layout;
025    import com.liferay.portal.model.LayoutType;
026    import com.liferay.portal.util.PortalUtil;
027    
028    import java.io.Serializable;
029    
030    import java.lang.reflect.Method;
031    
032    import java.util.ArrayList;
033    import java.util.List;
034    import java.util.Map;
035    
036    import javax.servlet.http.HttpServletRequest;
037    
038    /**
039     * Represents a portal navigation item, providing access to layouts and metadata
040     * from templates, which can be found in a theme's
041     * <code>portal-normal.vm</code>.
042     *
043     * @author Brian Wing Shun Chan
044     * @author Shuyang Zhou
045     */
046    public class NavItem implements Serializable {
047    
048            /**
049             * Creates a single level of navigation items from the layouts. Navigation
050             * items for nested layouts are only created when they are accessed.
051             *
052             * <p>
053             * No permission checks are performed in this method. Permissions of child
054             * layouts are honored when accessing them via {@link #getChildren()}.
055             * </p>
056             *
057             * @param  request the currently served {@link HttpServletRequest}
058             * @param  layouts the layouts from which to create the navigation items
059             * @return a single level of navigation items from the layouts, or
060             *         <code>null</code> if the collection of layouts was
061             *         <code>null</code>.
062             */
063            public static List<NavItem> fromLayouts(
064                    HttpServletRequest request, List<Layout> layouts,
065                    Map<String, Object> contextObjects) {
066    
067                    if (layouts == null) {
068                            return null;
069                    }
070    
071                    List<NavItem> navItems = new ArrayList<>(layouts.size());
072    
073                    for (Layout layout : layouts) {
074                            navItems.add(new NavItem(request, layout, contextObjects));
075                    }
076    
077                    return navItems;
078            }
079    
080            public NavItem(
081                    HttpServletRequest request, Layout layout,
082                    Map<String, Object> contextObjects) {
083    
084                    _request = request;
085                    _themeDisplay = (ThemeDisplay)request.getAttribute(
086                            WebKeys.THEME_DISPLAY);
087                    _layout = layout;
088                    _contextObjects = contextObjects;
089            }
090    
091            @Override
092            public boolean equals(Object obj) {
093                    if (this == obj) {
094                            return true;
095                    }
096    
097                    if (!(obj instanceof NavItem)) {
098                            return false;
099                    }
100    
101                    NavItem navItem = (NavItem)obj;
102    
103                    if (Validator.equals(getLayoutId(), navItem.getLayoutId())) {
104                            return true;
105                    }
106    
107                    return false;
108            }
109    
110            /**
111             * Returns all of the browsable child layouts that the current user has
112             * permission to access from this navigation item's layout.
113             *
114             * @return the list of all browsable child layouts that the current user has
115             *         permission to access from this navigation item's layout
116             * @throws Exception if an exception occurred
117             */
118            public List<NavItem> getBrowsableChildren() throws Exception {
119                    if (_browsableChildren == null) {
120                            List<NavItem> children = getChildren();
121    
122                            _browsableChildren = ListUtil.filter(
123                                    children,
124                                    new PredicateFilter<NavItem>() {
125    
126                                            @Override
127                                            public boolean filter(NavItem navItem) {
128                                                    return navItem.isBrowsable();
129                                            }
130    
131                                    });
132                    }
133    
134                    return _browsableChildren;
135            }
136    
137            /**
138             * Returns all of child layouts that the current user has permission to
139             * access from this navigation item's layout.
140             *
141             * @return the list of all child layouts that the current user has
142             *         permission to access from this navigation item's layout
143             * @throws Exception if an exception occurred
144             */
145            public List<NavItem> getChildren() throws Exception {
146                    if (_children == null) {
147                            List<Layout> layouts = _layout.getChildren(
148                                    _themeDisplay.getPermissionChecker());
149    
150                            _children = fromLayouts(_request, layouts, _contextObjects);
151                    }
152    
153                    return _children;
154            }
155    
156            /**
157             * Returns the navigation item's layout.
158             *
159             * @return the navigation item's layout
160             */
161            public Layout getLayout() {
162                    return _layout;
163            }
164    
165            /**
166             * Returns the ID of the navigation item's layout.
167             *
168             * @return the ID of the navigation item's layout
169             */
170            public long getLayoutId() {
171                    return _layout.getLayoutId();
172            }
173    
174            /**
175             * Returns the HTML-escaped name of the navigation item's layout.
176             *
177             * @return the HTML-escaped name of the navigation item's layout
178             */
179            public String getName() {
180                    return HtmlUtil.escape(getUnescapedName());
181            }
182    
183            /**
184             * Returns the full, absolute URL (including the portal's URL) of the
185             * navigation item's layout.
186             *
187             * @return the full, absolute URL of the navigation item's layout
188             * @throws Exception if an exception occurred
189             */
190            public String getRegularFullURL() throws Exception {
191                    String portalURL = PortalUtil.getPortalURL(_request);
192    
193                    String regularURL = getRegularURL();
194    
195                    if (StringUtil.startsWith(regularURL, portalURL) ||
196                            Validator.isUrl(regularURL)) {
197    
198                            return regularURL;
199                    }
200                    else {
201                            return portalURL.concat(regularURL);
202                    }
203            }
204    
205            /**
206             * Returns the regular URL of the navigation item's layout.
207             *
208             * @return the regular URL of the navigation item's layout
209             * @throws Exception if an exception occurred
210             */
211            public String getRegularURL() throws Exception {
212                    return _layout.getRegularURL(_request);
213            }
214    
215            public String getResetLayoutURL() throws Exception {
216                    return _layout.getResetLayoutURL(_request);
217            }
218    
219            public String getResetMaxStateURL() throws Exception {
220                    return _layout.getResetMaxStateURL(_request);
221            }
222    
223            /**
224             * Returns the target of the navigation item's layout.
225             *
226             * @return the target of the navigation item's layout
227             */
228            public String getTarget() {
229                    return _layout.getTarget();
230            }
231    
232            /**
233             * Returns the title of the navigation item's layout in the current
234             * request's locale.
235             *
236             * @return the title of the navigation item's layout in the current
237             *         request's locale
238             */
239            public String getTitle() {
240                    return _layout.getTitle(_themeDisplay.getLocale());
241            }
242    
243            /**
244             * Returns the unescaped name of the navigation item's layout in the current
245             * request's locale.
246             *
247             * @return the unescaped name of the navigation item's layout in the current
248             *         request's locale
249             */
250            public String getUnescapedName() {
251                    return _layout.getName(_themeDisplay.getLocale());
252            }
253    
254            /**
255             * Returns the URL of the navigation item's layout, in a format that makes
256             * it safe to use the URL as an HREF attribute value
257             *
258             * @return the URL of the navigation item's layout, in a format that makes
259             *         it safe to use the URL as an HREF attribute value
260             * @throws Exception if an exception occurred
261             */
262            public String getURL() throws Exception {
263                    return HtmlUtil.escapeHREF(getRegularFullURL());
264            }
265    
266            /**
267             * Returns <code>true</code> if the navigation item's layout has browsable
268             * child layouts.
269             *
270             * @return <code>true</code> if the navigation item's layout has browsable
271             *                 child layouts; <code>false</code> otherwise
272             * @throws Exception if an exception occurred
273             */
274            public boolean hasBrowsableChildren() throws Exception {
275                    List<NavItem> browsableChildren = getBrowsableChildren();
276    
277                    if (!browsableChildren.isEmpty()) {
278                            return true;
279                    }
280                    else {
281                            return false;
282                    }
283            }
284    
285            /**
286             * Returns <code>true</code> if the navigation item's layout has child
287             * layouts.
288             *
289             * @return <code>true</code> if the navigation item's layout has child
290             *         layouts; <code>false</code> otherwise
291             * @throws Exception if an exception occurred
292             */
293            public boolean hasChildren() throws Exception {
294                    List<NavItem> children = getChildren();
295    
296                    if (!children.isEmpty()) {
297                            return true;
298                    }
299                    else {
300                            return false;
301                    }
302            }
303    
304            @Override
305            public int hashCode() {
306                    return _layout.hashCode();
307            }
308    
309            public void icon() throws Exception {
310                    Object velocityTaglib = _contextObjects.get("theme");
311    
312                    Method method = (Method)_contextObjects.get(
313                            "velocityTaglib_layoutIcon");
314    
315                    method.invoke(velocityTaglib, _layout);
316            }
317    
318            public boolean isBrowsable() {
319                    LayoutType layoutType = _layout.getLayoutType();
320    
321                    return layoutType.isBrowsable();
322            }
323    
324            public boolean isChildSelected() throws PortalException {
325                    return _layout.isChildSelected(
326                            _themeDisplay.isTilesSelectable(), _themeDisplay.getLayout());
327            }
328    
329            public boolean isInNavigation(List<NavItem> navItems) {
330                    if (navItems == null) {
331                            return false;
332                    }
333    
334                    return navItems.contains(this);
335            }
336    
337            public boolean isSelected() throws Exception {
338                    return _layout.isSelected(
339                            _themeDisplay.isTilesSelectable(), _themeDisplay.getLayout(),
340                            _themeDisplay.getLayout().getAncestorPlid());
341            }
342    
343            private List<NavItem> _browsableChildren;
344            private List<NavItem> _children;
345            private final Map<String, Object> _contextObjects;
346            private final Layout _layout;
347            private final HttpServletRequest _request;
348            private final ThemeDisplay _themeDisplay;
349    
350    }