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.portlet.layoutsadmin.util;
016    
017    import com.liferay.portal.kernel.dao.orm.QueryUtil;
018    import com.liferay.portal.kernel.json.JSONArray;
019    import com.liferay.portal.kernel.json.JSONFactoryUtil;
020    import com.liferay.portal.kernel.json.JSONObject;
021    import com.liferay.portal.kernel.log.Log;
022    import com.liferay.portal.kernel.log.LogFactoryUtil;
023    import com.liferay.portal.kernel.security.permission.ActionKeys;
024    import com.liferay.portal.kernel.util.ArrayUtil;
025    import com.liferay.portal.kernel.util.ParamUtil;
026    import com.liferay.portal.kernel.util.StringBundler;
027    import com.liferay.portal.kernel.util.StringPool;
028    import com.liferay.portal.kernel.util.WebKeys;
029    import com.liferay.portal.model.Layout;
030    import com.liferay.portal.model.LayoutBranch;
031    import com.liferay.portal.model.LayoutConstants;
032    import com.liferay.portal.model.LayoutRevision;
033    import com.liferay.portal.model.LayoutSetBranch;
034    import com.liferay.portal.model.impl.VirtualLayout;
035    import com.liferay.portal.service.LayoutLocalServiceUtil;
036    import com.liferay.portal.service.LayoutServiceUtil;
037    import com.liferay.portal.service.LayoutSetBranchLocalServiceUtil;
038    import com.liferay.portal.service.permission.GroupPermissionUtil;
039    import com.liferay.portal.service.permission.LayoutPermissionUtil;
040    import com.liferay.portal.theme.ThemeDisplay;
041    import com.liferay.portal.util.PropsValues;
042    import com.liferay.portal.util.SessionClicks;
043    import com.liferay.portlet.exportimport.staging.LayoutStagingUtil;
044    import com.liferay.portlet.exportimport.staging.StagingUtil;
045    import com.liferay.sites.kernel.util.SitesUtil;
046    
047    import java.util.ArrayList;
048    import java.util.Collections;
049    import java.util.Iterator;
050    import java.util.List;
051    
052    import javax.servlet.http.HttpServletRequest;
053    import javax.servlet.http.HttpSession;
054    
055    /**
056     * @author Brian Wing Shun Chan
057     * @author Eduardo Lundgren
058     * @author Bruno Basto
059     * @author Marcellus Tavares
060     * @author Zsolt Szab??
061     * @author Tibor Lipusz
062     */
063    public class LayoutsTreeUtil {
064    
065            public static String getLayoutsJSON(
066                            HttpServletRequest request, long groupId, boolean privateLayout,
067                            long parentLayoutId, boolean incomplete, String treeId)
068                    throws Exception {
069    
070                    return getLayoutsJSON(
071                            request, groupId, privateLayout, parentLayoutId, null, incomplete,
072                            treeId);
073            }
074    
075            public static String getLayoutsJSON(
076                            HttpServletRequest request, long groupId, boolean privateLayout,
077                            long parentLayoutId, long[] expandedLayoutIds, boolean incomplete,
078                            String treeId)
079                    throws Exception {
080    
081                    if (_log.isDebugEnabled()) {
082                            StringBundler sb = new StringBundler(13);
083    
084                            sb.append("getLayoutsJSON(groupId=");
085                            sb.append(groupId);
086                            sb.append(", privateLayout=");
087                            sb.append(privateLayout);
088                            sb.append(", parentLayoutId=");
089                            sb.append(parentLayoutId);
090                            sb.append(", expandedLayoutIds=");
091                            sb.append(expandedLayoutIds);
092                            sb.append(", incomplete=");
093                            sb.append(incomplete);
094                            sb.append(", treeId=");
095                            sb.append(treeId);
096                            sb.append(StringPool.CLOSE_PARENTHESIS);
097    
098                            _log.debug(sb.toString());
099                    }
100    
101                    LayoutTreeNodes layoutTreeNodes = _getLayoutTreeNodes(
102                            request, groupId, privateLayout, parentLayoutId, incomplete,
103                            expandedLayoutIds, treeId);
104    
105                    return _toJSON(request, groupId, layoutTreeNodes);
106            }
107    
108            public static String getLayoutsJSON(
109                            HttpServletRequest request, long groupId, String treeId)
110                    throws Exception {
111    
112                    if (_log.isDebugEnabled()) {
113                            StringBundler sb = new StringBundler(5);
114    
115                            sb.append("getLayoutsJSON(groupId=");
116                            sb.append(groupId);
117                            sb.append(", treeId=");
118                            sb.append(treeId);
119                            sb.append(StringPool.CLOSE_PARENTHESIS);
120    
121                            _log.debug(sb.toString());
122                    }
123    
124                    LayoutTreeNodes layoutTreeNodes = new LayoutTreeNodes();
125    
126                    layoutTreeNodes.addAll(
127                            _getLayoutTreeNodes(
128                                    request, groupId, true,
129                                    LayoutConstants.DEFAULT_PARENT_LAYOUT_ID, false, null, treeId));
130                    layoutTreeNodes.addAll(
131                            _getLayoutTreeNodes(
132                                    request, groupId, false,
133                                    LayoutConstants.DEFAULT_PARENT_LAYOUT_ID, false, null, treeId));
134    
135                    return _toJSON(request, groupId, layoutTreeNodes);
136            }
137    
138            private static List<Layout> _getAncestorLayouts(HttpServletRequest request)
139                    throws Exception {
140    
141                    long selPlid = ParamUtil.getLong(request, "selPlid");
142    
143                    if (selPlid == 0) {
144                            return Collections.emptyList();
145                    }
146    
147                    List<Layout> ancestorLayouts = LayoutServiceUtil.getAncestorLayouts(
148                            selPlid);
149    
150                    Layout layout = LayoutLocalServiceUtil.getLayout(selPlid);
151    
152                    if (_log.isDebugEnabled()) {
153                            StringBundler sb = new StringBundler(7);
154    
155                            sb.append("_getAncestorLayouts(selPlid=");
156                            sb.append(selPlid);
157                            sb.append(", ancestorLayouts=");
158                            sb.append(ancestorLayouts);
159                            sb.append(", layout=");
160                            sb.append(layout);
161                            sb.append(StringPool.CLOSE_PARENTHESIS);
162    
163                            _log.debug(sb.toString());
164                    }
165    
166                    ancestorLayouts.add(layout);
167    
168                    return ancestorLayouts;
169            }
170    
171            private static LayoutTreeNodes _getLayoutTreeNodes(
172                            HttpServletRequest request, long groupId, boolean privateLayout,
173                            long parentLayoutId, boolean incomplete, long[] expandedLayoutIds,
174                            String treeId)
175                    throws Exception {
176    
177                    if (_log.isDebugEnabled()) {
178                            StringBundler sb = new StringBundler(13);
179    
180                            sb.append("_getLayoutTreeNodes(groupId=");
181                            sb.append(groupId);
182                            sb.append(", privateLayout=");
183                            sb.append(privateLayout);
184                            sb.append(", parentLayoutId=");
185                            sb.append(parentLayoutId);
186                            sb.append(", expandedLayoutIds=");
187                            sb.append(expandedLayoutIds);
188                            sb.append(", incomplete=");
189                            sb.append(incomplete);
190                            sb.append(", treeId=");
191                            sb.append(treeId);
192                            sb.append(StringPool.CLOSE_PARENTHESIS);
193    
194                            _log.debug(sb.toString());
195                    }
196    
197                    List<LayoutTreeNode> layoutTreeNodes = new ArrayList<>();
198    
199                    List<Layout> ancestorLayouts = _getAncestorLayouts(request);
200    
201                    List<Layout> layouts = LayoutServiceUtil.getLayouts(
202                            groupId, privateLayout, parentLayoutId, incomplete,
203                            QueryUtil.ALL_POS, QueryUtil.ALL_POS);
204    
205                    for (Layout layout :
206                                    _paginateLayouts(
207                                            request, groupId, privateLayout, parentLayoutId, layouts,
208                                            treeId)) {
209    
210                            LayoutTreeNode layoutTreeNode = new LayoutTreeNode(layout);
211    
212                            LayoutTreeNodes childLayoutTreeNodes = null;
213    
214                            if (_isExpandableLayout(
215                                            request, ancestorLayouts, expandedLayoutIds, layout)) {
216    
217                                    if (layout instanceof VirtualLayout) {
218                                            VirtualLayout virtualLayout = (VirtualLayout)layout;
219    
220                                            childLayoutTreeNodes = _getLayoutTreeNodes(
221                                                    request, virtualLayout.getSourceGroupId(),
222                                                    virtualLayout.isPrivateLayout(),
223                                                    virtualLayout.getLayoutId(), incomplete,
224                                                    expandedLayoutIds, treeId);
225                                    }
226                                    else {
227                                            childLayoutTreeNodes = _getLayoutTreeNodes(
228                                                    request, groupId, layout.isPrivateLayout(),
229                                                    layout.getLayoutId(), incomplete, expandedLayoutIds,
230                                                    treeId);
231                                    }
232                            }
233                            else {
234                                    int childLayoutsCount = LayoutServiceUtil.getLayoutsCount(
235                                            groupId, privateLayout, layout.getLayoutId());
236    
237                                    childLayoutTreeNodes = new LayoutTreeNodes(
238                                            new ArrayList<LayoutTreeNode>(), childLayoutsCount);
239                            }
240    
241                            layoutTreeNode.setChildLayoutTreeNodes(childLayoutTreeNodes);
242    
243                            layoutTreeNodes.add(layoutTreeNode);
244                    }
245    
246                    return new LayoutTreeNodes(layoutTreeNodes, layouts.size());
247            }
248    
249            private static int _getLoadedLayoutsCount(
250                            HttpSession session, long groupId, boolean privateLayout,
251                            long layoutId, String treeId)
252                    throws Exception {
253    
254                    StringBundler sb = new StringBundler(7);
255    
256                    sb.append(treeId);
257                    sb.append(StringPool.COLON);
258                    sb.append(groupId);
259                    sb.append(StringPool.COLON);
260                    sb.append(privateLayout);
261                    sb.append(StringPool.COLON);
262                    sb.append("Pagination");
263    
264                    String key = sb.toString();
265    
266                    String paginationJSON = SessionClicks.get(
267                            session, key, JSONFactoryUtil.getNullJSON());
268    
269                    JSONObject paginationJSONObject = JSONFactoryUtil.createJSONObject(
270                            paginationJSON);
271    
272                    if (_log.isDebugEnabled()) {
273                            sb = new StringBundler(9);
274    
275                            sb.append("_getLoadedLayoutsCount(key=");
276                            sb.append(key);
277                            sb.append(", layoutId=");
278                            sb.append(layoutId);
279                            sb.append(", paginationJSON=");
280                            sb.append(paginationJSON);
281                            sb.append(", paginationJSONObject");
282                            sb.append(paginationJSONObject);
283                            sb.append(StringPool.CLOSE_PARENTHESIS);
284    
285                            _log.debug(sb.toString());
286                    }
287    
288                    return paginationJSONObject.getInt(String.valueOf(layoutId), 0);
289            }
290    
291            private static boolean _isExpandableLayout(
292                    HttpServletRequest request, List<Layout> ancestorLayouts,
293                    long[] expandedLayoutIds, Layout layout) {
294    
295                    boolean expandParentLayouts = ParamUtil.getBoolean(
296                            request, "expandParentLayouts");
297    
298                    if (expandParentLayouts || ancestorLayouts.contains(layout) ||
299                            ArrayUtil.contains(expandedLayoutIds, layout.getLayoutId())) {
300    
301                            return true;
302                    }
303    
304                    return false;
305            }
306    
307            private static boolean _isPaginationEnabled(HttpServletRequest request) {
308                    boolean paginate = ParamUtil.getBoolean(request, "paginate", true);
309    
310                    if (paginate &&
311                            (PropsValues.LAYOUT_MANAGE_PAGES_INITIAL_CHILDREN > -1)) {
312    
313                            return true;
314                    }
315    
316                    return false;
317            }
318    
319            private static List<Layout> _paginateLayouts(
320                            HttpServletRequest request, long groupId, boolean privateLayout,
321                            long parentLayoutId, List<Layout> layouts, String treeId)
322                    throws Exception {
323    
324                    if (!_isPaginationEnabled(request)) {
325                            return layouts;
326                    }
327    
328                    HttpSession session = request.getSession();
329    
330                    int loadedLayoutsCount = _getLoadedLayoutsCount(
331                            session, groupId, privateLayout, parentLayoutId, treeId);
332    
333                    int start = ParamUtil.getInteger(request, "start");
334    
335                    start = Math.max(0, Math.min(start, layouts.size()));
336    
337                    int end = ParamUtil.getInteger(
338                            request, "end",
339                            start + PropsValues.LAYOUT_MANAGE_PAGES_INITIAL_CHILDREN);
340    
341                    if (loadedLayoutsCount > end) {
342                            end = loadedLayoutsCount;
343                    }
344    
345                    end = Math.max(start, Math.min(end, layouts.size()));
346    
347                    if (_log.isDebugEnabled()) {
348                            StringBundler sb = new StringBundler(7);
349    
350                            sb.append("_paginateLayouts(loadedLayoutsCount=");
351                            sb.append(loadedLayoutsCount);
352                            sb.append(", start=");
353                            sb.append(start);
354                            sb.append(", end=");
355                            sb.append(end);
356                            sb.append(StringPool.CLOSE_PARENTHESIS);
357    
358                            _log.debug(sb.toString());
359                    }
360    
361                    return layouts.subList(start, end);
362            }
363    
364            private static String _toJSON(
365                            HttpServletRequest request, long groupId,
366                            LayoutTreeNodes layoutTreeNodes)
367                    throws Exception {
368    
369                    if (_log.isDebugEnabled()) {
370                            StringBundler sb = new StringBundler(5);
371    
372                            sb.append("_toJSON(groupId=");
373                            sb.append(groupId);
374                            sb.append(", layoutTreeNodes=");
375                            sb.append(layoutTreeNodes);
376                            sb.append(StringPool.CLOSE_PARENTHESIS);
377    
378                            _log.debug(sb.toString());
379                    }
380    
381                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
382                            WebKeys.THEME_DISPLAY);
383    
384                    JSONArray jsonArray = JSONFactoryUtil.createJSONArray();
385    
386                    boolean hasManageLayoutsPermission = GroupPermissionUtil.contains(
387                            themeDisplay.getPermissionChecker(), groupId,
388                            ActionKeys.MANAGE_LAYOUTS);
389    
390                    for (LayoutTreeNode layoutTreeNode : layoutTreeNodes) {
391                            JSONObject jsonObject = JSONFactoryUtil.createJSONObject();
392    
393                            String childrenJSON = _toJSON(
394                                    request, groupId, layoutTreeNode.getChildLayoutTreeNodes());
395    
396                            jsonObject.put(
397                                    "children", JSONFactoryUtil.createJSONObject(childrenJSON));
398    
399                            Layout layout = layoutTreeNode.getLayout();
400    
401                            jsonObject.put("contentDisplayPage", layout.isContentDisplayPage());
402                            jsonObject.put("friendlyURL", layout.getFriendlyURL());
403    
404                            if (layout instanceof VirtualLayout) {
405                                    VirtualLayout virtualLayout = (VirtualLayout)layout;
406    
407                                    jsonObject.put("groupId", virtualLayout.getSourceGroupId());
408                            }
409                            else {
410                                    jsonObject.put("groupId", layout.getGroupId());
411                            }
412    
413                            jsonObject.put("hasChildren", layout.hasChildren());
414                            jsonObject.put("layoutId", layout.getLayoutId());
415                            jsonObject.put("name", layout.getName(themeDisplay.getLocale()));
416                            jsonObject.put(
417                                    "parentable",
418                                    LayoutPermissionUtil.contains(
419                                            themeDisplay.getPermissionChecker(), layout,
420                                            ActionKeys.ADD_LAYOUT));
421                            jsonObject.put("parentLayoutId", layout.getParentLayoutId());
422                            jsonObject.put("plid", layout.getPlid());
423                            jsonObject.put("priority", layout.getPriority());
424                            jsonObject.put("privateLayout", layout.isPrivateLayout());
425                            jsonObject.put("regularURL", layout.getRegularURL(request));
426                            jsonObject.put(
427                                    "sortable",
428                                    hasManageLayoutsPermission &&
429                                            SitesUtil.isLayoutSortable(layout));
430                            jsonObject.put("type", layout.getType());
431                            jsonObject.put(
432                                    "updateable",
433                                    LayoutPermissionUtil.contains(
434                                            themeDisplay.getPermissionChecker(), layout,
435                                            ActionKeys.UPDATE));
436                            jsonObject.put("uuid", layout.getUuid());
437    
438                            LayoutRevision layoutRevision = LayoutStagingUtil.getLayoutRevision(
439                                    layout);
440    
441                            if (layoutRevision != null) {
442                                    long layoutSetBranchId = layoutRevision.getLayoutSetBranchId();
443    
444                                    if (StagingUtil.isIncomplete(layout, layoutSetBranchId)) {
445                                            jsonObject.put("incomplete", true);
446                                    }
447    
448                                    LayoutSetBranch layoutSetBranch =
449                                            LayoutSetBranchLocalServiceUtil.getLayoutSetBranch(
450                                                    layoutSetBranchId);
451    
452                                    LayoutBranch layoutBranch = layoutRevision.getLayoutBranch();
453    
454                                    if (!layoutBranch.isMaster()) {
455                                            jsonObject.put(
456                                                    "layoutBranchId", layoutBranch.getLayoutBranchId());
457                                            jsonObject.put("layoutBranchName", layoutBranch.getName());
458                                    }
459    
460                                    if (layoutRevision.isHead()) {
461                                            jsonObject.put("layoutRevisionHead", true);
462                                    }
463    
464                                    jsonObject.put(
465                                            "layoutRevisionId", layoutRevision.getLayoutRevisionId());
466                                    jsonObject.put("layoutSetBranchId", layoutSetBranchId);
467                                    jsonObject.put(
468                                            "layoutSetBranchName", layoutSetBranch.getName());
469                            }
470    
471                            jsonArray.put(jsonObject);
472                    }
473    
474                    JSONObject responseJSONObject = JSONFactoryUtil.createJSONObject();
475    
476                    responseJSONObject.put("layouts", jsonArray);
477                    responseJSONObject.put("total", layoutTreeNodes.getTotal());
478    
479                    return responseJSONObject.toString();
480            }
481    
482            private static final Log _log = LogFactoryUtil.getLog(
483                    LayoutsTreeUtil.class);
484    
485            private static class LayoutTreeNode {
486    
487                    public LayoutTreeNode(Layout layout) {
488                            _layout = layout;
489                    }
490    
491                    public LayoutTreeNodes getChildLayoutTreeNodes() {
492                            return _childLayoutTreeNodes;
493                    }
494    
495                    public Layout getLayout() {
496                            return _layout;
497                    }
498    
499                    public void setChildLayoutTreeNodes(
500                            LayoutTreeNodes childLayoutTreeNodes) {
501    
502                            _childLayoutTreeNodes = childLayoutTreeNodes;
503                    }
504    
505                    @Override
506                    public String toString() {
507                            StringBundler sb = new StringBundler(5);
508    
509                            sb.append("{childLayoutTreeNodes=");
510                            sb.append(_childLayoutTreeNodes);
511                            sb.append(", layout=");
512                            sb.append(_layout);
513                            sb.append(StringPool.CLOSE_CURLY_BRACE);
514    
515                            return sb.toString();
516                    }
517    
518                    private LayoutTreeNodes _childLayoutTreeNodes = new LayoutTreeNodes();
519                    private final Layout _layout;
520    
521            }
522    
523            private static class LayoutTreeNodes implements Iterable<LayoutTreeNode> {
524    
525                    public LayoutTreeNodes() {
526                            _layoutTreeNodesList = new ArrayList<>();
527                    }
528    
529                    public LayoutTreeNodes(
530                            List<LayoutTreeNode> layoutTreeNodesList, int total) {
531    
532                            _layoutTreeNodesList = layoutTreeNodesList;
533                            _total = total;
534                    }
535    
536                    public void addAll(LayoutTreeNodes layoutTreeNodes) {
537                            _layoutTreeNodesList.addAll(
538                                    layoutTreeNodes.getLayoutTreeNodesList());
539    
540                            _total += layoutTreeNodes.getTotal();
541                    }
542    
543                    public List<LayoutTreeNode> getLayoutTreeNodesList() {
544                            return _layoutTreeNodesList;
545                    }
546    
547                    public int getTotal() {
548                            return _total;
549                    }
550    
551                    @Override
552                    public Iterator<LayoutTreeNode> iterator() {
553                            return _layoutTreeNodesList.iterator();
554                    }
555    
556                    @Override
557                    public String toString() {
558                            StringBundler sb = new StringBundler(5);
559    
560                            sb.append("{layoutTreeNodesList=");
561                            sb.append(_layoutTreeNodesList);
562                            sb.append(", total=");
563                            sb.append(_total);
564                            sb.append(StringPool.CLOSE_CURLY_BRACE);
565    
566                            return sb.toString();
567                    }
568    
569                    private final List<LayoutTreeNode> _layoutTreeNodesList;
570                    private int _total;
571    
572            }
573    
574    }