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