diff --git a/frontend/src/ketr-photos/ketr-photos.html b/frontend/src/ketr-photos/ketr-photos.html index 8ad37e9..1092da1 100755 --- a/frontend/src/ketr-photos/ketr-photos.html +++ b/frontend/src/ketr-photos/ketr-photos.html @@ -335,8 +335,14 @@
Trash
-
There are [[add(thumbnails.length,pendingPhotos.length)]] photos in the trash.
-
Do you want to purge the trash?
+
+

There are [[add(thumbnails.length,pendingPhotos.length)]] photos in the trash.

+

Files in the trash will be removed after 60 days.

+
+
Do you want to empty the trash now?
+ empty trash +

NOTE: This can not be undone.

+
Duplicate names
@@ -1421,6 +1427,22 @@ }.bind(this, thumbnail), {}, "PUT"); }, + purgeTrashAction: function() { + var query = ""; + if (this.mode == "trash") { + console.log("TODO: Prompt user 'Are you sure?' ?"); + query = "?permanent=1"; + } + window.fetch("api/v1/photos/" + query, function(error) { + if (error) { + console.log("Unable to take action on photo: " + error); + return; + } + + this.resetPhotos(); + }.bind(this), {}, "DELETE"); + }, + deleteAction: function(thumbnail) { thumbnail.disabled = true; var query = ""; diff --git a/server/routes/photos.js b/server/routes/photos.js index 0e9df0b..beedec3 100755 --- a/server/routes/photos.js +++ b/server/routes/photos.js @@ -5,7 +5,8 @@ const express = require("express"), config = require("config"), moment = require("moment"), crypto = require("crypto"), - util = require("util"); + util = require("util"), + Promise = require("bluebird"); const execFile = util.promisify(require("child_process").execFile); @@ -314,32 +315,45 @@ const getPhoto = function(id) { }); } -router.delete("/:id", function(req, res/*, next*/) { +router.delete("/:id?", function(req, res/*, next*/) { if (!req.user.maintainer) { return res.status(401).send("Unauthorized to delete photos."); } const replacements = { - id: req.params.id + id: req.params.id || "*" }; + if (!req.params.id && !req.query.permanent) { + return res.status(400).send("Trash can only be emptied if permanent."); + } + + let where = ""; + if (req.params.id) { + where = "photos.id=:id"; + if (req.query.permanent) { + where += " AND photos.deleted=1"; + } + } else { + where = "photos.deleted=1"; + } + console.log("DELETE /" + replacements.id, req.query); return photoDB.sequelize.query("SELECT " + "photos.*,albums.path AS path,photohashes.hash,(albums.path || photos.filename) AS filepath FROM photos " + "LEFT JOIN albums ON albums.id=photos.albumId " + "LEFT JOIN photohashes ON photohashes.photoId=photos.id " + - "WHERE photos.id=:id", { + "WHERE " + where, { replacements: replacements, type: photoDB.Sequelize.QueryTypes.SELECT, raw: true }).then(function(photos) { if (photos.length == 0) { - res.status(404).send("Unable to find photo " + req.params.id); + res.status(404).send("Unable to find photo " + replacements.id); return true; } - const photo = photos[0]; if (!req.query.permanent) { return photoDB.sequelize.query("UPDATE photos SET deleted=1,updated=CURRENT_TIMESTAMP WHERE id=:id", { replacements: replacements @@ -348,82 +362,83 @@ router.delete("/:id", function(req, res/*, next*/) { }); } - /* Delete the asset from disk and the DB - * 1. Look if there are duplicates. - * 2. Update all other duplicates to be duplicates of the first image that remains - * 3. If no duplicates, DELETE the entry from photohashes - * 4. If there are duplicates, update the HASH entry to point to the first image that remains - * 5. Delete the entry from photos - * 6. Delete the scaled, thumb, and original from disk - */ - return photoDB.sequelize.transaction(function(transaction) { - return photoDB.sequelize.query("SELECT id FROM photos WHERE duplicate=:id", { - replacements: photo, - type: photoDB.Sequelize.QueryTypes.SELECT, - raw: true - }).then(function(duplicates) { - if (!duplicates.length) { - return null; - } + /** + * Delete the asset from disk and the DB + * 1. Look if there are duplicates. + * 2. Update all other duplicates to be duplicates of the first image that remains + * 3. If no duplicates, DELETE the entry from photohashes + * 4. If there are duplicates, update the HASH entry to point to the first image that remains + * 5. Delete the entry from photos + * 6. Delete the scaled, thumb, and original from disk + */ + return Promise.mapSeries(photos, function(photo) { + return photoDB.sequelize.transaction(function(transaction) { + return photoDB.sequelize.query("SELECT id FROM photos WHERE duplicate=:id", { + replacements: photo, + type: photoDB.Sequelize.QueryTypes.SELECT, + raw: true + }).then(function(duplicates) { + if (!duplicates.length) { + return null; + } - let first = duplicates.shift(); - let needsUpdate = []; - duplicates.forEach(function(duplicate) { - needsUpdate.push(duplicate.id); - }); - - if (!needsUpdate.length) { - return first; - } + let first = duplicates.shift(); + let needsUpdate = []; + duplicates.forEach(function(duplicate) { + needsUpdate.push(duplicate.id); + }); + + if (!needsUpdate.length) { + return first; + } - // 2. Update all other duplicates to be duplicates of the first image that remains - console.log("Updating " + needsUpdate + " to point to " + first.id); - return photoDB.sequelize.query( - "UPDATE photos SET duplicate=:first WHERE id IN (:needsUpdate)", { - replacements: { - first: first.id, - needsUpdate: needsUpdate - }, - transaction: transaction - }).then(function() { - return first; - }); - }).then(function(first) { - if (!first) { - console.log("Deleting "+ photo.id + " from photohash."); - // 3. If no duplicates, DELETE the entry from photohashes + // 2. Update all other duplicates to be duplicates of the first image that remains + console.log("Updating " + needsUpdate + " to point to " + first.id); return photoDB.sequelize.query( - "DELETE FROM photohashes WHERE photoId=:photo", { + "UPDATE photos SET duplicate=:first WHERE id IN (:needsUpdate)", { replacements: { + first: first.id, + needsUpdate: needsUpdate + }, + transaction: transaction + }).then(function() { + return first; + }); + }).then(function(first) { + if (!first) { + console.log("Deleting "+ photo.id + " from photohash."); + // 3. If no duplicates, DELETE the entry from photohashes + return photoDB.sequelize.query( + "DELETE FROM photohashes WHERE photoId=:id", { + replacements: photo, + transaction: transaction + }); + } + console.log("Updating photohash for " + photo.id + " to point to " + first.id); + // 4. If there are duplicates, update the HASH entry to point to the first image that remains + return photoDB.sequelize.query( + "UPDATE photohashes SET photoId=:first WHERE photoId=:photo", { + replacements: { + first: first.id, photo: photo.id }, transaction: transaction }); - } - console.log("Updating photohash for " + photo.id + " to point to " + first.id); - // 4. If there are duplicates, update the HASH entry to point to the first image that remains - return photoDB.sequelize.query( - "UPDATE photohashes SET photoId=:first WHERE photoId=:photo", { - replacements: { - first: first.id, - photo: photo.id - }, - transaction: transaction - }); - }).then(function() { - console.log("Deleting " + photo.path + photo.filename + " from DB."); - // 5. Delete the entry from photos - return photoDB.sequelize.query("DELETE FROM photos WHERE id=:id", { - replacements: replacements, - transaction: transaction - }); - }).then(function() { - // 6. Delete the scaled, thumb, and original from disk - console.log("Deleting " + photo.path + photo.filename + " from disk."); - return unlink(photo.path + "thumbs/scaled/" + photo.filename).catch(function() {}).then(function() { - return unlink(photo.path + "thumbs/" + photo.filename).catch(function() {}).then(function() { - return unlink(photo.path + photo.filename).catch(function(error) { - console.log("Error removing file: " + error); + }).then(function() { + console.log("Deleting " + photo.path + photo.filename + " from DB."); + // 5. Delete the entry from photos + return photoDB.sequelize.query("DELETE FROM photos WHERE id=:id", { + replacements: photo, + transaction: transaction + }); + }).then(function() { + // 6. Delete the scaled, thumb, and original from disk + console.log("Deleting " + photo.path + photo.filename + " from disk."); + return unlink(photo.path + "thumbs/scaled/" + photo.filename).catch(function() {}).then(function() { + return unlink(photo.path + "thumbs/" + photo.filename).catch(function() {}).then(function() { + return unlink(photo.path + photo.filename).catch(function(error) { + console.log("Error removing file: " + error); + }); }); }); }); @@ -565,7 +580,7 @@ router.get("/trash", function(req, res/*, next*/) { return photoDB.sequelize.query( "SELECT photos.*,albums.path AS path,(albums.path || photos.filename) AS filepath FROM photos " + "LEFT JOIN albums ON albums.id=photos.albumId " + - "WHERE deleted=1 ORDER BY photos.updated", { + "WHERE deleted=1 ORDER BY photos.updated DESC", { type: photoDB.Sequelize.QueryTypes.SELECT, raw: true }).then(function(photos) {