Skip to content

Commit

Permalink
resolve qax-os#273 new feature: protect sheet support
Browse files Browse the repository at this point in the history
new feature: protect sheet support, relate issue qax-os#273
  • Loading branch information
HcySunYang authored and xuri committed Nov 2, 2018
1 parent 950dd95 commit 7f5d67d
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 20 deletions.
13 changes: 13 additions & 0 deletions excelize_test.go
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -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] == "" {
Expand Down
28 changes: 28 additions & 0 deletions lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
"io"
"log"
"math"
"strconv"
"strings"
"unicode"
)

Expand Down Expand Up @@ -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))
}
41 changes: 41 additions & 0 deletions sheet.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Binary file modified test/Book1.xlsx
100755 → 100644
Binary file not shown.
61 changes: 41 additions & 20 deletions xmlWorksheet.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}

0 comments on commit 7f5d67d

Please sign in to comment.