001
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.log.Log;
022 import com.liferay.portal.kernel.log.LogFactoryUtil;
023 import com.liferay.portal.kernel.util.CharPool;
024 import com.liferay.portal.kernel.util.CookieKeys;
025 import com.liferay.portal.kernel.util.HttpUtil;
026 import com.liferay.portal.kernel.util.ListUtil;
027 import com.liferay.portal.kernel.util.LocaleUtil;
028 import com.liferay.portal.kernel.util.StringPool;
029 import com.liferay.portal.kernel.util.StringUtil;
030 import com.liferay.portal.kernel.util.UnicodeProperties;
031 import com.liferay.portal.kernel.util.Validator;
032 import com.liferay.portal.model.ColorScheme;
033 import com.liferay.portal.model.Group;
034 import com.liferay.portal.model.Layout;
035 import com.liferay.portal.model.LayoutConstants;
036 import com.liferay.portal.model.LayoutSet;
037 import com.liferay.portal.model.LayoutType;
038 import com.liferay.portal.model.LayoutTypePortlet;
039 import com.liferay.portal.model.LayoutTypePortletConstants;
040 import com.liferay.portal.model.Theme;
041 import com.liferay.portal.security.permission.ActionKeys;
042 import com.liferay.portal.security.permission.PermissionChecker;
043 import com.liferay.portal.service.GroupLocalServiceUtil;
044 import com.liferay.portal.service.LayoutLocalServiceUtil;
045 import com.liferay.portal.service.LayoutSetLocalServiceUtil;
046 import com.liferay.portal.service.ThemeLocalServiceUtil;
047 import com.liferay.portal.service.permission.LayoutPermissionUtil;
048 import com.liferay.portal.theme.ThemeDisplay;
049 import com.liferay.portal.util.LayoutClone;
050 import com.liferay.portal.util.LayoutCloneFactory;
051 import com.liferay.portal.util.PortalUtil;
052 import com.liferay.portal.util.PropsValues;
053 import com.liferay.portal.util.WebKeys;
054 import com.liferay.portlet.PortletURLImpl;
055
056 import java.io.IOException;
057
058 import java.util.ArrayList;
059 import java.util.Iterator;
060 import java.util.List;
061 import java.util.Locale;
062
063 import javax.portlet.PortletException;
064 import javax.portlet.PortletMode;
065 import javax.portlet.PortletRequest;
066 import javax.portlet.WindowState;
067
068 import javax.servlet.http.HttpServletRequest;
069
070
073 public class LayoutImpl extends LayoutBaseImpl {
074
075 public static boolean hasFriendlyURLKeyword(String friendlyURL) {
076 String keyword = _getFriendlyURLKeyword(friendlyURL);
077
078 if (Validator.isNotNull(keyword)) {
079 return true;
080 }
081
082 return false;
083 }
084
085 public static int validateFriendlyURL(String friendlyURL) {
086 return validateFriendlyURL(friendlyURL, true);
087 }
088
089 public static int validateFriendlyURL(
090 String friendlyURL, boolean checkMaxLength) {
091
092 if (friendlyURL.length() < 2) {
093 return LayoutFriendlyURLException.TOO_SHORT;
094 }
095
096 if (checkMaxLength &&
097 (friendlyURL.length() > LayoutConstants.FRIENDLY_URL_MAX_LENGTH)) {
098
099 return LayoutFriendlyURLException.TOO_LONG;
100 }
101
102 if (!friendlyURL.startsWith(StringPool.SLASH)) {
103 return LayoutFriendlyURLException.DOES_NOT_START_WITH_SLASH;
104 }
105
106 if (friendlyURL.endsWith(StringPool.SLASH)) {
107 return LayoutFriendlyURLException.ENDS_WITH_SLASH;
108 }
109
110 if (friendlyURL.contains(StringPool.DOUBLE_SLASH)) {
111 return LayoutFriendlyURLException.ADJACENT_SLASHES;
112 }
113
114 for (char c : friendlyURL.toCharArray()) {
115 if (!Validator.isChar(c) && !Validator.isDigit(c) &&
116 (c != CharPool.DASH) && (c != CharPool.PERCENT) &&
117 (c != CharPool.PERIOD) && (c != CharPool.PLUS) &&
118 (c != CharPool.SLASH) && (c != CharPool.STAR) &&
119 (c != CharPool.UNDERLINE)) {
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 keyword = _getFriendlyURLKeyword(friendlyURL);
132
133 if (Validator.isNotNull(keyword)) {
134 LayoutFriendlyURLException lfurle =
135 new LayoutFriendlyURLException(
136 LayoutFriendlyURLException.KEYWORD_CONFLICT);
137
138 lfurle.setKeywordConflict(keyword);
139
140 throw lfurle;
141 }
142 }
143
144 public LayoutImpl() {
145 }
146
147 public List<Layout> getAllChildren() throws SystemException {
148 List<Layout> layouts = new ArrayList<Layout>();
149
150 for (Layout layout : getChildren()) {
151 layouts.add(layout);
152 layouts.addAll(layout.getAllChildren());
153 }
154
155 return layouts;
156 }
157
158 public long getAncestorLayoutId() throws PortalException, SystemException {
159 long layoutId = 0;
160
161 Layout layout = this;
162
163 while (true) {
164 if (!layout.isRootLayout()) {
165 layout = LayoutLocalServiceUtil.getLayout(
166 layout.getGroupId(), layout.isPrivateLayout(),
167 layout.getParentLayoutId());
168 }
169 else {
170 layoutId = layout.getLayoutId();
171
172 break;
173 }
174 }
175
176 return layoutId;
177 }
178
179 public long getAncestorPlid() throws PortalException, SystemException {
180 long plid = 0;
181
182 Layout layout = this;
183
184 while (true) {
185 if (!layout.isRootLayout()) {
186 layout = LayoutLocalServiceUtil.getLayout(
187 layout.getGroupId(), layout.isPrivateLayout(),
188 layout.getParentLayoutId());
189 }
190 else {
191 plid = layout.getPlid();
192
193 break;
194 }
195 }
196
197 return plid;
198 }
199
200 public List<Layout> getAncestors() throws PortalException, SystemException {
201 List<Layout> layouts = new ArrayList<Layout>();
202
203 Layout layout = this;
204
205 while (true) {
206 if (!layout.isRootLayout()) {
207 layout = LayoutLocalServiceUtil.getLayout(
208 layout.getGroupId(), layout.isPrivateLayout(),
209 layout.getParentLayoutId());
210
211 layouts.add(layout);
212 }
213 else {
214 break;
215 }
216 }
217
218 return layouts;
219 }
220
221 public List<Layout> getChildren() throws SystemException {
222 return LayoutLocalServiceUtil.getLayouts(
223 getGroupId(), isPrivateLayout(), getLayoutId());
224 }
225
226 public List<Layout> getChildren(PermissionChecker permissionChecker)
227 throws PortalException, SystemException {
228
229 List<Layout> layouts = ListUtil.copy(getChildren());
230
231 Iterator<Layout> itr = layouts.iterator();
232
233 while (itr.hasNext()) {
234 Layout layout = itr.next();
235
236 if (layout.isHidden() ||
237 !LayoutPermissionUtil.contains(
238 permissionChecker, layout, ActionKeys.VIEW)) {
239
240 itr.remove();
241 }
242 }
243
244 return layouts;
245 }
246
247 public ColorScheme getColorScheme()
248 throws PortalException, SystemException {
249
250 if (isInheritLookAndFeel()) {
251 return getLayoutSet().getColorScheme();
252 }
253 else {
254 return ThemeLocalServiceUtil.getColorScheme(
255 getCompanyId(), getTheme().getThemeId(), getColorSchemeId(),
256 false);
257 }
258 }
259
260 public String getCssText() throws PortalException, SystemException {
261 if (isInheritLookAndFeel()) {
262 return getLayoutSet().getCss();
263 }
264 else {
265 return getCss();
266 }
267 }
268
269 public Group getGroup() throws PortalException, SystemException {
270 return GroupLocalServiceUtil.getGroup(getGroupId());
271 }
272
273 public String getHTMLTitle(Locale locale) {
274 String localeLanguageId = LocaleUtil.toLanguageId(locale);
275
276 return getHTMLTitle(localeLanguageId);
277 }
278
279 public String getHTMLTitle(String localeLanguageId) {
280 String htmlTitle = getTitle(localeLanguageId);
281
282 if (Validator.isNull(htmlTitle)) {
283 htmlTitle = getName(localeLanguageId);
284 }
285
286 return htmlTitle;
287 }
288
289 public LayoutSet getLayoutSet() throws PortalException, SystemException {
290 if (_layoutSet == null) {
291 _layoutSet = LayoutSetLocalServiceUtil.getLayoutSet(
292 getGroupId(), isPrivateLayout());
293 }
294
295 return _layoutSet;
296 }
297
298 public LayoutType getLayoutType() {
299 if (_layoutType == null) {
300 _layoutType = new LayoutTypePortletImpl(this);
301 }
302
303 return _layoutType;
304 }
305
306 public long getParentPlid() throws PortalException, SystemException {
307 if (getParentLayoutId() == LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
308 return 0;
309 }
310
311 Layout layout = LayoutLocalServiceUtil.getLayout(
312 getGroupId(), isPrivateLayout(), getParentLayoutId());
313
314 return layout.getPlid();
315 }
316
317 public String getRegularURL(HttpServletRequest request)
318 throws PortalException, SystemException {
319
320 return _getURL(request, false, false);
321 }
322
323 public String getResetLayoutURL(HttpServletRequest request)
324 throws PortalException, SystemException {
325
326 return _getURL(request, true, true);
327 }
328
329 public String getResetMaxStateURL(HttpServletRequest request)
330 throws PortalException, SystemException {
331
332 return _getURL(request, true, false);
333 }
334
335 public Group getScopeGroup() throws PortalException, SystemException {
336 Group group = null;
337
338 try {
339 group = GroupLocalServiceUtil.getLayoutGroup(
340 getCompanyId(), getPlid());
341 }
342 catch (NoSuchGroupException nsge) {
343 }
344
345 return group;
346 }
347
348 public String getTarget() {
349 return PortalUtil.getLayoutTarget(this);
350 }
351
352 public Theme getTheme() throws PortalException, SystemException {
353 if (isInheritLookAndFeel()) {
354 return getLayoutSet().getTheme();
355 }
356 else {
357 return ThemeLocalServiceUtil.getTheme(
358 getCompanyId(), getThemeId(), false);
359 }
360 }
361
362 public String getThemeSetting(String key, String device) {
363 UnicodeProperties typeSettingsProperties = getTypeSettingsProperties();
364
365 String value = typeSettingsProperties.getProperty(
366 ThemeSettingImpl.namespaceProperty(device, key));
367
368 if (value != null) {
369 return value;
370 }
371
372 try {
373 LayoutSet layoutSet = getLayoutSet();
374
375 value = layoutSet.getThemeSetting(key, device);
376 }
377 catch (Exception e) {
378 }
379
380 return value;
381 }
382
383 @Override
384 public String getTypeSettings() {
385 if (_typeSettingsProperties == null) {
386 return super.getTypeSettings();
387 }
388 else {
389 return _typeSettingsProperties.toString();
390 }
391 }
392
393 public UnicodeProperties getTypeSettingsProperties() {
394 if (_typeSettingsProperties == null) {
395 _typeSettingsProperties = new UnicodeProperties(true);
396
397 _typeSettingsProperties.fastLoad(super.getTypeSettings());
398 }
399
400 return _typeSettingsProperties;
401 }
402
403 public String getTypeSettingsProperty(String key) {
404 UnicodeProperties typeSettingsProperties = getTypeSettingsProperties();
405
406 return typeSettingsProperties.getProperty(key);
407 }
408
409 public String getTypeSettingsProperty(String key, String defaultValue) {
410 UnicodeProperties typeSettingsProperties = getTypeSettingsProperties();
411
412 return typeSettingsProperties.getProperty(key, defaultValue);
413 }
414
415 public ColorScheme getWapColorScheme()
416 throws PortalException, SystemException {
417
418 if (isInheritLookAndFeel()) {
419 return getLayoutSet().getWapColorScheme();
420 }
421 else {
422 return ThemeLocalServiceUtil.getColorScheme(
423 getCompanyId(), getWapTheme().getThemeId(),
424 getWapColorSchemeId(), true);
425 }
426 }
427
428 public Theme getWapTheme() throws PortalException, SystemException {
429 if (isInheritWapLookAndFeel()) {
430 return getLayoutSet().getWapTheme();
431 }
432 else {
433 return ThemeLocalServiceUtil.getTheme(
434 getCompanyId(), getWapThemeId(), true);
435 }
436 }
437
438 public boolean hasAncestor(long layoutId)
439 throws PortalException, SystemException {
440
441 long parentLayoutId = getParentLayoutId();
442
443 while (parentLayoutId != LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
444 if (parentLayoutId == layoutId) {
445 return true;
446 }
447 else {
448 Layout parentLayout = LayoutLocalServiceUtil.getLayout(
449 getGroupId(), isPrivateLayout(), parentLayoutId);
450
451 parentLayoutId = parentLayout.getParentLayoutId();
452 }
453 }
454
455 return false;
456 }
457
458 public boolean hasChildren() throws SystemException {
459 return LayoutLocalServiceUtil.hasLayouts(
460 getGroupId(), isPrivateLayout(), getLayoutId());
461 }
462
463 public boolean hasScopeGroup() throws PortalException, SystemException {
464 Group group = getScopeGroup();
465
466 if (group != null) {
467 return true;
468 }
469 else {
470 return false;
471 }
472 }
473
474 public boolean isChildSelected(boolean selectable, Layout layout)
475 throws PortalException, SystemException {
476
477 if (selectable) {
478 long plid = getPlid();
479
480 List<Layout> ancestors = layout.getAncestors();
481
482 for (Layout curLayout : ancestors) {
483 if (plid == curLayout.getPlid()) {
484 return true;
485 }
486 }
487 }
488
489 return false;
490 }
491
492 public boolean isContentDisplayPage() {
493 UnicodeProperties typeSettingsProperties = getTypeSettingsProperties();
494
495 String defaultAssetPublisherPortletId =
496 typeSettingsProperties.getProperty(
497 LayoutTypePortletConstants.DEFAULT_ASSET_PUBLISHER_PORTLET_ID);
498
499 if (Validator.isNotNull(defaultAssetPublisherPortletId)) {
500 return true;
501 }
502 else {
503 return false;
504 }
505 }
506
507 public boolean isFirstChild() {
508 if (getPriority() == 0) {
509 return true;
510 }
511 else {
512 return false;
513 }
514 }
515
516 public boolean isFirstParent() {
517 if (isFirstChild() && isRootLayout()) {
518 return true;
519 }
520 else {
521 return false;
522 }
523 }
524
525 public boolean isInheritLookAndFeel() {
526 if (Validator.isNull(getThemeId()) ||
527 Validator.isNull(getColorSchemeId())) {
528
529 return true;
530 }
531 else {
532 return false;
533 }
534 }
535
536 public boolean isInheritWapLookAndFeel() {
537 if (Validator.isNull(getWapThemeId()) ||
538 Validator.isNull(getWapColorSchemeId())) {
539
540 return true;
541 }
542 else {
543 return false;
544 }
545 }
546
547 public boolean isLayoutPrototypeLinkActive() {
548 if (isLayoutPrototypeLinkEnabled() &&
549 Validator.isNotNull(getLayoutPrototypeUuid())) {
550
551 return true;
552 }
553
554 return false;
555 }
556
557 public boolean isPublicLayout() {
558 return !isPrivateLayout();
559 }
560
561 public boolean isRootLayout() {
562 if (getParentLayoutId() == LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
563 return true;
564 }
565 else {
566 return false;
567 }
568 }
569
570 public boolean isSelected(
571 boolean selectable, Layout layout, long ancestorPlid) {
572
573 if (selectable) {
574 long plid = getPlid();
575
576 if ((plid == layout.getPlid()) || (plid == ancestorPlid)) {
577 return true;
578 }
579 }
580
581 return false;
582 }
583
584 public boolean isSupportsEmbeddedPortlets() {
585 if (isTypeArticle() || isTypeEmbedded() || isTypePanel() ||
586 isTypePortlet()) {
587
588 return true;
589 }
590
591 return false;
592 }
593
594 public boolean isTypeArticle() {
595 if (getType().equals(LayoutConstants.TYPE_ARTICLE)) {
596 return true;
597 }
598 else {
599 return false;
600 }
601 }
602
603 public boolean isTypeControlPanel() {
604 if (getType().equals(LayoutConstants.TYPE_CONTROL_PANEL)) {
605 return true;
606 }
607 else {
608 return false;
609 }
610 }
611
612 public boolean isTypeEmbedded() {
613 if (getType().equals(LayoutConstants.TYPE_EMBEDDED)) {
614 return true;
615 }
616 else {
617 return false;
618 }
619 }
620
621 public boolean isTypeLinkToLayout() {
622 if (getType().equals(LayoutConstants.TYPE_LINK_TO_LAYOUT)) {
623 return true;
624 }
625 else {
626 return false;
627 }
628 }
629
630 public boolean isTypePanel() {
631 if (getType().equals(LayoutConstants.TYPE_PANEL)) {
632 return true;
633 }
634 else {
635 return false;
636 }
637 }
638
639 public boolean isTypePortlet() {
640 if (getType().equals(LayoutConstants.TYPE_PORTLET)) {
641 return true;
642 }
643 else {
644 return false;
645 }
646 }
647
648 public boolean isTypeURL() {
649 if (getType().equals(LayoutConstants.TYPE_URL)) {
650 return true;
651 }
652 else {
653 return false;
654 }
655 }
656
657 @Override
658 public void setGroupId(long groupId) {
659 super.setGroupId(groupId);
660
661 _layoutSet = null;
662 }
663
664 public void setLayoutSet(LayoutSet layoutSet) {
665 _layoutSet = layoutSet;
666 }
667
668 @Override
669 public void setPrivateLayout(boolean privateLayout) {
670 super.setPrivateLayout(privateLayout);
671
672 _layoutSet = null;
673 }
674
675 @Override
676 public void setTypeSettings(String typeSettings) {
677 _typeSettingsProperties = null;
678
679 super.setTypeSettings(typeSettings);
680 }
681
682 public void setTypeSettingsProperties(
683 UnicodeProperties typeSettingsProperties) {
684
685 _typeSettingsProperties = typeSettingsProperties;
686
687 super.setTypeSettings(_typeSettingsProperties.toString());
688 }
689
690 private static String _getFriendlyURLKeyword(String friendlyURL) {
691 friendlyURL = friendlyURL.toLowerCase();
692
693 for (String keyword : _friendlyURLKeywords) {
694 if (friendlyURL.startsWith(keyword)) {
695 return keyword;
696 }
697
698 if (keyword.equals(friendlyURL + StringPool.SLASH)) {
699 return friendlyURL;
700 }
701 }
702
703 return null;
704 }
705
706 private static void _initFriendlyURLKeywords() {
707 _friendlyURLKeywords =
708 new String[PropsValues.LAYOUT_FRIENDLY_URL_KEYWORDS.length];
709
710 for (int i = 0; i < PropsValues.LAYOUT_FRIENDLY_URL_KEYWORDS.length;
711 i++) {
712
713 String keyword = PropsValues.LAYOUT_FRIENDLY_URL_KEYWORDS[i];
714
715 keyword = StringPool.SLASH + keyword;
716
717 if (!keyword.contains(StringPool.PERIOD)) {
718 if (keyword.endsWith(StringPool.STAR)) {
719 keyword = keyword.substring(0, keyword.length() - 1);
720 }
721 else {
722 keyword = keyword + StringPool.SLASH;
723 }
724 }
725
726 _friendlyURLKeywords[i] = keyword.toLowerCase();
727 }
728 }
729
730 private LayoutTypePortlet _getLayoutTypePortletClone(
731 HttpServletRequest request)
732 throws IOException {
733
734 LayoutTypePortlet layoutTypePortlet = null;
735
736 LayoutClone layoutClone = LayoutCloneFactory.getInstance();
737
738 if (layoutClone != null) {
739 String typeSettings = layoutClone.get(request, getPlid());
740
741 if (typeSettings != null) {
742 UnicodeProperties typeSettingsProperties =
743 new UnicodeProperties(true);
744
745 typeSettingsProperties.load(typeSettings);
746
747 String stateMax = typeSettingsProperties.getProperty(
748 LayoutTypePortletConstants.STATE_MAX);
749 String stateMin = typeSettingsProperties.getProperty(
750 LayoutTypePortletConstants.STATE_MIN);
751
752 Layout layout = (Layout)this.clone();
753
754 layoutTypePortlet = (LayoutTypePortlet)layout.getLayoutType();
755
756 layoutTypePortlet.setStateMax(stateMax);
757 layoutTypePortlet.setStateMin(stateMin);
758 }
759 }
760
761 if (layoutTypePortlet == null) {
762 layoutTypePortlet = (LayoutTypePortlet)getLayoutType();
763 }
764
765 return layoutTypePortlet;
766 }
767
768 private String _getURL(
769 HttpServletRequest request, boolean resetMaxState,
770 boolean resetRenderParameters)
771 throws PortalException, SystemException {
772
773 ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
774 WebKeys.THEME_DISPLAY);
775
776 if (resetMaxState) {
777 Layout layout = themeDisplay.getLayout();
778
779 LayoutTypePortlet layoutTypePortlet = null;
780
781 if (layout.equals(this)) {
782 layoutTypePortlet = themeDisplay.getLayoutTypePortlet();
783 }
784 else {
785 try {
786 layoutTypePortlet = _getLayoutTypePortletClone(request);
787 }
788 catch (IOException ioe) {
789 _log.error("Unable to clone layout settings", ioe);
790
791 layoutTypePortlet = (LayoutTypePortlet)getLayoutType();
792 }
793 }
794
795 if (layoutTypePortlet.hasStateMax()) {
796 String portletId = StringUtil.split(
797 layoutTypePortlet.getStateMax())[0];
798
799 PortletURLImpl portletURLImpl = new PortletURLImpl(
800 request, portletId, getPlid(), PortletRequest.ACTION_PHASE);
801
802 try {
803 portletURLImpl.setWindowState(WindowState.NORMAL);
804 portletURLImpl.setPortletMode(PortletMode.VIEW);
805 }
806 catch (PortletException pe) {
807 throw new SystemException(pe);
808 }
809
810 portletURLImpl.setAnchor(false);
811
812 if (PropsValues.LAYOUT_DEFAULT_P_L_RESET &&
813 !resetRenderParameters) {
814
815 portletURLImpl.setParameter("p_l_reset", "0");
816 }
817 else if (!PropsValues.LAYOUT_DEFAULT_P_L_RESET &&
818 resetRenderParameters) {
819
820 portletURLImpl.setParameter("p_l_reset", "1");
821 }
822
823 return portletURLImpl.toString();
824 }
825 }
826
827 String portalURL = PortalUtil.getPortalURL(request);
828
829 String url = PortalUtil.getLayoutURL(this, themeDisplay);
830
831 if (!CookieKeys.hasSessionId(request) &&
832 (url.startsWith(portalURL) || url.startsWith(StringPool.SLASH))) {
833
834 url = PortalUtil.getURLWithSessionId(
835 url, request.getSession().getId());
836 }
837
838 if (!resetMaxState) {
839 return url;
840 }
841
842 if (PropsValues.LAYOUT_DEFAULT_P_L_RESET && !resetRenderParameters) {
843 url = HttpUtil.addParameter(url, "p_l_reset", 0);
844 }
845 else if (!PropsValues.LAYOUT_DEFAULT_P_L_RESET &&
846 resetRenderParameters) {
847
848 url = HttpUtil.addParameter(url, "p_l_reset", 1);
849 }
850
851 return url;
852 }
853
854 private static Log _log = LogFactoryUtil.getLog(LayoutImpl.class);
855
856 private static String[] _friendlyURLKeywords;
857
858 private LayoutSet _layoutSet;
859 private LayoutType _layoutType;
860 private UnicodeProperties _typeSettingsProperties;
861
862 static {
863 _initFriendlyURLKeywords();
864 }
865
866 }