diff --git a/docs/eventmanager.md b/docs/eventmanager.md
index 91bf9569..a0c4f152 100644
--- a/docs/eventmanager.md
+++ b/docs/eventmanager.md
@@ -28,10 +28,13 @@ The following placeholders are supported:
- `{{StatusString}}`. Status as string. Possible values "OK", "KO".
- `{{ErrorString}}`. Error details. Replaced with an empty string if no errors occur.
- `{{VirtualPath}}`. Path seen by SFTPGo users, for example `/adir/afile.txt`.
+- `{{VirtualDirPath}}`. Parent directory for VirtualPath, for example if VirtualPath is "/adir/afile.txt", VirtualDirPath is "/adir".
- `{{FsPath}}`. Full filesystem path, for example `/user/homedir/adir/afile.txt` or `C:/data/user/homedir/adir/afile.txt` on Windows.
- `{{ObjectName}}`. File/directory name, for example `afile.txt` or provider object name.
- `{{ObjectType}}`. Object type for provider events: `user`, `group`, `admin`, etc.
- `{{VirtualTargetPath}}`. Virtual target path for renames.
+- `{{VirtualTargetDirPath}}`. Parent directory for VirtualTargetPath.
+- `{{TargetName}}`. Target object name for renames.
- `{{FsTargetPath}}`. Full filesystem target path for renames.
- `{{FileSize}}`. File size.
- `{{Protocol}}`. Used protocol, for example `SFTP`, `FTP`.
diff --git a/internal/common/eventmanager.go b/internal/common/eventmanager.go
index 63f070ad..58214e93 100644
--- a/internal/common/eventmanager.go
+++ b/internal/common/eventmanager.go
@@ -483,7 +483,13 @@ func (p *EventParams) setBackupParams(backupPath string) {
}
p.sender = dataprovider.ActionExecutorSystem
p.FsPath = backupPath
- p.VirtualPath = filepath.Base(backupPath)
+ p.ObjectName = filepath.Base(backupPath)
+ p.VirtualPath = "/" + p.ObjectName
+ p.Timestamp = time.Now().UnixNano()
+ info, err := os.Stat(backupPath)
+ if err == nil {
+ p.FileSize = info.Size()
+ }
}
func (p *EventParams) getStatusString() string {
@@ -607,6 +613,13 @@ func (p *EventParams) getStringReplacements(addObjectData bool) []string {
"{{Timestamp}}", fmt.Sprintf("%d", p.Timestamp),
"{{StatusString}}", p.getStatusString(),
}
+ if p.VirtualPath != "" {
+ replacements = append(replacements, "{{VirtualDirPath}}", path.Dir(p.VirtualPath))
+ }
+ if p.VirtualTargetPath != "" {
+ replacements = append(replacements, "{{VirtualTargetDirPath}}", path.Dir(p.VirtualTargetPath))
+ replacements = append(replacements, "{{TargetName}}", path.Base(p.VirtualTargetPath))
+ }
if len(p.errors) > 0 {
replacements = append(replacements, "{{ErrorString}}", strings.Join(p.errors, ", "))
} else {
diff --git a/internal/common/protocol_test.go b/internal/common/protocol_test.go
index 66b4c7b6..a0534b84 100644
--- a/internal/common/protocol_test.go
+++ b/internal/common/protocol_test.go
@@ -3868,7 +3868,7 @@ func TestEventRuleFsActions(t *testing.T) {
Type: dataprovider.FilesystemActionRename,
Renames: []dataprovider.KeyValue{
{
- Key: "/{{VirtualPath}}",
+ Key: "/{{VirtualDirPath}}/{{ObjectName}}",
Value: "/{{ObjectName}}_renamed",
},
},
@@ -5294,6 +5294,86 @@ func TestEventRuleFirstUploadDownloadActions(t *testing.T) {
require.NoError(t, err)
}
+func TestEventRuleRenameEvent(t *testing.T) {
+ smtpCfg := smtp.Config{
+ Host: "127.0.0.1",
+ Port: 2525,
+ From: "notify@example.com",
+ TemplatesPath: "templates",
+ }
+ err := smtpCfg.Initialize(configDir)
+ require.NoError(t, err)
+
+ a1 := dataprovider.BaseEventAction{
+ Name: "action1",
+ Type: dataprovider.ActionTypeEmail,
+ Options: dataprovider.BaseEventActionOptions{
+ EmailConfig: dataprovider.EventActionEmailConfig{
+ Recipients: []string{"test@example.com"},
+ Subject: `"{{Event}}" from "{{Name}}"`,
+ Body: `Fs path {{FsPath}}, Target path "{{VirtualTargetDirPath}}/{{TargetName}}", size: {{FileSize}}`,
+ },
+ },
+ }
+ action1, _, err := httpdtest.AddEventAction(a1, http.StatusCreated)
+ assert.NoError(t, err)
+ r1 := dataprovider.EventRule{
+ Name: "test rename rule",
+ Trigger: dataprovider.EventTriggerFsEvent,
+ Conditions: dataprovider.EventConditions{
+ FsEvents: []string{"rename"},
+ },
+ Actions: []dataprovider.EventAction{
+ {
+ BaseEventAction: dataprovider.BaseEventAction{
+ Name: action1.Name,
+ },
+ Order: 1,
+ },
+ },
+ }
+ rule1, _, err := httpdtest.AddEventRule(r1, http.StatusCreated)
+ assert.NoError(t, err)
+
+ user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
+ assert.NoError(t, err)
+ conn, client, err := getSftpClient(user)
+ if assert.NoError(t, err) {
+ defer conn.Close()
+ defer client.Close()
+
+ testFileSize := int64(32768)
+ lastReceivedEmail.reset()
+ err = writeSFTPFileNoCheck(testFileName, testFileSize, client)
+ assert.NoError(t, err)
+ err = client.Mkdir("subdir")
+ assert.NoError(t, err)
+ err = client.Rename(testFileName, path.Join("/subdir", testFileName))
+ assert.NoError(t, err)
+ assert.Eventually(t, func() bool {
+ return lastReceivedEmail.get().From != ""
+ }, 1500*time.Millisecond, 100*time.Millisecond)
+ email := lastReceivedEmail.get()
+ assert.Len(t, email.To, 1)
+ assert.True(t, util.Contains(email.To, "test@example.com"))
+ assert.Contains(t, email.Data, fmt.Sprintf(`Subject: "rename" from "%s"`, user.Username))
+ assert.Contains(t, email.Data, fmt.Sprintf("Target path %q", path.Join("/subdir", testFileName)))
+ }
+
+ _, err = httpdtest.RemoveEventRule(rule1, http.StatusOK)
+ assert.NoError(t, err)
+ _, err = httpdtest.RemoveEventAction(action1, http.StatusOK)
+ assert.NoError(t, err)
+ _, err = httpdtest.RemoveUser(user, http.StatusOK)
+ assert.NoError(t, err)
+ err = os.RemoveAll(user.GetHomeDir())
+ assert.NoError(t, err)
+
+ smtpCfg = smtp.Config{}
+ err = smtpCfg.Initialize(configDir)
+ require.NoError(t, err)
+}
+
func TestEventRuleCertificate(t *testing.T) {
smtpCfg := smtp.Config{
Host: "127.0.0.1",
diff --git a/templates/webadmin/eventaction.html b/templates/webadmin/eventaction.html
index 3a31dad1..eed0d461 100644
--- a/templates/webadmin/eventaction.html
+++ b/templates/webadmin/eventaction.html
@@ -669,6 +669,9 @@ along with this program. If not, see
{{`{{VirtualPath}}`}} => Path seen by SFTPGo users, for example "/adir/afile.txt".
++ {{`{{VirtualDirPath}}`}} => Parent directory for VirtualPath, for example if VirtualPath is "/adir/afile.txt", VirtualDirPath is "/adir". +
{{`{{FsPath}}`}} => Full filesystem path, for example "/user/homedir/adir/afile.txt" or "C:/data/user/homedir/adir/afile.txt" on Windows.
@@ -681,6 +684,12 @@ along with this program. If not, see{{`{{VirtualTargetPath}}`}} => Virtual target path for renames.
++ {{`{{VirtualTargetDirPath}}`}} => Parent directory for VirtualTargetPath. +
++ {{`{{TargetName}}`}} => Target object name for renames. +
{{`{{FsTargetPath}}`}} => Full filesystem target path for renames.