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.model.impl;
016    
017    import com.liferay.portal.LayoutFriendlyURLException;
018    import com.liferay.portal.NoSuchGroupException;
019    import com.liferay.portal.kernel.exception.PortalException;
020    import com.liferay.portal.kernel.exception.SystemException;
021    import com.liferay.portal.kernel.language.LanguageUtil;
022    import com.liferay.portal.kernel.log.Log;
023    import com.liferay.portal.kernel.log.LogFactoryUtil;
024    import com.liferay.portal.kernel.util.ArrayUtil;
025    import com.liferay.portal.kernel.util.CharPool;
026    import com.liferay.portal.kernel.util.CookieKeys;
027    import com.liferay.portal.kernel.util.GetterUtil;
028    import com.liferay.portal.kernel.util.HttpUtil;
029    import com.liferay.portal.kernel.util.ListUtil;
030    import com.liferay.portal.kernel.util.LocaleUtil;
031    import com.liferay.portal.kernel.util.LocalizationUtil;
032    import com.liferay.portal.kernel.util.PropsKeys;
033    import com.liferay.portal.kernel.util.StringPool;
034    import com.liferay.portal.kernel.util.StringUtil;
035    import com.liferay.portal.kernel.util.UnicodeProperties;
036    import com.liferay.portal.kernel.util.Validator;
037    import com.liferay.portal.model.ColorScheme;
038    import com.liferay.portal.model.Group;
039    import com.liferay.portal.model.Layout;
040    import com.liferay.portal.model.LayoutConstants;
041    import com.liferay.portal.model.LayoutFriendlyURL;
042    import com.liferay.portal.model.LayoutSet;
043    import com.liferay.portal.model.LayoutType;
044    import com.liferay.portal.model.LayoutTypeController;
045    import com.liferay.portal.model.LayoutTypePortlet;
046    import com.liferay.portal.model.LayoutTypePortletConstants;
047    import com.liferay.portal.model.Theme;
048    import com.liferay.portal.security.permission.ActionKeys;
049    import com.liferay.portal.security.permission.PermissionChecker;
050    import com.liferay.portal.service.GroupLocalServiceUtil;
051    import com.liferay.portal.service.LayoutFriendlyURLLocalServiceUtil;
052    import com.liferay.portal.service.LayoutLocalServiceUtil;
053    import com.liferay.portal.service.LayoutSetLocalServiceUtil;
054    import com.liferay.portal.service.ThemeLocalServiceUtil;
055    import com.liferay.portal.service.permission.LayoutPermissionUtil;
056    import com.liferay.portal.theme.ThemeDisplay;
057    import com.liferay.portal.util.LayoutClone;
058    import com.liferay.portal.util.LayoutCloneFactory;
059    import com.liferay.portal.util.LayoutTypePortletFactoryUtil;
060    import com.liferay.portal.util.PortalUtil;
061    import com.liferay.portal.util.PropsValues;
062    import com.liferay.portal.util.WebKeys;
063    import com.liferay.portlet.PortletURLImpl;
064    
065    import java.io.IOException;
066    
067    import java.util.ArrayList;
068    import java.util.HashMap;
069    import java.util.Iterator;
070    import java.util.List;
071    import java.util.Locale;
072    import java.util.Map;
073    
074    import javax.portlet.PortletException;
075    import javax.portlet.PortletMode;
076    import javax.portlet.PortletRequest;
077    import javax.portlet.WindowState;
078    
079    import javax.servlet.http.HttpServletRequest;
080    import javax.servlet.http.HttpServletResponse;
081    
082    /**
083     * Represents a portal layout, providing access to the layout's URLs, parent
084     * layouts, child layouts, theme settings, type settings, and more.
085     *
086     * <p>
087     * The UI name for a layout is "page." Thus, a layout represents a page in the
088     * portal. A single page is either part of the public or private layout set of a
089     * group (site). Layouts can be organized hierarchically and are summarized in a
090     * {@link LayoutSet}.
091     * </p>
092     *
093     * @author Brian Wing Shun Chan
094     */
095    public class LayoutImpl extends LayoutBaseImpl {
096    
097            public static boolean hasFriendlyURLKeyword(String friendlyURL) {
098                    String keyword = _getFriendlyURLKeyword(friendlyURL);
099    
100                    if (Validator.isNotNull(keyword)) {
101                            return true;
102                    }
103    
104                    return false;
105            }
106    
107            public static int validateFriendlyURL(String friendlyURL) {
108                    return validateFriendlyURL(friendlyURL, true);
109            }
110    
111            /**
112             * Checks whether the URL is a valid friendly URL. It checks for minimal
113             * length and that syntactic restrictions are met, and can check that the
114             * URL's length does not exceed the maximum length.
115             *
116             * @param  friendlyURL the URL to be checked
117             * @param  checkMaxLength whether to check that the URL's length does not
118             *         exceed the maximum length
119             * @return <code>-1</code> if the URL is a valid friendly URL; a {@link
120             *         LayoutFriendlyURLException} constant otherwise
121             */
122            public static int validateFriendlyURL(
123                    String friendlyURL, boolean checkMaxLength) {
124    
125                    if (friendlyURL.length() < 2) {
126                            return LayoutFriendlyURLException.TOO_SHORT;
127                    }
128    
129                    if (checkMaxLength &&
130                            (friendlyURL.length() > LayoutConstants.FRIENDLY_URL_MAX_LENGTH)) {
131    
132                            return LayoutFriendlyURLException.TOO_LONG;
133                    }
134    
135                    if (!friendlyURL.startsWith(StringPool.SLASH)) {
136                            return LayoutFriendlyURLException.DOES_NOT_START_WITH_SLASH;
137                    }
138    
139                    if (friendlyURL.endsWith(StringPool.SLASH)) {
140                            return LayoutFriendlyURLException.ENDS_WITH_SLASH;
141                    }
142    
143                    if (friendlyURL.contains(StringPool.DOUBLE_SLASH)) {
144                            return LayoutFriendlyURLException.ADJACENT_SLASHES;
145                    }
146    
147                    for (char c : friendlyURL.toCharArray()) {
148                            if (!Validator.isChar(c) && !Validator.isDigit(c) &&
149                                    (c != CharPool.DASH) && (c != CharPool.PERCENT) &&
150                                    (c != CharPool.PERIOD) && (c != CharPool.PLUS) &&
151                                    (c != CharPool.SLASH) && (c != CharPool.STAR) &&
152                                    (c != CharPool.UNDERLINE)) {
153    
154                                    return LayoutFriendlyURLException.INVALID_CHARACTERS;
155                            }
156                    }
157    
158                    return -1;
159            }
160    
161            public static void validateFriendlyURLKeyword(String friendlyURL)
162                    throws LayoutFriendlyURLException {
163    
164                    String keyword = _getFriendlyURLKeyword(friendlyURL);
165    
166                    if (Validator.isNotNull(keyword)) {
167                            LayoutFriendlyURLException lfurle = new LayoutFriendlyURLException(
168                                    LayoutFriendlyURLException.KEYWORD_CONFLICT);
169    
170                            lfurle.setKeywordConflict(keyword);
171    
172                            throw lfurle;
173                    }
174            }
175    
176            /**
177             * Returns all layouts that are direct or indirect children of the current
178             * layout.
179             *
180             * @return the layouts that are direct or indirect children of the current
181             *         layout
182             */
183            @Override
184            public List<Layout> getAllChildren() {
185                    List<Layout> layouts = new ArrayList<Layout>();
186    
187                    for (Layout layout : getChildren()) {
188                            layouts.add(layout);
189                            layouts.addAll(layout.getAllChildren());
190                    }
191    
192                    return layouts;
193            }
194    
195            /**
196             * Returns the ID of the topmost parent layout (e.g. n-th parent layout) of
197             * the current layout.
198             *
199             * @return the ID of the topmost parent layout of the current layout
200             * @throws PortalException if a matching layout could not be found
201             */
202            @Override
203            public long getAncestorLayoutId() throws PortalException {
204                    long layoutId = 0;
205    
206                    Layout layout = this;
207    
208                    while (true) {
209                            if (!layout.isRootLayout()) {
210                                    layout = LayoutLocalServiceUtil.getLayout(
211                                            layout.getGroupId(), layout.isPrivateLayout(),
212                                            layout.getParentLayoutId());
213                            }
214                            else {
215                                    layoutId = layout.getLayoutId();
216    
217                                    break;
218                            }
219                    }
220    
221                    return layoutId;
222            }
223    
224            /**
225             * Returns the plid of the topmost parent layout (e.g. n-th parent layout)
226             * of the current layout.
227             *
228             * @return the plid of the topmost parent layout of the current layout
229             * @throws PortalException if a matching layout could not be found
230             */
231            @Override
232            public long getAncestorPlid() throws PortalException {
233                    long plid = 0;
234    
235                    Layout layout = this;
236    
237                    while (true) {
238                            if (!layout.isRootLayout()) {
239                                    layout = LayoutLocalServiceUtil.getLayout(
240                                            layout.getGroupId(), layout.isPrivateLayout(),
241                                            layout.getParentLayoutId());
242                            }
243                            else {
244                                    plid = layout.getPlid();
245    
246                                    break;
247                            }
248                    }
249    
250                    return plid;
251            }
252    
253            /**
254             * Returns all parent layouts of the current layout. The list is retrieved
255             * recursively with the direct parent layout listed first, and most distant
256             * parent listed last.
257             *
258             * @return the current layout's list of parent layouts
259             * @throws PortalException if a matching layout could not be found
260             */
261            @Override
262            public List<Layout> getAncestors() throws PortalException {
263                    List<Layout> layouts = new ArrayList<Layout>();
264    
265                    Layout layout = this;
266    
267                    while (!layout.isRootLayout()) {
268                            layout = LayoutLocalServiceUtil.getLayout(
269                                    layout.getGroupId(), layout.isPrivateLayout(),
270                                    layout.getParentLayoutId());
271    
272                            layouts.add(layout);
273                    }
274    
275                    return layouts;
276            }
277    
278            /**
279             * Returns all child layouts of the current layout, independent of user
280             * access permissions.
281             *
282             * @return the list of all child layouts
283             */
284            @Override
285            public List<Layout> getChildren() {
286                    return LayoutLocalServiceUtil.getLayouts(
287                            getGroupId(), isPrivateLayout(), getLayoutId());
288            }
289    
290            /**
291             * Returns all child layouts of the current layout that the user has
292             * permission to access.
293             *
294             * @param  permissionChecker the user-specific context to check permissions
295             * @return the list of all child layouts that the user has permission to
296             *         access
297             * @throws PortalException if a portal exception occurred
298             */
299            @Override
300            public List<Layout> getChildren(PermissionChecker permissionChecker)
301                    throws PortalException {
302    
303                    List<Layout> layouts = ListUtil.copy(getChildren());
304    
305                    Iterator<Layout> itr = layouts.iterator();
306    
307                    while (itr.hasNext()) {
308                            Layout layout = itr.next();
309    
310                            if (layout.isHidden() ||
311                                    !LayoutPermissionUtil.contains(
312                                            permissionChecker, layout, ActionKeys.VIEW)) {
313    
314                                    itr.remove();
315                            }
316                    }
317    
318                    return layouts;
319            }
320    
321            /**
322             * Returns the color scheme that is configured for the current layout, or
323             * the color scheme of the layout set that contains the current layout if no
324             * color scheme is configured.
325             *
326             * @return the color scheme that is configured for the current layout, or
327             *         the color scheme  of the layout set that contains the current
328             *         layout if no color scheme is configured
329             * @throws PortalException if a portal exception occurred
330             */
331            @Override
332            public ColorScheme getColorScheme() throws PortalException {
333                    if (isInheritLookAndFeel()) {
334                            return getLayoutSet().getColorScheme();
335                    }
336                    else {
337                            return ThemeLocalServiceUtil.getColorScheme(
338                                    getCompanyId(), getTheme().getThemeId(), getColorSchemeId(),
339                                    false);
340                    }
341            }
342    
343            /**
344             * Returns the CSS text for the current layout, or for the layout set if no
345             * CSS text is configured in the current layout.
346             *
347             * <p>
348             * Layouts and layout sets can configure CSS that is applied in addition to
349             * the theme's CSS.
350             * </p>
351             *
352             * @return the CSS text for the current layout, or for the layout set if no
353             *         CSS text is configured in the current layout
354             * @throws PortalException if a portal exception occurred
355             */
356            @Override
357            public String getCssText() throws PortalException {
358                    if (isInheritLookAndFeel()) {
359                            return getLayoutSet().getCss();
360                    }
361                    else {
362                            return getCss();
363                    }
364            }
365    
366            @Override
367            public String getDefaultThemeSetting(
368                    String key, String device, boolean inheritLookAndFeel) {
369    
370                    if (!inheritLookAndFeel) {
371                            try {
372                                    Theme theme = getTheme(device);
373    
374                                    return theme.getSetting(key);
375                            }
376                            catch (Exception e) {
377                            }
378                    }
379    
380                    try {
381                            LayoutSet layoutSet = getLayoutSet();
382    
383                            return layoutSet.getThemeSetting(key, device);
384                    }
385                    catch (Exception e) {
386                    }
387    
388                    return StringPool.BLANK;
389            }
390    
391            /**
392             * Returns the layout's friendly URL for the given locale.
393             *
394             * @param  locale the locale that the friendly URL should be retrieved for
395             * @return the layout's friendly URL for the given locale
396             */
397            @Override
398            public String getFriendlyURL(Locale locale) {
399                    Layout layout = this;
400    
401                    String friendlyURL = layout.getFriendlyURL();
402    
403                    try {
404                            Group group = layout.getGroup();
405    
406                            UnicodeProperties typeSettingsProperties =
407                                    group.getTypeSettingsProperties();
408    
409                            if (!GetterUtil.getBoolean(
410                                            typeSettingsProperties.getProperty("inheritLocales"),
411                                            true)) {
412    
413                                    String[] locales = StringUtil.split(
414                                            typeSettingsProperties.getProperty(PropsKeys.LOCALES));
415    
416                                    if (!ArrayUtil.contains(
417                                                    locales, LanguageUtil.getLanguageId(locale))) {
418    
419                                            return friendlyURL;
420                                    }
421                            }
422    
423                            LayoutFriendlyURL layoutFriendlyURL =
424                                    LayoutFriendlyURLLocalServiceUtil.getLayoutFriendlyURL(
425                                            layout.getPlid(), LocaleUtil.toLanguageId(locale));
426    
427                            friendlyURL = layoutFriendlyURL.getFriendlyURL();
428                    }
429                    catch (Exception e) {
430                    }
431    
432                    return friendlyURL;
433            }
434    
435            /**
436             * Returns the friendly URLs for all configured locales.
437             *
438             * @return the friendly URLs for all configured locales
439             */
440            @Override
441            public Map<Locale, String> getFriendlyURLMap() {
442                    Map<Locale, String> friendlyURLMap = new HashMap<Locale, String>();
443    
444                    List<LayoutFriendlyURL> layoutFriendlyURLs =
445                            LayoutFriendlyURLLocalServiceUtil.getLayoutFriendlyURLs(getPlid());
446    
447                    for (LayoutFriendlyURL layoutFriendlyURL : layoutFriendlyURLs) {
448                            friendlyURLMap.put(
449                                    LocaleUtil.fromLanguageId(layoutFriendlyURL.getLanguageId()),
450                                    layoutFriendlyURL.getFriendlyURL());
451                    }
452    
453                    return friendlyURLMap;
454            }
455    
456            @Override
457            public String getFriendlyURLsXML() {
458                    Map<Locale, String> friendlyURLMap = getFriendlyURLMap();
459    
460                    return LocalizationUtil.updateLocalization(
461                            friendlyURLMap, StringPool.BLANK, "FriendlyURL",
462                            LocaleUtil.toLanguageId(LocaleUtil.getDefault()));
463            }
464    
465            /**
466             * Returns the current layout's group.
467             *
468             * <p>
469             * Group is Liferay's technical name for a site.
470             * </p>
471             *
472             * @return the current layout's group
473             * @throws PortalException if a group with the primary key could not be
474             *         found
475             */
476            @Override
477            public Group getGroup() throws PortalException {
478                    return GroupLocalServiceUtil.getGroup(getGroupId());
479            }
480    
481            /**
482             * Returns the current layout's HTML title for the given locale, or the
483             * current layout's name for the given locale if no HTML title is
484             * configured.
485             *
486             * @param  locale the locale that the HTML title should be retrieved for
487             * @return the current layout's HTML title for the given locale, or the
488             *         current layout's name for the given locale if no HTML title is
489             *         configured
490             */
491            @Override
492            public String getHTMLTitle(Locale locale) {
493                    String localeLanguageId = LocaleUtil.toLanguageId(locale);
494    
495                    return getHTMLTitle(localeLanguageId);
496            }
497    
498            /**
499             * Returns the current layout's HTML title for the given locale language ID,
500             * or the current layout's name if no HTML title is configured.
501             *
502             * @param  localeLanguageId the locale that the HTML title should be
503             *         retrieved for
504             * @return the current layout's HTML title for the given locale language ID,
505             *         or the current layout's name if no HTML title is configured
506             */
507            @Override
508            public String getHTMLTitle(String localeLanguageId) {
509                    String htmlTitle = getTitle(localeLanguageId);
510    
511                    if (Validator.isNull(htmlTitle)) {
512                            htmlTitle = getName(localeLanguageId);
513                    }
514    
515                    return htmlTitle;
516            }
517    
518            /**
519             * Returns <code>true</code> if the current layout has a configured icon.
520             *
521             * @return <code>true</code> if the current layout has a configured icon;
522             *         <code>false</code> otherwise
523             */
524            @Override
525            public boolean getIconImage() {
526                    if (getIconImageId() > 0) {
527                            return true;
528                    }
529    
530                    return false;
531            }
532    
533            /**
534             * Returns the current layout's {@link LayoutSet}.
535             *
536             * @return the current layout's layout set
537             * @throws PortalException if a portal exception occurred
538             */
539            @Override
540            public LayoutSet getLayoutSet() throws PortalException {
541                    if (_layoutSet == null) {
542                            _layoutSet = LayoutSetLocalServiceUtil.getLayoutSet(
543                                    getGroupId(), isPrivateLayout());
544                    }
545    
546                    return _layoutSet;
547            }
548    
549            /**
550             * Returns the current layout's {@link LayoutType}.
551             *
552             * @return the current layout's layout type
553             */
554            @Override
555            public LayoutType getLayoutType() {
556                    if (_layoutType == null) {
557                            _layoutType = LayoutTypePortletFactoryUtil.create(this);
558                    }
559    
560                    return _layoutType;
561            }
562    
563            /**
564             * Returns the current layout's linked layout.
565             *
566             * @return the current layout's linked layout, or <code>null</code> if no
567             *         linked layout could be found
568             */
569            @Override
570            public Layout getLinkedToLayout() {
571                    long linkToLayoutId = GetterUtil.getLong(
572                            getTypeSettingsProperty("linkToLayoutId"));
573    
574                    if (linkToLayoutId <= 0) {
575                            return null;
576                    }
577    
578                    return LayoutLocalServiceUtil.fetchLayout(
579                            getGroupId(), isPrivateLayout(), linkToLayoutId);
580            }
581    
582            /**
583             * Returns the current layout's parent plid.
584             *
585             * @return the current layout's parent plid, or <code>0</code> if the
586             *         current layout is the topmost parent layout
587             * @throws PortalException if a matching layout could not be found
588             */
589            @Override
590            public long getParentPlid() throws PortalException {
591                    if (getParentLayoutId() == LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
592                            return 0;
593                    }
594    
595                    Layout layout = LayoutLocalServiceUtil.getLayout(
596                            getGroupId(), isPrivateLayout(), getParentLayoutId());
597    
598                    return layout.getPlid();
599            }
600    
601            @Override
602            public String getRegularURL(HttpServletRequest request)
603                    throws PortalException {
604    
605                    return _getURL(request, false, false);
606            }
607    
608            @Override
609            public String getResetLayoutURL(HttpServletRequest request)
610                    throws PortalException {
611    
612                    return _getURL(request, true, true);
613            }
614    
615            @Override
616            public String getResetMaxStateURL(HttpServletRequest request)
617                    throws PortalException {
618    
619                    return _getURL(request, true, false);
620            }
621    
622            @Override
623            public Group getScopeGroup() throws PortalException {
624                    Group group = null;
625    
626                    try {
627                            group = GroupLocalServiceUtil.getLayoutGroup(
628                                    getCompanyId(), getPlid());
629                    }
630                    catch (NoSuchGroupException nsge) {
631                    }
632    
633                    return group;
634            }
635    
636            @Override
637            public String getTarget() {
638                    return PortalUtil.getLayoutTarget(this);
639            }
640    
641            /**
642             * Returns the current layout's theme, or the layout set's theme if no
643             * layout theme is configured.
644             *
645             * @return the current layout's theme, or the layout set's theme if no
646             *         layout theme is configured
647             * @throws PortalException if a portal exception occurred
648             */
649            @Override
650            public Theme getTheme() throws PortalException {
651                    if (isInheritLookAndFeel()) {
652                            return getLayoutSet().getTheme();
653                    }
654                    else {
655                            return ThemeLocalServiceUtil.getTheme(
656                                    getCompanyId(), getThemeId(), false);
657                    }
658            }
659    
660            @Override
661            public String getThemeSetting(String key, String device) {
662                    return getThemeSetting(key, device, isInheritLookAndFeel());
663            }
664    
665            @Override
666            public String getThemeSetting(
667                    String key, String device, boolean inheritLookAndFeel) {
668    
669                    UnicodeProperties typeSettingsProperties = getTypeSettingsProperties();
670    
671                    String value = typeSettingsProperties.getProperty(
672                            ThemeSettingImpl.namespaceProperty(device, key));
673    
674                    if (value != null) {
675                            return value;
676                    }
677    
678                    return getDefaultThemeSetting(key, device, inheritLookAndFeel);
679            }
680    
681            @Override
682            public String getTypeSettings() {
683                    if (_typeSettingsProperties == null) {
684                            return super.getTypeSettings();
685                    }
686                    else {
687                            return _typeSettingsProperties.toString();
688                    }
689            }
690    
691            @Override
692            public UnicodeProperties getTypeSettingsProperties() {
693                    if (_typeSettingsProperties == null) {
694                            _typeSettingsProperties = new UnicodeProperties(true);
695    
696                            _typeSettingsProperties.fastLoad(super.getTypeSettings());
697                    }
698    
699                    return _typeSettingsProperties;
700            }
701    
702            @Override
703            public String getTypeSettingsProperty(String key) {
704                    UnicodeProperties typeSettingsProperties = getTypeSettingsProperties();
705    
706                    return typeSettingsProperties.getProperty(key);
707            }
708    
709            @Override
710            public String getTypeSettingsProperty(String key, String defaultValue) {
711                    UnicodeProperties typeSettingsProperties = getTypeSettingsProperties();
712    
713                    return typeSettingsProperties.getProperty(key, defaultValue);
714            }
715    
716            @Override
717            public ColorScheme getWapColorScheme() throws PortalException {
718                    if (isInheritLookAndFeel()) {
719                            return getLayoutSet().getWapColorScheme();
720                    }
721                    else {
722                            return ThemeLocalServiceUtil.getColorScheme(
723                                    getCompanyId(), getWapTheme().getThemeId(),
724                                    getWapColorSchemeId(), true);
725                    }
726            }
727    
728            @Override
729            public Theme getWapTheme() throws PortalException {
730                    if (isInheritWapLookAndFeel()) {
731                            return getLayoutSet().getWapTheme();
732                    }
733                    else {
734                            return ThemeLocalServiceUtil.getTheme(
735                                    getCompanyId(), getWapThemeId(), true);
736                    }
737            }
738    
739            /**
740             * Returns <code>true</code> if the given layout ID matches one of the
741             * current layout's hierarchical parents.
742             *
743             * @param  layoutId the layout ID to search for in the current layout's
744             *         parent list
745             * @return <code>true</code> if the given layout ID matches one of the
746             *         current layout's hierarchical parents; <code>false</code>
747             *         otherwise
748             * @throws PortalException if any one of the current layout's acestors could
749             *         not be retrieved
750             */
751            @Override
752            public boolean hasAncestor(long layoutId) throws PortalException {
753                    long parentLayoutId = getParentLayoutId();
754    
755                    while (parentLayoutId != LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
756                            if (parentLayoutId == layoutId) {
757                                    return true;
758                            }
759    
760                            Layout parentLayout = LayoutLocalServiceUtil.getLayout(
761                                    getGroupId(), isPrivateLayout(), parentLayoutId);
762    
763                            parentLayoutId = parentLayout.getParentLayoutId();
764                    }
765    
766                    return false;
767            }
768    
769            /**
770             * Returns <code>true</code> if the current layout has child layouts.
771             *
772             * @return <code>true</code> if the current layout has child layouts,
773             *         <code>false</code> otherwise
774             */
775            @Override
776            public boolean hasChildren() {
777                    return LayoutLocalServiceUtil.hasLayouts(
778                            getGroupId(), isPrivateLayout(), getLayoutId());
779            }
780    
781            @Override
782            public boolean hasScopeGroup() throws PortalException {
783                    Group group = getScopeGroup();
784    
785                    if (group != null) {
786                            return true;
787                    }
788                    else {
789                            return false;
790                    }
791            }
792    
793            @Override
794            public boolean includeLayoutContent(
795                            HttpServletRequest request, HttpServletResponse response)
796                    throws Exception {
797    
798                    LayoutType layoutType = getLayoutType();
799    
800                    LayoutTypeController layoutTypeController =
801                            layoutType.getLayoutTypeController();
802    
803                    return layoutTypeController.includeLayoutContent(
804                            request, response, this);
805            }
806    
807            @Override
808            public boolean isChildSelected(boolean selectable, Layout layout)
809                    throws PortalException {
810    
811                    if (selectable) {
812                            long plid = getPlid();
813    
814                            List<Layout> ancestors = layout.getAncestors();
815    
816                            for (Layout curLayout : ancestors) {
817                                    if (plid == curLayout.getPlid()) {
818                                            return true;
819                                    }
820                            }
821                    }
822    
823                    return false;
824            }
825    
826            /**
827             * Returns <code>true</code> if the current layout can be used as a content
828             * display page.
829             *
830             * <p>
831             * A content display page must have an Asset Publisher portlet that is
832             * configured as the default Asset Publisher for the layout.
833             * </p>
834             *
835             * @return <code>true</code> if the current layout can be used as a content
836             *         display page; <code>false</code> otherwise
837             */
838            @Override
839            public boolean isContentDisplayPage() {
840                    UnicodeProperties typeSettingsProperties = getTypeSettingsProperties();
841    
842                    String defaultAssetPublisherPortletId =
843                            typeSettingsProperties.getProperty(
844                                    LayoutTypePortletConstants.DEFAULT_ASSET_PUBLISHER_PORTLET_ID);
845    
846                    if (Validator.isNotNull(defaultAssetPublisherPortletId)) {
847                            return true;
848                    }
849    
850                    return false;
851            }
852    
853            /**
854             * Returns <code>true</code> if the current layout is the first layout in
855             * its parent's hierarchical list of children layouts.
856             *
857             * @return <code>true</code> if the current layout is the first layout in
858             *         its parent's hierarchical list of children layouts;
859             *         <code>false</code> otherwise
860             */
861            @Override
862            public boolean isFirstChild() {
863                    if (getPriority() == 0) {
864                            return true;
865                    }
866    
867                    return false;
868            }
869    
870            /**
871             * Returns <code>true</code> if the current layout is the topmost parent
872             * layout.
873             *
874             * @return <code>true</code> if the current layout is the topmost parent
875             *         layout; <code>false</code> otherwise
876             */
877            @Override
878            public boolean isFirstParent() {
879                    if (isFirstChild() && isRootLayout()) {
880                            return true;
881                    }
882    
883                    return false;
884            }
885    
886            @Override
887            public boolean isIconImage() {
888                    return getIconImage();
889            }
890    
891            /**
892             * Returns <code>true</code> if the current layout utilizes its {@link
893             * LayoutSet}'s look and feel options (e.g. theme and color scheme).
894             *
895             * @return <code>true</code> if the current layout utilizes its layout set's
896             *         look and feel options; <code>false</code> otherwise
897             */
898            @Override
899            public boolean isInheritLookAndFeel() {
900                    if (Validator.isNull(getThemeId()) ||
901                            Validator.isNull(getColorSchemeId())) {
902    
903                            return true;
904                    }
905    
906                    return false;
907            }
908    
909            @Override
910            public boolean isInheritWapLookAndFeel() {
911                    if (Validator.isNull(getWapThemeId()) ||
912                            Validator.isNull(getWapColorSchemeId())) {
913    
914                            return true;
915                    }
916    
917                    return false;
918            }
919    
920            /**
921             * Returns <code>true</code> if the current layout is built from a layout
922             * template and still maintains an active connection to it.
923             *
924             * @return <code>true</code> if the current layout is built from a layout
925             *         template and still maintains an active connection to it;
926             *         <code>false</code> otherwise
927             */
928            @Override
929            public boolean isLayoutPrototypeLinkActive() {
930                    if (isLayoutPrototypeLinkEnabled() &&
931                            Validator.isNotNull(getLayoutPrototypeUuid())) {
932    
933                            return true;
934                    }
935    
936                    return false;
937            }
938    
939            /**
940             * Returns <code>true</code> if the current layout is part of the public
941             * {@link LayoutSet}.
942             *
943             * <p>
944             * Note, the returned value reflects the layout's default access options,
945             * not its access permissions.
946             * </p>
947             *
948             * @return <code>true</code> if the current layout is part of the public
949             *         layout set; <code>false</code> otherwise
950             */
951            @Override
952            public boolean isPublicLayout() {
953                    return !isPrivateLayout();
954            }
955    
956            /**
957             * Returns <code>true</code> if the current layout is the root layout.
958             *
959             * @return <code>true</code> if the current layout is the root layout;
960             *         <code>false</code> otherwise
961             */
962            @Override
963            public boolean isRootLayout() {
964                    if (getParentLayoutId() == LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
965                            return true;
966                    }
967    
968                    return false;
969            }
970    
971            @Override
972            public boolean isSelected(
973                    boolean selectable, Layout layout, long ancestorPlid) {
974    
975                    if (selectable) {
976                            long plid = getPlid();
977    
978                            if ((plid == layout.getPlid()) || (plid == ancestorPlid)) {
979                                    return true;
980                            }
981                    }
982    
983                    return false;
984            }
985    
986            /**
987             * Returns <code>true</code> if the current layout can hold embedded
988             * portlets.
989             *
990             * @return <code>true</code> if the current layout can hold embedded
991             *         portlets; <code>false</code> otherwise
992             */
993            @Override
994            public boolean isSupportsEmbeddedPortlets() {
995                    if (isTypeArticle() || isTypeEmbedded() || isTypePanel() ||
996                            isTypePortlet()) {
997    
998                            return true;
999                    }
1000    
1001                    return false;
1002            }
1003    
1004            @Override
1005            public boolean isTypeArticle() {
1006                    if (getType().equals(LayoutConstants.TYPE_ARTICLE)) {
1007                            return true;
1008                    }
1009    
1010                    return false;
1011            }
1012    
1013            @Override
1014            public boolean isTypeControlPanel() {
1015                    if (getType().equals(LayoutConstants.TYPE_CONTROL_PANEL)) {
1016                            return true;
1017                    }
1018    
1019                    return false;
1020            }
1021    
1022            @Override
1023            public boolean isTypeEmbedded() {
1024                    if (getType().equals(LayoutConstants.TYPE_EMBEDDED)) {
1025                            return true;
1026                    }
1027    
1028                    return false;
1029            }
1030    
1031            @Override
1032            public boolean isTypeLinkToLayout() {
1033                    if (getType().equals(LayoutConstants.TYPE_LINK_TO_LAYOUT)) {
1034                            return true;
1035                    }
1036    
1037                    return false;
1038            }
1039    
1040            @Override
1041            public boolean isTypePanel() {
1042                    if (getType().equals(LayoutConstants.TYPE_PANEL)) {
1043                            return true;
1044                    }
1045    
1046                    return false;
1047            }
1048    
1049            @Override
1050            public boolean isTypePortlet() {
1051                    if (getType().equals(LayoutConstants.TYPE_PORTLET)) {
1052                            return true;
1053                    }
1054    
1055                    return false;
1056            }
1057    
1058            @Override
1059            public boolean isTypeURL() {
1060                    if (getType().equals(LayoutConstants.TYPE_URL)) {
1061                            return true;
1062                    }
1063    
1064                    return false;
1065            }
1066    
1067            @Override
1068            public boolean matches(HttpServletRequest request, String friendlyURL) {
1069                    LayoutType layoutType = getLayoutType();
1070    
1071                    LayoutTypeController layoutTypeController =
1072                            layoutType.getLayoutTypeController();
1073    
1074                    return layoutTypeController.matches(request, friendlyURL, this);
1075            }
1076    
1077            @Override
1078            public void setGroupId(long groupId) {
1079                    super.setGroupId(groupId);
1080    
1081                    _layoutSet = null;
1082            }
1083    
1084            @Override
1085            public void setLayoutSet(LayoutSet layoutSet) {
1086                    _layoutSet = layoutSet;
1087            }
1088    
1089            @Override
1090            public void setPrivateLayout(boolean privateLayout) {
1091                    super.setPrivateLayout(privateLayout);
1092    
1093                    _layoutSet = null;
1094            }
1095    
1096            @Override
1097            public void setTypeSettings(String typeSettings) {
1098                    _typeSettingsProperties = null;
1099    
1100                    super.setTypeSettings(typeSettings);
1101            }
1102    
1103            @Override
1104            public void setTypeSettingsProperties(
1105                    UnicodeProperties typeSettingsProperties) {
1106    
1107                    _typeSettingsProperties = typeSettingsProperties;
1108    
1109                    super.setTypeSettings(_typeSettingsProperties.toString());
1110            }
1111    
1112            protected Theme getTheme(String device) throws PortalException {
1113                    if (device.equals("regular")) {
1114                            return getTheme();
1115                    }
1116                    else {
1117                            return getWapTheme();
1118                    }
1119            }
1120    
1121            private static String _getFriendlyURLKeyword(String friendlyURL) {
1122                    friendlyURL = StringUtil.toLowerCase(friendlyURL);
1123    
1124                    for (String keyword : _friendlyURLKeywords) {
1125                            if (friendlyURL.startsWith(keyword)) {
1126                                    return keyword;
1127                            }
1128    
1129                            if (keyword.equals(friendlyURL + StringPool.SLASH)) {
1130                                    return friendlyURL;
1131                            }
1132                    }
1133    
1134                    return null;
1135            }
1136    
1137            private static void _initFriendlyURLKeywords() {
1138                    _friendlyURLKeywords =
1139                            new String[PropsValues.LAYOUT_FRIENDLY_URL_KEYWORDS.length];
1140    
1141                    for (int i = 0; i < PropsValues.LAYOUT_FRIENDLY_URL_KEYWORDS.length;
1142                                    i++) {
1143    
1144                            String keyword = PropsValues.LAYOUT_FRIENDLY_URL_KEYWORDS[i];
1145    
1146                            keyword = StringPool.SLASH + keyword;
1147    
1148                            if (!keyword.contains(StringPool.PERIOD)) {
1149                                    if (keyword.endsWith(StringPool.STAR)) {
1150                                            keyword = keyword.substring(0, keyword.length() - 1);
1151                                    }
1152                                    else {
1153                                            keyword = keyword + StringPool.SLASH;
1154                                    }
1155                            }
1156    
1157                            _friendlyURLKeywords[i] = StringUtil.toLowerCase(keyword);
1158                    }
1159            }
1160    
1161            private LayoutTypePortlet _getLayoutTypePortletClone(
1162                            HttpServletRequest request)
1163                    throws IOException {
1164    
1165                    LayoutTypePortlet layoutTypePortlet = null;
1166    
1167                    LayoutClone layoutClone = LayoutCloneFactory.getInstance();
1168    
1169                    if (layoutClone != null) {
1170                            String typeSettings = layoutClone.get(request, getPlid());
1171    
1172                            if (typeSettings != null) {
1173                                    UnicodeProperties typeSettingsProperties =
1174                                            new UnicodeProperties(true);
1175    
1176                                    typeSettingsProperties.load(typeSettings);
1177    
1178                                    String stateMax = typeSettingsProperties.getProperty(
1179                                            LayoutTypePortletConstants.STATE_MAX);
1180                                    String stateMin = typeSettingsProperties.getProperty(
1181                                            LayoutTypePortletConstants.STATE_MIN);
1182    
1183                                    Layout layout = (Layout)this.clone();
1184    
1185                                    layoutTypePortlet = (LayoutTypePortlet)layout.getLayoutType();
1186    
1187                                    layoutTypePortlet.setStateMax(stateMax);
1188                                    layoutTypePortlet.setStateMin(stateMin);
1189                            }
1190                    }
1191    
1192                    if (layoutTypePortlet == null) {
1193                            layoutTypePortlet = (LayoutTypePortlet)getLayoutType();
1194                    }
1195    
1196                    return layoutTypePortlet;
1197            }
1198    
1199            private String _getURL(
1200                            HttpServletRequest request, boolean resetMaxState,
1201                            boolean resetRenderParameters)
1202                    throws PortalException {
1203    
1204                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
1205                            WebKeys.THEME_DISPLAY);
1206    
1207                    if (resetMaxState) {
1208                            Layout layout = themeDisplay.getLayout();
1209    
1210                            LayoutTypePortlet layoutTypePortlet = null;
1211    
1212                            if (layout.equals(this)) {
1213                                    layoutTypePortlet = themeDisplay.getLayoutTypePortlet();
1214                            }
1215                            else {
1216                                    try {
1217                                            layoutTypePortlet = _getLayoutTypePortletClone(request);
1218                                    }
1219                                    catch (IOException ioe) {
1220                                            _log.error("Unable to clone layout settings", ioe);
1221    
1222                                            layoutTypePortlet = (LayoutTypePortlet)getLayoutType();
1223                                    }
1224                            }
1225    
1226                            if (layoutTypePortlet.hasStateMax()) {
1227                                    String portletId = StringUtil.split(
1228                                            layoutTypePortlet.getStateMax())[0];
1229    
1230                                    PortletURLImpl portletURLImpl = new PortletURLImpl(
1231                                            request, portletId, getPlid(), PortletRequest.ACTION_PHASE);
1232    
1233                                    try {
1234                                            portletURLImpl.setWindowState(WindowState.NORMAL);
1235                                            portletURLImpl.setPortletMode(PortletMode.VIEW);
1236                                    }
1237                                    catch (PortletException pe) {
1238                                            throw new SystemException(pe);
1239                                    }
1240    
1241                                    portletURLImpl.setAnchor(false);
1242    
1243                                    if (PropsValues.LAYOUT_DEFAULT_P_L_RESET &&
1244                                            !resetRenderParameters) {
1245    
1246                                            portletURLImpl.setParameter("p_l_reset", "0");
1247                                    }
1248                                    else if (!PropsValues.LAYOUT_DEFAULT_P_L_RESET &&
1249                                                     resetRenderParameters) {
1250    
1251                                            portletURLImpl.setParameter("p_l_reset", "1");
1252                                    }
1253    
1254                                    return portletURLImpl.toString();
1255                            }
1256                    }
1257    
1258                    String portalURL = PortalUtil.getPortalURL(request);
1259    
1260                    String url = PortalUtil.getLayoutURL(this, themeDisplay);
1261    
1262                    if (!CookieKeys.hasSessionId(request) &&
1263                            (url.startsWith(portalURL) || url.startsWith(StringPool.SLASH))) {
1264    
1265                            url = PortalUtil.getURLWithSessionId(
1266                                    url, request.getSession().getId());
1267                    }
1268    
1269                    if (!resetMaxState) {
1270                            return url;
1271                    }
1272    
1273                    if (PropsValues.LAYOUT_DEFAULT_P_L_RESET && !resetRenderParameters) {
1274                            url = HttpUtil.addParameter(url, "p_l_reset", 0);
1275                    }
1276                    else if (!PropsValues.LAYOUT_DEFAULT_P_L_RESET &&
1277                                     resetRenderParameters) {
1278    
1279                            url = HttpUtil.addParameter(url, "p_l_reset", 1);
1280                    }
1281    
1282                    return url;
1283            }
1284    
1285            private static Log _log = LogFactoryUtil.getLog(LayoutImpl.class);
1286    
1287            private static String[] _friendlyURLKeywords;
1288    
1289            static {
1290                    _initFriendlyURLKeywords();
1291            }
1292    
1293            private LayoutSet _layoutSet;
1294            private LayoutType _layoutType;
1295            private UnicodeProperties _typeSettingsProperties;
1296    
1297    }