From adaa23d39f911690e5058e7d04887eea2b34870e Mon Sep 17 00:00:00 2001 From: Nils Asmussen Date: Wed, 3 Aug 2016 21:33:50 +0200 Subject: [PATCH 1/8] Syntax adjustments in album.js. --- src/scripts/album.js | 35 ++++++++++++++++++++--------------- src/scripts/contextMenu.js | 2 +- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/scripts/album.js b/src/scripts/album.js index e8e43ad..3a55193 100644 --- a/src/scripts/album.js +++ b/src/scripts/album.js @@ -3,6 +3,26 @@ * @copyright 2015 by Tobias Reich */ +const buildAlbumOptions = function(albums, select, parent = 0, layer = 0) { + + var cmbxOptions = '' + + for (i in albums) { + if (albums[i].parent==parent) { + + let title = (layer>0 ? '  '.repeat(layer - 1) + '└ ' : '') + albums[i].title + let selected = select==albums[i].id ? ' selected="selected"' : '' + + cmbxOptions += `${ title }` + cmbxOptions += buildAlbumOptions(albums, select, albums[i].id, layer + 1) + + } + } + + return cmbxOptions + +} + album = { json: null, @@ -148,21 +168,6 @@ album.parse = function() { } -function buildAlbumOptions(albums, select, parent = 0, layer = 0) { - var cmbxOptions = '' - for (i in albums) { - if (albums[i].parent == parent) { - let title = (layer > 0 ? "  ".repeat(layer - 1) + "└ " : "") + albums[i].title - cmbxOptions += `` - cmbxOptions += buildAlbumOptions(albums, select, albums[i].id, layer + 1) - } - } - return cmbxOptions -} - album.add = function(albumID = 0) { const action = function(data) { diff --git a/src/scripts/contextMenu.js b/src/scripts/contextMenu.js index 59f82ba..9a16b4e 100644 --- a/src/scripts/contextMenu.js +++ b/src/scripts/contextMenu.js @@ -193,7 +193,7 @@ contextMenu.mergeAlbum = function(albumID, e) { // It's not possible to move them into us let exclude = [ albumID ] let a = getAlbumFrom(data.albums, selalbum.parent) - while (a != null) { + while (a!=null) { exclude.push(a.id) a = getAlbumFrom(data.albums, a.parent) } From 9b5328d888e02968815e091a3b113b0209fe939e Mon Sep 17 00:00:00 2001 From: Nils Asmussen Date: Wed, 3 Aug 2016 22:58:17 +0200 Subject: [PATCH 2/8] Don't validate merge request on server. Since we don't show the user invalid merge requests to send to the server, the user would have to handcraft such requests. Only admins can merge albums, which we trust anyway. So, let's remove this check. --- php/Modules/Album.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/php/Modules/Album.php b/php/Modules/Album.php index 06b40ab..0c91348 100644 --- a/php/Modules/Album.php +++ b/php/Modules/Album.php @@ -637,13 +637,6 @@ final class Album { $albumID = array_splice($albumIDs, 0, 1); $albumID = $albumID[0]; - // Ensure that we don't merge an album into its own subalbum - foreach($albumIDs as $id) { - foreach($this->getSubAlbums($id) as $sid) { - if($sid == $albumID) return false; - } - } - // Move photos $query = Database::prepare(Database::get(), "UPDATE ? SET album = ? WHERE album IN (?)", array(LYCHEE_TABLE_PHOTOS, $albumID, $this->albumIDs)); $result = Database::execute(Database::get(), $query, __METHOD__, __LINE__); From b91759e6c16089b1d8340d4c91b8b25f4d675706 Mon Sep 17 00:00:00 2001 From: Nils Asmussen Date: Wed, 3 Aug 2016 23:04:47 +0200 Subject: [PATCH 3/8] Added a move album operation. Since albums can have subalbums now, it makes sense to not only merge albums, but also move them. --- php/Access/Admin.php | 9 +++++++ php/Modules/Album.php | 34 ++++++++++++++++++++++++ src/scripts/album.js | 54 +++++++++++++++++++++++++++++++++++--- src/scripts/contextMenu.js | 52 ++++++++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 4 deletions(-) diff --git a/php/Access/Admin.php b/php/Access/Admin.php index e66dc5f..df622f6 100644 --- a/php/Access/Admin.php +++ b/php/Access/Admin.php @@ -28,6 +28,7 @@ final class Admin extends Access { case 'Album::setPublic': self::setAlbumPublicAction(); break; case 'Album::delete': self::deleteAlbumAction(); break; case 'Album::merge': self::mergeAlbumsAction(); break; + case 'Album::move': self::moveAlbumsAction(); break; // Photo functions case 'Photo::get': self::getPhotoAction(); break; @@ -143,6 +144,14 @@ final class Admin extends Access { } + private static function moveAlbumsAction() { + + Validator::required(isset($_POST['albumIDs']), __METHOD__); + $album = new Album($_POST['albumIDs']); + Response::json($album->move()); + + } + // Photo functions private static function getPhotoAction() { diff --git a/php/Modules/Album.php b/php/Modules/Album.php index 0c91348..95ebee5 100644 --- a/php/Modules/Album.php +++ b/php/Modules/Album.php @@ -665,6 +665,40 @@ final class Album { } + /** + * @return boolean Returns true when successful. + */ + public function move() { + + // Check dependencies + Validator::required(isset($this->albumIDs), __METHOD__); + + // Call plugins + Plugins::get()->activate(__METHOD__, 0, func_get_args()); + + // Convert to array + $albumIDs = explode(',', $this->albumIDs); + + // Get first albumID + $albumID = array_splice($albumIDs, 0, 1); + $albumID = $albumID[0]; + + // $albumIDs contains all IDs without the first albumID + // Convert to string + $filteredIDs = implode(',', $albumIDs); + + // Move albums + $query = Database::prepare(Database::get(), "UPDATE ? SET parent = ? WHERE id IN (?)", array(LYCHEE_TABLE_ALBUMS, $albumID, $filteredIDs)); + $result = Database::execute(Database::get(), $query, __METHOD__, __LINE__); + + // Call plugins + Plugins::get()->activate(__METHOD__, 1, func_get_args()); + + if ($result===false) return false; + return true; + + } + /** * @return boolean Returns true when successful. */ diff --git a/src/scripts/album.js b/src/scripts/album.js index 3a55193..346a19b 100644 --- a/src/scripts/album.js +++ b/src/scripts/album.js @@ -635,7 +635,7 @@ album.getArchive = function(albumID) { } -album.merge = function(albumIDs, titles = []) { +const getMessage = function(albumIDs, titles, operation) { let title = '' let sTitle = '' @@ -660,14 +660,20 @@ album.merge = function(albumIDs, titles = []) { // Fallback for second album without a title if (sTitle==='') sTitle = 'Untitled' - msg = lychee.html`

Are you sure you want to merge the album '$${ sTitle }' into the album '$${ title }'?

` + msg = lychee.html`

Are you sure you want to ${ operation } the album '$${ sTitle }' into the album '$${ title }'?

` } else { - msg = lychee.html`

Are you sure you want to merge all selected albums into the album '$${ title }'?

` + msg = lychee.html`

Are you sure you want to ${ operation } all selected albums into the album '$${ title }'?

` } + return msg + +} + +album.merge = function(albumIDs, titles = []) { + const action = function() { basicModal.close() @@ -690,7 +696,7 @@ album.merge = function(albumIDs, titles = []) { } basicModal.show({ - body: msg, + body: getMessage(albumIDs, titles, 'merge'), buttons: { action: { title: 'Merge Albums', @@ -704,4 +710,44 @@ album.merge = function(albumIDs, titles = []) { } }) +} + +album.move = function(albumIDs, titles = []) { + + const action = function() { + + basicModal.close() + + let params = { + albumIDs: albumIDs.join() + } + + api.post('Album::move', params, function(data) { + + if (data!==true) { + lychee.error(null, params, data) + } else { + albums.refresh() + lychee.goto() + } + + }) + + } + + basicModal.show({ + body: getMessage(albumIDs, titles, 'move'), + buttons: { + action: { + title: 'Move Albums', + fn: action, + class: 'red' + }, + cancel: { + title: "Don't Move", + fn: basicModal.close + } + } + }) + } \ No newline at end of file diff --git a/src/scripts/contextMenu.js b/src/scripts/contextMenu.js index 9a16b4e..fec875f 100644 --- a/src/scripts/contextMenu.js +++ b/src/scripts/contextMenu.js @@ -51,6 +51,24 @@ const getAlbumFrom = function(albums, id) { } +const getSubIDs = function(albums, albumID) { + + let ids = [ albumID ] + + for (a in albums) { + if (albums[a].parent==albumID) { + + let sub = getSubIDs(albums, albums[a].id) + for (id in sub) + ids.push(sub[id]) + + } + } + + return ids + +} + const countSubAlbums = function(photoIDs) { let count = 0 @@ -123,6 +141,7 @@ contextMenu.album = function(albumID, e) { let items = [ { title: build.iconic('pencil') + 'Rename', fn: () => album.setTitle([ albumID ]) }, { title: build.iconic('collapse-left') + 'Merge', visible: showMerge, fn: () => { basicContext.close(); contextMenu.mergeAlbum(albumID, e) } }, + { title: build.iconic('folder') + 'Move', visible: showMerge, fn: () => { basicContext.close(); contextMenu.moveAlbum([ albumID ], e) } }, { title: build.iconic('trash') + 'Delete', fn: () => album.delete([ albumID ]) } ] @@ -147,6 +166,7 @@ contextMenu.albumMulti = function(albumIDs, e) { { title: build.iconic('pencil') + 'Rename All', fn: () => album.setTitle(albumIDs) }, { title: build.iconic('collapse-left') + 'Merge All', visible: showMerge && autoMerge, fn: () => album.merge(albumIDs) }, { title: build.iconic('collapse-left') + 'Merge', visible: showMerge && !autoMerge, fn: () => { basicContext.close(); contextMenu.mergeAlbum(albumIDs[0], e) } }, + { title: build.iconic('folder') + 'Move All', visible: showMerge, fn: () => { basicContext.close(); contextMenu.moveAlbum(albumIDs, e) } }, { title: build.iconic('trash') + 'Delete All', fn: () => album.delete(albumIDs) } ] @@ -210,6 +230,38 @@ contextMenu.mergeAlbum = function(albumID, e) { } +contextMenu.moveAlbum = function(albumIDs, e) { + + api.post('Albums::get', { parent: -1 }, function(data) { + + let items = [] + + if (data.albums && data.num>1) { + + let title = albums.getByID(albumIDs[0]).title + // Disable all childs + // It's not possible to move us into them + let exclude = [] + for (i in albumIDs) { + let sub = getSubIDs(data.albums, String(albumIDs[i])) + for (s in sub) + exclude.push(sub[s]) + } + + items = buildAlbumList(data.albums, exclude, (a) => album.move([ a.id ].concat(albumIDs), [ a.title, title ])) + + items.unshift({ title: 'Root', fn: () => album.move([ 0 ].concat(albumIDs), [ 'Root', title ]) }) + + } + + if (items.length===0) return false + + basicContext.show(items, e.originalEvent, contextMenu.close) + + }) + +} + contextMenu.photo = function(photoID, e) { // Notice for 'Move': From 20c194f00c38747db8030c0d19fea2302e69c75b Mon Sep 17 00:00:00 2001 From: Nils Asmussen Date: Thu, 4 Aug 2016 07:35:49 +0200 Subject: [PATCH 4/8] Always show merge&move operations in context menu. Previously, we calculated whether there are at least two albums so that we can actually perform the operation. Due to subalbums this simple check is not sufficient anymore. Since the rules are more complicated and we don't have all albums loaded at that point to calculate it properly, this commit removes the check entirely and always displays the operations. The only disadvantage is that if an installation really has just one album (should rarely happen), the user still sees the merge operation (not merge all), which results in an album-list with one disabled album. I think this is okay. --- src/scripts/contextMenu.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/scripts/contextMenu.js b/src/scripts/contextMenu.js index fec875f..5f0ff92 100644 --- a/src/scripts/contextMenu.js +++ b/src/scripts/contextMenu.js @@ -135,13 +135,10 @@ contextMenu.album = function(albumID, e) { if (album.isSmartID(albumID)) return false - // Show merge-item when there's more than one album - let showMerge = (albums.json && albums.json.albums && Object.keys(albums.json.albums).length>1) - let items = [ { title: build.iconic('pencil') + 'Rename', fn: () => album.setTitle([ albumID ]) }, - { title: build.iconic('collapse-left') + 'Merge', visible: showMerge, fn: () => { basicContext.close(); contextMenu.mergeAlbum(albumID, e) } }, - { title: build.iconic('folder') + 'Move', visible: showMerge, fn: () => { basicContext.close(); contextMenu.moveAlbum([ albumID ], e) } }, + { title: build.iconic('collapse-left') + 'Merge', fn: () => { basicContext.close(); contextMenu.mergeAlbum(albumID, e) } }, + { title: build.iconic('folder') + 'Move', fn: () => { basicContext.close(); contextMenu.moveAlbum([ albumID ], e) } }, { title: build.iconic('trash') + 'Delete', fn: () => album.delete([ albumID ]) } ] @@ -159,14 +156,11 @@ contextMenu.albumMulti = function(albumIDs, e) { // Show list of albums otherwise let autoMerge = (albumIDs.length>1 ? true : false) - // Show merge-item when there's more than one album - let showMerge = (albums.json && albums.json.albums && Object.keys(albums.json.albums).length>1) - let items = [ { title: build.iconic('pencil') + 'Rename All', fn: () => album.setTitle(albumIDs) }, - { title: build.iconic('collapse-left') + 'Merge All', visible: showMerge && autoMerge, fn: () => album.merge(albumIDs) }, - { title: build.iconic('collapse-left') + 'Merge', visible: showMerge && !autoMerge, fn: () => { basicContext.close(); contextMenu.mergeAlbum(albumIDs[0], e) } }, - { title: build.iconic('folder') + 'Move All', visible: showMerge, fn: () => { basicContext.close(); contextMenu.moveAlbum(albumIDs, e) } }, + { title: build.iconic('collapse-left') + 'Merge All', visible: autoMerge, fn: () => album.merge(albumIDs) }, + { title: build.iconic('collapse-left') + 'Merge', visible: !autoMerge, fn: () => { basicContext.close(); contextMenu.mergeAlbum(albumIDs[0], e) } }, + { title: build.iconic('folder') + 'Move All', fn: () => { basicContext.close(); contextMenu.moveAlbum(albumIDs, e) } }, { title: build.iconic('trash') + 'Delete All', fn: () => album.delete(albumIDs) } ] From 58d3ad727d046e30573d3101d49e08d7e9d3fca5 Mon Sep 17 00:00:00 2001 From: Nils Asmussen Date: Sun, 7 Aug 2016 14:47:45 +0200 Subject: [PATCH 5/8] Hide download button only w/o photos and subalbums. Previously, an album with subalbums, but without photos, was hiding the download button. --- src/scripts/header.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scripts/header.js b/src/scripts/header.js index 93d2eba..6468d30 100644 --- a/src/scripts/header.js +++ b/src/scripts/header.js @@ -141,8 +141,8 @@ header.setMode = function(mode) { header.dom('.header__toolbar--album').addClass('header__toolbar--visible') // Hide download button when album empty - if (album.json.content===false) $('#button_archive').hide() - else $('#button_archive').show() + if (album.json.content===false && album.subjson.num==0) $('#button_archive').hide() + else $('#button_archive').show() // Hide download button when not logged in and album not downloadable if (lychee.publicMode===true && album.json.downloadable==='0') $('#button_archive').hide() From 7126354d7abd0a7717f7958676bb5a110919cec7 Mon Sep 17 00:00:00 2001 From: Nils Asmussen Date: Sat, 13 Aug 2016 16:01:41 +0200 Subject: [PATCH 6/8] Hide "Photos" divider if photos are deleted. If we have deleted all photos from an album, we need to remove the divider between albums and photos. --- src/scripts/build.js | 4 ++-- src/scripts/view.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/scripts/build.js b/src/scripts/build.js index 513f39b..bc8cff0 100644 --- a/src/scripts/build.js +++ b/src/scripts/build.js @@ -15,11 +15,11 @@ build.iconic = function(icon, classes = '') { } -build.divider = function(title) { +build.divider = function(title, id = '') { let html = '' - html += lychee.html`

$${ title }

` + html += lychee.html`

$${ title }

` return html diff --git a/src/scripts/view.js b/src/scripts/view.js index ad1c3b6..d8d8e67 100644 --- a/src/scripts/view.js +++ b/src/scripts/view.js @@ -158,7 +158,7 @@ view.album = { if (album.json.content && album.json.content!==false) { - photosData += build.divider('Photos') + photosData += build.divider('Photos', 'divider-photos') // Build photos $.each(album.json.content, function() { @@ -236,6 +236,7 @@ view.album = { if (!visible.albums()) { album.json.num-- view.album.num() + if (album.json.num == 0) $('#divider-photos').hide() } }) From 5ee34c50a46f1b24a55e3badc292ae5c23e2f919 Mon Sep 17 00:00:00 2001 From: Nils Asmussen Date: Sat, 13 Aug 2016 16:03:37 +0200 Subject: [PATCH 7/8] Properly redirect after album {delete,move,merge}. That is, we don't redirect to the albums view, but to the current album or the parent album if the current one has been deleted. --- src/scripts/album.js | 50 +++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/src/scripts/album.js b/src/scripts/album.js index 346a19b..31902c0 100644 --- a/src/scripts/album.js +++ b/src/scripts/album.js @@ -256,6 +256,21 @@ album.delete = function(albumIDs) { albums.deleteByID(id) }) + } else if (visible.album()) { + + // if we deleted the current album, go to its parent + if (albumIDs.length==1 && album.json.id==albumIDs[0]) { + + let id = album.getParent() + album.refresh() + lychee.goto(id) + + } + // otherwise, we deleted a subalbum + else { + album.reload() + } + } else { albums.refresh() @@ -684,12 +699,8 @@ album.merge = function(albumIDs, titles = []) { api.post('Album::merge', params, function(data) { - if (data!==true) { - lychee.error(null, params, data) - } else { - albums.refresh() - lychee.goto() - } + if (data!==true) lychee.error(null, params, data) + else album.reload() }) @@ -724,12 +735,8 @@ album.move = function(albumIDs, titles = []) { api.post('Album::move', params, function(data) { - if (data!==true) { - lychee.error(null, params, data) - } else { - albums.refresh() - lychee.goto() - } + if (data!==true) lychee.error(null, params, data) + else album.reload() }) @@ -750,4 +757,23 @@ album.move = function(albumIDs, titles = []) { } }) +} + +album.reload = function() { + + let albumID = album.getID() + + album.refresh() + albums.refresh() + + if (visible.album()) lychee.goto(albumID) + else lychee.goto() + +} + +album.refresh = function() { + + album.json = null + album.subjson = null + } \ No newline at end of file From 3040ab1d6243bcf57c875fbdb6e74de2a61210cd Mon Sep 17 00:00:00 2001 From: Nils Asmussen Date: Sat, 13 Aug 2016 16:50:05 +0200 Subject: [PATCH 8/8] Improved display of root album in move operation. Root is now shown as the parent of all albums with parent=0. --- src/scripts/album.js | 4 ++-- src/scripts/contextMenu.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/scripts/album.js b/src/scripts/album.js index 31902c0..6fbe3d8 100644 --- a/src/scripts/album.js +++ b/src/scripts/album.js @@ -675,11 +675,11 @@ const getMessage = function(albumIDs, titles, operation) { // Fallback for second album without a title if (sTitle==='') sTitle = 'Untitled' - msg = lychee.html`

Are you sure you want to ${ operation } the album '$${ sTitle }' into the album '$${ title }'?

` + msg = lychee.html`

Are you sure you want to ${ operation } the album '$${ sTitle }' into '$${ title }'?

` } else { - msg = lychee.html`

Are you sure you want to ${ operation } all selected albums into the album '$${ title }'?

` + msg = lychee.html`

Are you sure you want to ${ operation } all selected albums into '$${ title }'?

` } diff --git a/src/scripts/contextMenu.js b/src/scripts/contextMenu.js index 5f0ff92..ee8bd5b 100644 --- a/src/scripts/contextMenu.js +++ b/src/scripts/contextMenu.js @@ -242,7 +242,7 @@ contextMenu.moveAlbum = function(albumIDs, e) { exclude.push(sub[s]) } - items = buildAlbumList(data.albums, exclude, (a) => album.move([ a.id ].concat(albumIDs), [ a.title, title ])) + items = buildAlbumList(data.albums, exclude, (a) => album.move([ a.id ].concat(albumIDs), [ a.title, title ]), 0, 1) items.unshift({ title: 'Root', fn: () => album.move([ 0 ].concat(albumIDs), [ 'Root', title ]) })