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.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.kernel.util.WebKeys;
038    import com.liferay.portal.model.ColorScheme;
039    import com.liferay.portal.model.Group;
040    import com.liferay.portal.model.GroupConstants;
041    import com.liferay.portal.model.Layout;
042    import com.liferay.portal.model.LayoutConstants;
043    import com.liferay.portal.model.LayoutFriendlyURL;
044    import com.liferay.portal.model.LayoutSet;
045    import com.liferay.portal.model.LayoutType;
046    import com.liferay.portal.model.LayoutTypeController;
047    import com.liferay.portal.model.LayoutTypePortlet;
048    import com.liferay.portal.model.LayoutTypePortletConstants;
049    import com.liferay.portal.model.Portlet;
050    import com.liferay.portal.model.PortletPreferences;
051    import com.liferay.portal.model.Theme;
052    import com.liferay.portal.security.permission.ActionKeys;
053    import com.liferay.portal.security.permission.PermissionChecker;
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 =
407                            PortletPreferencesLocalServiceUtil.getPortletPreferences(
408                                    groupId, PortletKeys.PREFS_OWNER_TYPE_LAYOUT,
409                                    PortletKeys.PREFS_PLID_SHARED);
410    
411                    if (isTypePortlet()) {
412                            LayoutTypePortlet layoutTypePortlet =
413                                    (LayoutTypePortlet)getLayoutType();
414    
415                            PortalPreferences portalPreferences =
416                                    layoutTypePortlet.getPortalPreferences();
417    
418                            if (layoutTypePortlet.isCustomizable() &&
419                                    (portalPreferences != null)) {
420    
421                                    portletPreferences = ListUtil.copy(portletPreferences);
422    
423                                    portletPreferences.addAll(
424                                            PortletPreferencesLocalServiceUtil.getPortletPreferences(
425                                                    portalPreferences.getUserId(),
426                                                    PortletKeys.PREFS_OWNER_TYPE_USER, getPlid()));
427                            }
428                    }
429    
430                    if (portletPreferences.isEmpty()) {
431                            return Collections.emptyList();
432                    }
433    
434                    List<Portlet> portlets = new ArrayList<>(portletPreferences.size());
435    
436                    for (PortletPreferences portletPreference : portletPreferences) {
437                            String portletId = portletPreference.getPortletId();
438    
439                            Portlet portlet = PortletLocalServiceUtil.getPortletById(
440                                    getCompanyId(), portletId);
441    
442                            if ((portlet == null) || !portlet.isReady() ||
443                                    portlet.isUndeployedPortlet() || !portlet.isActive()) {
444    
445                                    continue;
446                            }
447    
448                            Portlet embeddedPortlet = portlet;
449    
450                            if (portlet.isInstanceable()) {
451    
452                                    // Instanceable portlets do not need to be cloned because they
453                                    // are already cloned. See the method getPortletById in the
454                                    // class PortletLocalServiceImpl and how it references the
455                                    // method getClonedInstance in the class PortletImpl.
456    
457                            }
458                            else {
459                                    embeddedPortlet = (Portlet)embeddedPortlet.clone();
460                            }
461    
462                            // We set embedded portlets as static on order to avoid adding the
463                            // close and/or move icons.
464    
465                            embeddedPortlet.setStatic(true);
466    
467                            portlets.add(embeddedPortlet);
468                    }
469    
470                    return portlets;
471            }
472    
473            /**
474             * Returns the layout's friendly URL for the given locale.
475             *
476             * @param  locale the locale that the friendly URL should be retrieved for
477             * @return the layout's friendly URL for the given locale
478             */
479            @Override
480            public String getFriendlyURL(Locale locale) {
481                    Layout layout = this;
482    
483                    String friendlyURL = layout.getFriendlyURL();
484    
485                    try {
486                            Group group = layout.getGroup();
487    
488                            UnicodeProperties typeSettingsProperties =
489                                    group.getTypeSettingsProperties();
490    
491                            if (!GetterUtil.getBoolean(
492                                            typeSettingsProperties.getProperty(
493                                                    GroupConstants.TYPE_SETTINGS_KEY_INHERIT_LOCALES),
494                                            true)) {
495    
496                                    String[] locales = StringUtil.split(
497                                            typeSettingsProperties.getProperty(PropsKeys.LOCALES));
498    
499                                    if (!ArrayUtil.contains(
500                                                    locales, LanguageUtil.getLanguageId(locale))) {
501    
502                                            return friendlyURL;
503                                    }
504                            }
505    
506                            LayoutFriendlyURL layoutFriendlyURL =
507                                    LayoutFriendlyURLLocalServiceUtil.getLayoutFriendlyURL(
508                                            layout.getPlid(), LocaleUtil.toLanguageId(locale));
509    
510                            friendlyURL = layoutFriendlyURL.getFriendlyURL();
511                    }
512                    catch (Exception e) {
513                    }
514    
515                    return friendlyURL;
516            }
517    
518            /**
519             * Returns the friendly URLs for all configured locales.
520             *
521             * @return the friendly URLs for all configured locales
522             */
523            @Override
524            public Map<Locale, String> getFriendlyURLMap() {
525                    Map<Locale, String> friendlyURLMap = new HashMap<>();
526    
527                    List<LayoutFriendlyURL> layoutFriendlyURLs =
528                            LayoutFriendlyURLLocalServiceUtil.getLayoutFriendlyURLs(getPlid());
529    
530                    for (LayoutFriendlyURL layoutFriendlyURL : layoutFriendlyURLs) {
531                            friendlyURLMap.put(
532                                    LocaleUtil.fromLanguageId(layoutFriendlyURL.getLanguageId()),
533                                    layoutFriendlyURL.getFriendlyURL());
534                    }
535    
536                    // If the site/portal default language changes, there may not exist a
537                    // value for the new default language. In this situation, we will use
538                    // the value from the previous default language.
539    
540                    Locale defaultSiteLocale = LocaleUtil.getSiteDefault();
541    
542                    if (Validator.isNull(friendlyURLMap.get(defaultSiteLocale))) {
543                            Locale defaultLocale = LocaleUtil.fromLanguageId(
544                                    getDefaultLanguageId());
545    
546                            String defaultFriendlyURL = friendlyURLMap.get(defaultLocale);
547    
548                            friendlyURLMap.put(defaultSiteLocale, defaultFriendlyURL);
549                    }
550    
551                    return friendlyURLMap;
552            }
553    
554            @Override
555            public String getFriendlyURLsXML() {
556                    Map<Locale, String> friendlyURLMap = getFriendlyURLMap();
557    
558                    return LocalizationUtil.updateLocalization(
559                            friendlyURLMap, StringPool.BLANK, "FriendlyURL",
560                            LocaleUtil.toLanguageId(LocaleUtil.getSiteDefault()));
561            }
562    
563            /**
564             * Returns the current layout's group.
565             *
566             * <p>
567             * Group is Liferay's technical name for a site.
568             * </p>
569             *
570             * @return the current layout's group
571             */
572            @Override
573            public Group getGroup() throws PortalException {
574                    return GroupLocalServiceUtil.getGroup(getGroupId());
575            }
576    
577            /**
578             * Returns the current layout's HTML title for the given locale, or the
579             * current layout's name for the given locale if no HTML title is
580             * configured.
581             *
582             * @param  locale the locale that the HTML title should be retrieved for
583             * @return the current layout's HTML title for the given locale, or the
584             *         current layout's name for the given locale if no HTML title is
585             *         configured
586             */
587            @Override
588            public String getHTMLTitle(Locale locale) {
589                    String localeLanguageId = LocaleUtil.toLanguageId(locale);
590    
591                    return getHTMLTitle(localeLanguageId);
592            }
593    
594            /**
595             * Returns the current layout's HTML title for the given locale language ID,
596             * or the current layout's name if no HTML title is configured.
597             *
598             * @param  localeLanguageId the locale that the HTML title should be
599             *         retrieved for
600             * @return the current layout's HTML title for the given locale language ID,
601             *         or the current layout's name if no HTML title is configured
602             */
603            @Override
604            public String getHTMLTitle(String localeLanguageId) {
605                    String htmlTitle = getTitle(localeLanguageId);
606    
607                    if (Validator.isNull(htmlTitle)) {
608                            htmlTitle = getName(localeLanguageId);
609                    }
610    
611                    return htmlTitle;
612            }
613    
614            /**
615             * Returns <code>true</code> if the current layout has a configured icon.
616             *
617             * @return <code>true</code> if the current layout has a configured icon;
618             *         <code>false</code> otherwise
619             */
620            @Override
621            public boolean getIconImage() {
622                    if (getIconImageId() > 0) {
623                            return true;
624                    }
625    
626                    return false;
627            }
628    
629            /**
630             * Returns the current layout's {@link LayoutSet}.
631             *
632             * @return the current layout's layout set
633             */
634            @Override
635            public LayoutSet getLayoutSet() throws PortalException {
636                    if (_layoutSet == null) {
637                            _layoutSet = LayoutSetLocalServiceUtil.getLayoutSet(
638                                    getGroupId(), isPrivateLayout());
639                    }
640    
641                    return _layoutSet;
642            }
643    
644            /**
645             * Returns the current layout's {@link LayoutType}.
646             *
647             * @return the current layout's layout type
648             */
649            @Override
650            public LayoutType getLayoutType() {
651                    if (_layoutType == null) {
652                            _layoutType = LayoutTypePortletFactoryUtil.create(this);
653                    }
654    
655                    return _layoutType;
656            }
657    
658            /**
659             * Returns the current layout's linked layout.
660             *
661             * @return the current layout's linked layout, or <code>null</code> if no
662             *         linked layout could be found
663             */
664            @Override
665            public Layout getLinkedToLayout() {
666                    long linkToLayoutId = GetterUtil.getLong(
667                            getTypeSettingsProperty("linkToLayoutId"));
668    
669                    if (linkToLayoutId <= 0) {
670                            return null;
671                    }
672    
673                    return LayoutLocalServiceUtil.fetchLayout(
674                            getGroupId(), isPrivateLayout(), linkToLayoutId);
675            }
676    
677            /**
678             * Returns the current layout's parent plid.
679             *
680             * @return the current layout's parent plid, or <code>0</code> if the
681             *         current layout is the topmost parent layout
682             */
683            @Override
684            public long getParentPlid() throws PortalException {
685                    if (getParentLayoutId() == LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
686                            return 0;
687                    }
688    
689                    Layout layout = LayoutLocalServiceUtil.getLayout(
690                            getGroupId(), isPrivateLayout(), getParentLayoutId());
691    
692                    return layout.getPlid();
693            }
694    
695            @Override
696            public String getRegularURL(HttpServletRequest request)
697                    throws PortalException {
698    
699                    return _getURL(request, false, false);
700            }
701    
702            @Override
703            public String getResetLayoutURL(HttpServletRequest request)
704                    throws PortalException {
705    
706                    return _getURL(request, true, true);
707            }
708    
709            @Override
710            public String getResetMaxStateURL(HttpServletRequest request)
711                    throws PortalException {
712    
713                    return _getURL(request, true, false);
714            }
715    
716            @Override
717            public Group getScopeGroup() throws PortalException {
718                    Group group = null;
719    
720                    try {
721                            group = GroupLocalServiceUtil.getLayoutGroup(
722                                    getCompanyId(), getPlid());
723                    }
724                    catch (NoSuchGroupException nsge) {
725                    }
726    
727                    return group;
728            }
729    
730            @Override
731            public String getTarget() {
732                    return PortalUtil.getLayoutTarget(this);
733            }
734    
735            /**
736             * Returns the current layout's theme, or the layout set's theme if no
737             * layout theme is configured.
738             *
739             * @return the current layout's theme, or the layout set's theme if no
740             *         layout theme is configured
741             */
742            @Override
743            public Theme getTheme() throws PortalException {
744                    if (isInheritLookAndFeel()) {
745                            LayoutSet layoutSet = getLayoutSet();
746    
747                            return layoutSet.getTheme();
748                    }
749                    else {
750                            return ThemeLocalServiceUtil.getTheme(
751                                    getCompanyId(), getThemeId(), false);
752                    }
753            }
754    
755            @Override
756            public String getThemeSetting(String key, String device) {
757                    return getThemeSetting(key, device, isInheritLookAndFeel());
758            }
759    
760            @Override
761            public String getThemeSetting(
762                    String key, String device, boolean inheritLookAndFeel) {
763    
764                    UnicodeProperties typeSettingsProperties = getTypeSettingsProperties();
765    
766                    String value = typeSettingsProperties.getProperty(
767                            ThemeSettingImpl.namespaceProperty(device, key));
768    
769                    if (value != null) {
770                            return value;
771                    }
772    
773                    return getDefaultThemeSetting(key, device, inheritLookAndFeel);
774            }
775    
776            @Override
777            public String getTypeSettings() {
778                    if (_typeSettingsProperties == null) {
779                            return super.getTypeSettings();
780                    }
781                    else {
782                            return _typeSettingsProperties.toString();
783                    }
784            }
785    
786            @Override
787            public UnicodeProperties getTypeSettingsProperties() {
788                    if (_typeSettingsProperties == null) {
789                            _typeSettingsProperties = new UnicodeProperties(true);
790    
791                            _typeSettingsProperties.fastLoad(super.getTypeSettings());
792                    }
793    
794                    return _typeSettingsProperties;
795            }
796    
797            @Override
798            public String getTypeSettingsProperty(String key) {
799                    UnicodeProperties typeSettingsProperties = getTypeSettingsProperties();
800    
801                    return typeSettingsProperties.getProperty(key);
802            }
803    
804            @Override
805            public String getTypeSettingsProperty(String key, String defaultValue) {
806                    UnicodeProperties typeSettingsProperties = getTypeSettingsProperties();
807    
808                    return typeSettingsProperties.getProperty(key, defaultValue);
809            }
810    
811            @Override
812            public ColorScheme getWapColorScheme() throws PortalException {
813                    if (isInheritLookAndFeel()) {
814                            LayoutSet layoutSet = getLayoutSet();
815    
816                            return layoutSet.getWapColorScheme();
817                    }
818                    else {
819                            Theme theme = getWapTheme();
820    
821                            return ThemeLocalServiceUtil.getColorScheme(
822                                    getCompanyId(), theme.getThemeId(), getWapColorSchemeId(),
823                                    true);
824                    }
825            }
826    
827            @Override
828            public Theme getWapTheme() throws PortalException {
829                    if (isInheritWapLookAndFeel()) {
830                            LayoutSet layoutSet = getLayoutSet();
831    
832                            return layoutSet.getWapTheme();
833                    }
834                    else {
835                            return ThemeLocalServiceUtil.getTheme(
836                                    getCompanyId(), getWapThemeId(), true);
837                    }
838            }
839    
840            /**
841             * Returns <code>true</code> if the given layout ID matches one of the
842             * current layout's hierarchical parents.
843             *
844             * @param  layoutId the layout ID to search for in the current layout's
845             *         parent list
846             * @return <code>true</code> if the given layout ID matches one of the
847             *         current layout's hierarchical parents; <code>false</code>
848             *         otherwise
849             */
850            @Override
851            public boolean hasAncestor(long layoutId) throws PortalException {
852                    long parentLayoutId = getParentLayoutId();
853    
854                    while (parentLayoutId != LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
855                            if (parentLayoutId == layoutId) {
856                                    return true;
857                            }
858    
859                            Layout parentLayout = LayoutLocalServiceUtil.getLayout(
860                                    getGroupId(), isPrivateLayout(), parentLayoutId);
861    
862                            parentLayoutId = parentLayout.getParentLayoutId();
863                    }
864    
865                    return false;
866            }
867    
868            /**
869             * Returns <code>true</code> if the current layout has child layouts.
870             *
871             * @return <code>true</code> if the current layout has child layouts,
872             *         <code>false</code> otherwise
873             */
874            @Override
875            public boolean hasChildren() {
876                    return LayoutLocalServiceUtil.hasLayouts(
877                            getGroupId(), isPrivateLayout(), getLayoutId());
878            }
879    
880            @Override
881            public boolean hasScopeGroup() throws PortalException {
882                    Group group = getScopeGroup();
883    
884                    if (group != null) {
885                            return true;
886                    }
887                    else {
888                            return false;
889                    }
890            }
891    
892            @Override
893            public boolean hasSetModifiedDate() {
894                    return true;
895            }
896    
897            @Override
898            public boolean includeLayoutContent(
899                            HttpServletRequest request, HttpServletResponse response)
900                    throws Exception {
901    
902                    LayoutType layoutType = getLayoutType();
903    
904                    LayoutTypeController layoutTypeController =
905                            layoutType.getLayoutTypeController();
906    
907                    return layoutTypeController.includeLayoutContent(
908                            request, response, this);
909            }
910    
911            @Override
912            public boolean isChildSelected(boolean selectable, Layout layout)
913                    throws PortalException {
914    
915                    if (selectable) {
916                            long plid = getPlid();
917    
918                            List<Layout> ancestors = layout.getAncestors();
919    
920                            for (Layout curLayout : ancestors) {
921                                    if (plid == curLayout.getPlid()) {
922                                            return true;
923                                    }
924                            }
925                    }
926    
927                    return false;
928            }
929    
930            /**
931             * Returns <code>true</code> if the current layout can be used as a content
932             * display page.
933             *
934             * <p>
935             * A content display page must have an Asset Publisher portlet that is
936             * configured as the default Asset Publisher for the layout.
937             * </p>
938             *
939             * @return <code>true</code> if the current layout can be used as a content
940             *         display page; <code>false</code> otherwise
941             */
942            @Override
943            public boolean isContentDisplayPage() {
944                    UnicodeProperties typeSettingsProperties = getTypeSettingsProperties();
945    
946                    String defaultAssetPublisherPortletId =
947                            typeSettingsProperties.getProperty(
948                                    LayoutTypePortletConstants.DEFAULT_ASSET_PUBLISHER_PORTLET_ID);
949    
950                    if (Validator.isNotNull(defaultAssetPublisherPortletId)) {
951                            return true;
952                    }
953    
954                    return false;
955            }
956    
957            /**
958             * Returns <code>true</code> if the current layout is the first layout in
959             * its parent's hierarchical list of children layouts.
960             *
961             * @return <code>true</code> if the current layout is the first layout in
962             *         its parent's hierarchical list of children layouts;
963             *         <code>false</code> otherwise
964             */
965            @Override
966            public boolean isFirstChild() {
967                    if (getPriority() == 0) {
968                            return true;
969                    }
970    
971                    return false;
972            }
973    
974            /**
975             * Returns <code>true</code> if the current layout is the topmost parent
976             * layout.
977             *
978             * @return <code>true</code> if the current layout is the topmost parent
979             *         layout; <code>false</code> otherwise
980             */
981            @Override
982            public boolean isFirstParent() {
983                    if (isFirstChild() && isRootLayout()) {
984                            return true;
985                    }
986    
987                    return false;
988            }
989    
990            @Override
991            public boolean isIconImage() {
992                    return getIconImage();
993            }
994    
995            /**
996             * Returns <code>true</code> if the current layout utilizes its {@link
997             * LayoutSet}'s look and feel options (e.g. theme and color scheme).
998             *
999             * @return <code>true</code> if the current layout utilizes its layout set's
1000             *         look and feel options; <code>false</code> otherwise
1001             */
1002            @Override
1003            public boolean isInheritLookAndFeel() {
1004                    if (Validator.isNull(getThemeId()) ||
1005                            Validator.isNull(getColorSchemeId())) {
1006    
1007                            return true;
1008                    }
1009    
1010                    return false;
1011            }
1012    
1013            @Override
1014            public boolean isInheritWapLookAndFeel() {
1015                    if (Validator.isNull(getWapThemeId()) ||
1016                            Validator.isNull(getWapColorSchemeId())) {
1017    
1018                            return true;
1019                    }
1020    
1021                    return false;
1022            }
1023    
1024            /**
1025             * Returns <code>true</code> if the current layout is built from a layout
1026             * template and still maintains an active connection to it.
1027             *
1028             * @return <code>true</code> if the current layout is built from a layout
1029             *         template and still maintains an active connection to it;
1030             *         <code>false</code> otherwise
1031             */
1032            @Override
1033            public boolean isLayoutPrototypeLinkActive() {
1034                    if (isLayoutPrototypeLinkEnabled() &&
1035                            Validator.isNotNull(getLayoutPrototypeUuid())) {
1036    
1037                            return true;
1038                    }
1039    
1040                    return false;
1041            }
1042    
1043            /**
1044             * Returns <code>true</code> if the current layout is part of the public
1045             * {@link LayoutSet}.
1046             *
1047             * <p>
1048             * Note, the returned value reflects the layout's default access options,
1049             * not its access permissions.
1050             * </p>
1051             *
1052             * @return <code>true</code> if the current layout is part of the public
1053             *         layout set; <code>false</code> otherwise
1054             */
1055            @Override
1056            public boolean isPublicLayout() {
1057                    return !isPrivateLayout();
1058            }
1059    
1060            /**
1061             * Returns <code>true</code> if the current layout is the root layout.
1062             *
1063             * @return <code>true</code> if the current layout is the root layout;
1064             *         <code>false</code> otherwise
1065             */
1066            @Override
1067            public boolean isRootLayout() {
1068                    if (getParentLayoutId() == LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
1069                            return true;
1070                    }
1071    
1072                    return false;
1073            }
1074    
1075            @Override
1076            public boolean isSelected(
1077                    boolean selectable, Layout layout, long ancestorPlid) {
1078    
1079                    if (selectable) {
1080                            long plid = getPlid();
1081    
1082                            if ((plid == layout.getPlid()) || (plid == ancestorPlid)) {
1083                                    return true;
1084                            }
1085                    }
1086    
1087                    return false;
1088            }
1089    
1090            /**
1091             * Returns <code>true</code> if the current layout can hold embedded
1092             * portlets.
1093             *
1094             * @return <code>true</code> if the current layout can hold embedded
1095             *         portlets; <code>false</code> otherwise
1096             */
1097            @Override
1098            public boolean isSupportsEmbeddedPortlets() {
1099                    if (isTypeEmbedded() || isTypePanel() || isTypePortlet()) {
1100                            return true;
1101                    }
1102    
1103                    return false;
1104            }
1105    
1106            /**
1107             * @deprecated As of 7.0.0, with no direct replacement
1108             */
1109            @Deprecated
1110            @Override
1111            public boolean isTypeArticle() {
1112                    return false;
1113            }
1114    
1115            @Override
1116            public boolean isTypeControlPanel() {
1117                    if (Validator.equals(getType(), LayoutConstants.TYPE_CONTROL_PANEL) ||
1118                            Validator.equals(
1119                                    _getLayoutTypeControllerType(),
1120                                    LayoutConstants.TYPE_CONTROL_PANEL)) {
1121    
1122                            return true;
1123                    }
1124    
1125                    return false;
1126            }
1127    
1128            @Override
1129            public boolean isTypeEmbedded() {
1130                    if (Validator.equals(getType(), LayoutConstants.TYPE_EMBEDDED) ||
1131                            Validator.equals(
1132                                    _getLayoutTypeControllerType(),
1133                                    LayoutConstants.TYPE_EMBEDDED)) {
1134    
1135                            return true;
1136                    }
1137    
1138                    return false;
1139            }
1140    
1141            @Override
1142            public boolean isTypeLinkToLayout() {
1143                    if (Validator.equals(getType(), LayoutConstants.TYPE_LINK_TO_LAYOUT) ||
1144                            Validator.equals(
1145                                    _getLayoutTypeControllerType(),
1146                                    LayoutConstants.TYPE_LINK_TO_LAYOUT)) {
1147    
1148                            return true;
1149                    }
1150    
1151                    return false;
1152            }
1153    
1154            @Override
1155            public boolean isTypePanel() {
1156                    if (Validator.equals(getType(), LayoutConstants.TYPE_PANEL) ||
1157                            Validator.equals(
1158                                    _getLayoutTypeControllerType(), LayoutConstants.TYPE_PANEL)) {
1159    
1160                            return true;
1161                    }
1162    
1163                    return false;
1164            }
1165    
1166            @Override
1167            public boolean isTypePortlet() {
1168                    if (Validator.equals(getType(), LayoutConstants.TYPE_PORTLET) ||
1169                            Validator.equals(
1170                                    _getLayoutTypeControllerType(), LayoutConstants.TYPE_PORTLET)) {
1171    
1172                            return true;
1173                    }
1174    
1175                    return false;
1176            }
1177    
1178            @Override
1179            public boolean isTypeSharedPortlet() {
1180                    if (Validator.equals(getType(), LayoutConstants.TYPE_SHARED_PORTLET)) {
1181                            return true;
1182                    }
1183    
1184                    return false;
1185            }
1186    
1187            @Override
1188            public boolean isTypeURL() {
1189                    if (Validator.equals(getType(), LayoutConstants.TYPE_URL)) {
1190                            return true;
1191                    }
1192    
1193                    return false;
1194            }
1195    
1196            @Override
1197            public boolean matches(HttpServletRequest request, String friendlyURL) {
1198                    LayoutType layoutType = getLayoutType();
1199    
1200                    LayoutTypeController layoutTypeController =
1201                            layoutType.getLayoutTypeController();
1202    
1203                    return layoutTypeController.matches(request, friendlyURL, this);
1204            }
1205    
1206            @Override
1207            public void setGroupId(long groupId) {
1208                    super.setGroupId(groupId);
1209    
1210                    _layoutSet = null;
1211            }
1212    
1213            @Override
1214            public void setLayoutSet(LayoutSet layoutSet) {
1215                    _layoutSet = layoutSet;
1216            }
1217    
1218            @Override
1219            public void setPrivateLayout(boolean privateLayout) {
1220                    super.setPrivateLayout(privateLayout);
1221    
1222                    _layoutSet = null;
1223            }
1224    
1225            @Override
1226            public void setTypeSettings(String typeSettings) {
1227                    _typeSettingsProperties = null;
1228    
1229                    super.setTypeSettings(typeSettings);
1230            }
1231    
1232            @Override
1233            public void setTypeSettingsProperties(
1234                    UnicodeProperties typeSettingsProperties) {
1235    
1236                    _typeSettingsProperties = typeSettingsProperties;
1237    
1238                    super.setTypeSettings(_typeSettingsProperties.toString());
1239            }
1240    
1241            protected Theme getTheme(String device) throws PortalException {
1242                    if (device.equals("regular")) {
1243                            return getTheme();
1244                    }
1245                    else {
1246                            return getWapTheme();
1247                    }
1248            }
1249    
1250            private static String _getFriendlyURLKeyword(String friendlyURL) {
1251                    friendlyURL = StringUtil.toLowerCase(friendlyURL);
1252    
1253                    for (String keyword : _friendlyURLKeywords) {
1254                            if (friendlyURL.startsWith(keyword)) {
1255                                    return keyword;
1256                            }
1257    
1258                            if (keyword.equals(friendlyURL + StringPool.SLASH)) {
1259                                    return friendlyURL;
1260                            }
1261                    }
1262    
1263                    return null;
1264            }
1265    
1266            private static void _initFriendlyURLKeywords() {
1267                    _friendlyURLKeywords =
1268                            new String[PropsValues.LAYOUT_FRIENDLY_URL_KEYWORDS.length];
1269    
1270                    for (int i = 0; i < PropsValues.LAYOUT_FRIENDLY_URL_KEYWORDS.length;
1271                                    i++) {
1272    
1273                            String keyword = PropsValues.LAYOUT_FRIENDLY_URL_KEYWORDS[i];
1274    
1275                            keyword = StringPool.SLASH + keyword;
1276    
1277                            if (!keyword.contains(StringPool.PERIOD)) {
1278                                    if (keyword.endsWith(StringPool.STAR)) {
1279                                            keyword = keyword.substring(0, keyword.length() - 1);
1280                                    }
1281                                    else {
1282                                            keyword = keyword + StringPool.SLASH;
1283                                    }
1284                            }
1285    
1286                            _friendlyURLKeywords[i] = StringUtil.toLowerCase(keyword);
1287                    }
1288            }
1289    
1290            private String _getLayoutTypeControllerType() {
1291                    LayoutType layoutType = getLayoutType();
1292    
1293                    LayoutTypeController layoutTypeController =
1294                            layoutType.getLayoutTypeController();
1295    
1296                    return layoutTypeController.getType();
1297            }
1298    
1299            private LayoutTypePortlet _getLayoutTypePortletClone(
1300                            HttpServletRequest request)
1301                    throws IOException {
1302    
1303                    LayoutTypePortlet layoutTypePortlet = null;
1304    
1305                    LayoutClone layoutClone = LayoutCloneFactory.getInstance();
1306    
1307                    if (layoutClone != null) {
1308                            String typeSettings = layoutClone.get(request, getPlid());
1309    
1310                            if (typeSettings != null) {
1311                                    UnicodeProperties typeSettingsProperties =
1312                                            new UnicodeProperties(true);
1313    
1314                                    typeSettingsProperties.load(typeSettings);
1315    
1316                                    String stateMax = typeSettingsProperties.getProperty(
1317                                            LayoutTypePortletConstants.STATE_MAX);
1318                                    String stateMin = typeSettingsProperties.getProperty(
1319                                            LayoutTypePortletConstants.STATE_MIN);
1320    
1321                                    Layout layout = (Layout)this.clone();
1322    
1323                                    layoutTypePortlet = (LayoutTypePortlet)layout.getLayoutType();
1324    
1325                                    layoutTypePortlet.setStateMax(stateMax);
1326                                    layoutTypePortlet.setStateMin(stateMin);
1327                            }
1328                    }
1329    
1330                    if (layoutTypePortlet == null) {
1331                            layoutTypePortlet = (LayoutTypePortlet)getLayoutType();
1332                    }
1333    
1334                    return layoutTypePortlet;
1335            }
1336    
1337            private String _getURL(
1338                            HttpServletRequest request, boolean resetMaxState,
1339                            boolean resetRenderParameters)
1340                    throws PortalException {
1341    
1342                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
1343                            WebKeys.THEME_DISPLAY);
1344    
1345                    if (resetMaxState) {
1346                            Layout layout = themeDisplay.getLayout();
1347    
1348                            LayoutTypePortlet layoutTypePortlet = null;
1349    
1350                            if (layout.equals(this)) {
1351                                    layoutTypePortlet = themeDisplay.getLayoutTypePortlet();
1352                            }
1353                            else {
1354                                    try {
1355                                            layoutTypePortlet = _getLayoutTypePortletClone(request);
1356                                    }
1357                                    catch (IOException ioe) {
1358                                            _log.error("Unable to clone layout settings", ioe);
1359    
1360                                            layoutTypePortlet = (LayoutTypePortlet)getLayoutType();
1361                                    }
1362                            }
1363    
1364                            if (layoutTypePortlet.hasStateMax()) {
1365                                    String portletId = StringUtil.split(
1366                                            layoutTypePortlet.getStateMax())[0];
1367    
1368                                    PortletURLImpl portletURLImpl = new PortletURLImpl(
1369                                            request, portletId, getPlid(), PortletRequest.ACTION_PHASE);
1370    
1371                                    try {
1372                                            portletURLImpl.setWindowState(WindowState.NORMAL);
1373                                            portletURLImpl.setPortletMode(PortletMode.VIEW);
1374                                    }
1375                                    catch (PortletException pe) {
1376                                            throw new SystemException(pe);
1377                                    }
1378    
1379                                    portletURLImpl.setAnchor(false);
1380    
1381                                    if (PropsValues.LAYOUT_DEFAULT_P_L_RESET &&
1382                                            !resetRenderParameters) {
1383    
1384                                            portletURLImpl.setParameter("p_l_reset", "0");
1385                                    }
1386                                    else if (!PropsValues.LAYOUT_DEFAULT_P_L_RESET &&
1387                                                     resetRenderParameters) {
1388    
1389                                            portletURLImpl.setParameter("p_l_reset", "1");
1390                                    }
1391    
1392                                    return portletURLImpl.toString();
1393                            }
1394                    }
1395    
1396                    String portalURL = PortalUtil.getPortalURL(request);
1397    
1398                    String url = PortalUtil.getLayoutURL(this, themeDisplay);
1399    
1400                    if (!CookieKeys.hasSessionId(request) &&
1401                            (url.startsWith(portalURL) || url.startsWith(StringPool.SLASH))) {
1402    
1403                            HttpSession session = request.getSession();
1404    
1405                            url = PortalUtil.getURLWithSessionId(url, session.getId());
1406                    }
1407    
1408                    if (!resetMaxState) {
1409                            return url;
1410                    }
1411    
1412                    if (PropsValues.LAYOUT_DEFAULT_P_L_RESET && !resetRenderParameters) {
1413                            url = HttpUtil.addParameter(url, "p_l_reset", 0);
1414                    }
1415                    else if (!PropsValues.LAYOUT_DEFAULT_P_L_RESET &&
1416                                     resetRenderParameters) {
1417    
1418                            url = HttpUtil.addParameter(url, "p_l_reset", 1);
1419                    }
1420    
1421                    return url;
1422            }
1423    
1424            private static final Log _log = LogFactoryUtil.getLog(LayoutImpl.class);
1425    
1426            private static String[] _friendlyURLKeywords;
1427    
1428            static {
1429                    _initFriendlyURLKeywords();
1430            }
1431    
1432            private LayoutSet _layoutSet;
1433            private transient LayoutType _layoutType;
1434            private UnicodeProperties _typeSettingsProperties;
1435    
1436    }