- All new redefined interface
- Faster animations and transitions
- Import from Dropbox
- Import from Server
- Download public albums
- Several sorting options
- Installation assistent
- Infobox and description for albums
- Faster loading and improved performance
- Better file handling and upload
- Album covers are chosen intelligent
- Prettier URLs
- Massive changes under the hood
- IPTC support (Headline and Caption)
- EXIF Orientation support

How to update:
1. Replace all files, excluding `uploads/`
2. Open Lychee and enter your database details
pull/56/head v2.0
Tobias Reich 10 years ago
parent f580d43c35
commit dd2f6ebc77

@ -2,9 +2,9 @@ IndexIgnore *
# Uncomment these lines to change PHP parameters if you are using the PHP Apache module
#<IfModule mod_php5.c>
# php_value max_execution_time 300
# php_value post_max_size 100M
# php_value upload_max_size 100M
# php_value upload_max_filesize 5M
# 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>

@ -1,8 +1,8 @@
; Uncomment these lines to change PHP parameters if you are using PHP with CGI or FastCGI. Only works with PHP starting from 5.3.0.
; Note that with FastCGI, you might also want to set FcgidBusyTimeout, FcgidIOTimeout and FcgidMaxRequestLen in your Apache config
;max_execution_time = 60
;post_max_size = 100M
;upload_max_size = 100M
;upload_max_filesize = 5M
;max_file_uploads = 100
;max_execution_time = 200
;post_max_size = 200M
;upload_max_size = 200M
;upload_max_filesize = 20M
;max_file_uploads = 200

File diff suppressed because one or more lines are too long

@ -0,0 +1,11 @@
/**
* @name reset.css
*/
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video { margin:0; padding:0; border:0; font-size:100%; font:inherit; vertical-align:baseline; }
article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section { display:block; }
body { line-height:1; }
ol,ul { list-style:none; }
blockquote,q { quotes:none; }
blockquote:before,blockquote:after,q:before,q:after { content:''; content:none; }
table { border-collapse:collapse; border-spacing:0; }

@ -0,0 +1,311 @@
/**
* @name animations.css
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
/* Animation Setter ------------------------------------------------*/
.fadeIn {
-webkit-animation-name: fadeIn;
-webkit-animation-duration: .3s;
-webkit-animation-fill-mode: forwards;
-moz-animation-name: fadeIn;
-moz-animation-duration: .3s;
-moz-animation-fill-mode: forwards;
animation-name: fadeIn;
animation-duration: .3s;
animation-fill-mode: forwards; }
.fadeOut {
-webkit-animation-name: fadeOut;
-webkit-animation-duration: .3s;
-webkit-animation-fill-mode: forwards;
-moz-animation-name: fadeOut;
-moz-animation-duration: .3s;
-moz-animation-fill-mode: forwards;
animation-name: fadeOut;
animation-duration: .3s;
animation-fill-mode: forwards; }
.contentZoomIn {
-webkit-animation-name: zoomIn;
-webkit-animation-duration: .2s;
-webkit-animation-fill-mode: forwards;
-moz-animation-name: zoomIn;
-moz-animation-duration: .2s;
-moz-animation-fill-mode: forwards;
animation-name: zoomIn;
animation-duration: .2s;
animation-fill-mode: forwards; }
.contentZoomOut {
-webkit-animation-name: zoomOut;
-webkit-animation-duration: .2s;
-webkit-animation-fill-mode: forwards;
-moz-animation-name: zoomOut;
-moz-animation-duration: .2s;
-moz-animation-fill-mode: forwards;
animation-name: zoomOut;
animation-duration: .2s;
animation-fill-mode: forwards; }
/* moveUp ------------------------------------------------*/
@-webkit-keyframes moveUp {
0% {
-webkit-transform: translateY(30px);
opacity: 0;
}
100% {
-webkit-transform: translateY(0);
opacity: 1;
}
}
@-moz-keyframes moveUp {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes moveUp {
0% {
transform: translateY(30px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
/* fadeIn ------------------------------------------------*/
@-webkit-keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@-moz-keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
/* fadeOut ------------------------------------------------*/
@-webkit-keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@-moz-keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
/* moveBackground ------------------------------------------------*/
@-webkit-keyframes moveBackground {
0% {
background-position-x: 0px;
}
100% {
background-position-x: -100px;
}
}
@-moz-keyframes moveBackground {
0% {
background-position-x: 0px;
}
100% {
background-position-x: -100px;
}
}
@keyframes moveBackground {
0% {
background-position-x: 0px;
}
100% {
background-position-x: -100px;
}
}
/* zoomIn ------------------------------------------------*/
@-webkit-keyframes zoomIn {
0% {
opacity: 0;
-webkit-transform: scale(.8);
}
100% {
opacity: 1;
-webkit-transform: scale(1);
}
}
@-moz-keyframes zoomIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes zoomIn {
0% {
opacity: 0;
transform: scale(.8);
}
100% {
opacity: 1;
transform: scale(1);
}
}
/* zoomOut ------------------------------------------------*/
@-webkit-keyframes zoomOut {
0% {
opacity: 1;
-webkit-transform: scale(1);
}
100% {
opacity: 0;
-webkit-transform: scale(.8);
}
}
@-moz-keyframes zoomOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes zoomOut {
0% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(.8);
}
}
/* popIn ------------------------------------------------*/
@-webkit-keyframes popIn {
0% {
opacity: 0;
-webkit-transform: scale(0);
}
100% {
opacity: 1;
-webkit-transform: scale(1);
}
}
@-moz-keyframes popIn {
0% {
opacity: 0;
-moz-transform: scale(0);
}
100% {
opacity: 1;
-moz-transform: scale(1);
}
}
@keyframes popIn {
0% {
opacity: 0;
transform: scale(0);
}
100% {
opacity: 1;
transform: scale(1);
}
}
/* pulse ------------------------------------------------*/
@-webkit-keyframes pulse {
0% {
opacity: 1;
}
50% {
opacity: 0.3;
}
100% {
opacity: 1;
}
}
@-moz-keyframes pulse {
0% {
opacity: 1;
}
50% {
opacity: 0.8;
}
100% {
opacity: 1;
}
}
@keyframes pulse {
0% {
opacity: 1;
}
50% {
opacity: 0.8;
}
100% {
opacity: 1;
}
}
/* rotate ------------------------------------------------
@-webkit-keyframes rotate {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
@-moz-keyframes rotate {
0% {
-moz-transform: rotate(0deg);
}
100% {
-moz-transform: rotate(360deg);
}
}
@keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
} */

@ -0,0 +1,256 @@
/**
* @name content.css
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
#content::before {
content: "";
position: absolute;
left: 0px;
width: 100%;
height: 20px;
background-image: -webkit-linear-gradient(top, #262626, #222);
background-image: -moz-linear-gradient(top, #262626, #222);
background-image: -ms-linear-gradient(top, #262626, #222);
background-image: linear-gradient(top, #262626, #222);
border-top: 1px solid #333;
}
/* Modes ------------------------------------------------*/
#content.view::before {
display: none;
}
#content {
position: absolute;
padding: 50px 0px 33px 0px;
width: 100%;
-webkit-overflow-scrolling: touch;
}
/* Photo ------------------------------------------------*/
.photo {
float: left;
display: inline-block;
width: 206px;
height: 206px;
margin: 30px 0px 0px 30px;
cursor: pointer;
}
.photo img {
position: absolute;
width: 200px;
height: 200px;
background-color: #222;
border-radius: 2px;
border: 2px solid #ccc;
}
.photo:hover img,
.photo.active img {
box-shadow: 0px 0px 5px #005ecc;
}
.photo:active {
-webkit-transition-duration: .1s;
-webkit-transform: scale(.98);
-moz-transition-duration: .1s;
-moz-transform: scale(.98);
transition-duration: .1s;
transform: scale(.98);
}
/* Album ------------------------------------------------*/
.album {
float: left;
display: inline-block;
width: 204px;
height: 204px;
margin: 30px 0px 0px 30px;
cursor: pointer;
}
.album img:first-child,
.album img:nth-child(2) {
-webkit-transform: rotate(0deg) translateY(0px) translateX(0px);
-moz-transform: rotate(0deg) translateY(0px) translateX(0px);
transform: rotate(0deg) translateY(0px) translateX(0px);
opacity: 0;
}
.album:hover img:first-child {
-webkit-transform: rotate(-2deg) translateY(10px) translateX(-12px);
-moz-transform: rotate(-2deg) translateY(10px) translateX(-12px);
transform: rotate(-2deg) translateY(10px) translateX(-12px);
opacity: 1;
}
.album:hover img:nth-child(2) {
-webkit-transform: rotate(5deg) translateY(-8px) translateX(12px);
-moz-transform: rotate(5deg) translateY(-8px) translateX(12px);
transform: rotate(5deg) translateY(-8px) translateX(12px);
opacity: 1;
}
.album img {
position: absolute;
width: 200px;
height: 200px;
background-color: #222;
border-radius: 2px;
border: 2px solid #ccc;
}
.album:hover img,
.album.active img {
box-shadow: 0px 0px 5px #005ecc;
}
/* Album/Photo Overlay ------------------------------------------------*/
.album .overlay,
.photo .overlay {
position: absolute;
width: 200px;
height: 200px;
margin: 2px;
}
.album .overlay {
background: -moz-linear-gradient(top, rgba(0,0,0,0) 0%, rgba(0,0,0,0) 20%, rgba(0,0,0,0.9) 100%); /* FF3.6+ */
background: -webkit-linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,0) 20%,rgba(0,0,0,0.9) 100%); /* Chrome10+,Safari5.1+ */
background: -ms-linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,0) 20%,rgba(0,0,0,0.9) 100%); /* IE10+ */
background: linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,0) 20%,rgba(0,0,0,0.9) 100%); /* W3C */
}
.photo .overlay {
background: rgba(0, 0, 0, .6);
opacity: 0;
}
.photo:hover .overlay,
.photo.active .overlay {
opacity: 1;
}
.album .overlay h1,
.photo .overlay h1 {
min-height: 19px;
width: 190px;
margin: 153px 0px 3px 15px;
color: #fff;
font-size: 16px;
font-weight: bold;
overflow: hidden;
}
.album .overlay a,
.photo .overlay a {
font-size: 11px;
color: #aaa;
}
.album .overlay a {
margin-left: 15px;
}
.photo .overlay a {
margin: 155px 0px 5px 15px;
}
/* Badges ------------------------------------------------*/
.album .badge,
.photo .badge {
position: absolute;
margin-top: -1px;
margin-left: 12px;
padding: 12px 7px 3px 7px;
box-shadow: 0px 0px 3px #000;
border-radius: 0px 0px 3px 3px;
border: 1px solid #fff;
border-top: none;
color: #fff;
font-size: 24px;
text-shadow: 0px 1px 0px #000;
opacity: .9;
}
.album .badge.icon-star,
.photo .badge.icon-star {
padding: 12px 8px 3px 8px;
}
.album .badge.icon-share,
.photo .badge.icon-share {
padding: 12px 6px 3px 8px;
}
.album .badge::after,
.photo .badge::after {
content: "";
position: absolute;
margin-top: -12px;
margin-left: -26px;
width: 38px;
height: 5px;
background: -moz-linear-gradient(top, rgba(0,0,0,1) 0%, rgba(0,0,0,0) 100%); /* FF3.6+ */
background: -webkit-linear-gradient(top, rgba(0,0,0,1) 0%,rgba(0,0,0,0) 100%); /* Chrome10+,Safari5.1+ */
background: -ms-linear-gradient(top, rgba(0,0,0,1) 0%,rgba(0,0,0,0) 100%); /* IE10+ */
background: linear-gradient(top, rgba(0,0,0,1) 0%,rgba(0,0,0,0) 100%); /* W3C */
opacity: .4;
}
.album .badge.icon-star::after,
.photo .badge.icon-star::after {
margin-left: -29px;
}
.album .badge.icon-share::after,
.photo .badge.icon-share::after {
margin-left: -31px;
}
.album .badge.icon-reorder::after {
margin-left: -30px;
}
.album .badge:nth-child(2n),
.photo .badge:nth-child(2n) {
margin-left: 57px;
}
.album .badge.red,
.photo .badge.red {
background: #d64b4b;
background: -webkit-linear-gradient(top, #d64b4b, #ab2c2c);
background: -moz-linear-gradient(top, #d64b4b, #ab2c2c);
background: -ms-linear-gradient(top, #d64b4b, #ab2c2c);
}
.album .badge.blue,
.photo .badge.blue {
background: #d64b4b;
background: -webkit-linear-gradient(top, #347cd6, #2945ab);
background: -moz-linear-gradient(top, #347cd6, #2945ab);
background: -ms-linear-gradient(top, #347cd6, #2945ab);
}
/* Divider ------------------------------------------------*/
.divider {
float: left;
width: 100%;
margin-top: 50px;
opacity: 0;
border-top: 1px solid #2E2E2E;
box-shadow: 0px -1px 0px #151515;
}
.divider:first-child {
margin-top: 0px;
border-top: none;
}
.divider h1 {
float: left;
margin: 20px 0px 0px 30px;
color: #fff;
font-size: 14px;
font-weight: bold;
text-shadow: 0px -1px 0px #000;
}
/* No Content ------------------------------------------------*/
.no_content {
position: absolute;
top: 50%;
left: 50%;
height: 160px;
width: 180px;
margin-top: -80px;
margin-left: -90px;
padding-top: 20px;
color: rgba(20, 20, 20, 1);
text-shadow: 0px 1px 0px rgba(255, 255, 255, .05);
text-align: center;
}
.no_content .icon {
font-size: 120px;
}
.no_content p {
font-size: 18px;
}

@ -0,0 +1,101 @@
/**
* @name contextmenu.css
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
.contextmenu_bg {
position: fixed;
height: 100%;
width: 100%;
z-index: 1000;
}
.contextmenu {
position: fixed;
top: 110%;
left: 110%;
padding: 5px 0px 6px 0px;
background-color: #393939;
background-image: -webkit-linear-gradient(top, #444, #2d2d2d);
background-image: -moz-linear-gradient(top, #393939, #2d2d2d);
background-image: -ms-linear-gradient(top, #393939, #2d2d2d);
background-image: linear-gradient(top, #393939, #2d2d2d);
border: 1px solid rgba(0,0,0,0.7);
border-bottom: 1px solid rgba(0,0,0,.9);
border-radius: 5px;
box-shadow: 0px 4px 5px rgba(0,0,0,0.3), inset 0px 1px 0px rgba(255,255,255,0.15), inset 1px 0px 0px rgba(255,255,255,0.05), inset -1px 0px 0px rgba(255,255,255,0.05);
opacity: .98;
z-index: 1001;
}
/* Items ------------------------------------------------*/
.contextmenu tr {
font-size: 14px;
color: #eee;
text-shadow: 0px -1px 0px rgba(0,0,0,.6);
cursor: pointer;
}
.contextmenu tr:hover {
background-color: #6a84f2;
background-image: -webkit-linear-gradient(top, #6a84f2, #3959ef);
background-image: -moz-linear-gradient(top, #6a84f2, #3959ef);
background-image: -ms-linear-gradient(top, #6a84f2, #3959ef);
background-image: linear-gradient(top, #6a84f2, #3959ef);
}
.contextmenu tr.no_hover:hover {
cursor: inherit;
background-color: inherit;
background-image: none;
}
.contextmenu tr.separator {
float: left;
height: 1px;
width: 100%;
background-color: #1c1c1c;
border-bottom: 1px solid #4a4a4a;
margin: 5px 0px;
cursor: inherit;
}
.contextmenu tr.separator:hover {
background-color: #222;
background-image: none;
}
.contextmenu tr td {
padding: 7px 30px 6px 12px;
white-space: nowrap;
-webkit-transition: none;
-moz-transition: none;
transition: none;
}
.contextmenu tr:hover td {
color: #fff;
box-shadow: inset 0px 1px 0px rgba(255,255,255,.05);
text-shadow: 0px -1px 0px rgba(0,0,0,.4);
}
.contextmenu tr.no_hover:hover td {
box-shadow: none;
}
.contextmenu tr a {
float: left;
width: 10px;
margin-right: 10px;
text-align: center;
}
/* Direct Link Input ------------------------------------------------*/
.contextmenu #link {
float: right;
width: 140px;
margin: 0px -17px -1px 0px;
padding: 4px 6px 5px 6px;
background-color: #444;
color: #fff;
border: none;
border: 1px solid #111;
box-shadow: 0px 1px 0px rgba(255,255,255,.1);
outline: none;
border-radius: 5px;
}
.contextmenu tr a#link_icon {
padding-top: 4px;
}

@ -23,8 +23,8 @@
*/
@font-face {
font-family: 'FontAwesome';
src: url('font/fontawesome-webfont.eot');
src: url('font/fontawesome-webfont.eot?#iefix') format('eot'), url('font/fontawesome-webfont.woff') format('woff'), url('font/fontawesome-webfont.ttf') format('truetype'), url('font/fontawesome-webfont.svg#FontAwesome') format('svg');
src: url('../../font/fontawesome-webfont.eot');
src: url('../../font/fontawesome-webfont.eot?#iefix') format('eot'), url('../../font/fontawesome-webfont.woff') format('woff'), url('../../font/fontawesome-webfont.ttf') format('truetype'), url('../../font/fontawesome-webfont.svg#FontAwesome') format('svg');
font-weight: normal;
font-style: normal;
}
@ -300,4 +300,4 @@ li[class^="icon-"].icon-large:before, li[class*=" icon-"].icon-large:before {
.icon-umbrella:before { content: "\f0e9"; }
.icon-paste:before { content: "\f0ea"; }
.icon-user-md:before { content: "\f200"; }
.icon-user-md:before { content: "\f200"; }

@ -0,0 +1,165 @@
/**
* @name _header.css
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
header {
position: fixed;
height: 49px;
width: 100%;
background-image: -webkit-linear-gradient(top, #3E3E3E, #282828);
background-image: -moz-linear-gradient(top, #3E3E3E, #282828);
background-image: -ms-linear-gradient(top, #3E3E3E, #282828);
background-image: linear-gradient(top, #3E3E3E, #282828);
border-bottom: 1px solid #161616;
z-index: 1;
-webkit-transition: -webkit-transform .3s ease-out;
-moz-transition: -moz-transform .3s ease-out;
transition: transform .3s ease-out;
}
/* Modes ------------------------------------------------*/
header.hidden {
-webkit-transform: translateY(-60px);
-moz-transform: translateY(-60px);
transform: translateY(-60px);
}
header.loading {
-webkit-transform: translateY(2px);
-moz-transform: translateY(2px);
transform: translateY(2px);
}
header.error {
-webkit-transform: translateY(40px);
-moz-transform: translateY(40px);
transform: translateY(40px);
}
header.view.error {
background-color: rgba(10,10,10,.99);
}
header.view {
background-image: none;
border-bottom: none;
}
header.view .button,
header.view #title,
header.view .tools {
text-shadow: none !important;
}
/* Title ------------------------------------------------*/
header #title {
position: absolute;
margin: 0px 30%;
width: 40%;
padding: 15px 0px;
color: #fff;
font-size: 16px;
font-weight: bold;
text-align: center;
text-shadow: 0px -1px 0px #222;
}
header #title.editable {
cursor: pointer;
}
/* Button ------------------------------------------------*/
header .button {
color: #888;
font-family: 'FontAwesome';
font-size: 21px;
font-weight: bold;
text-decoration: none !important;
cursor: pointer;
text-shadow: 0px -1px 0px #222;
}
header .button.left {
float: left;
position: absolute;
padding: 16px 10px 8px 18px;
}
header .button.right {
float: right;
position: relative;
padding: 16px 19px 13px 11px;
}
header .button:hover {
color: #fff;
}
header #tools_albums,
header #tools_album,
header #tools_photo,
header #button_signin {
display: none;
}
/* Button Divider ------------------------------------------------*/
header .button_divider {
float: right;
position: relative;
width: 14px;
height: 50px;
}
/* Search ------------------------------------------------*/
header #search {
float: right;
width: 80px;
margin: 12px 12px 0px 0px;
padding: 5px 12px 6px 12px;
background-color: #383838;
color: #fff;
border: none;
border: 1px solid #131313;
box-shadow: 0px 1px 0px rgba(255,255,255,.1);
outline: none;
border-radius: 50px;
opacity: .6;
-webkit-transition: opacity .3s ease-out, -webkit-transform .3s ease-out, box-shadow .3s, width .2s ease-out;
-moz-transition: opacity .3s ease-out, -moz-transform .3s ease-out, box-shadow .3s, width .2s ease-out;
transition: opacity .3s ease-out, transform .3s ease-out, box-shadow .3s, width .2s ease-out;
}
header #search:focus {
width: 140px;
}
/* Tools ------------------------------------------------*/
header .tools:first-of-type {
margin-right: 6px;
}
header .tools {
float: right;
padding: 14px 8px;
color: #888;
font-size: 21px;
text-shadow: 0px -1px 0px #222;
cursor: pointer;
}
header .tools:hover a {
color: #fff;
}
header .tools .icon-star {
color: #f0ef77;
}
header .tools .icon-share.active {
color: #ff9737;
}
/* Hosted with Lychee ------------------------------------------------*/
header #hostedwith {
float: right;
padding: 5px 10px;
margin: 13px 9px;
color: #888;
font-size: 13px;
text-shadow: 0px -1px 0px #222;
display: none;
cursor: pointer;
}
header #hostedwith:hover {
background-color: rgba(0, 0, 0, .2);
border-radius: 100px;
}

@ -0,0 +1,88 @@
/**
* @name imageview.css
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
#imageview {
position: fixed;
display: none;
width: 100%;
min-height: 100%;
background-color: rgba(10,10,10,.99);
-webkit-transition: background-color .3s;
}
/* Modes ------------------------------------------------*/
#imageview.view {
background-color: inherit;
}
#imageview.full {
background-color: #040404;
}
/* ImageView ------------------------------------------------*/
#imageview #image {
position: absolute;
top: 60px;
right: 30px;
bottom: 30px;
left: 30px;
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: contain;
-webkit-transition: top .3s, bottom .3s, margin-top .3s;
-webkit-animation-name: zoomIn;
-webkit-animation-duration: .3s;
-moz-animation-name: zoomIn;
-moz-animation-duration: .3s;
animation-name: zoomIn;
animation-duration: .3s;
}
#imageview #image.small {
top: 50%;
right: auto;
bottom: auto;
left: 50%;
}
/* Previous/Next Buttons ------------------------------------------------*/
#imageview .arrow_wrapper {
position: fixed;
width: 20%;
height: calc(100% - 60px);
top: 60px;
z-index: 1;
}
#imageview .arrow_wrapper.previous {
left: 0;
}
#imageview .arrow_wrapper.next {
right: 0;
}
#imageview .arrow_wrapper a {
position: fixed;
top: 50%;
margin-top: -10px;
color: #fff;
font-size: 50px;
text-shadow: 0px 1px 2px #000;
cursor: pointer;
opacity: 0;
z-index: 2;
-webkit-transition: opacity .2s;
-moz-transition: opacity .2s;
transition: opacity .2s;
}
#imageview .arrow_wrapper:hover a {
opacity: .2;
}
#imageview .arrow_wrapper a#previous {
left: 20px;
}
#imageview .arrow_wrapper a#next {
right: 20px;
}

@ -0,0 +1,173 @@
/**
* @name infobox.css
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
#infobox_overlay {
z-index: 3;
position: fixed;
width: 100%;
height: 100%;
top: 0px;
left: 0px;
background-color: rgba(0,0,0,.85);
}
#infobox {
z-index: 4;
position: fixed;
right: 0px;
width: 300px;
height: 100%;
background-color: rgba(20,20,20,0.98);
box-shadow: -1px 0px 2px rgba(0,0,0,.8);
display: none;
-webkit-transform: translateX(320px);
-moz-transform: translateX(320px);
transform: translateX(320px);
-webkit-user-select: text;
-moz-user-select: text;
user-select: text;
-webkit-transition: -webkit-transform .5s cubic-bezier(.225,.5,.165,1);
-moz-transition: -moz-transform .5s cubic-bezier(.225,.5,.165,1);
transition: transform .5s cubic-bezier(.225,.5,.165,1);
}
#infobox.active {
-webkit-transform: translateX(0px);
-moz-transform: translateX(0px);
transform: translateX(0px);
}
/* Misc ------------------------------------------------*/
#infobox .wrapper {
float: left;
height: 100%;
overflow: scroll;
}
#infobox .edit {
display: inline;
margin-left: 3px;
width: 20px;
height: 5px;
cursor: pointer;
}
#infobox .bumper {
float: left;
width: 100%;
height: 50px;
}
/* Header ------------------------------------------------*/
#infobox .header {
float: left;
height: 49px;
width: 100%;
background-color: #1d1d1d;
background-image: -webkit-linear-gradient(top, #2A2A2A, #131313);
background-image: -moz-linear-gradient(top, #2A2A2A, #131313);
background-image: -ms-linear-gradient(top, #2A2A2A, #131313);
background-image: linear-gradient(top, #2A2A2A, #131313);
border-bottom: 1px solid #000;
}
#infobox .header h1 {
position: absolute;
margin: 15px 30%;
width: 40%;
color: #fff;
font-size: 16px;
font-weight: bold;
text-align: center;
text-shadow: 0px -1px 0px #000;
}
#infobox .header a {
float: right;
padding: 15px 15px;
color: #fff;
font-size: 20px;
font-weight: bold;
text-shadow: 0px -1px 0px #000;
opacity: .5;
cursor: pointer;
}
#infobox .header a:hover {
opacity: 1;
}
/* Seperator ------------------------------------------------*/
#infobox .separator {
float: left;
width: 100%;
border-top: 1px solid rgba(255,255,255,.04);
box-shadow: 0px -1px 0px #000;
}
#infobox .separator h1 {
margin: 20px 0px 5px 20px;
color: #fff;
font-size: 14px;
font-weight: bold;
text-shadow: 0px -1px 0px #000;
}
/* Table ------------------------------------------------*/
#infobox table {
float: left;
margin: 10px 0px 15px 20px;
}
#infobox table tr td {
padding: 5px 0px;
color: #fff;
font-size: 14px;
line-height: 19px;
}
#infobox table tr td:first-child {
width: 110px;
}
#infobox table tr td:last-child {
padding-right: 10px;
}
/* Tags ------------------------------------------------*/
#infobox #tags {
margin: 20px 20px 15px 20px;
color: #fff;
display: inline-block;
}
#infobox .tag {
float: left;
padding: 4px 7px;
margin: 0px 6px 8px 0px;
background-color: rgba(0,0,0,.5);
border: 2px solid rgba(255,255,255,.3);
border-radius: 100px;
font-size: 12px;
-webkit-transition: border .3s;
-moz-transition: border .3s;
transition: border .3s;
}
#infobox .tag:hover {
border: 2px solid #aaa;
}
#infobox .tag span {
float: right;
width: 0px;
padding: 0px;
margin: 0px 0px -2px 0px;
color: red;
font-size: 12px;
cursor: pointer;
overflow: hidden;
-webkit-transform: scale(0);
transform: scale(0);
-webkit-transition: width .3s, margin .3s, -webkit-transform .3s;
-moz-transition: width .3s, margin .3s;
transition: width .3s, margin .3s, transform .3s;
}
#infobox .tag:hover span {
width: 10px;
margin: 0px 0px -2px 6px;
-webkit-transform: scale(1);
transform: scale(1);
}

@ -0,0 +1,59 @@
/**
* @name loading.css
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
#loading {
position: fixed;
width: 100%;
height: 3px;
background-size: 100px 3px;
background-repeat: repeat-x;
border-bottom: 1px solid rgba(0,0,0,.3);
display: none;
/* Animation */
-webkit-animation-name: moveBackground;
-webkit-animation-duration: .3s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear;
-moz-animation-name: moveBackground;
-moz-animation-duration: .3s;
-moz-animation-iteration-count: infinite;
-moz-animation-timing-function: linear;
animation-name: moveBackground;
animation-duration: .3s;
animation-iteration-count: infinite;
animation-timing-function: linear;
}
/* Modes ------------------------------------------------*/
#loading.loading {
background-image: -webkit-linear-gradient(left, #153674 0%, #153674 47%, #2651AE 53%, #2651AE 100%);
background-image: -moz-linear-gradient(left, #153674 0%, #153674 47%, #2651AE 53%, #2651AE 100%);
background-image: linear-gradient(left right, #153674 0%, #153674 47%, #2651AE 53%, #2651AE 100%);
z-index: 2;
}
#loading.error {
background-color: #2f0d0e;
background-image: -webkit-linear-gradient(left, #451317 0%, #451317 47%, #AA3039 53%, #AA3039 100%);
background-image: -moz-linear-gradient(left, #451317 0%, #451317 47%, #AA3039 53%, #AA3039 100%);
background-image: linear-gradient(left right, #451317 0%, #451317 47%, #AA3039 53%, #AA3039 100%);
z-index: 1;
}
/* Content ------------------------------------------------*/
#loading h1 {
margin: 13px;
color: #ddd;
font-size: 14px;
font-weight: bold;
text-shadow: 0px 1px 0px #000;
text-transform: capitalize;
}
#loading h1 span {
margin-left: 10px;
font-weight: normal;
text-transform: none;
}

@ -0,0 +1,61 @@
/**
* @name mediaquery.css
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
@media only screen and (max-width: 900px) {
#title {
margin: 0px 20% !important;
width: 40% !important;
}
#title.view {
margin: 0px 20% !important;
width: 60% !important;
}
#title span {
display: none !important;
}
}
@media only screen and (max-width: 640px) {
#title {
display: none !important;
}
#title.view {
display: block !important;
width: 70% !important;
margin: 0px 20% 0px 10% !important;
}
#button_move {
display: none !important;
}
#button_archive {
display: none !important;
}
.center {
top: 0px !important;
left: 0px !important;
}
.album, .photo {
margin: 40px 0px 0px 50px !important;
}
.message {
position: fixed !important;
width: 100% !important;
height: 100% !important;
margin: 1px 0px 0px 0px !important;
border-radius: 0px !important;
/* Animation */
-webkit-animation: moveUp .3s !important;
-moz-animation: moveUp .3s !important;
animation: moveUp .3s !important;
}
}

@ -0,0 +1,198 @@
/**
* @name message.css
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
.message_overlay {
position: fixed;
width: 100%;
height: 100%;
top: 0px;
left: 0px;
background-color: rgba(0,0,0,.85);
z-index: 1000;
}
.message {
position: absolute;
display: inline-block;
width: 500px;
margin-left: -250px;
margin-top: -95px;
background-color: #444;
background-image: -webkit-linear-gradient(top, rgb(75, 75, 75), rgb(45, 45, 45));
background-image: -moz-linear-gradient(top, rgb(75, 75, 75), rgb(45, 45, 45));
background-image: -ms-linear-gradient(top, rgb(75, 75, 75), rgb(45, 45, 45));
background-image: linear-gradient(top, rgb(75, 75, 75), rgb(45, 45, 45));
border-radius: 5px;
box-shadow: 0px 0px 5px #000, inset 0px 1px 0px rgba(255,255,255,.08), inset 1px 0px 0px rgba(255,255,255,.03), inset -1px 0px 0px rgba(255,255,255,.03);
/* Animation */
-webkit-animation-name: moveUp;
-webkit-animation-duration: .3s;
-webkit-animation-timing-function: ease-out;
-moz-animation-name: moveUp;
-moz-animation-duration: .3s;
-moz-animation-timing-function: ease-out;
animation-name: moveUp;
animation-duration: .3s;
animation-timing-function: ease-out;
}
/* Header ------------------------------------------------*/
.message h1 {
float: left;
width: 100%;
padding: 12px 0px;
color: #fff;
font-size: 16px;
font-weight: bold;
text-shadow: 0px -1px 0px #222;
text-align: center;
}
.message .close {
position: absolute;
top: 0px;
right: 0px;
padding: 12px 14px 6px 7px;
color: #aaa;
font-size: 20px;
text-shadow: 0px -1px 0px #222;
cursor: pointer;
}
.message .close:hover {
color: #fff;
}
/* Text ------------------------------------------------*/
.message p {
float: left;
width: 90%;
margin-top: 1px;
padding: 12px 5% 15px 5%;
color: #eee;
font-size: 14px;
text-shadow: 0px -1px 0px #222;
line-height: 20px;
}
.message p b {
font-weight: bold;
}
.message p a {
color: #eee;
text-decoration: none;
border-bottom: 1px dashed #888;
}
/* Button ------------------------------------------------*/
.message .button {
float: right;
margin: 15px 15px 15px 0px;
padding: 6px 10px 8px 10px;
background-color: #4e4e4e;
background-image: -webkit-linear-gradient(top, rgb(60, 60, 60), rgb(45, 45, 45));
background-image: -moz-linear-gradient(top, rgb(60, 60, 60), rgb(45, 45, 45));
background-image: -ms-linear-gradient(top, rgb(60, 60, 60), rgb(45, 45, 45));
background-image: linear-gradient(top, rgb(60, 60, 60), rgb(45, 45, 45));
color: #ccc;
font-size: 14px;
font-weight: bold;
text-align: center;
text-shadow: 0px -1px 0px #222;
border-radius: 5px;
border: 1px solid #191919;
box-shadow: inset 0px 1px 0px rgba(255,255,255,.1), 0px 1px 0px rgba(255,255,255,.1);
cursor: pointer;
}
.message .button:first-of-type {
margin: 15px 5% 18px 0px !important;
}
.message .button.active {
color: #fff;
box-shadow: inset 0px 1px 0px rgba(255,255,255,.1), 0px 1px 0px rgba(255,255,255,.1), 0px 0px 4px #005ecc;
}
.message .button:hover {
background-color: #565757;
background-image: -webkit-linear-gradient(top, rgb(80, 80, 80), rgb(57, 57, 57));
background-image: -moz-linear-gradient(top, rgb(80, 80, 80), rgb(57, 57, 57));
background-image: -ms-linear-gradient(top, rgb(80, 80, 80), rgb(57, 57, 57));
background-image: linear-gradient(top, rgb(80, 80, 80), rgb(57, 57, 57));
}
.message .button:active,
.message .button.pressed {
background-color: #393939;
background-image: -webkit-linear-gradient(top, rgb(57, 57, 57), rgb(70, 70, 70));
background-image: -moz-linear-gradient(top, rgb(57, 57, 57), rgb(70, 70, 70));
background-image: -ms-linear-gradient(top, rgb(57, 57, 57), rgb(70, 70, 70));
background-image: linear-gradient(top, rgb(57, 57, 57), rgb(70, 70, 70));
}
/* Sign in ------------------------------------------------*/
.sign_in {
float: left;
width: 100%;
margin-top: 1px;
padding: 5px 0px;
color: #eee;
font-size: 14px;
text-shadow: 0px -1px 0px #222;
line-height: 20px;
}
.sign_in input {
float: left;
width: 88%;
padding: 7px 1% 9px 1%;
margin: 0px 5%;
background-color: transparent;
color: #fff;
text-shadow: 0px -1px 0px #222;
border: none;
border-bottom: 1px solid #222;
box-shadow: 0px 1px 0px rgba(255,255,255,.1);
border-radius: 0px;
outline: none;
}
.sign_in input:first-of-type {
margin-bottom: 10px;
}
.sign_in input.error:focus {
box-shadow: 0px 1px 0px rgba(204, 0, 7, 0.6);
}
.message #version {
display: inline-block;
margin-top: 23px;
margin-left: 5%;
color: #888;
text-shadow: 0px -1px 0px #111;
}
.message #version span {
display: none;
}
.message #version span a {
color: #888;
}
/* Input Misc ------------------------------------------------*/
.message input.text {
float: left;
width: calc(100% - 10px);
padding: 17px 5px 9px 5px;
margin-top: 10px;
background-color: transparent;
color: #fff;
text-shadow: 0px -1px 0px #222;
border: none;
box-shadow: 0px 1px 0px rgba(255,255,255,.1);
border-bottom: 1px solid #222;
border-radius: 0px;
outline: none;
}
.message input.less {
margin-bottom: -10px;
}
.message input.more {
margin-bottom: 30px;
}
.message .copylink {
margin-bottom: 20px;
}

@ -0,0 +1,35 @@
/**
* @name misc.css
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
html,
body {
min-height: 100%;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
body {
background-color: #222;
font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 12px;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-smoothing: antialiased;
}
body.view {
background-color: #0f0f0f;
}
.center {
position: absolute;
left: 50%;
top:50%;
}
* {
-webkit-transition: color .3s, opacity .3s ease-out, -webkit-transform .3s ease-out, box-shadow .3s;
-moz-transition: opacity .3s ease-out, -moz-transform .3s ease-out, box-shadow .3s;
transition: color .3s, opacity .3s ease-out, transform .3s ease-out, box-shadow .3s;
}

@ -0,0 +1,39 @@
/**
* @name tooltip.css
*/
.tipsy {
padding: 4px;
font-size: 12px;
position: absolute;
z-index: 100000;
/* Animation */
-webkit-animation-name: fadeIn;
-webkit-animation-duration: .3s;
-moz-animation-name: fadeIn;
-moz-animation-duration: .3s;
animation-name: fadeIn;
animation-duration: .3s;
}
.tipsy-inner {
padding: 8px 10px 7px;
color: #fff;
max-width: 200px;
text-align: center;
background: rgba(0, 0, 0, .8);
border-radius: 25px;
}
.tipsy-arrow { position: absolute; width: 0; height: 0; line-height: 0; border: 5px dashed rgba(0, 0, 0, .8); }
.tipsy-arrow-n { border-bottom-color: rgba(0, 0, 0, .8); }
.tipsy-arrow-s { border-top-color: rgba(0, 0, 0, .8); }
.tipsy-arrow-e { border-left-color: rgba(0, 0, 0, .8); }
.tipsy-arrow-w { border-right-color: rgba(0, 0, 0, .8); }
.tipsy-n .tipsy-arrow { top: 0px; left: 50%; margin-left: -5px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent; }
.tipsy-nw .tipsy-arrow { top: 0; left: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;}
.tipsy-ne .tipsy-arrow { top: 0; right: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;}
.tipsy-s .tipsy-arrow { bottom: 0; left: 50%; margin-left: -5px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
.tipsy-sw .tipsy-arrow { bottom: 0; left: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
.tipsy-se .tipsy-arrow { bottom: 0; right: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
.tipsy-e .tipsy-arrow { right: 0; top: 50%; margin-top: -5px; border-left-style: solid; border-right: none; border-top-color: transparent; border-bottom-color: transparent; }
.tipsy-w .tipsy-arrow { left: 0; top: 50%; margin-top: -5px; border-right-style: solid; border-left: none; border-top-color: transparent; border-bottom-color: transparent; }

@ -0,0 +1,123 @@
/**
* @name upload.css
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
#upload {
display: none;
}
.upload_overlay {
position: fixed;
width: 100%;
height: 100%;
top: 0px;
left: 0px;
background-color: rgba(0,0,0,.85);
z-index: 1000;
}
.upload_message {
position: absolute;
display: inline-block;
width: 200px;
margin-left: -100px;
margin-top: -85px;
background-color: #444;
background-image: -webkit-linear-gradient(top, rgb(75, 75, 75), rgb(45, 45, 45));
background-image: -moz-linear-gradient(top, rgb(75, 75, 75), rgb(45, 45, 45));
background-image: -ms-linear-gradient(top, rgb(75, 75, 75), rgb(45, 45, 45));
background-image: linear-gradient(top, rgb(75, 75, 75), rgb(45, 45, 45));
border-radius: 5px;
box-shadow: 0px 0px 5px #000, inset 0px 1px 0px rgba(255,255,255,.08), inset 1px 0px 0px rgba(255,255,255,.03), inset -1px 0px 0px rgba(255,255,255,.03);
/* Animation */
-webkit-animation-name: moveUp;
-webkit-animation-duration: .3s;
-webkit-animation-timing-function: ease-out;
-moz-animation-name: moveUp;
-moz-animation-duration: .3s;
-moz-animation-timing-function: ease-out;
animation-name: moveUp;
animation-duration: .3s;
animation-timing-function: ease-out;
}
/* Icon ------------------------------------------------*/
.upload_message a {
float: left;
width: 100%;
margin: 35px 0px 5px 0px;
color: #fff;
font-size: 70px;
text-shadow: 0px 1px 2px rgba(0,0,0,.5);
text-align: center;
-webkit-animation-name: pulse;
-webkit-animation-duration: 2s;
-webkit-animation-timing-function: ease-in-out;
-webkit-animation-iteration-count: infinite;
-moz-animation-name: pulse;
-moz-animation-duration: 2s;
-moz-animation-timing-function: ease-in-out;
-moz-animation-iteration-count: infinite;
animation-name: pulse;
animation-duration: 2s;
animation-timing-function: ease-in-out;
animation-iteration-count: infinite;
}
/* Text ------------------------------------------------*/
.upload_message p {
float: left;
width: 100%;
margin: 10px 0px 35px 0px;
color: #fff;
font-size: 14px;
text-align: center;
text-shadow: 0px -1px 0px rgba(0,0,0,.5);
}
/* Progress ------------------------------------------------*/
.upload_message .progressbar {
float: left;
width: 170px;
height: 25px;
margin: 15px;
background-size: 50px 25px;
background-repeat: repeat-x;
background-image: -webkit-linear-gradient(left, #191919 0%, #191919 47%, #1D1D1D 53%, #1D1D1D 100%);
background-image: -moz-linear-gradient(left, #191919 0%, #191919 47%, #1D1D1D 53%, #1D1D1D 100%);
background-image: linear-gradient(left right, #191919 0%, #191919 47%, #1D1D1D 53%, #1D1D1D 100%);
border: 1px solid #090909;
box-shadow: 0 1px 0 rgba(255,255,255,.06), inset 0px 0px 2px #222;
border-radius: 50px;
/* Animation */
-webkit-animation-name: moveBackground;
-webkit-animation-duration: 1s;
-webkit-animation-timing-function: linear;
-webkit-animation-iteration-count: infinite;
-moz-animation-name: moveBackground;
-moz-animation-duration: 1s;
-moz-animation-timing-function: linear;
-moz-animation-iteration-count: infinite;
animation-name: moveBackground;
animation-duration: 1s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
.upload_message .progressbar div {
float: left;
width: 0%;
height: 100%;
box-shadow: 0 1px 0 #000, 1px 0px 2px #000;
background-color: #f5f2f7;
background-image: -webkit-linear-gradient(top, #f5f2f7, #c7c6c8);
background-image: -moz-linear-gradient(top, #f5f2f7, #c7c6c8);
background-image: -ms-linear-gradient(top, #f5f2f7, #c7c6c8);
background-image: linear-gradient(top, #f5f2f7, #c7c6c8);
border-radius: 50px;
-webkit-transition: width .2s, opacity .5;
-moz-transition: width .2s, opacity .5;
transition: width .2s, opacity .5;
}

Before

Width:  |  Height:  |  Size: 127 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

@ -0,0 +1 @@
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"><title>no_images</title><g sketch:type="MSLayerGroup" fill="none"><rect fill="#222" sketch:type="MSShapeGroup" width="200" height="200"/><g transform="translate(68 52)" sketch:type="MSShapeGroup"><rect stroke="#B4B4B4" stroke-width="4" width="50" height="42" rx="3"/><rect fill="#222" x="11" y="11" width="44" height="36"/><rect stroke="#B4B4B4" stroke-width="4" x="20" y="20" width="50" height="42" rx="3"/></g></g></svg>

After

Width:  |  Height:  |  Size: 542 B

@ -0,0 +1 @@
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"><title>password</title><g sketch:type="MSLayerGroup" fill="none"><rect fill="#222" sketch:type="MSShapeGroup" width="200" height="200"/><path d="M118 73h2.999c4.413 0 8.001 3.579 8.001 7.994v28.013c0 4.412-3.582 7.994-8.001 7.994h-40.999c-4.413 0-8.001-3.579-8.001-7.994v-28.013c0-4.412 3.582-7.994 8.001-7.994h2.999v-9.495c0-9.665 7.835-17.505 17.5-17.505 9.667 0 17.5 7.837 17.5 17.505v9.495zm-17.5-20c-5.799 0-10.5 4.702-10.5 10.498v9.502h21v-9.502c0-5.798-4.7-10.498-10.5-10.498zm0 0" fill="#B4B4B4" sketch:type="MSShapeGroup"/></g></svg>

After

Width:  |  Height:  |  Size: 662 B

File diff suppressed because one or more lines are too long

@ -1,11 +1,8 @@
/**
* @name album.js
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2013 by Philipp Maurer, Tobias Reich
*
* Album Module
* Takes care of every action an album can handle and execute.
* @name Album Module
* @description Takes care of every action an album can handle and execute.
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
album = {
@ -33,8 +30,7 @@ album = {
var startTime,
params,
durationTime,
waitTime,
photosData = "";
waitTime;
password.get(albumID, function() {
@ -47,14 +43,14 @@ album = {
startTime = new Date().getTime();
params = "getAlbum&albumID=" + albumID + "&password=" + password.value;
lychee.api(params, "json", function(data) {
lychee.api(params, function(data) {
if (data=="HTTP/1.1 403 Album private!") {
if (data==="Warning: Album private!") {
lychee.setMode("view");
return false;
}
if (data=="HTTP/1.1 403 Wrong password!") {
if (data==="Warning: Wrong password!") {
album.load(albumID, refresh);
return false;
}
@ -66,14 +62,14 @@ album = {
if (!visible.albums()&&!visible.photo()&&!visible.album()) waitTime = 0;
setTimeout(function() {
view.album.init();
if (!refresh) {
lychee.animate(".album, .photo", "contentZoomIn");
view.header.mode("album");
}
}, waitTime);
});
@ -91,22 +87,35 @@ album = {
add: function() {
var title = prompt("Please enter a title for this album:", "Untitled"),
params;
var title,
params,
buttons;
if (title.length>0&&title.length<31) {
buttons = [
["Create Album", function() {
modal.close();
title = $(".message input.text").val();
params = "addAlbum&title=" + escape(encodeURI(title));
lychee.api(params, "text", function(data) {
if (title==="") title = "Untitled";
if (data) lychee.goto("a" + data);
else loadingBar.show("error");
if (title.length>0&&title.length<31) {
});
modal.close();
} else if (title.length>0) loadingBar.show("error", "Error", "Title to short or too long. Please try another one!");
params = "addAlbum&title=" + escape(encodeURI(title));
lychee.api(params, function(data) {
if (data!==false) lychee.goto(data);
else lychee.error(null, params, data);
});
} else loadingBar.show("error", "Title too short or too long. Please try again!");
}],
["Cancel", function() {}]
];
modal.show("New Album", "Please enter a title for this album: <input class='text' type='text' placeholder='Title' value='Untitled'>", buttons);
},
@ -120,12 +129,14 @@ album = {
["Delete Album and Photos", function() {
params = "deleteAlbum&albumID=" + albumID + "&delAll=true";
lychee.api(params, "text", function(data) {
lychee.api(params, function(data) {
if (visible.albums()) view.albums.content.delete(albumID);
else lychee.goto("");
if (visible.albums()) {
albums.json.num--;
view.albums.content.delete(albumID);
} else lychee.goto("");
if (!data) loadingBar.show("error");
if (data!==true) lychee.error(null, params, data);
});
@ -133,7 +144,7 @@ album = {
["Keep Album", function() {}]
];
if (albumID==0) {
if (albumID==="0") {
buttons[0][0] = "Clear Unsorted";
modal.show("Clear Unsorted", "Are you sure you want to delete all photos from 'Unsorted'?<br>This action can't be undone!", buttons)
@ -152,66 +163,113 @@ album = {
var oldTitle = "",
newTitle,
params;
params,
buttons;
if (!albumID) albumID = album.getID();
if (!albumID) return false;
if (album.json) oldTitle = album.json.title;
else if (albums.json) oldTitle = albums.json.content[albumID].title;
newTitle = prompt("Please enter a new title for this album:", oldTitle);
buttons = [
["Set Title", function() {
if (albumID!=""&&albumID!=null&&albumID&&newTitle.length>0&&newTitle.length<31) {
newTitle = $(".message input.text").val();
if (visible.album()) {
if (newTitle==="") newTitle = "Untitled";
album.json.title = newTitle;
view.album.title();
if (albumID!==""&&albumID!=null&&albumID&&newTitle.length<31) {
} else if (visible.albums()) {
if (visible.album()) {
albums.json.content[albumID].title = newTitle;
view.albums.content.title(albumID);
album.json.title = newTitle;
view.album.title(oldTitle);
}
} else if (visible.albums()) {
params = "setAlbumTitle&albumID=" + albumID + "&title=" + escape(encodeURI(newTitle));
lychee.api(params, "text", function(data) {
albums.json.content[albumID].title = newTitle;
view.albums.content.title(albumID);
if (!data) loadingBar.show("error");
}
});
params = "setAlbumTitle&albumID=" + albumID + "&title=" + escape(encodeURI(newTitle));
lychee.api(params, function(data) {
if (data!==true) lychee.error(null, params, data);
});
} else if (newTitle.length>0) loadingBar.show("error", "New title too short or too long. Please try again!");
}],
["Cancel", function() {}]
];
modal.show("Set Title", "Please enter a new title for this album: <input class='text' type='text' placeholder='Title' value='" + oldTitle + "'>", buttons);
},
setDescription: function(photoID) {
var oldDescription = album.json.description,
description,
params,
buttons;
buttons = [
["Set Description", function() {
description = $(".message input.text").val();
if (description.length<800) {
if (visible.album()) {
album.json.description = description;
view.album.description();
}
params = "setAlbumDescription&albumID=" + photoID + "&description=" + escape(description);
lychee.api(params, function(data) {
if (data!==true) lychee.error(null, params, data);
} else if (newTitle.length>0) loadingBar.show("error", "Error", "New title to short or too long. Please try another one!");
});
} else loadingBar.show("error", "Description too long. Please try again!");
}],
["Cancel", function() {}]
];
modal.show("Set Description", "Please enter a description for this album: <input class='text' type='text' placeholder='Description' value='" + oldDescription + "'>", buttons);
},
setPublic: function(albumID, e) {
var params;
if ($("input.password").length>0&&$("input.password").val().length>0) {
params = "setAlbumPublic&albumID=" + albumID + "&password=" + hex_md5($("input.password").val());
if ($(".message input.text").length>0&&$(".message input.text").val().length>0) {
params = "setAlbumPublic&albumID=" + albumID + "&password=" + hex_md5($(".message input.text").val());
album.json.password = true;
} else {
params = "setAlbumPublic&albumID=" + albumID;
album.json.password = false;
}
if (visible.album()) {
album.json.public = (album.json.public==0) ? 1 : 0;
view.album.public();
view.album.password();
if (album.json.public==1) contextMenu.shareAlbum(albumID, e);
}
lychee.api(params, "text", function(data) {
if (!data) loadingBar.show("error");
lychee.api(params, function(data) {
if (data!==true) lychee.error(null, params, data);
});
@ -247,6 +305,9 @@ album = {
if (location.href.indexOf("index.html")>0) link = location.href.replace(location.hash, "").replace("index.html", "php/api.php?function=getAlbumArchive&albumID=" + albumID);
else link = location.href.replace(location.hash, "") + "php/api.php?function=getAlbumArchive&albumID=" + albumID;
if (lychee.publicMode) link += "&password=" + password.value;
location.href = link;
}

@ -1,11 +1,8 @@
/**
* @name albums.js
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2013 by Philipp Maurer, Tobias Reich
*
* Albums Module
* Takes care of every action albums can handle and execute.
* @name Albums Module
* @description Takes care of every action albums can handle and execute.
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
albums = {
@ -16,18 +13,14 @@ albums = {
var startTime,
durationTime,
unsortedAlbum,
starredAlbum,
publicAlbum,
smartData = "",
albumsData = "";
waitTime;
lychee.animate(".album, .photo", "contentZoomOut");
lychee.animate(".divider", "fadeOut");
startTime = new Date().getTime();
lychee.api("getAlbums", "json", function(data) {
lychee.api("getAlbums", function(data) {
/* Smart Albums */
data.unsortedAlbum = {
@ -81,13 +74,13 @@ albums = {
parse: function(album) {
if (album.password&&lychee.publicMode) {
album.thumb0 = "img/password.png";
album.thumb1 = "img/password.png";
album.thumb2 = "img/password.png";
album.thumb0 = "assets/img/password.svg";
album.thumb1 = "assets/img/password.svg";
album.thumb2 = "assets/img/password.svg";
} else {
if (album.thumb0) album.thumb0 = lychee.upload_path_thumb + album.thumb0; else album.thumb0 = "img/no_images.png";
if (album.thumb1) album.thumb1 = lychee.upload_path_thumb + album.thumb1; else album.thumb1 = "img/no_images.png";
if (album.thumb2) album.thumb2 = lychee.upload_path_thumb + album.thumb2; else album.thumb2 = "img/no_images.png";
if (album.thumb0) album.thumb0 = lychee.upload_path_thumb + album.thumb0; else album.thumb0 = "assets/img/no_images.svg";
if (album.thumb1) album.thumb1 = lychee.upload_path_thumb + album.thumb1; else album.thumb1 = "assets/img/no_images.svg";
if (album.thumb2) album.thumb2 = lychee.upload_path_thumb + album.thumb2; else album.thumb2 = "assets/img/no_images.svg";
}
}

@ -0,0 +1,410 @@
/**
* @name Build Module
* @description This module is used to generate HTML-Code.
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
build = {
divider: function(title) {
return "<div class='divider fadeIn'><h1>" + title + "</h1></div>";
},
editIcon: function(id) {
return "<div id='" + id + "' class='edit'><a class='icon-pencil'></a></div>";
},
album: function(albumJSON) {
if (!albumJSON) return "";
var album = "",
title = albumJSON.title;
if (title.length>18) title = albumJSON.title.substr(0, 18) + "...";
album += "<div class='album' data-id='" + albumJSON.id + "' data-password='" + albumJSON.password + "'>";
album += "<img src='" + albumJSON.thumb2 + "' width='200' height='200' alt='thumb'>";
album += "<img src='" + albumJSON.thumb1 + "' width='200' height='200' alt='thumb'>";
album += "<img src='" + albumJSON.thumb0 + "' width='200' height='200' alt='thumb'>";
album += "<div class='overlay'>";
if (albumJSON.password&&!lychee.publicMode) album += "<h1><span class='icon-lock'></span> " + title + "</h1>";
else album += "<h1>" + title + "</h1>";
album += "<a>" + albumJSON.sysdate + "</a>";
album += "</div>";
if(!lychee.publicMode&&albumJSON.star==1) album += "<a class='badge red icon-star'></a>";
if(!lychee.publicMode&&albumJSON.public==1) album += "<a class='badge red icon-share'></a>";
if(!lychee.publicMode&&albumJSON.unsorted==1) album += "<a class='badge red icon-reorder'></a>";
album += "</div>";
return album;
},
photo: function(photoJSON) {
if (!photoJSON) return "";
var photo = "",
title = photoJSON.title;
if (title.length>18) title = photoJSON.title.substr(0, 18) + "...";
photo += "<div class='photo' data-album-id='" + photoJSON.album + "' data-id='" + photoJSON.id + "'>";
photo += "<img src='" + photoJSON.thumbUrl + "' width='200' height='200' alt='thumb'>";
photo += "<div class='overlay'>";
photo += "<h1>" + title + "</h1>";
photo += "<a>" + photoJSON.sysdate + "</a>";
photo += "</div>";
if (photoJSON.star==1) photo += "<a class='badge red icon-star'></a>";
if (!lychee.publicMode&&photoJSON.public==1&&album.json.public!=1) photo += "<a class='badge red icon-share'></a>";
photo += "</div>";
return photo;
},
imageview: function(photoJSON, isSmall, visibleControls) {
if (!photoJSON) return "";
var view = "";
view += "<div class='arrow_wrapper previous'><a id='previous' class='icon-caret-left'></a></div>";
view += "<div class='arrow_wrapper next'><a id='next' class='icon-caret-right'></a></div>";
if (isSmall) {
if (visibleControls)
view += "<div id='image' class='small' style='background-image: url(" + photoJSON.url + "); width: " + photoJSON.width + "px; height: " + photoJSON.height + "px; margin-top: -" + parseInt(photoJSON.height/2-20) + "px; margin-left: -" + photoJSON.width/2 + "px;'></div>";
else
view += "<div id='image' class='small' style='background-image: url(" + photoJSON.url + "); width: " + photoJSON.width + "px; height: " + photoJSON.height + "px; margin-top: -" + parseInt(photoJSON.height/2) + "px; margin-left: -" + photoJSON.width/2 + "px;'></div>";
} else {
if (visibleControls)
view += "<div id='image' style='background-image: url(" + photoJSON.url + ")'></div>";
else
view += "<div id='image' style='background-image: url(" + photoJSON.url + "); top: 0px; right: 0px; bottom: 0px; left: 0px;'></div>";
}
return view;
},
no_content: function(typ) {
var no_content = "";
no_content += "<div class='no_content fadeIn'>";
no_content += "<a class='icon icon-" + typ + "'></a>";
if (typ==="search") no_content += "<p>No results</p>";
else if (typ==="picture") no_content += "<p>No public albums</p>";
else if (typ==="cog") no_content += "<p>No Configuration!</p>";
no_content += "</div>";
return no_content;
},
modal: function(title, text, button, marginTop, closeButton) {
var modal = "",
custom_style = "";
if (marginTop) custom_style = "style='margin-top: " + marginTop + "px;'";
modal += "<div class='message_overlay fadeIn'>";
modal += "<div class='message center'" + custom_style + ">";
modal += "<h1>" + title + "</h1>";
if (closeButton!=false) {
modal += "<a class='close icon-remove-sign'></a>";
}
modal += "<p>" + text + "</p>";
$.each(button, function(index) {
if (this[0]!="") {
if (index===0) modal += "<a class='button active'>" + this[0] + "</a>";
else modal += "<a class='button'>" + this[0] + "</a>";
}
});
modal += "</div>";
modal += "</div>";
return modal;
},
signInModal: function() {
var modal = "";
modal += "<div class='message_overlay'>";
modal += "<div class='message center'>";
modal += "<h1><a class='icon-lock'></a> Sign In</h1>";
modal += "<a class='close icon-remove-sign'></a>";
modal += "<div class='sign_in'>";
modal += "<input id='username' type='text' name='' value='' placeholder='username'>";
modal += "<input id='password' type='password' name='' value='' placeholder='password'>";
modal += "</div>";
modal += "<div id='version'>Version " + lychee.version + "<span> &#8211; <a target='_blank' href='" + lychee.updateURL + "'>Update available!</a><span></div>";
modal += "<a onclick='lychee.login()' class='button active'>Sign in</a>";
modal += "</div>";
modal += "</div>";
return modal;
},
uploadModal: function(icon, text) {
var modal = "";
modal += "<div class='upload_overlay fadeIn'>";
modal += "<div class='upload_message center'>";
modal += "<a class='icon-" + icon + "'></a>";
if (text!==undefined) modal += "<p>" + text + "</p>";
else modal += "<div class='progressbar'><div></div></div>";
modal += "</div>";
modal += "</div>";
return modal;
},
contextMenu: function(items, orientation) {
var menu = "";
menu += "<div class='contextmenu_bg'></div>";
menu += "<div class='contextmenu " + orientation + "'>";
menu += "<table>";
menu += "<tbody>";
$.each(items, function(index) {
if (items[index][0]==="separator"&&items[index][1]===-1) menu += "<tr class='separator'></tr>";
else if (items[index][1]===-1) menu += "<tr class='no_hover'><td>" + items[index][0] + "</td></tr>";
else if (items[index][2]!=undefined) menu += "<tr><td onclick='" + items[index][2] + "; window.contextMenu.close();'>" + items[index][0] + "</td></tr>";
else menu += "<tr><td onclick='window.contextMenu.fns[" + items[index][1] + "](); window.contextMenu.close();'>" + items[index][0] + "</td></tr>";
});
menu += "</tbody>";
menu += "</table>";
menu += "</div>";
return menu;
},
infoboxPhoto: function(photoJSON, forView) {
if (!photoJSON) return "";
var infobox = "",
public,
editTitleHTML,
editDescriptionHTML,
infos;
infobox += "<div class='header'><h1>About</h1><a class='icon-remove-sign'></a></div>";
infobox += "<div class='wrapper'>";
switch (photoJSON.public) {
case "0":
public = "Private";
break;
case "1":
public = "Public";
break;
case "2":
public = "Public (Album)";
break;
default:
public = "-";
break;
}
editTitleHTML = (forView===true||lychee.publicMode) ? "" : " " + build.editIcon("edit_title");
editDescriptionHTML = (forView===true||lychee.publicMode) ? "" : " " + build.editIcon("edit_description");
//["Tags", "<a class='tag'>Abstract<span class='icon-remove'></span></a><a class='tag'>Colors<span class='icon-remove'></span></a><a class='tag'>Photoshop<span class='icon-remove'></span></a><a class='tag'>Something<span class='icon-remove'></span></a><a class='tag'>Lychee<span class='icon-remove'></span></a><a class='tag'>Tags<span class='icon-remove'></span></a><a class='tag icon-plus'></a>"]
infos = [
["", "Basics"],
["Name", photoJSON.title + editTitleHTML],
["Uploaded", photoJSON.sysdate],
["Description", photoJSON.description + editDescriptionHTML],
["", "Image"],
["Size", photoJSON.size],
["Format", photoJSON.type],
["Resolution", photoJSON.width + " x " + photoJSON.height]
];
if ((photoJSON.takedate+photoJSON.make+photoJSON.model+photoJSON.shutter+photoJSON.aperture+photoJSON.focal+photoJSON.iso)!="") {
infos = infos.concat([
["", "Camera"],
["Captured", photoJSON.takedate],
["Make", photoJSON.make],
["Type/Model", photoJSON.model],
["Shutter Speed", photoJSON.shutter],
["Aperture", photoJSON.aperture],
["Focal Length", photoJSON.focal],
["ISO", photoJSON.iso]
]);
}
infos = infos.concat([
["", "Share"],
["Visibility", public]
]);
$.each(infos, function(index) {
if (infos[index][1]===""||infos[index][1]==undefined||infos[index][1]==null) infos[index][1] = "-";
switch (infos[index][0]) {
case "": // Separator
infobox += "</table>";
infobox += "<div class='separator'><h1>" + infos[index][1] + "</h1></div>";
infobox += "<table>";
break;
case "Tags": // Tags
infobox += "</table>";
infobox += "<div class='separator'><h1>" + infos[index][0] + "</h1></div>";
infobox += "<tr>";
infobox += "<div id='tags'>" + infos[index][1] + "</div>";
infobox += "</tr>";
break;
default: // Item
infobox += "<tr>";
infobox += "<td>" + infos[index][0] + "</td>";
infobox += "<td class='attr_" + infos[index][0].toLowerCase() + "'>" + infos[index][1] + "</td>";
infobox += "</tr>";
break;
}
});
infobox += "</table>";
infobox += "<div class='bumper'></div>";
infobox += "</div>";
return infobox;
},
infoboxAlbum: function(albumJSON, forView) {
if (!albumJSON) return "";
var infobox = "",
public,
password,
editTitleHTML,
editDescriptionHTML,
infos;
infobox += "<div class='header'><h1>About</h1><a class='icon-remove-sign'></a></div>";
infobox += "<div class='wrapper'>";
switch (albumJSON.public) {
case "0":
public = "Private";
break;
case "1":
public = "Public";
break;
default:
public = "-";
break;
}
switch (albumJSON.password) {
case false:
password = "No";
break;
case true:
password = "Yes";
break;
default:
password = "-";
break;
}
editTitleHTML = (forView===true||lychee.publicMode) ? "" : " " + build.editIcon("edit_title_album");
editDescriptionHTML = (forView===true||lychee.publicMode) ? "" : " " + build.editIcon("edit_description_album");
infos = [
["", "Basics"],
["Name", albumJSON.title + editTitleHTML],
["Description", albumJSON.description + editDescriptionHTML],
["", "Album"],
["Created", albumJSON.sysdate],
["Images", albumJSON.num],
["", "Share"],
["Visibility", public],
["Password", password]
];
$.each(infos, function(index) {
if (infos[index][1]===""||infos[index][1]==undefined||infos[index][1]==null) infos[index][1] = "-";
if (infos[index][0]==="") {
infobox += "</table>";
infobox += "<div class='separator'><h1>" + infos[index][1] + "</h1></div>";
infobox += "<table id='infos'>";
} else {
infobox += "<tr>";
infobox += "<td>" + infos[index][0] + "</td>";
infobox += "<td class='attr_" + infos[index][0].toLowerCase() + "'>" + infos[index][1] + "</td>";
infobox += "</tr>";
}
});
infobox += "</table>";
infobox += "<div class='bumper'></div>";
infobox += "</div>";
return infobox;
}
}

@ -1,26 +1,101 @@
/**
* @name contextMenu.js
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2013 by Philipp Maurer, Tobias Reich
*
* ContextMenu Module
* This module is used for the context menu.
* @name ContextMenu Module
* @description This module is used for the context menu.
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
contextMenu = {
fns: null,
album: function(e) {
show: function(items, mouse_x, mouse_y, orientation) {
contextMenu.close();
$("body")
.css("overflow", "hidden")
.append(build.contextMenu(items, orientation));
if (orientation==="left") mouse_x -= $(".contextmenu").outerWidth(true);
if (!mouse_x||!mouse_y) {
mouse_x = "10px";
mouse_y = "10px";
}
$(".contextmenu").css({
"top": mouse_y,
"left": mouse_x
});
},
add: function(e) {
var mouse_x = e.pageX,
mouse_y = e.pageY,
items;
mouse_y -= $(document).scrollTop();
contextMenu.fns = [
function() { $("#upload_files").click() },
function() { upload.start.url() },
function() { upload.start.dropbox() },
function() { upload.start.server() },
function() { album.add() }
];
items = [
["<a class='icon-picture'></a> Upload Photo", 0],
["separator", -1],
["<a class='icon-link'></a> Import from Link", 1],
["<a class='icon-folder-open'></a> Import from Dropbox", 2],
["<a class='icon-hdd'></a> Import from Server", 3],
["separator", -1],
["<a class='icon-folder-close'></a> New Album", 4]
];
contextMenu.show(items, mouse_x, mouse_y, "left");
},
settings: function(e) {
var mouse_x = e.pageX,
mouse_y = e.pageY,
items;
mouse_y -= $(document).scrollTop();
contextMenu.fns = [
function() { settings.setLogin() },
function() { settings.setSorting() },
function() { window.open(lychee.website,"_newtab"); },
function() { lychee.logout() }
];
items = [
["<a class='icon-user'></a> Change Login", 0],
["<a class='icon-sort'></a> Change Sorting", 1],
["<a class='icon-info-sign'></a> About Lychee", 2],
["separator", -1],
["<a class='icon-signout'></a> Sign Out", 3]
];
contextMenu.show(items, mouse_x, mouse_y, "right");
},
album: function(albumID, e) {
e.preventDefault();
var mouse_x = e.pageX,
mouse_y = e.pageY,
albumID = album.getID(),
items;
if (albumID=="0"||albumID=="f"||albumID=="s") return false;
if (albumID==="0"||albumID==="f"||albumID==="s") return false;
mouse_y -= $(document).scrollTop();
@ -34,24 +109,17 @@ contextMenu = {
["<a class='icon-trash'></a> Delete", 1]
];
contextMenu.close();
contextMenu.show(items, mouse_x, mouse_y, "right");
$(".album[data-id='" + albumID + "']").addClass("active");
$("body")
.css("overflow", "hidden")
.append(build.contextMenu(items));
$(".contextmenu").css({
"top": mouse_y,
"left": mouse_x
});
},
photo: function(e) {
photo: function(photoID, e) {
e.preventDefault();
var mouse_x = e.pageX,
mouse_y = e.pageY,
photoID = photo.getID(),
items;
mouse_y -= $(document).scrollTop();
@ -59,7 +127,7 @@ contextMenu = {
contextMenu.fns = [
function() { photo.setStar(photoID) },
function() { photo.setTitle(photoID) },
function() { contextMenu.move(photoID, e) },
function() { contextMenu.move(photoID, e, "right") },
function() { photo.delete(photoID) }
];
@ -71,41 +139,36 @@ contextMenu = {
["<a class='icon-trash'></a> Delete", 3]
];
contextMenu.close();
contextMenu.show(items, mouse_x, mouse_y, "right");
$(".photo[data-id='" + photoID + "']").addClass("active");
$("body")
.css("overflow", "hidden")
.append(build.contextMenu(items));
$(".contextmenu").css({
"top": mouse_y,
"left": mouse_x
});
},
move: function(photoID, e) {
move: function(photoID, e, orientation) {
var mouse_x = e.pageX,
mouse_y = e.pageY,
items = [],
albumID;
items = [];
contextMenu.fns = [];
mouse_y -= $(document).scrollTop();
if (orientation===undefined) orientation = "left";
if (!mouse_x||!mouse_y) {
mouse_x = "10px";
mouse_y = "10px";
}
if (album.getID()!=0) {
if (album.getID()!=="0") {
items = [
["Unsorted", 0, "photo.setAlbum(0, " + photoID + ")"],
["separator", -1]
];
}
lychee.api("getAlbums", "json", function(data) {
lychee.api("getAlbums", function(data) {
if (!data.albums) {
items = [["New Album", 0, "album.add()"]];
@ -119,7 +182,7 @@ contextMenu = {
$(".photo[data-id='" + photoID + "']").addClass("active");
$("body")
.css("overflow", "hidden")
.append(build.contextMenu(items));
.append(build.contextMenu(items, orientation));
if (!visible.photo()) mouse_x += $(".contextmenu").width();
$(".contextmenu").css({
"top": mouse_y,
@ -138,11 +201,6 @@ contextMenu = {
mouse_y -= $(document).scrollTop();
if (!mouse_x||!mouse_y) {
mouse_x = "10px";
mouse_y = "10px";
}
contextMenu.fns = [
function() { photo.setPublic(photoID) },
function() { photo.share(photoID, 0) },
@ -151,40 +209,18 @@ contextMenu = {
function() { photo.share(photoID, 3) }
];
if (document.location.hostname!="localhost") {
items = [
["<input readonly id='link' value='" + photo.getViewLink(photoID) + "'>", -1],
["separator", -1],
["<a class='icon-eye-close'></a> Make Private", 0],
["separator", -1],
["<a class='icon-twitter'></a> Twitter", 1],
["<a class='icon-facebook'></a> Facebook", 2],
["<a class='icon-envelope'></a> Mail", 3],
["<a class='icon-hdd'></a> Dropbox", 4]
];
} else {
items = [
["<input readonly id='link' value='" + photo.getViewLink(photoID) + "'>", -1],
["separator", -1],
["<a class='icon-eye-close'></a> Make Private", 0],
["separator", -1],
["<a class='icon-envelope'></a> Mail", 3]
];
}
contextMenu.close();
$("body")
.css("overflow", "hidden")
.append(build.contextMenu(items));
$(".contextmenu").css({
"top": mouse_y,
"left": mouse_x+20-$(".contextmenu").width()
});
items = [
["<input readonly id='link' value='" + photo.getViewLink(photoID) + "'>", -1],
["separator", -1],
["<a class='icon-eye-close'></a> Make Private", 0],
["separator", -1],
["<a class='icon-twitter'></a> Twitter", 1],
["<a class='icon-facebook'></a> Facebook", 2],
["<a class='icon-envelope'></a> Mail", 3],
["<a class='icon-hdd'></a> Dropbox", 4]
];
contextMenu.show(items, mouse_x, mouse_y, "left");
$(".contextmenu input").focus();
},
@ -197,11 +233,6 @@ contextMenu = {
mouse_y -= $(document).scrollTop();
if (!mouse_x||!mouse_y) {
mouse_x = "10px";
mouse_y = "10px";
}
contextMenu.fns = [
function() { album.setPublic(albumID) },
function() { password.set(albumID) },
@ -211,43 +242,20 @@ contextMenu = {
function() { password.remove(albumID) }
];
if (document.location.hostname!="localhost") {
items = [
["<input readonly id='link' value='" + location.href + "'>", -1],
["separator", -1],
["<a class='icon-eye-close'></a> Make Private", 0],
["<a class='icon-lock'></a> Set Password", 1],
["separator", -1],
["<a class='icon-twitter'></a> Twitter", 2],
["<a class='icon-facebook'></a> Facebook", 3],
["<a class='icon-envelope'></a> Mail", 4],
];
} else {
items = [
["<input readonly id='link' value='" + location.href + "'>", -1],
["separator", -1],
["<a class='icon-eye-close'></a> Make Private", 0],
["<a class='icon-lock'></a> Set Password", 1],
["separator", -1],
["<a class='icon-envelope'></a> Mail", 4],
];
}
items = [
["<input readonly id='link' value='" + location.href + "'>", -1],
["separator", -1],
["<a class='icon-eye-close'></a> Make Private", 0],
["<a class='icon-lock'></a> Set Password", 1],
["separator", -1],
["<a class='icon-twitter'></a> Twitter", 2],
["<a class='icon-facebook'></a> Facebook", 3],
["<a class='icon-envelope'></a> Mail", 4],
];
if (album.json.password==true) items[3] = ["<a class='icon-unlock'></a> Remove Password", 5];
contextMenu.close();
$("body")
.css("overflow", "hidden")
.append(build.contextMenu(items));
$(".contextmenu").css({
"top": mouse_y,
"left": mouse_x+20-$(".contextmenu").width()
});
contextMenu.show(items, mouse_x, mouse_y, "left");
$(".contextmenu input").focus();
},

@ -0,0 +1,138 @@
/**
* @name Init Module
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
$(document).ready(function(){
/* Event Name */
var event_name = (mobileBrowser()) ? "touchend" : "click";
/* Notifications */
if (window.webkitNotifications) window.webkitNotifications.requestPermission();
/* Tooltips */
if (!mobileBrowser()) $(".tools").tipsy({gravity: 'n', fade: false, delayIn: 0, opacity: 1});
/* Header */
$("#hostedwith").on(event_name, function() { window.open(lychee.website,"_newtab") });
$("#button_signin").on(event_name, lychee.loginDialog);
$("#button_settings").on(event_name, contextMenu.settings);
$("#button_share").on(event_name, function(e) {
if (photo.json.public==1||photo.json.public==2) contextMenu.sharePhoto(photo.getID(), e);
else photo.setPublic(photo.getID(), e);
});
$("#button_share_album").on(event_name, function(e) {
if (album.json.public==1) contextMenu.shareAlbum(album.getID(), e);
else modal.show("Share Album", "All photos inside this album will be public and visible for everyone. Existing public photos will have the same sharing permission as this album. Are your sure you want to share this album? <input class='text' type='password' placeholder='password (optional)' value=''>", [["Share Album", function() { album.setPublic(album.getID(), e) }], ["Cancel", function() {}]]);
});
$("#button_download").on(event_name, function() { window.open(photo.getDirectLink(),"_newtab") });
$("#button_trash_album").on(event_name, function() { album.delete(album.getID()) });
$("#button_move").on(event_name, function(e) { contextMenu.move(photo.getID(), e) });
$("#button_trash").on(event_name, function() { photo.delete(photo.getID()) });
$("#button_info_album").on(event_name, function() { view.infobox.show() });
$("#button_info").on(event_name, function() { view.infobox.show() });
$("#button_archive").on(event_name, function() { album.getArchive(album.getID()) });
$("#button_star").on(event_name, function() { photo.setStar(photo.getID()) });
/* Search */
$("#search").on("keyup click", function() { search.find($(this).val()) });
/* Back Buttons */
$("#button_back_home").on(event_name, function() { lychee.goto("") });
$("#button_back").on(event_name, function() { lychee.goto(album.getID()) });
/* Image View */
lychee.imageview
.on(event_name, ".arrow_wrapper.previous", function() {
if (album.json&&album.json.content[photo.getID()]&&album.json.content[photo.getID()].previousPhoto!=="") lychee.goto(album.getID() + "/" + album.json.content[photo.getID()].previousPhoto)
})
.on(event_name, ".arrow_wrapper.next", function() {
if (album.json&&album.json.content[photo.getID()]&&album.json.content[photo.getID()].nextPhoto!=="") lychee.goto(album.getID() + "/" + album.json.content[photo.getID()].nextPhoto)
});
/* Infobox */
$("#infobox")
.on(event_name, ".header a", function() { view.infobox.hide() })
.on(event_name, "#edit_title_album", function() { album.setTitle(album.getID()) })
.on(event_name, "#edit_description_album", function() { album.setDescription(album.getID()) })
.on(event_name, "#edit_title", function() { photo.setTitle(photo.getID()) })
.on(event_name, "#edit_description", function() { photo.setDescription(photo.getID()) });
/* Keyboard */
Mousetrap
.bind('u', function() { $("#upload_files").click() })
.bind('s', function() { if (visible.photo()) $("#button_star").click() })
.bind('f', function() { if (visible.photo()) $("#button_download").click() })
.bind('command+backspace', function() { if (visible.photo()&&!visible.message()) photo.delete(photo.getID()) })
.bind('left', function() { if (visible.photo()) $("#imageview a#previous").click() })
.bind('right', function() { if (visible.photo()) $("#imageview a#next").click() })
.bind('i', function() {
if (visible.infobox()) view.infobox.hide();
else if (!visible.albums()) view.infobox.show();
});
Mousetrap.bindGlobal('enter', function() {
if ($(".message .button.active").length) $(".message .button.active").addClass("pressed").click()
});
Mousetrap.bindGlobal(['esc', 'command+up'], function(e) {
e.preventDefault();
if (visible.message()&&$(".message .close").length>0) modal.close();
else if (visible.contextMenu()) contextMenu.close();
else if (visible.infobox()) view.infobox.hide();
else if (visible.photo()) lychee.goto(album.getID());
else if (visible.album()) lychee.goto("");
else if (visible.albums()&&$("#search").val().length!==0) search.reset();
});
/* Document */
$(document)
/* Login */
.on("keyup", "#password", function() { if ($(this).val().length>0) $(this).removeClass("error") })
/* Header */
.on(event_name, "#title.editable", function() {
if (visible.photo()) photo.setTitle(photo.getID());
else album.setTitle(album.getID());
})
/* Navigation */
.on("click", ".album", function() { lychee.goto($(this).attr("data-id")) })
.on("click", ".photo", function() { lychee.goto(album.getID() + "/" + $(this).attr("data-id")) })
/* Modal */
.on(event_name, ".message .close", modal.close)
.on(event_name, ".message .button:first", function() { if (modal.fns!=null) modal.fns[0](); if (!visible.signin()) modal.close() })
.on(event_name, ".message .button:last", function() { if (modal.fns!=null) modal.fns[1](); if (!visible.signin()) modal.close() })
/* Add Dialog */
.on(event_name, ".button_add", function(e) { contextMenu.add(e) })
/* Upload */
.on("change", "#upload_files", function() { modal.close(); upload.start.local(this.files) })
/* Context Menu */
.on("contextmenu", ".photo", function(e) { contextMenu.photo(photo.getID(), e) })
.on("contextmenu", ".album", function(e) { contextMenu.album(album.getID(), e) })
.on(event_name, ".contextmenu_bg", contextMenu.close)
/* Infobox */
.on(event_name, "#infobox_overlay", view.infobox.hide)
/* Upload */
.on("dragover", function(e) { e.preventDefault(); }, false)
.on("drop", function(e) {
e.stopPropagation();
e.preventDefault();
if (e.originalEvent.dataTransfer.files.length>0) upload.start.local(e.originalEvent.dataTransfer.files);
else if (e.originalEvent.dataTransfer.getData('Text').length>3) upload.start.url(e.originalEvent.dataTransfer.getData('Text'));
return true;
});
/* Init */
lychee.init();
});

@ -1,33 +1,29 @@
/**
* @name loadingBar.js
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2013 by Philipp Maurer, Tobias Reich
*
* LoadingBar Module
* This module is used to show and hide the loading bar.
* @name LoadingBar Module
* @description This module is used to show and hide the loading bar.
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
loadingBar = {
status: null,
show: function(status, errorTitle, errorText) {
show: function(status, errorText) {
if (status=="error") {
if (status==="error") {
loadingBar.status = "error";
if (!errorTitle) errorTitle = "Error";
if (!errorText) errorText = "Whoops, it looks like something went wrong. Please reload the site and try again!"
lychee.loadingBar
.removeClass("loading uploading error")
.addClass(status)
.html("<h1>" + errorTitle + ": <span>" + errorText + "</span></h1>")
.html("<h1>Error: <span>" + errorText + "</span></h1>")
.show()
.css("height", "40px");
if (visible.controls()) lychee.header.css("margin-Top", "40px");
if (visible.controls()) lychee.header.addClass("error");
clearTimeout(lychee.loadingBar.data("timeout"));
lychee.loadingBar.data("timeout", setTimeout(function() { loadingBar.hide(true) }, 3000));
@ -42,7 +38,7 @@ loadingBar = {
.show()
.removeClass("loading uploading error")
.addClass("loading");
if (visible.controls()) lychee.header.css("margin-Top", "3px");
if (visible.controls()) lychee.header.addClass("loading");
}, 1000));
}
@ -51,12 +47,12 @@ loadingBar = {
hide: function(force_hide) {
if ((loadingBar.status!="error"&&loadingBar.status!=null)||force_hide) {
if ((loadingBar.status!=="error"&&loadingBar.status!=null)||force_hide) {
loadingBar.status = null;
clearTimeout(lychee.loadingBar.data("timeout"));
lychee.loadingBar.html("").css("height", "3px");
if (visible.controls()) lychee.header.css("marginTop", "0px");
if (visible.controls()) lychee.header.removeClass("error loading");
setTimeout(function() { lychee.loadingBar.hide() }, 300);
}

@ -1,52 +1,72 @@
/**
* @name lychee.js
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2013 by Philipp Maurer, Tobias Reich
*
* Lychee Module
* This module provides the basic functions of Lychee.
* @name Lychee Module
* @description This module provides the basic functions of Lychee.
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
var lychee = {
init: function() {
version: "2.0",
this.version = "1.3.2";
this.api_path = "php/api.php";
this.update_path = "http://lychee.electerious.com/version/index.php";
this.updateURL = "https://github.com/electerious/Lychee";
api_path: "php/api.php",
update_path: "http://lychee.electerious.com/version/index.php",
updateURL: "https://github.com/electerious/Lychee",
website: "http://lychee.electerious.com",
this.upload_path_thumb = "uploads/thumb/";
this.upload_path_big = "uploads/big/";
upload_path_thumb: "uploads/thumb/",
upload_path_big: "uploads/big/",
this.publicMode = false;
this.viewMode = false;
publicMode: false,
viewMode: false,
debugMode: false,
this.checkForUpdates = false;
username: "",
checkForUpdates: false,
sorting: "",
this.dropbox = false;
dropbox: false,
this.loadingBar = $("#loading");
this.header = $("header");
this.content = $("#content");
this.imageview = $("#imageview");
this.infobox = $("#infobox");
loadingBar: $("#loading"),
header: $("header"),
content: $("#content"),
imageview: $("#imageview"),
infobox: $("#infobox"),
},
init: function() {
lychee.api("init", function(data) {
if (data.loggedIn!==true) {
lychee.setMode("public");
} else {
lychee.username = data.config.username;
lychee.sorting = data.config.sorting;
}
// No configuration
if (data==="Warning: No configuration!") {
lychee.header.hide();
lychee.content.hide();
$("body").append(build.no_content("cog"));
settings.createConfig();
return true;
}
run: function() {
// No login
if (data.config.login===false) {
settings.createLogin();
}
lychee.api("init", "json", function(data) {
lychee.checkForUpdates = data.config.checkForUpdates;
if (!data.loggedIn) lychee.setMode("public");
$(window).bind("popstate", lychee.load);
lychee.load();
});
},
api: function(params, type, callback, loading) {
api: function(params, callback, loading) {
if (loading==undefined) loadingBar.show();
@ -54,13 +74,31 @@ var lychee = {
type: "POST",
url: lychee.api_path,
data: "function=" + params,
dataType: type,
success:
function(data) {
setTimeout(function() { loadingBar.hide() }, 100);
callback(data);
},
error: lychee.error
dataType: "text",
success: function(data) {
setTimeout(function() { loadingBar.hide() }, 100);
if (typeof data==="string"&&data.substring(0, 7)==="Error: ") {
lychee.error(data.substring(7, data.length), params, data);
return false;
}
if (data==="1") data = true;
else if (data==="") data = false;
if (typeof data==="string"&&data.substring(0, 1)==="{"&&data.substring(data.length-1, data.length)==="}") data = $.parseJSON(data);
if (lychee.debugMode) console.log(data);
callback(data);
},
error: function(jqXHR, textStatus, errorThrown) {
lychee.error("Server error or API not found.", params, errorThrown);
}
});
},
@ -72,8 +110,8 @@ var lychee = {
params;
params = "login&user=" + user + "&password=" + password;
lychee.api(params, "text", function(data) {
if (data) {
lychee.api(params, function(data) {
if (data===true) {
localStorage.setItem("username", user);
window.location.reload();
} else {
@ -86,6 +124,8 @@ var lychee = {
loginDialog: function() {
var local_username;
$("body").append(build.signInModal());
$("#username").focus();
if (localStorage) {
@ -95,13 +135,13 @@ var lychee = {
$("#password").focus();
}
}
if (lychee.checkForUpdates) lychee.getUpdate();
if (lychee.checkForUpdates==="1") lychee.getUpdate();
},
logout: function() {
lychee.api("logout", "text", function(data) {
lychee.api("logout", function(data) {
window.location.reload();
});
@ -118,12 +158,12 @@ var lychee = {
var albumID = "",
photoID = "",
hash = document.location.hash.replace("#", "");
hash = document.location.hash.replace("#", "").split("/");
contextMenu.close();
if (hash.indexOf("a")!=-1) albumID = hash.split("p")[0].replace("a", "");
if (hash.indexOf("p")!=-1) photoID = hash.split("p")[1];
if (hash[0]!==undefined) albumID = hash[0];
if (hash[1]!==undefined) photoID = hash[1];
if (albumID&&photoID) {
@ -132,11 +172,10 @@ var lychee = {
photo.json = null;
// Show Photo
if (lychee.content.html()==""||($("#search").length&&$("#search").val().length!=0)) {
if (lychee.content.html()===""||($("#search").length&&$("#search").val().length!==0)) {
lychee.content.hide();
album.load(albumID, true);
}
if (!visible.photo()) view.photo.show();
photo.load(photoID, albumID);
} else if (albumID) {
@ -159,6 +198,7 @@ var lychee = {
search.code = "";
// Show Albums
if (visible.album()) view.album.hide();
if (visible.photo()) view.photo.hide();
albums.load();
@ -175,12 +215,11 @@ var lychee = {
},
setTitle: function(title, count, editable) {
setTitle: function(title, editable) {
if (title=="Albums") document.title = "Lychee";
if (title==="Albums") document.title = "Lychee";
else document.title = "Lychee - " + title;
if (count) title += "<span> - " + count + " photos</span>";
if (editable) $("#title").addClass("editable");
else $("#title").removeClass("editable");
@ -190,8 +229,8 @@ var lychee = {
setMode: function(mode) {
$("#button_signout, #search, #button_trash_album, #button_share_album, #button_edit_album, .button_add, #button_archive, .button_divider").remove();
$("#button_trash, #button_move, #button_edit, #button_share, #button_star").remove();
$("#button_settings, #button_settings, #button_search, #search, #button_trash_album, #button_share_album, .button_add, .button_divider").remove();
$("#button_trash, #button_move, #button_share, #button_star").remove();
$(document)
.on("mouseenter", "#title.editable", function() { $(this).removeClass("editable") })
@ -207,12 +246,12 @@ var lychee = {
.unbind('s')
.unbind('backspace');
if (mode=="public") {
if (mode==="public") {
$("#button_signin").show();
$("header #button_signin, header #hostedwith").show();
lychee.publicMode = true;
} else if (mode=="view") {
} else if (mode==="view") {
Mousetrap.unbind('esc');
$("#button_back, a#next, a#previous").remove();
@ -262,7 +301,7 @@ var lychee = {
g.setAttribute("data-app-key", "iq7lioj9wu0ieqs");
g.onload = g.onreadystatechange = function() {
var rs = this.readyState;
if (rs&&rs!="complete"&&rs!="loaded") return;
if (rs&&rs!=="complete"&&rs!=="loaded") return;
lychee.dropbox = true;
loadingBar.hide();
callback();
@ -273,12 +312,12 @@ var lychee = {
},
error: function(jqXHR, textStatus, errorThrown) {
error: function(errorThrown, params, data) {
console.log(jqXHR);
console.log(textStatus);
console.log(errorThrown);
loadingBar.show("error", textStatus, errorThrown);
console.log("Error Description: " + errorThrown);
console.log("Error Params: " + params);
console.log("Server Response: " + data);
loadingBar.show("error", errorThrown);
}

@ -0,0 +1,35 @@
/**
* @name Modal Module
* @description Build, show and hide a modal.
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
modal = {
fns: null,
show: function(title, text, buttons, marginTop, closeButton) {
if (!buttons) {
var buttons = [
["", function() {}],
["", function() {}]
];
}
modal.fns = [buttons[0][1], buttons[1][1]];
$("body").append(build.modal(title, text, buttons, marginTop, closeButton));
$(".message input:first-child").focus();
},
close: function() {
modal.fns = null;
$(".message_overlay").removeClass("fadeIn").css("opacity", 0);
setTimeout(function() { $(".message_overlay").remove() }, 300);
}
}

@ -1,11 +1,8 @@
/**
* @name password.js
* @author Philipp Maurer
* @name Password Module
* @description Controls the access to password-protected albums and photos.
* @author Tobias Reich
* @copyright 2013 by Philipp Maurer, Tobias Reich
*
* Password Module
* Controls the access to password-protected albums and photos.
* @copyright 2014 by Tobias Reich
*/
password = {
@ -20,25 +17,28 @@ password = {
buttons = [
["Set Password", function() {
if (visible.album()) album.json.password = true;
if (visible.album()) {
album.json.password = true;
view.album.password();
}
params = "setAlbumPassword&albumID=" + albumID + "&password=" + hex_md5($("input.password").val());
lychee.api(params, "text", function(data) {
params = "setAlbumPassword&albumID=" + albumID + "&password=" + hex_md5($(".message input.text").val());
lychee.api(params, function(data) {
if (!data) loadingBar.show("error");
if (data!==true) lychee.error(null, params, data);
});
}],
["Cancel", function() {}]
];
modal.show("Set Password", "Set a password to protect '" + album.json.title + "' from unauthorized viewers. Only people with this password can view this album. <input class='password' type='password' placeholder='password' value=''>", buttons);
modal.show("Set Password", "Set a password to protect '" + album.json.title + "' from unauthorized viewers. Only people with this password can view this album. <input class='text' type='password' placeholder='password' value=''>", buttons);
},
get: function(albumID, callback) {
var passwd = $("input.password").val(),
var passwd = $(".message input.text").val(),
params;
if (!lychee.publicMode) callback();
@ -59,14 +59,14 @@ password = {
// Check password
params = "checkAlbumAccess&albumID=" + albumID + "&password=" + hex_md5(passwd);
lychee.api(params, "text", function(data) {
lychee.api(params, function(data) {
if (data) {
if (data===true) {
password.value = hex_md5(passwd);
callback();
} else {
lychee.goto("");
loadingBar.show("error", "Error", "Access denied. Wrong password!");
loadingBar.show("error", "Access denied. Wrong password!");
}
});
@ -83,7 +83,7 @@ password = {
["Enter", function() { password.get(albumID, callback) }],
["Cancel", lychee.goto]
];
modal.show("<a class='icon-lock'></a> Enter Password", "This album is protected with a password. Enter the password below to view the photos of this album: <input class='password' type='password' placeholder='password' value=''>", buttons);
modal.show("<a class='icon-lock'></a> Enter Password", "This album is protected by a password. Enter the password below to view the photos of this album: <input class='text' type='password' placeholder='password' value=''>", buttons, -110, false);
},
@ -91,12 +91,15 @@ password = {
var params;
if (visible.album()) album.json.password = false;
if (visible.album()) {
album.json.password = false;
view.album.password();
}
params = "setAlbumPassword&albumID=" + albumID + "&password=";
lychee.api(params, "text", function(data) {
lychee.api(params, function(data) {
if (!data) loadingBar.show("error");
if (data!==true) lychee.error(null, params, data);
});

@ -0,0 +1,315 @@
/**
* @name Photo Module
* @description Takes care of every action a photo can handle and execute.
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
photo = {
json: null,
getID: function() {
var id;
if (photo.json) id = photo.json.id;
else id = $(".photo:hover, .photo.active").attr("data-id");
if (id) return id;
else return false;
},
load: function(photoID, albumID) {
var params,
checkPasswd;
params = "getPhoto&photoID=" + photoID + "&albumID=" + albumID + "&password=" + password.value;
lychee.api(params, function(data) {
if (data==="Warning: Wrong password!") {
checkPasswd = function() {
if (password.value!=="") photo.load(photoID, albumID);
else setTimeout(checkPasswd, 250);
}
checkPasswd();
return false;
}
photo.json = data;
if (!visible.photo()) view.photo.show();
view.photo.init();
lychee.imageview.show();
setTimeout(function() { lychee.content.show() }, 300);
});
},
parse: function() {
if (!photo.json.title) photo.json.title = "Untitled";
photo.json.url = lychee.upload_path_big + photo.json.url;
},
delete: function(photoID) {
var params,
buttons,
photoTitle;
if (!photoID) return false;
if (visible.photo()) photoTitle = photo.json.title;
else photoTitle = album.json.content[photoID].title;
if (photoTitle=="") photoTitle = "Untitled";
buttons = [
["Delete Photo", function() {
// Change reference for the next and previous photo
if (album.json.content[photoID].nextPhoto!==""||album.json.content[photoID].previousPhoto!=="") {
nextPhoto = album.json.content[photoID].nextPhoto;
previousPhoto = album.json.content[photoID].previousPhoto;
album.json.content[previousPhoto].nextPhoto = nextPhoto;
album.json.content[nextPhoto].previousPhoto = previousPhoto;
}
album.json.content[photoID] = null;
view.album.content.delete(photoID);
// Only when search is not active
if (!visible.albums()) lychee.goto(album.getID());
params = "deletePhoto&photoID=" + photoID;
lychee.api(params, function(data) {
if (data!==true) lychee.error(null, params, data);
});
}],
["Keep Photo", function() {}]
];
modal.show("Delete Photo", "Are you sure you want to delete the photo '" + photoTitle + "'?<br>This action can't be undone!", buttons);
},
setTitle: function(photoID) {
var oldTitle = "",
newTitle,
params,
buttons;
if (!photoID) return false;
if (photo.json) oldTitle = photo.json.title;
else if (album.json) oldTitle = album.json.content[photoID].title;
buttons = [
["Set Title", function() {
newTitle = $(".message input.text").val();
if (photoID!=null&&photoID&&newTitle.length<31) {
if (visible.photo()) {
photo.json.title = (newTitle==="") ? "Untitled" : newTitle;
view.photo.title(oldTitle);
}
album.json.content[photoID].title = newTitle;
view.album.content.title(photoID);
params = "setPhotoTitle&photoID=" + photoID + "&title=" + escape(encodeURI(newTitle));
lychee.api(params, function(data) {
if (data!==true) lychee.error(null, params, data);
});
} else if (newTitle.length>0) loadingBar.show("error", "New title to short or too long. Please try another one!");
}],
["Cancel", function() {}]
];
modal.show("Set Title", "Please enter a new title for this photo: <input class='text' type='text' placeholder='Title' value='" + oldTitle + "'>", buttons);
},
setAlbum: function(albumID, photoID) {
var params;
if (albumID>=0) {
if (visible.photo) lychee.goto(album.getID());
album.json.content[photoID] = null;
view.album.content.delete(photoID);
params = "setAlbum&photoID=" + photoID + "&albumID=" + albumID;
lychee.api(params, function(data) {
if (data!==true) lychee.error(null, params, data);
});
}
},
setStar: function(photoID) {
var params;
if (visible.photo()) {
photo.json.star = (photo.json.star==0) ? 1 : 0;
view.photo.star();
}
album.json.content[photoID].star = (album.json.content[photoID].star==0) ? 1 : 0;
view.album.content.star(photoID);
params = "setPhotoStar&photoID=" + photoID;
lychee.api(params, function(data) {
if (data!==true) lychee.error(null, params, data);
});
},
setPublic: function(photoID, e) {
var params;
if (photo.json.public==2) {
modal.show("Public Album", "This photo is located in a public album. To make this photo private or public, edit the visibility of the associated album.", [["Show Album", function() { lychee.goto(photo.json.original_album) }], ["Close", function() {}]]);
return false;
}
if (visible.photo()) {
photo.json.public = (photo.json.public==0) ? 1 : 0;
view.photo.public();
if (photo.json.public==1) contextMenu.sharePhoto(photoID, e);
}
album.json.content[photoID].public = (album.json.content[photoID].public==0) ? 1 : 0;
view.album.content.public(photoID);
params = "setPhotoPublic&photoID=" + photoID + "&url=" + photo.getViewLink(photoID);
lychee.api(params, function(data) {
if (data!==true) lychee.error(null, params, data);
});
},
setDescription: function(photoID) {
var oldDescription = photo.json.description,
description,
params,
buttons;
buttons = [
["Set Description", function() {
description = $(".message input.text").val();
if (description.length<800) {
if (visible.photo()) {
photo.json.description = description;
view.photo.description();
}
params = "setPhotoDescription&photoID=" + photoID + "&description=" + escape(description);
lychee.api(params, function(data) {
if (data!==true) lychee.error(null, params, data);
});
} else loadingBar.show("error", "Description too long. Please try again!");
}],
["Cancel", function() {}]
];
modal.show("Set Description", "Please enter a description for this photo: <input class='text' type='text' placeholder='Description' value='" + oldDescription + "'>", buttons);
},
share: function(photoID, service) {
var link = "",
url = photo.getViewLink(photoID),
filename = "unknown";
switch (service) {
case 0:
link = "https://twitter.com/share?url=" + encodeURI(url);
break;
case 1:
link = "http://www.facebook.com/sharer.php?u=" + encodeURI(url) + "&t=" + encodeURI(photo.json.title);
break;
case 2:
link = "mailto:?subject=" + encodeURI(photo.json.title) + "&body=" + encodeURI(url);
break;
case 3:
lychee.loadDropbox(function() {
filename = photo.json.title + "." + photo.getDirectLink().split('.').pop();
Dropbox.save(photo.getDirectLink(), filename);
});
break;
default:
link = "";
break;
}
if (link.length>5) location.href = link;
},
isSmall: function() {
var size = [
["width", false],
["height", false]
];
if (photo.json.width<$(window).width()-60) size["width"] = true;
if (photo.json.height<$(window).height()-100) size["height"] = true;
if (size["width"]&&size["height"]) return true;
else return false;
},
getDirectLink: function() {
return $("#imageview #image").css("background-image").replace(/"/g,"").replace(/url\(|\)$/ig, "");
},
getViewLink: function(photoID) {
if (location.href.indexOf("index.html")>0) return location.href.replace("index.html" + location.hash, "view.php?p=" + photoID);
else return location.href.replace(location.hash, "view.php?p=" + photoID);
}
}

@ -1,11 +1,8 @@
/**
* @name search.js
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2013 by Philipp Maurer, Tobias Reich
*
* Search Module
* Searches through your photos and albums.
* @name Search Module
* @description Searches through your photos and albums.
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
search = {
@ -22,10 +19,10 @@ search = {
clearTimeout($(window).data("timeout"));
$(window).data("timeout", setTimeout(function() {
if ($("#search").val().length!=0) {
if ($("#search").val().length!==0) {
params = "search&term=" + term;
lychee.api(params, "json", function(data) {
lychee.api(params, function(data) {
if (data&&data.albums) {
albums.json = { content: data.albums };
@ -43,12 +40,12 @@ search = {
});
}
if (albumsData==""&&photosData=="") code = "error";
else if (albumsData=="") code = build.divider("Photos")+photosData;
else if (photosData=="") code = build.divider("Albums")+albumsData;
if (albumsData===""&&photosData==="") code = "error";
else if (albumsData==="") code = build.divider("Photos")+photosData;
else if (photosData==="") code = build.divider("Albums")+albumsData;
else code = build.divider("Photos")+photosData+build.divider("Albums")+albumsData;
if (search.code!=hex_md5(code)) {
if (search.code!==hex_md5(code)) {
$(".no_content").remove();
@ -59,7 +56,7 @@ search = {
setTimeout(function() {
if (code=="error") $("body").append(build.no_content("search"));
if (code==="error") $("body").append(build.no_content("search"));
else {
lychee.content.html(code);
lychee.animate(".album, .photo", "contentZoomIn");
@ -82,7 +79,7 @@ search = {
$("#search").val("");
$(".no_content").remove();
if (search.code!="") {
if (search.code!=="") {
search.code = "";
lychee.animate(".divider", "fadeOut");

@ -0,0 +1,238 @@
/**
* @name Settings Module
* @description Lets you change settings.
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
var settings = {
createConfig: function() {
var dbName,
dbUser,
dbPassword,
dbHost,
buttons;
buttons = [
["Connect", function() {
dbHost = $(".message input.text#dbHost").val();
dbUser = $(".message input.text#dbUser").val();
dbPassword = $(".message input.text#dbPassword").val();
dbName = $(".message input.text#dbName").val();
if (dbHost.length<1) dbHost = "localhost";
if (dbName.length<1) dbName = "lychee";
params = "createConfig&dbName=" + escape(dbName) + "&dbUser=" + escape(dbUser) + "&dbPassword=" + escape(dbPassword) + "&dbHost=" + escape(dbHost);
lychee.api(params, function(data) {
if (data!==true) {
// Configuration failed
setTimeout(function() {
// Connection failed
if (data.indexOf("Warning: Connection failed!")!==-1) {
buttons = [
["Retry", function() { setTimeout(settings.createConfig, 400) }],
["", function() {}]
];
modal.show("Connection Failed", "Unable to connect to host database because access was denied. Double-check your host, username and password and ensure that access from your current location is permitted.", buttons, null, false);
return false;
}
// Could not create file
if (data.indexOf("Warning: Could not create file!")!==-1) {
buttons = [
["Retry", function() { setTimeout(settings.createConfig, 400) }],
["", function() {}]
];
modal.show("Saving Failed", "Unable to save this configuration. Permission denied in <b>'php/'</b>. Please set the read, write and execute rights for others in <b>'php/'</b> and <b>'uploads/'</b>. Take a look the readme for more information.", buttons, null, false);
return false;
}
// Something went wrong
buttons = [
["Retry", function() { setTimeout(settings.createConfig, 400) }],
["", function() {}]
];
modal.show("Configuration Failed", "Something unexpected happened. Please try again and check your installation and server. Take a look the readme for more information.", buttons, null, false);
return false;
}, 400);
} else {
// Configuration successful
lychee.api("update", function(data) { window.location.reload() });
}
});
}],
["", function() {}]
];
modal.show("Configuration", "Enter your database connection details below: <input id='dbHost' class='text less' type='text' placeholder='Host (optional)' value=''><input id='dbUser' class='text less' type='text' placeholder='Username' value=''><input id='dbPassword' class='text more' type='password' placeholder='Password' value=''><br>Lychee will create its own database. If required, you can enter the name of an existing database instead:<input id='dbName' class='text more' type='text' placeholder='Database (optional)' value=''>", buttons, -215, false);
},
createLogin: function() {
var username,
password,
params,
buttons;
buttons = [
["Create Login", function() {
username = $(".message input.text#username").val();
password = $(".message input.text#password").val();
if (username.length<1||password.length<1) {
setTimeout(function() {
buttons = [
["Retry", function() { setTimeout(settings.createLogin, 400) }],
["", function() {}]
];
modal.show("Wrong Input", "The username or password you entered is not long enough. Please try again with another username and password!", buttons, null, false);
return false;
}, 400);
} else {
params = "setLogin&username=" + escape(username) + "&password=" + hex_md5(password);
lychee.api(params, function(data) {
if (data!==true) {
setTimeout(function() {
buttons = [
["Retry", function() { setTimeout(settings.createLogin, 400) }],
["", function() {}]
];
modal.show("Creation Failed", "Unable to save login. Please try again with another username and password!", buttons, null, false);
return false;
}, 400);
}
});
}
}],
["", function() {}]
];
modal.show("Create Login", "Enter a username and password for your installation: <input id='username' class='text less' type='text' placeholder='New Username' value=''><input id='password' class='text' type='password' placeholder='New Password' value=''>", buttons, -122, false);
},
setLogin: function() {
var old_password,
username,
password,
params,
buttons;
buttons = [
["Change Login", function() {
old_password = $(".message input.text#old_password").val();
username = $(".message input.text#username").val();
password = $(".message input.text#password").val();
if (old_password.length<1) {
loadingBar.show("error", "Your old password was entered incorrectly. Please try again!");
return false;
}
if (username.length<1) {
loadingBar.show("error", "Your new username was entered incorrectly. Please try again!");
return false;
}
if (password.length<1) {
loadingBar.show("error", "Your new password was entered incorrectly. Please try again!");
return false;
}
params = "setLogin&oldPassword=" + hex_md5(old_password) + "&username=" + escape(username) + "&password=" + hex_md5(password);
lychee.api(params, function(data) {
if (data!==true) lychee.error(null, params, data);
});
}],
["Cancel", function() {}]
];
modal.show("Change Login", "Enter your current password: <input id='old_password' class='text more' type='password' placeholder='Current Password' value=''><br>Your username and password will be changed to the following: <input id='username' class='text less' type='text' placeholder='New Username' value=''><input id='password' class='text' type='password' placeholder='New Password' value=''>", buttons, -171);
},
setSorting: function() {
var buttons,
sorting;
buttons = [
["Change Sorting", function() {
sorting[0] = $("select#settings_type").val();
sorting[1] = $("select#settings_order").val();
params = "setSorting&type=" + sorting[0] + "&order=" + sorting[1];
lychee.api(params, function(data) {
if (data===true) {
lychee.sorting = "ORDER BY " + sorting[0] + " " + sorting[1];
lychee.load();
} else lychee.error(null, params, data);
});
}],
["Cancel", function() {}]
];
modal.show("Change Sorting",
"Sort photos by \
<select id='settings_type'> \
<option value='id'>Upload Time</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> \
in an \
<select id='settings_order'> \
<option value='ASC'>Ascending</option> \
<option value='DESC'>Descending</option> \
</select> \
order.\
", buttons);
if (lychee.sorting!="") {
sorting = lychee.sorting.replace("ORDER BY ", "").replace(" ", ";").split(";");
$("select#settings_type").val(sorting[0]);
$("select#settings_order").val(sorting[1]);
}
}
}

@ -0,0 +1,253 @@
/**
* @name Album Module
* @description Takes care of every action an album can handle and execute.
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
upload = {
show: function(icon, text) {
if (icon===undefined) icon = "upload";
upload.close(true);
$("body").append(build.uploadModal(icon, text));
},
setIcon: function(icon) {
$(".upload_message a").remove();
$(".upload_message").prepend("<a class='icon-" + icon + "'></a>");
},
setProgress: function(progress) {
$(".progressbar div").css("width", progress + "%");
},
setText: function(text) {
$(".progressbar").remove();
$(".upload_message").append("<p>" + text + "</p>");
},
start: {
local: function(files) {
var pre_progress = 0,
formData = new FormData(),
xhr = new XMLHttpRequest(),
albumID = album.getID(),
popup,
progress;
if (files.length<=0) return false;
if (albumID===false) albumID = 0;
formData.append("function", "upload");
formData.append("albumID", albumID);
for (var i = 0; i < files.length; i++) {
if (files[i].type!=="image/jpeg"&&files[i].type!=="image/jpg"&&files[i].type!=="image/png"&&files[i].type!=="image/gif") {
loadingBar.show("error", "The file format of " + files[i].name + " is not supported.");
return false;
} else {
formData.append(i, files[i]);
}
}
upload.show();
window.onbeforeunload = function() { return "Lychee is currently uploading!"; };
xhr.open("POST", lychee.api_path);
xhr.onload = function() {
if (xhr.status===200) {
upload.close();
// WebKit Notification
if (window.webkitNotifications&&BrowserDetect.browser==="Safari") {
popup = window.webkitNotifications.createNotification("", "Upload complete", "You can now manage your new photo.");
popup.show();
}
window.onbeforeunload = null;
if (album.getID()===false) lychee.goto("0");
else album.load(albumID);
}
};
xhr.upload.onprogress = function(e) {
if (e.lengthComputable) {
progress = (e.loaded / e.total * 100 | 0);
if (progress>pre_progress) {
upload.setProgress(progress);
pre_progress = progress;
}
if (progress>=100) {
upload.setIcon("cog");
upload.setText("Processing photos");
}
}
};
$("#upload_files").val("");
xhr.send(formData);
},
url: function() {
var albumID = album.getID(),
params,
extension,
buttons;
if (albumID===false) albumID = 0;
buttons = [
["Import", function() {
link = $(".message input.text").val();
if (link&&link.length>3) {
extension = link.split('.').pop();
if (extension!=="jpeg"&&extension!=="jpg"&&extension!=="png"&&extension!=="gif") {
loadingBar.show("error", "The file format of this link is not supported.");
return false;
}
modal.close();
upload.show("cog", "Importing from URL");
params = "importUrl&url=" + escape(encodeURI(link)) + "&albumID=" + albumID;
lychee.api(params, function(data) {
upload.close();
if (album.getID()===false) lychee.goto("0");
else album.load(albumID);
if (data!==true) lychee.error(null, params, data);
});
} else loadingBar.show("error", "Link to short or too long. Please try another one!");
}],
["Cancel", function() {}]
];
modal.show("Import from Link", "Please enter the direct link to a photo to import it: <input class='text' type='text' placeholder='http://' value='http://'>", buttons);
},
server: function() {
var albumID = album.getID(),
params,
buttons;
if (albumID===false) albumID = 0;
buttons = [
["Import", function() {
modal.close();
upload.show("cog", "Importing photos");
params = "importServer&albumID=" + albumID;
lychee.api(params, function(data) {
upload.close();
if (album.getID()===false) lychee.goto("0");
else album.load(albumID);
if (data==="Warning: Folder empty!") lychee.error("Folder empty. No photos imported!", params, data);
else if (data!==true) lychee.error(null, params, data);
});
}],
["Cancel", function() {}]
];
modal.show("Import from Server", "This action will import all photos which are located in <b>'uploads/import/'</b> of your Lychee installation.", buttons);
},
dropbox: function() {
var albumID = album.getID(),
params;
if (albumID===false) albumID = 0;
lychee.loadDropbox(function() {
Dropbox.choose({
linkType: "direct",
multiselect: true,
success: function(files) {
if (files.length>1) {
for (var i = 0; i < files.length; i++) files[i] = files[i].link;
} else files = files[0].link;
upload.show("cog", "Importing photos");
params = "importUrl&url=" + escape(files) + "&albumID=" + albumID;
lychee.api(params, function(data) {
upload.close();
if (album.getID()===false) lychee.goto("0");
else album.load(albumID);
if (data!==true) lychee.error(null, params, data);
});
}
});
});
}
},
close: function(force) {
if (force===true) {
$(".upload_overlay").remove();
} else {
upload.setProgress(100);
$(".upload_overlay").removeClass("fadeIn").css("opacity", 0);
setTimeout(function() { $(".upload_overlay").remove() }, 300);
}
}
}

@ -1,11 +1,8 @@
/**
* @name view.js
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2013 by Philipp Maurer, Tobias Reich
*
* UI View
* Responsible to reflect data changes to the UI.
* @name UI View
* @description Responsible to reflect data changes to the UI.
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
view = {
@ -19,14 +16,14 @@ view = {
if (visible.photo()) {
lychee.imageview.removeClass("full");
lychee.loadingBar.css("opacity", 1);
lychee.header.css("margin-Top", "0px");
lychee.header.removeClass("hidden");
if ($("#imageview #image.small").length>0) {
$("#imageview #image").css({
marginTop: -1*($("#imageview #image").height()/2)+20
});
} else {
$("#imageview #image").css({
top: 70,
top: 60,
right: 30,
bottom: 30,
left: 30
@ -43,7 +40,7 @@ view = {
$(window).data("timeout", setTimeout(function() {
lychee.imageview.addClass("full");
lychee.loadingBar.css("opacity", 0);
lychee.header.css("margin-Top", "-45px");
lychee.header.addClass("hidden");
if ($("#imageview #image.small").length>0) {
$("#imageview #image").css({
marginTop: -1*($("#imageview #image").height()/2)
@ -63,31 +60,58 @@ view = {
mode: function(mode) {
var albumID;
var albumID = album.getID();
switch (mode) {
case "albums":
lychee.header.removeClass("view");
$("#tools_album, #tools_photo").hide();
$("#tools_albums").show();
break;
case "album":
lychee.header.removeClass("view");
$("#tools_albums, #tools_photo").hide();
$("#tools_album").show();
albumID = album.getID();
if (albumID=="s"||albumID=="f") $("#button_edit_album, #button_trash_album, #button_share_album").hide();
else if (albumID==0) $("#button_edit_album, #button_share_album").hide();
else $("#button_edit_album, #button_trash_album, #button_share_album").show();
if (albumID==="s"||albumID==="f") {
$("#button_info_album, #button_trash_album, #button_share_album").hide();
} else if (albumID==="0") {
$("#button_info_album, #button_share_album").hide();
$("#button_trash_album").show();
} else {
$("#button_info_album, #button_trash_album, #button_share_album").show();
}
break;
case "photo":
lychee.header.addClass("view");
$("#tools_albums, #tools_album").hide();
$("#tools_photo").show();
break;
}
}
},
infobox: {
show: function() {
if (!visible.infobox()) $("body").append("<div id='infobox_overlay' class='fadeIn'></div>");
lychee.infobox.addClass("active");
},
hide: function() {
lychee.animate("#infobox_overlay", "fadeOut");
setTimeout(function() { $("#infobox_overlay").remove() }, 300);
lychee.infobox.removeClass("active");
}
},
albums: {
init: function() {
@ -99,7 +123,7 @@ view = {
title: function() {
lychee.setTitle("Albums", null, false);
lychee.setTitle("Albums", false);
},
@ -127,7 +151,7 @@ view = {
}
if (smartData==""&&albumsData=="") $("body").append(build.no_content("picture"));
if (smartData===""&&albumsData==="") $("body").append(build.no_content("picture"));
else lychee.content.html(smartData + albumsData);
$("img").retina();
@ -153,6 +177,7 @@ view = {
marginLeft: 0
}, 300, function() {
$(this).remove();
if (albums.json.num<=0) lychee.animate(".divider:last-of-type", "fadeOut");
});
}
@ -167,6 +192,7 @@ view = {
album.parse();
view.album.infobox();
view.album.title();
view.album.public();
view.album.content.init();
@ -175,22 +201,29 @@ view = {
},
title: function() {
hide: function() {
view.infobox.hide();
},
title: function(oldTitle) {
if ((visible.album()||!album.json.init)&&!visible.photo()) {
switch (album.getID()) {
case "f":
lychee.setTitle("Starred", album.json.num, false);
lychee.setTitle("Starred", false);
break;
case "s":
lychee.setTitle("Public", album.json.num, false);
lychee.setTitle("Public", false);
break;
case "0":
lychee.setTitle("Unsorted", album.json.num, false);
lychee.setTitle("Unsorted", false);
break;
default:
lychee.setTitle(album.json.title, album.json.num, true);
if (album.json.init) $("#infobox .attr_name").html(album.json.title + " " + build.editIcon("edit_title_album"));
lychee.setTitle(album.json.title, true);
break;
}
@ -198,6 +231,12 @@ view = {
},
description: function() {
$("#infobox .attr_description").html(album.json.description + " " + build.editIcon("edit_description_album"));
},
content: {
init: function() {
@ -248,6 +287,7 @@ view = {
// Only when search is not active
if (!visible.albums()) {
album.json.num--;
view.album.num();
view.album.title();
}
});
@ -256,17 +296,38 @@ view = {
},
num: function() {
$("#infobox .attr_images").html(album.json.num);
},
public: function() {
if (album.json.public==1) {
$("#button_share_album a").addClass("active");
$("#button_share_album").attr("title", "Share Album");
$(".photo .icon-share").remove();
if (album.json.init) $("#infobox .attr_visibility").html("Public");
} else {
$("#button_share_album a").removeClass("active");
$("#button_share_album").attr("title", "Make Public");
if (album.json.init) $("#infobox .attr_visibility").html("Private");
}
},
password: function() {
if (album.json.password==1) $("#infobox .attr_password").html("Yes");
else $("#infobox .attr_password").html("No");
},
infobox: function() {
if ((visible.album()||!album.json.init)&&!visible.photo()) lychee.infobox.html(build.infoboxAlbum(album.json)).show();
}
},
@ -289,11 +350,18 @@ view = {
show: function() {
// Change header
lychee.content.addClass("view");
view.header.mode("photo");
// Make body not scrollable
$("body").css("overflow", "hidden");
// Fullscreen
$(document)
.bind("mouseenter", view.header.show)
.bind("mouseleave", view.header.hide);
lychee.animate(lychee.imageview, "fadeIn");
},
@ -301,43 +369,38 @@ view = {
hide: function() {
if (!visible.controls()) view.header.show();
if (visible.infobox) view.photo.hideInfobox();
if (visible.infobox) view.infobox.hide();
lychee.content.removeClass("view");
view.header.mode("album");
// Make body scrollable
$("body").css("overflow", "scroll");
// Disable Fullscreen
$(document)
.unbind("mouseenter")
.unbind("mouseleave");
// Hide Photo
lychee.animate(lychee.imageview, "fadeOut");
setTimeout(function() { lychee.imageview.hide() }, 300);
},
showInfobox: function() {
if (!visible.infobox()) $("body").append("<div id='infobox_overlay'></div>");
lychee.infobox.css("right", "0px");
},
hideInfobox: function() {
$("#infobox_overlay").remove();
lychee.infobox.css("right", "-320px");
setTimeout(function() {
lychee.imageview.hide();
view.album.infobox();
}, 300);
},
title: function(oldTitle) {
if (photo.json.init) $("#infobox .attr_name").html($("#infobox .attr_name").html().replace(oldTitle, photo.json.title));
lychee.setTitle(photo.json.title, null, true);
if (photo.json.init) $("#infobox .attr_name").html(photo.json.title + " " + build.editIcon("edit_title"));
lychee.setTitle(photo.json.title, true);
},
description: function(oldDescription) {
description: function() {
if (photo.json.init) $("#infobox .attr_description").html($("#infobox .attr_description").html().replace(oldDescription, photo.json.description));
if (photo.json.init) $("#infobox .attr_description").html(photo.json.description + " " + build.editIcon("edit_description"));
},
@ -374,19 +437,16 @@ view = {
photo: function() {
if (visible.controls()&&photo.isSmall()) lychee.imageview.html("<a id='previous' class='icon-caret-left'></a><a id='next' class='icon-caret-right'></a><div id='image' class='small' style='background-image: url(" + photo.json.url + "); width: " + photo.json.width + "px; height: " + photo.json.height + "px; margin-top: -" + parseInt(photo.json.height/2-20) + "px; margin-left: -" + photo.json.width/2 + "px;'></div>");
else if (visible.controls()) lychee.imageview.html("<a id='previous' class='icon-caret-left'></a><a id='next' class='icon-caret-right'></a><div id='image' style='background-image: url(" + photo.json.url + ")'></div>");
else if (photo.isSmall()) lychee.imageview.html("<a id='previous' style='left: -50px' class='icon-caret-left'></a><a id='next' style='right: -50px' class='icon-caret-right'></a><div id='image' class='small' style='background-image: url(" + photo.json.url + "); width: " + photo.json.width + "px; height: " + photo.json.height + "px; margin-top: -" + parseInt(photo.json.height/2) + "px; margin-left: -" + photo.json.width/2 + "px;'></div>");
else lychee.imageview.html("<a id='previous' style='left: -50px' class='icon-caret-left'></a><a id='next' style='right: -50px' class='icon-caret-right'></a><div id='image' style='background-image: url(" + photo.json.url + "); top: 0px; right: 0px; bottom: 0px; left: 0px;'></div>");
lychee.imageview.html(build.imageview(photo.json, photo.isSmall(), visible.controls()));
if (!photo.json.nextPhoto||lychee.viewMode) $("a#next").hide();
if (!photo.json.previousPhoto||lychee.viewMode) $("a#previous").hide();
if ((album.json&&album.json.content[photo.getID()]&&album.json.content[photo.getID()].nextPhoto==="")||lychee.viewMode) $("a#next").hide();
if ((album.json&&album.json.content[photo.getID()]&&album.json.content[photo.getID()].previousPhoto==="")||lychee.viewMode) $("a#previous").hide();
},
infobox: function() {
lychee.infobox.html(build.infobox(photo.json)).show();
lychee.infobox.html(build.infoboxPhoto(photo.json)).show();
}

@ -1,22 +1,19 @@
/**
* @name visible.js
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2013 by Philipp Maurer, Tobias Reich
*
* Visible Module
* This module is used to check if elements are visible or not.
* @name Visible Module
* @description This module is used to check if elements are visible or not.
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
visible = {
albums: function() {
if ($("#tools_albums").css("display")=="block") return true;
if ($("#tools_albums").css("display")==="block") return true;
else return false;
},
album: function() {
if ($("#tools_album").css("display")=="block") return true;
if ($("#tools_album").css("display")==="block") return true;
else return false;
},
@ -26,8 +23,8 @@ visible = {
},
infobox: function() {
if (parseInt(lychee.infobox.css("right").replace("px", ""))==-320) return false;
else return true;
if ($("#infobox.active").length>0) return true;
else return false;
},
controls: function() {
@ -40,6 +37,11 @@ visible = {
else return false;
},
signin: function() {
if ($(".message .sign_in").length>0) return true;
else return false;
},
contextMenu: function() {
if ($(".contextmenu").length>0) return true;
else return false;

@ -1,8 +1,8 @@
/**
* @name view.js
* @author Philipp Maurer
* @name View
* @description Used to view single photos with view.php
* @author Tobias Reich
* @copyright 2013 by Philipp Maurer, Tobias Reich
* @copyright 2014 by Tobias Reich
*/
var header = $("header"),
@ -38,7 +38,7 @@ $(document).ready(function(){
function key(e) {
code = (e.keyCode ? e.keyCode : e.which);
if (code==27&&visibleInfobox()) { hideInfobox(); e.preventDefault(); }
if (code===27&&visibleInfobox()) { hideInfobox(); e.preventDefault(); }
}
@ -66,15 +66,16 @@ function isPhotoSmall(photo) {
function showInfobox() {
$("body").append("<div id='infobox_overlay'></div>");
infobox.css("right", "0px");
$("body").append("<div id='infobox_overlay' class='fadeIn'></div>");
infobox.addClass("active");
}
function hideInfobox() {
$("#infobox_overlay").remove();
infobox.css("right", "-320px");
$("#infobox_overlay").removeClass("fadeIn").addClass("fadeOut");
setTimeout(function() { $("#infobox_overlay").remove() }, 300);
infobox.removeClass("active");
}
@ -91,10 +92,10 @@ function loadPhotoInfo(photoID) {
imageview.attr("data-id", photoID);
if (isPhotoSmall(data)) imageview.html("<div id='image' class='small' style='background-image: url(" + data.url + "); width: " + data.width + "px; height: " + data.height + "px; margin-top: -" + parseInt((data.height/2)-20) + "px; margin-left: -" + data.width/2 + "px;'></div>");
else imageview.html("<div id='image' style='background-image: url(" + data.url + "); top: 70px; right: 30px; bottom: 30px; left: 30px;'></div>");
else imageview.html("<div id='image' style='background-image: url(" + data.url + ");'></div>");
imageview.removeClass("fadeOut").addClass("fadeIn").show();
infobox.html(build.infobox(data, true)).show();
infobox.html(build.infoboxPhoto(data, true)).show();
}, error: ajaxError });

@ -1,485 +0,0 @@
/**
* @name animations.css
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2013 by Philipp Maurer, Tobias Reich
*/
/* bounceInDown ------------------------------------------------*/
@-webkit-keyframes bounceInDown {
0% {
-webkit-transform: translateY(-2000px);
}
60% {
-webkit-transform: translateY(30px);
}
80% {
-webkit-transform: translateY(-10px);
}
100% {
-webkit-transform: translateY(0);
}
}
@-moz-keyframes bounceInDown {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@-ms-keyframes bounceInDown {
0% {
-ms-transform: translateY(-2000px);
}
60% {
-ms-transform: translateY(30px);
}
80% {
-ms-transform: translateY(-10px);
}
100% {
-ms-transform: translateY(0);
}
}
@-o-keyframes bounceInDown {
0% {
-o-transform: translateY(-2000px);
}
60% {
-o-transform: translateY(30px);
}
80% {
-o-transform: translateY(-10px);
}
100% {
-o-transform: translateY(0);
}
}
@keyframes bounceInDown {
0% {
transform: translateY(-2000px);
}
60% {
transform: translateY(30px);
}
80% {
transform: translateY(-10px);
}
100% {
transform: translateY(0);
}
}
/* bounceOutUp ------------------------------------------------*/
@-webkit-keyframes bounceOutUp {
0% {
-webkit-transform: translateY(0);
}
20% {
-webkit-transform: translateY(20px);
}
100% {
-webkit-transform: translateY(-2000px);
}
}
@-moz-keyframes bounceOutUp {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@-ms-keyframes bounceOutUp {
0% {
-ms-transform: translateY(0);
}
20% {
-ms-transform: translateY(20px);
}
100% {
-ms-transform: translateY(-2000px);
}
}
@-o-keyframes bounceOutUp {
0% {
-o-transform: translateY(0);
}
20% {
-o-transform: translateY(20px);
}
100% {
-o-transform: translateY(-2000px);
}
}
@keyframes bounceOutUp {
0% {
transform: translateY(0);
}
20% {
transform: translateY(20px);
}
100% {
transform: translateY(-2000px);
}
}
/* moveUp ------------------------------------------------*/
@-webkit-keyframes moveUp {
0% {
-webkit-transform: translateY(1000px);
}
100% {
-webkit-transform: translateY(0px);
}
}
@-moz-keyframes moveUp {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@-ms-keyframes moveUp {
0% {
-ms-transform: translateY(1000px);
}
100% {
-ms-transform: translateY(0px);
}
}
@-o-keyframes moveUp {
0% {
-o-transform: translateY(1000px);
}
100% {
-o-transform: translateY(0px);
}
}
@keyframes moveUp {
0% {
transform: translateY(1000px);
}
100% {
transform: translateY(0px);
}
}
/* moveDown ------------------------------------------------*/
@-webkit-keyframes moveDown {
0% {
-webkit-transform: translateY(-100px);
}
100% {
-webkit-transform: translateY(0);
}
}
@-moz-keyframes moveDown {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@-ms-keyframes moveDown {
0% {
-ms-transform: translateY(-100px);
}
100% {
-ms-transform: translateY(0);
}
}
@-o-keyframes moveDown {
0% {
-o-transform: translateY(-100px);
}
100% {
-o-transform: translateY(0);
}
}
@keyframes moveDown {
0% {
transform: translateY(-100px);
}
100% {
transform: translateY(0);
}
}
/* fadeIn ------------------------------------------------*/
@-webkit-keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@-moz-keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@-ms-keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@-o-keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
/* fadeOut ------------------------------------------------*/
@-webkit-keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@-moz-keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@-ms-keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@-o-keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
/* moveBackground ------------------------------------------------*/
@-webkit-keyframes moveBackground {
0% {
background-position-x: 0px;
}
100% {
background-position-x: -33px;
}
}
@-moz-keyframes moveBackground {
0% {
background-position-x: 0px;
}
100% {
background-position-x: -33px;
}
}
@-ms-keyframes moveBackground {
0% {
background-position-x: 0px;
}
100% {
background-position-x: -33px;
}
}
@-o-keyframes moveBackground {
0% {
background-position-x: 0px;
}
100% {
background-position-x: -33px;
}
}
@keyframes moveBackground {
0% {
background-position-x: 0px;
}
100% {
background-position-x: -33px;
}
}
/* zoomOut ------------------------------------------------*/
@-webkit-keyframes zoomOut {
0% {
opacity: 1;
-webkit-transform: scale(1);
}
100% {
opacity: 0;
-webkit-transform: scale(.5);
}
}
@-moz-keyframes zoomOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@-ms-keyframes zoomOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@-o-keyframes zoomOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes zoomOut {
0% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(.5);
}
}
/* zoomIn ------------------------------------------------*/
@-webkit-keyframes zoomIn {
0% {
opacity: 0;
-webkit-transform: scale(.5);
}
100% {
opacity: 1;
-webkit-transform: scale(1);
}
}
@-moz-keyframes zoomIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@-ms-keyframes zoomIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@-o-keyframes zoomIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes zoomIn {
0% {
opacity: 0;
transform: scale(.5);
}
100% {
opacity: 1;
transform: scale(1);
}
}
/* pulse ------------------------------------------------*/
@-webkit-keyframes pulse {
0% {
opacity: 1;
}
50% {
opacity: 0.3;
}
100% {
opacity: 1;
}
}
@-moz-keyframes pulse {
0% {
opacity: 1;
}
50% {
opacity: 0.8;
}
100% {
opacity: 1;
}
}
@-ms-keyframes pulse {
0% {
opacity: 1;
}
50% {
opacity: 0.8;
}
100% {
opacity: 1;
}
}
@-o-keyframes pulse {
0% {
opacity: 1;
}
50% {
opacity: 0.8;
}
100% {
opacity: 1;
}
}
@keyframes pulse {
0% {
opacity: 1;
}
50% {
opacity: 0.8;
}
100% {
opacity: 1;
}
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>app</key>
<string>com.bohemiancoding.sketch</string>
<key>build</key>
<integer>5302</integer>
<key>commit</key>
<string>9460a4bc62af5e9ba50dd4143578fd9401710ce5</string>
<key>version</key>
<integer>18</integer>
</dict>
</plist>

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>app</key>
<string>com.bohemiancoding.sketch</string>
<key>build</key>
<integer>5302</integer>
<key>commit</key>
<string>9460a4bc62af5e9ba50dd4143578fd9401710ce5</string>
<key>version</key>
<integer>18</integer>
</dict>
</plist>

@ -0,0 +1,32 @@
#!/bin/bash
folderCSS="../assets/css"
folderJS="../assets/js"
if [ -e "$folderCSS/modules/" ]
then
echo "Compiling CSS ..."
awk 'FNR==1{print ""}1' $folderCSS/modules/*.css > $folderCSS/min/main.css
csso $folderCSS/min/main.css $folderCSS/min/main.css
echo "CSS compiled!"
else
echo "CSS files not found in $folderCSS"
fi
if [ -e "$folderJS/modules/" ]
then
echo "Compiling JS ..."
awk 'FNR==1{print ""}1' $folderJS/modules/*.js > $folderJS/min/main.js
uglifyjs $folderJS/min/main.js -o $folderJS/min/main.js
echo "JS compiled!"
else
echo "JS files not found in $folderJS"
fi

@ -0,0 +1,40 @@
#!/bin/sh
VERSION='1.3.2'
echo 'Press ENTER to continue or any other key to abort'
read -s -n 1 key
if [[ "$key" = "" ]]
then
if [ -e Lychee-$VERSION ]
then
echo "The folder 'Lychee-$VERSION' already exists. Please delete it before you try to install Lychee."
exit 1
fi
if [ -e lychee ]
then
echo "The folder 'lychee' already exists. Please delete it before you try to install Lychee."
exit 1
fi
echo 'Downloading and installing Lychee...' && \
curl -sS https://codeload.github.com/electerious/Lychee/zip/v$VERSION > lychee.zip && \
echo 'Downloaded.' && \
echo 'Unzipping...' && \
unzip lychee.zip && \
rm lychee.zip && \
mv Lychee-$VERSION lychee && \
cd lychee && \
echo 'The required directories will be made writable and executable for others. Please enter your password if prompted to do so.' && \
sudo chmod -R 777 uploads php && \
echo 'Installation successful!' && \
exit 0
fi

@ -0,0 +1,16 @@
### v2.0
- All new redefined interface
- Faster animations and transitions
- Import from Dropbox
- Import from Server
- Download public albums
- Several sorting options
- Installation assistent
- Infobox and description for albums
- Faster loading and improved performance
- Better file handling and upload
- Album covers are chosen intelligent
- Prettier URLs
- Massive changes under the hood
- IPTC support (Headline and Caption)
- EXIF Orientation support

@ -0,0 +1,44 @@
#### Lychee is not working
If Lychee is not working properly, try to open `plugins/check.php`. This script will display all errors it can find. Everything should work if you can see the message "Lychee is ready. Lets rock!".
#### What do I need to run Lychee on my server?
To run Lychee, everything you need is a web-server with PHP 5.3 or later and a MySQL-Database.
#### I can't upload multiple photos at once
If you experience problems uploading large amounts of photos, you might want to change the PHP parameters in `.htaccess` (if you are using the PHP Apache module) or in `.user.ini` (if you are using PHP >= 5.3 with CGI or FastCGI).
If possible, change these settings directly in your `php.ini`. We recommend to increase the values of the following properties:
max_execution_time = 200
post_max_size = 200M
upload_max_size = 200M
upload_max_filesize = 20M
max_file_uploads = 100
#### Which browsers are supported?
Lychee supports the latest versions of Google Chrome, Apple Safari, Mozilla Firefox and Opera. Photos you share with others can be viewed from every browser.
#### How can I set thumbnails for my albums?
Thumbnails are choosen automatically by the photos you have starred and in the order you uploaded them. Star a photo inside a album to set it as an thumbnail.
#### What is new?
Take a look at the [Changelog](Changelog.md) to see whats new.
#### How can I backup my installation?
To backup your Lychee installation you need to do the following steps:
1. Create a copy of the whole Lychee folder
2. Run the following MySQL Queries:
- CREATE TABLE lychee_albums_backup LIKE lychee_albums;
- INSERT INTO lychee_albums_backup SELECT * FROM lychee_albums;
- CREATE TABLE lychee_photos_backup LIKE lychee_photos;
- INSERT INTO lychee_photos_backup SELECT * FROM lychee_photos;
- CREATE TABLE lychee_settings_backup LIKE lychee_settings;
- INSERT INTO lychee_settings_backup SELECT * FROM lychee_settings;
#### How to update?
1. Replace all files, excluding `uploads/`
2. Open Lychee and enter your database details
#### Can I upload videos?
No. Video support is not planned.

@ -0,0 +1,13 @@
### Import from server
To import photos from your server (photos you have uploaded via FTP to your server), sign in and click the add-icon on the top right. Then choose 'Import from Server'.
### Upload and share single photos
You can upload photos directly with every FTP client into Lychee. This feature helps you to share single images quickly with others.
1. Upload an image to `uploads/import/`
2. Navigate your browser to the place where Lychee is located (e.g. `http://example.com/view.php?p=filename.png`). `filename.png` must be replaced with the filename of your uploaded file.
3. Share the link.
Lychee will import the file as an public image, delete the original (unused) file and display it in the browser. [Sample FTP configuration](http://l.electerious.com/view.php?p=13657692738813).

@ -0,0 +1,29 @@
### Requirements
Everything you need is a web-server with PHP 5.3 or later and a MySQL-Database.
### PHP configuration `php.ini`
The following extensions must be activated:
extension = php_mbstring.dll
extension = php_exif.dll
extension = php_gd2.dll
To use Lychee without restrictions, we recommend to increase the values of the following properties:
max_execution_time = 200
post_max_size = 200M
upload_max_size = 200M
upload_max_filesize = 20M
max_file_uploads = 100
### Folder permissions
Change the permissions of `uploads/` and `php/` to 777, including all subfolders:
chmod -R 777 uploads/ php/
### Lychee installation
Open Lychee in your browser and follow the given steps.
If you have trouble, take a look at the [FAQ](FAQ.md).

@ -0,0 +1,17 @@
### Everywhere
| Key | Action |
|:-----------|:------------|
| `enter` | Confirm Dialog |
| `u` | Upload photo |
| `esc` | Close/Back |
| `cmd`+`up` | Close/Back |
### Photoview
| Key | Action |
|:-----------|:------------|
| `s` | Star photo |
| `i` | Show information |
| `f` | Show photo in new tab
| `cmd`+`backspace` | Delete photo
| `left` | Previous photo
| `right` | Next photo

@ -0,0 +1,41 @@
### Database Details
Your MySQL details are stored in `php/config.php`. This file doesn't exist until you installed Lychee. If you need to change your connection details, you can edit this file manually.
$dbHost = Your MySQL host (in most cases you can use localhost)
$dbUser = Your MySQL username
$dbPassword = Your MySQL password
$dbName = The name of the database you want to use
Fill these properties with your MySQL information. Lychee will create the database and tables for you, if they doesn't exist.
### Settings
All settings are stored in the database. You can change the properties manually, but we recommend to use the menu in Lychee. You can find this menu on the top left corner after you signed in.
#### Login
username = Username for Lychee
password = Password for Lychee, saved as an md5 hash
Your photos and albums are protected by a username and password you need to set. If both rows are empty, Lychee will prompt you to set them.
#### Thumb Quality
thumbQuality = [0-100]
Less means a inferiority quality of your thumbs, but faster loading. More means a better quality of your thumbs, but slower loading. The default value is 90. The allowed values are between 0 and 100.
#### Check For Updates
checkForUpdates = [0|1]
If `1`, Lychee will check if you are using the latest version. The notice will be displayed beside the version-number when you sign in.
#### Sorting
sorting = ORDER BY [row] [ASC|DESC]
A typical part of an MySQL statement. This string will be appended to mostly every MySQL query.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

@ -5,16 +5,35 @@
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>Lychee</title>
<meta name="author" content="Tobias Reich, Philipp Maurer">
<meta name="author" content="Tobias Reich">
<meta name="keywords" content="">
<meta name="description" content="">
<link type="text/css" rel="stylesheet" href="css/animations.css">
<link type="text/css" rel="stylesheet" href="css/font-awesome.css">
<link type="text/css" rel="stylesheet" href="css/style.css">
<link rel="shortcut icon" href="img/favicon.png">
<!-- CSS -->
<link type="text/css" rel="stylesheet" href="assets/css/min/reset.css">
<!-- Development
<link type="text/css" rel="stylesheet" href="assets/css/modules/upload.css">
<link type="text/css" rel="stylesheet" href="assets/css/modules/tooltip.css">
<link type="text/css" rel="stylesheet" href="assets/css/modules/misc.css">
<link type="text/css" rel="stylesheet" href="assets/css/modules/message.css">
<link type="text/css" rel="stylesheet" href="assets/css/modules/mediaquery.css">
<link type="text/css" rel="stylesheet" href="assets/css/modules/loading.css">
<link type="text/css" rel="stylesheet" href="assets/css/modules/infobox.css">
<link type="text/css" rel="stylesheet" href="assets/css/modules/imageview.css">
<link type="text/css" rel="stylesheet" href="assets/css/modules/header.css">
<link type="text/css" rel="stylesheet" href="assets/css/modules/font.css">
<link type="text/css" rel="stylesheet" href="assets/css/modules/contextmenu.css">
<link type="text/css" rel="stylesheet" href="assets/css/modules/content.css">
<link type="text/css" rel="stylesheet" href="assets/css/modules/animations.css"> -->
<!-- Production -->
<link type="text/css" rel="stylesheet" href="assets/css/min/main.css">
<link rel="shortcut icon" href="assets/img/favicon.ico">
<link rel="apple-touch-icon" href="assets/img/apple-touch-icon-iphone.png" sizes="120x120">
<link rel="apple-touch-icon" href="assets/img/apple-touch-icon-ipad.png" sizes="152x152">
<link rel="apple-touch-icon-precomposed" href="img/apple-touch-icon.png">
<meta name="apple-mobile-web-app-status-bar-style" content="black" >
<meta name="viewport" content="user-scalable=no, initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
@ -30,30 +49,30 @@
<!-- Buttons -->
<div id="tools_albums">
<a class="button" id="button_signout">Sign Out</a>
<a class="button" id="button_signin">Sign In</a>
<a class="button icon icon-plus-sign button_add"></a>
<a class="button left icon-cog" id="button_settings"></a>
<a class="button left icon-signout" id="button_signin"></a>
<a id="hostedwith">Hosted with Lychee</a>
<a class="button right icon icon-plus button_add"></a>
<a class="button_divider"></a>
<input id="search" type="text" name="search" placeholder="Search …">
</div>
<div id="tools_album">
<a class="button" id="button_back_home">Back</a>
<a class="button icon icon-plus-sign button_add"></a>
<a class="button left icon-arrow-left" id="button_back_home"></a>
<a class="button right icon icon-plus button_add"></a>
<a class="button_divider"></a>
<div class="tools" id="button_trash_album" title="Delete Album"><a class="icon-trash"></a></div>
<div class="tools" id="button_archive" title="Download Album"><a class="icon-circle-arrow-down"></a></div>
<div class="tools" id="button_edit_album" title="Edit Title"><a class="icon-edit"></a></div>
<div class="tools" id="button_info_album" title="Show Info"><a class="icon-info-sign"></a></div>
<div class="tools" id="button_share_album" title="Share Album"><a class="icon-share"></a></div>
</div>
<div id="tools_photo">
<a class="button" id="button_back">Back</a>
<a class="button left icon-arrow-left" id="button_back"></a>
<div class="tools" id="button_trash" title="Delete"><a class="icon-trash"></a></div>
<div class="tools" id="button_move" title="Move to Album"><a class="icon-folder-open"></a></div>
<div class="tools" id="button_edit" title="Edit Title"><a class="icon-edit"></a></div>
<a class="button_divider less"></a>
<div class="tools" id="button_move" title="Move"><a class="icon-folder-open"></a></div>
<a class="button_divider"></a>
<div class="tools" id="button_download" title="Full Photo"><a class="icon-resize-full"></a></div>
<div class="tools" id="button_info" title="Show Info"><a class="icon-info-sign"></a></div>
<a class="button_divider less"></a>
<a class="button_divider"></a>
<div class="tools" id="button_share" title="Share Photo"><a class="icon-share"></a></div>
<div class="tools" id="button_star" title="Star Photo"><a class="icon-star-empty"></a></div>
</div>
@ -74,31 +93,30 @@
<!-- Upload -->
<div id="upload">
<input id="upload_files" type="file" name="fileElem[]" multiple="true" accept="image/*">
<div id="auswahl"></div>
<input id="upload_button" type="button" onclick="sendFiles();" value="Upload">
</div>
<!-- JS -->
<script defer type="text/javascript" src="js/frameworks.js"></script>
<script defer type="text/javascript" src="assets/js/min/frameworks.js"></script>
<!-- Development
<script defer type="text/javascript" src="js/modules/lychee.js"></script>
<script defer type="text/javascript" src="js/modules/build.js"></script>
<script defer type="text/javascript" src="js/modules/view.js"></script>
<script defer type="text/javascript" src="js/modules/password.js"></script>
<script defer type="text/javascript" src="js/modules/modal.js"></script>
<script defer type="text/javascript" src="js/modules/album.js"></script>
<script defer type="text/javascript" src="js/modules/albums.js"></script>
<script defer type="text/javascript" src="js/modules/photo.js"></script>
<script defer type="text/javascript" src="js/modules/visible.js"></script>
<script defer type="text/javascript" src="js/modules/loadingBar.js"></script>
<script defer type="text/javascript" src="js/modules/contextMenu.js"></script>
<script defer type="text/javascript" src="js/modules/search.js"></script> -->
<script defer type="text/javascript" src="assets/js/modules/init.js"></script>
<script defer type="text/javascript" src="assets/js/modules/lychee.js"></script>
<script defer type="text/javascript" src="assets/js/modules/build.js"></script>
<script defer type="text/javascript" src="assets/js/modules/settings.js"></script>
<script defer type="text/javascript" src="assets/js/modules/upload.js"></script>
<script defer type="text/javascript" src="assets/js/modules/view.js"></script>
<script defer type="text/javascript" src="assets/js/modules/password.js"></script>
<script defer type="text/javascript" src="assets/js/modules/modal.js"></script>
<script defer type="text/javascript" src="assets/js/modules/album.js"></script>
<script defer type="text/javascript" src="assets/js/modules/albums.js"></script>
<script defer type="text/javascript" src="assets/js/modules/photo.js"></script>
<script defer type="text/javascript" src="assets/js/modules/visible.js"></script>
<script defer type="text/javascript" src="assets/js/modules/loadingBar.js"></script>
<script defer type="text/javascript" src="assets/js/modules/contextMenu.js"></script>
<script defer type="text/javascript" src="assets/js/modules/search.js"></script> -->
<!-- Production -->
<script defer type="text/javascript" src="js/functions.js"></script>
<script defer type="text/javascript" src="js/main.js"></script>
<script defer type="text/javascript" src="assets/js/min/main.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

@ -1,147 +0,0 @@
/**
* @name main.js
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2013 by Philipp Maurer, Tobias Reich
*/
$(document).ready(function(){
/* Init */
lychee.init();
/* Event Name */
var event_name = (mobileBrowser()) ? "touchend" : "click";
/* Notifications */
if (window.webkitNotifications) window.webkitNotifications.requestPermission();
/* Tooltips */
if (!mobileBrowser()) $(".tools").tipsy({gravity: 'n'});
/* Header */
$("#button_share").on(event_name, function(e) {
if (photo.json.public==1||photo.json.public==2) contextMenu.sharePhoto(photo.getID(), e);
else photo.setPublic(photo.getID(), e);
});
$("#button_share_album").on(event_name, function(e) {
if (album.json.public==1) contextMenu.shareAlbum(album.getID(), e);
else modal.show("Share Album", "All photos inside this album will be public and visible for everyone. Existing public photos will have the same sharing permission as this album. Are your sure you want to share this album? <input class='password' type='password' placeholder='password (optional)' value=''>", [["Share Album", function() { album.setPublic(album.getID(), e) }], ["Cancel", function() {}]]);
});
$("#button_signout").on(event_name, lychee.logout);
$("#button_download").on(event_name, function() { window.open(photo.getDirectLink(),"_newtab") });
$("#button_trash_album").on(event_name, function() { album.delete(album.getID()) });
$("#button_move").on(event_name, function(e) { contextMenu.move(photo.getID(), e) });
$("#button_trash").on(event_name, function() { photo.delete() });
$("#button_edit_album").on(event_name, function() { album.setTitle() });
$("#button_edit").on(event_name, function() { photo.setTitle(photo.getID()) });
$("#button_info").on(event_name, function() { view.photo.showInfobox() });
$("#button_archive").on(event_name, function() { album.getArchive(album.getID()) });
$("#button_star").on(event_name, function() { photo.setStar(photo.getID()) });
/* Search */
$("#search").on("keyup click", function() { search.find($(this).val()) });
/* Back Buttons */
$("#button_back_home").on(event_name, function() { lychee.goto("") });
$("#button_back").on(event_name, function() { lychee.goto("a" + album.getID()) });
/* Image View */
lychee.imageview
.on(event_name, "a#previous", function() {
if (photo.json&&photo.json.previousPhoto) lychee.goto("a" + album.getID() + "p" + photo.json.previousPhoto)
})
.on(event_name, "a#next", function() {
if (photo.json&&photo.json.nextPhoto) lychee.goto("a" + album.getID() + "p" + photo.json.nextPhoto)
});
/* Infobox */
$("#infobox")
.on(event_name, ".header a", function() { view.photo.hideInfobox() })
.on(event_name, "#edit_title", function() { photo.setTitle(photo.getID()) })
.on(event_name, "#edit_description", function() { photo.setDescription(photo.getID()) });
/* Keyboard */
Mousetrap
.bind('n', function(e) { if (!visible.message()) $("body").append(build.addModal) })
.bind('u', function(e) { $("#auswahl").html(""); $("#upload_files").click() })
.bind('s', function(e) { if (visible.photo()) $("#button_star").click() })
.bind('f', function(e) { if (visible.photo()) $("#button_download").click() })
.bind('command+backspace', function(e) { if (visible.photo()&&!visible.message()) photo.delete() })
.bind('left', function(e) { if (visible.photo()) $("#imageview a#previous").click() })
.bind('right', function(e) { if (visible.photo()) $("#imageview a#next").click() })
.bind('i', function(e) {
if (visible.infobox()) view.photo.hideInfobox();
else if (visible.photo()) view.photo.showInfobox();
});
Mousetrap.bindGlobal('enter', function(e) {
if ($(".message .button.active").length) $(".message .button.active").addClass("pressed").click()
});
Mousetrap.bindGlobal(['esc', 'command+up'], function(e) {
e.preventDefault();
if (visible.message()) modal.close();
else if (visible.contextMenu()) contextMenu.close();
else if (visible.infobox()) view.photo.hideInfobox();
else if (visible.photo()) lychee.goto("a" + album.getID());
else if (visible.album()) lychee.goto("");
else if (visible.albums()&&$("#search").val().length!=0) search.reset();
});
/* Document */
$(document)
/* Login */
.on(event_name, "#button_signin", function() { lychee.loginDialog() })
.on("keyup", "#password", function() { if ($(this).val().length>0) $(this).removeClass("error") })
/* Header */
.on(event_name, "#title.editable", function() {
if (visible.photo()) photo.setTitle(photo.getID());
else album.setTitle();
})
/* Navigation */
.on("click", ".album", function() { lychee.goto("a" + $(this).attr("data-id")) })
.on("click", ".photo", function() { lychee.goto("a" + album.getID() + "p" + $(this).attr("data-id")) })
/* Modal */
.on(event_name, ".message .close", modal.close)
.on(event_name, ".message .button:first", function() { modal.fns[0](); modal.close(); })
.on(event_name, ".message .button:last", function() { modal.fns[1](); modal.close(); })
/* Add Dialog */
.on(event_name, ".button_add", function() { $("body").append(build.addModal) })
.on(event_name, "#add_album", album.add)
.on(event_name, "#add_link", function() { photo.add.url() })
.on(event_name, "#add_photo", function() { $("#auswahl").html(""); $("#upload_files").click() })
/* Upload */
.on("change", "#upload_files", function() { modal.close(); photo.add.files(this.files); })
/* Context Menu */
.on("contextmenu", ".photo", contextMenu.photo)
.on("contextmenu", ".album", contextMenu.album)
.on(event_name, ".contextmenu_bg", contextMenu.close)
/* Infobox */
.on(event_name, "#infobox_overlay", function() { view.photo.hideInfobox() })
/* Controls */
.bind("mouseenter", view.header.show)
.bind("mouseleave", view.header.hide)
/* Upload */
.on("dragover", function(e) { e.preventDefault(); }, false)
.on("drop", function(e) {
e.stopPropagation();
e.preventDefault();
photo.add.files(e.originalEvent.dataTransfer.files);
return true;
});
/* Run */
lychee.run();
});

@ -1,288 +0,0 @@
/**
* @name build.js
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2013 by Philipp Maurer, Tobias Reich
*
* Build Module
* This module is used to generate HTML-Code.
*/
build = {
divider: function(title) {
return "<div class='divider fadeIn'><h1>" + title + "</h1></div>";
},
album: function(albumJSON) {
if (!albumJSON) return "";
var album = ""
title = albumJSON.title;
if (title.length>18) title = albumJSON.title.substr(0, 18) + "...";
album += "<div class='album' data-id='" + albumJSON.id + "' data-password='" + albumJSON.password + "'>";
album += "<img src='" + albumJSON.thumb2 + "' width='200' height='200' alt='thumb'>";
album += "<img src='" + albumJSON.thumb1 + "' width='200' height='200' alt='thumb'>";
album += "<img src='" + albumJSON.thumb0 + "' width='200' height='200' alt='thumb'>";
album += "<div class='overlay'>";
if (albumJSON.password&&!lychee.publicMode) album += "<h1><span class='icon-lock'></span> " + title + "</h1>";
else album += "<h1>" + title + "</h1>";
album += "<a>" + albumJSON.sysdate + "</a>";
album += "</div>";
if(!lychee.publicMode&&albumJSON.star==1) album += "<a class='badge red icon-star'></a>";
if(!lychee.publicMode&&albumJSON.public==1) album += "<a class='badge red icon-share'></a>";
if(!lychee.publicMode&&albumJSON.unsorted==1) album += "<a class='badge red icon-reorder'></a>";
album += "</div>";
return album;
},
photo: function(photoJSON) {
if (!photoJSON) return "";
var photo = "",
title = photoJSON.title;
if (title.length>18) title = photoJSON.title.substr(0, 18) + "...";
photo += "<div class='photo' data-album-id='" + photoJSON.album + "' data-id='" + photoJSON.id + "'>";
photo += "<img src='" + photoJSON.thumbUrl + "' width='200' height='200' alt='thumb'>";
photo += "<div class='overlay'>";
photo += "<h1>" + title + "</h1>";
photo += "<a>" + photoJSON.sysdate + "</a>";
photo += "</div>";
if (photoJSON.star==1) photo += "<a class='badge red icon-star'></a>";
if (!lychee.publicMode&&photoJSON.public==1&&album.json.public!=1) photo += "<a class='badge red icon-share'></a>";
photo += "</div>";
return photo;
},
no_content: function(typ) {
var no_content = "";
no_content += "<div class='no_content fadeIn'>";
no_content += "<a class='icon icon-" + typ + "'></a>";
if (typ=="search") no_content += "<p>No results</p>";
else if (typ=="picture") no_content += "<p>No public albums</p>";
no_content += "</div>";
return no_content;
},
modal: function(title, text, button) {
var modal = "";
modal += "<div class='message_overlay fadeIn'>";
modal += "<div class='message center'>";
modal += "<h1>" + title + "</h1>";
modal += "<a class='close icon-remove-sign'></a>";
modal += "<p>" + text + "</p>";
$.each(button, function(index) {
if (this[0]!="") {
if (index==0) modal += "<a class='button active'>" + this[0] + "</a>";
else modal += "<a class='button'>" + this[0] + "</a>";
}
});
modal += "</div>";
modal += "</div>";
return modal;
},
addModal: function() {
var modal = "";
modal += "<div class='message_overlay fadeIn'>";
modal += "<div class='message center add'>";
modal += "<h1>Add Album or Photo</h1>";
modal += "<a class='close icon-remove-sign'></a>";
modal += "<div id='add_album' class='add_album'>";
modal += "<div class='icon icon-folder-close'></div>";
modal += "<a>New Album</a>";
modal += "</div>";
modal += "<div id='add_link' class='add_album'>";
modal += "<div class='icon icon-link'></div>";
modal += "<a>Import Link</a>";
modal += "</div>";
modal += "<div id='add_photo' class='add_album'>";
modal += "<div class='icon icon-picture'></div>";
modal += "<a>Upload Photo</a>";
modal += "</div>";
modal += "</div>";
modal += "</div>";
return modal;
},
signInModal: function() {
var modal = "";
modal += "<div class='message_overlay'>";
modal += "<div class='message center'>";
modal += "<h1><a class='icon-lock'></a> Sign In</h1>";
modal += "<div class='sign_in'>";
modal += "<input id='username' type='text' name='' value='' placeholder='username'>";
modal += "<input id='password' type='password' name='' value='' placeholder='password'>";
modal += "</div>";
modal += "<div id='version'>Version " + lychee.version + "<span> &#8211; <a target='_blank' href='" + lychee.updateURL + "'>Update available!</a><span></div>";
modal += "<a onclick='lychee.login()' class='button active'>Sign in</a>";
modal += "</div>";
modal += "</div>";
return modal;
},
uploadModal: function() {
var modal = "";
modal += "<div class='upload_overlay fadeIn'>";
modal += "<div class='upload_message center'>";
modal += "<a class='icon-upload'></a>";
modal += "<div class='progressbar'><div></div></div>";
modal += "</div>";
modal += "</div>";
return modal;
},
contextMenu: function(items) {
var menu = "";
menu += "<div class='contextmenu_bg'></div>";
menu += "<div class='contextmenu'>";
menu += "<table>";
menu += "<tbody>";
$.each(items, function(index) {
if (items[index][0]=="separator"&&items[index][1]==-1) menu += "<tr class='separator'></tr>";
else if (items[index][1]==-1) menu += "<tr class='no_hover'><td>" + items[index][0] + "</td></tr>";
else if (items[index][2]!=undefined) menu += "<tr><td onclick='" + items[index][2] + "; window.contextMenu.close();'>" + items[index][0] + "</td></tr>";
else menu += "<tr><td onclick='window.contextMenu.fns[" + items[index][1] + "](); window.contextMenu.close();'>" + items[index][0] + "</td></tr>";
});
menu += "</tbody>";
menu += "</table>";
menu += "</div>";
return menu;
},
infobox: function(photoJSON, forView) {
if (!photoJSON) return "";
var infobox = "",
public,
editTitleHTML,
editDescriptionHTML,
infos;
infobox += "<div class='header'><h1>About</h1><a class='icon-remove-sign'></a></div>";
infobox += "<div class='wrapper'>";
switch (photoJSON.public) {
case "0":
public = "Private";
break;
case "1":
public = "Public";
break;
case "2":
public = "Public (Album)";
break;
default:
public = "-";
break;
}
editTitleHTML = (forView==true||lychee.publicMode) ? "" : " <div id='edit_title'><a class='icon-pencil'></a></div>";
editDescriptionHTML = (forView==true||lychee.publicMode) ? "" : " <div id='edit_description'><a class='icon-pencil'></a></div>";
infos = [
["", "Basics"],
["Name", photoJSON.title + editTitleHTML],
["Uploaded", photoJSON.sysdate],
["Description", photoJSON.description + editDescriptionHTML],
["", "Image"],
["Size", photoJSON.size],
["Format", photoJSON.type],
["Resolution", photoJSON.width + " x " + photoJSON.height],
["", "Camera"],
["Captured", photoJSON.takedate],
["Make", photoJSON.make],
["Type/Model", photoJSON.model],
["Shutter Speed", photoJSON.shutter],
["Aperture", photoJSON.aperture],
["Focal Length", photoJSON.focal],
["ISO", photoJSON.iso],
["", "Share"],
["Visibility", public]
];
$.each(infos, function(index) {
if (infos[index][1]==""||infos[index][1]==undefined||infos[index][1]==null) infos[index][1] = "-";
if (infos[index][0]=="") {
infobox += "</table>";
infobox += "<div class='separater'><h1>" + infos[index][1] + "</h1></div>";
infobox += "<table id='infos'>";
} else {
infobox += "<tr>";
infobox += "<td>" + infos[index][0] + "</td>";
infobox += "<td class='attr_" + infos[index][0].toLowerCase() + "'>" + infos[index][1] + "</td>";
infobox += "</tr>";
}
});
infobox += "</table>";
infobox += "<div class='bumper'></div>";
infobox += "</div>";
return infobox;
}
}

@ -1,37 +0,0 @@
/**
* @name modal.js
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2013 by Philipp Maurer, Tobias Reich
*
* Modal Module
* Build, show and hide a modal.
*/
modal = {
fns: null,
show: function(title, text, buttons) {
if (!buttons) {
var buttons = [];
buttons[0] = ["", function() {}];
buttons[1] = ["", function() {}];
}
modal.fns = [buttons[0][1], buttons[1][1]];
$("body").append(build.modal(title, text, buttons));
$(".message input").focus();
},
close: function() {
modal.fns = null;
$(".message_overlay").removeClass("fadeIn").css("opacity", 0);
setTimeout(function() { $(".message_overlay").remove() }, 300);
}
}

@ -1,403 +0,0 @@
/**
* @name photo.js
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2013 by Philipp Maurer, Tobias Reich
*
* Photo Module
* Takes care of every action a photo can handle and execute.
*/
photo = {
json: null,
getID: function() {
var id;
if (photo.json) id = photo.json.id;
else id = $(".photo:hover, .photo.active").attr("data-id");
if (id) return id;
else return false;
},
load: function(photoID, albumID) {
var params,
checkPasswd;
params = "getPhoto&photoID=" + photoID + "&albumID=" + albumID + "&password=" + password.value;
lychee.api(params, "json", function(data) {
if (data=="HTTP/1.1 403 Wrong password!") {
checkPasswd = function() {
if (password.value!="") photo.load(photoID, albumID);
else setTimeout(checkPasswd, 250);
}
checkPasswd();
return false;
}
photo.json = data;
view.photo.init();
lychee.imageview.show();
setTimeout(function() { lychee.content.show() }, 300);
});
},
parse: function() {
if (!photo.json.title) photo.json.title = "Untitled";
photo.json.url = lychee.upload_path_big + photo.json.url;
},
add: {
files: function(files) {
var pre_progress = 0,
formData = new FormData(),
xhr = new XMLHttpRequest(),
popup,
progress;
$(".upload_overlay").remove();
$("body").append(build.uploadModal());
window.onbeforeunload = function() { return "Lychee is currently uploading!"; };
for (var i = 0; i < files.length; i++) formData.append(i, files[i]);
formData.append("function", "upload");
if (album.getID()=="") formData.append("albumID", 0);
else formData.append("albumID", album.getID());
xhr.open('POST', lychee.api_path);
xhr.onload = function() {
if (xhr.status===200) {
$(".progressbar div").css("width", "100%");
$(".upload_overlay").removeClass("fadeIn").css("opacity", 0);
setTimeout(function() { $(".upload_overlay").remove() }, 300);
if (window.webkitNotifications&&BrowserDetect.browser=="Safari") {
popup = window.webkitNotifications.createNotification("", "Upload complete", "You can now manage your new photo.");
popup.show();
}
window.onbeforeunload = null;
if (album.getID()=="") lychee.goto("a0");
else album.load(album.getID());
}
};
xhr.upload.onprogress = function(event) {
if (event.lengthComputable) {
progress = (event.loaded / event.total * 100 | 0);
if (progress>pre_progress) {
$(".progressbar div").css("width", progress + "%");
pre_progress = progress;
}
if (progress>=100) $(".progressbar div").css("opacity", 0.2);
}
};
$("#upload_files").val("");
xhr.send(formData);
},
url: function(link) {
var albumID = album.getID();
if (!link) link = prompt("Please enter the direct link to a photo to import it:", "");
if (album.getID()=="") albumID = 0;
if (link&&link.length>3) {
modal.close();
$(".upload_overlay").remove();
$("body").append(build.uploadModal());
$(".progressbar div").css({
"opacity": 0.2,
"width": "100%"
});
params = "importUrl&url=" + escape(link) + "&albumID=" + albumID;
lychee.api(params, "text", function(data) {
$(".upload_overlay").removeClass("fadeIn").css("opacity", 0);
setTimeout(function() { $(".upload_overlay").remove() }, 300);
if (data) {
if (album.getID()=="") lychee.goto("a0");
else album.load(album.getID());
} else loadingBar.show("error");
});
} else if (link.length>0) loadingBar.show("error", "Error", "Link to short or too long. Please try another one!");
},
dropbox: function() {
lychee.loadDropbox(function() {
Dropbox.choose({
linkType: "direct",
multiselect: false,
success: function(files) { photo.add.url(files[0].link) }
});
});
}
},
delete: function(photoID) {
var params,
buttons,
photoTitle;
if (!photoID) photoID = photo.getID();
if (visible.photo()) photoTitle = photo.json.title;
else photoTitle = album.json.content[photoID].title;
if (photoTitle=="") photoTitle = "Untitled";
buttons = [
["Delete Photo", function() {
album.json.content[photoID] = null;
view.album.content.delete(photoID);
// Only when search is not active
if (!visible.albums()) lychee.goto("a" + album.getID());
params = "deletePhoto&photoID=" + photoID;
lychee.api(params, "text", function(data) {
if (!data) loadingBar.show("error");
});
}],
["Keep Photo", function() {}]
];
modal.show("Delete Photo", "Are you sure you want to delete the photo '" + photoTitle + "'?<br>This action can't be undone!", buttons);
},
setTitle: function(photoID) {
var oldTitle = "",
newTitle,
params;
if (!photoID) photoID = photo.getID();
if (photo.json) oldTitle = photo.json.title;
else if (album.json) oldTitle = album.json.content[photoID].title;
newTitle = prompt("Please enter a new title for this photo:", oldTitle);
if (photoID!=null&&photoID&&newTitle.length<31) {
if (visible.photo()) {
photo.json.title = (newTitle=="") ? "Untitled" : newTitle;
view.photo.title(oldTitle);
}
album.json.content[photoID].title = newTitle;
view.album.content.title(photoID);
params = "setPhotoTitle&photoID=" + photoID + "&title=" + escape(encodeURI(newTitle));
lychee.api(params, "text", function(data) {
if (!data) loadingBar.show("error");
});
} else if (newTitle.length>0) loadingBar.show("error", "Error", "New title to short or too long. Please try another one!");
},
setAlbum: function(albumID, photoID) {
var params;
if (albumID>=0) {
if (visible.photo) lychee.goto("a" + album.getID());
album.json.content[photoID] = null;
view.album.content.delete(photoID);
params = "setAlbum&photoID=" + photoID + "&albumID=" + albumID;
lychee.api(params, "text", function(data) {
if (!data) loadingBar.show("error");
});
}
},
setStar: function(photoID) {
var params;
if (visible.photo()) {
photo.json.star = (photo.json.star==0) ? 1 : 0;
view.photo.star();
}
album.json.content[photoID].star = (album.json.content[photoID].star==0) ? 1 : 0;
view.album.content.star(photoID);
params = "setPhotoStar&photoID=" + photoID;
lychee.api(params, "text", function(data) {
if (!data) loadingBar.show("error");
});
},
setPublic: function(photoID, e) {
var params;
if (photo.json.public==2) {
modal.show("Public Album", "This photo is located in a public album. To make this photo private or public, edit the visibility of the associated album.", [["Show Album", function() { lychee.goto("a" + photo.json.original_album) }], ["Close", function() {}]]);
return false;
}
if (visible.photo()) {
photo.json.public = (photo.json.public==0) ? 1 : 0;
view.photo.public();
if (photo.json.public==1) contextMenu.sharePhoto(photoID, e);
}
album.json.content[photoID].public = (album.json.content[photoID].public==0) ? 1 : 0;
view.album.content.public(photoID);
params = "setPhotoPublic&photoID=" + photoID + "&url=" + photo.getViewLink(photoID);
lychee.api(params, "text", function(data) {
if (!data) loadingBar.show("error");
});
},
setDescription: function(photoID) {
var oldDescription = photo.json.description,
description = prompt("Please enter a description for this photo:", oldDescription),
params;
if (description.length>0&&description.length<160) {
if (visible.photo()) {
photo.json.description = description;
view.photo.description(oldDescription);
}
params = "setPhotoDescription&photoID=" + photoID + "&description=" + escape(description);
lychee.api(params, "text", function(data) {
if (!data) loadingBar.show("error");
});
} else if (description.length>0) loadingBar.show("error", "Error", "Description to short or too long. Please try another one!");
},
share: function(photoID, service) {
var link = "",
url = photo.getViewLink(photoID),
params,
filename = "unknown";
switch (service) {
case 0:
link = "https://twitter.com/share?url=" + encodeURI(url);
break;
case 1:
link = "http://www.facebook.com/sharer.php?u=" + encodeURI(url) + "&t=" + encodeURI(photo.json.title);
break;
case 2:
link = "mailto:?subject=" + encodeURI(photo.json.title) + "&body=" + encodeURI(url);
break;
case 3:
lychee.loadDropbox(function() {
filename = photo.json.title + "." + photo.getDirectLink().split('.').pop();
Dropbox.save(photo.getDirectLink(), filename);
});
break;
default:
link = "";
break;
}
if (link.length>5) location.href = link;
},
isSmall: function() {
var size = [
["width", false],
["height", false]
];
if (photo.json.width<$(window).width()-60) size["width"] = true;
if (photo.json.height<$(window).height()-100) size["height"] = true;
if (size["width"]&&size["height"]) return true;
else return false;
},
getDirectLink: function() {
return $("#imageview #image").css("background-image").replace(/"/g,"").replace(/url\(|\)$/ig, "");
},
getViewLink: function(photoID) {
if (location.href.indexOf("index.html")>0) return location.href.replace("index.html" + location.hash, "view.php?p=" + photoID);
else return location.href.replace(location.hash, "view.php?p=" + photoID);
}
}

@ -1,431 +0,0 @@
/**
* @name photos.js
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2013 by Philipp Maurer, Tobias Reich
*
* Photos Module
* Takes care of every action photos can handle and execute.
*/
photos = {
load: function(albumID, refresh) {
// If refresh is true the function will only refresh the content and not change the toolbar buttons either the title
/*password = localStorage.getItem("album" + albumID);
if (password==null) {
if (lychee.publicMode) password = prompt("Please enter a password for this album:", ""); else password = "";
if (password!="") password = hex_md5(password);
localStorage.setItem("album" + albumID, password);
}*/
password = "";
if (!refresh) {
loadingBar.show();
if (visible.imageview()) photos.hideView();
lychee.animate(".album, .photo", "contentZoomOut");
lychee.animate(".divider", "fadeOut");
}
startTime = new Date().getTime();
params = "getPhotos&albumID=" + albumID + "&password=" + password;
lychee.api(params, "json", function(data) {
if (data=="HTTP/1.1 403 Wrong password!") {
localStorage.removeItem("album" + albumID);
photos.load(albumID, refresh);
return false;
}
durationTime = (new Date().getTime() - startTime);
if (durationTime>300) waitTime = 0; else if (refresh) waitTime = 0; else waitTime = 300 - durationTime;
$.timer(waitTime,function(){
photosData = "";
$.each(data, function() { photosData += build.photo(this); });
lychee.content.html(photosData);
if (!refresh) {
lychee.animate(".album, .photo", "contentZoomIn");
$("#tools_albums, #tools_photo").hide();
$("#tools_album").show();
$("img").retina();
albums.loadInfo(albumID, password);
}
}, false);
});
},
loadInfo: function(photoID, albumID) {
/*password = localStorage.getItem("album" + albumID);
if (password==null) {
if (lychee.publicMode) password = prompt("Please enter a password for this album:", ""); else password = "";
if (password!="") password = hex_md5(password);
localStorage.setItem("album" + albumID, password);
}*/
password = "";
photos.showView();
params = "getPhotoInfo&photoID=" + photoID + "&password=" + password;
lychee.api(params, "json", function(data) {
if (data=="HTTP/1.1 403 Wrong password!") {
localStorage.removeItem("album" + albumID);
photos.loadInfo(photoID, albumID);
return false;
}
if (!data.title) data.title = "Untitled";
document.title = "Lychee - " + data.title;
lychee.headerTitle.html(data.title).addClass("editable");
$("#button_star a").removeClass("icon-star-empty icon-star");
if (data.star=="1") {
$("#button_star a").addClass("icon-star");
$("#button_star").attr("title", "Unstar Photo");
} else {
$("#button_star a").addClass("icon-star-empty");
$("#button_star").attr("title", "Star Photo");
}
if (data.public=="1") {
$("#button_share a").addClass("active");
$("#button_share").attr("title", "Share Photo");
} else {
$("#button_share a").removeClass("active");
$("#button_share").attr("title", "Make Public");
}
data.url = lychee.upload_path + data.url;
if (visible.controls()&&photos.isSmall(data)) lychee.image_view.html("<a id='previous' class='icon-caret-left'></a><a id='next' class='icon-caret-right'></a><div id='image' class='small' style='background-image: url(" + data.url + "); width: " + data.width + "px; height: " + data.height + "px; margin-top: -" + parseInt(data.height/2-20) + "px; margin-left: -" + data.width/2 + "px;'></div>");
else if (visible.controls()) lychee.image_view.html("<a id='previous' class='icon-caret-left'></a><a id='next' class='icon-caret-right'></a><div id='image' style='background-image: url(" + data.url + ")'></div>");
else if (photos.isSmall(data)) lychee.image_view.html("<a id='previous' style='left: -50px' class='icon-caret-left'></a><a id='next' style='right: -50px' class='icon-caret-right'></a><div id='image' class='small' style='background-image: url(" + data.url + "); width: " + data.width + "px; height: " + data.height + "px; margin-top: -" + parseInt(data.height/2) + "px; margin-left: -" + data.width/2 + "px;'></div>");
else lychee.image_view.html("<a id='previous' style='left: -50px' class='icon-caret-left'></a><a id='next' style='right: -50px' class='icon-caret-right'></a><div id='image' style='background-image: url(" + data.url + "); top: 0px; right: 0px; bottom: 0px; left: 0px;'></div>");
lychee.animate(image_view, "fadeIn");
lychee.image_view.show();
if (!visible.controls()) lychee.hideControls();
lychee.infobox.html(build.infobox(data)).show();
$.timer(300,function(){ lychee.content.show(); });
});
},
isSmall: function(photo) {
size = [
["width", false],
["height", false]
];
if (photo.width<$(window).width()-60) size["width"] = true;
if (photo.height<$(window).height()-100) size["height"] = true;
if (size["width"]&&size["height"]) return true;
else return false;
},
showView: function() {
// Change toolbar-buttons
$("#tools_albums, #tools_album").hide();
$("#tools_photo").show();
// Make body not scrollable
$("body").css("overflow", "hidden");
},
hideView: function() {
// Change toolbar-buttons
$("#tools_photo, #tools_albums").hide();
$("#tools_album").show();
// Make body scrollable
$("body").css("overflow", "scroll");
// Change website title and url by using albums.loadInfo
albums.loadInfo(lychee.content.attr("data-id"));
// Hide ImageViewer
lychee.animate(image_view, "fadeOut");
$.timer(300,function(){ lychee.image_view.hide() });
},
showInfobox: function() {
if (!visible.infobox()) $("body").append("<div id='infobox_overlay'></div>");
lychee.infobox.css("right", "0px");
},
hideInfobox: function() {
$("#infobox_overlay").remove();
lychee.infobox.css("right", "-320px");
},
hide: function(photoID) {
$(".photo[data-id='" + photoID + "']").css("opacity", 0).animate({
width: 0,
marginLeft: 0
}, 300, function() {
$(this).remove();
if (!visible.imageview()) {
imgNum = parseInt($("#title span").html().replace("- ", "").replace(" photos", ""))-1;
$("#title span").html(" - " + imgNum + " photos");
}
});
},
delete: function(photoID) {
params = "deletePhoto&photoID=" + photoID;
lychee.api(params, "text", function(data) {
if (data) {
photos.hide(photoID);
lychee.goto("a" + lychee.content.attr("data-id"));
} else loadingBar.show("error");
});
},
deleteDialog: function(photoID) {
if (!photoID) photoID = lychee.image_view.attr("data-id");
if (visible.imageview()) photoTitle = lychee.title();
else photoTitle = $(".photo[data-id='" + photoID + "'] .overlay h1").html();
if (photoTitle=="") photoTitle = "Untitled";
f1 = "photos.delete(" + photoID + ");";
f2 = "";
modal = build.modal("Delete Photo", "Are you sure you want to delete the photo '" + photoTitle + "'?<br>This action can't be undone!", ["Delete Photo", "Keep Photo"], [f1, f2]);
$("body").append(modal);
},
setTitle: function(photoID) {
if (!photoID) oldTitle = lychee.title(); else oldTitle = "";
if (!photoID) photoID = lychee.image_view.attr("data-id");
newTitle = prompt("Please enter a new title for this photo:", oldTitle);
if (photoID!=null&&photoID&&newTitle.length<31) {
if (newTitle=="") newTitle = "Untitled";
params = "setPhotoTitle&photoID=" + photoID + "&title=" + encodeURI(newTitle);
lychee.api(params, "text", function(data) {
if (data) {
if (visible.imageview()) {
$("#infobox .attr_name").html($("#infobox .attr_name").html().replace(lychee.title(), newTitle));
lychee.headerTitle.html(newTitle);
document.title = "Lychee - " + newTitle;
}
$(".photo[data-id='" + photoID + "'] .overlay h1").html(newTitle);
} else loadingBar.show("error");
});
} else if (newTitle.length>0) loadingBar.show("error", "Error", "New title to short or too long. Please try another one!");
},
setAlbum: function(photoID, albumID) {
if (albumID>=0) {
params = "setAlbum&photoID=" + photoID + "&albumID=" + albumID;
lychee.api(params, "text", function(data) {
if (data) {
photos.hide(photoID);
lychee.goto("a" + lychee.content.attr("data-id"));
} else loadingBar.show("error");
});
}
},
setStar: function() {
photoID = lychee.image_view.attr("data-id");
params = "setPhotoStar&photoID=" + photoID;
lychee.api(params, "text", function(data) {
if (data) {
if ($("#button_star a.icon-star-empty").length) {
$("#button_star a").removeClass("icon-star-empty icon-star").addClass("icon-star");
$("#button_star").attr("title", "Unstar Photo");
} else {
$("#button_star a").removeClass("icon-star-empty icon-star").addClass("icon-star-empty");
$("#button_star").attr("title", "Star Photo");
}
photos.load(lychee.content.attr("data-id"), true);
} else loadingBar.show("error");
});
},
setPublic: function(e) {
photoID = lychee.image_view.attr("data-id");
params = "setPhotoPublic&photoID=" + photoID + "&url=" + photos.getViewLink(photoID);
lychee.api(params, "text", function(data) {
if (data) {
if ($("#button_share a.active").length) {
$("#button_share a").removeClass("active");
$("#button_share").attr("title", "Make Public");
$("#infobox .attr_visibility").html("Private");
} else {
$("#button_share a").addClass("active");
$("#button_share").attr("title", "Share Photo");
$("#infobox .attr_visibility").html("Public");
contextMenu.share(photoID, e.pageX, e.pageY);
}
photos.load(lychee.content.attr("data-id"), true);
} else loadingBar.show("error");
});
},
setDescription: function() {
description = prompt("Please enter a description for this photo:", "");
photoID = lychee.image_view.attr("data-id");
if (description.length>0&&description.length<160) {
params = "setPhotoDescription&photoID=" + photoID + "&description=" + escape(description);
lychee.api(params, "text", function(data) {
if (data) {
$("#infobox .attr_description").html(description + " <div id='edit_description'><a class='icon-pencil'></a></div>");
} else loadingBar.show("error");
});
} else if (description.length>0) loadingBar.show("error", "Error", "Description to short or too long. Please try another one!");
},
share: function(service, photoID) {
link = "";
url = photos.getViewLink(photoID);
switch (service) {
case 0:
link = "https://twitter.com/share?url=" + encodeURI(url);
break;
case 1:
link = "http://www.facebook.com/sharer.php?u=" + encodeURI(url) + "&t=" + encodeURI(lychee.title());
break;
case 2:
link = "mailto:?subject=" + encodeURI(lychee.title()) + "&body=" + encodeURI("Hi! Check this out: " + url);
break;
case 3:
modal = build.modal("Copy Link", "Everyone can view your public photos, but only you can edit them. Use this link to share your photo with others: <input class='copylink' value='" + url + "'>", ["Close"], [""]);
$("body").append(modal);
$(".copylink").focus();
break;
case 4:
params = "getShortlink&photoID=" + photoID;
lychee.api(params, "text", function(data) {
if (data=="") data = "Something went wrong!";
modal = build.modal("Copy Shortlink", "Everyone can view your public photos, but only you can edit them. Use this link to share your photo with others: <input class='copylink' value='" + data + "'>", ["Close"], [""]);
$("body").append(modal);
$(".copylink").focus();
});
break;
default:
link = "";
break;
}
if (link.length>5) location.href = link;
},
getViewLink: function(photoID) {
if (location.href.indexOf("index.html")>0) return location.href.replace("index.html" + location.hash, "view.php?p=" + photoID);
else return location.href.replace(location.hash, "view.php?p=" + photoID);
},
previous: function() {
albumID = lychee.content.attr("data-id");
photoID = lychee.image_view.attr("data-id");
params = "previousPhoto&photoID=" + photoID + "&albumID=" + albumID;
lychee.api(params, "json", function(data) {
if (data!=false) lychee.goto("a" + albumID + "p" + data.id);
});
},
next: function() {
albumID = lychee.content.attr("data-id");
photoID = lychee.image_view.attr("data-id");
params = "nextPhoto&photoID=" + photoID + "&albumID=" + albumID;
lychee.api(params, "json", function(data) {
if (data) lychee.goto("a" + albumID + "p" + data.id);
});
}
}

@ -1,48 +0,0 @@
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
CREATE TABLE IF NOT EXISTS `lychee_albums` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(50) NOT NULL,
`sysdate` varchar(10) NOT NULL,
`public` TINYINT(1) DEFAULT '0',
`password` VARCHAR(100),
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `lychee_photos` (
`id` bigint(14) NOT NULL,
`title` varchar(50) NOT NULL,
`description` varchar(160) NOT NULL,
`url` varchar(100) NOT NULL,
`public` tinyint(1) NOT NULL,
`shortlink` varchar(20) NOT NULL,
`type` varchar(10) NOT NULL,
`width` int(11) NOT NULL,
`height` int(11) NOT NULL,
`size` varchar(10) NOT NULL,
`sysdate` varchar(10) NOT NULL,
`systime` varchar(8) NOT NULL,
`iso` varchar(15) NOT NULL,
`aperture` varchar(10) NOT NULL,
`make` varchar(20) NOT NULL,
`model` varchar(50) NOT NULL,
`shutter` varchar(10) NOT NULL,
`focal` varchar(10) NOT NULL,
`takedate` varchar(10) NOT NULL,
`taketime` varchar(8) NOT NULL,
`star` tinyint(1) NOT NULL,
`thumbUrl` varchar(50) NOT NULL,
`album` varchar(30) NOT NULL DEFAULT '0',
`import_name` varchar(100) DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

@ -1,115 +1,282 @@
<?php
/**
* @name api.php
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2013 by Philipp Maurer, Tobias Reich
*/
if (floatval(phpversion())<5.2) die('Please upgrade to PHP 5.2 or higher!');
if (!empty($_POST['function'])||!empty($_GET['function'])) {
session_start();
define('LYCHEE', true);
require('config.php');
require('functions.php');
// Security
if (isset($_POST['albumID'])&&($_POST['albumID']==''||$_POST['albumID']<0)) exit('Wrong parameter type for albumID!');
if (isset($_POST['photoID'])&&$_POST['photoID']=='') exit('Wrong parameter type for photoID!');
//Connect to DB
$database = dbConnect();
if (isset($_SESSION['login'])&&$_SESSION['login']==true) {
/**
* Admin Mode
* Full access to Lychee. Only with correct password.
*/
// Album Functions
if ($_POST['function']=='getAlbums') echo json_encode(getAlbums(false));
if ($_POST['function']=='getSmartInfo') echo json_encode(getSmartInfo());
if ($_POST['function']=='getAlbum'&&isset($_POST['albumID'])) echo json_encode(getAlbum($_POST['albumID']));
if ($_POST['function']=='addAlbum'&&isset($_POST['title'])) echo addAlbum($_POST['title']);
if ($_POST['function']=='setAlbumTitle'&&isset($_POST['albumID'])&&isset($_POST['title'])) echo setAlbumTitle($_POST['albumID'], $_POST['title']);
if ($_POST['function']=='setAlbumPublic'&&isset($_POST['albumID'])) echo setAlbumPublic($_POST['albumID'], $_POST['password']);
if ($_POST['function']=='setAlbumPassword'&&isset($_POST['albumID'])&&isset($_POST['password'])) echo setAlbumPassword($_POST['albumID'], $_POST['password']);
if ($_POST['function']=='deleteAlbum'&&isset($_POST['albumID'])&&isset($_POST['delAll'])) echo deleteAlbum($_POST['albumID'], $_POST['delAll']);
if (isset($_GET['function'])&&$_GET['function']=='getAlbumArchive'&&isset($_GET['albumID'])) getAlbumArchive($_GET['albumID']);
// Photo Functions
if ($_POST['function']=='getPhoto'&&isset($_POST['photoID'])&&isset($_POST['albumID'])) echo json_encode(getPhoto($_POST['photoID'], $_POST['albumID']));
if ($_POST['function']=='deletePhoto'&&isset($_POST['photoID'])) echo deletePhoto($_POST['photoID']);
if ($_POST['function']=='setAlbum'&&isset($_POST['photoID'])&&isset($_POST['albumID'])) echo setAlbum($_POST['photoID'], $_POST['albumID']);
if ($_POST['function']=='setPhotoTitle'&&isset($_POST['photoID'])&&isset($_POST['title'])) echo setPhotoTitle($_POST['photoID'], $_POST['title']);
if ($_POST['function']=='setPhotoStar'&&isset($_POST['photoID'])) echo setPhotoStar($_POST['photoID']);
if ($_POST['function']=='setPhotoPublic'&&isset($_POST['photoID'])&&isset($_POST['url'])) echo setPhotoPublic($_POST['photoID'], $_POST['url']);
if ($_POST['function']=='setPhotoDescription'&&isset($_POST['photoID'])&&isset($_POST['description'])) echo setPhotoDescription($_POST['photoID'], $_POST['description']);
// Add Function
if ($_POST['function']=='upload'&&isset($_FILES)&&isset($_POST['albumID'])) echo upload($_FILES, $_POST['albumID']);
if ($_POST['function']=='importUrl'&&isset($_POST['url'])&&isset($_POST['albumID'])) echo importUrl($_POST['url'], $_POST['albumID']);
// Search Function
if ($_POST['function']=='search'&&isset($_POST['term'])) echo json_encode(search($_POST['term']));
// Session Functions
if ($_POST['function']=='init') echo json_encode(init('admin'));
if ($_POST['function']=='login') echo login($_POST['user'], $_POST['password']);
if ($_POST['function']=='logout') logout();
} else {
/**
* Public Mode
* Access to view all public folders and photos in Lychee.
*/
// Album Functions
if ($_POST['function']=='getAlbums') echo json_encode(getAlbums(true));
if ($_POST['function']=='getAlbum'&&isset($_POST['albumID'])&&isset($_POST['password'])) {
if (isAlbumPublic($_POST['albumID'])) {
// Album Public
if (checkAlbumPassword($_POST['albumID'], $_POST['password'])) echo json_encode(getAlbum($_POST['albumID']));
else echo json_encode('HTTP/1.1 403 Wrong password!');
} else {
// Album Private
echo json_encode('HTTP/1.1 403 Album private!');
}
}
if ($_POST['function']=='checkAlbumAccess'&&isset($_POST['albumID'])&&isset($_POST['password'])) {
if (isAlbumPublic($_POST['albumID'])) {
// Album Public
if (checkAlbumPassword($_POST['albumID'], $_POST['password'])) echo true;
else echo false;
} else {
// Album Private
echo false;
}
}
// Photo Functions
if ($_POST['function']=='getPhoto'&&isset($_POST['photoID'])&&isset($_POST['albumID'])&&isset($_POST['password'])) {
if (isPhotoPublic($_POST['photoID'], $_POST['password'])) echo json_encode(getPhoto($_POST['photoID'], $_POST['albumID']));
else echo json_encode('HTTP/1.1 403 Wrong password!');
}
// Session Functions
if ($_POST['function']=='init') echo json_encode(init('public'));
if ($_POST['function']=='login') echo login($_POST['user'], $_POST['password']);
}
} else {
header('HTTP/1.1 401 Unauthorized');
die('Error: No permission!');
}
<?php
/**
* @name API
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2014 by Philipp Maurer, Tobias Reich
*/
@ini_set('max_execution_time', '200');
@ini_set('post_max_size', '200M');
@ini_set('upload_max_size', '200M');
@ini_set('upload_max_filesize', '20M');
@ini_set('max_file_uploads', '100');
if (!empty($_POST['function'])||!empty($_GET['function'])) {
session_start();
define('LYCHEE', true);
require('modules/db.php');
require('modules/session.php');
require('modules/settings.php');
require('modules/upload.php');
require('modules/album.php');
require('modules/photo.php');
require('modules/misc.php');
if (file_exists('config.php')) require('config.php');
else {
/**
* Installation Mode
* Limited access to configure Lychee. Only available when the config.php file is missing.
*/
switch ($_POST['function']) {
case 'createConfig': if (isset($_POST['dbHost'])&&isset($_POST['dbUser'])&&isset($_POST['dbPassword'])&&isset($_POST['dbName']))
echo createConfig($_POST['dbHost'], $_POST['dbUser'], $_POST['dbPassword'], $_POST['dbName']);
break;
default: echo 'Warning: No configuration!';
break;
}
exit();
}
// Connect to DB
$database = dbConnect();
// Get Settings
$settings = getSettings();
// Security
if (isset($_POST['albumID'])&&($_POST['albumID']==''||$_POST['albumID']<0)) exit('Error: Wrong parameter type for albumID!');
if (isset($_POST['photoID'])&&$_POST['photoID']=='') exit('Error: Wrong parameter type for photoID!');
foreach(array_keys($_POST) as $key) $_POST[$key] = mysqli_real_escape_string($database, urldecode($_POST[$key]));
if (isset($_SESSION['login'])&&$_SESSION['login']==true) {
/**
* Admin Mode
* Full access to Lychee. Only with correct password/session.
*/
switch ($_POST['function']) {
// Album Functions
case 'getAlbums': echo json_encode(getAlbums(false));
break;
case 'getAlbum': if (isset($_POST['albumID']))
echo json_encode(getAlbum($_POST['albumID']));
break;
case 'addAlbum': if (isset($_POST['title']))
echo addAlbum($_POST['title']);
break;
case 'setAlbumTitle': if (isset($_POST['albumID'])&&isset($_POST['title']))
echo setAlbumTitle($_POST['albumID'], $_POST['title']);
break;
case 'setAlbumDescription': if (isset($_POST['albumID'])&&isset($_POST['description']))
echo setAlbumDescription($_POST['albumID'], $_POST['description']);
break;
case 'setAlbumPublic': if (isset($_POST['albumID']))
echo setAlbumPublic($_POST['albumID'], $_POST['password']);
break;
case 'setAlbumPassword':if (isset($_POST['albumID'])&&isset($_POST['password']))
echo setAlbumPassword($_POST['albumID'], $_POST['password']);
break;
case 'deleteAlbum': if (isset($_POST['albumID'])&&isset($_POST['delAll']))
echo deleteAlbum($_POST['albumID'], $_POST['delAll']);
break;
// Photo Functions
case 'getPhoto': if (isset($_POST['photoID'])&&isset($_POST['albumID']))
echo json_encode(getPhoto($_POST['photoID'], $_POST['albumID']));
break;
case 'deletePhoto': if (isset($_POST['photoID']))
echo deletePhoto($_POST['photoID']);
break;
case 'setAlbum': if (isset($_POST['photoID'])&&isset($_POST['albumID']))
echo setAlbum($_POST['photoID'], $_POST['albumID']);
break;
case 'setPhotoTitle': if (isset($_POST['photoID'])&&isset($_POST['title']))
echo setPhotoTitle($_POST['photoID'], $_POST['title']);
break;
case 'setPhotoStar': if (isset($_POST['photoID']))
echo setPhotoStar($_POST['photoID']);
break;
case 'setPhotoPublic': if (isset($_POST['photoID'])&&isset($_POST['url']))
echo setPhotoPublic($_POST['photoID'], $_POST['url']);
break;
case 'setPhotoDescription': if (isset($_POST['photoID'])&&isset($_POST['description']))
echo setPhotoDescription($_POST['photoID'], $_POST['description']);
break;
// Add Functions
case 'upload': if (isset($_FILES)&&isset($_POST['albumID']))
echo upload($_FILES, $_POST['albumID']);
break;
case 'importUrl': if (isset($_POST['url'])&&isset($_POST['albumID']))
echo importUrl($_POST['url'], $_POST['albumID']);
break;
case 'importServer': if (isset($_POST['albumID']))
echo importServer($_POST['albumID']);
break;
// Search Function
case 'search': if (isset($_POST['term']))
echo json_encode(search($_POST['term']));
break;
// Session Function
case 'init': echo json_encode(init('admin'));
break;
case 'login': if (isset($_POST['user'])&&isset($_POST['password']))
echo login($_POST['user'], $_POST['password']);
break;
case 'logout': logout();
break;
// Settings
case 'setLogin': if (isset($_POST['username'])&&isset($_POST['password']))
echo setLogin($_POST['oldPassword'], $_POST['username'], $_POST['password']);
break;
case 'setSorting': if (isset($_POST['type'])&&isset($_POST['order']))
echo setSorting($_POST['type'], $_POST['order']);
break;
// Miscellaneous
case 'update': echo update();
default: if (isset($_GET['function'])&&$_GET['function']=='getAlbumArchive'&&isset($_GET['albumID']))
// Album Archive
getAlbumArchive($_GET['albumID']);
else if (isset($_GET['function'])&&$_GET['function']=='update')
// Update Lychee
echo update();
else
exit('Error: Function not found! Please check the spelling of the called function.');
break;
}
} else {
/**
* Public Mode
* Access to view all public folders and photos in Lychee.
*/
switch ($_POST['function']) {
// Album Functions
case 'getAlbums': echo json_encode(getAlbums(true));
break;
case 'getAlbum': if (isset($_POST['albumID'])&&isset($_POST['password'])) {
if (isAlbumPublic($_POST['albumID'])) {
// Album Public
if (checkAlbumPassword($_POST['albumID'], $_POST['password']))
echo json_encode(getAlbum($_POST['albumID']));
else
echo 'Warning: Wrong password!';
} else {
// Album Private
echo 'Warning: Album private!';
}
}
break;
case 'checkAlbumAccess':if (isset($_POST['albumID'])&&isset($_POST['password'])) {
if (isAlbumPublic($_POST['albumID'])) {
// Album Public
if (checkAlbumPassword($_POST['albumID'], $_POST['password']))
echo true;
else
echo false;
} else {
// Album Private
echo false;
}
}
break;
// Photo Functions
case 'getPhoto': if (isset($_POST['photoID'])&&isset($_POST['albumID'])&&isset($_POST['password'])) {
if (isPhotoPublic($_POST['photoID'], $_POST['password']))
echo json_encode(getPhoto($_POST['photoID'], $_POST['albumID']));
else
echo 'Warning: Wrong password!';
}
break;
// Session Functions
case 'init': echo json_encode(init('public'));
break;
case 'login': if (isset($_POST['user'])&&isset($_POST['password']))
echo login($_POST['user'], $_POST['password']);
break;
// Miscellaneous
default: if (isset($_GET['function'])&&$_GET['function']=='getAlbumArchive'&&isset($_GET['albumID'])&&isset($_GET['password'])) {
if (isAlbumPublic($_GET['albumID'])) {
// Album Public
if (checkAlbumPassword($_GET['albumID'], $_GET['password']))
getAlbumArchive($_GET['albumID']);
else
echo 'Warning: Wrong password!';
} else {
// Album Private
echo 'Warning: Album private or not downloadable!';
}
} else {
exit('Error: Function not found! Please check the spelling of the called function.');
}
break;
}
}
} else {
exit('Error: No permission!');
}
?>

@ -1,45 +0,0 @@
<?php
define('LYCHEE', true);
// Declare
$error = '';
// Include
require('config.php');
// PHP Version
if (floatval(phpversion())<5.2) $error .= ('Error 100: Please upgrade to PHP 5.2 or higher!<br>\n');
// Extensions
if (!extension_loaded('exif')) $error .= ('Error 200: PHP exif extension not activated.<br>\n');
if (!extension_loaded('mbstring')) $error .= ('Error 201: PHP mbstring extension not activated.<br>\n');
if (!extension_loaded('gd')) $error .= ('Error 202: PHP gd extension not activated.<br>\n');
if (!extension_loaded('mysqli')) $error .= ('Error 203: PHP mysqli extension not activated.<br>\n');
// Config
if (!$db||$db=='') $error .= ('Error 300: No property for \$db in config.php.<br>\n');
if (!$dbUser||$dbUser=='') $error .= ('Error 301: No property for \$dbUser in config.php.<br>\n');
if (!$dbPassword||$dbPassword=='') $error .= ('Error 302: No property for \$dbPassword in config.php.<br>\n');
if (!$dbHost||$dbHost=='') $error .= ('Error 303: No property for \$dbHost in config.php.<br>\n');
if (!$user||$user=='') $error .= ('Error 304: No property for \$user in config.php.<br>\n');
if (!$password||$password=='') $error .= ('Error 305: No property for \$password in config.php.<br>\n');
// Additional Config
//if ($checkForUpdates!=true&&heckForUpdates!=false) $error .= ('Error 306: No property for \$checkForUpdates in config.php.<br>\n');
if (!is_numeric($thumbQuality)||$thumbQuality<=0||$thumbQuality>=100) $error .= ('Error 307: Wrong property for \$thumbQuality in config.php.<br>\n');
if ($sorting!='ASC'&&$sorting!='DESC') $error .= ('Error 308: Wrong property for \$sorting in config.php.<br>\n');
// Database
$database = new mysqli($dbHost, $dbUser, $dbPassword, $db);
if (mysqli_connect_errno()!=0) $error .= ('Error 400: ' . mysqli_connect_errno() . ': ' . mysqli_connect_error() . '<br>\n');
// Permissions
if (substr(sprintf('%o', fileperms('../uploads/big/')), -4)!='0777') $error .= ('Error 500: Wrong permissions for \'/uploads/big\' (777 required).<br>\n');
if (substr(sprintf('%o', fileperms('../uploads/thumb/')), -4)!='0777') $error .= ('Error 501: Wrong permissions for \'/uploads/thumb\' (777 required).<br>\n');
if (substr(sprintf('%o', fileperms('../uploads/import/')), -4)!='0777') $error .= ('Error 502: Wrong permissions for \'/uploads/import\' (777 required).<br>\n');
if (substr(sprintf('%o', fileperms('../uploads/')), -4)!='0777') $error .= ('Error 503: Wrong permissions for \'/uploads\' (777 required).<br>\n');
if ($error=='') echo('Lychee is ready!'); else echo $error;
?>

@ -1,27 +0,0 @@
<?php
/**
* @name config.php
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2013 by Philipp Maurer, Tobias Reich
*/
if(!defined('LYCHEE')) die('Direct access is not allowed!');
// Database configurations
$db = 'lychee'; //Database name
$dbUser = 'lychee'; //Username of the database
$dbPassword = 'lychee_passwd'; //Password of the Database
$dbHost = 'localhost'; //Host of the Database
// Admin Login
$user = 'lychee'; //Admin Username
$password = '1234'; //Admin Password
// Additional settings
$checkForUpdates = true;
$thumbQuality = 95; //Quality of the Thumbs (0-100). Default: 95
$sorting = 'DESC'; //ASC or DESC sorting of albums and photos
?>

@ -1,801 +0,0 @@
<?php
/**
* @name functions.php
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2013 by Philipp Maurer, Tobias Reich
*/
if (!defined('LYCHEE')) die('Direct access is not allowed!');
// Database Functions
function dbConnect() {
global $db, $dbUser, $dbPassword, $dbHost;
$database = new mysqli($dbHost, $dbUser, $dbPassword);
if (mysqli_connect_errno() != 0) {
echo mysqli_connect_errno().': '.mysqli_connect_error();
return false;
}
if (!$database->select_db($db)) {
createDatabase($db, $database);
}
$query = "SELECT * FROM lychee_photos, lychee_albums;";
if (!$database->query($query)) createTables($database);
return $database;
}
function dbClose() {
global $database;
if (!$database->close()) {
echo "Closing the connection failed!";
return false;
}
return true;
}
function createDatabase($db, $database) {
$result = $database->query("CREATE DATABASE IF NOT EXISTS $db;");
$database->select_db($db);
if (!$result) return false;
return true;
}
function createTables($database) {
$query = "CREATE TABLE IF NOT EXISTS `lychee_albums` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(50) NOT NULL,
`sysdate` varchar(10) NOT NULL,
`public` TINYINT(1) DEFAULT '0',
`password` VARCHAR(100),
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;";
$result = $database->query($query);
if (!$result) return false;
$query = "CREATE TABLE `lychee_photos` (
`id` bigint(14) NOT NULL,
`title` varchar(50) NOT NULL,
`description` varchar(160) NOT NULL,
`url` varchar(100) NOT NULL,
`public` tinyint(1) NOT NULL,
`type` varchar(10) NOT NULL,
`width` int(11) NOT NULL,
`height` int(11) NOT NULL,
`size` varchar(10) NOT NULL,
`sysdate` varchar(10) NOT NULL,
`systime` varchar(8) NOT NULL,
`iso` varchar(15) NOT NULL,
`aperture` varchar(10) NOT NULL,
`make` varchar(20) NOT NULL,
`model` varchar(50) NOT NULL,
`shutter` varchar(20) NOT NULL,
`focal` varchar(10) NOT NULL,
`takedate` varchar(10) NOT NULL,
`taketime` varchar(8) NOT NULL,
`star` tinyint(1) NOT NULL,
`thumbUrl` varchar(50) NOT NULL,
`album` varchar(30) NOT NULL DEFAULT '0',
`import_name` varchar(100) DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;";
$result = $database->query($query);
if (!$result) return false;
return true;
}
// Upload Functions
function upload($files, $albumID) {
global $database;
switch($albumID) {
// s for public (share)
case 's':
$public = 1;
$star = 0;
$albumID = 0;
break;
// f for starred (fav)
case 'f':
$star = 1;
$public = 0;
$albumID = 0;
break;
default:
$star = 0;
$public = 0;
}
foreach ($files as $file) {
$id = str_replace('.', '', microtime(true));
while(strlen($id)<14) $id .= 0;
$tmp_name = $file["tmp_name"];
$type = getimagesize($tmp_name);
if (($type[2]!=1)&&($type[2]!=2)&&($type[2]!=3)) return false;
$data = $file["name"];
$data = explode('.',$data);
$data = array_reverse ($data);
$data = $data[0];
// Import if not uploaded via web
if (!is_uploaded_file($file)) {
if (copy($tmp_name, "../uploads/big/" . md5($id) . ".$data")) {
unlink($tmp_name);
$import_name = $tmp_name;
}
} else {
move_uploaded_file($tmp_name, "../uploads/big/" . md5($id) . ".$data");
$import_name = "";
}
// Create Thumb
createThumb(md5($id).".".$data);
// Read infos
$info = getCamera(md5($id).".".$data);
$title = "";
if (isset($info['type'])){$type=$info['type'];} else {$type="";}
if (isset($info['width'])){$width=$info['width'];} else {$width="";}
if (isset($info['height'])){$height=$info['height'] OR "";} else {$height="";}
if (isset($info['size'])){$size=$info['size'] OR "";} else {$size="";}
if (isset($info['date'])){$sysdate=$info['date'];} else {$sysdate="";}
if (isset($info['time'])){$systime=$info['time'];} else {$systime="";}
if (isset($info['iso'])){$iso=$info['iso'];} else {$iso="";}
if (isset($info['aperture'])){$aperture=$info['aperture'];} else {$aperture="";}
if (isset($info['make'])){$make=$info['make'];} else {$make="";}
if (isset($info['model'])){$model=$info['model'] OR "";} else {$model="";}
if (isset($info['shutter'])){$shutter=$info['shutter'];} else {$shutter="";}
if (isset($info['focal'])){$focal=$info['focal'];} else {$focal="";}
if (isset($info['takeDate'])){$takeDate=$info['takeDate'];} else {$takeDate="";}
if (isset($info['takeTime'])){$takeTime=$info['takeTime'];} else {$takeTime="";}
$query = "INSERT INTO lychee_photos (id, title, url, description, type, width, height, size, sysdate, systime, iso, aperture, make, model, shutter, focal, takedate, taketime, thumbUrl, album, public, star, import_name)
VALUES ('$id', '$title', '" . md5($id) . ".$data', '', '$type', '$width', '$height', '$size', '$sysdate', '$systime', '$iso', '$aperture', '$make', '$model', '$shutter', '$focal', '$takeDate', '$takeTime', '" . md5($id) . ".$data', '$albumID', '$public', '$star', '$import_name');";
$result = $database->query($query);
}
return true;
}
function getCamera($filename) {
global $database;
$url = "../uploads/big/$filename";
$type = getimagesize($url);
$type = $type['mime'];
if (($type == "image/jpeg") && function_exists('exif_read_data') ){
$exif = exif_read_data($url, "EXIF", 0);
// General information
$return['name'] = $exif['FileName'];
$generalInfos = getimagesize($url);
$return['type'] = $generalInfos['mime'];
$return['width'] = $generalInfos[0];
$return['height'] = $generalInfos[1];
$size = (filesize($url) / 1024);
if ($size >= 1024){$size=round($size/1024,1)." MB";} else {$size=round($size,1)." KB";}
$return['size'] = $size;
$return['date'] = date("d.m.Y",filectime($url));
$return['time'] = date("H:i:s",filectime($url));
// Camera Information
if (isset($exif['ISOSpeedRatings'])){$return['iso']="ISO-".$exif['ISOSpeedRatings'];}
if (isset($exif['COMPUTED']['ApertureFNumber'])){$return['aperture']=$exif['COMPUTED']['ApertureFNumber'];}
if (isset($exif['Make'])){$return['make']=$exif['Make'];}
if (isset($exif['Model'])){$return['model']=$exif['Model'];}
if (isset($exif['ExposureTime'])){$return['shutter']=$exif['ExposureTime']." Sek.";}
if (isset($exif['FocalLength'])){$return['focal']=($exif['FocalLength']/1)." mm";}
if (isset($exif['Software'])){$return['software']=$exif['Software'];}
if (isset($exif['DateTimeOriginal'])) {
$exifDate = explode(" ",$exif['DateTimeOriginal']);
$date = explode(":", $exifDate[0]); $return['takeDate'] = $date[2].".".$date[1].".".$date[0];
$return['takeTime'] = $exifDate[1];
}
} else {
$exif = getimagesize($url);
$return['type'] = $exif['mime'];
$return['width'] = $exif[0];
$return['height'] = $exif[1];
$size = (filesize($url) / 1024);
if ($size >= 1024){$size=round($size/1024,1)." MB";} else {$size=round($size,1)." KB";}
$return['size'] = $size;
$return['date'] = date("d.m.Y",filectime($url));
$return['time'] = date("H:i:s",filectime($url));
}
return $return;
}
function createThumb($filename, $width = 200, $width2x = 400, $height = 200, $height2x = 400) {
global $database, $thumbQuality;
$photoUrl = "../uploads/big/$filename";
$newUrl = "../uploads/thumb/$filename";
$thumbPhotoName = explode(".", $filename);
$newUrl2x = "../uploads/thumb/".$thumbPhotoName[0]."@2x.".$thumbPhotoName[1];
$oldImg = getimagesize($photoUrl);
$type = $oldImg['mime'];
// Set position and size
$thumb = imagecreatetruecolor($width, $height);
$thumb2x = imagecreatetruecolor($width2x, $height2x);
if ($oldImg[0]<$oldImg[1]) {
$newSize = $oldImg[0];
$startWidth = 0;
$startHeight = $oldImg[1]/2 - $oldImg[0]/2;
} else {
$newSize = $oldImg[1];
$startWidth = $oldImg[0]/2 - $oldImg[1]/2;
$startHeight = 0;
}
// Create new image
switch($type) {
case "image/jpeg": $sourceImg = imagecreatefromjpeg($photoUrl); break;
case "image/png": $sourceImg = imagecreatefrompng($photoUrl); break;
case "image/gif": $sourceImg = imagecreatefromgif($photoUrl); break;
default: return false;
}
imagecopyresampled($thumb,$sourceImg,0,0,$startWidth,$startHeight,$width,$height,$newSize,$newSize);
imagecopyresampled($thumb2x,$sourceImg,0,0,$startWidth,$startHeight,$width2x,$height2x,$newSize,$newSize);
switch($type) {
case "image/jpeg": imagejpeg($thumb,$newUrl,$thumbQuality); imagejpeg($thumb2x,$newUrl2x,$thumbQuality); break;
case "image/png": imagepng($thumb,$newUrl); imagepng($thumb2x,$newUrl2x); break;
case "image/gif": imagegif($thumb,$newUrl); imagegif($thumb2x,$newUrl2x); break;
default: return false;
}
return true;
}
// Session Functions
function init($mode) {
global $checkForUpdates;
$return["config"]["checkForUpdates"] = $checkForUpdates;
if ($mode=="admin") $return["loggedIn"] = true;
else $return["loggedIn"] = false;
return $return;
}
function login($loginUser, $loginPassword) {
global $database, $user, $password;
if ($loginUser==$user&&$loginPassword==md5($password)) {
// Admin Login
$_SESSION['login'] = true;
return true;
} else {
return false;
}
}
function logout() {
session_destroy();
return true;
}
// Album Functions
function addAlbum($title) {
global $database;
$title = mysqli_real_escape_string($database, urldecode($title));
if (strlen($title)<1||strlen($title)>30) return false;
$sysdate = date("d.m.Y");
$query = "INSERT INTO lychee_albums (title, sysdate) VALUES ('$title', '$sysdate');";
$result = $database->query($query);
if (!$result) return false;
return $database->insert_id;
}
function getAlbums($public) {
global $database, $sorting;
// Smart Albums
if (!$public) $return = getSmartInfo();
// Albums
if ($public) $query = "SELECT * FROM lychee_albums WHERE public = 1 ORDER BY id $sorting;";
else $query = "SELECT * FROM lychee_albums ORDER BY id $sorting;";
$result = $database->query($query) OR die("Error: $result <br>".$database->error);
$i=0;
while($row = $result->fetch_object()) {
$return["content"][$row->id]['id'] = $row->id;
$return["content"][$row->id]['title'] = $row->title;
$return["content"][$row->id]['public'] = $row->public;
$return["content"][$row->id]['sysdate'] = $row->sysdate;
if ($row->password=="") $return["content"][$row->id]['password'] = false;
else $return["content"][$row->id]['password'] = true;
// Thumbs
if (($public&&$row->password=="")||(!$public)) {
$albumID = $row->id;
$query = "SELECT thumbUrl FROM lychee_photos WHERE album = '$albumID' ORDER BY id $sorting LIMIT 0, 3;";
$result2 = $database->query($query);
$k = 0;
while($row2 = $result2->fetch_object()){
$return["content"][$row->id]["thumb$k"] = $row2->thumbUrl;
$k++;
}
if (!isset($return["content"][$row->id]["thumb0"])) $return["content"][$row->id]["thumb0"]="";
if (!isset($return["content"][$row->id]["thumb1"])) $return["content"][$row->id]["thumb1"]="";
if (!isset($return["content"][$row->id]["thumb2"])) $return["content"][$row->id]["thumb2"]="";
}
$i++;
}
if ($i==0) $return["albums"] = false;
else $return["albums"] = true;
return $return;
}
function getAlbum($albumID) {
global $database, $sorting;
switch($albumID) {
case "f":
$return['public'] = false;
$query = "SELECT id, title, sysdate, public, star, album, thumbUrl FROM lychee_photos WHERE star = 1 ORDER BY id $sorting;";
break;
case "s":
$return['public'] = false;
$query = "SELECT id, title, sysdate, public, star, album, thumbUrl FROM lychee_photos WHERE public = 1 ORDER BY id $sorting;";
break;
case 0:
$return['public'] = false;
$query = "SELECT id, title, sysdate, public, star, album, thumbUrl FROM lychee_photos WHERE album = 0 ORDER BY id $sorting;";
default:
$result = $database->query("SELECT title, public, password FROM lychee_albums WHERE id = '$albumID';");
$row = $result->fetch_object();
$return['title'] = $row->title;
$return['public'] = $row->public;
if ($row->password=="") $return['password'] = false;
else $return['password'] = true;
$query = "SELECT id, title, sysdate, public, star, album, thumbUrl FROM lychee_photos WHERE album = '$albumID' ORDER BY id $sorting;";
break;
}
$result = $database->query($query);
$i = 0;
while($row = $result->fetch_array()) {
$return['content'][$row['id']] = $row;
$i++;
}
if ($i==0) $return['content'] = false;
$return['id'] = $albumID;
$return['num'] = $i;
return $return;
}
function getSmartInfo() {
global $database, $sorting;
// Unsorted
$query = "SELECT * FROM lychee_photos WHERE album = 0 ORDER BY id $sorting;";
$result = $database->query($query);
$i = 0;
while($row = $result->fetch_object()) {
if ($i<3) $return["unsortedThumb$i"] = $row->thumbUrl;
$i++;
}
$return['unsortedNum'] = $i;
// Public
$query2 = "SELECT * FROM lychee_photos WHERE public = 1 ORDER BY id $sorting;";
$result2 = $database->query($query2);
$i = 0;
while($row2 = $result2->fetch_object()) {
if ($i<3) $return["publicThumb$i"] = $row2->thumbUrl;
$i++;
}
$return['publicNum'] = $i;
// Starred
$query3 = "SELECT * FROM lychee_photos WHERE star = 1 ORDER BY id $sorting;";
$result3 = $database->query($query3);
$i = 0;
while($row3 = $result3->fetch_object()) {
if ($i<3) $return["starredThumb$i"] = $row3->thumbUrl;
$i++;
}
$return['starredNum'] = $i;
return $return;
}
function setAlbumTitle($albumID, $title) {
global $database;
$title = mysqli_real_escape_string($database, urldecode($title));
if (strlen($title)<1||strlen($title)>30) return false;
$query = "UPDATE lychee_albums SET title = '$title' WHERE id = '$albumID';";
$result = $database->query($query);
if (!$result) return false;
return true;
}
function deleteAlbum($albumID, $delAll) {
global $database;
if ($delAll=="true") {
$query = "SELECT id FROM lychee_photos WHERE album = '$albumID';";
$result = $database->query($query);
$error = false;
while($row = $result->fetch_object()) {
if (!deletePhoto($row->id)) $error = true;
}
} else {
$query = "UPDATE lychee_photos SET album = '0' WHERE album = '$albumID';";
$result = $database->query($query);
if (!$result) return false;
}
if ($albumID!=0) {
$query = "DELETE FROM lychee_albums WHERE id = '$albumID';";
$result = $database->query($query);
if (!$result) return false;
}
if ($error) return false;
return true;
}
function getAlbumArchive($albumID) {
global $database;
switch($albumID) {
case 's':
$query = "SELECT * FROM lychee_photos WHERE public = '1';";
$zipTitle = "Public";
break;
case 'f':
$query = "SELECT * FROM lychee_photos WHERE star = '1';";
$zipTitle = "Starred";
break;
default:
$query = "SELECT * FROM lychee_photos WHERE album = '$albumID';";
$zipTitle = "Unsorted";
}
$result = $database->query($query);
$files = array();
$i=0;
while($row = $result->fetch_object()) {
$files[$i] = "../uploads/big/".$row->url;
$i++;
}
$query = "SELECT * FROM lychee_albums WHERE id = '$albumID';";
$result = $database->query($query);
$row = $result->fetch_object();
if ($albumID!=0&&is_numeric($albumID))$zipTitle = $row->title;
$filename = "../uploads/".$zipTitle.".zip";
$zip = new ZipArchive();
if ($zip->open($filename, ZIPARCHIVE::CREATE)!==TRUE) {
return false;
}
foreach($files AS $zipFile) {
$newFile = explode("/",$zipFile);
$newFile = array_reverse($newFile);
$zip->addFile($zipFile, $zipTitle."/".$newFile[0]);
}
$zip->close();
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename=\"$zipTitle.zip\"");
header("Content-Length: ".filesize($filename));
readfile($filename);
unlink($filename);
return true;
}
function setAlbumPublic($albumID, $password) {
global $database;
$query = "SELECT public FROM lychee_albums WHERE id = '$albumID';";
$result = $database->query($query);
$row = $result->fetch_object();
if ($row->public == 0){
$public = 1;
} else {
$public = 0;
}
$query = "UPDATE lychee_albums SET public = '$public', password = NULL WHERE id = '$albumID';";
$result = $database->query($query);
if (!$result) return false;
if ($public==1) {
$query = "UPDATE lychee_photos SET public = 0 WHERE album = '$albumID';";
$result = $database->query($query);
if (!$result) return false;
}
if (strlen($password)>0) return setAlbumPassword($albumID, $password);
else return true;
}
function setAlbumPassword($albumID, $password) {
global $database;
$query = "UPDATE lychee_albums SET password = '$password' WHERE id = '$albumID';";
$result = $database->query($query);
if (!$result) return false;
return true;
}
function checkAlbumPassword($albumID, $password) {
global $database;
$query = "SELECT public, password FROM lychee_albums WHERE id = '$albumID';";
$result = $database->query($query);
$row = $result->fetch_object();
if ($row->password=="") return true;
else if ($row->password==$password) return true;
else return false;
}
function isAlbumPublic($albumID) {
global $database;
$query = "SELECT public, password FROM lychee_albums WHERE id = '$albumID';";
$result = $database->query($query);
$row = $result->fetch_object();
if ($row->public==1) return true;
else return false;
}
// Photo Functions
function getPhoto($photoID, $albumID) {
global $database;
if (!is_numeric($photoID)) {
$query = "SELECT COUNT(*) AS quantity FROM lychee_photos WHERE import_name = '../uploads/import/$photoID';";
$result = $database->query($query);
$row = $result->fetch_object();
if ($row->quantity == 0) {
importPhoto($photoID, 's');
}
if (is_file("../uploads/import/$photoID")) {
importPhoto($photoID, 's');
}
$query = "SELECT * FROM lychee_photos WHERE import_name = '../uploads/import/$photoID' ORDER BY ID DESC;";
} else {
$query = "SELECT * FROM lychee_photos WHERE id = '$photoID';";
}
$result = $database->query($query);
$return = $result->fetch_array();
if ($albumID!='false') {
if ($return['album']!=0) {
$result = $database->query("SELECT public FROM lychee_albums WHERE id = " . $return['album'] . ";");
$return_album = $result->fetch_array();
if ($return_album['public']=="1") $return['public'] = "2";
}
$return['original_album'] = $return['album'];
$return['album'] = $albumID;
$nextPhoto = getNextPhotoID($photoID, $albumID, false);
if ($nextPhoto==$photoID) $return['nextPhoto'] = false;
else $return['nextPhoto'] = $nextPhoto;
$previousPhoto = getPreviousPhotoID($photoID, $albumID, false);
if ($previousPhoto==$photoID) $return['previousPhoto'] = false;
else $return['previousPhoto'] = $previousPhoto;
}
unset($return['album_public']);
return $return;
}
function setPhotoPublic($photoID, $url) {
global $database;
$query = "SELECT public FROM lychee_photos WHERE id = '$photoID';";
$result = $database->query($query);
$row = $result->fetch_object();
if ($row->public == 0){
$public = 1;
} else {
$public = 0;
}
$query = "UPDATE lychee_photos SET public = '$public' WHERE id = '$photoID';";
$result = $database->query($query);
if (!$result) return false;
return true;
}
function setPhotoStar($photoID) {
global $database;
$query = "SELECT star FROM lychee_photos WHERE id = '$photoID';";
$result = $database->query($query);
$row = $result->fetch_object();
if ($row->star == 0) {
$star = 1;
} else {
$star = 0;
}
$query = "UPDATE lychee_photos SET star = '$star' WHERE id = '$photoID';";
$result = $database->query($query);
return true;
}
function getNextPhotoID($photoID, $albumID, $innerCall) {
global $database, $sorting;
if (!$innerCall&&$sorting=="ASC") return getPreviousPhotoID($photoID, $albumID, true);
switch($albumID) {
case 'f': $query = "SELECT id FROM lychee_photos WHERE id < '$photoID' AND star = '1' ORDER BY id DESC LIMIT 0, 1;";
break;
case 's': $query = "SELECT id FROM lychee_photos WHERE id < '$photoID' AND public = '1' ORDER BY id DESC LIMIT 0, 1;";
break;
default: $query = "SELECT id FROM lychee_photos WHERE id < '$photoID' AND album = '$albumID' ORDER BY id DESC LIMIT 0, 1;";
}
$result = $database->query($query);
$return = $result->fetch_array();
if (!$return || ($return==0)) {
switch($albumID) {
case 'f': $query = "SELECT id FROM lychee_photos WHERE star = '1' ORDER BY id DESC LIMIT 0, 1;";
break;
case 's': $query = "SELECT id FROM lychee_photos WHERE public = '1' ORDER BY id DESC LIMIT 0, 1;";
break;
default: $query = "SELECT id FROM lychee_photos WHERE album = '$albumID' ORDER BY id DESC LIMIT 0, 1;";
}
$result = $database->query($query);
$return = $result->fetch_array();
}
return $return['id'];
}
function getPreviousPhotoID($photoID, $albumID, $innerCall) {
global $database, $sorting;
if (!$innerCall&&$sorting=="ASC") return getNextPhotoID($photoID, $albumID, true);
switch($albumID) {
case 'f': $query = "SELECT id FROM lychee_photos WHERE id > '$photoID' AND star = '1' ORDER BY id LIMIT 0, 1;";
break;
case 's': $query = "SELECT id FROM lychee_photos WHERE id > '$photoID' AND public = '1' ORDER BY id LIMIT 0, 1;";
break;
default: $query = "SELECT id FROM lychee_photos WHERE id > '$photoID' AND album = '$albumID' ORDER BY id LIMIT 0, 1;";
}
$result = $database->query($query);
$return = $result->fetch_array();
if (!$return || ($return==0)) {
switch($albumID) {
case 'f': $query = "SELECT id FROM lychee_photos WHERE star = '1' ORDER BY id LIMIT 0, 1;";
break;
case 's': $query = "SELECT id FROM lychee_photos WHERE public = '1' ORDER BY id LIMIT 0, 1;";
break;
default: $query = "SELECT id FROM lychee_photos WHERE album = '$albumID' ORDER BY id LIMIT 0, 1;";
}
$result = $database->query($query);
$return = $result->fetch_array();
}
return $return['id'];
}
function setAlbum($photoID, $newAlbum) {
global $database;
$query = "UPDATE lychee_photos SET album = '$newAlbum' WHERE id = '$photoID';";
$result = $database->query($query);
if (!$result) return false;
else return true;
}
function setPhotoTitle($photoID, $title) {
global $database;
$title = mysqli_real_escape_string($database, urldecode($title));
if (strlen($title)>30) return false;
$query = "UPDATE lychee_photos SET title = '$title' WHERE id = '$photoID';";
$result = $database->query($query);
if (!$result) return false;
else return true;
}
function setPhotoDescription($photoID, $description) {
global $database;
$description = mysqli_real_escape_string($database, htmlentities($description));
if (strlen($description)>160) return false;
$query = "UPDATE lychee_photos SET description = '$description' WHERE id = '$photoID';";
$result = $database->query($query);
if (!$result) return false;
return true;
}
function deletePhoto($photoID) {
global $database;
$query = "SELECT * FROM lychee_photos WHERE id = '$photoID';";
$result = $database->query($query);
if (!$result) return false;
$row = $result->fetch_object();
$retinaUrl = explode(".", $row->thumbUrl);
$unlink1 = unlink("../uploads/big/".$row->url);
$unlink2 = unlink("../uploads/thumb/".$row->thumbUrl);
$unlink3 = unlink("../uploads/thumb/".$retinaUrl[0].'@2x.'.$retinaUrl[1]);
$query = "DELETE FROM lychee_photos WHERE id = '$photoID';";
$result = $database->query($query);
if (!$unlink1 || !$unlink2 || !$unlink3) return false;
if (!$result) return false;
return true;
}
function importPhoto($name, $albumID) {
$tmp_name = "../uploads/import/$name";
$details = getimagesize($tmp_name);
$size = filesize($tmp_name);
$nameFile = array(array());
$nameFile[0]['name'] = $name;
$nameFile[0]['type'] = $details['mime'];
$nameFile[0]['tmp_name'] = $tmp_name;
$nameFile[0]['error'] = 0;
$nameFile[0]['size'] = $size;
if (!upload($nameFile, $albumID)) return false;
else return true;
}
function importUrl($url, $albumID) {
if (@getimagesize($url)) {
$pathinfo = pathinfo($url);
$filename = $pathinfo['filename'].".".$pathinfo['extension'];
$tmp_name = "../uploads/import/$filename";
copy($url, $tmp_name);
return importPhoto($filename, $albumID);
} else {
return false;
}
}
// Share Functions
function facebookHeader($photoID) {
$database = dbConnect();
if (!is_numeric($photoID)) return false;
$query = "SELECT * FROM lychee_photos WHERE id = '$photoID';";
$result = $database->query($query);
$row = $result->fetch_object();
$parseUrl = parse_url("http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
$thumb = $parseUrl['scheme']."://".$parseUrl['host'].$parseUrl['path']."/../uploads/big/".$row->thumbUrl;
$return .= '<!-- General Meta Data -->';
$return = '<meta name="title" content="'.$row->title.'" />';
$return .= '<meta name="description" content="'.$row->description.' - via Lychee" />';
$return .= '<link rel="image_src" type="image/jpeg" href="'.$thumb.'" />';
$return .= '<!-- Twitter Meta Data -->';
$return .= '<meta name="twitter:card" content="photo">';
$return .= '<meta name="twitter:title" content="'.$row->title.'">';
$return .= '<meta name="twitter:image:src" content="'.$thumb.'">';
$return .= '<!-- Facebook Meta Data -->';
$return .= '<meta property="og:title" content="'.$row->title.'">';
$return .= '<meta property="og:image" content="'.$thumb.'">';
return $return;
}
function isPhotoPublic($photoID, $password) {
global $database;
$photoID = mysqli_real_escape_string($database, $photoID);
if (is_numeric($photoID)) {
$query = "SELECT * FROM lychee_photos WHERE id = '$photoID';";
} else {
$query = "SELECT * FROM lychee_photos WHERE import_name = '../uploads/import/$photoID';";
}
$result = $database->query($query);
$row = $result->fetch_object();
if (!is_numeric($photoID)&&!$row) return true;
if ($row->public==1) return true;
else {
$cAP = checkAlbumPassword($row->album, $password);
$iAP = isAlbumPublic($row->album);
if ($iAP&&$cAP) return true;
else return false;
}
}
// Search Function
function search($term) {
global $database, $sorting;
$return["albums"] = "";
$term = mysqli_real_escape_string($database, $term);
$query = "SELECT * FROM lychee_photos WHERE title like '%$term%' OR description like '%$term%';";
$result = $database->query($query);
while($row = $result->fetch_array()) {
$return['photos'][$row['id']] = $row;
}
$query = "SELECT * FROM lychee_albums WHERE title like '%$term%';";
$result = $database->query($query);
$i=0;
while($row = $result->fetch_object()) {
$return["albums"][$row->id]['id'] = $row->id;
$return["albums"][$row->id]['title'] = $row->title;
$return["albums"][$row->id]['public'] = $row->public;
$return["albums"][$row->id]['sysdate'] = $row->sysdate;
if ($row->password=="") $return["albums"][$row->id]['password'] = false;
else $return["albums"][$row->id]['password'] = true;
$query2 = "SELECT thumbUrl FROM lychee_photos WHERE album = '".$row->id."' ORDER BY id $sorting LIMIT 0, 3;";
$result2 = $database->query($query2);
$k = 0;
while($row2 = $result2->fetch_object()){
$return['albums'][$row->id]["thumb$k"] = $row2->thumbUrl;
$k++;
}
$i++;
}
return $return;
}
?>

@ -0,0 +1,354 @@
<?php
/**
* @name Album Module
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2014 by Philipp Maurer, Tobias Reich
*/
if (!defined('LYCHEE')) exit('Error: Direct access is not allowed!');
function addAlbum($title) {
global $database;
if (strlen($title)<1||strlen($title)>30) return false;
$sysdate = date("d.m.Y");
$result = $database->query("INSERT INTO lychee_albums (title, sysdate) VALUES ('$title', '$sysdate');");
if (!$result) return false;
return $database->insert_id;
}
function getAlbums($public) {
global $database, $settings;
// Smart Albums
if (!$public) $return = getSmartInfo();
// Albums
if ($public) $query = "SELECT * FROM lychee_albums WHERE public = 1";
else $query = "SELECT * FROM lychee_albums";
$result = $database->query($query) OR exit("Error: $result <br>".$database->error);
$i = 0;
while($row = $result->fetch_object()) {
// Info
$return["content"][$row->id]['id'] = $row->id;
$return["content"][$row->id]['title'] = $row->title;
$return["content"][$row->id]['public'] = $row->public;
$return["content"][$row->id]['sysdate'] = date('F Y', strtotime($row->sysdate));
if ($row->password=="") $return["content"][$row->id]['password'] = false;
else $return["content"][$row->id]['password'] = true;
// Thumbs
if (($public&&$row->password=="")||(!$public)) {
$albumID = $row->id;
$result2 = $database->query("SELECT thumbUrl FROM lychee_photos WHERE album = '$albumID' ORDER BY star DESC, " . substr($settings['sorting'], 9) . " LIMIT 0, 3");
$k = 0;
while($row2 = $result2->fetch_object()){
$return["content"][$row->id]["thumb$k"] = $row2->thumbUrl;
$k++;
}
if (!isset($return["content"][$row->id]["thumb0"])) $return["content"][$row->id]["thumb0"] = "";
if (!isset($return["content"][$row->id]["thumb1"])) $return["content"][$row->id]["thumb1"] = "";
if (!isset($return["content"][$row->id]["thumb2"])) $return["content"][$row->id]["thumb2"] = "";
}
// Album count
$i++;
}
$return["num"] = $i;
if ($i==0) $return["albums"] = false;
else $return["albums"] = true;
return $return;
}
function getSmartInfo() {
global $database, $settings;
// Unsorted
$result = $database->query("SELECT thumbUrl FROM lychee_photos WHERE album = 0 " . $settings['sorting']);
$i = 0;
while($row = $result->fetch_object()) {
if ($i<3) $return["unsortedThumb$i"] = $row->thumbUrl;
$i++;
}
$return['unsortedNum'] = $i;
// Public
$result2 = $database->query("SELECT thumbUrl FROM lychee_photos WHERE public = 1 " . $settings['sorting']);
$i = 0;
while($row2 = $result2->fetch_object()) {
if ($i<3) $return["publicThumb$i"] = $row2->thumbUrl;
$i++;
}
$return['publicNum'] = $i;
// Starred
$result3 = $database->query("SELECT thumbUrl FROM lychee_photos WHERE star = 1 " . $settings['sorting']);
$i = 0;
while($row3 = $result3->fetch_object()) {
if ($i<3) $return["starredThumb$i"] = $row3->thumbUrl;
$i++;
}
$return['starredNum'] = $i;
return $return;
}
function getAlbum($albumID) {
global $database, $settings;
// Get album information
switch($albumID) {
case "f": $return['public'] = false;
$query = "SELECT id, title, sysdate, public, star, album, thumbUrl FROM lychee_photos WHERE star = 1 " . $settings['sorting'];
break;
case "s": $return['public'] = false;
$query = "SELECT id, title, sysdate, public, star, album, thumbUrl FROM lychee_photos WHERE public = 1 " . $settings['sorting'];
break;
case 0: $return['public'] = false;
$query = "SELECT id, title, sysdate, public, star, album, thumbUrl FROM lychee_photos WHERE album = 0 " . $settings['sorting'];
default: $result = $database->query("SELECT * FROM lychee_albums WHERE id = '$albumID';");
$row = $result->fetch_object();
$return['title'] = $row->title;
$return['description'] = $row->description;
$return['sysdate'] = date('d M. Y', strtotime($row->sysdate));
$return['public'] = $row->public;
if ($row->password=="") $return['password'] = false;
else $return['password'] = true;
$query = "SELECT id, title, sysdate, public, star, album, thumbUrl FROM lychee_photos WHERE album = '$albumID' " . $settings['sorting'];
break;
}
// Get photos
$result = $database->query($query);
$previousPhotoID = "";
$i = 0;
while($row = $result->fetch_array()) {
$return['content'][$row['id']]['id'] = $row['id'];
$return['content'][$row['id']]['title'] = $row['title'];
$return['content'][$row['id']]['sysdate'] = date('d F Y', strtotime($row['sysdate']));
$return['content'][$row['id']]['public'] = $row['public'];
$return['content'][$row['id']]['star'] = $row['star'];
$return['content'][$row['id']]['album'] = $row['album'];
$return['content'][$row['id']]['thumbUrl'] = $row['thumbUrl'];
$return['content'][$row['id']]['previousPhoto'] = $previousPhotoID;
$return['content'][$row['id']]['nextPhoto'] = "";
if ($previousPhotoID!="") $return['content'][$previousPhotoID]['nextPhoto'] = $row['id'];
$previousPhotoID = $row['id'];
$i++;
}
if ($i==0) {
// Empty album
$return['content'] = false;
} else {
// Enable next and previous for the first and last photo
$lastElement = end($return['content']);
$lastElementId = $lastElement['id'];
$firstElement = reset($return['content']);
$firstElementId = $firstElement['id'];
if ($lastElementId!==$firstElementId) {
$return['content'][$lastElementId]['nextPhoto'] = $firstElementId;
$return['content'][$firstElementId]['previousPhoto'] = $lastElementId;
}
}
$return['id'] = $albumID;
$return['num'] = $i;
return $return;
}
function setAlbumTitle($albumID, $title) {
global $database;
if (strlen($title)<1||strlen($title)>30) return false;
$result = $database->query("UPDATE lychee_albums SET title = '$title' WHERE id = '$albumID';");
if (!$result) return false;
return true;
}
function setAlbumDescription($albumID, $description) {
global $database;
$description = htmlentities($description);
if (strlen($description)>800) return false;
$result = $database->query("UPDATE lychee_albums SET description = '$description' WHERE id = '$albumID';");
if (!$result) return false;
return true;
}
function deleteAlbum($albumID, $delAll) {
global $database;
if ($delAll=="true") {
$result = $database->query("SELECT id FROM lychee_photos WHERE album = '$albumID';");
$error = false;
while($row = $result->fetch_object()) {
if (!deletePhoto($row->id)) $error = true;
}
} else {
$result = $database->query("UPDATE lychee_photos SET album = '0' WHERE album = '$albumID';");
if (!$result) return false;
}
if ($albumID!=0) {
$result = $database->query("DELETE FROM lychee_albums WHERE id = '$albumID';");
if (!$result) return false;
}
if ($error) return false;
return true;
}
function getAlbumArchive($albumID) {
global $database;
switch($albumID) {
case 's':
$query = "SELECT * FROM lychee_photos WHERE public = '1';";
$zipTitle = "Public";
break;
case 'f':
$query = "SELECT * FROM lychee_photos WHERE star = '1';";
$zipTitle = "Starred";
break;
default:
$query = "SELECT * FROM lychee_photos WHERE album = '$albumID';";
$zipTitle = "Unsorted";
}
$result = $database->query($query);
$files = array();
$i=0;
while($row = $result->fetch_object()) {
$files[$i] = "../uploads/big/".$row->url;
$i++;
}
$result = $database->query("SELECT * FROM lychee_albums WHERE id = '$albumID';");
$row = $result->fetch_object();
if ($albumID!=0&&is_numeric($albumID))$zipTitle = $row->title;
$filename = "../uploads/".$zipTitle.".zip";
$zip = new ZipArchive();
if ($zip->open($filename, ZIPARCHIVE::CREATE)!==TRUE) {
return false;
}
foreach($files AS $zipFile) {
$newFile = explode("/",$zipFile);
$newFile = array_reverse($newFile);
$zip->addFile($zipFile, $zipTitle."/".$newFile[0]);
}
$zip->close();
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename=\"$zipTitle.zip\"");
header("Content-Length: ".filesize($filename));
readfile($filename);
unlink($filename);
return true;
}
function setAlbumPublic($albumID, $password) {
global $database;
$result = $database->query("SELECT public FROM lychee_albums WHERE id = '$albumID';");
$row = $result->fetch_object();
if ($row->public == 0){
$public = 1;
} else {
$public = 0;
}
$result = $database->query("UPDATE lychee_albums SET public = '$public', password = NULL WHERE id = '$albumID';");
if (!$result) return false;
if ($public==1) {
$result = $database->query("UPDATE lychee_photos SET public = 0 WHERE album = '$albumID';");
if (!$result) return false;
}
if (strlen($password)>0) return setAlbumPassword($albumID, $password);
else return true;
}
function setAlbumPassword($albumID, $password) {
global $database;
$result = $database->query("UPDATE lychee_albums SET password = '$password' WHERE id = '$albumID';");
if (!$result) return false;
return true;
}
function checkAlbumPassword($albumID, $password) {
global $database;
$result = $database->query("SELECT password FROM lychee_albums WHERE id = '$albumID';");
$row = $result->fetch_object();
if ($row->password=="") return true;
else if ($row->password==$password) return true;
else return false;
}
function isAlbumPublic($albumID) {
global $database;
$result = $database->query("SELECT public FROM lychee_albums WHERE id = '$albumID';");
$row = $result->fetch_object();
if ($row->public==1) return true;
else return false;
}
?>

@ -0,0 +1,183 @@
<?php
/**
* @name DB Module
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2014 by Philipp Maurer, Tobias Reich
*/
if (!defined('LYCHEE')) exit('Error: Direct access is not allowed!');
function dbConnect() {
global $dbUser, $dbPassword, $dbHost, $dbName;
$database = new mysqli($dbHost, $dbUser, $dbPassword);
if (mysqli_connect_errno()) {
echo mysqli_connect_errno().': '.mysqli_connect_error();
return false;
}
if (!$database->select_db($dbName))
if (!createDatabase($dbName, $database)) exit('Error: Could not create database!');
if (!$database->query("SELECT * FROM lychee_photos, lychee_albums, lychee_settings;"))
if (!createTables($database)) exit('Error: Could not create tables!');
return $database;
}
function dbClose() {
global $database;
if (!$database->close()) exit("Error: Closing the connection failed!");
return true;
}
function createConfig($dbHost = 'localhost', $dbUser, $dbPassword, $dbName = 'lychee') {
$dbPassword = urldecode($dbPassword);
$database = new mysqli($dbHost, $dbUser, $dbPassword);
if (mysqli_connect_errno()||$dbUser=="") return "Warning: Connection failed!";
else {
$config = "<?php
/**
* @name Config
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2014 by Philipp Maurer, Tobias Reich
*/
if(!defined('LYCHEE')) exit('Error: Direct access is not allowed!');
// Database configurations
\$dbHost = '$dbHost'; //Host of the Database
\$dbUser = '$dbUser'; //Username of the database
\$dbPassword = '$dbPassword'; //Password of the Database
\$dbName = '$dbName'; //Database name
?>";
if (file_put_contents("config.php", $config)===false) return "Warning: Could not create file!";
else {
$_SESSION['login'] = true;
return true;
}
}
}
function createDatabase($dbName, $database) {
$result = $database->query("CREATE DATABASE IF NOT EXISTS $dbName;");
$database->select_db($dbName);
if (!$database->select_db($dbName)||!$result) return false;
return true;
}
function createTables($database) {
if (!$database->query("SELECT * FROM lychee_settings;")) {
$query = "
CREATE TABLE `lychee_settings` (
`key` varchar(50) NOT NULL DEFAULT '',
`value` varchar(50) DEFAULT ''
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
";
if (!$database->query($query)) return false;
$query = "
INSERT INTO `lychee_settings` (`key`, `value`)
VALUES
('username',''),
('password',''),
('thumbQuality','90'),
('checkForUpdates','1'),
('sorting','ORDER BY id DESC');
";
if (!$database->query($query)) return false;
}
if (!$database->query("SELECT * FROM lychee_albums;")) {
$query = "
CREATE TABLE `lychee_albums` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(50) NOT NULL,
`description` varchar(1000) DEFAULT '',
`sysdate` varchar(10) NOT NULL,
`public` tinyint(1) DEFAULT '0',
`password` varchar(100) DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
";
if (!$database->query($query)) return false;
}
if (!$database->query("SELECT * FROM lychee_photos;")) {
$query = "
CREATE TABLE `lychee_photos` (
`id` bigint(14) NOT NULL,
`title` varchar(50) NOT NULL,
`description` varchar(1000) NOT NULL DEFAULT '',
`url` varchar(100) NOT NULL,
`public` tinyint(1) NOT NULL,
`type` varchar(10) NOT NULL,
`width` int(11) NOT NULL,
`height` int(11) NOT NULL,
`size` varchar(20) NOT NULL,
`sysdate` varchar(10) NOT NULL,
`systime` varchar(8) NOT NULL,
`iso` varchar(15) NOT NULL,
`aperture` varchar(20) NOT NULL,
`make` varchar(20) NOT NULL,
`model` varchar(50) NOT NULL,
`shutter` varchar(30) NOT NULL,
`focal` varchar(20) NOT NULL,
`takedate` varchar(20) NOT NULL,
`taketime` varchar(8) NOT NULL,
`star` tinyint(1) NOT NULL,
`thumbUrl` varchar(50) NOT NULL,
`album` varchar(30) NOT NULL DEFAULT '0',
`import_name` varchar(100) DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
";
if (!$database->query($query)) return false;
}
return true;
}
?>

@ -0,0 +1,95 @@
<?php
/**
* @name Misc Module
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2014 by Philipp Maurer, Tobias Reich
*/
if (!defined('LYCHEE')) exit('Error: Direct access is not allowed!');
function openGraphHeader($photoID) {
global $database;
if (!is_numeric($photoID)) return false;
$result = $database->query("SELECT * FROM lychee_photos WHERE id = '$photoID';");
$row = $result->fetch_object();
$parseUrl = parse_url("http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
$picture = $parseUrl['scheme']."://".$parseUrl['host'].$parseUrl['path']."/../uploads/big/".$row->url;
$return = '<!-- General Meta Data -->';
$return .= '<meta name="title" content="'.$row->title.'" />';
$return .= '<meta name="description" content="'.$row->description.' - via Lychee" />';
$return .= '<link rel="image_src" type="image/jpeg" href="'.$picture.'" />';
$return .= '<!-- Twitter Meta Data -->';
$return .= '<meta name="twitter:card" content="photo">';
$return .= '<meta name="twitter:title" content="'.$row->title.'">';
$return .= '<meta name="twitter:image:src" content="'.$picture.'">';
$return .= '<!-- Facebook Meta Data -->';
$return .= '<meta property="og:title" content="'.$row->title.'">';
$return .= '<meta property="og:image" content="'.$picture.'">';
return $return;
}
function search($term) {
global $database, $settings;
$return["albums"] = "";
$result = $database->query("SELECT * FROM lychee_photos WHERE title like '%$term%' OR description like '%$term%';");
while($row = $result->fetch_array()) {
$return['photos'][$row['id']] = $row;
$return['photos'][$row['id']]['sysdate'] = date('d F Y', strtotime($row['sysdate']));
}
$result = $database->query("SELECT * FROM lychee_albums WHERE title like '%$term%';");
$i=0;
while($row = $result->fetch_object()) {
$return["albums"][$row->id]['id'] = $row->id;
$return["albums"][$row->id]['title'] = $row->title;
$return["albums"][$row->id]['public'] = $row->public;
$return["albums"][$row->id]['sysdate'] = date('F Y', strtotime($row->sysdate));
if ($row->password=="") $return["albums"][$row->id]['password'] = false;
else $return["albums"][$row->id]['password'] = true;
$result2 = $database->query("SELECT thumbUrl FROM lychee_photos WHERE album = '" . $row->id . "' " . $settings['sorting'] . " LIMIT 0, 3;");
$k = 0;
while($row2 = $result2->fetch_object()){
$return['albums'][$row->id]["thumb$k"] = $row2->thumbUrl;
$k++;
}
$i++;
}
return $return;
}
function update() {
global $database;
if(!$database->query("SELECT `public` FROM `lychee_albums`;")) $database->query("ALTER TABLE `lychee_albums` ADD `public` TINYINT( 1 ) NOT NULL DEFAULT '0'");
if(!$database->query("SELECT `password` FROM `lychee_albums`;")) $database->query("ALTER TABLE `lychee_albums` ADD `password` VARCHAR( 100 ) NULL DEFAULT ''");
if(!$database->query("SELECT `description` FROM `lychee_albums`;")) $database->query("ALTER TABLE `lychee_albums` ADD `description` VARCHAR( 1000 ) NULL DEFAULT ''");
if($database->query("SELECT `password` FROM `lychee_albums`;")) $database->query("ALTER TABLE `lychee_albums` CHANGE `password` `password` VARCHAR( 100 ) NULL DEFAULT ''");
if($database->query("SELECT `description` FROM `lychee_photos`;")) $database->query("ALTER TABLE `lychee_photos` CHANGE `description` `description` VARCHAR( 1000 ) NULL DEFAULT ''");
if($database->query("SELECT `shortlink` FROM `lychee_photos`;")) $database->query("ALTER TABLE `lychee_photos` DROP `shortlink`");
$database->query("UPDATE `lychee_photos` SET url = replace(url, 'uploads/big/', ''), thumbUrl = replace(thumbUrl, 'uploads/thumb/', '')");
return true;
}
?>

@ -0,0 +1,167 @@
<?php
/**
* @name Photo Module
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2014 by Philipp Maurer, Tobias Reich
*/
if (!defined('LYCHEE')) exit('Error: Direct access is not allowed!');
function getPhoto($photoID, $albumID) {
global $database;
if (!is_numeric($photoID)) {
$result = $database->query("SELECT COUNT(*) AS quantity FROM lychee_photos WHERE import_name = '../uploads/import/$photoID';");
$row = $result->fetch_object();
if ($row->quantity == 0) {
importPhoto($photoID, 's');
}
if (is_file("../uploads/import/$photoID")) {
importPhoto($photoID, 's');
}
$query = "SELECT * FROM lychee_photos WHERE import_name = '../uploads/import/$photoID' ORDER BY ID DESC;";
} else {
$query = "SELECT * FROM lychee_photos WHERE id = '$photoID';";
}
$result = $database->query($query);
$return = $result->fetch_array();
if ($albumID!='false') {
if ($return['album']!=0) {
$result = $database->query("SELECT public FROM lychee_albums WHERE id = " . $return['album'] . ";");
$return_album = $result->fetch_array();
if ($return_album['public']=="1") $return['public'] = "2";
}
$return['original_album'] = $return['album'];
$return['album'] = $albumID;
$return['sysdate'] = date('d M. Y', strtotime($return['sysdate']));
if (strlen($return['takedate'])>0) $return['takedate'] = date('d M. Y', strtotime($return['takedate']));
}
unset($return['album_public']);
return $return;
}
function setPhotoPublic($photoID, $url) {
global $database;
$result = $database->query("SELECT public FROM lychee_photos WHERE id = '$photoID';");
$row = $result->fetch_object();
if ($row->public == 0){
$public = 1;
} else {
$public = 0;
}
$result = $database->query("UPDATE lychee_photos SET public = '$public' WHERE id = '$photoID';");
if (!$result) return false;
return true;
}
function setPhotoStar($photoID) {
global $database;
$result = $database->query("SELECT star FROM lychee_photos WHERE id = '$photoID';");
$row = $result->fetch_object();
if ($row->star == 0) {
$star = 1;
} else {
$star = 0;
}
$result = $database->query("UPDATE lychee_photos SET star = '$star' WHERE id = '$photoID';");
return true;
}
function setAlbum($photoID, $newAlbum) {
global $database;
$result = $database->query("UPDATE lychee_photos SET album = '$newAlbum' WHERE id = '$photoID';");
if (!$result) return false;
else return true;
}
function setPhotoTitle($photoID, $title) {
global $database;
if (strlen($title)>30) return false;
$result = $database->query("UPDATE lychee_photos SET title = '$title' WHERE id = '$photoID';");
if (!$result) return false;
else return true;
}
function setPhotoDescription($photoID, $description) {
global $database;
$description = htmlentities($description);
if (strlen($description)>800) return false;
$result = $database->query("UPDATE lychee_photos SET description = '$description' WHERE id = '$photoID';");
if (!$result) return false;
return true;
}
function deletePhoto($photoID) {
global $database;
$result = $database->query("SELECT * FROM lychee_photos WHERE id = '$photoID';");
if (!$result) return false;
$row = $result->fetch_object();
$retinaUrl = explode(".", $row->thumbUrl);
$unlink1 = unlink("../uploads/big/".$row->url);
$unlink2 = unlink("../uploads/thumb/".$row->thumbUrl);
$unlink3 = unlink("../uploads/thumb/".$retinaUrl[0].'@2x.'.$retinaUrl[1]);
$result = $database->query("DELETE FROM lychee_photos WHERE id = '$photoID';");
if (!$unlink1 || !$unlink2 || !$unlink3) return false;
if (!$result) return false;
return true;
}
function isPhotoPublic($photoID, $password) {
global $database;
if (is_numeric($photoID)) {
$query = "SELECT * FROM lychee_photos WHERE id = '$photoID';";
} else {
$query = "SELECT * FROM lychee_photos WHERE import_name = '../uploads/import/$photoID';";
}
$result = $database->query($query);
$row = $result->fetch_object();
if (!is_numeric($photoID)&&!$row) return true;
if ($row->public==1) return true;
else {
$cAP = checkAlbumPassword($row->album, $password);
$iAP = isAlbumPublic($row->album);
if ($iAP&&$cAP) return true;
else return false;
}
}
?>

@ -0,0 +1,64 @@
<?php
/**
* @name Session Module
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2014 by Philipp Maurer, Tobias Reich
*/
if (!defined('LYCHEE')) exit('Error: Direct access is not allowed!');
function init($mode) {
global $settings;
$return['config'] = $settings;
unset($return['config']['password']);
// No login
if ($settings['username']===''&&$settings['password']==='') $return['config']['login'] = false;
else $return['config']['login'] = true;
if ($mode==='admin') {
$return['loggedIn'] = true;
} else {
unset($return['config']['username']);
unset($return['config']['thumbQuality']);
unset($return['config']['sorting']);
unset($return['config']['login']);
$return['loggedIn'] = false;
}
return $return;
}
function login($username, $password) {
global $database, $settings;
// Check login
if ($username===$settings['username']&&$password===$settings['password']) {
$_SESSION['login'] = true;
return true;
}
// No login
if ($settings['username']===''&&$settings['password']==='') {
$_SESSION['login'] = true;
return true;
}
return false;
}
function logout() {
session_destroy();
return true;
}
?>

@ -0,0 +1,137 @@
<?php
/**
* @name Settings Module
* @author Tobias Reich
* @copyright 2014 by Tobias Reich
*/
if (!defined('LYCHEE')) exit('Error: Direct access is not allowed!');
function getSettings() {
global $database;
$result = $database->query('SELECT * FROM lychee_settings;');
while($row = $result->fetch_object()) {
$return[$row->key] = $row->value;
}
return $return;
}
function setLogin($oldPassword = '', $username, $password) {
global $settings;
if ($oldPassword==$settings['password']) {
if (!setUsername($username)) exit('Error: Updating username failed!');
if (!setPassword($password)) exit('Error: Updating password failed!');
return true;
}
exit('Error: Current password entered incorrectly!');
}
function setUsername($username) {
global $database;
$username = htmlentities($username);
if (strlen($username)>50) return false;
$result = $database->query("UPDATE lychee_settings SET value = '$username' WHERE `key` = 'username';");
if (!$result) return false;
return true;
}
function setPassword($password) {
global $database;
if (strlen($password)<1||strlen($password)>50) return false;
$result = $database->query("UPDATE lychee_settings SET value = '$password' WHERE `key` = 'password';");
if (!$result) return false;
return true;
}
/*function setCheckForUpdates() {
global $database;
$result = $database->query("SELECT value FROM lychee_settings WHERE `key` = 'checkForUpdates';");
$row = $result->fetch_object();
if ($row->value==0) $checkForUpdates = 1;
else $checkForUpdates = 0;
$result = $database->query("UPDATE lychee_settings SET value = '$checkForUpdates' WHERE `key` = 'checkForUpdates';");
if (!$result) return false;
return true;
}*/
function setSorting($type, $order) {
global $database;
$sorting = 'ORDER BY ';
switch ($type) {
case 'id': $sorting .= 'id';
break;
case 'title': $sorting .= 'title';
break;
case 'description': $sorting .= 'description';
break;
case 'public': $sorting .= 'public';
break;
case 'type': $sorting .= 'type';
break;
case 'star': $sorting .= 'star';
break;
default: exit('Error: Unknown type for sorting!');
}
$sorting .= ' ';
switch ($order) {
case 'ASC': $sorting .= 'ASC';
break;
case 'DESC': $sorting .= 'DESC';
break;
default: exit('Error: Unknown order for sorting!');
}
$result = $database->query("UPDATE lychee_settings SET value = '$sorting' WHERE `key` = 'sorting';");
if (!$result) return false;
return true;
}
?>

@ -0,0 +1,378 @@
<?php
/**
* @name Upload Module
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2014 by Philipp Maurer, Tobias Reich
*/
if (!defined('LYCHEE')) exit('Error: Direct access is not allowed!');
function upload($files, $albumID) {
global $database;
switch($albumID) {
// s for public (share)
case 's':
$public = 1;
$star = 0;
$albumID = 0;
break;
// f for starred (fav)
case 'f':
$star = 1;
$public = 0;
$albumID = 0;
break;
default:
$star = 0;
$public = 0;
}
foreach ($files as $file) {
$id = str_replace('.', '', microtime(true));
while(strlen($id)<14) $id .= 0;
$tmp_name = $file["tmp_name"];
$type = getimagesize($tmp_name);
if (($type[2]!=1)&&($type[2]!=2)&&($type[2]!=3)) return false;
$data = array_reverse(explode('.', $file["name"]));
$data = $data[0];
$photo_name = md5($id) . ".$data";
// Import if not uploaded via web
if (!is_uploaded_file($tmp_name)) {
if (copy($tmp_name, "../uploads/big/" . $photo_name)) {
unlink($tmp_name);
$import_name = $tmp_name;
}
} else {
move_uploaded_file($tmp_name, "../uploads/big/" . $photo_name);
$import_name = "";
}
// Read infos
$info = getInfo($photo_name);
// Set orientation based on EXIF data
if (isset($info['orientation'])&&isset($info['width'])&&isset($info['height'])) {
if ($info['orientation']==3||$info['orientation']==6||$info['orientation']==8) {
$newWidth = $info['width'];
$newHeight = $info['height'];
$sourceImg = imagecreatefromjpeg("../uploads/big/$photo_name");
switch($info['orientation']){
case 2:
// mirror
// not yet implemented
break;
case 3:
$sourceImg = imagerotate($sourceImg, -180, 0);
break;
case 4:
// rotate 180 and mirror
// not yet implemented
break;
case 5:
// rotate 90 and mirror
// not yet implemented
break;
case 6:
$sourceImg = imagerotate($sourceImg, -90, 0);
$newWidth = $info['height'];
$newHeight = $info['width'];
break;
case 7:
// rotate -90 and mirror
// not yet implemented
break;
case 8:
$sourceImg = imagerotate($sourceImg, 90, 0);
$newWidth = $info['height'];
$newHeight = $info['width'];
break;
}
$newSourceImg = imagecreatetruecolor($newWidth, $newHeight);
imagecopyresampled($newSourceImg, $sourceImg, 0, 0, 0, 0, $newWidth, $newHeight, $newWidth, $newHeight);
imagejpeg($newSourceImg, "../uploads/big/$photo_name", 100);
}
}
// Create Thumb
if (!createThumb($photo_name)) return false;
// Save to DB
$query = "INSERT INTO lychee_photos (id, title, url, description, type, width, height, size, sysdate, systime, iso, aperture, make, model, shutter, focal, takedate, taketime, thumbUrl, album, public, star, import_name)
VALUES (
'" . $id . "',
'" . $info['title'] . "',
'" . $photo_name . "',
'" . $info['description'] . "',
'" . $info['type'] . "',
'" . $info['width'] . "',
'" . $info['height'] . "',
'" . $info['size'] . "',
'" . $info['date'] . "',
'" . $info['time'] . "',
'" . $info['iso'] . "',
'" . $info['aperture'] . "',
'" . $info['make'] . "',
'" . $info['model'] . "',
'" . $info['shutter'] . "',
'" . $info['focal'] . "',
'" . $info['takeDate'] . "',
'" . $info['takeTime'] . "',
'" . md5($id) . ".jpeg',
'" . $albumID . "',
'" . $public . "',
'" . $star . "',
'" . $import_name . "');";
$result = $database->query($query);
if (!$result) return false;
}
return true;
}
function getInfo($filename) {
global $database;
$url = '../uploads/big/' . $filename;
$iptcArray = array();
$info = getimagesize($url, $iptcArray);
// General information
$return['type'] = $info['mime'];
$return['width'] = $info[0];
$return['height'] = $info[1];
$return['date'] = date('d.m.Y', filectime($url));
$return['time'] = date('H:i:s', filectime($url));
// Size
$size = filesize($url)/1024;
if ($size>=1024) $return['size'] = round($size/1024, 1) . ' MB';
else $return['size'] = round($size, 1) . ' KB';
// IPTC Metadata Fallback
$return['title'] = '';
$return['description'] = '';
// IPTC Metadata
$iptcInfo = iptcparse($iptcArray['APP13']);
if (is_array($iptcInfo)) {
$temp = $iptcInfo['2#105'][0];
if (isset($temp)&&strlen($temp)>0) $return['title'] = $temp;
$temp = $iptcInfo['2#120'][0];
if (isset($temp)&&strlen($temp)>0) $return['description'] = $temp;
}
// EXIF Metadata Fallback
$return['orientation'] = '';
$return['iso'] = '';
$return['aperture'] = '';
$return['make'] = '';
$return['model'] = '';
$return['shutter'] = '';
$return['focal'] = '';
$return['takeDate'] = '';
$return['takeTime'] = '';
// EXIF Metadata
if ($info['mime']=='image/jpeg'&&function_exists('exif_read_data')&&@exif_read_data($url, 'EXIF', 0)) {
$exif = exif_read_data($url, 'EXIF', 0);
$temp = $exif['Orientation'];
if (isset($temp)) $return['orientation'] = $temp;
$temp = $exif['ISOSpeedRatings'];
if (isset($temp)) $return['iso'] = $temp;
$temp = $exif['COMPUTED']['ApertureFNumber'];
if (isset($temp)) $return['aperture'] = $temp;
$temp = $exif['Make'];
if (isset($temp)) $return['make'] = $exif['Make'];
$temp = $exif['Model'];
if (isset($temp)) $return['model'] = $temp;
$temp = $exif['ExposureTime'];
if (isset($temp)) $return['shutter'] = $exif['ExposureTime'] . ' Sec.';
$temp = $exif['FocalLength'];
if (isset($temp)) $return['focal'] = ($temp/1) . ' mm';
$temp = $exif['DateTimeOriginal'];
if (isset($temp)) {
$exifDate = explode(' ', $temp);
$date = explode(':', $exifDate[0]);
$return['takeDate'] = $date[2].'.'.$date[1].'.'.$date[0];
$return['takeTime'] = $exifDate[1];
}
}
// Security
foreach(array_keys($return) as $key) $return[$key] = mysqli_real_escape_string($database, $return[$key]);
return $return;
}
function createThumb($filename, $width = 200, $height = 200) {
global $settings;
$url = "../uploads/big/$filename";
$info = getimagesize($url);
$photoName = explode(".", $filename);
$newUrl = "../uploads/thumb/".$photoName[0].".jpeg";
$newUrl2x = "../uploads/thumb/".$photoName[0]."@2x.jpeg";
// Set position and size
$thumb = imagecreatetruecolor($width, $height);
$thumb2x = imagecreatetruecolor($width*2, $height*2);
if ($info[0]<$info[1]) {
$newSize = $info[0];
$startWidth = 0;
$startHeight = $info[1]/2 - $info[0]/2;
} else {
$newSize = $info[1];
$startWidth = $info[0]/2 - $info[1]/2;
$startHeight = 0;
}
// Create new image
switch($info['mime']) {
case 'image/jpeg': $sourceImg = imagecreatefromjpeg($url); break;
case 'image/png': $sourceImg = imagecreatefrompng($url); break;
case 'image/gif': $sourceImg = imagecreatefromgif($url); break;
default: return false;
}
imagecopyresampled($thumb,$sourceImg,0,0,$startWidth,$startHeight,$width,$height,$newSize,$newSize);
imagecopyresampled($thumb2x,$sourceImg,0,0,$startWidth,$startHeight,$width*2,$height*2,$newSize,$newSize);
imagejpeg($thumb,$newUrl,$settings['thumbQuality']);
imagejpeg($thumb2x,$newUrl2x,$settings['thumbQuality']);
return true;
}
function importPhoto($name, $albumID = 0) {
$tmp_name = "../uploads/import/$name";
$info = getimagesize($tmp_name);
$size = filesize($tmp_name);
$nameFile = array(array());
$nameFile[0]['name'] = $name;
$nameFile[0]['type'] = $info['mime'];
$nameFile[0]['tmp_name'] = $tmp_name;
$nameFile[0]['error'] = 0;
$nameFile[0]['size'] = $size;
if (upload($nameFile, $albumID)) return true;
return false;
}
function importUrl($url, $albumID = 0) {
if (strpos($url, ',')!==false) {
// Multiple photos
$url = explode(',', $url);
foreach ($url as &$key) {
$key = str_replace(' ', '%20', $key);
if (@getimagesize($key)) {
$pathinfo = pathinfo($key);
$filename = $pathinfo['filename'].".".$pathinfo['extension'];
$tmp_name = "../uploads/import/$filename";
copy($key, $tmp_name);
}
}
return importServer($albumID);
} else {
// One photo
$url = str_replace(' ', '%20', $url);
if (@getimagesize($url)) {
$pathinfo = pathinfo($url);
$filename = $pathinfo['filename'].".".$pathinfo['extension'];
$tmp_name = "../uploads/import/$filename";
copy($url, $tmp_name);
return importPhoto($filename, $albumID);
}
}
return false;
}
function importServer($albumID = 0) {
global $database;
$i = 0;
$files = glob('../uploads/import/*');
foreach ($files as $file) {
if (@getimagesize($file)) {
if (!importPhoto(basename($file), $albumID)) return false;
$i++;
}
}
if ($i===0) return "Warning: Folder empty!";
return true;
}
?>

@ -1,17 +0,0 @@
<?php
define('LYCHEE', true);
// Include
require('check.php');
if($error=='') {
if(!$database->query("SELECT `public` FROM `lychee_albums`;")) $database->query("ALTER TABLE `lychee_albums` ADD `public` TINYINT( 1 ) NOT NULL DEFAULT '0'");
if(!$database->query("SELECT `password` FROM `lychee_albums`;")) $database->query("ALTER TABLE `lychee_albums` ADD `password` VARCHAR( 100 ) NULL DEFAULT NULL");
$database->query("UPDATE `lychee_photos` SET url = replace(url, 'uploads/big/', ''), thumbUrl = replace(thumbUrl, 'uploads/thumb/', '')");
echo "\nUpdate complete!";
} else {
echo "\nCould not Update!";
}
?>

@ -0,0 +1,65 @@
<?php
/**
* @name check.php
* @author Philipp Maurer
* @author Tobias Reich
* @copyright 2014 by Philipp Maurer, Tobias Reich
* @description This file takes a look at your Lychee-configuration and displays all errors it can find.
* Everything should work if you can see the message 'Lychee is ready!'.
*/
define('LYCHEE', true);
header('content-type: text/plain');
// Declare
$error = '';
// Include
if (!file_exists('../php/config.php')) exit('Error 001: Configuration not found. Please install Lychee first.');
require('../php/config.php');
require('../php/modules/settings.php');
// Database
$database = new mysqli($dbHost, $dbUser, $dbPassword, $dbName);
if (mysqli_connect_errno()!=0) $error .= ('Error 100: ' . mysqli_connect_errno() . ': ' . mysqli_connect_error() . '' . PHP_EOL);
// Get Settings
$settings = getSettings();
// PHP Version
if (floatval(phpversion())<5.2) $error .= ('Error 200: Please upgrade to PHP 5.2 or higher!' . PHP_EOL);
// Extensions
if (!extension_loaded('exif')) $error .= ('Error 300: PHP exif extension not activated' . PHP_EOL);
if (!extension_loaded('mbstring')) $error .= ('Error 301: PHP mbstring extension not activated' . PHP_EOL);
if (!extension_loaded('gd')) $error .= ('Error 302: PHP gd extension not activated' . PHP_EOL);
if (!extension_loaded('mysqli')) $error .= ('Error 303: PHP mysqli extension not activated' . PHP_EOL);
// Config
if (!isset($dbName)||$dbName=='') $error .= ('Error 400: No property for $dbName in config.php' . PHP_EOL);
if (!isset($dbUser)||$dbUser=='') $error .= ('Error 401: No property for $dbUser in config.php' . PHP_EOL);
if (!isset($dbPassword)) $error .= ('Error 402: No property for $dbPassword in config.php' . PHP_EOL);
if (!isset($dbHost)||$dbHost=='') $error .= ('Error 403: No property for $dbHost in config.php' . PHP_EOL);
// Database Config
if (!$settings['username']||$settings['username']=='') $error .= ('Error 404: Username empty or not set' . PHP_EOL);
if (!$settings['password']||$settings['password']=='') $error .= ('Error 405: Password empty or not set' . PHP_EOL);
if (!$settings['checkForUpdates']||($settings['checkForUpdates']!='0'&&$settings['checkForUpdates']!='1')) $error .= ('Error 406: No or wrong property for checkForUpdates' . PHP_EOL);
if (!$settings['thumbQuality']||$settings['thumbQuality']=='') $error .= ('Error 407: No or wrong property for thumbQuality' . PHP_EOL);
if (!$settings['sorting']||$settings['sorting']=='') $error .= ('Error 408: Wrong property for sorting' . PHP_EOL);
// Permissions
if (substr(sprintf('%o', @fileperms('../uploads/big/')), -4)!='0777') $error .= ('Error 500: Wrong permissions for \'uploads/big\' (777 required)' . PHP_EOL);
if (substr(sprintf('%o', @fileperms('../uploads/thumb/')), -4)!='0777') $error .= ('Error 501: Wrong permissions for \'uploads/thumb\' (777 required)' . PHP_EOL);
if (substr(sprintf('%o', @fileperms('../uploads/import/')), -4)!='0777') $error .= ('Error 502: Wrong permissions for \'uploads/import\' (777 required)' . PHP_EOL);
if (substr(sprintf('%o', @fileperms('../uploads/')), -4)!='0777') $error .= ('Error 503: Wrong permissions for \'uploads/\' (777 required)' . PHP_EOL);
if (substr(sprintf('%o', @fileperms('../php/')), -4)!='0777') $error .= ('Error 504: Wrong permissions for \'php/\' (777 required)' . PHP_EOL);
if ($error=='') echo('Lychee is ready. Lets rock!' . PHP_EOL); else echo $error;
// Check php.ini Settings
if (ini_get('max_execution_time')<200&&ini_set('upload_max_filesize', '20M')!==true) echo('Warning: You may experience problems when uploading a large amount of photos. Take a look in the FAQ for details.' . PHP_EOL);
?>

@ -2,71 +2,66 @@
#### A great looking and easy-to-use Photo-Management-System.
![Lychee](http://l.electerious.com/uploads/big/13582806160093.png)
![Lychee](http://l.electerious.com/uploads/big/13582805615704.png)
![Lychee](http://l.electerious.com/uploads/big/136b4779d133a94666d5f0d151b8ea2f.png)
![Lychee](http://l.electerious.com/uploads/big/580f1300f884c330fa34b652decb0571.png)
Lychee is a free, easy to use and great looking photo-management-system you can run on your server to manage and share photos. Just download the source and follow the instructions to install Lychee wherever you want.
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.
## Installation
To run Lychee, everything you need is a web-server with PHP 5.3 or later and a MySQL-Database. Follow the instructions to install Lychee on your server. [Installation &#187;](https://github.com/electerious/Lychee/wiki/Installation)
To run Lychee, everything you need is a web-server with PHP 5.3 or later and a MySQL-Database. Follow the instructions to install Lychee on your server. [Installation &#187;](docs/md/Installation.md)
## Settings
## How to use
Settings are located inside the `php/config.php`. All settings are optional and doesn't need to be changed. [Settings &#187;](https://github.com/electerious/Lychee/wiki/Settings)
You can use Lychee right after the installation. Here are some advanced features to get the most out of it.
## How to use
### Settings
After the configuration, navigate your browser to the place where Lychee is located. Everything should work now.
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 `php/config.php`. Other options and settings are stored directly in the database. [Settings &#187;](docs/md/Settings.md)
#### FTP Upload
### Update
You can upload photos directly with every FTP client into Lychee. This feature helps you to share single images quickly with others. [FTP Upload &#187;](https://github.com/electerious/Lychee/wiki/FTP-Upload)
1. Replace all files, excluding `uploads/`
2. Open Lychee and enter your database details
#### Keyboard Shortcuts
### FTP Upload
This shortcuts will help you to use Lychee even faster. [Keyboard Shortcuts &#187;](https://github.com/electerious/Lychee/wiki/Keyboard-Shortcuts)
You can import photos from your server or upload photos directly with every FTP client into Lychee. [FTP Upload &#187;](docs/md/FTP Upload.md)
#### Twitter Cards
### Keyboard Shortcuts
Lychee supports [Twitter Cards](https://dev.twitter.com/docs/cards) and [Open Graph](http://opengraphprotocol.org) for shared images (not albums). In order to use Twitter Cards you need to request an approval for your domain. Simply share an image with Lychee, copy its link and paste it in [Twitters Card Validator](https://dev.twitter.com/docs/cards/validation/validator).
These shortcuts will help you to use Lychee even faster. [Keyboard Shortcuts &#187;](docs/md/Keyboard Shortcuts.md)
## Browser Support
### Twitter Cards
Lychee supports the latest versions of Google Chrome, Apple Safari, Mozilla Firefox and Opera. Photos you share with others can be viewed from every browser. For the best experience we are recommending to use Google Chrome or Apple Safari.
Lychee supports [Twitter Cards](https://dev.twitter.com/docs/cards) and [Open Graph](http://opengraphprotocol.org) for shared images (not albums). In order to use Twitter Cards you need to request an approval for your domain. Simply share an image with Lychee, copy its link and paste it in [Twitters Card Validator](https://dev.twitter.com/docs/cards/validation/validator).
## Update
## Troubleshooting
####From version 1.0/1.1/1.2 to 1.3:
1. Replace all files, excluding `uploads/`
2. Open `php/config.php` and reconfigure your installation
3. Open `php/update.php` in your browser
Take a look at the [FAQ](docs/md/faq.md) if you have problems.
## Extensions
| Name | Description | Link |
|:-----------|:------------|:------------|
| lycheesync | Command line interface tool to sync lychee with any directory containing photos | https://github.com/GustavePate/lycheesync |
## Troubleshooting
If Lychee is not working properly, try to open `php/check.php`. This file will take a look at your configuration and displays all errors it can find. Everything should work if you can see the message "Lychee is ready!".
If you experience problems uploading large amounts of photos, you might want to change the PHP parameters in `.htaccess` (if you are using the PHP Apache module) or in `.user.ini` (if you are using PHP >= 5.3 with CGI or FastCGI).
| lycheesync | Sync Lychee with any directory containing photos | https://github.com/GustavePate/lycheesync |
## About
## Developer
- [electerious](https://github.com/electerious) / [Tobias Reich](http://electerious.com)
Lychee is made by [Tobias Reich](http://electerious.com) (HTML, CSS, JS, Design, Website Design and Development) with the help of [Philipp Maurer](http://phinal.net) (PHP, MySQL).
## Contributors
- [phinal](https://github.com/phinal) / [Philipp Maurer](http://phinal.net)
- [cdauth](https://github.com/cdauth)
##License
(MIT License)
Copyright (C) 2013 [Tobias Reich](http://electerious.com)
Copyright (C) 2013 [Philipp Maurer](http://phinal.net)
Copyright (C) 2014 [Tobias Reich](http://electerious.com)
Copyright (C) 2013 [Philipp Maurer](http://phinal.net)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.SE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save