XLSX write Sheet Protection

fixes  h/t @Mior

note: @sheetjsdev rewrote implementation, original PR author used
This commit is contained in:
Mior 2017-04-04 20:41:11 +02:00 committed by SheetJS
parent 6a3afe56c2
commit d086dbecbf
6 changed files with 111 additions and 0 deletions

@ -574,6 +574,28 @@ In addition to the base sheet keys, worksheets also add:
will write all cells in the merge range if they exist, so be sure that only will write all cells in the merge range if they exist, so be sure that only
the first cell (upper-left) in the range is set. the first cell (upper-left) in the range is set.
- `ws['protect']`: object of write sheet protection properties. The `password`
key specifies the password. The writer uses the XOR obfuscation method. The
following keys control the sheet protection (same as ECMA-376 18.3.1.85):
| key | functionality disabled if value is true |
|:----------------------|:-----------------------------------------------------|
| `selectLockedCells` | Select locked cells |
| `selectUnlockedCells` | Select unlocked cells |
| `formatCells` | Format cells |
| `formatColumns` | Format columns |
| `formatRows` | Format rows |
| `insertColumns` | Insert columns |
| `insertRows` | Insert rows |
| `insertHyperlinks` | Insert hyperlinks |
| `deleteColumns` | Delete columns |
| `deleteRows` | Delete rows |
| `sort` | Sort |
| `autoFilter` | Filter |
| `pivotTables` | Use PivotTable reports |
| `objects` | Edit objects |
| `scenarios` | Edit scenarios |
#### Chartsheet Object #### Chartsheet Object
Chartsheets are represented as standard sheets. They are distinguished with the Chartsheets are represented as standard sheets. They are distinguished with the

@ -70,6 +70,24 @@ function write_ws_xml_merges(merges) {
return o + '</mergeCells>'; return o + '</mergeCells>';
} }
/* 18.3.1.85 sheetPr CT_SheetProtection */
function write_ws_xml_protection(sp)/*:string*/ {
// algorithmName, hashValue, saltValue, spinCountpassword
var o = ({sheet:1}/*:any*/);
var deffalse = ["objects", "scenarios", "selectLockedCells", "selectUnlockedCells"];
var deftrue = [
"formatColumns", "formatRows", "formatCells",
"insertColumns", "insertRows", "insertHyperlinks",
"deleteColumns", "deleteRows",
"sort", "autoFilter", "pivotTables"
];
deffalse.forEach(function(n) { if(sp[n] != null && sp[n]) o[n] = "1"; });
deftrue.forEach(function(n) { if(sp[n] != null && !sp[n]) o[n] = "0"; });
/* TODO: algorithm */
if(sp.password) o.password = crypto_CreatePasswordVerifier_Method1(sp.password).toString(16).toUpperCase();
return writextag('sheetProtection', null, o);
}
function parse_ws_xml_hlinks(s, data/*:Array<string>*/, rels) { function parse_ws_xml_hlinks(s, data/*:Array<string>*/, rels) {
for(var i = 0; i != data.length; ++i) { for(var i = 0; i != data.length; ++i) {
var val = parsexmltag(data[i], true); var val = parsexmltag(data[i], true);
@ -348,6 +366,8 @@ function write_ws_xml(idx/*:number*/, opts, wb/*:Workbook*/, rels)/*:string*/ {
} }
if(o.length>sidx+1) { o[o.length] = ('</sheetData>'); o[sidx]=o[sidx].replace("/>",">"); } if(o.length>sidx+1) { o[o.length] = ('</sheetData>'); o[sidx]=o[sidx].replace("/>",">"); }
if(ws['!protect'] != null) o[o.length] = write_ws_xml_protection(ws['!protect']);
if(ws['!merges'] != null && ws['!merges'].length > 0) o[o.length] = (write_ws_xml_merges(ws['!merges'])); if(ws['!merges'] != null && ws['!merges'].length > 0) o[o.length] = (write_ws_xml_merges(ws['!merges']));
var relc = -1, rel, rId = -1; var relc = -1, rel, rId = -1;

@ -13,6 +13,28 @@ In addition to the base sheet keys, worksheets also add:
will write all cells in the merge range if they exist, so be sure that only will write all cells in the merge range if they exist, so be sure that only
the first cell (upper-left) in the range is set. the first cell (upper-left) in the range is set.
- `ws['protect']`: object of write sheet protection properties. The `password`
key specifies the password. The writer uses the XOR obfuscation method. The
following keys control the sheet protection (same as ECMA-376 18.3.1.85):
| key | functionality disabled if value is true |
|:----------------------|:-----------------------------------------------------|
| `selectLockedCells` | Select locked cells |
| `selectUnlockedCells` | Select unlocked cells |
| `formatCells` | Format cells |
| `formatColumns` | Format columns |
| `formatRows` | Format rows |
| `insertColumns` | Insert columns |
| `insertRows` | Insert rows |
| `insertHyperlinks` | Insert hyperlinks |
| `deleteColumns` | Delete columns |
| `deleteRows` | Delete rows |
| `sort` | Sort |
| `autoFilter` | Filter |
| `pivotTables` | Use PivotTable reports |
| `objects` | Edit objects |
| `scenarios` | Edit scenarios |
#### Chartsheet Object #### Chartsheet Object
Chartsheets are represented as standard sheets. They are distinguished with the Chartsheets are represented as standard sheets. They are distinguished with the

@ -102,6 +102,13 @@ wb.Props = {
ws['A4'].c = []; ws['A4'].c = [];
ws['A4'].c.push({a:"SheetJS",t:"I'm a little comment, short and stout!\n\nWell, Stout may be the wrong word"}); ws['A4'].c.push({a:"SheetJS",t:"I'm a little comment, short and stout!\n\nWell, Stout may be the wrong word"});
/* TEST: sheet protection */
ws['!protect'] = {
password:"password",
objects:1,
scenarios:1
};
console.log("Worksheet Model:") console.log("Worksheet Model:")
console.log(ws); console.log(ws);

@ -9715,6 +9715,24 @@ function write_ws_xml_merges(merges) {
return o + '</mergeCells>'; return o + '</mergeCells>';
} }
/* 18.3.1.85 sheetPr CT_SheetProtection */
function write_ws_xml_protection(sp)/*:string*/ {
// algorithmName, hashValue, saltValue, spinCountpassword
var o = ({sheet:1}/*:any*/);
var deffalse = ["objects", "scenarios", "selectLockedCells", "selectUnlockedCells"];
var deftrue = [
"formatColumns", "formatRows", "formatCells",
"insertColumns", "insertRows", "insertHyperlinks",
"deleteColumns", "deleteRows",
"sort", "autoFilter", "pivotTables"
];
deffalse.forEach(function(n) { if(sp[n] != null && sp[n]) o[n] = "1"; });
deftrue.forEach(function(n) { if(sp[n] != null && !sp[n]) o[n] = "0"; });
/* TODO: algorithm */
if(sp.password) o.password = crypto_CreatePasswordVerifier_Method1(sp.password).toString(16).toUpperCase();
return writextag('sheetProtection', null, o);
}
function parse_ws_xml_hlinks(s, data/*:Array<string>*/, rels) { function parse_ws_xml_hlinks(s, data/*:Array<string>*/, rels) {
for(var i = 0; i != data.length; ++i) { for(var i = 0; i != data.length; ++i) {
var val = parsexmltag(data[i], true); var val = parsexmltag(data[i], true);
@ -9993,6 +10011,8 @@ function write_ws_xml(idx/*:number*/, opts, wb/*:Workbook*/, rels)/*:string*/ {
} }
if(o.length>sidx+1) { o[o.length] = ('</sheetData>'); o[sidx]=o[sidx].replace("/>",">"); } if(o.length>sidx+1) { o[o.length] = ('</sheetData>'); o[sidx]=o[sidx].replace("/>",">"); }
if(ws['!protect'] != null) o[o.length] = write_ws_xml_protection(ws['!protect']);
if(ws['!merges'] != null && ws['!merges'].length > 0) o[o.length] = (write_ws_xml_merges(ws['!merges'])); if(ws['!merges'] != null && ws['!merges'].length > 0) o[o.length] = (write_ws_xml_merges(ws['!merges']));
var relc = -1, rel, rId = -1; var relc = -1, rel, rId = -1;

20
xlsx.js

@ -9658,6 +9658,24 @@ function write_ws_xml_merges(merges) {
return o + '</mergeCells>'; return o + '</mergeCells>';
} }
/* 18.3.1.85 sheetPr CT_SheetProtection */
function write_ws_xml_protection(sp) {
// algorithmName, hashValue, saltValue, spinCountpassword
var o = ({sheet:1});
var deffalse = ["objects", "scenarios", "selectLockedCells", "selectUnlockedCells"];
var deftrue = [
"formatColumns", "formatRows", "formatCells",
"insertColumns", "insertRows", "insertHyperlinks",
"deleteColumns", "deleteRows",
"sort", "autoFilter", "pivotTables"
];
deffalse.forEach(function(n) { if(sp[n] != null && sp[n]) o[n] = "1"; });
deftrue.forEach(function(n) { if(sp[n] != null && !sp[n]) o[n] = "0"; });
/* TODO: algorithm */
if(sp.password) o.password = crypto_CreatePasswordVerifier_Method1(sp.password).toString(16).toUpperCase();
return writextag('sheetProtection', null, o);
}
function parse_ws_xml_hlinks(s, data, rels) { function parse_ws_xml_hlinks(s, data, rels) {
for(var i = 0; i != data.length; ++i) { for(var i = 0; i != data.length; ++i) {
var val = parsexmltag(data[i], true); var val = parsexmltag(data[i], true);
@ -9936,6 +9954,8 @@ function write_ws_xml(idx, opts, wb, rels) {
} }
if(o.length>sidx+1) { o[o.length] = ('</sheetData>'); o[sidx]=o[sidx].replace("/>",">"); } if(o.length>sidx+1) { o[o.length] = ('</sheetData>'); o[sidx]=o[sidx].replace("/>",">"); }
if(ws['!protect'] != null) o[o.length] = write_ws_xml_protection(ws['!protect']);
if(ws['!merges'] != null && ws['!merges'].length > 0) o[o.length] = (write_ws_xml_merges(ws['!merges'])); if(ws['!merges'] != null && ws['!merges'].length > 0) o[o.length] = (write_ws_xml_merges(ws['!merges']));
var relc = -1, rel, rId = -1; var relc = -1, rel, rId = -1;