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