Initial commit: Backup der Webseiten

- zoesch.de
- blitzkiste.net
- gruene-hassberge (norbert.zoesch.de)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Felix Zösch
2025-12-13 01:17:15 +01:00
commit 07c290a453
4607 changed files with 1202735 additions and 0 deletions

View File

@@ -0,0 +1,336 @@
#!/usr/bin/perl
use warnings;
use strict;
use Getopt::Long;
use DBI;
use File::Basename;
use Time::Local;
use List::Util qw/shuffle min/;
my %opt;
GetOptions(
\%opt,
qw/dbname=s dbuser=s dbpass=s prefix=s
total=i start_date=s end_date=s
help/
);
if (defined($opt{help}))
{
print <<FIN;
Fill the user comments table of Piwigo.
Usage: pwg_fill_comments.pl --dbname=<database_name>
--dbuser=<username>
--dbpass=<password>
--tagfile=<tags filename>
[--prefix=<tables prefix>]
[--help]
--dbname, --dbuser and --dbpass are connexion parameters.
--tagfile
--prefix : determines the prefix for your table names.
--help : show this help
FIN
exit(0);
}
my $usage = "\n\n".basename($0)." --help for help\n\n";
foreach my $option (qw/dbname dbuser dbpass start_date end_date/) {
if (not exists $opt{$option}) {
die 'Error: '.$option.' is a mandatory option', $usage;
}
}
$opt{prefix} = 'piwigo_' if (not defined $opt{prefix});
my $dbh = DBI->connect(
'DBI:mysql:'.$opt{dbname},
$opt{dbuser},
$opt{dbpass}
);
my $query;
my $sth;
# retrieve all available users
$query = '
SELECT id
FROM '.$opt{prefix}.'users
';
my @user_ids = keys %{ $dbh->selectall_hashref($query, 'id') };
# set a list of IP addresses for each users
my %user_IPs = ();
foreach my $user_id (@user_ids) {
for (1 .. 1 + int rand 5) {
push(
@{ $user_IPs{$user_id} },
join(
'.',
map {1 + int rand 255} 1..4
)
);
}
}
# use Data::Dumper; print Dumper(\%user_IPs); exit();
# start and end dates
my ($year,$month,$day,$hour,$min,$sec)
= ($opt{start_date} =~ m/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/);
my $start_unixtime = timelocal(0,0,0,$day,$month-1,$year);
($year,$month,$day,$hour,$min,$sec)
= ($opt{end_date} =~ m/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/);
my $end_unixtime = timelocal(0,0,0,$day,$month-1,$year);
# "tags from image" and "images from tag"
$query = '
SELECT image_id, tag_id
FROM '.$opt{prefix}.'image_tag
';
my %image_tags = ();
my %tag_images = ();
my %related_tag_of = ();
my @tags = ();
$sth = $dbh->prepare($query);
$sth->execute();
while (my $row = $sth->fetchrow_hashref()) {
push(
@{$image_tags{$row->{image_id}}},
$row->{tag_id}
);
push(
@{$tag_images{$row->{tag_id}}},
$row->{image_id}
);
push (
@tags,
$row->{tag_id}
);
}
# foreach my $tag_id (keys %tag_images) {
# printf(
# "tag %5u: %5u images\n",
# $tag_id,
# scalar @{$tag_images{$tag_id}}
# );
# }
# exit();
# use Data::Dumper; print Dumper(\%tag_images); exit();
# categories from image_id
$query = '
SELECT image_id, category_id
FROM '.$opt{prefix}.'image_category
';
my %image_categories = ();
my %category_images =();
my %categories = ();
$sth = $dbh->prepare($query);
$sth->execute();
while (my $row = $sth->fetchrow_hashref()) {
push(
@{$image_categories{$row->{image_id}}},
$row->{category_id}
);
push(
@{$category_images{$row->{category_id}}},
$row->{image_id}
);
$categories{ $row->{category_id} }++;
}
my @images = keys %image_categories;
my @categories = keys %categories;
# use Data::Dumper;
# print Dumper(\%image_categories);
my @sections = (
'categories',
# 'tags',
# 'search',
# 'list',
# 'favorites',
# 'most_visited',
# 'best_rated',
# 'recent_pics',
# 'recent_cats',
);
my @inserts = ();
USER : foreach my $user_id (@user_ids) {
print 'user_id: ', $user_id, "\n";
my $current_unixtime = $start_unixtime;
my @IPs = @{ $user_IPs{$user_id} };
VISIT : foreach my $visit_num (1..100_000) {
print 'visit: ', $visit_num, "\n";
my @temp_inserts = ();
my $IP = (@IPs)[int rand @IPs];
my $current_page = 0;
my $visit_size = 10 + int rand 90;
$current_unixtime+= 86_400 + int rand(86_400 * 30);
my $section = $sections[int rand scalar @sections];
# print 'section: ', $section, "\n";
push(
@temp_inserts,
{
section => $section,
}
);
if ($section eq 'categories') {
CATEGORY : foreach my $category_id (
(shuffle @categories)[1..int rand scalar @categories]
) {
# print 'category: ', $category_id, "\n";
push(
@temp_inserts,
{
category_id => $category_id,
}
);
my @images = @{$category_images{$category_id}};
IMAGE : foreach my $image_id (
(shuffle @images)[1..min(10, scalar @images)]
) {
push(
@temp_inserts,
{
category_id => $category_id,
image_id => $image_id,
}
);
}
}
}
if ($section eq 'tags') {
# TODO
}
# transfert @temp_inserts to @inserts
print 'temp_insert size: ', scalar @temp_inserts, "\n";
foreach my $temp_insert (@temp_inserts) {
$current_unixtime+= 5 + int rand 25;
next VISIT if ++$current_page == $visit_size;
last VISIT if $current_unixtime >= $end_unixtime;
my $date = unixtime_to_mysqldate($current_unixtime);
my $time = unixtime_to_mysqltime($current_unixtime);
my ($year, $month, $day) = split '-', $date;
my ($hour) = split ':', $time;
$temp_insert->{date} = $date;
$temp_insert->{time} = $time;
$temp_insert->{year} = $year;
$temp_insert->{month} = $month;
$temp_insert->{day} = $day;
$temp_insert->{hour} = $hour;
$temp_insert->{IP} = $IP;
$temp_insert->{section} = $section;
$temp_insert->{user_id} = $user_id;
push(@inserts, $temp_insert);
}
}
}
@inserts = sort {
$a->{date} cmp $b->{date}
or $a->{time} cmp $b->{time}
} @inserts;
if (scalar @inserts) {
my @columns =
qw/
date time year month day hour
user_id IP
section category_id image_id
/;
my $question_marks_string = '(';
$question_marks_string.= join(
',',
map {'?'} @columns
);
$question_marks_string.= ')';
my $query = '
INSERT INTO '.$opt{prefix}.'history
('.join(', ', @columns).')
VALUES
';
$query.= join(
',',
map {$question_marks_string} (1 .. scalar @inserts)
);
$query.= '
';
# print $query, "\n";
my @values = ();
foreach my $insert (@inserts) {
push(
@values,
map {
$insert->{$_}
} @columns
);
}
$sth = $dbh->prepare($query);
$sth->execute(@values)
or die 'cannot execute insert query';
}
sub unixtime_to_mysqldate {
my ($unixtime) = @_;
($sec,$min,$hour,$day,$month,$year) = localtime($unixtime);
return sprintf(
'%d-%02d-%02d',
$year+1900,
$month+1,
$day,
);
}
sub unixtime_to_mysqltime {
my ($unixtime) = @_;
($sec,$min,$hour,$day,$month,$year) = localtime($unixtime);
return sprintf(
'%d:%d:%d',
$hour,
$min,
$sec
);
}

View File

@@ -0,0 +1,30 @@
<?php
// +-----------------------------------------------------------------------+
// | Piwigo - a PHP based photo gallery |
// +-----------------------------------------------------------------------+
// | Copyright(C) 2008-2016 Piwigo Team http://piwigo.org |
// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
// +-----------------------------------------------------------------------+
// | This program is free software; you can redistribute it and/or modify |
// | it under the terms of the GNU General Public License as published by |
// | the Free Software Foundation |
// | |
// | This program is distributed in the hope that it will be useful, but |
// | WITHOUT ANY WARRANTY; without even the implied warranty of |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
// | General Public License for more details. |
// | |
// | You should have received a copy of the GNU General Public License |
// | along with this program; if not, write to the Free Software |
// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
// | USA. |
// +-----------------------------------------------------------------------+
// Recursive call
$url = '../';
header( 'Request-URI: '.$url );
header( 'Content-Location: '.$url );
header( 'Location: '.$url );
exit();
?>

View File

@@ -0,0 +1,653 @@
<?php
$validated_keys = array(
'af_ZA' => array(
'IP: %s',
'IP',
'IPTC Metadata',
'Albums',
'Album',
'%d Kb',
'in %d sub-album',
'in %d sub-albums',
'Admin: %s',
'Album: %s',
'letters',
'EXIF Metadata',
),
'ar_SA' => array(
'IP: %s',
'IP',
'the wiki',
'localhost, sql.multimania.com, toto.freesurf.fr',
),
'bg_BG' => array(
'N/A',
'IP: %s',
),
'cs_CZ' => array(
'Album',
'IP: %s',
'Album: %s',
'IP',
'EXIF Metadata',
'IPTC Metadata',
),
'da_DK' => array(
'%d Kb',
'%d hit',
'%d hits',
'Connection settings',
'Login',
'Display',
'Filter',
'Links',
'N/A',
'Password',
'Quick connect',
'RSS feed',
'Reset',
'Tag',
'Tags',
'Webmaster',
'Administration',
'caddie',
'download',
'Logout',
'Powered by',
'Auto login',
'slideshow',
'SQL queries in',
'Thumbnails',
'Menu',
'IP: %s',
'Browser: %s',
'Email: %s',
'Admin: %s',
'Rank',
'cloud',
'Pause of slideshow',
'Database type',
'Installation',
'Webmaster login',
'localhost, sql.multimania.com, toto.freesurf.fr',
'Webmaster password',
'%d tag',
'%d tags',
'Caddie',
'Database',
'Elements',
'Form',
'Gallery URL',
'Path',
'Permalink',
'Permalinks',
'Plugins',
'Position',
'Rating',
'Representant',
'Site manager',
'Status',
'Version',
'Main',
'File',
'High definition',
'email',
'Send',
'Remote',
'Piwigo administration',
'GD version',
'[Simulation]',
'Upload',
'user_status_admin',
'user_status_webmaster',
'IP',
'Element',
'Element type',
'Hoverbox display',
'Level 8',
'Plugins up to date',
'Support',
'Piwigo Administration',
'Piwigo version',
'Original templates',
'Downloads',
'Download',
'Hit',
'Resize',
'Miscellaneous',
'Virtual Links',
'SQL queries',
'Edit',
'%d rates',
'Rating score',
'Password Reset',
'Invalid key',
'EXIF Metadata',
'IPTC Metadata',
),
'de_DE' => array(
'%d Kb',
'Filter',
'Links',
'Tag',
'Tags',
'Webmaster',
'Administration',
'Thumbnails',
'IP: %s',
'Browser: %s',
'Name',
'Permalink',
'Permalinks',
'Plugins',
'Position',
'Status',
'Version',
'High definition',
'Parameter',
'private',
'Thumbnail',
'Simulation',
'Upload',
'user_status_admin',
'user_status_webmaster',
'IP',
'Privacy level',
'Demo',
'Support',
'Bugs',
'Downloads',
'Installation',
'localhost, sql.multimania.com, toto.freesurf.fr',
'Upgrade',
'Download,',
'Piwigo Uploader',
'[Simulation]',
'Album',
'Album: %s',
'Remote',
),
'el_GR' => array(
'%d Kb',
'Email: %s',
'IP: %s',
'Powered by',
),
'es_AR' => array(
'%d Kb',
'Webmaster',
'IP: %s',
'No',
),
'es_ES' => array(
'Tag',
'Tags',
'IP: %s',
'Email: %s',
'No',
'%d tag',
'%d tags',
'Plugins',
'is_high_disabled',
'nbm_content_goto_2',
'nbm_content_hello_2',
'site_local',
'update_simulation_title',
'IP',
'Level 8',
'DEMO',
'BUGS',
'step1_host_info',
'Albums',
'General',
'Local',
'Demo',
'Bugs',
'localhost, sql.multimania.com, toto.freesurf.fr',
'Piwigo Uploader',
),
'fr_FR' => array(
'Dimensions',
'Identification',
'Notification',
'Tag',
'Tags',
'admin',
'identification',
'redirect_msg',
'search_date_from',
'title_menu',
'IP: %s',
'Email: %s',
'%d element',
'%d elements',
'%d association',
'%d associations',
'%d tag',
'%d tags',
'Actions',
'Description',
'Informations',
'Maintenance',
'Options',
'Plugins',
'Position',
'Validation',
'Version',
'config',
'Date',
'instructions',
'is_high_disabled',
'nbm_content_goto_2',
'nbm_content_hello_2',
'nbm_item_notification',
'nbm_send_options',
'permissions',
'public',
'site_local',
'update_simulation_title',
'IP',
'image',
'section',
'tags',
'high',
'c13y_Correction',
'Level 1',
'Level 8',
'FORUM',
'EXTENSIONS',
'WIKI / DOC',
'Installation',
'step1_host_info',
'Administration',
'Redirection...',
'Menu',
'%d photo',
'%d photos',
'Configuration',
'Instructions',
'Permissions',
'Local',
'[Simulation]',
'Section',
'Correction',
'Support',
'Extensions',
'Documentation',
'Photos',
'photo',
'pixels',
'Piwigo Uploader',
'localhost, sql.multimania.com, toto.freesurf.fr',
'email',
'Albums',
'Album',
'Menu Management',
'Action',
'Simulation',
),
'he_IL' => array(
'localhost, sql.multimania.com, toto.freesurf.fr',
),
'hr_HR' => array(
'%d Kb',
'Filter',
'Tag',
'Webmaster',
'IP: %s',
'%d tag',
'user_status_webmaster',
'IP',
'localhost, sql.multimania.com, toto.freesurf.fr',
),
'hu_HU' => array(
'Album',
'Album: %s',
),
'is_IS' => array(
'%d Kb',
'N/A',
'IP: %s',
'Admin: %s',
),
'it_IT' => array(
'Album',
'Album: %s',
'Browser: %s',
'Database',
'Directory',
'Download',
'Downloads',
'email',
'Email',
'Email: %s',
'File',
'Home',
'high',
'Host',
'IP',
'IP: %s',
'Links',
'Logout',
'Menu',
'N/A',
'No',
'Password',
'Permalink',
'Permalinks',
'pixels',
'Piwigo Uploader',
'Plugin',
'Plugins',
'Tag',
'Tags',
'%d tag',
'%d tags',
'Tools',
'user_status_webmaster',
'Webmaster',
'in %d sub-album',
),
'ja_JP' => array(
'About',
'Since',
'Powered by',
'IP: %s',
'IP',
'Sent by',
'%d Kb',
'',
),
'lv_LV' => array(
'%d Kb',
'N/A',
'IP: %s',
'IP',
'localhost, sql.multimania.com, toto.freesurf.fr',
),
'nl_NL' => array(
'%d Kb',
'Filter',
'Help',
'Links',
'RSS feed',
'Reset',
'Tag',
'Tags',
'Week %d',
'download',
'Powered by',
'Auto login',
'Contact',
'SQL queries in',
'Menu',
'IP: %s',
'Browser: %s',
'Email: %s',
'Piwigo Help',
'%d tag',
'%d tags',
'Batch management',
'Database',
'Permalink',
'Permalinks',
'Plugins',
'Representant',
'Site manager',
'Status',
'email',
'Parameter',
'Parameters',
'Remote site',
'test',
'Remote',
'unit mode',
'Upload',
'IP',
'Level 8',
'Demo',
'Hits',
'Downloads',
'localhost, sql.multimania.com, toto.freesurf.fr',
'Upgrade',
'SQL queries',
'letters',
'This author removed the comment with id %d',
'Database type',
'Sorry!',
'Hope to see you back soon.',
'Hit',
'pixels',
'Download',
'Piwigo Uploader',
'Download,',
'Compact',
'Updates',
'ERROR',
'Piwigo Update',
'Dump Database',
'Start Upload',
),
'no_NO' => array(
'IP: %s',
'Browser: %s',
'Email: %s',
'Admin: %s',
'Pause of slideshow',
'Album',
'Filter',
'N/A',
'Webmaster',
'%d Kb',
'representative',
'slideshow',
'Album: %s',
'Album results for',
'%d tag',
'Database',
'Permalink',
'Representant',
'Representative',
'Status',
'Main',
'email',
'Parameter',
'Send',
'test',
'status',
'user_status_admin',
'user_status_webmaster',
'IP',
'Element',
'Tags',
'Element type',
'Database type',
'Host',
'localhost, sql.multimania.com, toto.freesurf.fr',
),
'pl_PL' => array(
'%d Kb',
'RSS feed',
'Webmaster',
'IP: %s',
'Tag',
'Powered by',
'Email: %s',
'Admin: %s',
'%d tag',
'Status',
'test',
'status',
'user_status_admin',
'user_status_webmaster',
'localhost, sql.multimania.com, toto.freesurf.fr',
'IP',
'Menu',
'Album',
),
'pt_BR' => array(
'localhost, sql.multimania.com, toto.freesurf.fr',
),
'pt_PT' => array(
'%d Kb',
'Links',
'Webmaster',
'Powered by',
'IP: %s',
'Email: %s',
'localhost, sql.multimania.com, toto.freesurf.fr',
'Permalinks',
'Plugins',
'Local',
'user_status_webmaster',
'IP',
'Downloads',
'Download',
'Download,',
'EXIF Metadata',
'IPTC Metadata',
'Original',
'Zoom',
'',
),
'ro_RO' => array(
'%d Kb',
'N/A',
'RSS feed',
'Webmaster',
'Calendar',
'Contact',
),
'ru_RU' => array(
'IP',
'IP: %s',
'localhost, sql.multimania.com, toto.freesurf.fr',
'',
),
'sl_SL' => array(
'%d Kb',
'Filter',
'N/A',
'caddie',
'IP: %s',
'show tag cloud',
'cloud',
),
'sk_SK' => array(
'%d Kb',
'Album',
'Filter',
'Webmaster',
'IP: %s',
'Album: %s',
'test',
'user_status_webmaster',
'IP',
'EXIF Metadata',
'IPTC Metadata',
),
'sr_RS' => array(
'%d Kb',
'Filter',
'Webmaster',
'IP: %s',
'user_status_admin',
'user_status_webmaster',
'IP',
'Element',
'Host',
'localhost, sql.multimania.com, toto.freesurf.fr',
),
'sv_SE' => array(
'%d association',
'Plugins',
'Position',
'Representant',
'Status',
'Version',
'High definition',
'test',
'status',
'Piwigo administration',
'GD version',
'user_status_webmaster',
'IP',
'Element',
'Piwigo Administration',
'Piwigo version',
'IP: %s',
'Admin: %s',
'Filter',
'Administration',
'%d Kb',
'Installation',
'localhost, sql.multimania.com, toto.freesurf.fr',
'Album: %s',
'Album',
'Informations',
'EXIF Metadata',
'IPTC Metadata',
'Original',
'Desktop',
),
'tr_TR' => array(
'%d hit',
'N/A',
'Webmaster',
'IP: %s',
'Host',
'localhost, sql.multimania.com, toto.freesurf.fr',
),
'vi_VN' => array(
'%d Kb',
'Caddie',
'Form',
'Plugins',
'user_status_webmaster',
'Templates',
'pixels',
'localhost, sql.multimania.com, toto.freesurf.fr',
'Desktop',
),
'zh_CN' => array(
'IP',
'localhost, sql.multimania.com, toto.freesurf.fr',
'%d Kb',
),
'zh_TW' => array(
'IP',
'localhost, sql.multimania.com, toto.freesurf.fr',
),
);
?>

View File

@@ -0,0 +1,97 @@
<?php
// +-----------------------------------------------------------------------+
// | Piwigo - a PHP based photo gallery |
// +-----------------------------------------------------------------------+
// | Copyright(C) 2008-2016 Piwigo Team http://piwigo.org |
// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
// +-----------------------------------------------------------------------+
// | This program is free software; you can redistribute it and/or modify |
// | it under the terms of the GNU General Public License as published by |
// | the Free Software Foundation |
// | |
// | This program is distributed in the hope that it will be useful, but |
// | WITHOUT ANY WARRANTY; without even the implied warranty of |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
// | General Public License for more details. |
// | |
// | You should have received a copy of the GNU General Public License |
// | along with this program; if not, write to the Free Software |
// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
// | USA. |
// +-----------------------------------------------------------------------+
$filename = 'sample.jpg';
echo 'Informations are read from '.$filename.'<br><br><br>';
/**
* return a cleaned IPTC value
*
* @param string value
* @return string
*/
function clean_iptc_value($value)
{
// strip leading zeros (weird Kodak Scanner software)
while ( isset($value[0]) and $value[0] == chr(0))
{
$value = substr($value, 1);
}
// remove binary nulls
$value = str_replace(chr(0x00), ' ', $value);
return $value;
}
$iptc_result = array();
$imginfo = array();
getimagesize($filename, $imginfo);
if (isset($imginfo['APP13']))
{
$iptc = iptcparse($imginfo['APP13']);
if (is_array($iptc))
{
foreach (array_keys($iptc) as $iptc_key)
{
if (isset($iptc[$iptc_key][0]))
{
if ($iptc_key == '2#025')
{
$value = implode(
',',
array_map(
'clean_iptc_value',
$iptc[$iptc_key]
)
);
}
else
{
$value = clean_iptc_value($iptc[$iptc_key][0]);
}
$iptc_result[$iptc_key] = $value;
}
}
}
echo 'IPTC Fields in '.$filename.'<br>';
$keys = array_keys($iptc_result);
sort($keys);
foreach ($keys as $key)
{
echo '<br>'.$key.' = '.$iptc_result[$key];
}
}
else
{
echo 'no IPTC information';
}
echo '<br><br><br>';
echo 'EXIF Fields in '.$filename.'<br>';
$exif = read_exif_data($filename);
echo '<pre>';
print_r($exif);
echo '</pre>';
?>

View File

@@ -0,0 +1,193 @@
#!/usr/bin/perl
use strict;
use warnings;
use File::Find;
our %used_keys = ();
our %registered_keys = ();
my $piwigo_dir = $ARGV[0]; # '/home/pierrick/public_html/piwigo/dev/trunk';
my $type = $ARGV[1]; # common, admin, install, upgrade
find(\&used_keys, $piwigo_dir);
load_registered_keys($type);
# foreach my $key (sort keys %used_keys) {
# # print "{".$key."}", ' is used', "\n";
# if (not defined $registered_keys{$key}) {
# # print "{".$key."}", ' is missing', "\n";
# print '$lang[\''.$key.'\'] = \''.$key.'\';', "\n";
# }
# }
my %ignore_keys = (
'%d new photo' => 1,
'%d new photos' => 1,
'%d category updated' => 1,
'%d categories updated' => 1,
'%d new comment' => 1,
'%d new comments' => 1,
'%d comment to validate' => 1,
'%d comments to validate' => 1,
'%d new user' => 1,
'%d new users' => 1,
'%d waiting element' => 1,
'%d waiting elements' => 1,
'user_status_admin' => '',
'user_status_generic' => '',
'user_status_guest' => '',
'user_status_normal' => '',
'user_status_webmaster' => '',
'Level 0' => '',
'Level 1' => '',
'Level 2' => '',
'Level 4' => '',
'Level 8' => '',
'ACCESS_0' => '',
'ACCESS_1' => '',
'ACCESS_2' => '',
'ACCESS_3' => '',
'ACCESS_4' => '',
'ACCESS_5' => '',
'month' => '',
'day' => '',
'chronology_monthly_calendar' => '',
'chronology_monthly_list' => '',
'chronology_weekly_list' => '',
'public' => '',
'private' => '',
'none' => '',
'other' => '',
'high' => '',
'Waiting page: %s' => '',
'Admin: %s' => '',
'Manage this user comment: %s' => '',
'Main "guest" user does not exist' => '',
'Main "guest" user status is incorrect' => '',
'Main "webmaster" user does not exist' => '',
'Main "webmaster" user status is incorrect' => '',
'Default user does not exist' => '',
'(!) This comment requires validation' => '',
);
foreach my $key (sort keys %registered_keys) {
if (not defined $used_keys{$key} and not defined $ignore_keys{$key}) {
print "{".$key."}", ' is not used anywhere', "\n";
}
}
sub used_keys {
if ($File::Find::name !~ m/(tpl|php)$/) {
return 0;
}
if ($File::Find::name =~ m{/(plugins|language|_data)/}) {
return 0;
}
if ('upgrade' eq $type) {
if ($File::Find::name !~ m{upgrade\.(tpl|php)$}) {
# return 0;
}
}
if ('install' eq $type) {
if ($File::Find::name =~ m{upgrade\.(tpl|php)$}) {
return 0;
}
if ($File::Find::name !~ m{/install(\.tpl|\.php|/)}) {
return 0;
}
}
if ('admin' eq $type) {
if ($File::Find::name =~ m{upgrade\.(tpl|php)$}) {
return 0;
}
if ($File::Find::name =~ m{/install(\.tpl|\.php|/)}) {
return 0;
}
my $is_admin = 0;
if ($File::Find::name =~ m{themes/default/template/mail}) {
$is_admin = 1;
}
if ($File::Find::name =~ m{/admin/}) {
$is_admin = 1;
}
if ($File::Find::name =~ m{/admin\.php$}) {
$is_admin = 1;
}
if (not $is_admin) {
return 0;
}
}
if ('common' eq $type) {
if ($File::Find::name =~ m{upgrade\.(tpl|php)$}) {
return 0;
}
if ($File::Find::name =~ m{/install(\.tpl|\.php|/)}) {
return 0;
}
if ($File::Find::name =~ m{/admin(/|\.php)} or $File::Find::name =~ m{themes/default/template/mail}) {
return 0;
}
}
if (-f) {
my $big_string = '';
open(my $fhi, '<', $File::Find::name);
while (<$fhi>) {
chomp;
s{//.*$}{};
$big_string.= $_;
}
close($fhi);
while ($big_string =~ m/\{(['"])(.+?)\1\|\@translate/g) {
$used_keys{$2}++;
}
while ($big_string =~ m/l10n \s* \( \s* (['"]) (.+?) \1 \s* \)/xg) {
$used_keys{$2}++;
}
while ($big_string =~ m/l10n_args \s* \( \s* (['"]) (.+?) \1 \s* ,/xg) {
$used_keys{$2}++;
}
while ($big_string =~ m/l10n_dec \s* \( \s* (['"]) (.+?) \1 \s* ,\s* (['"]) (.+?) \3 \s* ,/xg) {
$used_keys{$2}++;
$used_keys{$4}++;
}
}
}
sub load_registered_keys {
my ($type) = @_;
my %files_for_type = (
common => [qw/common/],
admin => [qw/common admin/],
install => [qw/common admin install/],
upgrade => [qw/common admin install upgrade/],
);
foreach my $file_code (@{$files_for_type{$type}}) {
my $filepath = $piwigo_dir.'/language/en_UK/'.$file_code.'.lang.php';
open(my $fhi, '<', $filepath);
while (<$fhi>) {
if (m/\$lang\[ \s* (['"]) (.+?) \1 \s* \]/x) {
$registered_keys{$2}++;
}
}
}
}

View File

@@ -0,0 +1,434 @@
#!/usr/bin/perl
####
# Usage examples
#
# perl piwigo_remote.pl --action=pwg.images.add --file=erwann_rocher-web.jpg --define categories=9
use strict;
use warnings;
use JSON;
use LWP::UserAgent;
# LWP::Debug::level('+');
use Getopt::Long;
use Encode qw/is_utf8 decode/;
use POSIX qw(ceil floor);
my %opt = ();
GetOptions(
\%opt,
qw/
action=s
file=s
original=s
categories=s
chunk_size=i
base_url=s
username=s
password=s
define=s%
/
);
our $ua = LWP::UserAgent->new;
$ua->agent('Mozilla/piwigo_remote.pl 1.25');
$ua->cookie_jar({});
my %conf;
$conf{response_format} = 'json';
$conf{limit} = 10;
my %conf_default = (
base_url => 'http://localhost/piwigo/2.0',
username => 'plg',
password => 'plg',
chunk_size => 500_000,
);
foreach my $conf_key (keys %conf_default) {
$conf{$conf_key} = defined $opt{$conf_key} ? $opt{$conf_key} : $conf_default{$conf_key}
}
$ua->default_headers->authorization_basic(
$conf{username},
$conf{password}
);
my $result = undef;
my $query = undef;
binmode STDOUT, ":encoding(utf-8)";
# TODO : don't connect at each script call, use the session duration instead.
my $form = {
method => 'pwg.session.login',
username => $conf{username},
password => $conf{password},
};
$result = $ua->post(
$conf{base_url}.'/ws.php?format=json',
$form
);
# print "\n", $ua->cookie_jar->as_string, "\n";
if ($opt{action} eq 'pwg.images.add') {
use Digest::MD5::File qw/file_md5_hex/;
$form = {};
$form->{method} = $opt{action};
my $original = $opt{file};
if (defined $opt{original}) {
$original = $opt{original};
}
my $original_sum = file_md5_hex($original);
$form->{original_sum} = $original_sum;
send_chunks(
filepath => $opt{file},
type => 'file',
original_sum => $original_sum,
);
$form->{file_sum} = file_md5_hex($opt{file});
foreach my $key (keys %{ $opt{define} }) {
$form->{$key} = $opt{define}{$key};
}
my $response = $ua->post(
$conf{base_url}.'/ws.php?format=json',
$form
);
print "-" x 50, "\n";
printf("response code : %u\n", $response->code);
printf("response message : %s\n", $response->message);
print "-" x 50, "\n";
print "\n";
# use Data::Dumper;
# print Dumper($response->content);
# print Dumper(from_json($response->content));
if ($response->is_success) {
print "upload successful\n";
}
else {
print Dumper($response);
warn 'A problem has occured during upload', "\n";
warn $response->decoded_content, "\n";
die $response->status_line;
}
}
if ($opt{action} eq 'pwg.images.addFile') {
use Digest::MD5::File qw/file_md5_hex/;
if (not defined $opt{define}{image_id}) {
die '--define image_id=1234 is missing';
}
# which file type are we going to add/update?
my $type = undef;
foreach my $test_type (qw/thumbnail file high/) {
if (defined $opt{$test_type}) {
$type = $test_type;
last;
}
}
if (not defined $type) {
die 'at least one of file/thumbnail/high parameters must be set';
}
my $type_code = typecode_from_typename($type);
send_chunks(
filepath => $opt{$type},
type => $type_code,
original_sum => file_md5_hex($opt{original}),
);
$form = {};
$form->{method} = $opt{action};
$form->{type} = $type_code;
$form->{sum} = file_md5_hex($opt{$type});
foreach my $key (keys %{ $opt{define} }) {
$form->{$key} = $opt{define}{$key};
}
my $response = $ua->post(
$conf{base_url}.'/ws.php?format=json',
$form
);
print "-" x 50, "\n";
printf("response code : %u\n", $response->code);
printf("response message : %s\n", $response->message);
print "-" x 50, "\n";
print "\n";
# use Data::Dumper;
# print Dumper($response->content);
# print Dumper(from_json($response->content));
if ($response->is_success) {
print "upload successful\n";
}
else {
print Dumper($response);
warn 'A problem has occured during upload', "\n";
warn $response->decoded_content, "\n";
die $response->status_line;
}
}
if ($opt{action} eq 'pwg.tags.list') {
use Text::ASCIITable;
$query = pwg_ws_get_query(
method => 'pwg.tags.getList',
sort_by_counter => 'true',
);
$result = $ua->get($query);
my $tag_result = from_json($result->content);
my $t = Text::ASCIITable->new({ headingText => 'Tags' });
$t->setCols('id','counter','name');
my $tag_number = 1;
foreach my $tag_href (@{ $tag_result->{result}{tags} }) {
$t->addRow(
$tag_href->{id},
$tag_href->{counter},
$tag_href->{name}
);
last if $tag_number++ >= $conf{limit};
}
print $t;
}
if ($opt{action} eq 'pwg.tags.getAdminList') {
$query = pwg_ws_get_query(
method => 'pwg.tags.getAdminList'
);
$result = $ua->get($query);
print Dumper($result);
my $tags = from_json($result->content)->{result}{tags};
foreach my $tag (@{$tags}) {
# print join(',', keys %{$tag}), "\n"; exit();
printf(
'{%u} %s ',
$tag->{id},
$tag->{name}
);
}
print "\n";
}
if ($opt{action} eq 'pwg.categories.add') {
$form = {
method => 'pwg.categories.add',
name => $opt{define}{name},
parent => $opt{define}{parent},
comment => $opt{define}{comment},
};
my $response = $ua->post(
$conf{base_url}.'/ws.php?format=json',
$form
);
use Data::Dumper;
print Dumper(from_json($response->content));
}
if ($opt{action} eq 'pwg.tags.add') {
$form = {
method => 'pwg.tags.add',
name => $opt{define}{name},
};
my $response = $ua->post(
$conf{base_url}.'/ws.php?format=json',
$form
);
use Data::Dumper;
print Dumper(from_json($response->content));
}
if ($opt{action} eq 'pwg.images.exist') {
$form = {
method => $opt{action},
};
foreach my $key (keys %{ $opt{define} }) {
$form->{$key} = $opt{define}{$key};
}
my $response = $ua->post(
$conf{base_url}.'/ws.php?format=json',
$form
);
use Data::Dumper;
print Dumper(from_json($response->content)->{result});
# print Dumper($response);
}
if ($opt{action} eq 'pwg.images.checkFiles') {
use Digest::MD5::File qw/file_md5_hex/;
$form = {};
$form->{method} = $opt{action};
foreach my $type (qw/thumbnail file high/) {
if (defined $opt{$type}) {
$form->{$type.'_sum'} = file_md5_hex($opt{$type});
}
}
foreach my $key (keys %{ $opt{define} }) {
$form->{$key} = $opt{define}{$key};
}
my $response = $ua->post(
$conf{base_url}.'/ws.php?format=json',
$form
);
print "-" x 50, "\n";
printf("response code : %u\n", $response->code);
printf("response message : %s\n", $response->message);
print "-" x 50, "\n";
print "\n";
use Data::Dumper;
print Dumper(from_json($response->content));
}
if ($opt{action} eq 'pwg.images.setInfo' or $opt{action} eq 'pwg.categories.setInfo') {
$form = {
method => $opt{action},
};
foreach my $key (keys %{ $opt{define} }) {
$form->{$key} = $opt{define}{$key};
}
my $response = $ua->post(
$conf{base_url}.'/ws.php?format=json',
$form
);
use Data::Dumper;
# print Dumper(from_json($response->content)->{result});
print Dumper($response);
}
if ($opt{action} eq 'pwg.categories.getList') {
$form = {
method => $opt{action},
};
foreach my $key (keys %{ $opt{define} }) {
$form->{$key} = $opt{define}{$key};
}
my $response = $ua->post(
$conf{base_url}.'/ws.php?format=json',
$form
);
use Data::Dumper;
print Dumper($response->content);
print Dumper(from_json($response->content)->{result});
print Dumper($response);
}
$query = pwg_ws_get_query(
method => 'pwg.session.logout'
);
$ua->get($query);
sub pwg_ws_get_query {
my %params = @_;
my $query = $conf{base_url}.'/ws.php?format='.$conf{response_format};
foreach my $key (keys %params) {
$query .= '&'.$key.'='.$params{$key};
}
return $query;
}
sub send_chunks {
my %params = @_;
use MIME::Base64 qw(encode_base64);
use File::Slurp;
my $content = read_file($params{filepath});
my $content_length = length($content);
my $nb_chunks = ceil($content_length / $conf{chunk_size});
my $chunk_pos = 0;
my $chunk_id = 1;
while ($chunk_pos < $content_length) {
my $chunk = substr(
$content,
$chunk_pos,
$conf{chunk_size}
);
$chunk_pos += $conf{chunk_size};
my $response = $ua->post(
$conf{base_url}.'/ws.php?format=json',
{
method => 'pwg.images.addChunk',
data => encode_base64($chunk),
original_sum => $params{original_sum},
position => $chunk_id,
type => $params{type},
}
);
printf(
'chunk %05u of %05u for %s "%s"'."\n",
$chunk_id,
$nb_chunks,
$params{type},
$params{filepath}
);
if ($response->code != 200) {
printf("response code : %u\n", $response->code);
printf("response message : %s\n", $response->message);
}
$chunk_id++;
}
}
sub typecode_from_typename {
my ($typename) = @_;
my $typecode = $typename;
if ('thumbnail' eq $typename) {
$typecode = 'thumb';
}
return $typecode;
}

View File

@@ -0,0 +1,121 @@
#!/usr/bin/perl
####
# Usage
#
# perl piwigo_upload.pl --url=http://piwigo.org/demo --user=admin --password=secret --file=photo.jpg --album_id=9
use strict;
use warnings;
use JSON;
use LWP::UserAgent;
use Getopt::Long;
use POSIX qw(ceil floor);
use Digest::MD5 qw/md5 md5_hex/;
use File::Slurp;
use File::Basename;
my %opt = ();
GetOptions(
\%opt,
qw/
file=s
album_id=i
category=s
url=s
username=s
password=s
/
);
our %conf = (
chunk_size => 500_000,
);
our $ua = LWP::UserAgent->new;
$ua->agent('Mozilla/piwigo_upload.pl 1.56');
$ua->cookie_jar({});
my $result = undef;
my $form = {
method => 'pwg.session.login',
username => $opt{username},
password => $opt{password},
};
$result = $ua->post(
$opt{url}.'/ws.php?format=json',
$form
);
my $response = $ua->post(
$opt{url}.'/ws.php?format=json',
{
method => 'pwg.session.getStatus',
}
);
my $pwg_token = from_json($response->content)->{result}->{pwg_token};
my $content = read_file($opt{file});
my $content_length = length($content);
my $nb_chunks = ceil($content_length / $conf{chunk_size});
my $chunk_pos = 0;
my $chunk_id = 0;
while ($chunk_pos < $content_length) {
my $chunk = substr(
$content,
$chunk_pos,
$conf{chunk_size}
);
# write the chunk as a temporary local file
my $chunk_path = '/tmp/'.md5_hex($opt{file}).'.chunk';
open(my $ofh, '>'.$chunk_path) or die "problem for writing temporary local chunk";
print {$ofh} $chunk;
close($ofh);
$chunk_pos += $conf{chunk_size};
my $response = $ua->post(
$opt{url}.'/ws.php?format=json',
{
method => 'pwg.images.upload',
chunk => $chunk_id,
chunks => $nb_chunks,
category => $opt{album_id},
pwg_token => $pwg_token,
file => [$chunk_path],
name => basename($opt{file}),
},
'Content_Type' => 'form-data',
);
unlink($chunk_path);
printf(
'chunk %03u of %03u for "%s"'."\n",
$chunk_id+1,
$nb_chunks,
$opt{file}
);
if ($response->code != 200) {
printf("response code : %u\n", $response->code);
printf("response message : %s\n", $response->message);
}
$chunk_id++;
}
$result = $ua->get(
$opt{url}.'/ws.php?format=json',
{
method => 'pwg.session.logout'
}
);

View File

@@ -0,0 +1,124 @@
#!/bin/bash
scriptdir=$(dirname $(readlink -e $0))
# +--------------------------------------------------------------------------+
# | pwg_rel_create.sh |
# +--------------------------------------------------------------------------+
# | author : Pierrick LE GALL <http://le-gall.net/pierrick> |
# | project : Piwigo |
# +--------------------------------------------------------------------------+
if [ $# -lt 1 ]
then
echo
echo 'usage : '$(basename $0)' <version number> [<sha>]'
echo
exit 1
fi
version=$1
sha=$2
name=piwigo-$version
cd /tmp
if [ -e $version ]
then
rm -rf $version
fi
mkdir $version
cd $version
git clone https://github.com/Piwigo/Piwigo.git piwigo
cd piwigo
if [ $# -eq 2 ]
then
git checkout $2
fi
# remove Git metadata
rm -rf /tmp/$version/piwigo/.git
# +--------------------------------------------------------------------------+
# | plugins |
# +--------------------------------------------------------------------------+
cd plugins
for plugin in TakeATour AdminTools LocalFilesEditor LanguageSwitch
do
cd /tmp/$version/piwigo/plugins
plugin_dir=$plugin
if [ $plugin = "LanguageSwitch" ]
then
plugin_dir=language_switch
fi
# clone repo
git clone https://github.com/Piwigo/${plugin}.git $plugin_dir
cd /tmp/$version/piwigo/plugins/$plugin_dir
# change version
perl $scriptdir/replace_version.pl --file=main.inc.php --version=$version
# register metadata in dedicated file
echo https://github.com/Piwigo/${plugin}.git > pem_metadata.txt
git log -n 1 --pretty=format:"%H %ad" --date=iso8601 >> pem_metadata.txt
# remove Git metadata
rm -rf .git
done
# +--------------------------------------------------------------------------+
# | themes |
# +--------------------------------------------------------------------------+
cd /tmp/$version/piwigo/themes
for themefile in $(ls */themeconf.inc.php)
do
# change version
perl $scriptdir/replace_version.pl --file=$themefile --version=$version
done
# +--------------------------------------------------------------------------+
# | languages |
# +--------------------------------------------------------------------------+
cd /tmp/$version/piwigo/language
for languagefile in $(ls */common.lang.php)
do
# change version
perl $scriptdir/replace_version.pl --file=$languagefile --version=$version
done
# +--------------------------------------------------------------------------+
# | data directories + zip 1 |
# +--------------------------------------------------------------------------+
# create "data" directories
cd /tmp/$version
mkdir piwigo/upload
mkdir piwigo/_data
touch piwigo/_data/dummy.txt
zip -q -r $name-nochmod.zip piwigo
# +--------------------------------------------------------------------------+
# | permissions + zip 2 |
# +--------------------------------------------------------------------------+
chmod -R a+w piwigo/local
chmod a+w piwigo/_data
chmod a+w piwigo/upload
chmod a+w piwigo/plugins
chmod a+w piwigo/themes
zip -q -r $name.zip piwigo
echo cd /tmp/$version

View File

@@ -0,0 +1,39 @@
#!/usr/bin/perl
####
# Usage
#
# perl replace_version.pl --file=/path/to/file.php --version=2.8.0
use strict;
use warnings;
use Getopt::Long;
use File::Basename;
my %opt = ();
GetOptions(
\%opt,
qw/
file=s
version=s
/
);
if (not -e $opt{file}) {
die "file missing ".$opt{file};
}
my $new_content = '';
open(my $ifh, '<'.$opt{file}) or die 'Houston, problem with "'.$opt{file}.'" for reading';
while (<$ifh>) {
if (/^Version:/) {
$_ = 'Version: '.$opt{version}.''."\n";
}
$new_content.= $_;
}
close($ifh);
open(my $ofh, '>'.$opt{file}) or die 'Houston, problem with "'.$opt{file}.'" for writing';
print {$ofh} $new_content;
close($ofh);

View File

@@ -0,0 +1,169 @@
<?php
// +-----------------------------------------------------------------------+
// | Piwigo - a PHP based photo gallery |
// +-----------------------------------------------------------------------+
// | Copyright(C) 2008-2016 Piwigo Team http://piwigo.org |
// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
// +-----------------------------------------------------------------------+
// | This program is free software; you can redistribute it and/or modify |
// | it under the terms of the GNU General Public License as published by |
// | the Free Software Foundation |
// | |
// | This program is distributed in the hope that it will be useful, but |
// | WITHOUT ANY WARRANTY; without even the implied warranty of |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
// | General Public License for more details. |
// | |
// | You should have received a copy of the GNU General Public License |
// | along with this program; if not, write to the Free Software |
// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
// | USA. |
// +-----------------------------------------------------------------------+
define('PHPWG_ROOT_PATH', '../');
include_once( PHPWG_ROOT_PATH.'include/common.inc.php' );
include_once( PHPWG_ROOT_PATH.'tools/language/translation_validated.inc.php' );
$languages = array_keys(get_languages());
sort($languages);
$page['ref_compare'] = 'en_UK';
$page['ref_default_values'] = 'en_UK';
if (!isset($_GET['lang']))
{
echo '<a href="?lang=all">All languages</a><br><br>';
echo '<ul>';
foreach ($languages as $language)
{
if ($page['ref_compare'] == $language)
{
continue;
}
echo '<li><a href="?lang='.$language.'">'.$language.'</a></li>';
}
echo '</ul>';
exit();
}
else if (in_array($_GET['lang'], $languages))
{
$languages = array($_GET['lang']);
}
$file_list = array('common', 'admin', 'install', 'upgrade');
$metalang = array();
// preload reference languages
$metalang[ $page['ref_compare'] ] = load_metalang($page['ref_compare'], $file_list);
$metalang[ $page['ref_default_values'] ] = load_metalang($page['ref_default_values'], $file_list);
foreach ($languages as $language)
{
if (in_array($language, array($page['ref_compare'], $page['ref_default_values'])))
{
continue;
}
echo '<h2>'.$language.'</h2>';
$metalang[$language] = load_metalang($language, $file_list);
foreach ($file_list as $file)
{
if (isset($metalang[ $language ][$file]))
{
$missing_keys = array_diff(
array_keys($metalang[ $page['ref_compare'] ][$file]),
array_keys($metalang[ $language ][$file])
);
$output_missing = '';
foreach ($missing_keys as $key)
{
$output_missing.= get_line_to_translate($file, $key);
}
// strings not "really" translated?
$output_duplicated = '';
$output_lost = '';
foreach (array_keys($metalang[$language][$file]) as $key)
{
$exceptions = array('Level 0');
if (in_array($key, $exceptions))
{
continue;
}
if (isset($validated_keys[$language]) and in_array($key, $validated_keys[$language]))
{
continue;
}
$local_value = $metalang[$language][$file][$key];
if (!isset($metalang[ $page['ref_default_values'] ][$file][$key]))
{
$output_lost.= '#'.$key.'# does not exist in the reference language'."\n";
}
else
{
$ref_value = $metalang[ $page['ref_default_values'] ][$file][$key];
if ($local_value == $ref_value)
{
$output_duplicated.= get_line_to_translate($file, $key);
}
}
}
echo '<h3>'.$file.'.lang.php</h3>';
if ('' != $output_missing or '' != $output_duplicated)
{
$output = '';
if ('' != $output_missing)
{
$output.= "// missing translations\n".$output_missing;
}
if ('' != $output_duplicated)
{
$output.= "\n// untranslated yet\n".$output_duplicated;
}
echo '<textarea style="width:100%;height:250px;">'.$output.'</textarea>';
}
if ('' != $output_lost)
{
echo '<pre>'.$output_lost.'</pre>';
}
}
else
{
echo '<h3>'.$file.'.lang.php is missing</h3>';
}
}
}
function load_metalang($language, $file_list)
{
global $lang, $user;
$metalang = array();
foreach ($file_list as $file)
{
$lang = array();
$user['language'] = $language;
if (load_language($file.'.lang', '', array('language'=>$language, 'no_fallback'=>true)))
{
$metalang[$file] = $lang;
}
}
return $metalang;
}
function get_line_to_translate($file, $key)
{
global $metalang, $page;
$print_key = str_replace("'", '\\\'', $key);
$print_value = str_replace("'", '\\\'', $metalang[ $page['ref_default_values'] ][$file][$key]);
return '$'."lang['".$print_key."'] = '".$print_value."';\n";
}
?>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,574 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Piwigo web API (web-services) explorer</title>
<link rel="stylesheet" href="//cdn.jsdelivr.net/tiptip/1.3/tipTip.css">
<style>
/* BEGIN CSS RESET
http://meyerweb.com/eric/tools/css/reset
v2.0 | 20110126 | License: none (public domain) */
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li,
fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video
{margin:0;padding:0;border:0;font-size:100%;vertical-align:baseline;}
article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {display:block;}
body {line-height:1.1;}
blockquote, q {quotes:none;}
blockquote:before, blockquote:after, q:before, q:after {content:'';content:none;}
table {border-collapse:collapse;border-spacing:0;}
/* END CSS RESET */
html {font-family:"Corbel","Lucida Grande","Verdana",sans-serif;color:#222;font-size:13px;}
a {color:#247EBF;text-decoration:none;}
a:hover {color:#EB9C39;border-bottom-width:1px;border-style:dotted;text-shadow:1px 1px 0 #ddd;}
blockquote {border:1px solid #cdcdcd;background:#F9F9F9;padding:8px;}
hr {margin:10px 30px;color:#fff;}
ul {margin-left:25px;}
p {margin:8px 0;}
h1 {color:#fff;font-size:26px;padding:10px 15px;text-shadow:1px 1px 0 #999;
background:#45484d;background:linear-gradient(to bottom, #45484d 0%,#333333 100%);
}
h2 {color:#fff;font-size:20px;padding:5px 10px;text-shadow:1px 1px 0 #555;
background:#f2a841;background:linear-gradient(to bottom, #f2a841 0%,#ef6b13 100%);
}
h2#errorWrapper {color:#F42C00;font-weight:normal;
background:#eaeaea;background:linear-gradient(to bottom, #eaeaea 0%, #afafaf 100%);
}
h3 {display:inline-block;padding:5px 10px;color:#555;font-weight:bold;text-align:center;
border-radius:8px 8px 0 0;text-shadow:1px 1px 0 #bbb;
background:#f2f2f2;background:linear-gradient(to bottom, #f2f2f2 0%,#cecece 100%);
}
#the_header {border-bottom:1px solid #cdcdcd;margin-bottom:1px;}
#the_footer {background:#EAEAEA;border-top:1px solid #cdcdcd;padding:10px;clear:both;}
#the_methods {width:250px;float:left;border-style:solid;border-color:#cdcdcd;border-width:1px 1px 0 0;
background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAH0lEQVQImSXHMQEAMAwCMOrfK0jIjuVL2gLBzyHJtgd7wBdU3Vt/7AAAAABJRU5ErkJggg==);
}
#methodsList {font-size:1.1em;margin:5px 0 10px 10px;list-style:none;}
#methodsList li:before {content:"\203A\00A0";font-weight:bold;color:#EB9C39;font-size:1.1em;}
#methodsList li:hover:before {content:"\00A0\203A";}
#the_page {margin-left:252px;border-style:solid;border-color:#cdcdcd;border-width:1px 0 0 1px;}
#the_content {padding:10px;}
#methodParams {margin-right:230px;}
#methodParams thead td {background:#DEE3E9;font-weight:bold;padding:2px 5px;}
#methodParams td {padding:2px;border:1px solid #cdcdcd;vertical-align:middle;}
#methodParams tbody tr:nth-child(even) {background:#f7f7f7;}
#methodParams tbody tr td:first-child {font-family:monospace;font-size:0.95em;}
#methodParams td.mini {width:0px;text-align:center;}
#methodParams tfoot {font-size:0.95em;}
#methodParams td.input {text-align:center;}
#methodParams td.input input[type="text"] {width:97%;font-size:0.9em;background:#f7f7f7;border:1px solid #ccc;border-radius:2px;}
#methodParams td.input input[type="text"]:hover, #methodParams td.input input[type="text"]:focus {border-color:#C7E2F1;border-top-color:#96BCD7;background:#fff;}
#methodParams .type {display:inline-block;width:16px;height:16px;font-size:12px;line-height:16px;background:#ddd;border-radius:8px;font-weight:bold;text-align:center;color:#222;}
#methodParams .subtype {vertical-align:super;}
#testForm {float:right;}
#testForm td {padding:2px 0;}
#testForm tr:last-child td {padding:8px 0 5px 0;}
#testForm blockquote {width:200px;}
#introMessage {font-size:1.1em;}
#urlForm {margin-bottom:10px;}
a.button {color:#fff;padding:3px 8px;border:1px solid #91bb5c;font-size:0.9em;margin-right:3px;display:inline-block;
border-radius:5px;text-shadow:1px 1px 0 #666;
background:#84bb3c;background:linear-gradient(to bottom, #84bb3c 0%, #3f5a1d 100%);
}
a.button:hover {color:#E5FF00;}
.methodInfo {float:right;display:inline-block;width:16px;height:16px;font-size:12px;line-height:16px;background:#555;border-radius:8px;font-family:"Times New Roman",sans-serif;font-style:italic;font-weight:bold;text-align:center;color:#fff;}
.methodInfo:hover {border:none;text-shadow:none;background:#888;cursor:pointer;color:#fff;}
#tiptip_content { font-size:12px; }
#iframeWrapper {width:100%;height:300px;padding:3px 3px 20px 3px;background:#F9F9F9;border:1px solid #cdcdcd;overflow:hidden;position:relative;}
iframe {width:100%;height:100%;background:#fff;}
div.onlys {background:#faa;color:#fff;border:1px solid #f22;padding:.25em .5em;display:table;border-radius:4px;margin-bottom:0.5em;}
</style>
</head>
<body>
<a name="top"></a>
<div id="the_header">
<h1>Piwigo web API (web-services) explorer</h1>
</div> <!-- the_header -->
<div id="the_methods">
<h2>Available methods</h2>
<ul id="methodsList">
</ul>
</div> <!-- the_methods -->
<div id="the_page">
<h2 id="methodName" style="display:none;"></h2>
<h2 id="errorWrapper" style="display:none;"></h2>
<div id="the_content">
<form id="urlForm" style="display:none;">
<input type="text" name="ws_url" size="60">
<input type="submit" value="Go!">
</form>
<blockquote id="introMessage">
<p>
<b>API = Application Programming Interface.</b><br>
This is the way other applications can communicate with Piwigo. This feature is also know as Web Services.
</p>
<p>Examples:</p>
<ul>
<li>Wordpress (web blog software) can display random photos from a Piwigo gallery in its sidebar</li>
<li>Lightroom (photo management software for desktop) can create albums and upload photos to Piwigo</li>
</ul>
<p>
This page lists all API methods available on your Piwigo installation, part of the Piwigo core or added by third-party plugins.
For each method you can consult required and optional parameters, and even test them in direct live!
</p>
<p>
For more information you can consult our Wiki <a href="http://piwigo.org/doc/doku.php?id=dev:webapi:start" target="_blank">Piwigo Web API</a> and <a href="http://piwigo.org/forum" target="_blank">our forums</a>.
</p>
</blockquote> <!-- introMessage -->
<form id="methodWrapper" style="display:none;">
<div id="methodDescription" style="display:none;">
<h3>Description</h3>
<blockquote>
</blockquote>
<br>
</div> <!-- methodDescription -->
<div id="testForm">
<h3>Test</h3>
<blockquote>
<table>
<tr>
<td>Request format :</td>
<td>
<select id="requestFormat">
<option value="get" selected>GET</option>
<option value="post">POST</option>
</select>
</td>
</tr>
<tr>
<td>Response format :</td>
<td>
<select id="responseFormat">
<option value="rest" selected>REST (xml)</option>
<option value="json">JSON</option>
<option value="php">PHP serial</option>
<option value="xmlrpc">XML RPC</option>
</select>
</td>
</tr>
<tr>
<td colspan="2">
<a href="#" class="button" id="invokeMethod">INVOKE</a>
<a href="#" class="button" id="invokeMethodBlank">INVOKE (new window)</a>
</td>
</tr>
</table>
</blockquote>
</div> <!-- testForm -->
<div id="methodParams">
<h3>Method parameters</h3>
<table>
<thead>
<tr>
<td style="width:150px;">Name</td>
<td class="mini">Extra</td>
<td class="mini">Type</td>
<td style="width:300px;">Value</td>
<td class="mini">Send</td>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td colspan="5">
<b>*</b>: required, <b>?</b>: optional, <b>[]</b>: can be an array (use a pipe | to split values)<br>
<b>B</b>: boolean, <b>I</b>: integer, <b>F</b>: float, <b>+</b>: positive, <b>&oslash;</b>: not null
</td>
</tr>
</tfoot>
</table>
</div> <!-- methodParams -->
<div id="requestDisplay" style="display:none;">
<br>
<h3>Request</h3>
<blockquote>
<pre class="url"></pre>
<pre class="params"></pre>
</blockquote>
</div> <!-- requestDisplay -->
<br>
<h3>Result</h3>
<div id="iframeWrapper">
<iframe src="" id="invokeFrame" name="invokeFrame"></iframe>
<a href="#iframe-bottom" id="increaseIframe"><b>&darr;</b> increase height</a> &#8226;
<a href="#iframe-bottom" id="decreaseIframe"><b>&uarr;</b> decrease height</a>
<a name="iframe-bottom"></a>
</div>
</form> <!-- iframeWrapper -->
<!-- hidden form for POST submition -->
<form method="post" action="" target="" id="invokeForm" style="display:none;"></form>
</div> <!-- the_content -->
</div> <!-- the_page -->
<div id="the_footer">
Copyright &copy; 2002-2016 <a href="http://piwigo.org">Piwigo Team</a>
</div> <!-- the_footer -->
<script src="//code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="//cdn.jsdelivr.net/tiptip/1.3/jquery.tipTip.minified.js"></script>
<script>
// global vars
var cachedMethods = new Array;
var ws_url = "http://";
// automatic detection of ws_url
match = document.location.toString().match(/^(https?.*\/)tools\/ws\.html?/);
if (match==null) {
askForUrl();
}
else {
ws_url = match[1]+'ws.php';
getMethodList();
}
// manual set of ws_url
$("#urlForm").submit(function() {
ws_url = $(this).children("input[name='ws_url']").val();
getMethodList();
return false;
});
// invoke buttons
$("#invokeMethod").click(function() {
invokeMethod($("#methodName").html(), false);
return false;
});
$("#invokeMethodBlank").click(function() {
invokeMethod($("#methodName").html(), true);
return false;
});
// resizable iframe
$("#increaseIframe").click(function() {
$("#iframeWrapper").css('height', $("#iframeWrapper").height()+100);
adaptHeight();
});
$("#decreaseIframe").click(function() {
if ($("#iframeWrapper").height() > 200) {
$("#iframeWrapper").css('height', $("#iframeWrapper").height()-100);
adaptHeight();
}
});
// mask all wrappers
function resetDisplay() {
$("#errorWrapper").hide();
$("#methodWrapper").hide();
$("#methodName").hide();
$("#urlForm").hide();
$("#methodDescription blockquote").empty();
$("#methodDescription").hide();
$("#requestDisplay").hide();
$("#invokeFrame").attr('src','');
}
// give the same size to methods list and main page
function adaptHeight() {
$("#the_page").css('height', 'auto');
$("#the_methods").css('height', 'auto');
min_h = $(window).height()-$("#the_header").outerHeight()-$("#the_footer").outerHeight()-3;
h = Math.max(min_h, Math.max($("#the_methods").height(), $("#the_page").height()));
$("#the_page").css('height', h);
$("#the_methods").css('height', h);
}
// display error wrapper
function displayError(error) {
resetDisplay();
$("#errorWrapper").html("<b>Error:</b> "+ error).show();
adaptHeight();
}
// display ws_url form
function askForUrl() {
displayError("can't contact web-services, please give absolute url to 'ws.php'");
if ($("#urlForm input[name='ws_url']").val() == "") {
$("#urlForm input[name='ws_url']").val(ws_url);
}
$("#urlForm").show();
}
// parse Piwigo JSON
function parsePwgJSON(json) {
try {
resp = jQuery.parseJSON(json);
if (resp==null | resp.result==null | resp.stat==null | resp.stat!='ok') {
throw new Error();
}
}
catch(e) {
displayError("unable to parse JSON string");
resp = {"stat": "ko", "result": "null"};
}
return resp.result;
}
// fetch methods list
function getMethodList() {
resetDisplay();
$.ajax({
type: "GET",
url: ws_url,
data: { format: "json", method: "reflection.getMethodList" }
}).done(function(result) {
result = parsePwgJSON(result);
if (result!=null) {
methods = result.methods;
var ml = '';
for (var i=0; i<methods.length; i++)
{
ml += '<li><a href="#top">'+ methods[i]+'</a></li>';
}
$("#methodsList").html(ml).show();
adaptHeight();
// trigger method selection
$("#methodsList li a").click(function() {
selectMethod($(this).html());
});
}
}).error(function(jqXHR, textStatus, errorThrown) {
askForUrl();
});
}
// select method
function selectMethod(methodName) {
$("#introMessage").hide();
$("#tiptip_holder").fadeOut(200);
if (cachedMethods[ methodName ]) {
fillNewMethod(methodName);
}
else {
$.ajax({
type: "GET",
url: ws_url,
data: { format: "json", method: "reflection.getMethodDetails", methodName: methodName }
}).done(function(result) {
result = parsePwgJSON(result);
if (result!=null) {
if (result.options.post_only || result.options.admin_only) {
var onlys = '<div class="onlys">';
if (result.options.post_only) {
onlys+= 'POST only. ';
}
if (result.options.admin_only) {
onlys+= 'Admin only. ';
}
onlys+= '</div>';
result.description = onlys + result.description;
}
cachedMethods[ methodName ] = result;
fillNewMethod(methodName);
}
}).error(function(jqXHR, textStatus, errorThrown) {
displayError("unknown error");
});
}
}
// display method details
function fillNewMethod(methodName) {
resetDisplay();
method = cachedMethods[ methodName ];
$("#methodName").html(method.name).show();
if (method.description != "") {
$("#methodDescription blockquote").html(method.description);
$("#methodDescription").show();
}
$("#requestFormat").val(method.options.post_only ? 'post' : 'get');
var methodParams = '';
if (method.params && method.params.length>0) {
for (var i=0; i<method.params.length; i++) {
var param = method.params[i],
isOptional = param.optional,
acceptArray = param.acceptArray,
defaultValue = param.defaultValue == null ? '' : param.defaultValue,
info = param.info == null ? '' : '<a class="methodInfo" title="'+ param.info.replace(/"/g, '&quot;') + '">i</a>',
type = '';
if (param.type.match(/bool/)) type+= '<span class=type>B</span>';
if (param.type.match(/int/)) type+= '<span class=type>I</span>';
if (param.type.match(/float/)) type+= '<span class=type>F</span>';
if (param.type.match(/positive/)) type+= '<span class=subtype>+</span>';
if (param.type.match(/notnull/)) type+= '<span class=subtype>&oslash;</span>';
// if an array is direclty printed, the delimiter is a comma where we use a pipe
if (typeof defaultValue == 'object') {
defaultValue = defaultValue.join('|');
}
methodParams+= '<tr>'+
'<td>'+ param.name + info +'</td>'+
'<td class="mini">'+ (isOptional ? '?':'*') + (acceptArray ? ' []':'') +'</td>'+
'<td class="mini">'+ type +'</td>'+
'<td class="input"><input type="text" class="methodParameterValue" data-id="'+ i +'" value="'+ defaultValue +'"></td>'+
'<td class="mini"><input type="checkbox" class="methodParameterSend" data-id="'+ i +'" '+ (isOptional ? '':'checked="checked"') +'></td>'+
'</tr>';
}
}
else {
methodParams = '<tr><td colspan="4">This method takes no parameters</td></tr>';
}
$("#methodParams tbody").html(methodParams);
$("#methodWrapper").show();
adaptHeight();
// trigger field modification
$("input.methodParameterValue").change(function() {
$("input.methodParameterSend[data-id='"+ $(this).data('id') +"']").attr('checked', 'checked');
});
// tiptip
$(".methodInfo").tipTip({
maxWidth:"300px",
defaultPosition:"right",
delay:0
});
}
// invoke method
function invokeMethod(methodName, newWindow) {
var method = cachedMethods[ methodName ];
var reqUrl = ws_url +"?format="+ $("#responseFormat").val();
// GET
if ($("#requestFormat").val() == 'get') {
reqUrl+= "&method="+ methodName;
for (var i=0; i<method.params.length; i++) {
if (! $("input.methodParameterSend[data-id='"+ i +"']").is(":checked")) {
continue;
}
var paramValue = $("input.methodParameterValue[data-id='"+ i +"']").val();
var paramSplitted = paramValue.split('|');
if (method.params[i].acceptArray && paramSplitted.length > 1) {
$.each(paramSplitted, function(v) {
reqUrl+= '&'+ method.params[i].name +'[]='+ paramSplitted[v];
});
}
else {
reqUrl+= '&'+ method.params[i].name +'='+ paramValue;
}
}
if (newWindow) {
window.open(reqUrl);
}
else {
$("#invokeFrame").attr('src', reqUrl);
}
$('#requestDisplay').show()
.find('.url').html(reqUrl).end()
.find('.params').hide();
}
// POST
else {
var params = {};
var form = $("#invokeForm");
form.attr('action', reqUrl);
var t = '<input type="hidden" name="method" value="'+ methodName +'">';
for (var i=0; i<method.params.length; i++) {
if (! $("input.methodParameterSend[data-id='"+ i +"']").is(":checked")) {
continue;
}
var paramValue = $("input.methodParameterValue[data-id='"+ i +"']").val(),
paramName = method.params[i].name,
paramSplitted = paramValue.split('|');
if (method.params[i].acceptArray && paramSplitted.length > 1) {
params[paramName] = [];
$.each(paramSplitted, function(i, value) {
params[paramName].push(value);
t+= '<input type="hidden" name="'+ paramName +'[]" value="'+ value +'">';
});
}
else {
params[paramName] = paramValue;
t+= '<input type="hidden" name="'+ paramName +'" value="'+ paramValue +'">';
}
}
form.html(t);
form.attr('target', newWindow ? "_blank" : "invokeFrame");
form.submit();
$('#requestDisplay').show()
.find('.url').html(reqUrl).end()
.find('.params').show().html(JSON.stringify(params, null, 4));
}
return false;
}
</script>
</body>
</html>