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