aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2023-12-25 11:54:07 +0100
committerPřemysl Eric Janouch <p@janouch.name>2023-12-25 11:54:07 +0100
commitccf9aa8512e46261cca0df3e27f71904c22234d8 (patch)
treeeecf47eab17fb767679113fcb693c8b5a2e51639
parentb1857cbe0437060faa1fc5aa64bc4676a34acd78 (diff)
downloadgallery-ccf9aa8512e46261cca0df3e27f71904c22234d8.tar.gz
gallery-ccf9aa8512e46261cca0df3e27f71904c22234d8.tar.xz
gallery-ccf9aa8512e46261cca0df3e27f71904c22234d8.zip
Add a remove command
-rw-r--r--main.go56
-rw-r--r--public/style.css2
2 files changed, 52 insertions, 6 deletions
diff --git a/main.go b/main.go
index 8179446..811a2c6 100644
--- a/main.go
+++ b/main.go
@@ -344,7 +344,7 @@ func handleAPIBrowse(w http.ResponseWriter, r *http.Request) {
}
defer tx.Rollback()
- parent, err := idForPath(tx, decodeWebPath(params.Path), false)
+ parent, err := idForDirectoryPath(tx, decodeWebPath(params.Path), false)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
@@ -621,7 +621,7 @@ func getOrphanReplacement(webPath string) (*webOrphanImage, error) {
return nil, nil
}
- parent, err := idForPath(tx, path[:len(path)-1], false)
+ parent, err := idForDirectoryPath(tx, path[:len(path)-1], false)
if err != nil {
return nil, err
}
@@ -1070,7 +1070,7 @@ func cmdWeb(args []string) error {
// --- Import ------------------------------------------------------------------
-func idForPath(tx *sql.Tx, path []string, create bool) (int64, error) {
+func idForDirectoryPath(tx *sql.Tx, path []string, create bool) (int64, error) {
var parent sql.NullInt64
for _, name := range path {
if err := tx.QueryRow(`SELECT id FROM node
@@ -1130,7 +1130,7 @@ func (dm *directoryManager) IDForDirectoryPath(
return id, nil
}
- id, err := idForPath(tx, list, true)
+ id, err := idForDirectoryPath(tx, list, true)
if err != nil {
return 0, err
}
@@ -1688,7 +1688,7 @@ func syncRoot(c *syncContext, fsPath string) error {
//
// Synchronizing F → D or * → F are special cases not worth implementing.
crumbs := decodeWebPath(filepath.ToSlash(fsPath))
- dbParent, err := idForPath(c.tx, crumbs, true)
+ dbParent, err := idForDirectoryPath(c.tx, crumbs, true)
if err != nil {
return err
}
@@ -1881,6 +1881,51 @@ func cmdSync(args []string) error {
return tx.Commit()
}
+// --- Removal -----------------------------------------------------------------
+
+// cmdRemove is for manual removal of subtrees from the database.
+// Beware that inputs are database, not filesystem paths.
+func cmdRemove(args []string) error {
+ if len(args) < 2 {
+ return errors.New("usage: GD PATH...")
+ }
+ if err := openDB(args[0]); err != nil {
+ return err
+ }
+
+ tx, err := db.BeginTx(context.Background(), nil)
+ if err != nil {
+ return err
+ }
+ defer tx.Rollback()
+
+ for _, path := range args[1:] {
+ var id sql.NullInt64
+ for _, name := range decodeWebPath(path) {
+ if err := tx.QueryRow(`SELECT id FROM node
+ WHERE parent IS ? AND name = ?`,
+ id, name).Scan(&id); err != nil {
+ return err
+ }
+ }
+ if id.Int64 == 0 {
+ return errors.New("can't remove root")
+ }
+
+ if _, err = tx.Exec(disposeCTE+`
+ INSERT OR IGNORE INTO orphan(sha1, path)
+ SELECT sha1, path FROM orphaned`, id); err != nil {
+ return err
+ }
+ if _, err = tx.Exec(disposeCTE+`
+ DELETE FROM node WHERE id
+ IN (SELECT DISTINCT id FROM children)`, id); err != nil {
+ return err
+ }
+ }
+ return tx.Commit()
+}
+
// --- Tagging -----------------------------------------------------------------
// cmdTag mass imports tags from data passed on stdin as a TSV
@@ -2338,6 +2383,7 @@ var commands = map[string]struct {
"import": {cmdImport},
"tag": {cmdTag},
"sync": {cmdSync},
+ "remove": {cmdRemove},
"check": {cmdCheck},
"thumbnail": {cmdThumbnail},
"dhash": {cmdDhash},
diff --git a/public/style.css b/public/style.css
index e8eb0bc..e0fc004 100644
--- a/public/style.css
+++ b/public/style.css
@@ -49,7 +49,7 @@ a { color: inherit; }
.browser { overflow: auto; display: flex; flex-wrap: wrap;
align-content: flex-start; justify-content: center; align-items: center;
- gap: 3px; padding: 9px; }
+ gap: 3px; padding: 9px; flex-grow: 1; }
.browser:focus-visible { outline: 0; box-shadow: none; }
.tags { padding: .5rem; flex-grow: 1; overflow: auto; }