summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHcySunYang <HcySunYang@outlook.com>2018-11-02 23:08:31 +0800
committerxuri <xuri.me@gmail.com>2018-11-02 23:08:31 +0800
commit4dbc78ce0a0d40be189b4c77207cdbb598846c73 (patch)
treeb8295d120ecafa5c7e9e7aad22be09e65fc76aed
parent30122d0346dc751b01c9865af3febb1f99e1f5e0 (diff)
resolve #273 new feature: protect sheet support
new feature: protect sheet support, relate issue #273
-rwxr-xr-x[-rw-r--r--]excelize_test.go13
-rw-r--r--lib.go28
-rw-r--r--sheet.go41
-rw-r--r--[-rwxr-xr-x]test/Book1.xlsxbin33070 -> 23099 bytes
-rw-r--r--xmlWorksheet.go61
5 files changed, 123 insertions, 20 deletions
diff --git a/excelize_test.go b/excelize_test.go
index d021b55..f7a70d9 100644..100755
--- a/excelize_test.go
+++ b/excelize_test.go
@@ -1208,6 +1208,19 @@ func TestSearchSheet(t *testing.T) {
t.Log(xlsx.SearchSheet("Sheet1", "A"))
}
+func TestProtectSheet(t *testing.T) {
+ xlsx := NewFile()
+ xlsx.ProtectSheet("Sheet1", nil)
+ xlsx.ProtectSheet("Sheet1", &FormatSheetProtection{
+ Password: "password",
+ EditScenarios: false,
+ })
+ err := xlsx.SaveAs("./test/Book_protect_sheet.xlsx")
+ if err != nil {
+ t.Error(err)
+ }
+}
+
func trimSliceSpace(s []string) []string {
for {
if len(s) > 0 && s[len(s)-1] == "" {
diff --git a/lib.go b/lib.go
index 8e63da9..cf43dc9 100644
--- a/lib.go
+++ b/lib.go
@@ -15,6 +15,8 @@ import (
"io"
"log"
"math"
+ "strconv"
+ "strings"
"unicode"
)
@@ -199,3 +201,29 @@ func namespaceStrictToTransitional(content []byte) []byte {
}
return content
}
+
+// genSheetPasswd provides a method to generate password for worksheet
+// protection by given plaintext. When an Excel sheet is being protected with
+// a password, a 16-bit (two byte) long hash is generated. To verify a
+// password, it is compared to the hash. Obviously, if the input data volume
+// is great, numerous passwords will match the same hash. Here is the
+// algorithm to create the hash value:
+//
+// take the ASCII values of all characters shift left the first character 1 bit, the second 2 bits and so on (use only the lower 15 bits and rotate all higher bits, the highest bit of the 16-bit value is always 0 [signed short])
+// XOR all these values
+// XOR the count of characters
+// XOR the constant 0xCE4B
+func genSheetPasswd(plaintext string) string {
+ var password int64 = 0x0000
+ var charPos uint = 1
+ for _, v := range plaintext {
+ value := int64(v) << charPos
+ charPos++
+ rotatedBits := value >> 15 // rotated bits beyond bit 15
+ value &= 0x7fff // first 15 bits
+ password ^= (value | rotatedBits)
+ }
+ password ^= int64(len(plaintext))
+ password ^= 0xCE4B
+ return strings.ToUpper(strconv.FormatInt(password, 16))
+}
diff --git a/sheet.go b/sheet.go
index 522d112..8ddb8c9 100644
--- a/sheet.go
+++ b/sheet.go
@@ -711,6 +711,47 @@ func (f *File) SearchSheet(sheet, value string) []string {
return result
}
+// ProtectSheet provides a function to prevent other users from accidentally
+// or deliberately changing, moving, or deleting data in a worksheet. For
+// example protect Sheet1 with protection settings:
+//
+// xlsx.ProtectSheet("Sheet1", &excelize.FormatSheetProtection{
+// Password: "password",
+// EditScenarios: false,
+// })
+//
+func (f *File) ProtectSheet(sheet string, settings *FormatSheetProtection) {
+ xlsx := f.workSheetReader(sheet)
+ if settings == nil {
+ settings = &FormatSheetProtection{
+ EditObjects: true,
+ EditScenarios: true,
+ SelectLockedCells: true,
+ }
+ }
+ xlsx.SheetProtection = &xlsxSheetProtection{
+ AutoFilter: settings.AutoFilter,
+ DeleteColumns: settings.DeleteColumns,
+ DeleteRows: settings.DeleteRows,
+ FormatCells: settings.FormatCells,
+ FormatColumns: settings.FormatColumns,
+ FormatRows: settings.FormatRows,
+ InsertColumns: settings.InsertColumns,
+ InsertHyperlinks: settings.InsertHyperlinks,
+ InsertRows: settings.InsertRows,
+ Objects: settings.EditObjects,
+ PivotTables: settings.PivotTables,
+ Scenarios: settings.EditScenarios,
+ SelectLockedCells: settings.SelectLockedCells,
+ SelectUnlockedCells: settings.SelectUnlockedCells,
+ Sheet: true,
+ Sort: settings.Sort,
+ }
+ if settings.Password != "" {
+ xlsx.SheetProtection.Password = genSheetPasswd(settings.Password)
+ }
+}
+
// trimSheetName provides a function to trim invaild characters by given worksheet
// name.
func trimSheetName(name string) string {
diff --git a/test/Book1.xlsx b/test/Book1.xlsx
index 7d9886e..84c43d1 100755..100644
--- a/test/Book1.xlsx
+++ b/test/Book1.xlsx
Binary files differ
diff --git a/xmlWorksheet.go b/xmlWorksheet.go
index 072ecce..42f8ddb 100644
--- a/xmlWorksheet.go
+++ b/xmlWorksheet.go
@@ -377,26 +377,27 @@ type xlsxF struct {
// xlsxSheetProtection collection expresses the sheet protection options to
// enforce when the sheet is protected.
type xlsxSheetProtection struct {
- AlgorithmName string `xml:"algorithmName,attr,omitempty"`
- AutoFilter int `xml:"autoFilter,attr,omitempty"`
- DeleteColumns int `xml:"deleteColumns,attr,omitempty"`
- DeleteRows int `xml:"deleteRows,attr,omitempty"`
- FormatCells int `xml:"formatCells,attr,omitempty"`
- FormatColumns int `xml:"formatColumns,attr,omitempty"`
- FormatRows int `xml:"formatRows,attr,omitempty"`
- HashValue string `xml:"hashValue,attr,omitempty"`
- InsertColumns int `xml:"insertColumns,attr,omitempty"`
- InsertHyperlinks int `xml:"insertHyperlinks,attr,omitempty"`
- InsertRows int `xml:"insertRows,attr,omitempty"`
- Objects int `xml:"objects,attr,omitempty"`
- PivotTables int `xml:"pivotTables,attr,omitempty"`
- SaltValue string `xml:"saltValue,attr,omitempty"`
- Scenarios int `xml:"scenarios,attr,omitempty"`
- SelectLockedCells int `xml:"selectLockedCells,attr,omitempty"`
- SelectUnlockedCell int `xml:"selectUnlockedCell,attr,omitempty"`
- Sheet int `xml:"sheet,attr,omitempty"`
- Sort int `xml:"sort,attr,omitempty"`
- SpinCount int `xml:"spinCount,attr,omitempty"`
+ AlgorithmName string `xml:"algorithmName,attr,omitempty"`
+ AutoFilter bool `xml:"autoFilter,attr,omitempty"`
+ DeleteColumns bool `xml:"deleteColumns,attr,omitempty"`
+ DeleteRows bool `xml:"deleteRows,attr,omitempty"`
+ FormatCells bool `xml:"formatCells,attr,omitempty"`
+ FormatColumns bool `xml:"formatColumns,attr,omitempty"`
+ FormatRows bool `xml:"formatRows,attr,omitempty"`
+ HashValue string `xml:"hashValue,attr,omitempty"`
+ InsertColumns bool `xml:"insertColumns,attr,omitempty"`
+ InsertHyperlinks bool `xml:"insertHyperlinks,attr,omitempty"`
+ InsertRows bool `xml:"insertRows,attr,omitempty"`
+ Objects bool `xml:"objects,attr,omitempty"`
+ Password string `xml:"password,attr,omitempty"`
+ PivotTables bool `xml:"pivotTables,attr,omitempty"`
+ SaltValue string `xml:"saltValue,attr,omitempty"`
+ Scenarios bool `xml:"scenarios,attr,omitempty"`
+ SelectLockedCells bool `xml:"selectLockedCells,attr,omitempty"`
+ SelectUnlockedCells bool `xml:"selectUnlockedCells,attr,omitempty"`
+ Sheet bool `xml:"sheet,attr,omitempty"`
+ Sort bool `xml:"sort,attr,omitempty"`
+ SpinCount int `xml:"spinCount,attr,omitempty"`
}
// xlsxPhoneticPr (Phonetic Properties) represents a collection of phonetic
@@ -599,3 +600,23 @@ type formatConditional struct {
MultiRange string `json:"multi_range,omitempty"`
BarColor string `json:"bar_color,omitempty"`
}
+
+// FormatSheetProtection directly maps the settings of worksheet protection.
+type FormatSheetProtection struct {
+ AutoFilter bool
+ DeleteColumns bool
+ DeleteRows bool
+ EditObjects bool
+ EditScenarios bool
+ FormatCells bool
+ FormatColumns bool
+ FormatRows bool
+ InsertColumns bool
+ InsertHyperlinks bool
+ InsertRows bool
+ Password string
+ PivotTables bool
+ SelectLockedCells bool
+ SelectUnlockedCells bool
+ Sort bool
+}