Compare commits
58 Commits
master
...
feature/su
Author | SHA1 | Date | |
---|---|---|---|
![]() |
727c74c294 | ||
![]() |
635b31eb0b | ||
![]() |
a0abb1a460 | ||
![]() |
f460569bdc | ||
![]() |
a2def81b52 | ||
![]() |
ab3a5a4d63 | ||
![]() |
3f7f2ffcd1 | ||
![]() |
a765b5ee06 | ||
![]() |
425749eb85 | ||
![]() |
7002e039b3 | ||
![]() |
0cb50e5090 | ||
![]() |
1fc3ca05cc | ||
![]() |
bdd2aca6f2 | ||
![]() |
d2c7cab3e3 | ||
![]() |
ff5637a7da | ||
![]() |
4b08f7e6f0 | ||
![]() |
1bf8f8d2f8 | ||
![]() |
774fda3a7c | ||
![]() |
e64e8ae621 | ||
![]() |
9d5e9a4137 | ||
![]() |
5c18fbeed8 | ||
![]() |
4c58ae0733 | ||
![]() |
993738af49 | ||
![]() |
7ba384e70a | ||
![]() |
9548f43b06 | ||
![]() |
5bbac75481 | ||
![]() |
7ebe2a1aa0 | ||
![]() |
ea073b4324 | ||
![]() |
55ae8a4be2 | ||
![]() |
0746acda79 | ||
![]() |
3040ab1d62 | ||
![]() |
5ee34c50a4 | ||
![]() |
7126354d7a | ||
![]() |
58d3ad727d | ||
![]() |
20c194f00c | ||
![]() |
b91759e6c1 | ||
![]() |
9b5328d888 | ||
![]() |
adaa23d39f | ||
![]() |
96bd6b240b | ||
![]() |
61a62421f4 | ||
![]() |
066fc90056 | ||
![]() |
2b698ca024 | ||
![]() |
2da195f925 | ||
![]() |
0c7d95fb15 | ||
![]() |
1020b0d1dc | ||
![]() |
8b37d122b9 | ||
![]() |
f7e31c2be7 | ||
![]() |
ffef7e3d8f | ||
![]() |
daf7d37c84 | ||
![]() |
cdd130b243 | ||
![]() |
d121e2c4f6 | ||
![]() |
55247e4f56 | ||
![]() |
6feae99bf1 | ||
![]() |
1b6c74bcb6 | ||
![]() |
eddb666b5a | ||
![]() |
212b241d0e | ||
![]() |
ef9040870a | ||
![]() |
6d4df5f6b7 |
BIN
dist/main.css
vendored
BIN
dist/main.css
vendored
Binary file not shown.
BIN
dist/main.js
vendored
BIN
dist/main.js
vendored
Binary file not shown.
BIN
dist/view.js
vendored
Executable file → Normal file
BIN
dist/view.js
vendored
Executable file → Normal file
Binary file not shown.
@ -14,8 +14,8 @@ The following keys and shortcuts can be used in Lychee.
|
|||||||
| Key | Action |
|
| Key | Action |
|
||||||
|:-----------|:------------|
|
|:-----------|:------------|
|
||||||
| `s` or `f` | Search |
|
| `s` or `f` | Search |
|
||||||
| `cmd`+`a` | Select all albums |
|
| `meta-key`+`left-click` | Select album individually |
|
||||||
| `ctrl`+`a` | Select all albums |
|
| `right-click` | Open context-menu |
|
||||||
|
|
||||||
### Album
|
### Album
|
||||||
| Key | Action |
|
| Key | Action |
|
||||||
@ -25,8 +25,8 @@ The following keys and shortcuts can be used in Lychee.
|
|||||||
| `i` | Show information |
|
| `i` | Show information |
|
||||||
| `cmd`+`backspace` | Delete album |
|
| `cmd`+`backspace` | Delete album |
|
||||||
| `ctrl`+`backspace` | Delete album |
|
| `ctrl`+`backspace` | Delete album |
|
||||||
| `cmd`+`a` | Select all photos |
|
| `meta-key`+`left-click` | Select album/photo individually |
|
||||||
| `ctrl`+`a` | Select all photos |
|
| `right-click` | Open context-menu |
|
||||||
|
|
||||||
### Photo
|
### Photo
|
||||||
| Key | Action |
|
| Key | Action |
|
||||||
|
@ -28,6 +28,7 @@ final class Admin extends Access {
|
|||||||
case 'Album::setPublic': self::setAlbumPublicAction(); break;
|
case 'Album::setPublic': self::setAlbumPublicAction(); break;
|
||||||
case 'Album::delete': self::deleteAlbumAction(); break;
|
case 'Album::delete': self::deleteAlbumAction(); break;
|
||||||
case 'Album::merge': self::mergeAlbumsAction(); break;
|
case 'Album::merge': self::mergeAlbumsAction(); break;
|
||||||
|
case 'Album::move': self::moveAlbumsAction(); break;
|
||||||
|
|
||||||
// Photo functions
|
// Photo functions
|
||||||
case 'Photo::get': self::getPhotoAction(); break;
|
case 'Photo::get': self::getPhotoAction(); break;
|
||||||
@ -72,8 +73,10 @@ final class Admin extends Access {
|
|||||||
|
|
||||||
private static function getAlbumsAction() {
|
private static function getAlbumsAction() {
|
||||||
|
|
||||||
|
Validator::required(isset($_POST['parent']), __METHOD__);
|
||||||
|
|
||||||
$albums = new Albums();
|
$albums = new Albums();
|
||||||
Response::json($albums->get(false));
|
Response::json($albums->get(false, $_POST['parent']));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,10 +93,10 @@ final class Admin extends Access {
|
|||||||
|
|
||||||
private static function addAlbumAction() {
|
private static function addAlbumAction() {
|
||||||
|
|
||||||
Validator::required(isset($_POST['title']), __METHOD__);
|
Validator::required(isset($_POST['title'], $_POST['parent']), __METHOD__);
|
||||||
|
|
||||||
$album = new Album(null);
|
$album = new Album(null);
|
||||||
Response::json($album->add($_POST['title']), JSON_NUMERIC_CHECK);
|
Response::json($album->add($_POST['title'], $_POST['parent']), JSON_NUMERIC_CHECK);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,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
|
// Photo functions
|
||||||
|
|
||||||
private static function getPhotoAction() {
|
private static function getPhotoAction() {
|
||||||
|
@ -44,8 +44,10 @@ final class Guest extends Access {
|
|||||||
|
|
||||||
private static function getAlbumsAction() {
|
private static function getAlbumsAction() {
|
||||||
|
|
||||||
|
Validator::required(isset($_POST['parent']), __METHOD__);
|
||||||
|
|
||||||
$albums = new Albums();
|
$albums = new Albums();
|
||||||
Response::json($albums->get(true));
|
Response::json($albums->get(true, $_POST['parent']));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ final class Album {
|
|||||||
/**
|
/**
|
||||||
* @return string|false ID of the created album.
|
* @return string|false ID of the created album.
|
||||||
*/
|
*/
|
||||||
public function add($title = 'Untitled') {
|
public function add($title = 'Untitled', $parent = 0) {
|
||||||
|
|
||||||
// Call plugins
|
// Call plugins
|
||||||
Plugins::get()->activate(__METHOD__, 0, func_get_args());
|
Plugins::get()->activate(__METHOD__, 0, func_get_args());
|
||||||
@ -35,7 +35,7 @@ final class Album {
|
|||||||
$visible = 1;
|
$visible = 1;
|
||||||
|
|
||||||
// Database
|
// Database
|
||||||
$query = Database::prepare(Database::get(), "INSERT INTO ? (id, title, sysstamp, public, visible) VALUES ('?', '?', '?', '?', '?')", array(LYCHEE_TABLE_ALBUMS, $id, $title, $sysstamp, $public, $visible));
|
$query = Database::prepare(Database::get(), "INSERT INTO ? (id, title, sysstamp, public, visible, parent) VALUES ('?', '?', '?', '?', '?', '?')", array(LYCHEE_TABLE_ALBUMS, $id, $title, $sysstamp, $public, $visible, $parent));
|
||||||
$result = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
$result = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
||||||
|
|
||||||
// Call plugins
|
// Call plugins
|
||||||
@ -69,6 +69,7 @@ final class Album {
|
|||||||
if (isset($data['description'])) $album['description'] = $data['description'];
|
if (isset($data['description'])) $album['description'] = $data['description'];
|
||||||
if (isset($data['visible'])) $album['visible'] = $data['visible'];
|
if (isset($data['visible'])) $album['visible'] = $data['visible'];
|
||||||
if (isset($data['downloadable'])) $album['downloadable'] = $data['downloadable'];
|
if (isset($data['downloadable'])) $album['downloadable'] = $data['downloadable'];
|
||||||
|
if (isset($data['parent'])) $album['parent'] = $data['parent'];
|
||||||
|
|
||||||
// Parse date
|
// Parse date
|
||||||
$album['sysdate'] = strftime('%B %Y', $data['sysstamp']);
|
$album['sysdate'] = strftime('%B %Y', $data['sysstamp']);
|
||||||
@ -79,6 +80,7 @@ final class Album {
|
|||||||
// Parse thumbs or set default value
|
// Parse thumbs or set default value
|
||||||
$album['thumbs'] = (isset($data['thumbs']) ? explode(',', $data['thumbs']) : array());
|
$album['thumbs'] = (isset($data['thumbs']) ? explode(',', $data['thumbs']) : array());
|
||||||
|
|
||||||
|
|
||||||
return $album;
|
return $album;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -193,12 +195,6 @@ final class Album {
|
|||||||
// Call plugins
|
// Call plugins
|
||||||
Plugins::get()->activate(__METHOD__, 0, func_get_args());
|
Plugins::get()->activate(__METHOD__, 0, func_get_args());
|
||||||
|
|
||||||
// Illicit chars
|
|
||||||
$badChars = array_merge(
|
|
||||||
array_map('chr', range(0,31)),
|
|
||||||
array("<", ">", ":", '"', "/", "\\", "|", "?", "*")
|
|
||||||
);
|
|
||||||
|
|
||||||
// Photos query
|
// Photos query
|
||||||
switch($this->albumIDs) {
|
switch($this->albumIDs) {
|
||||||
case 's':
|
case 's':
|
||||||
@ -214,12 +210,10 @@ final class Album {
|
|||||||
$zipTitle = 'Recent';
|
$zipTitle = 'Recent';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$photos = Database::prepare(Database::get(), "SELECT title, url FROM ? WHERE album = '?'", array(LYCHEE_TABLE_PHOTOS, $this->albumIDs));
|
|
||||||
$zipTitle = 'Unsorted';
|
$zipTitle = 'Unsorted';
|
||||||
}
|
|
||||||
|
|
||||||
// Get title from database when album is not a SmartAlbum
|
// Get title from database when album is not a SmartAlbum
|
||||||
if ($this->albumIDs!=0&&is_numeric($this->albumIDs)) {
|
if ($this->albumIDs!=0 && is_numeric($this->albumIDs)) {
|
||||||
|
|
||||||
$query = Database::prepare(Database::get(), "SELECT title FROM ? WHERE id = '?' LIMIT 1", array(LYCHEE_TABLE_ALBUMS, $this->albumIDs));
|
$query = Database::prepare(Database::get(), "SELECT title FROM ? WHERE id = '?' LIMIT 1", array(LYCHEE_TABLE_ALBUMS, $this->albumIDs));
|
||||||
$album = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
$album = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
||||||
@ -239,9 +233,11 @@ final class Album {
|
|||||||
$zipTitle = $album->title;
|
$zipTitle = $album->title;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Escape title
|
// Escape title
|
||||||
$zipTitle = str_replace($badChars, '', $zipTitle);
|
$zipTitle = $this->cleanZipName($zipTitle);
|
||||||
|
|
||||||
$filename = LYCHEE_DATA . $zipTitle . '.zip';
|
$filename = LYCHEE_DATA . $zipTitle . '.zip';
|
||||||
|
|
||||||
@ -252,56 +248,16 @@ final class Album {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute query
|
// Add photos to zip
|
||||||
$photos = Database::execute(Database::get(), $photos, __METHOD__, __LINE__);
|
switch($this->albumIDs) {
|
||||||
|
case 's':
|
||||||
if ($album===null) return false;
|
case 'f':
|
||||||
|
case 'r':
|
||||||
// Check if album empty
|
$this->addPhotosToZip($zip, $zipTitle, $photos);
|
||||||
if ($photos->num_rows==0) {
|
break;
|
||||||
Log::error(Database::get(), __METHOD__, __LINE__, 'Could not create ZipArchive without images');
|
default:
|
||||||
return false;
|
$this->addAlbumToZip($zip, $zipTitle, $this->albumIDs);
|
||||||
}
|
break;
|
||||||
|
|
||||||
// Parse each path
|
|
||||||
$files = array();
|
|
||||||
while ($photo = $photos->fetch_object()) {
|
|
||||||
|
|
||||||
// Parse url
|
|
||||||
$photo->url = LYCHEE_UPLOADS_BIG . $photo->url;
|
|
||||||
|
|
||||||
// Parse title
|
|
||||||
$photo->title = str_replace($badChars, '', $photo->title);
|
|
||||||
if (!isset($photo->title)||$photo->title==='') $photo->title = 'Untitled';
|
|
||||||
|
|
||||||
// Check if readable
|
|
||||||
if (!@is_readable($photo->url)) continue;
|
|
||||||
|
|
||||||
// Get extension of image
|
|
||||||
$extension = getExtension($photo->url, false);
|
|
||||||
|
|
||||||
// Set title for photo
|
|
||||||
$zipFileName = $zipTitle . '/' . $photo->title . $extension;
|
|
||||||
|
|
||||||
// Check for duplicates
|
|
||||||
if (!empty($files)) {
|
|
||||||
$i = 1;
|
|
||||||
while (in_array($zipFileName, $files)) {
|
|
||||||
|
|
||||||
// Set new title for photo
|
|
||||||
$zipFileName = $zipTitle . '/' . $photo->title . '-' . $i . $extension;
|
|
||||||
|
|
||||||
$i++;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add to array
|
|
||||||
$files[] = $zipFileName;
|
|
||||||
|
|
||||||
// Add photo to zip
|
|
||||||
$zip->addFile($photo->url, $zipFileName);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finish zip
|
// Finish zip
|
||||||
@ -323,6 +279,84 @@ final class Album {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function cleanZipName($name) {
|
||||||
|
|
||||||
|
// Illicit chars
|
||||||
|
$badChars = array_merge(
|
||||||
|
array_map('chr', range(0,31)),
|
||||||
|
array("<", ">", ":", '"', "/", "\\", "|", "?", "*")
|
||||||
|
);
|
||||||
|
|
||||||
|
return str_replace($badChars, '', $name);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addAlbumToZip($zip, $path, $albumID) {
|
||||||
|
|
||||||
|
// Fetch album title
|
||||||
|
$photos = Database::prepare(Database::get(), "SELECT title, url FROM ? WHERE album = '?'", array(LYCHEE_TABLE_PHOTOS, $albumID));
|
||||||
|
|
||||||
|
$this->addPhotosToZip($zip, $path, $photos);
|
||||||
|
|
||||||
|
// Fetch subalbums
|
||||||
|
$query = Database::prepare(Database::get(), "SELECT id, title FROM ? WHERE parent = '?'", array(LYCHEE_TABLE_ALBUMS, $albumID));
|
||||||
|
$albums = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
||||||
|
|
||||||
|
// Add them recursively
|
||||||
|
while($album = $albums->fetch_assoc()) {
|
||||||
|
$this->addAlbumToZip($zip, $path . '/' . $this->cleanZipName($album['title']), $album['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addPhotosToZip($zip, $path, $query) {
|
||||||
|
|
||||||
|
// Execute query
|
||||||
|
$photos = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
||||||
|
|
||||||
|
// Parse each path
|
||||||
|
$files = array();
|
||||||
|
while ($photo = $photos->fetch_object()) {
|
||||||
|
|
||||||
|
// Parse url
|
||||||
|
$photo->url = LYCHEE_UPLOADS_BIG . $photo->url;
|
||||||
|
|
||||||
|
// Parse title
|
||||||
|
$photo->title = $this->cleanZipName($photo->title);
|
||||||
|
if (!isset($photo->title)||$photo->title==='') $photo->title = 'Untitled';
|
||||||
|
|
||||||
|
// Check if readable
|
||||||
|
if (!@is_readable($photo->url)) continue;
|
||||||
|
|
||||||
|
// Get extension of image
|
||||||
|
$extension = getExtension($photo->url, false);
|
||||||
|
|
||||||
|
// Set title for photo
|
||||||
|
$zipFileName = $path . '/' . $photo->title . $extension;
|
||||||
|
|
||||||
|
// Check for duplicates
|
||||||
|
if (!empty($files)) {
|
||||||
|
$i = 1;
|
||||||
|
while (in_array($zipFileName, $files)) {
|
||||||
|
|
||||||
|
// Set new title for photo
|
||||||
|
$zipFileName = $path . '/' . $photo->title . '-' . $i . $extension;
|
||||||
|
|
||||||
|
$i++;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to array
|
||||||
|
$files[] = $zipFileName;
|
||||||
|
|
||||||
|
// Add photo to zip
|
||||||
|
$zip->addFile($photo->url, $zipFileName);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return boolean Returns true when successful.
|
* @return boolean Returns true when successful.
|
||||||
*/
|
*/
|
||||||
@ -441,6 +475,31 @@ final class Album {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getSubAlbums($albumID) {
|
||||||
|
|
||||||
|
$query = Database::prepare(Database::get(), "SELECT id FROM ? WHERE parent = '?'", array(LYCHEE_TABLE_ALBUMS, $albumID));
|
||||||
|
$albums = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
||||||
|
|
||||||
|
$ids = array();
|
||||||
|
while($album = $albums->fetch_assoc()) {
|
||||||
|
$ids = array_merge($ids, array($album['id']), $this->getSubAlbums($album['id']));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ids;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addSubAlbumIDs($ids) {
|
||||||
|
|
||||||
|
$res = array();
|
||||||
|
|
||||||
|
foreach(explode(',', $ids) as $id)
|
||||||
|
$res = array_merge($res, array($id), $this->getSubAlbums($id));
|
||||||
|
|
||||||
|
return implode(',', $res);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return boolean Returns true when successful.
|
* @return boolean Returns true when successful.
|
||||||
*/
|
*/
|
||||||
@ -457,8 +516,11 @@ final class Album {
|
|||||||
$visible = ($visible==='1' ? 1 : 0);
|
$visible = ($visible==='1' ? 1 : 0);
|
||||||
$downloadable = ($downloadable==='1' ? 1 : 0);
|
$downloadable = ($downloadable==='1' ? 1 : 0);
|
||||||
|
|
||||||
|
// Get all album ids, including subalbums
|
||||||
|
$ids = $this->addSubAlbumIDs($this->albumIDs);
|
||||||
|
|
||||||
// Set public
|
// Set public
|
||||||
$query = Database::prepare(Database::get(), "UPDATE ? SET public = '?', visible = '?', downloadable = '?', password = NULL WHERE id IN (?)", array(LYCHEE_TABLE_ALBUMS, $public, $visible, $downloadable, $this->albumIDs));
|
$query = Database::prepare(Database::get(), "UPDATE ? SET public = '?', visible = '?', downloadable = '?', password = NULL WHERE id IN (?)", array(LYCHEE_TABLE_ALBUMS, $public, $visible, $downloadable, $ids));
|
||||||
$result = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
$result = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
||||||
|
|
||||||
if ($result===false) return false;
|
if ($result===false) return false;
|
||||||
@ -466,7 +528,7 @@ final class Album {
|
|||||||
// Reset permissions for photos
|
// Reset permissions for photos
|
||||||
if ($public===1) {
|
if ($public===1) {
|
||||||
|
|
||||||
$query = Database::prepare(Database::get(), "UPDATE ? SET public = 0 WHERE album IN (?)", array(LYCHEE_TABLE_PHOTOS, $this->albumIDs));
|
$query = Database::prepare(Database::get(), "UPDATE ? SET public = 0 WHERE album IN (?)", array(LYCHEE_TABLE_PHOTOS, $ids));
|
||||||
$result = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
$result = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
||||||
|
|
||||||
if ($result===false) return false;
|
if ($result===false) return false;
|
||||||
@ -575,6 +637,7 @@ final class Album {
|
|||||||
$albumID = array_splice($albumIDs, 0, 1);
|
$albumID = array_splice($albumIDs, 0, 1);
|
||||||
$albumID = $albumID[0];
|
$albumID = $albumID[0];
|
||||||
|
|
||||||
|
// Move photos
|
||||||
$query = Database::prepare(Database::get(), "UPDATE ? SET album = ? WHERE album IN (?)", array(LYCHEE_TABLE_PHOTOS, $albumID, $this->albumIDs));
|
$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__);
|
$result = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
||||||
|
|
||||||
@ -584,6 +647,13 @@ final class Album {
|
|||||||
// Convert to string
|
// Convert to string
|
||||||
$filteredIDs = implode(',', $albumIDs);
|
$filteredIDs = implode(',', $albumIDs);
|
||||||
|
|
||||||
|
// Move subalbums
|
||||||
|
$query = Database::prepare(Database::get(), "UPDATE ? SET parent = ? WHERE parent IN (?)", array(LYCHEE_TABLE_ALBUMS, $albumID, $filteredIDs));
|
||||||
|
$result = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
||||||
|
|
||||||
|
if ($result===false) return false;
|
||||||
|
|
||||||
|
// Delete other albums
|
||||||
$query = Database::prepare(Database::get(), "DELETE FROM ? WHERE id IN (?)", array(LYCHEE_TABLE_ALBUMS, $filteredIDs));
|
$query = Database::prepare(Database::get(), "DELETE FROM ? WHERE id IN (?)", array(LYCHEE_TABLE_ALBUMS, $filteredIDs));
|
||||||
$result = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
$result = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
||||||
|
|
||||||
@ -595,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.
|
* @return boolean Returns true when successful.
|
||||||
*/
|
*/
|
||||||
@ -606,11 +710,14 @@ final class Album {
|
|||||||
// Call plugins
|
// Call plugins
|
||||||
Plugins::get()->activate(__METHOD__, 0, func_get_args());
|
Plugins::get()->activate(__METHOD__, 0, func_get_args());
|
||||||
|
|
||||||
|
// Get all album ids, including subalbums
|
||||||
|
$ids = $this->addSubAlbumIDs($this->albumIDs);
|
||||||
|
|
||||||
// Init vars
|
// Init vars
|
||||||
$photoIDs = array();
|
$photoIDs = array();
|
||||||
|
|
||||||
// Execute query
|
// Execute query
|
||||||
$query = Database::prepare(Database::get(), "SELECT id FROM ? WHERE album IN (?)", array(LYCHEE_TABLE_PHOTOS, $this->albumIDs));
|
$query = Database::prepare(Database::get(), "SELECT id FROM ? WHERE album IN (?)", array(LYCHEE_TABLE_PHOTOS, $ids));
|
||||||
$photos = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
$photos = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
||||||
|
|
||||||
if ($photos===false) return false;
|
if ($photos===false) return false;
|
||||||
@ -631,7 +738,7 @@ final class Album {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete albums
|
// Delete albums
|
||||||
$query = Database::prepare(Database::get(), "DELETE FROM ? WHERE id IN (?)", array(LYCHEE_TABLE_ALBUMS, $this->albumIDs));
|
$query = Database::prepare(Database::get(), "DELETE FROM ? WHERE id IN (?)", array(LYCHEE_TABLE_ALBUMS, $ids));
|
||||||
$result = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
$result = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
||||||
|
|
||||||
// Call plugins
|
// Call plugins
|
||||||
|
@ -16,7 +16,7 @@ final class Albums {
|
|||||||
/**
|
/**
|
||||||
* @return array|false Returns an array of albums or false on failure.
|
* @return array|false Returns an array of albums or false on failure.
|
||||||
*/
|
*/
|
||||||
public function get($public = true) {
|
public function get($public = true, $parent = 0) {
|
||||||
|
|
||||||
// Call plugins
|
// Call plugins
|
||||||
Plugins::get()->activate(__METHOD__, 0, func_get_args());
|
Plugins::get()->activate(__METHOD__, 0, func_get_args());
|
||||||
@ -32,8 +32,8 @@ final class Albums {
|
|||||||
if ($public===false) $return['smartalbums'] = $this->getSmartAlbums();
|
if ($public===false) $return['smartalbums'] = $this->getSmartAlbums();
|
||||||
|
|
||||||
// Albums query
|
// Albums query
|
||||||
if ($public===false) $query = Database::prepare(Database::get(), 'SELECT id, title, public, sysstamp, password FROM ? ' . Settings::get()['sortingAlbums'], array(LYCHEE_TABLE_ALBUMS));
|
if ($public===false) $query = Database::prepare(Database::get(), "SELECT id, title, public, sysstamp, password, parent FROM ? " . ($parent != -1 ? "WHERE parent = '?' " : "") . Settings::get()['sortingAlbums'], array(LYCHEE_TABLE_ALBUMS, $parent));
|
||||||
else $query = Database::prepare(Database::get(), 'SELECT id, title, public, sysstamp, password FROM ? WHERE public = 1 AND visible <> 0 ' . Settings::get()['sortingAlbums'], array(LYCHEE_TABLE_ALBUMS));
|
else $query = Database::prepare(Database::get(), "SELECT id, title, public, sysstamp, password, parent FROM ? " . ($parent != -1 ? "WHERE parent = '?' " : "") . " AND public = 1 AND visible <> 0 " . Settings::get()['sortingAlbums'], array(LYCHEE_TABLE_ALBUMS, $parent));
|
||||||
|
|
||||||
// Execute query
|
// Execute query
|
||||||
$albums = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
$albums = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
|
||||||
|
@ -15,7 +15,8 @@ final class Database {
|
|||||||
'030001', // 3.0.1
|
'030001', // 3.0.1
|
||||||
'030003', // 3.0.3
|
'030003', // 3.0.3
|
||||||
'030100', // 3.1.0
|
'030100', // 3.1.0
|
||||||
'030102' // 3.1.2
|
'030102', // 3.1.2
|
||||||
|
'030200' // 3.2.0
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
29
php/database/update_030200.php
Normal file
29
php/database/update_030200.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update to version 3.2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Lychee\Modules\Database;
|
||||||
|
use Lychee\Modules\Response;
|
||||||
|
|
||||||
|
// Add parent field to albums
|
||||||
|
$query = Database::prepare($connection, "SELECT `parent` FROM `?` LIMIT 1", array(LYCHEE_TABLE_ALBUMS));
|
||||||
|
$result = Database::execute($connection, $query, "update_030200", __LINE__);
|
||||||
|
|
||||||
|
if ($result===false) {
|
||||||
|
|
||||||
|
$query = Database::prepare($connection, "ALTER TABLE `?` ADD `parent` BIGINT(14) NOT NULL DEFAULT 0", array(LYCHEE_TABLE_ALBUMS));
|
||||||
|
$result = Database::execute($connection, $query, "update_030200", __LINE__);
|
||||||
|
|
||||||
|
if ($result===false) {
|
||||||
|
Log::error($database, 'update_030200', __LINE__, 'Could not update database (' . $database->error . ')');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set version
|
||||||
|
// if (Database::setVersion($connection, '030200')===false) Response::error('Could not update version of database!');
|
||||||
|
|
||||||
|
?>
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Lychee",
|
"name": "Lychee",
|
||||||
"version": "3.1.5",
|
"version": "3.2.0",
|
||||||
"description": "Self-hosted photo-management done right.",
|
"description": "Self-hosted photo-management done right.",
|
||||||
"authors": "Tobias Reich <tobias@electerious.com>",
|
"authors": "Tobias Reich <tobias@electerious.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
@ -2,9 +2,36 @@
|
|||||||
* @description Takes care of every action an album can handle and execute.
|
* @description Takes care of every action an album can handle and execute.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
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 += `<option${ selected } value='${ albums[i].id }'>${ title }</option>`
|
||||||
|
cmbxOptions += buildAlbumOptions(albums, select, albums[i].id, layer + 1)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmbxOptions
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
album = {
|
album = {
|
||||||
|
|
||||||
json: null
|
json: null,
|
||||||
|
subjson: null
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
album.isSmartID = function(id) {
|
||||||
|
|
||||||
|
return (id==='0' || id==='f' || id==='s' || id==='r')
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -13,19 +40,30 @@ album.getID = function() {
|
|||||||
let id = null
|
let id = null
|
||||||
|
|
||||||
let isID = (id) => {
|
let isID = (id) => {
|
||||||
if (id==='0' || id==='f' || id==='s' || id==='r') return true
|
if (album.isSmartID(id)===true) return true
|
||||||
return $.isNumeric(id)
|
return $.isNumeric(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (photo.json) id = photo.json.album
|
|
||||||
else if (album.json) id = album.json.id
|
|
||||||
|
|
||||||
// Search
|
// Search
|
||||||
if (isID(id)===false) id = $('.album:hover, .album.active').attr('data-id')
|
if (isID(id)===false) id = $('.album:hover, .album.active').attr('data-id')
|
||||||
if (isID(id)===false) id = $('.photo:hover, .photo.active').attr('data-album-id')
|
if (isID(id)===false) id = $('.photo:hover, .photo.active').attr('data-album-id')
|
||||||
|
|
||||||
|
if (isID(id)===false) {
|
||||||
|
if (photo.json) id = photo.json.album
|
||||||
|
else if (album.json) id = album.json.id
|
||||||
|
}
|
||||||
|
|
||||||
if (isID(id)===true) return id
|
if (isID(id)===true) return id
|
||||||
else return false
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
album.getParent = function() {
|
||||||
|
|
||||||
|
if (album.json==null || album.isSmartID(album.json.id)===true || album.json.parent==0) return ''
|
||||||
|
|
||||||
|
return album.json.parent
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,17 +116,45 @@ album.load = function(albumID, refresh = false) {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
||||||
|
let finish = function() {
|
||||||
view.album.init()
|
view.album.init()
|
||||||
|
|
||||||
if (refresh===false) {
|
if (refresh===false) {
|
||||||
lychee.animate(lychee.content, 'contentZoomIn')
|
lychee.animate(lychee.content, 'contentZoomIn')
|
||||||
header.setMode('album')
|
header.setMode('album')
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (album.isSmartID(albumID)===false) {
|
||||||
|
|
||||||
|
params = {
|
||||||
|
parent: albumID
|
||||||
|
}
|
||||||
|
|
||||||
|
api.post('Albums::get', params, function(data) {
|
||||||
|
|
||||||
|
let waitTime = 0
|
||||||
|
|
||||||
|
album.subjson = data
|
||||||
|
|
||||||
|
// Calculate delay
|
||||||
|
let durationTime = (new Date().getTime() - startTime)
|
||||||
|
if (durationTime>300) waitTime = 0
|
||||||
|
else waitTime = 300 - durationTime
|
||||||
|
|
||||||
|
setTimeout(finish, waitTime)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
finish()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}, waitTime)
|
}, waitTime)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -99,18 +165,20 @@ album.parse = function() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
album.add = function() {
|
album.add = function(albumID = 0) {
|
||||||
|
|
||||||
const action = function(data) {
|
const action = function(data) {
|
||||||
|
|
||||||
let title = data.title
|
let title = data.title
|
||||||
|
let parent = albumID
|
||||||
|
|
||||||
const isNumber = (n) => (!isNaN(parseFloat(n)) && isFinite(n))
|
const isNumber = (n) => (!isNaN(parseFloat(n)) && isFinite(n))
|
||||||
|
|
||||||
basicModal.close()
|
basicModal.close()
|
||||||
|
|
||||||
let params = {
|
let params = {
|
||||||
title
|
title,
|
||||||
|
parent
|
||||||
}
|
}
|
||||||
|
|
||||||
api.post('Album::add', params, function(data) {
|
api.post('Album::add', params, function(data) {
|
||||||
@ -126,8 +194,14 @@ album.add = function() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
api.post('Albums::get', { parent: -1 }, function(data) {
|
||||||
|
|
||||||
|
let msg = `
|
||||||
|
<p>Enter a title for the new album: <input class='text' name='title' type='text' maxlength='50' placeholder='Title' value='Untitled'></p>
|
||||||
|
`
|
||||||
|
|
||||||
basicModal.show({
|
basicModal.show({
|
||||||
body: `<p>Enter a title for the new album: <input class='text' name='title' type='text' maxlength='50' placeholder='Title' value='Untitled'></p>`,
|
body: msg,
|
||||||
buttons: {
|
buttons: {
|
||||||
action: {
|
action: {
|
||||||
title: 'Create Album',
|
title: 'Create Album',
|
||||||
@ -140,6 +214,8 @@ album.add = function() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
album.delete = function(albumIDs) {
|
album.delete = function(albumIDs) {
|
||||||
@ -169,6 +245,21 @@ album.delete = function(albumIDs) {
|
|||||||
albums.deleteByID(id)
|
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 {
|
} else {
|
||||||
|
|
||||||
albums.refresh()
|
albums.refresh()
|
||||||
@ -197,20 +288,20 @@ album.delete = function(albumIDs) {
|
|||||||
cancel.title = 'Keep Album'
|
cancel.title = 'Keep Album'
|
||||||
|
|
||||||
// Get title
|
// Get title
|
||||||
if (album.json) albumTitle = album.json.title
|
if (album.json && album.json.id == albumIDs[0]) albumTitle = album.json.title
|
||||||
else if (albums.json) albumTitle = albums.getByID(albumIDs).title
|
else if (albums.json || album.subjson) albumTitle = albums.getByID(albumIDs).title
|
||||||
|
|
||||||
// Fallback for album without a title
|
// Fallback for album without a title
|
||||||
if (albumTitle==='') albumTitle = 'Untitled'
|
if (albumTitle==='') albumTitle = 'Untitled'
|
||||||
|
|
||||||
msg = lychee.html`<p>Are you sure you want to delete the album '$${ albumTitle }' and all of the photos it contains? This action can't be undone!</p>`
|
msg = lychee.html`<p>Are you sure you want to delete the album '$${ albumTitle }' and all of the photos and subalbums it contains? This action can't be undone!</p>`
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
action.title = 'Delete Albums and Photos'
|
action.title = 'Delete Albums and Photos'
|
||||||
cancel.title = 'Keep Albums'
|
cancel.title = 'Keep Albums'
|
||||||
|
|
||||||
msg = lychee.html`<p>Are you sure you want to delete all $${ albumIDs.length } selected albums and all of the photos they contain? This action can't be undone!</p>`
|
msg = lychee.html`<p>Are you sure you want to delete all $${ albumIDs.length } selected albums and all of the photos and subalbums they contain? This action can't be undone!</p>`
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,8 +333,8 @@ album.setTitle = function(albumIDs) {
|
|||||||
if (albumIDs.length===1) {
|
if (albumIDs.length===1) {
|
||||||
|
|
||||||
// Get old title if only one album is selected
|
// Get old title if only one album is selected
|
||||||
if (album.json) oldTitle = album.json.title
|
if (album.json && album.json.id == albumIDs[0]) oldTitle = album.json.title
|
||||||
else if (albums.json) oldTitle = albums.getByID(albumIDs).title
|
else if (albums.json || album.subjson) oldTitle = albums.getByID(albumIDs).title
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,10 +348,20 @@ album.setTitle = function(albumIDs) {
|
|||||||
|
|
||||||
// Rename only one album
|
// Rename only one album
|
||||||
|
|
||||||
|
if (album.json.id == albumIDs[0]) {
|
||||||
album.json.title = newTitle
|
album.json.title = newTitle
|
||||||
view.album.title()
|
view.album.title()
|
||||||
|
}
|
||||||
|
|
||||||
if (albums.json) albums.getByID(albumIDs[0]).title = newTitle
|
if (albums.json || album.subjson) {
|
||||||
|
albumIDs.forEach(function(id) {
|
||||||
|
let a = albums.getByID(id)
|
||||||
|
if (a) {
|
||||||
|
a.title = newTitle
|
||||||
|
view.album.content.title(id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
} else if (visible.albums()) {
|
} else if (visible.albums()) {
|
||||||
|
|
||||||
@ -538,7 +639,7 @@ album.getArchive = function(albumID) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
album.merge = function(albumIDs) {
|
const getMessage = function(albumIDs, titles, operation) {
|
||||||
|
|
||||||
let title = ''
|
let title = ''
|
||||||
let sTitle = ''
|
let sTitle = ''
|
||||||
@ -548,7 +649,8 @@ album.merge = function(albumIDs) {
|
|||||||
if (albumIDs instanceof Array===false) albumIDs = [ albumIDs ]
|
if (albumIDs instanceof Array===false) albumIDs = [ albumIDs ]
|
||||||
|
|
||||||
// Get title of first album
|
// Get title of first album
|
||||||
if (albums.json) title = albums.getByID(albumIDs[0]).title
|
if (titles.length > 0) title = titles[0]
|
||||||
|
else if (albums.json || album.subjson) title = albums.getByID(albumIDs[0]).title
|
||||||
|
|
||||||
// Fallback for first album without a title
|
// Fallback for first album without a title
|
||||||
if (title==='') title = 'Untitled'
|
if (title==='') title = 'Untitled'
|
||||||
@ -556,19 +658,26 @@ album.merge = function(albumIDs) {
|
|||||||
if (albumIDs.length===2) {
|
if (albumIDs.length===2) {
|
||||||
|
|
||||||
// Get title of second album
|
// Get title of second album
|
||||||
if (albums.json) sTitle = albums.getByID(albumIDs[1]).title
|
if (titles.length > 1) sTitle = titles[1]
|
||||||
|
else if (albums.json || album.subjson) sTitle = albums.getByID(albumIDs[1]).title
|
||||||
|
|
||||||
// Fallback for second album without a title
|
// Fallback for second album without a title
|
||||||
if (sTitle==='') sTitle = 'Untitled'
|
if (sTitle==='') sTitle = 'Untitled'
|
||||||
|
|
||||||
msg = lychee.html`<p>Are you sure you want to merge the album '$${ sTitle }' into the album '$${ title }'?</p>`
|
msg = lychee.html`<p>Are you sure you want to ${ operation } the album '$${ sTitle }' into '$${ title }'?</p>`
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
msg = lychee.html`<p>Are you sure you want to merge all selected albums into the album '$${ title }'?</p>`
|
msg = lychee.html`<p>Are you sure you want to ${ operation } all selected albums into '$${ title }'?</p>`
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return msg
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
album.merge = function(albumIDs, titles = []) {
|
||||||
|
|
||||||
const action = function() {
|
const action = function() {
|
||||||
|
|
||||||
basicModal.close()
|
basicModal.close()
|
||||||
@ -579,19 +688,15 @@ album.merge = function(albumIDs) {
|
|||||||
|
|
||||||
api.post('Album::merge', params, function(data) {
|
api.post('Album::merge', params, function(data) {
|
||||||
|
|
||||||
if (data!==true) {
|
if (data!==true) lychee.error(null, params, data)
|
||||||
lychee.error(null, params, data)
|
else album.reload()
|
||||||
} else {
|
|
||||||
albums.refresh()
|
|
||||||
lychee.goto()
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
basicModal.show({
|
basicModal.show({
|
||||||
body: msg,
|
body: getMessage(albumIDs, titles, 'merge'),
|
||||||
buttons: {
|
buttons: {
|
||||||
action: {
|
action: {
|
||||||
title: 'Merge Albums',
|
title: 'Merge Albums',
|
||||||
@ -606,3 +711,58 @@ album.merge = function(albumIDs) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 album.reload()
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
basicModal.show({
|
||||||
|
body: getMessage(albumIDs, titles, 'move'),
|
||||||
|
buttons: {
|
||||||
|
action: {
|
||||||
|
title: 'Move Albums',
|
||||||
|
fn: action,
|
||||||
|
class: 'red'
|
||||||
|
},
|
||||||
|
cancel: {
|
||||||
|
title: "Don't Move",
|
||||||
|
fn: basicModal.close
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
}
|
@ -16,7 +16,11 @@ albums.load = function() {
|
|||||||
|
|
||||||
if (albums.json===null) {
|
if (albums.json===null) {
|
||||||
|
|
||||||
api.post('Albums::get', {}, function(data) {
|
let params = {
|
||||||
|
parent: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
api.post('Albums::get', params, function(data) {
|
||||||
|
|
||||||
let waitTime = 0
|
let waitTime = 0
|
||||||
|
|
||||||
@ -108,18 +112,21 @@ albums.getByID = function(albumID) {
|
|||||||
// Function returns the JSON of an album
|
// Function returns the JSON of an album
|
||||||
|
|
||||||
if (albumID==null) return undefined
|
if (albumID==null) return undefined
|
||||||
if (!albums.json) return undefined
|
if (albumID instanceof Array)
|
||||||
if (!albums.json.albums) return undefined
|
albumID = albumID[0]
|
||||||
|
|
||||||
let json = undefined
|
let json = undefined
|
||||||
|
|
||||||
$.each(albums.json.albums, function(i) {
|
let func = function() {
|
||||||
|
if (this.id==albumID) json = this
|
||||||
|
}
|
||||||
|
|
||||||
let elem = albums.json.albums[i]
|
if (albums.json && albums.json.albums) {
|
||||||
|
$.each(albums.json.albums, func)
|
||||||
if (elem.id==albumID) json = elem
|
}
|
||||||
|
else if (album.subjson && album.subjson.albums) {
|
||||||
})
|
$.each(album.subjson.albums, func)
|
||||||
|
}
|
||||||
|
|
||||||
return json
|
return json
|
||||||
|
|
||||||
|
@ -14,11 +14,11 @@ build.iconic = function(icon, classes = '') {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
build.divider = function(title) {
|
build.divider = function(title, id = '') {
|
||||||
|
|
||||||
let html = ''
|
let html = ''
|
||||||
|
|
||||||
html += lychee.html`<div class='divider'><h1>$${ title }</h1></div>`
|
html += lychee.html`<div class='divider' id='$${ id }'><h1>$${ title }</h1></div>`
|
||||||
|
|
||||||
return html
|
return html
|
||||||
|
|
||||||
|
@ -2,9 +2,75 @@
|
|||||||
* @description This module is used for the context menu.
|
* @description This module is used for the context menu.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const buildAlbumList = function(albums, exclude, action, parent = 0, layer = 0) {
|
||||||
|
|
||||||
|
let items = []
|
||||||
|
|
||||||
|
for (i in albums) {
|
||||||
|
if ((layer==0 && !albums[i].parent) || albums[i].parent==parent) {
|
||||||
|
|
||||||
|
let album = albums[i]
|
||||||
|
|
||||||
|
let thumb = 'src/images/no_cover.svg'
|
||||||
|
if (album.thumbs && album.thumbs[0]) thumb = album.thumbs[0]
|
||||||
|
else if (album.thumbUrl) thumb = album.thumbUrl
|
||||||
|
|
||||||
|
if (album.title==='') album.title = 'Untitled'
|
||||||
|
|
||||||
|
let prefix = (layer > 0 ? ' '.repeat(layer - 1) + '└ ' : '')
|
||||||
|
|
||||||
|
let html = lychee.html`
|
||||||
|
${ prefix }
|
||||||
|
<img class='cover' width='16' height='16' src='$${ thumb }'>
|
||||||
|
<div class='title'>$${ album.title }</div>
|
||||||
|
`
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
title: html,
|
||||||
|
disabled: (exclude.indexOf(album.id)!==-1),
|
||||||
|
fn: () => action(album)
|
||||||
|
})
|
||||||
|
|
||||||
|
items = items.concat(buildAlbumList(albums, exclude, action, album.id, layer + 1))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return items
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAlbumFrom = function(albums, id) {
|
||||||
|
|
||||||
|
for (a in albums) {
|
||||||
|
if (albums[a].id == id) return albums[a]
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
contextMenu = {}
|
contextMenu = {}
|
||||||
|
|
||||||
contextMenu.add = function(e) {
|
contextMenu.add = function(albumID, e) {
|
||||||
|
|
||||||
let items = [
|
let items = [
|
||||||
{ title: build.iconic('image') + 'Upload Photo', fn: () => $('#upload_files').click() },
|
{ title: build.iconic('image') + 'Upload Photo', fn: () => $('#upload_files').click() },
|
||||||
@ -13,7 +79,7 @@ contextMenu.add = function(e) {
|
|||||||
{ title: build.iconic('dropbox', 'ionicons') + 'Import from Dropbox', fn: upload.start.dropbox },
|
{ title: build.iconic('dropbox', 'ionicons') + 'Import from Dropbox', fn: upload.start.dropbox },
|
||||||
{ title: build.iconic('terminal') + 'Import from Server', fn: upload.start.server },
|
{ title: build.iconic('terminal') + 'Import from Server', fn: upload.start.server },
|
||||||
{ },
|
{ },
|
||||||
{ title: build.iconic('folder') + 'New Album', fn: album.add }
|
{ title: build.iconic('folder') + 'New Album', fn: () => album.add(albumID) }
|
||||||
]
|
]
|
||||||
|
|
||||||
basicContext.show(items, e.originalEvent)
|
basicContext.show(items, e.originalEvent)
|
||||||
@ -46,18 +112,16 @@ contextMenu.album = function(albumID, e) {
|
|||||||
// fn must call basicContext.close() first,
|
// fn must call basicContext.close() first,
|
||||||
// in order to keep the selection
|
// in order to keep the selection
|
||||||
|
|
||||||
if (albumID==='0' || albumID==='f' || albumID==='s' || albumID==='r') return false
|
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 = [
|
let items = [
|
||||||
{ title: build.iconic('pencil') + 'Rename', fn: () => album.setTitle([ albumID ]) },
|
{ 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('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 ]) }
|
{ title: build.iconic('trash') + 'Delete', fn: () => album.delete([ albumID ]) }
|
||||||
]
|
]
|
||||||
|
|
||||||
$('.album[data-id="' + albumID + '"]').addClass('active')
|
multiselect.select('.album[data-id="' + albumID + '"]')
|
||||||
|
|
||||||
basicContext.show(items, e.originalEvent, contextMenu.close)
|
basicContext.show(items, e.originalEvent, contextMenu.close)
|
||||||
|
|
||||||
@ -71,13 +135,11 @@ contextMenu.albumMulti = function(albumIDs, e) {
|
|||||||
// Show list of albums otherwise
|
// Show list of albums otherwise
|
||||||
let autoMerge = (albumIDs.length>1 ? true : false)
|
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 = [
|
let items = [
|
||||||
{ title: build.iconic('pencil') + 'Rename All', fn: () => album.setTitle(albumIDs) },
|
{ 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 All', visible: 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('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) }
|
{ title: build.iconic('trash') + 'Delete All', fn: () => album.delete(albumIDs) }
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -89,26 +151,13 @@ contextMenu.albumMulti = function(albumIDs, e) {
|
|||||||
|
|
||||||
contextMenu.albumTitle = function(albumID, e) {
|
contextMenu.albumTitle = function(albumID, e) {
|
||||||
|
|
||||||
api.post('Albums::get', {}, function(data) {
|
api.post('Albums::get', { parent: -1 }, function(data) {
|
||||||
|
|
||||||
let items = []
|
let items = []
|
||||||
|
|
||||||
if (data.albums && data.num>1) {
|
if (data.albums && data.num>1) {
|
||||||
|
|
||||||
// Generate list of albums
|
items = buildAlbumList(data.albums, [ albumID ], (a) => lychee.goto(a.id))
|
||||||
$.each(data.albums, function() {
|
|
||||||
|
|
||||||
if (!this.thumbs[0]) this.thumbs[0] = 'src/images/no_cover.svg'
|
|
||||||
if (this.title==='') this.title = 'Untitled'
|
|
||||||
|
|
||||||
let html = lychee.html`<img class='cover' width='16' height='16' src='$${ this.thumbs[0] }'><div class='title'>$${ this.title }</div>`
|
|
||||||
|
|
||||||
if (this.id!=albumID) items.push({
|
|
||||||
title: html,
|
|
||||||
fn: () => lychee.goto(this.id)
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
items.unshift({ })
|
items.unshift({ })
|
||||||
|
|
||||||
@ -124,26 +173,58 @@ contextMenu.albumTitle = function(albumID, e) {
|
|||||||
|
|
||||||
contextMenu.mergeAlbum = function(albumID, e) {
|
contextMenu.mergeAlbum = function(albumID, e) {
|
||||||
|
|
||||||
api.post('Albums::get', {}, function(data) {
|
api.post('Albums::get', { parent: -1 }, function(data) {
|
||||||
|
|
||||||
let items = []
|
let items = []
|
||||||
|
|
||||||
if (data.albums && data.num>1) {
|
if (data.albums && data.num>1) {
|
||||||
|
|
||||||
$.each(data.albums, function() {
|
let selalbum = albums.getByID(albumID)
|
||||||
|
let title = selalbum.title
|
||||||
|
|
||||||
if (!this.thumbs[0]) this.thumbs[0] = 'src/images/no_cover.svg'
|
// Disable all parents
|
||||||
if (this.title==='') this.title = 'Untitled'
|
// It's not possible to move them into us
|
||||||
|
let exclude = [ albumID ]
|
||||||
|
let a = getAlbumFrom(data.albums, selalbum.parent)
|
||||||
|
while (a!=null) {
|
||||||
|
exclude.push(a.id)
|
||||||
|
a = getAlbumFrom(data.albums, a.parent)
|
||||||
|
}
|
||||||
|
|
||||||
let html = lychee.html`<img class='cover' width='16' height='16' src='$${ this.thumbs[0] }'><div class='title'>$${ this.title }</div>`
|
items = buildAlbumList(data.albums, exclude, (a) => album.merge([ albumID, a.id ], [ title, a.title ]))
|
||||||
|
|
||||||
if (this.id!=albumID) items.push({
|
}
|
||||||
title: html,
|
|
||||||
fn: () => album.merge([ albumID, this.id ])
|
if (items.length===0) return false
|
||||||
})
|
|
||||||
|
basicContext.show(items, e.originalEvent, contextMenu.close)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ]), 0, 1)
|
||||||
|
|
||||||
|
items.unshift({ title: 'Root', fn: () => album.move([ 0 ].concat(albumIDs), [ 'Root', title ]) })
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (items.length===0) return false
|
if (items.length===0) return false
|
||||||
@ -170,7 +251,7 @@ contextMenu.photo = function(photoID, e) {
|
|||||||
{ title: build.iconic('trash') + 'Delete', fn: () => photo.delete([ photoID ]) }
|
{ title: build.iconic('trash') + 'Delete', fn: () => photo.delete([ photoID ]) }
|
||||||
]
|
]
|
||||||
|
|
||||||
$('.photo[data-id="' + photoID + '"]').addClass('active')
|
multiselect.select('.photo[data-id="' + photoID + '"]')
|
||||||
|
|
||||||
basicContext.show(items, e.originalEvent, contextMenu.close)
|
basicContext.show(items, e.originalEvent, contextMenu.close)
|
||||||
|
|
||||||
@ -178,12 +259,12 @@ contextMenu.photo = function(photoID, e) {
|
|||||||
|
|
||||||
contextMenu.photoMulti = function(photoIDs, e) {
|
contextMenu.photoMulti = function(photoIDs, e) {
|
||||||
|
|
||||||
|
multiselect.stopResize()
|
||||||
|
|
||||||
// Notice for 'Move All':
|
// Notice for 'Move All':
|
||||||
// fn must call basicContext.close() first,
|
// fn must call basicContext.close() first,
|
||||||
// in order to keep the selection and multiselect
|
// in order to keep the selection and multiselect
|
||||||
|
|
||||||
multiselect.stopResize()
|
|
||||||
|
|
||||||
let items = [
|
let items = [
|
||||||
{ title: build.iconic('star') + 'Star All', fn: () => photo.setStar(photoIDs) },
|
{ title: build.iconic('star') + 'Star All', fn: () => photo.setStar(photoIDs) },
|
||||||
{ title: build.iconic('tag') + 'Tag All', fn: () => photo.editTags(photoIDs) },
|
{ title: build.iconic('tag') + 'Tag All', fn: () => photo.editTags(photoIDs) },
|
||||||
@ -210,19 +291,7 @@ contextMenu.photoTitle = function(albumID, photoID, e) {
|
|||||||
|
|
||||||
items.push({ })
|
items.push({ })
|
||||||
|
|
||||||
// Generate list of albums
|
items = items.concat(buildAlbumList(data.content, [ photoID ], (a) => lychee.goto(albumID + '/' + a.id)))
|
||||||
$.each(data.content, function(index) {
|
|
||||||
|
|
||||||
if (this.title==='') this.title = 'Untitled'
|
|
||||||
|
|
||||||
let html = lychee.html`<img class='cover' width='16' height='16' src='$${ this.thumbUrl }'><div class='title'>$${ this.title }</div>`
|
|
||||||
|
|
||||||
if (this.id!=photoID) items.push({
|
|
||||||
title: html,
|
|
||||||
fn: () => lychee.goto(albumID + '/' + this.id)
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,7 +319,7 @@ contextMenu.move = function(photoIDs, e) {
|
|||||||
|
|
||||||
let items = []
|
let items = []
|
||||||
|
|
||||||
api.post('Albums::get', {}, function(data) {
|
api.post('Albums::get', { parent: -1 }, function(data) {
|
||||||
|
|
||||||
if (data.num===0) {
|
if (data.num===0) {
|
||||||
|
|
||||||
@ -261,20 +330,7 @@ contextMenu.move = function(photoIDs, e) {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Generate list of albums
|
items = buildAlbumList(data.albums, [ album.getID() ], (a) => photo.setAlbum(photoIDs, a.id))
|
||||||
$.each(data.albums, function() {
|
|
||||||
|
|
||||||
if (!this.thumbs[0]) this.thumbs[0] = 'src/images/no_cover.svg'
|
|
||||||
if (this.title==='') this.title = 'Untitled'
|
|
||||||
|
|
||||||
let html = lychee.html`<img class='cover' width='16' height='16' src='$${ this.thumbs[0] }'><div class='title'>$${ this.title }</div>`
|
|
||||||
|
|
||||||
if (this.id!=album.getID()) items.push({
|
|
||||||
title: html,
|
|
||||||
fn: () => photo.setAlbum(photoIDs, this.id)
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
// Show Unsorted when unsorted is not the current album
|
// Show Unsorted when unsorted is not the current album
|
||||||
if (album.getID()!=='0') {
|
if (album.getID()!=='0') {
|
||||||
@ -344,7 +400,7 @@ contextMenu.close = function() {
|
|||||||
|
|
||||||
basicContext.close()
|
basicContext.close()
|
||||||
|
|
||||||
$('.photo.active, .album.active').removeClass('active')
|
multiselect.deselect('.photo.active, .album.active')
|
||||||
if (visible.multiselect()) multiselect.close()
|
if (visible.multiselect()) multiselect.close()
|
||||||
|
|
||||||
}
|
}
|
@ -43,7 +43,7 @@ header.bind = function() {
|
|||||||
header.dom('#button_settings') .on(eventName, contextMenu.settings)
|
header.dom('#button_settings') .on(eventName, contextMenu.settings)
|
||||||
header.dom('#button_info_album') .on(eventName, sidebar.toggle)
|
header.dom('#button_info_album') .on(eventName, sidebar.toggle)
|
||||||
header.dom('#button_info') .on(eventName, sidebar.toggle)
|
header.dom('#button_info') .on(eventName, sidebar.toggle)
|
||||||
header.dom('.button_add') .on(eventName, contextMenu.add)
|
header.dom('.button_add') .on(eventName, function(e) { contextMenu.add(album.getID(), e) })
|
||||||
header.dom('#button_more') .on(eventName, function(e) { contextMenu.photoMore(photo.getID(), e) })
|
header.dom('#button_more') .on(eventName, function(e) { contextMenu.photoMore(photo.getID(), e) })
|
||||||
header.dom('#button_move') .on(eventName, function(e) { contextMenu.move([ photo.getID() ], e) })
|
header.dom('#button_move') .on(eventName, function(e) { contextMenu.move([ photo.getID() ], e) })
|
||||||
header.dom('.header__hostedwith') .on(eventName, function() { window.open(lychee.website) })
|
header.dom('.header__hostedwith') .on(eventName, function() { window.open(lychee.website) })
|
||||||
@ -51,7 +51,7 @@ header.bind = function() {
|
|||||||
header.dom('#button_trash') .on(eventName, function() { photo.delete([ photo.getID() ]) })
|
header.dom('#button_trash') .on(eventName, function() { photo.delete([ photo.getID() ]) })
|
||||||
header.dom('#button_archive') .on(eventName, function() { album.getArchive(album.getID()) })
|
header.dom('#button_archive') .on(eventName, function() { album.getArchive(album.getID()) })
|
||||||
header.dom('#button_star') .on(eventName, function() { photo.setStar([ photo.getID() ]) })
|
header.dom('#button_star') .on(eventName, function() { photo.setStar([ photo.getID() ]) })
|
||||||
header.dom('#button_back_home') .on(eventName, function() { lychee.goto() })
|
header.dom('#button_back_home') .on(eventName, function() { lychee.goto(album.getParent()) })
|
||||||
header.dom('#button_back') .on(eventName, function() { lychee.goto(album.getID()) })
|
header.dom('#button_back') .on(eventName, function() { lychee.goto(album.getID()) })
|
||||||
|
|
||||||
header.dom('.header__search').on('keyup click', function() { search.find($(this).val()) })
|
header.dom('.header__search').on('keyup click', function() { search.find($(this).val()) })
|
||||||
@ -132,7 +132,7 @@ header.setMode = function(mode) {
|
|||||||
header.dom('.header__toolbar--album').addClass('header__toolbar--visible')
|
header.dom('.header__toolbar--album').addClass('header__toolbar--visible')
|
||||||
|
|
||||||
// Hide download button when album empty
|
// Hide download button when album empty
|
||||||
if (album.json.content===false) $('#button_archive').hide()
|
if (album.json.content===false && album.subjson.num==0) $('#button_archive').hide()
|
||||||
else $('#button_archive').show()
|
else $('#button_archive').show()
|
||||||
|
|
||||||
// Hide download button when not logged in and album not downloadable
|
// Hide download button when not logged in and album not downloadable
|
||||||
|
@ -54,10 +54,6 @@ $(document).ready(function() {
|
|||||||
if (visible.photo() && basicModal.visible()===false) { photo.delete([photo.getID()]); return false }
|
if (visible.photo() && basicModal.visible()===false) { photo.delete([photo.getID()]); return false }
|
||||||
else if (visible.album() && basicModal.visible()===false) { album.delete([album.getID()]); return false }
|
else if (visible.album() && basicModal.visible()===false) { album.delete([album.getID()]); return false }
|
||||||
})
|
})
|
||||||
.bind([ 'command+a', 'ctrl+a' ], function() {
|
|
||||||
if (visible.album() && basicModal.visible()===false) { multiselect.selectAll(); return false }
|
|
||||||
else if (visible.albums() && basicModal.visible()===false) { multiselect.selectAll(); return false }
|
|
||||||
})
|
|
||||||
|
|
||||||
Mousetrap.bindGlobal('enter', function() {
|
Mousetrap.bindGlobal('enter', function() {
|
||||||
if (basicModal.visible()===true) basicModal.action()
|
if (basicModal.visible()===true) basicModal.action()
|
||||||
@ -67,7 +63,7 @@ $(document).ready(function() {
|
|||||||
if (basicModal.visible()===true) basicModal.cancel()
|
if (basicModal.visible()===true) basicModal.cancel()
|
||||||
else if (visible.contextMenu()) contextMenu.close()
|
else if (visible.contextMenu()) contextMenu.close()
|
||||||
else if (visible.photo()) lychee.goto(album.getID())
|
else if (visible.photo()) lychee.goto(album.getID())
|
||||||
else if (visible.album()) lychee.goto()
|
else if (visible.album()) lychee.goto(album.getParent())
|
||||||
else if (visible.albums() && header.dom('.header__search').val().length!==0) search.reset()
|
else if (visible.albums() && header.dom('.header__search').val().length!==0) search.reset()
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
@ -95,12 +91,12 @@ $(document).ready(function() {
|
|||||||
$(document)
|
$(document)
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
.on('click', '.album', function() { lychee.goto($(this).attr('data-id')) })
|
.on('click', '.album', function(e) { multiselect.albumClick(e, $(this)) })
|
||||||
.on('click', '.photo', function() { lychee.goto(album.getID() + '/' + $(this).attr('data-id')) })
|
.on('click', '.photo', function(e) { multiselect.photoClick(e, $(this)) })
|
||||||
|
|
||||||
// Context Menu
|
// Context Menu
|
||||||
.on('contextmenu', '.photo', function(e) { contextMenu.photo(photo.getID(), e) })
|
.on('contextmenu', '.photo', function(e) { multiselect.photoContextMenu(e, $(this)) })
|
||||||
.on('contextmenu', '.album', function(e) { contextMenu.album(album.getID(), e) })
|
.on('contextmenu', '.album', function(e) { multiselect.albumContextMenu(e, $(this)) })
|
||||||
|
|
||||||
// Upload
|
// Upload
|
||||||
.on('change', '#upload_files', function() { basicModal.close(); upload.start.local(this.files) })
|
.on('change', '#upload_files', function() { basicModal.close(); upload.start.local(this.files) })
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
lychee = {
|
lychee = {
|
||||||
|
|
||||||
title : document.title,
|
title : document.title,
|
||||||
version : '3.1.5',
|
version : '3.2.0',
|
||||||
versionCode : '030105',
|
versionCode : '030200',
|
||||||
|
|
||||||
updatePath : '//update.electerious.com/index.json',
|
updatePath : '//update.electerious.com/index.json',
|
||||||
updateURL : 'https://github.com/electerious/Lychee',
|
updateURL : 'https://github.com/electerious/Lychee',
|
||||||
@ -169,6 +169,7 @@ lychee.load = function() {
|
|||||||
|
|
||||||
// Trash data
|
// Trash data
|
||||||
photo.json = null
|
photo.json = null
|
||||||
|
albums.json = null
|
||||||
|
|
||||||
// Show Photo
|
// Show Photo
|
||||||
if (lychee.content.html()==='' || (header.dom('.header__search').length && header.dom('.header__search').val().length!==0)) {
|
if (lychee.content.html()==='' || (header.dom('.header__search').length && header.dom('.header__search').val().length!==0)) {
|
||||||
@ -181,10 +182,11 @@ lychee.load = function() {
|
|||||||
|
|
||||||
// Trash data
|
// Trash data
|
||||||
photo.json = null
|
photo.json = null
|
||||||
|
albums.json = null
|
||||||
|
|
||||||
// Show Album
|
// Show Album
|
||||||
if (visible.photo()) view.photo.hide()
|
if (visible.photo()) view.photo.hide()
|
||||||
if (visible.sidebar() && (albumID==='0' || albumID==='f' || albumID==='s' || albumID==='r')) sidebar.toggle()
|
if (visible.sidebar() && album.isSmartID(albumID)) sidebar.toggle()
|
||||||
if (album.json && albumID==album.json.id) view.album.title()
|
if (album.json && albumID==album.json.id) view.album.title()
|
||||||
else album.load(albumID)
|
else album.load(albumID)
|
||||||
|
|
||||||
@ -197,6 +199,7 @@ lychee.load = function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Trash data
|
// Trash data
|
||||||
|
album.subjson = null
|
||||||
album.json = null
|
album.json = null
|
||||||
photo.json = null
|
photo.json = null
|
||||||
|
|
||||||
|
@ -2,7 +2,19 @@
|
|||||||
* @description Select multiple albums or photos.
|
* @description Select multiple albums or photos.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
multiselect = {}
|
const isSelectKeyPressed = function(e) {
|
||||||
|
|
||||||
|
return e.metaKey || e.ctrlKey
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
multiselect = {
|
||||||
|
|
||||||
|
ids : [],
|
||||||
|
albumsSelected : 0,
|
||||||
|
photosSelected : 0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
multiselect.position = {
|
multiselect.position = {
|
||||||
|
|
||||||
@ -21,6 +33,121 @@ multiselect.bind = function() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
multiselect.isSelected = function(id) {
|
||||||
|
|
||||||
|
let pos = $.inArray(id, multiselect.ids)
|
||||||
|
|
||||||
|
return {
|
||||||
|
selected : (pos===-1 ? false : true),
|
||||||
|
position : pos
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
multiselect.toggleItem = function(object, id) {
|
||||||
|
|
||||||
|
let selected = multiselect.isSelected(id).selected
|
||||||
|
|
||||||
|
if (selected===false) multiselect.addItem(object, id)
|
||||||
|
else multiselect.removeItem(object, id)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
multiselect.addItem = function(object, id) {
|
||||||
|
|
||||||
|
if (album.isSmartID(id)) return
|
||||||
|
if (multiselect.isSelected(id).selected===true) return
|
||||||
|
|
||||||
|
let isAlbum = object.hasClass('album')
|
||||||
|
|
||||||
|
if ((isAlbum && multiselect.photosSelected > 0) ||
|
||||||
|
(!isAlbum && multiselect.albumsSelected > 0)) {
|
||||||
|
lychee.error('Please select either albums or photos!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
multiselect.ids.push(id)
|
||||||
|
multiselect.select(object)
|
||||||
|
|
||||||
|
if (isAlbum) multiselect.albumsSelected++
|
||||||
|
else multiselect.photosSelected++
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
multiselect.removeItem = function(object, id) {
|
||||||
|
|
||||||
|
let { selected, pos } = multiselect.isSelected(id)
|
||||||
|
|
||||||
|
if (selected===false) return
|
||||||
|
|
||||||
|
multiselect.ids.splice(pos, 1)
|
||||||
|
multiselect.deselect(object)
|
||||||
|
|
||||||
|
let isAlbum = object.hasClass('album')
|
||||||
|
|
||||||
|
if (isAlbum) multiselect.albumsSelected--
|
||||||
|
else multiselect.photosSelected--
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
multiselect.albumClick = function(e, albumObj) {
|
||||||
|
|
||||||
|
let id = albumObj.attr('data-id')
|
||||||
|
|
||||||
|
if (isSelectKeyPressed(e)) multiselect.toggleItem(albumObj, id)
|
||||||
|
else lychee.goto(id)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
multiselect.photoClick = function(e, photoObj) {
|
||||||
|
|
||||||
|
let id = photoObj.attr('data-id')
|
||||||
|
|
||||||
|
if (isSelectKeyPressed(e)) multiselect.toggleItem(photoObj, id)
|
||||||
|
else lychee.goto(album.getID() + '/' + id)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
multiselect.albumContextMenu = function(e, albumObj) {
|
||||||
|
|
||||||
|
let id = albumObj.attr('data-id')
|
||||||
|
let selected = multiselect.isSelected(id).selected
|
||||||
|
|
||||||
|
if (selected!==false) {
|
||||||
|
contextMenu.albumMulti(multiselect.ids, e)
|
||||||
|
multiselect.clearSelection(false)
|
||||||
|
} else {
|
||||||
|
multiselect.clearSelection()
|
||||||
|
contextMenu.album(album.getID(), e)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
multiselect.photoContextMenu = function(e, photoObj) {
|
||||||
|
|
||||||
|
let id = photoObj.attr('data-id')
|
||||||
|
let selected = multiselect.isSelected(id).selected
|
||||||
|
|
||||||
|
if (selected!==false) {
|
||||||
|
contextMenu.photoMulti(multiselect.ids, e)
|
||||||
|
multiselect.clearSelection(false)
|
||||||
|
} else {
|
||||||
|
multiselect.clearSelection()
|
||||||
|
contextMenu.photo(photo.getID(), e)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
multiselect.clearSelection = function(deselect = true) {
|
||||||
|
|
||||||
|
if (deselect) multiselect.deselect('.photo.active, .album.active')
|
||||||
|
|
||||||
|
multiselect.ids = []
|
||||||
|
multiselect.albumsSelected = 0
|
||||||
|
multiselect.photosSelected = 0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
multiselect.show = function(e) {
|
multiselect.show = function(e) {
|
||||||
|
|
||||||
if (lychee.publicMode) return false
|
if (lychee.publicMode) return false
|
||||||
@ -44,43 +171,6 @@ multiselect.show = function(e) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
multiselect.selectAll = function() {
|
|
||||||
|
|
||||||
if (lychee.publicMode) return false
|
|
||||||
if (visible.search()) return false
|
|
||||||
if (!visible.albums() && !visible.album) return false
|
|
||||||
if (visible.multiselect()) $('#multiselect').remove()
|
|
||||||
|
|
||||||
sidebar.setSelectable(false)
|
|
||||||
|
|
||||||
multiselect.position.top = 70
|
|
||||||
multiselect.position.right = 40
|
|
||||||
multiselect.position.bottom = 90
|
|
||||||
multiselect.position.left = 20
|
|
||||||
|
|
||||||
$('body').append(build.multiselect(multiselect.position.top, multiselect.position.left))
|
|
||||||
|
|
||||||
let documentSize = {
|
|
||||||
width : $(document).width(),
|
|
||||||
height : $(document).height()
|
|
||||||
}
|
|
||||||
|
|
||||||
let newSize = {
|
|
||||||
width : documentSize.width - multiselect.position.right + 2,
|
|
||||||
height : documentSize.height - multiselect.position.bottom
|
|
||||||
}
|
|
||||||
|
|
||||||
let e = {
|
|
||||||
pageX : documentSize.width - (multiselect.position.right / 2),
|
|
||||||
pageY : documentSize.height - multiselect.position.bottom
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#multiselect').css(newSize)
|
|
||||||
|
|
||||||
multiselect.getSelection(e)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
multiselect.resize = function(e) {
|
multiselect.resize = function(e) {
|
||||||
|
|
||||||
if (multiselect.position.top === null ||
|
if (multiselect.position.top === null ||
|
||||||
@ -187,6 +277,11 @@ multiselect.getSelection = function(e) {
|
|||||||
if (visible.contextMenu()) return false
|
if (visible.contextMenu()) return false
|
||||||
if (!visible.multiselect()) return false
|
if (!visible.multiselect()) return false
|
||||||
|
|
||||||
|
if (!isSelectKeyPressed(e) && (size.width==0 || size.height==0)) {
|
||||||
|
multiselect.close()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
$('.photo, .album').each(function() {
|
$('.photo, .album').each(function() {
|
||||||
|
|
||||||
let offset = $(this).offset()
|
let offset = $(this).offset()
|
||||||
@ -196,26 +291,37 @@ multiselect.getSelection = function(e) {
|
|||||||
(offset.top + 206)<=(size.top + size.height + tolerance) &&
|
(offset.top + 206)<=(size.top + size.height + tolerance) &&
|
||||||
(offset.left + 206)<=(size.left + size.width + tolerance)) {
|
(offset.left + 206)<=(size.left + size.width + tolerance)) {
|
||||||
|
|
||||||
let id = $(this).data('id')
|
let id = $(this).attr('data-id')
|
||||||
|
|
||||||
if (id!=='0' && id!==0 && id!=='f' && id!=='s' && id!=='r' && id!=null) {
|
multiselect.addItem($(this), id)
|
||||||
|
|
||||||
ids.push(id)
|
|
||||||
$(this).addClass('active')
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if (ids.length!==0 && visible.album()) contextMenu.photoMulti(ids, e)
|
multiselect.hide()
|
||||||
else if (ids.length!==0 && visible.albums()) contextMenu.albumMulti(ids, e)
|
|
||||||
else multiselect.close()
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
multiselect.close = function() {
|
multiselect.select = function(id) {
|
||||||
|
|
||||||
|
let el = $(id)
|
||||||
|
|
||||||
|
el.addClass('selected')
|
||||||
|
el.addClass('active')
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
multiselect.deselect = function(id) {
|
||||||
|
|
||||||
|
let el = $(id)
|
||||||
|
|
||||||
|
el.removeClass('selected')
|
||||||
|
el.removeClass('active')
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
multiselect.hide = function() {
|
||||||
|
|
||||||
sidebar.setSelectable(true)
|
sidebar.setSelectable(true)
|
||||||
|
|
||||||
@ -230,3 +336,11 @@ multiselect.close = function() {
|
|||||||
setTimeout(() => $('#multiselect').remove(), 300)
|
setTimeout(() => $('#multiselect').remove(), 300)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
multiselect.close = function() {
|
||||||
|
|
||||||
|
multiselect.clearSelection()
|
||||||
|
|
||||||
|
multiselect.hide()
|
||||||
|
|
||||||
|
}
|
@ -72,7 +72,7 @@ settings.createConfig = function() {
|
|||||||
if (data==='Warning: Could not create file!') {
|
if (data==='Warning: Could not create file!') {
|
||||||
|
|
||||||
basicModal.show({
|
basicModal.show({
|
||||||
body: "<p>Unable to save this configuration. Permission denied in <b>'data/'</b>. Please set the read, write and execute rights for others in <b>'data/'</b> and <b>'uploads/'</b>. Take a look at the readme for more information.</p>",
|
body: `<p>Unable to save this configuration. Permission denied in <b>'data/'</b>. Please set the read, write and execute rights for others in <b>'data/'</b> and <b>'uploads/'</b>. Take a look at the readme for more information.</p>`,
|
||||||
buttons: {
|
buttons: {
|
||||||
action: {
|
action: {
|
||||||
title: 'Retry',
|
title: 'Retry',
|
||||||
|
@ -124,13 +124,17 @@ sidebar.createStructure.photo = function(data) {
|
|||||||
// Set value for public
|
// Set value for public
|
||||||
switch (data.public) {
|
switch (data.public) {
|
||||||
|
|
||||||
case '0' : _public = 'No'
|
case '0':
|
||||||
|
_public = 'No'
|
||||||
break
|
break
|
||||||
case '1' : _public = 'Yes'
|
case '1':
|
||||||
|
_public = 'Yes'
|
||||||
break
|
break
|
||||||
case '2' : _public = 'Yes (Album)'
|
case '2':
|
||||||
|
_public = 'Yes (Album)'
|
||||||
break
|
break
|
||||||
default : _public = '-'
|
default:
|
||||||
|
_public = '-'
|
||||||
break
|
break
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -143,8 +143,22 @@ view.album = {
|
|||||||
|
|
||||||
let photosData = ''
|
let photosData = ''
|
||||||
|
|
||||||
|
// Sub albums
|
||||||
|
if (album.subjson && album.subjson.albums && album.subjson.num!==0) {
|
||||||
|
|
||||||
|
photosData = build.divider('Albums')
|
||||||
|
|
||||||
|
$.each(album.subjson.albums, function() {
|
||||||
|
albums.parse(this)
|
||||||
|
photosData += build.album(this)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if (album.json.content && album.json.content!==false) {
|
if (album.json.content && album.json.content!==false) {
|
||||||
|
|
||||||
|
photosData += build.divider('Photos', 'divider-photos')
|
||||||
|
|
||||||
// Build photos
|
// Build photos
|
||||||
$.each(album.json.content, function() {
|
$.each(album.json.content, function() {
|
||||||
photosData += build.photo(this)
|
photosData += build.photo(this)
|
||||||
@ -163,13 +177,32 @@ view.album = {
|
|||||||
|
|
||||||
title: function(photoID) {
|
title: function(photoID) {
|
||||||
|
|
||||||
let title = album.json.content[photoID].title
|
let ntitle = ''
|
||||||
|
let prefix = ''
|
||||||
|
|
||||||
title = lychee.escapeHTML(title)
|
if (album.json.content[photoID]) {
|
||||||
|
|
||||||
$('.photo[data-id="' + photoID + '"] .overlay h1')
|
prefix = '.photo'
|
||||||
.html(title)
|
ntitle = album.json.content[photoID].title
|
||||||
.attr('title', title)
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
prefix = '.album'
|
||||||
|
|
||||||
|
for (i in album.subjson.albums) {
|
||||||
|
if (album.subjson.albums[i].id==photoID) {
|
||||||
|
ntitle = album.subjson.albums[i].title
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ntitle = lychee.escapeHTML(ntitle)
|
||||||
|
|
||||||
|
$(prefix + '[data-id="' + photoID + '"] .overlay h1')
|
||||||
|
.html(ntitle)
|
||||||
|
.attr('title', ntitle)
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -202,6 +235,7 @@ view.album = {
|
|||||||
if (!visible.albums()) {
|
if (!visible.albums()) {
|
||||||
album.json.num--
|
album.json.num--
|
||||||
view.album.num()
|
view.album.num()
|
||||||
|
if (album.json.num == 0) $('#divider-photos').hide()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -72,6 +72,10 @@
|
|||||||
transition: none;
|
transition: none;
|
||||||
border-color: darken($colorBlue, 15%);
|
border-color: darken($colorBlue, 15%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.selected img {
|
||||||
|
outline: 1px solid $colorBlue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Album -------------------------------------------------------------- //
|
// Album -------------------------------------------------------------- //
|
||||||
|
Loading…
Reference in New Issue
Block a user