1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.model.impl;
24  
25  import com.liferay.portal.LayoutFriendlyURLException;
26  import com.liferay.portal.PortalException;
27  import com.liferay.portal.SystemException;
28  import com.liferay.portal.kernel.security.permission.ActionKeys;
29  import com.liferay.portal.kernel.security.permission.PermissionChecker;
30  import com.liferay.portal.kernel.util.LocaleUtil;
31  import com.liferay.portal.kernel.util.NullSafeProperties;
32  import com.liferay.portal.kernel.util.PropertiesUtil;
33  import com.liferay.portal.kernel.util.StringPool;
34  import com.liferay.portal.kernel.util.StringUtil;
35  import com.liferay.portal.kernel.util.Validator;
36  import com.liferay.portal.model.ColorScheme;
37  import com.liferay.portal.model.Group;
38  import com.liferay.portal.model.Layout;
39  import com.liferay.portal.model.LayoutSet;
40  import com.liferay.portal.model.LayoutType;
41  import com.liferay.portal.model.LayoutTypePortlet;
42  import com.liferay.portal.model.Theme;
43  import com.liferay.portal.service.GroupLocalServiceUtil;
44  import com.liferay.portal.service.LayoutLocalServiceUtil;
45  import com.liferay.portal.service.LayoutSetLocalServiceUtil;
46  import com.liferay.portal.service.impl.ThemeLocalUtil;
47  import com.liferay.portal.service.permission.LayoutPermissionUtil;
48  import com.liferay.portal.theme.ThemeDisplay;
49  import com.liferay.portal.util.CookieKeys;
50  import com.liferay.portal.util.LayoutClone;
51  import com.liferay.portal.util.LayoutCloneFactory;
52  import com.liferay.portal.util.PortalUtil;
53  import com.liferay.portal.util.PropsUtil;
54  import com.liferay.portal.util.PropsValues;
55  import com.liferay.portal.util.WebKeys;
56  import com.liferay.portlet.PortletURLImpl;
57  import com.liferay.util.Http;
58  import com.liferay.util.LocalizationUtil;
59  
60  import java.io.IOException;
61  
62  import java.util.ArrayList;
63  import java.util.Iterator;
64  import java.util.List;
65  import java.util.Locale;
66  import java.util.Properties;
67  
68  import javax.portlet.PortletException;
69  import javax.portlet.PortletMode;
70  import javax.portlet.WindowState;
71  
72  import javax.servlet.http.HttpServletRequest;
73  
74  import org.apache.commons.logging.Log;
75  import org.apache.commons.logging.LogFactory;
76  
77  /**
78   * <a href="LayoutImpl.java.html"><b><i>View Source</i></b></a>
79   *
80   * @author Brian Wing Shun Chan
81   *
82   */
83  public class LayoutImpl extends LayoutModelImpl implements Layout {
84  
85      public static final long DEFAULT_PLID = 0;
86  
87      public static final long DEFAULT_PARENT_LAYOUT_ID = 0;
88  
89      public static final String[] TYPES =
90          PropsUtil.getArray(PropsUtil.LAYOUT_TYPES);
91  
92      public static final String TYPE_PORTLET = "portlet";
93  
94      public static final String TYPE_EMBEDDED = "embedded";
95  
96      public static final String TYPE_URL = "url";
97  
98      public static final String TYPE_ARTICLE = "article";
99  
100     public static int validateFriendlyURL(String friendlyURL) {
101         if (friendlyURL.length() < 2) {
102             return LayoutFriendlyURLException.TOO_SHORT;
103         }
104 
105         if (!friendlyURL.startsWith(StringPool.SLASH)) {
106             return LayoutFriendlyURLException.DOES_NOT_START_WITH_SLASH;
107         }
108 
109         if (friendlyURL.endsWith(StringPool.SLASH)) {
110             return LayoutFriendlyURLException.ENDS_WITH_SLASH;
111         }
112 
113         if (friendlyURL.indexOf(StringPool.DOUBLE_SLASH) != -1) {
114             return LayoutFriendlyURLException.ADJACENT_SLASHES;
115         }
116 
117         char[] c = friendlyURL.toCharArray();
118 
119         for (int i = 0; i < c.length; i++) {
120             if ((!Validator.isChar(c[i])) && (!Validator.isDigit(c[i])) &&
121                 (c[i] != '/') && (c[i] != '-') && (c[i] != '_') &&
122                 (c[i] != '.')) {
123 
124                 return LayoutFriendlyURLException.INVALID_CHARACTERS;
125             }
126         }
127 
128         return -1;
129     }
130 
131     public static void validateFriendlyURLKeyword(String friendlyURL)
132         throws LayoutFriendlyURLException {
133 
134         String[] keywords = PropsUtil.getArray(
135             PropsUtil.LAYOUT_FRIENDLY_URL_KEYWORDS);
136 
137         for (int i = 0; i < keywords.length; i++) {
138             String keyword = keywords[i];
139 
140             if ((friendlyURL.indexOf(
141                     StringPool.SLASH + keyword + StringPool.SLASH) != -1) ||
142                 (friendlyURL.endsWith(StringPool.SLASH + keyword))) {
143 
144                 LayoutFriendlyURLException lfurle =
145                     new LayoutFriendlyURLException(
146                         LayoutFriendlyURLException.KEYWORD_CONFLICT);
147 
148                 lfurle.setKeywordConflict(keyword);
149 
150                 throw lfurle;
151             }
152         }
153     }
154 
155     public LayoutImpl() {
156     }
157 
158     public Group getGroup() {
159         Group group = null;
160 
161         try {
162             group = GroupLocalServiceUtil.getGroup(getGroupId());
163         }
164         catch (Exception e) {
165             group = new GroupImpl();
166 
167             _log.error(e);
168         }
169 
170         return group;
171     }
172 
173     /**
174      * @deprecated Will always return true.
175      */
176     public boolean isShared() {
177         return true;
178     }
179 
180     public long getAncestorPlid() {
181         long ancestorPlid = 0;
182 
183         try {
184             Layout ancestorLayout = this;
185 
186             while (true) {
187                 if (!ancestorLayout.isRootLayout()) {
188                     ancestorLayout = LayoutLocalServiceUtil.getLayout(
189                         ancestorLayout.getGroupId(),
190                         ancestorLayout.isPrivateLayout(),
191                         ancestorLayout.getParentLayoutId());
192                 }
193                 else {
194                     ancestorPlid = ancestorLayout.getPlid();
195 
196                     break;
197                 }
198             }
199         }
200         catch (Exception e) {
201             _log.error(e);
202         }
203 
204         return ancestorPlid;
205     }
206 
207     public long getAncestorLayoutId() {
208         long ancestorLayoutId = 0;
209 
210         try {
211             Layout ancestorLayout = this;
212 
213             while (true) {
214                 if (!ancestorLayout.isRootLayout()) {
215                     ancestorLayout = LayoutLocalServiceUtil.getLayout(
216                         ancestorLayout.getGroupId(),
217                         ancestorLayout.isPrivateLayout(),
218                         ancestorLayout.getParentLayoutId());
219                 }
220                 else {
221                     ancestorLayoutId = ancestorLayout.getLayoutId();
222 
223                     break;
224                 }
225             }
226         }
227         catch (Exception e) {
228             _log.error(e);
229         }
230 
231         return ancestorLayoutId;
232     }
233 
234     public List getAncestors() throws SystemException, PortalException {
235         List ancestors = new ArrayList();
236 
237         Layout layout = this;
238 
239         while (true) {
240             if (!layout.isRootLayout()) {
241                 layout = LayoutLocalServiceUtil.getLayout(
242                     layout.getGroupId(), layout.isPrivateLayout(),
243                     layout.getParentLayoutId());
244 
245                 ancestors.add(layout);
246             }
247             else {
248                 break;
249             }
250         }
251 
252         return ancestors;
253     }
254 
255     public boolean hasAncestor(long layoutId)
256         throws PortalException, SystemException {
257 
258         long parentLayoutId = getParentLayoutId();
259 
260         while (isRootLayout()) {
261             if (parentLayoutId == layoutId) {
262                 return true;
263             }
264             else {
265                 Layout parentLayout = LayoutLocalServiceUtil.getLayout(
266                     getGroupId(), isPrivateLayout(), parentLayoutId);
267 
268                 parentLayoutId = parentLayout.getParentLayoutId();
269             }
270         }
271 
272         return false;
273     }
274 
275     public boolean isFirstParent() {
276         if (isFirstChild() && isRootLayout()) {
277             return true;
278         }
279         else {
280             return false;
281         }
282     }
283 
284     public boolean isFirstChild() {
285         if (getPriority() == 0) {
286             return true;
287         }
288         else {
289             return false;
290         }
291     }
292 
293     public boolean isRootLayout() {
294         if (this.getParentLayoutId() == LayoutImpl.DEFAULT_PARENT_LAYOUT_ID) {
295             return true;
296         }
297         else {
298             return false;
299         }
300     }
301 
302     public List getChildren() throws PortalException, SystemException {
303         return LayoutLocalServiceUtil.getLayouts(
304             getGroupId(), isPrivateLayout(), getLayoutId());
305     }
306 
307     public List getAllChildren() throws PortalException, SystemException {
308         List allChildren = new ArrayList();
309 
310         Iterator itr = getChildren().iterator();
311 
312         while (itr.hasNext()) {
313             Layout child = (Layout)itr.next();
314 
315             allChildren.add(child);
316             allChildren.addAll(child.getChildren());
317         }
318 
319         return allChildren;
320     }
321 
322     public List getChildren(PermissionChecker permissionChecker)
323         throws PortalException, SystemException {
324 
325         List layouts = getChildren();
326 
327         Iterator itr = layouts.iterator();
328 
329         while (itr.hasNext()) {
330             Layout layout = (Layout)itr.next();
331 
332             if (layout.isHidden() ||
333                 !LayoutPermissionUtil.contains(
334                     permissionChecker, layout, ActionKeys.VIEW)) {
335 
336                 itr.remove();
337             }
338         }
339 
340         return layouts;
341     }
342 
343     public String getName(Locale locale) {
344         String localeLanguageId = LocaleUtil.toLanguageId(locale);
345 
346         return getName(localeLanguageId);
347     }
348 
349     public String getName(String localeLanguageId) {
350         return LocalizationUtil.getLocalization(getName(), localeLanguageId);
351     }
352 
353     public String getName(Locale locale, boolean useDefault) {
354         String localeLanguageId = LocaleUtil.toLanguageId(locale);
355 
356         return getName(localeLanguageId, useDefault);
357     }
358 
359     public String getName(String localeLanguageId, boolean useDefault) {
360         return LocalizationUtil.getLocalization(
361             getName(), localeLanguageId, useDefault);
362     }
363 
364     public void setName(String name, Locale locale) {
365         String localeLanguageId = LocaleUtil.toLanguageId(locale);
366 
367         setName(
368             LocalizationUtil.updateLocalization(
369                 getName(), "name", name, localeLanguageId));
370     }
371 
372     public String getTitle(Locale locale) {
373         String localeLanguageId = LocaleUtil.toLanguageId(locale);
374 
375         return getTitle(localeLanguageId);
376     }
377 
378     public String getTitle(String localeLanguageId) {
379         return LocalizationUtil.getLocalization(getTitle(), localeLanguageId);
380     }
381 
382     public String getTitle(Locale locale, boolean useDefault) {
383         String localeLanguageId = LocaleUtil.toLanguageId(locale);
384 
385         return getTitle(localeLanguageId, useDefault);
386     }
387 
388     public String getTitle(String localeLanguageId, boolean useDefault) {
389         return LocalizationUtil.getLocalization(
390             getTitle(), localeLanguageId, useDefault);
391     }
392 
393     public String getHTMLTitle(Locale locale) {
394         String localeLanguageId = LocaleUtil.toLanguageId(locale);
395 
396         return getHTMLTitle(localeLanguageId);
397     }
398 
399     public String getHTMLTitle(String localeLanguageId) {
400         String htmlTitle = getTitle(localeLanguageId);
401 
402         if (Validator.isNull(htmlTitle)) {
403             htmlTitle = getName(localeLanguageId);
404         }
405 
406         return htmlTitle;
407     }
408 
409     public void setTitle(String title, Locale locale) {
410         String localeLanguageId = LocaleUtil.toLanguageId(locale);
411 
412         setTitle(
413             LocalizationUtil.updateLocalization(
414                 getTitle(), "title", title, localeLanguageId));
415     }
416 
417     public LayoutType getLayoutType() {
418         return new LayoutTypePortletImpl(this);
419     }
420 
421     public String getTypeSettings() {
422         if (_typeSettingsProperties == null) {
423             return super.getTypeSettings();
424         }
425         else {
426             return PropertiesUtil.toString(_typeSettingsProperties);
427         }
428     }
429 
430     public void setTypeSettings(String typeSettings) {
431         _typeSettingsProperties = null;
432 
433         super.setTypeSettings(typeSettings);
434     }
435 
436     public Properties getTypeSettingsProperties() {
437         if (_typeSettingsProperties == null) {
438             _typeSettingsProperties = new NullSafeProperties();
439 
440             try {
441                 PropertiesUtil.load(
442                     _typeSettingsProperties, super.getTypeSettings());
443             }
444             catch (IOException ioe) {
445                 _log.error(ioe);
446             }
447         }
448 
449         return _typeSettingsProperties;
450     }
451 
452     public void setTypeSettingsProperties(Properties typeSettingsProperties) {
453         _typeSettingsProperties = typeSettingsProperties;
454 
455         super.setTypeSettings(PropertiesUtil.toString(_typeSettingsProperties));
456     }
457 
458     public String getDefaultFriendlyURL() {
459         return StringPool.SLASH + getLayoutId();
460     }
461 
462     public LayoutSet getLayoutSet() {
463         LayoutSet layoutSet = null;
464 
465         try {
466             layoutSet = LayoutSetLocalServiceUtil.getLayoutSet(
467                 getGroupId(), isPrivateLayout());
468         }
469         catch (Exception e) {
470             layoutSet = new LayoutSetImpl();
471 
472             _log.error(e);
473         }
474 
475         return layoutSet;
476     }
477 
478     public boolean isInheritLookAndFeel() {
479         if (Validator.isNull(getThemeId()) ||
480             Validator.isNull(getColorSchemeId())) {
481 
482             return true;
483         }
484         else {
485             return false;
486         }
487     }
488 
489     public Theme getTheme() throws PortalException, SystemException {
490         if (isInheritLookAndFeel()) {
491             return getLayoutSet().getTheme();
492         }
493         else {
494             return ThemeLocalUtil.getTheme(getCompanyId(), getThemeId(), false);
495         }
496     }
497 
498     public ColorScheme getColorScheme()
499         throws PortalException, SystemException {
500 
501         if (isInheritLookAndFeel()) {
502             return getLayoutSet().getColorScheme();
503         }
504         else {
505             return ThemeLocalUtil.getColorScheme(
506                 getCompanyId(), getTheme().getThemeId(), getColorSchemeId(),
507                 false);
508         }
509     }
510 
511     public boolean isInheritWapLookAndFeel() {
512         if (Validator.isNull(getWapThemeId()) ||
513             Validator.isNull(getWapColorSchemeId())) {
514 
515             return true;
516         }
517         else {
518             return false;
519         }
520     }
521 
522     public Theme getWapTheme() throws PortalException, SystemException {
523         if (isInheritWapLookAndFeel()) {
524             return getLayoutSet().getWapTheme();
525         }
526         else {
527             return ThemeLocalUtil.getTheme(
528                 getCompanyId(), getWapThemeId(), true);
529         }
530     }
531 
532     public ColorScheme getWapColorScheme()
533         throws PortalException, SystemException {
534 
535         if (isInheritLookAndFeel()) {
536             return getLayoutSet().getWapColorScheme();
537         }
538         else {
539             return ThemeLocalUtil.getColorScheme(
540                 getCompanyId(), getWapTheme().getThemeId(),
541                 getWapColorSchemeId(), true);
542         }
543     }
544 
545     public String getCssText() throws PortalException, SystemException {
546         if (isInheritLookAndFeel()) {
547             return getLayoutSet().getCss();
548         }
549         else {
550             return getCss();
551         }
552     }
553 
554     public String getRegularURL(HttpServletRequest req)
555         throws PortalException, SystemException {
556 
557         return _getURL(req, false, false);
558     }
559 
560     public String getResetMaxStateURL(HttpServletRequest req)
561         throws PortalException, SystemException {
562 
563         return _getURL(req, true, false);
564     }
565 
566     public String getResetLayoutURL(HttpServletRequest req)
567         throws PortalException, SystemException {
568 
569         return _getURL(req, true, true);
570     }
571 
572     public String getTarget() {
573         return PortalUtil.getLayoutTarget(this);
574     }
575 
576     public boolean isSelected(
577         boolean selectable, Layout layout, long ancestorPlid) {
578 
579         if (selectable) {
580             long plid = getPlid();
581 
582             if ((plid == layout.getPlid()) || (plid == ancestorPlid)) {
583                 return true;
584             }
585         }
586 
587         return false;
588     }
589 
590     private LayoutTypePortlet _getLayoutTypePortletClone(
591             HttpServletRequest req)
592         throws IOException {
593 
594         LayoutTypePortlet layoutTypePortlet = null;
595 
596         LayoutClone layoutClone = LayoutCloneFactory.getInstance();
597 
598         if (layoutClone != null) {
599             String typeSettings = layoutClone.get(req, getPlid());
600 
601             if (typeSettings != null) {
602                 Properties props = new NullSafeProperties();
603 
604                 PropertiesUtil.load(props, typeSettings);
605 
606                 String stateMax = props.getProperty(
607                     LayoutTypePortletImpl.STATE_MAX);
608                 String stateMin = props.getProperty(
609                     LayoutTypePortletImpl.STATE_MIN);
610 
611                 Layout layout = (Layout)((LayoutImpl)this).clone();
612 
613                 layoutTypePortlet = (LayoutTypePortlet)layout.getLayoutType();
614 
615                 layoutTypePortlet.setStateMax(stateMax);
616                 layoutTypePortlet.setStateMin(stateMin);
617             }
618         }
619 
620         if (layoutTypePortlet == null) {
621             layoutTypePortlet = (LayoutTypePortlet)getLayoutType();
622         }
623 
624         return layoutTypePortlet;
625     }
626 
627     private String _getURL(
628             HttpServletRequest req, boolean resetMaxState,
629             boolean resetRenderParameters)
630         throws PortalException, SystemException {
631 
632         ThemeDisplay themeDisplay =
633             (ThemeDisplay)req.getAttribute(WebKeys.THEME_DISPLAY);
634 
635         if (resetMaxState) {
636             Layout layout = themeDisplay.getLayout();
637 
638             LayoutTypePortlet layoutTypePortlet = null;
639 
640             if (layout.equals(this)) {
641                 layoutTypePortlet = themeDisplay.getLayoutTypePortlet();
642             }
643             else {
644                 try {
645                     layoutTypePortlet = _getLayoutTypePortletClone(req);
646                 }
647                 catch (IOException ioe) {
648                     _log.error("Unable to clone layout settings", ioe);
649 
650                     layoutTypePortlet = (LayoutTypePortlet)getLayoutType();
651                 }
652             }
653 
654             if (layoutTypePortlet.hasStateMax()) {
655                 String portletId =
656                     StringUtil.split(layoutTypePortlet.getStateMax())[0];
657 
658                 PortletURLImpl portletURLImpl = new PortletURLImpl(
659                     req, portletId, getPlid(), true);
660 
661                 try {
662                     portletURLImpl.setWindowState(WindowState.NORMAL);
663                     portletURLImpl.setPortletMode(PortletMode.VIEW);
664                 }
665                 catch (PortletException pe) {
666                     throw new SystemException(pe);
667                 }
668 
669                 portletURLImpl.setAnchor(false);
670 
671                 if (PropsValues.LAYOUT_DEFAULT_P_L_RESET &&
672                     !resetRenderParameters) {
673 
674                     portletURLImpl.setParameter("p_l_reset", "0");
675                 }
676                 else if (!PropsValues.LAYOUT_DEFAULT_P_L_RESET &&
677                          resetRenderParameters) {
678 
679                     portletURLImpl.setParameter("p_l_reset", "1");
680                 }
681 
682                 return portletURLImpl.toString();
683             }
684         }
685 
686         String url = PortalUtil.getLayoutURL(this, themeDisplay);
687 
688         if (!CookieKeys.hasSessionId(req)) {
689             url = PortalUtil.getURLWithSessionId(url, req.getSession().getId());
690         }
691 
692         if (!resetMaxState && !resetMaxState) {
693             return url;
694         }
695 
696         if (PropsValues.LAYOUT_DEFAULT_P_L_RESET && !resetRenderParameters) {
697             url = Http.addParameter(url, "p_l_reset", 0);
698         }
699         else if (!PropsValues.LAYOUT_DEFAULT_P_L_RESET &&
700                  resetRenderParameters) {
701 
702             url = Http.addParameter(url, "p_l_reset", 1);
703         }
704 
705         return url;
706     }
707 
708     private static Log _log = LogFactory.getLog(LayoutImpl.class);
709 
710     private Properties _typeSettingsProperties = null;
711 
712 }