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