Skip to content

Commit e51269e

Browse files
fix: prevent paste to locked cells (#7927) (CP: 23.6) (#7968)
* fix: prevent paste to locked cells (#7927) * add isCellLocked overload * replace usages of deprecated method * fix LockIT * add lock sheet test with existing cell * cleanup * chore: run formatter --------- Co-authored-by: Sascha Ißbrücker <sissbruecker@vaadin.com>
1 parent 275e233 commit e51269e

File tree

9 files changed

+166
-42
lines changed

9 files changed

+166
-42
lines changed

vaadin-spreadsheet-flow-parent/vaadin-spreadsheet-flow-integration-tests/src/main/java/com/vaadin/flow/component/spreadsheet/tests/fixtures/LockCellFixture.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ public class LockCellFixture implements SpreadsheetFixture {
2222

2323
@Override
2424
public void loadFixture(Spreadsheet spreadsheet) {
25-
26-
spreadsheet.setActiveSheetProtected("pwd");
25+
// Sheet already needs to be locked for this fixture to work
26+
// See LockSheetFixture
2727
Cell cell = spreadsheet.createCell(0, 0, "");
2828
Workbook wb = cell.getSheet().getWorkbook();
2929

@@ -35,7 +35,7 @@ public void loadFixture(Spreadsheet spreadsheet) {
3535
cellRef.getCol(), "");
3636
}
3737

38-
boolean wasLocked = spreadsheet.isCellLocked(cell);
38+
boolean wasLocked = spreadsheet.isCellLocked(cell.getAddress());
3939

4040
CellStyle cellStyle = wb.createCellStyle();
4141
// Toggle cell locked state
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* Copyright 2000-2025 Vaadin Ltd.
3+
*
4+
* This program is available under Vaadin Commercial License and Service Terms.
5+
*
6+
* See {@literal <https://vaadin.com/commercial-license-and-service-terms>} for the full
7+
* license.
8+
*/
9+
package com.vaadin.flow.component.spreadsheet.tests.fixtures;
10+
11+
import com.vaadin.flow.component.spreadsheet.Spreadsheet;
12+
13+
public class LockSheetFixture implements SpreadsheetFixture {
14+
15+
@Override
16+
public void loadFixture(Spreadsheet spreadsheet) {
17+
spreadsheet.setActiveSheetProtected("pwd");
18+
spreadsheet.getSpreadsheetStyleFactory()
19+
.reloadActiveSheetColumnRowStyles();
20+
}
21+
}

vaadin-spreadsheet-flow-parent/vaadin-spreadsheet-flow-integration-tests/src/main/java/com/vaadin/flow/component/spreadsheet/tests/fixtures/TestFixtures.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public enum TestFixtures {
3939
CustomEditor(SimpleCustomEditorFixture.class),
4040
Styles(StylesFixture.class),
4141
LockCell(LockCellFixture.class),
42+
LockSheet(LockSheetFixture.class),
4243
CustomComponent(CustomComponentFixture.class),
4344
Action(ActionFixture.class),
4445
InsertRow(InsertRowFixture.class),

vaadin-spreadsheet-flow-parent/vaadin-spreadsheet-flow-integration-tests/src/test/java/com/vaadin/flow/component/spreadsheet/test/LockIT.java

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,20 @@ public void init() {
3030
public void testLockedCells() {
3131
SpreadsheetElement spreadsheet = getSpreadsheet();
3232

33-
// Lock cell C3
34-
selectCell("C3");
35-
loadTestFixture(TestFixtures.LockCell);
36-
37-
spreadsheet.getCellAt("B2").setValue("value");
38-
39-
// Toggle locked state of the region
40-
selectRegion("B2", "D4");
41-
loadTestFixture(TestFixtures.LockCell);
33+
// Lock sheet, which locks all cells
34+
loadTestFixture(TestFixtures.LockSheet);
4235

4336
// Assert that a locked cell cannot be edited
44-
Assert.assertEquals("locked", spreadsheet.getCellAt("B2").getValue());
4537
SheetCellElement lockedCell = spreadsheet.getCellAt("B2");
46-
selectCell("B2"); // work around Selenium issue with double-click
47-
// targeting wrong cell
38+
// work around Selenium issue with double-click targeting wrong cell
39+
selectCell("B2");
4840
lockedCell.doubleClick();
4941
Assert.assertFalse(spreadsheet.getCellValueInput().isDisplayed());
5042

43+
// Toggle locked state of the region (should unlock the cells)
44+
selectRegion("B2", "D4");
45+
loadTestFixture(TestFixtures.LockCell);
46+
5147
// Assert that an unlocked cell can be edited
5248
Assert.assertEquals("unlocked", spreadsheet.getCellAt("C3").getValue());
5349
setCellValue("C3", "value");

vaadin-spreadsheet-flow-parent/vaadin-spreadsheet-flow/src/main/java/com/vaadin/flow/component/spreadsheet/CellSelectionManager.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import org.apache.poi.ss.usermodel.CellType;
1818
import org.apache.poi.ss.usermodel.Row;
1919
import org.apache.poi.ss.usermodel.Workbook;
20+
import org.apache.poi.ss.util.CellAddress;
2021
import org.apache.poi.ss.util.CellRangeAddress;
2122
import org.apache.poi.ss.util.CellRangeUtil;
2223
import org.apache.poi.ss.util.CellReference;
@@ -312,10 +313,13 @@ void handleCellAddressChange(int rowIndex, int colIndex,
312313
}
313314
spreadsheet.getRpcProxy().showSelectedCell(name, colIndex,
314315
rowIndex, value, formula,
315-
spreadsheet.isCellLocked(cell), initialSelection);
316+
spreadsheet.isCellLocked(cell.getAddress()),
317+
initialSelection);
316318
} else {
317319
spreadsheet.getRpcProxy().showSelectedCell(name, colIndex,
318-
rowIndex, "", false, spreadsheet.isCellLocked(cell),
320+
rowIndex, "", false,
321+
spreadsheet.isCellLocked(
322+
new CellAddress(rowIndex, colIndex)),
319323
initialSelection);
320324
}
321325
} else {

vaadin-spreadsheet-flow-parent/vaadin-spreadsheet-flow/src/main/java/com/vaadin/flow/component/spreadsheet/CellValueManager.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.apache.poi.ss.usermodel.Row;
4848
import org.apache.poi.ss.usermodel.Sheet;
4949
import org.apache.poi.ss.usermodel.Workbook;
50+
import org.apache.poi.ss.util.CellAddress;
5051
import org.apache.poi.ss.util.CellRangeAddress;
5152
import org.apache.poi.ss.util.CellReference;
5253
import org.apache.poi.xssf.usermodel.XSSFCell;
@@ -217,7 +218,7 @@ protected CellData createCellDataForCell(Cell cell) {
217218
cellData.col = cell.getColumnIndex() + 1;
218219
CellStyle cellStyle = cell.getCellStyle();
219220
cellData.cellStyle = "cs" + cellStyle.getIndex();
220-
cellData.locked = spreadsheet.isCellLocked(cell);
221+
cellData.locked = spreadsheet.isCellLocked(cell.getAddress());
221222
try {
222223
if (!spreadsheet.isCellHidden(cell)) {
223224
if (cell.getCellType() == CellType.FORMULA) {
@@ -807,16 +808,17 @@ public void onDeleteSelectedCells() {
807808
if (selectedCellReference != null) {
808809
Row row = activeSheet.getRow(selectedCellReference.getRow());
809810
if (row != null && spreadsheet.isCellLocked(
810-
row.getCell(selectedCellReference.getCol()))) {
811+
new CellAddress(selectedCellReference.getRow(),
812+
selectedCellReference.getCol()))) {
811813
return;
812814
}
813815
}
814816
List<CellReference> individualSelectedCells = getCellSelectionManager()
815817
.getIndividualSelectedCells();
816818
for (CellReference cr : individualSelectedCells) {
817819
final Row row = activeSheet.getRow(cr.getRow());
818-
if (row != null
819-
&& spreadsheet.isCellLocked(row.getCell(cr.getCol()))) {
820+
if (row != null && spreadsheet
821+
.isCellLocked(new CellAddress(cr.getRow(), cr.getCol()))) {
820822
return;
821823
}
822824
}

vaadin-spreadsheet-flow-parent/vaadin-spreadsheet-flow/src/main/java/com/vaadin/flow/component/spreadsheet/Spreadsheet.java

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.apache.poi.ss.formula.BaseFormulaEvaluator;
4040
import org.apache.poi.ss.formula.ConditionalFormattingEvaluator;
4141
import org.apache.poi.ss.usermodel.Cell;
42+
import org.apache.poi.ss.usermodel.CellStyle;
4243
import org.apache.poi.ss.usermodel.CellType;
4344
import org.apache.poi.ss.usermodel.ClientAnchor.AnchorType;
4445
import org.apache.poi.ss.usermodel.Comment;
@@ -1541,16 +1542,10 @@ protected boolean isRangeEditable(CellRangeAddress cellRangeAddress) {
15411542
protected boolean isRangeEditable(int row1, int col1, int row2, int col2) {
15421543
if (isActiveSheetProtected()) {
15431544
for (int r = row1; r <= row2; r++) {
1544-
final Row row = getActiveSheet().getRow(r);
1545-
if (row != null) {
1546-
for (int c = col1; c <= col2; c++) {
1547-
final Cell cell = row.getCell(c);
1548-
if (isCellLocked(cell)) {
1549-
return false;
1550-
}
1545+
for (int c = col1; c <= col2; c++) {
1546+
if (isCellLocked(new CellAddress(r, c))) {
1547+
return false;
15511548
}
1552-
} else {
1553-
return false;
15541549
}
15551550
}
15561551
}
@@ -3761,7 +3756,11 @@ public boolean isCellHidden(Cell cell) {
37613756
* @param cell
37623757
* The cell to check
37633758
* @return true if the cell is locked, false otherwise
3759+
* @deprecated Due to requiring a cell instance, this method can not
3760+
* determine the locked state of cells that have not been
3761+
* created yet. Use {@link #isCellLocked(CellAddress)} instead.
37643762
*/
3763+
@Deprecated(since = "24.9.0", forRemoval = true)
37653764
public boolean isCellLocked(Cell cell) {
37663765
if (isActiveSheetProtected()) {
37673766
if (cell != null) {
@@ -3781,6 +3780,39 @@ && getLockedRowIndexes()
37813780
}
37823781
}
37833782

3783+
/**
3784+
* Returns whether or not the cell at the given address in the active sheet
3785+
* is locked.
3786+
*
3787+
* @param cellAddress
3788+
* The address of the cell to check
3789+
* @return true if the cell is locked, false otherwise
3790+
*/
3791+
public boolean isCellLocked(CellAddress cellAddress) {
3792+
// Locking cells only works if the sheet is protected
3793+
if (!isActiveSheetProtected()) {
3794+
return false;
3795+
}
3796+
3797+
Sheet sheet = getActiveSheet();
3798+
Row row = sheet.getRow(cellAddress.getRow());
3799+
Cell cell = row != null ? row.getCell(cellAddress.getColumn()) : null;
3800+
3801+
// If there is a cell with a custom cell style, return its locked state
3802+
if (cell != null && cell.getCellStyle().getIndex() != 0) {
3803+
return cell.getCellStyle().getLocked();
3804+
}
3805+
3806+
// Otherwise inherit locked state from row or column styles
3807+
// If neither is unlocked, the locked state is inherited from the sheet
3808+
CellStyle rowStyle = row != null ? row.getRowStyle() : null;
3809+
CellStyle columnStyle = sheet.getColumnStyle(cellAddress.getColumn());
3810+
boolean rowLocked = rowStyle == null || rowStyle.getLocked();
3811+
boolean columnLocked = columnStyle == null || columnStyle.getLocked();
3812+
3813+
return rowLocked && columnLocked;
3814+
}
3815+
37843816
/**
37853817
* Gets the RPC proxy for communication to the client side.
37863818
*
@@ -4541,7 +4573,7 @@ void loadRangeComponents(HashSet<Component> newCustomComponents,
45414573
key);
45424574
newCustomComponents.add(customComponent);
45434575
rowsWithComponents.add(r);
4544-
} else if (!isCellLocked(cell)) {
4576+
} else if (!isCellLocked(new CellAddress(r, c))) {
45454577
// no custom component and not locked, check if
45464578
// the cell has a custom editor
45474579
Component customEditor = customComponentFactory

vaadin-spreadsheet-flow-parent/vaadin-spreadsheet-flow/src/main/java/com/vaadin/flow/component/spreadsheet/SpreadsheetHandlerImpl.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.apache.poi.ss.usermodel.Row;
2727
import org.apache.poi.ss.usermodel.Sheet;
2828
import org.apache.poi.ss.usermodel.Workbook;
29+
import org.apache.poi.ss.util.CellAddress;
2930
import org.apache.poi.ss.util.CellRangeAddress;
3031
import org.apache.poi.ss.util.CellReference;
3132

@@ -270,14 +271,12 @@ public void onPaste(String text) {
270271

271272
// Check for protected cells at target
272273
for (int i = 0; i < pasteHeight; i++) {
273-
Row row = activesheet.getRow(rowIndex + i);
274-
if (row != null) {
275-
for (int j = 0; j < pasteWidth; j++) {
276-
Cell cell = row.getCell(colIndex + j);
277-
if (spreadsheet.isCellLocked(cell)) {
278-
protectedCellWriteAttempted();
279-
return;
280-
}
274+
for (int j = 0; j < pasteWidth; j++) {
275+
CellAddress cellAddress = new CellAddress(rowIndex + i,
276+
colIndex + j);
277+
if (spreadsheet.isCellLocked(cellAddress)) {
278+
protectedCellWriteAttempted();
279+
return;
281280
}
282281
}
283282
}
@@ -414,7 +413,7 @@ public void clearSelectedCellsOnCut() {
414413
.getLastColumn(); col++) {
415414
Cell cell = spreadsheet.getCell(row, col);
416415
if (cell != null) {
417-
if (spreadsheet.isCellLocked(cell)) {
416+
if (spreadsheet.isCellLocked(cell.getAddress())) {
418417
protectedCellWriteAttempted();
419418
return;
420419
}
@@ -429,7 +428,7 @@ public void clearSelectedCellsOnCut() {
429428
.getSelectedCellReference();
430429
Cell cell = spreadsheet.getCell(reference.getRow(), reference.getCol());
431430
if (cell != null) {
432-
if (spreadsheet.isCellLocked(cell)) {
431+
if (spreadsheet.isCellLocked(cell.getAddress())) {
433432
protectedCellWriteAttempted();
434433
return;
435434
}

vaadin-spreadsheet-flow-parent/vaadin-spreadsheet-flow/src/test/java/com/vaadin/flow/component/spreadsheet/tests/ClipboardTest.java

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,71 @@ public void lockedCell_paste_firesProtectedEditEvent() {
103103
Assert.assertNotNull(event.get());
104104
}
105105

106+
@Test
107+
public void lockSheet_pasteIntoNonExistingCell_cellNotCreated() {
108+
lockSheet();
109+
spreadsheet.setSelection("A1");
110+
// Try pasting the value to a non-existing cell in a locked sheet
111+
paste("['A1']");
112+
Assert.assertNull(spreadsheet.getCell("A1"));
113+
}
114+
115+
@Test
116+
public void createCell_lockSheet_pasteIntoExistingCell_cellValueNotUpdated() {
117+
spreadsheet.createCell(9, 1, "initial");
118+
119+
lockSheet();
120+
121+
// Try pasting after locking
122+
spreadsheet.setSelection("B10");
123+
paste("['new data']");
124+
Assert.assertEquals("initial", getCellValue("B10"));
125+
}
126+
127+
@Test
128+
public void lockSheet_unlockRow_shouldAllowPaste() {
129+
lockSheet();
130+
131+
// Unlock a row
132+
var workbook = spreadsheet.getActiveSheet().getWorkbook();
133+
var unlockedRowStyle = workbook.createCellStyle();
134+
unlockedRowStyle.setLocked(false);
135+
136+
var row = spreadsheet.getActiveSheet().createRow(5);
137+
row.setRowStyle(unlockedRowStyle);
138+
139+
// Try pasting to a cell in the unlocked row
140+
spreadsheet.setSelection("A6");
141+
paste("['test data']");
142+
Assert.assertEquals("test data", getCellValue("A6"));
143+
144+
// Try pasting again after cell has been created
145+
paste("['new data']");
146+
Assert.assertEquals("new data", getCellValue("A6"));
147+
}
148+
149+
@Test
150+
public void lockSheet_unlockColumn_shouldAllowPaste() {
151+
lockSheet();
152+
153+
// Unlock a column
154+
var workbook = spreadsheet.getActiveSheet().getWorkbook();
155+
var unlockedColumnStyle = workbook.createCellStyle();
156+
unlockedColumnStyle.setLocked(false);
157+
158+
spreadsheet.getActiveSheet().setDefaultColumnStyle(1,
159+
unlockedColumnStyle);
160+
161+
// Try pasting to a cell in the unlocked column
162+
spreadsheet.setSelection("B10");
163+
paste("['test data']");
164+
Assert.assertEquals("test data", getCellValue("B10"));
165+
166+
// Try pasting again after cell has been created
167+
paste("['new data']");
168+
Assert.assertEquals("new data", getCellValue("B10"));
169+
}
170+
106171
@Test
107172
public void paste_selectionUpdated() {
108173
spreadsheet.setSelection("A1:A2");
@@ -244,8 +309,12 @@ private void cut() {
244309
"[]");
245310
}
246311

247-
private void lockCell(String cellAddress) {
312+
private void lockSheet() {
248313
spreadsheet.setSheetProtected(0, "password");
314+
}
315+
316+
private void lockCell(String cellAddress) {
317+
lockSheet();
249318
spreadsheet.createCell(0, 0, "locked");
250319
var lockedCellStyle = spreadsheet.getActiveSheet().getWorkbook()
251320
.createCellStyle();

0 commit comments

Comments
 (0)