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.service.impl;
016    
017    import com.liferay.portal.LayoutFriendlyURLException;
018    import com.liferay.portal.LayoutFriendlyURLsException;
019    import com.liferay.portal.LayoutNameException;
020    import com.liferay.portal.LayoutParentLayoutIdException;
021    import com.liferay.portal.LayoutTypeException;
022    import com.liferay.portal.NoSuchLayoutException;
023    import com.liferay.portal.kernel.bean.BeanReference;
024    import com.liferay.portal.kernel.bean.IdentifiableBean;
025    import com.liferay.portal.kernel.exception.PortalException;
026    import com.liferay.portal.kernel.language.LanguageUtil;
027    import com.liferay.portal.kernel.portlet.FriendlyURLMapper;
028    import com.liferay.portal.kernel.util.FriendlyURLNormalizerUtil;
029    import com.liferay.portal.kernel.util.LocaleUtil;
030    import com.liferay.portal.kernel.util.StringPool;
031    import com.liferay.portal.kernel.util.StringUtil;
032    import com.liferay.portal.kernel.util.Validator;
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.LayoutFriendlyURL;
037    import com.liferay.portal.model.LayoutSet;
038    import com.liferay.portal.model.LayoutSetPrototype;
039    import com.liferay.portal.model.LayoutType;
040    import com.liferay.portal.model.LayoutTypeController;
041    import com.liferay.portal.model.ResourceConstants;
042    import com.liferay.portal.model.Role;
043    import com.liferay.portal.model.RoleConstants;
044    import com.liferay.portal.model.impl.LayoutImpl;
045    import com.liferay.portal.security.permission.ActionKeys;
046    import com.liferay.portal.service.PortletLocalServiceUtil;
047    import com.liferay.portal.service.ResourcePermissionLocalService;
048    import com.liferay.portal.service.RoleLocalServiceUtil;
049    import com.liferay.portal.service.persistence.LayoutFriendlyURLPersistence;
050    import com.liferay.portal.service.persistence.LayoutPersistence;
051    import com.liferay.portal.service.persistence.LayoutSetPersistence;
052    import com.liferay.portal.util.LayoutTypeControllerTracker;
053    import com.liferay.portal.util.Portal;
054    import com.liferay.portal.util.PortalUtil;
055    import com.liferay.portal.util.comparator.LayoutPriorityComparator;
056    import com.liferay.portlet.sites.util.SitesUtil;
057    
058    import java.util.HashMap;
059    import java.util.List;
060    import java.util.Locale;
061    import java.util.Map;
062    
063    /**
064     * @author Raymond Aug??
065     */
066    public class LayoutLocalServiceHelper implements IdentifiableBean {
067    
068            @Override
069            public String getBeanIdentifier() {
070                    return _beanIdentifier;
071            }
072    
073            public String getFriendlyURL(
074                            long groupId, boolean privateLayout, long layoutId, String name,
075                            String friendlyURL)
076                    throws PortalException {
077    
078                    friendlyURL = getFriendlyURL(friendlyURL);
079    
080                    if (Validator.isNotNull(friendlyURL)) {
081                            return friendlyURL;
082                    }
083    
084                    friendlyURL = StringPool.SLASH + getFriendlyURL(name);
085    
086                    String originalFriendlyURL = friendlyURL;
087    
088                    for (int i = 1;; i++) {
089                            try {
090                                    validateFriendlyURL(
091                                            groupId, privateLayout, layoutId, friendlyURL);
092    
093                                    break;
094                            }
095                            catch (LayoutFriendlyURLException lfurle) {
096                                    int type = lfurle.getType();
097    
098                                    if (type == LayoutFriendlyURLException.DUPLICATE) {
099                                            friendlyURL = originalFriendlyURL + i;
100                                    }
101                                    else {
102                                            friendlyURL = StringPool.SLASH + layoutId;
103    
104                                            break;
105                                    }
106                            }
107                    }
108    
109                    return friendlyURL;
110            }
111    
112            public String getFriendlyURL(String friendlyURL) {
113                    return FriendlyURLNormalizerUtil.normalize(friendlyURL);
114            }
115    
116            public Map<Locale, String> getFriendlyURLMap(
117                            long groupId, boolean privateLayout, long layoutId, String name,
118                            Map<Locale, String> friendlyURLMap)
119                    throws PortalException {
120    
121                    Map<Locale, String> newFriendlyURLMap = new HashMap<Locale, String>();
122    
123                    Locale[] locales = LanguageUtil.getAvailableLocales(groupId);
124    
125                    for (Locale locale : locales) {
126                            String friendlyURL = friendlyURLMap.get(locale);
127    
128                            if (Validator.isNotNull(friendlyURL)) {
129                                    friendlyURL = getFriendlyURL(
130                                            groupId, privateLayout, layoutId, name, friendlyURL);
131    
132                                    newFriendlyURLMap.put(locale, friendlyURL);
133                            }
134                    }
135    
136                    Locale siteDefaultLocale = LocaleUtil.getSiteDefault();
137    
138                    if (newFriendlyURLMap.isEmpty() ||
139                            Validator.isNull(newFriendlyURLMap.get(siteDefaultLocale))) {
140    
141                            String friendlyURL = getFriendlyURL(
142                                    groupId, privateLayout, layoutId, name, StringPool.BLANK);
143    
144                            newFriendlyURLMap.put(siteDefaultLocale, friendlyURL);
145                    }
146    
147                    return newFriendlyURLMap;
148            }
149    
150            public int getNextPriority(
151                    long groupId, boolean privateLayout, long parentLayoutId,
152                    String sourcePrototypeLayoutUuid, int defaultPriority) {
153    
154                    try {
155                            int priority = defaultPriority;
156    
157                            if (priority < 0) {
158                                    Layout layout = layoutPersistence.findByG_P_P_First(
159                                            groupId, privateLayout, parentLayoutId,
160                                            new LayoutPriorityComparator(false));
161    
162                                    priority = layout.getPriority() + 1;
163                            }
164    
165                            if ((priority < _PRIORITY_BUFFER) &&
166                                    Validator.isNull(sourcePrototypeLayoutUuid)) {
167    
168                                    LayoutSet layoutSet = layoutSetPersistence.fetchByG_P(
169                                            groupId, privateLayout);
170    
171                                    if (Validator.isNotNull(
172                                                    layoutSet.getLayoutSetPrototypeUuid()) &&
173                                            layoutSet.isLayoutSetPrototypeLinkEnabled()) {
174    
175                                            priority = priority + _PRIORITY_BUFFER;
176                                    }
177                            }
178    
179                            return priority;
180                    }
181                    catch (NoSuchLayoutException nsle) {
182                            return 0;
183                    }
184            }
185    
186            public long getParentLayoutId(
187                    long groupId, boolean privateLayout, long parentLayoutId) {
188    
189                    if (parentLayoutId != LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
190    
191                            // Ensure parent layout exists
192    
193                            Layout parentLayout = layoutPersistence.fetchByG_P_L(
194                                    groupId, privateLayout, parentLayoutId);
195    
196                            if (parentLayout == null) {
197                                    parentLayoutId = LayoutConstants.DEFAULT_PARENT_LAYOUT_ID;
198                            }
199                    }
200    
201                    return parentLayoutId;
202            }
203    
204            public boolean hasLayoutSetPrototypeLayout(
205                            LayoutSetPrototype layoutSetPrototype, String layoutUuid)
206                    throws PortalException {
207    
208                    Layout layout = layoutPersistence.fetchByUUID_G_P(
209                            layoutUuid, layoutSetPrototype.getGroupId(), true);
210    
211                    if (layout != null) {
212                            return true;
213                    }
214    
215                    return false;
216            }
217    
218            @Override
219            public void setBeanIdentifier(String beanIdentifier) {
220                    _beanIdentifier = beanIdentifier;
221            }
222    
223            public void validate(
224                            long groupId, boolean privateLayout, long layoutId,
225                            long parentLayoutId, String name, String type, boolean hidden,
226                            Map<Locale, String> friendlyURLMap)
227                    throws PortalException {
228    
229                    validateName(name);
230    
231                    boolean firstLayout = false;
232    
233                    if (parentLayoutId == LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
234                            List<Layout> layouts = layoutPersistence.findByG_P_P(
235                                    groupId, privateLayout, parentLayoutId, 0, 1);
236    
237                            if (layouts.isEmpty()) {
238                                    firstLayout = true;
239                            }
240                            else {
241                                    long firstLayoutId = layouts.get(0).getLayoutId();
242    
243                                    if (firstLayoutId == layoutId) {
244                                            firstLayout = true;
245                                    }
246                            }
247                    }
248                    else {
249    
250                            // Layout cannot become a child of a layout that is not sortable
251                            // because it is linked to a layout set prototype
252    
253                            Layout parentLayout = layoutPersistence.findByG_P_L(
254                                    groupId, privateLayout, parentLayoutId);
255    
256                            if (!SitesUtil.isLayoutSortable(parentLayout)) {
257                                    throw new LayoutParentLayoutIdException(
258                                            LayoutParentLayoutIdException.NOT_SORTABLE);
259                            }
260                    }
261    
262                    if (firstLayout) {
263                            validateFirstLayout(type);
264                    }
265    
266                    LayoutTypeController layoutTypeController =
267                            LayoutTypeControllerTracker.getLayoutTypeController(type);
268    
269                    if (!layoutTypeController.isParentable()) {
270                            if (layoutPersistence.countByG_P_P(
271                                            groupId, privateLayout, layoutId) > 0) {
272    
273                                    throw new LayoutTypeException(
274                                            LayoutTypeException.NOT_PARENTABLE);
275                            }
276                    }
277    
278                    validateFriendlyURLs(groupId, privateLayout, layoutId, friendlyURLMap);
279            }
280    
281            public void validateFirstLayout(Layout layout) throws PortalException {
282                    Group group = layout.getGroup();
283    
284                    if (group.isGuest() && layout.isPublicLayout() &&
285                            !hasGuestViewPermission(layout)) {
286    
287                            LayoutTypeException lte = new LayoutTypeException(
288                                    LayoutTypeException.FIRST_LAYOUT_PERMISSION);
289    
290                            throw lte;
291                    }
292    
293                    validateFirstLayout(layout.getType());
294            }
295    
296            public void validateFirstLayout(String type) throws PortalException {
297                    LayoutTypeController layoutTypeController =
298                            LayoutTypeControllerTracker.getLayoutTypeController(type);
299    
300                    if (Validator.isNull(type) || !layoutTypeController.isFirstPageable()) {
301                            LayoutTypeException lte = new LayoutTypeException(
302                                    LayoutTypeException.FIRST_LAYOUT);
303    
304                            lte.setLayoutType(type);
305    
306                            throw lte;
307                    }
308            }
309    
310            public void validateFriendlyURL(
311                            long groupId, boolean privateLayout, long layoutId,
312                            String friendlyURL)
313                    throws PortalException {
314    
315                    if (Validator.isNull(friendlyURL)) {
316                            return;
317                    }
318    
319                    int exceptionType = LayoutImpl.validateFriendlyURL(friendlyURL);
320    
321                    if (exceptionType != -1) {
322                            throw new LayoutFriendlyURLException(exceptionType);
323                    }
324    
325                    List<LayoutFriendlyURL> layoutFriendlyURLs =
326                            layoutFriendlyURLPersistence.findByG_P_F(
327                                    groupId, privateLayout, friendlyURL);
328    
329                    for (LayoutFriendlyURL layoutFriendlyURL : layoutFriendlyURLs) {
330                            Layout layout = layoutPersistence.findByPrimaryKey(
331                                    layoutFriendlyURL.getPlid());
332    
333                            if (layout.getLayoutId() != layoutId) {
334                                    LayoutFriendlyURLException lfurle =
335                                            new LayoutFriendlyURLException(
336                                                    LayoutFriendlyURLException.DUPLICATE);
337    
338                                    lfurle.setDuplicateClassPK(layout.getPlid());
339                                    lfurle.setDuplicateClassName(Layout.class.getName());
340    
341                                    throw lfurle;
342                            }
343                    }
344    
345                    LayoutImpl.validateFriendlyURLKeyword(friendlyURL);
346    
347                    if (friendlyURL.contains(Portal.FRIENDLY_URL_SEPARATOR)) {
348                            LayoutFriendlyURLException lfurle = new LayoutFriendlyURLException(
349                                    LayoutFriendlyURLException.KEYWORD_CONFLICT);
350    
351                            lfurle.setKeywordConflict(Portal.FRIENDLY_URL_SEPARATOR);
352    
353                            throw lfurle;
354                    }
355    
356                    List<FriendlyURLMapper> friendlyURLMappers =
357                            PortletLocalServiceUtil.getFriendlyURLMappers();
358    
359                    for (FriendlyURLMapper friendlyURLMapper : friendlyURLMappers) {
360                            if (friendlyURLMapper.isCheckMappingWithPrefix()) {
361                                    continue;
362                            }
363    
364                            String mapping = StringPool.SLASH + friendlyURLMapper.getMapping();
365    
366                            if (friendlyURL.contains(mapping + StringPool.SLASH) ||
367                                    friendlyURL.endsWith(mapping)) {
368    
369                                    LayoutFriendlyURLException lfurle =
370                                            new LayoutFriendlyURLException(
371                                                    LayoutFriendlyURLException.KEYWORD_CONFLICT);
372    
373                                    lfurle.setKeywordConflict(friendlyURLMapper.getMapping());
374    
375                                    throw lfurle;
376                            }
377                    }
378    
379                    Locale[] availableLocales = LanguageUtil.getAvailableLocales();
380    
381                    for (Locale locale : availableLocales) {
382                            String languageId = StringUtil.toLowerCase(
383                                    LocaleUtil.toLanguageId(locale));
384    
385                            String i18nPathLanguageId =
386                                    StringPool.SLASH +
387                                            PortalUtil.getI18nPathLanguageId(locale, languageId);
388    
389                            if (friendlyURL.startsWith(i18nPathLanguageId + StringPool.SLASH) ||
390                                    friendlyURL.startsWith(
391                                            StringPool.SLASH + languageId + StringPool.SLASH) ||
392                                    friendlyURL.endsWith(i18nPathLanguageId) ||
393                                    friendlyURL.endsWith(StringPool.SLASH + languageId)) {
394    
395                                    LayoutFriendlyURLException lfurle =
396                                            new LayoutFriendlyURLException(
397                                                    LayoutFriendlyURLException.KEYWORD_CONFLICT);
398    
399                                    lfurle.setKeywordConflict(i18nPathLanguageId);
400    
401                                    throw lfurle;
402                            }
403                    }
404    
405                    String layoutIdFriendlyURL = friendlyURL.substring(1);
406    
407                    if (Validator.isNumber(layoutIdFriendlyURL) &&
408                            !layoutIdFriendlyURL.equals(String.valueOf(layoutId))) {
409    
410                            LayoutFriendlyURLException lfurle = new LayoutFriendlyURLException(
411                                    LayoutFriendlyURLException.POSSIBLE_DUPLICATE);
412    
413                            lfurle.setKeywordConflict(layoutIdFriendlyURL);
414    
415                            throw lfurle;
416                    }
417            }
418    
419            public void validateFriendlyURLs(
420                            long groupId, boolean privateLayout, long layoutId,
421                            Map<Locale, String> friendlyURLMap)
422                    throws PortalException {
423    
424                    LayoutFriendlyURLsException layoutFriendlyURLsException = null;
425    
426                    for (Map.Entry<Locale, String> entry : friendlyURLMap.entrySet()) {
427                            try {
428                                    String friendlyURL = entry.getValue();
429    
430                                    validateFriendlyURL(
431                                            groupId, privateLayout, layoutId, friendlyURL);
432                            }
433                            catch (LayoutFriendlyURLException lfurle) {
434                                    Locale locale = entry.getKey();
435    
436                                    if (layoutFriendlyURLsException == null) {
437                                            layoutFriendlyURLsException =
438                                                    new LayoutFriendlyURLsException(lfurle);
439                                    }
440                                    else {
441                                            layoutFriendlyURLsException.addSuppressed(lfurle);
442                                    }
443    
444                                    layoutFriendlyURLsException.addLocalizedException(
445                                            locale, lfurle);
446                            }
447                    }
448    
449                    if (layoutFriendlyURLsException != null) {
450                            throw layoutFriendlyURLsException;
451                    }
452            }
453    
454            public void validateName(String name) throws PortalException {
455                    if (Validator.isNull(name)) {
456                            throw new LayoutNameException();
457                    }
458            }
459    
460            public void validateName(String name, String languageId)
461                    throws PortalException {
462    
463                    String defaultLanguageId = LocaleUtil.toLanguageId(
464                            LocaleUtil.getSiteDefault());
465    
466                    if (defaultLanguageId.equals(languageId)) {
467                            validateName(name);
468                    }
469            }
470    
471            public void validateParentLayoutId(
472                            long groupId, boolean privateLayout, long layoutId,
473                            long parentLayoutId)
474                    throws PortalException {
475    
476                    Layout layout = layoutPersistence.findByG_P_L(
477                            groupId, privateLayout, layoutId);
478    
479                    if (parentLayoutId == layout.getParentLayoutId()) {
480                            return;
481                    }
482    
483                    // Layouts can always be moved to the root level
484    
485                    if (parentLayoutId == LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
486                            return;
487                    }
488    
489                    // Layout cannot become a child of a layout that is not parentable
490    
491                    Layout parentLayout = layoutPersistence.findByG_P_L(
492                            groupId, privateLayout, parentLayoutId);
493    
494                    LayoutType parentLayoutType = parentLayout.getLayoutType();
495    
496                    if (!parentLayoutType.isParentable()) {
497                            throw new LayoutParentLayoutIdException(
498                                    LayoutParentLayoutIdException.NOT_PARENTABLE);
499                    }
500    
501                    // Layout cannot become a child of a layout that is not sortable because
502                    // it is linked to a layout set prototype
503    
504                    if (!SitesUtil.isLayoutSortable(parentLayout)) {
505                            throw new LayoutParentLayoutIdException(
506                                    LayoutParentLayoutIdException.NOT_SORTABLE);
507                    }
508    
509                    // Layout cannot become descendant of itself
510    
511                    if (PortalUtil.isLayoutDescendant(layout, parentLayoutId)) {
512                            throw new LayoutParentLayoutIdException(
513                                    LayoutParentLayoutIdException.SELF_DESCENDANT);
514                    }
515    
516                    // If layout is moved, the new first layout must be valid
517    
518                    if (layout.getParentLayoutId() ==
519                                    LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
520    
521                            List<Layout> layouts = layoutPersistence.findByG_P_P(
522                                    groupId, privateLayout,
523                                    LayoutConstants.DEFAULT_PARENT_LAYOUT_ID, 0, 2);
524    
525                            // You can only reach this point if there are more than two layouts
526                            // at the root level because of the descendant check
527    
528                            long firstLayoutId = layouts.get(0).getLayoutId();
529    
530                            if (firstLayoutId == layoutId) {
531                                    Layout secondLayout = layouts.get(1);
532    
533                                    LayoutType layoutType = secondLayout.getLayoutType();
534    
535                                    if (Validator.isNull(secondLayout.getType()) ||
536                                            !layoutType.isFirstPageable()) {
537    
538                                            throw new LayoutParentLayoutIdException(
539                                                    LayoutParentLayoutIdException.FIRST_LAYOUT_TYPE);
540                                    }
541                            }
542                    }
543            }
544    
545            protected boolean hasGuestViewPermission(Layout layout)
546                    throws PortalException {
547    
548                    Role role = RoleLocalServiceUtil.getRole(
549                            layout.getCompanyId(), RoleConstants.GUEST);
550    
551                    return resourcePermissionLocalService.hasResourcePermission(
552                            layout.getCompanyId(), Layout.class.getName(),
553                            ResourceConstants.SCOPE_INDIVIDUAL,
554                            String.valueOf(layout.getPlid()), role.getRoleId(),
555                            ActionKeys.VIEW);
556            }
557    
558            @BeanReference(type = LayoutFriendlyURLPersistence.class)
559            protected LayoutFriendlyURLPersistence layoutFriendlyURLPersistence;
560    
561            @BeanReference(type = LayoutPersistence.class)
562            protected LayoutPersistence layoutPersistence;
563    
564            @BeanReference(type = LayoutSetPersistence.class)
565            protected LayoutSetPersistence layoutSetPersistence;
566    
567            @BeanReference(type = ResourcePermissionLocalService.class)
568            protected ResourcePermissionLocalService resourcePermissionLocalService;
569    
570            private static final int _PRIORITY_BUFFER = 1000000;
571    
572            private String _beanIdentifier;
573    
574    }