Compare commits

..

33 Commits

Author SHA1 Message Date
7d8eb8cea1
use latest alpine image
All checks were successful
continuous-integration/drone/push Build is passing
2019-09-29 22:27:15 +02:00
5b0af8a081
update readme [skip ci] 2019-05-05 22:36:58 +02:00
1c58dda774
migrate to drone 1.1
All checks were successful
continuous-integration/drone/push Build is passing
2019-04-29 09:10:12 +02:00
ecbd9281dc Merge branch 'master' of https://github.com/electerious/Lychee 2018-12-27 11:03:53 +01:00
Tobias Reich
a0c2254511 Removed demo 2018-12-19 09:26:37 +01:00
c4a27ae7f1
Dockerfile fixes
All checks were successful
the build was successful
2018-07-14 11:32:03 +02:00
c23dbc327e
add Dockerfile and .drone.yml
Some checks failed
the build failed
2018-07-14 11:25:37 +02:00
Fly Man
2a8b1f9ec2
Update photos_table.sql
Fixing a DEFAULT that wasn't set
2018-06-13 01:36:31 +02:00
Tobias Reich
b2997c37da
Fix screenshots 2018-06-08 17:35:59 +02:00
Fly Man
9efb4ab038
Merge pull request #771 from MicroWorldwide/master
Merging multiple PR's into master
2018-04-25 13:57:15 +02:00
Fly Man
4680f59483
Update README.md
Updating the README with the relocation text for the LycheeOrg organisation
2018-04-22 22:33:29 +02:00
Fly Man
8f44348542
Merge pull request #774 from RobLui/master
Spelling corrections
2018-03-11 23:34:09 +01:00
Robbert
9cbac99e5f
Removed ',' x2
As requested
2018-03-11 23:22:55 +01:00
Robbert
6de5846ce2
Spelling corrections
Corrections
2018-03-11 00:41:33 +01:00
Fly Man
a9676b0169
Create CODE_OF_CONDUCT.md 2018-02-25 20:42:22 +01:00
Fly Man
20a1ff2d61
Merge pull request #754 from electerious/develop
* Drag and Drop Sorting using Jquery-ui

* Updating database tables

* version 3.1.7
2018-02-24 01:41:07 +01:00
Fly Man
ab0f86f5fb
Merge pull request #693 from electerious/develop
* Show captured time in info panel
2018-02-24 01:07:32 +01:00
Fly Man
3c07cadb17
Merge pull request #558 from electerious/develop
* Allow photo titles up to 100 characters
2018-02-24 00:52:41 +01:00
Fly Man
878ecd0e32
Merge pull request #766 from electerious/develop
* Change the infamous `chmod 777` for something more sensible
2018-02-24 00:40:13 +01:00
Fly Man
f53ad85ca1
Merge pull request #761 from electerious/develop
* Some EXIF data are in the IFD0 section
2018-02-24 00:36:19 +01:00
Tobias Reich
8b2c07861d Merge pull request #748 from electerious/develop
Removed flattr
2017-10-20 20:59:18 +02:00
Tobias Reich
95e3f25522 Removed flattr 2017-10-20 20:51:03 +02:00
Tobias Reich
27f207dcba Merge pull request #689 from jimmcslim/patch-1
Updated README.md to fix link
2017-04-03 17:00:05 +02:00
James Webster
fe1a9edd38 Updated README.md to fix link
Keyboard Shortcuts link was broken, added HTML entity for space to ensure renders correctly in GitHub Markdown renderer.
2017-04-03 10:32:56 +10:00
Tobias Reich
63cba554cb Merge pull request #687 from electerious/develop
Lychee 3.1.6
2017-03-20 09:44:53 +01:00
Tobias Reich
34d2c740fd Updated version 2017-03-20 09:43:05 +01:00
Tobias Reich
dfd18977e7 Updated changelog 2017-03-20 09:42:40 +01:00
Tobias Reich
bd71bd4352 Updated deps 2017-03-20 09:33:08 +01:00
Tobias Reich
676e4109ef Updated copyright 2017-01-22 17:57:37 +01:00
Tobias Reich
767d97780b Fixed "Downloading a SmartAlbum results in crash" #652 2016-11-24 21:01:04 +01:00
Tobias Reich
d050935981 htaccess IfModule for PHP7 #653 2016-11-24 20:45:04 +01:00
Tobias Reich
3eaaed7271 Merge pull request #637 from electerious/develop
Lychee 3.1.5
2016-10-25 18:22:38 +02:00
Tobias Reich
c0de7b8c63 Merge pull request #610 from electerious/develop
Lychee 3.1.4
2016-08-28 13:14:18 +02:00
42 changed files with 712 additions and 853 deletions

47
.drone.yml Normal file
View File

@ -0,0 +1,47 @@
---
kind: pipeline
name: default
platform:
os: linux
arch: amd64
steps:
- name: publish
pull: default
image: plugins/docker:18.09
settings:
registry: https://registry.nixaid.com
repo: "registry.nixaid.com/${DRONE_REPO_NAMESPACE}/${DRONE_REPO_NAME}"
tags:
- latest
username:
from_secret: docker_username
password:
from_secret: docker_password
# storage_path: /drone/docker
# storage_driver: aufs
# ipv6: false
# debug: true
when:
branch:
- master
event:
- push
- tag
- name: notify
pull: default
image: drillster/drone-email:latest
settings:
from: "Drone CI <noreply@nixaid.com>"
host: mx.nixaid.com
port: 587
subject: "NIXAID Drone Pipeline {{#success build.status}}SUCCESS{{else}}FAILURE{{/success}} Notification"
when:
event:
- push
- tag
status:
- success
- failure

View File

@ -10,6 +10,13 @@ Options -Indexes
# php_value upload_max_filesize 20M
# php_value max_file_uploads 100
#</IfModule>
#<IfModule mod_php7.c>
# php_value max_execution_time 200
# php_value post_max_size 200M
# php_value upload_max_size 200M
# php_value upload_max_filesize 20M
# php_value max_file_uploads 100
#</IfModule>
# ---
# Uncomment these lines when you want to allow access to the Lychee API from different origins

46
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at lychee@electerious.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

58
Dockerfile Normal file
View File

@ -0,0 +1,58 @@
FROM alpine
ARG LYCHEE_VERSION=3.1.6
ENV UID=991 GID=991
RUN BUILD_DEPS=" \
imagemagick-dev \
tar \
libressl \
ca-certificates \
build-base \
autoconf \
pcre-dev \
libtool" \
&& apk -U upgrade && apk add \
${BUILD_DEPS} \
nginx \
php7-mbstring \
php7-fpm \
php7-exif \
php7-gd \
php7-json \
php7-mysqli \
php7-zip \
php7-session \
php7-pear \
php7-dev \
s6 \
su-exec \
imagemagick \
&& pecl install imagick \
&& echo "extension=imagick.so" > /etc/php7/conf.d/imagick.ini \
&& sed -i -e "s/max_execution_time = 30/max_execution_time = 200/g" \
-e "s/post_max_size = 8M/post_max_size = 100M/g" \
-e "s/upload_max_filesize = 2M/upload_max_filesize = 20M/g" \
-e "s/memory_limit = 256M/memory_limit = 512M/g" \
/etc/php7/php.ini \
&& mkdir /lychee \
&& apk del ${BUILD_DEPS} php7-pear php7-dev \
&& rm -rf /var/cache/apk/* /tmp/*
COPY . /lychee/
RUN mv /lychee/docker/nginx.conf /etc/nginx/nginx.conf && \
mv /lychee/docker/php-fpm.conf /etc/php7/php-fpm.conf && \
mv /lychee/docker/s6.d /etc/ && \
mv /lychee/docker/run.sh /usr/local/bin/run.sh
RUN chmod +x /usr/local/bin/run.sh /etc/s6.d/*/* /etc/s6.d/.s6-svscan/*
VOLUME /lychee/uploads /lychee/data
EXPOSE 8888
LABEL maintainer="Andrey Arapov <andrey.arapov@nixaid.com>"
CMD ["run.sh"]

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2016 Tobias Reich (http://electerious.com/)
Copyright (c) 2017 Tobias Reich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,11 +1,15 @@
[![Build Status](https://drone.nixaid.com/api/badges/arno/lychee/status.svg)](https://drone.nixaid.com/arno/lychee)
# Lychee
#### A great looking and easy-to-use photo-management-system.
![Lychee](http://l.electerious.com/uploads/big/c4b58cb87d95aeaed78fdca581cc908c.jpg)
![Lychee](http://l.electerious.com/uploads/big/075ac5de5b5d6c593acbb700f0e1d739.jpg)
*Since the 1st of April 2018 this project has moved to it's own Organisation (https://github.com/LycheeOrg) where people are able to submit their fixes to it. We, the Organisation owners, want to thank electerious (Tobias Reich) for the opportunity to make this project live on.*
Lychee is a free photo-management tool, which runs on your server or web-space. Installing is a matter of seconds. Upload, manage and share photos like from a native application. Lychee comes with everything you need and all your photos are stored securely. Try the [Live Demo](http://ld.electerious.com) or read more on our [Website](http://lychee.electerious.com).
![Lychee](https://s.electerious.com/images/lychee/1.jpeg)
![Lychee](https://s.electerious.com/images/lychee/2.jpeg)
Lychee is a free photo-management tool, which runs on your server or web-space. Installing is a matter of seconds. Upload, manage and share photos like from a native application. Lychee comes with everything you need and all your photos are stored securely. Read more on our [website](http://lychee.electerious.com).
## Installation
@ -17,7 +21,7 @@ You can use Lychee right after the installation. Here are some advanced features
### Settings
Sign in and click the gear on the top left corner to change your settings. If you want to edit them manually: MySQL details are stored in `data/config.php`. Other options and hidden settings are stored directly in the database. [Settings &#187;](docs/Settings.md)
Sign in and click the gear in the top left corner to change your settings. If you want to edit them manually: MySQL details are stored in `data/config.php`. Other options and hidden settings are stored directly in the database. [Settings &#187;](docs/Settings.md)
### Update
@ -29,7 +33,7 @@ Lychee is ready to use, right out of the box. If you want to contribute and edit
### Keyboard Shortcuts
These shortcuts will help you to use Lychee even faster. [Keyboard Shortcuts &#187;](docs/Keyboard Shortcuts.md)
These shortcuts will help you to use Lychee even faster. [Keyboard Shortcuts &#187;](docs/Keyboard%20Shortcuts.md)
### Dropbox import
@ -45,13 +49,13 @@ Lychee uses [Imagick](http://www.imagemagick.org) when installed on your server.
### Docker
Browse the [Docker Hub Registry](https://hub.docker.com/r/kdelfour/lychee-docker/) for various automated Lychee-Docker builds. We recommed to use [lychee-docker](https://hub.docker.com/r/kdelfour/lychee-docker/) by [kdelfour](https://github.com/kdelfour).
Browse the [Docker Hub Registry](https://hub.docker.com/r/kdelfour/lychee-docker/) for various automated Lychee-Docker builds. We recommend using [lychee-docker](https://hub.docker.com/r/kdelfour/lychee-docker/) by [kdelfour](https://github.com/kdelfour).
### Plugins and Extensions
The plugin-system of Lychee allows you to execute scripts, when a certain action fires. Plugins are hooks, which are injected directly into Lychee. [Plugin documentation &#187;](docs/Plugins.md)
The plugin-system of Lychee allows you to execute scripts when a certain action fires. Plugins are hooks, which are injected directly into Lychee. [Plugin documentation &#187;](docs/Plugins.md)
It's also possible to build extensions upon Lychee. The way to do so isn't documented and can change every time. We recommend to use the plugin-system, when possible.
It's also possible to build extensions upon Lychee. The way to do so isn't documented and can change every time. We recommend using the plugin-system, when possible.
Here's a list of all available Plugins and Extensions:
@ -70,7 +74,3 @@ Here's a list of all available Plugins and Extensions:
## Troubleshooting
Take a look at the [FAQ](docs/FAQ.md) if you have problems. Discovered a bug? Please create an issue here on GitHub!
## Donate
I am working hard on continuously developing and maintaining Lychee. Please consider making a donation via [Flattr](https://flattr.com/submit/auto?user_id=electerious&url=http%3A%2F%2Flychee.electerious.com&title=Lychee&category=software) or PayPal (from [our site](http://lychee.electerious.com/)) to keep the project going strong and me motivated.

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.

26
docker/README.md Normal file
View File

@ -0,0 +1,26 @@
## wonderfall/lychee
![](http://lychee.electerious.com/assets/images/showcase.jpg)
#### What is this?
Lychee is a free photo-management tool, which runs on your server or web-space. Installing is a matter of seconds. Upload, manage and share photos like from a native application. Lychee comes with everything you need and all your photos are stored securely.
#### Features
- Based on Alpine Linux.
- nginx + PHP7.
- Active Imagemagick + pecl ext.
#### Build-time variables
- **LYCHEE_VERSION** : version of Lychee.
- **IMAGICK_EXT_VERSION** : version of imagick pecl extension.
#### Environment variables
- **UID** : lychee user id *(default : 991)*.
- **GID** : lychee group id *(default : 991)*.
#### Volumes
- **/lychee/uploads** : uploads.
- **/lychee/data** : data files.
#### Ports
- **8888** [(reverse proxy!)](https://github.com/hardware/mailserver/wiki/Reverse-proxy-configuration)

88
docker/nginx.conf Normal file
View File

@ -0,0 +1,88 @@
worker_processes auto;
pid /tmp/nginx.pid;
daemon off;
events {
worker_connections 1024;
use epoll;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log off;
error_log /tmp/ngx_error.log error;
sendfile on;
keepalive_timeout 15;
keepalive_disable msie6;
keepalive_requests 100;
tcp_nopush on;
tcp_nodelay on;
server_tokens off;
fastcgi_temp_path /tmp/fastcgi 1 2;
client_body_temp_path /tmp/client_body 1 2;
proxy_temp_path /tmp/proxy 1 2;
uwsgi_temp_path /tmp/uwsgi 1 2;
scgi_temp_path /tmp/scgi 1 2;
gzip on;
gzip_comp_level 5;
gzip_min_length 512;
gzip_buffers 4 8k;
gzip_proxied any;
gzip_vary on;
gzip_disable "msie6";
gzip_types
text/css
text/javascript
text/xml
text/plain
text/x-component
application/javascript
application/x-javascript
application/json
application/xml
application/rss+xml
application/vnd.ms-fontobject
font/truetype
font/opentype
image/svg+xml;
server {
listen 8888;
root /lychee;
index index.php index.html;
client_max_body_size 100M;
location / {
try_files $uri $uri/ /index.html;
}
location ~* \.(?:ico|css|js|gif|jpe?g|png|ttf|woff)\$ {
access_log off;
expires 30d;
add_header Pragma public;
add_header Cache-Control "public, mustrevalidate, proxy-revalidate";
}
location ~ \.php$ {
fastcgi_split_path_info ^(.*\.php)(/.*)?$;
fastcgi_pass unix:/tmp/php-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_intercept_errors off;
fastcgi_buffer_size 16k;
fastcgi_buffers 4 16k;
}
location ~ /\.ht {
deny all;
}
}
}

11
docker/php-fpm.conf Normal file
View File

@ -0,0 +1,11 @@
[global]
daemonize = no
[www]
listen = /tmp/php-fpm.sock
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
chdir = /

4
docker/run.sh Normal file
View File

@ -0,0 +1,4 @@
#!/bin/sh
mkdir /lychee/uploads/big /lychee/uploads/import /lychee/uploads/medium /lychee/uploads/thumb
chown -R $UID:$GID /lychee /etc/nginx /etc/php7 /var/log /var/lib/nginx /tmp /etc/s6.d
exec su-exec $UID:$GID /bin/s6-svscan /etc/s6.d

View File

@ -0,0 +1,3 @@
#!/bin/sh
exit 0

2
docker/s6.d/nginx/run Normal file
View File

@ -0,0 +1,2 @@
#!/bin/sh
exec nginx

2
docker/s6.d/php/run Normal file
View File

@ -0,0 +1,2 @@
#!/bin/sh
exec php-fpm7

View File

@ -1,3 +1,10 @@
## v3.1.6
Released March 20, 2017
- `Fixed` Downloading a SmartAlbum results in crash (#652)
- `Fixed` htaccess IfModule for PHP7 (#653)
## v3.1.5
Released October 25, 2016

View File

@ -27,7 +27,7 @@ You can also use the [direct download](https://github.com/electerious/Lychee/arc
Change the permissions of `uploads/`, `data/` and all their subfolders. Sufficient read/write privileges are required.
chmod -R 777 uploads/ data/
chmod -R 750 uploads/ data/
### 4. Finish

View File

@ -14,8 +14,8 @@ The following keys and shortcuts can be used in Lychee.
| Key | Action |
|:-----------|:------------|
| `s` or `f` | Search |
| `meta-key`+`left-click` | Select album individually |
| `right-click` | Open context-menu |
| `cmd`+`a` | Select all albums |
| `ctrl`+`a` | Select all albums |
### Album
| Key | Action |
@ -25,8 +25,8 @@ The following keys and shortcuts can be used in Lychee.
| `i` | Show information |
| `cmd`+`backspace` | Delete album |
| `ctrl`+`backspace` | Delete album |
| `meta-key`+`left-click` | Select album/photo individually |
| `right-click` | Open context-menu |
| `cmd`+`a` | Select all photos |
| `ctrl`+`a` | Select all photos |
### Photo
| Key | Action |

View File

@ -26,9 +26,9 @@ final class Admin extends Access {
case 'Album::setTitle': self::setAlbumTitleAction(); break;
case 'Album::setDescription': self::setAlbumDescriptionAction(); break;
case 'Album::setPublic': self::setAlbumPublicAction(); break;
case 'Album::setPosition': self::setPositionAction(); break;
case 'Album::delete': self::deleteAlbumAction(); break;
case 'Album::merge': self::mergeAlbumsAction(); break;
case 'Album::move': self::moveAlbumsAction(); break;
// Photo functions
case 'Photo::get': self::getPhotoAction(); break;
@ -73,10 +73,8 @@ final class Admin extends Access {
private static function getAlbumsAction() {
Validator::required(isset($_POST['parent']), __METHOD__);
$albums = new Albums();
Response::json($albums->get(false, $_POST['parent']));
Response::json($albums->get(false));
}
@ -93,10 +91,10 @@ final class Admin extends Access {
private static function addAlbumAction() {
Validator::required(isset($_POST['title'], $_POST['parent']), __METHOD__);
Validator::required(isset($_POST['title']), __METHOD__);
$album = new Album(null);
Response::json($album->add($_POST['title'], $_POST['parent']), JSON_NUMERIC_CHECK);
Response::json($album->add($_POST['title']), JSON_NUMERIC_CHECK);
}
@ -118,6 +116,15 @@ final class Admin extends Access {
}
private static function setPositionAction() {
Validator::required(isset($_POST['albumID'],$_POST['photoOrder']), __METHOD__);
$album = new Album($_POST['albumID']);
Response::json($album->setPosition());
}
private static function setAlbumPublicAction() {
Validator::required(isset($_POST['albumID'], $_POST['password'], $_POST['visible'], $_POST['downloadable']), __METHOD__);
@ -144,14 +151,6 @@ final class Admin extends Access {
}
private static function moveAlbumsAction() {
Validator::required(isset($_POST['albumIDs']), __METHOD__);
$album = new Album($_POST['albumIDs']);
Response::json($album->move());
}
// Photo functions
private static function getPhotoAction() {

View File

@ -44,10 +44,8 @@ final class Guest extends Access {
private static function getAlbumsAction() {
Validator::required(isset($_POST['parent']), __METHOD__);
$albums = new Albums();
Response::json($albums->get(true, $_POST['parent']));
Response::json($albums->get(true));
}

View File

@ -23,7 +23,7 @@ final class Album {
/**
* @return string|false ID of the created album.
*/
public function add($title = 'Untitled', $parent = 0) {
public function add($title = 'Untitled') {
// Call plugins
Plugins::get()->activate(__METHOD__, 0, func_get_args());
@ -35,7 +35,7 @@ final class Album {
$visible = 1;
// Database
$query = Database::prepare(Database::get(), "INSERT INTO ? (id, title, sysstamp, public, visible, parent) VALUES ('?', '?', '?', '?', '?', '?')", array(LYCHEE_TABLE_ALBUMS, $id, $title, $sysstamp, $public, $visible, $parent));
$query = Database::prepare(Database::get(), "INSERT INTO ? (id, title, sysstamp, public, visible) VALUES ('?', '?', '?', '?', '?')", array(LYCHEE_TABLE_ALBUMS, $id, $title, $sysstamp, $public, $visible));
$result = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
// Call plugins
@ -69,7 +69,6 @@ final class Album {
if (isset($data['description'])) $album['description'] = $data['description'];
if (isset($data['visible'])) $album['visible'] = $data['visible'];
if (isset($data['downloadable'])) $album['downloadable'] = $data['downloadable'];
if (isset($data['parent'])) $album['parent'] = $data['parent'];
// Parse date
$album['sysdate'] = strftime('%B %Y', $data['sysstamp']);
@ -80,7 +79,6 @@ final class Album {
// Parse thumbs or set default value
$album['thumbs'] = (isset($data['thumbs']) ? explode(',', $data['thumbs']) : array());
return $album;
}
@ -195,6 +193,12 @@ final class Album {
// Call plugins
Plugins::get()->activate(__METHOD__, 0, func_get_args());
// Illicit chars
$badChars = array_merge(
array_map('chr', range(0,31)),
array("<", ">", ":", '"', "/", "\\", "|", "?", "*")
);
// Photos query
switch($this->albumIDs) {
case 's':
@ -210,34 +214,34 @@ final class Album {
$zipTitle = 'Recent';
break;
default:
$photos = Database::prepare(Database::get(), "SELECT title, url FROM ? WHERE album = '?'", array(LYCHEE_TABLE_PHOTOS, $this->albumIDs));
$zipTitle = 'Unsorted';
}
// Get title from database when album is not a SmartAlbum
if ($this->albumIDs!=0 && is_numeric($this->albumIDs)) {
// Get title from database when album is not a SmartAlbum
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));
$album = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
$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__);
if ($album===false) return false;
if ($album===false) return false;
// Get album object
$album = $album->fetch_object();
// Get album object
$album = $album->fetch_object();
// Album not found?
if ($album===null) {
Log::error(Database::get(), __METHOD__, __LINE__, 'Could not find specified album');
return false;
}
// Album not found?
if ($album===null) {
Log::error(Database::get(), __METHOD__, __LINE__, 'Could not find specified album');
return false;
}
// Set title
$zipTitle = $album->title;
// Set title
$zipTitle = $album->title;
}
break;
}
// Escape title
$zipTitle = $this->cleanZipName($zipTitle);
$zipTitle = str_replace($badChars, '', $zipTitle);
$filename = LYCHEE_DATA . $zipTitle . '.zip';
@ -248,16 +252,54 @@ final class Album {
return false;
}
// Add photos to zip
switch($this->albumIDs) {
case 's':
case 'f':
case 'r':
$this->addPhotosToZip($zip, $zipTitle, $photos);
break;
default:
$this->addAlbumToZip($zip, $zipTitle, $this->albumIDs);
break;
// Execute query
$photos = Database::execute(Database::get(), $photos, __METHOD__, __LINE__);
// Check if album empty
if ($photos->num_rows==0) {
Log::error(Database::get(), __METHOD__, __LINE__, 'Could not create ZipArchive without images');
return false;
}
// 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
@ -279,84 +321,6 @@ 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.
*/
@ -380,6 +344,41 @@ final class Album {
}
public function setPosition(){
// Check dependencies
Validator::required(isset($_POST['photoOrder']), __METHOD__);
// Call plugins
Plugins::get()->activate(__METHOD__, 0, func_get_args());
$id_list = implode(',', $_POST['photoOrder']);
$indices = [];
$size = count(explode(',',$id_list));
for($i = 0; $i < $size; $i++){
$indices[$i] = $i;
}
$whens = implode(
" ",
array_map(
function ($id, $value) {
return "WHEN {$id} THEN {$value}";
},
explode(',',$id_list),
$indices
)
);
$query = Database::prepare(Database::get(), "UPDATE ? SET position = CASE id ? END WHERE id IN (?)", array(LYCHEE_TABLE_PHOTOS, $whens, $id_list));
$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.
*/
@ -475,31 +474,6 @@ 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.
*/
@ -516,11 +490,8 @@ final class Album {
$visible = ($visible==='1' ? 1 : 0);
$downloadable = ($downloadable==='1' ? 1 : 0);
// Get all album ids, including subalbums
$ids = $this->addSubAlbumIDs($this->albumIDs);
// Set public
$query = Database::prepare(Database::get(), "UPDATE ? SET public = '?', visible = '?', downloadable = '?', password = NULL WHERE id IN (?)", array(LYCHEE_TABLE_ALBUMS, $public, $visible, $downloadable, $ids));
$query = Database::prepare(Database::get(), "UPDATE ? SET public = '?', visible = '?', downloadable = '?', password = NULL WHERE id IN (?)", array(LYCHEE_TABLE_ALBUMS, $public, $visible, $downloadable, $this->albumIDs));
$result = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
if ($result===false) return false;
@ -528,7 +499,7 @@ final class Album {
// Reset permissions for photos
if ($public===1) {
$query = Database::prepare(Database::get(), "UPDATE ? SET public = 0 WHERE album IN (?)", array(LYCHEE_TABLE_PHOTOS, $ids));
$query = Database::prepare(Database::get(), "UPDATE ? SET public = 0 WHERE album IN (?)", array(LYCHEE_TABLE_PHOTOS, $this->albumIDs));
$result = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
if ($result===false) return false;
@ -637,7 +608,6 @@ final class Album {
$albumID = array_splice($albumIDs, 0, 1);
$albumID = $albumID[0];
// Move photos
$query = Database::prepare(Database::get(), "UPDATE ? SET album = ? WHERE album IN (?)", array(LYCHEE_TABLE_PHOTOS, $albumID, $this->albumIDs));
$result = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
@ -647,13 +617,6 @@ final class Album {
// Convert to string
$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));
$result = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
@ -665,40 +628,6 @@ 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.
*/
@ -710,14 +639,11 @@ final class Album {
// Call plugins
Plugins::get()->activate(__METHOD__, 0, func_get_args());
// Get all album ids, including subalbums
$ids = $this->addSubAlbumIDs($this->albumIDs);
// Init vars
$photoIDs = array();
// Execute query
$query = Database::prepare(Database::get(), "SELECT id FROM ? WHERE album IN (?)", array(LYCHEE_TABLE_PHOTOS, $ids));
$query = Database::prepare(Database::get(), "SELECT id FROM ? WHERE album IN (?)", array(LYCHEE_TABLE_PHOTOS, $this->albumIDs));
$photos = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
if ($photos===false) return false;
@ -738,7 +664,7 @@ final class Album {
}
// Delete albums
$query = Database::prepare(Database::get(), "DELETE FROM ? WHERE id IN (?)", array(LYCHEE_TABLE_ALBUMS, $ids));
$query = Database::prepare(Database::get(), "DELETE FROM ? WHERE id IN (?)", array(LYCHEE_TABLE_ALBUMS, $this->albumIDs));
$result = Database::execute(Database::get(), $query, __METHOD__, __LINE__);
// Call plugins

View File

@ -16,7 +16,7 @@ final class Albums {
/**
* @return array|false Returns an array of albums or false on failure.
*/
public function get($public = true, $parent = 0) {
public function get($public = true) {
// Call plugins
Plugins::get()->activate(__METHOD__, 0, func_get_args());
@ -32,8 +32,8 @@ final class Albums {
if ($public===false) $return['smartalbums'] = $this->getSmartAlbums();
// Albums query
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, parent FROM ? " . ($parent != -1 ? "WHERE parent = '?' " : "") . " AND public = 1 AND visible <> 0 " . Settings::get()['sortingAlbums'], array(LYCHEE_TABLE_ALBUMS, $parent));
if ($public===false) $query = Database::prepare(Database::get(), 'SELECT id, title, public, sysstamp, password FROM ? ' . Settings::get()['sortingAlbums'], array(LYCHEE_TABLE_ALBUMS));
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));
// Execute query
$albums = Database::execute(Database::get(), $query, __METHOD__, __LINE__);

View File

@ -15,8 +15,7 @@ final class Database {
'030001', // 3.0.1
'030003', // 3.0.3
'030100', // 3.1.0
'030102', // 3.1.2
'030200' // 3.2.0
'030102' // 3.1.2
);
/**

View File

@ -208,7 +208,7 @@ final class Photo {
$info = $this->getInfo($path);
// Use title of file if IPTC title missing
if ($info['title']==='') $info['title'] = substr(basename($file['name'], $extension), 0, 30);
if ($info['title']==='') $info['title'] = substr(basename($file['name'], $extension), 0, 100);
if ($exists===false) {
@ -709,7 +709,7 @@ final class Photo {
// Parse photo
$photo['sysdate'] = strftime('%d %b. %Y', substr($photo['id'], 0, -4));
if (strlen($photo['takestamp'])>1) $photo['takedate'] = strftime('%d %b. %Y', $photo['takestamp']);
if (strlen($photo['takestamp'])>1) $photo['takedate'] = strftime('%d %b. %Y %T', $photo['takestamp']);
// Parse medium
if ($photo['medium']==='1') $photo['medium'] = LYCHEE_URL_UPLOADS_MEDIUM . $photo['url'];
@ -832,7 +832,7 @@ final class Photo {
}
// Read EXIF
if ($info['mime']=='image/jpeg') $exif = @exif_read_data($url, 'EXIF', false, false);
if ($info['mime']=='image/jpeg') $exif = @exif_read_data($url, 'EXIF, IFD0', false, false);
else $exif = false;
// EXIF Metadata

View File

@ -24,7 +24,8 @@ CREATE TABLE IF NOT EXISTS `?` (
`album` bigint(20) unsigned NOT NULL,
`checksum` char(40) DEFAULT NULL,
`medium` tinyint(1) NOT NULL DEFAULT '0',
`position` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `Index_album` (`album`),
KEY `Index_star` (`star`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

View File

@ -0,0 +1,25 @@
<?php
/**
* Update to version 3.1.7
*/
use Lychee\Modules\Database;
use Lychee\Modules\Response;
// Add position to photos
$query = Database::prepare($connection, "SELECT `position` FROM `?` LIMIT 1", array(LYCHEE_TABLE_PHOTOS));
$result = Database::execute($connection, $query, 'update_030107', __LINE__);
if ($result===false) {
$query = Database::prepare($connection, "ALTER TABLE `?` ADD `position` TINYINT(1) NOT NULL", array(LYCHEE_TABLE_PHOTOS));
$result = Database::execute($connection, $query, 'update_030107', __LINE__);
if ($result===false) Response::error('Could not add position to database!');
}
// Set version
if (Database::setVersion($connection, '030107')===false) Response::error('Could not update version of database!');
?>

View File

@ -1,29 +0,0 @@
<?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!');
?>

View File

@ -83,6 +83,7 @@ paths.main = {
],
scripts: [
'node_modules/jquery/dist/jquery.min.js',
'node_modules/jquery-ui-dist/jquery-ui.min.js',
'node_modules/mousetrap/mousetrap.min.js',
'node_modules/mousetrap/plugins/global-bind/mousetrap-global-bind.min.js',
'node_modules/basiccontext/dist/basicContext.min.js',

View File

@ -1,6 +1,6 @@
{
"name": "Lychee",
"version": "3.2.0",
"version": "3.1.6",
"description": "Self-hosted photo-management done right.",
"authors": "Tobias Reich <tobias@electerious.com>",
"license": "MIT",
@ -14,20 +14,21 @@
"compile": "gulp"
},
"dependencies": {
"babel-preset-es2015": "^6.18.0",
"babel-preset-es2015": "^6.24.0",
"basiccontext": "^3.5.1",
"basicmodal": "^3.3.7",
"basicmodal": "^3.3.8",
"gulp": "^3.9.1",
"gulp-autoprefixer": "3.1.1",
"gulp-babel": "^6.1.2",
"gulp-concat": "^2.6.0",
"gulp-inject": "^4.1.0",
"gulp-load-plugins": "^1.3.0",
"gulp-concat": "^2.6.1",
"gulp-inject": "^4.2.0",
"gulp-load-plugins": "^1.5.0",
"gulp-minify-css": "^1.2.4",
"gulp-rimraf": "^0.2.0",
"gulp-sass": "^2.3.2",
"gulp-uglify": "^2.0.0",
"jquery": "^3.1.1",
"gulp-rimraf": "^0.2.1",
"gulp-sass": "^3.1.0",
"gulp-uglify": "^2.1.1",
"jquery": "^3.2.0",
"jquery-ui": "^1.12.1",
"mousetrap": "^1.6.0"
}
}

View File

@ -2,36 +2,9 @@
* @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 ? '&nbsp;&nbsp;'.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 = {
json: null,
subjson: null
}
album.isSmartID = function(id) {
return (id==='0' || id==='f' || id==='s' || id==='r')
json: null
}
@ -40,34 +13,38 @@ album.getID = function() {
let id = null
let isID = (id) => {
if (album.isSmartID(id)===true) return true
if (id==='0' || id==='f' || id==='s' || id==='r') return true
return $.isNumeric(id)
}
if (photo.json) id = photo.json.album
else if (album.json) id = album.json.id
// Search
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) {
if (photo.json) id = photo.json.album
else if (album.json) id = album.json.id
}
if (isID(id)===true) return id
return false
}
album.getParent = function() {
if (album.json==null || album.isSmartID(album.json.id)===true || album.json.parent==0) return ''
return album.json.parent
else return false
}
album.load = function(albumID, refresh = false) {
$('.content').sortable({
stop: function(event, ui){
let albumID = ui.item.data('album-id');
let photoOrder = $('.content').children().map(function(){
return $(this).data('id');
}).get();
let params = {
albumID: albumID,
photoOrder: photoOrder
};
api.post('Album::setPosition', params, function(data) {
if (data!==true) lychee.error(null, params, data)
})
}
});
password.get(albumID, function() {
@ -116,45 +93,17 @@ album.load = function(albumID, refresh = false) {
setTimeout(() => {
let finish = function() {
view.album.init()
if (refresh===false) {
lychee.animate(lychee.content, 'contentZoomIn')
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()
view.album.init()
if (refresh===false) {
lychee.animate(lychee.content, 'contentZoomIn')
header.setMode('album')
}
}, waitTime)
})
})
}
@ -165,20 +114,18 @@ album.parse = function() {
}
album.add = function(albumID = 0) {
album.add = function() {
const action = function(data) {
let title = data.title
let parent = albumID
const isNumber = (n) => (!isNaN(parseFloat(n)) && isFinite(n))
basicModal.close()
let params = {
title,
parent
title
}
api.post('Album::add', params, function(data) {
@ -194,26 +141,18 @@ album.add = function(albumID = 0) {
}
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({
body: msg,
buttons: {
action: {
title: 'Create Album',
fn: action
},
cancel: {
title: 'Cancel',
fn: basicModal.close
}
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>`,
buttons: {
action: {
title: 'Create Album',
fn: action
},
cancel: {
title: 'Cancel',
fn: basicModal.close
}
})
}
})
}
@ -245,21 +184,6 @@ album.delete = function(albumIDs) {
albums.deleteByID(id)
})
} else if (visible.album()) {
// if we deleted the current album, go to its parent
if (albumIDs.length==1 && album.json.id==albumIDs[0]) {
let id = album.getParent()
album.refresh()
lychee.goto(id)
}
// otherwise, we deleted a subalbum
else {
album.reload()
}
} else {
albums.refresh()
@ -288,20 +212,20 @@ album.delete = function(albumIDs) {
cancel.title = 'Keep Album'
// Get title
if (album.json && album.json.id == albumIDs[0]) albumTitle = album.json.title
else if (albums.json || album.subjson) albumTitle = albums.getByID(albumIDs).title
if (album.json) albumTitle = album.json.title
else if (albums.json) albumTitle = albums.getByID(albumIDs).title
// Fallback for album without a title
if (albumTitle==='') albumTitle = 'Untitled'
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>`
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>`
} else {
action.title = 'Delete Albums and Photos'
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 and subalbums 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 they contain? This action can't be undone!</p>`
}
@ -333,8 +257,8 @@ album.setTitle = function(albumIDs) {
if (albumIDs.length===1) {
// Get old title if only one album is selected
if (album.json && album.json.id == albumIDs[0]) oldTitle = album.json.title
else if (albums.json || album.subjson) oldTitle = albums.getByID(albumIDs).title
if (album.json) oldTitle = album.json.title
else if (albums.json) oldTitle = albums.getByID(albumIDs).title
}
@ -348,20 +272,10 @@ album.setTitle = function(albumIDs) {
// Rename only one album
if (album.json.id == albumIDs[0]) {
album.json.title = newTitle
view.album.title()
}
album.json.title = newTitle
view.album.title()
if (albums.json || album.subjson) {
albumIDs.forEach(function(id) {
let a = albums.getByID(id)
if (a) {
a.title = newTitle
view.album.content.title(id)
}
})
}
if (albums.json) albums.getByID(albumIDs[0]).title = newTitle
} else if (visible.albums()) {
@ -639,7 +553,7 @@ album.getArchive = function(albumID) {
}
const getMessage = function(albumIDs, titles, operation) {
album.merge = function(albumIDs) {
let title = ''
let sTitle = ''
@ -649,8 +563,7 @@ const getMessage = function(albumIDs, titles, operation) {
if (albumIDs instanceof Array===false) albumIDs = [ albumIDs ]
// Get title of first album
if (titles.length > 0) title = titles[0]
else if (albums.json || album.subjson) title = albums.getByID(albumIDs[0]).title
if (albums.json) title = albums.getByID(albumIDs[0]).title
// Fallback for first album without a title
if (title==='') title = 'Untitled'
@ -658,26 +571,19 @@ const getMessage = function(albumIDs, titles, operation) {
if (albumIDs.length===2) {
// Get title of second album
if (titles.length > 1) sTitle = titles[1]
else if (albums.json || album.subjson) sTitle = albums.getByID(albumIDs[1]).title
if (albums.json) sTitle = albums.getByID(albumIDs[1]).title
// Fallback for second album without a title
if (sTitle==='') sTitle = 'Untitled'
msg = lychee.html`<p>Are you sure you want to ${ operation } the album '$${ sTitle }' into '$${ title }'?</p>`
msg = lychee.html`<p>Are you sure you want to merge the album '$${ sTitle }' into the album '$${ title }'?</p>`
} else {
msg = lychee.html`<p>Are you sure you want to ${ operation } all selected albums into '$${ title }'?</p>`
msg = lychee.html`<p>Are you sure you want to merge all selected albums into the album '$${ title }'?</p>`
}
return msg
}
album.merge = function(albumIDs, titles = []) {
const action = function() {
basicModal.close()
@ -688,15 +594,19 @@ album.merge = function(albumIDs, titles = []) {
api.post('Album::merge', params, function(data) {
if (data!==true) lychee.error(null, params, data)
else album.reload()
if (data!==true) {
lychee.error(null, params, data)
} else {
albums.refresh()
lychee.goto()
}
})
}
basicModal.show({
body: getMessage(albumIDs, titles, 'merge'),
body: msg,
buttons: {
action: {
title: 'Merge Albums',
@ -710,59 +620,4 @@ album.merge = function(albumIDs, titles = []) {
}
})
}
album.move = function(albumIDs, titles = []) {
const action = function() {
basicModal.close()
let params = {
albumIDs: albumIDs.join()
}
api.post('Album::move', params, function(data) {
if (data!==true) lychee.error(null, params, data)
else 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
}

View File

@ -16,11 +16,7 @@ albums.load = function() {
if (albums.json===null) {
let params = {
parent: 0
}
api.post('Albums::get', params, function(data) {
api.post('Albums::get', {}, function(data) {
let waitTime = 0
@ -112,21 +108,18 @@ albums.getByID = function(albumID) {
// Function returns the JSON of an album
if (albumID==null) return undefined
if (albumID instanceof Array)
albumID = albumID[0]
if (!albums.json) return undefined
if (!albums.json.albums) return undefined
let json = undefined
let func = function() {
if (this.id==albumID) json = this
}
$.each(albums.json.albums, function(i) {
if (albums.json && albums.json.albums) {
$.each(albums.json.albums, func)
}
else if (album.subjson && album.subjson.albums) {
$.each(album.subjson.albums, func)
}
let elem = albums.json.albums[i]
if (elem.id==albumID) json = elem
})
return json

View File

@ -14,11 +14,11 @@ build.iconic = function(icon, classes = '') {
}
build.divider = function(title, id = '') {
build.divider = function(title) {
let html = ''
html += lychee.html`<div class='divider' id='$${ id }'><h1>$${ title }</h1></div>`
html += lychee.html`<div class='divider'><h1>$${ title }</h1></div>`
return html

View File

@ -2,75 +2,9 @@
* @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 ? '&nbsp;&nbsp;'.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.add = function(albumID, e) {
contextMenu.add = function(e) {
let items = [
{ title: build.iconic('image') + 'Upload Photo', fn: () => $('#upload_files').click() },
@ -79,7 +13,7 @@ contextMenu.add = function(albumID, e) {
{ 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('folder') + 'New Album', fn: () => album.add(albumID) }
{ title: build.iconic('folder') + 'New Album', fn: album.add }
]
basicContext.show(items, e.originalEvent)
@ -112,16 +46,18 @@ contextMenu.album = function(albumID, e) {
// fn must call basicContext.close() first,
// in order to keep the selection
if (album.isSmartID(albumID)) return false
if (albumID==='0' || albumID==='f' || albumID==='s' || albumID==='r') return false
// Show merge-item when there's more than one album
let showMerge = (albums.json && albums.json.albums && Object.keys(albums.json.albums).length>1)
let items = [
{ title: build.iconic('pencil') + 'Rename', fn: () => album.setTitle([ albumID ]) },
{ title: build.iconic('collapse-left') + 'Merge', fn: () => { basicContext.close(); contextMenu.mergeAlbum(albumID, e) } },
{ title: build.iconic('folder') + 'Move', fn: () => { basicContext.close(); contextMenu.moveAlbum([ albumID ], e) } },
{ title: build.iconic('collapse-left') + 'Merge', visible: showMerge, fn: () => { basicContext.close(); contextMenu.mergeAlbum(albumID, e) } },
{ title: build.iconic('trash') + 'Delete', fn: () => album.delete([ albumID ]) }
]
multiselect.select('.album[data-id="' + albumID + '"]')
$('.album[data-id="' + albumID + '"]').addClass('active')
basicContext.show(items, e.originalEvent, contextMenu.close)
@ -135,11 +71,13 @@ contextMenu.albumMulti = function(albumIDs, e) {
// Show list of albums otherwise
let autoMerge = (albumIDs.length>1 ? true : false)
// Show merge-item when there's more than one album
let showMerge = (albums.json && albums.json.albums && Object.keys(albums.json.albums).length>1)
let items = [
{ title: build.iconic('pencil') + 'Rename All', fn: () => album.setTitle(albumIDs) },
{ title: build.iconic('collapse-left') + 'Merge All', visible: autoMerge, fn: () => album.merge(albumIDs) },
{ title: build.iconic('collapse-left') + 'Merge', visible: !autoMerge, fn: () => { basicContext.close(); contextMenu.mergeAlbum(albumIDs[0], e) } },
{ title: build.iconic('folder') + 'Move All', fn: () => { basicContext.close(); contextMenu.moveAlbum(albumIDs, e) } },
{ title: build.iconic('collapse-left') + 'Merge All', visible: showMerge && autoMerge, fn: () => album.merge(albumIDs) },
{ title: build.iconic('collapse-left') + 'Merge', visible: showMerge && !autoMerge, fn: () => { basicContext.close(); contextMenu.mergeAlbum(albumIDs[0], e) } },
{ title: build.iconic('trash') + 'Delete All', fn: () => album.delete(albumIDs) }
]
@ -151,13 +89,26 @@ contextMenu.albumMulti = function(albumIDs, e) {
contextMenu.albumTitle = function(albumID, e) {
api.post('Albums::get', { parent: -1 }, function(data) {
api.post('Albums::get', {}, function(data) {
let items = []
if (data.albums && data.num>1) {
items = buildAlbumList(data.albums, [ albumID ], (a) => lychee.goto(a.id))
// Generate list of albums
$.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({ })
@ -173,57 +124,25 @@ contextMenu.albumTitle = function(albumID, e) {
contextMenu.mergeAlbum = function(albumID, e) {
api.post('Albums::get', { parent: -1 }, function(data) {
api.post('Albums::get', {}, function(data) {
let items = []
if (data.albums && data.num>1) {
let selalbum = albums.getByID(albumID)
let title = selalbum.title
$.each(data.albums, function() {
// Disable all parents
// 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)
}
if (!this.thumbs[0]) this.thumbs[0] = 'src/images/no_cover.svg'
if (this.title==='') this.title = 'Untitled'
items = buildAlbumList(data.albums, exclude, (a) => album.merge([ albumID, a.id ], [ title, a.title ]))
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: () => 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 ]) })
})
}
@ -251,7 +170,7 @@ contextMenu.photo = function(photoID, e) {
{ title: build.iconic('trash') + 'Delete', fn: () => photo.delete([ photoID ]) }
]
multiselect.select('.photo[data-id="' + photoID + '"]')
$('.photo[data-id="' + photoID + '"]').addClass('active')
basicContext.show(items, e.originalEvent, contextMenu.close)
@ -259,12 +178,12 @@ contextMenu.photo = function(photoID, e) {
contextMenu.photoMulti = function(photoIDs, e) {
multiselect.stopResize()
// Notice for 'Move All':
// fn must call basicContext.close() first,
// in order to keep the selection and multiselect
multiselect.stopResize()
let items = [
{ title: build.iconic('star') + 'Star All', fn: () => photo.setStar(photoIDs) },
{ title: build.iconic('tag') + 'Tag All', fn: () => photo.editTags(photoIDs) },
@ -291,7 +210,19 @@ contextMenu.photoTitle = function(albumID, photoID, e) {
items.push({ })
items = items.concat(buildAlbumList(data.content, [ photoID ], (a) => lychee.goto(albumID + '/' + a.id)))
// Generate list of albums
$.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)
})
})
}
@ -319,7 +250,7 @@ contextMenu.move = function(photoIDs, e) {
let items = []
api.post('Albums::get', { parent: -1 }, function(data) {
api.post('Albums::get', {}, function(data) {
if (data.num===0) {
@ -330,7 +261,20 @@ contextMenu.move = function(photoIDs, e) {
} else {
items = buildAlbumList(data.albums, [ album.getID() ], (a) => photo.setAlbum(photoIDs, a.id))
// Generate list of albums
$.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
if (album.getID()!=='0') {
@ -400,7 +344,7 @@ contextMenu.close = function() {
basicContext.close()
multiselect.deselect('.photo.active, .album.active')
$('.photo.active, .album.active').removeClass('active')
if (visible.multiselect()) multiselect.close()
}

View File

@ -43,7 +43,7 @@ header.bind = function() {
header.dom('#button_settings') .on(eventName, contextMenu.settings)
header.dom('#button_info_album') .on(eventName, sidebar.toggle)
header.dom('#button_info') .on(eventName, sidebar.toggle)
header.dom('.button_add') .on(eventName, function(e) { contextMenu.add(album.getID(), e) })
header.dom('.button_add') .on(eventName, contextMenu.add)
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('.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_archive') .on(eventName, function() { album.getArchive(album.getID()) })
header.dom('#button_star') .on(eventName, function() { photo.setStar([ photo.getID() ]) })
header.dom('#button_back_home') .on(eventName, function() { lychee.goto(album.getParent()) })
header.dom('#button_back_home') .on(eventName, function() { lychee.goto() })
header.dom('#button_back') .on(eventName, function() { lychee.goto(album.getID()) })
header.dom('.header__search').on('keyup click', function() { search.find($(this).val()) })
@ -132,8 +132,8 @@ header.setMode = function(mode) {
header.dom('.header__toolbar--album').addClass('header__toolbar--visible')
// Hide download button when album empty
if (album.json.content===false && album.subjson.num==0) $('#button_archive').hide()
else $('#button_archive').show()
if (album.json.content===false) $('#button_archive').hide()
else $('#button_archive').show()
// Hide download button when not logged in and album not downloadable
if (lychee.publicMode===true && album.json.downloadable==='0') $('#button_archive').hide()

View File

@ -54,6 +54,10 @@ $(document).ready(function() {
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 }
})
.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() {
if (basicModal.visible()===true) basicModal.action()
@ -63,7 +67,7 @@ $(document).ready(function() {
if (basicModal.visible()===true) basicModal.cancel()
else if (visible.contextMenu()) contextMenu.close()
else if (visible.photo()) lychee.goto(album.getID())
else if (visible.album()) lychee.goto(album.getParent())
else if (visible.album()) lychee.goto()
else if (visible.albums() && header.dom('.header__search').val().length!==0) search.reset()
return false
})
@ -91,12 +95,12 @@ $(document).ready(function() {
$(document)
// Navigation
.on('click', '.album', function(e) { multiselect.albumClick(e, $(this)) })
.on('click', '.photo', function(e) { multiselect.photoClick(e, $(this)) })
.on('click', '.album', function() { lychee.goto($(this).attr('data-id')) })
.on('click', '.photo', function() { lychee.goto(album.getID() + '/' + $(this).attr('data-id')) })
// Context Menu
.on('contextmenu', '.photo', function(e) { multiselect.photoContextMenu(e, $(this)) })
.on('contextmenu', '.album', function(e) { multiselect.albumContextMenu(e, $(this)) })
.on('contextmenu', '.photo', function(e) { contextMenu.photo(photo.getID(), e) })
.on('contextmenu', '.album', function(e) { contextMenu.album(album.getID(), e) })
// Upload
.on('change', '#upload_files', function() { basicModal.close(); upload.start.local(this.files) })

View File

@ -5,8 +5,8 @@
lychee = {
title : document.title,
version : '3.2.0',
versionCode : '030200',
version : '3.1.7',
versionCode : '030107',
updatePath : '//update.electerious.com/index.json',
updateURL : 'https://github.com/electerious/Lychee',
@ -169,7 +169,6 @@ lychee.load = function() {
// Trash data
photo.json = null
albums.json = null
// Show Photo
if (lychee.content.html()==='' || (header.dom('.header__search').length && header.dom('.header__search').val().length!==0)) {
@ -182,11 +181,10 @@ lychee.load = function() {
// Trash data
photo.json = null
albums.json = null
// Show Album
if (visible.photo()) view.photo.hide()
if (visible.sidebar() && album.isSmartID(albumID)) sidebar.toggle()
if (visible.sidebar() && (albumID==='0' || albumID==='f' || albumID==='s' || albumID==='r')) sidebar.toggle()
if (album.json && albumID==album.json.id) view.album.title()
else album.load(albumID)
@ -199,7 +197,6 @@ lychee.load = function() {
}
// Trash data
album.subjson = null
album.json = null
photo.json = null
@ -429,4 +426,4 @@ lychee.error = function(errorThrown, params, data) {
loadingBar.show('error', errorThrown)
}
}

View File

@ -2,19 +2,7 @@
* @description Select multiple albums or photos.
*/
const isSelectKeyPressed = function(e) {
return e.metaKey || e.ctrlKey
}
multiselect = {
ids : [],
albumsSelected : 0,
photosSelected : 0
}
multiselect = {}
multiselect.position = {
@ -33,121 +21,6 @@ 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) {
if (lychee.publicMode) return false
@ -171,6 +44,43 @@ 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) {
if (multiselect.position.top === null ||
@ -277,11 +187,6 @@ multiselect.getSelection = function(e) {
if (visible.contextMenu()) return false
if (!visible.multiselect()) return false
if (!isSelectKeyPressed(e) && (size.width==0 || size.height==0)) {
multiselect.close()
return false
}
$('.photo, .album').each(function() {
let offset = $(this).offset()
@ -291,37 +196,26 @@ multiselect.getSelection = function(e) {
(offset.top + 206)<=(size.top + size.height + tolerance) &&
(offset.left + 206)<=(size.left + size.width + tolerance)) {
let id = $(this).attr('data-id')
let id = $(this).data('id')
multiselect.addItem($(this), id)
if (id!=='0' && id!==0 && id!=='f' && id!=='s' && id!=='r' && id!=null) {
ids.push(id)
$(this).addClass('active')
}
}
})
multiselect.hide()
if (ids.length!==0 && visible.album()) contextMenu.photoMulti(ids, e)
else if (ids.length!==0 && visible.albums()) contextMenu.albumMulti(ids, e)
else multiselect.close()
}
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() {
multiselect.close = function() {
sidebar.setSelectable(true)
@ -335,12 +229,4 @@ multiselect.hide = function() {
lychee.animate('#multiselect', 'fadeOut')
setTimeout(() => $('#multiselect').remove(), 300)
}
multiselect.close = function() {
multiselect.clearSelection()
multiselect.hide()
}

View File

@ -72,7 +72,7 @@ settings.createConfig = function() {
if (data==='Warning: Could not create file!') {
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: {
action: {
title: 'Retry',

View File

@ -124,18 +124,14 @@ sidebar.createStructure.photo = function(data) {
// Set value for public
switch (data.public) {
case '0':
_public = 'No'
break
case '1':
_public = 'Yes'
break
case '2':
_public = 'Yes (Album)'
break
default:
_public = '-'
break
case '0' : _public = 'No'
break
case '1' : _public = 'Yes'
break
case '2' : _public = 'Yes (Album)'
break
default : _public = '-'
break
}

View File

@ -143,22 +143,8 @@ view.album = {
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) {
photosData += build.divider('Photos', 'divider-photos')
// Build photos
$.each(album.json.content, function() {
photosData += build.photo(this)
@ -177,32 +163,13 @@ view.album = {
title: function(photoID) {
let ntitle = ''
let prefix = ''
let title = album.json.content[photoID].title
if (album.json.content[photoID]) {
title = lychee.escapeHTML(title)
prefix = '.photo'
ntitle = album.json.content[photoID].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)
$('.photo[data-id="' + photoID + '"] .overlay h1')
.html(title)
.attr('title', title)
},
@ -235,7 +202,6 @@ view.album = {
if (!visible.albums()) {
album.json.num--
view.album.num()
if (album.json.num == 0) $('#divider-photos').hide()
}
})

View File

@ -72,10 +72,6 @@
transition: none;
border-color: darken($colorBlue, 15%);
}
&.selected img {
outline: 1px solid $colorBlue;
}
}
// Album -------------------------------------------------------------- //