Skip to content

Commit 58fd5ca

Browse files
authored
Merge pull request #4702 from oleibman/issue4695
Protected Ranges and Insert/Delete Columns/Rows
2 parents 4f6c850 + 2bc326b commit 58fd5ca

File tree

3 files changed

+91
-9
lines changed

3 files changed

+91
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). Thia is a
2929

3030
### Fixed
3131

32+
- Protected ranges and insert/delete rows/columns. [Issue #4695](https://github.com/PHPOffice/PhpSpreadsheet/issues/4695) [PR #4702](https://github.com/PHPOffice/PhpSpreadsheet/pull/4702)
3233
- Unexpected Exception in Php DateTime. [Issue #4696](https://github.com/PHPOffice/PhpSpreadsheet/issues/4696) [Issue #917](https://github.com/PHPOffice/PhpSpreadsheet/issues/917) [PR #4697](https://github.com/PHPOffice/PhpSpreadsheet/pull/4697)
3334

3435
## 2025-10-25 - 5.2.0

src/PhpSpreadsheet/ReferenceHelper.php

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -330,15 +330,37 @@ protected function adjustMergeCells(Worksheet $worksheet): void
330330
protected function adjustProtectedCells(Worksheet $worksheet, int $numberOfColumns, int $numberOfRows): void
331331
{
332332
$aProtectedCells = $worksheet->getProtectedCellRanges();
333-
($numberOfColumns > 0 || $numberOfRows > 0)
334-
? uksort($aProtectedCells, [self::class, 'cellReverseSort'])
335-
: uksort($aProtectedCells, [self::class, 'cellSort']);
336-
foreach ($aProtectedCells as $cellAddress => $protectedRange) {
337-
$newReference = $this->updateCellReference($cellAddress);
338-
if ($cellAddress !== $newReference) {
339-
$worksheet->unprotectCells($cellAddress);
340-
if ($newReference) {
341-
$worksheet->protectCells($newReference, $protectedRange->getPassword(), true);
333+
/** @var CellReferenceHelper */
334+
$cellReferenceHelper = $this->cellReferenceHelper;
335+
if ($numberOfRows >= 0 && $numberOfColumns >= 0) {
336+
foreach ($aProtectedCells as $key2 => $value) {
337+
$ranges = $value->allRanges();
338+
$newKey = $separator = '';
339+
foreach ($ranges as $key => $range) {
340+
$oldKey = $range[0] . (array_key_exists(1, $range) ? (':' . $range[1]) : '');
341+
$newKey .= $separator . $this->updateCellReference($oldKey);
342+
$separator = ' ';
343+
}
344+
if ($key2 !== $newKey) {
345+
$worksheet->unprotectCells($key2);
346+
$worksheet->protectCells($newKey, $value->getPassword(), true, $value->getName(), $value->getSecurityDescriptor());
347+
}
348+
}
349+
} else {
350+
foreach ($aProtectedCells as $key2 => $value) {
351+
$range = str_replace([' ', ',', "\0"], ["\0", ' ', ','], $key2);
352+
$extracted = Coordinate::extractAllCellReferencesInRange($range);
353+
$outArray = [];
354+
foreach ($extracted as $cellAddress) {
355+
if (!$cellReferenceHelper->cellAddressInDeleteRange($cellAddress)) {
356+
$outArray[$this->updateCellReference($cellAddress)] = 'x';
357+
}
358+
}
359+
$outArray2 = Coordinate::mergeRangesInCollection($outArray);
360+
$newKey = implode(' ', array_keys($outArray2));
361+
if ($key2 !== $newKey) {
362+
$worksheet->unprotectCells($key2);
363+
$worksheet->protectCells($newKey, $value->getPassword(), true, $value->getName(), $value->getSecurityDescriptor());
342364
}
343365
}
344366
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
6+
7+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
8+
use PHPUnit\Framework\TestCase;
9+
10+
class ProtectedRangeTest extends TestCase
11+
{
12+
public function testInsertRow1(): void
13+
{
14+
$spreadsheet = new Spreadsheet();
15+
$sheet = $spreadsheet->getActiveSheet();
16+
$sheet->protectCells('B2:D4 J2:L4 F2:H4', name: 'ProtectedBlock1');
17+
$sheet->protectCells('M2:O4 Q7:R9 T1:T3');
18+
$sheet->protectCells('B8 C9 D1', name: 'ProtectedBlock3');
19+
$sheet->insertNewRowBefore(2);
20+
$ranges = $sheet->getProtectedCellRanges();
21+
$rangeKeys = array_keys($ranges);
22+
self::assertSame(
23+
[
24+
'B3:D5 J3:L5 F3:H5',
25+
'M3:O5 Q8:R10 T1:T4',
26+
'B9 C10 D1',
27+
],
28+
$rangeKeys
29+
);
30+
self::assertSame('ProtectedBlock1', $ranges[$rangeKeys[0]]->getName());
31+
self::assertSame('ProtectedBlock3', $ranges[$rangeKeys[2]]->getName());
32+
$spreadsheet->disconnectWorksheets();
33+
}
34+
35+
public function testRemoveRow1(): void
36+
{
37+
$spreadsheet = new Spreadsheet();
38+
$sheet = $spreadsheet->getActiveSheet();
39+
$sheet->protectCells('B2:D4 J2:L4 F2:H4');
40+
$sheet->protectCells('M2:O4 Q7:R9 T1:T3', name: 'ProtectedBlock2');
41+
$sheet->protectCells('B8 C9 D1 E3');
42+
$sheet->removeRow(3);
43+
$ranges = $sheet->getProtectedCellRanges();
44+
$rangeKeys = array_keys($ranges);
45+
// PhpSpreadsheet has methods to merge cell addresses in a row,
46+
// but not in a column. So the results here are not as concise as
47+
// they might be, but they are nevertheless accurate.
48+
self::assertSame(
49+
[
50+
'B2:B3 C2:C3 D2:D3 F2:F3 G2:G3 H2:H3 J2:J3 K2:K3 L2:L3',
51+
'M2:M3 N2:N3 O2:O3 Q6:Q8 R6:R8 T1:T2',
52+
'B7 C8 D1',
53+
],
54+
$rangeKeys
55+
);
56+
self::assertSame('ProtectedBlock2', $ranges[$rangeKeys[1]]->getName());
57+
$spreadsheet->disconnectWorksheets();
58+
}
59+
}

0 commit comments

Comments
 (0)