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