1   /**
2    * Copyright (c) 2000-2007 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.LayoutClone;
50  import com.liferay.portal.util.LayoutCloneFactory;
51  import com.liferay.portal.util.PortalUtil;
52  import com.liferay.portal.util.PropsUtil;
53  import com.liferay.portal.util.WebKeys;
54  import com.liferay.portlet.PortletURLImpl;
55  import com.liferay.util.Http;
56  import com.liferay.util.LocalizationUtil;
57  
58  import java.io.IOException;
59  
60  import java.util.ArrayList;
61  import java.util.Iterator;
62  import java.util.List;
63  import java.util.Locale;
64  import java.util.Properties;
65  
66  import javax.portlet.PortletException;
67  import javax.portlet.PortletMode;
68  import javax.portlet.WindowState;
69  
70  import javax.servlet.http.HttpServletRequest;
71  
72  import org.apache.commons.logging.Log;
73  import org.apache.commons.logging.LogFactory;
74  
75  /**
76   * <a href="LayoutImpl.java.html"><b><i>View Source</i></b></a>
77   *
78   * @author Brian Wing Shun Chan
79   *
80   */
81  public class LayoutImpl extends LayoutModelImpl implements Layout {
82  
83      public static final long DEFAULT_PLID = 0;
84  
85      public static final long DEFAULT_PARENT_LAYOUT_ID = 0;
86  
87      public static final String[] TYPES =
88          PropsUtil.getArray(PropsUtil.LAYOUT_TYPES);
89  
90      public static final String TYPE_PORTLET = "portlet";
91  
92      public static final String TYPE_EMBEDDED = "embedded";
93  
94      public static final String TYPE_URL = "url";
95  
96      public static final String TYPE_ARTICLE = "article";
97  
98      public static int validateFriendlyURL(String friendlyURL) {
99          if (friendlyURL.length() < 2) {
100             return LayoutFriendlyURLException.TOO_SHORT;
101         }
102 
103         if (!friendlyURL.startsWith(StringPool.SLASH)) {
104             return LayoutFriendlyURLException.DOES_NOT_START_WITH_SLASH;
105         }
106 
107         if (friendlyURL.endsWith(StringPool.SLASH)) {
108             return LayoutFriendlyURLException.ENDS_WITH_SLASH;
109         }
110 
111         if (friendlyURL.indexOf(StringPool.DOUBLE_SLASH) != -1) {
112             return LayoutFriendlyURLException.ADJACENT_SLASHES;
113         }
114 
115         char[] c = friendlyURL.toCharArray();
116 
117         for (int i = 0; i < c.length; i++) {
118             if ((!Validator.isChar(c[i])) && (!Validator.isDigit(c[i])) &&
119                 (c[i] != '/') && (c[i] != '-') && (c[i] != '_')) {
120 
121                 return LayoutFriendlyURLException.INVALID_CHARACTERS;
122             }
123         }
124 
125         return -1;
126     }
127 
128     public static void validateFriendlyURLKeyword(String friendlyURL)
129         throws LayoutFriendlyURLException {
130 
131         String[] keywords = PropsUtil.getArray(
132             PropsUtil.LAYOUT_FRIENDLY_URL_KEYWORDS);
133 
134         for (int i = 0; i < keywords.length; i++) {
135             String keyword = keywords[i];
136 
137             if ((friendlyURL.indexOf(
138                     StringPool.SLASH + keyword + StringPool.SLASH) != -1) ||
139                 (friendlyURL.endsWith(StringPool.SLASH + keyword))) {
140 
141                 LayoutFriendlyURLException lfurle =
142                     new LayoutFriendlyURLException(
143                         LayoutFriendlyURLException.KEYWORD_CONFLICT);
144 
145                 lfurle.setKeywordConflict(keyword);
146 
147                 throw lfurle;
148             }
149         }
150     }
151 
152     public LayoutImpl() {
153     }
154 
155     public Group getGroup() {
156         Group group = null;
157 
158         try {
159             group = GroupLocalServiceUtil.getGroup(getGroupId());
160         }
161         catch (Exception e) {
162             group = new GroupImpl();
163 
164             _log.error(e);
165         }
166 
167         return group;
168     }
169 
170     /**
171      * @deprecated Will always return true.
172      */
173     public boolean isShared() {
174         return true;
175     }
176 
177     public long getAncestorPlid() {
178         long ancestorPlid = 0;
179 
180         try {
181             Layout ancestorLayout = this;
182 
183             while (true) {
184                 if (!ancestorLayout.isRootLayout()) {
185                     ancestorLayout = LayoutLocalServiceUtil.getLayout(
186                         ancestorLayout.getGroupId(),
187                         ancestorLayout.isPrivateLayout(),
188                         ancestorLayout.getParentLayoutId());
189                 }
190                 else {
191                     ancestorPlid = ancestorLayout.getPlid();
192 
193                     break;
194                 }
195             }
196         }
197         catch (Exception e) {
198             _log.error(e);
199         }
200 
201         return ancestorPlid;
202     }
203 
204     public long getAncestorLayoutId() {
205         long ancestorLayoutId = 0;
206 
207         try {
208             Layout ancestorLayout = this;
209 
210             while (true) {
211                 if (!ancestorLayout.isRootLayout()) {
212                     ancestorLayout = LayoutLocalServiceUtil.getLayout(
213                         ancestorLayout.getGroupId(),
214                         ancestorLayout.isPrivateLayout(),
215                         ancestorLayout.getParentLayoutId());
216                 }
217                 else {
218                     ancestorLayoutId = ancestorLayout.getLayoutId();
219 
220                     break;
221                 }
222             }
223         }
224         catch (Exception e) {
225             _log.error(e);
226         }
227 
228         return ancestorLayoutId;
229     }
230 
231     public List getAncestors() throws SystemException, PortalException {
232         List ancestors = new ArrayList();
233 
234         Layout layout = this;
235 
236         while (true) {
237             if (!layout.isRootLayout()) {
238                 layout = LayoutLocalServiceUtil.getLayout(
239                     layout.getGroupId(), layout.isPrivateLayout(),
240                     layout.getParentLayoutId());
241 
242                 ancestors.add(layout);
243             }
244             else {
245                 break;
246             }
247         }
248 
249         return ancestors;
250     }
251 
252     public boolean hasAncestor(long layoutId)
253         throws PortalException, SystemException {
254 
255         long parentLayoutId = getParentLayoutId();
256 
257         while (isRootLayout()) {
258             if (parentLayoutId == layoutId) {
259                 return true;
260             }
261             else {
262                 Layout parentLayout = LayoutLocalServiceUtil.getLayout(
263                     getGroupId(), isPrivateLayout(), parentLayoutId);
264 
265                 parentLayoutId = parentLayout.getParentLayoutId();
266             }
267         }
268 
269         return false;
270     }
271 
272     public boolean isFirstParent() {
273         if (isFirstChild() && isRootLayout()) {
274             return true;
275         }
276         else {
277             return false;
278         }
279     }
280 
281     public boolean isFirstChild() {
282         if (getPriority() == 0) {
283             return true;
284         }
285         else {
286             return false;
287         }
288     }
289 
290     public boolean isRootLayout() {
291         if (this.getParentLayoutId() == LayoutImpl.DEFAULT_PARENT_LAYOUT_ID) {
292             return true;
293         }
294         else {
295             return false;
296         }
297     }
298 
299     public List getChildren() throws PortalException, SystemException {
300         return LayoutLocalServiceUtil.getLayouts(
301             getGroupId(), isPrivateLayout(), getLayoutId());
302     }
303 
304     public List getChildren(PermissionChecker permissionChecker)
305         throws PortalException, SystemException {
306 
307         List layouts = getChildren();
308 
309         Iterator itr = layouts.iterator();
310 
311         while (itr.hasNext()) {
312             Layout layout = (Layout)itr.next();
313 
314             if (layout.isHidden() ||
315                 !LayoutPermissionUtil.contains(
316                     permissionChecker, layout, ActionKeys.VIEW)) {
317 
318                 itr.remove();
319             }
320         }
321 
322         return layouts;
323     }
324 
325     public String getName(Locale locale) {
326         String localeLanguageId = LocaleUtil.toLanguageId(locale);
327 
328         return getName(localeLanguageId);
329     }
330 
331     public String getName(String localeLanguageId) {
332         return LocalizationUtil.getLocalization(getName(), localeLanguageId);
333     }
334 
335     public void setName(String name, Locale locale) {
336         String localeLanguageId = LocaleUtil.toLanguageId(locale);
337 
338         setName(
339             LocalizationUtil.updateLocalization(
340                 getName(), "name", name, localeLanguageId));
341     }
342 
343     public String getTitle(Locale locale) {
344         String localeLanguageId = LocaleUtil.toLanguageId(locale);
345 
346         return getTitle(localeLanguageId);
347     }
348 
349     public String getTitle(String localeLanguageId) {
350         return LocalizationUtil.getLocalization(getTitle(), localeLanguageId);
351     }
352 
353     public String getHTMLTitle(Locale locale) {
354         String localeLanguageId = LocaleUtil.toLanguageId(locale);
355 
356         return getHTMLTitle(localeLanguageId);
357     }
358 
359     public String getHTMLTitle(String localeLanguageId) {
360         String htmlTitle = getTitle(localeLanguageId);
361 
362         if (Validator.isNull(htmlTitle)) {
363             htmlTitle = getName(localeLanguageId);
364         }
365 
366         return htmlTitle;
367     }
368 
369     public void setTitle(String title, Locale locale) {
370         String localeLanguageId = LocaleUtil.toLanguageId(locale);
371 
372         setTitle(
373             LocalizationUtil.updateLocalization(
374                 getTitle(), "title", title, localeLanguageId));
375     }
376 
377     public LayoutType getLayoutType() {
378         return new LayoutTypePortletImpl(this);
379     }
380 
381     public String getTypeSettings() {
382         if (_typeSettingsProperties == null) {
383             return super.getTypeSettings();
384         }
385         else {
386             return PropertiesUtil.toString(_typeSettingsProperties);
387         }
388     }
389 
390     public void setTypeSettings(String typeSettings) {
391         _typeSettingsProperties = null;
392 
393         super.setTypeSettings(typeSettings);
394     }
395 
396     public Properties getTypeSettingsProperties() {
397         if (_typeSettingsProperties == null) {
398             _typeSettingsProperties = new NullSafeProperties();
399 
400             try {
401                 PropertiesUtil.load(
402                     _typeSettingsProperties, super.getTypeSettings());
403             }
404             catch (IOException ioe) {
405                 _log.error(ioe);
406             }
407         }
408 
409         return _typeSettingsProperties;
410     }
411 
412     public void setTypeSettingsProperties(Properties typeSettingsProperties) {
413         _typeSettingsProperties = typeSettingsProperties;
414 
415         super.setTypeSettings(PropertiesUtil.toString(_typeSettingsProperties));
416     }
417 
418     public String getDefaultFriendlyURL() {
419         return StringPool.SLASH + getLayoutId();
420     }
421 
422     public LayoutSet getLayoutSet() {
423         LayoutSet layoutSet = null;
424 
425         try {
426             layoutSet = LayoutSetLocalServiceUtil.getLayoutSet(
427                 getGroupId(), isPrivateLayout());
428         }
429         catch (Exception e) {
430             layoutSet = new LayoutSetImpl();
431 
432             _log.error(e);
433         }
434 
435         return layoutSet;
436     }
437 
438     public boolean isInheritLookAndFeel() {
439         if (Validator.isNull(getThemeId()) ||
440             Validator.isNull(getColorSchemeId())) {
441 
442             return true;
443         }
444         else {
445             return false;
446         }
447     }
448 
449     public Theme getTheme() throws PortalException, SystemException {
450         if (isInheritLookAndFeel()) {
451             return getLayoutSet().getTheme();
452         }
453         else {
454             return ThemeLocalUtil.getTheme(getCompanyId(), getThemeId(), false);
455         }
456     }
457 
458     public ColorScheme getColorScheme()
459         throws PortalException, SystemException {
460 
461         if (isInheritLookAndFeel()) {
462             return getLayoutSet().getColorScheme();
463         }
464         else {
465             return ThemeLocalUtil.getColorScheme(
466                 getCompanyId(), getTheme().getThemeId(), getColorSchemeId(),
467                 false);
468         }
469     }
470 
471     public boolean isInheritWapLookAndFeel() {
472         if (Validator.isNull(getWapThemeId()) ||
473             Validator.isNull(getWapColorSchemeId())) {
474 
475             return true;
476         }
477         else {
478             return false;
479         }
480     }
481 
482     public Theme getWapTheme() throws PortalException, SystemException {
483         if (isInheritWapLookAndFeel()) {
484             return getLayoutSet().getWapTheme();
485         }
486         else {
487             return ThemeLocalUtil.getTheme(
488                 getCompanyId(), getWapThemeId(), true);
489         }
490     }
491 
492     public ColorScheme getWapColorScheme()
493         throws PortalException, SystemException {
494 
495         if (isInheritLookAndFeel()) {
496             return getLayoutSet().getWapColorScheme();
497         }
498         else {
499             return ThemeLocalUtil.getColorScheme(
500                 getCompanyId(), getWapTheme().getThemeId(),
501                 getWapColorSchemeId(), true);
502         }
503     }
504 
505     public String getCssText() throws PortalException, SystemException {
506         if (isInheritLookAndFeel()) {
507             return getLayoutSet().getCss();
508         }
509         else {
510             return getCss();
511         }
512     }
513 
514     public String getRegularURL(HttpServletRequest req)
515         throws PortalException, SystemException {
516 
517         return _getURL(req, false, false);
518     }
519 
520     public String getResetMaxStateURL(HttpServletRequest req)
521         throws PortalException, SystemException {
522 
523         return _getURL(req, true, false);
524     }
525 
526     public String getResetLayoutURL(HttpServletRequest req)
527         throws PortalException, SystemException {
528 
529         return _getURL(req, true, true);
530     }
531 
532     public String getTarget() {
533         return PortalUtil.getLayoutTarget(this);
534     }
535 
536     public boolean isSelected(
537         boolean selectable, Layout layout, long ancestorPlid) {
538 
539         if (selectable) {
540             long plid = getPlid();
541 
542             if ((plid == layout.getPlid()) || (plid == ancestorPlid)) {
543                 return true;
544             }
545         }
546 
547         return false;
548     }
549 
550     private LayoutTypePortlet _getLayoutTypePortletClone(
551             HttpServletRequest req)
552         throws IOException {
553 
554         LayoutTypePortlet layoutTypePortlet = null;
555 
556         LayoutClone layoutClone = LayoutCloneFactory.getInstance();
557 
558         if (layoutClone != null) {
559             String typeSettings = layoutClone.get(req, getPlid());
560 
561             if (typeSettings != null) {
562                 Properties props = new NullSafeProperties();
563 
564                 PropertiesUtil.load(props, typeSettings);
565 
566                 String stateMax = props.getProperty(
567                     LayoutTypePortletImpl.STATE_MAX);
568                 String stateMin = props.getProperty(
569                     LayoutTypePortletImpl.STATE_MIN);
570 
571                 Layout layout = (Layout)((LayoutImpl)this).clone();
572 
573                 layoutTypePortlet = (LayoutTypePortlet)layout.getLayoutType();
574 
575                 layoutTypePortlet.setStateMax(stateMax);
576                 layoutTypePortlet.setStateMin(stateMin);
577             }
578         }
579 
580         if (layoutTypePortlet == null) {
581             layoutTypePortlet = (LayoutTypePortlet)getLayoutType();
582         }
583 
584         return layoutTypePortlet;
585     }
586 
587     private String _getURL(
588             HttpServletRequest req, boolean resetMaxState,
589             boolean resetRenderParameters)
590         throws PortalException, SystemException {
591 
592         ThemeDisplay themeDisplay =
593             (ThemeDisplay)req.getAttribute(WebKeys.THEME_DISPLAY);
594 
595         if (resetMaxState) {
596             Layout layout = themeDisplay.getLayout();
597 
598             LayoutTypePortlet layoutTypePortlet = null;
599 
600             if (layout.equals(this)) {
601                 layoutTypePortlet = themeDisplay.getLayoutTypePortlet();
602             }
603             else {
604                 try {
605                     layoutTypePortlet = _getLayoutTypePortletClone(req);
606                 }
607                 catch (IOException ioe) {
608                     _log.error("Unable to clone layout settings", ioe);
609 
610                     layoutTypePortlet = (LayoutTypePortlet)getLayoutType();
611                 }
612             }
613 
614             if (layoutTypePortlet.hasStateMax()) {
615                 String portletId =
616                     StringUtil.split(layoutTypePortlet.getStateMax())[0];
617 
618                 PortletURLImpl portletURLImpl = new PortletURLImpl(
619                     req, portletId, getPlid(), true);
620 
621                 try {
622                     portletURLImpl.setWindowState(WindowState.NORMAL);
623                     portletURLImpl.setPortletMode(PortletMode.VIEW);
624                 }
625                 catch (PortletException pe) {
626                     throw new SystemException(pe);
627                 }
628 
629                 portletURLImpl.setAnchor(false);
630 
631                 if (PortalUtil.DEFAULT_P_L_RESET && !resetRenderParameters) {
632                     portletURLImpl.setParameter("p_l_reset", "0");
633                 }
634                 else if (!PortalUtil.DEFAULT_P_L_RESET &&
635                          resetRenderParameters) {
636 
637                     portletURLImpl.setParameter("p_l_reset", "1");
638                 }
639 
640                 return portletURLImpl.toString();
641             }
642         }
643 
644         String url = PortalUtil.getLayoutURL(this, themeDisplay);
645 
646         if (!resetMaxState && !resetMaxState) {
647             return url;
648         }
649 
650         if (PortalUtil.DEFAULT_P_L_RESET && !resetRenderParameters) {
651             url = Http.addParameter(url, "p_l_reset", 0);
652         }
653         else if (!PortalUtil.DEFAULT_P_L_RESET && resetRenderParameters) {
654             url = Http.addParameter(url, "p_l_reset", 1);
655         }
656 
657         return url;
658     }
659 
660     private static Log _log = LogFactory.getLog(LayoutImpl.class);
661 
662     private Properties _typeSettingsProperties = null;
663 
664 }