21 Commits

Author SHA1 Message Date
Luc Didry
f5867f4fd6 🔖 — Bump version (0.16.0) 2023-12-29 08:39:14 +01:00
Luc Didry
03f63e4ec6 🔀 Merge branch 'development' 2023-12-29 08:38:27 +01:00
Luc Didry
2a466f0140 👷 — Fix missing CI stage 2023-12-29 08:31:46 +01:00
Luc Didry
613ba30752 👷 — Update the create-release and pouet-it snippet’s URLs 2023-12-29 08:27:08 +01:00
Luc Didry
125a7103a0 🩹 — Fix use of deprecated method after dependencies upgrade 2023-12-29 08:09:34 +01:00
Luc Didry
9477e1add1 🩹 — Replace old URL of the project 2023-12-29 08:02:47 +01:00
Luc Didry
03dbeecac0 — Replace moment.js with Date().toLocaleDateString(…) 2023-12-29 07:50:49 +01:00
Luc Didry
a76b240ed8 🐛 — update gallery, zip and random URLs when closing image dialog box 2023-12-29 07:43:33 +01:00
Luc Didry
5d6f715396 🩹 — Remove twitter.css minification in Makefile
And update minified CSS files
2023-12-29 07:33:31 +01:00
Luc Didry
5bf1a9d717 🎨 — Use template literals in js 2023-12-29 07:30:15 +01:00
Luc Didry
fff56a89bb 💥 No more twitter cards 2023-12-28 07:33:30 +01:00
Luc Didry
2cb07f8565 ⬆ Update jQuery 2023-12-27 08:27:13 +01:00
Luc Didry
ebf9f66e47 🔖 — Bump version (0.15.0) 2023-12-19 14:55:26 +01:00
Luc Didry
ed70fbac2e 🔀 Merge branch 'development' 2023-12-19 14:54:34 +01:00
Luc Didry
af11a1e7ec 🔀 Merge branch 'fix-134' into development 2023-12-19 14:40:40 +01:00
Luc Didry
438ce5050e — Add --nuke option to image command (fix #134) 2023-12-19 14:40:03 +01:00
Luc Didry
d67d66d0fd 🔖 — Bump version (0.14.0) 2023-12-18 04:47:19 +01:00
Luc Didry
cc03ba6d3f 🔀 Merge branch 'development' into 'master'
Development

See merge request fiat-tux/hat-softwares/lutim!94
2023-12-18 03:43:02 +00:00
Luc Didry
5b4f56b9f6 🔀 Merge branch 'update-deps' into 'development'
Update deps

See merge request fiat-tux/hat-softwares/lutim!93
2023-12-18 03:31:42 +00:00
Luc Didry
8b6766f498 🩹 — Update tests and code after dependencies upgrade 2023-12-17 09:51:06 +01:00
Luc Didry
68518dd85c ⬆️ — Upgrade dependencies 2023-12-17 05:37:50 +01:00
57 changed files with 2883 additions and 2651 deletions

View File

@@ -1,6 +1,6 @@
image: hatsoftwares/lutim-test-ci:latest
stages:
- publish_changelog
- create_release
- pouet_it
- podcheck
- carton
@@ -63,8 +63,8 @@ variables:
##
#
include:
- 'https://framagit.org/fiat-tux/gitlabci-snippets/raw/4e4e03322e95e9b0124c714456ebf1bdc02ad43f/publish_changelog.gitlab-ci.yml'
- 'https://framagit.org/fiat-tux/gitlabci-snippets/raw/4e4e03322e95e9b0124c714456ebf1bdc02ad43f/pouet-it-from-ci.gitlab-ci.yml'
- 'https://framagit.org/fiat-tux/gitlabci-snippets/-/raw/2aac6c1f3dd725d9aed57549da67a92759f9f9ec/create-release-from-ci.gitlab-ci.yml'
- 'https://framagit.org/fiat-tux/gitlabci-snippets/-/raw/41345a919d3c927991782f5fd17e0c7b338a3f3a/pouet-it-from-ci.gitlab-ci.yml'
### Podcheck
##

View File

@@ -240,7 +240,7 @@
# array of memcached servers to cache URL in order to accelerate responses to often-viewed URL.
# If set to [], the use of memcached is disabled.
# If you use memcached, the internal cache (see cache_max_size option above) will not be used.
# Please see https://framagit.org/luc/lutim/wikis/memcached to know how to configure your memcached
# Please see https://framagit.org/fiat-tux/hat-softwares/lutim/-/wikis/memcached to know how to configure your memcached
# servers.
# exemple of valid value: ['127.0.0.1:11211']
# optional, default is []

View File

@@ -1,6 +1,22 @@
Revision history for Lutim
0.14.0 ????-??-??
0.17.0 ????-??-??
0.16.0 2023-12-29
- ⬆️ UUpdate jQuery
- 💥 BREAKING CHANGE: no more twitter cards
- 🎨 — Use template literals in js
- 🐛 — Gallery, zip and random URLs are now updated when closing image dialog box
— Replace moment.js with Date().toLocaleDateString(…)
- 🩹 — Replace old URL of the project
0.15.0 2023-12-19
- ✨ — Add --nuke option to image command
0.14.0 2023-12-18
- ⬆️ Update dependencies
- 💥 BREAKING CHANGE: Use `?_format=json` instead of `?format=json`
- 💥 BREAKING CHANGE: Use `?_format=json` instead of terminating the URL with `.json`
0.13.0 2023-04-26
- 💄 — Add Korrigan theme (Nicolas Frandeboeuf)

View File

@@ -1,3 +1,3 @@
# Contributing
See how to contribute on the [wiki](https://framagit.org/luc/lutim/wikis/contribute).
See how to contribute on the [wiki](https://framagit.org/fiat-tux/hat-softwares/lutim/-/wikis/contribute).

View File

@@ -5,9 +5,9 @@ ARG VCS_REF
ARG VERSION
LABEL org.label-schema.build-date=$BUILD_DATE \
org.label-schema.name="Lets Upload That Image" \
org.label-schema.url="https://lut.im/" \
org.label-schema.url="https://lutim.fiat-tux.fr/" \
org.label-schema.vcs-ref=$VCS_REF \
org.label-schema.vcs-url="https://git.framasoft.org/luc/lutim" \
org.label-schema.vcs-url="https://framagit.org/fiat-tux/hat-softwares/lutim" \
org.label-schema.vendor="Luc Didry" \
org.label-schema.version=$VERSION \
org.label-schema.schema-version="1.0"
@@ -45,4 +45,4 @@ RUN apk --no-cache add perl~=5 \
USER lutim
EXPOSE 8080
ENTRYPOINT ["/bin/sh", "/home/lutim/docker/entrypoint.sh"]
ENTRYPOINT ["/bin/sh", "/home/lutim/docker/entrypoint.sh"]

View File

@@ -13,7 +13,6 @@ minify:
@cd ./themes/default/public/css/ && cat bootstrap.min.css fontello.css hennypenny.css lutim.css toastify.css | csso > common.min.css
@cd ./themes/default/public/css/ && cat animation.css uploader.css markdown.css | csso > not_stats.min.css
@cd ./themes/default/public/css/ && cat photoswipe.css default-skin/default-skin.css | csso > gallery.min.css
@cd ./themes/default/public/css/ && cat twitter.css | csso > twitter.min.css
locales:
$(XGETTEXT) $(EXTRACTDIR) -o $(POT) 2>/dev/null

View File

@@ -4,27 +4,24 @@
It means Let's Upload That Image.
## What does it do?
It stores images and allows you to see them, download them or share them on social networks. From version 0.5, the gif images can be displayed as animated gifs in Twitter, but you need an HTTPS server (Twitter requires that. Lutim detects if you have a HTTPS server and displays a static image twitter card if you don't);
It stores images and allows you to see them, download them or share them on social networks.
Images are indefinitely stored unless you request that they will be deleted at first view or after 24 hours / one week / one month / one year.
## License
Lutim is licensed under the terms of the AGPL. See the LICENSE file.
## Official instance
You can see it working at https://lut.im.
## Logo
Lutim's logo is an adaptation of [Lutin](http://commons.wikimedia.org/wiki/File:Lutin_by_godo.jpg) by [Godo](http://godoillustrateur.wordpress.com/), licensed under the terms of the CC-BY-SA 3.0 license.
![Lutim's logo](https://lut.im/img/Lutim_small.png)
![Lutim's logo](https://lutim.fiat-tux.fr/img/Lutim_small.png)
## Wiki
The official wiki contains all you need to know about Lutim (installation, API, etc.). Go to <https://framagit.org/luc/lutim/wikis/home> or clone it:
The official wiki contains all you need to know about Lutim (installation, API, etc.). Go to <https://framagit.org/fiat-tux/hat-softwares/lutim/-/wikis/home> or clone it:
```
git clone https://framagit.org/luc/lutim.wiki.git
git clone https://framagit.org/fiat-tux/hat-softwares/lutim.wiki.git
```
## Encryption
@@ -43,9 +40,9 @@ See [AUTHORS.md](AUTHORS.md) file.
## Contribute!
Please consider contributing, either by [reporting issues](https://framagit.org/luc/lutim/issues) or by helping the [internationalization](https://weblate.framasoft.org/projects/lutim/). And of course, code contribution are welcome!
Please consider contributing, either by [reporting issues](https://framagit.org/fiat-tux/hat-softwares/lutim/issues) or by helping the [internationalization](https://weblate.framasoft.org/projects/lutim/). And of course, code contribution are welcome!
The details on how to contribute are on the [wiki](https://framagit.org/luc/lutim/wikis/contribute).
The details on how to contribute are on the [wiki](https://framagit.org/fiat-tux/hat-softwares/lutim/wikis/contribute).
## Make a donation

View File

@@ -15,6 +15,7 @@ requires 'Filesys::DiskUsage';
requires 'Switch';
requires 'Crypt::CBC';
requires 'Crypt::Blowfish';
requires 'Digest::MD5';
requires 'Locale::Maketext';
requires 'Locale::Maketext::Extract';
requires 'File::MimeInfo';

File diff suppressed because it is too large Load Diff

View File

@@ -291,7 +291,7 @@ sub startup {
});
$r->get('/')->
over('authorized')->
requires('authorized')->
to('Image#home')->
name('index');
$r->get('/')->
@@ -330,7 +330,7 @@ sub startup {
to('Image#change_lang')->
name('lang');
$r->get('/partial/:file' => sub {
$r->get('/partial/<:file>.<:f>' => sub {
my $c = shift;
$c->render(
template => 'partial/'.$c->param('file'),
@@ -351,7 +351,7 @@ sub startup {
})->name('gallery');
$r->get('/myfiles')->
over('authorized')->
requires('authorized')->
name('myfiles');
$r->get('/myfiles')->
to('Authent#index');
@@ -369,28 +369,28 @@ sub startup {
->name('random');
$r->post('/')->
over('authorized')->
requires('authorized')->
to('Image#add')->
name('add');
$r->post('/')->
to('Authent#index');
$r->get('/d/:short/:token')->
over('authorized')->
requires('authorized')->
to('Image#delete')->
name('delete');
$r->get('/d/:short/:token')->
to('Authent#index');
$r->post('/m/:short/:token')->
over('authorized')->
requires('authorized')->
to('Image#modify')->
name('modify');
$r->post('/m/:short/:token')->
to('Authent#index');
$r->post('/c')->
over('authorized')->
requires('authorized')->
to('Image#get_counter')->
name('counter');
$r->post('/c')->

View File

@@ -198,11 +198,11 @@ var disabled_donut = {
};
EOF
Mojo::File->new('themes/'.$config->{theme}.'/templates/stats.json.ep')->spurt(encode_json($stats));
Mojo::File->new('themes/'.$config->{theme}.'/templates/data.html.ep')->spurt($dom);
Mojo::File->new('themes/'.$config->{theme}.'/templates/raw.html.ep')->spurt(encode('UTF-8', $dom2));
Mojo::File->new('themes/'.$config->{theme}.'/templates/stats.json.ep')->spew(encode_json($stats));
Mojo::File->new('themes/'.$config->{theme}.'/templates/data.html.ep')->spew($dom);
Mojo::File->new('themes/'.$config->{theme}.'/templates/raw.html.ep')->spew(encode('UTF-8', $dom2));
mkdir 'themes/'.$config->{theme}.'/templates/partial/' unless -d 'themes/'.$config->{theme}.'/templates/partial/';
Mojo::File->new('themes/'.$config->{theme}.'/templates/partial/raw.js.ep')->spurt(encode('UTF-8', $js));
Mojo::File->new('themes/'.$config->{theme}.'/templates/partial/raw.js.ep')->spew(encode('UTF-8', $js));
}
=encoding utf8

View File

@@ -46,7 +46,8 @@ sub run {
'r|remove=s{1,}' => \my @remove,
'y|yes' => \my $yes,
'q|quiet' => \my $quiet,
's|search=s' => \my $ip
's|search=s' => \my $ip,
'n|nuke=s' => \my $nuke,
;
if (scalar @info) {
@@ -85,9 +86,43 @@ sub run {
push @shorts, $e->short;
print_infos($e, $csv);
});
say sprintf('%d matching URLs', $u->size);
say sprintf('%d matching images', $u->size);
say sprintf("If you want to delete those images, please do:\n carton exec script/lutim image --remove %s", join(' ', @shorts)) if @shorts;
}
if ($nuke) {
my $i = get_short($c, $nuke);
if ($i && $i->created_by) {
my $u = Lutim::DB::Image->new(app => $c->app)->search_exact_created_by($i->created_by);
my @shorts;
say sprintf('%d images created by the same IP address (%s) than image %s', $u->size, $i->created_by, $nuke);
my $confirm = ($yes) ? 'yes' : undef;
unless (defined $confirm) {
printf('Are you sure you want to remove those %d images? [N/y] ', $u->size);
$confirm = <STDIN>;
chomp $confirm;
}
if ($confirm =~ m/^y(es)?$/i) {
$u->each(sub {
my ($e, $num) = @_;
my $i = get_short($c, $e->short);
if ($i) {
print_infos($i, $csv);
if ($i->enabled) {
delete_short($c, $i, 1);
} else {
say sprintf('The image %s is already disabled', $e->short);
}
}
});
} else {
say 'Answer was not "y" or "yes". Aborting deletion.';
}
} elsif (! $i->created_by) {
say sprintf('Image %s does not contain its creators IP address.', $nuke);
} else {
say sprintf('Sorry, cant find image %s', $nuke);
}
}
}
sub get_short {
@@ -168,7 +203,7 @@ sub delete_short {
my $confirm = ($y) ? 'yes' : undef;
unless (defined $confirm) {
printf('Are you sure you want to remove this image (%s) ? [N/y] ', $i->short);
printf('Are you sure you want to remove this image (%s)? [N/y] ', $i->short);
$confirm = <STDIN>;
chomp $confirm;
}
@@ -191,6 +226,7 @@ Lutim::Command::image - Manage URL in Lutim's database
carton exec script/lutim image --info <short> <short> [--csv] Print infos about the space-separated images (--csv creates a CSV output)
carton exec script/lutim image --remove <short> <short> [--yes] [--quiet] Delete the space-separated images (--yes disables confirmation, --quiet disables informations printing)
carton exec script/lutim image --search <ip> Print infos about the images uploaded by this IP (database LIKE, may include images uploaded by other IPs)
carton exec script/lutim image --nuke <short> Delete the image and all images sent by the same IP address and print infos about the deleted images
=cut

View File

@@ -1,8 +1,8 @@
# vim:set sw=4 ts=4 sts=4 expandtab:
package Lutim::Controller::Image;
use Mojo::Asset::Memory;
use Mojo::Base 'Mojolicious::Controller';
use Mojo::Util qw(url_escape url_unescape b64_encode encode);
use Mojo::Asset::Memory;
use Mojo::JSON qw(true false);
use Lutim::DB::Image;
use DateTime;
@@ -487,12 +487,10 @@ sub add {
if ($mediatype ne 'image/svg+xml' && $mediatype !~ m#image/(x-)?xcf# && $mediatype ne 'image/webp') {
# Remove the EXIF tags
my $data = new IO::Scalar \$upload->slurp();
my $et = new Image::ExifTool;
my $et = Image::ExifTool->new;
# Use $data in Image::ExifTool object
$et->ExtractInfo($data);
# Remove all metadata
$et->SetNewValue('*', undef);
$et->SetNewValue('*');
# Create a temporary IO::Scalar to write into
my $temp;
@@ -657,11 +655,11 @@ sub short {
}
}
return $c->render(
template => 'twitter',
template => 'share',
layout => undef,
short => $short,
filename => $image->filename,
mimetype => ($c->req->url->to_abs()->scheme eq 'https') ? $image->mediatype : '',
mimetype => $image->mediatype,
width => $width,
height => $height
);

View File

@@ -345,6 +345,20 @@ sub to_hash {
=back
=head2 search_exact_created_by
=over 1
=item B<Usage> : C<$c-E<gt>search_exact_created_by($ip)>
=item B<Arguments> : an IP address
=item B<Purpose> : get enabled images that have been uploaded by this IP address
=item B<Returns> : a Mojo::Collection object containing the matching images as Lutim::DB::Image objects
=back
=cut
1;

View File

@@ -213,6 +213,27 @@ sub search_created_by {
return c(@images);
}
sub search_exact_created_by {
my $c = shift;
my $ip = shift;
my @images;
my $records = $c->app->pg->db->select('lutim', undef, { enabled => 1, created_by => $ip })->hashes;
$records->each(
sub {
my ($e, $num) = @_;
my $i = Lutim::DB::Image->new(app => $c->app);
$i->_slurp($e);
push @images, $i;
}
);
return c(@images);
}
sub _slurp {
my $c = shift;
my $r = shift;

View File

@@ -214,6 +214,27 @@ sub search_created_by {
return c(@images);
}
sub search_exact_created_by {
my $c = shift;
my $ip = shift;
my @images;
my $records = $c->app->sqlite->db->select('lutim', undef, { enabled => 1, created_by => $ip })->hashes;
$records->each(
sub {
my ($e, $num) = @_;
my $i = Lutim::DB::Image->new(app => $c->app);
$i->_slurp($e);
push @images, $i;
}
);
return c(@images);
}
sub _slurp {
my $c = shift;
my $r = shift;

View File

@@ -8,6 +8,7 @@ use Data::Entropy qw(entropy_source);
use DateTime;
use Mojo::Util qw(decode);
use ISO::639_1;
use Digest::MD5 'md5';
sub register {
my ($self, $app) = @_;
@@ -258,6 +259,18 @@ sub _is_wm_selected {
return ($wm eq $c->config('watermark_default')) ? 'selected="selected"' : '';
}
sub _key_from_key {
my $key = shift;
# Key size for Blowfish is 56
my $ks = 56;
my $material = md5($key);
while (length($material) < $ks) {
$material .= md5($material);
}
return substr($material,0,$ks);
}
sub _crypt {
my $c = shift;
my $upload = shift;
@@ -267,10 +280,12 @@ sub _crypt {
my $iv = $c->shortener(8);
my $cipher = Crypt::CBC->new(
-key => $key,
-cipher => 'Blowfish',
-header => 'none',
-iv => $iv
-key => _key_from_key($key),
-cipher => 'Blowfish',
-header => 'none',
-literal_key => 1,
-pbkdf => 'pbkdf2',
-iv => $iv
);
$cipher->start('encrypting');
@@ -289,16 +304,18 @@ sub _crypt {
sub _decrypt {
my $c = shift;
my $key = shift;
my $key = _key_from_key(shift);
my $file = shift;
my $iv = shift;
$iv = 'dupajasi' unless $iv;
my $cipher = Crypt::CBC->new(
-key => $key,
-cipher => 'Blowfish',
-header => 'none',
-iv => $iv
-key => $key,
-cipher => 'Blowfish',
-header => 'none',
-literal_key => 1,
-pbkdf => 'pbkdf2',
-iv => $iv
);
$cipher->start('decrypting');

View File

@@ -49,11 +49,6 @@
# optional, default is 5
#anti_flood_delay => 5,
# twitter account which will appear on twitter cards
# see https://dev.twitter.com/docs/cards/validation/validator to register your Lutim instance on twitter
# optional, no default
#tweet_card_via => '@foo',
# max image size, in octets
# you can write it 10*1024*1024
# optional, default is 10485760
@@ -244,7 +239,7 @@
# array of memcached servers to cache URL in order to accelerate responses to often-viewed URL.
# If set to [], the use of memcached is disabled.
# If you use memcached, the internal cache (see cache_max_size option above) will not be used.
# Please see https://framagit.org/luc/lutim/wikis/memcached to know how to configure your memcached
# Please see https://framagit.org/fiat-tux/hat-softwares/lutim/-/wikis/memcached to know how to configure your memcached
# servers.
# exemple of valid value: ['127.0.0.1:11211']
# optional, default is []

View File

@@ -49,11 +49,6 @@
# optional, default is 5
#anti_flood_delay => 5,
# twitter account which will appear on twitter cards
# see https://dev.twitter.com/docs/cards/validation/validator to register your Lutim instance on twitter
# optional, default is @framasky
#tweet_card_via => '@framasky',
# max image size, in octets
# you can write it 10*1024*1024
# optional, default is 10485760
@@ -194,7 +189,7 @@
# array of memcached servers to cache URL in order to accelerate responses to often-viewed URL.
# If set to [], the use of memcached is disabled.
# If you use memcached, the internal cache (see cache_max_size option above) will not be used.
# Please see https://framagit.org/luc/lutim/wikis/memcached to know how to configure your memcached
# Please see https://framagit.org/fiat-tux/hat-softwares/lutim/-/wikis/memcached to know how to configure your memcached
# servers.
# exemple of valid value: ['127.0.0.1:11211']
# optional, default is []

View File

@@ -49,11 +49,6 @@
# optional, default is 5
#anti_flood_delay => 5,
# twitter account which will appear on twitter cards
# see https://dev.twitter.com/docs/cards/validation/validator to register your Lutim instance on twitter
# optional, default is @framasky
#tweet_card_via => '@framasky',
# max image size, in octets
# you can write it 10*1024*1024
# optional, default is 10485760
@@ -194,7 +189,7 @@
# array of memcached servers to cache URL in order to accelerate responses to often-viewed URL.
# If set to [], the use of memcached is disabled.
# If you use memcached, the internal cache (see cache_max_size option above) will not be used.
# Please see https://framagit.org/luc/lutim/wikis/memcached to know how to configure your memcached
# Please see https://framagit.org/fiat-tux/hat-softwares/lutim/-/wikis/memcached to know how to configure your memcached
# servers.
# exemple of valid value: ['127.0.0.1:11211']
# optional, default is []

View File

@@ -49,11 +49,6 @@
# optional, default is 5
#anti_flood_delay => 5,
# twitter account which will appear on twitter cards
# see https://dev.twitter.com/docs/cards/validation/validator to register your Lutim instance on twitter
# optional, default is @framasky
#tweet_card_via => '@framasky',
# max image size, in octets
# you can write it 10*1024*1024
# optional, default is 10485760
@@ -194,7 +189,7 @@
# array of memcached servers to cache URL in order to accelerate responses to often-viewed URL.
# If set to [], the use of memcached is disabled.
# If you use memcached, the internal cache (see cache_max_size option above) will not be used.
# Please see https://framagit.org/luc/lutim/wikis/memcached to know how to configure your memcached
# Please see https://framagit.org/fiat-tux/hat-softwares/lutim/-/wikis/memcached to know how to configure your memcached
# servers.
# exemple of valid value: ['127.0.0.1:11211']
# optional, default is []

View File

@@ -49,11 +49,6 @@
# optional, default is 5
#anti_flood_delay => 5,
# twitter account which will appear on twitter cards
# see https://dev.twitter.com/docs/cards/validation/validator to register your Lutim instance on twitter
# optional, default is @framasky
#tweet_card_via => '@framasky',
# max image size, in octets
# you can write it 10*1024*1024
# optional, default is 10485760
@@ -194,7 +189,7 @@
# array of memcached servers to cache URL in order to accelerate responses to often-viewed URL.
# If set to [], the use of memcached is disabled.
# If you use memcached, the internal cache (see cache_max_size option above) will not be used.
# Please see https://framagit.org/luc/lutim/wikis/memcached to know how to configure your memcached
# Please see https://framagit.org/fiat-tux/hat-softwares/lutim/-/wikis/memcached to know how to configure your memcached
# servers.
# exemple of valid value: ['127.0.0.1:11211']
# optional, default is []

View File

@@ -49,11 +49,6 @@
# optional, default is 5
#anti_flood_delay => 5,
# twitter account which will appear on twitter cards
# see https://dev.twitter.com/docs/cards/validation/validator to register your Lutim instance on twitter
# optional, default is @framasky
#tweet_card_via => '@framasky',
# max image size, in octets
# you can write it 10*1024*1024
# optional, default is 10485760
@@ -194,7 +189,7 @@
# array of memcached servers to cache URL in order to accelerate responses to often-viewed URL.
# If set to [], the use of memcached is disabled.
# If you use memcached, the internal cache (see cache_max_size option above) will not be used.
# Please see https://framagit.org/luc/lutim/wikis/memcached to know how to configure your memcached
# Please see https://framagit.org/fiat-tux/hat-softwares/lutim/-/wikis/memcached to know how to configure your memcached
# servers.
# exemple of valid value: ['127.0.0.1:11211']
# optional, default is []

View File

@@ -49,11 +49,6 @@
# optional, default is 5
#anti_flood_delay => 5,
# twitter account which will appear on twitter cards
# see https://dev.twitter.com/docs/cards/validation/validator to register your Lutim instance on twitter
# optional, default is @framasky
#tweet_card_via => '@framasky',
# max image size, in octets
# you can write it 10*1024*1024
# optional, default is 10485760
@@ -194,7 +189,7 @@
# array of memcached servers to cache URL in order to accelerate responses to often-viewed URL.
# If set to [], the use of memcached is disabled.
# If you use memcached, the internal cache (see cache_max_size option above) will not be used.
# Please see https://framagit.org/luc/lutim/wikis/memcached to know how to configure your memcached
# Please see https://framagit.org/fiat-tux/hat-softwares/lutim/-/wikis/memcached to know how to configure your memcached
# servers.
# exemple of valid value: ['127.0.0.1:11211']
# optional, default is []

View File

@@ -81,7 +81,7 @@ $t->get_ok('/css/lutim.css')
# Instance settings informations
$t->get_ok('/infos')
->status_is(200)
->json_has('image_magick')
->json_has('/image_magick')
->json_is(
'/always_encrypt' => false,
'/broadcast_message' => 'test broadcast message',
@@ -96,7 +96,7 @@ $t->get_ok('/infos')
my $image = Mojo::File->new($Bin, '..', 'themes', 'default', 'public', 'img', 'Lutim.png')->to_string;
$t->post_ok('/' => form => { file => { file => $image }, format => 'json' })
->status_is(200)
->json_has('msg', 'success')
->json_has('/msg', '/success')
->json_is('/success' => true, '/msg/filename' => 'Lutim.png')
->json_like('/msg/short' => qr#[-_a-zA-Z0-9]{8}#, '/msg/real_short' => qr#[-_a-zA-Z0-9]{8}#, '/msg/token' => qr#[-_a-zA-Z0-9]{24}#);
@@ -138,7 +138,7 @@ my $token = $raw->json('/msg/token');
$t->get_ok('/'.$rshort)
->status_is(200);
$t->get_ok('/d/'.$rshort.'/'.$token, form => { format => 'json' })
$t->get_ok('/d/'.$rshort.'/'.$token, form => { _format => 'json' })
->status_is('200')
->json_is(
{

View File

@@ -179,14 +179,6 @@ msgstr "Exportar datos de almacenamiento local"
msgid "File name"
msgstr "Nombre de archivo"
#: themes/default/templates/about.html.ep:24
msgid ""
"For more details, see the <a href=\"https://framagit.org/luc/"
"lutim\">homepage of the project</a>."
msgstr ""
"Para más detalles, vea la <a href=\"https://framagit.org/luc/lutim\">página "
"del proyecto</a>."
#: themes/default/templates/partial/navbar.html.ep:80
msgid "Fork me!"
msgstr "¡Clóname!"

View File

@@ -171,12 +171,6 @@ msgstr "Exportera localStorage-data"
msgid "File name"
msgstr "Filnamn"
#: themes/default/templates/about.html.ep:24
msgid "For more details, see the <a href=\"https://framagit.org/luc/lutim\">homepage of the project</a>."
msgstr ""
"Se <a href=\"https://framagit.org/luc/lutim\">projektets hemsida</a> för mer "
"information."
#: themes/default/templates/partial/navbar.html.ep:66
msgid "Fork me!"
msgstr "Grena mig!"

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
@-moz-keyframes spin{0%{-moz-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-moz-transform:rotate(359deg);-o-transform:rotate(359deg);-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-moz-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-moz-transform:rotate(359deg);-o-transform:rotate(359deg);-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@-o-keyframes spin{0%{-moz-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-moz-transform:rotate(359deg);-o-transform:rotate(359deg);-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@-ms-keyframes spin{0%{-moz-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-moz-transform:rotate(359deg);-o-transform:rotate(359deg);-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spin{0%{-moz-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-moz-transform:rotate(359deg);-o-transform:rotate(359deg);-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.animate-spin{-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;-webkit-animation:spin 2s infinite linear;animation:spin 2s infinite linear;display:inline-block}.uploader{border:2px dotted #a5a5c7;width:100%;color:#92aab0;text-align:center;vertical-align:middle;padding:30px 0;margin-bottom:10px;font-size:200%;cursor:default;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.uploader div.or{font-size:50%;font-weight:700;color:silver;padding:10px}@media (max-width:768px){.uploader div.browser label{max-width:95%}}@media (min-width:768px){.uploader div.browser label{width:300px}}.uploader div.browser label{background-color:#5a7bc2;color:#fff;padding:6px 0;font-size:40%;font-weight:700;cursor:pointer;border-radius:2px;position:relative;overflow:hidden;display:block;margin:20px auto 0;box-shadow:2px 2px 2px #888}.uploader div.browser span{cursor:pointer}.uploader div.browser input{position:absolute;top:0;right:0;margin:0;border:solid transparent;border-width:0 0 100px 200px;opacity:0;filter:alpha(opacity= 0);-o-transform:translate(250px,-50px) scale(1);-moz-transform:translate(-300px,0) scale(4);direction:ltr;cursor:pointer}.uploader div.browser label:hover{background-color:#427fed}@font-face{font-family:'markdown';src:url(../../font/markdown.eot?-6fnbp5);src:url(../../font/markdown.eot?#iefix-6fnbp5) format('embedded-opentype'),url(../../font/markdown.woff?-6fnbp5) format('woff'),url(../../font/markdown.ttf?-6fnbp5) format('truetype'),url(../../font/markdown.svg?-6fnbp5#markdown) format('svg');font-weight:400;font-style:normal}[class*=" markdown-"],[class^=markdown-]{font-family:'markdown';speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.markdown-mark:before{content:"\e600"}.markdown-mark-solid:before{content:"\e601"}
@-moz-keyframes spin{0%{-moz-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-moz-transform:rotate(359deg);-o-transform:rotate(359deg);-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-moz-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-moz-transform:rotate(359deg);-o-transform:rotate(359deg);-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@-o-keyframes spin{0%{-moz-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-moz-transform:rotate(359deg);-o-transform:rotate(359deg);-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@-ms-keyframes spin{0%{-moz-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-moz-transform:rotate(359deg);-o-transform:rotate(359deg);-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spin{0%{-moz-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-moz-transform:rotate(359deg);-o-transform:rotate(359deg);-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.animate-spin{-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;-webkit-animation:spin 2s infinite linear;animation:spin 2s infinite linear;display:inline-block}.uploader{border:2px dotted #a5a5c7;width:100%;color:#92aab0;text-align:center;vertical-align:middle;padding:30px 0;margin-bottom:10px;font-size:200%;cursor:default;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.uploader div.or{font-size:50%;font-weight:700;color:silver;padding:10px}@media (max-width:768px){.uploader div.browser label{max-width:95%}}@media (min-width:768px){.uploader div.browser label{width:300px}}.uploader div.browser label{background-color:#5a7bc2;color:#fff;padding:6px 0;font-size:40%;font-weight:700;cursor:pointer;border-radius:2px;position:relative;overflow:hidden;display:block;margin:20px auto 0;box-shadow:2px 2px 2px #888}.uploader div.browser span{cursor:pointer}.uploader div.browser input{position:absolute;top:0;right:0;margin:0;border:solid transparent;border-width:0 0 100px 200px;opacity:0;filter:alpha(opacity= 0);-o-transform:translate(250px,-50px) scale(1);-moz-transform:translate(-300px,0) scale(4);direction:ltr;cursor:pointer}.uploader div.browser label:hover{background-color:#427fed}@font-face{font-family:"markdown";src:url(../../font/markdown.eot?-6fnbp5);src:url(../../font/markdown.eot?#iefix-6fnbp5)format("embedded-opentype"),url(../../font/markdown.woff?-6fnbp5)format("woff"),url(../../font/markdown.ttf?-6fnbp5)format("truetype"),url(../../font/markdown.svg?-6fnbp5#markdown)format("svg");font-weight:400;font-style:normal}[class*=" markdown-"],[class^=markdown-]{font-family:"markdown";speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.markdown-mark:before{content:""}.markdown-mark-solid:before{content:""}

View File

@@ -1,11 +0,0 @@
/* vim:set sw=4 ts=4 sts=4 ft=css expandtab: */
html {
max-height:100%;
}
.height-97 {
height: 97%;
}
.freezeframe {
max-width: 100%;
max-height:100%;
}

View File

@@ -1 +0,0 @@
html{max-height:100%}.height-97{height:97%}.freezeframe{max-width:100%;max-height:100%}

View File

@@ -1 +0,0 @@
jquery-3.5.1.min.js

View File

@@ -1 +0,0 @@
jquery-3.5.1.min.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +0,0 @@
freezeframe_options = {
trigger_event: "click",
animation_play_duration: 60000
}

View File

@@ -69,7 +69,6 @@
% # Display image informations
<h4>
<a href="<%= $url.'.'.stash('ext') %>" target="_blank"><%= stash('filename') %></a>
&nbsp;&nbsp;&nbsp;<a title="<%= l('Tweet it!') %>" target="_blank" href="https://twitter.com/share?url=<%= $url %>?t"><span class="icon icon-twitter"></span></a>
</h4>
% my $delete_url = url_for('delete', {short => stash('real_short'), token => stash('token')})->to_abs();
<form class="form">

View File

@@ -1,9 +1,6 @@
% # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab:
% use Mojo::Util qw(url_escape);
% my $twitter_url = 'https://twitter.com/share';
% my $url = url_for('/')->to_abs();
% $twitter_url .= '?url='.url_escape("$url")
% .'&text=Check out this %23Lutim instance! ';
<!DOCTYPE html>
<html>
<head>
@@ -32,7 +29,7 @@
% }
</head>
<body>
%= include 'partial/navbar', twitter_url => $twitter_url
%= include 'partial/navbar'
<div class="container">
<div>
% if (defined(config('hosted_by'))) {
@@ -62,7 +59,7 @@
% if (defined(config('piwik_img'))) {
<img src="<%== config('piwik_img') %>" class="border-zero" alt="">
% }
%= javascript '/js/jquery-3.2.1.min.js'
%= javascript '/js/jquery-3.7.1.min.js'
%= javascript '/partial/manifest.js'
%= javascript '/js/toastify.js'
%= javascript '/js/bootstrap.min.js'
@@ -89,7 +86,6 @@
%= javascript '/partial/lutim.js'
% }
% if (current_route 'myfiles') {
%= javascript '/js/moment-with-locales.min.js'
%= javascript '/partial/myfiles.js'
% }
</body>

View File

@@ -1,6 +1,6 @@
{
"name": "Lutim",
"description": "Let's Upload That Image!\n\nThis is a simple image sharing app which use <%= url_for('/')->to_abs %> for storing the images. Once you have uploaded an image, you'll be provided differents links:\n a view link, which points directly to the image\n a download link, which force the download of the image\n a twitter link, which you can share on twitter : the image will natively appeared in twitter\n a delete link, which allows you to delete the image anytime you want\n\nThe image is stored on <%= url_for('/')->to_abs %> for a delay which you can define.\n\nThe particularity of Lutim is that the image is encrypted and its usage is as most anonymous as possible. See the information page (<%= url_for('about')->to_abs %>) for more details.\nLicense: AGPLv3 (https://www.gnu.org/licenses/agpl-3.0.html)",
"description": "Let's Upload That Image!\n\nThis is a simple image sharing app which use <%= url_for('/')->to_abs %> for storing the images. Once you have uploaded an image, you'll be provided differents links:\n a view link, which points directly to the image\n a download link, which force the download of the image\n a social link, which you can share on social networks\n a delete link, which allows you to delete the image anytime you want\n\nThe image is stored on <%= url_for('/')->to_abs %> for a delay which you can define.\n\nThe particularity of Lutim is that the image is encrypted and its usage is as most anonymous as possible. See the information page (<%= url_for('about')->to_abs %>) for more details.\nLicense: AGPLv3 (https://www.gnu.org/licenses/agpl-3.0.html)",
"launch_path": "<%= url_for('/') %>",
"icons": {
"32": "<%= url_for('/img/lutim32.png') %>",
@@ -11,13 +11,13 @@
"256": "<%= url_for('/img/lutim256.png') %>"
},
"developer": {
"name": "Lutim team !",
"url": "https://framagit.org/luc/lutim"
"name": "Lutim team!",
"url": "https://framagit.org/fiat-tux/hat-softwares/lutim"
},
"default_locale": "en",
"locales": {
"fr": {
"description": "Envoyons cette image !\n\nCeci est une application de partage simple d'images qui utilise <%= url_for('/')->to_abs %> pour enregistrer les images. Une fois que vous avez envoyé une image, vous obtiendrez différents liens :\n un lien de visualisation, qui pointe directement sur l'image\n un lien de téléchargement, qui force le téléchargement de l'image\n un lien twitter, que vous pouvez partager sur twitter : l'image apparaîtra nativement dans twitter\n un lien de suppression, qui vous permet de supprimer l'image quand vous le souhaitez\n\nL'image est conservée sur <%= url_for('/')->to_abs %> pour un délai que vous pouvez définir.\n\nLa particularité de Lutim est que l'image est chiffrée et que son usage est aussi anonyme que possible. Voir la page d'information (<%= url_for('about')->to_abs %>) pour plus de détails.\nLicence : AGPLv3 (https://www.gnu.org/licenses/agpl-3.0.html)"
"description": "Envoyons cette image !\n\nCeci est une application de partage simple d'images qui utilise <%= url_for('/')->to_abs %> pour enregistrer les images. Une fois que vous avez envoyé une image, vous obtiendrez différents liens :\n un lien de visualisation, qui pointe directement sur l'image\n un lien de téléchargement, qui force le téléchargement de l'image\n un lien de partage, que vous pouvez partager sur les réseaux sociaux\n un lien de suppression, qui vous permet de supprimer l'image quand vous le souhaitez\n\nL'image est conservée sur <%= url_for('/')->to_abs %> pour un délai que vous pouvez définir.\n\nLa particularité de Lutim est que l'image est chiffrée et que son usage est aussi anonyme que possible. Voir la page d'information (<%= url_for('about')->to_abs %>) pour plus de détails.\nLicence : AGPLv3 (https://www.gnu.org/licenses/agpl-3.0.html)"
}
},
"activities": {

View File

@@ -9,14 +9,14 @@ function addToShortHash(short) {
window.short_hash[short] = 1;
if (Object.keys(window.short_hash).length > 0) {
$('#gallery-url').removeClass('hidden');
$('#gallery-url-input').val(window.gallery_url+Object.keys(window.short_hash).join(','));
$('#gallery-url-link').attr('href', window.gallery_url+Object.keys(window.short_hash).join(','));
$('#gallery-url-input').val(`${window.gallery_url}${Object.keys(window.short_hash).join(',')}`);
$('#gallery-url-link').attr('href', `${window.gallery_url}${Object.keys(window.short_hash).join(',')}`);
}
}
function rmFromShortHash(short) {
delete window.short_hash[short];
$('#gallery-url-input').val(window.gallery_url+Object.keys(window.short_hash).join(','));
$('#gallery-url-link').attr('href', window.gallery_url+Object.keys(window.short_hash).join(','));
$('#gallery-url-input').val(`${window.gallery_url}${Object.keys(window.short_hash).join(',')}`);
$('#gallery-url-link').attr('href', `${window.gallery_url}${Object.keys(window.short_hash).join(',')}`);
if (Object.keys(window.short_hash).length === 0) {
$('#gallery-url').addClass('hidden');
}
@@ -25,14 +25,14 @@ function addToZipHash(short) {
window.zip_hash[short] = 1;
if (Object.keys(window.zip_hash).length > 0) {
$('#zip-url').removeClass('hidden');
$('#zip-url-input').val(window.zip_url+Object.keys(window.zip_hash).join('&i='));
$('#zip-url-link').attr('href', window.zip_url+Object.keys(window.zip_hash).join('&i='));
$('#zip-url-input').val(`${window.zip_url}${Object.keys(window.zip_hash).join('&i=')}`);
$('#zip-url-link').attr('href', `${window.zip_url}${Object.keys(window.zip_hash).join('&i=')}`);
}
}
function rmFromZipHash(short) {
delete window.zip_hash[short];
$('#zip-url-input').val(window.zip_url+Object.keys(window.zip_hash).join('&i='));
$('#zip-url-link').attr('href', window.zip_url+Object.keys(window.zip_hash).join('&i='));
$('#zip-url-input').val(`${window.zip_url}${Object.keys(window.zip_hash).join('&i=')}`);
$('#zip-url-link').attr('href', `${window.zip_url}${Object.keys(window.zip_hash).join('&i=')}`);
if (Object.keys(window.zip_hash).length === 0) {
$('#zip-url').addClass('hidden');
}
@@ -41,14 +41,14 @@ function addToRandomHash(short) {
window.random_hash[short] = 1;
if (Object.keys(window.random_hash).length > 0) {
$('#random-url').removeClass('hidden');
$('#random-url-input').val(window.random_url+Object.keys(window.random_hash).join('&i='));
$('#random-url-link').attr('href', window.random_url+Object.keys(window.random_hash).join('&i='));
$('#random-url-input').val(`${window.random_url}${Object.keys(window.random_hash).join('&i=')}`);
$('#random-url-link').attr('href', `${window.random_url}${Object.keys(window.random_hash).join('&i=')}`);
}
}
function rmFromRandomHash(short) {
delete window.random_hash[short];
$('#random-url-input').val(window.random_url+Object.keys(window.random_hash).join('&i='));
$('#random-url-link').attr('href', window.random_url+Object.keys(window.random_hash).join('&i='));
$('#random-url-input').val(`${window.random_url}${Object.keys(window.random_hash).join('&i=')}`);
$('#random-url-link').attr('href', `${window.random_url}${Object.keys(window.random_hash).join('&i=')}`);
if (Object.keys(window.random_hash).length === 0) {
$('#random-url').addClass('hidden');
}
@@ -85,10 +85,10 @@ function copyLink(e) {
e.preventDefault();
var successful = copyText($(this).prop('href'));
var msg = successful ? 'successful' : 'unsuccessful';
console.debug('Copying text command was ' + msg);
console.debug(`Copying text command was ${msg}`);
if (!successful) {
badToast('<%= l('Unable to copy to clipboard') %>');
throw new Error('Copying text command was ' + msg);
throw new Error(`Copying text command was ${msg}`);
} else {
goodToast('<%= l('Copied to clipboard') %>');
}
@@ -105,10 +105,10 @@ function copyToClipboard(el) {
try {
var successful = copyInput(el);
var msg = successful ? 'successful' : 'unsuccessful';
console.debug('Copying text command was ' + msg);
console.debug(`Copying text command was ${msg}`);
if (!successful) {
badToast('<%= l('Unable to copy to clipboard') %>');
throw new Error('Copying text command was ' + msg);
throw new Error(`Copying text command was ${msg}`);
} else {
goodToast('<%= l('Copied to clipboard') %>');
}
@@ -126,9 +126,9 @@ function copyAllToClipboard(e) {
try {
var successful = copyText(text.join("\n"));
var msg = successful ? 'successful' : 'unsuccessful';
console.debug('Copying text command was ' + msg);
console.debug(`Copying text command was ${msg}`);
if (!successful) {
throw new Error('Copying text command was ' + msg);
throw new Error(`Copying text command was ${msg}`);
}
} catch (err) {
prompt('<%= l('Hit Ctrl+C, then Enter to copy the short link') %>', text.join(" "));
@@ -144,14 +144,14 @@ function delImage(e) {
var short = $(this).attr('data-short');
var token = $(this).attr('data-token');
$.ajax({
url: '<%= url_for('/') %>d/'+short+'/'+token,
url: `<%= url_for('/') %>d/${short}/${token}`,
method: 'GET',
data: {
format: 'json'
_format: 'json'
},
success: function(data) {
if (data.success) {
$('#alert-'+short).remove();
$(`#alert-${short}`).remove();
evaluateCopyAll();
delItem(short);
goodToast('<%= l('Image deleted') %>');
@@ -168,30 +168,33 @@ function delImage(e) {
function link(url, dl, token, modify, only_url) {
if (token !== undefined) {
if (modify !== undefined && modify === true) {
return '<%== url_for('/m/')->to_abs() %>'+url+'/'+token;
return `<%== url_for('/m/')->to_abs() %>${url}/${token}`;
}
var link = '<%== url_for('/')->to_abs() %>d/'+url+'/'+token;
var link = `<%== url_for('/')->to_abs() %>d/${url}/${token}`;
if (only_url !== undefined && only_url === true) {
return link;
}
return [
'<label class="sr-only" for="link-del-', url, '"><%= l('Deletion link') %></label>',
'<div class="input-group input-group-sm">',
'<div class="input-group-btn adjust-addon">',
'<a class="btn btn-default text-danger" href="#" title="<%= l('Deletion link') %>" id="del-', url, '" data-short="', url, '" data-token="', token, '">',
'<span class="icon icon-trash"></span> ',
'</a>',
'</div>',
'<input type="text" class="form-control" id="link-del-', url, '" value="', link, '" readonly>',
'<a href="#" class="input-group-addon copy-to-clipboard-link" title="<%= l('Copy to clipboard') %>">',
'<span class="icon icon-clipboard"></span>',
'</a>',
'</div>'
].join('');
return `<label class="sr-only" for="link-del-${url}"><%= l('Deletion link') %></label>
<div class="input-group input-group-sm">
<div class="input-group-btn adjust-addon">
<a href="#"
class="btn btn-default text-danger"
title="<%= l('Deletion link') %>"
id="del-${url}"
data-short="${url}"
data-token="${token}">
<span class="icon icon-trash"></span>
</a>
</div>
<input type="text" class="form-control" id="link-del-${url}" value="${link}" readonly>
<a href="#" class="input-group-addon copy-to-clipboard-link" title="<%= l('Copy to clipboard') %>">
<span class="icon icon-clipboard"></span>
</a>
</div>`;
} else if (dl !== '') {
url = url+'?'+dl;
url = `${url}?${dl}`;
}
return '<%== prefix() %>'+url;
return `<%== prefix() %>${url}`;
}
function badToast(msg) {
@@ -216,3 +219,14 @@ function goodToast(msg) {
positionLeft: false
}).showToast();
}
function formatDate(unixTimestamp) {
return new Date(unixTimestamp * 1000).toLocaleString(window.navigator.language, {
year: 'numeric',
month: 'long',
day: 'numeric',
weekday: 'long',
hour: '2-digit',
minute: '2-digit',
})
}

View File

@@ -193,7 +193,7 @@ var initPhotoSwipeFromDOM = function(gallerySelector) {
var galleryElements = document.querySelectorAll( gallerySelector );
for(var i = 0, l = galleryElements.length; i < l; i++) {
galleryElements[i].setAttribute('data-pswp-uid', i+1);
galleryElements[i].setAttribute('data-pswp-uid', i + 1);
galleryElements[i].onclick = onThumbnailsClick;
}
@@ -205,25 +205,19 @@ var initPhotoSwipeFromDOM = function(gallerySelector) {
};
function addAlert(e) {
$('#infos-msg').append([
'<div class="alert alert-danger">',
'<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>',
'<strong><%= l('Something bad happened') %></strong><br>',
cleanName(e, false),
'</div>'
].join(''));
$('#infos-msg').append(`<div class="alert alert-danger">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
<strong><%= l('Something bad happened') %></strong><br>
${cleanName(e, false)}
</div>`);
}
function appendToGallery(url, width, height) {
$('.gallery').append(
[
'<figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">',
' <a href="', url, '" itemprop="contentUrl" data-size="', width, 'x', height, '">',
' <img src="', url, '?width=275" itemprop="thumbnail" alt="" class="img-responsive"/>',
' </a>',
'</figure>'
].join('')
);
$('.gallery').append(`<figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
<a href="${url}" itemprop="contentUrl" data-size="${width}x${height}">
<img src="${url}?width=275" itemprop="thumbnail" alt="" class="img-responsive"/>
</a>
</figure>`);
}
function nextOrInitGallery(keys) {
@@ -238,21 +232,21 @@ function addElement(keys) {
element = keys.shift();
if (!element.match('xcf')) {
$.ajax({
url: '<%= url_for('about_img') %>'+element,
url: `<%= url_for('about_img') %>${element}`,
method: 'GET',
dataType: 'json',
success: function(data, textStatus, jqXHR) {
if (data.success) {
if (data.data.width && data.data.height) {
appendToGallery(absUrl+element, data.data.width, data.data.height);
appendToGallery(`${absUrl}${element}`, data.data.width, data.data.height);
nextOrInitGallery(keys);
} else {
var img = new Image();
img.onload = function(){
appendToGallery(absUrl+element, img.width, img.height);
appendToGallery(`${absUrl}${element}`, img.width, img.height);
nextOrInitGallery(keys);
}
img.src = absUrl+element;
img.src = `${absUrl}${element}`;
}
} else {
addAlert(data.msg);
@@ -269,14 +263,14 @@ function addElement(keys) {
$(document).ready(function() {
var key = window.location.hash.substring(1); // Get key
// First, strip everything after the equal sign (=) which signals end of base64 string.
i = key.indexOf('='); if (i>-1) { key = key.substring(0,i+1); }
i = key.indexOf('='); if (i>-1) { key = key.substring(0, i + 1); }
// If the equal sign was not present, some parameters may remain:
i = key.indexOf('&'); if (i>-1) { key = key.substring(0,i); }
i = key.indexOf('&'); if (i>-1) { key = key.substring(0, i); }
var keys = key.split(',');
$('#img-nb').append('<p><%= l('There is XXXX image(s) in the gallery') %></p>'.replace('XXXX', keys.length));
$('#download-all').attr('href', $('#download-all').attr('href')+keys.join('&i='));
$('#download-all').attr('href', $('#download-all').attr('href') + keys.join('&i='));
addElement(keys);
});

View File

@@ -12,13 +12,6 @@ function cleanName(name, empty) {
return name.replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
}
function tw_url(url) {
return btn = [
'<a title="<%= l('Tweet it!') %>" target="_blank" href="https://twitter.com/share?url=<%== url_for('/')->to_abs() %>', url, '?t" class="btn btn-default">',
'<span class="icon icon-twitter"></span>',
'</a>'
].join('');
}
function modifyImage(e) {
e.preventDefault();
var url = $(this).data('modlink');
@@ -26,13 +19,13 @@ function modifyImage(e) {
modify(url, short);
}
function modify(url, short) {
var limit = $('#day-'+short).val();
var del_at_view = ($('#first-view-'+short).prop('checked')) ? 1 : 0;
var limit = $(`#day-${short}`).val();
var del_at_view = ($(`#first-view-${short}`).prop('checked')) ? 1 : 0;
$.ajax({
url : url,
type : 'POST',
data : {
'image_url' : '<%== url_for('/')->to_abs() %>'+short,
'image_url' : `<%== url_for('/')->to_abs() %>${short}`,
'format' : 'json',
'delete-day' : limit,
'first-view' : del_at_view
@@ -50,135 +43,131 @@ function modify(url, short) {
function buildMessage(success, msg) {
if(success) {
var s_url = link([msg.short, '.', msg.ext].join(''), '');
var thumb = (msg.thumb !== null) ? [
'<div class="col-sm-1">',
'<a href="', s_url, '" target="_blank">',
'<img class="thumbnail img-responsive" alt="', cleanName(msg.filename, true), ' thumbnail" src="', msg.thumb, '">',
'</a>',
'</div>'
].join('') : ''
return [
'<div class="alert alert-success" id="alert-', msg.real_short, '">',
'<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>',
'<div class="row">', thumb,
'<div class="col-sm-11">',
'<h4>',
'<a href="', s_url, '" target="_blank">',
msg.filename,
'</a>',
'</h4>',
'<form class="form">',
'<div class="form-group">',
'<label class="sr-only" for="view', msg.real_short, '"><%= l('View link') %></label>',
'<div class="input-group input-group-sm col-sm-6">',
'<div class="input-group-btn adjust-addon">',
'<a href="', s_url, '" target="_blank" class="btn btn-default">',
'<span class="icon icon-eye" title=" <%= l('View link') %>"></span>',
'</a>',
'</div>',
'<input type="text" class="form-control view-link-input" id="view', msg.real_short, '" value="', s_url, '" readonly>',
'<a href="#" class="input-group-addon copy-to-clipboard-link" title="<%= l('Copy to clipboard') %>">',
'<span class="icon icon-clipboard"></span>',
'</a>',
'</div>',
'</div>',
'<div class="form-group">',
'<label class="sr-only" for="markdown', msg.real_short, '"><%= l('Markdown syntax') %></label>',
'<div class="input-group input-group-sm col-sm-6">',
'<div class="input-group-addon adjust-addon">',
'<span class="markdown-mark-solid" title="<%= l('Markdown syntax') %>"></span>',
'</div>',
'<input type="text" class="form-control" id="markdown', msg.real_short, '" value="![](', link(msg.short, ''), ')" readonly>',
'<a href="#" class="input-group-addon copy-to-clipboard-link" title="<%= l('Copy to clipboard') %>">',
'<span class="icon icon-clipboard"></span>',
'</a>',
'</div>',
'</div>',
'<div class="form-group">',
'<label class="sr-only" for="download', msg.real_short, '"><%= l('Download link') %></label>',
'<div class="input-group input-group-sm col-sm-6">',
'<div class="input-group-btn adjust-addon">',
'<a href="', link(msg.short, 'dl'), '" class="btn btn-default">',
'<span class="icon icon-download" title="<%= l('Download link') %>"></span>',
'</a>',
'</div>',
'<input type="text" class="form-control" id="download', msg.real_short, '" value="', link(msg.short, 'dl'), '" readonly>',
'<a href="#" class="input-group-addon copy-to-clipboard-link" title="<%= l('Copy to clipboard') %>">',
'<span class="icon icon-clipboard"></span>',
'</a>',
'</div>',
'</div>',
'<div class="form-group">',
'<label class="sr-only" for="share', msg.real_short, '"><%= l('Link for share on social networks') %></label>',
'<div class="input-group input-group-sm col-sm-6">',
'<div class="input-group-btn adjust-addon">',
'<a href="', link(msg.short, 't'), '" target="_blank" class="btn btn-default">',
'<span class="icon icon-share" title="<%= l('Link for share on social networks') %>"></span>',
'</a>',
tw_url(msg.short),
'</div>',
'<input type="text" class="form-control" id="share', msg.real_short, '" value="', link(msg.short, 't'), '" readonly>',
'<a href="#" class="input-group-addon copy-to-clipboard-link" title="<%= l('Copy to clipboard') %>">',
'<span class="icon icon-clipboard"></span>',
'</a>',
'</div>',
'</div>',
'<div class="form-group">',
'<div class="input-group col-sm-6 col-xs-12">',
'<span class="form-control-static">', link(msg.real_short, '', msg.token), '</span>',
'</div>',
'</div>',
'</form>',
'</div>',
'</div>',
'<div class="row">',
'<form class="form col-sm-11 col-sm-offset-1" role="form" method="POST" action="', link(msg.real_short, '', msg.token, true), '">',
'<div class="form-group form-inline">',
'<select id="day-', msg.real_short, '" name="delete-day" class="form-control">',
var thumb = (msg.thumb !== null) ? `<div class="col-sm-1">
<a href="${s_url}" target="_blank">
<img class="thumbnail img-responsive" alt="${cleanName(msg.filename, true)} thumbnail" src="${msg.thumb}">
</a>
</div>` : '';
return `<div class="alert alert-success" id="alert-${msg.real_short}">
<button id="close-${msg.real_short}" type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
<div class="row">${thumb}
<div class="col-sm-11">
<h4>
<a href="${s_url}" target="_blank">
${msg.filename}
</a>
</h4>
<form class="form">
<div class="form-group">
<label class="sr-only" for="view${msg.real_short}"><%= l('View link') %></label>
<div class="input-group input-group-sm col-sm-6">
<div class="input-group-btn adjust-addon">
<a href="${s_url}" target="_blank" class="btn btn-default">
<span class="icon icon-eye" title=" <%= l('View link') %>"></span>
</a>
</div>
<input type="text" class="form-control view-link-input" id="view${msg.real_short}" value="${s_url}" readonly>
<a href="#" class="input-group-addon copy-to-clipboard-link" title="<%= l('Copy to clipboard') %>">
<span class="icon icon-clipboard"></span>
</a>
</div>
</div>
<div class="form-group">
<label class="sr-only" for="markdown${msg.real_short}"><%= l('Markdown syntax') %></label>
<div class="input-group input-group-sm col-sm-6">
<div class="input-group-addon adjust-addon">
<span class="markdown-mark-solid" title="<%= l('Markdown syntax') %>"></span>
</div>
<input type="text" class="form-control" id="markdown${msg.real_short}" value="![](${link(msg.short, '')})" readonly>
<a href="#" class="input-group-addon copy-to-clipboard-link" title="<%= l('Copy to clipboard') %>">
<span class="icon icon-clipboard"></span>
</a>
</div>
</div>
<div class="form-group">
<label class="sr-only" for="download${msg.real_short}"><%= l('Download link') %></label>
<div class="input-group input-group-sm col-sm-6">
<div class="input-group-btn adjust-addon">
<a href="${link(msg.short, 'dl')}" class="btn btn-default">
<span class="icon icon-download" title="<%= l('Download link') %>"></span>
</a>
</div>
<input type="text" class="form-control" id="download${msg.real_short}" value="${link(msg.short, 'dl')}" readonly>
<a href="#" class="input-group-addon copy-to-clipboard-link" title="<%= l('Copy to clipboard') %>">
<span class="icon icon-clipboard"></span>
</a>
</div>
</div>
<div class="form-group">
<label class="sr-only" for="share${msg.real_short}"><%= l('Link for share on social networks') %></label>
<div class="input-group input-group-sm col-sm-6">
<div class="input-group-btn adjust-addon">
<a href="${link(msg.short, 't')}" target="_blank" class="btn btn-default">
<span class="icon icon-share" title="<%= l('Link for share on social networks') %>"></span>
</a>
</div>
<input type="text" class="form-control" id="share${msg.real_short}" value="${link(msg.short, 't')}" readonly>
<a href="#" class="input-group-addon copy-to-clipboard-link" title="<%= l('Copy to clipboard') %>">
<span class="icon icon-clipboard"></span>
</a>
</div>
</div>
<div class="form-group">
<div class="input-group col-sm-6 col-xs-12">
<span class="form-control-static">${link(msg.real_short, '', msg.token)}</span>
</div>
</div>
</form>
</div>
</div>
<div class="row">
<form class="form col-sm-11 col-sm-offset-1" role="form" method="POST" action="${link(msg.real_short, '', msg.token, true)}">
<div class="form-group form-inline">
<select id="day-${msg.real_short}" name="delete-day" class="form-control">
% my @delays = split(',', $self->config('proposed_delays'));
% for my $delay (@delays) {
% my $text = (defined($d->{'delay_'.$delay})) ? $d->{'delay_'.$delay} : l('%1 days', $delay);
% if (config('max_delay')) {
% if ($delay) {
% if ($delay < config('max_delay')) {
'<option value="<%= $delay %>" <%== is_selected($delay) %>><%= $text %></option>',
<option value="<%= $delay %>" <%== is_selected($delay) %>><%= $text %></option>
% } elsif ($delay == config('max_delay')) {
'<option value="<%= $delay %>" <%== is_selected($delay) %>><%= $text %></option>',
<option value="<%= $delay %>" <%== is_selected($delay) %>><%= $text %></option>
% last;
% } else {
% my $text = ($delay == 1) ? l('24 hours') : l('%1 days', $delay);
'<option value="<%= config('max_delay') %>" <%== is_selected(config('max_delay')) %>><%= l('%1 days', config('max_delay')) %></option>',
<option value="<%= config('max_delay') %>" <%== is_selected(config('max_delay')) %>><%= l('%1 days', config('max_delay')) %></option>
% last;
% }
% }
% } else {
'<option value="<%= $delay %>" <%== is_selected($delay) %>><%= $text %></option>',
<option value="<%= $delay %>" <%== is_selected($delay) %>><%= $text %></option>
% }
% }
'</select>&nbsp;',
'<div class="checkbox">',
'<label>',
'<input id="first-view-', msg.real_short, '" type="checkbox" name="first-view"> <%= l('Delete at first view?') %>',
'</label>',
'</div>&nbsp;',
'<a href="#" class="btn btn-sm btn-default btn-primary modify-image" data-modlink="', link(msg.real_short, '', msg.token, true),'" data-modshort="', msg.real_short,'">',
'<%= l('Let\'s go!') %>',
'</a>',
'</div>',
'</form>',
'</div>',
'</div>'
].join('');
</select>&nbsp;
<div class="checkbox">
<label>
<input id="first-view-${msg.real_short}" type="checkbox" name="first-view"> <%= l('Delete at first view?') %>
</label>
</div>&nbsp;
<a href="#"
class="btn btn-sm btn-default btn-primary modify-image"
data-modlink="${link(msg.real_short, '', msg.token, true)}"
data-modshort="${msg.real_short}">
<%= l('Let\'s go!') %>
</a>
</div>
</form>
</div>
</div>`
} else {
return [
'<div class="alert alert-danger">',
'<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>',
'<strong><%= l('Something bad happened') %></strong><br>',
cleanName(msg.filename, false),
'<br>',
cleanName(msg.msg, false),
'</div>'
].join('');
return `<div class="alert alert-danger">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
<strong><%= l('Something bad happened') %></strong><br>
${cleanName(msg.filename, false)}
<br>
${cleanName(msg.msg, false)}
</div>`;
}
}
function bindddz(firstview, deleteday) {
@@ -188,51 +177,58 @@ function bindddz(firstview, deleteday) {
allowedTypes: 'image/*',
maxFileSize: <%= config('max_file_size') %>,
onNewFile: function(id, file){
$('.messages').append([
'<div id="', id, '-div">',
cleanName(file.name), '<br>',
'<div class="progress">',
'<div id="', id, '"class="progress-bar progress-striped active width-zero" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">',
'<span id="', id, '-text" class="pull-left pdg-left-10"> 0%</span>',
'</div>',
'</div>',
'</div>'
].join(''));
$('.messages').append(`<div id="${id}-div">
${cleanName(file.name)}<br>
<div class="progress">
<div id="${id}"
class="progress-bar progress-striped active width-zero"
role="progressbar"
aria-valuenow="0"
aria-valuemin="0"
aria-valuemax="100">
<span id="${id}-text" class="pull-left pdg-left-10"> 0%</span>
</div>
</div>
</div>`);
},
onUploadProgress: function(id, percent){
var percentStr = ' '+percent+'%';
$('#'+id).prop('aria-valuenow', percent);
$('#'+id).css('width', percent+'%');
$('#'+id+'-text').html(percentStr);
var percentStr = ` ${percent}%`;
$(`#${id}`).prop('aria-valuenow', percent);
$(`#${id}`).css('width', `${percent}%`);
$(`#${id}-text`).html(percentStr);
},
onUploadSuccess: function(id, data){
data.msg.filename = cleanName(data.msg.filename);
$('#'+id+'-div').remove();
$(`#${id}-div`).remove();
if ($('#copy-all').length === 0 && data.success) {
$('.messages').prepend([
'<div class="col-xs-12 col-sm-11 col-sm-offset-1">',
'<a id="copy-all" href="#" class="btn btn-info copy-all-to-clipboard-link">',
'<%= l('Copy all view links to clipboard') %>',
'</a>',
'</div>'
].join(''));
$('.messages').prepend(`<div class="col-xs-12 col-sm-11 col-sm-offset-1">
<a id="copy-all" href="#" class="btn btn-info copy-all-to-clipboard-link">
<%= l('Copy all view links to clipboard') %>
</a>
</div>`);
}
$('.messages').append(buildMessage(data.success, data.msg));
$('#del-'+data.msg.real_short).on('click', function(e) {
$(`#close-${data.msg.real_short}`).on('click', function(e) {
e.preventDefault();
rmFromShortHash(data.msg.short+'.'+data.msg.ext)
rmFromShortHash(`${data.msg.short}.${data.msg.ext}`);
rmFromZipHash(data.msg.short);
rmFromRandomHash(data.msg.short);
});
$('#del-'+data.msg.real_short).on('click', delImage);
$(`#del-${data.msg.real_short}`).on('click', function(e) {
e.preventDefault();
rmFromShortHash(`${data.msg.short}.${data.msg.ext}`);
rmFromZipHash(data.msg.short);
rmFromRandomHash(data.msg.short);
});
$(`#del-${data.msg.real_short}`).on('click', delImage);
if (data.success) {
addToShortHash(data.msg.short+'.'+data.msg.ext);
addToShortHash(`${data.msg.short}.${data.msg.ext}`);
addToZipHash(data.msg.short);
addToRandomHash(data.msg.short);
$('.close').unbind('click', evaluateCopyAll);
$('.close').on('click', evaluateCopyAll);
$('input[type=\'text\']').unbind("click", selectInput);
$('input[type=\'text\']').on("click", selectInput);
$('input[type="text"]').unbind("click", selectInput);
$('input[type="text"]').on("click", selectInput);
$('.copy-all-to-clipboard-link').unbind('click', copyAllToClipboard);
$('.copy-all-to-clipboard-link').on('click', copyAllToClipboard);
$('.modify-image').unbind('click', modifyImage);
@@ -272,19 +268,17 @@ function upload_url(e) {
$('.messages').append(buildMessage(data.success, data.msg));
if (data.success) {
if ($('#copy-all').length === 0) {
$('.messages').prepend([
'<div class="col-xs-12 col-sm-11 col-sm-offset-1">',
'<a id="copy-all" href="#" class="btn btn-info copy-all-to-clipboard-link">',
'<%= l('Copy all view links to clipboard') %>',
'</a>',
'</div>'
].join(''));
$('.messages').prepend(`<div class="col-xs-12 col-sm-11 col-sm-offset-1">
<a id="copy-all" href="#" class="btn btn-info copy-all-to-clipboard-link">
<%= l('Copy all view links to clipboard') %>
</a>
</div>`);
}
$('#lutim-file-url').val('');
addToShortHash(data.msg.short+'.'+data.msg.ext);
addToShortHash(`${data.msg.short}.${data.msg.ext}`);
addToZipHash(data.msg.short);
addToRandomHash(data.msg.short);
$('#del-'+data.msg.real_short).on('click', delImage);
$(`#del-${data.msg.real_short}`).on('click', delImage);
$('.close').unbind('click', evaluateCopyAll);
$('.close').on('click', evaluateCopyAll);
addItem(data.msg);
@@ -316,15 +310,18 @@ function fileUpload(file) {
fd.append('crypt', ($('#crypt').prop('checked')) ? 1 : 0);
fd.append('delete-day', ($('#delete-day').val()));
$('.messages').append([
'<div id="1-div">', cleanName(file.name), '<br>',
'<div class="progress">',
'<div id="1" class="progress-bar progress-striped active width-zero" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">',
'<span id="1-text" class="pull-left pdg-left-10"> 0%</span>',
'</div>',
'</div>',
'</div>'
].join(''));
$('.messages').append(`<div id="1-div">${cleanName(file.name)}<br>
<div class="progress">
<div id="1"
class="progress-bar progress-striped active width-zero"
role="progressbar"
aria-valuenow="0"
aria-valuemin="0"
aria-valuemax="100">
<span id="1-text" class="pull-left pdg-left-10"> 0%</span>
</div>
</div>
</div>`);
// Ajax Submit
$.ajax({
url: '<%== url_for('/') %>',
@@ -346,9 +343,9 @@ function fileUpload(file) {
percent = Math.ceil(position / total * 100);
}
var percentStr = ' '+percent+'%';
var percentStr = ` ${percent}%`;
$('#1').prop('aria-valuenow', percent);
$('#1').css('width', percent+'%');
$('#1').css('width', `${percent}%`);
$('#1-text').html(percentStr);
}, false);
}
@@ -358,13 +355,11 @@ function fileUpload(file) {
success: function (data, message, xhr){
$('#1-div').remove();
if ($('#copy-all').length === 0 && data.success) {
$('.messages').prepend([
'<div class="col-xs-12 col-sm-11 col-sm-offset-1">',
'<a id="copy-all" href="#" class="btn btn-info copy-all-to-clipboard-link">',
'<%= l('Copy all view links to clipboard') %>',
'</a>',
'</div>'
].join(''));
$('.messages').prepend(`<div class="col-xs-12 col-sm-11 col-sm-offset-1">
<a id="copy-all" href="#" class="btn btn-info copy-all-to-clipboard-link">
<%= l('Copy all view links to clipboard') %>
</a>
</div>`);
}
data.msg.filename = cleanName(data.msg.filename);
$('.messages').append(buildMessage(data.success, data.msg));

View File

@@ -57,7 +57,7 @@ function onCheck(e) {
var short = $(this).data('short');
var ext = $(this).data('ext');
if ($(this).is(':checked')) {
addToShortHash(short+'.'+ext);
addToShortHash(`${short}.${ext}`);
addToZipHash(short);
addToRandomHash(short);
if (!$('#check-all').is(':checked') && isAllChecked()) {
@@ -67,7 +67,7 @@ function onCheck(e) {
if ($('#check-all').is(':checked')) {
$('#check-all').prop('checked', false);
}
rmFromShortHash(short+'.'+ext);
rmFromShortHash(`${short}.${ext}`);
rmFromZipHash(short);
rmFromRandomHash(short);
}
@@ -117,11 +117,11 @@ function modifyDelay() {
},
success: function(data) {
updateItem(short, parseInt(limit), del_at_view);
var newLimit = (parseInt(limit, 10) === 0) ? '<%= l('No limit') %>' : moment.unix(limit * 86400 + created_at).locale(window.navigator.language).format('LLLL');
$('#limit-'+short).html(newLimit);
$('#del_at_view-'+short).html(delView(del_at_view));
$('#edit-'+short).data('moddelay', limit);
$('#edit-'+short).data('modfirstview', del_at_view);
var newLimit = (parseInt(limit, 10) === 0) ? '<%= l('No limit') %>' : formatDate(limit * 86400 + created_at);
$(`#limit-${short}`).html(newLimit);
$(`#del_at_view-${short}`).html(delView(del_at_view));
$(`#edit-${short}`).data('moddelay', limit);
$(`#edit-${short}`).data('modfirstview', del_at_view);
goodToast(data.msg);
},
error: function() {
@@ -157,35 +157,62 @@ function populateFilesTable() {
$('#myfiles').empty();
files.forEach(function(element, index, array) {
var real_short = element.real_short;
var vlink = link(element.short+'.'+element.ext, '');
var vlink = link(`${element.short}.${element.ext}`, '');
var dlink = link(real_short, 'dl', element.token, false, true);
var limit = (element.limit === 0) ? '<%= l('No limit') %>' : moment.unix(element.limit * 86400 + element.created_at).locale(window.navigator.language).format('LLLL');
var created_at = moment.unix(element.created_at).locale(window.navigator.language).format('LLLL');
var limit = (element.limit === 0) ? '<%= l('No limit') %>' : formatDate(element.limit * 86400 + element.created_at);
var created_at = formatDate(element.created_at);
var name = element.filename.replace(/</g, '&lt;').replace(/>/g, '&gt;');
var tr = [
'<tr id="alert-',real_short,'">',
'<td><span class="checkbox"><label><input type="checkbox" class="ckbx" data-short="', element.short, '" data-ext="', element.ext, '"><label></span></td>',
'<td class="ellips"><span title="', name,'">', name, '</span></td>',
'<td class="text-center">',
'<a href="',vlink,'" target="_blank"><i class="icon icon-eye"></i></a>',
'&nbsp;<a href="',vlink,'" class="copy-to-clipboard" title="<%= l('Copy to clipboard') %>"><i class="icon icon-clipboard"></i></a>',
'</td>',
'<td id="count-',real_short,'" class="text-center"></td>',
'<td id="del_at_view-', real_short, '" class="text-center">', delView(element.del_at_view), '</td>',
'<td>', created_at, '</td>',
'<td>',
'<span id="limit-', element.real_short, '">', limit, '</span>',
'&nbsp;<a href="#" id="edit-', element.real_short, '" data-modlink="', link(element.real_short, '', element.token, true), '" data-modshort="', element.real_short, '" data-modfirstview="', element.del_at_view, '" data-moddelay="', element.limit, '" data-modcreated_at="', element.created_at, '" data-modname="', name, '" title="<%= l('Modify expiration delay') %>">',
'<i class="icon icon-edit"></i>',
'</a>',
'</td>',
'<td class="text-center"><a id="del-',real_short,'" data-short="',real_short,'" data-token="',element.token,'" href="#" class="remove-link"><i class="icon icon-trash"></i></a></td>',
'</tr>'
].join('');
var tr = `<tr id="alert-${real_short}">',
<td>
<span class="checkbox">
<label>
<input type="checkbox"
class="ckbx"
data-short="${element.short}"
data-ext="${element.ext}">
</label>
</span>
</td>
<td class="ellips"><span title="${name}">${name}</span></td>
<td class="text-center">
<a href="${vlink}" target="_blank"><i class="icon icon-eye"></i></a>
&nbsp;<a href="${vlink}"
class="copy-to-clipboard"
title="<%= l('Copy to clipboard') %>">
<i class="icon icon-clipboard"></i>
</a>
</td>
<td id="count-${real_short}" class="text-center"></td>
<td id="del_at_view-${real_short}" class="text-center">${delView(element.del_at_view)}</td>
<td>${created_at}</td>
<td>
<span id="limit-${element.real_short}">${limit}</span>
&nbsp;<a href="#"
id="edit-${element.real_short}"
data-modlink="${link(element.real_short, '', element.token, true)}"
data-modshort="${element.real_short}"
data-modfirstview="${element.del_at_view}"
data-moddelay="${element.limit}"
data-modcreated_at="${element.created_at}"
data-modname="${name}"
title="<%= l('Modify expiration delay') %>">
<i class="icon icon-edit"></i>
</a>
</td>
<td class="text-center">
<a id="del-${real_short}"
data-short="${real_short}"
data-token="${element.token}"
href="#"
class="remove-link">
<i class="icon icon-trash"></i>
</a>
</td>
</tr>`;
$('#myfiles').append(tr);
$('#del-'+real_short).on('click', delImage);
$('#edit-'+real_short).on('click', editImage);
$(`#del-${real_short}`).on('click', delImage);
$(`#edit-${real_short}`).on('click', editImage);
$.ajax({
url : '<%== url_for('counter') %>',
@@ -197,17 +224,17 @@ function populateFilesTable() {
success: function(data) {
if (data.success) {
if (data.enabled) {
$('#count-'+real_short).text(data.counter);
$(`#count-${real_short}`).text(data.counter);
} else {
delItem(real_short);
$('#alert-'+real_short).remove();
$(`#alert-${real_short}`).remove();
}
} else {
badToast(element.filename+' '+data.msg);
badToast(`${element.filename} ${data.msg}`);
}
},
error: function() {
badToast(element.filename+'<%= l(': Error while trying to get the counter.') %>');
badToast(`${element.filename}<%= l(': Error while trying to get the counter.') %>`);
}
});
});

View File

@@ -63,7 +63,7 @@
<%= link_to url_for('about') => begin %><%= l('About') %><% end %>
</li>
<li>
<%= link_to 'https://framagit.org/luc/lutim' => begin %><%= l('Fork me!') %><% end %>
<%= link_to 'https://framagit.org/fiat-tux/hat-softwares/lutim' => begin %><%= l('Fork me!') %><% end %>
</li>
</ul>
</li>

View File

@@ -0,0 +1,23 @@
% # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab:
% my $abs = url_for('/'.$short)->to_abs();
<!DOCTYPE html>
<html>
<head>
<title>Lutim</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="utf-8" />
<link rel="icon" type="image/png" href="<%= url_for('/img/favicon.png')->to_abs() %>">
<meta property="og:title" content="Lutim" />
<meta property="og:type" content="website" />
<meta property="og:url" content="<%= $abs %>?t" />
<meta property="og:description" content="Image shared with Lutim" />
<meta property="og:image" content="<%= $abs %>" />
<meta property="og:image:url" content="<%= $abs %>" />
<meta property="og:image:type" content="<%= $mimetype %>" />
</head>
<body>
<img src="<%= $abs %>" alt="">
</body>
</html>

View File

@@ -1,41 +0,0 @@
% # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab:
% my $g = ($mimetype eq 'image/gif') ? 1 : 0;
% my $abs = url_for('/'.$short)->to_abs();
<!DOCTYPE html>
<html>
<head>
<title>Lutim</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="utf-8" />
<link rel="icon" type="image/png" href="<%= url_for('/img/favicon.png')->to_abs() %>">
%= stylesheet '/css/twitter.min.css'
<meta property="og:title" content="Lutim" />
<meta property="og:type" content="website" />
<meta property="og:url" content="<%= $abs %>?t" />
<meta property="og:image" content="<%= $abs %>" />
<meta property="og:image:url" content="<%= $abs %>" />
<meta property="og:image:type" content="<%= $mimetype %>" />
% if (config('tweet_card_via')) {
<meta name="twitter:site" content="<%= config('tweet_card_via') %>">
% }
<meta name="twitter:image:src" content="<%= $abs %>">
% if ($g) {
<meta name="twitter:card" content="player">
<meta name="twitter:image" content="<%= $abs %>">
<meta name="twitter:player" content="<%= $abs %>?t">
<meta name="twitter:title" content="<%= $filename %>">
<meta name="twitter:player:width" content="<%= $width %>">
<meta name="twitter:player:height" content="<%= $height %>">
%= javascript '/js/jquery-3.2.1.min.js'
%= javascript '/js/freezeframe.min.js'
%= javascript '/js/twitter.js'
% } else {
<meta name="twitter:card" content="photo">
% }
</head>
<body<%== ($g) ? '' : ' class="height-97"' %>>
<img class="freezeframe" src="<%= $abs %><%= '.gif' if ($g) %>" alt="<%= $filename %>">
</body>
</html>

View File

@@ -70,7 +70,6 @@
% # Display image informations
<h4>
<a href="<%= $url.'.'.stash('ext') %>" target="_blank"><%= stash('filename') %></a>
&nbsp;&nbsp;&nbsp;<a title="<%= l('Tweet it!') %>" target="_blank" href="https://twitter.com/share?url=<%= $url %>?t"><span class="icon icon-twitter"></span></a>
</h4>
% my $delete_url = url_for('delete', {short => stash('real_short'), token => stash('token')})->to_abs();
<form class="form">

View File

@@ -1,9 +1,6 @@
% # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab:
% use Mojo::Util qw(url_escape);
% my $twitter_url = 'https://twitter.com/share';
% my $url = url_for('/')->to_abs();
% $twitter_url .= '?url='.url_escape("$url")
% .'&text=Check out this %23Lutim instance! ';
<!DOCTYPE html>
<html>
<head>
@@ -33,7 +30,7 @@
%= stylesheet '/css/korrigan.css'
</head>
<body class="<%== current_route %>">
%= include 'partial/navbar', twitter_url => $twitter_url
%= include 'partial/navbar'
<div class="container">
<div class="upload-info"><!-- Warning: ends in index.html.ep -->
<div>
@@ -64,7 +61,7 @@
% if (defined(config('piwik_img'))) {
<img src="<%== config('piwik_img') %>" class="border-zero" alt="">
% }
%= javascript '/js/jquery-3.2.1.min.js'
%= javascript '/js/jquery-3.7.1.min.js'
%= javascript '/partial/manifest.js'
%= javascript '/js/toastify.js'
%= javascript '/js/bootstrap.min.js'
@@ -81,6 +78,7 @@
% }
% if (current_route 'index') {
%= javascript '/partial/lutim.js'
%= javascript '/partial/korrigan.js'
% }
% if (current_route 'gallery') {
%= javascript '/js/photoswipe.min.js'
@@ -89,9 +87,9 @@
%= javascript '/js/FileSaver.min.js'
%= javascript '/partial/gallery.js'
%= javascript '/partial/lutim.js'
%= javascript '/partial/korrigan.js'
% }
% if (current_route 'myfiles') {
%= javascript '/js/moment-with-locales.min.js'
%= javascript '/partial/myfiles.js'
% }
</body>

View File

@@ -0,0 +1,131 @@
% # vim:set sw=4 ts=4 sts=4 ft=javascript expandtab:
function buildMessage(success, msg) {
if(success) {
var s_url = link([msg.short, '.', msg.ext].join(''), '');
var thumb = (msg.thumb !== null) ? `<div class="preview">
<a href="${s_url}" target="_blank">
<img class="thumbnail img-responsive" alt="${cleanName(msg.filename, true)} thumbnail" src="${msg.thumb}">
</a>
</div>` : '';
return `<div class="alert alert-success" id="alert-${msg.real_short}">
<button id="close-${msg.real_short}" type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
<div>${thumb}
<div>
<h4>
<a href="${s_url}" target="_blank">
${msg.filename}
</a>
</h4>
<form class="form">
<div class="form-group">
<label class="sr-only" for="view${msg.real_short}"><%= l('View link') %></label>
<div class="input-group input-group-sm">
<div class="input-group-btn adjust-addon">
<a href="${s_url}" target="_blank" class="btn btn-default">
<span class="icon icon-eye" title=" <%= l('View link') %>"></span>
</a>
</div>
<input type="text" class="form-control view-link-input" id="view${msg.real_short}" value="${s_url}" readonly>
<a href="#" class="input-group-addon copy-to-clipboard-link" title="<%= l('Copy to clipboard') %>">
<span class="icon icon-clipboard"></span>
</a>
</div>
</div>
<div class="form-group">
<label class="sr-only" for="markdown${msg.real_short}"><%= l('Markdown syntax') %></label>
<div class="input-group input-group-sm">
<div class="input-group-addon adjust-addon">
<span class="markdown-mark-solid" title="<%= l('Markdown syntax') %>"></span>
</div>
<input type="text" class="form-control" id="markdown${msg.real_short}" value="![](${link(msg.short, '')})" readonly>
<a href="#" class="input-group-addon copy-to-clipboard-link" title="<%= l('Copy to clipboard') %>">
<span class="icon icon-clipboard"></span>
</a>
</div>
</div>
<div class="form-group">
<label class="sr-only" for="download${msg.real_short}"><%= l('Download link') %></label>
<div class="input-group input-group-sm">
<div class="input-group-btn adjust-addon">
<a href="${link(msg.short, 'dl')}" class="btn btn-default">
<span class="icon icon-download" title="<%= l('Download link') %>"></span>
</a>
</div>
<input type="text" class="form-control" id="download${msg.real_short}" value="${link(msg.short, 'dl')}" readonly>
<a href="#" class="input-group-addon copy-to-clipboard-link" title="<%= l('Copy to clipboard') %>">
<span class="icon icon-clipboard"></span>
</a>
</div>
</div>
<div class="form-group">
<label class="sr-only" for="share${msg.real_short}"><%= l('Link for share on social networks') %></label>
<div class="input-group input-group-sm">
<div class="input-group-btn adjust-addon">
<a href="${link(msg.short, 't')}" target="_blank" class="btn btn-default">
<span class="icon icon-share" title="<%= l('Link for share on social networks') %>"></span>
</a>
</div>
<input type="text" class="form-control" id="share${msg.real_short}" value="${link(msg.short, 't')}" readonly>
<a href="#" class="input-group-addon copy-to-clipboard-link" title="<%= l('Copy to clipboard') %>">
<span class="icon icon-clipboard"></span>
</a>
</div>
</div>
<div class="form-group">
<div class="input-group">
<span class="form-control-static">${link(msg.real_short, '', msg.token)}</span>
</div>
</div>
</form>
</div>
</div>
<div class="row">
<form class="form col-sm-11 col-sm-offset-1" role="form" method="POST" action="${link(msg.real_short, '', msg.token, true)}">
<div class="form-group form-inline">
<select id="day-${msg.real_short}" name="delete-day" class="form-control">
% my @delays = split(',', $self->config('proposed_delays'));
% for my $delay (@delays) {
% my $text = (defined($d->{'delay_'.$delay})) ? $d->{'delay_'.$delay} : l('%1 days', $delay);
% if (config('max_delay')) {
% if ($delay) {
% if ($delay < config('max_delay')) {
<option value="<%= $delay %>" <%== is_selected($delay) %>><%= $text %></option>
% } elsif ($delay == config('max_delay')) {
<option value="<%= $delay %>" <%== is_selected($delay) %>><%= $text %></option>
% last;
% } else {
% my $text = ($delay == 1) ? l('24 hours') : l('%1 days', $delay);
<option value="<%= config('max_delay') %>" <%== is_selected(config('max_delay')) %>><%= l('%1 days', config('max_delay')) %></option>
% last;
% }
% }
% } else {
<option value="<%= $delay %>" <%== is_selected($delay) %>><%= $text %></option>
% }
% }
</select>&nbsp;
<div class="checkbox">
<label>
<input id="first-view-${msg.real_short}" type="checkbox" name="first-view"> <%= l('Delete at first view?') %>
</label>
</div>&nbsp;
<a href="#"
class="btn btn-sm btn-default btn-primary modify-image"
data-modlink="${link(msg.real_short, '', msg.token, true)}"
data-modshort="${msg.real_short}">
<%= l('Let\'s go!') %>
</a>
</div>
</form>
</div>
</div>`
} else {
return `<div class="alert alert-danger">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
<strong><%= l('Something bad happened') %></strong><br>
${cleanName(msg.filename, false)}
<br>
${cleanName(msg.msg, false)}
</div>`;
}
}

View File

@@ -1,494 +0,0 @@
% # vim:set sw=4 ts=4 sts=4 ft=javascript expandtab:
function selectInput() {
$(this).select();
}
function cleanName(name, empty) {
if (typeof(name) === 'undefined') {
return name;
}
if (empty !== undefined && empty !== null && empty) {
return name.replace(/&(l|g)t;/g, '').replace(/"/g, '\'');
} else {
return name.replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
}
function tw_url(url) {
return btn = [
'<a title="<%= l('Tweet it!') %>" target="_blank" href="https://twitter.com/share?url=<%== url_for('/')->to_abs() %>', url, '?t" class="btn btn-default">',
'<span class="icon icon-twitter"></span>',
'</a>'
].join('');
}
function modifyImage(e) {
e.preventDefault();
var url = $(this).data('modlink');
var short = $(this).data('modshort');
modify(url, short);
}
function modify(url, short) {
var limit = $('#day-'+short).val();
var del_at_view = ($('#first-view-'+short).prop('checked')) ? 1 : 0;
$.ajax({
url : url,
type : 'POST',
data : {
'image_url' : '<%== url_for('/')->to_abs() %>'+short,
'format' : 'json',
'delete-day' : limit,
'first-view' : del_at_view
},
success: function(data) {
updateItem(short, limit, del_at_view);
goodToast(data.msg);
},
error: function() {
badToast('<%= l('Error while trying to modify the image.') %>');
}
});
}
function buildMessage(success, msg) {
if(success) {
var s_url = link([msg.short, '.', msg.ext].join(''), '');
var thumb = (msg.thumb !== null) ? [
'<div class="preview">',
'<a href="', s_url, '" target="_blank">',
'<img class="thumbnail img-responsive" alt="', cleanName(msg.filename, true), ' thumbnail" src="', msg.thumb, '">',
'</a>',
'</div>'
].join('') : ''
return [
'<div class="alert alert-success" id="alert-', msg.real_short, '">',
'<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>',
'<div>', thumb,
'<div>',
'<h4>',
'<a href="', s_url, '" target="_blank">',
msg.filename,
'</a>',
'</h4>',
'<form class="form">',
'<div class="form-group">',
'<label class="sr-only" for="view', msg.real_short, '"><%= l('View link') %></label>',
'<div class="input-group input-group-sm">',
'<div class="input-group-btn adjust-addon">',
'<a href="', s_url, '" target="_blank" class="btn btn-default">',
'<span class="icon icon-eye" title=" <%= l('View link') %>"></span>',
'</a>',
'</div>',
'<input type="text" class="form-control view-link-input" id="view', msg.real_short, '" value="', s_url, '" readonly>',
'<a href="#" class="input-group-addon copy-to-clipboard-link" title="<%= l('Copy to clipboard') %>">',
'<span class="icon icon-clipboard"></span>',
'</a>',
'</div>',
'</div>',
'<div class="form-group">',
'<label class="sr-only" for="markdown', msg.real_short, '"><%= l('Markdown syntax') %></label>',
'<div class="input-group input-group-sm">',
'<div class="input-group-addon adjust-addon">',
'<span class="markdown-mark-solid" title="<%= l('Markdown syntax') %>"></span>',
'</div>',
'<input type="text" class="form-control" id="markdown', msg.real_short, '" value="![](', link(msg.short, ''), ')" readonly>',
'<a href="#" class="input-group-addon copy-to-clipboard-link" title="<%= l('Copy to clipboard') %>">',
'<span class="icon icon-clipboard"></span>',
'</a>',
'</div>',
'</div>',
'<div class="form-group">',
'<label class="sr-only" for="download', msg.real_short, '"><%= l('Download link') %></label>',
'<div class="input-group input-group-sm">',
'<div class="input-group-btn adjust-addon">',
'<a href="', link(msg.short, 'dl'), '" class="btn btn-default">',
'<span class="icon icon-download" title="<%= l('Download link') %>"></span>',
'</a>',
'</div>',
'<input type="text" class="form-control" id="download', msg.real_short, '" value="', link(msg.short, 'dl'), '" readonly>',
'<a href="#" class="input-group-addon copy-to-clipboard-link" title="<%= l('Copy to clipboard') %>">',
'<span class="icon icon-clipboard"></span>',
'</a>',
'</div>',
'</div>',
'<div class="form-group">',
'<label class="sr-only" for="share', msg.real_short, '"><%= l('Link for share on social networks') %></label>',
'<div class="input-group input-group-sm">',
'<div class="input-group-btn adjust-addon">',
'<a href="', link(msg.short, 't'), '" target="_blank" class="btn btn-default">',
'<span class="icon icon-share" title="<%= l('Link for share on social networks') %>"></span>',
'</a>',
tw_url(msg.short),
'</div>',
'<input type="text" class="form-control" id="share', msg.real_short, '" value="', link(msg.short, 't'), '" readonly>',
'<a href="#" class="input-group-addon copy-to-clipboard-link" title="<%= l('Copy to clipboard') %>">',
'<span class="icon icon-clipboard"></span>',
'</a>',
'</div>',
'</div>',
'<div class="form-group">',
'<div class="input-group">',
'<span class="form-control-static">', link(msg.real_short, '', msg.token), '</span>',
'</div>',
'</div>',
'</form>',
'</div>',
'</div>',
'<div class="row">',
'<form class="form col-sm-11 col-sm-offset-1" role="form" method="POST" action="', link(msg.real_short, '', msg.token, true), '">',
'<div class="form-group form-inline">',
'<select id="day-', msg.real_short, '" name="delete-day" class="form-control">',
% my @delays = split(',', $self->config('proposed_delays'));
% for my $delay (@delays) {
% my $text = (defined($d->{'delay_'.$delay})) ? $d->{'delay_'.$delay} : l('%1 days', $delay);
% if (config('max_delay')) {
% if ($delay) {
% if ($delay < config('max_delay')) {
'<option value="<%= $delay %>" <%== is_selected($delay) %>><%= $text %></option>',
% } elsif ($delay == config('max_delay')) {
'<option value="<%= $delay %>" <%== is_selected($delay) %>><%= $text %></option>',
% last;
% } else {
% my $text = ($delay == 1) ? l('24 hours') : l('%1 days', $delay);
'<option value="<%= config('max_delay') %>" <%== is_selected(config('max_delay')) %>><%= l('%1 days', config('max_delay')) %></option>',
% last;
% }
% }
% } else {
'<option value="<%= $delay %>" <%== is_selected($delay) %>><%= $text %></option>',
% }
% }
'</select>&nbsp;',
'<div class="checkbox">',
'<label>',
'<input id="first-view-', msg.real_short, '" type="checkbox" name="first-view"> <%= l('Delete at first view?') %>',
'</label>',
'</div>&nbsp;',
'<a href="#" class="btn btn-sm btn-default btn-primary modify-image" data-modlink="', link(msg.real_short, '', msg.token, true),'" data-modshort="', msg.real_short,'">',
'<%= l('Let\'s go!') %>',
'</a>',
'</div>',
'</form>',
'</div>',
'</div>'
].join('');
} else {
return [
'<div class="alert alert-danger">',
'<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>',
'<strong><%= l('Something bad happened') %></strong><br>',
cleanName(msg.filename, false),
'<br>',
cleanName(msg.msg, false),
'</div>'
].join('');
}
}
function bindddz(firstview, deleteday) {
$('#drag-and-drop-zone').dmUploader({
url: '<%== url_for('/') %>',
dataType: 'json',
allowedTypes: 'image/*',
maxFileSize: <%= config('max_file_size') %>,
onNewFile: function(id, file){
$('.messages').append([
'<div id="', id, '-div">',
cleanName(file.name), '<br>',
'<div class="progress">',
'<div id="', id, '"class="progress-bar progress-striped active width-zero" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">',
'<span id="', id, '-text" class="pull-left pdg-left-10"> 0%</span>',
'</div>',
'</div>',
'</div>'
].join(''));
},
onUploadProgress: function(id, percent){
var percentStr = ' '+percent+'%';
$('#'+id).prop('aria-valuenow', percent);
$('#'+id).css('width', percent+'%');
$('#'+id+'-text').html(percentStr);
},
onUploadSuccess: function(id, data){
data.msg.filename = cleanName(data.msg.filename);
$('#'+id+'-div').remove();
if ($('#copy-all').length === 0 && data.success) {
$('.messages').prepend([
'<div class="col-xs-12 col-sm-11 col-sm-offset-1">',
'<a id="copy-all" href="#" class="btn btn-info copy-all-to-clipboard-link">',
'<%= l('Copy all view links to clipboard') %>',
'</a>',
'</div>'
].join(''));
}
$('.messages').append(buildMessage(data.success, data.msg));
$('#del-'+data.msg.real_short).on('click', function(e) {
e.preventDefault();
rmFromShortHash(data.msg.short+'.'+data.msg.ext)
rmFromZipHash(data.msg.short);
rmFromRandomHash(data.msg.short);
});
$('#del-'+data.msg.real_short).on('click', delImage);
if (data.success) {
addToShortHash(data.msg.short+'.'+data.msg.ext);
addToZipHash(data.msg.short);
addToRandomHash(data.msg.short);
$('.close').unbind('click', evaluateCopyAll);
$('.close').on('click', evaluateCopyAll);
$('input[type=\'text\']').unbind("click", selectInput);
$('input[type=\'text\']').on("click", selectInput);
$('.copy-all-to-clipboard-link').unbind('click', copyAllToClipboard);
$('.copy-all-to-clipboard-link').on('click', copyAllToClipboard);
$('.modify-image').unbind('click', modifyImage);
$('.modify-image').on('click', modifyImage);
$('.copy-to-clipboard-link').unbind('click', clickOnCopyLink);
$('.copy-to-clipboard-link').on('click', clickOnCopyLink);
addItem(data.msg);
}
},
onUploadError: function(id, message){
$('.messages').append(buildMessage(false, ''));
},
onFileSizeError: function(file){
$('.messages').append(buildMessage(false, { filename: file.name, msg: '<%= l('The file exceed the size limit (%1)', config('max_file_size')) %>'}));
}
});
}
function upload_url(e) {
e.preventDefault();
var val = $('#lutim-file-url').val();
if (val !== undefined && val !== '') {
$('#lutim-file-url').prop('disabled', 'disabled');
$('.hidden-spin').css('display', 'block');
$.ajax({
url : '<%== url_for('/') %>',
type : 'POST',
data : {
'lutim-file-url' : val,
'format' : 'json',
'first-view' : ($('#first-view').prop('checked')) ? 1 : 0,
'crypt' : ($('#crypt').prop('checked')) ? 1 : 0,
'delete-day' : $('#delete-day').val()
},
success: function(data) {
data.msg.filename = cleanName(data.msg.filename);
$('.messages').append(buildMessage(data.success, data.msg));
if (data.success) {
if ($('#copy-all').length === 0) {
$('.messages').prepend([
'<div class="col-xs-12 col-sm-11 col-sm-offset-1">',
'<a id="copy-all" href="#" class="btn btn-info copy-all-to-clipboard-link">',
'<%= l('Copy all view links to clipboard') %>',
'</a>',
'</div>'
].join(''));
}
$('#lutim-file-url').val('');
addToShortHash(data.msg.short+'.'+data.msg.ext);
addToZipHash(data.msg.short);
addToRandomHash(data.msg.short);
$('#del-'+data.msg.real_short).on('click', delImage);
$('.close').unbind('click', evaluateCopyAll);
$('.close').on('click', evaluateCopyAll);
addItem(data.msg);
}
},
error: function() {
$('.messages').append(buildMessage(false, ''));
},
complete: function() {
$('#lutim-file-url').prop('disabled', '');
$('.hidden-spin').css('display', 'none');
$('.copy-all-to-clipboard-link').unbind('click', copyAllToClipboard);
$('.copy-all-to-clipboard-link').on('click', copyAllToClipboard);
$('.modify-image').unbind('click', modifyImage);
$('.modify-image').on('click', modifyImage);
$('.copy-to-clipboard-link').unbind('click', clickOnCopyLink);
$('.copy-to-clipboard-link').on('click', clickOnCopyLink);
}
});
}
}
function fileUpload(file) {
var fd = new FormData();
fd.append('file', file);
fd.append('format', 'json');
fd.append('first-view', ($('#first-view').prop('checked')) ? 1 : 0);
fd.append('crypt', ($('#crypt').prop('checked')) ? 1 : 0);
fd.append('delete-day', ($('#delete-day').val()));
$('.messages').append([
'<div id="1-div">', cleanName(file.name), '<br>',
'<div class="progress">',
'<div id="1" class="progress-bar progress-striped active width-zero" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">',
'<span id="1-text" class="pull-left pdg-left-10"> 0%</span>',
'</div>',
'</div>',
'</div>'
].join(''));
// Ajax Submit
$.ajax({
url: '<%== url_for('/') %>',
type: 'POST',
dataType: 'json',
data: fd,
cache: false,
contentType: false,
processData: false,
forceSync: false,
xhr: function(){
var xhrobj = $.ajaxSettings.xhr();
if(xhrobj.upload){
xhrobj.upload.addEventListener('progress', function(event) {
var percent = 0;
var position = event.loaded || event.position;
var total = event.total || e.totalSize;
if(event.lengthComputable){
percent = Math.ceil(position / total * 100);
}
var percentStr = ' '+percent+'%';
$('#1').prop('aria-valuenow', percent);
$('#1').css('width', percent+'%');
$('#1-text').html(percentStr);
}, false);
}
return xhrobj;
},
success: function (data, message, xhr){
$('#1-div').remove();
if ($('#copy-all').length === 0 && data.success) {
$('.messages').prepend([
'<div class="col-xs-12 col-sm-11 col-sm-offset-1">',
'<a id="copy-all" href="#" class="btn btn-info copy-all-to-clipboard-link">',
'<%= l('Copy all view links to clipboard') %>',
'</a>',
'</div>'
].join(''));
}
data.msg.filename = cleanName(data.msg.filename);
$('.messages').append(buildMessage(data.success, data.msg));
if (data.success) {
$('.close').unbind('click', evaluateCopyAll);
$('.close').on('click', evaluateCopyAll);
addItem(data.msg);
}
},
error: function (xhr, status, errMsg){
$('.messages').append(buildMessage(false, ''));
},
complete: function () {
$('.copy-all-to-clipboard-link').unbind('click', copyAllToClipboard);
$('.copy-all-to-clipboard-link').on('click', copyAllToClipboard);
$('.modify-image').unbind('click', modifyImage);
$('.modify-image').on('click', modifyImage);
$('.copy-to-clipboard-link').unbind('click', clickOnCopyLink);
$('.copy-to-clipboard-link').on('click', clickOnCopyLink);
}
});
}
function initPaste() {
/*
actually FF and Chrome doesn't handle paste events the same way...
for ff we need to create a editable div and register an event to it.
When user paste, the image is "really" pasted in the div. Then, we need to iterate throught
the div childs to get images. Previsouly FF didn't have the paste event so it was esay to figure on wich browser we were.
But firefox now have a paste event so I test it...
on Chrome the file object is directlyt in the clipboard.
*/
var b = 'FF';
try {
//FF
var cbe = new ClipboardEvent('hop');
} catch(hop) {
//under webkkit Clipboard doesn't have arguments...
b = 'WK'
}
if (b === 'FF') {
var pasteDiv = document.createElement('div');
pasteDiv.addEventListener('paste', onPasteFF);
pasteDiv.setAttribute('class', 'pasteZone');
pasteDiv.setAttribute('contenteditable', true);
document.getElementsByTagName('body')[0].appendChild(pasteDiv);
pasteDiv.focus();
document.addEventListener('click', function(event) {
var t = $(event.target);
switch (t[0].nodeName.toUpperCase()) {
case 'A':
case 'BUTTON':
case 'INPUT':
case 'SELECT':
case 'SPAN':
case 'LABEL':
break;
default:
if (t[0].parentNode.nodeName.toUpperCase() !== 'SELECT') {
pasteDiv.focus();
}
}
});
} else {
document.addEventListener('paste', onPaste);
}
}
function waitforpastedata(elem, savedcontent) {
if (elem.childNodes && elem.childNodes.length > 0) {
processpaste(elem, savedcontent);
} else {
var that = {
e: elem,
s: savedcontent
};
that.callself = function () {
waitforpastedata(that.e, that.s);
}
setTimeout(that.callself, 20);
}
}
function processpaste(elem, savedcontent) {
var pasteZone = document.getElementsByClassName('pasteZone')[0];
var f = new Image();
f.onload = function(){
var canvas = document.createElement('canvas');
canvas.width = f.width;
canvas.height = f.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(f, 0, 0, canvas.width, canvas.height);
canvas.toBlob(function(blob) {
var url = window.URL.createObjectURL(blob);
fileUpload(blob);
});
}
f.src = pasteZone.childNodes[0].src;
pasteZone.innerHTML = '';
}
function onPasteFF(e) {
var pasteZone = document.getElementsByClassName('pasteZone')[0];
waitforpastedata(pasteZone, 'savedcontent');
}
function onPaste(e) {
var items = e.clipboardData.items;
for(var i = 0; i < items.length; i++) {
var item = items[i];
if (/image/.test(item.type)) {
var file = item.getAsFile();
fileUpload(file);
} else {
//not image..
}
}
}

View File

@@ -39,7 +39,7 @@ $d->dir( $ENV{'SHUTTER_INTL'} );
my %upload_plugin_info = (
'module' => "Lutim",
'url' => "https://lut.im/",
'url' => "https://lutim.fiat-tux.fr/",
'registration' => "-",
'name' => "Lutim",
'description' => "Upload screenshots to Lutim",
@@ -102,7 +102,7 @@ sub upload {
#upload the file
eval{
my $url = 'https://lut.im/';
my $url = 'https://lutim.fiat-tux.fr/';
my $request = HTTP::Request::Common::POST(
$url,
Content_Type => 'multipart/form-data',

View File

@@ -1,6 +1,6 @@
[Unit]
Description=Image hosting and sharing service job queue
Documentation=https://framagit.org/luc/lutim
Documentation=https://framagit.org/fiat-tux/hat-softwares/lutim
After=lutim.service
[Service]

View File

@@ -1,6 +1,6 @@
[Unit]
Description=Image hosting and sharing service
Documentation=https://framagit.org/luc/lutim
Documentation=https://framagit.org/fiat-tux/hat-softwares/lutim
Requires=network.target
After=network.target
#Requires=postgresql.service