eventmanager: auto-create destination folder for renames
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
51f0ded222
commit
f0dedbfabf
8 changed files with 17 additions and 37 deletions
|
@ -117,19 +117,13 @@ Create an action named `move to recycle` with the settings you can see in the fo
|
||||||
|
|
||||||
![Recycle move action](./img/recycle-move-action.png)
|
![Recycle move action](./img/recycle-move-action.png)
|
||||||
|
|
||||||
Add another action, named `create move folder`, to create the parent directory for the move destination.
|
|
||||||
|
|
||||||
![Recycle create move folder action](./img/recycle-move-folder-action.png)
|
|
||||||
|
|
||||||
Now select `Event rules` and create a rule named `Recycle rule`, select `Filesystem events` as trigger, `pre-delete` as filesystem event and exclude the `/recycle` path.
|
Now select `Event rules` and create a rule named `Recycle rule`, select `Filesystem events` as trigger, `pre-delete` as filesystem event and exclude the `/recycle` path.
|
||||||
|
|
||||||
![Recycle rule](./img/recycle-rule.png)
|
![Recycle rule](./img/recycle-rule.png)
|
||||||
|
|
||||||
![Recycle rule exclude path](./img/recycle-rule-path.png)
|
![Recycle rule exclude path](./img/recycle-rule-path.png)
|
||||||
|
|
||||||
As actions, select `create move folder` and `move to recycle` and for both set `Execute sync`.
|
As actions, select `move to recycle` and set `Execute sync`.
|
||||||
|
|
||||||
![Recycle rule actions](./img/recycle-rule-actions.png)
|
|
||||||
|
|
||||||
Done! Try deleting a file, it will be moved to the Recycle Bin.
|
Done! Try deleting a file, it will be moved to the Recycle Bin.
|
||||||
|
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 47 KiB |
Binary file not shown.
Before Width: | Height: | Size: 35 KiB |
4
go.mod
4
go.mod
|
@ -34,10 +34,10 @@ require (
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/hashicorp/go-hclog v1.4.0
|
github.com/hashicorp/go-hclog v1.4.0
|
||||||
github.com/hashicorp/go-plugin v1.4.8
|
github.com/hashicorp/go-plugin v1.4.8
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.1
|
github.com/hashicorp/go-retryablehttp v0.7.2
|
||||||
github.com/jackc/pgx/v5 v5.2.0
|
github.com/jackc/pgx/v5 v5.2.0
|
||||||
github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
|
github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
|
||||||
github.com/klauspost/compress v1.15.13
|
github.com/klauspost/compress v1.15.14
|
||||||
github.com/lestrrat-go/jwx/v2 v2.0.8
|
github.com/lestrrat-go/jwx/v2 v2.0.8
|
||||||
github.com/lithammer/shortuuid/v3 v3.0.7
|
github.com/lithammer/shortuuid/v3 v3.0.7
|
||||||
github.com/mattn/go-sqlite3 v1.14.16
|
github.com/mattn/go-sqlite3 v1.14.16
|
||||||
|
|
7
go.sum
7
go.sum
|
@ -930,8 +930,9 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9
|
||||||
github.com/hashicorp/go-plugin v1.4.8 h1:CHGwpxYDOttQOY7HOWgETU9dyVjOXzniXDqJcYJE1zM=
|
github.com/hashicorp/go-plugin v1.4.8 h1:CHGwpxYDOttQOY7HOWgETU9dyVjOXzniXDqJcYJE1zM=
|
||||||
github.com/hashicorp/go-plugin v1.4.8/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s=
|
github.com/hashicorp/go-plugin v1.4.8/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s=
|
||||||
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ=
|
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0=
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||||
|
@ -1066,8 +1067,8 @@ github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs
|
||||||
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
github.com/klauspost/compress v1.15.13 h1:NFn1Wr8cfnenSJSA46lLq4wHCcBzKTSjnBIexDMMOV0=
|
github.com/klauspost/compress v1.15.14 h1:i7WCKDToww0wA+9qrUZ1xOjp218vfFo3nTU6UHp+gOc=
|
||||||
github.com/klauspost/compress v1.15.13/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
github.com/klauspost/compress v1.15.14/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.2/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
github.com/klauspost/cpuid/v2 v2.2.2/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
|
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
|
||||||
|
|
|
@ -676,6 +676,10 @@ func (c *BaseConnection) Copy(virtualSourcePath, virtualTargetPath string) error
|
||||||
|
|
||||||
// Rename renames (moves) virtualSourcePath to virtualTargetPath
|
// Rename renames (moves) virtualSourcePath to virtualTargetPath
|
||||||
func (c *BaseConnection) Rename(virtualSourcePath, virtualTargetPath string) error {
|
func (c *BaseConnection) Rename(virtualSourcePath, virtualTargetPath string) error {
|
||||||
|
return c.renameInternal(virtualSourcePath, virtualTargetPath, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BaseConnection) renameInternal(virtualSourcePath, virtualTargetPath string, checkParentDestination bool) error {
|
||||||
if virtualSourcePath == virtualTargetPath {
|
if virtualSourcePath == virtualTargetPath {
|
||||||
return fmt.Errorf("the rename source and target cannot be the same: %w", c.GetOpUnsupportedError())
|
return fmt.Errorf("the rename source and target cannot be the same: %w", c.GetOpUnsupportedError())
|
||||||
}
|
}
|
||||||
|
@ -696,6 +700,7 @@ func (c *BaseConnection) Rename(virtualSourcePath, virtualTargetPath string) err
|
||||||
}
|
}
|
||||||
initialSize := int64(-1)
|
initialSize := int64(-1)
|
||||||
if dstInfo, err := fsDst.Lstat(fsTargetPath); err == nil {
|
if dstInfo, err := fsDst.Lstat(fsTargetPath); err == nil {
|
||||||
|
checkParentDestination = false
|
||||||
if dstInfo.IsDir() {
|
if dstInfo.IsDir() {
|
||||||
c.Log(logger.LevelWarn, "attempted to rename %q overwriting an existing directory %q",
|
c.Log(logger.LevelWarn, "attempted to rename %q overwriting an existing directory %q",
|
||||||
fsSourcePath, fsTargetPath)
|
fsSourcePath, fsTargetPath)
|
||||||
|
@ -720,6 +725,9 @@ func (c *BaseConnection) Rename(virtualSourcePath, virtualTargetPath string) err
|
||||||
c.Log(logger.LevelInfo, "denying cross rename due to space limit")
|
c.Log(logger.LevelInfo, "denying cross rename due to space limit")
|
||||||
return c.GetGenericError(ErrQuotaExceeded)
|
return c.GetGenericError(ErrQuotaExceeded)
|
||||||
}
|
}
|
||||||
|
if checkParentDestination {
|
||||||
|
c.CheckParentDirs(path.Dir(virtualTargetPath)) //nolint:errcheck
|
||||||
|
}
|
||||||
if err := fsDst.Rename(fsSourcePath, fsTargetPath); err != nil {
|
if err := fsDst.Rename(fsSourcePath, fsTargetPath); err != nil {
|
||||||
c.Log(logger.LevelError, "failed to rename %q -> %q: %+v", fsSourcePath, fsTargetPath, err)
|
c.Log(logger.LevelError, "failed to rename %q -> %q: %+v", fsSourcePath, fsTargetPath, err)
|
||||||
return c.GetFsError(fsSrc, err)
|
return c.GetFsError(fsSrc, err)
|
||||||
|
|
|
@ -1477,7 +1477,7 @@ func executeRenameFsActionForUser(renames []dataprovider.KeyValue, replacer *str
|
||||||
for _, item := range renames {
|
for _, item := range renames {
|
||||||
source := util.CleanPath(replaceWithReplacer(item.Key, replacer))
|
source := util.CleanPath(replaceWithReplacer(item.Key, replacer))
|
||||||
target := util.CleanPath(replaceWithReplacer(item.Value, replacer))
|
target := util.CleanPath(replaceWithReplacer(item.Value, replacer))
|
||||||
if err = conn.Rename(source, target); err != nil {
|
if err = conn.renameInternal(source, target, true); err != nil {
|
||||||
return fmt.Errorf("unable to rename %q->%q, user %q: %w", source, target, user.Username, err)
|
return fmt.Errorf("unable to rename %q->%q, user %q: %w", source, target, user.Username, err)
|
||||||
}
|
}
|
||||||
eventManagerLog(logger.LevelDebug, "rename %q->%q ok, user %q", source, target, user.Username)
|
eventManagerLog(logger.LevelDebug, "rename %q->%q ok, user %q", source, target, user.Username)
|
||||||
|
|
|
@ -4200,18 +4200,6 @@ func TestEventRulePreDelete(t *testing.T) {
|
||||||
a1 := dataprovider.BaseEventAction{
|
a1 := dataprovider.BaseEventAction{
|
||||||
Name: "a1",
|
Name: "a1",
|
||||||
Type: dataprovider.ActionTypeFilesystem,
|
Type: dataprovider.ActionTypeFilesystem,
|
||||||
Options: dataprovider.BaseEventActionOptions{
|
|
||||||
FsConfig: dataprovider.EventActionFilesystemConfig{
|
|
||||||
Type: dataprovider.FilesystemActionMkdirs,
|
|
||||||
MkDirs: []string{fmt.Sprintf("/%s/{{VirtualDirPath}}", movePath)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
action1, resp, err := httpdtest.AddEventAction(a1, http.StatusCreated)
|
|
||||||
assert.NoError(t, err, string(resp))
|
|
||||||
a2 := dataprovider.BaseEventAction{
|
|
||||||
Name: "a2",
|
|
||||||
Type: dataprovider.ActionTypeFilesystem,
|
|
||||||
Options: dataprovider.BaseEventActionOptions{
|
Options: dataprovider.BaseEventActionOptions{
|
||||||
FsConfig: dataprovider.EventActionFilesystemConfig{
|
FsConfig: dataprovider.EventActionFilesystemConfig{
|
||||||
Type: dataprovider.FilesystemActionRename,
|
Type: dataprovider.FilesystemActionRename,
|
||||||
|
@ -4224,7 +4212,7 @@ func TestEventRulePreDelete(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
action2, resp, err := httpdtest.AddEventAction(a2, http.StatusCreated)
|
action1, resp, err := httpdtest.AddEventAction(a1, http.StatusCreated)
|
||||||
assert.NoError(t, err, string(resp))
|
assert.NoError(t, err, string(resp))
|
||||||
r1 := dataprovider.EventRule{
|
r1 := dataprovider.EventRule{
|
||||||
Name: "rule1",
|
Name: "rule1",
|
||||||
|
@ -4250,15 +4238,6 @@ func TestEventRulePreDelete(t *testing.T) {
|
||||||
ExecuteSync: true,
|
ExecuteSync: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
BaseEventAction: dataprovider.BaseEventAction{
|
|
||||||
Name: action2.Name,
|
|
||||||
},
|
|
||||||
Order: 2,
|
|
||||||
Options: dataprovider.EventActionOptions{
|
|
||||||
ExecuteSync: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
rule1, _, err := httpdtest.AddEventRule(r1, http.StatusCreated)
|
rule1, _, err := httpdtest.AddEventRule(r1, http.StatusCreated)
|
||||||
|
@ -4325,8 +4304,6 @@ func TestEventRulePreDelete(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
_, err = httpdtest.RemoveEventAction(action1, http.StatusOK)
|
_, err = httpdtest.RemoveEventAction(action1, http.StatusOK)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
_, err = httpdtest.RemoveEventAction(action2, http.StatusOK)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = os.RemoveAll(user.GetHomeDir())
|
err = os.RemoveAll(user.GetHomeDir())
|
||||||
|
|
Loading…
Reference in a new issue