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