001/*
002 * (C) Copyright 2013 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Martin Pernollet
016 */
017
018package org.nuxeo.ecm.platform.groups.audit.service.acl;
019
020import java.io.IOException;
021import java.util.ArrayList;
022import java.util.List;
023import java.util.Set;
024
025import org.apache.poi.ss.usermodel.Cell;
026import org.apache.poi.ss.usermodel.CellStyle;
027import org.nuxeo.ecm.platform.groups.audit.service.acl.ReportLayoutSettings.SpanMode;
028import org.nuxeo.ecm.platform.groups.audit.service.acl.excel.ByteColor;
029import org.nuxeo.ecm.platform.groups.audit.service.acl.excel.ExcelBuilder;
030import org.nuxeo.ecm.platform.groups.audit.service.acl.filter.IContentFilter;
031
032import com.google.common.collect.Multimap;
033
034/**
035 * An excel layout builder that uses one group of columns per user, using one column for each right type (read, write,
036 * etc).
037 *
038 * @author Martin Pernollet <mpernollet@nuxeo.com>
039 */
040public class AclExcelLayoutBuilderMultiColumn extends AclExcelLayoutBuilder {
041    protected static int USERS_ROW = 0;
042
043    protected static int PERMISSIONS_ROW = 1;
044
045    public static ReportLayoutSettings defaultLayout() {
046        ReportLayoutSettings layout = new ReportLayoutSettings();
047        layout.userHeaderHeight = -1;
048        layout.userHeaderRotation = 0;
049        layout.aclHeaderHeight = -1;// fit vertically full ACL name 1800;
050        layout.aclHeaderRotation = 0;
051
052        layout.fileTreeColumnWidth = 2.5; // in number of char
053        layout.aclColumnWidth = 2.5;
054        layout.defaultRowHeight = 100;
055        layout.splitPaneX = 500;
056        layout.splitPaneY = 1500;
057        layout.freezePaneRowSplit = 2;
058        layout.treeLineCursorRowStart = 2;
059
060        layout.aclHeaderCommentColSpan = 10;
061        layout.aclHeaderCommentRowSpan = 2;
062        layout.aclHeaderFontSize = 6; // in font unit
063
064        layout.spanMode = SpanMode.COLUMN_OVERFLOW_ON_NEXT_SHEETS;
065
066        layout.zoomRatioDenominator = 2;
067        layout.zoomRatioNumerator = 1;
068
069        layout.logoImageFile = "src/main/resources/file-delete.png";
070
071        return layout;
072    }
073
074    /* Prebuilt styles */
075
076    protected CellStyle acceptStyle;
077
078    protected CellStyle acceptStyleLeft;
079
080    protected CellStyle acceptStyleRight;
081
082    protected CellStyle denyStyle;
083
084    protected CellStyle denyStyleLeft;
085
086    protected CellStyle denyStyleRight;
087
088    protected CellStyle emptyStyle;
089
090    protected CellStyle emptyStyleLeft;
091
092    protected CellStyle emptyStyleRight;
093
094    protected int logoPictureId = -1;
095
096    public AclExcelLayoutBuilderMultiColumn() {
097        super(defaultLayout());
098    }
099
100    public AclExcelLayoutBuilderMultiColumn(IContentFilter filter) {
101        this(defaultLayout(), filter);
102    }
103
104    public AclExcelLayoutBuilderMultiColumn(ReportLayoutSettings layout, IContentFilter filter) {
105        super(layout, filter);
106    }
107
108    @Override
109    protected void renderInit() {
110        super.renderInit();
111        acceptStyle = excel.newColoredCellStyle(ByteColor.GREEN);
112
113        acceptStyleLeft = excel.newColoredCellStyle(ByteColor.GREEN);
114        acceptStyleLeft.setBorderLeft(CellStyle.BORDER_THIN);
115        acceptStyleLeft.setLeftBorderColor(excel.getColor(ByteColor.BLACK).getIndex());
116
117        acceptStyleRight = excel.newColoredCellStyle(ByteColor.GREEN);
118        acceptStyleRight.setBorderRight(CellStyle.BORDER_THIN);
119        acceptStyleRight.setRightBorderColor(excel.getColor(ByteColor.BLACK).getIndex());
120
121        denyStyle = excel.newColoredCellStyle(ByteColor.RED);
122        denyStyle.setFillPattern(CellStyle.THIN_FORWARD_DIAG); // TODO:
123                                                               // generaliser
124                                                               // autres
125                                                               // cellules
126        denyStyle.setFillBackgroundColor(excel.getColor(ByteColor.WHITE).getIndex());
127
128        denyStyleLeft = excel.newColoredCellStyle(ByteColor.RED);
129        denyStyleLeft.setBorderLeft(CellStyle.BORDER_THIN);
130        denyStyleLeft.setLeftBorderColor(excel.getColor(ByteColor.BLACK).getIndex());
131
132        denyStyleRight = excel.newColoredCellStyle(ByteColor.RED);
133        denyStyleRight.setBorderRight(CellStyle.BORDER_THIN);
134        denyStyleRight.setRightBorderColor(excel.getColor(ByteColor.BLACK).getIndex());
135
136        emptyStyle = excel.newColoredCellStyle(ByteColor.WHITE);
137
138        emptyStyleLeft = excel.newColoredCellStyle(ByteColor.WHITE);
139        emptyStyleLeft.setBorderLeft(CellStyle.BORDER_THIN);
140        emptyStyleLeft.setLeftBorderColor(excel.getColor(ByteColor.BLACK).getIndex());
141
142        emptyStyleRight = excel.newColoredCellStyle(ByteColor.WHITE);
143        emptyStyleRight.setBorderRight(CellStyle.BORDER_THIN);
144        emptyStyleRight.setRightBorderColor(excel.getColor(ByteColor.BLACK).getIndex());
145
146        if (layoutSettings.logoImageFile != null)
147            try {
148                logoPictureId = excel.loadPicture(layoutSettings.logoImageFile);
149            } catch (IOException e) {
150                log.error(e, e);
151            }
152    }
153
154    /* HEADER RENDERING */
155
156    /**
157     * Write users and groups on the first row. Memorize the user (or group) column which can later be retrieved with
158     * getColumn(user)
159     */
160    @Override
161    protected void renderHeader(int tableStartColumn, Set<String> userOrGroups, Set<String> permissions) {
162        renderHeaderPicture();
163        renderHeaderUsers(tableStartColumn, userOrGroups, permissions);
164        renderHeaderAcl(userOrGroups, permissions);
165        formatHeaderRowHeight();
166    }
167
168    protected void renderHeaderPicture() {
169        // excel.mergeRange(USERS_ROW, 0, PERMISSIONS_ROW, tableStartColumn-1);
170        excel.setPicture(logoPictureId, 0, 0, false);
171    }
172
173    protected void renderHeaderUsers(int tableStartColumn, Set<String> userOrGroups, Set<String> permissions) {
174
175        int userColumn = tableStartColumn;
176        for (String user : userOrGroups) {
177            // render the user column header
178            excel.setCell(USERS_ROW, userColumn, user, userHeaderStyle);
179            layout.setUserColumn(userColumn, user);
180
181            // merge cells indicating user name
182            final int from = userColumn;
183            final int to = userColumn + permissions.size() - 1;
184            if (from < ExcelBuilder.LAST_COLUMN && to < ExcelBuilder.LAST_COLUMN)
185                excel.mergeRange(USERS_ROW, from, USERS_ROW, to);
186
187            userColumn += permissions.size();
188            log.debug("user header: " + USERS_ROW + "," + userColumn + " > " + user);
189        }
190    }
191
192    protected void renderHeaderAcl(Set<String> userOrGroups, Set<String> permissions) {
193        for (String user : userOrGroups) {
194            // render ACL column header for this user
195            int userColumn;
196            int aclColumn = 0;
197            int aclHeaderColumn = 0;
198            String aclHeaderText;
199            String aclHeaderShort;
200
201            for (String permission : permissions) {
202                userColumn = layout.getUserColumn(user);
203                aclHeaderColumn = userColumn + aclColumn;
204                aclHeaderText = permission;// formatPermission(permission);
205                aclHeaderShort = formatPermission(permission);
206
207                Cell cell = excel.setCell(PERMISSIONS_ROW, aclHeaderColumn, aclHeaderShort, aclHeaderStyle);
208                excel.setColumnWidth(aclHeaderColumn, (int) (layoutSettings.aclColumnWidth * CELL_WIDTH_UNIT));
209
210                // add a comment with the acl complete name
211                if ((aclHeaderColumn + layoutSettings.aclHeaderCommentColSpan) < ExcelBuilder.LAST_COLUMN)
212                    excel.addComment(cell, aclHeaderText, PERMISSIONS_ROW, aclHeaderColumn,
213                            layoutSettings.aclHeaderCommentColSpan, layoutSettings.aclHeaderCommentRowSpan);
214
215                layout.setUserAclColumn(aclHeaderColumn, Pair.of(user, permission));
216                aclColumn++;
217
218                log.debug("permission header: " + PERMISSIONS_ROW + "," + aclHeaderColumn + " > "
219                        + formatPermission(permission));
220            }
221        }
222    }
223
224    protected void formatHeaderRowHeight() {
225        if (layoutSettings.aclHeaderHeight != -1)
226            excel.setRowHeight(PERMISSIONS_ROW, layoutSettings.aclHeaderHeight);
227        if (layoutSettings.userHeaderHeight != -1)
228            excel.setRowHeight(USERS_ROW, layoutSettings.userHeaderHeight);
229    }
230
231    /* FILE TREE AND MATRIX CONTENT RENDERING */
232
233    @Override
234    protected void renderAcl(Multimap<String, Pair<String, Boolean>> userAcls) {
235        for (String user : userAcls.keySet()) {
236            List<Pair<String, Boolean>> acls = new ArrayList<Pair<String, Boolean>>(userAcls.get(user));
237            int last = acls.size() - 1;
238
239            // TODO: IF ACLS not contain an ACL that should be first or last,
240            // thus showing border, post draw white cells
241
242            for (int i = 0; i < acls.size(); i++) {
243                boolean isFirst = false;// (i == 0);
244                boolean isLast = false;// (i == last);
245
246                Pair<String, Boolean> ace = acls.get(i);
247                String permission = ace.a;
248                boolean accept = ace.b;
249                int aclColumn = layout.getUserAclColumn(Pair.of(user, permission));
250                String aceText = "";// formatAce(ace)
251
252                if (accept) {
253                    // draws an accept cell
254                    renderAcceptCell(isFirst, isLast, aclColumn, aceText);
255                } else {
256                    // draws a deny cell
257                    renderDenyCell(isFirst, isLast, aclColumn, aceText);
258                }
259            }
260            // String info = formatAcl(userAcls.get(user));
261        }
262    }
263
264    /**
265     * Render a cell with a 'deny' color with left, right or no border according to its position.
266     */
267    protected void renderDenyCell(boolean isFirst, boolean isLast, int aclColumn, String aceText) {
268        if (isFirst) {
269            excel.setCell(treeLineCursor, aclColumn, aceText, denyStyleLeft);
270        } else if (isLast) {
271            excel.setCell(treeLineCursor, aclColumn, aceText, denyStyleRight);
272        } else {
273            excel.setCell(treeLineCursor, aclColumn, aceText, denyStyle);
274        }
275    }
276
277    /**
278     * Render a cell with a 'accept' color with left, right or no border according to its position.
279     */
280    protected void renderAcceptCell(boolean isFirst, boolean isLast, int aclColumn, String aceText) {
281        if (isFirst) {
282            excel.setCell(treeLineCursor, aclColumn, aceText, acceptStyleLeft);
283        } else if (isLast) {
284            excel.setCell(treeLineCursor, aclColumn, aceText, acceptStyleRight);
285        } else {
286            excel.setCell(treeLineCursor, aclColumn, aceText, acceptStyle);
287        }
288    }
289}