File: //usr/share/usermin/authentic-theme/extensions/file-manager/list-images.cgi
#!/usr/bin/perl
#
# Authentic Theme (https://github.com/authentic-theme/authentic-theme)
# Copyright Ilia Rostovtsev <ilia@virtualmin.com>
# Licensed under MIT (https://github.com/authentic-theme/authentic-theme/blob/master/LICENSE)
#
use strict;
our (
%in, %text, %gconfig,
%request_uri, %userconfig, @allowed_paths,
@remote_user_info, $cwd, $path
);
require($ENV{'THEME_ROOT'}."/extensions/file-manager/file-manager-lib.pl");
kill_previous($0, $$);
my %data;
my @list;
my @items;
my $auto_orient = " -auto-orient";
my $width = int($in{'width'});
my $height = int($in{'height'});
my $file_requested = $in{'file_requested'};
my $files_all = $in{'files_all'};
my $files_selected = $in{'files_selected'};
my $search = $in{'query'};
# Use only current file or read list of files
if (!$files_all) {
@list = ($file_requested);
if ($files_selected) {
@list = split(/\0/, $in{'name'});
}
}
else {
if ($search) {
@list = exec_search();
}
else {
unless (opendir(DIR, $cwd)) {
print_error(
"$text{'theme_xhred_global_error'}: [tt]`$cwd`[/tt]- $!."
);
exit;
}
@list = grep { $_ ne '.' && $_ ne '..' } readdir(DIR);
closedir(DIR);
}
my $page = 1;
my $pagelimit = 4294967295;
my $pages = 0;
my $max_allowed = int($userconfig{'max_allowed'});
if ($max_allowed !~ /^[0-9,.E]+$/ || $max_allowed < 100 || $max_allowed > 10000)
{
$max_allowed = 1000;
}
my $totals = scalar(@list);
my $totals_spliced = $totals;
my $tuconfig_per_page =
get_user_config('config_portable_module_filemanager_records_per_page');
if (server_pagination_enabled($totals, $max_allowed, $search)) {
$page = int($in{'page'}) || 1;
$pagelimit = int($in{'paginate'}) || int($tuconfig_per_page) || 30;
$pages = ceil(($totals) / $pagelimit);
if ($page > $pages) {
$page = $pages;
$in{'page'} = $page;
}
my $splice_start = $pagelimit * ($page - 1);
my $splice_end = $pagelimit;
@list = sort { $a cmp $b } @list;
@list = splice(@list, $splice_start, $splice_end);
$totals_spliced = scalar(@list);
}
}
# Leave only allowed
if (test_allowed_paths()) {
my @tmp_list;
for $path (@allowed_paths) {
my $slashed = $path;
$slashed .= "/" if ($slashed !~ /\/$/);
push @tmp_list, grep {
string_starts_with($slashed, "$cwd/$_/") ||
index($slashed, $_) != -1 ||
index("$cwd/$_", $slashed) != -1
} @list;
}
my %hash = map { $_, 1 } @tmp_list;
@list = keys %hash;
}
my @checked_list;
foreach my $filename (@list) {
$filename = fm_normalize_path_name($filename, $cwd);
my $file = fm_checked_cwd_path($filename);
if (!$file) {
print_error(fm_not_allowed_error($filename)) if (!$files_all);
next;
}
push(@checked_list, [$filename, $file]);
}
my $index = 0;
my $index_requested = 0;
if (has_command('identify')) {
foreach my $entry (@checked_list) {
my ($filename, $file) = @$entry;
my $type = mimetype($file);
my $files_size = -s $file;
my $file_encoded;
my $processed = 0;
my ($w, $h, $o) = (0, 0, undef);
if (string_starts_with($type, 'image')) {
if ($files_all ||
$files_selected ||
$file_requested eq $filename)
{
my $data_auto =
($auto_orient ? '%w %h %[orientation]' : '%w %h');
my $data =
`identify -ping -format '$data_auto' @{[quotemeta($file)]}`;
($w, $h, $o) =
$data =~ /(?|(\d+)\s(\d+)\s(\w+)|(\d+)\s(\d+))/;
my $orientation = ($w > $h ? $w : $h);
my $factor = ($w > $h ? $width : $height);
my $types_excluded = (string_contains($type, 'svg') ||
string_contains($type, 'microsoft.icon'));
if ($w && $h) {
# Process image in case it's larger than 600kb or its dimensions larger than user's screen resolution
if (
!$types_excluded &&
($orientation > $factor ||
$files_size > 600000)
)
{
# Fix orientation sizes
if ($auto_orient && $o) {
if (
string_starts_with($o,
'Left') ||
string_starts_with($o,
'Right'))
{
my $w_ = $w;
my $h_ = $h;
$h = $w_;
$w = $h_;
}
}
# Extract smaller version of the file
my $file_out = "$file.bak~";
my $convert_size = (
($w > $h ? undef : 'x').
( $orientation > $factor
? $factor
: $orientation)
);
system(
"convert -quality 60% -resize $convert_size @{[quotemeta($file)]} $auto_orient @{[quotemeta($file_out)]}"
);
# Store smaller version data
$file_encoded = encode_base64(
read_file_contents($file_out));
if ($orientation > $factor) {
$h = int($factor / ($w / $h));
$w = $factor;
}
# Remove temporary file
unlink_file($file_out);
$processed++;
}
else {
$file_encoded = encode_base64(
read_file_contents($file));
}
}
if ($file_requested eq $filename) {
$index_requested = $index;
}
}
if ($file_encoded) {
push(
@items,
{
index => $index,
title => html_escape(
"$filename (@{[theme_nice_size_local($files_size, -1)]})"
),
file => $filename,
cwd => $cwd,
src => (
"data:$type;base64,$file_encoded"
),
p => $processed,
w => int($w),
h => int($h),
}
);
$index++;
}
}
}
if (@items) {
$data{'items'} = \@items;
$data{'index_requested'} = $index_requested;
}
else {
$data{'warning'} =
$text{'theme_xhred_filemanager_preview_images_error'};
}
}
else {
# Let user install ImageMagick in single click
$data{'error'} = $text{'theme_xhred_filemanager_preview_images_deps_error1'};
my $package_updates = "package-updates";
if (foreign_available($package_updates)) {
my $redir = "../$request_uri{'module'}";
my $pkgname =
($gconfig{'os_type'} =~ /debian/ ? 'imagemagick' : 'ImageMagick');
my %package_updates_lang = load_language($package_updates);
my $update_failed = html_escape($package_updates_lang{'update_failed'});
my $update_ok =
html_escape(text('global_deps_installed', "<code>$pkgname</code>"));
my $update_error =
html_escape(text('global_deps_error', "<kbd>$pkgname</kbd>"));
my $installing_text = html_escape(
text('global_deps_installing', "<code>$pkgname</code>"));
my $install_link =
"../$package_updates/update.cgi?source=3&confirm=1&mode=new&u=$pkgname&redir=$redir&redirdesc=File%20Manager&xnavigation=1";
$data{'error'} .= " ".
text(
'theme_xhred_filemanager_preview_images_deps_error2',
ui_link(
'javascript:;',
$text{'theme_xhred_global_click_here'},
'text-darker',
" nref onclick='fm_install_deps(\"$install_link\", [\"$update_failed\", \"$update_ok\", \"$update_error\", \"$installing_text\"], this)'"
)
);
}
}
print_json([\%data]);