001
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.GetterUtil;
030 import com.liferay.portal.kernel.util.LocaleUtil;
031 import com.liferay.portal.kernel.util.StringPool;
032 import com.liferay.portal.kernel.util.StringUtil;
033 import com.liferay.portal.kernel.util.Validator;
034 import com.liferay.portal.model.Group;
035 import com.liferay.portal.model.Layout;
036 import com.liferay.portal.model.LayoutConstants;
037 import com.liferay.portal.model.LayoutFriendlyURL;
038 import com.liferay.portal.model.LayoutSet;
039 import com.liferay.portal.model.LayoutSetPrototype;
040 import com.liferay.portal.model.LayoutType;
041 import com.liferay.portal.model.LayoutTypeController;
042 import com.liferay.portal.model.ResourceConstants;
043 import com.liferay.portal.model.Role;
044 import com.liferay.portal.model.RoleConstants;
045 import com.liferay.portal.model.impl.LayoutImpl;
046 import com.liferay.portal.security.permission.ActionKeys;
047 import com.liferay.portal.service.PortletLocalServiceUtil;
048 import com.liferay.portal.service.ResourcePermissionLocalService;
049 import com.liferay.portal.service.RoleLocalServiceUtil;
050 import com.liferay.portal.service.ServiceContext;
051 import com.liferay.portal.service.persistence.LayoutFriendlyURLPersistence;
052 import com.liferay.portal.service.persistence.LayoutPersistence;
053 import com.liferay.portal.service.persistence.LayoutSetPersistence;
054 import com.liferay.portal.util.LayoutTypeControllerTracker;
055 import com.liferay.portal.util.Portal;
056 import com.liferay.portal.util.PortalUtil;
057 import com.liferay.portal.util.comparator.LayoutPriorityComparator;
058 import com.liferay.portlet.sites.util.SitesUtil;
059
060 import java.util.HashMap;
061 import java.util.List;
062 import java.util.Locale;
063 import java.util.Map;
064
065
068 public class LayoutLocalServiceHelper implements IdentifiableBean {
069
070 @Override
071 public String getBeanIdentifier() {
072 return _beanIdentifier;
073 }
074
075 public String getFriendlyURL(
076 long groupId, boolean privateLayout, long layoutId, String name,
077 String friendlyURL)
078 throws PortalException {
079
080 friendlyURL = getFriendlyURL(friendlyURL);
081
082 if (Validator.isNotNull(friendlyURL)) {
083 return friendlyURL;
084 }
085
086 friendlyURL = StringPool.SLASH + getFriendlyURL(name);
087
088 String originalFriendlyURL = friendlyURL;
089
090 for (int i = 1;; i++) {
091 try {
092 validateFriendlyURL(
093 groupId, privateLayout, layoutId, friendlyURL);
094
095 break;
096 }
097 catch (LayoutFriendlyURLException lfurle) {
098 int type = lfurle.getType();
099
100 if (type == LayoutFriendlyURLException.DUPLICATE) {
101 friendlyURL = originalFriendlyURL + i;
102 }
103 else {
104 friendlyURL = StringPool.SLASH + layoutId;
105
106 break;
107 }
108 }
109 }
110
111 return friendlyURL;
112 }
113
114 public String getFriendlyURL(String friendlyURL) {
115 return FriendlyURLNormalizerUtil.normalize(friendlyURL);
116 }
117
118 public Map<Locale, String> getFriendlyURLMap(
119 long groupId, boolean privateLayout, long layoutId, String name,
120 Map<Locale, String> friendlyURLMap)
121 throws PortalException {
122
123 Map<Locale, String> newFriendlyURLMap = new HashMap<>();
124
125 for (Locale locale : LanguageUtil.getAvailableLocales(groupId)) {
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
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, ServiceContext serviceContext)
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
251
252
253 Layout layout = layoutPersistence.fetchByG_P_L(
254 groupId, privateLayout, layoutId);
255 Layout parentLayout = layoutPersistence.findByG_P_L(
256 groupId, privateLayout, parentLayoutId);
257
258 if (((layout == null) ||
259 Validator.isNull(layout.getSourcePrototypeLayoutUuid())) &&
260 !SitesUtil.isLayoutSortable(parentLayout)) {
261
262 throw new LayoutParentLayoutIdException(
263 LayoutParentLayoutIdException.NOT_SORTABLE);
264 }
265 }
266
267 if (firstLayout) {
268 validateFirstLayout(type);
269 }
270
271 LayoutTypeController layoutTypeController =
272 LayoutTypeControllerTracker.getLayoutTypeController(type);
273
274 if (!layoutTypeController.isInstanceable()) {
275 boolean layoutInstanceableAllowed = GetterUtil.getBoolean(
276 serviceContext.getAttribute("layout.instanceable.allowed"));
277
278 if (!layoutInstanceableAllowed) {
279 throw new LayoutTypeException(
280 LayoutTypeException.NOT_INSTANCEABLE);
281 }
282 }
283
284 if (!layoutTypeController.isParentable()) {
285 if (layoutPersistence.countByG_P_P(
286 groupId, privateLayout, layoutId) > 0) {
287
288 throw new LayoutTypeException(
289 LayoutTypeException.NOT_PARENTABLE);
290 }
291 }
292
293 validateFriendlyURLs(groupId, privateLayout, layoutId, friendlyURLMap);
294 }
295
296 public void validateFirstLayout(Layout layout) throws PortalException {
297 Group group = layout.getGroup();
298
299 if (group.isGuest() && layout.isPublicLayout() &&
300 !hasGuestViewPermission(layout)) {
301
302 LayoutTypeException lte = new LayoutTypeException(
303 LayoutTypeException.FIRST_LAYOUT_PERMISSION);
304
305 throw lte;
306 }
307
308 validateFirstLayout(layout.getType());
309 }
310
311 public void validateFirstLayout(String type) throws PortalException {
312 LayoutTypeController layoutTypeController =
313 LayoutTypeControllerTracker.getLayoutTypeController(type);
314
315 if (Validator.isNull(type) || !layoutTypeController.isFirstPageable()) {
316 LayoutTypeException lte = new LayoutTypeException(
317 LayoutTypeException.FIRST_LAYOUT);
318
319 lte.setLayoutType(type);
320
321 throw lte;
322 }
323 }
324
325 public void validateFriendlyURL(
326 long groupId, boolean privateLayout, long layoutId,
327 String friendlyURL)
328 throws PortalException {
329
330 if (Validator.isNull(friendlyURL)) {
331 return;
332 }
333
334 int exceptionType = LayoutImpl.validateFriendlyURL(friendlyURL);
335
336 if (exceptionType != -1) {
337 throw new LayoutFriendlyURLException(exceptionType);
338 }
339
340 List<LayoutFriendlyURL> layoutFriendlyURLs =
341 layoutFriendlyURLPersistence.findByG_P_F(
342 groupId, privateLayout, friendlyURL);
343
344 for (LayoutFriendlyURL layoutFriendlyURL : layoutFriendlyURLs) {
345 Layout layout = layoutPersistence.findByPrimaryKey(
346 layoutFriendlyURL.getPlid());
347
348 if (layout.getLayoutId() != layoutId) {
349 LayoutFriendlyURLException lfurle =
350 new LayoutFriendlyURLException(
351 LayoutFriendlyURLException.DUPLICATE);
352
353 lfurle.setDuplicateClassPK(layout.getPlid());
354 lfurle.setDuplicateClassName(Layout.class.getName());
355
356 throw lfurle;
357 }
358 }
359
360 LayoutImpl.validateFriendlyURLKeyword(friendlyURL);
361
362 if (friendlyURL.contains(Portal.FRIENDLY_URL_SEPARATOR)) {
363 LayoutFriendlyURLException lfurle = new LayoutFriendlyURLException(
364 LayoutFriendlyURLException.KEYWORD_CONFLICT);
365
366 lfurle.setKeywordConflict(Portal.FRIENDLY_URL_SEPARATOR);
367
368 throw lfurle;
369 }
370
371 List<FriendlyURLMapper> friendlyURLMappers =
372 PortletLocalServiceUtil.getFriendlyURLMappers();
373
374 for (FriendlyURLMapper friendlyURLMapper : friendlyURLMappers) {
375 if (friendlyURLMapper.isCheckMappingWithPrefix()) {
376 continue;
377 }
378
379 String mapping = StringPool.SLASH + friendlyURLMapper.getMapping();
380
381 if (friendlyURL.contains(mapping + StringPool.SLASH) ||
382 friendlyURL.endsWith(mapping)) {
383
384 LayoutFriendlyURLException lfurle =
385 new LayoutFriendlyURLException(
386 LayoutFriendlyURLException.KEYWORD_CONFLICT);
387
388 lfurle.setKeywordConflict(friendlyURLMapper.getMapping());
389
390 throw lfurle;
391 }
392 }
393
394 for (Locale locale : LanguageUtil.getAvailableLocales()) {
395 String languageId = StringUtil.toLowerCase(
396 LocaleUtil.toLanguageId(locale));
397
398 String i18nPathLanguageId =
399 StringPool.SLASH +
400 PortalUtil.getI18nPathLanguageId(locale, languageId);
401
402 if (friendlyURL.startsWith(i18nPathLanguageId + StringPool.SLASH) ||
403 friendlyURL.startsWith(
404 StringPool.SLASH + languageId + StringPool.SLASH) ||
405 friendlyURL.endsWith(i18nPathLanguageId) ||
406 friendlyURL.endsWith(StringPool.SLASH + languageId)) {
407
408 LayoutFriendlyURLException lfurle =
409 new LayoutFriendlyURLException(
410 LayoutFriendlyURLException.KEYWORD_CONFLICT);
411
412 lfurle.setKeywordConflict(i18nPathLanguageId);
413
414 throw lfurle;
415 }
416 }
417
418 String layoutIdFriendlyURL = friendlyURL.substring(1);
419
420 if (Validator.isNumber(layoutIdFriendlyURL) &&
421 !layoutIdFriendlyURL.equals(String.valueOf(layoutId))) {
422
423 LayoutFriendlyURLException lfurle = new LayoutFriendlyURLException(
424 LayoutFriendlyURLException.POSSIBLE_DUPLICATE);
425
426 lfurle.setKeywordConflict(layoutIdFriendlyURL);
427
428 throw lfurle;
429 }
430 }
431
432 public void validateFriendlyURLs(
433 long groupId, boolean privateLayout, long layoutId,
434 Map<Locale, String> friendlyURLMap)
435 throws PortalException {
436
437 LayoutFriendlyURLsException layoutFriendlyURLsException = null;
438
439 for (Map.Entry<Locale, String> entry : friendlyURLMap.entrySet()) {
440 try {
441 String friendlyURL = entry.getValue();
442
443 validateFriendlyURL(
444 groupId, privateLayout, layoutId, friendlyURL);
445 }
446 catch (LayoutFriendlyURLException lfurle) {
447 Locale locale = entry.getKey();
448
449 if (layoutFriendlyURLsException == null) {
450 layoutFriendlyURLsException =
451 new LayoutFriendlyURLsException(lfurle);
452 }
453 else {
454 layoutFriendlyURLsException.addSuppressed(lfurle);
455 }
456
457 layoutFriendlyURLsException.addLocalizedException(
458 locale, lfurle);
459 }
460 }
461
462 if (layoutFriendlyURLsException != null) {
463 throw layoutFriendlyURLsException;
464 }
465 }
466
467 public void validateName(String name) throws PortalException {
468 if (Validator.isNull(name)) {
469 throw new LayoutNameException();
470 }
471 }
472
473 public void validateName(String name, String languageId)
474 throws PortalException {
475
476 String defaultLanguageId = LocaleUtil.toLanguageId(
477 LocaleUtil.getSiteDefault());
478
479 if (defaultLanguageId.equals(languageId)) {
480 validateName(name);
481 }
482 }
483
484 public void validateParentLayoutId(
485 long groupId, boolean privateLayout, long layoutId,
486 long parentLayoutId)
487 throws PortalException {
488
489 Layout layout = layoutPersistence.findByG_P_L(
490 groupId, privateLayout, layoutId);
491
492 if (parentLayoutId == layout.getParentLayoutId()) {
493 return;
494 }
495
496
497
498 if (parentLayoutId == LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
499 return;
500 }
501
502
503
504 Layout parentLayout = layoutPersistence.findByG_P_L(
505 groupId, privateLayout, parentLayoutId);
506
507 LayoutType parentLayoutType = parentLayout.getLayoutType();
508
509 if (!parentLayoutType.isParentable()) {
510 throw new LayoutParentLayoutIdException(
511 LayoutParentLayoutIdException.NOT_PARENTABLE);
512 }
513
514
515
516
517 if (Validator.isNull(layout.getSourcePrototypeLayoutUuid()) &&
518 !SitesUtil.isLayoutSortable(parentLayout)) {
519
520 throw new LayoutParentLayoutIdException(
521 LayoutParentLayoutIdException.NOT_SORTABLE);
522 }
523
524
525
526 if (PortalUtil.isLayoutDescendant(layout, parentLayoutId)) {
527 throw new LayoutParentLayoutIdException(
528 LayoutParentLayoutIdException.SELF_DESCENDANT);
529 }
530
531
532
533 if (layout.getParentLayoutId() ==
534 LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
535
536 List<Layout> layouts = layoutPersistence.findByG_P_P(
537 groupId, privateLayout,
538 LayoutConstants.DEFAULT_PARENT_LAYOUT_ID, 0, 2);
539
540
541
542
543 long firstLayoutId = layouts.get(0).getLayoutId();
544
545 if (firstLayoutId == layoutId) {
546 Layout secondLayout = layouts.get(1);
547
548 LayoutType layoutType = secondLayout.getLayoutType();
549
550 if (Validator.isNull(secondLayout.getType()) ||
551 !layoutType.isFirstPageable()) {
552
553 throw new LayoutParentLayoutIdException(
554 LayoutParentLayoutIdException.FIRST_LAYOUT_TYPE);
555 }
556 }
557 }
558 }
559
560 protected boolean hasGuestViewPermission(Layout layout)
561 throws PortalException {
562
563 Role role = RoleLocalServiceUtil.getRole(
564 layout.getCompanyId(), RoleConstants.GUEST);
565
566 return resourcePermissionLocalService.hasResourcePermission(
567 layout.getCompanyId(), Layout.class.getName(),
568 ResourceConstants.SCOPE_INDIVIDUAL,
569 String.valueOf(layout.getPlid()), role.getRoleId(),
570 ActionKeys.VIEW);
571 }
572
573 @BeanReference(type = LayoutFriendlyURLPersistence.class)
574 protected LayoutFriendlyURLPersistence layoutFriendlyURLPersistence;
575
576 @BeanReference(type = LayoutPersistence.class)
577 protected LayoutPersistence layoutPersistence;
578
579 @BeanReference(type = LayoutSetPersistence.class)
580 protected LayoutSetPersistence layoutSetPersistence;
581
582 @BeanReference(type = ResourcePermissionLocalService.class)
583 protected ResourcePermissionLocalService resourcePermissionLocalService;
584
585 private static final int _PRIORITY_BUFFER = 1000000;
586
587 private String _beanIdentifier;
588
589 }