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