Merge pull request #354 from electerious/develop

v3.0.1
This commit is contained in:
Tobias Reich 2015-05-26 20:20:34 +02:00
commit d4c724b02c
31 changed files with 432 additions and 147 deletions

BIN
dist/main.css vendored

Binary file not shown.

BIN
dist/main.js vendored

Binary file not shown.

BIN
dist/view.js vendored

Binary file not shown.

View File

@ -1,6 +1,15 @@
## v3.0.1
Released May 24, 2015
- `New` Album Sorting (Thanks @ophian, #98)
- `New` Identifier to prevent login of multiple instances of lychee (#344)
- `Improved` Albums and photos now can have a title with up to 50 chars (#332)
- `Fixed` Removing last Tag from photo not possible in Firefox (#269)
## v3.0.0 ## v3.0.0
Released April 6, 2015 Released May 6, 2015
**Warning**: You need to enter a new username and password when upgrading from a previous version. Your installation is accessible for everyone till you enter a new login by visiting your Lychee. Both fields are now stored in a secure way. Legacy md5 code has been removed. **Warning**: You need to enter a new username and password when upgrading from a previous version. Your installation is accessible for everyone till you enter a new login by visiting your Lychee. Both fields are now stored in a secure way. Legacy md5 code has been removed.

View File

@ -279,9 +279,14 @@ class Admin extends Access {
private function setSorting() { private function setSorting() {
Module::dependencies(isset($_POST['type'], $_POST['order'])); Module::dependencies(isset($_POST['typeAlbums'], $_POST['orderAlbums'], $_POST['typePhotos'], $_POST['orderPhotos']));
$this->settings = new Settings($this->database); $this->settings = new Settings($this->database);
echo $this->settings->setSorting($_POST['type'], $_POST['order']);
$sA = $this->settings->setSortingAlbums($_POST['typeAlbums'], $_POST['orderAlbums']);
$sP = $this->settings->setSortingPhotos($_POST['typePhotos'], $_POST['orderPhotos']);
if ($sA===true&&$sP===true) echo true;
else echo false;
} }

View File

@ -63,7 +63,8 @@ if (!empty($_POST['function'])||!empty($_GET['function'])) {
if (isset($_POST['function'])) $fn = $_POST['function']; if (isset($_POST['function'])) $fn = $_POST['function'];
else $fn = $_GET['function']; else $fn = $_GET['function'];
if (isset($_SESSION['login'])&&$_SESSION['login']==true) { if ((isset($_SESSION['login'])&&$_SESSION['login']===true)&&
(isset($_SESSION['identifier'])&&$_SESSION['identifier']===$settings['identifier'])) {
### ###
# Admin Access # Admin Access

View File

@ -4,12 +4,12 @@
CREATE TABLE IF NOT EXISTS `?` ( CREATE TABLE IF NOT EXISTS `?` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(50) NOT NULL, `title` varchar(100) NOT NULL DEFAULT '',
`description` varchar(1000) DEFAULT '', `description` varchar(1000) DEFAULT '',
`sysstamp` int(11) NOT NULL, `sysstamp` int(11) NOT NULL,
`public` tinyint(1) NOT NULL DEFAULT '0', `public` tinyint(1) NOT NULL DEFAULT '0',
`visible` tinyint(1) NOT NULL DEFAULT '1', `visible` tinyint(1) NOT NULL DEFAULT '1',
`downloadable` tinyint(1) NOT NULL DEFAULT '0', `downloadable` tinyint(1) NOT NULL DEFAULT '0',
`password` varchar(100) DEFAULT '', `password` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; ) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

View File

@ -4,7 +4,7 @@
CREATE TABLE IF NOT EXISTS `?` ( CREATE TABLE IF NOT EXISTS `?` (
`id` bigint(14) NOT NULL, `id` bigint(14) NOT NULL,
`title` varchar(50) NOT NULL, `title` varchar(100) NOT NULL,
`description` varchar(1000) DEFAULT '', `description` varchar(1000) DEFAULT '',
`url` varchar(100) NOT NULL, `url` varchar(100) NOT NULL,
`tags` varchar(1000) NOT NULL DEFAULT '', `tags` varchar(1000) NOT NULL DEFAULT '',

View File

@ -9,6 +9,10 @@ VALUES
('password',''), ('password',''),
('thumbQuality','90'), ('thumbQuality','90'),
('checkForUpdates','1'), ('checkForUpdates','1'),
('sorting','ORDER BY id DESC'), ('sortingPhotos','ORDER BY id DESC'),
('sortingAlbums','ORDER BY id DESC'),
('medium','1'),
('imagick','1'),
('dropboxKey',''), ('dropboxKey',''),
('identifier',''),
('plugins',''); ('plugins','');

View File

@ -0,0 +1,70 @@
<?php
###
# @name Update to version 3.0.1
# @copyright 2015 by Tobias Reich
###
if (!defined('LYCHEE')) exit('Error: Direct access is not allowed!');
# Change length of photo title
$query = Database::prepare($database, "ALTER TABLE `?` CHANGE `title` `title` VARCHAR( 100 ) NOT NULL DEFAULT ''", array(LYCHEE_TABLE_PHOTOS));
$result = $database->query($query);
if (!$result) {
Log::error($database, 'update_030001', __LINE__, 'Could not update database (' . $database->error . ')');
return false;
}
# Change length of album title
$query = Database::prepare($database, "ALTER TABLE `?` CHANGE `title` `title` VARCHAR( 100 ) NOT NULL DEFAULT ''", array(LYCHEE_TABLE_ALBUMS));
$result = $database->query($query);
if (!$result) {
Log::error($database, 'update_030001', __LINE__, 'Could not update database (' . $database->error . ')');
return false;
}
# Add album sorting to settings
$query = Database::prepare($database, "SELECT `key` FROM `?` WHERE `key` = 'sortingAlbums' LIMIT 1", array(LYCHEE_TABLE_SETTINGS));
$result = $database->query($query);
if ($result->num_rows===0) {
$query = Database::prepare($database, "INSERT INTO `?` (`key`, `value`) VALUES ('sortingAlbums', 'ORDER BY id DESC')", array(LYCHEE_TABLE_SETTINGS));
$result = $database->query($query);
if (!$result) {
Log::error($database, 'update_030001', __LINE__, 'Could not update database (' . $database->error . ')');
return false;
}
}
# Rename sorting to sortingPhotos
$query = Database::prepare($database, "UPDATE ? SET `key` = 'sortingPhotos' WHERE `key` = 'sorting' LIMIT 1", array(LYCHEE_TABLE_SETTINGS));
$result = $database->query($query);
if (!$result) {
Log::error($database, 'update_030001', __LINE__, 'Could not update database (' . $database->error . ')');
return false;
}
# Add identifier to settings
$query = Database::prepare($database, "SELECT `key` FROM `?` WHERE `key` = 'identifier' LIMIT 1", array(LYCHEE_TABLE_SETTINGS));
$result = $database->query($query);
if ($result->num_rows===0) {
$identifier = md5(microtime(true));
$query = Database::prepare($database, "INSERT INTO `?` (`key`, `value`) VALUES ('identifier', '?')", array(LYCHEE_TABLE_SETTINGS, $identifier));
$result = $database->query($query);
if (!$result) {
Log::error($database, 'update_030001', __LINE__, 'Could not update database (' . $database->error . ')');
return false;
}
} else {
$identifier = md5(microtime(true));
$query = Database::prepare($database, "UPDATE `?` SET `value` = '?' WHERE `key` = 'identifier' LIMIT 1", array(LYCHEE_TABLE_SETTINGS, $identifier));
$result = $database->query($query);
if (!$result) {
Log::error($database, 'update_030001', __LINE__, 'Could not reset public albums (' . $database->error . ')');
return false;
}
}
# Set version
if (Database::setVersion($database, '030001')===false) return false;
?>

View File

@ -94,19 +94,19 @@ class Album extends Module {
switch ($this->albumIDs) { switch ($this->albumIDs) {
case 'f': $return['public'] = '0'; case 'f': $return['public'] = '0';
$query = Database::prepare($this->database, "SELECT id, title, tags, public, star, album, thumbUrl, takestamp, url FROM ? WHERE star = 1 " . $this->settings['sorting'], array(LYCHEE_TABLE_PHOTOS)); $query = Database::prepare($this->database, "SELECT id, title, tags, public, star, album, thumbUrl, takestamp, url FROM ? WHERE star = 1 " . $this->settings['sortingPhotos'], array(LYCHEE_TABLE_PHOTOS));
break; break;
case 's': $return['public'] = '0'; case 's': $return['public'] = '0';
$query = Database::prepare($this->database, "SELECT id, title, tags, public, star, album, thumbUrl, takestamp, url FROM ? WHERE public = 1 " . $this->settings['sorting'], array(LYCHEE_TABLE_PHOTOS)); $query = Database::prepare($this->database, "SELECT id, title, tags, public, star, album, thumbUrl, takestamp, url FROM ? WHERE public = 1 " . $this->settings['sortingPhotos'], array(LYCHEE_TABLE_PHOTOS));
break; break;
case 'r': $return['public'] = '0'; case 'r': $return['public'] = '0';
$query = Database::prepare($this->database, "SELECT id, title, tags, public, star, album, thumbUrl, takestamp, url FROM ? WHERE LEFT(id, 10) >= unix_timestamp(DATE_SUB(NOW(), INTERVAL 1 DAY)) " . $this->settings['sorting'], array(LYCHEE_TABLE_PHOTOS)); $query = Database::prepare($this->database, "SELECT id, title, tags, public, star, album, thumbUrl, takestamp, url FROM ? WHERE LEFT(id, 10) >= unix_timestamp(DATE_SUB(NOW(), INTERVAL 1 DAY)) " . $this->settings['sortingPhotos'], array(LYCHEE_TABLE_PHOTOS));
break; break;
case '0': $return['public'] = '0'; case '0': $return['public'] = '0';
$query = Database::prepare($this->database, "SELECT id, title, tags, public, star, album, thumbUrl, takestamp, url FROM ? WHERE album = 0 " . $this->settings['sorting'], array(LYCHEE_TABLE_PHOTOS)); $query = Database::prepare($this->database, "SELECT id, title, tags, public, star, album, thumbUrl, takestamp, url FROM ? WHERE album = 0 " . $this->settings['sortingPhotos'], array(LYCHEE_TABLE_PHOTOS));
break; break;
default: $query = Database::prepare($this->database, "SELECT * FROM ? WHERE id = '?' LIMIT 1", array(LYCHEE_TABLE_ALBUMS, $this->albumIDs)); default: $query = Database::prepare($this->database, "SELECT * FROM ? WHERE id = '?' LIMIT 1", array(LYCHEE_TABLE_ALBUMS, $this->albumIDs));
@ -114,7 +114,7 @@ class Album extends Module {
$return = $albums->fetch_assoc(); $return = $albums->fetch_assoc();
$return['sysdate'] = date('d M. Y', $return['sysstamp']); $return['sysdate'] = date('d M. Y', $return['sysstamp']);
$return['password'] = ($return['password']=='' ? '0' : '1'); $return['password'] = ($return['password']=='' ? '0' : '1');
$query = Database::prepare($this->database, "SELECT id, title, tags, public, star, album, thumbUrl, takestamp, url FROM ? WHERE album = '?' " . $this->settings['sorting'], array(LYCHEE_TABLE_PHOTOS, $this->albumIDs)); $query = Database::prepare($this->database, "SELECT id, title, tags, public, star, album, thumbUrl, takestamp, url FROM ? WHERE album = '?' " . $this->settings['sortingPhotos'], array(LYCHEE_TABLE_PHOTOS, $this->albumIDs));
break; break;
} }
@ -189,8 +189,8 @@ class Album extends Module {
if ($public===false) $return['smartalbums'] = $this->getSmartInfo(); if ($public===false) $return['smartalbums'] = $this->getSmartInfo();
# Albums query # Albums query
$query = Database::prepare($this->database, 'SELECT id, title, public, sysstamp, password FROM ? WHERE public = 1 AND visible <> 0', array(LYCHEE_TABLE_ALBUMS)); if ($public===false) $query = Database::prepare($this->database, 'SELECT id, title, public, sysstamp, password FROM ? ' . $this->settings['sortingAlbums'], array(LYCHEE_TABLE_ALBUMS));
if ($public===false) $query = Database::prepare($this->database, 'SELECT id, title, public, sysstamp, password FROM ?', array(LYCHEE_TABLE_ALBUMS)); else $query = Database::prepare($this->database, 'SELECT id, title, public, sysstamp, password FROM ? WHERE public = 1 AND visible <> 0 ' . $this->settings['sortingAlbums'], array(LYCHEE_TABLE_ALBUMS));
# Execute query # Execute query
$albums = $this->database->query($query); $albums = $this->database->query($query);
@ -210,7 +210,7 @@ class Album extends Module {
($public===false)) { ($public===false)) {
# Execute query # Execute query
$query = Database::prepare($this->database, "SELECT thumbUrl FROM ? WHERE album = '?' ORDER BY star DESC, " . substr($this->settings['sorting'], 9) . " LIMIT 3", array(LYCHEE_TABLE_PHOTOS, $album['id'])); $query = Database::prepare($this->database, "SELECT thumbUrl FROM ? WHERE album = '?' ORDER BY star DESC, " . substr($this->settings['sortingPhotos'], 9) . " LIMIT 3", array(LYCHEE_TABLE_PHOTOS, $album['id']));
$thumbs = $this->database->query($query); $thumbs = $this->database->query($query);
# For each thumb # For each thumb
@ -223,7 +223,7 @@ class Album extends Module {
} }
# Add to return # Add to return
$return['albums'][$album['id']] = $album; $return['albums'][] = $album;
} }
@ -254,7 +254,7 @@ class Album extends Module {
# Unsorted # Unsorted
### ###
$query = Database::prepare($this->database, 'SELECT thumbUrl FROM ? WHERE album = 0 ' . $this->settings['sorting'], array(LYCHEE_TABLE_PHOTOS)); $query = Database::prepare($this->database, 'SELECT thumbUrl FROM ? WHERE album = 0 ' . $this->settings['sortingPhotos'], array(LYCHEE_TABLE_PHOTOS));
$unsorted = $this->database->query($query); $unsorted = $this->database->query($query);
$i = 0; $i = 0;
@ -274,7 +274,7 @@ class Album extends Module {
# Starred # Starred
### ###
$query = Database::prepare($this->database, 'SELECT thumbUrl FROM ? WHERE star = 1 ' . $this->settings['sorting'], array(LYCHEE_TABLE_PHOTOS)); $query = Database::prepare($this->database, 'SELECT thumbUrl FROM ? WHERE star = 1 ' . $this->settings['sortingPhotos'], array(LYCHEE_TABLE_PHOTOS));
$starred = $this->database->query($query); $starred = $this->database->query($query);
$i = 0; $i = 0;
@ -294,7 +294,7 @@ class Album extends Module {
# Public # Public
### ###
$query = Database::prepare($this->database, 'SELECT thumbUrl FROM ? WHERE public = 1 ' . $this->settings['sorting'], array(LYCHEE_TABLE_PHOTOS)); $query = Database::prepare($this->database, 'SELECT thumbUrl FROM ? WHERE public = 1 ' . $this->settings['sortingPhotos'], array(LYCHEE_TABLE_PHOTOS));
$public = $this->database->query($query); $public = $this->database->query($query);
$i = 0; $i = 0;
@ -314,7 +314,7 @@ class Album extends Module {
# Recent # Recent
### ###
$query = Database::prepare($this->database, 'SELECT thumbUrl FROM ? WHERE LEFT(id, 10) >= unix_timestamp(DATE_SUB(NOW(), INTERVAL 1 DAY)) ' . $this->settings['sorting'], array(LYCHEE_TABLE_PHOTOS)); $query = Database::prepare($this->database, 'SELECT thumbUrl FROM ? WHERE LEFT(id, 10) >= unix_timestamp(DATE_SUB(NOW(), INTERVAL 1 DAY)) ' . $this->settings['sortingPhotos'], array(LYCHEE_TABLE_PHOTOS));
$recent = $this->database->query($query); $recent = $this->database->query($query);
$i = 0; $i = 0;
@ -484,7 +484,7 @@ class Album extends Module {
$this->plugins(__METHOD__, 0, func_get_args()); $this->plugins(__METHOD__, 0, func_get_args());
# Parse # Parse
if (strlen($title)>50) $title = substr($title, 0, 50); if (strlen($title)>100) $title = substr($title, 0, 100);
# Execute query # Execute query
$query = Database::prepare($this->database, "UPDATE ? SET title = '?' WHERE id IN (?)", array(LYCHEE_TABLE_ALBUMS, $title, $this->albumIDs)); $query = Database::prepare($this->database, "UPDATE ? SET title = '?' WHERE id IN (?)", array(LYCHEE_TABLE_ALBUMS, $title, $this->albumIDs));

View File

@ -55,7 +55,8 @@ class Database extends Module {
'020601', #2.6.1 '020601', #2.6.1
'020602', #2.6.2 '020602', #2.6.2
'020700', #2.7.0 '020700', #2.7.0
'030000' #3.0.0 '030000', #3.0.0
'030001' #3.0.1
); );
# For each update # For each update

View File

@ -842,7 +842,7 @@ class Photo extends Module {
$this->plugins(__METHOD__, 0, func_get_args()); $this->plugins(__METHOD__, 0, func_get_args());
# Parse # Parse
if (strlen($title)>50) $title = substr($title, 0, 50); if (strlen($title)>100) $title = substr($title, 0, 100);
# Set title # Set title
$query = Database::prepare($this->database, "UPDATE ? SET title = '?' WHERE id IN (?)", array(LYCHEE_TABLE_PHOTOS, $title, $this->photoIDs)); $query = Database::prepare($this->database, "UPDATE ? SET title = '?' WHERE id IN (?)", array(LYCHEE_TABLE_PHOTOS, $title, $this->photoIDs));

View File

@ -44,6 +44,9 @@ class Session extends Module {
unset($return['config']['username']); unset($return['config']['username']);
unset($return['config']['password']); unset($return['config']['password']);
# Remove identifier from response
unset($return['config']['identifier']);
# Path to Lychee for the server-import dialog # Path to Lychee for the server-import dialog
$return['config']['location'] = LYCHEE; $return['config']['location'] = LYCHEE;
@ -67,10 +70,13 @@ class Session extends Module {
# Unset unused vars # Unset unused vars
unset($return['config']['thumbQuality']); unset($return['config']['thumbQuality']);
unset($return['config']['sorting']); unset($return['config']['sortingAlbums']);
unset($return['config']['sortingPhotos']);
unset($return['config']['dropboxKey']); unset($return['config']['dropboxKey']);
unset($return['config']['login']); unset($return['config']['login']);
unset($return['config']['location']); unset($return['config']['location']);
unset($return['config']['imagick']);
unset($return['config']['medium']);
unset($return['config']['plugins']); unset($return['config']['plugins']);
} }
@ -96,15 +102,13 @@ class Session extends Module {
# Check login with crypted hash # Check login with crypted hash
if ($this->settings['username']===$username&& if ($this->settings['username']===$username&&
$this->settings['password']===$password) { $this->settings['password']===$password) {
$_SESSION['login'] = true; $_SESSION['login'] = true;
$_SESSION['identifier'] = $this->settings['identifier'];
return true; return true;
} }
# No login # No login
if ($this->noLogin()===true) { if ($this->noLogin()===true) return true;
$_SESSION['login'] = true;
return true;
}
# Call plugins # Call plugins
$this->plugins(__METHOD__, 1, func_get_args()); $this->plugins(__METHOD__, 1, func_get_args());
@ -121,7 +125,8 @@ class Session extends Module {
# Check if login credentials exist and login if they don't # Check if login credentials exist and login if they don't
if ($this->settings['username']===''&& if ($this->settings['username']===''&&
$this->settings['password']==='') { $this->settings['password']==='') {
$_SESSION['login'] = true; $_SESSION['login'] = true;
$_SESSION['identifier'] = $this->settings['identifier'];
return true; return true;
} }
@ -134,6 +139,9 @@ class Session extends Module {
# Call plugins # Call plugins
$this->plugins(__METHOD__, 0, func_get_args()); $this->plugins(__METHOD__, 0, func_get_args());
$_SESSION['login'] = null;
$_SESSION['identifier'] = null;
session_destroy(); session_destroy();
# Call plugins # Call plugins

View File

@ -129,7 +129,7 @@ class Settings extends Module {
} }
public function setSorting($type, $order) { public function setSortingPhotos($type, $order) {
# Check dependencies # Check dependencies
self::dependencies(isset($this->database, $type, $order)); self::dependencies(isset($this->database, $type, $order));
@ -182,7 +182,64 @@ class Settings extends Module {
# Execute query # Execute query
# Do not prepare $sorting because it is a true statement # Do not prepare $sorting because it is a true statement
# Preparing (escaping) the sorting would destroy it # Preparing (escaping) the sorting would destroy it
$query = Database::prepare($this->database, "UPDATE ? SET value = '$sorting' WHERE `key` = 'sorting'", array(LYCHEE_TABLE_SETTINGS)); # $sorting is save and can't contain user-input
$query = Database::prepare($this->database, "UPDATE ? SET value = '$sorting' WHERE `key` = 'sortingPhotos'", array(LYCHEE_TABLE_SETTINGS));
$result = $this->database->query($query);
if (!$result) {
Log::error($this->database, __METHOD__, __LINE__, $this->database->error);
return false;
}
return true;
}
public function setSortingAlbums($type, $order) {
# Check dependencies
self::dependencies(isset($this->database, $type, $order));
$sorting = 'ORDER BY ';
# Set row
switch ($type) {
case 'id': $sorting .= 'id';
break;
case 'title': $sorting .= 'title';
break;
case 'description': $sorting .= 'description';
break;
case 'public': $sorting .= 'public';
break;
default: exit('Error: Unknown type for sorting!');
}
$sorting .= ' ';
# Set order
switch ($order) {
case 'ASC': $sorting .= 'ASC';
break;
case 'DESC': $sorting .= 'DESC';
break;
default: exit('Error: Unknown order for sorting!');
}
# Execute query
# Do not prepare $sorting because it is a true statement
# Preparing (escaping) the sorting would destroy it
# $sorting is save and can't contain user-input
$query = Database::prepare($this->database, "UPDATE ? SET value = '$sorting' WHERE `key` = 'sortingAlbums'", array(LYCHEE_TABLE_SETTINGS));
$result = $this->database->query($query); $result = $this->database->query($query);
if (!$result) { if (!$result) {

View File

@ -47,7 +47,7 @@ function search($database, $settings, $term) {
$album = Album::prepareData($album); $album = Album::prepareData($album);
# Thumbs # Thumbs
$query = Database::prepare($database, "SELECT thumbUrl FROM ? WHERE album = '?' " . $settings['sorting'] . " LIMIT 0, 3", array(LYCHEE_TABLE_PHOTOS, $album['id'])); $query = Database::prepare($database, "SELECT thumbUrl FROM ? WHERE album = '?' " . $settings['sortingPhotos'] . " LIMIT 0, 3", array(LYCHEE_TABLE_PHOTOS, $album['id']));
$thumbs = $database->query($query); $thumbs = $database->query($query);
# For each thumb # For each thumb

View File

@ -2,7 +2,7 @@
"name": "Lychee", "name": "Lychee",
"private": true, "private": true,
"dependencies": { "dependencies": {
"jQuery": "~2.1.3", "jQuery": "~2.1.4",
"mousetrap": "~1.5.2", "mousetrap": "~1.5.2",
"basicContext": "~2.0.9", "basicContext": "~2.0.9",
"basicModal": "~2.0.8" "basicModal": "~2.0.8"

View File

@ -1,6 +1,6 @@
{ {
"name": "Lychee", "name": "Lychee",
"version": "3.0.0", "version": "3.0.1",
"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",
@ -18,7 +18,7 @@
"gulp-load-plugins": "^0.10.0", "gulp-load-plugins": "^0.10.0",
"gulp-minify-css": "^1.1.1", "gulp-minify-css": "^1.1.1",
"gulp-rimraf": "^0.1.1", "gulp-rimraf": "^0.1.1",
"gulp-sass": "^1.3.3", "gulp-sass": "^2.0.1",
"gulp-uglify": "^1.2.0" "gulp-uglify": "^1.2.0"
} }
} }

View File

@ -138,7 +138,7 @@ album.add = function() {
} }
basicModal.show({ basicModal.show({
body: "<p>Enter a title for the new album: <input class='text' data-name='title' type='text' maxlength='30' placeholder='Title' value='Untitled'></p>", body: "<p>Enter a title for the new album: <input class='text' data-name='title' type='text' maxlength='50' placeholder='Title' value='Untitled'></p>",
buttons: { buttons: {
action: { action: {
title: 'Create Album', title: 'Create Album',
@ -180,7 +180,7 @@ album.delete = function(albumIDs) {
albumIDs.forEach(function(id) { albumIDs.forEach(function(id) {
albums.json.num--; albums.json.num--;
view.albums.content.delete(id); view.albums.content.delete(id);
delete albums.json.albums[id]; albums.deleteByID(id);
}); });
} else { } else {
@ -210,7 +210,7 @@ album.delete = function(albumIDs) {
// Get title // Get title
if (album.json) albumTitle = album.json.title; if (album.json) albumTitle = album.json.title;
else if (albums.json) albumTitle = albums.json.albums[albumIDs].title; else if (albums.json) albumTitle = albums.getByID(albumIDs).title;
msg = "<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 = "<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>";
@ -254,7 +254,7 @@ album.setTitle = function(albumIDs) {
// 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) oldTitle = album.json.title;
else if (albums.json) oldTitle = albums.json.albums[albumIDs].title; else if (albums.json) oldTitle = albums.getByID(albumIDs).title;
if (!oldTitle) oldTitle = ''; if (!oldTitle) oldTitle = '';
oldTitle = oldTitle.replace(/'/g, '&apos;'); oldTitle = oldTitle.replace(/'/g, '&apos;');
@ -281,13 +281,13 @@ album.setTitle = function(albumIDs) {
if (albums.json) { if (albums.json) {
var id = albumIDs[0]; var id = albumIDs[0];
albums.json.albums[id].title = newTitle; albums.getByID(id).title = newTitle;
} }
} else if (visible.albums()) { } else if (visible.albums()) {
albumIDs.forEach(function(id) { albumIDs.forEach(function(id) {
albums.json.albums[id].title = newTitle; albums.getByID(id).title = newTitle;
view.albums.content.title(id); view.albums.content.title(id);
}); });
@ -306,7 +306,7 @@ album.setTitle = function(albumIDs) {
} }
input = "<input class='text' data-name='title' type='text' maxlength='30' placeholder='Title' value='" + oldTitle + "'>"; input = "<input class='text' data-name='title' type='text' maxlength='50' placeholder='Title' value='" + oldTitle + "'>";
if (albumIDs.length===1) msg = "<p>Enter a new title for this album: " + input + "</p>"; if (albumIDs.length===1) msg = "<p>Enter a new title for this album: " + input + "</p>";
else msg = "<p>Enter a title for all " + albumIDs.length + " selected albums: " + input +"</p>"; else msg = "<p>Enter a title for all " + albumIDs.length + " selected albums: " + input +"</p>";
@ -580,7 +580,7 @@ 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.json.albums[albumIDs[0]].title; if (albums.json) title = albums.getByID(albumIDs[0]).title;
if (!title) title = ''; if (!title) title = '';
title = title.replace(/'/g, '&apos;'); title = title.replace(/'/g, '&apos;');
@ -588,7 +588,7 @@ 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.json.albums[albumIDs[1]].title; if (albums.json) sTitle = albums.getByID(albumIDs[1]).title;
if (!sTitle) sTitle = ''; if (!sTitle) sTitle = '';
sTitle = sTitle.replace(/'/g, '&apos;'); sTitle = sTitle.replace(/'/g, '&apos;');

View File

@ -105,6 +105,52 @@ albums._createSmartAlbums = function(data) {
} }
albums.getByID = function(albumID) {
// Function returns the JSON of an album
if (albumID===undefined||albumID===null) return undefined;
if (!albums.json) return undefined;
if (!albums.json.albums) return undefined;
var json = undefined;
$.each(albums.json.albums, function(i) {
let elem = albums.json.albums[i];
if (elem.id==albumID) json = elem;
});
return json;
}
albums.deleteByID = function(albumID) {
// Function returns the JSON of an album
if (albumID===undefined||albumID===null) return false;
if (!albums.json) return false;
if (!albums.json.albums) return false;
var deleted = false;
$.each(albums.json.albums, function(i) {
if (albums.json.albums[i].id==albumID) {
albums.json.albums.splice(i, 1);
deleted = true;
return false;
}
});
return deleted;
}
albums.refresh = function() { albums.refresh = function() {
albums.json = null; albums.json = null;

View File

@ -59,16 +59,7 @@ build.album = function(data) {
if (data===null||data===undefined) return ''; if (data===null||data===undefined) return '';
var html = '', var html = '';
title = data.title,
longTitle = '';
if (title!==null&&title.length>18) {
title = data.title.substr(0, 18) + '...';
longTitle = data.title;
}
var {path: thumbPath, hasRetina: thumbRetina} = lychee.retinize(data.thumbs[0]); var {path: thumbPath, hasRetina: thumbRetina} = lychee.retinize(data.thumbs[0]);
@ -78,7 +69,7 @@ build.album = function(data) {
<img src='${ data.thumbs[1] }' width='200' height='200' alt='thumb' data-retina='false'> <img src='${ data.thumbs[1] }' width='200' height='200' alt='thumb' data-retina='false'>
<img src='${ thumbPath }' width='200' height='200' alt='thumb' data-retina='${ thumbRetina }'> <img src='${ thumbPath }' width='200' height='200' alt='thumb' data-retina='${ thumbRetina }'>
<div class='overlay'> <div class='overlay'>
<h1 title='${ longTitle }'>${ title }</h1> <h1 title='${ data.title }'>${ data.title }</h1>
<a>${ data.sysdate }</a> <a>${ data.sysdate }</a>
</div> </div>
` `
@ -103,16 +94,7 @@ build.photo = function(data) {
if (data===null||data===undefined) return ''; if (data===null||data===undefined) return '';
var html = '', var html = '';
title = data.title,
longTitle = '';
if (title!==null&&title.length>18) {
title = data.title.substr(0, 18) + '...';
longTitle = data.title;
}
var {path: thumbPath, hasRetina: thumbRetina} = lychee.retinize(data.thumbUrl); var {path: thumbPath, hasRetina: thumbRetina} = lychee.retinize(data.thumbUrl);
@ -120,7 +102,7 @@ build.photo = function(data) {
<div class='photo' data-album-id='${ data.album }' data-id='${ data.id }'> <div class='photo' data-album-id='${ data.album }' data-id='${ data.id }'>
<img src='${ thumbPath }' width='200' height='200' alt='thumb'> <img src='${ thumbPath }' width='200' height='200' alt='thumb'>
<div class='overlay'> <div class='overlay'>
<h1 title='${ longTitle }'>${ title }</h1> <h1 title='${ data.title }'>${ data.title }</h1>
` `
if (data.cameraDate==='1') html += `<a><span title='Camera Date'>${ build.iconic('camera-slr') }</span>${ data.sysdate }</a>`; if (data.cameraDate==='1') html += `<a><span title='Camera Date'>${ build.iconic('camera-slr') }</span>${ data.sysdate }</a>`;

View File

@ -84,7 +84,7 @@ contextMenu.albumTitle = function(albumID, e) {
api.post('Album::getAll', {}, function(data) { api.post('Album::getAll', {}, function(data) {
if (data.num>1) { if (data.albums&&data.num>1) {
// Generate list of albums // Generate list of albums
$.each(data.albums, function(index) { $.each(data.albums, function(index) {
@ -96,7 +96,7 @@ contextMenu.albumTitle = function(albumID, e) {
title = "<img class='cover' width='16' height='16' src='" + that.thumbs[0] + "'><div class='title'>" + that.title + "</div>"; title = "<img class='cover' width='16' height='16' src='" + that.thumbs[0] + "'><div class='title'>" + that.title + "</div>";
if (that.id!=albumID) items.unshift({ type: 'item', title, fn: function() { lychee.goto(that.id) } }); if (that.id!=albumID) items.push({ type: 'item', title, fn: function() { lychee.goto(that.id) } });
}); });
@ -114,28 +114,30 @@ contextMenu.albumTitle = function(albumID, e) {
contextMenu.mergeAlbum = function(albumID, e) { contextMenu.mergeAlbum = function(albumID, e) {
var items = []; var items = [];
api.post('Album::getAll', {}, function(data) { api.post('Album::getAll', {}, function(data) {
$.each(data.albums, function(){ if (data.albums&&data.num>1) {
var that = this; $.each(data.albums, function(){
if (!that.thumbs[0]) that.thumbs[0] = 'src/images/no_cover.svg'; var that = this;
that.contextTitle = "<img class='cover' width='16' height='16' src='" + that.thumbs[0] + "'><div class='title'>" + that.title + "</div>";
if (that.id!=album.getID()) { if (!that.thumbs[0]) that.thumbs[0] = 'src/images/no_cover.svg';
items.unshift({ type: 'item', title: that.contextTitle, fn: function() { album.merge([albumID, that.id]) } }); that.contextTitle = "<img class='cover' width='16' height='16' src='" + that.thumbs[0] + "'><div class='title'>" + that.title + "</div>";
}
}); if (that.id!=albumID) items.push({ type: 'item', title: that.contextTitle, fn: function() { album.merge([albumID, that.id]) } });
if (items.length===0) return false; });
basicContext.show(items, e, contextMenu.close); }
}) if (items.length===0) return false;
basicContext.show(items, e, contextMenu.close);
});
} }
@ -191,7 +193,7 @@ contextMenu.photoTitle = function(albumID, photoID, e) {
var data = album.json; var data = album.json;
if (data.num>1) { if (data.content!==false&&data.num>1) {
items.push({ type: 'separator' }); items.push({ type: 'separator' });
@ -253,7 +255,7 @@ contextMenu.move = function(photoIDs, e) {
if (!that.thumbs[0]) that.thumbs[0] = 'src/images/no_cover.svg'; if (!that.thumbs[0]) that.thumbs[0] = 'src/images/no_cover.svg';
that.title = "<img class='cover' width='16' height='16' src='" + that.thumbs[0] + "'><div class='title'>" + that.title + "</div>"; that.title = "<img class='cover' width='16' height='16' src='" + that.thumbs[0] + "'><div class='title'>" + that.title + "</div>";
if (that.id!=album.getID()) items.unshift({ type: 'item', title: that.title, fn: function() { photo.setAlbum(photoIDs, that.id) } }); if (that.id!=album.getID()) items.push({ type: 'item', title: that.title, fn: function() { photo.setAlbum(photoIDs, that.id) } });
}); });

View File

@ -85,7 +85,7 @@ loadingBar.hide = function(force) {
loadingBar.status = null; loadingBar.status = null;
// Move header up // Move header up
if (visible.header()) header.dom().removeClass('error loading'); header.dom().removeClass('error loading');
// Set timeout // Set timeout
clearTimeout(loadingBar._timeout); clearTimeout(loadingBar._timeout);

View File

@ -6,8 +6,8 @@
lychee = { lychee = {
title: document.title, title: document.title,
version: '3.0.0', version: '3.0.1',
version_code: '030000', version_code: '030001',
update_path: 'http://lychee.electerious.com/version/index.php', update_path: 'http://lychee.electerious.com/version/index.php',
updateURL: 'https://github.com/electerious/Lychee', updateURL: 'https://github.com/electerious/Lychee',
@ -18,7 +18,8 @@ lychee = {
debugMode: false, debugMode: false,
checkForUpdates:'1', checkForUpdates:'1',
sorting: '', sortingPhotos: '',
sortingAlbums: '',
location: '', location: '',
dropbox: false, dropbox: false,
@ -48,7 +49,8 @@ lychee.init = function() {
// Logged in // Logged in
lychee.sorting = data.config.sorting || ''; lychee.sortingPhotos = data.config.sortingPhotos || '';
lychee.sortingAlbums = data.config.sortingAlbums || '';
lychee.dropboxKey = data.config.dropboxKey || ''; lychee.dropboxKey = data.config.dropboxKey || '';
lychee.location = data.config.location || ''; lychee.location = data.config.location || '';
lychee.checkForUpdates = data.config.checkForUpdates || '1'; lychee.checkForUpdates = data.config.checkForUpdates || '1';
@ -382,10 +384,13 @@ lychee.loadDropbox = function(callback) {
} }
lychee.removeHTML = function(html) { lychee.removeHTML = function(html = '') {
if (html==='') return html;
var tmp = document.createElement('DIV'); var tmp = document.createElement('DIV');
tmp.innerHTML = html; tmp.innerHTML = html;
return tmp.textContent || tmp.innerText; return tmp.textContent || tmp.innerText;
} }

View File

@ -14,9 +14,9 @@ password.get = function(albumID, callback) {
var passwd = $('.basicModal input.text').val(), var passwd = $('.basicModal input.text').val(),
params; params;
if (lychee.publicMode===false) callback(); if (lychee.publicMode===false) callback();
else if (album.json&&album.json.password==='0') callback(); else if (album.json&&album.json.password==='0') callback();
else if (albums.json&&albums.json.albums[albumID].password==='0') callback(); else if (albums.json&&albums.getByID(albumID).password==='0') callback();
else if (!albums.json&&!album.json) { else if (!albums.json&&!album.json) {
// Continue without password // Continue without password

View File

@ -223,7 +223,7 @@ photo.delete = function(photoIDs) {
} }
album.json.content[id] = null; delete album.json.content[id];
view.album.content.delete(id); view.album.content.delete(id);
}); });
@ -330,7 +330,7 @@ photo.setTitle = function(photoIDs) {
} }
input = "<input class='text' data-name='title' type='text' maxlength='30' placeholder='Title' value='" + oldTitle + "'>"; input = "<input class='text' data-name='title' type='text' maxlength='50' placeholder='Title' value='" + oldTitle + "'>";
if (photoIDs.length===1) msg = "<p>Enter a new title for this photo: " + input + "</p>"; if (photoIDs.length===1) msg = "<p>Enter a new title for this photo: " + input + "</p>";
else msg = "<p>Enter a title for all " + photoIDs.length + " selected photos: " + input + "</p>"; else msg = "<p>Enter a title for all " + photoIDs.length + " selected photos: " + input + "</p>";

View File

@ -281,7 +281,8 @@ settings.setLogin = function() {
settings.setSorting = function() { settings.setSorting = function() {
var sorting = [], var sortingPhotos = [],
sortingAlbums = [],
action, action,
msg = ''; msg = '';
@ -289,21 +290,27 @@ settings.setSorting = function() {
var params; var params;
sorting[0] = $('.basicModal select#settings_type').val(); sortingAlbums[0] = $('.basicModal select#settings_albums_type').val();
sorting[1] = $('.basicModal select#settings_order').val(); sortingAlbums[1] = $('.basicModal select#settings_albums_order').val();
sortingPhotos[0] = $('.basicModal select#settings_photos_type').val();
sortingPhotos[1] = $('.basicModal select#settings_photos_order').val();
basicModal.close(); basicModal.close();
albums.refresh(); albums.refresh();
params = { params = {
type: sorting[0], typeAlbums: sortingAlbums[0],
order: sorting[1] orderAlbums: sortingAlbums[1],
typePhotos: sortingPhotos[0],
orderPhotos: sortingPhotos[1]
} }
api.post('Settings::setSorting', params, function(data) { api.post('Settings::setSorting', params, function(data) {
if (data===true) { if (data===true) {
lychee.sorting = 'ORDER BY ' + sorting[0] + ' ' + sorting[1]; lychee.sortingAlbums = 'ORDER BY ' + sortingAlbums[0] + ' ' + sortingAlbums[1];
lychee.sortingPhotos = 'ORDER BY ' + sortingPhotos[0] + ' ' + sortingPhotos[1];
lychee.load(); lychee.load();
} else lychee.error(null, params, data); } else lychee.error(null, params, data);
@ -313,21 +320,44 @@ settings.setSorting = function() {
msg = ` msg = `
<p> <p>
Sort photos by Sort albums by
<select id='settings_type'> <span class="select">
<option value='id'>Upload Time</option> <select id='settings_albums_type'>
<option value='takestamp'>Take Date</option> <option value='id'>Creation Time</option>
<option value='title'>Title</option> <option value='title'>Title</option>
<option value='description'>Description</option> <option value='description'>Description</option>
<option value='public'>Public</option> <option value='public'>Public</option>
<option value='star'>Star</option> </select>
<option value='type'>Photo Format</option> </span>
</select>
in an in an
<select id='settings_order'> <span class="select">
<option value='ASC'>Ascending</option> <select id='settings_albums_order'>
<option value='DESC'>Descending</option> <option value='ASC'>Ascending</option>
</select> <option value='DESC'>Descending</option>
</select>
</span>
order.
</p>
<p>
Sort photos by
<span class="select">
<select id='settings_photos_type'>
<option value='id'>Upload Time</option>
<option value='takestamp'>Take Date</option>
<option value='title'>Title</option>
<option value='description'>Description</option>
<option value='public'>Public</option>
<option value='star'>Star</option>
<option value='type'>Photo Format</option>
</select>
</span>
in an
<span class="select">
<select id='settings_photos_order'>
<option value='ASC'>Ascending</option>
<option value='DESC'>Descending</option>
</select>
</span>
order. order.
</p> </p>
` `
@ -346,12 +376,21 @@ settings.setSorting = function() {
} }
}); });
if (lychee.sorting!=='') { if (lychee.sortingAlbums!=='') {
sorting = lychee.sorting.replace('ORDER BY ', '').split(' '); sortingAlbums = lychee.sortingAlbums.replace('ORDER BY ', '').split(' ');
$('.basicModal select#settings_type').val(sorting[0]); $('.basicModal select#settings_albums_type').val(sortingAlbums[0]);
$('.basicModal select#settings_order').val(sorting[1]); $('.basicModal select#settings_albums_order').val(sortingAlbums[1]);
}
if (lychee.sortingPhotos!=='') {
sortingPhotos = lychee.sortingPhotos.replace('ORDER BY ', '').split(' ');
$('.basicModal select#settings_photos_type').val(sortingPhotos[0]);
$('.basicModal select#settings_photos_order').val(sortingPhotos[1]);
} }

View File

@ -46,9 +46,7 @@ view.albums = {
$.each(albums.json.albums, function() { $.each(albums.json.albums, function() {
albums.parse(this); albums.parse(this);
albumsData += build.album(this);
// Display albums in reverse order
albumsData = build.album(this) + albumsData;
}); });
// Add divider // Add divider
@ -73,17 +71,11 @@ view.albums = {
title: function(albumID) { title: function(albumID) {
var longTitle = '', var title = albums.getByID(albumID).title;
title = albums.json.albums[albumID].title;
if (title!==null&&title.length>18) {
longTitle = title;
title = title.substr(0, 18) + '...';
}
$('.album[data-id="' + albumID + '"] .overlay h1') $('.album[data-id="' + albumID + '"] .overlay h1')
.html(title) .html(title)
.attr('title', longTitle); .attr('title', title);
}, },
@ -94,7 +86,7 @@ view.albums = {
marginLeft: 0 marginLeft: 0
}, 300, function() { }, 300, function() {
$(this).remove(); $(this).remove();
if (albums.json.num<=0) lychee.animate('#content .divider:last-of-type', 'fadeOut'); if (albums.json.num<=0) lychee.content.find('.divider:last-child').remove();
}); });
} }
@ -155,27 +147,27 @@ view.album = {
view.albums.content.scrollPosition = $(document).scrollTop(); view.albums.content.scrollPosition = $(document).scrollTop();
$('html, body').scrollTop(0); $('html, body').scrollTop(0);
$.each(album.json.content, function() { if (album.json.content&&album.json.content!==false) {
photosData += build.photo(this);
});
// Build photos
$.each(album.json.content, function() {
photosData += build.photo(this);
});
}
// Add photos to view
lychee.content.html(photosData); lychee.content.html(photosData);
}, },
title: function(photoID) { title: function(photoID) {
var longTitle = '', var title = album.json.content[photoID].title;
title = album.json.content[photoID].title;
if (title!==null&&title.length>18) {
longTitle = title;
title = title.substr(0, 18) + '...';
}
$('.photo[data-id="' + photoID + '"] .overlay h1') $('.photo[data-id="' + photoID + '"] .overlay h1')
.html(title) .html(title)
.attr('title', longTitle); .attr('title', title);
}, },

View File

@ -125,13 +125,15 @@
.album .overlay h1, .album .overlay h1,
.photo .overlay h1 { .photo .overlay h1 {
min-height: 19px; min-height: 19px;
width: 185px; width: 180px;
margin: 12px 0 5px 15px; margin: 12px 0 5px 15px;
color: #fff; color: #fff;
text-shadow: 0 1px 3px black(.4); text-shadow: 0 1px 3px black(.4);
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
overflow: hidden; overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
} }
.album .overlay a, .album .overlay a,
@ -218,13 +220,13 @@
.iconic { .iconic {
fill: white(.3); fill: white(.3);
margin: 0 0 10px; margin: 0 0 10px;
width: 60px; width: 50px;
height: 60px; height: 50px;
filter: drop-shadow(0 -1px 0 black(.4)); filter: drop-shadow(0 -1px 0 black(.4));
} }
p { p {
font-size: 18px; font-size: 16px;
font-weight: bold; font-weight: bold;
} }
} }

View File

@ -38,6 +38,9 @@ header {
text-align: center; text-align: center;
z-index: 1; z-index: 1;
cursor: default; cursor: default;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
.iconic { .iconic {
display: none; display: none;

View File

@ -172,6 +172,65 @@
} }
} }
/* Select ------------------------------------------------*/
.select {
display: inline-block;
position: relative;
margin: 1px 5px;
padding: 0;
width: 110px;
background: black(.3);
color: #fff;
border-radius: 3px;
border: 1px solid black(.2);
box-shadow: 0 1px 0 white(.02);
font-size: 11px;
line-height: 16px;
overflow: hidden;
outline: 0;
vertical-align: middle;
&::after {
position: absolute;
content: '';
right: 8px;
top: 4px;
color: $colorBlue;
font-size: 16px;
line-height: 16px;
font-weight: bold;
pointer-events: none;
}
select {
margin: 0;
padding: 4px 8px;
width: 120%;
color: #fff;
font-size: 11px;
line-height: 16px;
border: 0;
outline: 0;
box-shadow: none;
border-radius: 0;
background-color: transparent;
background-image: none;
-moz-appearance: none;
-webkit-appearance: none;
appearance: none;
&:focus { outline: none; }
}
select option {
margin: 0;
padding: 0;
background: #fff;
color: #333;
transition: none;
}
}
/* Version ------------------------------------------------*/ /* Version ------------------------------------------------*/
.version { .version {
margin: -5px 0 0; margin: -5px 0 0;