XLSX write Sheet Protection

fixes #363 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
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
Chartsheets are represented as standard sheets. They are distinguished with the

@ -70,6 +70,24 @@ function write_ws_xml_merges(merges) {
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) {
for(var i = 0; i != data.length; ++i) {
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(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']));
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
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
Chartsheets are represented as standard sheets. They are distinguished with the

@ -102,6 +102,13 @@ wb.Props = {
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"});
/* TEST: sheet protection */
ws['!protect'] = {
password:"password",
objects:1,
scenarios:1
};
console.log("Worksheet Model:")
console.log(ws);

@ -9715,6 +9715,24 @@ function write_ws_xml_merges(merges) {
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) {
for(var i = 0; i != data.length; ++i) {
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(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']));
var relc = -1, rel, rId = -1;

20
xlsx.js

@ -9658,6 +9658,24 @@ function write_ws_xml_merges(merges) {
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) {
for(var i = 0; i != data.length; ++i) {
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(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']));
var relc = -1, rel, rId = -1;