commit 677d1bffff14d4a503701120d95628616cb80719
Author: monsieur-a
Date: Sun Jul 6 13:21:10 2014 +0200
initial commit
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..eb3641e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+ynh_piwigo
+==========
diff --git a/conf/config.sql~ b/conf/config.sql~
new file mode 100644
index 0000000..3783d01
--- /dev/null
+++ b/conf/config.sql~
@@ -0,0 +1,66 @@
+-- initial configuration for Piwigo
+
+INSERT INTO piwigo_config (param,value,comment) VALUES ('activate_comments','true','Global parameter for usage of comments system');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('nb_comment_page','10','number of comments to display on each page');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('log','true','keep an history of visits on your website');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('comments_validation','false','administrators validate users comments before becoming visible');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('comments_forall','false','even guest not registered can post comments');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('comments_order','ASC','comments order on picture page and cie');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('comments_author_mandatory','false','Comment author is mandatory');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('comments_email_mandatory','false','Comment email is mandatory');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('user_can_delete_comment','false','administrators can allow user delete their own comments');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('user_can_edit_comment','false','administrators can allow user edit their own comments');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('email_admin_on_comment_edition','false','Send an email to the administrators when a comment is modified');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('email_admin_on_comment_deletion','false','Send an email to the administrators when a comment is deleted');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('gallery_locked','false','Lock your gallery temporary for non admin users');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('gallery_title','','Title at top of each page and for RSS feed');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('rate','true','Rating pictures feature is enabled');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('rate_anonymous','true','Rating pictures feature is also enabled for visitors');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('page_banner','','html displayed on the top each page of your gallery');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('history_admin','false','keep a history of administrator visits on your website');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('history_guest','true','keep a history of guest visits on your website');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('allow_user_registration','true','allow visitors to register?');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('allow_user_customization','true','allow users to customize their gallery?');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('nb_categories_page','12','Param for categories pagination');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('nbm_send_html_mail','true','Send mail on HTML format for notification by mail');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('nbm_send_mail_as','','Send mail as param value for notification by mail');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('nbm_send_detailed_content','true','Send detailed content for notification by mail');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('nbm_complementary_mail_content','','Complementary mail content for notification by mail');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('nbm_send_recent_post_dates','true','Send recent post by dates for notification by mail');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('email_admin_on_new_user','false','Send an email to theadministrators when a user registers');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('email_admin_on_comment','false','Send an email to the administrators when a valid comment is entered');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('email_admin_on_comment_validation','true','Send an email to the administrators when a comment requires validation');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('obligatory_user_mail_address','false','Mail address is obligatory for users');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('c13y_ignore',null,'List of ignored anomalies');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('extents_for_templates','a:0:{}','Actived template-extension(s)');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('blk_menubar','','Menubar options');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('menubar_filter_icon','false','Display filter icon');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('index_sort_order_input','true','Display image order selection list');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('index_flat_icon','false','Display flat icon');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('index_posted_date_icon','true','Display calendar by posted date');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('index_created_date_icon','true','Display calendar by creation date icon');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('index_slideshow_icon','true','Display slideshow icon');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('index_new_icon','true','Display new icons next albums and pictures');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('picture_metadata_icon','true','Display metadata icon on picture page');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('picture_slideshow_icon','true','Display slideshow icon on picture page');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('picture_favorite_icon','true','Display favorite icon on picture page');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('picture_download_icon','true','Display download icon on picture page');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('picture_navigation_icons','true','Display navigation icons on picture page');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('picture_navigation_thumb','true','Display navigation thumbnails on picture page');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('picture_menu','false','Show menubar on picture page');
+INSERT INTO piwigo_config (param,value,comment)
+ VALUES (
+ 'picture_informations',
+ 'a:11:{s:6:"author";b:1;s:10:"created_on";b:1;s:9:"posted_on";b:1;s:10:"dimensions";b:0;s:4:"file";b:0;s:8:"filesize";b:0;s:4:"tags";b:1;s:10:"categories";b:1;s:6:"visits";b:1;s:12:"rating_score";b:1;s:13:"privacy_level";b:1;}',
+ 'Information displayed on picture page'
+ );
+INSERT INTO piwigo_config (param,value,comment) VALUES ('week_starts_on','monday','Monday may not be the first day of the week');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('updates_ignored','a:3:{s:7:"plugins";a:0:{}s:6:"themes";a:0:{}s:9:"languages";a:0:{}}','Extensions ignored for update');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('order_by','ORDER BY date_available DESC, file ASC, id ASC','default photo order');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('order_by_inside_category','ORDER BY date_available DESC, file ASC, id ASC','default photo order inside category');
+INSERT INTO piwigo_config (param,value) VALUES ('original_resize','false');
+INSERT INTO piwigo_config (param,value) VALUES ('original_resize_maxwidth','2016');
+INSERT INTO piwigo_config (param,value) VALUES ('original_resize_maxheight','2016');
+INSERT INTO piwigo_config (param,value) VALUES ('original_resize_quality','95');
+INSERT INTO piwigo_config (param,value) VALUES ('mobile_theme',null);
+INSERT INTO piwigo_config (param,value) VALUES ('mail_theme','clear');
diff --git a/conf/database.inc.php b/conf/database.inc.php
new file mode 100644
index 0000000..c7db0ee
--- /dev/null
+++ b/conf/database.inc.php
@@ -0,0 +1,15 @@
+
diff --git a/conf/database.inc.php~ b/conf/database.inc.php~
new file mode 100644
index 0000000..b3e055f
--- /dev/null
+++ b/conf/database.inc.php~
@@ -0,0 +1,15 @@
+
diff --git a/conf/index.php b/conf/index.php
new file mode 100644
index 0000000..c8de97f
--- /dev/null
+++ b/conf/index.php
@@ -0,0 +1,30 @@
+
diff --git a/conf/nginx.conf b/conf/nginx.conf
new file mode 100644
index 0000000..f0d478a
--- /dev/null
+++ b/conf/nginx.conf
@@ -0,0 +1,18 @@
+location PATHTOCHANGE {
+ alias ALIASTOCHANGE;
+ client_max_body_size 10G;
+ index index.php;
+ default_type text/html;
+ location ~ [^/]\.php(/|$) {
+ fastcgi_split_path_info ^(.+?\.php)(/.*)$;
+ fastcgi_pass unix:/var/run/php5-fpm.sock;
+ fastcgi_index index.php;
+ include fastcgi_params;
+ fastcgi_param REMOTE_USER $remote_user;
+ fastcgi_param PATH_INFO $fastcgi_path_info;
+ }
+
+
+ # Include SSOWAT user panel.
+ #include conf.d/yunohost_panel.conf.inc;
+}
diff --git a/conf/nginx.conf~ b/conf/nginx.conf~
new file mode 100644
index 0000000..3f17a3f
--- /dev/null
+++ b/conf/nginx.conf~
@@ -0,0 +1,17 @@
+location PATHTOCHANGE {
+ alias ALIASTOCHANGE;
+ index index.php index.html index.htm;
+ default_type text/html;
+ location ~ [^/]\.php(/|$) {
+ fastcgi_split_path_info ^(.+?\.php)(/.*)$;
+ fastcgi_pass unix:/var/run/php5-fpm.sock;
+ fastcgi_index index.php;
+ include fastcgi_params;
+ fastcgi_param REMOTE_USER $remote_user;
+ fastcgi_param PATH_INFO $fastcgi_path_info;
+ }
+
+
+ # Include SSOWAT user panel.
+ #include conf.d/yunohost_panel.conf.inc;
+}
diff --git a/conf/php-fpm.conf b/conf/php-fpm.conf
new file mode 100644
index 0000000..c27d48a
--- /dev/null
+++ b/conf/php-fpm.conf
@@ -0,0 +1,239 @@
+; Start a new pool named 'www'.
+; the variable $pool can we used in any directive and will be replaced by the
+; pool name ('www' here)
+[NAMETOCHANGE]
+
+; Per pool prefix
+; It only applies on the following directives:
+; - 'slowlog'
+; - 'listen' (unixsocket)
+; - 'chroot'
+; - 'chdir'
+; - 'php_values'
+; - 'php_admin_values'
+; When not set, the global prefix (or /usr) applies instead.
+; Note: This directive can also be relative to the global prefix.
+; Default Value: none
+;prefix = /path/to/pools/$pool
+
+; The address on which to accept FastCGI requests.
+; Valid syntaxes are:
+; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific address on
+; a specific port;
+; 'port' - to listen on a TCP socket to all addresses on a
+; specific port;
+; '/path/to/unix/socket' - to listen on a unix socket.
+; Note: This value is mandatory.
+listen = /var/run/php5-fpm-NAMETOCHANGE.sock
+
+; Set listen(2) backlog. A value of '-1' means unlimited.
+; Default Value: 128 (-1 on FreeBSD and OpenBSD)
+;listen.backlog = -1
+
+; List of ipv4 addresses of FastCGI clients which are allowed to connect.
+; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original
+; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address
+; must be separated by a comma. If this value is left blank, connections will be
+; accepted from any ip address.
+; Default Value: any
+;listen.allowed_clients = 127.0.0.1
+
+; Set permissions for unix socket, if one is used. In Linux, read/write
+; permissions must be set in order to allow connections from a web server. Many
+; BSD-derived systems allow connections regardless of permissions.
+; Default Values: user and group are set as the running user
+; mode is set to 0666
+listen.owner = www-data
+listen.group = www-data
+listen.mode = 0600
+
+; Unix user/group of processes
+; Note: The user is mandatory. If the group is not set, the default user's group
+; will be used.
+user = NAMETOCHANGE
+group = NAMETOCHANGE
+
+; Choose how the process manager will control the number of child processes.
+; Possible Values:
+; static - a fixed number (pm.max_children) of child processes;
+; dynamic - the number of child processes are set dynamically based on the
+; following directives:
+; pm.max_children - the maximum number of children that can
+; be alive at the same time.
+; pm.start_servers - the number of children created on startup.
+; pm.min_spare_servers - the minimum number of children in 'idle'
+; state (waiting to process). If the number
+; of 'idle' processes is less than this
+; number then some children will be created.
+; pm.max_spare_servers - the maximum number of children in 'idle'
+; state (waiting to process). If the number
+; of 'idle' processes is greater than this
+; number then some children will be killed.
+; Note: This value is mandatory.
+pm = dynamic
+
+; The number of child processes to be created when pm is set to 'static' and the
+; maximum number of child processes to be created when pm is set to 'dynamic'.
+; This value sets the limit on the number of simultaneous requests that will be
+; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
+; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
+; CGI.
+; Note: Used when pm is set to either 'static' or 'dynamic'
+; Note: This value is mandatory.
+pm.max_children = 6
+
+; The number of child processes created on startup.
+; Note: Used only when pm is set to 'dynamic'
+; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2
+pm.start_servers = 3
+
+; The desired minimum number of idle server processes.
+; Note: Used only when pm is set to 'dynamic'
+; Note: Mandatory when pm is set to 'dynamic'
+pm.min_spare_servers = 3
+
+; The desired maximum number of idle server processes.
+; Note: Used only when pm is set to 'dynamic'
+; Note: Mandatory when pm is set to 'dynamic'
+pm.max_spare_servers = 5
+
+; The number of requests each child process should execute before respawning.
+; This can be useful to work around memory leaks in 3rd party libraries. For
+; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS.
+; Default Value: 0
+pm.max_requests = 500
+
+; The URI to view the FPM status page. If this value is not set, no URI will be
+; recognized as a status page. By default, the status page shows the following
+; information:
+; accepted conn - the number of request accepted by the pool;
+; pool - the name of the pool;
+; process manager - static or dynamic;
+; idle processes - the number of idle processes;
+; active processes - the number of active processes;
+; total processes - the number of idle + active processes.
+; max children reached - number of times, the process limit has been reached,
+; when pm tries to start more children (works only for
+; pm 'dynamic')
+; The values of 'idle processes', 'active processes' and 'total processes' are
+; updated each second. The value of 'accepted conn' is updated in real time.
+; Example output:
+; accepted conn: 12073
+; pool: www
+; process manager: static
+; idle processes: 35
+; active processes: 65
+; total processes: 100
+; max children reached: 1
+; By default the status page output is formatted as text/plain. Passing either
+; 'html' or 'json' as a query string will return the corresponding output
+; syntax. Example:
+; http://www.foo.bar/status
+; http://www.foo.bar/status?json
+; http://www.foo.bar/status?html
+; Note: The value must start with a leading slash (/). The value can be
+; anything, but it may not be a good idea to use the .php extension or it
+; may conflict with a real PHP file.
+; Default Value: not set
+pm.status_path = /fpm-status
+
+; The ping URI to call the monitoring page of FPM. If this value is not set, no
+; URI will be recognized as a ping page. This could be used to test from outside
+; that FPM is alive and responding, or to
+; - create a graph of FPM availability (rrd or such);
+; - remove a server from a group if it is not responding (load balancing);
+; - trigger alerts for the operating team (24/7).
+; Note: The value must start with a leading slash (/). The value can be
+; anything, but it may not be a good idea to use the .php extension or it
+; may conflict with a real PHP file.
+; Default Value: not set
+ping.path = /ping
+
+; This directive may be used to customize the response of a ping request. The
+; response is formatted as text/plain with a 200 response code.
+; Default Value: pong
+;ping.response = pong
+
+; The timeout for serving a single request after which the worker process will
+; be killed. This option should be used when the 'max_execution_time' ini option
+; does not stop script execution for some reason. A value of '0' means 'off'.
+; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
+; Default Value: 0
+request_terminate_timeout = 120s
+
+; The timeout for serving a single request after which a PHP backtrace will be
+; dumped to the 'slowlog' file. A value of '0s' means 'off'.
+; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
+; Default Value: 0
+request_slowlog_timeout = 5s
+
+; The log file for slow requests
+; Default Value: not set
+; Note: slowlog is mandatory if request_slowlog_timeout is set
+slowlog = /var/log/nginx/NAMETOCHANGE.slow.log
+
+; Set open file descriptor rlimit.
+; Default Value: system defined value
+rlimit_files = 4096
+
+; Set max core size rlimit.
+; Possible Values: 'unlimited' or an integer greater or equal to 0
+; Default Value: system defined value
+rlimit_core = 0
+
+; Chroot to this directory at the start. This value must be defined as an
+; absolute path. When this value is not set, chroot is not used.
+; Note: you can prefix with '$prefix' to chroot to the pool prefix or one
+; of its subdirectories. If the pool prefix is not set, the global prefix
+; will be used instead.
+; Note: chrooting is a great security feature and should be used whenever
+; possible. However, all PHP paths will be relative to the chroot
+; (error_log, sessions.save_path, ...).
+; Default Value: not set
+;chroot =
+
+; Chdir to this directory at the start.
+; Note: relative path can be used.
+; Default Value: current directory or / when chroot
+chdir = /var/www/NAMETOCHANGE
+
+; Redirect worker stdout and stderr into main error log. If not set, stdout and
+; stderr will be redirected to /dev/null according to FastCGI specs.
+; Note: on highloaded environement, this can cause some delay in the page
+; process time (several ms).
+; Default Value: no
+catch_workers_output = yes
+
+; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from
+; the current environment.
+; Default Value: clean env
+;env[HOSTNAME] = $HOSTNAME
+;env[PATH] = /usr/local/bin:/usr/bin:/bin
+;env[TMP] = /tmp
+;env[TMPDIR] = /tmp
+;env[TEMP] = /tmp
+
+; Additional php.ini defines, specific to this pool of workers. These settings
+; overwrite the values previously defined in the php.ini. The directives are the
+; same as the PHP SAPI:
+; php_value/php_flag - you can set classic ini defines which can
+; be overwritten from PHP call 'ini_set'.
+; php_admin_value/php_admin_flag - these directives won't be overwritten by
+; PHP call 'ini_set'
+; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no.
+
+; Defining 'extension' will load the corresponding shared extension from
+; extension_dir. Defining 'disable_functions' or 'disable_classes' will not
+; overwrite previously defined php.ini values, but will append the new value
+; instead.
+
+; Note: path INI options can be relative and will be expanded with the prefix
+; (pool, global or /usr)
+
+; Default Value: nothing is defined by default except the values in php.ini and
+; specified at startup with the -d argument
+;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com
+;php_flag[display_errors] = off
+;php_admin_value[error_log] = /var/log/fpm-php.www.log
+;php_admin_flag[log_errors] = on
+;php_admin_value[memory_limit] = 32M
diff --git a/conf/php-fpm.conf~ b/conf/php-fpm.conf~
new file mode 100644
index 0000000..e69de29
diff --git a/conf/php-fpm.ini b/conf/php-fpm.ini
new file mode 100644
index 0000000..5cf1490
--- /dev/null
+++ b/conf/php-fpm.ini
@@ -0,0 +1,2 @@
+upload_max_filesize=10G
+post_max_size=10G
diff --git a/conf/php-fpm.ini~ b/conf/php-fpm.ini~
new file mode 100644
index 0000000..e69de29
diff --git a/conf/piwigo.sql~ b/conf/piwigo.sql~
new file mode 100644
index 0000000..3916f7f
--- /dev/null
+++ b/conf/piwigo.sql~
@@ -0,0 +1,977 @@
+-- phpMyAdmin SQL Dump
+-- version 4.2.0
+-- http://www.phpmyadmin.net
+--
+-- Client : localhost
+-- Généré le : Sam 28 Juin 2014 à 23:02
+-- Version du serveur : 5.5.37-0+wheezy1
+-- Version de PHP : 5.4.4-14+deb7u11
+USE piwigo;
+SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
+SET time_zone = "+00:00";
+
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+
+--
+-- Base de données : `piwigo`
+--
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_caddie`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_caddie` (
+ `user_id` smallint(5) NOT NULL DEFAULT '0',
+ `element_id` mediumint(8) NOT NULL DEFAULT '0'
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_categories`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_categories` (
+`id` smallint(5) unsigned NOT NULL,
+ `name` varchar(255) NOT NULL DEFAULT '',
+ `id_uppercat` smallint(5) unsigned DEFAULT NULL,
+ `comment` text,
+ `dir` varchar(255) DEFAULT NULL,
+ `rank` smallint(5) unsigned DEFAULT NULL,
+ `status` enum('public','private') NOT NULL DEFAULT 'public',
+ `site_id` tinyint(4) unsigned DEFAULT NULL,
+ `visible` enum('true','false') NOT NULL DEFAULT 'true',
+ `representative_picture_id` mediumint(8) unsigned DEFAULT NULL,
+ `uppercats` varchar(255) NOT NULL DEFAULT '',
+ `commentable` enum('true','false') NOT NULL DEFAULT 'true',
+ `global_rank` varchar(255) DEFAULT NULL,
+ `image_order` varchar(128) DEFAULT NULL,
+ `permalink` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_comments`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_comments` (
+`id` int(11) unsigned NOT NULL,
+ `image_id` mediumint(8) unsigned NOT NULL DEFAULT '0',
+ `date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+ `author` varchar(255) DEFAULT NULL,
+ `email` varchar(255) DEFAULT NULL,
+ `author_id` smallint(5) DEFAULT NULL,
+ `anonymous_id` varchar(45) NOT NULL,
+ `website_url` varchar(255) DEFAULT NULL,
+ `content` longtext,
+ `validated` enum('true','false') NOT NULL DEFAULT 'false',
+ `validation_date` datetime DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_config`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_config` (
+ `param` varchar(40) NOT NULL DEFAULT '',
+ `value` text,
+ `comment` varchar(255) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='configuration table';
+
+--
+-- Contenu de la table `piwigo_config`
+--
+
+INSERT INTO `piwigo_config` (`param`, `value`, `comment`) VALUES
+('activate_comments', 'true', 'Global parameter for usage of comments system'),
+('nb_comment_page', '10', 'number of comments to display on each page'),
+('log', 'true', 'keep an history of visits on your website'),
+('comments_validation', 'false', 'administrators validate users comments before becoming visible'),
+('comments_forall', 'false', 'even guest not registered can post comments'),
+('comments_order', 'ASC', 'comments order on picture page and cie'),
+('comments_author_mandatory', 'false', 'Comment author is mandatory'),
+('comments_email_mandatory', 'false', 'Comment email is mandatory'),
+('user_can_delete_comment', 'false', 'administrators can allow user delete their own comments'),
+('user_can_edit_comment', 'false', 'administrators can allow user edit their own comments'),
+('email_admin_on_comment_edition', 'false', 'Send an email to the administrators when a comment is modified'),
+('email_admin_on_comment_deletion', 'false', 'Send an email to the administrators when a comment is deleted'),
+('gallery_locked', 'false', 'Lock your gallery temporary for non admin users'),
+('gallery_title', 'Une galerie Piwigo de plus', 'Title at top of each page and for RSS feed'),
+('rate', 'true', 'Rating pictures feature is enabled'),
+('rate_anonymous', 'true', 'Rating pictures feature is also enabled for visitors'),
+('page_banner', '%gallery_title% \n\nBienvenue sur ma galerie photo
', 'html displayed on the top each page of your gallery'),
+('history_admin', 'false', 'keep a history of administrator visits on your website'),
+('history_guest', 'true', 'keep a history of guest visits on your website'),
+('allow_user_registration', 'true', 'allow visitors to register?'),
+('allow_user_customization', 'true', 'allow users to customize their gallery?'),
+('nb_categories_page', '12', 'Param for categories pagination'),
+('nbm_send_html_mail', 'true', 'Send mail on HTML format for notification by mail'),
+('nbm_send_mail_as', '', 'Send mail as param value for notification by mail'),
+('nbm_send_detailed_content', 'true', 'Send detailed content for notification by mail'),
+('nbm_complementary_mail_content', '', 'Complementary mail content for notification by mail'),
+('nbm_send_recent_post_dates', 'true', 'Send recent post by dates for notification by mail'),
+('email_admin_on_new_user', 'false', 'Send an email to theadministrators when a user registers'),
+('email_admin_on_comment', 'false', 'Send an email to the administrators when a valid comment is entered'),
+('email_admin_on_comment_validation', 'true', 'Send an email to the administrators when a comment requires validation'),
+('obligatory_user_mail_address', 'false', 'Mail address is obligatory for users'),
+('c13y_ignore', 'a:2:{s:7:"version";s:5:"2.6.3";s:4:"list";a:0:{}}', 'List of ignored anomalies'),
+('extents_for_templates', 'a:0:{}', 'Actived template-extension(s)'),
+('blk_menubar', '', 'Menubar options'),
+('menubar_filter_icon', 'false', 'Display filter icon'),
+('index_sort_order_input', 'true', 'Display image order selection list'),
+('index_flat_icon', 'false', 'Display flat icon'),
+('index_posted_date_icon', 'true', 'Display calendar by posted date'),
+('index_created_date_icon', 'true', 'Display calendar by creation date icon'),
+('index_slideshow_icon', 'true', 'Display slideshow icon'),
+('index_new_icon', 'true', 'Display new icons next albums and pictures'),
+('picture_metadata_icon', 'true', 'Display metadata icon on picture page'),
+('picture_slideshow_icon', 'true', 'Display slideshow icon on picture page'),
+('picture_favorite_icon', 'true', 'Display favorite icon on picture page'),
+('picture_download_icon', 'true', 'Display download icon on picture page'),
+('picture_navigation_icons', 'true', 'Display navigation icons on picture page'),
+('picture_navigation_thumb', 'true', 'Display navigation thumbnails on picture page'),
+('picture_menu', 'false', 'Show menubar on picture page'),
+('picture_informations', 'a:11:{s:6:"author";b:1;s:10:"created_on";b:1;s:9:"posted_on";b:1;s:10:"dimensions";b:0;s:4:"file";b:0;s:8:"filesize";b:0;s:4:"tags";b:1;s:10:"categories";b:1;s:6:"visits";b:1;s:12:"rating_score";b:1;s:13:"privacy_level";b:1;}', 'Information displayed on picture page'),
+('week_starts_on', 'monday', 'Monday may not be the first day of the week'),
+('updates_ignored', 'a:3:{s:7:"plugins";a:0:{}s:6:"themes";a:0:{}s:9:"languages";a:0:{}}', 'Extensions ignored for update'),
+('order_by', 'ORDER BY date_available DESC, file ASC, id ASC', 'default photo order'),
+('order_by_inside_category', 'ORDER BY date_available DESC, file ASC, id ASC', 'default photo order inside category'),
+('original_resize', 'false', NULL),
+('original_resize_maxwidth', '2016', NULL),
+('original_resize_maxheight', '2016', NULL),
+('original_resize_quality', '95', NULL),
+('mobile_theme', 'smartpocket', NULL),
+('mail_theme', 'clear', NULL),
+('secret_key', '8c52ac66f4b2bb51a8e2a850990e900d', 'a secret key specific to the gallery for internal use'),
+('piwigo_db_version', '2.6', NULL),
+('smartpocket', 'a:2:{s:4:"loop";b:1;s:8:"autohide";i:5000;}', 'loop#autohide'),
+('derivatives', 'a:4:{s:1:"d";a:9:{s:6:"square";O:16:"DerivativeParams":3:{s:13:"last_mod_time";i:1403987679;s:6:"sizing";O:12:"SizingParams":3:{s:10:"ideal_size";a:2:{i:0;i:120;i:1;i:120;}s:8:"max_crop";i:1;s:8:"min_size";a:2:{i:0;i:120;i:1;i:120;}}s:7:"sharpen";i:0;}s:5:"thumb";O:16:"DerivativeParams":3:{s:13:"last_mod_time";i:1403987679;s:6:"sizing";O:12:"SizingParams":3:{s:10:"ideal_size";a:2:{i:0;i:144;i:1;i:144;}s:8:"max_crop";i:0;s:8:"min_size";N;}s:7:"sharpen";i:0;}s:6:"2small";O:16:"DerivativeParams":3:{s:13:"last_mod_time";i:1403987679;s:6:"sizing";O:12:"SizingParams":3:{s:10:"ideal_size";a:2:{i:0;i:240;i:1;i:240;}s:8:"max_crop";i:0;s:8:"min_size";N;}s:7:"sharpen";i:0;}s:6:"xsmall";O:16:"DerivativeParams":3:{s:13:"last_mod_time";i:1403987679;s:6:"sizing";O:12:"SizingParams":3:{s:10:"ideal_size";a:2:{i:0;i:432;i:1;i:324;}s:8:"max_crop";i:0;s:8:"min_size";N;}s:7:"sharpen";i:0;}s:5:"small";O:16:"DerivativeParams":3:{s:13:"last_mod_time";i:1403987679;s:6:"sizing";O:12:"SizingParams":3:{s:10:"ideal_size";a:2:{i:0;i:576;i:1;i:432;}s:8:"max_crop";i:0;s:8:"min_size";N;}s:7:"sharpen";i:0;}s:6:"medium";O:16:"DerivativeParams":3:{s:13:"last_mod_time";i:1403987679;s:6:"sizing";O:12:"SizingParams":3:{s:10:"ideal_size";a:2:{i:0;i:792;i:1;i:594;}s:8:"max_crop";i:0;s:8:"min_size";N;}s:7:"sharpen";i:0;}s:5:"large";O:16:"DerivativeParams":3:{s:13:"last_mod_time";i:1403987679;s:6:"sizing";O:12:"SizingParams":3:{s:10:"ideal_size";a:2:{i:0;i:1008;i:1;i:756;}s:8:"max_crop";i:0;s:8:"min_size";N;}s:7:"sharpen";i:0;}s:6:"xlarge";O:16:"DerivativeParams":3:{s:13:"last_mod_time";i:1403987679;s:6:"sizing";O:12:"SizingParams":3:{s:10:"ideal_size";a:2:{i:0;i:1224;i:1;i:918;}s:8:"max_crop";i:0;s:8:"min_size";N;}s:7:"sharpen";i:0;}s:7:"xxlarge";O:16:"DerivativeParams":3:{s:13:"last_mod_time";i:1403987679;s:6:"sizing";O:12:"SizingParams":3:{s:10:"ideal_size";a:2:{i:0;i:1656;i:1;i:1242;}s:8:"max_crop";i:0;s:8:"min_size";N;}s:7:"sharpen";i:0;}}s:1:"q";i:95;s:1:"w";O:15:"WatermarkParams":6:{s:4:"file";s:0:"";s:8:"min_size";a:2:{i:0;i:500;i:1;i:500;}s:4:"xpos";i:50;s:4:"ypos";i:50;s:7:"xrepeat";i:0;s:7:"opacity";i:100;}s:1:"c";a:0:{}}', NULL),
+('data_dir_checked', '1', NULL),
+('elegant', 'a:3:{s:11:"p_main_menu";s:2:"on";s:12:"p_pict_descr";s:2:"on";s:14:"p_pict_comment";s:3:"off";}', 'p_main_menu#'),
+('no_photo_yet', 'false', NULL);
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_favorites`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_favorites` (
+ `user_id` smallint(5) NOT NULL DEFAULT '0',
+ `image_id` mediumint(8) unsigned NOT NULL DEFAULT '0'
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_groups`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_groups` (
+`id` smallint(5) unsigned NOT NULL,
+ `name` varchar(255) NOT NULL DEFAULT '',
+ `is_default` enum('true','false') NOT NULL DEFAULT 'false'
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_group_access`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_group_access` (
+ `group_id` smallint(5) unsigned NOT NULL DEFAULT '0',
+ `cat_id` smallint(5) unsigned NOT NULL DEFAULT '0'
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_history`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_history` (
+`id` int(10) unsigned NOT NULL,
+ `date` date NOT NULL DEFAULT '0000-00-00',
+ `time` time NOT NULL DEFAULT '00:00:00',
+ `user_id` smallint(5) NOT NULL DEFAULT '0',
+ `IP` varchar(15) NOT NULL DEFAULT '',
+ `section` enum('categories','tags','search','list','favorites','most_visited','best_rated','recent_pics','recent_cats') DEFAULT NULL,
+ `category_id` smallint(5) DEFAULT NULL,
+ `tag_ids` varchar(50) DEFAULT NULL,
+ `image_id` mediumint(8) DEFAULT NULL,
+ `summarized` enum('true','false') DEFAULT 'false',
+ `image_type` enum('picture','high','other') DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=9 ;
+
+--
+-- Contenu de la table `piwigo_history`
+--
+
+INSERT INTO `piwigo_history` (`id`, `date`, `time`, `user_id`, `IP`, `section`, `category_id`, `tag_ids`, `image_id`, `summarized`, `image_type`) VALUES
+(1, '2014-06-28', '22:50:57', 2, '192.168.0.13', 'categories', NULL, NULL, NULL, 'false', NULL),
+(2, '2014-06-28', '22:54:12', 2, '192.168.0.13', 'categories', NULL, NULL, NULL, 'false', NULL),
+(3, '2014-06-28', '22:56:13', 2, '192.168.0.13', 'categories', NULL, NULL, NULL, 'false', NULL),
+(4, '2014-06-28', '22:57:03', 2, '192.168.0.13', 'categories', NULL, NULL, NULL, 'false', NULL),
+(5, '2014-06-28', '22:57:35', 2, '192.168.0.13', 'categories', NULL, NULL, NULL, 'false', NULL),
+(6, '2014-06-28', '22:58:24', 2, '192.168.0.13', 'categories', NULL, NULL, NULL, 'false', NULL),
+(7, '2014-06-28', '22:58:59', 3, '192.168.0.13', 'categories', NULL, NULL, NULL, 'false', NULL),
+(8, '2014-06-28', '22:59:07', 2, '192.168.0.13', 'categories', NULL, NULL, NULL, 'false', NULL);
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_history_summary`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_history_summary` (
+ `year` smallint(4) NOT NULL DEFAULT '0',
+ `month` tinyint(2) DEFAULT NULL,
+ `day` tinyint(2) DEFAULT NULL,
+ `hour` tinyint(2) DEFAULT NULL,
+ `nb_pages` int(11) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_images`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_images` (
+`id` mediumint(8) unsigned NOT NULL,
+ `file` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
+ `date_available` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+ `date_creation` datetime DEFAULT NULL,
+ `name` varchar(255) DEFAULT NULL,
+ `comment` text,
+ `author` varchar(255) DEFAULT NULL,
+ `hit` int(10) unsigned NOT NULL DEFAULT '0',
+ `filesize` mediumint(9) unsigned DEFAULT NULL,
+ `width` smallint(9) unsigned DEFAULT NULL,
+ `height` smallint(9) unsigned DEFAULT NULL,
+ `coi` char(4) DEFAULT NULL COMMENT 'center of interest',
+ `representative_ext` varchar(4) DEFAULT NULL,
+ `date_metadata_update` date DEFAULT NULL,
+ `rating_score` float(5,2) unsigned DEFAULT NULL,
+ `path` varchar(255) NOT NULL DEFAULT '',
+ `storage_category_id` smallint(5) unsigned DEFAULT NULL,
+ `level` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `md5sum` char(32) DEFAULT NULL,
+ `added_by` smallint(5) NOT NULL DEFAULT '0',
+ `rotation` tinyint(3) unsigned DEFAULT NULL,
+ `latitude` double(8,6) DEFAULT NULL,
+ `longitude` double(9,6) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_image_category`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_image_category` (
+ `image_id` mediumint(8) unsigned NOT NULL DEFAULT '0',
+ `category_id` smallint(5) unsigned NOT NULL DEFAULT '0',
+ `rank` mediumint(8) unsigned DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_image_tag`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_image_tag` (
+ `image_id` mediumint(8) unsigned NOT NULL DEFAULT '0',
+ `tag_id` smallint(5) unsigned NOT NULL DEFAULT '0'
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_languages`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_languages` (
+ `id` varchar(64) NOT NULL DEFAULT '',
+ `version` varchar(64) NOT NULL DEFAULT '0',
+ `name` varchar(64) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+--
+-- Contenu de la table `piwigo_languages`
+--
+
+INSERT INTO `piwigo_languages` (`id`, `version`, `name`) VALUES
+('af_ZA', '2.6.0', 'Afrikaans [ZA]'),
+('es_AR', '2.6.0', 'Argentina [AR]'),
+('az_AZ', '2.5.0', 'Azərbaycanca [AZ]'),
+('id_ID', '2.6.0', 'Bahasa Indonesia [ID]'),
+('pt_BR', '2.6.1', 'Brasil [BR]'),
+('br_FR', '2.6.2', 'Brezhoneg [FR]'),
+('ca_ES', '2.6.1', 'Català [CA]'),
+('da_DK', '2.6.0', 'Dansk [DK]'),
+('de_DE', '2.6.0', 'Deutsch [DE]'),
+('dv_MV', '2.5.0', 'Dhivehi [MV]'),
+('en_GB', '2.6.1', 'English [GB]'),
+('en_UK', '2.6.0', 'English [UK]'),
+('en_US', '2.6.0', 'English [US]'),
+('es_ES', '2.6.0', 'Español [ES]'),
+('eo_EO', '2.6.0', 'Esperanto [EO]'),
+('et_EE', '2.6.0', 'Estonian [EE]'),
+('fi_FI', '2.6.0', 'Finnish [FI]'),
+('fr_FR', '2.6.0', 'Français [FR]'),
+('fr_CA', '2.6.0', 'Français [QC]'),
+('ga_IE', '2.5.0', 'Gaeilge [IE]'),
+('gl_ES', '2.6.1', 'Galego [ES]'),
+('hr_HR', '2.6.0', 'Hrvatski [HR]'),
+('it_IT', '2.6.0', 'Italiano [IT]'),
+('lv_LV', '2.6.0', 'Latviešu [LV]'),
+('lt_LT', '2.6.0', 'Lietuviu [LT]'),
+('lb_LU', '2.5.0', 'Lëtzebuergesch [LU]'),
+('hu_HU', '2.6.0', 'Magyar [HU]'),
+('ms_MY', '2.5.0', 'Malay [MY]'),
+('nl_NL', '2.6.0', 'Nederlands [NL]'),
+('nb_NO', '2.6.0', 'Norwegian [NO]'),
+('nn_NO', '2.6.0', 'Norwegian nynorsk [NO]'),
+('pl_PL', '2.6.1', 'Polski [PL]'),
+('pt_PT', '2.6.0', 'Português [PT]'),
+('ro_RO', '2.6.0', 'Română [RO]'),
+('sk_SK', '2.6.0', 'Slovensky [SK]'),
+('sl_SI', '2.6.1', 'Slovenšcina [SI]'),
+('sh_RS', '2.6.1', 'Srpski [SR]'),
+('sv_SE', '2.6.0', 'Svenska [SE]'),
+('vi_VN', '2.6.0', 'Tiếng Việt [VN]'),
+('tr_TR', '2.6.0', 'Türkçe [TR]'),
+('is_IS', '2.6.0', 'Íslenska [IS]'),
+('cs_CZ', '2.6.0', 'Česky [CZ]'),
+('el_GR', '2.6.0', 'Ελληνικά [GR]'),
+('bg_BG', '2.6.0', 'Български [BG]'),
+('mk_MK', '2.6.0', 'Македонски [MK]'),
+('mn_MN', '2.6.2', 'Монгол [MN]'),
+('ru_RU', '2.6.0', 'Русский [RU]'),
+('sr_RS', '2.6.1', 'Српски [SR]'),
+('uk_UA', '2.6.0', 'Українська [UA]'),
+('he_IL', '2.6.2', 'עברית [IL]'),
+('ar_SA', '2.6.0', 'العربية [AR]'),
+('ar_MA', '2.5.0', 'العربية [MA]'),
+('fa_IR', '2.6.0', 'پارسی [IR]'),
+('kok_IN', '2.5.0', 'कोंकणी [IN]'),
+('bn_IN', '2.5.0', 'বাংলা[IN]'),
+('ta_IN', '2.6.1', 'தமிழ் [IN]'),
+('kn_IN', '2.6.0', 'ಕನ್ನಡ [IN]'),
+('th_TH', '2.6.0', 'ภาษาไทย [TH]'),
+('ka_GE', '2.6.0', 'ქართული [GE]'),
+('km_KH', '2.6.2', 'ខ្មែរ [KH]'),
+('zh_TW', '2.6.0', '中文 (繁體) [TW]'),
+('zh_HK', '2.6.0', '中文 (香港) [HK]'),
+('ja_JP', '2.6.0', '日本語 [JP]'),
+('zh_CN', '2.6.0', '简体中文 [CN]'),
+('ko_KR', '2.6.0', '한국어 [KR]');
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_old_permalinks`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_old_permalinks` (
+ `cat_id` smallint(5) unsigned NOT NULL DEFAULT '0',
+ `permalink` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
+ `date_deleted` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+ `last_hit` datetime DEFAULT NULL,
+ `hit` int(10) unsigned NOT NULL DEFAULT '0'
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_plugins`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_plugins` (
+ `id` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
+ `state` enum('inactive','active') NOT NULL DEFAULT 'inactive',
+ `version` varchar(64) NOT NULL DEFAULT '0'
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+--
+-- Contenu de la table `piwigo_plugins`
+--
+
+INSERT INTO `piwigo_plugins` (`id`, `state`, `version`) VALUES
+('Ldap_Login', 'active', '1.1');
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_rate`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_rate` (
+ `user_id` smallint(5) NOT NULL DEFAULT '0',
+ `element_id` mediumint(8) unsigned NOT NULL DEFAULT '0',
+ `anonymous_id` varchar(45) NOT NULL DEFAULT '',
+ `rate` tinyint(2) unsigned NOT NULL DEFAULT '0',
+ `date` date NOT NULL DEFAULT '0000-00-00'
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_search`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_search` (
+`id` int(10) unsigned NOT NULL,
+ `last_seen` date DEFAULT NULL,
+ `rules` text
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_sessions`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_sessions` (
+ `id` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
+ `data` mediumtext NOT NULL,
+ `expiration` datetime NOT NULL DEFAULT '0000-00-00 00:00:00'
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+--
+-- Contenu de la table `piwigo_sessions`
+--
+
+INSERT INTO `piwigo_sessions` (`id`, `data`, `expiration`) VALUES
+('C0A8q02887miht1f856vh8u19emb13', 'pwg_device|s:7:"desktop";pwg_mobile_theme|b:0;', '2014-06-28 22:59:15');
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_sites`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_sites` (
+`id` tinyint(4) NOT NULL,
+ `galleries_url` varchar(255) NOT NULL DEFAULT ''
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;
+
+--
+-- Contenu de la table `piwigo_sites`
+--
+
+INSERT INTO `piwigo_sites` (`id`, `galleries_url`) VALUES
+(1, './galleries/');
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_tags`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_tags` (
+`id` smallint(5) unsigned NOT NULL,
+ `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
+ `url_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT ''
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_themes`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_themes` (
+ `id` varchar(64) NOT NULL DEFAULT '',
+ `version` varchar(64) NOT NULL DEFAULT '0',
+ `name` varchar(64) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+--
+-- Contenu de la table `piwigo_themes`
+--
+
+INSERT INTO `piwigo_themes` (`id`, `version`, `name`) VALUES
+('smartpocket', '2.6.2', 'Smart Pocket'),
+('elegant', '2.6.2', 'elegant');
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_upgrade`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_upgrade` (
+ `id` varchar(20) NOT NULL DEFAULT '',
+ `applied` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+ `description` varchar(255) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+--
+-- Contenu de la table `piwigo_upgrade`
+--
+
+INSERT INTO `piwigo_upgrade` (`id`, `applied`, `description`) VALUES
+('61', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('62', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('63', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('64', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('65', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('66', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('67', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('68', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('69', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('70', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('71', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('72', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('73', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('74', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('75', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('76', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('77', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('78', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('79', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('80', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('81', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('82', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('83', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('84', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('85', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('86', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('87', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('88', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('89', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('90', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('91', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('92', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('93', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('94', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('95', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('96', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('97', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('98', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('99', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('100', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('101', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('102', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('103', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('104', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('105', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('106', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('107', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('108', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('109', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('110', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('111', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('112', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('113', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('114', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('115', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('116', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('117', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('118', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('119', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('120', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('121', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('122', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('123', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('124', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('125', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('126', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('127', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('128', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('129', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('130', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('131', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('132', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('133', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('134', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('135', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('136', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('137', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('138', '2014-06-28 22:33:36', 'upgrade included in installation'),
+('139', '2014-06-28 22:33:36', 'upgrade included in installation');
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_users`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_users` (
+`id` smallint(5) NOT NULL,
+ `username` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
+ `password` varchar(255) DEFAULT NULL,
+ `mail_address` varchar(255) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;
+
+--
+-- Contenu de la table `piwigo_users`
+--
+
+INSERT INTO `piwigo_users` (`id`, `username`, `password`, `mail_address`) VALUES
+(1, 'ADMINUSER', '', 'webmaster@monsieur-a.com'),
+(2, 'guest', NULL, NULL);
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_user_access`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_user_access` (
+ `user_id` smallint(5) NOT NULL DEFAULT '0',
+ `cat_id` smallint(5) unsigned NOT NULL DEFAULT '0'
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_user_cache`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_user_cache` (
+ `user_id` smallint(5) NOT NULL DEFAULT '0',
+ `need_update` enum('true','false') NOT NULL DEFAULT 'true',
+ `cache_update_time` int(10) unsigned NOT NULL DEFAULT '0',
+ `forbidden_categories` mediumtext,
+ `nb_total_images` mediumint(8) unsigned DEFAULT NULL,
+ `last_photo_date` datetime DEFAULT NULL,
+ `nb_available_tags` int(5) DEFAULT NULL,
+ `nb_available_comments` int(5) DEFAULT NULL,
+ `image_access_type` enum('NOT IN','IN') NOT NULL DEFAULT 'NOT IN',
+ `image_access_list` mediumtext
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_user_cache_categories`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_user_cache_categories` (
+ `user_id` smallint(5) NOT NULL DEFAULT '0',
+ `cat_id` smallint(5) unsigned NOT NULL DEFAULT '0',
+ `date_last` datetime DEFAULT NULL,
+ `max_date_last` datetime DEFAULT NULL,
+ `nb_images` mediumint(8) unsigned NOT NULL DEFAULT '0',
+ `count_images` mediumint(8) unsigned DEFAULT '0',
+ `nb_categories` mediumint(8) unsigned DEFAULT '0',
+ `count_categories` mediumint(8) unsigned DEFAULT '0',
+ `user_representative_picture_id` mediumint(8) unsigned DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_user_feed`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_user_feed` (
+ `id` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
+ `user_id` smallint(5) NOT NULL DEFAULT '0',
+ `last_check` datetime DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_user_group`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_user_group` (
+ `user_id` smallint(5) NOT NULL DEFAULT '0',
+ `group_id` smallint(5) unsigned NOT NULL DEFAULT '0'
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_user_infos`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_user_infos` (
+ `user_id` smallint(5) NOT NULL DEFAULT '0',
+ `nb_image_page` smallint(3) unsigned NOT NULL DEFAULT '15',
+ `status` enum('webmaster','admin','normal','generic','guest') NOT NULL DEFAULT 'guest',
+ `language` varchar(50) NOT NULL DEFAULT 'en_UK',
+ `expand` enum('true','false') NOT NULL DEFAULT 'false',
+ `show_nb_comments` enum('true','false') NOT NULL DEFAULT 'false',
+ `show_nb_hits` enum('true','false') NOT NULL DEFAULT 'false',
+ `recent_period` tinyint(3) unsigned NOT NULL DEFAULT '7',
+ `theme` varchar(255) NOT NULL DEFAULT 'elegant',
+ `registration_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+ `enabled_high` enum('true','false') NOT NULL DEFAULT 'true',
+ `level` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `activation_key` char(20) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+--
+-- Contenu de la table `piwigo_user_infos`
+--
+
+INSERT INTO `piwigo_user_infos` (`user_id`, `nb_image_page`, `status`, `language`, `expand`, `show_nb_comments`, `show_nb_hits`, `recent_period`, `theme`, `registration_date`, `enabled_high`, `level`, `activation_key`) VALUES
+(1, 15, 'webmaster', 'fr_FR', 'false', 'false', 'false', 7, 'elegant', '2014-06-28 22:33:36', 'true', 8, NULL),
+(2, 15, 'guest', 'fr_FR', 'false', 'false', 'false', 7, 'elegant', '2014-06-28 22:33:36', 'true', 0, NULL);
+
+-- --------------------------------------------------------
+
+--
+-- Structure de la table `piwigo_user_mail_notification`
+--
+
+CREATE TABLE IF NOT EXISTS `piwigo_user_mail_notification` (
+ `user_id` smallint(5) NOT NULL DEFAULT '0',
+ `check_key` varchar(16) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
+ `enabled` enum('true','false') NOT NULL DEFAULT 'false',
+ `last_send` datetime DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+--
+-- Index pour les tables exportées
+--
+
+--
+-- Index pour la table `piwigo_caddie`
+--
+ALTER TABLE `piwigo_caddie`
+ ADD PRIMARY KEY (`user_id`,`element_id`);
+
+--
+-- Index pour la table `piwigo_categories`
+--
+ALTER TABLE `piwigo_categories`
+ ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `categories_i3` (`permalink`), ADD KEY `categories_i2` (`id_uppercat`);
+
+--
+-- Index pour la table `piwigo_comments`
+--
+ALTER TABLE `piwigo_comments`
+ ADD PRIMARY KEY (`id`), ADD KEY `comments_i2` (`validation_date`), ADD KEY `comments_i1` (`image_id`);
+
+--
+-- Index pour la table `piwigo_config`
+--
+ALTER TABLE `piwigo_config`
+ ADD PRIMARY KEY (`param`);
+
+--
+-- Index pour la table `piwigo_favorites`
+--
+ALTER TABLE `piwigo_favorites`
+ ADD PRIMARY KEY (`user_id`,`image_id`);
+
+--
+-- Index pour la table `piwigo_groups`
+--
+ALTER TABLE `piwigo_groups`
+ ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `groups_ui1` (`name`);
+
+--
+-- Index pour la table `piwigo_group_access`
+--
+ALTER TABLE `piwigo_group_access`
+ ADD PRIMARY KEY (`group_id`,`cat_id`);
+
+--
+-- Index pour la table `piwigo_history`
+--
+ALTER TABLE `piwigo_history`
+ ADD PRIMARY KEY (`id`), ADD KEY `history_i1` (`summarized`);
+
+--
+-- Index pour la table `piwigo_history_summary`
+--
+ALTER TABLE `piwigo_history_summary`
+ ADD UNIQUE KEY `history_summary_ymdh` (`year`,`month`,`day`,`hour`);
+
+--
+-- Index pour la table `piwigo_images`
+--
+ALTER TABLE `piwigo_images`
+ ADD PRIMARY KEY (`id`), ADD KEY `images_i2` (`date_available`), ADD KEY `images_i3` (`rating_score`), ADD KEY `images_i4` (`hit`), ADD KEY `images_i5` (`date_creation`), ADD KEY `images_i1` (`storage_category_id`), ADD KEY `images_i6` (`latitude`);
+
+--
+-- Index pour la table `piwigo_image_category`
+--
+ALTER TABLE `piwigo_image_category`
+ ADD PRIMARY KEY (`image_id`,`category_id`), ADD KEY `image_category_i1` (`category_id`);
+
+--
+-- Index pour la table `piwigo_image_tag`
+--
+ALTER TABLE `piwigo_image_tag`
+ ADD PRIMARY KEY (`image_id`,`tag_id`), ADD KEY `image_tag_i1` (`tag_id`);
+
+--
+-- Index pour la table `piwigo_languages`
+--
+ALTER TABLE `piwigo_languages`
+ ADD PRIMARY KEY (`id`);
+
+--
+-- Index pour la table `piwigo_old_permalinks`
+--
+ALTER TABLE `piwigo_old_permalinks`
+ ADD PRIMARY KEY (`permalink`);
+
+--
+-- Index pour la table `piwigo_plugins`
+--
+ALTER TABLE `piwigo_plugins`
+ ADD PRIMARY KEY (`id`);
+
+--
+-- Index pour la table `piwigo_rate`
+--
+ALTER TABLE `piwigo_rate`
+ ADD PRIMARY KEY (`element_id`,`user_id`,`anonymous_id`);
+
+--
+-- Index pour la table `piwigo_search`
+--
+ALTER TABLE `piwigo_search`
+ ADD PRIMARY KEY (`id`);
+
+--
+-- Index pour la table `piwigo_sessions`
+--
+ALTER TABLE `piwigo_sessions`
+ ADD PRIMARY KEY (`id`);
+
+--
+-- Index pour la table `piwigo_sites`
+--
+ALTER TABLE `piwigo_sites`
+ ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `sites_ui1` (`galleries_url`);
+
+--
+-- Index pour la table `piwigo_tags`
+--
+ALTER TABLE `piwigo_tags`
+ ADD PRIMARY KEY (`id`), ADD KEY `tags_i1` (`url_name`);
+
+--
+-- Index pour la table `piwigo_themes`
+--
+ALTER TABLE `piwigo_themes`
+ ADD PRIMARY KEY (`id`);
+
+--
+-- Index pour la table `piwigo_upgrade`
+--
+ALTER TABLE `piwigo_upgrade`
+ ADD PRIMARY KEY (`id`);
+
+--
+-- Index pour la table `piwigo_users`
+--
+ALTER TABLE `piwigo_users`
+ ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `users_ui1` (`username`);
+
+--
+-- Index pour la table `piwigo_user_access`
+--
+ALTER TABLE `piwigo_user_access`
+ ADD PRIMARY KEY (`user_id`,`cat_id`);
+
+--
+-- Index pour la table `piwigo_user_cache`
+--
+ALTER TABLE `piwigo_user_cache`
+ ADD PRIMARY KEY (`user_id`);
+
+--
+-- Index pour la table `piwigo_user_cache_categories`
+--
+ALTER TABLE `piwigo_user_cache_categories`
+ ADD PRIMARY KEY (`user_id`,`cat_id`);
+
+--
+-- Index pour la table `piwigo_user_feed`
+--
+ALTER TABLE `piwigo_user_feed`
+ ADD PRIMARY KEY (`id`);
+
+--
+-- Index pour la table `piwigo_user_group`
+--
+ALTER TABLE `piwigo_user_group`
+ ADD PRIMARY KEY (`group_id`,`user_id`);
+
+--
+-- Index pour la table `piwigo_user_infos`
+--
+ALTER TABLE `piwigo_user_infos`
+ ADD PRIMARY KEY (`user_id`);
+
+--
+-- Index pour la table `piwigo_user_mail_notification`
+--
+ALTER TABLE `piwigo_user_mail_notification`
+ ADD PRIMARY KEY (`user_id`), ADD UNIQUE KEY `user_mail_notification_ui1` (`check_key`);
+
+--
+-- AUTO_INCREMENT pour les tables exportées
+--
+
+--
+-- AUTO_INCREMENT pour la table `piwigo_categories`
+--
+ALTER TABLE `piwigo_categories`
+MODIFY `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT;
+--
+-- AUTO_INCREMENT pour la table `piwigo_comments`
+--
+ALTER TABLE `piwigo_comments`
+MODIFY `id` int(11) unsigned NOT NULL AUTO_INCREMENT;
+--
+-- AUTO_INCREMENT pour la table `piwigo_groups`
+--
+ALTER TABLE `piwigo_groups`
+MODIFY `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT;
+--
+-- AUTO_INCREMENT pour la table `piwigo_history`
+--
+ALTER TABLE `piwigo_history`
+MODIFY `id` int(10) unsigned NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=9;
+--
+-- AUTO_INCREMENT pour la table `piwigo_images`
+--
+ALTER TABLE `piwigo_images`
+MODIFY `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT;
+--
+-- AUTO_INCREMENT pour la table `piwigo_search`
+--
+ALTER TABLE `piwigo_search`
+MODIFY `id` int(10) unsigned NOT NULL AUTO_INCREMENT;
+--
+-- AUTO_INCREMENT pour la table `piwigo_sites`
+--
+ALTER TABLE `piwigo_sites`
+MODIFY `id` tinyint(4) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=2;
+--
+-- AUTO_INCREMENT pour la table `piwigo_tags`
+--
+ALTER TABLE `piwigo_tags`
+MODIFY `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT;
+--
+-- AUTO_INCREMENT pour la table `piwigo_users`
+--
+ALTER TABLE `piwigo_users`
+MODIFY `id` smallint(5) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=4;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
diff --git a/conf/piwigo_structure-mysql.sql~ b/conf/piwigo_structure-mysql.sql~
new file mode 100644
index 0000000..dadad1d
--- /dev/null
+++ b/conf/piwigo_structure-mysql.sql~
@@ -0,0 +1,455 @@
+-- MySQL dump 9.11
+--
+-- Host: localhost Database: pwg-bsf
+-- ------------------------------------------------------
+-- Server version 4.0.24_Debian-10-log
+
+--
+-- Table structure for table `piwigo_caddie`
+--
+
+DROP TABLE IF EXISTS `piwigo_caddie`;
+CREATE TABLE `piwigo_caddie` (
+ `user_id` smallint(5) NOT NULL default '0',
+ `element_id` mediumint(8) NOT NULL default '0',
+ PRIMARY KEY (`user_id`,`element_id`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_categories`
+--
+
+DROP TABLE IF EXISTS `piwigo_categories`;
+CREATE TABLE `piwigo_categories` (
+ `id` smallint(5) unsigned NOT NULL auto_increment,
+ `name` varchar(255) NOT NULL default '',
+ `id_uppercat` smallint(5) unsigned default NULL,
+ `comment` text,
+ `dir` varchar(255) default NULL,
+ `rank` smallint(5) unsigned default NULL,
+ `status` enum('public','private') NOT NULL default 'public',
+ `site_id` tinyint(4) unsigned default NULL,
+ `visible` enum('true','false') NOT NULL default 'true',
+ `representative_picture_id` mediumint(8) unsigned default NULL,
+ `uppercats` varchar(255) NOT NULL default '',
+ `commentable` enum('true','false') NOT NULL default 'true',
+ `global_rank` varchar(255) default NULL,
+ `image_order` varchar(128) default NULL,
+ `permalink` varchar(64) binary default NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `categories_i3` (`permalink`),
+ KEY `categories_i2` (`id_uppercat`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_comments`
+--
+
+DROP TABLE IF EXISTS `piwigo_comments`;
+CREATE TABLE `piwigo_comments` (
+ `id` int(11) unsigned NOT NULL auto_increment,
+ `image_id` mediumint(8) unsigned NOT NULL default '0',
+ `date` datetime NOT NULL default '0000-00-00 00:00:00',
+ `author` varchar(255) default NULL,
+ `email` varchar(255) default NULL,
+ `author_id` smallint(5) DEFAULT NULL,
+ `anonymous_id` varchar(45) NOT NULL,
+ `website_url` varchar(255) DEFAULT NULL,
+ `content` longtext,
+ `validated` enum('true','false') NOT NULL default 'false',
+ `validation_date` datetime default NULL,
+ PRIMARY KEY (`id`),
+ KEY `comments_i2` (`validation_date`),
+ KEY `comments_i1` (`image_id`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_config`
+--
+
+DROP TABLE IF EXISTS `piwigo_config`;
+CREATE TABLE `piwigo_config` (
+ `param` varchar(40) NOT NULL default '',
+ `value` text,
+ `comment` varchar(255) default NULL,
+ PRIMARY KEY (`param`)
+) ENGINE=MyISAM COMMENT='configuration table';
+
+--
+-- Table structure for table `piwigo_favorites`
+--
+
+DROP TABLE IF EXISTS `piwigo_favorites`;
+CREATE TABLE `piwigo_favorites` (
+ `user_id` smallint(5) NOT NULL default '0',
+ `image_id` mediumint(8) unsigned NOT NULL default '0',
+ PRIMARY KEY (`user_id`,`image_id`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_group_access`
+--
+
+DROP TABLE IF EXISTS `piwigo_group_access`;
+CREATE TABLE `piwigo_group_access` (
+ `group_id` smallint(5) unsigned NOT NULL default '0',
+ `cat_id` smallint(5) unsigned NOT NULL default '0',
+ PRIMARY KEY (`group_id`,`cat_id`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_groups`
+--
+
+DROP TABLE IF EXISTS `piwigo_groups`;
+CREATE TABLE `piwigo_groups` (
+ `id` smallint(5) unsigned NOT NULL auto_increment,
+ `name` varchar(255) NOT NULL default '',
+ `is_default` enum('true','false') NOT NULL default 'false',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `groups_ui1` (`name`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_history`
+--
+
+DROP TABLE IF EXISTS `piwigo_history`;
+CREATE TABLE `piwigo_history` (
+ `id` int(10) unsigned NOT NULL auto_increment,
+ `date` date NOT NULL default '0000-00-00',
+ `time` time NOT NULL default '00:00:00',
+ `user_id` smallint(5) NOT NULL default '0',
+ `IP` varchar(15) NOT NULL default '',
+ `section` enum('categories','tags','search','list','favorites','most_visited','best_rated','recent_pics','recent_cats') default NULL,
+ `category_id` smallint(5) default NULL,
+ `tag_ids` varchar(50) default NULL,
+ `image_id` mediumint(8) default NULL,
+ `summarized` enum('true','false') default 'false',
+ `image_type` enum('picture','high','other') default NULL,
+ PRIMARY KEY (`id`),
+ KEY `history_i1` (`summarized`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_history_summary`
+--
+
+DROP TABLE IF EXISTS `piwigo_history_summary`;
+CREATE TABLE `piwigo_history_summary` (
+ `year` smallint(4) NOT NULL default '0',
+ `month` tinyint(2) default NULL,
+ `day` tinyint(2) default NULL,
+ `hour` tinyint(2) default NULL,
+ `nb_pages` int(11) default NULL,
+ UNIQUE KEY history_summary_ymdh (`year`,`month`,`day`,`hour`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_image_category`
+--
+
+DROP TABLE IF EXISTS `piwigo_image_category`;
+CREATE TABLE `piwigo_image_category` (
+ `image_id` mediumint(8) unsigned NOT NULL default '0',
+ `category_id` smallint(5) unsigned NOT NULL default '0',
+ `rank` mediumint(8) unsigned default NULL,
+ PRIMARY KEY (`image_id`,`category_id`),
+ KEY `image_category_i1` (`category_id`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_image_tag`
+--
+
+DROP TABLE IF EXISTS `piwigo_image_tag`;
+CREATE TABLE `piwigo_image_tag` (
+ `image_id` mediumint(8) unsigned NOT NULL default '0',
+ `tag_id` smallint(5) unsigned NOT NULL default '0',
+ PRIMARY KEY (`image_id`,`tag_id`),
+ KEY `image_tag_i1` (`tag_id`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_images`
+--
+
+DROP TABLE IF EXISTS `piwigo_images`;
+CREATE TABLE `piwigo_images` (
+ `id` mediumint(8) unsigned NOT NULL auto_increment,
+ `file` varchar(255) binary NOT NULL default '',
+ `date_available` datetime NOT NULL default '0000-00-00 00:00:00',
+ `date_creation` datetime default NULL,
+ `name` varchar(255) default NULL,
+ `comment` text,
+ `author` varchar(255) default NULL,
+ `hit` int(10) unsigned NOT NULL default '0',
+ `filesize` mediumint(9) unsigned default NULL,
+ `width` smallint(9) unsigned default NULL,
+ `height` smallint(9) unsigned default NULL,
+ `coi` char(4) default NULL COMMENT 'center of interest',
+ `representative_ext` varchar(4) default NULL,
+ `date_metadata_update` date default NULL,
+ `rating_score` float(5,2) unsigned default NULL,
+ `path` varchar(255) NOT NULL default '',
+ `storage_category_id` smallint(5) unsigned default NULL,
+ `level` tinyint unsigned NOT NULL default '0',
+ `md5sum` char(32) default NULL,
+ `added_by` smallint(5) NOT NULL default '0',
+ `rotation` tinyint unsigned default NULL,
+ `latitude` double(8, 6) default NULL,
+ `longitude` double(9, 6) default NULL,
+ PRIMARY KEY (`id`),
+ KEY `images_i2` (`date_available`),
+ KEY `images_i3` (`rating_score`),
+ KEY `images_i4` (`hit`),
+ KEY `images_i5` (`date_creation`),
+ KEY `images_i1` (`storage_category_id`),
+ KEY `images_i6` (`latitude`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_languages`
+--
+
+DROP TABLE IF EXISTS `piwigo_languages`;
+CREATE TABLE `piwigo_languages` (
+ `id` varchar(64) NOT NULL default '',
+ `version` varchar(64) NOT NULL default '0',
+ `name` varchar(64) default NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_old_permalinks`
+--
+
+DROP TABLE IF EXISTS `piwigo_old_permalinks`;
+CREATE TABLE `piwigo_old_permalinks` (
+ `cat_id` smallint(5) unsigned NOT NULL default '0',
+ `permalink` varchar(64) binary NOT NULL default '',
+ `date_deleted` datetime NOT NULL default '0000-00-00 00:00:00',
+ `last_hit` datetime default NULL,
+ `hit` int(10) unsigned NOT NULL default '0',
+ PRIMARY KEY (`permalink`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_plugins`
+--
+
+DROP TABLE IF EXISTS `piwigo_plugins`;
+CREATE TABLE `piwigo_plugins` (
+ `id` varchar(64) binary NOT NULL default '',
+ `state` enum('inactive','active') NOT NULL default 'inactive',
+ `version` varchar(64) NOT NULL default '0',
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_rate`
+--
+
+DROP TABLE IF EXISTS `piwigo_rate`;
+CREATE TABLE `piwigo_rate` (
+ `user_id` smallint(5) NOT NULL default '0',
+ `element_id` mediumint(8) unsigned NOT NULL default '0',
+ `anonymous_id` varchar(45) NOT NULL default '',
+ `rate` tinyint(2) unsigned NOT NULL default '0',
+ `date` date NOT NULL default '0000-00-00',
+ PRIMARY KEY (`element_id`,`user_id`,`anonymous_id`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_search`
+--
+
+DROP TABLE IF EXISTS `piwigo_search`;
+CREATE TABLE `piwigo_search` (
+ `id` int(10) unsigned NOT NULL auto_increment,
+ `last_seen` date default NULL,
+ `rules` text,
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_sessions`
+--
+
+DROP TABLE IF EXISTS `piwigo_sessions`;
+CREATE TABLE `piwigo_sessions` (
+ `id` varchar(255) binary NOT NULL default '',
+ `data` mediumtext NOT NULL,
+ `expiration` datetime NOT NULL default '0000-00-00 00:00:00',
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_sites`
+--
+
+DROP TABLE IF EXISTS `piwigo_sites`;
+CREATE TABLE `piwigo_sites` (
+ `id` tinyint(4) NOT NULL auto_increment,
+ `galleries_url` varchar(255) NOT NULL default '',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `sites_ui1` (`galleries_url`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_tags`
+--
+
+DROP TABLE IF EXISTS `piwigo_tags`;
+CREATE TABLE `piwigo_tags` (
+ `id` smallint(5) unsigned NOT NULL auto_increment,
+ `name` varchar(255) binary NOT NULL default '',
+ `url_name` varchar(255) binary NOT NULL default '',
+ PRIMARY KEY (`id`),
+ KEY `tags_i1` (`url_name`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_themes`
+--
+
+DROP TABLE IF EXISTS `piwigo_themes`;
+CREATE TABLE `piwigo_themes` (
+ `id` varchar(64) NOT NULL default '',
+ `version` varchar(64) NOT NULL default '0',
+ `name` varchar(64) default NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_upgrade`
+--
+
+DROP TABLE IF EXISTS `piwigo_upgrade`;
+CREATE TABLE `piwigo_upgrade` (
+ `id` varchar(20) NOT NULL default '',
+ `applied` datetime NOT NULL default '0000-00-00 00:00:00',
+ `description` varchar(255) default NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_user_access`
+--
+
+DROP TABLE IF EXISTS `piwigo_user_access`;
+CREATE TABLE `piwigo_user_access` (
+ `user_id` smallint(5) NOT NULL default '0',
+ `cat_id` smallint(5) unsigned NOT NULL default '0',
+ PRIMARY KEY (`user_id`,`cat_id`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_user_cache`
+--
+
+DROP TABLE IF EXISTS `piwigo_user_cache`;
+CREATE TABLE `piwigo_user_cache` (
+ `user_id` smallint(5) NOT NULL default '0',
+ `need_update` enum('true','false') NOT NULL default 'true',
+ `cache_update_time` integer unsigned NOT NULL default 0,
+ `forbidden_categories` mediumtext,
+ `nb_total_images` mediumint(8) unsigned default NULL,
+ `last_photo_date` datetime DEFAULT NULL,
+ `nb_available_tags` INT(5) DEFAULT NULL,
+ `nb_available_comments` INT(5) DEFAULT NULL,
+ `image_access_type` enum('NOT IN','IN') NOT NULL default 'NOT IN',
+ `image_access_list` mediumtext default NULL,
+ PRIMARY KEY (`user_id`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_user_cache_categories`
+--
+
+DROP TABLE IF EXISTS `piwigo_user_cache_categories`;
+CREATE TABLE `piwigo_user_cache_categories` (
+ `user_id` smallint(5) NOT NULL default '0',
+ `cat_id` smallint(5) unsigned NOT NULL default '0',
+ `date_last` datetime default NULL,
+ `max_date_last` datetime default NULL,
+ `nb_images` mediumint(8) unsigned NOT NULL default '0',
+ `count_images` mediumint(8) unsigned default '0',
+ `nb_categories` mediumint(8) unsigned default '0',
+ `count_categories` mediumint(8) unsigned default '0',
+ `user_representative_picture_id` mediumint(8) unsigned default NULL,
+ PRIMARY KEY (`user_id`,`cat_id`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_user_feed`
+--
+
+DROP TABLE IF EXISTS `piwigo_user_feed`;
+CREATE TABLE `piwigo_user_feed` (
+ `id` varchar(50) binary NOT NULL default '',
+ `user_id` smallint(5) NOT NULL default '0',
+ `last_check` datetime default NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_user_group`
+--
+
+DROP TABLE IF EXISTS `piwigo_user_group`;
+CREATE TABLE `piwigo_user_group` (
+ `user_id` smallint(5) NOT NULL default '0',
+ `group_id` smallint(5) unsigned NOT NULL default '0',
+ PRIMARY KEY (`group_id`,`user_id`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_user_infos`
+--
+
+DROP TABLE IF EXISTS `piwigo_user_infos`;
+CREATE TABLE `piwigo_user_infos` (
+ `user_id` smallint(5) NOT NULL default '0',
+ `nb_image_page` smallint(3) unsigned NOT NULL default '15',
+ `status` enum('webmaster','admin','normal','generic','guest') NOT NULL default 'guest',
+ `language` varchar(50) NOT NULL default 'en_UK',
+ `expand` enum('true','false') NOT NULL default 'false',
+ `show_nb_comments` enum('true','false') NOT NULL default 'false',
+ `show_nb_hits` enum('true','false') NOT NULL default 'false',
+ `recent_period` tinyint(3) unsigned NOT NULL default '7',
+ `theme` varchar(255) NOT NULL default 'elegant',
+ `registration_date` datetime NOT NULL default '0000-00-00 00:00:00',
+ `enabled_high` enum('true','false') NOT NULL default 'true',
+ `level` tinyint unsigned NOT NULL default '0',
+ `activation_key` char(20) default NULL,
+ PRIMARY KEY (`user_id`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_user_mail_notification`
+--
+
+DROP TABLE IF EXISTS `piwigo_user_mail_notification`;
+CREATE TABLE `piwigo_user_mail_notification` (
+ `user_id` smallint(5) NOT NULL default '0',
+ `check_key` varchar(16) binary NOT NULL default '',
+ `enabled` enum('true','false') NOT NULL default 'false',
+ `last_send` datetime default NULL,
+ PRIMARY KEY (`user_id`),
+ UNIQUE KEY `user_mail_notification_ui1` (`check_key`)
+) ENGINE=MyISAM;
+
+--
+-- Table structure for table `piwigo_users`
+--
+
+DROP TABLE IF EXISTS `piwigo_users`;
+CREATE TABLE `piwigo_users` (
+ `id` smallint(5) NOT NULL auto_increment,
+ `username` varchar(100) binary NOT NULL default '',
+ `password` varchar(255) default NULL,
+ `mail_address` varchar(255) default NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `users_ui1` (`username`)
+) ENGINE=MyISAM;
diff --git a/conf/plug.sql~ b/conf/plug.sql~
new file mode 100644
index 0000000..c8e0161
--- /dev/null
+++ b/conf/plug.sql~
@@ -0,0 +1 @@
+INSERT INTO plugins
diff --git a/manifest.json b/manifest.json
new file mode 100644
index 0000000..6b51bd9
--- /dev/null
+++ b/manifest.json
@@ -0,0 +1,49 @@
+{
+ "name": "Piwigo",
+ "id": "piwigo",
+ "description": {
+ "en": "photo gallery",
+ "fr": "Gallerie photo"
+ },
+ "developer": {
+ "name": "monsieur-a",
+ "email": "simon@monsieur-a.fr",
+ "url": "http://piwigo.org"
+ },
+ "multi_instance": "true",
+ "arguments": {
+ "install" : [
+ {
+ "name": "domain",
+ "ask": {
+ "en": "Choose a domain for Piwigo"
+ },
+ "example": "domain.org"
+ },
+ {
+ "name": "path",
+ "ask": {
+ "en": "Choose a path for Piwigo"
+ },
+ "example": "/piwigo",
+ "default": "/piwigo"
+ },
+ {
+ "name": "admin",
+ "ask": {
+ "en": "Choose the Piwigo administrator (must be an existing YunoHost user)"
+ },
+ "example": "homer"
+ },
+ {
+ "name": "public_site",
+ "ask": {
+ "en": "Is it a public Piwigo site ?",
+ "fr": "Est-ce un site public ?"
+ },
+ "choices": ["Yes", "No"],
+ "default": "Yes"
+ }
+ ]
+ }
+}
diff --git a/plugins/Ldap_Login/TODO b/plugins/Ldap_Login/TODO
new file mode 100644
index 0000000..f7eafc9
--- /dev/null
+++ b/plugins/Ldap_Login/TODO
@@ -0,0 +1,30 @@
+TODO :
+auto config (may use ajax): fill in the settings, then the page guess the next settings.
+Ex : after filled the host address, the page guess the base dn. The last to guess is the users OU.
+ Users OU can come with a select field, as the ld_attr.
+
+correct init plugin
+if mail isn't furnished ?
+create common piwigo users upon successfull ldap connection when piwigo user doesn't exist => done !
+ Groups : users may belong to ldap group to allow connection
+ Groups : users belonging to «sudo» or «admin» ldap group become piwigo admin when created this way
+ fetching attributes from ldap (mail…).
+
+config of the previous one (need tabs)
+config page to render better
+
+########
+
+à faire :
+initialisation du plugin correcte
+qu'est-ce qu'on fait si le mail est pas fourni ?
+configuration automatique (javascript/ajax probable): entrée des paramètres et la page recherche le paramètre suivant si possible.
+Ex : adresse du serveur -> la page trouve toute seule la racine ldap, reste plus que la OU des utilisateurs (qu'on peut selectionner via une liste déroulante).
+ l'attribut d'identification peut être selectionné par une liste déroulante.
+
+creation d'un utilisateur piwigo suite à une authentification ldap quand l'utilisateur n'existe pas. => fait !
+ question de groups : les utilisateurs membres du groupe ldap «sudo» ou autre pourraient automatiquement être admins piwigo
+ question de groupe : les utilisateurs devraient appartenir à un groupe ldap pour se connecter…
+ récuperer les attributs dans le ldap (mail…).
+
+page de config à peaufiner, option précédente à intégrer dans un onglet.
\ No newline at end of file
diff --git a/plugins/Ldap_Login/admin.php b/plugins/Ldap_Login/admin.php
new file mode 100644
index 0000000..7bd3743
--- /dev/null
+++ b/plugins/Ldap_Login/admin.php
@@ -0,0 +1,25 @@
+set_id('ldap_login');
+
+$tabsheet->add('configuration', l10n('Configuration'), LDAP_LOGIN_ADMIN . '-configuration');
+$tabsheet->add('newusers', l10n('New users when ldap auth is successfull'), LDAP_LOGIN_ADMIN . '-newusers');
+$tabsheet->select($page['tab']);
+$tabsheet->assign();
+
+// include page
+include(LDAP_LOGIN_PATH . 'admin/' . $page['tab'] . '.php');
+
+// template vars
+$template->assign('LDAP_LOGIN_PATH', get_root_url() . LDAP_LOGIN_PATH );
+
+?>
\ No newline at end of file
diff --git a/plugins/Ldap_Login/admin/configuration.php b/plugins/Ldap_Login/admin/configuration.php
new file mode 100644
index 0000000..be6ab6e
--- /dev/null
+++ b/plugins/Ldap_Login/admin/configuration.php
@@ -0,0 +1,58 @@
+set_filenames( array('plugin_admin_content' => dirname(__FILE__).'/configuration.tpl') );
+$template->assign(
+ array(
+ 'PLUGIN_ACTION' => get_root_url().'admin.php?page=plugin-Ldap_Login-configuration',
+ 'PLUGIN_CHECK' => get_root_url().'admin.php?page=plugin-Ldap_Login-configuration',
+ ));
+
+$me = new Ldap();
+$me->load_config();
+//$me = get_plugin_data($plugin_id);
+
+$template->assign('HOST', $me->config['host']);
+$template->assign('BASEDN', $me->config['basedn']); // racine !
+$template->assign('PORT', $me->config['port']);
+$template->assign('LD_ATTR', $me->config['ld_attr']);
+$template->assign('LD_USE_SSL', $me->config['ld_use_ssl']);
+$template->assign('LD_BINDPW', $me->config['ld_bindpw']);
+$template->assign('LD_BINDDN', $me->config['ld_binddn']);
+
+if (isset($_POST['save'])){
+ $me->config['host'] = $_POST['HOST'];
+ $me->config['basedn'] = $_POST['BASEDN'];
+ $me->config['port'] = $_POST['PORT'];
+ $me->config['ld_attr'] = $_POST['LD_ATTR'];
+ $me->config['ld_binddn'] = $_POST['LD_BINDDN'];
+ $me->config['ld_bindpw'] = $_POST['LD_BINDPW'];
+
+ if (isset($_POST['LD_USE_SSL'])){
+ $me->config['ld_use_ssl'] = True;
+ } else {
+ $me->config['ld_use_ssl'] = False;
+ }
+}
+
+// Save LDAP configuration
+if (isset($_POST['save'])){
+ $me->save_config();
+}
+
+// Check LDAP configuration
+if (isset($_POST['check_ldap'])){
+$check = $me->ldap_name($_POST['USERNAME']);
+$error = $me->check_ldap();
+
+ if ($me->ldap_bind_as($_POST['USERNAME'],$_POST['PASSWORD'])){
+ $template->assign('LD_CHECK_LDAP','Configuration LDAP OK : '.$check.'
');
+ }
+ else {
+ $template->assign('LD_CHECK_LDAP','Error :'.$error.' test '.$me->config['uri'].' '.$check.'
');
+ }
+}
+
+$template->assign_var_from_handle( 'ADMIN_CONTENT', 'plugin_admin_content');
+?>
\ No newline at end of file
diff --git a/plugins/Ldap_Login/admin/configuration.tpl b/plugins/Ldap_Login/admin/configuration.tpl
new file mode 100644
index 0000000..5d6b220
--- /dev/null
+++ b/plugins/Ldap_Login/admin/configuration.tpl
@@ -0,0 +1,109 @@
+{'Ldap_Login Plugin'|@translate}
+
+
+
+
{'All LDAP users can use their ldap password everywhere on piwigo if needed.'|@translate}
+
+
+
+
+
diff --git a/plugins/Ldap_Login/admin/index.php b/plugins/Ldap_Login/admin/index.php
new file mode 100644
index 0000000..df13f7e
--- /dev/null
+++ b/plugins/Ldap_Login/admin/index.php
@@ -0,0 +1,30 @@
+
\ No newline at end of file
diff --git a/plugins/Ldap_Login/admin/ldap_login_plugin_admin.php b/plugins/Ldap_Login/admin/ldap_login_plugin_admin.php
new file mode 100644
index 0000000..07c9104
--- /dev/null
+++ b/plugins/Ldap_Login/admin/ldap_login_plugin_admin.php
@@ -0,0 +1,22 @@
+config['host'] = $_POST['HOST'];
+ $me->config['basedn'] = $_POST['BASEDN'];
+ $me->config['pref'] = $_POST['PREF'];
+ $me->save_config();
+}
+
+global $template;
+$template->set_filenames( array('plugin_admin_content' => dirname(__FILE__).'/ldap_login_plugin_admin.tpl') );
+
+$template->assign('HOST', $me->config['host']);
+$template->assign('BASEDN', $me->config['basedn']);
+$template->assign('PREF', $me->config['pref']);
+
+$template->assign_var_from_handle( 'ADMIN_CONTENT', 'plugin_admin_content');
+?>
\ No newline at end of file
diff --git a/plugins/Ldap_Login/admin/ldap_login_plugin_admin.tpl b/plugins/Ldap_Login/admin/ldap_login_plugin_admin.tpl
new file mode 100644
index 0000000..4ece27e
--- /dev/null
+++ b/plugins/Ldap_Login/admin/ldap_login_plugin_admin.tpl
@@ -0,0 +1,24 @@
+
+
Ldap_Login PlugIn
+
+
+Configuration du plugin Ldap_Login
+
+
\ No newline at end of file
diff --git a/plugins/Ldap_Login/admin/newusers.php b/plugins/Ldap_Login/admin/newusers.php
new file mode 100644
index 0000000..e54aceb
--- /dev/null
+++ b/plugins/Ldap_Login/admin/newusers.php
@@ -0,0 +1,52 @@
+set_filenames( array('plugin_admin_content' => dirname(__FILE__).'/newusers.tpl') );
+$template->assign(
+ array(
+ 'PLUGIN_NEWUSERS' => get_root_url().'admin.php?page=plugin-Ldap_Login-newusers',
+ ));
+
+$me = new Ldap();
+$me->load_config();
+//$me = get_plugin_data($plugin_id);
+
+$template->assign('ALLOW_NEWUSERS', $me->config['allow_newusers']);
+$template->assign('ADVERTISE_ADMINS', $me->config['advertise_admin_new_ldapuser']);
+$template->assign('SEND_CASUAL_MAIL', $me->config['send_password_by_mail_ldap']);
+
+if (isset($_POST['save'])){
+
+ if (isset($_POST['ALLOW_NEWUSERS'])){
+ $me->config['allow_newusers'] = True;
+ } else {
+ $me->config['allow_newusers'] = False;
+ }
+
+ if (isset($_POST['ADVERTISE_ADMINS'])){
+ $me->config['advertise_admin_new_ldapuser'] = True;
+ } else {
+ $me->config['advertise_admin_new_ldapuser'] = False;
+ }
+
+ if (isset($_POST['SEND_CASUAL_MAIL'])){
+ $me->config['send_password_by_mail_ldap'] = True;
+ } else {
+ $me->config['send_password_by_mail_ldap'] = False;
+ }
+}
+
+// Save LDAP configuration
+if (isset($_POST['save'])){
+ $me->save_config();
+}
+
+// do we allow to create new piwigo users in case of auth along the ldap ?
+// does he have to belong an ldap group ?
+// does ldap groups give some power ?
+// what do we do when there's no mail in the ldap ?
+// do we send mail to admins ?
+
+$template->assign_var_from_handle( 'ADMIN_CONTENT', 'plugin_admin_content');
+?>
\ No newline at end of file
diff --git a/plugins/Ldap_Login/admin/newusers.tpl b/plugins/Ldap_Login/admin/newusers.tpl
new file mode 100644
index 0000000..ec9d9a6
--- /dev/null
+++ b/plugins/Ldap_Login/admin/newusers.tpl
@@ -0,0 +1,54 @@
+{literal}
+
+{/literal}
+
+
+
{'Ldap_Login Plugin'|@translate}
+
+
+{"If the LDAP doesn't furnish the mail address, users can set it up in the profile page."|@translate}
+
\ No newline at end of file
diff --git a/plugins/Ldap_Login/class.ldap.php b/plugins/Ldap_Login/class.ldap.php
new file mode 100644
index 0000000..c062c6a
--- /dev/null
+++ b/plugins/Ldap_Login/class.ldap.php
@@ -0,0 +1,192 @@
+ldap_conn()) {
+ return $this->getErrorString();
+ }
+
+ // test du compte root si renseigné
+ if (!empty($this->config['ld_binddn']) && !empty($this->config['ld_bindpw'])){ // if empty ld_binddn, anonymous search
+ // authentication with rootdn and rootpw for search
+ if (!$this->ldap_bind_as($this->config['ld_binddn'],$this->config['ld_bindpw'])){
+ return $this->getErrorString();
+ }
+ } else {
+ // sinon recherche du basedn (cf comportement ldap_connect avec OpenLDAP)
+ if (!$this->ldap_check_basedn()){ // search userdn
+ return $this->getErrorString();
+ }
+ }
+ return true;
+ }
+
+ public function load_default_config()
+ {
+ $this->config['host'] = 'localhost';
+ $this->config['basedn'] = 'ou=people,dc=example,dc=com'; // racine !
+ $this->config['port'] = ''; // if port is empty, I count on the software to care of it !
+ $this->config['ld_attr'] = 'uid';
+ $this->config['ld_use_ssl'] = False;
+ $this->config['ld_bindpw'] ='';
+ $this->config['ld_binddn'] ='';
+
+ $this->config['allow_newusers'] = False;
+ $this->config['advertise_admin_new_ldapuser'] = False;
+ $this->config['send_password_by_mail_ldap'] = False;
+ }
+
+ function load_config() {
+ // first we load the base config
+ $conf_file = @file_get_contents( LDAP_LOGIN_PATH.'data.dat' );
+ if ($conf_file!==false)
+ {
+ $this->config = unserialize($conf_file);
+ }
+ }
+
+ function save_config()
+ {
+ $file = fopen( LDAP_LOGIN_PATH.'/data.dat', 'w' );
+ fwrite($file, serialize($this->config) );
+ fclose( $file );
+ }
+
+ function ldap_admin_menu($menu)
+ {
+ array_push($menu,
+ array(
+ 'NAME' => 'Ldap Login',
+ 'URL' => get_admin_plugin_menu_link(LDAP_LOGIN_PATH.'/admin.php') )
+ );
+ return $menu;
+ }
+
+ public function ldap_conn(){
+ if ($this->config['ld_use_ssl'] == 1){
+ if (empty($this->config['port'])){
+ $this->config['uri'] = 'ldaps://'.$this->config['host'];
+ }
+ else {
+ $this->config['uri'] = 'ldaps://'.$this->config['host'].':'.$this->config['port'];
+ }
+ }
+
+ // now, it's without ssl
+ else {
+ if (empty($this->config['port'])){
+ $this->config['uri'] = 'ldap://'.$this->config['host'];
+ }
+ else {
+ $this->config['uri'] = 'ldap://'.$this->config['host'].':'.$this->config['port'];
+ }
+ }
+
+ if ($this->cnx = @ldap_connect($this->config['uri'])){
+ @ldap_set_option($this->cnx, LDAP_OPT_PROTOCOL_VERSION, 3); // LDAPv3 if possible
+ return true;
+ }
+ return false;
+
+ // connect with rootdn in case not anonymous.
+ if (!empty($obj->config['ld_binddn']) && !empty($obj->config['ld_bindpw'])){ // if empty ld_binddn, anonymous work
+
+ // authentication with rootdn and rootpw for dn search
+ // carefull ! rootdn should be in full ldap style ! Nothing is supposed (to be one of the users the plugin auth…).
+ if (@ldap_bind($obj->config['ld_binddn'],$obj->config['ld_bindpw'])){
+ return false;
+ }
+ }
+ }
+
+ // return ldap error
+ public function getErrorString(){
+ return ldap_err2str(ldap_errno($this->cnx));
+ }
+
+ // return the name ldap understand
+ public function ldap_name($name){
+ return $this->config['ld_attr'].'='.$name.','.$this->config['basedn'];
+ }
+
+ // authentication
+ public function ldap_bind_as($user,$user_passwd){
+ if (@ldap_bind($this->cnx,$this->ldap_name($user),$user_passwd)){
+ return true;
+ }
+ return false;
+ }
+
+ public function ldap_mail($name){
+
+ //echo $this->cnx;
+ //echo $this->ldap_name($name);
+ $sr=@ldap_read($this->cnx, $this->ldap_name($name), "(objectclass=*)", array('mail'));
+ $entry = @ldap_get_entries($this->cnx, $sr);
+
+ if (!empty($entry[0]['mail'])) {
+ return $entry[0]['mail'][0];
+ }
+ return False;
+ }
+
+ // return userdn (and username) for authentication
+ /* public function ldap_search_dn($to_search){
+ $filter = str_replace('%s',$to_search,$this->config['ld_filter']);
+ //$this->write_log('$filter '.$filter);
+
+ if ($search = @ldap_search($this->cnx,$this->config['basedn'],$filter,array('dn',$this->config['ld_attr']),0,1)){
+ $entry = @ldap_get_entries($this->cnx, $search);
+ if (!empty($entry[0][strtolower($this->config['ld_attr'])][0])) {
+ return $entry;
+ }
+ }
+ return false;
+ } */
+
+
+ public function getAttr() {
+ $search = @ldap_read($this->cnx, "cn=subschema", "(objectClass=*)", array('*', 'subschemasubentry'));
+ $entries = @ldap_get_entries($this->cnx, $search);
+ echo count($entries);
+ }
+
+ public function getRootDse() {
+
+ $search = @ldap_read($this->cnx, NULL, 'objectClass=*', array("*", "+"));
+ $entries = @ldap_get_entries($this->cnx, $search);
+ return $entries[0];
+ }
+
+
+ public function ldap_check_basedn(){
+ if ($read = @ldap_read($this->cnx,$this->config['basedn'],'(objectClass=*)',array('dn'))){
+ $entry = @ldap_get_entries($this->cnx, $read);
+ if (!empty($entry[0]['dn'])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
+?>
\ No newline at end of file
diff --git a/plugins/Ldap_Login/data.dat b/plugins/Ldap_Login/data.dat
new file mode 100644
index 0000000..dd1c67d
--- /dev/null
+++ b/plugins/Ldap_Login/data.dat
@@ -0,0 +1 @@
+a:10:{s:4:"host";s:0:"";s:6:"basedn";s:27:"ou=users,dc=yunohost,dc=org";s:4:"port";s:0:"";s:7:"ld_attr";s:3:"uid";s:9:"ld_binddn";s:0:"";s:9:"ld_bindpw";s:0:"";s:10:"ld_use_ssl";b:0;s:14:"allow_newusers";b:1;s:28:"advertise_admin_new_ldapuser";b:0;s:26:"send_password_by_mail_ldap";b:0;}
diff --git a/plugins/Ldap_Login/index.php b/plugins/Ldap_Login/index.php
new file mode 100644
index 0000000..df13f7e
--- /dev/null
+++ b/plugins/Ldap_Login/index.php
@@ -0,0 +1,30 @@
+
\ No newline at end of file
diff --git a/plugins/Ldap_Login/language/da_DK/plugin.lang.php b/plugins/Ldap_Login/language/da_DK/plugin.lang.php
new file mode 100644
index 0000000..eef142e
--- /dev/null
+++ b/plugins/Ldap_Login/language/da_DK/plugin.lang.php
@@ -0,0 +1,56 @@
+
\ No newline at end of file
diff --git a/plugins/Ldap_Login/language/de_DE/index.php b/plugins/Ldap_Login/language/de_DE/index.php
new file mode 100644
index 0000000..ec66053
--- /dev/null
+++ b/plugins/Ldap_Login/language/de_DE/index.php
@@ -0,0 +1,7 @@
+
diff --git a/plugins/Ldap_Login/language/de_DE/plugin.lang.php b/plugins/Ldap_Login/language/de_DE/plugin.lang.php
new file mode 100644
index 0000000..596dfd3
--- /dev/null
+++ b/plugins/Ldap_Login/language/de_DE/plugin.lang.php
@@ -0,0 +1,51 @@
+
\ No newline at end of file
diff --git a/plugins/Ldap_Login/language/el_GR/index.php b/plugins/Ldap_Login/language/el_GR/index.php
new file mode 100644
index 0000000..ec66053
--- /dev/null
+++ b/plugins/Ldap_Login/language/el_GR/index.php
@@ -0,0 +1,7 @@
+
diff --git a/plugins/Ldap_Login/language/el_GR/plugin.lang.php b/plugins/Ldap_Login/language/el_GR/plugin.lang.php
new file mode 100644
index 0000000..722ef9a
--- /dev/null
+++ b/plugins/Ldap_Login/language/el_GR/plugin.lang.php
@@ -0,0 +1,51 @@
+
\ No newline at end of file
diff --git a/plugins/Ldap_Login/language/en_UK/description.txt b/plugins/Ldap_Login/language/en_UK/description.txt
new file mode 100644
index 0000000..cbcc1c5
--- /dev/null
+++ b/plugins/Ldap_Login/language/en_UK/description.txt
@@ -0,0 +1 @@
+allow to login piwigo users with their ldap credentials (login, mail, or another attribute specified by piwigo admins + ldap password).
diff --git a/plugins/Ldap_Login/language/en_UK/plugin.lang.php b/plugins/Ldap_Login/language/en_UK/plugin.lang.php
new file mode 100644
index 0000000..44a7e8c
--- /dev/null
+++ b/plugins/Ldap_Login/language/en_UK/plugin.lang.php
@@ -0,0 +1,59 @@
+
\ No newline at end of file
diff --git a/plugins/Ldap_Login/language/es_ES/index.php b/plugins/Ldap_Login/language/es_ES/index.php
new file mode 100644
index 0000000..ec66053
--- /dev/null
+++ b/plugins/Ldap_Login/language/es_ES/index.php
@@ -0,0 +1,7 @@
+
diff --git a/plugins/Ldap_Login/language/es_ES/plugin.lang.php b/plugins/Ldap_Login/language/es_ES/plugin.lang.php
new file mode 100644
index 0000000..558e223
--- /dev/null
+++ b/plugins/Ldap_Login/language/es_ES/plugin.lang.php
@@ -0,0 +1,52 @@
+
\ No newline at end of file
diff --git a/plugins/Ldap_Login/language/fr_CA/plugin.lang.php b/plugins/Ldap_Login/language/fr_CA/plugin.lang.php
new file mode 100644
index 0000000..5de7507
--- /dev/null
+++ b/plugins/Ldap_Login/language/fr_CA/plugin.lang.php
@@ -0,0 +1,59 @@
+
\ No newline at end of file
diff --git a/plugins/Ldap_Login/language/fr_FR/plugin.lang.php b/plugins/Ldap_Login/language/fr_FR/plugin.lang.php
new file mode 100644
index 0000000..5de7507
--- /dev/null
+++ b/plugins/Ldap_Login/language/fr_FR/plugin.lang.php
@@ -0,0 +1,59 @@
+
\ No newline at end of file
diff --git a/plugins/Ldap_Login/language/index.php b/plugins/Ldap_Login/language/index.php
new file mode 100644
index 0000000..df13f7e
--- /dev/null
+++ b/plugins/Ldap_Login/language/index.php
@@ -0,0 +1,30 @@
+
\ No newline at end of file
diff --git a/plugins/Ldap_Login/language/it_IT/index.php b/plugins/Ldap_Login/language/it_IT/index.php
new file mode 100644
index 0000000..ec66053
--- /dev/null
+++ b/plugins/Ldap_Login/language/it_IT/index.php
@@ -0,0 +1,7 @@
+
diff --git a/plugins/Ldap_Login/language/it_IT/plugin.lang.php b/plugins/Ldap_Login/language/it_IT/plugin.lang.php
new file mode 100644
index 0000000..14b2264
--- /dev/null
+++ b/plugins/Ldap_Login/language/it_IT/plugin.lang.php
@@ -0,0 +1,51 @@
+
\ No newline at end of file
diff --git a/plugins/Ldap_Login/language/lv_LV/index.php b/plugins/Ldap_Login/language/lv_LV/index.php
new file mode 100644
index 0000000..ec66053
--- /dev/null
+++ b/plugins/Ldap_Login/language/lv_LV/index.php
@@ -0,0 +1,7 @@
+
diff --git a/plugins/Ldap_Login/language/lv_LV/plugin.lang.php b/plugins/Ldap_Login/language/lv_LV/plugin.lang.php
new file mode 100644
index 0000000..19c0a03
--- /dev/null
+++ b/plugins/Ldap_Login/language/lv_LV/plugin.lang.php
@@ -0,0 +1,52 @@
+
\ No newline at end of file
diff --git a/plugins/Ldap_Login/language/pt_BR/index.php b/plugins/Ldap_Login/language/pt_BR/index.php
new file mode 100644
index 0000000..ec66053
--- /dev/null
+++ b/plugins/Ldap_Login/language/pt_BR/index.php
@@ -0,0 +1,7 @@
+
diff --git a/plugins/Ldap_Login/language/pt_BR/plugin.lang.php b/plugins/Ldap_Login/language/pt_BR/plugin.lang.php
new file mode 100644
index 0000000..4762502
--- /dev/null
+++ b/plugins/Ldap_Login/language/pt_BR/plugin.lang.php
@@ -0,0 +1,52 @@
+
\ No newline at end of file
diff --git a/plugins/Ldap_Login/language/pt_PT/index.php b/plugins/Ldap_Login/language/pt_PT/index.php
new file mode 100644
index 0000000..ec66053
--- /dev/null
+++ b/plugins/Ldap_Login/language/pt_PT/index.php
@@ -0,0 +1,7 @@
+
diff --git a/plugins/Ldap_Login/language/pt_PT/plugin.lang.php b/plugins/Ldap_Login/language/pt_PT/plugin.lang.php
new file mode 100644
index 0000000..4f5ca9d
--- /dev/null
+++ b/plugins/Ldap_Login/language/pt_PT/plugin.lang.php
@@ -0,0 +1,50 @@
+
\ No newline at end of file
diff --git a/plugins/Ldap_Login/language/ru_RU/index.php b/plugins/Ldap_Login/language/ru_RU/index.php
new file mode 100644
index 0000000..ec66053
--- /dev/null
+++ b/plugins/Ldap_Login/language/ru_RU/index.php
@@ -0,0 +1,7 @@
+
diff --git a/plugins/Ldap_Login/language/ru_RU/plugin.lang.php b/plugins/Ldap_Login/language/ru_RU/plugin.lang.php
new file mode 100644
index 0000000..cefb268
--- /dev/null
+++ b/plugins/Ldap_Login/language/ru_RU/plugin.lang.php
@@ -0,0 +1,51 @@
+
\ No newline at end of file
diff --git a/plugins/Ldap_Login/language/sk_SK/index.php b/plugins/Ldap_Login/language/sk_SK/index.php
new file mode 100644
index 0000000..ec66053
--- /dev/null
+++ b/plugins/Ldap_Login/language/sk_SK/index.php
@@ -0,0 +1,7 @@
+
diff --git a/plugins/Ldap_Login/language/sk_SK/plugin.lang.php b/plugins/Ldap_Login/language/sk_SK/plugin.lang.php
new file mode 100644
index 0000000..2da26aa
--- /dev/null
+++ b/plugins/Ldap_Login/language/sk_SK/plugin.lang.php
@@ -0,0 +1,51 @@
+
\ No newline at end of file
diff --git a/plugins/Ldap_Login/language/tr_TR/index.php b/plugins/Ldap_Login/language/tr_TR/index.php
new file mode 100644
index 0000000..ec66053
--- /dev/null
+++ b/plugins/Ldap_Login/language/tr_TR/index.php
@@ -0,0 +1,7 @@
+
diff --git a/plugins/Ldap_Login/language/tr_TR/plugin.lang.php b/plugins/Ldap_Login/language/tr_TR/plugin.lang.php
new file mode 100644
index 0000000..02431a8
--- /dev/null
+++ b/plugins/Ldap_Login/language/tr_TR/plugin.lang.php
@@ -0,0 +1,51 @@
+
\ No newline at end of file
diff --git a/plugins/Ldap_Login/main.inc.php b/plugins/Ldap_Login/main.inc.php
new file mode 100644
index 0000000..99b254d
--- /dev/null
+++ b/plugins/Ldap_Login/main.inc.php
@@ -0,0 +1,114 @@
+load_config();
+set_plugin_data($plugin['id'], $ldap);
+unset($ldap);
+
+// +-----------------------------------------------------------------------+
+// | functions |
+// +-----------------------------------------------------------------------+
+
+function random_password( $length = 8 ) {
+ $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_-=+;:,.?";
+ $password = substr( str_shuffle( $chars ), 0, $length );
+ return $password;
+}
+
+function ld_init(){
+ load_language('plugin.lang', LDAP_LOGIN_PATH);
+}
+
+
+function login($success, $username, $password, $remember_me){
+
+ global $conf;
+
+ $obj = new Ldap();
+ $obj->load_config();
+ $obj->ldap_conn() or die("Unable to connect LDAP server : ".$ldap->getErrorString());
+
+ if (!$obj->ldap_bind_as($username,$password)){ // bind with userdn
+ trigger_action('login_failure', stripslashes($username));
+ return false; // wrong password
+ }
+
+ // search user in piwigo database
+$query = 'SELECT '.$conf['user_fields']['id'].' AS id FROM '.USERS_TABLE.' WHERE '.$conf['user_fields']['username'].' = \''.pwg_db_real_escape_string($username).'\' ;';
+
+ $row = pwg_db_fetch_assoc(pwg_query($query));
+
+ // if query is not empty, it means everything is ok and we can continue, auth is done !
+ if (!empty($row['id'])) {
+ log_user($row['id'], $remember_me);
+ trigger_action('login_success', stripslashes($username));
+ return true;
+ }
+
+ // if query is empty but ldap auth is done we can create a piwigo user if it's said so !
+ else {
+ // this is where we check we are allowed to create new users upon that.
+ if ($obj->config['allow_newusers']) {
+
+ // we got the email address
+ if ($obj->ldap_mail($username)) {
+ $mail = $obj->ldap_mail($username);
+ }
+ else {
+ $mail = NULL;
+ }
+
+ // we actually register the new user
+ $new_id = register_user($username,random_password(8),$mail);
+
+ // now we fetch again his id in the piwigo db, and we get them, as we just created him !
+ //$query = 'SELECT '.$conf['user_fields']['id'].' AS id FROM '.USERS_TABLE.' WHERE '.$conf['user_fields']['username'].' = \''.pwg_db_real_escape_string($username).'\' ;';
+ //$row = pwg_db_fetch_assoc(pwg_query($query));
+
+ log_user($new_id, False);
+ trigger_action('login_success', stripslashes($username));
+ redirect('profile.php');
+ return true;
+ }
+ // else : this is the normal behavior ! user is not created.
+ else {
+ trigger_action('login_failure', stripslashes($username));
+ return false;
+ }
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/plugins/Ldap_Login/maintain.inc.php b/plugins/Ldap_Login/maintain.inc.php
new file mode 100644
index 0000000..ff5015b
--- /dev/null
+++ b/plugins/Ldap_Login/maintain.inc.php
@@ -0,0 +1,75 @@
+load_config();
+ }
+
+ else {
+ $config->load_default_config();
+ }
+
+ $config->save_config();
+
+ $this->installed = true;
+ }
+
+ /**
+ * plugin activation
+ *
+ * this function is triggered after installation, by manual activation
+ * or after a plugin update
+ * for this last case you must manage updates tasks of your plugin in this function
+ */
+ function activate($plugin_version, &$errors=array())
+ {
+ if (!$this->installed)
+ {
+ $this->install($plugin_version, $errors);
+ }
+ }
+
+ function deactivate()
+ {
+ }
+
+ function uninstall()
+ {
+ }
+
+}
\ No newline at end of file
diff --git a/plugins/Ldap_Login/pem_metadata.txt b/plugins/Ldap_Login/pem_metadata.txt
new file mode 100644
index 0000000..ac2f543
--- /dev/null
+++ b/plugins/Ldap_Login/pem_metadata.txt
@@ -0,0 +1,4 @@
+File automatically created from SVN repository.
+
+URL: http://piwigo.org/svn/extensions/Ldap_Login
+Revision: 27288
\ No newline at end of file
diff --git a/scripts/install b/scripts/install
new file mode 100644
index 0000000..5eec2a2
--- /dev/null
+++ b/scripts/install
@@ -0,0 +1,112 @@
+#!/bin/bash
+
+# Retrieve arguments
+domain=$1
+path=$2
+user=$3
+is_public=$4
+
+# Check user parameter
+sudo yunohost user list --json | grep -q "\"username\": \"$user\""
+if [[ ! $? -eq 0 ]]; then
+ echo "Wrong user"
+ exit 1
+fi
+sudo yunohost app setting piwigo admin_user -v $user
+
+# Check domain/path availability
+sudo yunohost app checkurl $domain$path -a piwigo
+if [[ ! $? -eq 0 ]]; then
+ exit 1
+fi
+
+# Generate random password
+
+db_pwd=$(dd if=/dev/urandom bs=1 count=200 2> /dev/null | tr -c -d 'A-Za-z0-9' | sed -n 's/\(.\{24\}\).*/\1/p')
+
+# Use 'piwigo' as database name and user
+db_user=piwigo
+
+# Initialize database and store mysql password for upgrade
+sudo yunohost app initdb $db_user -p $db_pwd
+sudo yunohost app setting piwigo mysqlpwd -v $db_pwd
+
+
+# Modify Nginx configuration file and copy it to Nginx conf directory
+
+final_path=/var/www/piwigo
+
+sed -i "s@PATHTOCHANGE@$path@g" ../conf/nginx.conf
+sed -i "s@ALIASTOCHANGE@$final_path/@g" ../conf/nginx.conf
+sudo cp ../conf/nginx.conf /etc/nginx/conf.d/$domain.d/piwigo.conf
+
+# Copy files to the right place
+
+
+datapath=/home/yunohost.app/piwigo
+
+sudo mkdir -p $final_path
+sudo mkdir -p $datapath
+sudo mkdir -p $datapath/galleries
+sudo mkdir -p $datapath/upload
+sudo mkdir -p $final_path/_data
+sudo cp -a ../sources/* $final_path
+sudo ln -sd $datapath/_data $final_path/galleries
+sudo ln -sd $datapath/upload $final_path/upload
+sudo cp ../conf/index.php $final_path/upload
+
+
+sudo cp ../conf/nginx.conf /etc/nginx/conf.d/$domain.d/piwigo.conf
+#sudo cp ../conf/php-fpm.conf /etc/php5/fpm/pool.d/piwigo.conf
+#sudo cp ../conf/php-fpm.ini /etc/php5/fpm/conf.d/20-piwigo.ini
+sudo cp -R ../plugins/Ldap_Login $final_path/plugins/Ldap_Login
+
+
+# set permission
+
+sudo chown -R www-data:www-data $final_path
+sudo chmod -R 755 $final_path
+sudo chmod 777 $final_path/_data
+sudo chmod 777 $final_path/upload
+sudo chmod 755 -R $final_path/galleries
+
+
+# Reload Nginx and regenerate SSOwat conf
+sudo service php5-fpm restart
+sudo service nginx reload
+sudo yunohost app setting piwigo skipped_uris -v "/"
+sudo yunohost app ssowatconf
+
+# Generate random password for admin
+
+adm_pwd=$(dd if=/dev/urandom bs=1 count=200 2> /dev/null | tr -c -d 'A-Za-z0-9' | sed -n 's/\(.\{24\}\).*/\1/p')
+
+#configure piwigo via curl
+echo "127.0.0.1 $domain #yunopiwigo" | sudo tee -a /etc/hosts
+sleep 2
+curl -kL -X POST https://$domain$path/install.php?language=fr_FR --data "install=true&dbuser=$db_user&dbpasswd=$db_pwd&dbname=$db_user&admin_name=$user&admin_pass1=$adm_pwd&admin_pass2=$adm_pwd&admin_mail=webmaster@$domain" > /home/admin/test
+
+#change variable in local/config/database.inc.php
+
+sudo sed -i "s@DBTOCHANGE@$db_user@g" ../conf/database.inc.php
+sudo sed -i "s@USERTOCHANGE@$db_user@g" ../conf/database.inc.php
+sudo sed -i "s@PASSTOCHANGE@$db_pwd@g" ../conf/database.inc.php
+
+sudo cp ../conf/database.inc.php $final_path/local/config/database.inc.php
+
+#activate ldap plugin
+
+mysql -u $db_user -p$db_pwd $db_user -e "INSERT INTO plugins (id,state,version) VALUES ('Ldap_Login','active','1.1');"
+
+#protect URIs
+
+if [ $is_public = "No" ];
+then
+ sudo yunohost app setting piwigo protected_uris -v "/"
+ sudo yunohost app ssowatconf
+fi
+
+# Remove temporary entry in /etc/hosts
+sudo sed -i '/yunopiwigo/d' /etc/hosts
+
+
diff --git a/scripts/remove b/scripts/remove
new file mode 100644
index 0000000..4329e0d
--- /dev/null
+++ b/scripts/remove
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+db_user=piwigo
+db_name=piwigo
+root_pwd=$(sudo cat /etc/yunohost/mysql)
+domain=$(sudo yunohost app setting piwigo domain)
+
+mysql -u root -p$root_pwd -e "DROP DATABASE $db_name ; DROP USER $db_user@localhost ;"
+
+sudo rm -rf /var/www/piwigo
+sudo rm -f /etc/nginx/conf.d/$domain.d/piwigo.conf
+sudo rm -rf /home/yunohost.app/piwigo
+
+sudo service nginx reload
+
diff --git a/scripts/remove~ b/scripts/remove~
new file mode 100644
index 0000000..60973d0
--- /dev/null
+++ b/scripts/remove~
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+db_user=piwigo
+db_name=piwigo
+root_pwd=$(sudo cat /etc/yunohost/mysql)
+domain=$(sudo yunohost app setting piwigo domain)
+
+mysql -u root -p$root_pwd -e "DROP DATABASE $db_name ; DROP USER $db_user@localhost ;"
+
+sudo rm -rf /var/www/piwigo
+sudo rm -f /etc/nginx/conf.d/$domain.d/piwigo.conf
+sudo rm -f /etc/php5/fpm/pool.d/piwigo.conf
+sudo rm -f /etc/php5/fpm/conf.d/20-piwigo.ini
+sudo rm -rf /home/yunohost.app/piwigo
+
+sudo service nginx reload
+
diff --git a/sources/_data/dummy.txt b/sources/_data/dummy.txt
new file mode 100644
index 0000000..e69de29
diff --git a/sources/about.php b/sources/about.php
new file mode 100644
index 0000000..e4f04bf
--- /dev/null
+++ b/sources/about.php
@@ -0,0 +1,63 @@
+set_filename('about', 'about.tpl');
+
+$template->assign('ABOUT_MESSAGE', load_language('about.html','', array('return'=>true)) );
+
+$theme_about = load_language('about.html', PHPWG_THEMES_PATH.$user['theme'].'/', array('return' => true));
+if ( $theme_about !== false )
+{
+ $template->assign('THEME_ABOUT', $theme_about);
+}
+
+// include menubar
+$themeconf = $template->get_template_vars('themeconf');
+if (!isset($themeconf['hide_menu_on']) OR !in_array('theAboutPage', $themeconf['hide_menu_on']))
+{
+ include( PHPWG_ROOT_PATH.'include/menubar.inc.php');
+}
+
+include(PHPWG_ROOT_PATH.'include/page_header.php');
+flush_page_messages();
+$template->pparse('about');
+include(PHPWG_ROOT_PATH.'include/page_tail.php');
+?>
diff --git a/sources/action.php b/sources/action.php
new file mode 100644
index 0000000..4b92e05
--- /dev/null
+++ b/sources/action.php
@@ -0,0 +1,200 @@
+ restriction)
+$query='
+SELECT id
+ FROM '.CATEGORIES_TABLE.'
+ INNER JOIN '.IMAGE_CATEGORY_TABLE.' ON category_id = id
+ WHERE image_id = '.$_GET['id'].'
+'.get_sql_condition_FandF(
+ array(
+ 'forbidden_categories' => 'category_id',
+ 'forbidden_images' => 'image_id',
+ ),
+ ' AND'
+ ).'
+ LIMIT 1
+;';
+if ( pwg_db_num_rows(pwg_query($query))<1 )
+{
+ do_error(401, 'Access denied');
+}
+
+include_once(PHPWG_ROOT_PATH.'include/functions_picture.inc.php');
+$file='';
+switch ($_GET['part'])
+{
+ case 'e':
+ if ( !$user['enabled_high'] )
+ {
+ $deriv = new DerivativeImage(IMG_XXLARGE, new SrcImage($element_info));
+ if ( !$deriv->same_as_source() )
+ {
+ do_error(401, 'Access denied e');
+ }
+ }
+ $file = get_element_path($element_info);
+ break;
+ case 'r':
+ $file = original_to_representative( get_element_path($element_info), $element_info['representative_ext'] );
+ break;
+}
+
+if ( empty($file) )
+{
+ do_error(404, 'Requested file not found');
+}
+
+if ($_GET['part'] == 'e') {
+ pwg_log($_GET['id'], 'high');
+}
+else if ($_GET['part'] == 'e')
+{
+ pwg_log($_GET['id'], 'other');
+}
+
+$http_headers = array();
+
+$ctype = null;
+if (!url_is_remote($file))
+{
+ if ( !@is_readable($file) )
+ {
+ do_error(404, "Requested file not found - $file");
+ }
+ $http_headers[] = 'Content-Length: '.@filesize($file);
+ if ( function_exists('mime_content_type') )
+ {
+ $ctype = mime_content_type($file);
+ }
+
+ $gmt_mtime = gmdate('D, d M Y H:i:s', filemtime($file)).' GMT';
+ $http_headers[] = 'Last-Modified: '.$gmt_mtime;
+
+ // following lines would indicate how the client should handle the cache
+ /* $max_age=300;
+ $http_headers[] = 'Expires: '.gmdate('D, d M Y H:i:s', time()+$max_age).' GMT';
+ // HTTP/1.1 only
+ $http_headers[] = 'Cache-Control: private, must-revalidate, max-age='.$max_age;*/
+
+ if ( isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) )
+ {
+ set_status_header(304);
+ foreach ($http_headers as $header)
+ {
+ header( $header );
+ }
+ exit();
+ }
+}
+
+if (!isset($ctype))
+{ // give it a guess
+ $ctype = guess_mime_type( get_extension($file) );
+}
+
+$http_headers[] = 'Content-Type: '.$ctype;
+
+if (isset($_GET['download']))
+{
+ $http_headers[] = 'Content-Disposition: attachment; filename="'.$element_info['file'].'";';
+ $http_headers[] = 'Content-Transfer-Encoding: binary';
+}
+else
+{
+ $http_headers[] = 'Content-Disposition: inline; filename="'
+ .basename($file).'";';
+}
+
+foreach ($http_headers as $header)
+{
+ header( $header );
+}
+
+// Looking at the safe_mode configuration for execution time
+if (ini_get('safe_mode') == 0)
+{
+ @set_time_limit(0);
+}
+
+@readfile($file);
+
+?>
\ No newline at end of file
diff --git a/sources/admin.php b/sources/admin.php
new file mode 100644
index 0000000..cf36ce5
--- /dev/null
+++ b/sources/admin.php
@@ -0,0 +1,309 @@
+ 0)
+ {
+ $redirect_url.= '?'.implode('&', $url_params);
+ }
+
+ redirect($redirect_url);
+}
+
+// +-----------------------------------------------------------------------+
+// | Synchronize user informations |
+// +-----------------------------------------------------------------------+
+
+// sync_user() is only useful when external authentication is activated
+if ($conf['external_authentification'])
+{
+ sync_users();
+}
+
+// +-----------------------------------------------------------------------+
+// | Variables init |
+// +-----------------------------------------------------------------------+
+
+$change_theme_url = PHPWG_ROOT_PATH.'admin.php?';
+$test_get = $_GET;
+unset($test_get['page']);
+unset($test_get['section']);
+unset($test_get['tag']);
+if (count($test_get) == 0 and !empty($_SERVER['QUERY_STRING']))
+{
+ $change_theme_url.= str_replace('&', '&', $_SERVER['QUERY_STRING']).'&';
+}
+$change_theme_url.= 'change_theme=1';
+
+// ?page=plugin-community-pendings is an clean alias of
+// ?page=plugin§ion=community/admin.php&tab=pendings
+if (isset($_GET['page']) and preg_match('/^plugin-([^-]*)(?:-(.*))?$/', $_GET['page'], $matches))
+{
+ $_GET['page'] = 'plugin';
+ $_GET['section'] = $matches[1].'/admin.php';
+ if (isset($matches[2]))
+ {
+ $_GET['tab'] = $matches[2];
+ }
+}
+
+// ?page=album-134-properties is an clean alias of
+// ?page=album&cat_id=134&tab=properties
+if (isset($_GET['page']) and preg_match('/^album-(\d+)(?:-(.*))?$/', $_GET['page'], $matches))
+{
+ $_GET['page'] = 'album';
+ $_GET['cat_id'] = $matches[1];
+ if (isset($matches[2]))
+ {
+ $_GET['tab'] = $matches[2];
+ }
+}
+
+// ?page=photo-1234-properties is an clean alias of
+// ?page=photo&image_id=1234&tab=properties
+if (isset($_GET['page']) and preg_match('/^photo-(\d+)(?:-(.*))?$/', $_GET['page'], $matches))
+{
+ $_GET['page'] = 'photo';
+ $_GET['image_id'] = $matches[1];
+ if (isset($matches[2]))
+ {
+ $_GET['tab'] = $matches[2];
+ }
+}
+
+if (isset($_GET['page'])
+ and preg_match('/^[a-z_]*$/', $_GET['page'])
+ and is_file(PHPWG_ROOT_PATH.'admin/'.$_GET['page'].'.php'))
+{
+ $page['page'] = $_GET['page'];
+}
+else
+{
+ $page['page'] = 'intro';
+}
+
+$link_start = PHPWG_ROOT_PATH.'admin.php?page=';
+$conf_link = $link_start.'configuration§ion=';
+
+// +-----------------------------------------------------------------------+
+// | Template init |
+// +-----------------------------------------------------------------------+
+
+$title = l10n('Piwigo Administration'); // for include/page_header.php
+$page['page_banner'] = ''.l10n('Piwigo Administration').' ';
+$page['body_id'] = 'theAdminPage';
+
+$template->set_filenames(array('admin' => 'admin.tpl'));
+
+$template->assign(
+ array(
+ 'USERNAME' => $user['username'],
+ 'ENABLE_SYNCHRONIZATION' => $conf['enable_synchronization'],
+ 'U_SITE_MANAGER'=> $link_start.'site_manager',
+ 'U_HISTORY_STAT'=> $link_start.'stats',
+ 'U_FAQ'=> $link_start.'help',
+ 'U_SITES'=> $link_start.'remote_site',
+ 'U_MAINTENANCE'=> $link_start.'maintenance',
+ 'U_NOTIFICATION_BY_MAIL'=> $link_start.'notification_by_mail',
+ 'U_CONFIG_GENERAL'=> $link_start.'configuration',
+ 'U_CONFIG_DISPLAY'=> $conf_link.'default',
+ 'U_CONFIG_EXTENTS'=> $link_start.'extend_for_templates',
+ 'U_CONFIG_MENUBAR'=> $link_start.'menubar',
+ 'U_CONFIG_LANGUAGES' => $link_start.'languages',
+ 'U_CONFIG_THEMES'=> $link_start.'themes',
+ 'U_CATEGORIES'=> $link_start.'cat_list',
+ 'U_CAT_OPTIONS'=> $link_start.'cat_options',
+ 'U_CAT_UPDATE'=> $link_start.'site_update&site=1',
+ 'U_RATING'=> $link_start.'rating',
+ 'U_RECENT_SET'=> $link_start.'batch_manager&filter=prefilter-last_import',
+ 'U_BATCH'=> $link_start.'batch_manager',
+ 'U_TAGS'=> $link_start.'tags',
+ 'U_USERS'=> $link_start.'user_list',
+ 'U_GROUPS'=> $link_start.'group_list',
+ 'U_RETURN'=> get_gallery_home_url(),
+ 'U_ADMIN'=> PHPWG_ROOT_PATH.'admin.php',
+ 'U_LOGOUT'=> PHPWG_ROOT_PATH.'index.php?act=logout',
+ 'U_PLUGINS'=> $link_start.'plugins',
+ 'U_ADD_PHOTOS' => $link_start.'photos_add',
+ 'U_CHANGE_THEME' => $change_theme_url,
+ 'U_UPDATES' => $link_start.'updates',
+ )
+ );
+
+if ($conf['activate_comments'])
+{
+ $template->assign('U_COMMENTS', $link_start.'comments');
+
+ // pending comments
+ $query = '
+SELECT COUNT(*)
+ FROM '.COMMENTS_TABLE.'
+ WHERE validated=\'false\'
+;';
+ list($nb_comments) = pwg_db_fetch_row(pwg_query($query));
+
+ if ($nb_comments > 0)
+ {
+ $template->assign('NB_PENDING_COMMENTS', $nb_comments);
+ }
+}
+
+// any photo in the caddie?
+$query = '
+SELECT COUNT(*)
+ FROM '.CADDIE_TABLE.'
+ WHERE user_id = '.$user['id'].'
+;';
+list($nb_photos_in_caddie) = pwg_db_fetch_row(pwg_query($query));
+
+if ($nb_photos_in_caddie > 0)
+{
+ $template->assign(
+ array(
+ 'NB_PHOTOS_IN_CADDIE' => $nb_photos_in_caddie,
+ 'U_CADDIE' => $link_start.'batch_manager&filter=prefilter-caddie',
+ )
+ );
+}
+
+// +-----------------------------------------------------------------------+
+// | Plugin menu |
+// +-----------------------------------------------------------------------+
+
+$plugin_menu_links = trigger_event('get_admin_plugin_menu_links', array() );
+
+function UC_name_compare($a, $b)
+{
+ return strcmp(strtolower($a['NAME']), strtolower($b['NAME']));
+}
+usort($plugin_menu_links, 'UC_name_compare');
+$template->assign('plugin_menu_items', $plugin_menu_links);
+
+// +-----------------------------------------------------------------------+
+// | Refresh permissions |
+// +-----------------------------------------------------------------------+
+
+// Only for pages witch change permissions
+if (
+ in_array($page['page'],
+ array(
+ 'site_manager', // delete site
+ 'site_update', // ?only POST
+ )
+ )
+ or ( !empty($_POST) and in_array($page['page'],
+ array(
+ 'album', // public/private; lock/unlock, permissions
+ 'cat_move',
+ 'cat_options', // public/private; lock/unlock
+ 'batch_manager', // associate/dissociate; delete; set level
+ 'user_list', // group assoc; user level
+ 'user_perm',
+ )
+ )
+ )
+ )
+{
+ invalidate_user_cache();
+}
+
+// +-----------------------------------------------------------------------+
+// | Include specific page |
+// +-----------------------------------------------------------------------+
+
+trigger_action('loc_begin_admin_page');
+include(PHPWG_ROOT_PATH.'admin/'.$page['page'].'.php');
+
+$template->assign('ACTIVE_MENU', get_active_menu($page['page']));
+
+// +-----------------------------------------------------------------------+
+// | Sending html code |
+// +-----------------------------------------------------------------------+
+
+// Add the Piwigo Official menu
+$template->assign( 'pwgmenu', pwg_URL() );
+
+include(PHPWG_ROOT_PATH.'include/page_header.php');
+
+trigger_action('loc_end_admin');
+
+flush_page_messages();
+
+$template->pparse('admin');
+
+include(PHPWG_ROOT_PATH.'include/page_tail.php');
+?>
diff --git a/sources/admin/album.php b/sources/admin/album.php
new file mode 100644
index 0000000..d1186aa
--- /dev/null
+++ b/sources/admin/album.php
@@ -0,0 +1,90 @@
+set_id('album');
+$tabsheet->select($page['tab']);
+$tabsheet->assign();
+
+// +-----------------------------------------------------------------------+
+// | Load the tab |
+// +-----------------------------------------------------------------------+
+
+if ('properties' == $page['tab'])
+{
+ include(PHPWG_ROOT_PATH.'admin/cat_modify.php');
+}
+elseif ('sort_order' == $page['tab'])
+{
+ include(PHPWG_ROOT_PATH.'admin/element_set_ranks.php');
+}
+elseif ('permissions' == $page['tab'])
+{
+ $_GET['cat'] = $_GET['cat_id'];
+ include(PHPWG_ROOT_PATH.'admin/cat_perm.php');
+}
+else
+{
+ include(PHPWG_ROOT_PATH.'admin/album_'.$page['tab'].'.php');
+}
+?>
\ No newline at end of file
diff --git a/sources/admin/album_notification.php b/sources/admin/album_notification.php
new file mode 100644
index 0000000..d565b6c
--- /dev/null
+++ b/sources/admin/album_notification.php
@@ -0,0 +1,197 @@
+ 0)
+ {
+ $element = pwg_db_fetch_assoc($result);
+
+ $img_url = ' ';
+ }
+ }
+
+ if (!isset($img_url))
+ {
+ $img_url = '';
+ }
+
+ pwg_mail_group(
+ $_POST['group'],
+ array(
+ 'subject' => l10n('[%s] Visit album %s', $conf['gallery_title'], trigger_event('render_category_name', $category['name'], 'admin_cat_list')),
+ // TODO : change this language variable to 'Visit album %s'
+ // TODO : 'language_selected' => ....
+ ),
+ array(
+ 'filename' => 'cat_group_info',
+ 'assign' => array(
+ 'IMG_URL' => $img_url,
+ 'CAT_NAME' => trigger_event('render_category_name', $category['name'], 'admin_cat_list'),
+ 'LINK' => make_index_url(array(
+ 'category' => array(
+ 'id' => $category['id'],
+ 'name' => trigger_event('render_category_name', $category['name'], 'admin_cat_list'),
+ 'permalink' => $category['permalink']
+ )
+ )),
+ 'CPL_CONTENT' => empty($_POST['mail_content']) ? '' : stripslashes($_POST['mail_content']),
+ )
+ )
+ );
+
+ unset_make_full_url();
+
+ $query = '
+SELECT
+ name
+ FROM '.GROUPS_TABLE.'
+ WHERE id = '.$_POST['group'].'
+;';
+ list($group_name) = pwg_db_fetch_row(pwg_query($query));
+
+ $page['infos'][] = l10n('An information email was sent to group "%s"', $group_name);
+}
+
+// +-----------------------------------------------------------------------+
+// | template initialization |
+// +-----------------------------------------------------------------------+
+
+$template->set_filename('album_notification', 'album_notification.tpl');
+
+$template->assign(
+ array(
+ 'CATEGORIES_NAV' =>
+ get_cat_display_name_from_id(
+ $page['cat'],
+ 'admin.php?page=album-'
+ ),
+ 'F_ACTION' => $admin_album_base_url.'-notification',
+ 'PWG_TOKEN' => get_pwg_token(),
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | form construction |
+// +-----------------------------------------------------------------------+
+
+$query = '
+SELECT
+ id AS group_id
+ FROM '.GROUPS_TABLE.'
+;';
+$all_group_ids = array_from_query($query, 'group_id');
+
+if (count($all_group_ids) == 0)
+{
+ $template->assign('no_group_in_gallery', true);
+}
+else
+{
+ if ('private' == $category['status'])
+ {
+ $query = '
+SELECT
+ group_id
+ FROM '.GROUP_ACCESS_TABLE.'
+ WHERE cat_id = '.$category['id'].'
+;';
+ $group_ids = array_from_query($query, 'group_id');
+
+ if (count($group_ids) == 0)
+ {
+ $template->assign('permission_url', $admin_album_base_url.'-permissions');
+ }
+ }
+ else
+ {
+ $group_ids = $all_group_ids;
+ }
+
+ if (count($group_ids) > 0)
+ {
+ $query = '
+SELECT
+ id,
+ name
+ FROM '.GROUPS_TABLE.'
+ WHERE id IN ('.implode(',', $group_ids).')
+ ORDER BY name ASC
+;';
+ $template->assign(
+ 'group_mail_options',
+ simple_hash_from_query($query, 'id', 'name')
+ );
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'album_notification');
+?>
diff --git a/sources/admin/batch_manager.php b/sources/admin/batch_manager.php
new file mode 100644
index 0000000..2cb76f7
--- /dev/null
+++ b/sources/admin/batch_manager.php
@@ -0,0 +1,564 @@
+'; print_r($_POST); echo '';
+ unset($_REQUEST['start']); // new photo set must reset the page
+ $_SESSION['bulk_manager_filter'] = array();
+
+ if (isset($_POST['filter_prefilter_use']))
+ {
+ $_SESSION['bulk_manager_filter']['prefilter'] = $_POST['filter_prefilter'];
+ }
+
+ if (isset($_POST['filter_category_use']))
+ {
+ $_SESSION['bulk_manager_filter']['category'] = $_POST['filter_category'];
+
+ if (isset($_POST['filter_category_recursive']))
+ {
+ $_SESSION['bulk_manager_filter']['category_recursive'] = true;
+ }
+ }
+
+ if (isset($_POST['filter_tags_use']))
+ {
+ $_SESSION['bulk_manager_filter']['tags'] = get_tag_ids($_POST['filter_tags'], false);
+
+ if (isset($_POST['tag_mode']) and in_array($_POST['tag_mode'], array('AND', 'OR')))
+ {
+ $_SESSION['bulk_manager_filter']['tag_mode'] = $_POST['tag_mode'];
+ }
+ }
+
+ if (isset($_POST['filter_level_use']))
+ {
+ if (in_array($_POST['filter_level'], $conf['available_permission_levels']))
+ {
+ $_SESSION['bulk_manager_filter']['level'] = $_POST['filter_level'];
+
+ if (isset($_POST['filter_level_include_lower']))
+ {
+ $_SESSION['bulk_manager_filter']['level_include_lower'] = true;
+ }
+ }
+ }
+
+ if (isset($_POST['filter_dimension_use']))
+ {
+ foreach (array('min_width','max_width','min_height','max_height') as $type)
+ {
+ if ( preg_match('#^[0-9]+$#', $_POST['filter_dimension_'. $type ]) )
+ {
+ $_SESSION['bulk_manager_filter']['dimension'][$type] = $_POST['filter_dimension_'. $type ];
+ }
+ }
+ foreach (array('min_ratio','max_ratio') as $type)
+ {
+ if ( preg_match('#^[0-9\.]+$#', $_POST['filter_dimension_'. $type ]) )
+ {
+ $_SESSION['bulk_manager_filter']['dimension'][$type] = $_POST['filter_dimension_'. $type ];
+ }
+ }
+ }
+}
+// filters from url
+else if (isset($_GET['filter']))
+{
+ if (!is_array($_GET['filter']))
+ {
+ $_GET['filter'] = explode(',', $_GET['filter']);
+ }
+
+ $_SESSION['bulk_manager_filter'] = array();
+
+ foreach ($_GET['filter'] as $filter)
+ {
+ list($type, $value) = explode('-', $filter);
+
+ switch ($type)
+ {
+ case 'prefilter':
+ $_SESSION['bulk_manager_filter']['prefilter'] = $value;
+ break;
+
+ case 'album':
+ if (is_numeric($value))
+ {
+ $_SESSION['bulk_manager_filter']['category'] = $value;
+ }
+ break;
+
+ case 'tag':
+ if (is_numeric($value))
+ {
+ $_SESSION['bulk_manager_filter']['tags'] = array($value);
+ $_SESSION['bulk_manager_filter']['tag_mode'] = 'AND';
+ }
+ break;
+
+ case 'level':
+ if (is_numeric($value) && in_array($value, $conf['available_permission_levels']))
+ {
+ $_SESSION['bulk_manager_filter']['level'] = $value;
+ }
+ break;
+ }
+ }
+}
+
+if (empty($_SESSION['bulk_manager_filter']))
+{
+ $_SESSION['bulk_manager_filter'] = array(
+ 'prefilter' => 'caddie'
+ );
+}
+
+// echo ''; print_r($_SESSION['bulk_manager_filter']); echo ' ';
+
+// depending on the current filter (in session), we find the appropriate photos
+$filter_sets = array();
+if (isset($_SESSION['bulk_manager_filter']['prefilter']))
+{
+ switch ($_SESSION['bulk_manager_filter']['prefilter'])
+ {
+ case 'caddie':
+ $query = '
+SELECT element_id
+ FROM '.CADDIE_TABLE.'
+ WHERE user_id = '.$user['id'].'
+;';
+ $filter_sets[] = array_from_query($query, 'element_id');
+
+ break;
+
+ case 'favorites':
+ $query = '
+SELECT image_id
+ FROM '.FAVORITES_TABLE.'
+ WHERE user_id = '.$user['id'].'
+;';
+ $filter_sets[] = array_from_query($query, 'image_id');
+
+ break;
+
+ case 'last_import':
+ $query = '
+SELECT MAX(date_available) AS date
+ FROM '.IMAGES_TABLE.'
+;';
+ $row = pwg_db_fetch_assoc(pwg_query($query));
+ if (!empty($row['date']))
+ {
+ $query = '
+SELECT id
+ FROM '.IMAGES_TABLE.'
+ WHERE date_available BETWEEN '.pwg_db_get_recent_period_expression(1, $row['date']).' AND \''.$row['date'].'\'
+;';
+ $filter_sets[] = array_from_query($query, 'id');
+ }
+
+ break;
+
+ case 'no_virtual_album':
+ // we are searching elements not linked to any virtual category
+ $query = '
+ SELECT id
+ FROM '.IMAGES_TABLE.'
+ ;';
+ $all_elements = array_from_query($query, 'id');
+
+ $query = '
+ SELECT id
+ FROM '.CATEGORIES_TABLE.'
+ WHERE dir IS NULL
+ ;';
+ $virtual_categories = array_from_query($query, 'id');
+ if (!empty($virtual_categories))
+ {
+ $query = '
+ SELECT DISTINCT(image_id)
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ WHERE category_id IN ('.implode(',', $virtual_categories).')
+ ;';
+ $linked_to_virtual = array_from_query($query, 'image_id');
+ }
+
+ $filter_sets[] = array_diff($all_elements, $linked_to_virtual);
+
+ break;
+
+ case 'no_album':
+ $query = '
+SELECT
+ id
+ FROM '.IMAGES_TABLE.'
+ LEFT JOIN '.IMAGE_CATEGORY_TABLE.' ON id = image_id
+ WHERE category_id is null
+;';
+ $filter_sets[] = array_from_query($query, 'id');
+
+ break;
+
+ case 'no_tag':
+ $query = '
+SELECT
+ id
+ FROM '.IMAGES_TABLE.'
+ LEFT JOIN '.IMAGE_TAG_TABLE.' ON id = image_id
+ WHERE tag_id is null
+;';
+ $filter_sets[] = array_from_query($query, 'id');
+
+ break;
+
+
+ case 'duplicates':
+ // we could use the group_concat MySQL function to retrieve the list of
+ // image_ids but it would not be compatible with PostgreSQL, so let's
+ // perform 2 queries instead. We hope there are not too many duplicates.
+ $query = '
+SELECT file
+ FROM '.IMAGES_TABLE.'
+ GROUP BY file
+ HAVING COUNT(*) > 1
+;';
+ $duplicate_files = array_from_query($query, 'file');
+
+ $query = '
+SELECT id
+ FROM '.IMAGES_TABLE.'
+ WHERE file IN (\''.implode("','", array_map('pwg_db_real_escape_string', $duplicate_files)).'\')
+;';
+ $filter_sets[] = array_from_query($query, 'id');
+
+ break;
+
+ case 'all_photos':
+ $query = '
+SELECT id
+ FROM '.IMAGES_TABLE.'
+ '.$conf['order_by'];
+
+ $filter_sets[] = array_from_query($query, 'id');
+
+ break;
+ }
+
+ $filter_sets = trigger_event('perform_batch_manager_prefilters', $filter_sets, $_SESSION['bulk_manager_filter']['prefilter']);
+}
+
+if (isset($_SESSION['bulk_manager_filter']['category']))
+{
+ $categories = array();
+
+ if (isset($_SESSION['bulk_manager_filter']['category_recursive']))
+ {
+ $categories = get_subcat_ids(array($_SESSION['bulk_manager_filter']['category']));
+ }
+ else
+ {
+ $categories = array($_SESSION['bulk_manager_filter']['category']);
+ }
+
+ $query = '
+ SELECT DISTINCT(image_id)
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ WHERE category_id IN ('.implode(',', $categories).')
+ ;';
+ $filter_sets[] = array_from_query($query, 'image_id');
+}
+
+if (isset($_SESSION['bulk_manager_filter']['level']))
+{
+ $operator = '=';
+ if (isset($_SESSION['bulk_manager_filter']['level_include_lower']))
+ {
+ $operator = '<=';
+ }
+
+ $query = '
+SELECT id
+ FROM '.IMAGES_TABLE.'
+ WHERE level '.$operator.' '.$_SESSION['bulk_manager_filter']['level'].'
+ '.$conf['order_by'];
+
+ $filter_sets[] = array_from_query($query, 'id');
+}
+
+if (!empty($_SESSION['bulk_manager_filter']['tags']))
+{
+ $filter_sets[] = get_image_ids_for_tags(
+ $_SESSION['bulk_manager_filter']['tags'],
+ $_SESSION['bulk_manager_filter']['tag_mode'],
+ null,
+ null,
+ false // we don't apply permissions in administration screens
+ );
+}
+
+if (isset($_SESSION['bulk_manager_filter']['dimension']))
+{
+ $where_clauses = array();
+ if (isset($_SESSION['bulk_manager_filter']['dimension']['min_width']))
+ {
+ $where_clause[] = 'width >= '.$_SESSION['bulk_manager_filter']['dimension']['min_width'];
+ }
+ if (isset($_SESSION['bulk_manager_filter']['dimension']['max_width']))
+ {
+ $where_clause[] = 'width <= '.$_SESSION['bulk_manager_filter']['dimension']['max_width'];
+ }
+ if (isset($_SESSION['bulk_manager_filter']['dimension']['min_height']))
+ {
+ $where_clause[] = 'height >= '.$_SESSION['bulk_manager_filter']['dimension']['min_height'];
+ }
+ if (isset($_SESSION['bulk_manager_filter']['dimension']['max_height']))
+ {
+ $where_clause[] = 'height <= '.$_SESSION['bulk_manager_filter']['dimension']['max_height'];
+ }
+ if (isset($_SESSION['bulk_manager_filter']['dimension']['min_ratio']))
+ {
+ $where_clause[] = 'width/height >= '.$_SESSION['bulk_manager_filter']['dimension']['min_ratio'];
+ }
+ if (isset($_SESSION['bulk_manager_filter']['dimension']['max_ratio']))
+ {
+ // max_ratio is a floor value, so must be a bit increased
+ $where_clause[] = 'width/height < '.($_SESSION['bulk_manager_filter']['dimension']['max_ratio']+0.01);
+ }
+
+ $query = '
+SELECT id
+ FROM '.IMAGES_TABLE.'
+ WHERE '.implode(' AND ',$where_clause).'
+ '.$conf['order_by'];
+
+ $filter_sets[] = array_from_query($query, 'id');
+}
+
+$current_set = array_shift($filter_sets);
+foreach ($filter_sets as $set)
+{
+ $current_set = array_intersect($current_set, $set);
+}
+$page['cat_elements_id'] = $current_set;
+
+
+// +-----------------------------------------------------------------------+
+// | first element to display |
+// +-----------------------------------------------------------------------+
+
+// $page['start'] contains the number of the first element in its
+// category. For exampe, $page['start'] = 12 means we must show elements #12
+// and $page['nb_images'] next elements
+
+if (!isset($_REQUEST['start'])
+ or !is_numeric($_REQUEST['start'])
+ or $_REQUEST['start'] < 0
+ or (isset($_REQUEST['display']) and 'all' == $_REQUEST['display']))
+{
+ $page['start'] = 0;
+}
+else
+{
+ $page['start'] = $_REQUEST['start'];
+}
+
+
+// +-----------------------------------------------------------------------+
+// | Tabs |
+// +-----------------------------------------------------------------------+
+$manager_link = get_root_url().'admin.php?page=batch_manager&mode=';
+
+if (isset($_GET['mode']))
+{
+ $page['tab'] = $_GET['mode'];
+}
+else
+{
+ $page['tab'] = 'global';
+}
+
+$tabsheet = new tabsheet();
+$tabsheet->set_id('batch_manager');
+$tabsheet->select($page['tab']);
+$tabsheet->assign();
+
+
+// +-----------------------------------------------------------------------+
+// | tags |
+// +-----------------------------------------------------------------------+
+
+$query = '
+SELECT id, name
+ FROM '.TAGS_TABLE.'
+;';
+$template->assign('tags', get_taglist($query, false));
+
+
+// +-----------------------------------------------------------------------+
+// | dimensions |
+// +-----------------------------------------------------------------------+
+
+$widths = array();
+$heights = array();
+$ratios = array();
+
+// get all width, height and ratios
+$query = '
+SELECT
+ DISTINCT width, height
+ FROM '.IMAGES_TABLE.'
+ WHERE width IS NOT NULL
+ AND height IS NOT NULL
+;';
+$result = pwg_query($query);
+
+if (pwg_db_num_rows($result))
+{
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ if ($row['width']>0 && $row['height']>0)
+ {
+ $widths[] = $row['width'];
+ $heights[] = $row['height'];
+ $ratios[] = floor($row['width'] / $row['height'] * 100) / 100;
+ }
+ }
+}
+if (empty($widths))
+{ // arbitrary values, only used when no photos on the gallery
+ $widths = array(600, 1920, 3500);
+ $heights = array(480, 1080, 2300);
+ $ratios = array(1.25, 1.52, 1.78);
+}
+
+
+
+$widths = array_unique($widths);
+sort($widths);
+
+$heights = array_unique($heights);
+sort($heights);
+
+$ratios = array_unique($ratios);
+sort($ratios);
+
+$dimensions['widths'] = implode(',', $widths);
+$dimensions['heights'] = implode(',', $heights);
+$dimensions['ratios'] = implode(',', $ratios);
+
+$dimensions['bounds'] = array(
+ 'min_width' => $widths[0],
+ 'max_width' => $widths[count($widths)-1],
+ 'min_height' => $heights[0],
+ 'max_height' => $heights[count($heights)-1],
+ 'min_ratio' => $ratios[0],
+ 'max_ratio' => $ratios[count($ratios)-1],
+ );
+
+// find ratio categories
+$ratio_categories = array(
+ 'portrait' => array(),
+ 'square' => array(),
+ 'landscape' => array(),
+ 'panorama' => array(),
+ );
+
+foreach ($ratios as $ratio)
+{
+ if ($ratio < 0.95)
+ {
+ $ratio_categories['portrait'][] = $ratio;
+ }
+ else if ($ratio >= 0.95 and $ratio <= 1.05)
+ {
+ $ratio_categories['square'][] = $ratio;
+ }
+ else if ($ratio > 1.05 and $ratio < 2)
+ {
+ $ratio_categories['landscape'][] = $ratio;
+ }
+ else if ($ratio >= 2)
+ {
+ $ratio_categories['panorama'][] = $ratio;
+ }
+}
+
+foreach (array_keys($ratio_categories) as $ratio_category)
+{
+ if (count($ratio_categories[$ratio_category]) > 0)
+ {
+ $dimensions['ratio_'.$ratio_category] = array(
+ 'min' => $ratio_categories[$ratio_category][0],
+ 'max' => array_pop($ratio_categories[$ratio_category]),
+ );
+ }
+}
+
+// selected=bound if nothing selected
+foreach (array_keys($dimensions['bounds']) as $type)
+{
+ $dimensions['selected'][$type] = isset($_SESSION['bulk_manager_filter']['dimension'][$type])
+ ? $_SESSION['bulk_manager_filter']['dimension'][$type]
+ : $dimensions['bounds'][$type]
+ ;
+}
+
+$template->assign('dimensions', $dimensions);
+
+
+// +-----------------------------------------------------------------------+
+// | open specific mode |
+// +-----------------------------------------------------------------------+
+
+include(PHPWG_ROOT_PATH.'admin/batch_manager_'.$page['tab'].'.php');
+?>
\ No newline at end of file
diff --git a/sources/admin/batch_manager_global.php b/sources/admin/batch_manager_global.php
new file mode 100644
index 0000000..2828d8b
--- /dev/null
+++ b/sources/admin/batch_manager_global.php
@@ -0,0 +1,734 @@
+ 0)
+ {
+ $query = '
+DELETE
+ FROM '.IMAGE_TAG_TABLE.'
+ WHERE image_id IN ('.implode(',', $collection).')
+ AND tag_id IN ('.implode(',', $_POST['del_tags']).')
+;';
+ pwg_query($query);
+ }
+ else
+ {
+ $page['errors'][] = l10n('Select at least one tag');
+ }
+ }
+
+ if ('associate' == $action)
+ {
+ associate_images_to_categories(
+ $collection,
+ array($_POST['associate'])
+ );
+
+ $_SESSION['page_infos'] = array(
+ l10n('Information data registered in database')
+ );
+
+ // let's refresh the page because we the current set might be modified
+ if ('no_album' == $page['prefilter'])
+ {
+ redirect($redirect_url);
+ }
+
+ if ('no_virtual_album' == $page['prefilter'])
+ {
+ $category_info = get_cat_info($_POST['associate']);
+ if (empty($category_info['dir']))
+ {
+ redirect($redirect_url);
+ }
+ }
+ }
+
+ if ('move' == $action)
+ {
+ move_images_to_categories($collection, array($_POST['move']));
+
+ $_SESSION['page_infos'] = array(
+ l10n('Information data registered in database')
+ );
+
+ // let's refresh the page because we the current set might be modified
+ if ('no_album' == $page['prefilter'])
+ {
+ redirect($redirect_url);
+ }
+
+ if ('no_virtual_album' == $page['prefilter'])
+ {
+ $category_info = get_cat_info($_POST['move']);
+ if (empty($category_info['dir']))
+ {
+ redirect($redirect_url);
+ }
+ }
+
+ if (isset($_SESSION['bulk_manager_filter']['category'])
+ and $_POST['move'] != $_SESSION['bulk_manager_filter']['category'])
+ {
+ redirect($redirect_url);
+ }
+ }
+
+ if ('dissociate' == $action)
+ {
+ // physical links must not be broken, so we must first retrieve image_id
+ // which create virtual links with the category to "dissociate from".
+ $query = '
+SELECT id
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ INNER JOIN '.IMAGES_TABLE.' ON image_id = id
+ WHERE category_id = '.$_POST['dissociate'].'
+ AND id IN ('.implode(',', $collection).')
+ AND (
+ category_id != storage_category_id
+ OR storage_category_id IS NULL
+ )
+;';
+ $dissociables = array_from_query($query, 'id');
+
+ if (!empty($dissociables))
+ {
+ $query = '
+DELETE
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ WHERE category_id = '.$_POST['dissociate'].'
+ AND image_id IN ('.implode(',', $dissociables).')
+';
+ pwg_query($query);
+
+ $_SESSION['page_infos'] = array(
+ l10n('Information data registered in database')
+ );
+
+ // let's refresh the page because the current set might be modified
+ redirect($redirect_url);
+ }
+ }
+
+ // author
+ if ('author' == $action)
+ {
+ if (isset($_POST['remove_author']))
+ {
+ $_POST['author'] = null;
+ }
+
+ $datas = array();
+ foreach ($collection as $image_id)
+ {
+ $datas[] = array(
+ 'id' => $image_id,
+ 'author' => $_POST['author']
+ );
+ }
+
+ mass_updates(
+ IMAGES_TABLE,
+ array('primary' => array('id'), 'update' => array('author')),
+ $datas
+ );
+ }
+
+ // title
+ if ('title' == $action)
+ {
+ if (isset($_POST['remove_title']))
+ {
+ $_POST['title'] = null;
+ }
+
+ $datas = array();
+ foreach ($collection as $image_id)
+ {
+ $datas[] = array(
+ 'id' => $image_id,
+ 'name' => $_POST['title']
+ );
+ }
+
+ mass_updates(
+ IMAGES_TABLE,
+ array('primary' => array('id'), 'update' => array('name')),
+ $datas
+ );
+ }
+
+ // date_creation
+ if ('date_creation' == $action)
+ {
+ $date_creation = sprintf(
+ '%u-%u-%u',
+ $_POST['date_creation_year'],
+ $_POST['date_creation_month'],
+ $_POST['date_creation_day']
+ );
+
+ if (isset($_POST['remove_date_creation']))
+ {
+ $date_creation = null;
+ }
+
+ $datas = array();
+ foreach ($collection as $image_id)
+ {
+ $datas[] = array(
+ 'id' => $image_id,
+ 'date_creation' => $date_creation
+ );
+ }
+
+ mass_updates(
+ IMAGES_TABLE,
+ array('primary' => array('id'), 'update' => array('date_creation')),
+ $datas
+ );
+ }
+
+ // privacy_level
+ if ('level' == $action)
+ {
+ $datas = array();
+ foreach ($collection as $image_id)
+ {
+ $datas[] = array(
+ 'id' => $image_id,
+ 'level' => $_POST['level']
+ );
+ }
+
+ mass_updates(
+ IMAGES_TABLE,
+ array('primary' => array('id'), 'update' => array('level')),
+ $datas
+ );
+
+ if (isset($_SESSION['bulk_manager_filter']['level']))
+ {
+ if ($_POST['level'] < $_SESSION['bulk_manager_filter']['level'])
+ {
+ redirect($redirect_url);
+ }
+ }
+ }
+
+ // add_to_caddie
+ if ('add_to_caddie' == $action)
+ {
+ fill_caddie($collection);
+ }
+
+ // delete
+ if ('delete' == $action)
+ {
+ if (isset($_POST['confirm_deletion']) and 1 == $_POST['confirm_deletion'])
+ {
+ $deleted_count = delete_elements($collection, true);
+ if ($deleted_count > 0)
+ {
+ $_SESSION['page_infos'][] = l10n_dec(
+ '%d photo was deleted', '%d photos were deleted',
+ $deleted_count
+ );
+
+ $redirect_url = get_root_url().'admin.php?page='.$_GET['page'];
+ redirect($redirect_url);
+ }
+ else
+ {
+ $page['errors'][] = l10n('No photo can be deleted');
+ }
+ }
+ else
+ {
+ $page['errors'][] = l10n('You need to confirm deletion');
+ }
+ }
+
+ // synchronize metadata
+ if ('metadata' == $action)
+ {
+ sync_metadata($collection);
+ $page['infos'][] = l10n('Metadata synchronized from file');
+ }
+
+ if ('delete_derivatives' == $action)
+ {
+ $query='SELECT path,representative_ext FROM '.IMAGES_TABLE.'
+ WHERE id IN ('.implode(',', $collection).')';
+ $result = pwg_query($query);
+ while ($info = pwg_db_fetch_assoc($result))
+ {
+ foreach( $_POST['del_derivatives_type'] as $type)
+ {
+ delete_element_derivatives($info, $type);
+ }
+ }
+ }
+
+ if ('generate_derivatives' == $action)
+ {
+ if ($_POST['regenerateSuccess'] != '0')
+ {
+ $page['infos'][] = l10n('%s photos have been regenerated', $_POST['regenerateSuccess']);
+ }
+ if ($_POST['regenerateError'] != '0')
+ {
+ $page['warnings'][] = l10n('%s photos can not be regenerated', $_POST['regenerateError']);
+ }
+ }
+
+ trigger_action('element_set_global_action', $action, $collection);
+}
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+$template->set_filenames(array('batch_manager_global' => 'batch_manager_global.tpl'));
+
+$base_url = get_root_url().'admin.php';
+
+$prefilters = array(
+ array('ID' => 'caddie', 'NAME' => l10n('Caddie')),
+ array('ID' => 'favorites', 'NAME' => l10n('Your favorites')),
+ array('ID' => 'last_import', 'NAME' => l10n('Last import')),
+ array('ID' => 'no_album', 'NAME' => l10n('With no album')),
+ array('ID' => 'no_tag', 'NAME' => l10n('With no tag')),
+ array('ID' => 'duplicates', 'NAME' => l10n('Duplicates')),
+ array('ID' => 'all_photos', 'NAME' => l10n('All'))
+);
+
+if ($conf['enable_synchronization'])
+{
+ $prefilters[] = array('ID' => 'no_virtual_album', 'NAME' => l10n('With no virtual album'));
+}
+
+$prefilters = trigger_event('get_batch_manager_prefilters', $prefilters);
+usort($prefilters, 'UC_name_compare');
+
+$template->assign(
+ array(
+ 'prefilters' => $prefilters,
+ 'filter' => $_SESSION['bulk_manager_filter'],
+ 'selection' => $collection,
+ 'all_elements' => $page['cat_elements_id'],
+ 'START' => $page['start'],
+ 'U_DISPLAY'=>$base_url.get_query_string_diff(array('display')),
+ 'F_ACTION'=>$base_url.get_query_string_diff(array('cat','start','tag','filter')),
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | caddie options |
+// +-----------------------------------------------------------------------+
+$template->assign('IN_CADDIE', 'caddie' == $page['prefilter']);
+
+
+// +-----------------------------------------------------------------------+
+// | global mode form |
+// +-----------------------------------------------------------------------+
+
+// privacy level
+foreach ($conf['available_permission_levels'] as $level)
+{
+ $level_options[$level] = l10n(sprintf('Level %d', $level));
+
+ if (0 == $level)
+ {
+ $level_options[$level] = l10n('Everybody');
+ }
+}
+$template->assign(
+ array(
+ 'filter_level_options'=> $level_options,
+ 'filter_level_options_selected' => isset($_SESSION['bulk_manager_filter']['level'])
+ ? $_SESSION['bulk_manager_filter']['level']
+ : 0,
+ )
+ );
+
+// tags
+if (!empty($_SESSION['bulk_manager_filter']['tags']))
+{
+ $query = '
+SELECT
+ id,
+ name
+ FROM '.TAGS_TABLE.'
+ WHERE id IN ('.implode(',', $_SESSION['bulk_manager_filter']['tags']).')
+;';
+ $template->assign('filter_tags', get_taglist($query));
+}
+
+// Virtualy associate a picture to a category
+$query = '
+SELECT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.'
+;';
+$categories = array_from_query($query);
+usort($categories, 'global_rank_compare');
+display_select_categories($categories, array(), 'category_full_name_options', true);
+
+display_select_cat_wrapper($query, array(), 'category_parent_options');
+
+// in the filter box, which category to select by default
+$selected_category = array();
+
+if (isset($_SESSION['bulk_manager_filter']['category']))
+{
+ $selected_category = array($_SESSION['bulk_manager_filter']['category']);
+}
+else
+{
+ // we need to know the category in which the last photo was added
+ $query = '
+SELECT
+ category_id,
+ id_uppercat
+ FROM '.IMAGES_TABLE.' AS i
+ JOIN '.IMAGE_CATEGORY_TABLE.' AS ic ON image_id = i.id
+ JOIN '.CATEGORIES_TABLE.' AS c ON category_id = c.id
+ ORDER BY i.id DESC
+ LIMIT 1
+;';
+ $result = pwg_query($query);
+ if (pwg_db_num_rows($result) > 0)
+ {
+ $row = pwg_db_fetch_assoc($result);
+ $selected_category = array($row['category_id']);
+ }
+}
+
+$template->assign( 'filter_category_selected', $selected_category);
+
+// Dissociate from a category : categories listed for dissociation can only
+// represent virtual links. We can't create orphans. Links to physical
+// categories can't be broken.
+if (count($page['cat_elements_id']) > 0)
+{
+ $query = '
+SELECT
+ DISTINCT(category_id) AS id,
+ c.name,
+ c.uppercats,
+ c.global_rank
+ FROM '.IMAGE_CATEGORY_TABLE.' AS ic
+ JOIN '.CATEGORIES_TABLE.' AS c ON c.id = ic.category_id
+ JOIN '.IMAGES_TABLE.' AS i ON i.id = ic.image_id
+ WHERE ic.image_id IN ('.implode(',', $page['cat_elements_id']).')
+ AND (
+ ic.category_id != i.storage_category_id
+ OR i.storage_category_id IS NULL
+ )
+;';
+ display_select_cat_wrapper($query, array(), 'dissociate_options', true);
+}
+
+if (count($page['cat_elements_id']) > 0)
+{
+ // remove tags
+ $tags = get_common_tags($page['cat_elements_id'], -1);
+
+ $template->assign(
+ array(
+ 'DEL_TAG_SELECTION' => get_html_tag_selection($tags, 'del_tags'),
+ )
+ );
+}
+
+// creation date
+$day =
+empty($_POST['date_creation_day']) ? date('j') : $_POST['date_creation_day'];
+
+$month =
+empty($_POST['date_creation_month']) ? date('n') : $_POST['date_creation_month'];
+
+$year =
+empty($_POST['date_creation_year']) ? date('Y') : $_POST['date_creation_year'];
+
+$month_list = $lang['month'];
+$month_list[0]='------------';
+ksort($month_list);
+$template->assign( array(
+ 'month_list' => $month_list,
+ 'DATE_CREATION_DAY' => (int)$day,
+ 'DATE_CREATION_MONTH'=> (int)$month,
+ 'DATE_CREATION_YEAR' => (int)$year,
+ )
+ );
+
+// image level options
+$template->assign(
+ array(
+ 'level_options'=> get_privacy_level_options(),
+ 'level_options_selected' => 0,
+ )
+ );
+
+// metadata
+include_once( PHPWG_ROOT_PATH.'admin/site_reader_local.php');
+$site_reader = new LocalSiteReader('./');
+$used_metadata = implode( ', ', $site_reader->get_metadata_attributes());
+
+$template->assign(
+ array(
+ 'used_metadata' => $used_metadata,
+ )
+ );
+
+//derivatives
+$del_deriv_map = array();
+foreach(ImageStdParams::get_defined_type_map() as $params)
+{
+ $del_deriv_map[$params->type] = l10n($params->type);
+}
+$gen_deriv_map = $del_deriv_map;
+$del_deriv_map[IMG_CUSTOM] = l10n(IMG_CUSTOM);
+$template->assign(
+ array(
+ 'del_derivatives_types' => $del_deriv_map,
+ 'generate_derivatives_types' => $gen_deriv_map,
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | global mode thumbnails |
+// +-----------------------------------------------------------------------+
+
+// how many items to display on this page
+if (!empty($_GET['display']))
+{
+ if ('all' == $_GET['display'])
+ {
+ $page['nb_images'] = count($page['cat_elements_id']);
+ }
+ else
+ {
+ $page['nb_images'] = intval($_GET['display']);
+ }
+}
+else
+{
+ $page['nb_images'] = 20;
+}
+
+$nb_thumbs_page = 0;
+
+if (count($page['cat_elements_id']) > 0)
+{
+ $nav_bar = create_navigation_bar(
+ $base_url.get_query_string_diff(array('start')),
+ count($page['cat_elements_id']),
+ $page['start'],
+ $page['nb_images']
+ );
+ $template->assign('navbar', $nav_bar);
+
+ $is_category = false;
+ if (isset($_SESSION['bulk_manager_filter']['category'])
+ and !isset($_SESSION['bulk_manager_filter']['category_recursive']))
+ {
+ $is_category = true;
+ }
+
+ if (isset($_SESSION['bulk_manager_filter']['prefilter'])
+ and 'duplicates' == $_SESSION['bulk_manager_filter']['prefilter'])
+ {
+ $conf['order_by'] = ' ORDER BY file, id';
+ }
+
+ $query = '
+SELECT id,path,representative_ext,file,filesize,level,name,width,height,rotation
+ FROM '.IMAGES_TABLE;
+
+ if ($is_category)
+ {
+ $category_info = get_cat_info($_SESSION['bulk_manager_filter']['category']);
+
+ $conf['order_by'] = $conf['order_by_inside_category'];
+ if (!empty($category_info['image_order']))
+ {
+ $conf['order_by'] = ' ORDER BY '.$category_info['image_order'];
+ }
+
+ $query.= '
+ JOIN '.IMAGE_CATEGORY_TABLE.' ON id = image_id';
+ }
+
+ $query.= '
+ WHERE id IN ('.implode(',', $page['cat_elements_id']).')';
+
+ if ($is_category)
+ {
+ $query.= '
+ AND category_id = '.$_SESSION['bulk_manager_filter']['category'];
+ }
+
+ $query.= '
+ '.$conf['order_by'].'
+ LIMIT '.$page['nb_images'].' OFFSET '.$page['start'].'
+;';
+ $result = pwg_query($query);
+
+ $thumb_params = ImageStdParams::get_by_type(IMG_THUMB);
+ // template thumbnail initialization
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $nb_thumbs_page++;
+ $src_image = new SrcImage($row);
+
+ $ttitle = render_element_name($row);
+ if ($ttitle != get_name_from_file($row['file']))
+ {
+ $ttitle.= ' ('.$row['file'].')';
+ }
+
+ $template->append(
+ 'thumbnails', array_merge($row,
+ array(
+ 'thumb' => new DerivativeImage($thumb_params, $src_image),
+ 'TITLE' => $ttitle,
+ 'FILE_SRC' => DerivativeImage::url(IMG_LARGE, $src_image),
+ 'U_EDIT' => get_root_url().'admin.php?page=photo-'.$row['id'],
+ )
+ ));
+ }
+ $template->assign('thumb_params', $thumb_params);
+}
+
+$template->assign(
+ array(
+ 'nb_thumbs_page' => $nb_thumbs_page,
+ 'nb_thumbs_set' => count($page['cat_elements_id']),
+ )
+ );
+
+trigger_action('loc_end_element_set_global');
+
+//----------------------------------------------------------- sending html code
+$template->assign_var_from_handle('ADMIN_CONTENT', 'batch_manager_global');
+?>
diff --git a/sources/admin/batch_manager_unit.php b/sources/admin/batch_manager_unit.php
new file mode 100644
index 0000000..ddf4206
--- /dev/null
+++ b/sources/admin/batch_manager_unit.php
@@ -0,0 +1,287 @@
+ array('id'),
+ 'update' => array('name','author','level','comment','date_creation')
+ ),
+ $datas
+ );
+
+ $page['infos'][] = l10n('Photo informations updated');
+}
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+
+$template->set_filenames(
+ array('batch_manager_unit' => 'batch_manager_unit.tpl'));
+
+$base_url = PHPWG_ROOT_PATH.'admin.php';
+
+$month_list = $lang['month'];
+$month_list[0]='------------';
+ksort($month_list);
+
+$template->assign(
+ array(
+ 'U_ELEMENTS_PAGE' => $base_url.get_query_string_diff(array('display','start')),
+ 'F_ACTION'=>$base_url.get_query_string_diff(array()),
+ 'month_list' => $month_list,
+ 'level_options' => get_privacy_level_options(),
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | global mode thumbnails |
+// +-----------------------------------------------------------------------+
+
+// how many items to display on this page
+if (!empty($_GET['display']))
+{
+ if ('all' == $_GET['display'])
+ {
+ $page['nb_images'] = count($page['cat_elements_id']);
+ }
+ else
+ {
+ $page['nb_images'] = intval($_GET['display']);
+ }
+}
+else
+{
+ $page['nb_images'] = 5;
+}
+
+
+
+if (count($page['cat_elements_id']) > 0)
+{
+ $nav_bar = create_navigation_bar(
+ $base_url.get_query_string_diff(array('start')),
+ count($page['cat_elements_id']),
+ $page['start'],
+ $page['nb_images']
+ );
+ $template->assign(array('navbar' => $nav_bar));
+
+ $element_ids = array();
+
+ $is_category = false;
+ if (isset($_SESSION['bulk_manager_filter']['category'])
+ and !isset($_SESSION['bulk_manager_filter']['category_recursive']))
+ {
+ $is_category = true;
+ }
+
+ if (isset($_SESSION['bulk_manager_filter']['prefilter'])
+ and 'duplicates' == $_SESSION['bulk_manager_filter']['prefilter'])
+ {
+ $conf['order_by'] = ' ORDER BY file, id';
+ }
+
+
+ $query = '
+SELECT *
+ FROM '.IMAGES_TABLE;
+
+ if ($is_category)
+ {
+ $category_info = get_cat_info($_SESSION['bulk_manager_filter']['category']);
+
+ $conf['order_by'] = $conf['order_by_inside_category'];
+ if (!empty($category_info['image_order']))
+ {
+ $conf['order_by'] = ' ORDER BY '.$category_info['image_order'];
+ }
+
+ $query.= '
+ JOIN '.IMAGE_CATEGORY_TABLE.' ON id = image_id';
+ }
+
+ $query.= '
+ WHERE id IN ('.implode(',', $page['cat_elements_id']).')';
+
+ if ($is_category)
+ {
+ $query.= '
+ AND category_id = '.$_SESSION['bulk_manager_filter']['category'];
+ }
+
+ $query.= '
+ '.$conf['order_by'].'
+ LIMIT '.$page['nb_images'].' OFFSET '.$page['start'].'
+;';
+ $result = pwg_query($query);
+
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $element_ids[] = $row['id'];
+
+ $src_image = new SrcImage($row);
+
+ // creation date
+ if (!empty($row['date_creation']))
+ {
+ list($year,$month,$day) = explode('-', $row['date_creation']);
+ }
+ else
+ {
+ list($year,$month,$day) = array('',0,0);
+ }
+
+ $query = '
+SELECT
+ id,
+ name
+ FROM '.IMAGE_TAG_TABLE.' AS it
+ JOIN '.TAGS_TABLE.' AS t ON t.id = it.tag_id
+ WHERE image_id = '.$row['id'].'
+;';
+ $tag_selection = get_taglist($query);
+
+ $legend = render_element_name($row);
+ if ($legend != get_name_from_file($row['file']))
+ {
+ $legend.= ' ('.$row['file'].')';
+ }
+
+ $template->append(
+ 'elements', array_merge($row,
+ array(
+ 'ID' => $row['id'],
+ 'TN_SRC' => DerivativeImage::url(IMG_THUMB, $src_image),
+ 'FILE_SRC' => DerivativeImage::url(IMG_LARGE, $src_image),
+ 'LEGEND' => $legend,
+ 'U_EDIT' => get_root_url().'admin.php?page=photo-'.$row['id'],
+ 'NAME' => htmlspecialchars(@$row['name']),
+ 'AUTHOR' => htmlspecialchars(@$row['author']),
+ 'LEVEL' => !empty($row['level'])?$row['level']:'0',
+ 'DESCRIPTION' => htmlspecialchars(@$row['comment']),
+ 'DATE_CREATION_YEAR' => $year,
+ 'DATE_CREATION_MONTH' => (int)$month,
+ 'DATE_CREATION_DAY' => (int)$day,
+ 'TAGS' => $tag_selection,
+ )
+ ));
+ }
+
+ $template->assign('ELEMENT_IDS', implode(',', $element_ids));
+}
+
+trigger_action('loc_end_element_set_unit');
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'batch_manager_unit');
+?>
\ No newline at end of file
diff --git a/sources/admin/cat_list.php b/sources/admin/cat_list.php
new file mode 100644
index 0000000..729fe84
--- /dev/null
+++ b/sources/admin/cat_list.php
@@ -0,0 +1,372 @@
+ $id, 'rank' => $current_rank);
+ }
+ $fields = array('primary' => array('id'), 'update' => array('rank'));
+ mass_updates(CATEGORIES_TABLE, $fields, $datas);
+
+ update_global_rank();
+}
+
+// +-----------------------------------------------------------------------+
+// | initialization |
+// +-----------------------------------------------------------------------+
+
+check_input_parameter('parent_id', $_GET, false, PATTERN_ID);
+
+$categories = array();
+
+$base_url = get_root_url().'admin.php?page=cat_list';
+$navigation = '';
+$navigation.= l10n('Home');
+$navigation.= ' ';
+
+// +-----------------------------------------------------------------------+
+// | tabs |
+// +-----------------------------------------------------------------------+
+
+$page['tab'] = 'list';
+include(PHPWG_ROOT_PATH.'admin/include/albums_tab.inc.php');
+
+// +-----------------------------------------------------------------------+
+// | virtual categories management |
+// +-----------------------------------------------------------------------+
+// request to delete a virtual category
+if (isset($_GET['delete']) and is_numeric($_GET['delete']))
+{
+ delete_categories(array($_GET['delete']));
+ $_SESSION['page_infos'] = array(l10n('Virtual album deleted'));
+ update_global_rank();
+ invalidate_user_cache();
+
+ $redirect_url = get_root_url().'admin.php?page=cat_list';
+ if (isset($_GET['parent_id']))
+ {
+ $redirect_url.= '&parent_id='.$_GET['parent_id'];
+ }
+ redirect($redirect_url);
+}
+// request to add a virtual category
+elseif (isset($_POST['submitAdd']))
+{
+ $output_create = create_virtual_category(
+ $_POST['virtual_name'],
+ @$_GET['parent_id']
+ );
+
+ invalidate_user_cache();
+ if (isset($output_create['error']))
+ {
+ $page['errors'][] = $output_create['error'];
+ }
+ else
+ {
+ $page['infos'][] = $output_create['info'];
+ }
+}
+// save manual category ordering
+elseif (isset($_POST['submitManualOrder']))
+{
+ asort($_POST['catOrd'], SORT_NUMERIC);
+ save_categories_order(array_keys($_POST['catOrd']));
+
+ $page['infos'][] = l10n('Album manual order was saved');
+}
+elseif (isset($_POST['submitAutoOrder']))
+{
+ $query = '
+SELECT id
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id_uppercat '.
+ (!isset($_GET['parent_id']) ? 'IS NULL' : '= '.$_GET['parent_id']).'
+;';
+ $category_ids = array_from_query($query, 'id');
+
+ if (isset($_POST['recursive']))
+ {
+ $category_ids = get_subcat_ids($category_ids);
+ }
+
+ $categories = array();
+ $names = array();
+ $id_uppercats = array();
+
+ $query = '
+SELECT id, name, id_uppercat
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id IN ('.implode(',', $category_ids).')
+;';
+ $result = pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $categories[] = array(
+ 'id' => $row['id'],
+ 'id_uppercat' => $row['id_uppercat'],
+ );
+ $names[] = $row['name'];
+ }
+
+ array_multisort(
+ $names,
+ SORT_REGULAR,
+ 'asc' == $_POST['ascdesc'] ? SORT_ASC : SORT_DESC,
+ $categories
+ );
+ save_categories_order($categories);
+
+ $page['infos'][] = l10n('Albums automatically sorted');
+}
+
+// +-----------------------------------------------------------------------+
+// | Navigation path |
+// +-----------------------------------------------------------------------+
+
+if (isset($_GET['parent_id']))
+{
+ $navigation.= $conf['level_separator'];
+
+ $navigation.= get_cat_display_name_from_id(
+ $_GET['parent_id'],
+ $base_url.'&parent_id='
+ );
+}
+// +-----------------------------------------------------------------------+
+// | template initialization |
+// +-----------------------------------------------------------------------+
+$template->set_filename('categories', 'cat_list.tpl');
+
+$form_action = PHPWG_ROOT_PATH.'admin.php?page=cat_list';
+if (isset($_GET['parent_id']))
+{
+ $form_action.= '&parent_id='.$_GET['parent_id'];
+}
+
+$template->assign(array(
+ 'CATEGORIES_NAV'=>$navigation,
+ 'F_ACTION'=>$form_action,
+ 'PWG_TOKEN' => get_pwg_token(),
+ ));
+
+// +-----------------------------------------------------------------------+
+// | Categories display |
+// +-----------------------------------------------------------------------+
+
+$categories = array();
+
+$query = '
+SELECT id, name, permalink, dir, rank, status
+ FROM '.CATEGORIES_TABLE;
+if (!isset($_GET['parent_id']))
+{
+ $query.= '
+ WHERE id_uppercat IS NULL';
+}
+else
+{
+ $query.= '
+ WHERE id_uppercat = '.$_GET['parent_id'];
+}
+$query.= '
+ ORDER BY rank ASC
+;';
+$categories = hash_from_query($query, 'id');
+
+// get the categories containing images directly
+$categories_with_images = array();
+if (count($categories))
+{
+ $query = '
+SELECT
+ category_id,
+ COUNT(*) AS nb_photos
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ GROUP BY category_id
+;';
+ // WHERE category_id IN ('.implode(',', array_keys($categories)).')
+
+ $nb_photos_in = query2array($query, 'category_id', 'nb_photos');
+
+ $query = '
+SELECT
+ id,
+ uppercats
+ FROM '.CATEGORIES_TABLE.'
+;';
+ $all_categories = query2array($query, 'id', 'uppercats');
+ $subcats_of = array();
+
+ foreach (array_keys($categories) as $cat_id)
+ {
+ foreach ($all_categories as $id => $uppercats)
+ {
+ if (preg_match('/(^|,)'.$cat_id.',/', $uppercats))
+ {
+ @$subcats_of[$cat_id][] = $id;
+ }
+ }
+ }
+
+ $nb_sub_photos = array();
+ foreach ($subcats_of as $cat_id => $subcat_ids)
+ {
+ $nb_photos = 0;
+ foreach ($subcat_ids as $id)
+ {
+ if (isset($nb_photos_in[$id]))
+ {
+ $nb_photos+= $nb_photos_in[$id];
+ }
+ }
+
+ $nb_sub_photos[$cat_id] = $nb_photos;
+ }
+}
+
+$template->assign('categories', array());
+$base_url = get_root_url().'admin.php?page=';
+
+if (isset($_GET['parent_id']))
+{
+ $template->assign(
+ 'PARENT_EDIT',
+ $base_url.'album-'.$_GET['parent_id']
+ );
+}
+
+foreach ($categories as $category)
+{
+ $cat_list_url = $base_url.'cat_list';
+
+ $self_url = $cat_list_url;
+ if (isset($_GET['parent_id']))
+ {
+ $self_url.= '&parent_id='.$_GET['parent_id'];
+ }
+
+ $tpl_cat =
+ array(
+ 'NAME' =>
+ trigger_event(
+ 'render_category_name',
+ $category['name'],
+ 'admin_cat_list'
+ ),
+ 'NB_PHOTOS' => isset($nb_photos_in[$category['id']]) ? $nb_photos_in[$category['id']] : 0,
+ 'NB_SUB_PHOTOS' => isset($nb_sub_photos[$category['id']]) ? $nb_sub_photos[$category['id']] : 0,
+ 'NB_SUB_ALBUMS' => isset($subcats_of[$category['id']]) ? count($subcats_of[$category['id']]) : 0,
+ 'ID' => $category['id'],
+ 'RANK' => $category['rank']*10,
+
+ 'U_JUMPTO' => make_index_url(
+ array(
+ 'category' => $category
+ )
+ ),
+
+ 'U_CHILDREN' => $cat_list_url.'&parent_id='.$category['id'],
+ 'U_EDIT' => $base_url.'album-'.$category['id'],
+
+ 'IS_VIRTUAL' => empty($category['dir'])
+ );
+
+ if (empty($category['dir']))
+ {
+ $tpl_cat['U_DELETE'] = $self_url.'&delete='.$category['id'];
+ $tpl_cat['U_DELETE'].= '&pwg_token='.get_pwg_token();
+ }
+ else
+ {
+ if ($conf['enable_synchronization'])
+ {
+ $tpl_cat['U_SYNC'] = $base_url.'site_update&site=1&cat_id='.$category['id'];
+ }
+ }
+
+ $template->append('categories', $tpl_cat);
+}
+
+trigger_action('loc_end_cat_list');
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'categories');
+?>
diff --git a/sources/admin/cat_modify.php b/sources/admin/cat_modify.php
new file mode 100644
index 0000000..4eeeffe
--- /dev/null
+++ b/sources/admin/cat_modify.php
@@ -0,0 +1,390 @@
+ rex > 1_year_old" is on the the same site as the
+// Piwigo files and this category has 22 for identifier
+// get_complete_dir(22) returns "./galleries/pets/rex/1_year_old/"
+function get_complete_dir( $category_id )
+{
+ return get_site_url($category_id).get_local_dir($category_id);
+}
+
+// get_local_dir returns an array with complete path without the site url
+// Example : "pets > rex > 1_year_old" is on the the same site as the
+// Piwigo files and this category has 22 for identifier
+// get_local_dir(22) returns "pets/rex/1_year_old/"
+function get_local_dir( $category_id )
+{
+ global $page;
+
+ $uppercats = '';
+ $local_dir = '';
+
+ if ( isset( $page['plain_structure'][$category_id]['uppercats'] ) )
+ {
+ $uppercats = $page['plain_structure'][$category_id]['uppercats'];
+ }
+ else
+ {
+ $query = 'SELECT uppercats';
+ $query.= ' FROM '.CATEGORIES_TABLE.' WHERE id = '.$category_id;
+ $query.= ';';
+ $row = pwg_db_fetch_assoc( pwg_query( $query ) );
+ $uppercats = $row['uppercats'];
+ }
+
+ $upper_array = explode( ',', $uppercats );
+
+ $database_dirs = array();
+ $query = 'SELECT id,dir';
+ $query.= ' FROM '.CATEGORIES_TABLE.' WHERE id IN ('.$uppercats.')';
+ $query.= ';';
+ $result = pwg_query( $query );
+ while( $row = pwg_db_fetch_assoc( $result ) )
+ {
+ $database_dirs[$row['id']] = $row['dir'];
+ }
+ foreach ($upper_array as $id)
+ {
+ $local_dir.= $database_dirs[$id].'/';
+ }
+
+ return $local_dir;
+}
+
+// retrieving the site url : "http://domain.com/gallery/" or
+// simply "./galleries/"
+function get_site_url($category_id)
+{
+ global $page;
+
+ $query = '
+SELECT galleries_url
+ FROM '.SITES_TABLE.' AS s,'.CATEGORIES_TABLE.' AS c
+ WHERE s.id = c.site_id
+ AND c.id = '.$category_id.'
+;';
+ $row = pwg_db_fetch_assoc(pwg_query($query));
+ return $row['galleries_url'];
+}
+
+// +-----------------------------------------------------------------------+
+// | Check Access and exit when user status is not ok |
+// +-----------------------------------------------------------------------+
+check_status(ACCESS_ADMINISTRATOR);
+
+trigger_action('loc_begin_cat_modify');
+
+//---------------------------------------------------------------- verification
+if ( !isset( $_GET['cat_id'] ) || !is_numeric( $_GET['cat_id'] ) )
+{
+ trigger_error( 'missing cat_id param', E_USER_ERROR);
+}
+
+//--------------------------------------------------------- form criteria check
+if (isset($_POST['submit']))
+{
+ $data = array(
+ 'id' => $_GET['cat_id'],
+ 'name' => @$_POST['name'],
+ 'comment' =>
+ $conf['allow_html_descriptions'] ?
+ @$_POST['comment'] : strip_tags(@$_POST['comment']),
+ );
+
+ if ($conf['activate_comments'])
+ {
+ $data['commentable'] = isset($_POST['commentable'])?$_POST['commentable']:'false';
+ }
+
+ single_update(
+ CATEGORIES_TABLE,
+ $data,
+ array('id' => $data['id'])
+ );
+
+ // retrieve cat infos before continuing (following updates are expensive)
+ $cat_info = get_cat_info($_GET['cat_id']);
+
+ if ($cat_info['visible'] != get_boolean( $_POST['visible'] ) )
+ {
+ set_cat_visible(array($_GET['cat_id']), $_POST['visible']);
+ }
+
+ // in case the use moves his album to the gallery root, we force
+ // $_POST['parent'] from 0 to null to be compared with
+ // $cat_info['id_uppercat']
+ if (empty($_POST['parent']))
+ {
+ $_POST['parent'] = null;
+ }
+
+ // only move virtual albums
+ if (empty($cat_info['dir']) and $cat_info['id_uppercat'] != $_POST['parent'])
+ {
+ move_categories( array($_GET['cat_id']), $_POST['parent'] );
+ }
+
+ $_SESSION['page_infos'][] = l10n('Album updated successfully');
+ $redirect = true;
+}
+elseif (isset($_POST['set_random_representant']))
+{
+ set_random_representant(array($_GET['cat_id']));
+ $redirect = true;
+}
+elseif (isset($_POST['delete_representant']))
+{
+ $query = '
+UPDATE '.CATEGORIES_TABLE.'
+ SET representative_picture_id = NULL
+ WHERE id = '.$_GET['cat_id'].'
+;';
+ pwg_query($query);
+ $redirect = true;
+}
+
+if (isset($redirect))
+{
+ redirect($admin_album_base_url.'-properties');
+}
+
+// nullable fields
+foreach (array('comment','dir','site_id', 'id_uppercat') as $nullable)
+{
+ if (!isset($category[$nullable]))
+ {
+ $category[$nullable] = '';
+ }
+}
+
+$category['is_virtual'] = empty($category['dir']) ? true : false;
+
+$query = 'SELECT DISTINCT category_id
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ WHERE category_id = '.$_GET['cat_id'].'
+ LIMIT 1';
+$result = pwg_query($query);
+$category['has_images'] = pwg_db_num_rows($result)>0 ? true : false;
+
+// Navigation path
+$navigation = get_cat_display_name_cache(
+ $category['uppercats'],
+ get_root_url().'admin.php?page=album-'
+ );
+
+$form_action = $admin_album_base_url.'-properties';
+
+//----------------------------------------------------- template initialization
+$template->set_filename( 'album_properties', 'cat_modify.tpl');
+
+$base_url = get_root_url().'admin.php?page=';
+$cat_list_url = $base_url.'cat_list';
+
+$self_url = $cat_list_url;
+if (!empty($category['id_uppercat']))
+{
+ $self_url.= '&parent_id='.$category['id_uppercat'];
+}
+
+$template->assign(
+ array(
+ 'CATEGORIES_NAV' => $navigation,
+ 'CAT_ID' => $category['id'],
+ 'CAT_NAME' => @htmlspecialchars($category['name']),
+ 'CAT_COMMENT' => @htmlspecialchars($category['comment']),
+ 'CAT_VISIBLE' => boolean_to_string($category['visible']),
+
+ 'U_JUMPTO' => make_index_url(
+ array(
+ 'category' => $category
+ )
+ ),
+
+ 'U_ADD_PHOTOS_ALBUM' => $base_url.'photos_add&album='.$category['id'],
+ 'U_CHILDREN' => $cat_list_url.'&parent_id='.$category['id'],
+ 'U_HELP' => get_root_url().'admin/popuphelp.php?page=cat_modify',
+
+ 'F_ACTION' => $form_action,
+ )
+ );
+
+if ($conf['activate_comments'])
+{
+ $template->assign('CAT_COMMENTABLE', boolean_to_string($category['commentable']));
+}
+
+// manage album elements link
+if ($category['has_images'])
+{
+ $template->assign(
+ 'U_MANAGE_ELEMENTS',
+ $base_url.'batch_manager&filter=album-'.$category['id']
+ );
+
+ $query = '
+SELECT
+ COUNT(image_id),
+ MIN(DATE(date_available)),
+ MAX(DATE(date_available))
+ FROM '.IMAGES_TABLE.'
+ JOIN '.IMAGE_CATEGORY_TABLE.' ON image_id = id
+ WHERE category_id = '.$category['id'].'
+;';
+ list($image_count, $min_date, $max_date) = pwg_db_fetch_row(pwg_query($query));
+
+ if ($min_date == $max_date)
+ {
+ $intro = l10n(
+ 'This album contains %d photos, added on %s.',
+ $image_count,
+ format_date($min_date)
+ );
+ }
+ else
+ {
+ $intro = l10n(
+ 'This album contains %d photos, added between %s and %s.',
+ $image_count,
+ format_date($min_date),
+ format_date($max_date)
+ );
+ }
+}
+else
+{
+ $intro = l10n('This album contains no photo.');
+}
+
+$intro.= ' '.l10n('Numeric identifier : %d', $category['id']);
+
+$template->assign('INTRO', $intro);
+
+$template->assign(
+ 'U_MANAGE_RANKS',
+ $base_url.'element_set_ranks&cat_id='.$category['id']
+ );
+
+if ($category['is_virtual'])
+{
+ $template->assign(
+ array(
+ 'U_DELETE' => $self_url.'&delete='.$category['id'].'&pwg_token='.get_pwg_token(),
+ )
+ );
+}
+else
+{
+ $category['cat_full_dir'] = get_complete_dir($_GET['cat_id']);
+ $template->assign(
+ array(
+ 'CAT_FULL_DIR' => preg_replace('/\/$/', '', $category['cat_full_dir'])
+ )
+ );
+
+ if ($conf['enable_synchronization'])
+ {
+ $template->assign(
+ 'U_SYNC',
+ $base_url.'site_update&site=1&cat_id='.$category['id']
+ );
+ }
+
+}
+
+// representant management
+if ($category['has_images']
+ or !empty($category['representative_picture_id']))
+{
+ $tpl_representant = array();
+
+ // picture to display : the identified representant or the generic random
+ // representant ?
+ if (!empty($category['representative_picture_id']))
+ {
+ $query = '
+SELECT id,representative_ext,path
+ FROM '.IMAGES_TABLE.'
+ WHERE id = '.$category['representative_picture_id'].'
+;';
+ $row = pwg_db_fetch_assoc(pwg_query($query));
+ $src = DerivativeImage::thumb_url($row);
+ $url = get_root_url().'admin.php?page=photo-'.$category['representative_picture_id'];
+
+ $tpl_representant['picture'] =
+ array(
+ 'SRC' => $src,
+ 'URL' => $url
+ );
+ }
+
+ // can the admin choose to set a new random representant ?
+ $tpl_representant['ALLOW_SET_RANDOM'] = ($category['has_images']) ? true : false;
+
+ // can the admin delete the current representant ?
+ if (
+ ($category['has_images']
+ and $conf['allow_random_representative'])
+ or
+ (!$category['has_images']
+ and !empty($category['representative_picture_id'])))
+ {
+ $tpl_representant['ALLOW_DELETE'] = true;
+ }
+ $template->assign('representant', $tpl_representant);
+}
+
+if ($category['is_virtual'])
+{
+ // the category can be moved in any category but in itself, in any
+ // sub-category
+ $unmovables = get_subcat_ids(array($category['id']));
+
+ $query = '
+SELECT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id NOT IN ('.implode(',', $unmovables).')
+;';
+
+ display_select_cat_wrapper(
+ $query,
+ empty($category['id_uppercat']) ? array() : array($category['id_uppercat']),
+ 'move_cat_options'
+ );
+}
+
+trigger_action('loc_end_cat_modify');
+
+//----------------------------------------------------------- sending html code
+$template->assign_var_from_handle('ADMIN_CONTENT', 'album_properties');
+?>
diff --git a/sources/admin/cat_move.php b/sources/admin/cat_move.php
new file mode 100644
index 0000000..d596d4e
--- /dev/null
+++ b/sources/admin/cat_move.php
@@ -0,0 +1,108 @@
+ 0)
+ {
+ // TODO: tests
+ move_categories($_POST['selection'], $_POST['parent']);
+ }
+ else
+ {
+ $page['errors'][] = l10n('Select at least one album');
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | template initialization |
+// +-----------------------------------------------------------------------+
+$template->set_filename('cat_move', 'cat_move.tpl');
+
+$template->assign(
+ array(
+ 'U_HELP' => get_root_url().'admin/popuphelp.php?page=cat_move',
+ 'F_ACTION' => get_root_url().'admin.php?page=cat_move',
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | tabs |
+// +-----------------------------------------------------------------------+
+
+$page['tab'] = 'move';
+include(PHPWG_ROOT_PATH.'admin/include/albums_tab.inc.php');
+
+// +-----------------------------------------------------------------------+
+// | Categories display |
+// +-----------------------------------------------------------------------+
+
+$query = '
+SELECT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.'
+ WHERE dir IS NULL
+;';
+display_select_cat_wrapper(
+ $query,
+ array(),
+ 'category_to_move_options'
+ );
+
+$query = '
+SELECT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.'
+;';
+
+display_select_cat_wrapper(
+ $query,
+ array(),
+ 'category_parent_options'
+ );
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'cat_move');
+?>
diff --git a/sources/admin/cat_options.php b/sources/admin/cat_options.php
new file mode 100644
index 0000000..f191095
--- /dev/null
+++ b/sources/admin/cat_options.php
@@ -0,0 +1,256 @@
+';
+// print_r($_POST);
+// print '';
+if (isset($_POST['falsify'])
+ and isset($_POST['cat_true'])
+ and count($_POST['cat_true']) > 0)
+{
+ switch ($_GET['section'])
+ {
+ case 'comments' :
+ {
+ $query = '
+UPDATE '.CATEGORIES_TABLE.'
+ SET commentable = \'false\'
+ WHERE id IN ('.implode(',', $_POST['cat_true']).')
+;';
+ pwg_query($query);
+ break;
+ }
+ case 'visible' :
+ {
+ set_cat_visible($_POST['cat_true'], 'false');
+ break;
+ }
+ case 'status' :
+ {
+ set_cat_status($_POST['cat_true'], 'private');
+ break;
+ }
+ case 'representative' :
+ {
+ $query = '
+UPDATE '.CATEGORIES_TABLE.'
+ SET representative_picture_id = NULL
+ WHERE id IN ('.implode(',', $_POST['cat_true']).')
+;';
+ pwg_query($query);
+ break;
+ }
+ }
+}
+else if (isset($_POST['trueify'])
+ and isset($_POST['cat_false'])
+ and count($_POST['cat_false']) > 0)
+{
+ switch ($_GET['section'])
+ {
+ case 'comments' :
+ {
+ $query = '
+UPDATE '.CATEGORIES_TABLE.'
+ SET commentable = \'true\'
+ WHERE id IN ('.implode(',', $_POST['cat_false']).')
+;';
+ pwg_query($query);
+ break;
+ }
+ case 'visible' :
+ {
+ set_cat_visible($_POST['cat_false'], 'true');
+ break;
+ }
+ case 'status' :
+ {
+ set_cat_status($_POST['cat_false'], 'public');
+ break;
+ }
+ case 'representative' :
+ {
+ // theoretically, all categories in $_POST['cat_false'] contain at
+ // least one element, so Piwigo can find a representant.
+ set_random_representant($_POST['cat_false']);
+ break;
+ }
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+
+$template->set_filenames(
+ array(
+ 'cat_options' => 'cat_options.tpl',
+ 'double_select' => 'double_select.tpl'
+ )
+ );
+
+$page['section'] = isset($_GET['section']) ? $_GET['section'] : 'status';
+$base_url = PHPWG_ROOT_PATH.'admin.php?page=cat_options§ion=';
+
+$template->assign(
+ array(
+ 'U_HELP' => get_root_url().'admin/popuphelp.php?page=cat_options',
+ 'F_ACTION'=>$base_url.$page['section']
+ )
+ );
+
+// TabSheet
+$tabsheet = new tabsheet();
+$tabsheet->set_id('cat_options');
+$tabsheet->select($page['section']);
+$tabsheet->assign();
+
+// +-----------------------------------------------------------------------+
+// | form display |
+// +-----------------------------------------------------------------------+
+
+// for each section, categories in the multiselect field can be :
+//
+// - true : commentable for comment section
+// - false : un-commentable for comment section
+// - NA : (not applicable) for virtual categories
+//
+// for true and false status, we associates an array of category ids,
+// function display_select_categories will use the given CSS class for each
+// option
+$cats_true = array();
+$cats_false = array();
+switch ($page['section'])
+{
+ case 'comments' :
+ {
+ $query_true = '
+SELECT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.'
+ WHERE commentable = \'true\'
+;';
+ $query_false = '
+SELECT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.'
+ WHERE commentable = \'false\'
+;';
+ $template->assign(
+ array(
+ 'L_SECTION' => l10n('Authorize users to add comments on selected albums'),
+ 'L_CAT_OPTIONS_TRUE' => l10n('Authorized'),
+ 'L_CAT_OPTIONS_FALSE' => l10n('Forbidden'),
+ )
+ );
+ break;
+ }
+ case 'visible' :
+ {
+ $query_true = '
+SELECT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.'
+ WHERE visible = \'true\'
+;';
+ $query_false = '
+SELECT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.'
+ WHERE visible = \'false\'
+;';
+ $template->assign(
+ array(
+ 'L_SECTION' => l10n('Lock albums'),
+ 'L_CAT_OPTIONS_TRUE' => l10n('Unlocked'),
+ 'L_CAT_OPTIONS_FALSE' => l10n('Locked'),
+ )
+ );
+ break;
+ }
+ case 'status' :
+ {
+ $query_true = '
+SELECT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.'
+ WHERE status = \'public\'
+;';
+ $query_false = '
+SELECT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.'
+ WHERE status = \'private\'
+;';
+ $template->assign(
+ array(
+ 'L_SECTION' => l10n('Manage authorizations for selected albums'),
+ 'L_CAT_OPTIONS_TRUE' => l10n('Public'),
+ 'L_CAT_OPTIONS_FALSE' => l10n('Private'),
+ )
+ );
+ break;
+ }
+ case 'representative' :
+ {
+ $query_true = '
+SELECT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.'
+ WHERE representative_picture_id IS NOT NULL
+;';
+ $query_false = '
+SELECT DISTINCT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.' INNER JOIN '.IMAGE_CATEGORY_TABLE.' ON id=category_id
+ WHERE representative_picture_id IS NULL
+;';
+ $template->assign(
+ array(
+ 'L_SECTION' => l10n('Representative'),
+ 'L_CAT_OPTIONS_TRUE' => l10n('singly represented'),
+ 'L_CAT_OPTIONS_FALSE' => l10n('randomly represented')
+ )
+ );
+ break;
+ }
+}
+display_select_cat_wrapper($query_true,array(),'category_option_true');
+display_select_cat_wrapper($query_false,array(),'category_option_false');
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('DOUBLE_SELECT', 'double_select');
+$template->assign_var_from_handle('ADMIN_CONTENT', 'cat_options');
+?>
\ No newline at end of file
diff --git a/sources/admin/cat_perm.php b/sources/admin/cat_perm.php
new file mode 100644
index 0000000..71653d9
--- /dev/null
+++ b/sources/admin/cat_perm.php
@@ -0,0 +1,304 @@
+ 0)
+ {
+ // if you forbid access to an album, all sub-albums become
+ // automatically forbidden
+ $query = '
+DELETE
+ FROM '.GROUP_ACCESS_TABLE.'
+ WHERE group_id IN ('.implode(',', $deny_groups).')
+ AND cat_id IN ('.implode(',', get_subcat_ids(array($page['cat']))).')
+;';
+ pwg_query($query);
+ }
+
+ //
+ // add permissions to groups
+ //
+ $grant_groups = $_POST['groups'];
+ if (count($grant_groups) > 0)
+ {
+ $cat_ids = get_uppercat_ids(array($page['cat']));
+ if (isset($_POST['apply_on_sub']))
+ {
+ $cat_ids = array_merge($cat_ids, get_subcat_ids(array($page['cat'])));
+ }
+
+ $query = '
+SELECT id
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id IN ('.implode(',', $cat_ids).')
+ AND status = \'private\'
+;';
+ $private_cats = array_from_query($query, 'id');
+
+ $inserts = array();
+ foreach ($private_cats as $cat_id)
+ {
+ foreach ($grant_groups as $group_id)
+ {
+ $inserts[] = array(
+ 'group_id' => $group_id,
+ 'cat_id' => $cat_id
+ );
+ }
+ }
+
+ mass_inserts(
+ GROUP_ACCESS_TABLE,
+ array('group_id','cat_id'),
+ $inserts,
+ array('ignore'=>true)
+ );
+ }
+
+ //
+ // users
+ //
+ $query = '
+SELECT user_id
+ FROM '.USER_ACCESS_TABLE.'
+ WHERE cat_id = '.$page['cat'].'
+;';
+ $users_granted = array_from_query($query, 'user_id');
+
+ if (!isset($_POST['users']))
+ {
+ $_POST['users'] = array();
+ }
+
+ //
+ // remove permissions to users
+ //
+ $deny_users = array_diff($users_granted, $_POST['users']);
+ if (count($deny_users) > 0)
+ {
+ // if you forbid access to an album, all sub-album become automatically
+ // forbidden
+ $query = '
+DELETE
+ FROM '.USER_ACCESS_TABLE.'
+ WHERE user_id IN ('.implode(',', $deny_users).')
+ AND cat_id IN ('.implode(',', get_subcat_ids(array($page['cat']))).')
+;';
+ pwg_query($query);
+ }
+
+ //
+ // add permissions to users
+ //
+ $grant_users = $_POST['users'];
+ if (count($grant_users) > 0)
+ {
+ add_permission_on_category($page['cat'], $grant_users);
+ }
+ }
+
+ $page['infos'][] = l10n('Album updated successfully');
+}
+
+// +-----------------------------------------------------------------------+
+// | template initialization |
+// +-----------------------------------------------------------------------+
+
+$template->set_filename('cat_perm', 'cat_perm.tpl');
+
+$template->assign(
+ array(
+ 'CATEGORIES_NAV' =>
+ get_cat_display_name_from_id(
+ $page['cat'],
+ 'admin.php?page=album-'
+ ),
+ 'U_HELP' => get_root_url().'admin/popuphelp.php?page=cat_perm',
+ 'F_ACTION' => $admin_album_base_url.'-permissions',
+ 'private' => ('private' == $category['status']),
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | form construction |
+// +-----------------------------------------------------------------------+
+
+// groups denied are the groups not granted. So we need to find all groups
+// minus groups granted to find groups denied.
+
+$groups = array();
+
+$query = '
+SELECT id, name
+ FROM '.GROUPS_TABLE.'
+ ORDER BY name ASC
+;';
+$groups = simple_hash_from_query($query, 'id', 'name');
+$template->assign('groups', $groups);
+
+// groups granted to access the category
+$query = '
+SELECT group_id
+ FROM '.GROUP_ACCESS_TABLE.'
+ WHERE cat_id = '.$page['cat'].'
+;';
+$group_granted_ids = array_from_query($query, 'group_id');
+$template->assign('groups_selected', $group_granted_ids);
+
+// users...
+$users = array();
+
+$query = '
+SELECT '.$conf['user_fields']['id'].' AS id,
+ '.$conf['user_fields']['username'].' AS username
+ FROM '.USERS_TABLE.'
+;';
+$users = simple_hash_from_query($query, 'id', 'username');
+$template->assign('users', $users);
+
+
+$query = '
+SELECT user_id
+ FROM '.USER_ACCESS_TABLE.'
+ WHERE cat_id = '.$page['cat'].'
+;';
+$user_granted_direct_ids = array_from_query($query, 'user_id');
+$template->assign('users_selected', $user_granted_direct_ids);
+
+
+$user_granted_indirect_ids = array();
+if (count($group_granted_ids) > 0)
+{
+ $granted_groups = array();
+
+ $query = '
+SELECT user_id, group_id
+ FROM '.USER_GROUP_TABLE.'
+ WHERE group_id IN ('.implode(',', $group_granted_ids).')
+';
+ $result = pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ if (!isset($granted_groups[ $row['group_id'] ]))
+ {
+ $granted_groups[ $row['group_id'] ] = array();
+ }
+ $granted_groups[ $row['group_id'] ][] = $row['user_id'];
+ }
+
+ $user_granted_by_group_ids = array();
+
+ foreach ($granted_groups as $group_users)
+ {
+ $user_granted_by_group_ids = array_merge($user_granted_by_group_ids, $group_users);
+ }
+
+ $user_granted_by_group_ids = array_unique($user_granted_by_group_ids);
+
+ $user_granted_indirect_ids = array_diff(
+ $user_granted_by_group_ids,
+ $user_granted_direct_ids
+ );
+
+ $template->assign('nb_users_granted_indirect', count($user_granted_indirect_ids));
+
+ foreach ($granted_groups as $group_id => $group_users)
+ {
+ $group_usernames = array();
+ foreach ($group_users as $user_id)
+ {
+ if (in_array($user_id, $user_granted_indirect_ids))
+ {
+ $group_usernames[] = $users[$user_id];
+ }
+ }
+
+ $template->append(
+ 'user_granted_indirect_groups',
+ array(
+ 'group_name' => $groups[$group_id],
+ 'group_users' => implode(', ', $group_usernames),
+ )
+ );
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+$template->assign(array('PWG_TOKEN' => get_pwg_token(), 'INHERIT' => $conf['inheritance_by_default']));
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'cat_perm');
+?>
diff --git a/sources/admin/comments.php b/sources/admin/comments.php
new file mode 100644
index 0000000..2740d77
--- /dev/null
+++ b/sources/admin/comments.php
@@ -0,0 +1,232 @@
+set_filenames(array('comments'=>'comments.tpl'));
+
+$template->assign(
+ array(
+ 'F_ACTION' => get_root_url().'admin.php?page=comments'
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | Tabs |
+// +-----------------------------------------------------------------------+
+
+include_once(PHPWG_ROOT_PATH.'admin/include/tabsheet.class.php');
+
+$tabsheet = new tabsheet();
+$tabsheet->set_id('comments');
+$tabsheet->select('');
+$tabsheet->assign();
+
+// +-----------------------------------------------------------------------+
+// | comments display |
+// +-----------------------------------------------------------------------+
+
+$nb_total = 0;
+$nb_pending = 0;
+
+$query = '
+SELECT
+ COUNT(*) AS counter,
+ validated
+ FROM '.COMMENTS_TABLE.'
+ GROUP BY validated
+;';
+$result = pwg_query($query);
+while ($row = pwg_db_fetch_assoc($result))
+{
+ $nb_total+= $row['counter'];
+
+ if ('false' == $row['validated'])
+ {
+ $nb_pending = $row['counter'];
+ }
+}
+
+if (!isset($_GET['filter']) and $nb_pending > 0)
+{
+ $page['filter'] = 'pending';
+}
+else
+{
+ $page['filter'] = 'all';
+}
+
+if (isset($_GET['filter']) and 'pending' == $_GET['filter'])
+{
+ $page['filter'] = $_GET['filter'];
+}
+
+$template->assign(
+ array(
+ 'nb_total' => $nb_total,
+ 'nb_pending' => $nb_pending,
+ 'filter' => $page['filter'],
+ )
+ );
+
+$where_clauses = array('1=1');
+
+if ('pending' == $page['filter'])
+{
+ $where_clauses[] = 'validated=\'false\'';
+}
+
+$query = '
+SELECT
+ c.id,
+ c.image_id,
+ c.date,
+ c.author,
+ '.$conf['user_fields']['username'].' AS username,
+ c.content,
+ i.path,
+ i.representative_ext,
+ validated
+ FROM '.COMMENTS_TABLE.' AS c
+ INNER JOIN '.IMAGES_TABLE.' AS i
+ ON i.id = c.image_id
+ LEFT JOIN '.USERS_TABLE.' AS u
+ ON u.'.$conf['user_fields']['id'].' = c.author_id
+ WHERE '.implode(' AND ', $where_clauses).'
+ ORDER BY c.date DESC
+ LIMIT '.$page['start'].', '.$conf['comments_page_nb_comments'].'
+;';
+$result = pwg_query($query);
+while ($row = pwg_db_fetch_assoc($result))
+{
+ $thumb = DerivativeImage::thumb_url(
+ array(
+ 'id'=>$row['image_id'],
+ 'path'=>$row['path'],
+ )
+ );
+ if (empty($row['author_id']))
+ {
+ $author_name = $row['author'];
+ }
+ else
+ {
+ $author_name = stripslashes($row['username']);
+ }
+ $template->append(
+ 'comments',
+ array(
+ 'U_PICTURE' => get_root_url().'admin.php?page=photo-'.$row['image_id'],
+ 'ID' => $row['id'],
+ 'TN_SRC' => $thumb,
+ 'AUTHOR' => trigger_event('render_comment_author', $author_name),
+ 'DATE' => format_date($row['date'], true),
+ 'CONTENT' => trigger_event('render_comment_content',$row['content']),
+ 'IS_PENDING' => ('false' == $row['validated']),
+ )
+ );
+
+ $list[] = $row['id'];
+}
+
+// +-----------------------------------------------------------------------+
+// | navigation bar |
+// +-----------------------------------------------------------------------+
+
+$navbar = create_navigation_bar(
+ get_root_url().'admin.php'.get_query_string_diff(array('start')),
+ ('pending' == $page['filter'] ? $nb_pending : $nb_total),
+ $page['start'],
+ $conf['comments_page_nb_comments']
+ );
+
+$template->assign('navbar', $navbar);
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'comments');
+
+?>
diff --git a/sources/admin/configuration.php b/sources/admin/configuration.php
new file mode 100644
index 0000000..f1b943c
--- /dev/null
+++ b/sources/admin/configuration.php
@@ -0,0 +1,605 @@
+ '',
+ 'file ASC' => l10n('File name, A → Z'),
+ 'file DESC' => l10n('File name, Z → A'),
+ 'name ASC' => l10n('Photo title, A → Z'),
+ 'name DESC' => l10n('Photo title, Z → A'),
+ 'date_creation DESC' => l10n('Date created, new → old'),
+ 'date_creation ASC' => l10n('Date created, old → new'),
+ 'date_available DESC' => l10n('Date posted, new → old'),
+ 'date_available ASC' => l10n('Date posted, old → new'),
+ 'rating_score DESC' => l10n('Rating score, high → low'),
+ 'rating_score ASC' => l10n('Rating score, low → high'),
+ 'hit DESC' => l10n('Visits, high → low'),
+ 'hit ASC' => l10n('Visits, low → high'),
+ 'id ASC' => l10n('Numeric identifier, 1 → 9'),
+ 'id DESC' => l10n('Numeric identifier, 9 → 1'),
+ 'rank ASC' => l10n('Manual sort order'),
+ );
+
+$comments_order = array(
+ 'ASC' => l10n('Show oldest comments first'),
+ 'DESC' => l10n('Show latest comments first'),
+ );
+
+$mail_themes = array(
+ 'clear' => 'Clear',
+ 'dark' => 'Dark',
+ );
+
+//------------------------------ verification and registration of modifications
+if (isset($_POST['submit']))
+{
+ $int_pattern = '/^\d+$/';
+
+ switch ($page['section'])
+ {
+ case 'main' :
+ {
+ if ( !isset($conf['order_by_custom']) and !isset($conf['order_by_inside_category_custom']) )
+ {
+ if ( !empty($_POST['order_by']) )
+ {
+ $used = array();
+ foreach ($_POST['order_by'] as $i => $val)
+ {
+ if (empty($val) or isset($used[$val]))
+ {
+ unset($_POST['order_by'][$i]);
+ }
+ else
+ {
+ $used[$val] = true;
+ }
+ }
+ if ( !count($_POST['order_by']) )
+ {
+ $page['errors'][] = l10n('No order field selected');
+ }
+ else
+ {
+ // limit to the number of available parameters
+ $order_by = $order_by_inside_category = array_slice($_POST['order_by'], 0, ceil(count($sort_fields)/2));
+
+ // there is no rank outside categories
+ if ( ($i = array_search('rank ASC', $order_by)) !== false)
+ {
+ unset($order_by[$i]);
+ }
+
+ // must define a default order_by if user want to order by rank only
+ if ( count($order_by) == 0 )
+ {
+ $order_by = array('id ASC');
+ }
+
+ $_POST['order_by'] = 'ORDER BY '.implode(', ', $order_by);
+ $_POST['order_by_inside_category'] = 'ORDER BY '.implode(', ', $order_by_inside_category);
+ }
+ }
+ else
+ {
+ $page['errors'][] = l10n('No order field selected');
+ }
+ }
+
+ foreach( $main_checkboxes as $checkbox)
+ {
+ $_POST[$checkbox] = empty($_POST[$checkbox])?'false':'true';
+ }
+ break;
+ }
+ case 'watermark' :
+ {
+ include(PHPWG_ROOT_PATH.'admin/include/configuration_watermark_process.inc.php');
+ break;
+ }
+ case 'sizes' :
+ {
+ include(PHPWG_ROOT_PATH.'admin/include/configuration_sizes_process.inc.php');
+ break;
+ }
+ case 'comments' :
+ {
+ // the number of comments per page must be an integer between 5 and 50
+ // included
+ if (!preg_match($int_pattern, $_POST['nb_comment_page'])
+ or $_POST['nb_comment_page'] < 5
+ or $_POST['nb_comment_page'] > 50)
+ {
+ $page['errors'][] = l10n('The number of comments a page must be between 5 and 50 included.');
+ }
+ foreach( $comments_checkboxes as $checkbox)
+ {
+ $_POST[$checkbox] = empty($_POST[$checkbox])?'false':'true';
+ }
+ break;
+ }
+ case 'default' :
+ {
+ // Never go here
+ break;
+ }
+ case 'display' :
+ {
+ if (!preg_match($int_pattern, $_POST['nb_categories_page'])
+ or $_POST['nb_categories_page'] < 4)
+ {
+ $page['errors'][] = l10n('The number of albums a page must be above 4.');
+ }
+ foreach( $display_checkboxes as $checkbox)
+ {
+ $_POST[$checkbox] = empty($_POST[$checkbox])?'false':'true';
+ }
+ foreach( $display_info_checkboxes as $checkbox)
+ {
+ $_POST['picture_informations'][$checkbox] =
+ empty($_POST['picture_informations'][$checkbox])? false : true;
+ }
+ $_POST['picture_informations'] = addslashes(serialize($_POST['picture_informations']));
+ break;
+ }
+ }
+
+ // updating configuration if no error found
+ if (!in_array($page['section'], array('sizes', 'watermark')) and count($page['errors']) == 0)
+ {
+ //echo ''; print_r($_POST); echo ' ';
+ $result = pwg_query('SELECT param FROM '.CONFIG_TABLE);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ if (isset($_POST[$row['param']]))
+ {
+ $value = $_POST[$row['param']];
+
+ if ('gallery_title' == $row['param'])
+ {
+ if (!$conf['allow_html_descriptions'])
+ {
+ $value = strip_tags($value);
+ }
+ }
+
+ $query = '
+UPDATE '.CONFIG_TABLE.'
+SET value = \''. str_replace("\'", "''", $value).'\'
+WHERE param = \''.$row['param'].'\'
+;';
+ pwg_query($query);
+ }
+ }
+ $page['infos'][] = l10n('Information data registered in database');
+ }
+
+ //------------------------------------------------------ $conf reinitialization
+ load_conf_from_db();
+}
+
+// restore default derivatives settings
+if ('sizes' == $page['section'] and isset($_GET['action']) and 'restore_settings' == $_GET['action'])
+{
+ ImageStdParams::set_and_save( ImageStdParams::get_default_sizes() );
+ pwg_query('DELETE FROM '.CONFIG_TABLE.' WHERE param = \'disabled_derivatives\'');
+ clear_derivative_cache();
+
+ $page['infos'][] = l10n('Your configuration settings are saved');
+}
+
+//----------------------------------------------------- template initialization
+$template->set_filename('config', 'configuration.tpl');
+
+// TabSheet
+$tabsheet = new tabsheet();
+$tabsheet->set_id('configuration');
+$tabsheet->select($page['section']);
+$tabsheet->assign();
+
+$action = get_root_url().'admin.php?page=configuration';
+$action.= '§ion='.$page['section'];
+
+$template->assign(
+ array(
+ 'U_HELP' => get_root_url().'admin/popuphelp.php?page=configuration',
+ 'F_ACTION'=>$action
+ ));
+
+switch ($page['section'])
+{
+ case 'main' :
+ {
+
+ function order_by_is_local()
+ {
+ @include(PHPWG_ROOT_PATH. 'local/config/config.inc.php');
+ if (isset($conf['local_dir_site']))
+ {
+ @include(PHPWG_ROOT_PATH.PWG_LOCAL_DIR. 'config/config.inc.php');
+ }
+
+ return isset($conf['order_by']) or isset($conf['order_by_inside_category']);
+ }
+
+ if (order_by_is_local())
+ {
+ $page['warnings'][] = l10n('You have specified $conf[\'order_by\'] in your local configuration file, this parameter in deprecated, please remove it or rename it into $conf[\'order_by_custom\'] !');
+ }
+
+ if ( isset($conf['order_by_custom']) or isset($conf['order_by_inside_category_custom']) )
+ {
+ $order_by = array('');
+ $template->assign('ORDER_BY_IS_CUSTOM', true);
+ }
+ else
+ {
+ $out = array();
+ $order_by = trim($conf['order_by_inside_category']);
+ $order_by = str_replace('ORDER BY ', null, $order_by);
+ $order_by = explode(', ', $order_by);
+ }
+
+ $template->assign(
+ 'main',
+ array(
+ 'CONF_GALLERY_TITLE' => htmlspecialchars($conf['gallery_title']),
+ 'CONF_PAGE_BANNER' => htmlspecialchars($conf['page_banner']),
+ 'week_starts_on_options' => array(
+ 'sunday' => $lang['day'][0],
+ 'monday' => $lang['day'][1],
+ ),
+ 'week_starts_on_options_selected' => $conf['week_starts_on'],
+ 'mail_theme' => $conf['mail_theme'],
+ 'mail_theme_options' => $mail_themes,
+ 'order_by' => $order_by,
+ 'order_by_options' => $sort_fields,
+ )
+ );
+
+ foreach ($main_checkboxes as $checkbox)
+ {
+ $template->append(
+ 'main',
+ array(
+ $checkbox => $conf[$checkbox]
+ ),
+ true
+ );
+ }
+ break;
+ }
+ case 'comments' :
+ {
+ $template->assign(
+ 'comments',
+ array(
+ 'NB_COMMENTS_PAGE'=>$conf['nb_comment_page'],
+ 'comments_order'=>$conf['comments_order'],
+ 'comments_order_options'=> $comments_order
+ )
+ );
+
+ foreach ($comments_checkboxes as $checkbox)
+ {
+ $template->append(
+ 'comments',
+ array(
+ $checkbox => $conf[$checkbox]
+ ),
+ true
+ );
+ }
+ break;
+ }
+ case 'default' :
+ {
+ $edit_user = build_user($conf['guest_id'], false);
+ include_once(PHPWG_ROOT_PATH.'profile.php');
+
+ $errors = array();
+ if (save_profile_from_post($edit_user, $errors))
+ {
+ // Reload user
+ $edit_user = build_user($conf['guest_id'], false);
+ $page['infos'][] = l10n('Information data registered in database');
+ }
+ $page['errors'] = array_merge($page['errors'], $errors);
+
+ load_profile_in_template(
+ $action,
+ '',
+ $edit_user
+ );
+ $template->assign('default', array());
+ break;
+ }
+ case 'display' :
+ {
+ foreach ($display_checkboxes as $checkbox)
+ {
+ $template->append(
+ 'display',
+ array(
+ $checkbox => $conf[$checkbox]
+ ),
+ true
+ );
+ }
+ $template->append(
+ 'display',
+ array(
+ 'picture_informations' => unserialize($conf['picture_informations']),
+ 'NB_CATEGORIES_PAGE' => $conf['nb_categories_page'],
+ ),
+ true
+ );
+ break;
+ }
+ case 'sizes' :
+ {
+ // we only load the derivatives if it was not already loaded: it occurs
+ // when submitting the form and an error remains
+ if (!isset($page['sizes_loaded_in_tpl']))
+ {
+ $is_gd = (pwg_image::get_library()=='gd')? true : false;
+ $template->assign('is_gd', $is_gd);
+ $template->assign(
+ 'sizes',
+ array(
+ 'original_resize_maxwidth' => $conf['original_resize_maxwidth'],
+ 'original_resize_maxheight' => $conf['original_resize_maxheight'],
+ 'original_resize_quality' => $conf['original_resize_quality'],
+ )
+ );
+
+ foreach ($sizes_checkboxes as $checkbox)
+ {
+ $template->append(
+ 'sizes',
+ array(
+ $checkbox => $conf[$checkbox]
+ ),
+ true
+ );
+ }
+
+ // derivatives = multiple size
+ $enabled = ImageStdParams::get_defined_type_map();
+ $disabled = @unserialize(@$conf['disabled_derivatives']);
+ if ($disabled === false)
+ {
+ $disabled = array();
+ }
+
+ $tpl_vars = array();
+ foreach(ImageStdParams::get_all_types() as $type)
+ {
+ $tpl_var = array();
+
+ $tpl_var['must_square'] = ($type==IMG_SQUARE ? true : false);
+ $tpl_var['must_enable'] = ($type==IMG_SQUARE || $type==IMG_THUMB)? true : false;
+
+ if ($params = @$enabled[$type])
+ {
+ $tpl_var['enabled'] = true;
+ }
+ else
+ {
+ $tpl_var['enabled']=false;
+ $params=@$disabled[$type];
+ }
+
+ if ($params)
+ {
+ list($tpl_var['w'],$tpl_var['h']) = $params->sizing->ideal_size;
+ if ( ($tpl_var['crop'] = round(100*$params->sizing->max_crop)) > 0)
+ {
+ list($tpl_var['minw'],$tpl_var['minh']) = $params->sizing->min_size;
+ }
+ else
+ {
+ $tpl_var['minw'] = $tpl_var['minh'] = "";
+ }
+ $tpl_var['sharpen'] = $params->sharpen;
+ }
+ $tpl_vars[$type]=$tpl_var;
+ }
+ $template->assign('derivatives', $tpl_vars);
+ $template->assign('resize_quality', ImageStdParams::$quality);
+
+ $tpl_vars = array();
+ $now = time();
+ foreach(ImageStdParams::$custom as $custom=>$time)
+ {
+ $tpl_vars[$custom] = ($now-$time<=24*3600) ? l10n('today') : time_since($time, 'day');
+ }
+ $template->assign('custom_derivatives', $tpl_vars);
+ }
+
+ break;
+ }
+ case 'watermark' :
+ {
+ $watermark_files = array();
+ foreach (glob(PHPWG_ROOT_PATH.'themes/default/watermarks/*.png') as $file)
+ {
+ $watermark_files[] = substr($file, strlen(PHPWG_ROOT_PATH));
+ }
+ if ( ($glob=glob(PHPWG_ROOT_PATH.PWG_LOCAL_DIR.'watermarks/*.png')) !== false)
+ {
+ foreach ($glob as $file)
+ {
+ $watermark_files[] = substr($file, strlen(PHPWG_ROOT_PATH));
+ }
+ }
+ $watermark_filemap = array( '' => '---' );
+ foreach( $watermark_files as $file)
+ {
+ $display = basename($file);
+ $watermark_filemap[$file] = $display;
+ }
+ $template->assign('watermark_files', $watermark_filemap);
+
+ if ($template->get_template_vars('watermark') === null)
+ {
+ $wm = ImageStdParams::get_watermark();
+
+ $position = 'custom';
+ if ($wm->xpos == 0 and $wm->ypos == 0)
+ {
+ $position = 'topleft';
+ }
+ if ($wm->xpos == 100 and $wm->ypos == 0)
+ {
+ $position = 'topright';
+ }
+ if ($wm->xpos == 50 and $wm->ypos == 50)
+ {
+ $position = 'middle';
+ }
+ if ($wm->xpos == 0 and $wm->ypos == 100)
+ {
+ $position = 'bottomleft';
+ }
+ if ($wm->xpos == 100 and $wm->ypos == 100)
+ {
+ $position = 'bottomright';
+ }
+
+ if ($wm->xrepeat != 0)
+ {
+ $position = 'custom';
+ }
+
+ $template->assign(
+ 'watermark',
+ array(
+ 'file' => $wm->file,
+ 'minw' => $wm->min_size[0],
+ 'minh' => $wm->min_size[1],
+ 'xpos' => $wm->xpos,
+ 'ypos' => $wm->ypos,
+ 'xrepeat' => $wm->xrepeat,
+ 'opacity' => $wm->opacity,
+ 'position' => $position,
+ )
+ );
+ }
+
+ break;
+ }
+}
+
+//----------------------------------------------------------- sending html code
+$template->assign_var_from_handle('ADMIN_CONTENT', 'config');
+?>
diff --git a/sources/admin/element_set_ranks.php b/sources/admin/element_set_ranks.php
new file mode 100644
index 0000000..289cc2a
--- /dev/null
+++ b/sources/admin/element_set_ranks.php
@@ -0,0 +1,275 @@
+ $category_id,
+ 'image_id' => $id,
+ 'rank' => ++$current_rank,
+ );
+ }
+ $fields = array(
+ 'primary' => array('image_id', 'category_id'),
+ 'update' => array('rank')
+ );
+ mass_updates(IMAGE_CATEGORY_TABLE, $fields, $datas);
+}
+
+// +-----------------------------------------------------------------------+
+// | global mode form submission |
+// +-----------------------------------------------------------------------+
+
+$image_order_choices = array('default', 'rank', 'user_define');
+$image_order_choice = 'default';
+
+if (isset($_POST['submit']))
+{
+ if (isset($_POST['rank_of_image']))
+ {
+ asort($_POST['rank_of_image'], SORT_NUMERIC);
+
+ save_images_order(
+ $page['category_id'],
+ array_keys($_POST['rank_of_image'])
+ );
+
+ $page['infos'][] = l10n('Images manual order was saved');
+ }
+
+ if (!empty($_POST['image_order_choice'])
+ && in_array($_POST['image_order_choice'], $image_order_choices))
+ {
+ $image_order_choice = $_POST['image_order_choice'];
+ }
+
+ $image_order = null;
+ if ($image_order_choice=='user_define')
+ {
+ for ($i=0; $i<3; $i++)
+ {
+ if (!empty($_POST['image_order'][$i]))
+ {
+ if (!empty($image_order)) $image_order.= ',';
+ $image_order.= $_POST['image_order'][$i];
+ }
+ }
+ }
+ elseif ($image_order_choice=='rank')
+ {
+ $image_order = 'rank ASC';
+ }
+ $query = '
+UPDATE '.CATEGORIES_TABLE.'
+ SET image_order = '.(isset($image_order) ? '\''.$image_order.'\'' : 'NULL').'
+ WHERE id='.$page['category_id'];
+ pwg_query($query);
+
+ if (isset($_POST['image_order_subcats']))
+ {
+ $cat_info = get_cat_info($page['category_id']);
+
+ $query = '
+UPDATE '.CATEGORIES_TABLE.'
+ SET image_order = '.(isset($image_order) ? '\''.$image_order.'\'' : 'NULL').'
+ WHERE uppercats LIKE \''.$cat_info['uppercats'].',%\'';
+ pwg_query($query);
+ }
+
+ $page['infos'][] = l10n('Your configuration settings are saved');
+}
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+$template->set_filenames(
+ array('element_set_ranks' => 'element_set_ranks.tpl')
+ );
+
+$base_url = get_root_url().'admin.php';
+
+$query = '
+SELECT *
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id = '.$page['category_id'].'
+;';
+$category = pwg_db_fetch_assoc(pwg_query($query));
+
+if ($category['image_order']=='rank')
+{
+ $image_order_choice = 'rank';
+}
+elseif ($category['image_order']!='')
+{
+ $image_order_choice = 'user_define';
+}
+
+// Navigation path
+$navigation = get_cat_display_name_cache(
+ $category['uppercats'],
+ get_root_url().'admin.php?page=album-'
+ );
+
+$template->assign(
+ array(
+ 'CATEGORIES_NAV' => $navigation,
+ 'F_ACTION' => $base_url.get_query_string_diff(array()),
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | thumbnails |
+// +-----------------------------------------------------------------------+
+
+$query = '
+SELECT
+ id,
+ file,
+ path,
+ representative_ext,
+ width, height, rotation,
+ name,
+ rank
+ FROM '.IMAGES_TABLE.'
+ JOIN '.IMAGE_CATEGORY_TABLE.' ON image_id = id
+ WHERE category_id = '.$page['category_id'].'
+ ORDER BY rank
+;';
+$result = pwg_query($query);
+if (pwg_db_num_rows($result) > 0)
+{
+ // template thumbnail initialization
+ $current_rank = 1;
+ $derivativeParams = ImageStdParams::get_by_type(IMG_SQUARE);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $derivative = new DerivativeImage($derivativeParams, new SrcImage($row));
+
+ if ( !empty( $row['name'] ) )
+ {
+ $thumbnail_name = $row['name'];
+ }
+ else
+ {
+ $file_wo_ext = get_filename_wo_extension($row['file']);
+ $thumbnail_name = str_replace('_', ' ', $file_wo_ext);
+ }
+ $current_rank++;
+ $template->append(
+ 'thumbnails',
+ array(
+ 'ID' => $row['id'],
+ 'NAME' => $thumbnail_name,
+ 'TN_SRC' => $derivative->get_url(),
+ 'RANK' => $current_rank * 10,
+ 'SIZE' => $derivative->get_size(),
+ )
+ );
+ }
+}
+// image order management
+$sort_fields = array(
+ '' => '',
+ 'file ASC' => l10n('File name, A → Z'),
+ 'file DESC' => l10n('File name, Z → A'),
+ 'name ASC' => l10n('Photo title, A → Z'),
+ 'name DESC' => l10n('Photo title, Z → A'),
+ 'date_creation DESC' => l10n('Date created, new → old'),
+ 'date_creation ASC' => l10n('Date created, old → new'),
+ 'date_available DESC' => l10n('Date posted, new → old'),
+ 'date_available ASC' => l10n('Date posted, old → new'),
+ 'rating_score DESC' => l10n('Rating score, high → low'),
+ 'rating_score ASC' => l10n('Rating score, low → high'),
+ 'hit DESC' => l10n('Visits, high → low'),
+ 'hit ASC' => l10n('Visits, low → high'),
+ 'id ASC' => l10n('Numeric identifier, 1 → 9'),
+ 'id DESC' => l10n('Numeric identifier, 9 → 1'),
+ 'rank ASC' => l10n('Manual sort order'),
+ );
+
+$template->assign('image_order_options', $sort_fields);
+
+$image_order = explode(',', $category['image_order']);
+
+for ($i=0; $i<3; $i++) // 3 fields
+{
+ if ( isset($image_order[$i]) )
+ {
+ $template->append('image_order', $image_order[$i]);
+ }
+ else
+ {
+ $template->append('image_order', '');
+ }
+}
+
+$template->assign('image_order_choice', $image_order_choice);
+
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'element_set_ranks');
+?>
diff --git a/sources/admin/extend_for_templates.php b/sources/admin/extend_for_templates.php
new file mode 100644
index 0000000..0bab121
--- /dev/null
+++ b/sources/admin/extend_for_templates.php
@@ -0,0 +1,204 @@
+ Random */
+ 'tags',
+ );
+ $query = '
+SELECT permalink
+ FROM '.CATEGORIES_TABLE.'
+ WHERE permalink IS NOT NULL
+';
+
+/* Add active permalinks */
+$permalinks = array_from_query($query, 'permalink');
+$relevant_parameters = array_merge($relevant_parameters, $permalinks);
+
+/* Link all supported templates to their respective handle */
+$eligible_templates = array(
+ '----------' => 'N/A',
+ 'about.tpl' => 'about',
+ 'comments.tpl' => 'comments',
+ 'comment_list.tpl' => 'comment_list',
+ 'footer.tpl' => 'tail',
+ 'header.tpl' => 'header',
+ 'identification.tpl' => 'identification',
+ 'index.tpl' => 'index',
+ 'mainpage_categories.tpl' => 'index_category_thumbnails',
+ 'menubar.tpl' => 'menubar',
+ 'menubar_categories.tpl' => 'mbCategories',
+ 'menubar_identification.tpl' => 'mbIdentification',
+ 'menubar_links.tpl' => 'mbLinks',
+ 'menubar_menu.tpl' => 'mbMenu',
+ 'menubar_specials.tpl' => 'mbSpecials',
+ 'menubar_tags.tpl' => 'mbTags',
+ 'month_calendar.tpl' => 'month_calendar',
+ 'navigation_bar.tpl' => 'navbar',
+ 'nbm.tpl' => 'nbm',
+ 'notification.tpl' => 'notification',
+ 'password.tpl' => 'password',
+ 'picture.tpl' => 'picture',
+ 'picture_content.tpl' => 'default_content',
+ 'picture_nav_buttons.tpl' => 'picture_nav_buttons',
+ 'popuphelp.tpl' => 'popuphelp',
+ 'profile.tpl' => 'profile',
+ 'profile_content.tpl' => 'profile_content',
+ 'redirect.tpl' => 'redirect',
+ 'register.tpl' => 'register',
+ 'search.tpl' => 'search',
+ 'search_rules.tpl' => 'search_rules',
+ 'slideshow.tpl' => 'slideshow',
+ 'tags.tpl' => 'tags',
+ 'thumbnails.tpl' => 'index_thumbnails',
+);
+
+$flip_templates = array_flip($eligible_templates);
+
+$available_templates = array_merge(
+ array('N/A' => '----------'),
+ get_dirs(PHPWG_ROOT_PATH.'themes'));
+
+// +-----------------------------------------------------------------------+
+// | selected templates |
+// +-----------------------------------------------------------------------+
+
+if (isset($_POST['submit']))
+{
+ $replacements = array();
+ $i = 0;
+ while (isset($_POST['reptpl'][$i]))
+ {
+ $newtpl = $_POST['reptpl'][$i];
+ $original = $_POST['original'][$i];
+ $handle = $eligible_templates[$original];
+ $url_keyword = $_POST['url'][$i];
+ if ($url_keyword == '----------') $url_keyword = 'N/A';
+ $bound_tpl = $_POST['bound'][$i];
+ if ($bound_tpl == '----------') $bound_tpl = 'N/A';
+ if ($handle != 'N/A')
+ {
+ $replacements[$newtpl] = array($handle, $url_keyword, $bound_tpl);
+ }
+ $i++;
+ }
+ $conf['extents_for_templates'] = serialize($replacements);
+ $tpl_extension = $replacements;
+ /* ecrire la nouvelle conf */
+ $query = '
+UPDATE '.CONFIG_TABLE.'
+ SET value = \''. $conf['extents_for_templates'] .'\'
+WHERE param = \'extents_for_templates\';';
+ if (pwg_query($query))
+ {
+ $page['infos'][] = l10n('Templates configuration has been recorded.');
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+
+/* Clearing (remove old extents, add new ones) */
+foreach ($tpl_extension as $file => $conditions)
+{
+ if ( !in_array($file,$new_extensions) ) unset($tpl_extension[$file]);
+ else $new_extensions = array_diff($new_extensions,array($file));
+}
+foreach ($new_extensions as $file)
+{
+ $tpl_extension[$file] = array('N/A', 'N/A', 'N/A');
+}
+
+$template->set_filenames(array('extend_for_templates'
+ => 'extend_for_templates.tpl'));
+
+$base_url = PHPWG_ROOT_PATH.'admin.php?page=extend_for_templates';
+
+$template->assign(
+ array(
+ 'U_HELP' => get_root_url().'admin/popuphelp.php?page=extend_for_templates',
+ ));
+ksort($tpl_extension);
+foreach ($tpl_extension as $file => $conditions)
+{
+ $handle = $conditions[0];
+ $url_keyword = $conditions[1];
+ $bound_tpl = $conditions[2];
+ {
+ $template->append('extents',
+ array(
+ 'replacer' => $file,
+ 'url_parameter' => $relevant_parameters,
+ 'original_tpl' => array_keys($eligible_templates),
+ 'bound_tpl' => $available_templates,
+ 'selected_tpl' => $flip_templates[$handle],
+ 'selected_url' => $url_keyword,
+ 'selected_bound' => $bound_tpl,)
+ );
+ }
+}
+// +-----------------------------------------------------------------------+
+// | html code display |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'extend_for_templates');
+?>
\ No newline at end of file
diff --git a/sources/admin/group_list.php b/sources/admin/group_list.php
new file mode 100644
index 0000000..5c53a8b
--- /dev/null
+++ b/sources/admin/group_list.php
@@ -0,0 +1,431 @@
+set_id('groups');
+$tabsheet->select('group_list');
+$tabsheet->assign();
+
+// +-----------------------------------------------------------------------+
+// | Check Access and exit when user status is not ok |
+// +-----------------------------------------------------------------------+
+check_status(ACCESS_ADMINISTRATOR);
+
+if (!empty($_POST) or isset($_GET['delete']) or isset($_GET['toggle_is_default']))
+{
+ check_pwg_token();
+}
+// +-----------------------------------------------------------------------+
+// | add a group |
+// +-----------------------------------------------------------------------+
+
+if (isset($_POST['submit_add']))
+{
+ if (empty($_POST['groupname']))
+ {
+ $page['errors'][] = l10n('The name of a group must not contain " or \' or be empty.');
+ }
+ if (count($page['errors']) == 0)
+ {
+ // is the group not already existing ?
+ $query = '
+SELECT COUNT(*)
+ FROM '.GROUPS_TABLE.'
+ WHERE name = \''.$_POST['groupname'].'\'
+;';
+ list($count) = pwg_db_fetch_row(pwg_query($query));
+ if ($count != 0)
+ {
+ $page['errors'][] = l10n('This name is already used by another group.');
+ }
+ }
+ if (count($page['errors']) == 0)
+ {
+ // creating the group
+ $query = '
+INSERT INTO '.GROUPS_TABLE.'
+ (name)
+ VALUES
+ (\''.pwg_db_real_escape_string($_POST['groupname']).'\')
+;';
+ pwg_query($query);
+
+ $page['infos'][] = l10n('group "%s" added', $_POST['groupname']);
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | action send |
+// +-----------------------------------------------------------------------+
+if (isset($_POST['submit']) and isset($_POST['selectAction']) and isset($_POST['group_selection']))
+{
+ // if the user tries to apply an action, it means that there is at least 1
+ // photo in the selection
+ $groups = $_POST['group_selection'];
+ if (count($groups) == 0)
+ {
+ $page['errors'][] = l10n('Select at least one group');
+ }
+
+ $action = $_POST['selectAction'];
+
+ // +
+ // |rename a group
+ // +
+
+ if ($action=="rename")
+ {
+ // is the group not already existing ?
+ $query = '
+SELECT name
+ FROM '.GROUPS_TABLE.'
+;';
+ $group_names = array_from_query($query, 'name');
+ foreach($groups as $group)
+ {
+ if ( in_array($_POST['rename_'.$group.''], $group_names))
+ {
+ $page['errors'][] = $_POST['rename_'.$group.''].' | '.l10n('This name is already used by another group.');
+ }
+ elseif ( !empty($_POST['rename_'.$group.'']))
+ {
+ $query = '
+ UPDATE '.GROUPS_TABLE.'
+ SET name = \''.pwg_db_real_escape_string($_POST['rename_'.$group.'']).'\'
+ WHERE id = '.$group.'
+ ;';
+ pwg_query($query);
+ }
+ }
+ }
+
+ // +
+ // |delete a group
+ // +
+
+ if ($action=="delete" and isset($_POST['confirm_deletion']) and $_POST['confirm_deletion'])
+ {
+ foreach($groups as $group)
+ {
+ // destruction of the access linked to the group
+ $query = '
+ DELETE
+ FROM '.GROUP_ACCESS_TABLE.'
+ WHERE group_id = '.$group.'
+ ;';
+ pwg_query($query);
+
+ // destruction of the users links for this group
+ $query = '
+ DELETE
+ FROM '.USER_GROUP_TABLE.'
+ WHERE group_id = '.$group.'
+ ;';
+ pwg_query($query);
+
+ $query = '
+ SELECT name
+ FROM '.GROUPS_TABLE.'
+ WHERE id = '.$group.'
+ ;';
+ list($groupname) = pwg_db_fetch_row(pwg_query($query));
+
+ // destruction of the group
+ $query = '
+ DELETE
+ FROM '.GROUPS_TABLE.'
+ WHERE id = '.$group.'
+ ;';
+ pwg_query($query);
+
+ $page['infos'][] = l10n('group "%s" deleted', $groupname);
+ }
+ }
+
+ // +
+ // |merge groups into a new one
+ // +
+
+ if ($action=="merge" and count($groups) > 1)
+ {
+ // is the group not already existing ?
+ $query = '
+SELECT COUNT(*)
+ FROM '.GROUPS_TABLE.'
+ WHERE name = \''.pwg_db_real_escape_string($_POST['merge']).'\'
+;';
+ list($count) = pwg_db_fetch_row(pwg_query($query));
+ if ($count != 0)
+ {
+ $page['errors'][] = l10n('This name is already used by another group.');
+ }
+ else
+ {
+ // creating the group
+ $query = '
+ INSERT INTO '.GROUPS_TABLE.'
+ (name)
+ VALUES
+ (\''.pwg_db_real_escape_string($_POST['merge']).'\')
+ ;';
+ pwg_query($query);
+ $query = '
+ SELECT id
+ FROM '.GROUPS_TABLE.'
+ WHERE name = \''.pwg_db_real_escape_string($_POST['merge']).'\'
+ ;';
+ list($groupid) = pwg_db_fetch_row(pwg_query($query));
+ }
+ $grp_access = array();
+ $usr_grp = array();
+ foreach($groups as $group)
+ {
+ $query = '
+ SELECT *
+ FROM '.GROUP_ACCESS_TABLE.'
+ WHERE group_id = '.$group.'
+ ;';
+ $res=pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($res))
+ {
+ $new_grp_access= array(
+ 'cat_id' => $row['cat_id'],
+ 'group_id' => $groupid
+ );
+ if (!in_array($new_grp_access,$grp_access))
+ {
+ $grp_access[]=$new_grp_access;
+ }
+ }
+
+ $query = '
+ SELECT *
+ FROM '.USER_GROUP_TABLE.'
+ WHERE group_id = '.$group.'
+ ;';
+ $res=pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($res))
+ {
+ $new_usr_grp= array(
+ 'user_id' => $row['user_id'],
+ 'group_id' => $groupid
+ );
+ if (!in_array($new_usr_grp,$usr_grp))
+ {
+ $usr_grp[]=$new_usr_grp;
+ }
+ }
+ }
+ mass_inserts(USER_GROUP_TABLE, array('user_id','group_id'), $usr_grp);
+ mass_inserts(GROUP_ACCESS_TABLE, array('group_id','cat_id'), $grp_access);
+
+ $page['infos'][] = l10n('group "%s" added', $_POST['merge']);
+ }
+
+ // +
+ // |duplicate a group
+ // +
+
+ if ($action=="duplicate" )
+ {
+ foreach($groups as $group)
+ {
+ if ( empty($_POST['duplicate_'.$group.'']) )
+ {
+ break;
+ }
+ // is the group not already existing ?
+ $query = '
+ SELECT COUNT(*)
+ FROM '.GROUPS_TABLE.'
+ WHERE name = \''.pwg_db_real_escape_string($_POST['duplicate_'.$group.'']).'\'
+ ;';
+ list($count) = pwg_db_fetch_row(pwg_query($query));
+ if ($count != 0)
+ {
+ $page['errors'][] = l10n('This name is already used by another group.');
+ break;
+ }
+ // creating the group
+ $query = '
+ INSERT INTO '.GROUPS_TABLE.'
+ (name)
+ VALUES
+ (\''.pwg_db_real_escape_string($_POST['duplicate_'.$group.'']).'\')
+ ;';
+ pwg_query($query);
+ $query = '
+ SELECT id
+ FROM '.GROUPS_TABLE.'
+ WHERE name = \''.pwg_db_real_escape_string($_POST['duplicate_'.$group.'']).'\'
+ ;';
+
+ list($groupid) = pwg_db_fetch_row(pwg_query($query));
+ $query = '
+ SELECT *
+ FROM '.GROUP_ACCESS_TABLE.'
+ WHERE group_id = '.$group.'
+ ;';
+ $grp_access = array();
+ $res=pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($res))
+ {
+ $grp_access[] = array(
+ 'cat_id' => $row['cat_id'],
+ 'group_id' => $groupid
+ );
+ }
+ mass_inserts(GROUP_ACCESS_TABLE, array('group_id','cat_id'), $grp_access);
+
+ $query = '
+ SELECT *
+ FROM '.USER_GROUP_TABLE.'
+ WHERE group_id = '.$group.'
+ ;';
+ $usr_grp = array();
+ $res=pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($res))
+ {
+ $usr_grp[] = array(
+ 'user_id' => $row['user_id'],
+ 'group_id' => $groupid
+ );
+ }
+ mass_inserts(USER_GROUP_TABLE, array('user_id','group_id'), $usr_grp);
+
+ $page['infos'][] = l10n('group "%s" added', $_POST['duplicate_'.$group.'']);
+ }
+ }
+
+
+ // +
+ // | toggle_default
+ // +
+
+ if ($action=="toggle_default")
+ {
+ foreach($groups as $group)
+ {
+ $query = '
+ SELECT name, is_default
+ FROM '.GROUPS_TABLE.'
+ WHERE id = '.$group.'
+ ;';
+ list($groupname, $is_default) = pwg_db_fetch_row(pwg_query($query));
+
+ // update of the group
+ $query = '
+ UPDATE '.GROUPS_TABLE.'
+ SET is_default = \''.boolean_to_string(!get_boolean($is_default)).'\'
+ WHERE id = '.$group.'
+ ;';
+ pwg_query($query);
+
+ $page['infos'][] = l10n('group "%s" updated', $groupname);
+ }
+ }
+ invalidate_user_cache();
+}
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+
+$template->set_filenames(array('group_list' => 'group_list.tpl'));
+
+$template->assign(
+ array(
+ 'F_ADD_ACTION' => get_root_url().'admin.php?page=group_list',
+ 'U_HELP' => get_root_url().'admin/popuphelp.php?page=group_list',
+ 'PWG_TOKEN' => get_pwg_token(),
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | group list |
+// +-----------------------------------------------------------------------+
+
+$query = '
+SELECT id, name, is_default
+ FROM '.GROUPS_TABLE.'
+ ORDER BY name ASC
+;';
+$result = pwg_query($query);
+
+$admin_url = get_root_url().'admin.php?page=';
+$perm_url = $admin_url.'group_perm&group_id=';
+$del_url = $admin_url.'group_list&delete=';
+$toggle_is_default_url = $admin_url.'group_list&toggle_is_default=';
+
+while ($row = pwg_db_fetch_assoc($result))
+{
+ $query = '
+SELECT u.'. $conf['user_fields']['username'].' AS username
+ FROM '.USERS_TABLE.' AS u
+ INNER JOIN '.USER_GROUP_TABLE.' AS ug
+ ON u.'.$conf['user_fields']['id'].' = ug.user_id
+ WHERE ug.group_id = '.$row['id'].'
+;';
+ $members=array();
+ $res=pwg_query($query);
+ while ($us= pwg_db_fetch_assoc($res))
+ {
+ $members[]=$us['username'];
+ }
+ $template->append(
+ 'groups',
+ array(
+ 'NAME' => $row['name'],
+ 'ID' => $row['id'],
+ 'IS_DEFAULT' => (get_boolean($row['is_default']) ? ' ['.l10n('default').']' : ''),
+ 'NB_MEMBERS' => count($members),
+ 'L_MEMBERS' => implode(' · ', $members),
+ 'MEMBERS' => l10n_dec('%d member', '%d members', count($members)),
+ 'U_DELETE' => $del_url.$row['id'].'&pwg_token='.get_pwg_token(),
+ 'U_PERM' => $perm_url.$row['id'],
+ 'U_ISDEFAULT' => $toggle_is_default_url.$row['id'].'&pwg_token='.get_pwg_token(),
+ )
+ );
+}
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'group_list');
+
+?>
\ No newline at end of file
diff --git a/sources/admin/group_perm.php b/sources/admin/group_perm.php
new file mode 100644
index 0000000..28ea481
--- /dev/null
+++ b/sources/admin/group_perm.php
@@ -0,0 +1,182 @@
+ 0)
+{
+ // if you forbid access to a category, all sub-categories become
+ // automatically forbidden
+ $subcats = get_subcat_ids($_POST['cat_true']);
+ $query = '
+DELETE
+ FROM '.GROUP_ACCESS_TABLE.'
+ WHERE group_id = '.$page['group'].'
+ AND cat_id IN ('.implode(',', $subcats).')
+;';
+ pwg_query($query);
+}
+else if (isset($_POST['trueify'])
+ and isset($_POST['cat_false'])
+ and count($_POST['cat_false']) > 0)
+{
+ $uppercats = get_uppercat_ids($_POST['cat_false']);
+ $private_uppercats = array();
+
+ $query = '
+SELECT id
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id IN ('.implode(',', $uppercats).')
+ AND status = \'private\'
+;';
+ $result = pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $private_uppercats[] = $row['id'];
+ }
+
+ // retrying to authorize a category which is already authorized may cause
+ // an error (in SQL statement), so we need to know which categories are
+ // accesible
+ $authorized_ids = array();
+
+ $query = '
+SELECT cat_id
+ FROM '.GROUP_ACCESS_TABLE.'
+ WHERE group_id = '.$page['group'].'
+;';
+ $result = pwg_query($query);
+
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $authorized_ids[] = $row['cat_id'];
+ }
+
+ $inserts = array();
+ $to_autorize_ids = array_diff($private_uppercats, $authorized_ids);
+ foreach ($to_autorize_ids as $to_autorize_id)
+ {
+ $inserts[] = array(
+ 'group_id' => $page['group'],
+ 'cat_id' => $to_autorize_id
+ );
+ }
+
+ mass_inserts(GROUP_ACCESS_TABLE, array('group_id','cat_id'), $inserts);
+ invalidate_user_cache();
+}
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+
+$template->set_filenames(
+ array(
+ 'group_perm' => 'group_perm.tpl',
+ 'double_select' => 'double_select.tpl'
+ )
+ );
+
+$template->assign(
+ array(
+ 'TITLE' =>
+ l10n(
+ 'Manage permissions for group "%s"',
+ get_groupname($page['group'])
+ ),
+ 'L_CAT_OPTIONS_TRUE'=>l10n('Authorized'),
+ 'L_CAT_OPTIONS_FALSE'=>l10n('Forbidden'),
+
+ 'F_ACTION' =>
+ get_root_url().
+ 'admin.php?page=group_perm&group_id='.
+ $page['group']
+ )
+ );
+
+// only private categories are listed
+$query_true = '
+SELECT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.' INNER JOIN '.GROUP_ACCESS_TABLE.' ON cat_id = id
+ WHERE status = \'private\'
+ AND group_id = '.$page['group'].'
+;';
+display_select_cat_wrapper($query_true,array(),'category_option_true');
+
+$result = pwg_query($query_true);
+$authorized_ids = array();
+while ($row = pwg_db_fetch_assoc($result))
+{
+ $authorized_ids[] = $row['id'];
+}
+
+$query_false = '
+SELECT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.'
+ WHERE status = \'private\'';
+if (count($authorized_ids) > 0)
+{
+ $query_false.= '
+ AND id NOT IN ('.implode(',', $authorized_ids).')';
+}
+$query_false.= '
+;';
+display_select_cat_wrapper($query_false,array(),'category_option_false');
+
+// +-----------------------------------------------------------------------+
+// | html code display |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('DOUBLE_SELECT', 'double_select');
+$template->assign_var_from_handle('ADMIN_CONTENT', 'group_perm');
+
+?>
diff --git a/sources/admin/help.php b/sources/admin/help.php
new file mode 100644
index 0000000..97d8d05
--- /dev/null
+++ b/sources/admin/help.php
@@ -0,0 +1,67 @@
+set_id('help');
+$tabsheet->select($selected);
+$tabsheet->assign();
+
+$template->set_filenames(array('help' => 'help.tpl'));
+
+$template->assign(
+ array(
+ 'HELP_CONTENT' => load_language(
+ 'help/help_'.$tabsheet->selected.'.html',
+ '',
+ array('return'=>true)
+ ),
+ 'HELP_SECTION_TITLE' => $tabsheet->sheets[ $tabsheet->selected ]['caption'],
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'help');
+?>
diff --git a/sources/admin/history.php b/sources/admin/history.php
new file mode 100644
index 0000000..7d52f80
--- /dev/null
+++ b/sources/admin/history.php
@@ -0,0 +1,675 @@
+ l10n('No display'),
+ 'display_thumbnail_classic' => l10n('Classic display'),
+ 'display_thumbnail_hoverbox' => l10n('Hoverbox display')
+ );
+
+// +-----------------------------------------------------------------------+
+// | Check Access and exit when user status is not ok |
+// +-----------------------------------------------------------------------+
+
+check_status(ACCESS_ADMINISTRATOR);
+
+// +-----------------------------------------------------------------------+
+// | Build search criteria and redirect to results |
+// +-----------------------------------------------------------------------+
+
+$page['errors'] = array();
+$search = array();
+
+if (isset($_POST['submit']))
+{
+ // dates
+ if (!empty($_POST['start_year']))
+ {
+ $search['fields']['date-after'] = sprintf(
+ '%d-%02d-%02d',
+ $_POST['start_year'],
+ $_POST['start_month'],
+ $_POST['start_day']
+ );
+ }
+
+ if (!empty($_POST['end_year']))
+ {
+ $search['fields']['date-before'] = sprintf(
+ '%d-%02d-%02d',
+ $_POST['end_year'],
+ $_POST['end_month'],
+ $_POST['end_day']
+ );
+ }
+
+ if (empty($_POST['types']))
+ {
+ $search['fields']['types'] = $types;
+ }
+ else
+ {
+ $search['fields']['types'] = $_POST['types'];
+ }
+
+ $search['fields']['user'] = $_POST['user'];
+
+ if (!empty($_POST['image_id']))
+ {
+ $search['fields']['image_id'] = intval($_POST['image_id']);
+ }
+
+ if (!empty($_POST['filename']))
+ {
+ $search['fields']['filename'] = str_replace(
+ '*',
+ '%',
+ pwg_db_real_escape_string($_POST['filename'])
+ );
+ }
+
+ if (!empty($_POST['ip']))
+ {
+ $search['fields']['ip'] = str_replace(
+ '*',
+ '%',
+ pwg_db_real_escape_string($_POST['ip'])
+ );
+ }
+
+ $search['fields']['display_thumbnail'] = $_POST['display_thumbnail'];
+ // Display choise are also save to one cookie
+ if (!empty($_POST['display_thumbnail'])
+ and isset($display_thumbnails[$_POST['display_thumbnail']]))
+ {
+ $cookie_val = $_POST['display_thumbnail'];
+ }
+ else
+ {
+ $cookie_val = null;
+ }
+
+ pwg_set_cookie_var('display_thumbnail', $cookie_val, strtotime('+1 month') );
+
+ // TODO manage inconsistency of having $_POST['image_id'] and
+ // $_POST['filename'] simultaneously
+
+ if (!empty($search))
+ {
+ // register search rules in database, then they will be available on
+ // thumbnails page and picture page.
+ $query ='
+INSERT INTO '.SEARCH_TABLE.'
+ (rules)
+ VALUES
+ (\''.serialize($search).'\')
+;';
+ pwg_query($query);
+
+ $search_id = pwg_db_insert_id(SEARCH_TABLE);
+
+ redirect(
+ PHPWG_ROOT_PATH.'admin.php?page=history&search_id='.$search_id
+ );
+ }
+ else
+ {
+ $page['errors'][] = l10n('Empty query. No criteria has been entered.');
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+
+$template->set_filename('history', 'history.tpl');
+
+// TabSheet initialization
+history_tabsheet();
+
+$template->assign(
+ array(
+ 'U_HELP' => get_root_url().'admin/popuphelp.php?page=history',
+ 'F_ACTION' => get_root_url().'admin.php?page=history'
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | history lines |
+// +-----------------------------------------------------------------------+
+
+if (isset($_GET['search_id'])
+ and $page['search_id'] = (int)$_GET['search_id'])
+{
+ // what are the lines to display in reality ?
+ $query = '
+SELECT rules
+ FROM '.SEARCH_TABLE.'
+ WHERE id = '.$page['search_id'].'
+;';
+ list($serialized_rules) = pwg_db_fetch_row(pwg_query($query));
+
+ $page['search'] = unserialize($serialized_rules);
+
+ if (isset($_GET['user_id']))
+ {
+ if (!is_numeric($_GET['user_id']))
+ {
+ die('user_id GET parameter must be an integer value');
+ }
+
+ $page['search']['fields']['user'] = $_GET['user_id'];
+
+ $query ='
+INSERT INTO '.SEARCH_TABLE.'
+ (rules)
+ VALUES
+ (\''.serialize($page['search']).'\')
+;';
+ pwg_query($query);
+
+ $search_id = pwg_db_insert_id(SEARCH_TABLE);
+
+ redirect(
+ PHPWG_ROOT_PATH.'admin.php?page=history&search_id='.$search_id
+ );
+ }
+
+ $data = trigger_event('get_history', array(), $page['search'], $types);
+ usort($data, 'history_compare');
+
+ $page['nb_lines'] = count($data);
+
+ $history_lines = array();
+ $user_ids = array();
+ $username_of = array();
+ $category_ids = array();
+ $image_ids = array();
+ $has_tags = false;
+
+ foreach ($data as $row)
+ {
+ $user_ids[$row['user_id']] = 1;
+
+ if (isset($row['category_id']))
+ {
+ $category_ids[$row['category_id']] = 1;
+ }
+
+ if (isset($row['image_id']))
+ {
+ $image_ids[$row['image_id']] = 1;
+ }
+
+ if (isset($row['tag_ids']))
+ {
+ $has_tags = true;
+ }
+
+ $history_lines[] = $row;
+ }
+
+ // prepare reference data (users, tags, categories...)
+ if (count($user_ids) > 0)
+ {
+ $query = '
+SELECT '.$conf['user_fields']['id'].' AS id
+ , '.$conf['user_fields']['username'].' AS username
+ FROM '.USERS_TABLE.'
+ WHERE id IN ('.implode(',', array_keys($user_ids)).')
+;';
+ $result = pwg_query($query);
+
+ $username_of = array();
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $username_of[$row['id']] = stripslashes($row['username']);
+ }
+ }
+
+ if (count($category_ids) > 0)
+ {
+ $query = '
+SELECT id, uppercats
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id IN ('.implode(',', array_keys($category_ids)).')
+;';
+ $uppercats_of = simple_hash_from_query($query, 'id', 'uppercats');
+
+ $name_of_category = array();
+
+ foreach ($uppercats_of as $category_id => $uppercats)
+ {
+ $name_of_category[$category_id] = get_cat_display_name_cache(
+ $uppercats
+ );
+ }
+ }
+
+ if (count($image_ids) > 0)
+ {
+ $query = '
+SELECT
+ id,
+ IF(name IS NULL, file, name) AS label,
+ filesize,
+ file,
+ path,
+ representative_ext
+ FROM '.IMAGES_TABLE.'
+ WHERE id IN ('.implode(',', array_keys($image_ids)).')
+;';
+ // $label_of_image = simple_hash_from_query($query, 'id', 'label');
+ $label_of_image = array();
+ $filesize_of_image = array();
+ $file_of_image = array();
+ $path_of_image = array();
+ $representative_ext_of_image = array();
+
+ $result = pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $label_of_image[ $row['id'] ] = trigger_event('render_element_description', $row['label']);
+
+ if (isset($row['filesize']))
+ {
+ $filesize_of_image[ $row['id'] ] = $row['filesize'];
+ }
+
+ $file_of_image[ $row['id'] ] = $row['file'];
+ $path_of_image[ $row['id'] ] = $row['path'];
+ $representative_ext_of_image[ $row['id'] ] = $row['representative_ext'];
+ }
+ }
+
+ if ($has_tags > 0)
+ {
+ $query = '
+SELECT
+ id,
+ name, url_name
+ FROM '.TAGS_TABLE;
+
+ global $name_of_tag; // used for preg_replace
+ $name_of_tag = array();
+ $result = pwg_query($query);
+ while ($row=pwg_db_fetch_assoc($result))
+ {
+ $name_of_tag[ $row['id'] ] = ''.trigger_event("render_tag_name", $row['name'], $row).' ';
+ }
+ }
+
+ $i = 0;
+ $first_line = $page['start'] + 1;
+ $last_line = $page['start'] + $conf['nb_logs_page'];
+
+ $summary['total_filesize'] = 0;
+ $summary['guests_IP'] = array();
+
+ foreach ($history_lines as $line)
+ {
+ if (isset($line['image_type']) and $line['image_type'] == 'high')
+ {
+ if (isset($filesize_of_image[$line['image_id']]))
+ {
+ $summary['total_filesize'] += $filesize_of_image[$line['image_id']];
+ }
+ }
+
+ if ($line['user_id'] == $conf['guest_id'])
+ {
+ if (!isset($summary['guests_IP'][ $line['IP'] ]))
+ {
+ $summary['guests_IP'][ $line['IP'] ] = 0;
+ }
+
+ $summary['guests_IP'][ $line['IP'] ]++;
+ }
+
+ $i++;
+
+ if ($i < $first_line or $i > $last_line)
+ {
+ continue;
+ }
+
+ $user_string = '';
+ if (isset($username_of[$line['user_id']]))
+ {
+ $user_string.= $username_of[$line['user_id']];
+ }
+ else
+ {
+ $user_string.= $line['user_id'];
+ }
+ $user_string.= ' + ';
+
+ $tags_string = '';
+ if (isset($line['tag_ids']))
+ {
+ $tags_string = preg_replace_callback(
+ '/(\d+)/',
+ create_function('$m', 'global $name_of_tag; return isset($name_of_tag[$m[1]]) ? $name_of_tag[$m[1]] : $m[1];'),
+ str_replace(
+ ',',
+ ', ',
+ $line['tag_ids']
+ )
+ );
+ }
+
+ $image_string = '';
+ if (isset($line['image_id']))
+ {
+ $picture_url = make_picture_url(
+ array(
+ 'image_id' => $line['image_id'],
+ )
+ );
+
+ if (isset($file_of_image[$line['image_id']]))
+ {
+ $element = array(
+ 'id' => $line['image_id'],
+ 'file' => $file_of_image[$line['image_id']],
+ 'path' => $path_of_image[$line['image_id']],
+ 'representative_ext' => $representative_ext_of_image[$line['image_id']],
+ );
+ $thumbnail_display = $page['search']['fields']['display_thumbnail'];
+ }
+ else
+ {
+ $thumbnail_display = 'no_display_thumbnail';
+ }
+
+ $image_title = '('.$line['image_id'].')';
+
+ if (isset($label_of_image[$line['image_id']]))
+ {
+ $image_title.= ' '.$label_of_image[$line['image_id']];
+ }
+ else
+ {
+ $image_title.= ' unknown filename';
+ }
+
+ $image_string = '';
+
+ switch ($thumbnail_display)
+ {
+ case 'no_display_thumbnail':
+ {
+ $image_string= ''.$image_title.' ';
+ break;
+ }
+ case 'display_thumbnail_classic':
+ {
+ $image_string =
+ ''
+ .' '
+ .' ';
+ break;
+ }
+ case 'display_thumbnail_hoverbox':
+ {
+ $image_string =
+ ''
+ .' '
+ .' '.$image_title.' ';
+ break;
+ }
+ }
+ }
+
+ $template->append(
+ 'search_results',
+ array(
+ 'DATE' => $line['date'],
+ 'TIME' => $line['time'],
+ 'USER' => $user_string,
+ 'IP' => $line['IP'],
+ 'IMAGE' => $image_string,
+ 'TYPE' => $line['image_type'],
+ 'SECTION' => $line['section'],
+ 'CATEGORY' => isset($line['category_id'])
+ ? ( isset($name_of_category[$line['category_id']])
+ ? $name_of_category[$line['category_id']]
+ : 'deleted '.$line['category_id'] )
+ : '',
+ 'TAGS' => $tags_string,
+ )
+ );
+ }
+
+ $summary['nb_guests'] = 0;
+ if (count(array_keys($summary['guests_IP'])) > 0)
+ {
+ $summary['nb_guests'] = count(array_keys($summary['guests_IP']));
+
+ // we delete the "guest" from the $username_of hash so that it is
+ // avoided in next steps
+ unset($username_of[ $conf['guest_id'] ]);
+ }
+
+ $summary['nb_members'] = count($username_of);
+
+ $member_strings = array();
+ foreach ($username_of as $user_id => $user_name)
+ {
+ $member_string = $user_name.' + ';
+
+ $member_strings[] = $member_string;
+ }
+
+ $template->assign(
+ 'search_summary',
+ array(
+ 'NB_LINES' => l10n_dec(
+ '%d line filtered', '%d lines filtered',
+ $page['nb_lines']
+ ),
+ 'FILESIZE' => $summary['total_filesize'] != 0 ? ceil($summary['total_filesize']/1024).' MB' : '',
+ 'USERS' => l10n_dec(
+ '%d user', '%d users',
+ $summary['nb_members'] + $summary['nb_guests']
+ ),
+ 'MEMBERS' => sprintf(
+ l10n_dec('%d member', '%d members', $summary['nb_members']).': %s',
+ implode(', ', $member_strings)
+ ),
+ 'GUESTS' => l10n_dec(
+ '%d guest', '%d guests',
+ $summary['nb_guests']
+ ),
+ )
+ );
+
+ unset($name_of_tag);
+}
+
+// +-----------------------------------------------------------------------+
+// | navigation bar |
+// +-----------------------------------------------------------------------+
+
+if (isset($page['search_id']))
+{
+ $navbar = create_navigation_bar(
+ get_root_url().'admin.php'.get_query_string_diff(array('start')),
+ $page['nb_lines'],
+ $page['start'],
+ $conf['nb_logs_page']
+ );
+
+ $template->assign('navbar', $navbar);
+}
+
+// +-----------------------------------------------------------------------+
+// | filter form |
+// +-----------------------------------------------------------------------+
+
+$form = array();
+
+if (isset($page['search']))
+{
+ if (isset($page['search']['fields']['date-after']))
+ {
+ $tokens = explode('-', $page['search']['fields']['date-after']);
+
+ $form['start_year'] = (int)$tokens[0];
+ $form['start_month'] = (int)$tokens[1];
+ $form['start_day'] = (int)$tokens[2];
+ }
+
+ if (isset($page['search']['fields']['date-before']))
+ {
+ $tokens = explode('-', $page['search']['fields']['date-before']);
+
+ $form['end_year'] = (int)$tokens[0];
+ $form['end_month'] = (int)$tokens[1];
+ $form['end_day'] = (int)$tokens[2];
+ }
+
+ $form['types'] = $page['search']['fields']['types'];
+
+ if (isset($page['search']['fields']['user']))
+ {
+ $form['user'] = $page['search']['fields']['user'];
+ }
+ else
+ {
+ $form['user'] = null;
+ }
+
+ $form['image_id'] = @$page['search']['fields']['image_id'];
+ $form['filename'] = @$page['search']['fields']['filename'];
+ $form['ip'] = @$page['search']['fields']['ip'];
+
+ $form['display_thumbnail'] = @$page['search']['fields']['display_thumbnail'];
+}
+else
+{
+ // by default, at page load, we want the selected date to be the current
+ // date
+ $form['start_year'] = $form['end_year'] = date('Y');
+ $form['start_month'] = $form['end_month'] = date('n');
+ $form['start_day'] = $form['end_day'] = date('j');
+ $form['types'] = $types;
+ // Hoverbox by default
+ $form['display_thumbnail'] =
+ pwg_get_cookie_var('display_thumbnail', 'no_display_thumbnail');
+}
+
+
+$month_list = $lang['month'];
+$month_list[0]='------------';
+ksort($month_list);
+
+$template->assign(
+ array(
+ 'IMAGE_ID' => @$form['image_id'],
+ 'FILENAME' => @$form['filename'],
+ 'IP' => @$form['ip'],
+
+ 'month_list' => $month_list,
+
+ 'START_DAY_SELECTED' => @$form['start_day'],
+ 'START_MONTH_SELECTED' => @$form['start_month'],
+ 'START_YEAR' => @$form['start_year'],
+
+ 'END_DAY_SELECTED' => @$form['end_day'],
+ 'END_MONTH_SELECTED' => @$form['end_month'],
+ 'END_YEAR' => @$form['end_year'],
+ )
+ );
+
+$template->assign(
+ array(
+ 'type_option_values' => $types,
+ 'type_option_selected' => $form['types']
+ )
+ );
+
+
+$query = '
+SELECT
+ '.$conf['user_fields']['id'].' AS id,
+ '.$conf['user_fields']['username'].' AS username
+ FROM '.USERS_TABLE.'
+ ORDER BY username ASC
+;';
+$template->assign(
+ array(
+ 'user_options' => simple_hash_from_query($query, 'id','username'),
+ 'user_options_selected' => array(@$form['user'])
+ )
+);
+
+$template->assign('display_thumbnails', $display_thumbnails);
+$template->assign('display_thumbnail_selected', $form['display_thumbnail']);
+
+// +-----------------------------------------------------------------------+
+// | html code display |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'history');
+?>
\ No newline at end of file
diff --git a/sources/admin/include/add_core_tabs.inc.php b/sources/admin/include/add_core_tabs.inc.php
new file mode 100644
index 0000000..efc9cd0
--- /dev/null
+++ b/sources/admin/include/add_core_tabs.inc.php
@@ -0,0 +1,155 @@
+ ' '.l10n('Properties'), 'url' => $admin_album_base_url.'-properties');
+ $sheets['sort_order'] = array('caption' => ' '.l10n('Manage photo ranks'), 'url' => $admin_album_base_url.'-sort_order');
+ $sheets['permissions'] = array('caption' => ' '.l10n('Permissions'), 'url' => $admin_album_base_url.'-permissions');
+ $sheets['notification'] = array('caption' => ' '.l10n('Notification'), 'url' => $admin_album_base_url.'-notification');
+ break;
+
+ case 'albums':
+ global $my_base_url;
+ $sheets['list'] = array('caption' => ''.l10n('List'), 'url' => $my_base_url.'cat_list');
+ $sheets['move'] = array('caption' => ' '.l10n('Move'), 'url' => $my_base_url.'cat_move');
+ $sheets['permalinks'] = array('caption' => ' '.l10n('Permalinks'), 'url' => $my_base_url.'permalinks');
+ break;
+
+ case 'batch_manager':
+ global $manager_link;
+ $sheets['global'] = array('caption' => l10n('global mode'), 'url' => $manager_link.'global');
+ $sheets['unit'] = array('caption' => l10n('unit mode'), 'url' => $manager_link.'unit');
+ break;
+
+ case 'cat_options':
+ global $link_start, $conf;
+ $sheets['status'] = array('caption' => ' '.l10n('Public / Private'), 'url' => $link_start.'cat_options§ion=status');
+ $sheets['visible'] = array('caption' => ' '.l10n('Lock'), 'url' => $link_start.'cat_options§ion=visible');
+ if ($conf['activate_comments'])
+ $sheets['comments'] = array('caption' => ' '.l10n('Comments'), 'url' => $link_start.'cat_options§ion=comments');
+ if ($conf['allow_random_representative'])
+ $sheets['representative'] = array('caption' => l10n('Representative'), 'url' => $link_start.'cat_options§ion=representative');
+ break;
+
+ case 'comments':
+ $sheets[''] = array('caption' => l10n('User comments'), 'url' => '');
+ break;
+
+ case 'users':
+ $sheets[''] = array('caption' => ' '.l10n('User list'), 'url' => '');
+ break;
+
+ case 'groups':
+ $sheets[''] = array('caption' => ' '.l10n('Groups'), 'url' => '');
+ break;
+
+ case 'configuration':
+ global $conf_link;
+ $sheets['main'] = array('caption' => l10n('General'), 'url' => $conf_link.'main');
+ $sheets['sizes'] = array('caption' => l10n('Photo sizes'), 'url' => $conf_link.'sizes');
+ $sheets['watermark'] = array('caption' => l10n('Watermark'), 'url' => $conf_link.'watermark');
+ $sheets['display'] = array('caption' => l10n('Display'), 'url' => $conf_link.'display');
+ $sheets['comments'] = array('caption' => l10n('Comments'), 'url' => $conf_link.'comments');
+ $sheets['default'] = array('caption' => l10n('Guest Settings'), 'url' => $conf_link.'default');
+ break;
+
+ case 'help':
+ global $help_link;
+ $sheets['add_photos'] = array('caption' => l10n('Add Photos'), 'url' => $help_link.'add_photos');
+ $sheets['permissions'] = array('caption' => l10n('Permissions'), 'url' => $help_link.'permissions');
+ $sheets['groups'] = array('caption' => l10n('Groups'), 'url' => $help_link.'groups');
+ $sheets['virtual_links'] = array('caption' => l10n('Virtual Links'), 'url' => $help_link.'virtual_links');
+ $sheets['misc'] = array('caption' => l10n('Miscellaneous'), 'url' => $help_link.'misc');
+ break;
+
+ case 'history':
+ global $link_start;
+ $sheets['stats'] = array('caption' => ' '.l10n('Statistics'), 'url' => $link_start.'stats');
+ $sheets['history'] = array('caption' => ' '.l10n('Search'), 'url' => $link_start.'history');
+ break;
+
+ case 'languages':
+ global $my_base_url;
+ $sheets['installed'] = array('caption' => ' '.l10n('Installed Languages'), 'url' => $my_base_url.'&tab=installed');
+ $sheets['update'] = array('caption' => ' '.l10n('Check for updates'), 'url' => $my_base_url.'&tab=update');
+ $sheets['new'] = array('caption' => ' '.l10n('Add New Language'), 'url' => $my_base_url.'&tab=new');
+ break;
+
+ case 'nbm':
+ global $base_url;
+ $sheets['param'] = array('caption' => l10n('Parameter'), 'url' => $base_url.'?page=notification_by_mail&mode=param');
+ $sheets['subscribe'] = array('caption' => l10n('Subscribe'), 'url' => $base_url.'?page=notification_by_mail&mode=subscribe');
+ $sheets['send'] = array('caption' => l10n('Send'), 'url' => $base_url.'?page=notification_by_mail&mode=send');
+ break;
+
+ case 'photo':
+ global $admin_photo_base_url;
+ $sheets['properties'] = array('caption' => l10n('Properties'), 'url' => $admin_photo_base_url.'-properties');
+ $sheets['coi'] = array('caption' => ' '.l10n('Center of interest'), 'url' => $admin_photo_base_url.'-coi');
+ break;
+
+ case 'photos_add':
+ global $conf;
+ $sheets['direct'] = array('caption' => ' '.l10n('Web Form'), 'url' => PHOTOS_ADD_BASE_URL.'§ion=direct');
+ $sheets['applications'] = array('caption' => ' '.l10n('Applications'), 'url' => PHOTOS_ADD_BASE_URL.'§ion=applications');
+ if ($conf['enable_synchronization'])
+ $sheets['ftp'] = array('caption' => ' '.l10n('FTP + Synchronization'), 'url' => PHOTOS_ADD_BASE_URL.'§ion=ftp');
+ break;
+
+ case 'plugins':
+ global $my_base_url;
+ $sheets['installed'] = array('caption' => ' '.l10n('Plugin list'), 'url' => $my_base_url.'&tab=installed');
+ $sheets['update'] = array('caption' => ' '.l10n('Check for updates'), 'url' => $my_base_url.'&tab=update');
+ $sheets['new'] = array('caption' => ' '.l10n('Other plugins'), 'url' => $my_base_url.'&tab=new');
+ break;
+
+ case 'rating':
+ $sheets['rating'] = array('caption' => l10n('Photos'), 'url' => get_root_url().'admin.php?page=rating');
+ $sheets['rating_user'] = array('caption' => l10n('Users'), 'url' => get_root_url().'admin.php?page=rating_user');
+ break;
+
+ case 'themes':
+ global $my_base_url;
+ $sheets['installed'] = array('caption' => ' '.l10n('Installed Themes'), 'url' => $my_base_url.'&tab=installed');
+ $sheets['update'] = array('caption' => ' '.l10n('Check for updates'), 'url' => $my_base_url.'&tab=update');
+ $sheets['new'] = array('caption' => ' '.l10n('Add New Theme'), 'url' => $my_base_url.'&tab=new');
+ break;
+
+ case 'updates':
+ global $my_base_url;
+ $sheets['pwg'] = array('caption' => l10n('Piwigo Update'), 'url' => $my_base_url);
+ $sheets['ext'] = array('caption' => l10n('Extensions Update'), 'url' => $my_base_url.'&tab=ext');
+ break;
+ }
+
+ return $sheets;
+}
+
+?>
\ No newline at end of file
diff --git a/sources/admin/include/albums_tab.inc.php b/sources/admin/include/albums_tab.inc.php
new file mode 100644
index 0000000..399778d
--- /dev/null
+++ b/sources/admin/include/albums_tab.inc.php
@@ -0,0 +1,33 @@
+set_id('albums');
+$tabsheet->select($page['tab']);
+$tabsheet->assign();
+
+?>
\ No newline at end of file
diff --git a/sources/admin/include/c13y_internal.class.php b/sources/admin/include/c13y_internal.class.php
new file mode 100644
index 0000000..1037509
--- /dev/null
+++ b/sources/admin/include/c13y_internal.class.php
@@ -0,0 +1,258 @@
+ 'PHP',
+ 'current' => phpversion(),
+ 'required' => REQUIRED_PHP_VERSION,
+ );
+
+ $check_list[] = array(
+ 'type' => 'MySQL',
+ 'current' => pwg_get_db_version(),
+ 'required' => REQUIRED_MYSQL_VERSION,
+ );
+
+ foreach ($check_list as $elem)
+ {
+ if (version_compare($elem['current'], $elem['required'], '<'))
+ {
+ $c13y->add_anomaly(
+ sprintf(l10n('The version of %s [%s] installed is not compatible with the version required [%s]'), $elem['type'], $elem['current'], $elem['required']),
+ null,
+ null,
+ l10n('You need to upgrade your system to take full advantage of the application else the application will not work correctly, or not at all')
+ .' '.
+ $c13y->get_htlm_links_more_info());
+ }
+ }
+ }
+
+ /**
+ * Check exif
+ *
+ * @param c13y object
+ * @return void
+ */
+ function c13y_exif($c13y)
+ {
+ global $conf;
+
+ foreach (array('show_exif', 'use_exif') as $value)
+ {
+ if (($conf[$value]) and (!function_exists('read_exif_data')))
+ {
+ $c13y->add_anomaly(
+ sprintf(l10n('%s value is not correct file because exif are not supported'), '$conf[\''.$value.'\']'),
+ null,
+ null,
+ sprintf(l10n('%s must be to set to false in your local/config/config.inc.php file'), '$conf[\''.$value.'\']')
+ .' '.
+ $c13y->get_htlm_links_more_info());
+ }
+ }
+ }
+
+ /**
+ * Check user
+ *
+ * @param c13y object
+ * @return void
+ */
+ function c13y_user($c13y)
+ {
+ global $conf;
+
+ $c13y_users = array();
+ $c13y_users[$conf['guest_id']] = array(
+ 'status' => 'guest',
+ 'l10n_non_existent' => 'Main "guest" user does not exist',
+ 'l10n_bad_status' => 'Main "guest" user status is incorrect');
+
+ if ($conf['guest_id'] != $conf['default_user_id'])
+ {
+ $c13y_users[$conf['default_user_id']] = array(
+ 'password' => null,
+ 'l10n_non_existent' => 'Default user does not exist');
+ }
+
+ $c13y_users[$conf['webmaster_id']] = array(
+ 'status' => 'webmaster',
+ 'l10n_non_existent' => 'Main "webmaster" user does not exist',
+ 'l10n_bad_status' => 'Main "webmaster" user status is incorrect');
+
+ $query = '
+ select u.'.$conf['user_fields']['id'].' as id, ui.status
+ from '.USERS_TABLE.' as u
+ left join '.USER_INFOS_TABLE.' as ui
+ on u.'.$conf['user_fields']['id'].' = ui.user_id
+ where
+ u.'.$conf['user_fields']['id'].' in ('.implode(',', array_keys($c13y_users)).')
+ ;';
+
+
+ $status = array();
+
+ $result = pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $status[$row['id']] = $row['status'];
+ }
+
+ foreach ($c13y_users as $id => $data)
+ {
+ if (!array_key_exists($id, $status))
+ {
+ $c13y->add_anomaly(l10n($data['l10n_non_existent']), 'c13y_correction_user',
+ array('id' => $id, 'action' => 'creation'));
+ }
+ else
+ if (!empty($data['status']) and $status[$id] != $data['status'])
+ {
+ $c13y->add_anomaly(l10n($data['l10n_bad_status']), 'c13y_correction_user',
+ array('id' => $id, 'action' => 'status'));
+ }
+ }
+ }
+
+ /**
+ * Do correction user
+ *
+ * @param user_id, action
+ * @return boolean true if ok else false
+ */
+ function c13y_correction_user($id, $action)
+ {
+ global $conf, $page;
+
+ $result = false;
+
+ if (!empty($id))
+ {
+ switch ($action)
+ {
+ case 'creation':
+ if ($id == $conf['guest_id'])
+ {
+ $name = 'guest';
+ $password = null;
+ }
+ else if ($id == $conf['default_user_id'])
+ {
+ $name = 'guest';
+ $password = null;
+ }
+ else if ($id == $conf['webmaster_id'])
+ {
+ $name = 'webmaster';
+ $password = generate_key(6);
+ }
+
+ if (isset($name))
+ {
+ $name_ok = false;
+ while (!$name_ok)
+ {
+ $name_ok = (get_userid($name) === false);
+ if (!$name_ok)
+ {
+ $name .= generate_key(1);
+ }
+ }
+
+ $inserts = array(
+ array(
+ 'id' => $id,
+ 'username' => addslashes($name),
+ 'password' => $password
+ ),
+ );
+ mass_inserts(USERS_TABLE, array_keys($inserts[0]), $inserts);
+
+ create_user_infos($id);
+
+ $page['infos'][] = sprintf(l10n('User "%s" created with "%s" like password'), $name, $password);
+
+ $result = true;
+ }
+ break;
+ case 'status':
+ if ($id == $conf['guest_id'])
+ {
+ $status = 'guest';
+ }
+ else if ($id == $conf['default_user_id'])
+ {
+ $status = 'guest';
+ }
+ else if ($id == $conf['webmaster_id'])
+ {
+ $status = 'webmaster';
+ }
+
+ if (isset($status))
+ {
+ $updates = array(
+ array(
+ 'user_id' => $id,
+ 'status' => $status
+ ),
+ );
+ mass_updates(USER_INFOS_TABLE,
+ array('primary' => array('user_id'),'update' => array('status')),
+ $updates);
+
+ $page['infos'][] = sprintf(l10n('Status of user "%s" updated'), get_username($id));
+
+ $result = true;
+ }
+ break;
+ }
+ }
+
+ return $result;
+ }
+}
+
+?>
diff --git a/sources/admin/include/check_integrity.class.php b/sources/admin/include/check_integrity.class.php
new file mode 100644
index 0000000..da549d7
--- /dev/null
+++ b/sources/admin/include/check_integrity.class.php
@@ -0,0 +1,346 @@
+ignore_list = array();
+ $this->retrieve_list = array();
+ $this->build_ignore_list = array();
+ }
+
+ /**
+ * Check integrities
+ *
+ * @param void
+ * @return void
+ */
+ function check()
+ {
+ global $page, $header_notes, $conf;
+
+ // Ignore list
+ $conf_c13y_ignore = unserialize($conf['c13y_ignore']);
+ if (
+ is_array($conf_c13y_ignore) and
+ isset($conf_c13y_ignore['version']) and
+ ($conf_c13y_ignore['version'] == PHPWG_VERSION) and
+ is_array($conf_c13y_ignore['list'])
+ )
+ {
+ $ignore_list_changed = false;
+ $this->ignore_list = $conf_c13y_ignore['list'];
+ }
+ else
+ {
+ $ignore_list_changed = true;
+ $this->ignore_list = array();
+ }
+
+ // Retrieve list
+ $this->retrieve_list = array();
+ $this->build_ignore_list = array();
+
+ trigger_action('list_check_integrity', $this);
+
+ // Information
+ if (count($this->retrieve_list) > 0)
+ {
+ $header_notes[] = l10n_dec(
+ '%d anomaly has been detected.', '%d anomalies have been detected.',
+ count($this->retrieve_list)
+ );
+ }
+
+ // Treatments
+ if (isset($_POST['c13y_submit_correction']) and isset($_POST['c13y_selection']))
+ {
+ $corrected_count = 0;
+ $not_corrected_count = 0;
+
+ foreach ($this->retrieve_list as $i => $c13y)
+ {
+ if (!empty($c13y['correction_fct']) and
+ $c13y['is_callable'] and
+ in_array($c13y['id'], $_POST['c13y_selection']))
+ {
+ if (is_array($c13y['correction_fct_args']))
+ {
+ $args = $c13y['correction_fct_args'];
+ }
+ else
+ if (!is_null($c13y['correction_fct_args']))
+ {
+ $args = array($c13y['correction_fct_args']);
+ }
+ else
+ {
+ $args = array();
+ }
+ $this->retrieve_list[$i]['corrected'] = call_user_func_array($c13y['correction_fct'], $args);
+
+ if ($this->retrieve_list[$i]['corrected'])
+ {
+ $corrected_count += 1;
+ }
+ else
+ {
+ $not_corrected_count += 1;
+ }
+ }
+ }
+
+ if ($corrected_count > 0)
+ {
+ $page['infos'][] = l10n_dec(
+ '%d anomaly has been corrected.', '%d anomalies have been detected corrected.',
+ $corrected_count
+ );
+ }
+ if ($not_corrected_count > 0)
+ {
+ $page['errors'][] = l10n_dec(
+ '%d anomaly has not been corrected.', '%d anomalies have not been corrected.',
+ $not_corrected_count
+ );
+ }
+ }
+ else
+ {
+ if (isset($_POST['c13y_submit_ignore']) and isset($_POST['c13y_selection']))
+ {
+ $ignored_count = 0;
+
+ foreach ($this->retrieve_list as $i => $c13y)
+ {
+ if (in_array($c13y['id'], $_POST['c13y_selection']))
+ {
+ $this->build_ignore_list[] = $c13y['id'];
+ $this->retrieve_list[$i]['ignored'] = true;
+ $ignored_count += 1;
+ }
+ }
+
+ if ($ignored_count > 0)
+ {
+ $page['infos'][] = l10n_dec(
+ '%d anomaly has been ignored.', '%d anomalies have been ignored.',
+ $ignored_count
+ );
+ }
+ }
+ }
+
+ $ignore_list_changed =
+ (
+ ($ignore_list_changed) or
+ (count(array_diff($this->ignore_list, $this->build_ignore_list)) > 0) or
+ (count(array_diff($this->build_ignore_list, $this->ignore_list)) > 0)
+ );
+
+ if ($ignore_list_changed)
+ {
+ $this->update_conf($this->build_ignore_list);
+ }
+ }
+
+ /**
+ * Display anomalies list
+ *
+ * @param void
+ * @return void
+ */
+ function display()
+ {
+ global $template;
+
+ $check_automatic_correction = false;
+ $submit_automatic_correction = false;
+ $submit_ignore = false;
+
+ if (isset($this->retrieve_list) and count($this->retrieve_list) > 0)
+ {
+ $template->set_filenames(array('check_integrity' => 'check_integrity.tpl'));
+
+ foreach ($this->retrieve_list as $i => $c13y)
+ {
+ $can_select = false;
+ $c13y_display = array(
+ 'id' => $c13y['id'],
+ 'anomaly' => $c13y['anomaly'],
+ 'show_ignore_msg' => false,
+ 'show_correction_success_fct' => false,
+ 'correction_error_fct' => '',
+ 'show_correction_fct' => false,
+ 'correction_error_fct' => '',
+ 'show_correction_bad_fct' => false,
+ 'correction_msg' => ''
+ );
+
+ if (isset($c13y['ignored']))
+ {
+ if ($c13y['ignored'])
+ {
+ $c13y_display['show_ignore_msg'] = true;
+ }
+ else
+ {
+ die('$c13y[\'ignored\'] cannot be false');
+ }
+ }
+ else
+ {
+ if (!empty($c13y['correction_fct']))
+ {
+ if (isset($c13y['corrected']))
+ {
+ if ($c13y['corrected'])
+ {
+ $c13y_display['show_correction_success_fct'] = true;
+ }
+ else
+ {
+ $c13y_display['correction_error_fct'] = $this->get_htlm_links_more_info();
+ }
+ }
+ else if ($c13y['is_callable'])
+ {
+ $c13y_display['show_correction_fct'] = true;
+ $template->append('c13y_do_check', $c13y['id']);
+ $submit_automatic_correction = true;
+ $can_select = true;
+ }
+ else
+ {
+ $c13y_display['show_correction_bad_fct'] = true;
+ $can_select = true;
+ }
+ }
+ else
+ {
+ $can_select = true;
+ }
+
+ if (!empty($c13y['correction_msg']) and !isset($c13y['corrected']))
+ {
+ $c13y_display['correction_msg'] = $c13y['correction_msg'];
+ }
+ }
+
+ $c13y_display['can_select'] = $can_select;
+ if ($can_select)
+ {
+ $submit_ignore = true;
+ }
+
+ $template->append('c13y_list', $c13y_display);
+ }
+
+ $template->assign('c13y_show_submit_automatic_correction', $submit_automatic_correction);
+ $template->assign('c13y_show_submit_ignore', $submit_ignore);
+
+ $template->concat('ADMIN_CONTENT', $template->parse('check_integrity', true));
+
+ }
+ }
+
+ /**
+ * Add anomaly data
+ *
+ * @param anomaly arguments
+ * @return void
+ */
+ function add_anomaly($anomaly, $correction_fct = null, $correction_fct_args = null, $correction_msg = null)
+ {
+ $id = md5($anomaly.$correction_fct.serialize($correction_fct_args).$correction_msg);
+
+ if (in_array($id, $this->ignore_list))
+ {
+ $this->build_ignore_list[] = $id;
+ }
+ else
+ {
+ $this->retrieve_list[] =
+ array(
+ 'id' => $id,
+ 'anomaly' => $anomaly,
+ 'correction_fct' => $correction_fct,
+ 'correction_fct_args' => $correction_fct_args,
+ 'correction_msg' => $correction_msg,
+ 'is_callable' => is_callable($correction_fct));
+ }
+ }
+
+ /**
+ * Update table config
+ *
+ * @param ignore list array
+ * @return void
+ */
+ function update_conf($conf_ignore_list = array())
+ {
+ $conf_c13y_ignore = array();
+ $conf_c13y_ignore['version'] = PHPWG_VERSION;
+ $conf_c13y_ignore['list'] = $conf_ignore_list;
+ $query = 'update '.CONFIG_TABLE.' set value =\''.serialize($conf_c13y_ignore).'\'where param = \'c13y_ignore\';';
+ pwg_query($query);
+ }
+
+ /**
+ * Apply maintenance
+ *
+ * @param void
+ * @return void
+ */
+ function maintenance()
+ {
+ $this->update_conf();
+ }
+
+ /**
+ * Returns links more informations
+ *
+ * @param void
+ * @return html links
+ */
+ function get_htlm_links_more_info()
+ {
+ $pwg_links = pwg_URL();
+ $link_fmt = '%s ';
+ return
+ sprintf
+ (
+ l10n('Go to %s or %s for more informations'),
+ sprintf($link_fmt, $pwg_links['FORUM'], l10n('the forum')),
+ sprintf($link_fmt, $pwg_links['WIKI'], l10n('the wiki'))
+ );
+ }
+
+}
+
+?>
diff --git a/sources/admin/include/configuration_sizes_process.inc.php b/sources/admin/include/configuration_sizes_process.inc.php
new file mode 100644
index 0000000..47cfaf3
--- /dev/null
+++ b/sources/admin/include/configuration_sizes_process.inc.php
@@ -0,0 +1,280 @@
+ 98)
+{
+ $errors['resize_quality'] = '[50..98]';
+}
+
+$pderivatives = $_POST['d'];
+
+// step 1 - sanitize HTML input
+foreach ($pderivatives as $type => &$pderivative)
+{
+ if ($pderivative['must_square'] = ($type==IMG_SQUARE ? true : false))
+ {
+ $pderivative['h'] = $pderivative['w'];
+ $pderivative['minh'] = $pderivative['minw'] = $pderivative['w'];
+ $pderivative['crop'] = 100;
+ }
+ $pderivative['must_enable'] = ($type==IMG_SQUARE || $type==IMG_THUMB)? true : false;
+ $pderivative['enabled'] = isset($pderivative['enabled']) || $pderivative['must_enable'] ? true : false;
+
+ if (isset($pderivative['crop']))
+ {
+ $pderivative['crop'] = 100;
+ $pderivative['minw'] = $pderivative['w'];
+ $pderivative['minh'] = $pderivative['h'];
+ }
+ else
+ {
+ $pderivative['crop'] = 0;
+ $pderivative['minw'] = null;
+ $pderivative['minh'] = null;
+ }
+}
+unset($pderivative);
+
+// step 2 - check validity
+$prev_w = $prev_h = 0;
+foreach(ImageStdParams::get_all_types() as $type)
+{
+ $pderivative = $pderivatives[$type];
+ if (!$pderivative['enabled'])
+ {
+ continue;
+ }
+
+ if ($type == IMG_THUMB)
+ {
+ $w = intval($pderivative['w']);
+ if ($w <= 0)
+ {
+ $errors[$type]['w'] = '>0';
+ }
+
+ $h = intval($pderivative['h']);
+ if ($h <= 0)
+ {
+ $errors[$type]['h'] = '>0';
+ }
+
+ if (max($w,$h) <= $prev_w)
+ {
+ $errors[$type]['w'] = $errors[$type]['h'] = '>'.$prev_w;
+ }
+ }
+ else
+ {
+ $v = intval($pderivative['w']);
+ if ($v <= 0 or $v <= $prev_w)
+ {
+ $errors[$type]['w'] = '>'.$prev_w;
+ }
+
+ $v = intval($pderivative['h']);
+ if ($v <= 0 or $v <= $prev_h)
+ {
+ $errors[$type]['h'] = '>'.$prev_h;
+ }
+ }
+
+ if (count($errors) == 0)
+ {
+ $prev_w = intval($pderivative['w']);
+ $prev_h = intval($pderivative['h']);
+ }
+
+ $v = intval($pderivative['sharpen']);
+ if ($v<0 || $v>100)
+ {
+ $errors[$type]['sharpen'] = '[0..100]';
+ }
+}
+
+// step 3 - save data
+if (count($errors) == 0)
+{
+ $quality_changed = ImageStdParams::$quality != intval($_POST['resize_quality']);
+ ImageStdParams::$quality = intval($_POST['resize_quality']);
+
+ $enabled = ImageStdParams::get_defined_type_map();
+ $disabled = @unserialize( @$conf['disabled_derivatives'] );
+ if ($disabled === false)
+ {
+ $disabled = array();
+ }
+ $changed_types = array();
+
+ foreach (ImageStdParams::get_all_types() as $type)
+ {
+ $pderivative = $pderivatives[$type];
+
+ if ($pderivative['enabled'])
+ {
+ $new_params = new DerivativeParams(
+ new SizingParams(
+ array(intval($pderivative['w']), intval($pderivative['h'])),
+ round($pderivative['crop'] / 100, 2),
+ array(intval($pderivative['minw']), intval($pderivative['minh']))
+ )
+ );
+ $new_params->sharpen = intval($pderivative['sharpen']);
+
+ ImageStdParams::apply_global($new_params);
+
+ if (isset($enabled[$type]))
+ {
+ $old_params = $enabled[$type];
+ $same = true;
+ if (!size_equals($old_params->sizing->ideal_size, $new_params->sizing->ideal_size)
+ or $old_params->sizing->max_crop != $new_params->sizing->max_crop)
+ {
+ $same = false;
+ }
+
+ if ($same
+ and $new_params->sizing->max_crop != 0
+ and !size_equals($old_params->sizing->min_size, $new_params->sizing->min_size))
+ {
+ $same = false;
+ }
+
+ if ($quality_changed
+ || $new_params->sharpen != $old_params->sharpen)
+ {
+ $same = false;
+ }
+
+ if (!$same)
+ {
+ $new_params->last_mod_time = time();
+ $changed_types[] = $type;
+ }
+ else
+ {
+ $new_params->last_mod_time = $old_params->last_mod_time;
+ }
+ $enabled[$type] = $new_params;
+ }
+ else
+ {// now enabled, before was disabled
+ $enabled[$type] = $new_params;
+ unset($disabled[$type]);
+ }
+ }
+ else
+ {// disabled
+ if (isset($enabled[$type]))
+ {// now disabled, before was enabled
+ $changed_types[] = $type;
+ $disabled[$type] = $enabled[$type];
+ unset($enabled[$type]);
+ }
+ }
+ }
+
+ $enabled_by = array(); // keys ordered by all types
+ foreach(ImageStdParams::get_all_types() as $type)
+ {
+ if (isset($enabled[$type]))
+ {
+ $enabled_by[$type] = $enabled[$type];
+ }
+ }
+
+ foreach( array_keys(ImageStdParams::$custom) as $custom)
+ {
+ if (isset($_POST['delete_custom_derivative_'.$custom]))
+ {
+ $changed_types[] = $custom;
+ unset(ImageStdParams::$custom[$custom]);
+ }
+ }
+
+ ImageStdParams::set_and_save($enabled_by);
+ if (count($disabled) == 0)
+ {
+ $query='DELETE FROM '.CONFIG_TABLE.' WHERE param = \'disabled_derivatives\'';
+ pwg_query($query);
+ }
+ else
+ {
+ conf_update_param('disabled_derivatives', addslashes(serialize($disabled)) );
+ }
+ $conf['disabled_derivatives'] = serialize($disabled);
+
+ if (count($changed_types))
+ {
+ clear_derivative_cache($changed_types);
+ }
+
+ $page['infos'][] = l10n('Your configuration settings are saved');
+}
+else
+{
+ foreach ($original_fields as $field)
+ {
+ if (isset($_POST[$field]))
+ {
+ $template->append(
+ 'sizes',
+ array(
+ $field => $_POST[$field]
+ ),
+ true
+ );
+ }
+ }
+
+ $template->assign('derivatives', $pderivatives);
+ $template->assign('ferrors', $errors);
+ $template->assign('resize_quality', $_POST['resize_quality']);
+ $page['sizes_loaded_in_tpl'] = true;
+}
+?>
\ No newline at end of file
diff --git a/sources/admin/include/configuration_watermark_process.inc.php b/sources/admin/include/configuration_watermark_process.inc.php
new file mode 100644
index 0000000..ad23d92
--- /dev/null
+++ b/sources/admin/include/configuration_watermark_process.inc.php
@@ -0,0 +1,184 @@
+ 100)
+{
+ $errors['watermark']['xpos'] = '[0..100]';
+}
+
+$v = intval($pwatermark['ypos']);
+if ($v < 0 or $v > 100)
+{
+ $errors['watermark']['ypos'] = '[0..100]';
+}
+
+$v = intval($pwatermark['opacity']);
+if ($v <= 0 or $v > 100)
+{
+ $errors['watermark']['opacity'] = '(0..100]';
+}
+
+// step 3 - save data
+if (count($errors) == 0)
+{
+ $watermark = new WatermarkParams();
+ $watermark->file = $pwatermark['file'];
+ $watermark->xpos = intval($pwatermark['xpos']);
+ $watermark->ypos = intval($pwatermark['ypos']);
+ $watermark->xrepeat = intval($pwatermark['xrepeat']);
+ $watermark->opacity = intval($pwatermark['opacity']);
+ $watermark->min_size = array(intval($pwatermark['minw']),intval($pwatermark['minh']));
+
+ $old_watermark = ImageStdParams::get_watermark();
+ $watermark_changed =
+ $watermark->file != $old_watermark->file
+ || $watermark->xpos != $old_watermark->xpos
+ || $watermark->ypos != $old_watermark->ypos
+ || $watermark->xrepeat != $old_watermark->xrepeat
+ || $watermark->opacity != $old_watermark->opacity;
+
+ // save the new watermark configuration
+ ImageStdParams::set_watermark($watermark);
+
+ // do we have to regenerate the derivatives (and which types)?
+ $changed_types = array();
+
+ foreach (ImageStdParams::get_defined_type_map() as $type => $params)
+ {
+ $old_use_watermark = $params->use_watermark;
+ ImageStdParams::apply_global($params);
+
+ $changed = $params->use_watermark != $old_use_watermark;
+ if (!$changed and $params->use_watermark)
+ {
+ $changed = $watermark_changed;
+ }
+ if (!$changed and $params->use_watermark)
+ {
+ // if thresholds change and before/after the threshold is lower than the corresponding derivative side -> some derivatives might switch the watermark
+ $changed |= $watermark->min_size[0]!=$old_watermark->min_size[0] and ($watermark->min_size[0]<$params->max_width() or $old_watermark->min_size[0]<$params->max_width());
+ $changed |= $watermark->min_size[1]!=$old_watermark->min_size[1] and ($watermark->min_size[1]<$params->max_height() or $old_watermark->min_size[1]<$params->max_height());
+ }
+
+ if ($changed)
+ {
+ $params->last_mod_time = time();
+ $changed_types[] = $type;
+ }
+ }
+
+ ImageStdParams::save();
+
+ if (count($changed_types))
+ {
+ clear_derivative_cache($changed_types);
+ }
+
+ $page['infos'][] = l10n('Your configuration settings are saved');
+}
+else
+{
+ $template->assign('watermark', $pwatermark);
+ $template->assign('ferrors', $errors);
+}
+?>
\ No newline at end of file
diff --git a/sources/admin/include/functions.php b/sources/admin/include/functions.php
new file mode 100644
index 0000000..ac59d2d
--- /dev/null
+++ b/sources/admin/include/functions.php
@@ -0,0 +1,2730 @@
+ 0)
+ {
+ if ('delete_orphans' == $photo_deletion_mode)
+ {
+ $query = '
+SELECT
+ DISTINCT(image_id)
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ WHERE image_id IN ('.implode(',', $image_ids_linked).')
+ AND category_id NOT IN ('.implode(',', $ids).')
+;';
+ $image_ids_not_orphans = array_from_query($query, 'image_id');
+ $image_ids_to_delete = array_diff($image_ids_linked, $image_ids_not_orphans);
+ }
+
+ if ('force_delete' == $photo_deletion_mode)
+ {
+ $image_ids_to_delete = $image_ids_linked;
+ }
+
+ delete_elements($image_ids_to_delete, true);
+ }
+ }
+
+ // destruction of the links between images and this category
+ $query = '
+DELETE FROM '.IMAGE_CATEGORY_TABLE.'
+ WHERE category_id IN (
+'.wordwrap(implode(', ', $ids), 80, "\n").')
+;';
+ pwg_query($query);
+
+ // destruction of the access linked to the category
+ $query = '
+DELETE FROM '.USER_ACCESS_TABLE.'
+ WHERE cat_id IN (
+'.wordwrap(implode(', ', $ids), 80, "\n").')
+;';
+ pwg_query($query);
+
+ $query = '
+DELETE FROM '.GROUP_ACCESS_TABLE.'
+ WHERE cat_id IN (
+'.wordwrap(implode(', ', $ids), 80, "\n").')
+;';
+ pwg_query($query);
+
+ // destruction of the category
+ $query = '
+DELETE FROM '.CATEGORIES_TABLE.'
+ WHERE id IN (
+'.wordwrap(implode(', ', $ids), 80, "\n").')
+;';
+ pwg_query($query);
+
+ $query='
+DELETE FROM '.OLD_PERMALINKS_TABLE.'
+ WHERE cat_id IN ('.implode(',',$ids).')';
+ pwg_query($query);
+
+ $query='
+DELETE FROM '.USER_CACHE_CATEGORIES_TABLE.'
+ WHERE cat_id IN ('.implode(',',$ids).')';
+ pwg_query($query);
+
+ trigger_action('delete_categories', $ids);
+}
+
+/**
+ * Deletes all files (on disk) related to given image ids.
+ *
+ * @param int[] $ids
+ * @return 0|int[] image ids where files were successfully deleted
+ */
+function delete_element_files($ids)
+{
+ global $conf;
+ if (count($ids) == 0)
+ {
+ return 0;
+ }
+
+ $new_ids = array();
+
+ $query = '
+SELECT
+ id,
+ path,
+ representative_ext
+ FROM '.IMAGES_TABLE.'
+ WHERE id IN ('.implode(',', $ids).')
+;';
+ $result = pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ if (url_is_remote($row['path']))
+ {
+ continue;
+ }
+
+ $files = array();
+ $files[] = get_element_path($row);
+
+ if (!empty($row['representative_ext']))
+ {
+ $files[] = original_to_representative( $files[0], $row['representative_ext']);
+ }
+
+ $ok = true;
+ if (!isset($conf['never_delete_originals']))
+ {
+ foreach ($files as $path)
+ {
+ if (is_file($path) and !unlink($path))
+ {
+ $ok = false;
+ trigger_error('"'.$path.'" cannot be removed', E_USER_WARNING);
+ break;
+ }
+ }
+ }
+
+ if ($ok)
+ {
+ delete_element_derivatives($row);
+ $new_ids[] = $row['id'];
+ }
+ else
+ {
+ break;
+ }
+ }
+ return $new_ids;
+}
+
+/**
+ * Deletes elements from database.
+ * It also deletes :
+ * - all the comments related to elements
+ * - all the links between categories/tags and elements
+ * - all the favorites/rates associated to elements
+ * - removes elements from caddie
+ *
+ * @param int[] $ids
+ * @param bool $physical_deletion
+ * @return int number of deleted elements
+ */
+function delete_elements($ids, $physical_deletion=false)
+{
+ if (count($ids) == 0)
+ {
+ return 0;
+ }
+ trigger_action('begin_delete_elements', $ids);
+
+ if ($physical_deletion)
+ {
+ $ids = delete_element_files($ids);
+ if (count($ids)==0)
+ {
+ return 0;
+ }
+ }
+
+ $ids_str = wordwrap(implode(', ', $ids), 80, "\n");
+
+ // destruction of the comments on the image
+ $query = '
+DELETE FROM '.COMMENTS_TABLE.'
+ WHERE image_id IN ('. $ids_str .')
+;';
+ pwg_query($query);
+
+ // destruction of the links between images and categories
+ $query = '
+DELETE FROM '.IMAGE_CATEGORY_TABLE.'
+ WHERE image_id IN ('. $ids_str .')
+;';
+ pwg_query($query);
+
+ // destruction of the links between images and tags
+ $query = '
+DELETE FROM '.IMAGE_TAG_TABLE.'
+ WHERE image_id IN ('. $ids_str .')
+;';
+ pwg_query($query);
+
+ // destruction of the favorites associated with the picture
+ $query = '
+DELETE FROM '.FAVORITES_TABLE.'
+ WHERE image_id IN ('. $ids_str .')
+;';
+ pwg_query($query);
+
+ // destruction of the rates associated to this element
+ $query = '
+DELETE FROM '.RATE_TABLE.'
+ WHERE element_id IN ('. $ids_str .')
+;';
+ pwg_query($query);
+
+ // destruction of the caddie associated to this element
+ $query = '
+DELETE FROM '.CADDIE_TABLE.'
+ WHERE element_id IN ('. $ids_str .')
+;';
+ pwg_query($query);
+
+ // destruction of the image
+ $query = '
+DELETE FROM '.IMAGES_TABLE.'
+ WHERE id IN ('. $ids_str .')
+;';
+ pwg_query($query);
+
+ // are the photo used as category representant?
+ $query = '
+SELECT
+ id
+ FROM '.CATEGORIES_TABLE.'
+ WHERE representative_picture_id IN ('. $ids_str .')
+;';
+ $category_ids = array_from_query($query, 'id');
+ if (count($category_ids) > 0)
+ {
+ update_category($category_ids);
+ }
+
+ trigger_action('delete_elements', $ids);
+ return count($ids);
+}
+
+/**
+ * Deletes an user.
+ * It also deletes all related data (accesses, favorites, permissions, etc.)
+ * @todo : accept array input
+ *
+ * @param int $user_id
+ */
+function delete_user($user_id)
+{
+ global $conf;
+ $tables = array(
+ // destruction of the access linked to the user
+ USER_ACCESS_TABLE,
+ // destruction of data notification by mail for this user
+ USER_MAIL_NOTIFICATION_TABLE,
+ // destruction of data RSS notification for this user
+ USER_FEED_TABLE,
+ // deletion of calculated permissions linked to the user
+ USER_CACHE_TABLE,
+ // deletion of computed cache data linked to the user
+ USER_CACHE_CATEGORIES_TABLE,
+ // destruction of the group links for this user
+ USER_GROUP_TABLE,
+ // destruction of the favorites associated with the user
+ FAVORITES_TABLE,
+ // destruction of the caddie associated with the user
+ CADDIE_TABLE,
+ // deletion of piwigo specific informations
+ USER_INFOS_TABLE,
+ );
+
+ foreach ($tables as $table)
+ {
+ $query = '
+DELETE FROM '.$table.'
+ WHERE user_id = '.$user_id.'
+;';
+ pwg_query($query);
+ }
+
+ // purge of sessions
+ $query = '
+DELETE FROM '.SESSIONS_TABLE.'
+ WHERE data LIKE \'pwg_uid|i:'.(int)$user_id.';%\'
+;';
+ pwg_query($query);
+
+ // destruction of the user
+ $query = '
+DELETE FROM '.USERS_TABLE.'
+ WHERE '.$conf['user_fields']['id'].' = '.$user_id.'
+;';
+ pwg_query($query);
+
+ trigger_action('delete_user', $user_id);
+}
+
+/**
+ * Deletes all tags linked to no photo
+ */
+function delete_orphan_tags()
+{
+ $orphan_tags = get_orphan_tags();
+
+ if (count($orphan_tags) > 0)
+ {
+ $orphan_tag_ids = array();
+ foreach ($orphan_tags as $tag)
+ {
+ $orphan_tag_ids[] = $tag['id'];
+ }
+
+ $query = '
+DELETE
+ FROM '.TAGS_TABLE.'
+ WHERE id IN ('.implode(',', $orphan_tag_ids).')
+;';
+ pwg_query($query);
+ }
+}
+
+/**
+ * Get all tags (id + name) linked to no photo
+ */
+function get_orphan_tags()
+{
+ $query = '
+SELECT
+ id,
+ name
+ FROM '.TAGS_TABLE.'
+ LEFT JOIN '.IMAGE_TAG_TABLE.' ON id = tag_id
+ WHERE tag_id IS NULL
+;';
+ return array_from_query($query);
+}
+
+/**
+ * Verifies that the representative picture really exists in the db and
+ * picks up a random representative if possible and based on config.
+ *
+ * @param 'all'|int|int[] $ids
+ */
+function update_category($ids = 'all')
+{
+ global $conf;
+
+ if ($ids=='all')
+ {
+ $where_cats = '1=1';
+ }
+ elseif ( !is_array($ids) )
+ {
+ $where_cats = '%s='.$ids;
+ }
+ else
+ {
+ if (count($ids) == 0)
+ {
+ return false;
+ }
+ $where_cats = '%s IN('.wordwrap(implode(', ', $ids), 120, "\n").')';
+ }
+
+ // find all categories where the setted representative is not possible :
+ // the picture does not exist
+ $query = '
+SELECT DISTINCT c.id
+ FROM '.CATEGORIES_TABLE.' AS c LEFT JOIN '.IMAGES_TABLE.' AS i
+ ON c.representative_picture_id = i.id
+ WHERE representative_picture_id IS NOT NULL
+ AND '.sprintf($where_cats, 'c.id').'
+ AND i.id IS NULL
+;';
+ $wrong_representant = array_from_query($query, 'id');
+
+ if (count($wrong_representant) > 0)
+ {
+ $query = '
+UPDATE '.CATEGORIES_TABLE.'
+ SET representative_picture_id = NULL
+ WHERE id IN ('.wordwrap(implode(', ', $wrong_representant), 120, "\n").')
+;';
+ pwg_query($query);
+ }
+
+ if (!$conf['allow_random_representative'])
+ {
+ // If the random representant is not allowed, we need to find
+ // categories with elements and with no representant. Those categories
+ // must be added to the list of categories to set to a random
+ // representant.
+ $query = '
+SELECT DISTINCT id
+ FROM '.CATEGORIES_TABLE.' INNER JOIN '.IMAGE_CATEGORY_TABLE.'
+ ON id = category_id
+ WHERE representative_picture_id IS NULL
+ AND '.sprintf($where_cats, 'category_id').'
+;';
+ $to_rand = array_from_query($query, 'id');
+ if (count($to_rand) > 0)
+ {
+ set_random_representant($to_rand);
+ }
+ }
+}
+
+/**
+ * Checks and repairs IMAGE_CATEGORY_TABLE integrity.
+ * Removes all entries from the table which correspond to a deleted image.
+ */
+function images_integrity()
+{
+ $query = '
+SELECT
+ image_id
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ LEFT JOIN '.IMAGES_TABLE.' ON id = image_id
+ WHERE id IS NULL
+;';
+ $result = pwg_query($query);
+ $orphan_image_ids = array_from_query($query, 'image_id');
+
+ if (count($orphan_image_ids) > 0)
+ {
+ $query = '
+DELETE
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ WHERE image_id IN ('.implode(',', $orphan_image_ids).')
+;';
+ pwg_query($query);
+ }
+}
+
+/**
+ * Returns an array containing sub-directories which are potentially
+ * a category.
+ * Directories named ".svn", "thumbnail", "pwg_high" or "pwg_representative"
+ * are omitted.
+ *
+ * @param string $basedir (eg: ./galleries)
+ * @return string[]
+ */
+function get_fs_directories($path, $recursive = true)
+{
+ $dirs = array();
+ $path = rtrim($path, '/');
+
+ if (is_dir($path))
+ {
+ if ($contents = opendir($path))
+ {
+ while (($node = readdir($contents)) !== false)
+ {
+ if ($node != '.'
+ and $node != '..'
+ and $node != '.svn'
+ and $node != 'thumbnail'
+ and $node != 'pwg_high'
+ and $node != 'pwg_representative'
+ and is_dir($path.'/'.$node))
+ {
+ $dirs[] = $path.'/'.$node;
+ if ($recursive)
+ {
+ $dirs = array_merge($dirs, get_fs_directories($path.'/'.$node));
+ }
+ }
+ }
+ closedir($contents);
+ }
+ }
+
+ return $dirs;
+}
+
+/**
+ * Orders categories (update categories.rank and global_rank database fields)
+ * so that rank field are consecutive integers starting at 1 for each child.
+ */
+function update_global_rank()
+{
+ $query = '
+SELECT id, id_uppercat, uppercats, rank, global_rank
+ FROM '.CATEGORIES_TABLE.'
+ ORDER BY id_uppercat,rank,name';
+
+ global $cat_map; // used in preg_replace callback
+ $cat_map = array();
+
+ $current_rank = 0;
+ $current_uppercat = '';
+
+ $result = pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ if ($row['id_uppercat'] != $current_uppercat)
+ {
+ $current_rank = 0;
+ $current_uppercat = $row['id_uppercat'];
+ }
+ ++$current_rank;
+ $cat =
+ array(
+ 'rank' => $current_rank,
+ 'rank_changed' =>$current_rank!=$row['rank'],
+ 'global_rank' => $row['global_rank'],
+ 'uppercats' => $row['uppercats'],
+ );
+ $cat_map[ $row['id'] ] = $cat;
+ }
+
+ $datas = array();
+
+ $cat_map_callback = create_function('$m', 'global $cat_map; return $cat_map[$m[1]]["rank"];');
+
+ foreach( $cat_map as $id=>$cat )
+ {
+ $new_global_rank = preg_replace_callback(
+ '/(\d+)/',
+ $cat_map_callback,
+ str_replace(',', '.', $cat['uppercats'] )
+ );
+
+ if ( $cat['rank_changed']
+ or $new_global_rank!=$cat['global_rank']
+ )
+ {
+ $datas[] = array(
+ 'id' => $id,
+ 'rank' => $cat['rank'],
+ 'global_rank' => $new_global_rank,
+ );
+ }
+ }
+
+ unset($cat_map);
+
+ mass_updates(
+ CATEGORIES_TABLE,
+ array(
+ 'primary' => array('id'),
+ 'update' => array('rank', 'global_rank')
+ ),
+ $datas
+ );
+ return count($datas);
+}
+
+/**
+ * Change the **visible** property on a set of categories.
+ *
+ * @param int[] $categories
+ * @param boolean|string $value
+ */
+function set_cat_visible($categories, $value)
+{
+ if ( ($value = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) === null )
+ {
+ trigger_error("set_cat_visible invalid param $value", E_USER_WARNING);
+ return false;
+ }
+
+ // unlocking a category => all its parent categories become unlocked
+ if ($value)
+ {
+ $uppercats = get_uppercat_ids($categories);
+ $query = '
+UPDATE '.CATEGORIES_TABLE.'
+ SET visible = \'true\'
+ WHERE id IN ('.implode(',', $uppercats).')';
+ pwg_query($query);
+ }
+ // locking a category => all its child categories become locked
+ else
+ {
+ $subcats = get_subcat_ids($categories);
+ $query = '
+UPDATE '.CATEGORIES_TABLE.'
+ SET visible = \'false\'
+ WHERE id IN ('.implode(',', $subcats).')';
+ pwg_query($query);
+ }
+}
+
+/**
+ * Change the **status** property on a set of categories : private or public.
+ *
+ * @param int[] $categories
+ * @param string $value
+ */
+function set_cat_status($categories, $value)
+{
+ if (!in_array($value, array('public', 'private')))
+ {
+ trigger_error("set_cat_status invalid param $value", E_USER_WARNING);
+ return false;
+ }
+
+ // make public a category => all its parent categories become public
+ if ($value == 'public')
+ {
+ $uppercats = get_uppercat_ids($categories);
+ $query = '
+UPDATE '.CATEGORIES_TABLE.'
+ SET status = \'public\'
+ WHERE id IN ('.implode(',', $uppercats).')
+;';
+ pwg_query($query);
+ }
+
+ // make a category private => all its child categories become private
+ if ($value == 'private')
+ {
+ $subcats = get_subcat_ids($categories);
+
+ $query = '
+UPDATE '.CATEGORIES_TABLE.'
+ SET status = \'private\'
+ WHERE id IN ('.implode(',', $subcats).')';
+ pwg_query($query);
+
+ // We have to keep permissions consistant: a sub-album can't be
+ // permitted to a user or group if its parent album is not permitted to
+ // the same user or group. Let's remove all permissions on sub-albums if
+ // it is not consistant. Let's take the following example:
+ //
+ // A1 permitted to U1,G1
+ // A1/A2 permitted to U1,U2,G1,G2
+ // A1/A2/A3 permitted to U3,G1
+ // A1/A2/A4 permitted to U2
+ // A1/A5 permitted to U4
+ // A6 permitted to U4
+ // A6/A7 permitted to G1
+ //
+ // (we consider that it can be possible to start with inconsistant
+ // permission, given that public albums can have hidden permissions,
+ // revealed once the album returns to private status)
+ //
+ // The admin selects A2,A3,A4,A5,A6,A7 to become private (all but A1,
+ // which is private, which can be true if we're moving A2 into A1). The
+ // result must be:
+ //
+ // A2 permission removed to U2,G2
+ // A3 permission removed to U3
+ // A4 permission removed to U2
+ // A5 permission removed to U2
+ // A6 permission removed to U4
+ // A7 no permission removed
+ //
+ // 1) we must extract "top albums": A2, A5 and A6
+ // 2) for each top album, decide which album is the reference for permissions
+ // 3) remove all inconsistant permissions from sub-albums of each top-album
+
+ // step 1, search top albums
+ $all_categories = array();
+ $top_categories = array();
+ $parent_ids = array();
+
+ $query = '
+SELECT
+ id,
+ name,
+ id_uppercat,
+ uppercats,
+ global_rank
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id IN ('.implode(',', $categories).')
+;';
+ $result = pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $all_categories[] = $row;
+ }
+
+ usort($all_categories, 'global_rank_compare');
+
+ foreach ($all_categories as $cat)
+ {
+ $is_top = true;
+
+ if (!empty($cat['id_uppercat']))
+ {
+ foreach (explode(',', $cat['uppercats']) as $id_uppercat)
+ {
+ if (isset($top_categories[$id_uppercat]))
+ {
+ $is_top = false;
+ break;
+ }
+ }
+ }
+
+ if ($is_top)
+ {
+ $top_categories[$cat['id']] = $cat;
+
+ if (!empty($cat['id_uppercat']))
+ {
+ $parent_ids[] = $cat['id_uppercat'];
+ }
+ }
+ }
+
+ // step 2, search the reference album for permissions
+ //
+ // to find the reference of each top album, we will need the parent albums
+ $parent_cats = array();
+
+ if (count($parent_ids) > 0)
+ {
+ $query = '
+SELECT
+ id,
+ status
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id IN ('.implode(',', $parent_ids).')
+;';
+ $result = pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $parent_cats[$row['id']] = $row;
+ }
+ }
+
+ $tables = array(
+ USER_ACCESS_TABLE => 'user_id',
+ GROUP_ACCESS_TABLE => 'group_id'
+ );
+
+ foreach ($top_categories as $top_category)
+ {
+ // what is the "reference" for list of permissions? The parent album
+ // if it is private, else the album itself
+ $ref_cat_id = $top_category['id'];
+
+ if (!empty($top_category['id_uppercat'])
+ and isset($parent_cats[ $top_category['id_uppercat'] ])
+ and 'private' == $parent_cats[ $top_category['id_uppercat'] ]['status'])
+ {
+ $ref_cat_id = $top_category['id_uppercat'];
+ }
+
+ $subcats = get_subcat_ids(array($top_category['id']));
+
+ foreach ($tables as $table => $field)
+ {
+ // what are the permissions user/group of the reference album
+ $query = '
+SELECT '.$field.'
+ FROM '.$table.'
+ WHERE cat_id = '.$ref_cat_id.'
+;';
+ $ref_access = array_from_query($query, $field);
+
+ if (count($ref_access) == 0)
+ {
+ $ref_access[] = -1;
+ }
+
+ // step 3, remove the inconsistant permissions from sub-albums
+ $query = '
+DELETE
+ FROM '.$table.'
+ WHERE '.$field.' NOT IN ('.implode(',', $ref_access).')
+ AND cat_id IN ('.implode(',', $subcats).')
+;';
+ pwg_query($query);
+ }
+ }
+ }
+}
+
+/**
+ * Returns all uppercats category ids of the given category ids.
+ *
+ * @param int[] $cat_ids
+ * @return int[]
+ */
+function get_uppercat_ids($cat_ids)
+{
+ if (!is_array($cat_ids) or count($cat_ids) < 1)
+ {
+ return array();
+ }
+
+ $uppercats = array();
+
+ $query = '
+SELECT uppercats
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id IN ('.implode(',', $cat_ids).')
+;';
+ $result = pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $uppercats = array_merge($uppercats,
+ explode(',', $row['uppercats']));
+ }
+ $uppercats = array_unique($uppercats);
+
+ return $uppercats;
+}
+
+/**
+ * Set a new random representant to the categories.
+ *
+ * @param int[] $categories
+ */
+function set_random_representant($categories)
+{
+ $datas = array();
+ foreach ($categories as $category_id)
+ {
+ $query = '
+SELECT image_id
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ WHERE category_id = '.$category_id.'
+ ORDER BY '.DB_RANDOM_FUNCTION.'()
+ LIMIT 1
+;';
+ list($representative) = pwg_db_fetch_row(pwg_query($query));
+
+ $datas[] = array(
+ 'id' => $category_id,
+ 'representative_picture_id' => $representative,
+ );
+ }
+
+ mass_updates(
+ CATEGORIES_TABLE,
+ array(
+ 'primary' => array('id'),
+ 'update' => array('representative_picture_id')
+ ),
+ $datas
+ );
+}
+
+/**
+ * Returns the fulldir for each given category id.
+ *
+ * @param int[] intcat_ids
+ * @return string[]
+ */
+function get_fulldirs($cat_ids)
+{
+ if (count($cat_ids) == 0)
+ {
+ return array();
+ }
+
+ // caching directories of existing categories
+ global $cat_dirs; // used in preg_replace callback
+ $query = '
+SELECT id, dir
+ FROM '.CATEGORIES_TABLE.'
+ WHERE dir IS NOT NULL
+;';
+ $cat_dirs = simple_hash_from_query($query, 'id', 'dir');
+
+ // caching galleries_url
+ $query = '
+SELECT id, galleries_url
+ FROM '.SITES_TABLE.'
+;';
+ $galleries_url = simple_hash_from_query($query, 'id', 'galleries_url');
+
+ // categories : id, site_id, uppercats
+ $query = '
+SELECT id, uppercats, site_id
+ FROM '.CATEGORIES_TABLE.'
+ WHERE dir IS NOT NULL
+ AND id IN (
+'.wordwrap(implode(', ', $cat_ids), 80, "\n").')
+;';
+ $categories = array_from_query($query);
+
+ // filling $cat_fulldirs
+ $cat_dirs_callback = create_function('$m', 'global $cat_dirs; return $cat_dirs[$m[1]];');
+
+ $cat_fulldirs = array();
+ foreach ($categories as $category)
+ {
+ $uppercats = str_replace(',', '/', $category['uppercats']);
+ $cat_fulldirs[$category['id']] = $galleries_url[$category['site_id']];
+ $cat_fulldirs[$category['id']].= preg_replace_callback(
+ '/(\d+)/',
+ $cat_dirs_callback,
+ $uppercats
+ );
+ }
+
+ unset($cat_dirs);
+
+ return $cat_fulldirs;
+}
+
+/**
+ * Returns an array with all file system files according to $conf['file_ext']
+ *
+ * @param string $path
+ * @param bool $recursive
+ * @return array
+ */
+function get_fs($path, $recursive = true)
+{
+ global $conf;
+
+ // because isset is faster than in_array...
+ if (!isset($conf['flip_picture_ext']))
+ {
+ $conf['flip_picture_ext'] = array_flip($conf['picture_ext']);
+ }
+ if (!isset($conf['flip_file_ext']))
+ {
+ $conf['flip_file_ext'] = array_flip($conf['file_ext']);
+ }
+
+ $fs['elements'] = array();
+ $fs['thumbnails'] = array();
+ $fs['representatives'] = array();
+ $subdirs = array();
+
+ if (is_dir($path))
+ {
+ if ($contents = opendir($path))
+ {
+ while (($node = readdir($contents)) !== false)
+ {
+ if ($node == '.' or $node == '..') continue;
+
+ if (is_file($path.'/'.$node))
+ {
+ $extension = get_extension($node);
+
+// if (in_array($extension, $conf['picture_ext']))
+ if (isset($conf['flip_picture_ext'][$extension]))
+ {
+ if (basename($path) == 'thumbnail')
+ {
+ $fs['thumbnails'][] = $path.'/'.$node;
+ }
+ else if (basename($path) == 'pwg_representative')
+ {
+ $fs['representatives'][] = $path.'/'.$node;
+ }
+ else
+ {
+ $fs['elements'][] = $path.'/'.$node;
+ }
+ }
+// else if (in_array($extension, $conf['file_ext']))
+ else if (isset($conf['flip_file_ext'][$extension]))
+ {
+ $fs['elements'][] = $path.'/'.$node;
+ }
+ }
+ else if (is_dir($path.'/'.$node) and $node != 'pwg_high' and $recursive)
+ {
+ $subdirs[] = $node;
+ }
+ }
+ }
+ closedir($contents);
+
+ foreach ($subdirs as $subdir)
+ {
+ $tmp_fs = get_fs($path.'/'.$subdir);
+
+ $fs['elements'] = array_merge($fs['elements'],
+ $tmp_fs['elements']);
+
+ $fs['thumbnails'] = array_merge($fs['thumbnails'],
+ $tmp_fs['thumbnails']);
+
+ $fs['representatives'] = array_merge($fs['representatives'],
+ $tmp_fs['representatives']);
+ }
+ }
+ return $fs;
+}
+
+/**
+ * Synchronize base users list and related users list.
+ *
+ * Compares and synchronizes base users table (USERS_TABLE) with its child
+ * tables (USER_INFOS_TABLE, USER_ACCESS, USER_CACHE, USER_GROUP) : each
+ * base user must be present in child tables, users in child tables not
+ * present in base table must be deleted.
+ */
+function sync_users()
+{
+ global $conf;
+
+ $query = '
+SELECT '.$conf['user_fields']['id'].' AS id
+ FROM '.USERS_TABLE.'
+;';
+ $base_users = array_from_query($query, 'id');
+
+ $query = '
+SELECT user_id
+ FROM '.USER_INFOS_TABLE.'
+;';
+ $infos_users = array_from_query($query, 'user_id');
+
+ // users present in $base_users and not in $infos_users must be added
+ $to_create = array_diff($base_users, $infos_users);
+
+ if (count($to_create) > 0)
+ {
+ create_user_infos($to_create);
+ }
+
+ // users present in user related tables must be present in the base user
+ // table
+ $tables = array(
+ USER_MAIL_NOTIFICATION_TABLE,
+ USER_FEED_TABLE,
+ USER_INFOS_TABLE,
+ USER_ACCESS_TABLE,
+ USER_CACHE_TABLE,
+ USER_CACHE_CATEGORIES_TABLE,
+ USER_GROUP_TABLE
+ );
+
+ foreach ($tables as $table)
+ {
+ $query = '
+SELECT DISTINCT user_id
+ FROM '.$table.'
+;';
+ $to_delete = array_diff(
+ array_from_query($query, 'user_id'),
+ $base_users
+ );
+
+ if (count($to_delete) > 0)
+ {
+ $query = '
+DELETE
+ FROM '.$table.'
+ WHERE user_id in ('.implode(',', $to_delete).')
+;';
+ pwg_query($query);
+ }
+ }
+}
+
+/**
+ * Updates categories.uppercats field based on categories.id + categories.id_uppercat
+ */
+function update_uppercats()
+{
+ $query = '
+SELECT id, id_uppercat, uppercats
+ FROM '.CATEGORIES_TABLE.'
+;';
+ $cat_map = hash_from_query($query, 'id');
+
+ $datas = array();
+ foreach ($cat_map as $id => $cat)
+ {
+ $upper_list = array();
+
+ $uppercat = $id;
+ while ($uppercat)
+ {
+ $upper_list[] = $uppercat;
+ $uppercat = $cat_map[$uppercat]['id_uppercat'];
+ }
+
+ $new_uppercats = implode(',', array_reverse($upper_list));
+ if ($new_uppercats != $cat['uppercats'])
+ {
+ $datas[] = array(
+ 'id' => $id,
+ 'uppercats' => $new_uppercats
+ );
+ }
+ }
+ $fields = array('primary' => array('id'), 'update' => array('uppercats'));
+ mass_updates(CATEGORIES_TABLE, $fields, $datas);
+}
+
+/**
+ * Update images.path field base on images.file and storage categories fulldirs.
+ */
+function update_path()
+{
+ $query = '
+SELECT DISTINCT(storage_category_id)
+ FROM '.IMAGES_TABLE.'
+ WHERE storage_category_id IS NOT NULL
+;';
+ $cat_ids = array_from_query($query, 'storage_category_id');
+ $fulldirs = get_fulldirs($cat_ids);
+
+ foreach ($cat_ids as $cat_id)
+ {
+ $query = '
+UPDATE '.IMAGES_TABLE.'
+ SET path = '.pwg_db_concat(array("'".$fulldirs[$cat_id]."/'",'file')).'
+ WHERE storage_category_id = '.$cat_id.'
+;';
+ pwg_query($query);
+ }
+}
+
+/**
+ * Change the parent category of the given categories. The categories are
+ * supposed virtual.
+ *
+ * @param int[] $category_ids
+ * @param int $new_parent (-1 for root)
+ */
+function move_categories($category_ids, $new_parent = -1)
+{
+ global $page;
+
+ if (count($category_ids) == 0)
+ {
+ return;
+ }
+
+ $new_parent = $new_parent < 1 ? 'NULL' : $new_parent;
+
+ $categories = array();
+
+ $query = '
+SELECT id, id_uppercat, status, uppercats
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id IN ('.implode(',', $category_ids).')
+;';
+ $result = pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $categories[$row['id']] =
+ array(
+ 'parent' => empty($row['id_uppercat']) ? 'NULL' : $row['id_uppercat'],
+ 'status' => $row['status'],
+ 'uppercats' => $row['uppercats']
+ );
+ }
+
+ // is the movement possible? The movement is impossible if you try to move
+ // a category in a sub-category or itself
+ if ('NULL' != $new_parent)
+ {
+ $query = '
+SELECT uppercats
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id = '.$new_parent.'
+;';
+ list($new_parent_uppercats) = pwg_db_fetch_row(pwg_query($query));
+
+ foreach ($categories as $category)
+ {
+ // technically, you can't move a category with uppercats 12,125,13,14
+ // into a new parent category with uppercats 12,125,13,14,24
+ if (preg_match('/^'.$category['uppercats'].'(,|$)/', $new_parent_uppercats))
+ {
+ $page['errors'][] = l10n('You cannot move an album in its own sub album');
+ return;
+ }
+ }
+ }
+
+ $tables = array(
+ USER_ACCESS_TABLE => 'user_id',
+ GROUP_ACCESS_TABLE => 'group_id'
+ );
+
+ $query = '
+UPDATE '.CATEGORIES_TABLE.'
+ SET id_uppercat = '.$new_parent.'
+ WHERE id IN ('.implode(',', $category_ids).')
+;';
+ pwg_query($query);
+
+ update_uppercats();
+ update_global_rank();
+
+ // status and related permissions management
+ if ('NULL' == $new_parent)
+ {
+ $parent_status = 'public';
+ }
+ else
+ {
+ $query = '
+SELECT status
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id = '.$new_parent.'
+;';
+ list($parent_status) = pwg_db_fetch_row(pwg_query($query));
+ }
+
+ if ('private' == $parent_status)
+ {
+ set_cat_status(array_keys($categories), 'private');
+ }
+
+ $page['infos'][] = l10n_dec(
+ '%d album moved', '%d albums moved',
+ count($categories)
+ );
+}
+
+/**
+ * Create a virtual category.
+ *
+ * @param string $category_name
+ * @param int $parent_id
+ * @param array $options
+ * - boolean commentable
+ * - boolean visible
+ * - string status
+ * - string comment
+ * - boolean inherit
+ * @return array ('info', 'id') or ('error')
+ */
+function create_virtual_category($category_name, $parent_id=null, $options=array())
+{
+ global $conf, $user;
+
+ // is the given category name only containing blank spaces ?
+ if (preg_match('/^\s*$/', $category_name))
+ {
+ return array('error' => l10n('The name of an album must not be empty'));
+ }
+
+ $insert = array(
+ 'name' => $category_name,
+ 'rank' => 0,
+ 'global_rank' => 0,
+ );
+
+ // is the album commentable?
+ if (isset($options['commentable']) and is_bool($options['commentable']))
+ {
+ $insert['commentable'] = $options['commentable'];
+ }
+ else
+ {
+ $insert['commentable'] = $conf['newcat_default_commentable'];
+ }
+ $insert['commentable'] = boolean_to_string($insert['commentable']);
+
+ // is the album temporarily locked? (only visible by administrators,
+ // whatever permissions) (may be overwritten if parent album is not
+ // visible)
+ if (isset($options['visible']) and is_bool($options['visible']))
+ {
+ $insert['visible'] = $options['visible'];
+ }
+ else
+ {
+ $insert['visible'] = $conf['newcat_default_visible'];
+ }
+ $insert['visible'] = boolean_to_string($insert['visible']);
+
+ // is the album private? (may be overwritten if parent album is private)
+ if (isset($options['status']) and 'private' == $options['status'])
+ {
+ $insert['status'] = 'private';
+ }
+ else
+ {
+ $insert['status'] = $conf['newcat_default_status'];
+ }
+
+ // any description for this album?
+ if (isset($options['comment']))
+ {
+ $insert['comment'] = $conf['allow_html_descriptions'] ? $options['comment'] : strip_tags($options['comment']);
+ }
+
+ if (!empty($parent_id) and is_numeric($parent_id))
+ {
+ $query = '
+SELECT id, uppercats, global_rank, visible, status
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id = '.$parent_id.'
+;';
+ $parent = pwg_db_fetch_assoc(pwg_query($query));
+
+ $insert['id_uppercat'] = $parent['id'];
+ $insert['global_rank'] = $parent['global_rank'].'.'.$insert['rank'];
+
+ // at creation, must a category be visible or not ? Warning : if the
+ // parent category is invisible, the category is automatically create
+ // invisible. (invisible = locked)
+ if ('false' == $parent['visible'])
+ {
+ $insert['visible'] = 'false';
+ }
+
+ // at creation, must a category be public or private ? Warning : if the
+ // parent category is private, the category is automatically create
+ // private.
+ if ('private' == $parent['status'])
+ {
+ $insert['status'] = 'private';
+ }
+
+ $uppercats_prefix = $parent['uppercats'].',';
+ }
+ else
+ {
+ $uppercats_prefix = '';
+ }
+
+ // we have then to add the virtual category
+ single_insert(CATEGORIES_TABLE, $insert);
+ $inserted_id = pwg_db_insert_id(CATEGORIES_TABLE);
+
+ single_update(
+ CATEGORIES_TABLE,
+ array('uppercats' => $uppercats_prefix.$inserted_id),
+ array('id' => $inserted_id)
+ );
+
+ update_global_rank();
+
+ if ('private' == $insert['status'] and !empty($insert['id_uppercat']) and ((isset($options['inherit']) and $options['inherit']) or $conf['inheritance_by_default']) )
+ {
+ $query = '
+ SELECT group_id
+ FROM '.GROUP_ACCESS_TABLE.'
+ WHERE cat_id = '.$insert['id_uppercat'].'
+ ;';
+ $granted_grps = array_from_query($query, 'group_id');
+ $inserts = array();
+ foreach ($granted_grps as $granted_grp)
+ {
+ $inserts[] = array(
+ 'group_id' => $granted_grp,
+ 'cat_id' => $inserted_id
+ );
+ }
+ mass_inserts(GROUP_ACCESS_TABLE, array('group_id','cat_id'), $inserts);
+
+ $query = '
+ SELECT user_id
+ FROM '.USER_ACCESS_TABLE.'
+ WHERE cat_id = '.$insert['id_uppercat'].'
+ ;';
+ $granted_users = array_from_query($query, 'user_id');
+ add_permission_on_category($inserted_id, array_unique(array_merge(get_admins(), array($user['id']), $granted_users)));
+ }
+ else if ('private' == $insert['status'])
+ {
+ add_permission_on_category($inserted_id, array_unique(array_merge(get_admins(), array($user['id']))));
+ }
+
+ return array(
+ 'info' => l10n('Virtual album added'),
+ 'id' => $inserted_id,
+ );
+}
+
+/**
+ * Set tags to an image.
+ * Warning: given tags are all tags associated to the image, not additionnal tags.
+ *
+ * @param int[] $tags
+ * @param int $image_id
+ */
+function set_tags($tags, $image_id)
+{
+ set_tags_of( array($image_id=>$tags) );
+}
+
+/**
+ * Add new tags to a set of images.
+ *
+ * @param int[] $tags
+ * @param int[] $images
+ */
+function add_tags($tags, $images)
+{
+ if (count($tags) == 0 or count($images) == 0)
+ {
+ return;
+ }
+
+ // we can't insert twice the same {image_id,tag_id} so we must first
+ // delete lines we'll insert later
+ $query = '
+DELETE
+ FROM '.IMAGE_TAG_TABLE.'
+ WHERE image_id IN ('.implode(',', $images).')
+ AND tag_id IN ('.implode(',', $tags).')
+;';
+ pwg_query($query);
+
+ $inserts = array();
+ foreach ($images as $image_id)
+ {
+ foreach ( array_unique($tags) as $tag_id)
+ {
+ $inserts[] = array(
+ 'image_id' => $image_id,
+ 'tag_id' => $tag_id,
+ );
+ }
+ }
+ mass_inserts(
+ IMAGE_TAG_TABLE,
+ array_keys($inserts[0]),
+ $inserts
+ );
+ invalidate_user_cache_nb_tags();
+}
+
+/**
+ * Delete tags and tags associations.
+ *
+ * @param int[] $tag_ids
+ */
+function delete_tags($tag_ids)
+{
+ if (is_numeric($tag_ids))
+ {
+ $tag_ids = array($tag_ids);
+ }
+
+ if (!is_array($tag_ids))
+ {
+ return false;
+ }
+
+ $query = '
+DELETE
+ FROM '.IMAGE_TAG_TABLE.'
+ WHERE tag_id IN ('.implode(',', $tag_ids).')
+;';
+ pwg_query($query);
+
+ $query = '
+DELETE
+ FROM '.TAGS_TABLE.'
+ WHERE id IN ('.implode(',', $tag_ids).')
+;';
+ pwg_query($query);
+
+ invalidate_user_cache_nb_tags();
+}
+
+/**
+ * Returns a tag id from its name. If nothing found, create a new tag.
+ *
+ * @param string $tag_name
+ * @return int
+ */
+function tag_id_from_tag_name($tag_name)
+{
+ global $page;
+
+ $tag_name = trim($tag_name);
+ if (isset($page['tag_id_from_tag_name_cache'][$tag_name]))
+ {
+ return $page['tag_id_from_tag_name_cache'][$tag_name];
+ }
+
+ // search existing by exact name
+ $query = '
+SELECT id
+ FROM '.TAGS_TABLE.'
+ WHERE name = \''.$tag_name.'\'
+;';
+ if (count($existing_tags = array_from_query($query, 'id')) == 0)
+ {
+ // search existing by case insensitive name
+ $query = '
+SELECT id
+ FROM '.TAGS_TABLE.'
+ WHERE CONVERT(name, CHAR) = \''.$tag_name.'\'
+;';
+ if (count($existing_tags = array_from_query($query, 'id')) == 0)
+ {
+ $url_name = trigger_event('render_tag_url', $tag_name);
+ // search existing by url name
+ $query = '
+SELECT id
+ FROM '.TAGS_TABLE.'
+ WHERE url_name = \''.$url_name.'\'
+;';
+ if (count($existing_tags = array_from_query($query, 'id')) == 0)
+ {
+ mass_inserts(
+ TAGS_TABLE,
+ array('name', 'url_name'),
+ array(
+ array(
+ 'name' => $tag_name,
+ 'url_name' => $url_name,
+ )
+ )
+ );
+
+ $page['tag_id_from_tag_name_cache'][$tag_name] = pwg_db_insert_id(TAGS_TABLE);
+
+ invalidate_user_cache_nb_tags();
+
+ return $page['tag_id_from_tag_name_cache'][$tag_name];
+ }
+ }
+ }
+
+ $page['tag_id_from_tag_name_cache'][$tag_name] = $existing_tags[0];
+ return $page['tag_id_from_tag_name_cache'][$tag_name];
+}
+
+/**
+ * Set tags of images. Overwrites all existing associations.
+ *
+ * @param array $tags_of - keys are image ids, values are array of tag ids
+ */
+function set_tags_of($tags_of)
+{
+ if (count($tags_of) > 0)
+ {
+ $query = '
+DELETE
+ FROM '.IMAGE_TAG_TABLE.'
+ WHERE image_id IN ('.implode(',', array_keys($tags_of)).')
+;';
+ pwg_query($query);
+
+ $inserts = array();
+
+ foreach ($tags_of as $image_id => $tag_ids)
+ {
+ foreach (array_unique($tag_ids) as $tag_id)
+ {
+ $inserts[] = array(
+ 'image_id' => $image_id,
+ 'tag_id' => $tag_id,
+ );
+ }
+ }
+
+ if (count($inserts))
+ {
+ mass_inserts(
+ IMAGE_TAG_TABLE,
+ array_keys($inserts[0]),
+ $inserts
+ );
+ }
+
+ invalidate_user_cache_nb_tags();
+ }
+}
+
+/**
+ * Associate a list of images to a list of categories.
+ * The function will not duplicate links and will preserve ranks.
+ *
+ * @param int[] $images
+ * @param int[] $categories
+ */
+function associate_images_to_categories($images, $categories)
+{
+ if (count($images) == 0
+ or count($categories) == 0)
+ {
+ return false;
+ }
+
+ // get existing associations
+ $query = '
+SELECT
+ image_id,
+ category_id
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ WHERE image_id IN ('.implode(',', $images).')
+ AND category_id IN ('.implode(',', $categories).')
+;';
+ $result = pwg_query($query);
+
+ $existing = array();
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $existing[ $row['category_id'] ][] = $row['image_id'];
+ }
+
+ // get max rank of each categories
+ $query = '
+SELECT
+ category_id,
+ MAX(rank) AS max_rank
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ WHERE rank IS NOT NULL
+ AND category_id IN ('.implode(',', $categories).')
+ GROUP BY category_id
+;';
+
+ $current_rank_of = simple_hash_from_query(
+ $query,
+ 'category_id',
+ 'max_rank'
+ );
+
+ // associate only not already associated images
+ $inserts = array();
+ foreach ($categories as $category_id)
+ {
+ if (!isset($current_rank_of[$category_id]))
+ {
+ $current_rank_of[$category_id] = 0;
+ }
+ if (!isset($existing[$category_id]))
+ {
+ $existing[$category_id] = array();
+ }
+
+ foreach ($images as $image_id)
+ {
+ if (!in_array($image_id, $existing[$category_id]))
+ {
+ $rank = ++$current_rank_of[$category_id];
+
+ $inserts[] = array(
+ 'image_id' => $image_id,
+ 'category_id' => $category_id,
+ 'rank' => $rank,
+ );
+ }
+ }
+ }
+
+ if (count($inserts))
+ {
+ mass_inserts(
+ IMAGE_CATEGORY_TABLE,
+ array_keys($inserts[0]),
+ $inserts
+ );
+
+ update_category($categories);
+ }
+}
+
+/**
+ * Dissociate images from all old categories except their storage category and
+ * associate to new categories.
+ * This function will preserve ranks.
+ *
+ * @param int[] $images
+ * @param int[] $categories
+ */
+function move_images_to_categories($images, $categories)
+{
+ if (count($images) == 0)
+ {
+ return false;
+ }
+
+ // let's first break links with all old albums but their "storage album"
+ $query = '
+DELETE '.IMAGE_CATEGORY_TABLE.'.*
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ JOIN '.IMAGES_TABLE.' ON image_id=id
+ WHERE id IN ('.implode(',', $images).')
+';
+
+ if (is_array($categories) and count($categories) > 0)
+ {
+ $query.= '
+ AND category_id NOT IN ('.implode(',', $categories).')
+';
+ }
+
+ $query.= '
+ AND (storage_category_id IS NULL OR storage_category_id != category_id)
+;';
+ pwg_query($query);
+
+ if (is_array($categories) and count($categories) > 0)
+ {
+ associate_images_to_categories($images, $categories);
+ }
+}
+
+/**
+ * Associate images associated to a list of source categories to a list of
+ * destination categories.
+ *
+ * @param int[] $sources
+ * @param int[] $destinations
+ */
+function associate_categories_to_categories($sources, $destinations)
+{
+ if (count($sources) == 0)
+ {
+ return false;
+ }
+
+ $query = '
+SELECT image_id
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ WHERE category_id IN ('.implode(',', $sources).')
+;';
+ $images = array_from_query($query, 'image_id');
+
+ associate_images_to_categories($images, $destinations);
+}
+
+/**
+ * Refer main Piwigo URLs (currently PHPWG_DOMAIN domain)
+ *
+ * @return string[]
+ */
+function pwg_URL()
+{
+ $urls = array(
+ 'HOME' => PHPWG_URL,
+ 'WIKI' => PHPWG_URL.'/doc',
+ 'DEMO' => PHPWG_URL.'/demo',
+ 'FORUM' => PHPWG_URL.'/forum',
+ 'BUGS' => PHPWG_URL.'/bugs',
+ 'EXTENSIONS' => PHPWG_URL.'/ext',
+ );
+ return $urls;
+}
+
+/**
+ * Invalidates cached data (permissions and category counts) for all users.
+ */
+function invalidate_user_cache($full = true)
+{
+ if ($full)
+ {
+ $query = '
+TRUNCATE TABLE '.USER_CACHE_CATEGORIES_TABLE.';';
+ pwg_query($query);
+ $query = '
+TRUNCATE TABLE '.USER_CACHE_TABLE.';';
+ pwg_query($query);
+ }
+ else
+ {
+ $query = '
+UPDATE '.USER_CACHE_TABLE.'
+ SET need_update = \'true\';';
+ pwg_query($query);
+ }
+ trigger_action('invalidate_user_cache', $full);
+}
+
+/**
+ * Invalidates cached tags counter for all users.
+ */
+function invalidate_user_cache_nb_tags()
+{
+ global $user;
+ unset($user['nb_available_tags']);
+
+ $query = '
+UPDATE '.USER_CACHE_TABLE.'
+ SET nb_available_tags = NULL';
+ pwg_query($query);
+}
+
+/**
+ * Adds the caracter set to a create table sql query.
+ * All CREATE TABLE queries must call this function
+ *
+ * @param string $query
+ * @return string
+ */
+function create_table_add_character_set($query)
+{
+ defined('DB_CHARSET') or fatal_error('create_table_add_character_set DB_CHARSET undefined');
+ if ('DB_CHARSET'!='')
+ {
+ if ( version_compare(pwg_get_db_version(), '4.1.0', '<') )
+ {
+ return $query;
+ }
+ $charset_collate = " DEFAULT CHARACTER SET ".DB_CHARSET;
+ if (DB_COLLATE!='')
+ {
+ $charset_collate .= " COLLATE ".DB_COLLATE;
+ }
+ if ( is_array($query) )
+ {
+ foreach( $query as $id=>$q)
+ {
+ $q=trim($q);
+ $q=trim($q, ';');
+ if (preg_match('/^CREATE\s+TABLE/i',$q))
+ {
+ $q.=$charset_collate;
+ }
+ $q .= ';';
+ $query[$id] = $q;
+ }
+ }
+ else
+ {
+ $query=trim($query);
+ $query=trim($query, ';');
+ if (preg_match('/^CREATE\s+TABLE/i',$query))
+ {
+ $query.=$charset_collate;
+ }
+ $query .= ';';
+ }
+ }
+ return $query;
+}
+
+/**
+ * Returns access levels as array used on template with html_options functions.
+ *
+ * @param int $MinLevelAccess
+ * @param int $MaxLevelAccess
+ * @return array
+ */
+function get_user_access_level_html_options($MinLevelAccess = ACCESS_FREE, $MaxLevelAccess = ACCESS_CLOSED)
+{
+ $tpl_options = array();
+ for ($level = $MinLevelAccess; $level <= $MaxLevelAccess; $level++)
+ {
+ $tpl_options[$level] = l10n(sprintf('ACCESS_%d', $level));
+ }
+ return $tpl_options;
+}
+
+/**
+ * returns a list of templates currently available in template-extension.
+ * Each .tpl file is extracted from template-extension.
+ *
+ * @param string $start (internal use)
+ * @return string[]
+ */
+function get_extents($start='')
+{
+ if ($start == '') { $start = './template-extension'; }
+ $dir = opendir($start);
+ $extents = array();
+
+ while (($file = readdir($dir)) !== false)
+ {
+ if ( $file == '.' or $file == '..' or $file == '.svn') continue;
+ $path = $start . '/' . $file;
+ if (is_dir($path))
+ {
+ $extents = array_merge($extents, get_extents($path));
+ }
+ elseif ( !is_link($path) and file_exists($path)
+ and get_extension($path) == 'tpl' )
+ {
+ $extents[] = substr($path, 21);
+ }
+ }
+ return $extents;
+}
+
+/**
+ * Create a new tag.
+ *
+ * @param string $tag_name
+ * @return array ('id', info') or ('error')
+ */
+function create_tag($tag_name)
+{
+ // does the tag already exists?
+ $query = '
+SELECT id
+ FROM '.TAGS_TABLE.'
+ WHERE name = \''.$tag_name.'\'
+;';
+ $existing_tags = array_from_query($query, 'id');
+
+ if (count($existing_tags) == 0)
+ {
+ single_insert(
+ TAGS_TABLE,
+ array(
+ 'name' => $tag_name,
+ 'url_name' => trigger_event('render_tag_url', $tag_name),
+ )
+ );
+
+ $inserted_id = pwg_db_insert_id(TAGS_TABLE);
+
+ return array(
+ 'info' => l10n('Tag "%s" was added', stripslashes($tag_name)),
+ 'id' => $inserted_id,
+ );
+ }
+ else
+ {
+ return array(
+ 'error' => l10n('Tag "%s" already exists', stripslashes($tag_name))
+ );
+ }
+}
+
+/**
+ * Is the category accessible to the (Admin) user ?
+ * Note : if the user is not authorized to see this category, category jump
+ * will be replaced by admin cat_modify page
+ *
+ * @param int $category_id
+ * @return bool
+ */
+function cat_admin_access($category_id)
+{
+ global $user;
+
+ // $filter['visible_categories'] and $filter['visible_images']
+ // are not used because it's not necessary (filter <> restriction)
+ if (in_array($category_id, explode(',', $user['forbidden_categories'])))
+ {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Retrieve data from external URL.
+ *
+ * @param string $src
+ * @param string|Ressource $dest - can be a file ressource or string
+ * @param array $get_data - data added to request url
+ * @param array $post_data - data transmitted with POST
+ * @param string $user_agent
+ * @param int $step (internal use)
+ * @return bool
+ */
+function fetchRemote($src, &$dest, $get_data=array(), $post_data=array(), $user_agent='Piwigo', $step=0)
+{
+ // Try to retrieve data from local file?
+ if (!url_is_remote($src))
+ {
+ $content = @file_get_contents($src);
+ if ($content !== false)
+ {
+ is_resource($dest) ? @fwrite($dest, $content) : $dest = $content;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ // After 3 redirections, return false
+ if ($step > 3) return false;
+
+ // Initialization
+ $method = empty($post_data) ? 'GET' : 'POST';
+ $request = empty($post_data) ? '' : http_build_query($post_data, '', '&');
+ if (!empty($get_data))
+ {
+ $src .= strpos($src, '?') === false ? '?' : '&';
+ $src .= http_build_query($get_data, '', '&');
+ }
+
+ // Initialize $dest
+ is_resource($dest) or $dest = '';
+
+ // Try curl to read remote file
+ if (function_exists('curl_init'))
+ {
+ $ch = @curl_init();
+ @curl_setopt($ch, CURLOPT_URL, $src);
+ @curl_setopt($ch, CURLOPT_HEADER, 1);
+ @curl_setopt($ch, CURLOPT_USERAGENT, $user_agent);
+ @curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ if ($method == 'POST')
+ {
+ @curl_setopt($ch, CURLOPT_POST, 1);
+ @curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
+ }
+ $content = @curl_exec($ch);
+ $header_length = @curl_getinfo($ch, CURLINFO_HEADER_SIZE);
+ $status = @curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ @curl_close($ch);
+ if ($content !== false and $status >= 200 and $status < 400)
+ {
+ if (preg_match('/Location:\s+?(.+)/', substr($content, 0, $header_length), $m))
+ {
+ return fetchRemote($m[1], $dest, array(), array(), $user_agent, $step+1);
+ }
+ $content = substr($content, $header_length);
+ is_resource($dest) ? @fwrite($dest, $content) : $dest = $content;
+ return true;
+ }
+ }
+
+ // Try file_get_contents to read remote file
+ if (ini_get('allow_url_fopen'))
+ {
+ $opts = array(
+ 'http' => array(
+ 'method' => $method,
+ 'user_agent' => $user_agent,
+ )
+ );
+ if ($method == 'POST')
+ {
+ $opts['http']['content'] = $request;
+ }
+ $context = @stream_context_create($opts);
+ $content = @file_get_contents($src, false, $context);
+ if ($content !== false)
+ {
+ is_resource($dest) ? @fwrite($dest, $content) : $dest = $content;
+ return true;
+ }
+ }
+
+ // Try fsockopen to read remote file
+ $src = parse_url($src);
+ $host = $src['host'];
+ $path = isset($src['path']) ? $src['path'] : '/';
+ $path .= isset($src['query']) ? '?'.$src['query'] : '';
+
+ if (($s = @fsockopen($host,80,$errno,$errstr,5)) === false)
+ {
+ return false;
+ }
+
+ $http_request = $method." ".$path." HTTP/1.0\r\n";
+ $http_request .= "Host: ".$host."\r\n";
+ if ($method == 'POST')
+ {
+ $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n";
+ $http_request .= "Content-Length: ".strlen($request)."\r\n";
+ }
+ $http_request .= "User-Agent: ".$user_agent."\r\n";
+ $http_request .= "Accept: */*\r\n";
+ $http_request .= "\r\n";
+ $http_request .= $request;
+
+ fwrite($s, $http_request);
+
+ $i = 0;
+ $in_content = false;
+ while (!feof($s))
+ {
+ $line = fgets($s);
+
+ if (rtrim($line,"\r\n") == '' && !$in_content)
+ {
+ $in_content = true;
+ $i++;
+ continue;
+ }
+ if ($i == 0)
+ {
+ if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/',rtrim($line,"\r\n"), $m))
+ {
+ fclose($s);
+ return false;
+ }
+ $status = (integer) $m[2];
+ if ($status < 200 || $status >= 400)
+ {
+ fclose($s);
+ return false;
+ }
+ }
+ if (!$in_content)
+ {
+ if (preg_match('/Location:\s+?(.+)$/',rtrim($line,"\r\n"),$m))
+ {
+ fclose($s);
+ return fetchRemote(trim($m[1]),$dest,array(),array(),$user_agent,$step+1);
+ }
+ $i++;
+ continue;
+ }
+ is_resource($dest) ? @fwrite($dest, $line) : $dest .= $line;
+ $i++;
+ }
+ fclose($s);
+ return true;
+}
+
+/**
+ * Returns the groupname corresponding to the given group identifier if exists.
+ *
+ * @param int $group_id
+ * @return string|false
+ */
+function get_groupname($group_id)
+{
+ $query = '
+SELECT name
+ FROM '.GROUPS_TABLE.'
+ WHERE id = '.intval($group_id).'
+;';
+ $result = pwg_query($query);
+ if (pwg_db_num_rows($result) > 0)
+ {
+ list($groupname) = pwg_db_fetch_row($result);
+ }
+ else
+ {
+ return false;
+ }
+
+ return $groupname;
+}
+
+/**
+ * Returns the username corresponding to the given user identifier if exists.
+ *
+ * @param int $user_id
+ * @return string|false
+ */
+function get_username($user_id)
+{
+ global $conf;
+
+ $query = '
+SELECT '.$conf['user_fields']['username'].'
+ FROM '.USERS_TABLE.'
+ WHERE '.$conf['user_fields']['id'].' = '.intval($user_id).'
+;';
+ $result = pwg_query($query);
+ if (pwg_db_num_rows($result) > 0)
+ {
+ list($username) = pwg_db_fetch_row($result);
+ }
+ else
+ {
+ return false;
+ }
+
+ return stripslashes($username);
+}
+
+/**
+ * Get url on piwigo.org for newsletter subscription
+ *
+ * @param string $language (unused)
+ * @return string
+ */
+function get_newsletter_subscribe_base_url($language='en_UK')
+{
+ return PHPWG_URL.'/announcement/subscribe/';
+}
+
+/**
+ * Return admin menu id for accordion.
+ *
+ * @param string $menu_page
+ * @return int
+ */
+function get_active_menu($menu_page)
+{
+ global $page;
+
+ if (isset($page['active_menu']))
+ {
+ return $page['active_menu'];
+ }
+
+ switch ($menu_page)
+ {
+ case 'photo':
+ case 'photos_add':
+ case 'rating':
+ case 'tags':
+ case 'batch_manager':
+ return 0;
+
+ case 'album':
+ case 'cat_list':
+ case 'cat_move':
+ case 'cat_options':
+ case 'permalinks':
+ return 1;
+
+ case 'user_list':
+ case 'user_perm':
+ case 'group_list':
+ case 'group_perm':
+ case 'notification_by_mail':
+ return 2;
+
+ case 'plugins':
+ case 'plugin':
+ return 3;
+
+ case 'site_manager':
+ case 'site_update':
+ case 'stats':
+ case 'history':
+ case 'maintenance':
+ case 'comments':
+ case 'updates':
+ return 4;
+
+ case 'configuration':
+ case 'derivatives':
+ case 'extend_for_templates':
+ case 'menubar':
+ case 'themes':
+ case 'theme':
+ case 'languages':
+ return 5;
+
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Get tags list from SQL query (ids are surrounded by ~~, for get_tag_ids()).
+ *
+ * @param string $query
+ * @param boolean $only_user_language - if true, only local name is returned for
+ * multilingual tags (if ExtendedDescription plugin is active)
+ * @return array[] ('id', 'name')
+ */
+function get_taglist($query, $only_user_language=true)
+{
+ $result = pwg_query($query);
+
+ $taglist = array();
+ $altlist = array();
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $raw_name = $row['name'];
+ $name = trigger_event('render_tag_name', $raw_name, $row);
+
+ $taglist[] = array(
+ 'name' => $name,
+ 'id' => '~~'.$row['id'].'~~',
+ );
+
+ if (!$only_user_language)
+ {
+ $alt_names = trigger_event('get_tag_alt_names', array(), $raw_name);
+
+ foreach( array_diff( array_unique($alt_names), array($name) ) as $alt)
+ {
+ $altlist[] = array(
+ 'name' => $alt,
+ 'id' => '~~'.$row['id'].'~~',
+ );
+ }
+ }
+ }
+
+ usort($taglist, 'tag_alpha_compare');
+ if (count($altlist))
+ {
+ usort($altlist, 'tag_alpha_compare');
+ $taglist = array_merge($taglist, $altlist);
+ }
+
+ return $taglist;
+}
+
+/**
+ * Get tags ids from a list of raw tags (existing tags or new tags).
+ *
+ * In $raw_tags we receive something like array('~~6~~', '~~59~~', 'New
+ * tag', 'Another new tag') The ~~34~~ means that it is an existing
+ * tag. We added the surrounding ~~ to permit creation of tags like "10"
+ * or "1234" (numeric characters only)
+ *
+ * @param string|string[] $raw_tags - array or comma separated string
+ * @param boolean $allow_create
+ * @return int[]
+ */
+function get_tag_ids($raw_tags, $allow_create=true)
+{
+ $tag_ids = array();
+ if (!is_array($raw_tags))
+ {
+ $raw_tags = explode(',',$raw_tags);
+ }
+
+ foreach ($raw_tags as $raw_tag)
+ {
+ if (preg_match('/^~~(\d+)~~$/', $raw_tag, $matches))
+ {
+ $tag_ids[] = $matches[1];
+ }
+ elseif ($allow_create)
+ {
+ // we have to create a new tag
+ $tag_ids[] = tag_id_from_tag_name($raw_tag);
+ }
+ }
+
+ return $tag_ids;
+}
+
+/**
+ * Returns the argument_ids array with new sequenced keys based on related
+ * names. Sequence is not case sensitive.
+ * Warning: By definition, this function breaks original keys.
+ *
+ * @param int[] $elements_ids
+ * @param string[] $name - names of elements, indexed by ids
+ * @return int[]
+ */
+function order_by_name($element_ids, $name)
+{
+ $ordered_element_ids = array();
+ foreach ($element_ids as $k_id => $element_id)
+ {
+ $key = strtolower($name[$element_id]) .'-'. $name[$element_id] .'-'. $k_id;
+ $ordered_element_ids[$key] = $element_id;
+ }
+ ksort($ordered_element_ids);
+ return $ordered_element_ids;
+}
+
+/**
+ * Grant access to a list of categories for a list of users.
+ *
+ * @param int[] $category_ids
+ * @param int[] $user_ids
+ */
+function add_permission_on_category($category_ids, $user_ids)
+{
+ if (!is_array($category_ids))
+ {
+ $category_ids = array($category_ids);
+ }
+ if (!is_array($user_ids))
+ {
+ $user_ids = array($user_ids);
+ }
+
+ // check for emptiness
+ if (count($category_ids) == 0 or count($user_ids) == 0)
+ {
+ return;
+ }
+
+ // make sure categories are private and select uppercats or subcats
+ $cat_ids = get_uppercat_ids($category_ids);
+ if (isset($_POST['apply_on_sub']))
+ {
+ $cat_ids = array_merge($cat_ids, get_subcat_ids($category_ids));
+ }
+
+ $query = '
+SELECT id
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id IN ('.implode(',', $cat_ids).')
+ AND status = \'private\'
+;';
+ $private_cats = array_from_query($query, 'id');
+
+ if (count($private_cats) == 0)
+ {
+ return;
+ }
+
+ $inserts = array();
+ foreach ($private_cats as $cat_id)
+ {
+ foreach ($user_ids as $user_id)
+ {
+ $inserts[] = array(
+ 'user_id' => $user_id,
+ 'cat_id' => $cat_id
+ );
+ }
+ }
+
+ mass_inserts(
+ USER_ACCESS_TABLE,
+ array('user_id','cat_id'),
+ $inserts,
+ array('ignore'=>true)
+ );
+}
+
+/**
+ * Returns the list of admin users.
+ *
+ * @param boolean $include_webmaster
+ * @return int[]
+ */
+function get_admins($include_webmaster=true)
+{
+ $status_list = array('admin');
+
+ if ($include_webmaster)
+ {
+ $status_list[] = 'webmaster';
+ }
+
+ $query = '
+SELECT
+ user_id
+ FROM '.USER_INFOS_TABLE.'
+ WHERE status in (\''.implode("','", $status_list).'\')
+;';
+
+ return array_from_query($query, 'user_id');
+}
+
+/**
+ * Delete all derivative files for one or several types
+ *
+ * @param 'all'|int[] $types
+ */
+function clear_derivative_cache($types='all')
+{
+ if ($types === 'all')
+ {
+ $types = ImageStdParams::get_all_types();
+ $types[] = IMG_CUSTOM;
+ }
+ elseif (!is_array($types))
+ {
+ $types = array($types);
+ }
+
+ for ($i=0; $i1)
+ {
+ $pattern .= '(' . implode('|',$types) . ')';
+ }
+ else
+ {
+ $pattern .= $types[0];
+ }
+ $pattern.='\.[a-zA-Z0-9]{3,4}$#';
+
+ if ($contents = @opendir(PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR))
+ {
+ while (($node = readdir($contents)) !== false)
+ {
+ if ($node != '.'
+ and $node != '..'
+ and is_dir(PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR.$node))
+ {
+ clear_derivative_cache_rec(PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR.$node, $pattern);
+ }
+ }
+ closedir($contents);
+ }
+}
+
+/**
+ * Used by clear_derivative_cache()
+ * @ignore
+ */
+function clear_derivative_cache_rec($path, $pattern)
+{
+ $rmdir = true;
+ $rm_index = false;
+
+ if ($contents = opendir($path))
+ {
+ while (($node = readdir($contents)) !== false)
+ {
+ if ($node == '.' or $node == '..')
+ continue;
+ if (is_dir($path.'/'.$node))
+ {
+ $rmdir &= clear_derivative_cache_rec($path.'/'.$node, $pattern);
+ }
+ else
+ {
+ if (preg_match($pattern, $node))
+ {
+ unlink($path.'/'.$node);
+ }
+ elseif ($node=='index.htm')
+ {
+ $rm_index = true;
+ }
+ else
+ {
+ $rmdir = false;
+ }
+ }
+ }
+ closedir($contents);
+
+ if ($rmdir)
+ {
+ if ($rm_index)
+ {
+ unlink($path.'/index.htm');
+ }
+ clearstatcache();
+ @rmdir($path);
+ }
+ return $rmdir;
+ }
+}
+
+/**
+ * Deletes derivatives of a particular element
+ *
+ * @param array $infos ('path'[, 'representative_ext'])
+ * @param 'all'|int $type
+ */
+function delete_element_derivatives($infos, $type='all')
+{
+ $path = $infos['path'];
+ if (!empty($infos['representative_ext']))
+ {
+ $path = original_to_representative( $path, $infos['representative_ext']);
+ }
+ if (substr_compare($path, '../', 0, 3)==0)
+ {
+ $path = substr($path, 3);
+ }
+ $dot = strrpos($path, '.');
+ if ($type=='all')
+ {
+ $pattern = '-*';
+ }
+ else
+ {
+ $pattern = '-'.derivative_to_url($type).'*';
+ }
+ $path = substr_replace($path, $pattern, $dot, 0);
+ if ( ($glob=glob(PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR.$path)) !== false)
+ {
+ foreach( $glob as $file)
+ {
+ @unlink($file);
+ }
+ }
+}
+
+/**
+ * Returns an array containing sub-directories, excluding ".svn"
+ *
+ * @param string $directory
+ * @return string[]
+ */
+function get_dirs($directory)
+{
+ $sub_dirs = array();
+ if ($opendir = opendir($directory))
+ {
+ while ($file = readdir($opendir))
+ {
+ if ($file != '.'
+ and $file != '..'
+ and is_dir($directory.'/'.$file)
+ and $file != '.svn')
+ {
+ $sub_dirs[] = $file;
+ }
+ }
+ closedir($opendir);
+ }
+ return $sub_dirs;
+}
+
+/**
+ * Recursively delete a directory.
+ *
+ * @param string $path
+ * @param string $trash_path, try to move the directory to this path if it cannot be delete
+ */
+function deltree($path, $trash_path=null)
+{
+ if (is_dir($path))
+ {
+ $fh = opendir($path);
+ while ($file = readdir($fh))
+ {
+ if ($file != '.' and $file != '..')
+ {
+ $pathfile = $path . '/' . $file;
+ if (is_dir($pathfile))
+ {
+ deltree($pathfile, $trash_path);
+ }
+ else
+ {
+ @unlink($pathfile);
+ }
+ }
+ }
+ closedir($fh);
+
+ if (@rmdir($path))
+ {
+ return true;
+ }
+ elseif (!empty($trash_path))
+ {
+ if (!is_dir($trash_path))
+ {
+ @mkgetdir($trash_path, MKGETDIR_RECURSIVE|MKGETDIR_DIE_ON_ERROR|MKGETDIR_PROTECT_HTACCESS);
+ }
+ while ($r = $trash_path . '/' . md5(uniqid(rand(), true)))
+ {
+ if (!is_dir($r))
+ {
+ @rename($path, $r);
+ break;
+ }
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/sources/admin/include/functions_history.inc.php b/sources/admin/include/functions_history.inc.php
new file mode 100644
index 0000000..61d6765
--- /dev/null
+++ b/sources/admin/include/functions_history.inc.php
@@ -0,0 +1,162 @@
+set_id('history');
+ $tabsheet->select($page['page']);
+ $tabsheet->assign();
+}
+
+function history_compare($a, $b)
+{
+ return strcmp($a['date'].$a['time'], $b['date'].$b['time']);
+}
+
+function get_history($data, $search, $types)
+{
+ if (isset($search['fields']['filename']))
+ {
+ $query = '
+SELECT
+ id
+ FROM '.IMAGES_TABLE.'
+ WHERE file LIKE \''.$search['fields']['filename'].'\'
+;';
+ $search['image_ids'] = array_from_query($query, 'id');
+ }
+
+ // echo ''; print_r($search); echo ' ';
+
+ $clauses = array();
+
+ if (isset($search['fields']['date-after']))
+ {
+ $clauses[] = "date >= '".$search['fields']['date-after']."'";
+ }
+
+ if (isset($search['fields']['date-before']))
+ {
+ $clauses[] = "date <= '".$search['fields']['date-before']."'";
+ }
+
+ if (isset($search['fields']['types']))
+ {
+ $local_clauses = array();
+
+ foreach ($types as $type) {
+ if (in_array($type, $search['fields']['types'])) {
+ $clause = 'image_type ';
+ if ($type == 'none')
+ {
+ $clause.= 'IS NULL';
+ }
+ else
+ {
+ $clause.= "= '".$type."'";
+ }
+
+ $local_clauses[] = $clause;
+ }
+ }
+
+ if (count($local_clauses) > 0)
+ {
+ $clauses[] = implode(' OR ', $local_clauses);
+ }
+ }
+
+ if (isset($search['fields']['user'])
+ and $search['fields']['user'] != -1)
+ {
+ $clauses[] = 'user_id = '.$search['fields']['user'];
+ }
+
+ if (isset($search['fields']['image_id']))
+ {
+ $clauses[] = 'image_id = '.$search['fields']['image_id'];
+ }
+
+ if (isset($search['fields']['filename']))
+ {
+ if (count($search['image_ids']) == 0)
+ {
+ // a clause that is always false
+ $clauses[] = '1 = 2 ';
+ }
+ else
+ {
+ $clauses[] = 'image_id IN ('.implode(', ', $search['image_ids']).')';
+ }
+ }
+
+ if (isset($search['fields']['ip']))
+ {
+ $clauses[] = 'IP LIKE "'.$search['fields']['ip'].'"';
+ }
+
+ $clauses = prepend_append_array_items($clauses, '(', ')');
+
+ $where_separator =
+ implode(
+ "\n AND ",
+ $clauses
+ );
+
+ $query = '
+SELECT
+ date,
+ time,
+ user_id,
+ IP,
+ section,
+ category_id,
+ tag_ids,
+ image_id,
+ image_type
+ FROM '.HISTORY_TABLE.'
+ WHERE '.$where_separator.'
+;';
+
+ // LIMIT '.$conf['nb_logs_page'].' OFFSET '.$page['start'].'
+
+ $result = pwg_query($query);
+
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $data[] = $row;
+ }
+
+ return $data;
+}
+
+add_event_handler('get_history', 'get_history', EVENT_HANDLER_PRIORITY_NEUTRAL, 3);
+trigger_action('functions_history_included');
+
+?>
\ No newline at end of file
diff --git a/sources/admin/include/functions_install.inc.php b/sources/admin/include/functions_install.inc.php
new file mode 100644
index 0000000..9e479f5
--- /dev/null
+++ b/sources/admin/include/functions_install.inc.php
@@ -0,0 +1,102 @@
+fs_themes as $theme_id => $fs_theme)
+ {
+ if (in_array($theme_id, array('elegant', 'smartpocket')))
+ {
+ $themes->perform_action('activate', $theme_id);
+ }
+ }
+}
+
+function install_db_connect(&$infos, &$errors)
+{
+ try
+ {
+ pwg_db_connect($_POST['dbhost'], $_POST['dbuser'],
+ $_POST['dbpasswd'], $_POST['dbname']);
+ pwg_db_check_version();
+ }
+ catch (Exception $e)
+ {
+ $errors[] = l10n($e->getMessage());
+ }
+}
+?>
\ No newline at end of file
diff --git a/sources/admin/include/functions_metadata.php b/sources/admin/include/functions_metadata.php
new file mode 100644
index 0000000..5bcbb12
--- /dev/null
+++ b/sources/admin/include/functions_metadata.php
@@ -0,0 +1,320 @@
+ $value)
+ {
+ if (in_array($pwg_key, array('date_creation', 'date_available')))
+ {
+ if (preg_match('/(\d{4})(\d{2})(\d{2})/', $value, $matches))
+ {
+ $year = $matches[1];
+ $month = $matches[2];
+ $day = $matches[3];
+
+ if (!checkdate($month, $day, $year))
+ {
+ // we suppose the year is correct
+ $month = 1;
+ $day = 1;
+ }
+
+ $iptc[$pwg_key] = $year.'-'.$month.'-'.$day;
+ }
+ }
+ }
+
+ if (isset($iptc['keywords']))
+ {
+ // official keywords separator is the comma
+ $iptc['keywords'] = preg_replace('/[.;]/', ',', $iptc['keywords']);
+ $iptc['keywords'] = preg_replace('/,+/', ',', $iptc['keywords']);
+ $iptc['keywords'] = preg_replace('/^,+|,+$/', '', $iptc['keywords']);
+
+ $iptc['keywords'] = implode(
+ ',',
+ array_unique(
+ explode(
+ ',',
+ $iptc['keywords']
+ )
+ )
+ );
+ }
+
+ foreach ($iptc as $pwg_key => $value)
+ {
+ $iptc[$pwg_key] = addslashes($iptc[$pwg_key]);
+ }
+
+ return $iptc;
+}
+
+function get_sync_exif_data($file)
+{
+ global $conf;
+
+ $exif = get_exif_data($file, $conf['use_exif_mapping']);
+
+ foreach ($exif as $pwg_key => $value)
+ {
+ if (in_array($pwg_key, array('date_creation', 'date_available')))
+ {
+ if (preg_match('/^(\d{4}).(\d{2}).(\d{2}) (\d{2}).(\d{2}).(\d{2})/', $value, $matches))
+ {
+ $exif[$pwg_key] = $matches[1].'-'.$matches[2].'-'.$matches[3].' '.$matches[4].':'.$matches[5].':'.$matches[6];
+ }
+ elseif (preg_match('/^(\d{4}).(\d{2}).(\d{2})/', $value, $matches))
+ {
+ $exif[$pwg_key] = $matches[1].'-'.$matches[2].'-'.$matches[3];
+ }
+ else
+ {
+ unset($exif[$pwg_key]);
+ continue;
+ }
+ }
+ $exif[$pwg_key] = addslashes($exif[$pwg_key]);
+ }
+
+ return $exif;
+}
+
+
+function get_sync_metadata_attributes()
+{
+ global $conf;
+
+ $update_fields = array('filesize', 'width', 'height');
+
+ if ($conf['use_exif'])
+ {
+ $update_fields =
+ array_merge(
+ $update_fields,
+ array_keys($conf['use_exif_mapping']),
+ array('latitude', 'longitude')
+ );
+ }
+
+ if ($conf['use_iptc'])
+ {
+ $update_fields =
+ array_merge(
+ $update_fields,
+ array_keys($conf['use_iptc_mapping'])
+ );
+ }
+
+ return array_unique($update_fields);
+}
+
+function get_sync_metadata($infos)
+{
+ global $conf;
+ $file = PHPWG_ROOT_PATH.$infos['path'];
+ $fs = @filesize($file);
+
+ if ($fs===false)
+ {
+ return false;
+ }
+
+ $infos['filesize'] = floor($fs/1024);
+
+ if (isset($infos['representative_ext']))
+ {
+ $file = original_to_representative($file, $infos['representative_ext']);
+ }
+
+ if ($image_size = @getimagesize($file))
+ {
+ $infos['width'] = $image_size[0];
+ $infos['height'] = $image_size[1];
+ }
+
+ if ($conf['use_exif'])
+ {
+ $exif = get_sync_exif_data($file);
+ $infos = array_merge($infos, $exif);
+ }
+
+ if ($conf['use_iptc'])
+ {
+ $iptc = get_sync_iptc_data($file);
+ $infos = array_merge($infos, $iptc);
+ }
+
+ return $infos;
+}
+
+
+function sync_metadata($ids)
+{
+ global $conf;
+
+ if (!defined('CURRENT_DATE'))
+ {
+ define('CURRENT_DATE', date('Y-m-d'));
+ }
+
+ $datas = array();
+ $tags_of = array();
+
+ $query = '
+SELECT id, path, representative_ext
+ FROM '.IMAGES_TABLE.'
+ WHERE id IN (
+'.wordwrap(implode(', ', $ids), 160, "\n").'
+)
+;';
+
+ $result = pwg_query($query);
+ while ($data = pwg_db_fetch_assoc($result))
+ {
+ $data = get_sync_metadata($data);
+ if ($data === false)
+ {
+ continue;
+ }
+
+ $id = $data['id'];
+ foreach (array('keywords', 'tags') as $key)
+ {
+ if (isset($data[$key]))
+ {
+ if (!isset($tags_of[$id]))
+ {
+ $tags_of[$id] = array();
+ }
+
+ foreach (explode(',', $data[$key]) as $tag_name)
+ {
+ $tags_of[$id][] = tag_id_from_tag_name($tag_name);
+ }
+ }
+ }
+
+ $data['date_metadata_update'] = CURRENT_DATE;
+
+ $datas[] = $data;
+ }
+
+ if (count($datas) > 0)
+ {
+ $update_fields = get_sync_metadata_attributes();
+ $update_fields[] = 'date_metadata_update';
+
+ $update_fields = array_diff(
+ $update_fields,
+ array('tags', 'keywords')
+ );
+
+ mass_updates(
+ IMAGES_TABLE,
+ array(
+ 'primary' => array('id'),
+ 'update' => $update_fields
+ ),
+ $datas,
+ MASS_UPDATES_SKIP_EMPTY
+ );
+ }
+
+ set_tags_of($tags_of);
+}
+
+/**
+ * returns an array associating element id (images.id) with its complete
+ * path in the filesystem
+ *
+ * @param int id_uppercat
+ * @param int site_id
+ * @param boolean recursive ?
+ * @param boolean only newly added files ?
+ * @return array
+ */
+function get_filelist($category_id = '', $site_id=1, $recursive = false,
+ $only_new = false)
+{
+ // filling $cat_ids : all categories required
+ $cat_ids = array();
+
+ $query = '
+SELECT id
+ FROM '.CATEGORIES_TABLE.'
+ WHERE site_id = '.$site_id.'
+ AND dir IS NOT NULL';
+ if (is_numeric($category_id))
+ {
+ if ($recursive)
+ {
+ $query.= '
+ AND uppercats '.DB_REGEX_OPERATOR.' \'(^|,)'.$category_id.'(,|$)\'
+';
+ }
+ else
+ {
+ $query.= '
+ AND id = '.$category_id.'
+';
+ }
+ }
+ $query.= '
+;';
+ $result = pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $cat_ids[] = $row['id'];
+ }
+
+ if (count($cat_ids) == 0)
+ {
+ return array();
+ }
+
+ $query = '
+SELECT id, path, representative_ext
+ FROM '.IMAGES_TABLE.'
+ WHERE storage_category_id IN ('.implode(',', $cat_ids).')';
+ if ($only_new)
+ {
+ $query.= '
+ AND date_metadata_update IS NULL
+';
+ }
+ $query.= '
+;';
+ return hash_from_query($query, 'id');
+}
+?>
\ No newline at end of file
diff --git a/sources/admin/include/functions_notification_by_mail.inc.php b/sources/admin/include/functions_notification_by_mail.inc.php
new file mode 100644
index 0000000..8d0fe26
--- /dev/null
+++ b/sources/admin/include/functions_notification_by_mail.inc.php
@@ -0,0 +1,546 @@
+ get_moment(),
+ 'sendmail_timeout' => (intval(ini_get('max_execution_time')) * $conf['nbm_max_treatment_timeout_percent']),
+ 'is_sendmail_timeout' => false
+ );
+
+if
+ (
+ (!isset($env_nbm['sendmail_timeout'])) or
+ (!is_numeric($env_nbm['sendmail_timeout'])) or
+ ($env_nbm['sendmail_timeout'] <= 0)
+ )
+{
+ $env_nbm['sendmail_timeout'] = $conf['nbm_treatment_timeout_default'];
+}
+
+/*
+ * Search an available check_key
+ *
+ * It's a copy of function find_available_feed_id
+ *
+ * @return string nbm identifier
+ */
+function find_available_check_key()
+{
+ while (true)
+ {
+ $key = generate_key(16);
+ $query = '
+select
+ count(*)
+from
+ '.USER_MAIL_NOTIFICATION_TABLE.'
+where
+ check_key = \''.$key.'\';';
+
+ list($count) = pwg_db_fetch_row(pwg_query($query));
+ if ($count == 0)
+ {
+ return $key;
+ }
+ }
+}
+
+/*
+ * Check sendmail timeout state
+ *
+ * @return true, if it's timeout
+ */
+function check_sendmail_timeout()
+{
+ global $env_nbm;
+
+ $env_nbm['is_sendmail_timeout'] = ((get_moment() - $env_nbm['start_time']) > $env_nbm['sendmail_timeout']);
+
+ return $env_nbm['is_sendmail_timeout'];
+}
+
+
+/*
+ * Add quote to all elements of check_key_list
+ *
+ * @return quoted check key list
+ */
+function quote_check_key_list($check_key_list = array())
+{
+ return array_map(create_function('$s', 'return \'\\\'\'.$s.\'\\\'\';'), $check_key_list);
+}
+
+/*
+ * Execute all main queries to get list of user
+ *
+ * Type are the type of list 'subscribe', 'send'
+ *
+ * return array of users
+ */
+function get_user_notifications($action, $check_key_list = array(), $enabled_filter_value = '')
+{
+ global $conf;
+
+ $data_users = array();
+
+ if (in_array($action, array('subscribe', 'send')))
+ {
+ $quoted_check_key_list = quote_check_key_list($check_key_list);
+ if (count($quoted_check_key_list) != 0 )
+ {
+ $query_and_check_key = ' and
+ check_key in ('.implode(",", $quoted_check_key_list).') ';
+ }
+ else
+ {
+ $query_and_check_key = '';
+ }
+
+ $query = '
+select
+ N.user_id,
+ N.check_key,
+ U.'.$conf['user_fields']['username'].' as username,
+ U.'.$conf['user_fields']['email'].' as mail_address,
+ N.enabled,
+ N.last_send
+from
+ '.USER_MAIL_NOTIFICATION_TABLE.' as N,
+ '.USERS_TABLE.' as U
+where
+ N.user_id = U.'.$conf['user_fields']['id'];
+
+ if ($action == 'send')
+ {
+ // No mail empty and all users enabled
+ $query .= ' and
+ N.enabled = \'true\' and
+ U.'.$conf['user_fields']['email'].' is not null';
+ }
+
+ $query .= $query_and_check_key;
+
+ if (isset($enabled_filter_value) and ($enabled_filter_value != ''))
+ {
+ $query .= ' and
+ N.enabled = \''.boolean_to_string($enabled_filter_value).'\'';
+ }
+
+ $query .= '
+order by';
+
+ if ($action == 'send')
+ {
+ $query .= '
+ last_send, username;';
+ }
+ else
+ {
+ $query .= '
+ username;';
+ }
+
+ $query .= ';';
+
+ $result = pwg_query($query);
+ if (!empty($result))
+ {
+ while ($nbm_user = pwg_db_fetch_assoc($result))
+ {
+ $data_users[] = $nbm_user;
+ }
+ }
+ }
+ return $data_users;
+}
+
+/*
+ * Begin of use nbm environment
+ * Prepare and save current environment and initialize data in order to send mail
+ *
+ * Return none
+ */
+function begin_users_env_nbm($is_to_send_mail = false)
+{
+ global $user, $lang, $lang_info, $conf, $env_nbm;
+
+ // Save $user, $lang_info and $lang arrays (include/user.inc.php has been executed)
+ $env_nbm['save_user'] = $user;
+ // Save current language to stack, necessary because $user change during NBM
+ switch_lang_to($user['language']);
+
+ $env_nbm['is_to_send_mail'] = $is_to_send_mail;
+
+ if ($is_to_send_mail)
+ {
+ // Init mail configuration
+ $env_nbm['email_format'] = get_str_email_format($conf['nbm_send_html_mail']);
+ $env_nbm['send_as_name'] = ((isset($conf['nbm_send_mail_as']) and !empty($conf['nbm_send_mail_as'])) ? $conf['nbm_send_mail_as'] : get_mail_sender_name());
+ $env_nbm['send_as_mail_address'] = get_webmaster_mail_address();
+ $env_nbm['send_as_mail_formated'] = format_email($env_nbm['send_as_name'], $env_nbm['send_as_mail_address']);
+ // Init mail counter
+ $env_nbm['error_on_mail_count'] = 0;
+ $env_nbm['sent_mail_count'] = 0;
+ // Save sendmail message info and error in the original language
+ $env_nbm['msg_info'] = l10n('Mail sent to %s [%s].');
+ $env_nbm['msg_error'] = l10n('Error when sending email to %s [%s].');
+ }
+}
+
+/*
+ * End of use nbm environment
+ * Restore environment
+ *
+ * Return none
+ */
+function end_users_env_nbm()
+{
+ global $user, $lang, $lang_info, $env_nbm;
+
+ // Restore $user, $lang_info and $lang arrays (include/user.inc.php has been executed)
+ $user = $env_nbm['save_user'];
+ // Restore current language to stack, necessary because $user change during NBM
+ switch_lang_back();
+
+ if ($env_nbm['is_to_send_mail'])
+ {
+ unset($env_nbm['email_format']);
+ unset($env_nbm['send_as_name']);
+ unset($env_nbm['send_as_mail_address']);
+ unset($env_nbm['send_as_mail_formated']);
+ // Don t unset counter
+ //unset($env_nbm['error_on_mail_count']);
+ //unset($env_nbm['sent_mail_count']);
+ unset($env_nbm['msg_info']);
+ unset($env_nbm['msg_error']);
+ }
+
+ unset($env_nbm['save_user']);
+ unset($env_nbm['is_to_send_mail']);
+}
+
+/*
+ * Set user on nbm enviromnent
+ *
+ * Return none
+ */
+function set_user_on_env_nbm(&$nbm_user, $is_action_send)
+{
+ global $user, $lang, $lang_info, $env_nbm;
+
+ $user = build_user( $nbm_user['user_id'], true );
+
+ switch_lang_to($user['language']);
+
+ if ($is_action_send)
+ {
+ $env_nbm['mail_template'] = get_mail_template($env_nbm['email_format']);
+ $env_nbm['mail_template']->set_filename('notification_by_mail', 'notification_by_mail.tpl');
+ }
+}
+
+/*
+ * Unset user on nbm enviromnent
+ *
+ * Return none
+ */
+function unset_user_on_env_nbm()
+{
+ global $env_nbm;
+
+ switch_lang_back();
+ unset($env_nbm['mail_template']);
+}
+
+/*
+ * Inc Counter success
+ *
+ * Return none
+ */
+function inc_mail_sent_success($nbm_user)
+{
+ global $page, $env_nbm;
+
+ $env_nbm['sent_mail_count'] += 1;
+ $page['infos'][] = sprintf($env_nbm['msg_info'], stripslashes($nbm_user['username']), $nbm_user['mail_address']);
+}
+
+/*
+ * Inc Counter failed
+ *
+ * Return none
+ */
+function inc_mail_sent_failed($nbm_user)
+{
+ global $page, $env_nbm;
+
+ $env_nbm['error_on_mail_count'] += 1;
+ $page['errors'][] = sprintf($env_nbm['msg_error'], stripslashes($nbm_user['username']), $nbm_user['mail_address']);
+}
+
+/*
+ * Display Counter Info
+ *
+ * Return none
+ */
+function display_counter_info()
+{
+ global $page, $env_nbm;
+
+ if ($env_nbm['error_on_mail_count'] != 0)
+ {
+ $page['errors'][] = l10n_dec(
+ '%d mail was not sent.', '%d mails were not sent.',
+ $env_nbm['error_on_mail_count']
+ );
+
+ if ($env_nbm['sent_mail_count'] != 0)
+ {
+ $page['infos'][] = l10n_dec(
+ '%d mail was sent.', '%d mails were sent.',
+ $env_nbm['sent_mail_count']
+ );
+ }
+ }
+ else
+ {
+ if ($env_nbm['sent_mail_count'] == 0)
+ {
+ $page['infos'][] = l10n('No mail to send.');
+ }
+ else
+ {
+ $page['infos'][] = l10n_dec(
+ '%d mail was sent.', '%d mails were sent.',
+ $env_nbm['sent_mail_count']
+ );
+ }
+ }
+}
+
+function assign_vars_nbm_mail_content($nbm_user)
+{
+ global $env_nbm;
+
+ set_make_full_url();
+
+ $env_nbm['mail_template']->assign
+ (
+ array
+ (
+ 'USERNAME' => stripslashes($nbm_user['username']),
+
+ 'SEND_AS_NAME' => $env_nbm['send_as_name'],
+
+ 'UNSUBSCRIBE_LINK' => add_url_params(get_gallery_home_url().'/nbm.php', array('unsubscribe' => $nbm_user['check_key'])),
+ 'SUBSCRIBE_LINK' => add_url_params(get_gallery_home_url().'/nbm.php', array('subscribe' => $nbm_user['check_key'])),
+ 'CONTACT_EMAIL' => $env_nbm['send_as_mail_address']
+ )
+ );
+
+ unset_make_full_url();
+}
+
+/*
+ * Subscribe or unsubscribe notification by mail
+ *
+ * is_subscribe define if action=subscribe or unsubscribe
+ * check_key list where action will be done
+ *
+ * @return check_key list treated
+ */
+function do_subscribe_unsubscribe_notification_by_mail($is_admin_request, $is_subscribe = false, $check_key_list = array())
+{
+ global $conf, $page, $env_nbm, $conf;
+
+ set_make_full_url();
+
+ $check_key_treated = array();
+ $updated_data_count = 0;
+ $error_on_updated_data_count = 0;
+
+ if ($is_subscribe)
+ {
+ $msg_info = l10n('User %s [%s] was added to the subscription list.');
+ $msg_error = l10n('User %s [%s] was not added to the subscription list.');
+ }
+ else
+ {
+ $msg_info = l10n('User %s [%s] was removed from the subscription list.');
+ $msg_error = l10n('User %s [%s] was not removed from the subscription list.');
+ }
+
+ if (count($check_key_list) != 0)
+ {
+ $updates = array();
+ $enabled_value = boolean_to_string($is_subscribe);
+ $data_users = get_user_notifications('subscribe', $check_key_list, !$is_subscribe);
+
+ // Prepare message after change language
+ $msg_break_timeout = l10n('Time to send mail is limited. Others mails are skipped.');
+
+ // Begin nbm users environment
+ begin_users_env_nbm(true);
+
+ foreach ($data_users as $nbm_user)
+ {
+ if (check_sendmail_timeout())
+ {
+ // Stop fill list on 'send', if the quota is override
+ $page['errors'][] = $msg_break_timeout;
+ break;
+ }
+
+ // Fill return list
+ $check_key_treated[] = $nbm_user['check_key'];
+
+ $do_update = true;
+ if ($nbm_user['mail_address'] != '')
+ {
+ // set env nbm user
+ set_user_on_env_nbm($nbm_user, true);
+
+ $subject = '['.$conf['gallery_title'].'] '.($is_subscribe ? l10n('Subscribe to notification by mail'): l10n('Unsubscribe from notification by mail'));
+
+ // Assign current var for nbm mail
+ assign_vars_nbm_mail_content($nbm_user);
+
+ $section_action_by = ($is_subscribe ? 'subscribe_by_' : 'unsubscribe_by_');
+ $section_action_by .= ($is_admin_request ? 'admin' : 'himself');
+ $env_nbm['mail_template']->assign
+ (
+ array
+ (
+ $section_action_by => true,
+ 'GOTO_GALLERY_TITLE' => $conf['gallery_title'],
+ 'GOTO_GALLERY_URL' => get_gallery_home_url(),
+ )
+ );
+
+ $ret = pwg_mail(
+ array(
+ 'name' => stripslashes($nbm_user['username']),
+ 'email' => $nbm_user['mail_address'],
+ ),
+ array(
+ 'from' => $env_nbm['send_as_mail_formated'],
+ 'subject' => $subject,
+ 'email_format' => $env_nbm['email_format'],
+ 'content' => $env_nbm['mail_template']->parse('notification_by_mail', true),
+ 'content_format' => $env_nbm['email_format'],
+ )
+ );
+
+ if ($ret)
+ {
+ inc_mail_sent_success($nbm_user);
+ }
+ else
+ {
+ inc_mail_sent_failed($nbm_user);
+ $do_update = false;
+ }
+
+ // unset env nbm user
+ unset_user_on_env_nbm();
+
+ }
+
+ if ($do_update)
+ {
+ $updates[] = array(
+ 'check_key' => $nbm_user['check_key'],
+ 'enabled' => $enabled_value
+ );
+ $updated_data_count += 1;
+ $page['infos'][] = sprintf($msg_info, stripslashes($nbm_user['username']), $nbm_user['mail_address']);
+ }
+ else
+ {
+ $error_on_updated_data_count += 1;
+ $page['errors'][] = sprintf($msg_error, stripslashes($nbm_user['username']), $nbm_user['mail_address']);
+ }
+
+ }
+
+ // Restore nbm environment
+ end_users_env_nbm();
+
+ display_counter_info();
+
+ mass_updates(
+ USER_MAIL_NOTIFICATION_TABLE,
+ array(
+ 'primary' => array('check_key'),
+ 'update' => array('enabled')
+ ),
+ $updates
+ );
+
+ }
+
+ $page['infos'][] = l10n_dec(
+ '%d user was updated.', '%d users were updated.',
+ $updated_data_count
+ );
+
+ if ($error_on_updated_data_count != 0)
+ {
+ $page['errors'][] = l10n_dec(
+ '%d user was not updated.', '%d users were not updated.',
+ $error_on_updated_data_count
+ );
+ }
+
+ unset_make_full_url();
+
+ return $check_key_treated;
+}
+
+/*
+ * Unsubscribe notification by mail
+ *
+ * check_key list where action will be done
+ *
+ * @return check_key list treated
+ */
+function unsubscribe_notification_by_mail($is_admin_request, $check_key_list = array())
+{
+ return do_subscribe_unsubscribe_notification_by_mail($is_admin_request, false, $check_key_list);
+}
+
+/*
+ * Subscribe notification by mail
+ *
+ * check_key list where action will be done
+ *
+ * @return check_key list treated
+ */
+function subscribe_notification_by_mail($is_admin_request, $check_key_list = array())
+{
+ return do_subscribe_unsubscribe_notification_by_mail($is_admin_request, true, $check_key_list);
+}
+
+?>
\ No newline at end of file
diff --git a/sources/admin/include/functions_permalinks.php b/sources/admin/include/functions_permalinks.php
new file mode 100644
index 0000000..6c8ae4a
--- /dev/null
+++ b/sources/admin/include/functions_permalinks.php
@@ -0,0 +1,204 @@
+
diff --git a/sources/admin/include/functions_plugins.inc.php b/sources/admin/include/functions_plugins.inc.php
new file mode 100644
index 0000000..fb98c79
--- /dev/null
+++ b/sources/admin/include/functions_plugins.inc.php
@@ -0,0 +1,46 @@
+
\ No newline at end of file
diff --git a/sources/admin/include/functions_upgrade.php b/sources/admin/include/functions_upgrade.php
new file mode 100644
index 0000000..4bd639f
--- /dev/null
+++ b/sources/admin/include/functions_upgrade.php
@@ -0,0 +1,326 @@
+'.implode(', ', $plugins).' ';
+ }
+}
+
+// Deactivate all non-standard themes
+function deactivate_non_standard_themes()
+{
+ global $page, $conf;
+
+ $standard_themes = array(
+ 'clear',
+ 'Sylvia',
+ 'dark',
+ 'elegant',
+ 'smartpocket',
+ );
+
+ $query = '
+SELECT
+ id,
+ name
+ FROM '.PREFIX_TABLE.'themes
+ WHERE id NOT IN (\''.implode("','", $standard_themes).'\')
+;';
+ $result = pwg_query($query);
+ $theme_ids = array();
+ $theme_names = array();
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $theme_ids[] = $row['id'];
+ $theme_names[] = $row['name'];
+ }
+
+ if (!empty($theme_ids))
+ {
+ $query = '
+DELETE
+ FROM '.PREFIX_TABLE.'themes
+ WHERE id IN (\''.implode("','", $theme_ids).'\')
+;';
+ pwg_query($query);
+
+ $page['infos'][] = l10n('As a precaution, following themes have been deactivated. You must check for themes upgrade before reactiving them:')
+ .''.implode(', ', $theme_names).'
';
+
+ // what is the default theme?
+ $query = '
+SELECT theme
+ FROM '.PREFIX_TABLE.'user_infos
+ WHERE user_id = '.$conf['default_user_id'].'
+;';
+ list($default_theme) = pwg_db_fetch_row(pwg_query($query));
+
+ // if the default theme has just been deactivated, let's set another core theme as default
+ if (in_array($default_theme, $theme_ids))
+ {
+ $query = '
+UPDATE '.PREFIX_TABLE.'user_infos
+ SET theme = \'elegant\'
+ WHERE user_id = '.$conf['default_user_id'].'
+;';
+ pwg_query($query);
+ }
+ }
+}
+
+// Deactivate all templates
+function deactivate_templates()
+{
+ $query = '
+ UPDATE '.PREFIX_TABLE.'config
+ SET value = \''. array() .'\'
+ WHERE param = \'extents_for_templates\';';
+}
+
+// Check access rights
+function check_upgrade_access_rights()
+{
+ global $conf, $page, $current_release;
+
+ if (version_compare($current_release, '2.0', '>=') and isset($_COOKIE[session_name()]))
+ {
+ // Check if user is already connected as webmaster
+ session_start();
+ if (!empty($_SESSION['pwg_uid']))
+ {
+ $query = '
+SELECT status
+ FROM '.USER_INFOS_TABLE.'
+ WHERE user_id = '.$_SESSION['pwg_uid'].'
+;';
+ pwg_query($query);
+
+ $row = pwg_db_fetch_assoc(pwg_query($query));
+ if (isset($row['status']) and $row['status'] == 'webmaster')
+ {
+ define('PHPWG_IN_UPGRADE', true);
+ return;
+ }
+ }
+ }
+
+ if (!isset($_POST['username']) or !isset($_POST['password']))
+ {
+ return;
+ }
+
+ $username = $_POST['username'];
+ $password = $_POST['password'];
+
+ if(!@get_magic_quotes_gpc())
+ {
+ $username = pwg_db_real_escape_string($username);
+ }
+
+ if (version_compare($current_release, '2.0', '<'))
+ {
+ $username = utf8_decode($username);
+ $password = utf8_decode($password);
+ }
+
+ if (version_compare($current_release, '1.5', '<'))
+ {
+ $query = '
+SELECT password, status
+FROM '.USERS_TABLE.'
+WHERE username = \''.$username.'\'
+;';
+ }
+ else
+ {
+ $query = '
+SELECT u.password, ui.status
+FROM '.USERS_TABLE.' AS u
+INNER JOIN '.USER_INFOS_TABLE.' AS ui
+ON u.'.$conf['user_fields']['id'].'=ui.user_id
+WHERE '.$conf['user_fields']['username'].'=\''.$username.'\'
+;';
+ }
+ $row = pwg_db_fetch_assoc(pwg_query($query));
+
+ if (!$conf['password_verify']($password, $row['password']))
+ {
+ $page['errors'][] = l10n('Invalid password!');
+ }
+ elseif ($row['status'] != 'admin' and $row['status'] != 'webmaster')
+ {
+ $page['errors'][] = l10n('You do not have access rights to run upgrade');
+ }
+ else
+ {
+ define('PHPWG_IN_UPGRADE', true);
+ }
+}
+
+/**
+ * which upgrades are available ?
+ *
+ * @return array
+ */
+function get_available_upgrade_ids()
+{
+ $upgrades_path = PHPWG_ROOT_PATH.'install/db';
+
+ $available_upgrade_ids = array();
+
+ if ($contents = opendir($upgrades_path))
+ {
+ while (($node = readdir($contents)) !== false)
+ {
+ if (is_file($upgrades_path.'/'.$node)
+ and preg_match('/^(.*?)-database\.php$/', $node, $match))
+ {
+ $available_upgrade_ids[] = $match[1];
+ }
+ }
+ }
+ natcasesort($available_upgrade_ids);
+
+ return $available_upgrade_ids;
+}
+
+
+/**
+ * returns true if there are available upgrade files
+ */
+function check_upgrade_feed()
+{
+ // retrieve already applied upgrades
+ $query = '
+SELECT id
+ FROM '.UPGRADE_TABLE.'
+;';
+ $applied = array_from_query($query, 'id');
+
+ // retrieve existing upgrades
+ $existing = get_available_upgrade_ids();
+
+ // which upgrades need to be applied?
+ return (count(array_diff($existing, $applied)) > 0);
+}
+
+function upgrade_db_connect()
+{
+ global $conf;
+
+ try
+ {
+ pwg_db_connect($conf['db_host'], $conf['db_user'],
+ $conf['db_password'], $conf['db_base']);
+ pwg_db_check_version();
+ }
+ catch (Exception $e)
+ {
+ my_error(l10n($e->getMessage()), true);
+ }
+}
+?>
\ No newline at end of file
diff --git a/sources/admin/include/functions_upload.inc.php b/sources/admin/include/functions_upload.inc.php
new file mode 100644
index 0000000..40a53ad
--- /dev/null
+++ b/sources/admin/include/functions_upload.inc.php
@@ -0,0 +1,585 @@
+ array(
+ 'default' => false,
+ 'can_be_null' => false,
+ ),
+
+ 'original_resize_maxwidth' => array(
+ 'default' => 2000,
+ 'min' => 500,
+ 'max' => 20000,
+ 'pattern' => '/^\d+$/',
+ 'can_be_null' => false,
+ 'error_message' => l10n('The original maximum width must be a number between %d and %d'),
+ ),
+
+ 'original_resize_maxheight' => array(
+ 'default' => 2000,
+ 'min' => 300,
+ 'max' => 20000,
+ 'pattern' => '/^\d+$/',
+ 'can_be_null' => false,
+ 'error_message' => l10n('The original maximum height must be a number between %d and %d'),
+ ),
+
+ 'original_resize_quality' => array(
+ 'default' => 95,
+ 'min' => 50,
+ 'max' => 98,
+ 'pattern' => '/^\d+$/',
+ 'can_be_null' => false,
+ 'error_message' => l10n('The original image quality must be a number between %d and %d'),
+ ),
+ );
+
+ return $upload_form_config;
+}
+
+function save_upload_form_config($data, &$errors=array(), &$form_errors=array())
+{
+ if (!is_array($data) or empty($data))
+ {
+ return false;
+ }
+
+ $upload_form_config = get_upload_form_config();
+ $updates = array();
+
+ foreach ($data as $field => $value)
+ {
+ if (!isset($upload_form_config[$field]))
+ {
+ continue;
+ }
+ if (is_bool($upload_form_config[$field]['default']))
+ {
+ if (isset($value))
+ {
+ $value = true;
+ }
+ else
+ {
+ $value = false;
+ }
+
+ $updates[] = array(
+ 'param' => $field,
+ 'value' => boolean_to_string($value)
+ );
+ }
+ elseif ($upload_form_config[$field]['can_be_null'] and empty($value))
+ {
+ $updates[] = array(
+ 'param' => $field,
+ 'value' => 'false'
+ );
+ }
+ else
+ {
+ $min = $upload_form_config[$field]['min'];
+ $max = $upload_form_config[$field]['max'];
+ $pattern = $upload_form_config[$field]['pattern'];
+
+ if (preg_match($pattern, $value) and $value >= $min and $value <= $max)
+ {
+ $updates[] = array(
+ 'param' => $field,
+ 'value' => $value
+ );
+ }
+ else
+ {
+ $errors[] = sprintf(
+ $upload_form_config[$field]['error_message'],
+ $min, $max
+ );
+
+ $form_errors[$field] = '['.$min.' .. '.$max.']';
+ }
+ }
+ }
+
+ if (count($errors) == 0)
+ {
+ mass_updates(
+ CONFIG_TABLE,
+ array(
+ 'primary' => array('param'),
+ 'update' => array('value')
+ ),
+ $updates
+ );
+ return true;
+ }
+
+ return false;
+}
+
+function add_uploaded_file($source_filepath, $original_filename=null, $categories=null, $level=null, $image_id=null, $original_md5sum=null)
+{
+ // 1) move uploaded file to upload/2010/01/22/20100122003814-449ada00.jpg
+ //
+ // 2) keep/resize original
+ //
+ // 3) register in database
+
+ // TODO
+ // * check md5sum (already exists?)
+
+ global $conf, $user;
+
+ if (isset($original_md5sum))
+ {
+ $md5sum = $original_md5sum;
+ }
+ else
+ {
+ $md5sum = md5_file($source_filepath);
+ }
+
+ $file_path = null;
+ $is_tiff = false;
+
+ if (isset($image_id))
+ {
+ // this photo already exists, we update it
+ $query = '
+SELECT
+ path
+ FROM '.IMAGES_TABLE.'
+ WHERE id = '.$image_id.'
+;';
+ $result = pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $file_path = $row['path'];
+ }
+
+ if (!isset($file_path))
+ {
+ die('['.__FUNCTION__.'] this photo does not exist in the database');
+ }
+
+ // delete all physical files related to the photo (thumbnail, web site, HD)
+ delete_element_files(array($image_id));
+ }
+ else
+ {
+ // this photo is new
+
+ // current date
+ list($dbnow) = pwg_db_fetch_row(pwg_query('SELECT NOW();'));
+ list($year, $month, $day) = preg_split('/[^\d]/', $dbnow, 4);
+
+ // upload directory hierarchy
+ $upload_dir = sprintf(
+ PHPWG_ROOT_PATH.$conf['upload_dir'].'/%s/%s/%s',
+ $year,
+ $month,
+ $day
+ );
+
+ // compute file path
+ $date_string = preg_replace('/[^\d]/', '', $dbnow);
+ $random_string = substr($md5sum, 0, 8);
+ $filename_wo_ext = $date_string.'-'.$random_string;
+ $file_path = $upload_dir.'/'.$filename_wo_ext.'.';
+
+ list($width, $height, $type) = getimagesize($source_filepath);
+ if (IMAGETYPE_PNG == $type)
+ {
+ $file_path.= 'png';
+ }
+ elseif (IMAGETYPE_GIF == $type)
+ {
+ $file_path.= 'gif';
+ }
+ elseif (IMAGETYPE_TIFF_MM == $type or IMAGETYPE_TIFF_II == $type)
+ {
+ $is_tiff = true;
+ $file_path.= 'tif';
+ }
+ else
+ {
+ $file_path.= 'jpg';
+ }
+
+ prepare_directory($upload_dir);
+ }
+
+ if (is_uploaded_file($source_filepath))
+ {
+ move_uploaded_file($source_filepath, $file_path);
+ }
+ else
+ {
+ rename($source_filepath, $file_path);
+ }
+ @chmod($file_path, 0644);
+
+ if ($is_tiff and pwg_image::get_library() == 'ext_imagick')
+ {
+ // move the uploaded file to pwg_representative sub-directory
+ $representative_file_path = dirname($file_path).'/pwg_representative/';
+ $representative_file_path.= get_filename_wo_extension(basename($file_path)).'.';
+
+ $representative_ext = $conf['tiff_representative_ext'];
+ $representative_file_path.= $representative_ext;
+
+ prepare_directory(dirname($representative_file_path));
+
+ $exec = $conf['ext_imagick_dir'].'convert';
+
+ if ('jpg' == $conf['tiff_representative_ext'])
+ {
+ $exec .= ' -quality 98';
+ }
+
+ $exec .= ' "'.realpath($file_path).'"';
+
+ $dest = pathinfo($representative_file_path);
+ $exec .= ' "'.realpath($dest['dirname']).'/'.$dest['basename'].'"';
+
+ $exec .= ' 2>&1';
+ @exec($exec, $returnarray);
+
+ // sometimes ImageMagick creates file-0.jpg (full size) + file-1.jpg
+ // (thumbnail). I don't know how to avoid it.
+ $representative_file_abspath = realpath($dest['dirname']).'/'.$dest['basename'];
+ if (!file_exists($representative_file_abspath))
+ {
+ $first_file_abspath = preg_replace(
+ '/\.'.$representative_ext.'$/',
+ '-0.'.$representative_ext,
+ $representative_file_abspath
+ );
+
+ if (file_exists($first_file_abspath))
+ {
+ rename($first_file_abspath, $representative_file_abspath);
+ }
+ }
+ }
+
+ if (pwg_image::get_library() != 'gd')
+ {
+ if ($conf['original_resize'])
+ {
+ $need_resize = need_resize($file_path, $conf['original_resize_maxwidth'], $conf['original_resize_maxheight']);
+
+ if ($need_resize)
+ {
+ $img = new pwg_image($file_path);
+
+ $img->pwg_resize(
+ $file_path,
+ $conf['original_resize_maxwidth'],
+ $conf['original_resize_maxheight'],
+ $conf['original_resize_quality'],
+ $conf['upload_form_automatic_rotation'],
+ false
+ );
+
+ $img->destroy();
+ }
+ }
+ }
+
+ // we need to save the rotation angle in the database to compute
+ // width/height of "multisizes"
+ $rotation_angle = pwg_image::get_rotation_angle($file_path);
+ $rotation = pwg_image::get_rotation_code_from_angle($rotation_angle);
+
+ $file_infos = pwg_image_infos($file_path);
+
+ if (isset($image_id))
+ {
+ $update = array(
+ 'file' => pwg_db_real_escape_string(isset($original_filename) ? $original_filename : basename($file_path)),
+ 'filesize' => $file_infos['filesize'],
+ 'width' => $file_infos['width'],
+ 'height' => $file_infos['height'],
+ 'md5sum' => $md5sum,
+ 'added_by' => $user['id'],
+ 'rotation' => $rotation,
+ );
+
+ if (isset($level))
+ {
+ $update['level'] = $level;
+ }
+
+ single_update(
+ IMAGES_TABLE,
+ $update,
+ array('id' => $image_id)
+ );
+ }
+ else
+ {
+ // database registration
+ $file = pwg_db_real_escape_string(isset($original_filename) ? $original_filename : basename($file_path));
+ $insert = array(
+ 'file' => $file,
+ 'name' => get_name_from_file($file),
+ 'date_available' => $dbnow,
+ 'path' => preg_replace('#^'.preg_quote(PHPWG_ROOT_PATH).'#', '', $file_path),
+ 'filesize' => $file_infos['filesize'],
+ 'width' => $file_infos['width'],
+ 'height' => $file_infos['height'],
+ 'md5sum' => $md5sum,
+ 'added_by' => $user['id'],
+ 'rotation' => $rotation,
+ );
+
+ if (isset($level))
+ {
+ $insert['level'] = $level;
+ }
+
+ if (isset($representative_ext))
+ {
+ $insert['representative_ext'] = $representative_ext;
+ }
+
+ single_insert(IMAGES_TABLE, $insert);
+
+ $image_id = pwg_db_insert_id(IMAGES_TABLE);
+ }
+
+ if (isset($categories) and count($categories) > 0)
+ {
+ associate_images_to_categories(
+ array($image_id),
+ $categories
+ );
+ }
+
+ // update metadata from the uploaded file (exif/iptc)
+ if ($conf['use_exif'] and !function_exists('read_exif_data'))
+ {
+ $conf['use_exif'] = false;
+ }
+ sync_metadata(array($image_id));
+
+ invalidate_user_cache();
+
+ // cache thumbnail
+ $query = '
+SELECT
+ id,
+ path
+ FROM '.IMAGES_TABLE.'
+ WHERE id = '.$image_id.'
+;';
+ $image_infos = pwg_db_fetch_assoc(pwg_query($query));
+
+ set_make_full_url();
+ // in case we are on uploadify.php, we have to replace the false path
+ $thumb_url = preg_replace('#admin/include/i#', 'i', DerivativeImage::thumb_url($image_infos));
+ unset_make_full_url();
+
+ fetchRemote($thumb_url, $dest);
+
+
+ return $image_id;
+}
+
+function prepare_directory($directory)
+{
+ if (!is_dir($directory)) {
+ if (substr(PHP_OS, 0, 3) == 'WIN')
+ {
+ $directory = str_replace('/', DIRECTORY_SEPARATOR, $directory);
+ }
+ umask(0000);
+ $recursive = true;
+ if (!@mkdir($directory, 0777, $recursive))
+ {
+ die('[prepare_directory] cannot create directory "'.$directory.'"');
+ }
+ }
+
+ if (!is_writable($directory))
+ {
+ // last chance to make the directory writable
+ @chmod($directory, 0777);
+
+ if (!is_writable($directory))
+ {
+ die('[prepare_directory] directory "'.$directory.'" has no write access');
+ }
+ }
+
+ secure_directory($directory);
+}
+
+function need_resize($image_filepath, $max_width, $max_height)
+{
+ // TODO : the resize check should take the orientation into account. If a
+ // rotation must be applied to the resized photo, then we should test
+ // invert width and height.
+ list($width, $height) = getimagesize($image_filepath);
+
+ if ($width > $max_width or $height > $max_height)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+function pwg_image_infos($path)
+{
+ list($width, $height) = getimagesize($path);
+ $filesize = floor(filesize($path)/1024);
+
+ return array(
+ 'width' => $width,
+ 'height' => $height,
+ 'filesize' => $filesize,
+ );
+}
+
+function is_valid_image_extension($extension)
+{
+ return in_array(strtolower($extension), array('jpg', 'jpeg', 'png', 'gif'));
+}
+
+function file_upload_error_message($error_code)
+{
+ switch ($error_code) {
+ case UPLOAD_ERR_INI_SIZE:
+ return sprintf(
+ l10n('The uploaded file exceeds the upload_max_filesize directive in php.ini: %sB'),
+ get_ini_size('upload_max_filesize', false)
+ );
+ case UPLOAD_ERR_FORM_SIZE:
+ return l10n('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form');
+ case UPLOAD_ERR_PARTIAL:
+ return l10n('The uploaded file was only partially uploaded');
+ case UPLOAD_ERR_NO_FILE:
+ return l10n('No file was uploaded');
+ case UPLOAD_ERR_NO_TMP_DIR:
+ return l10n('Missing a temporary folder');
+ case UPLOAD_ERR_CANT_WRITE:
+ return l10n('Failed to write file to disk');
+ case UPLOAD_ERR_EXTENSION:
+ return l10n('File upload stopped by extension');
+ default:
+ return l10n('Unknown upload error');
+ }
+}
+
+function get_ini_size($ini_key, $in_bytes=true)
+{
+ $size = ini_get($ini_key);
+
+ if ($in_bytes)
+ {
+ $size = convert_shorthand_notation_to_bytes($size);
+ }
+
+ return $size;
+}
+
+function convert_shorthand_notation_to_bytes($value)
+{
+ $suffix = substr($value, -1);
+ $multiply_by = null;
+
+ if ('K' == $suffix)
+ {
+ $multiply_by = 1024;
+ }
+ else if ('M' == $suffix)
+ {
+ $multiply_by = 1024*1024;
+ }
+ else if ('G' == $suffix)
+ {
+ $multiply_by = 1024*1024*1024;
+ }
+
+ if (isset($multiply_by))
+ {
+ $value = substr($value, 0, -1);
+ $value*= $multiply_by;
+ }
+
+ return $value;
+}
+
+function add_upload_error($upload_id, $error_message)
+{
+ $_SESSION['uploads_error'][$upload_id][] = $error_message;
+}
+
+function ready_for_upload_message()
+{
+ global $conf;
+
+ $relative_dir = preg_replace('#^'.PHPWG_ROOT_PATH.'#', '', $conf['upload_dir']);
+
+ if (!is_dir($conf['upload_dir']))
+ {
+ if (!is_writable(dirname($conf['upload_dir'])))
+ {
+ return sprintf(
+ l10n('Create the "%s" directory at the root of your Piwigo installation'),
+ $relative_dir
+ );
+ }
+ }
+ else
+ {
+ if (!is_writable($conf['upload_dir']))
+ {
+ @chmod($conf['upload_dir'], 0777);
+
+ if (!is_writable($conf['upload_dir']))
+ {
+ return sprintf(
+ l10n('Give write access (chmod 777) to "%s" directory at the root of your Piwigo installation'),
+ $relative_dir
+ );
+ }
+ }
+ }
+
+ return null;
+}
+?>
\ No newline at end of file
diff --git a/sources/admin/include/image.class.php b/sources/admin/include/image.class.php
new file mode 100644
index 0000000..1013099
--- /dev/null
+++ b/sources/admin/include/image.class.php
@@ -0,0 +1,821 @@
+source_filepath = $source_filepath;
+
+ trigger_action('load_image_library', array(&$this) );
+
+ if (is_object($this->image))
+ {
+ return; // A plugin may have load its own library
+ }
+
+ $extension = strtolower(get_extension($source_filepath));
+
+ if (!in_array($extension, array('jpg', 'jpeg', 'png', 'gif')))
+ {
+ die('[Image] unsupported file extension');
+ }
+
+ if (!($this->library = self::get_library($library, $extension)))
+ {
+ die('No image library available on your server.');
+ }
+
+ $class = 'image_'.$this->library;
+ $this->image = new $class($source_filepath);
+ }
+
+ // Unknow methods will be redirected to image object
+ function __call($method, $arguments)
+ {
+ return call_user_func_array(array($this->image, $method), $arguments);
+ }
+
+ // Piwigo resize function
+ function pwg_resize($destination_filepath, $max_width, $max_height, $quality, $automatic_rotation=true, $strip_metadata=false, $crop=false, $follow_orientation=true)
+ {
+ $starttime = get_moment();
+
+ // width/height
+ $source_width = $this->image->get_width();
+ $source_height = $this->image->get_height();
+
+ $rotation = null;
+ if ($automatic_rotation)
+ {
+ $rotation = self::get_rotation_angle($this->source_filepath);
+ }
+ $resize_dimensions = self::get_resize_dimensions($source_width, $source_height, $max_width, $max_height, $rotation, $crop, $follow_orientation);
+
+ // testing on height is useless in theory: if width is unchanged, there
+ // should be no resize, because width/height ratio is not modified.
+ if ($resize_dimensions['width'] == $source_width and $resize_dimensions['height'] == $source_height)
+ {
+ // the image doesn't need any resize! We just copy it to the destination
+ copy($this->source_filepath, $destination_filepath);
+ return $this->get_resize_result($destination_filepath, $resize_dimensions['width'], $resize_dimensions['height'], $starttime);
+ }
+
+ $this->image->set_compression_quality($quality);
+
+ if ($strip_metadata)
+ {
+ // we save a few kilobytes. For example a thumbnail with metadata weights 25KB, without metadata 7KB.
+ $this->image->strip();
+ }
+
+ if (isset($resize_dimensions['crop']))
+ {
+ $this->image->crop($resize_dimensions['crop']['width'], $resize_dimensions['crop']['height'], $resize_dimensions['crop']['x'], $resize_dimensions['crop']['y']);
+ }
+
+ $this->image->resize($resize_dimensions['width'], $resize_dimensions['height']);
+
+ if (!empty($rotation))
+ {
+ $this->image->rotate($rotation);
+ }
+
+ $this->image->write($destination_filepath);
+
+ // everything should be OK if we are here!
+ return $this->get_resize_result($destination_filepath, $resize_dimensions['width'], $resize_dimensions['height'], $starttime);
+ }
+
+ static function get_resize_dimensions($width, $height, $max_width, $max_height, $rotation=null, $crop=false, $follow_orientation=true)
+ {
+ $rotate_for_dimensions = false;
+ if (isset($rotation) and in_array(abs($rotation), array(90, 270)))
+ {
+ $rotate_for_dimensions = true;
+ }
+
+ if ($rotate_for_dimensions)
+ {
+ list($width, $height) = array($height, $width);
+ }
+
+ if ($crop)
+ {
+ $x = 0;
+ $y = 0;
+
+ if ($width < $height and $follow_orientation)
+ {
+ list($max_width, $max_height) = array($max_height, $max_width);
+ }
+
+ $img_ratio = $width / $height;
+ $dest_ratio = $max_width / $max_height;
+
+ if($dest_ratio > $img_ratio)
+ {
+ $destHeight = round($width * $max_height / $max_width);
+ $y = round(($height - $destHeight) / 2 );
+ $height = $destHeight;
+ }
+ elseif ($dest_ratio < $img_ratio)
+ {
+ $destWidth = round($height * $max_width / $max_height);
+ $x = round(($width - $destWidth) / 2 );
+ $width = $destWidth;
+ }
+ }
+
+ $ratio_width = $width / $max_width;
+ $ratio_height = $height / $max_height;
+ $destination_width = $width;
+ $destination_height = $height;
+
+ // maximal size exceeded ?
+ if ($ratio_width > 1 or $ratio_height > 1)
+ {
+ if ($ratio_width < $ratio_height)
+ {
+ $destination_width = round($width / $ratio_height);
+ $destination_height = $max_height;
+ }
+ else
+ {
+ $destination_width = $max_width;
+ $destination_height = round($height / $ratio_width);
+ }
+ }
+
+ if ($rotate_for_dimensions)
+ {
+ list($destination_width, $destination_height) = array($destination_height, $destination_width);
+ }
+
+ $result = array(
+ 'width' => $destination_width,
+ 'height'=> $destination_height,
+ );
+
+ if ($crop and ($x or $y))
+ {
+ $result['crop'] = array(
+ 'width' => $width,
+ 'height' => $height,
+ 'x' => $x,
+ 'y' => $y,
+ );
+ }
+ return $result;
+ }
+
+ static function get_rotation_angle($source_filepath)
+ {
+ list($width, $height, $type) = getimagesize($source_filepath);
+ if (IMAGETYPE_JPEG != $type)
+ {
+ return null;
+ }
+
+ if (!function_exists('exif_read_data'))
+ {
+ return null;
+ }
+
+ $rotation = 0;
+
+ $exif = exif_read_data($source_filepath);
+
+ if (isset($exif['Orientation']) and preg_match('/^\s*(\d)/', $exif['Orientation'], $matches))
+ {
+ $orientation = $matches[1];
+ if (in_array($orientation, array(3, 4)))
+ {
+ $rotation = 180;
+ }
+ elseif (in_array($orientation, array(5, 6)))
+ {
+ $rotation = 270;
+ }
+ elseif (in_array($orientation, array(7, 8)))
+ {
+ $rotation = 90;
+ }
+ }
+
+ return $rotation;
+ }
+
+ static function get_rotation_code_from_angle($rotation_angle)
+ {
+ switch($rotation_angle)
+ {
+ case 0: return 0;
+ case 90: return 1;
+ case 180: return 2;
+ case 270: return 3;
+ }
+ }
+
+ static function get_rotation_angle_from_code($rotation_code)
+ {
+ switch($rotation_code%4)
+ {
+ case 0: return 0;
+ case 1: return 90;
+ case 2: return 180;
+ case 3: return 270;
+ }
+ }
+
+ /** Returns a normalized convolution kernel for sharpening*/
+ static function get_sharpen_matrix($amount)
+ {
+ // Amount should be in the range of 48-10
+ $amount = round(abs(-48 + ($amount * 0.38)), 2);
+
+ $matrix = array(
+ array(-1, -1, -1),
+ array(-1, $amount, -1),
+ array(-1, -1, -1),
+ );
+
+ $norm = array_sum(array_map('array_sum', $matrix));
+
+ for ($i=0; $i<3; $i++)
+ {
+ $line = & $matrix[$i];
+ for ($j=0; $j<3; $j++)
+ {
+ $line[$j] /= $norm;
+ }
+ }
+
+ return $matrix;
+ }
+
+ private function get_resize_result($destination_filepath, $width, $height, $time=null)
+ {
+ return array(
+ 'source' => $this->source_filepath,
+ 'destination' => $destination_filepath,
+ 'width' => $width,
+ 'height' => $height,
+ 'size' => floor(filesize($destination_filepath) / 1024).' KB',
+ 'time' => $time ? number_format((get_moment() - $time) * 1000, 2, '.', ' ').' ms' : null,
+ 'library' => $this->library,
+ );
+ }
+
+ static function is_imagick()
+ {
+ return (extension_loaded('imagick') and class_exists('Imagick'));
+ }
+
+ static function is_ext_imagick()
+ {
+ global $conf;
+
+ if (!function_exists('exec'))
+ {
+ return false;
+ }
+ @exec($conf['ext_imagick_dir'].'convert -version', $returnarray);
+ if (is_array($returnarray) and !empty($returnarray[0]) and preg_match('/ImageMagick/i', $returnarray[0]))
+ {
+ if (preg_match('/Version: ImageMagick (\d+\.\d+\.\d+-?\d*)/', $returnarray[0], $match))
+ {
+ self::$ext_imagick_version = $match[1];
+ }
+ return true;
+ }
+ return false;
+ }
+
+ static function is_gd()
+ {
+ return function_exists('gd_info');
+ }
+
+ static function get_library($library=null, $extension=null)
+ {
+ global $conf;
+
+ if (is_null($library))
+ {
+ $library = $conf['graphics_library'];
+ }
+
+ // Choose image library
+ switch (strtolower($library))
+ {
+ case 'auto':
+ case 'imagick':
+ if ($extension != 'gif' and self::is_imagick())
+ {
+ return 'imagick';
+ }
+ case 'ext_imagick':
+ if ($extension != 'gif' and self::is_ext_imagick())
+ {
+ return 'ext_imagick';
+ }
+ case 'gd':
+ if (self::is_gd())
+ {
+ return 'gd';
+ }
+ default:
+ if ($library != 'auto')
+ {
+ // Requested library not available. Try another library
+ return self::get_library('auto', $extension);
+ }
+ }
+ return false;
+ }
+
+ function destroy()
+ {
+ if (method_exists($this->image, 'destroy'))
+ {
+ return $this->image->destroy();
+ }
+ return true;
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | Class for Imagick extension |
+// +-----------------------------------------------------------------------+
+
+class image_imagick implements imageInterface
+{
+ var $image;
+
+ function __construct($source_filepath)
+ {
+ // A bug cause that Imagick class can not be extended
+ $this->image = new Imagick($source_filepath);
+ }
+
+ function get_width()
+ {
+ return $this->image->getImageWidth();
+ }
+
+ function get_height()
+ {
+ return $this->image->getImageHeight();
+ }
+
+ function set_compression_quality($quality)
+ {
+ return $this->image->setImageCompressionQuality($quality);
+ }
+
+ function crop($width, $height, $x, $y)
+ {
+ return $this->image->cropImage($width, $height, $x, $y);
+ }
+
+ function strip()
+ {
+ return $this->image->stripImage();
+ }
+
+ function rotate($rotation)
+ {
+ $this->image->rotateImage(new ImagickPixel(), -$rotation);
+ $this->image->setImageOrientation(Imagick::ORIENTATION_TOPLEFT);
+ return true;
+ }
+
+ function resize($width, $height)
+ {
+ $this->image->setInterlaceScheme(Imagick::INTERLACE_LINE);
+
+ // TODO need to explain this condition
+ if ($this->get_width()%2 == 0
+ && $this->get_height()%2 == 0
+ && $this->get_width() > 3*$width)
+ {
+ $this->image->scaleImage($this->get_width()/2, $this->get_height()/2);
+ }
+
+ return $this->image->resizeImage($width, $height, Imagick::FILTER_LANCZOS, 0.9);
+ }
+
+ function sharpen($amount)
+ {
+ $m = pwg_image::get_sharpen_matrix($amount);
+ return $this->image->convolveImage($m);
+ }
+
+ function compose($overlay, $x, $y, $opacity)
+ {
+ $ioverlay = $overlay->image->image;
+ /*if ($ioverlay->getImageAlphaChannel() !== Imagick::ALPHACHANNEL_OPAQUE)
+ {
+ // Force the image to have an alpha channel
+ $ioverlay->setImageAlphaChannel(Imagick::ALPHACHANNEL_OPAQUE);
+ }*/
+
+ global $dirty_trick_xrepeat;
+ if ( !isset($dirty_trick_xrepeat) && $opacity < 100)
+ {// NOTE: Using setImageOpacity will destroy current alpha channels!
+ $ioverlay->evaluateImage(Imagick::EVALUATE_MULTIPLY, $opacity / 100, Imagick::CHANNEL_ALPHA);
+ $dirty_trick_xrepeat = true;
+ }
+
+ return $this->image->compositeImage($ioverlay, Imagick::COMPOSITE_DISSOLVE, $x, $y);
+ }
+
+ function write($destination_filepath)
+ {
+ // use 4:2:2 chroma subsampling (reduce file size by 20-30% with "almost" no human perception)
+ $this->image->setSamplingFactors( array(2,1) );
+ return $this->image->writeImage($destination_filepath);
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | Class for ImageMagick external installation |
+// +-----------------------------------------------------------------------+
+
+class image_ext_imagick implements imageInterface
+{
+ var $imagickdir = '';
+ var $source_filepath = '';
+ var $width = '';
+ var $height = '';
+ var $commands = array();
+
+ function __construct($source_filepath)
+ {
+ global $conf;
+ $this->source_filepath = $source_filepath;
+ $this->imagickdir = $conf['ext_imagick_dir'];
+
+ if (strpos(@$_SERVER['SCRIPT_FILENAME'], '/kunden/') === 0) // 1and1
+ {
+ @putenv('MAGICK_THREAD_LIMIT=1');
+ }
+
+ $command = $this->imagickdir.'identify -format "%wx%h" "'.realpath($source_filepath).'"';
+ @exec($command, $returnarray);
+ if(!is_array($returnarray) or empty($returnarray[0]) or !preg_match('/^(\d+)x(\d+)$/', $returnarray[0], $match))
+ {
+ die("[External ImageMagick] Corrupt image\n" . var_export($returnarray, true));
+ }
+
+ $this->width = $match[1];
+ $this->height = $match[2];
+ }
+
+ function add_command($command, $params=null)
+ {
+ $this->commands[$command] = $params;
+ }
+
+ function get_width()
+ {
+ return $this->width;
+ }
+
+ function get_height()
+ {
+ return $this->height;
+ }
+
+ function crop($width, $height, $x, $y)
+ {
+ $this->add_command('crop', $width.'x'.$height.'+'.$x.'+'.$y);
+ return true;
+ }
+
+ function strip()
+ {
+ $this->add_command('strip');
+ return true;
+ }
+
+ function rotate($rotation)
+ {
+ if (empty($rotation))
+ {
+ return true;
+ }
+
+ if ($rotation==90 || $rotation==270)
+ {
+ $tmp = $this->width;
+ $this->width = $this->height;
+ $this->height = $tmp;
+ }
+ $this->add_command('rotate', -$rotation);
+ $this->add_command('orient', 'top-left');
+ return true;
+ }
+
+ function set_compression_quality($quality)
+ {
+ $this->add_command('quality', $quality);
+ return true;
+ }
+
+ function resize($width, $height)
+ {
+ $this->add_command('filter', 'Lanczos');
+ $this->add_command('resize', $width.'x'.$height.'!');
+ return true;
+ }
+
+ function sharpen($amount)
+ {
+ $m = pwg_image::get_sharpen_matrix($amount);
+
+ $param ='convolve "'.count($m).':';
+ foreach ($m as $line)
+ {
+ $param .= ' ';
+ $param .= implode(',', $line);
+ }
+ $param .= '"';
+ $this->add_command('morphology', $param);
+ return true;
+ }
+
+ function compose($overlay, $x, $y, $opacity)
+ {
+ $param = 'compose dissolve -define compose:args='.$opacity;
+ $param .= ' '.escapeshellarg(realpath($overlay->image->source_filepath));
+ $param .= ' -gravity NorthWest -geometry +'.$x.'+'.$y;
+ $param .= ' -composite';
+ $this->add_command($param);
+ return true;
+ }
+
+ function write($destination_filepath)
+ {
+ $this->add_command('interlace', 'line'); // progressive rendering
+ // use 4:2:2 chroma subsampling (reduce file size by 20-30% with "almost" no human perception)
+ //
+ // option deactivated for Piwigo 2.4.1, it doesn't work fo old versions
+ // of ImageMagick, see bug:2672. To reactivate once we have a better way
+ // to detect IM version and when we know which version supports this
+ // option
+ //
+ if (version_compare(pwg_image::$ext_imagick_version, '6.6') > 0)
+ {
+ $this->add_command('sampling-factor', '4:2:2' );
+ }
+
+ $exec = $this->imagickdir.'convert';
+ $exec .= ' "'.realpath($this->source_filepath).'"';
+
+ foreach ($this->commands as $command => $params)
+ {
+ $exec .= ' -'.$command;
+ if (!empty($params))
+ {
+ $exec .= ' '.$params;
+ }
+ }
+
+ $dest = pathinfo($destination_filepath);
+ $exec .= ' "'.realpath($dest['dirname']).'/'.$dest['basename'].'" 2>&1';
+ @exec($exec, $returnarray);
+
+ if (function_exists('ilog')) ilog($exec);
+ if (is_array($returnarray) && (count($returnarray)>0) )
+ {
+ if (function_exists('ilog')) ilog('ERROR', $returnarray);
+ foreach($returnarray as $line)
+ trigger_error($line, E_USER_WARNING);
+ }
+ return is_array($returnarray);
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | Class for GD library |
+// +-----------------------------------------------------------------------+
+
+class image_gd implements imageInterface
+{
+ var $image;
+ var $quality = 95;
+
+ function __construct($source_filepath)
+ {
+ $gd_info = gd_info();
+ $extension = strtolower(get_extension($source_filepath));
+
+ if (in_array($extension, array('jpg', 'jpeg')))
+ {
+ $this->image = imagecreatefromjpeg($source_filepath);
+ }
+ else if ($extension == 'png')
+ {
+ $this->image = imagecreatefrompng($source_filepath);
+ }
+ elseif ($extension == 'gif' and $gd_info['GIF Read Support'] and $gd_info['GIF Create Support'])
+ {
+ $this->image = imagecreatefromgif($source_filepath);
+ }
+ else
+ {
+ die('[Image GD] unsupported file extension');
+ }
+ }
+
+ function get_width()
+ {
+ return imagesx($this->image);
+ }
+
+ function get_height()
+ {
+ return imagesy($this->image);
+ }
+
+ function crop($width, $height, $x, $y)
+ {
+ $dest = imagecreatetruecolor($width, $height);
+
+ imagealphablending($dest, false);
+ imagesavealpha($dest, true);
+ if (function_exists('imageantialias'))
+ {
+ imageantialias($dest, true);
+ }
+
+ $result = imagecopymerge($dest, $this->image, 0, 0, $x, $y, $width, $height, 100);
+
+ if ($result !== false)
+ {
+ imagedestroy($this->image);
+ $this->image = $dest;
+ }
+ else
+ {
+ imagedestroy($dest);
+ }
+ return $result;
+ }
+
+ function strip()
+ {
+ return true;
+ }
+
+ function rotate($rotation)
+ {
+ $dest = imagerotate($this->image, $rotation, 0);
+ imagedestroy($this->image);
+ $this->image = $dest;
+ return true;
+ }
+
+ function set_compression_quality($quality)
+ {
+ $this->quality = $quality;
+ return true;
+ }
+
+ function resize($width, $height)
+ {
+ $dest = imagecreatetruecolor($width, $height);
+
+ imagealphablending($dest, false);
+ imagesavealpha($dest, true);
+ if (function_exists('imageantialias'))
+ {
+ imageantialias($dest, true);
+ }
+
+ $result = imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $width, $height, $this->get_width(), $this->get_height());
+
+ if ($result !== false)
+ {
+ imagedestroy($this->image);
+ $this->image = $dest;
+ }
+ else
+ {
+ imagedestroy($dest);
+ }
+ return $result;
+ }
+
+ function sharpen($amount)
+ {
+ $m = pwg_image::get_sharpen_matrix($amount);
+ return imageconvolution($this->image, $m, 1, 0);
+ }
+
+ function compose($overlay, $x, $y, $opacity)
+ {
+ $ioverlay = $overlay->image->image;
+ /* A replacement for php's imagecopymerge() function that supports the alpha channel
+ See php bug #23815: http://bugs.php.net/bug.php?id=23815 */
+
+ $ow = imagesx($ioverlay);
+ $oh = imagesy($ioverlay);
+
+ // Create a new blank image the site of our source image
+ $cut = imagecreatetruecolor($ow, $oh);
+
+ // Copy the blank image into the destination image where the source goes
+ imagecopy($cut, $this->image, 0, 0, $x, $y, $ow, $oh);
+
+ // Place the source image in the destination image
+ imagecopy($cut, $ioverlay, 0, 0, 0, 0, $ow, $oh);
+ imagecopymerge($this->image, $cut, $x, $y, 0, 0, $ow, $oh, $opacity);
+ imagedestroy($cut);
+ return true;
+ }
+
+ function write($destination_filepath)
+ {
+ $extension = strtolower(get_extension($destination_filepath));
+
+ if ($extension == 'png')
+ {
+ imagepng($this->image, $destination_filepath);
+ }
+ elseif ($extension == 'gif')
+ {
+ imagegif($this->image, $destination_filepath);
+ }
+ else
+ {
+ imagejpeg($this->image, $destination_filepath, $this->quality);
+ }
+ }
+
+ function destroy()
+ {
+ imagedestroy($this->image);
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/sources/admin/include/index.php b/sources/admin/include/index.php
new file mode 100644
index 0000000..c8de97f
--- /dev/null
+++ b/sources/admin/include/index.php
@@ -0,0 +1,30 @@
+
diff --git a/sources/admin/include/languages.class.php b/sources/admin/include/languages.class.php
new file mode 100644
index 0000000..c4caa2d
--- /dev/null
+++ b/sources/admin/include/languages.class.php
@@ -0,0 +1,410 @@
+get_fs_languages($target_charset);
+ }
+
+ /**
+ * Perform requested actions
+ * @param string - action
+ * @param string - language id
+ * @param array - errors
+ */
+ function perform_action($action, $language_id)
+ {
+ global $conf;
+
+ if (isset($this->db_languages[$language_id]))
+ {
+ $crt_db_language = $this->db_languages[$language_id];
+ }
+
+ $errors = array();
+
+ switch ($action)
+ {
+ case 'activate':
+ if (isset($crt_db_language))
+ {
+ $errors[] = 'CANNOT ACTIVATE - LANGUAGE IS ALREADY ACTIVATED';
+ break;
+ }
+
+ $query = '
+INSERT INTO '.LANGUAGES_TABLE.'
+ (id, version, name)
+ VALUES(\''.$language_id.'\',
+ \''.$this->fs_languages[$language_id]['version'].'\',
+ \''.$this->fs_languages[$language_id]['name'].'\')
+;';
+ pwg_query($query);
+ break;
+
+ case 'deactivate':
+ if (!isset($crt_db_language))
+ {
+ $errors[] = 'CANNOT DEACTIVATE - LANGUAGE IS ALREADY DEACTIVATED';
+ break;
+ }
+
+ if ($language_id == get_default_language())
+ {
+ $errors[] = 'CANNOT DEACTIVATE - LANGUAGE IS DEFAULT LANGUAGE';
+ break;
+ }
+
+ $query = '
+DELETE
+ FROM '.LANGUAGES_TABLE.'
+ WHERE id= \''.$language_id.'\'
+;';
+ pwg_query($query);
+ break;
+
+ case 'delete':
+ if (!empty($crt_db_language))
+ {
+ $errors[] = 'CANNOT DELETE - LANGUAGE IS ACTIVATED';
+ break;
+ }
+ if (!isset($this->fs_languages[$language_id]))
+ {
+ $errors[] = 'CANNOT DELETE - LANGUAGE DOES NOT EXIST';
+ break;
+ }
+
+ // Set default language to user who are using this language
+ $query = '
+UPDATE '.USER_INFOS_TABLE.'
+ SET language = \''.get_default_language().'\'
+ WHERE language = \''.$language_id.'\'
+;';
+ pwg_query($query);
+
+ deltree(PHPWG_ROOT_PATH.'language/'.$language_id, PHPWG_ROOT_PATH.'language/trash');
+ break;
+
+ case 'set_default':
+ $query = '
+UPDATE '.USER_INFOS_TABLE.'
+ SET language = \''.$language_id.'\'
+ WHERE user_id IN ('.$conf['default_user_id'].', '.$conf['guest_id'].')
+;';
+ pwg_query($query);
+ break;
+ }
+ return $errors;
+ }
+
+ /**
+ * Get languages defined in the language directory
+ */
+ function get_fs_languages($target_charset = null)
+ {
+ if ( empty($target_charset) )
+ {
+ $target_charset = get_pwg_charset();
+ }
+ $target_charset = strtolower($target_charset);
+
+ $dir = opendir(PHPWG_ROOT_PATH.'language');
+ while ($file = readdir($dir))
+ {
+ if ($file!='.' and $file!='..')
+ {
+ $path = PHPWG_ROOT_PATH.'language/'.$file;
+ if (is_dir($path) and !is_link($path)
+ and preg_match('/^[a-zA-Z0-9-_]+$/', $file )
+ and file_exists($path.'/common.lang.php')
+ )
+ {
+ $language = array(
+ 'name'=>$file,
+ 'code'=>$file,
+ 'version'=>'0',
+ 'uri'=>'',
+ 'author'=>'',
+ );
+ $plg_data = implode( '', file($path.'/common.lang.php') );
+
+ if ( preg_match("|Language Name: (.*)|", $plg_data, $val) )
+ {
+ $language['name'] = trim( $val[1] );
+ $language['name'] = convert_charset($language['name'], 'utf-8', $target_charset);
+ }
+ if (preg_match("|Version: (.*)|", $plg_data, $val))
+ {
+ $language['version'] = trim($val[1]);
+ }
+ if ( preg_match("|Language URI: (.*)|", $plg_data, $val) )
+ {
+ $language['uri'] = trim($val[1]);
+ }
+ if ( preg_match("|Author: (.*)|", $plg_data, $val) )
+ {
+ $language['author'] = trim($val[1]);
+ }
+ if ( preg_match("|Author URI: (.*)|", $plg_data, $val) )
+ {
+ $language['author uri'] = trim($val[1]);
+ }
+ if (!empty($language['uri']) and strpos($language['uri'] , 'extension_view.php?eid='))
+ {
+ list( , $extension) = explode('extension_view.php?eid=', $language['uri']);
+ if (is_numeric($extension)) $language['extension'] = $extension;
+ }
+ // IMPORTANT SECURITY !
+ $language = array_map('htmlspecialchars', $language);
+ $this->fs_languages[$file] = $language;
+ }
+ }
+ }
+ closedir($dir);
+ @uasort($this->fs_languages, 'name_compare');
+ }
+
+ function get_db_languages()
+ {
+ $query = '
+ SELECT id, name
+ FROM '.LANGUAGES_TABLE.'
+ ORDER BY name ASC
+ ;';
+ $result = pwg_query($query);
+
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $this->db_languages[ $row['id'] ] = $row['name'];
+ }
+ }
+
+ /**
+ * Retrieve PEM server datas to $server_languages
+ */
+ function get_server_languages($new=false)
+ {
+ global $user, $conf;
+
+ $get_data = array(
+ 'category_id' => $conf['pem_languages_category'],
+ 'format' => 'php',
+ );
+
+ // Retrieve PEM versions
+ $version = PHPWG_VERSION;
+ $versions_to_check = array();
+ $url = PEM_URL . '/api/get_version_list.php';
+ if (fetchRemote($url, $result, $get_data) and $pem_versions = @unserialize($result))
+ {
+ if (!preg_match('/^\d+\.\d+\.\d+$/', $version))
+ {
+ $version = $pem_versions[0]['name'];
+ }
+ $branch = get_branch_from_version($version);
+ foreach ($pem_versions as $pem_version)
+ {
+ if (strpos($pem_version['name'], $branch) === 0)
+ {
+ $versions_to_check[] = $pem_version['id'];
+ }
+ }
+ }
+ if (empty($versions_to_check))
+ {
+ return false;
+ }
+
+ // Languages to check
+ $languages_to_check = array();
+ foreach($this->fs_languages as $fs_language)
+ {
+ if (isset($fs_language['extension']))
+ {
+ $languages_to_check[] = $fs_language['extension'];
+ }
+ }
+
+ // Retrieve PEM languages infos
+ $url = PEM_URL . '/api/get_revision_list.php';
+ $get_data = array_merge($get_data, array(
+ 'last_revision_only' => 'true',
+ 'version' => implode(',', $versions_to_check),
+ 'lang' => $user['language'],
+ 'get_nb_downloads' => 'true',
+ )
+ );
+ if (!empty($languages_to_check))
+ {
+ if ($new)
+ {
+ $get_data['extension_exclude'] = implode(',', $languages_to_check);
+ }
+ else
+ {
+ $get_data['extension_include'] = implode(',', $languages_to_check);
+ }
+ }
+
+ if (fetchRemote($url, $result, $get_data))
+ {
+ $pem_languages = @unserialize($result);
+ if (!is_array($pem_languages))
+ {
+ return false;
+ }
+ foreach ($pem_languages as $language)
+ {
+ if (preg_match('/^.*? \[[A-Z]{2}\]$/', $language['extension_name']))
+ {
+ $this->server_languages[$language['extension_id']] = $language;
+ }
+ }
+ @uasort($this->server_languages, array($this, 'extension_name_compare'));
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Extract language files from archive
+ *
+ * @param string - install or upgrade
+ * @param string - remote revision identifier (numeric)
+ * @param string - language id or extension id
+ */
+ function extract_language_files($action, $revision, $dest='')
+ {
+ if ($archive = tempnam( PHPWG_ROOT_PATH.'language', 'zip'))
+ {
+ $url = PEM_URL . '/download.php';
+ $get_data = array(
+ 'rid' => $revision,
+ 'origin' => 'piwigo_'.$action,
+ );
+
+ if ($handle = @fopen($archive, 'wb') and fetchRemote($url, $handle, $get_data))
+ {
+ fclose($handle);
+ include_once(PHPWG_ROOT_PATH.'admin/include/pclzip.lib.php');
+ $zip = new PclZip($archive);
+ if ($list = $zip->listContent())
+ {
+ foreach ($list as $file)
+ {
+ // we search common.lang.php in archive
+ if (basename($file['filename']) == 'common.lang.php'
+ and (!isset($main_filepath)
+ or strlen($file['filename']) < strlen($main_filepath)))
+ {
+ $main_filepath = $file['filename'];
+ }
+ }
+ if (isset($main_filepath))
+ {
+ $root = basename(dirname($main_filepath)); // common.lang.php path in archive
+ if (preg_match('/^[a-z]{2}_[A-Z]{2}$/', $root))
+ {
+ if ($action == 'install')
+ {
+ $dest = $root;
+ }
+ $extract_path = PHPWG_ROOT_PATH.'language/'.$dest;
+ if (
+ $result = $zip->extract(
+ PCLZIP_OPT_PATH, $extract_path,
+ PCLZIP_OPT_REMOVE_PATH, $root,
+ PCLZIP_OPT_REPLACE_NEWER
+ )
+ )
+ {
+ foreach ($result as $file)
+ {
+ if ($file['stored_filename'] == $main_filepath)
+ {
+ $status = $file['status'];
+ break;
+ }
+ }
+ if ($status == 'ok')
+ {
+ $this->get_fs_languages();
+ if ($action == 'install')
+ {
+ $this->perform_action('activate', $dest);
+ }
+ }
+ if (file_exists($extract_path.'/obsolete.list')
+ and $old_files = file($extract_path.'/obsolete.list', FILE_IGNORE_NEW_LINES)
+ and !empty($old_files))
+ {
+ $old_files[] = 'obsolete.list';
+ foreach($old_files as $old_file)
+ {
+ $path = $extract_path.'/'.$old_file;
+ if (is_file($path))
+ {
+ @unlink($path);
+ }
+ elseif (is_dir($path))
+ {
+ deltree($path, PHPWG_ROOT_PATH.'language/trash');
+ }
+ }
+ }
+ }
+ else $status = 'extract_error';
+ }
+ else $status = 'archive_error';
+ }
+ else $status = 'archive_error';
+ }
+ else $status = 'archive_error';
+ }
+ else $status = 'dl_archive_error';
+ }
+ else $status = 'temp_path_error';
+
+ @unlink($archive);
+ return $status;
+ }
+
+ /**
+ * Sort functions
+ */
+ function extension_name_compare($a, $b)
+ {
+ return strcmp(strtolower($a['extension_name']), strtolower($b['extension_name']));
+ }
+}
+?>
\ No newline at end of file
diff --git a/sources/admin/include/mysqldump.php b/sources/admin/include/mysqldump.php
new file mode 100644
index 0000000..515d86a
--- /dev/null
+++ b/sources/admin/include/mysqldump.php
@@ -0,0 +1,421 @@
+
+* $connection = @mysql_connect($dbhost,$dbuser,$dbpsw);
+* $dumper = new MySQLDump($dbname,'filename.sql',false,false);
+* $dumper->doDump();
+*
+*
+* Special thanks to:
+* - Andrea Ingaglio helping in development of all class code
+* - Dylan Pugh for precious advices halfing the size of the output file and for helping in debug
+*
+* @name MySQLDump
+* @author Daniele Vigan - CreativeFactory.it
+* @version 2.20 - 02/11/2007
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*/
+
+class MySQLDump {
+ /**
+ * @access private
+ */
+ var $database = null;
+
+ /**
+ * @access private
+ */
+ var $compress = false;
+
+ /**
+ * @access private
+ */
+ var $hexValue = false;
+
+ /**
+ * The output filename
+ * @access private
+ */
+ var $filename = null;
+
+ /**
+ * The pointer of the output file
+ * @access private
+ */
+ var $file = null;
+
+ /**
+ * @access private
+ */
+ var $isWritten = false;
+
+ /**
+ * Class constructor
+ * @param string $db The database name
+ * @param string $filepath The file where the dump will be written
+ * @param boolean $compress It defines if the output file is compress (gzip) or not
+ * @param boolean $hexValue It defines if the outup values are base-16 or not
+ */
+ function MYSQLDump($db = null, $filepath = 'dump.sql', $compress = false, $hexValue = false){
+ $this->compress = $compress;
+ if ( !$this->setOutputFile($filepath) )
+ return false;
+ return $this->setDatabase($db);
+ }
+
+ /**
+ * Sets the database to work on
+ * @param string $db The database name
+ */
+ function setDatabase($db){
+ $this->database = $db;
+ if ( !@mysql_select_db($this->database) )
+ return false;
+ return true;
+ }
+
+ /**
+ * Returns the database where the class is working on
+ * @return string
+ */
+ function getDatabase(){
+ return $this->database;
+ }
+
+ /**
+ * Sets the output file type (It can be made only if the file hasn't been already written)
+ * @param boolean $compress If it's true, the output file will be compressed
+ */
+ function setCompress($compress){
+ if ( $this->isWritten )
+ return false;
+ $this->compress = $compress;
+ $this->openFile($this->filename);
+ return true;
+ }
+
+ /**
+ * Returns if the output file is or not compressed
+ * @return boolean
+ */
+ function getCompress(){
+ return $this->compress;
+ }
+
+ /**
+ * Sets the output file
+ * @param string $filepath The file where the dump will be written
+ */
+ function setOutputFile($filepath){
+ if ( $this->isWritten )
+ return false;
+ $this->filename = $filepath;
+ $this->file = $this->openFile($this->filename);
+ return $this->file;
+ }
+
+ /**
+ * Returns the output filename
+ * @return string
+ */
+ function getOutputFile(){
+ return $this->filename;
+ }
+
+ /**
+ * Writes to file the $table's structure
+ * @param string $table The table name
+ */
+ function getTableStructure($table){
+ if ( !$this->setDatabase($this->database) )
+ return false;
+ // Structure Header
+ $structure = "-- \n";
+ $structure .= "-- Table structure for table `{$table}` \n";
+ $structure .= "-- \n\n";
+ // Dump Structure
+ $structure .= 'DROP TABLE IF EXISTS `'.$table.'`;'."\n";
+ $structure .= "CREATE TABLE `".$table."` (\n";
+ $records = @mysql_query('SHOW FIELDS FROM `'.$table.'`');
+ if ( @mysql_num_rows($records) == 0 )
+ return false;
+ while ( $record = mysql_fetch_assoc($records) ) {
+ $structure .= '`'.$record['Field'].'` '.$record['Type'];
+ if ( isset($record['Default']) )
+ $structure .= ' DEFAULT \''.$record['Default'].'\'';
+ if ( @strcmp($record['Null'],'YES') != 0 )
+ $structure .= ' NOT NULL';
+ elseif ( is_null($record['Default']) )
+ $structure .= ' DEFAULT NULL';
+ if ( !empty($record['Extra']) )
+ $structure .= ' '.$record['Extra'];
+ $structure .= ",\n";
+ }
+ $structure = @ereg_replace(",\n$", null, $structure);
+
+ // Save all Column Indexes
+ $structure .= $this->getSqlKeysTable($table);
+ $structure .= "\n)";
+
+ //Save table engine
+ $records = @mysql_query("SHOW TABLE STATUS LIKE '".$table."'");
+
+ if ( $record = @mysql_fetch_assoc($records) ) {
+ if ( !empty($record['Engine']) )
+ $structure .= ' ENGINE='.$record['Engine'];
+ if ( !empty($record['Auto_increment']) )
+ $structure .= ' AUTO_INCREMENT='.$record['Auto_increment'];
+ }
+
+ $structure .= ";\n\n-- --------------------------------------------------------\n\n";
+ $this->saveToFile($this->file,$structure);
+ }
+
+ /**
+ * Writes to file the $table's data
+ * @param string $table The table name
+ * @param boolean $hexValue It defines if the output is base 16 or not
+ */
+ function getTableData($table,$hexValue = true) {
+ if ( !$this->setDatabase($this->database) )
+ return false;
+ // Header
+ $data = "-- \n";
+ $data .= "-- Dumping data for table `$table` \n";
+ $data .= "-- \n\n";
+
+ $records = mysql_query('SHOW FIELDS FROM `'.$table.'`');
+ $num_fields = @mysql_num_rows($records);
+ if ( $num_fields == 0 )
+ return false;
+ // Field names
+ $selectStatement = "SELECT ";
+ $insertStatement = "INSERT INTO `$table` (";
+ $hexField = array();
+ for ($x = 0; $x < $num_fields; $x++) {
+ $record = @mysql_fetch_assoc($records);
+ if ( ($hexValue) && ($this->isTextValue($record['Type'])) ) {
+ $selectStatement .= 'HEX(`'.$record['Field'].'`)';
+ $hexField [$x] = true;
+ }
+ else
+ $selectStatement .= '`'.$record['Field'].'`';
+ $insertStatement .= '`'.$record['Field'].'`';
+ $insertStatement .= ", ";
+ $selectStatement .= ", ";
+ }
+ $insertStatement = @substr($insertStatement,0,-2).') VALUES'."\n";
+ $selectStatement = @substr($selectStatement,0,-2).' FROM `'.$table.'`';
+
+ $records = @mysql_query($selectStatement);
+ $num_rows = @mysql_num_rows($records);
+ $num_fields = @mysql_num_fields($records);
+ // Dump data
+ if ( $num_rows > 0 ) {
+ $data .= $insertStatement;
+ for ($i = 0; $i < $num_rows; $i++) {
+ $record = @mysql_fetch_assoc($records);
+ $data .= ' (';
+ for ($j = 0; $j < $num_fields; $j++) {
+ $field_name = @mysql_field_name($records, $j);
+ if ( @$hexField[$j] && (@strlen($record[$field_name]) > 0) )
+ $data .= "0x".$record[$field_name];
+ elseif (is_null($record[$field_name]))
+ $data .= "NULL";
+ else
+ $data .= "'".@str_replace('\"','"',@mysql_real_escape_string($record[$field_name]))."'";
+ $data .= ',';
+ }
+ $data = @substr($data,0,-1).")";
+ $data .= ( $i < ($num_rows-1) ) ? ',' : ';';
+ $data .= "\n";
+ //if data in greather than 1MB save
+ if (strlen($data) > 1048576) {
+ $this->saveToFile($this->file,$data);
+ $data = '';
+ }
+ }
+ $data .= "\n-- --------------------------------------------------------\n\n";
+ $this->saveToFile($this->file,$data);
+ }
+ }
+
+ /**
+ * Writes to file all the selected database tables structure
+ * @return boolean
+ */
+ function getDatabaseStructure(){
+ $records = @mysql_query('SHOW TABLES');
+ if ( @mysql_num_rows($records) == 0 )
+ return false;
+ $structure = '';
+ while ( $record = @mysql_fetch_row($records) ) {
+ $structure .= $this->getTableStructure($record[0]);
+ }
+ return true;
+ }
+
+ /**
+ * Writes to file all the selected database tables data
+ * @param boolean $hexValue It defines if the output is base-16 or not
+ */
+ function getDatabaseData($hexValue = true){
+ $records = @mysql_query('SHOW TABLES');
+ if ( @mysql_num_rows($records) == 0 )
+ return false;
+ while ( $record = @mysql_fetch_row($records) ) {
+ $this->getTableData($record[0],$hexValue);
+ }
+ }
+
+ /**
+ * Writes to file the selected database dump
+ */
+ function doDump() {
+ $this->saveToFile($this->file,"SET FOREIGN_KEY_CHECKS = 0;\n\n");
+ $this->getDatabaseStructure();
+ $this->getDatabaseData($this->hexValue);
+ $this->saveToFile($this->file,"SET FOREIGN_KEY_CHECKS = 1;\n\n");
+ $this->closeFile($this->file);
+ return true;
+ }
+
+ /**
+ * @deprecated Look at the doDump() method
+ */
+ function writeDump($filename) {
+ if ( !$this->setOutputFile($filename) )
+ return false;
+ $this->doDump();
+ $this->closeFile($this->file);
+ return true;
+ }
+
+ /**
+ * @access private
+ */
+ function getSqlKeysTable ($table) {
+ $primary = "";
+ $unique = array();
+ $index = array();
+ $fulltext = array();
+ $results = mysql_query("SHOW KEYS FROM `{$table}`");
+ if ( @mysql_num_rows($results) == 0 )
+ return false;
+ while($row = mysql_fetch_object($results)) {
+ if (($row->Key_name == 'PRIMARY') AND ($row->Index_type == 'BTREE')) {
+ if ( $primary == "" )
+ $primary = " PRIMARY KEY (`{$row->Column_name}`";
+ else
+ $primary .= ", `{$row->Column_name}`";
+ }
+ if (($row->Key_name != 'PRIMARY') AND ($row->Non_unique == '0') AND ($row->Index_type == 'BTREE')) {
+ if ( (empty($unique)) OR (empty($unique[$row->Key_name])) )
+ $unique[$row->Key_name] = " UNIQUE KEY `{$row->Key_name}` (`{$row->Column_name}`";
+ else
+ $unique[$row->Key_name] .= ", `{$row->Column_name}`";
+ }
+ if (($row->Key_name != 'PRIMARY') AND ($row->Non_unique == '1') AND ($row->Index_type == 'BTREE')) {
+ if ( (empty($index)) OR (empty($index[$row->Key_name])) )
+ $index[$row->Key_name] = " KEY `{$row->Key_name}` (`{$row->Column_name}`";
+ else
+ $index[$row->Key_name] .= ", `{$row->Column_name}`";
+ }
+ if (($row->Key_name != 'PRIMARY') AND ($row->Non_unique == '1') AND ($row->Index_type == 'FULLTEXT')) {
+ if ( (empty($fulltext)) OR (empty($fulltext[$row->Key_name])) )
+ $fulltext[$row->Key_name] = " FULLTEXT `{$row->Key_name}` (`{$row->Column_name}`";
+ else
+ $fulltext[$row->Key_name] .= ", `{$row->Column_name}`";
+ }
+ }
+ $sqlKeyStatement = '';
+ // generate primary, unique, key and fulltext
+ if ( $primary != "" ) {
+ $sqlKeyStatement .= ",\n";
+ $primary .= ")";
+ $sqlKeyStatement .= $primary;
+ }
+ if (!empty($unique)) {
+ foreach ($unique as $keyName => $keyDef) {
+ $sqlKeyStatement .= ",\n";
+ $keyDef .= ")";
+ $sqlKeyStatement .= $keyDef;
+
+ }
+ }
+ if (!empty($index)) {
+ foreach ($index as $keyName => $keyDef) {
+ $sqlKeyStatement .= ",\n";
+ $keyDef .= ")";
+ $sqlKeyStatement .= $keyDef;
+ }
+ }
+ if (!empty($fulltext)) {
+ foreach ($fulltext as $keyName => $keyDef) {
+ $sqlKeyStatement .= ",\n";
+ $keyDef .= ")";
+ $sqlKeyStatement .= $keyDef;
+ }
+ }
+ return $sqlKeyStatement;
+ }
+
+ /**
+ * @access private
+ */
+ function isTextValue($field_type) {
+ switch ($field_type) {
+ case "tinytext":
+ case "text":
+ case "mediumtext":
+ case "longtext":
+ case "binary":
+ case "varbinary":
+ case "tinyblob":
+ case "blob":
+ case "mediumblob":
+ case "longblob":
+ return True;
+ break;
+ default:
+ return False;
+ }
+ }
+
+ /**
+ * @access private
+ */
+ function openFile($filename) {
+ $file = false;
+ if ( $this->compress )
+ $file = @gzopen($filename, "w9");
+ else
+ $file = @fopen($filename, "w");
+ return $file;
+ }
+
+ /**
+ * @access private
+ */
+ function saveToFile($file, $data) {
+ if ( $this->compress )
+ @gzwrite($file, $data);
+ else
+ @fwrite($file, $data);
+ $this->isWritten = true;
+ }
+
+ /**
+ * @access private
+ */
+ function closeFile($file) {
+ if ( $this->compress )
+ @gzclose($file);
+ else
+ @fclose($file);
+ }
+}
+?>
\ No newline at end of file
diff --git a/sources/admin/include/pclzip.lib.php b/sources/admin/include/pclzip.lib.php
new file mode 100644
index 0000000..e7facc1
--- /dev/null
+++ b/sources/admin/include/pclzip.lib.php
@@ -0,0 +1,5694 @@
+zipname = $p_zipname;
+ $this->zip_fd = 0;
+ $this->magic_quotes_status = -1;
+
+ // ----- Return
+ return;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function :
+ // create($p_filelist, $p_add_dir="", $p_remove_dir="")
+ // create($p_filelist, $p_option, $p_option_value, ...)
+ // Description :
+ // This method supports two different synopsis. The first one is historical.
+ // This method creates a Zip Archive. The Zip file is created in the
+ // filesystem. The files and directories indicated in $p_filelist
+ // are added in the archive. See the parameters description for the
+ // supported format of $p_filelist.
+ // When a directory is in the list, the directory and its content is added
+ // in the archive.
+ // In this synopsis, the function takes an optional variable list of
+ // options. See bellow the supported options.
+ // Parameters :
+ // $p_filelist : An array containing file or directory names, or
+ // a string containing one filename or one directory name, or
+ // a string containing a list of filenames and/or directory
+ // names separated by spaces.
+ // $p_add_dir : A path to add before the real path of the archived file,
+ // in order to have it memorized in the archive.
+ // $p_remove_dir : A path to remove from the real path of the file to archive,
+ // in order to have a shorter path memorized in the archive.
+ // When $p_add_dir and $p_remove_dir are set, $p_remove_dir
+ // is removed first, before $p_add_dir is added.
+ // Options :
+ // PCLZIP_OPT_ADD_PATH :
+ // PCLZIP_OPT_REMOVE_PATH :
+ // PCLZIP_OPT_REMOVE_ALL_PATH :
+ // PCLZIP_OPT_COMMENT :
+ // PCLZIP_CB_PRE_ADD :
+ // PCLZIP_CB_POST_ADD :
+ // Return Values :
+ // 0 on failure,
+ // The list of the added files, with a status of the add action.
+ // (see PclZip::listContent() for list entry format)
+ // --------------------------------------------------------------------------------
+ function create($p_filelist)
+ {
+ $v_result=1;
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Set default values
+ $v_options = array();
+ $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE;
+
+ // ----- Look for variable options arguments
+ $v_size = func_num_args();
+
+ // ----- Look for arguments
+ if ($v_size > 1) {
+ // ----- Get the arguments
+ $v_arg_list = func_get_args();
+
+ // ----- Remove from the options list the first argument
+ array_shift($v_arg_list);
+ $v_size--;
+
+ // ----- Look for first arg
+ if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) {
+
+ // ----- Parse the options
+ $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options,
+ array (PCLZIP_OPT_REMOVE_PATH => 'optional',
+ PCLZIP_OPT_REMOVE_ALL_PATH => 'optional',
+ PCLZIP_OPT_ADD_PATH => 'optional',
+ PCLZIP_CB_PRE_ADD => 'optional',
+ PCLZIP_CB_POST_ADD => 'optional',
+ PCLZIP_OPT_NO_COMPRESSION => 'optional',
+ PCLZIP_OPT_COMMENT => 'optional',
+ PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional',
+ PCLZIP_OPT_TEMP_FILE_ON => 'optional',
+ PCLZIP_OPT_TEMP_FILE_OFF => 'optional'
+ //, PCLZIP_OPT_CRYPT => 'optional'
+ ));
+ if ($v_result != 1) {
+ return 0;
+ }
+ }
+
+ // ----- Look for 2 args
+ // Here we need to support the first historic synopsis of the
+ // method.
+ else {
+
+ // ----- Get the first argument
+ $v_options[PCLZIP_OPT_ADD_PATH] = $v_arg_list[0];
+
+ // ----- Look for the optional second argument
+ if ($v_size == 2) {
+ $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1];
+ }
+ else if ($v_size > 2) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER,
+ "Invalid number / type of arguments");
+ return 0;
+ }
+ }
+ }
+
+ // ----- Look for default option values
+ $this->privOptionDefaultThreshold($v_options);
+
+ // ----- Init
+ $v_string_list = array();
+ $v_att_list = array();
+ $v_filedescr_list = array();
+ $p_result_list = array();
+
+ // ----- Look if the $p_filelist is really an array
+ if (is_array($p_filelist)) {
+
+ // ----- Look if the first element is also an array
+ // This will mean that this is a file description entry
+ if (isset($p_filelist[0]) && is_array($p_filelist[0])) {
+ $v_att_list = $p_filelist;
+ }
+
+ // ----- The list is a list of string names
+ else {
+ $v_string_list = $p_filelist;
+ }
+ }
+
+ // ----- Look if the $p_filelist is a string
+ else if (is_string($p_filelist)) {
+ // ----- Create a list from the string
+ $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist);
+ }
+
+ // ----- Invalid variable type for $p_filelist
+ else {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_filelist");
+ return 0;
+ }
+
+ // ----- Reformat the string list
+ if (sizeof($v_string_list) != 0) {
+ foreach ($v_string_list as $v_string) {
+ if ($v_string != '') {
+ $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string;
+ }
+ else {
+ }
+ }
+ }
+
+ // ----- For each file in the list check the attributes
+ $v_supported_attributes
+ = array ( PCLZIP_ATT_FILE_NAME => 'mandatory'
+ ,PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional'
+ ,PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional'
+ ,PCLZIP_ATT_FILE_MTIME => 'optional'
+ ,PCLZIP_ATT_FILE_CONTENT => 'optional'
+ ,PCLZIP_ATT_FILE_COMMENT => 'optional'
+ );
+ foreach ($v_att_list as $v_entry) {
+ $v_result = $this->privFileDescrParseAtt($v_entry,
+ $v_filedescr_list[],
+ $v_options,
+ $v_supported_attributes);
+ if ($v_result != 1) {
+ return 0;
+ }
+ }
+
+ // ----- Expand the filelist (expand directories)
+ $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options);
+ if ($v_result != 1) {
+ return 0;
+ }
+
+ // ----- Call the create fct
+ $v_result = $this->privCreate($v_filedescr_list, $p_result_list, $v_options);
+ if ($v_result != 1) {
+ return 0;
+ }
+
+ // ----- Return
+ return $p_result_list;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function :
+ // add($p_filelist, $p_add_dir="", $p_remove_dir="")
+ // add($p_filelist, $p_option, $p_option_value, ...)
+ // Description :
+ // This method supports two synopsis. The first one is historical.
+ // This methods add the list of files in an existing archive.
+ // If a file with the same name already exists, it is added at the end of the
+ // archive, the first one is still present.
+ // If the archive does not exist, it is created.
+ // Parameters :
+ // $p_filelist : An array containing file or directory names, or
+ // a string containing one filename or one directory name, or
+ // a string containing a list of filenames and/or directory
+ // names separated by spaces.
+ // $p_add_dir : A path to add before the real path of the archived file,
+ // in order to have it memorized in the archive.
+ // $p_remove_dir : A path to remove from the real path of the file to archive,
+ // in order to have a shorter path memorized in the archive.
+ // When $p_add_dir and $p_remove_dir are set, $p_remove_dir
+ // is removed first, before $p_add_dir is added.
+ // Options :
+ // PCLZIP_OPT_ADD_PATH :
+ // PCLZIP_OPT_REMOVE_PATH :
+ // PCLZIP_OPT_REMOVE_ALL_PATH :
+ // PCLZIP_OPT_COMMENT :
+ // PCLZIP_OPT_ADD_COMMENT :
+ // PCLZIP_OPT_PREPEND_COMMENT :
+ // PCLZIP_CB_PRE_ADD :
+ // PCLZIP_CB_POST_ADD :
+ // Return Values :
+ // 0 on failure,
+ // The list of the added files, with a status of the add action.
+ // (see PclZip::listContent() for list entry format)
+ // --------------------------------------------------------------------------------
+ function add($p_filelist)
+ {
+ $v_result=1;
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Set default values
+ $v_options = array();
+ $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE;
+
+ // ----- Look for variable options arguments
+ $v_size = func_num_args();
+
+ // ----- Look for arguments
+ if ($v_size > 1) {
+ // ----- Get the arguments
+ $v_arg_list = func_get_args();
+
+ // ----- Remove form the options list the first argument
+ array_shift($v_arg_list);
+ $v_size--;
+
+ // ----- Look for first arg
+ if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) {
+
+ // ----- Parse the options
+ $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options,
+ array (PCLZIP_OPT_REMOVE_PATH => 'optional',
+ PCLZIP_OPT_REMOVE_ALL_PATH => 'optional',
+ PCLZIP_OPT_ADD_PATH => 'optional',
+ PCLZIP_CB_PRE_ADD => 'optional',
+ PCLZIP_CB_POST_ADD => 'optional',
+ PCLZIP_OPT_NO_COMPRESSION => 'optional',
+ PCLZIP_OPT_COMMENT => 'optional',
+ PCLZIP_OPT_ADD_COMMENT => 'optional',
+ PCLZIP_OPT_PREPEND_COMMENT => 'optional',
+ PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional',
+ PCLZIP_OPT_TEMP_FILE_ON => 'optional',
+ PCLZIP_OPT_TEMP_FILE_OFF => 'optional'
+ //, PCLZIP_OPT_CRYPT => 'optional'
+ ));
+ if ($v_result != 1) {
+ return 0;
+ }
+ }
+
+ // ----- Look for 2 args
+ // Here we need to support the first historic synopsis of the
+ // method.
+ else {
+
+ // ----- Get the first argument
+ $v_options[PCLZIP_OPT_ADD_PATH] = $v_add_path = $v_arg_list[0];
+
+ // ----- Look for the optional second argument
+ if ($v_size == 2) {
+ $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1];
+ }
+ else if ($v_size > 2) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments");
+
+ // ----- Return
+ return 0;
+ }
+ }
+ }
+
+ // ----- Look for default option values
+ $this->privOptionDefaultThreshold($v_options);
+
+ // ----- Init
+ $v_string_list = array();
+ $v_att_list = array();
+ $v_filedescr_list = array();
+ $p_result_list = array();
+
+ // ----- Look if the $p_filelist is really an array
+ if (is_array($p_filelist)) {
+
+ // ----- Look if the first element is also an array
+ // This will mean that this is a file description entry
+ if (isset($p_filelist[0]) && is_array($p_filelist[0])) {
+ $v_att_list = $p_filelist;
+ }
+
+ // ----- The list is a list of string names
+ else {
+ $v_string_list = $p_filelist;
+ }
+ }
+
+ // ----- Look if the $p_filelist is a string
+ else if (is_string($p_filelist)) {
+ // ----- Create a list from the string
+ $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist);
+ }
+
+ // ----- Invalid variable type for $p_filelist
+ else {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type '".gettype($p_filelist)."' for p_filelist");
+ return 0;
+ }
+
+ // ----- Reformat the string list
+ if (sizeof($v_string_list) != 0) {
+ foreach ($v_string_list as $v_string) {
+ $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string;
+ }
+ }
+
+ // ----- For each file in the list check the attributes
+ $v_supported_attributes
+ = array ( PCLZIP_ATT_FILE_NAME => 'mandatory'
+ ,PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional'
+ ,PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional'
+ ,PCLZIP_ATT_FILE_MTIME => 'optional'
+ ,PCLZIP_ATT_FILE_CONTENT => 'optional'
+ ,PCLZIP_ATT_FILE_COMMENT => 'optional'
+ );
+ foreach ($v_att_list as $v_entry) {
+ $v_result = $this->privFileDescrParseAtt($v_entry,
+ $v_filedescr_list[],
+ $v_options,
+ $v_supported_attributes);
+ if ($v_result != 1) {
+ return 0;
+ }
+ }
+
+ // ----- Expand the filelist (expand directories)
+ $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options);
+ if ($v_result != 1) {
+ return 0;
+ }
+
+ // ----- Call the create fct
+ $v_result = $this->privAdd($v_filedescr_list, $p_result_list, $v_options);
+ if ($v_result != 1) {
+ return 0;
+ }
+
+ // ----- Return
+ return $p_result_list;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : listContent()
+ // Description :
+ // This public method, gives the list of the files and directories, with their
+ // properties.
+ // The properties of each entries in the list are (used also in other functions) :
+ // filename : Name of the file. For a create or add action it is the filename
+ // given by the user. For an extract function it is the filename
+ // of the extracted file.
+ // stored_filename : Name of the file / directory stored in the archive.
+ // size : Size of the stored file.
+ // compressed_size : Size of the file's data compressed in the archive
+ // (without the headers overhead)
+ // mtime : Last known modification date of the file (UNIX timestamp)
+ // comment : Comment associated with the file
+ // folder : true | false
+ // index : index of the file in the archive
+ // status : status of the action (depending of the action) :
+ // Values are :
+ // ok : OK !
+ // filtered : the file / dir is not extracted (filtered by user)
+ // already_a_directory : the file can not be extracted because a
+ // directory with the same name already exists
+ // write_protected : the file can not be extracted because a file
+ // with the same name already exists and is
+ // write protected
+ // newer_exist : the file was not extracted because a newer file exists
+ // path_creation_fail : the file is not extracted because the folder
+ // does not exist and can not be created
+ // write_error : the file was not extracted because there was a
+ // error while writing the file
+ // read_error : the file was not extracted because there was a error
+ // while reading the file
+ // invalid_header : the file was not extracted because of an archive
+ // format error (bad file header)
+ // Note that each time a method can continue operating when there
+ // is an action error on a file, the error is only logged in the file status.
+ // Return Values :
+ // 0 on an unrecoverable failure,
+ // The list of the files in the archive.
+ // --------------------------------------------------------------------------------
+ function listContent()
+ {
+ $v_result=1;
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Check archive
+ if (!$this->privCheckFormat()) {
+ return(0);
+ }
+
+ // ----- Call the extracting fct
+ $p_list = array();
+ if (($v_result = $this->privList($p_list)) != 1)
+ {
+ unset($p_list);
+ return(0);
+ }
+
+ // ----- Return
+ return $p_list;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function :
+ // extract($p_path="./", $p_remove_path="")
+ // extract([$p_option, $p_option_value, ...])
+ // Description :
+ // This method supports two synopsis. The first one is historical.
+ // This method extract all the files / directories from the archive to the
+ // folder indicated in $p_path.
+ // If you want to ignore the 'root' part of path of the memorized files
+ // you can indicate this in the optional $p_remove_path parameter.
+ // By default, if a newer file with the same name already exists, the
+ // file is not extracted.
+ //
+ // If both PCLZIP_OPT_PATH and PCLZIP_OPT_ADD_PATH aoptions
+ // are used, the path indicated in PCLZIP_OPT_ADD_PATH is append
+ // at the end of the path value of PCLZIP_OPT_PATH.
+ // Parameters :
+ // $p_path : Path where the files and directories are to be extracted
+ // $p_remove_path : First part ('root' part) of the memorized path
+ // (if any similar) to remove while extracting.
+ // Options :
+ // PCLZIP_OPT_PATH :
+ // PCLZIP_OPT_ADD_PATH :
+ // PCLZIP_OPT_REMOVE_PATH :
+ // PCLZIP_OPT_REMOVE_ALL_PATH :
+ // PCLZIP_CB_PRE_EXTRACT :
+ // PCLZIP_CB_POST_EXTRACT :
+ // Return Values :
+ // 0 or a negative value on failure,
+ // The list of the extracted files, with a status of the action.
+ // (see PclZip::listContent() for list entry format)
+ // --------------------------------------------------------------------------------
+ function extract()
+ {
+ $v_result=1;
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Check archive
+ if (!$this->privCheckFormat()) {
+ return(0);
+ }
+
+ // ----- Set default values
+ $v_options = array();
+// $v_path = "./";
+ $v_path = '';
+ $v_remove_path = "";
+ $v_remove_all_path = false;
+
+ // ----- Look for variable options arguments
+ $v_size = func_num_args();
+
+ // ----- Default values for option
+ $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE;
+
+ // ----- Look for arguments
+ if ($v_size > 0) {
+ // ----- Get the arguments
+ $v_arg_list = func_get_args();
+
+ // ----- Look for first arg
+ if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) {
+
+ // ----- Parse the options
+ $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options,
+ array (PCLZIP_OPT_PATH => 'optional',
+ PCLZIP_OPT_REMOVE_PATH => 'optional',
+ PCLZIP_OPT_REMOVE_ALL_PATH => 'optional',
+ PCLZIP_OPT_ADD_PATH => 'optional',
+ PCLZIP_CB_PRE_EXTRACT => 'optional',
+ PCLZIP_CB_POST_EXTRACT => 'optional',
+ PCLZIP_OPT_SET_CHMOD => 'optional',
+ PCLZIP_OPT_BY_NAME => 'optional',
+ PCLZIP_OPT_BY_EREG => 'optional',
+ PCLZIP_OPT_BY_PREG => 'optional',
+ PCLZIP_OPT_BY_INDEX => 'optional',
+ PCLZIP_OPT_EXTRACT_AS_STRING => 'optional',
+ PCLZIP_OPT_EXTRACT_IN_OUTPUT => 'optional',
+ PCLZIP_OPT_REPLACE_NEWER => 'optional'
+ ,PCLZIP_OPT_STOP_ON_ERROR => 'optional'
+ ,PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional',
+ PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional',
+ PCLZIP_OPT_TEMP_FILE_ON => 'optional',
+ PCLZIP_OPT_TEMP_FILE_OFF => 'optional'
+ ));
+ if ($v_result != 1) {
+ return 0;
+ }
+
+ // ----- Set the arguments
+ if (isset($v_options[PCLZIP_OPT_PATH])) {
+ $v_path = $v_options[PCLZIP_OPT_PATH];
+ }
+ if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) {
+ $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH];
+ }
+ if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) {
+ $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH];
+ }
+ if (isset($v_options[PCLZIP_OPT_ADD_PATH])) {
+ // ----- Check for '/' in last path char
+ if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) {
+ $v_path .= '/';
+ }
+ $v_path .= $v_options[PCLZIP_OPT_ADD_PATH];
+ }
+ }
+
+ // ----- Look for 2 args
+ // Here we need to support the first historic synopsis of the
+ // method.
+ else {
+
+ // ----- Get the first argument
+ $v_path = $v_arg_list[0];
+
+ // ----- Look for the optional second argument
+ if ($v_size == 2) {
+ $v_remove_path = $v_arg_list[1];
+ }
+ else if ($v_size > 2) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments");
+
+ // ----- Return
+ return 0;
+ }
+ }
+ }
+
+ // ----- Look for default option values
+ $this->privOptionDefaultThreshold($v_options);
+
+ // ----- Trace
+
+ // ----- Call the extracting fct
+ $p_list = array();
+ $v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path,
+ $v_remove_all_path, $v_options);
+ if ($v_result < 1) {
+ unset($p_list);
+ return(0);
+ }
+
+ // ----- Return
+ return $p_list;
+ }
+ // --------------------------------------------------------------------------------
+
+
+ // --------------------------------------------------------------------------------
+ // Function :
+ // extractByIndex($p_index, $p_path="./", $p_remove_path="")
+ // extractByIndex($p_index, [$p_option, $p_option_value, ...])
+ // Description :
+ // This method supports two synopsis. The first one is historical.
+ // This method is doing a partial extract of the archive.
+ // The extracted files or folders are identified by their index in the
+ // archive (from 0 to n).
+ // Note that if the index identify a folder, only the folder entry is
+ // extracted, not all the files included in the archive.
+ // Parameters :
+ // $p_index : A single index (integer) or a string of indexes of files to
+ // extract. The form of the string is "0,4-6,8-12" with only numbers
+ // and '-' for range or ',' to separate ranges. No spaces or ';'
+ // are allowed.
+ // $p_path : Path where the files and directories are to be extracted
+ // $p_remove_path : First part ('root' part) of the memorized path
+ // (if any similar) to remove while extracting.
+ // Options :
+ // PCLZIP_OPT_PATH :
+ // PCLZIP_OPT_ADD_PATH :
+ // PCLZIP_OPT_REMOVE_PATH :
+ // PCLZIP_OPT_REMOVE_ALL_PATH :
+ // PCLZIP_OPT_EXTRACT_AS_STRING : The files are extracted as strings and
+ // not as files.
+ // The resulting content is in a new field 'content' in the file
+ // structure.
+ // This option must be used alone (any other options are ignored).
+ // PCLZIP_CB_PRE_EXTRACT :
+ // PCLZIP_CB_POST_EXTRACT :
+ // Return Values :
+ // 0 on failure,
+ // The list of the extracted files, with a status of the action.
+ // (see PclZip::listContent() for list entry format)
+ // --------------------------------------------------------------------------------
+ //function extractByIndex($p_index, options...)
+ function extractByIndex($p_index)
+ {
+ $v_result=1;
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Check archive
+ if (!$this->privCheckFormat()) {
+ return(0);
+ }
+
+ // ----- Set default values
+ $v_options = array();
+// $v_path = "./";
+ $v_path = '';
+ $v_remove_path = "";
+ $v_remove_all_path = false;
+
+ // ----- Look for variable options arguments
+ $v_size = func_num_args();
+
+ // ----- Default values for option
+ $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE;
+
+ // ----- Look for arguments
+ if ($v_size > 1) {
+ // ----- Get the arguments
+ $v_arg_list = func_get_args();
+
+ // ----- Remove form the options list the first argument
+ array_shift($v_arg_list);
+ $v_size--;
+
+ // ----- Look for first arg
+ if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) {
+
+ // ----- Parse the options
+ $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options,
+ array (PCLZIP_OPT_PATH => 'optional',
+ PCLZIP_OPT_REMOVE_PATH => 'optional',
+ PCLZIP_OPT_REMOVE_ALL_PATH => 'optional',
+ PCLZIP_OPT_EXTRACT_AS_STRING => 'optional',
+ PCLZIP_OPT_ADD_PATH => 'optional',
+ PCLZIP_CB_PRE_EXTRACT => 'optional',
+ PCLZIP_CB_POST_EXTRACT => 'optional',
+ PCLZIP_OPT_SET_CHMOD => 'optional',
+ PCLZIP_OPT_REPLACE_NEWER => 'optional'
+ ,PCLZIP_OPT_STOP_ON_ERROR => 'optional'
+ ,PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional',
+ PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional',
+ PCLZIP_OPT_TEMP_FILE_ON => 'optional',
+ PCLZIP_OPT_TEMP_FILE_OFF => 'optional'
+ ));
+ if ($v_result != 1) {
+ return 0;
+ }
+
+ // ----- Set the arguments
+ if (isset($v_options[PCLZIP_OPT_PATH])) {
+ $v_path = $v_options[PCLZIP_OPT_PATH];
+ }
+ if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) {
+ $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH];
+ }
+ if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) {
+ $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH];
+ }
+ if (isset($v_options[PCLZIP_OPT_ADD_PATH])) {
+ // ----- Check for '/' in last path char
+ if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) {
+ $v_path .= '/';
+ }
+ $v_path .= $v_options[PCLZIP_OPT_ADD_PATH];
+ }
+ if (!isset($v_options[PCLZIP_OPT_EXTRACT_AS_STRING])) {
+ $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE;
+ }
+ else {
+ }
+ }
+
+ // ----- Look for 2 args
+ // Here we need to support the first historic synopsis of the
+ // method.
+ else {
+
+ // ----- Get the first argument
+ $v_path = $v_arg_list[0];
+
+ // ----- Look for the optional second argument
+ if ($v_size == 2) {
+ $v_remove_path = $v_arg_list[1];
+ }
+ else if ($v_size > 2) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments");
+
+ // ----- Return
+ return 0;
+ }
+ }
+ }
+
+ // ----- Trace
+
+ // ----- Trick
+ // Here I want to reuse extractByRule(), so I need to parse the $p_index
+ // with privParseOptions()
+ $v_arg_trick = array (PCLZIP_OPT_BY_INDEX, $p_index);
+ $v_options_trick = array();
+ $v_result = $this->privParseOptions($v_arg_trick, sizeof($v_arg_trick), $v_options_trick,
+ array (PCLZIP_OPT_BY_INDEX => 'optional' ));
+ if ($v_result != 1) {
+ return 0;
+ }
+ $v_options[PCLZIP_OPT_BY_INDEX] = $v_options_trick[PCLZIP_OPT_BY_INDEX];
+
+ // ----- Look for default option values
+ $this->privOptionDefaultThreshold($v_options);
+
+ // ----- Call the extracting fct
+ if (($v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, $v_remove_all_path, $v_options)) < 1) {
+ return(0);
+ }
+
+ // ----- Return
+ return $p_list;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function :
+ // delete([$p_option, $p_option_value, ...])
+ // Description :
+ // This method removes files from the archive.
+ // If no parameters are given, then all the archive is emptied.
+ // Parameters :
+ // None or optional arguments.
+ // Options :
+ // PCLZIP_OPT_BY_INDEX :
+ // PCLZIP_OPT_BY_NAME :
+ // PCLZIP_OPT_BY_EREG :
+ // PCLZIP_OPT_BY_PREG :
+ // Return Values :
+ // 0 on failure,
+ // The list of the files which are still present in the archive.
+ // (see PclZip::listContent() for list entry format)
+ // --------------------------------------------------------------------------------
+ function delete()
+ {
+ $v_result=1;
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Check archive
+ if (!$this->privCheckFormat()) {
+ return(0);
+ }
+
+ // ----- Set default values
+ $v_options = array();
+
+ // ----- Look for variable options arguments
+ $v_size = func_num_args();
+
+ // ----- Look for arguments
+ if ($v_size > 0) {
+ // ----- Get the arguments
+ $v_arg_list = func_get_args();
+
+ // ----- Parse the options
+ $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options,
+ array (PCLZIP_OPT_BY_NAME => 'optional',
+ PCLZIP_OPT_BY_EREG => 'optional',
+ PCLZIP_OPT_BY_PREG => 'optional',
+ PCLZIP_OPT_BY_INDEX => 'optional' ));
+ if ($v_result != 1) {
+ return 0;
+ }
+ }
+
+ // ----- Magic quotes trick
+ $this->privDisableMagicQuotes();
+
+ // ----- Call the delete fct
+ $v_list = array();
+ if (($v_result = $this->privDeleteByRule($v_list, $v_options)) != 1) {
+ $this->privSwapBackMagicQuotes();
+ unset($v_list);
+ return(0);
+ }
+
+ // ----- Magic quotes trick
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Return
+ return $v_list;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : deleteByIndex()
+ // Description :
+ // ***** Deprecated *****
+ // delete(PCLZIP_OPT_BY_INDEX, $p_index) should be prefered.
+ // --------------------------------------------------------------------------------
+ function deleteByIndex($p_index)
+ {
+
+ $p_list = $this->delete(PCLZIP_OPT_BY_INDEX, $p_index);
+
+ // ----- Return
+ return $p_list;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : properties()
+ // Description :
+ // This method gives the properties of the archive.
+ // The properties are :
+ // nb : Number of files in the archive
+ // comment : Comment associated with the archive file
+ // status : not_exist, ok
+ // Parameters :
+ // None
+ // Return Values :
+ // 0 on failure,
+ // An array with the archive properties.
+ // --------------------------------------------------------------------------------
+ function properties()
+ {
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Magic quotes trick
+ $this->privDisableMagicQuotes();
+
+ // ----- Check archive
+ if (!$this->privCheckFormat()) {
+ $this->privSwapBackMagicQuotes();
+ return(0);
+ }
+
+ // ----- Default properties
+ $v_prop = array();
+ $v_prop['comment'] = '';
+ $v_prop['nb'] = 0;
+ $v_prop['status'] = 'not_exist';
+
+ // ----- Look if file exists
+ if (@is_file($this->zipname))
+ {
+ // ----- Open the zip file
+ if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0)
+ {
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode');
+
+ // ----- Return
+ return 0;
+ }
+
+ // ----- Read the central directory informations
+ $v_central_dir = array();
+ if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
+ {
+ $this->privSwapBackMagicQuotes();
+ return 0;
+ }
+
+ // ----- Close the zip file
+ $this->privCloseFd();
+
+ // ----- Set the user attributes
+ $v_prop['comment'] = $v_central_dir['comment'];
+ $v_prop['nb'] = $v_central_dir['entries'];
+ $v_prop['status'] = 'ok';
+ }
+
+ // ----- Magic quotes trick
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Return
+ return $v_prop;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : duplicate()
+ // Description :
+ // This method creates an archive by copying the content of an other one. If
+ // the archive already exist, it is replaced by the new one without any warning.
+ // Parameters :
+ // $p_archive : The filename of a valid archive, or
+ // a valid PclZip object.
+ // Return Values :
+ // 1 on success.
+ // 0 or a negative value on error (error code).
+ // --------------------------------------------------------------------------------
+ function duplicate($p_archive)
+ {
+ $v_result = 1;
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Look if the $p_archive is a PclZip object
+ if ((is_object($p_archive)) && (get_class($p_archive) == 'pclzip'))
+ {
+
+ // ----- Duplicate the archive
+ $v_result = $this->privDuplicate($p_archive->zipname);
+ }
+
+ // ----- Look if the $p_archive is a string (so a filename)
+ else if (is_string($p_archive))
+ {
+
+ // ----- Check that $p_archive is a valid zip file
+ // TBC : Should also check the archive format
+ if (!is_file($p_archive)) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "No file with filename '".$p_archive."'");
+ $v_result = PCLZIP_ERR_MISSING_FILE;
+ }
+ else {
+ // ----- Duplicate the archive
+ $v_result = $this->privDuplicate($p_archive);
+ }
+ }
+
+ // ----- Invalid variable
+ else
+ {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add");
+ $v_result = PCLZIP_ERR_INVALID_PARAMETER;
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : merge()
+ // Description :
+ // This method merge the $p_archive_to_add archive at the end of the current
+ // one ($this).
+ // If the archive ($this) does not exist, the merge becomes a duplicate.
+ // If the $p_archive_to_add archive does not exist, the merge is a success.
+ // Parameters :
+ // $p_archive_to_add : It can be directly the filename of a valid zip archive,
+ // or a PclZip object archive.
+ // Return Values :
+ // 1 on success,
+ // 0 or negative values on error (see below).
+ // --------------------------------------------------------------------------------
+ function merge($p_archive_to_add)
+ {
+ $v_result = 1;
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Check archive
+ if (!$this->privCheckFormat()) {
+ return(0);
+ }
+
+ // ----- Look if the $p_archive_to_add is a PclZip object
+ if ((is_object($p_archive_to_add)) && (get_class($p_archive_to_add) == 'pclzip'))
+ {
+
+ // ----- Merge the archive
+ $v_result = $this->privMerge($p_archive_to_add);
+ }
+
+ // ----- Look if the $p_archive_to_add is a string (so a filename)
+ else if (is_string($p_archive_to_add))
+ {
+
+ // ----- Create a temporary archive
+ $v_object_archive = new PclZip($p_archive_to_add);
+
+ // ----- Merge the archive
+ $v_result = $this->privMerge($v_object_archive);
+ }
+
+ // ----- Invalid variable
+ else
+ {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add");
+ $v_result = PCLZIP_ERR_INVALID_PARAMETER;
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+
+
+ // --------------------------------------------------------------------------------
+ // Function : errorCode()
+ // Description :
+ // Parameters :
+ // --------------------------------------------------------------------------------
+ function errorCode()
+ {
+ if (PCLZIP_ERROR_EXTERNAL == 1) {
+ return(PclErrorCode());
+ }
+ else {
+ return($this->error_code);
+ }
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : errorName()
+ // Description :
+ // Parameters :
+ // --------------------------------------------------------------------------------
+ function errorName($p_with_code=false)
+ {
+ $v_name = array ( PCLZIP_ERR_NO_ERROR => 'PCLZIP_ERR_NO_ERROR',
+ PCLZIP_ERR_WRITE_OPEN_FAIL => 'PCLZIP_ERR_WRITE_OPEN_FAIL',
+ PCLZIP_ERR_READ_OPEN_FAIL => 'PCLZIP_ERR_READ_OPEN_FAIL',
+ PCLZIP_ERR_INVALID_PARAMETER => 'PCLZIP_ERR_INVALID_PARAMETER',
+ PCLZIP_ERR_MISSING_FILE => 'PCLZIP_ERR_MISSING_FILE',
+ PCLZIP_ERR_FILENAME_TOO_LONG => 'PCLZIP_ERR_FILENAME_TOO_LONG',
+ PCLZIP_ERR_INVALID_ZIP => 'PCLZIP_ERR_INVALID_ZIP',
+ PCLZIP_ERR_BAD_EXTRACTED_FILE => 'PCLZIP_ERR_BAD_EXTRACTED_FILE',
+ PCLZIP_ERR_DIR_CREATE_FAIL => 'PCLZIP_ERR_DIR_CREATE_FAIL',
+ PCLZIP_ERR_BAD_EXTENSION => 'PCLZIP_ERR_BAD_EXTENSION',
+ PCLZIP_ERR_BAD_FORMAT => 'PCLZIP_ERR_BAD_FORMAT',
+ PCLZIP_ERR_DELETE_FILE_FAIL => 'PCLZIP_ERR_DELETE_FILE_FAIL',
+ PCLZIP_ERR_RENAME_FILE_FAIL => 'PCLZIP_ERR_RENAME_FILE_FAIL',
+ PCLZIP_ERR_BAD_CHECKSUM => 'PCLZIP_ERR_BAD_CHECKSUM',
+ PCLZIP_ERR_INVALID_ARCHIVE_ZIP => 'PCLZIP_ERR_INVALID_ARCHIVE_ZIP',
+ PCLZIP_ERR_MISSING_OPTION_VALUE => 'PCLZIP_ERR_MISSING_OPTION_VALUE',
+ PCLZIP_ERR_INVALID_OPTION_VALUE => 'PCLZIP_ERR_INVALID_OPTION_VALUE',
+ PCLZIP_ERR_UNSUPPORTED_COMPRESSION => 'PCLZIP_ERR_UNSUPPORTED_COMPRESSION',
+ PCLZIP_ERR_UNSUPPORTED_ENCRYPTION => 'PCLZIP_ERR_UNSUPPORTED_ENCRYPTION'
+ ,PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE => 'PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE'
+ ,PCLZIP_ERR_DIRECTORY_RESTRICTION => 'PCLZIP_ERR_DIRECTORY_RESTRICTION'
+ );
+
+ if (isset($v_name[$this->error_code])) {
+ $v_value = $v_name[$this->error_code];
+ }
+ else {
+ $v_value = 'NoName';
+ }
+
+ if ($p_with_code) {
+ return($v_value.' ('.$this->error_code.')');
+ }
+ else {
+ return($v_value);
+ }
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : errorInfo()
+ // Description :
+ // Parameters :
+ // --------------------------------------------------------------------------------
+ function errorInfo($p_full=false)
+ {
+ if (PCLZIP_ERROR_EXTERNAL == 1) {
+ return(PclErrorString());
+ }
+ else {
+ if ($p_full) {
+ return($this->errorName(true)." : ".$this->error_string);
+ }
+ else {
+ return($this->error_string." [code ".$this->error_code."]");
+ }
+ }
+ }
+ // --------------------------------------------------------------------------------
+
+
+// --------------------------------------------------------------------------------
+// ***** UNDER THIS LINE ARE DEFINED PRIVATE INTERNAL FUNCTIONS *****
+// ***** *****
+// ***** THESES FUNCTIONS MUST NOT BE USED DIRECTLY *****
+// --------------------------------------------------------------------------------
+
+
+
+ // --------------------------------------------------------------------------------
+ // Function : privCheckFormat()
+ // Description :
+ // This method check that the archive exists and is a valid zip archive.
+ // Several level of check exists. (futur)
+ // Parameters :
+ // $p_level : Level of check. Default 0.
+ // 0 : Check the first bytes (magic codes) (default value))
+ // 1 : 0 + Check the central directory (futur)
+ // 2 : 1 + Check each file header (futur)
+ // Return Values :
+ // true on success,
+ // false on error, the error code is set.
+ // --------------------------------------------------------------------------------
+ function privCheckFormat($p_level=0)
+ {
+ $v_result = true;
+
+ // ----- Reset the file system cache
+ clearstatcache();
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Look if the file exits
+ if (!is_file($this->zipname)) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "Missing archive file '".$this->zipname."'");
+ return(false);
+ }
+
+ // ----- Check that the file is readeable
+ if (!is_readable($this->zipname)) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to read archive '".$this->zipname."'");
+ return(false);
+ }
+
+ // ----- Check the magic code
+ // TBC
+
+ // ----- Check the central header
+ // TBC
+
+ // ----- Check each file header
+ // TBC
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privParseOptions()
+ // Description :
+ // This internal methods reads the variable list of arguments ($p_options_list,
+ // $p_size) and generate an array with the options and values ($v_result_list).
+ // $v_requested_options contains the options that can be present and those that
+ // must be present.
+ // $v_requested_options is an array, with the option value as key, and 'optional',
+ // or 'mandatory' as value.
+ // Parameters :
+ // See above.
+ // Return Values :
+ // 1 on success.
+ // 0 on failure.
+ // --------------------------------------------------------------------------------
+ function privParseOptions(&$p_options_list, $p_size, &$v_result_list, $v_requested_options=false)
+ {
+ $v_result=1;
+
+ // ----- Read the options
+ $i=0;
+ while ($i<$p_size) {
+
+ // ----- Check if the option is supported
+ if (!isset($v_requested_options[$p_options_list[$i]])) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid optional parameter '".$p_options_list[$i]."' for this method");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Look for next option
+ switch ($p_options_list[$i]) {
+ // ----- Look for options that request a path value
+ case PCLZIP_OPT_PATH :
+ case PCLZIP_OPT_REMOVE_PATH :
+ case PCLZIP_OPT_ADD_PATH :
+ // ----- Check the number of parameters
+ if (($i+1) >= $p_size) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value
+ $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE);
+ $i++;
+ break;
+
+ case PCLZIP_OPT_TEMP_FILE_THRESHOLD :
+ // ----- Check the number of parameters
+ if (($i+1) >= $p_size) {
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+ return PclZip::errorCode();
+ }
+
+ // ----- Check for incompatible options
+ if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'");
+ return PclZip::errorCode();
+ }
+
+ // ----- Check the value
+ $v_value = $p_options_list[$i+1];
+ if ((!is_integer($v_value)) || ($v_value<0)) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Integer expected for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value (and convert it in bytes)
+ $v_result_list[$p_options_list[$i]] = $v_value*1048576;
+ $i++;
+ break;
+
+ case PCLZIP_OPT_TEMP_FILE_ON :
+ // ----- Check for incompatible options
+ if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'");
+ return PclZip::errorCode();
+ }
+
+ $v_result_list[$p_options_list[$i]] = true;
+ break;
+
+ case PCLZIP_OPT_TEMP_FILE_OFF :
+ // ----- Check for incompatible options
+ if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_ON])) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_ON'");
+ return PclZip::errorCode();
+ }
+ // ----- Check for incompatible options
+ if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_THRESHOLD'");
+ return PclZip::errorCode();
+ }
+
+ $v_result_list[$p_options_list[$i]] = true;
+ break;
+
+ case PCLZIP_OPT_EXTRACT_DIR_RESTRICTION :
+ // ----- Check the number of parameters
+ if (($i+1) >= $p_size) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value
+ if ( is_string($p_options_list[$i+1])
+ && ($p_options_list[$i+1] != '')) {
+ $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE);
+ $i++;
+ }
+ else {
+ }
+ break;
+
+ // ----- Look for options that request an array of string for value
+ case PCLZIP_OPT_BY_NAME :
+ // ----- Check the number of parameters
+ if (($i+1) >= $p_size) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value
+ if (is_string($p_options_list[$i+1])) {
+ $v_result_list[$p_options_list[$i]][0] = $p_options_list[$i+1];
+ }
+ else if (is_array($p_options_list[$i+1])) {
+ $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1];
+ }
+ else {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+ $i++;
+ break;
+
+ // ----- Look for options that request an EREG or PREG expression
+ case PCLZIP_OPT_BY_EREG :
+ // ereg() is deprecated starting with PHP 5.3. Move PCLZIP_OPT_BY_EREG
+ // to PCLZIP_OPT_BY_PREG
+ $p_options_list[$i] = PCLZIP_OPT_BY_PREG;
+ case PCLZIP_OPT_BY_PREG :
+ //case PCLZIP_OPT_CRYPT :
+ // ----- Check the number of parameters
+ if (($i+1) >= $p_size) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value
+ if (is_string($p_options_list[$i+1])) {
+ $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1];
+ }
+ else {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+ $i++;
+ break;
+
+ // ----- Look for options that takes a string
+ case PCLZIP_OPT_COMMENT :
+ case PCLZIP_OPT_ADD_COMMENT :
+ case PCLZIP_OPT_PREPEND_COMMENT :
+ // ----- Check the number of parameters
+ if (($i+1) >= $p_size) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE,
+ "Missing parameter value for option '"
+ .PclZipUtilOptionText($p_options_list[$i])
+ ."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value
+ if (is_string($p_options_list[$i+1])) {
+ $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1];
+ }
+ else {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE,
+ "Wrong parameter value for option '"
+ .PclZipUtilOptionText($p_options_list[$i])
+ ."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+ $i++;
+ break;
+
+ // ----- Look for options that request an array of index
+ case PCLZIP_OPT_BY_INDEX :
+ // ----- Check the number of parameters
+ if (($i+1) >= $p_size) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value
+ $v_work_list = array();
+ if (is_string($p_options_list[$i+1])) {
+
+ // ----- Remove spaces
+ $p_options_list[$i+1] = strtr($p_options_list[$i+1], ' ', '');
+
+ // ----- Parse items
+ $v_work_list = explode(",", $p_options_list[$i+1]);
+ }
+ else if (is_integer($p_options_list[$i+1])) {
+ $v_work_list[0] = $p_options_list[$i+1].'-'.$p_options_list[$i+1];
+ }
+ else if (is_array($p_options_list[$i+1])) {
+ $v_work_list = $p_options_list[$i+1];
+ }
+ else {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Value must be integer, string or array for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Reduce the index list
+ // each index item in the list must be a couple with a start and
+ // an end value : [0,3], [5-5], [8-10], ...
+ // ----- Check the format of each item
+ $v_sort_flag=false;
+ $v_sort_value=0;
+ for ($j=0; $j= $p_size) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value
+ $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1];
+ $i++;
+ break;
+
+ // ----- Look for options that request a call-back
+ case PCLZIP_CB_PRE_EXTRACT :
+ case PCLZIP_CB_POST_EXTRACT :
+ case PCLZIP_CB_PRE_ADD :
+ case PCLZIP_CB_POST_ADD :
+ /* for futur use
+ case PCLZIP_CB_PRE_DELETE :
+ case PCLZIP_CB_POST_DELETE :
+ case PCLZIP_CB_PRE_LIST :
+ case PCLZIP_CB_POST_LIST :
+ */
+ // ----- Check the number of parameters
+ if (($i+1) >= $p_size) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value
+ $v_function_name = $p_options_list[$i+1];
+
+ // ----- Check that the value is a valid existing function
+ if (!function_exists($v_function_name)) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Function '".$v_function_name."()' is not an existing function for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Set the attribute
+ $v_result_list[$p_options_list[$i]] = $v_function_name;
+ $i++;
+ break;
+
+ default :
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER,
+ "Unknown parameter '"
+ .$p_options_list[$i]."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Next options
+ $i++;
+ }
+
+ // ----- Look for mandatory options
+ if ($v_requested_options !== false) {
+ for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) {
+ // ----- Look for mandatory option
+ if ($v_requested_options[$key] == 'mandatory') {
+ // ----- Look if present
+ if (!isset($v_result_list[$key])) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+ }
+ }
+ }
+
+ // ----- Look for default values
+ if (!isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) {
+
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privOptionDefaultThreshold()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privOptionDefaultThreshold(&$p_options)
+ {
+ $v_result=1;
+
+ if (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD])
+ || isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) {
+ return $v_result;
+ }
+
+ // ----- Get 'memory_limit' configuration value
+ $v_memory_limit = ini_get('memory_limit');
+ $v_memory_limit = trim($v_memory_limit);
+ $last = strtolower(substr($v_memory_limit, -1));
+
+ if($last == 'g')
+ //$v_memory_limit = $v_memory_limit*1024*1024*1024;
+ $v_memory_limit = $v_memory_limit*1073741824;
+ if($last == 'm')
+ //$v_memory_limit = $v_memory_limit*1024*1024;
+ $v_memory_limit = $v_memory_limit*1048576;
+ if($last == 'k')
+ $v_memory_limit = $v_memory_limit*1024;
+
+ $p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] = floor($v_memory_limit*PCLZIP_TEMPORARY_FILE_RATIO);
+
+
+ // ----- Sanity check : No threshold if value lower than 1M
+ if ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] < 1048576) {
+ unset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]);
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privFileDescrParseAtt()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // 1 on success.
+ // 0 on failure.
+ // --------------------------------------------------------------------------------
+ function privFileDescrParseAtt(&$p_file_list, &$p_filedescr, $v_options, $v_requested_options=false)
+ {
+ $v_result=1;
+
+ // ----- For each file in the list check the attributes
+ foreach ($p_file_list as $v_key => $v_value) {
+
+ // ----- Check if the option is supported
+ if (!isset($v_requested_options[$v_key])) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file attribute '".$v_key."' for this file");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Look for attribute
+ switch ($v_key) {
+ case PCLZIP_ATT_FILE_NAME :
+ if (!is_string($v_value)) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'");
+ return PclZip::errorCode();
+ }
+
+ $p_filedescr['filename'] = PclZipUtilPathReduction($v_value);
+
+ if ($p_filedescr['filename'] == '') {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty filename for attribute '".PclZipUtilOptionText($v_key)."'");
+ return PclZip::errorCode();
+ }
+
+ break;
+
+ case PCLZIP_ATT_FILE_NEW_SHORT_NAME :
+ if (!is_string($v_value)) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'");
+ return PclZip::errorCode();
+ }
+
+ $p_filedescr['new_short_name'] = PclZipUtilPathReduction($v_value);
+
+ if ($p_filedescr['new_short_name'] == '') {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty short filename for attribute '".PclZipUtilOptionText($v_key)."'");
+ return PclZip::errorCode();
+ }
+ break;
+
+ case PCLZIP_ATT_FILE_NEW_FULL_NAME :
+ if (!is_string($v_value)) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'");
+ return PclZip::errorCode();
+ }
+
+ $p_filedescr['new_full_name'] = PclZipUtilPathReduction($v_value);
+
+ if ($p_filedescr['new_full_name'] == '') {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty full filename for attribute '".PclZipUtilOptionText($v_key)."'");
+ return PclZip::errorCode();
+ }
+ break;
+
+ // ----- Look for options that takes a string
+ case PCLZIP_ATT_FILE_COMMENT :
+ if (!is_string($v_value)) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'");
+ return PclZip::errorCode();
+ }
+
+ $p_filedescr['comment'] = $v_value;
+ break;
+
+ case PCLZIP_ATT_FILE_MTIME :
+ if (!is_integer($v_value)) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". Integer expected for attribute '".PclZipUtilOptionText($v_key)."'");
+ return PclZip::errorCode();
+ }
+
+ $p_filedescr['mtime'] = $v_value;
+ break;
+
+ case PCLZIP_ATT_FILE_CONTENT :
+ $p_filedescr['content'] = $v_value;
+ break;
+
+ default :
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER,
+ "Unknown parameter '".$v_key."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Look for mandatory options
+ if ($v_requested_options !== false) {
+ for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) {
+ // ----- Look for mandatory option
+ if ($v_requested_options[$key] == 'mandatory') {
+ // ----- Look if present
+ if (!isset($p_file_list[$key])) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")");
+ return PclZip::errorCode();
+ }
+ }
+ }
+ }
+
+ // end foreach
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privFileDescrExpand()
+ // Description :
+ // This method look for each item of the list to see if its a file, a folder
+ // or a string to be added as file. For any other type of files (link, other)
+ // just ignore the item.
+ // Then prepare the information that will be stored for that file.
+ // When its a folder, expand the folder with all the files that are in that
+ // folder (recursively).
+ // Parameters :
+ // Return Values :
+ // 1 on success.
+ // 0 on failure.
+ // --------------------------------------------------------------------------------
+ function privFileDescrExpand(&$p_filedescr_list, &$p_options)
+ {
+ $v_result=1;
+
+ // ----- Create a result list
+ $v_result_list = array();
+
+ // ----- Look each entry
+ for ($i=0; $iprivCalculateStoredFilename($v_descr, $p_options);
+
+ // ----- Add the descriptor in result list
+ $v_result_list[sizeof($v_result_list)] = $v_descr;
+
+ // ----- Look for folder
+ if ($v_descr['type'] == 'folder') {
+ // ----- List of items in folder
+ $v_dirlist_descr = array();
+ $v_dirlist_nb = 0;
+ if ($v_folder_handler = @opendir($v_descr['filename'])) {
+ while (($v_item_handler = @readdir($v_folder_handler)) !== false) {
+
+ // ----- Skip '.' and '..'
+ if (($v_item_handler == '.') || ($v_item_handler == '..')) {
+ continue;
+ }
+
+ // ----- Compose the full filename
+ $v_dirlist_descr[$v_dirlist_nb]['filename'] = $v_descr['filename'].'/'.$v_item_handler;
+
+ // ----- Look for different stored filename
+ // Because the name of the folder was changed, the name of the
+ // files/sub-folders also change
+ if (($v_descr['stored_filename'] != $v_descr['filename'])
+ && (!isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH]))) {
+ if ($v_descr['stored_filename'] != '') {
+ $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_descr['stored_filename'].'/'.$v_item_handler;
+ }
+ else {
+ $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_item_handler;
+ }
+ }
+
+ $v_dirlist_nb++;
+ }
+
+ @closedir($v_folder_handler);
+ }
+ else {
+ // TBC : unable to open folder in read mode
+ }
+
+ // ----- Expand each element of the list
+ if ($v_dirlist_nb != 0) {
+ // ----- Expand
+ if (($v_result = $this->privFileDescrExpand($v_dirlist_descr, $p_options)) != 1) {
+ return $v_result;
+ }
+
+ // ----- Concat the resulting list
+ $v_result_list = array_merge($v_result_list, $v_dirlist_descr);
+ }
+ else {
+ }
+
+ // ----- Free local array
+ unset($v_dirlist_descr);
+ }
+ }
+
+ // ----- Get the result list
+ $p_filedescr_list = $v_result_list;
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privCreate()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privCreate($p_filedescr_list, &$p_result_list, &$p_options)
+ {
+ $v_result=1;
+ $v_list_detail = array();
+
+ // ----- Magic quotes trick
+ $this->privDisableMagicQuotes();
+
+ // ----- Open the file in write mode
+ if (($v_result = $this->privOpenFd('wb')) != 1)
+ {
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Add the list of files
+ $v_result = $this->privAddList($p_filedescr_list, $p_result_list, $p_options);
+
+ // ----- Close
+ $this->privCloseFd();
+
+ // ----- Magic quotes trick
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privAdd()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privAdd($p_filedescr_list, &$p_result_list, &$p_options)
+ {
+ $v_result=1;
+ $v_list_detail = array();
+
+ // ----- Look if the archive exists or is empty
+ if ((!is_file($this->zipname)) || (filesize($this->zipname) == 0))
+ {
+
+ // ----- Do a create
+ $v_result = $this->privCreate($p_filedescr_list, $p_result_list, $p_options);
+
+ // ----- Return
+ return $v_result;
+ }
+ // ----- Magic quotes trick
+ $this->privDisableMagicQuotes();
+
+ // ----- Open the zip file
+ if (($v_result=$this->privOpenFd('rb')) != 1)
+ {
+ // ----- Magic quotes trick
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Read the central directory informations
+ $v_central_dir = array();
+ if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
+ {
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+ return $v_result;
+ }
+
+ // ----- Go to beginning of File
+ @rewind($this->zip_fd);
+
+ // ----- Creates a temporay file
+ $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp';
+
+ // ----- Open the temporary file in write mode
+ if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0)
+ {
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Copy the files from the archive to the temporary file
+ // TBC : Here I should better append the file and go back to erase the central dir
+ $v_size = $v_central_dir['offset'];
+ while ($v_size != 0)
+ {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = fread($this->zip_fd, $v_read_size);
+ @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Swap the file descriptor
+ // Here is a trick : I swap the temporary fd with the zip fd, in order to use
+ // the following methods on the temporary fil and not the real archive
+ $v_swap = $this->zip_fd;
+ $this->zip_fd = $v_zip_temp_fd;
+ $v_zip_temp_fd = $v_swap;
+
+ // ----- Add the files
+ $v_header_list = array();
+ if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1)
+ {
+ fclose($v_zip_temp_fd);
+ $this->privCloseFd();
+ @unlink($v_zip_temp_name);
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Store the offset of the central dir
+ $v_offset = @ftell($this->zip_fd);
+
+ // ----- Copy the block of file headers from the old archive
+ $v_size = $v_central_dir['size'];
+ while ($v_size != 0)
+ {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @fread($v_zip_temp_fd, $v_read_size);
+ @fwrite($this->zip_fd, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Create the Central Dir files header
+ for ($i=0, $v_count=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) {
+ fclose($v_zip_temp_fd);
+ $this->privCloseFd();
+ @unlink($v_zip_temp_name);
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Return
+ return $v_result;
+ }
+ $v_count++;
+ }
+
+ // ----- Transform the header to a 'usable' info
+ $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]);
+ }
+
+ // ----- Zip file comment
+ $v_comment = $v_central_dir['comment'];
+ if (isset($p_options[PCLZIP_OPT_COMMENT])) {
+ $v_comment = $p_options[PCLZIP_OPT_COMMENT];
+ }
+ if (isset($p_options[PCLZIP_OPT_ADD_COMMENT])) {
+ $v_comment = $v_comment.$p_options[PCLZIP_OPT_ADD_COMMENT];
+ }
+ if (isset($p_options[PCLZIP_OPT_PREPEND_COMMENT])) {
+ $v_comment = $p_options[PCLZIP_OPT_PREPEND_COMMENT].$v_comment;
+ }
+
+ // ----- Calculate the size of the central header
+ $v_size = @ftell($this->zip_fd)-$v_offset;
+
+ // ----- Create the central dir footer
+ if (($v_result = $this->privWriteCentralHeader($v_count+$v_central_dir['entries'], $v_size, $v_offset, $v_comment)) != 1)
+ {
+ // ----- Reset the file list
+ unset($v_header_list);
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Swap back the file descriptor
+ $v_swap = $this->zip_fd;
+ $this->zip_fd = $v_zip_temp_fd;
+ $v_zip_temp_fd = $v_swap;
+
+ // ----- Close
+ $this->privCloseFd();
+
+ // ----- Close the temporary file
+ @fclose($v_zip_temp_fd);
+
+ // ----- Magic quotes trick
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Delete the zip file
+ // TBC : I should test the result ...
+ @unlink($this->zipname);
+
+ // ----- Rename the temporary file
+ // TBC : I should test the result ...
+ //@rename($v_zip_temp_name, $this->zipname);
+ PclZipUtilRename($v_zip_temp_name, $this->zipname);
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privOpenFd()
+ // Description :
+ // Parameters :
+ // --------------------------------------------------------------------------------
+ function privOpenFd($p_mode)
+ {
+ $v_result=1;
+
+ // ----- Look if already open
+ if ($this->zip_fd != 0)
+ {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Zip file \''.$this->zipname.'\' already open');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Open the zip file
+ if (($this->zip_fd = @fopen($this->zipname, $p_mode)) == 0)
+ {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in '.$p_mode.' mode');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privCloseFd()
+ // Description :
+ // Parameters :
+ // --------------------------------------------------------------------------------
+ function privCloseFd()
+ {
+ $v_result=1;
+
+ if ($this->zip_fd != 0)
+ @fclose($this->zip_fd);
+ $this->zip_fd = 0;
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privAddList()
+ // Description :
+ // $p_add_dir and $p_remove_dir will give the ability to memorize a path which is
+ // different from the real path of the file. This is usefull if you want to have PclTar
+ // running in any directory, and memorize relative path from an other directory.
+ // Parameters :
+ // $p_list : An array containing the file or directory names to add in the tar
+ // $p_result_list : list of added files with their properties (specially the status field)
+ // $p_add_dir : Path to add in the filename path archived
+ // $p_remove_dir : Path to remove in the filename path archived
+ // Return Values :
+ // --------------------------------------------------------------------------------
+// function privAddList($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options)
+ function privAddList($p_filedescr_list, &$p_result_list, &$p_options)
+ {
+ $v_result=1;
+
+ // ----- Add the files
+ $v_header_list = array();
+ if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1)
+ {
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Store the offset of the central dir
+ $v_offset = @ftell($this->zip_fd);
+
+ // ----- Create the Central Dir files header
+ for ($i=0,$v_count=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) {
+ // ----- Return
+ return $v_result;
+ }
+ $v_count++;
+ }
+
+ // ----- Transform the header to a 'usable' info
+ $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]);
+ }
+
+ // ----- Zip file comment
+ $v_comment = '';
+ if (isset($p_options[PCLZIP_OPT_COMMENT])) {
+ $v_comment = $p_options[PCLZIP_OPT_COMMENT];
+ }
+
+ // ----- Calculate the size of the central header
+ $v_size = @ftell($this->zip_fd)-$v_offset;
+
+ // ----- Create the central dir footer
+ if (($v_result = $this->privWriteCentralHeader($v_count, $v_size, $v_offset, $v_comment)) != 1)
+ {
+ // ----- Reset the file list
+ unset($v_header_list);
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privAddFileList()
+ // Description :
+ // Parameters :
+ // $p_filedescr_list : An array containing the file description
+ // or directory names to add in the zip
+ // $p_result_list : list of added files with their properties (specially the status field)
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privAddFileList($p_filedescr_list, &$p_result_list, &$p_options)
+ {
+ $v_result=1;
+ $v_header = array();
+
+ // ----- Recuperate the current number of elt in list
+ $v_nb = sizeof($p_result_list);
+
+ // ----- Loop on the files
+ for ($j=0; ($jprivAddFile($p_filedescr_list[$j], $v_header,
+ $p_options);
+ if ($v_result != 1) {
+ return $v_result;
+ }
+
+ // ----- Store the file infos
+ $p_result_list[$v_nb++] = $v_header;
+ }
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privAddFile()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privAddFile($p_filedescr, &$p_header, &$p_options)
+ {
+ $v_result=1;
+
+ // ----- Working variable
+ $p_filename = $p_filedescr['filename'];
+
+ // TBC : Already done in the fileAtt check ... ?
+ if ($p_filename == "") {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file list parameter (invalid or empty list)");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Look for a stored different filename
+ /* TBC : Removed
+ if (isset($p_filedescr['stored_filename'])) {
+ $v_stored_filename = $p_filedescr['stored_filename'];
+ }
+ else {
+ $v_stored_filename = $p_filedescr['stored_filename'];
+ }
+ */
+
+ // ----- Set the file properties
+ clearstatcache();
+ $p_header['version'] = 20;
+ $p_header['version_extracted'] = 10;
+ $p_header['flag'] = 0;
+ $p_header['compression'] = 0;
+ $p_header['crc'] = 0;
+ $p_header['compressed_size'] = 0;
+ $p_header['filename_len'] = strlen($p_filename);
+ $p_header['extra_len'] = 0;
+ $p_header['disk'] = 0;
+ $p_header['internal'] = 0;
+ $p_header['offset'] = 0;
+ $p_header['filename'] = $p_filename;
+// TBC : Removed $p_header['stored_filename'] = $v_stored_filename;
+ $p_header['stored_filename'] = $p_filedescr['stored_filename'];
+ $p_header['extra'] = '';
+ $p_header['status'] = 'ok';
+ $p_header['index'] = -1;
+
+ // ----- Look for regular file
+ if ($p_filedescr['type']=='file') {
+ $p_header['external'] = 0x00000000;
+ $p_header['size'] = filesize($p_filename);
+ }
+
+ // ----- Look for regular folder
+ else if ($p_filedescr['type']=='folder') {
+ $p_header['external'] = 0x00000010;
+ $p_header['mtime'] = filemtime($p_filename);
+ $p_header['size'] = filesize($p_filename);
+ }
+
+ // ----- Look for virtual file
+ else if ($p_filedescr['type'] == 'virtual_file') {
+ $p_header['external'] = 0x00000000;
+ $p_header['size'] = strlen($p_filedescr['content']);
+ }
+
+
+ // ----- Look for filetime
+ if (isset($p_filedescr['mtime'])) {
+ $p_header['mtime'] = $p_filedescr['mtime'];
+ }
+ else if ($p_filedescr['type'] == 'virtual_file') {
+ $p_header['mtime'] = time();
+ }
+ else {
+ $p_header['mtime'] = filemtime($p_filename);
+ }
+
+ // ------ Look for file comment
+ if (isset($p_filedescr['comment'])) {
+ $p_header['comment_len'] = strlen($p_filedescr['comment']);
+ $p_header['comment'] = $p_filedescr['comment'];
+ }
+ else {
+ $p_header['comment_len'] = 0;
+ $p_header['comment'] = '';
+ }
+
+ // ----- Look for pre-add callback
+ if (isset($p_options[PCLZIP_CB_PRE_ADD])) {
+
+ // ----- Generate a local information
+ $v_local_header = array();
+ $this->privConvertHeader2FileInfo($p_header, $v_local_header);
+
+ // ----- Call the callback
+ // Here I do not use call_user_func() because I need to send a reference to the
+ // header.
+// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_ADD].'(PCLZIP_CB_PRE_ADD, $v_local_header);');
+ $v_result = $p_options[PCLZIP_CB_PRE_ADD](PCLZIP_CB_PRE_ADD, $v_local_header);
+ if ($v_result == 0) {
+ // ----- Change the file status
+ $p_header['status'] = "skipped";
+ $v_result = 1;
+ }
+
+ // ----- Update the informations
+ // Only some fields can be modified
+ if ($p_header['stored_filename'] != $v_local_header['stored_filename']) {
+ $p_header['stored_filename'] = PclZipUtilPathReduction($v_local_header['stored_filename']);
+ }
+ }
+
+ // ----- Look for empty stored filename
+ if ($p_header['stored_filename'] == "") {
+ $p_header['status'] = "filtered";
+ }
+
+ // ----- Check the path length
+ if (strlen($p_header['stored_filename']) > 0xFF) {
+ $p_header['status'] = 'filename_too_long';
+ }
+
+ // ----- Look if no error, or file not skipped
+ if ($p_header['status'] == 'ok') {
+
+ // ----- Look for a file
+ if ($p_filedescr['type'] == 'file') {
+ // ----- Look for using temporary file to zip
+ if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF]))
+ && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON])
+ || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD])
+ && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_header['size'])) ) ) {
+ $v_result = $this->privAddFileUsingTempFile($p_filedescr, $p_header, $p_options);
+ if ($v_result < PCLZIP_ERR_NO_ERROR) {
+ return $v_result;
+ }
+ }
+
+ // ----- Use "in memory" zip algo
+ else {
+
+ // ----- Open the source file
+ if (($v_file = @fopen($p_filename, "rb")) == 0) {
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode");
+ return PclZip::errorCode();
+ }
+
+ // ----- Read the file content
+ $v_content = @fread($v_file, $p_header['size']);
+
+ // ----- Close the file
+ @fclose($v_file);
+
+ // ----- Calculate the CRC
+ $p_header['crc'] = @crc32($v_content);
+
+ // ----- Look for no compression
+ if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) {
+ // ----- Set header parameters
+ $p_header['compressed_size'] = $p_header['size'];
+ $p_header['compression'] = 0;
+ }
+
+ // ----- Look for normal compression
+ else {
+ // ----- Compress the content
+ $v_content = @gzdeflate($v_content);
+
+ // ----- Set header parameters
+ $p_header['compressed_size'] = strlen($v_content);
+ $p_header['compression'] = 8;
+ }
+
+ // ----- Call the header generation
+ if (($v_result = $this->privWriteFileHeader($p_header)) != 1) {
+ @fclose($v_file);
+ return $v_result;
+ }
+
+ // ----- Write the compressed (or not) content
+ @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']);
+
+ }
+
+ }
+
+ // ----- Look for a virtual file (a file from string)
+ else if ($p_filedescr['type'] == 'virtual_file') {
+
+ $v_content = $p_filedescr['content'];
+
+ // ----- Calculate the CRC
+ $p_header['crc'] = @crc32($v_content);
+
+ // ----- Look for no compression
+ if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) {
+ // ----- Set header parameters
+ $p_header['compressed_size'] = $p_header['size'];
+ $p_header['compression'] = 0;
+ }
+
+ // ----- Look for normal compression
+ else {
+ // ----- Compress the content
+ $v_content = @gzdeflate($v_content);
+
+ // ----- Set header parameters
+ $p_header['compressed_size'] = strlen($v_content);
+ $p_header['compression'] = 8;
+ }
+
+ // ----- Call the header generation
+ if (($v_result = $this->privWriteFileHeader($p_header)) != 1) {
+ @fclose($v_file);
+ return $v_result;
+ }
+
+ // ----- Write the compressed (or not) content
+ @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']);
+ }
+
+ // ----- Look for a directory
+ else if ($p_filedescr['type'] == 'folder') {
+ // ----- Look for directory last '/'
+ if (@substr($p_header['stored_filename'], -1) != '/') {
+ $p_header['stored_filename'] .= '/';
+ }
+
+ // ----- Set the file properties
+ $p_header['size'] = 0;
+ //$p_header['external'] = 0x41FF0010; // Value for a folder : to be checked
+ $p_header['external'] = 0x00000010; // Value for a folder : to be checked
+
+ // ----- Call the header generation
+ if (($v_result = $this->privWriteFileHeader($p_header)) != 1)
+ {
+ return $v_result;
+ }
+ }
+ }
+
+ // ----- Look for post-add callback
+ if (isset($p_options[PCLZIP_CB_POST_ADD])) {
+
+ // ----- Generate a local information
+ $v_local_header = array();
+ $this->privConvertHeader2FileInfo($p_header, $v_local_header);
+
+ // ----- Call the callback
+ // Here I do not use call_user_func() because I need to send a reference to the
+ // header.
+// eval('$v_result = '.$p_options[PCLZIP_CB_POST_ADD].'(PCLZIP_CB_POST_ADD, $v_local_header);');
+ $v_result = $p_options[PCLZIP_CB_POST_ADD](PCLZIP_CB_POST_ADD, $v_local_header);
+ if ($v_result == 0) {
+ // ----- Ignored
+ $v_result = 1;
+ }
+
+ // ----- Update the informations
+ // Nothing can be modified
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privAddFileUsingTempFile()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privAddFileUsingTempFile($p_filedescr, &$p_header, &$p_options)
+ {
+ $v_result=PCLZIP_ERR_NO_ERROR;
+
+ // ----- Working variable
+ $p_filename = $p_filedescr['filename'];
+
+
+ // ----- Open the source file
+ if (($v_file = @fopen($p_filename, "rb")) == 0) {
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode");
+ return PclZip::errorCode();
+ }
+
+ // ----- Creates a compressed temporary file
+ $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz';
+ if (($v_file_compressed = @gzopen($v_gzip_temp_name, "wb")) == 0) {
+ fclose($v_file);
+ PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode');
+ return PclZip::errorCode();
+ }
+
+ // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks
+ $v_size = filesize($p_filename);
+ while ($v_size != 0) {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @fread($v_file, $v_read_size);
+ //$v_binary_data = pack('a'.$v_read_size, $v_buffer);
+ @gzputs($v_file_compressed, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Close the file
+ @fclose($v_file);
+ @gzclose($v_file_compressed);
+
+ // ----- Check the minimum file size
+ if (filesize($v_gzip_temp_name) < 18) {
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'gzip temporary file \''.$v_gzip_temp_name.'\' has invalid filesize - should be minimum 18 bytes');
+ return PclZip::errorCode();
+ }
+
+ // ----- Extract the compressed attributes
+ if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0) {
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode');
+ return PclZip::errorCode();
+ }
+
+ // ----- Read the gzip file header
+ $v_binary_data = @fread($v_file_compressed, 10);
+ $v_data_header = unpack('a1id1/a1id2/a1cm/a1flag/Vmtime/a1xfl/a1os', $v_binary_data);
+
+ // ----- Check some parameters
+ $v_data_header['os'] = bin2hex($v_data_header['os']);
+
+ // ----- Read the gzip file footer
+ @fseek($v_file_compressed, filesize($v_gzip_temp_name)-8);
+ $v_binary_data = @fread($v_file_compressed, 8);
+ $v_data_footer = unpack('Vcrc/Vcompressed_size', $v_binary_data);
+
+ // ----- Set the attributes
+ $p_header['compression'] = ord($v_data_header['cm']);
+ //$p_header['mtime'] = $v_data_header['mtime'];
+ $p_header['crc'] = $v_data_footer['crc'];
+ $p_header['compressed_size'] = filesize($v_gzip_temp_name)-18;
+
+ // ----- Close the file
+ @fclose($v_file_compressed);
+
+ // ----- Call the header generation
+ if (($v_result = $this->privWriteFileHeader($p_header)) != 1) {
+ return $v_result;
+ }
+
+ // ----- Add the compressed data
+ if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0)
+ {
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode');
+ return PclZip::errorCode();
+ }
+
+ // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks
+ fseek($v_file_compressed, 10);
+ $v_size = $p_header['compressed_size'];
+ while ($v_size != 0)
+ {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @fread($v_file_compressed, $v_read_size);
+ //$v_binary_data = pack('a'.$v_read_size, $v_buffer);
+ @fwrite($this->zip_fd, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Close the file
+ @fclose($v_file_compressed);
+
+ // ----- Unlink the temporary file
+ @unlink($v_gzip_temp_name);
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privCalculateStoredFilename()
+ // Description :
+ // Based on file descriptor properties and global options, this method
+ // calculate the filename that will be stored in the archive.
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privCalculateStoredFilename(&$p_filedescr, &$p_options)
+ {
+ $v_result=1;
+
+ // ----- Working variables
+ $p_filename = $p_filedescr['filename'];
+ if (isset($p_options[PCLZIP_OPT_ADD_PATH])) {
+ $p_add_dir = $p_options[PCLZIP_OPT_ADD_PATH];
+ }
+ else {
+ $p_add_dir = '';
+ }
+ if (isset($p_options[PCLZIP_OPT_REMOVE_PATH])) {
+ $p_remove_dir = $p_options[PCLZIP_OPT_REMOVE_PATH];
+ }
+ else {
+ $p_remove_dir = '';
+ }
+ if (isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH])) {
+ $p_remove_all_dir = $p_options[PCLZIP_OPT_REMOVE_ALL_PATH];
+ }
+ else {
+ $p_remove_all_dir = 0;
+ }
+
+
+ // ----- Look for full name change
+ if (isset($p_filedescr['new_full_name'])) {
+ // ----- Remove drive letter if any
+ $v_stored_filename = PclZipUtilTranslateWinPath($p_filedescr['new_full_name']);
+ }
+
+ // ----- Look for path and/or short name change
+ else {
+
+ // ----- Look for short name change
+ // Its when we cahnge just the filename but not the path
+ if (isset($p_filedescr['new_short_name'])) {
+ $v_path_info = pathinfo($p_filename);
+ $v_dir = '';
+ if ($v_path_info['dirname'] != '') {
+ $v_dir = $v_path_info['dirname'].'/';
+ }
+ $v_stored_filename = $v_dir.$p_filedescr['new_short_name'];
+ }
+ else {
+ // ----- Calculate the stored filename
+ $v_stored_filename = $p_filename;
+ }
+
+ // ----- Look for all path to remove
+ if ($p_remove_all_dir) {
+ $v_stored_filename = basename($p_filename);
+ }
+ // ----- Look for partial path remove
+ else if ($p_remove_dir != "") {
+ if (substr($p_remove_dir, -1) != '/')
+ $p_remove_dir .= "/";
+
+ if ( (substr($p_filename, 0, 2) == "./")
+ || (substr($p_remove_dir, 0, 2) == "./")) {
+
+ if ( (substr($p_filename, 0, 2) == "./")
+ && (substr($p_remove_dir, 0, 2) != "./")) {
+ $p_remove_dir = "./".$p_remove_dir;
+ }
+ if ( (substr($p_filename, 0, 2) != "./")
+ && (substr($p_remove_dir, 0, 2) == "./")) {
+ $p_remove_dir = substr($p_remove_dir, 2);
+ }
+ }
+
+ $v_compare = PclZipUtilPathInclusion($p_remove_dir,
+ $v_stored_filename);
+ if ($v_compare > 0) {
+ if ($v_compare == 2) {
+ $v_stored_filename = "";
+ }
+ else {
+ $v_stored_filename = substr($v_stored_filename,
+ strlen($p_remove_dir));
+ }
+ }
+ }
+
+ // ----- Remove drive letter if any
+ $v_stored_filename = PclZipUtilTranslateWinPath($v_stored_filename);
+
+ // ----- Look for path to add
+ if ($p_add_dir != "") {
+ if (substr($p_add_dir, -1) == "/")
+ $v_stored_filename = $p_add_dir.$v_stored_filename;
+ else
+ $v_stored_filename = $p_add_dir."/".$v_stored_filename;
+ }
+ }
+
+ // ----- Filename (reduce the path of stored name)
+ $v_stored_filename = PclZipUtilPathReduction($v_stored_filename);
+ $p_filedescr['stored_filename'] = $v_stored_filename;
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privWriteFileHeader()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privWriteFileHeader(&$p_header)
+ {
+ $v_result=1;
+
+ // ----- Store the offset position of the file
+ $p_header['offset'] = ftell($this->zip_fd);
+
+ // ----- Transform UNIX mtime to DOS format mdate/mtime
+ $v_date = getdate($p_header['mtime']);
+ $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2;
+ $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday'];
+
+ // ----- Packed data
+ $v_binary_data = pack("VvvvvvVVVvv", 0x04034b50,
+ $p_header['version_extracted'], $p_header['flag'],
+ $p_header['compression'], $v_mtime, $v_mdate,
+ $p_header['crc'], $p_header['compressed_size'],
+ $p_header['size'],
+ strlen($p_header['stored_filename']),
+ $p_header['extra_len']);
+
+ // ----- Write the first 148 bytes of the header in the archive
+ fputs($this->zip_fd, $v_binary_data, 30);
+
+ // ----- Write the variable fields
+ if (strlen($p_header['stored_filename']) != 0)
+ {
+ fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename']));
+ }
+ if ($p_header['extra_len'] != 0)
+ {
+ fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']);
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privWriteCentralFileHeader()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privWriteCentralFileHeader(&$p_header)
+ {
+ $v_result=1;
+
+ // TBC
+ //for(reset($p_header); $key = key($p_header); next($p_header)) {
+ //}
+
+ // ----- Transform UNIX mtime to DOS format mdate/mtime
+ $v_date = getdate($p_header['mtime']);
+ $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2;
+ $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday'];
+
+
+ // ----- Packed data
+ $v_binary_data = pack("VvvvvvvVVVvvvvvVV", 0x02014b50,
+ $p_header['version'], $p_header['version_extracted'],
+ $p_header['flag'], $p_header['compression'],
+ $v_mtime, $v_mdate, $p_header['crc'],
+ $p_header['compressed_size'], $p_header['size'],
+ strlen($p_header['stored_filename']),
+ $p_header['extra_len'], $p_header['comment_len'],
+ $p_header['disk'], $p_header['internal'],
+ $p_header['external'], $p_header['offset']);
+
+ // ----- Write the 42 bytes of the header in the zip file
+ fputs($this->zip_fd, $v_binary_data, 46);
+
+ // ----- Write the variable fields
+ if (strlen($p_header['stored_filename']) != 0)
+ {
+ fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename']));
+ }
+ if ($p_header['extra_len'] != 0)
+ {
+ fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']);
+ }
+ if ($p_header['comment_len'] != 0)
+ {
+ fputs($this->zip_fd, $p_header['comment'], $p_header['comment_len']);
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privWriteCentralHeader()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privWriteCentralHeader($p_nb_entries, $p_size, $p_offset, $p_comment)
+ {
+ $v_result=1;
+
+ // ----- Packed data
+ $v_binary_data = pack("VvvvvVVv", 0x06054b50, 0, 0, $p_nb_entries,
+ $p_nb_entries, $p_size,
+ $p_offset, strlen($p_comment));
+
+ // ----- Write the 22 bytes of the header in the zip file
+ fputs($this->zip_fd, $v_binary_data, 22);
+
+ // ----- Write the variable fields
+ if (strlen($p_comment) != 0)
+ {
+ fputs($this->zip_fd, $p_comment, strlen($p_comment));
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privList()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privList(&$p_list)
+ {
+ $v_result=1;
+
+ // ----- Magic quotes trick
+ $this->privDisableMagicQuotes();
+
+ // ----- Open the zip file
+ if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0)
+ {
+ // ----- Magic quotes trick
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Read the central directory informations
+ $v_central_dir = array();
+ if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
+ {
+ $this->privSwapBackMagicQuotes();
+ return $v_result;
+ }
+
+ // ----- Go to beginning of Central Dir
+ @rewind($this->zip_fd);
+ if (@fseek($this->zip_fd, $v_central_dir['offset']))
+ {
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Read each entry
+ for ($i=0; $i<$v_central_dir['entries']; $i++)
+ {
+ // ----- Read the file header
+ if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1)
+ {
+ $this->privSwapBackMagicQuotes();
+ return $v_result;
+ }
+ $v_header['index'] = $i;
+
+ // ----- Get the only interesting attributes
+ $this->privConvertHeader2FileInfo($v_header, $p_list[$i]);
+ unset($v_header);
+ }
+
+ // ----- Close the zip file
+ $this->privCloseFd();
+
+ // ----- Magic quotes trick
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privConvertHeader2FileInfo()
+ // Description :
+ // This function takes the file informations from the central directory
+ // entries and extract the interesting parameters that will be given back.
+ // The resulting file infos are set in the array $p_info
+ // $p_info['filename'] : Filename with full path. Given by user (add),
+ // extracted in the filesystem (extract).
+ // $p_info['stored_filename'] : Stored filename in the archive.
+ // $p_info['size'] = Size of the file.
+ // $p_info['compressed_size'] = Compressed size of the file.
+ // $p_info['mtime'] = Last modification date of the file.
+ // $p_info['comment'] = Comment associated with the file.
+ // $p_info['folder'] = true/false : indicates if the entry is a folder or not.
+ // $p_info['status'] = status of the action on the file.
+ // $p_info['crc'] = CRC of the file content.
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privConvertHeader2FileInfo($p_header, &$p_info)
+ {
+ $v_result=1;
+
+ // ----- Get the interesting attributes
+ $v_temp_path = PclZipUtilPathReduction($p_header['filename']);
+ $p_info['filename'] = $v_temp_path;
+ $v_temp_path = PclZipUtilPathReduction($p_header['stored_filename']);
+ $p_info['stored_filename'] = $v_temp_path;
+ $p_info['size'] = $p_header['size'];
+ $p_info['compressed_size'] = $p_header['compressed_size'];
+ $p_info['mtime'] = $p_header['mtime'];
+ $p_info['comment'] = $p_header['comment'];
+ $p_info['folder'] = (($p_header['external']&0x00000010)==0x00000010);
+ $p_info['index'] = $p_header['index'];
+ $p_info['status'] = $p_header['status'];
+ $p_info['crc'] = $p_header['crc'];
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privExtractByRule()
+ // Description :
+ // Extract a file or directory depending of rules (by index, by name, ...)
+ // Parameters :
+ // $p_file_list : An array where will be placed the properties of each
+ // extracted file
+ // $p_path : Path to add while writing the extracted files
+ // $p_remove_path : Path to remove (from the file memorized path) while writing the
+ // extracted files. If the path does not match the file path,
+ // the file is extracted with its memorized path.
+ // $p_remove_path does not apply to 'list' mode.
+ // $p_path and $p_remove_path are commulative.
+ // Return Values :
+ // 1 on success,0 or less on error (see error code list)
+ // --------------------------------------------------------------------------------
+ function privExtractByRule(&$p_file_list, $p_path, $p_remove_path, $p_remove_all_path, &$p_options)
+ {
+ $v_result=1;
+
+ // ----- Magic quotes trick
+ $this->privDisableMagicQuotes();
+
+ // ----- Check the path
+ if ( ($p_path == "")
+ || ( (substr($p_path, 0, 1) != "/")
+ && (substr($p_path, 0, 3) != "../")
+ && (substr($p_path,1,2)!=":/")))
+ $p_path = "./".$p_path;
+
+ // ----- Reduce the path last (and duplicated) '/'
+ if (($p_path != "./") && ($p_path != "/"))
+ {
+ // ----- Look for the path end '/'
+ while (substr($p_path, -1) == "/")
+ {
+ $p_path = substr($p_path, 0, strlen($p_path)-1);
+ }
+ }
+
+ // ----- Look for path to remove format (should end by /)
+ if (($p_remove_path != "") && (substr($p_remove_path, -1) != '/'))
+ {
+ $p_remove_path .= '/';
+ }
+ $p_remove_path_size = strlen($p_remove_path);
+
+ // ----- Open the zip file
+ if (($v_result = $this->privOpenFd('rb')) != 1)
+ {
+ $this->privSwapBackMagicQuotes();
+ return $v_result;
+ }
+
+ // ----- Read the central directory informations
+ $v_central_dir = array();
+ if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
+ {
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+
+ return $v_result;
+ }
+
+ // ----- Start at beginning of Central Dir
+ $v_pos_entry = $v_central_dir['offset'];
+
+ // ----- Read each entry
+ $j_start = 0;
+ for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++)
+ {
+
+ // ----- Read next Central dir entry
+ @rewind($this->zip_fd);
+ if (@fseek($this->zip_fd, $v_pos_entry))
+ {
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Read the file header
+ $v_header = array();
+ if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1)
+ {
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+
+ return $v_result;
+ }
+
+ // ----- Store the index
+ $v_header['index'] = $i;
+
+ // ----- Store the file position
+ $v_pos_entry = ftell($this->zip_fd);
+
+ // ----- Look for the specific extract rules
+ $v_extract = false;
+
+ // ----- Look for extract by name rule
+ if ( (isset($p_options[PCLZIP_OPT_BY_NAME]))
+ && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) {
+
+ // ----- Look if the filename is in the list
+ for ($j=0; ($j strlen($p_options[PCLZIP_OPT_BY_NAME][$j]))
+ && (substr($v_header['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) {
+ $v_extract = true;
+ }
+ }
+ // ----- Look for a filename
+ elseif ($v_header['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) {
+ $v_extract = true;
+ }
+ }
+ }
+
+ // ----- Look for extract by ereg rule
+ // ereg() is deprecated with PHP 5.3
+ /*
+ else if ( (isset($p_options[PCLZIP_OPT_BY_EREG]))
+ && ($p_options[PCLZIP_OPT_BY_EREG] != "")) {
+
+ if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header['stored_filename'])) {
+ $v_extract = true;
+ }
+ }
+ */
+
+ // ----- Look for extract by preg rule
+ else if ( (isset($p_options[PCLZIP_OPT_BY_PREG]))
+ && ($p_options[PCLZIP_OPT_BY_PREG] != "")) {
+
+ if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header['stored_filename'])) {
+ $v_extract = true;
+ }
+ }
+
+ // ----- Look for extract by index rule
+ else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX]))
+ && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) {
+
+ // ----- Look if the index is in the list
+ for ($j=$j_start; ($j=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) {
+ $v_extract = true;
+ }
+ if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) {
+ $j_start = $j+1;
+ }
+
+ if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) {
+ break;
+ }
+ }
+ }
+
+ // ----- Look for no rule, which means extract all the archive
+ else {
+ $v_extract = true;
+ }
+
+ // ----- Check compression method
+ if ( ($v_extract)
+ && ( ($v_header['compression'] != 8)
+ && ($v_header['compression'] != 0))) {
+ $v_header['status'] = 'unsupported_compression';
+
+ // ----- Look for PCLZIP_OPT_STOP_ON_ERROR
+ if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR]))
+ && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) {
+
+ $this->privSwapBackMagicQuotes();
+
+ PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_COMPRESSION,
+ "Filename '".$v_header['stored_filename']."' is "
+ ."compressed by an unsupported compression "
+ ."method (".$v_header['compression'].") ");
+
+ return PclZip::errorCode();
+ }
+ }
+
+ // ----- Check encrypted files
+ if (($v_extract) && (($v_header['flag'] & 1) == 1)) {
+ $v_header['status'] = 'unsupported_encryption';
+
+ // ----- Look for PCLZIP_OPT_STOP_ON_ERROR
+ if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR]))
+ && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) {
+
+ $this->privSwapBackMagicQuotes();
+
+ PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION,
+ "Unsupported encryption for "
+ ." filename '".$v_header['stored_filename']
+ ."'");
+
+ return PclZip::errorCode();
+ }
+ }
+
+ // ----- Look for real extraction
+ if (($v_extract) && ($v_header['status'] != 'ok')) {
+ $v_result = $this->privConvertHeader2FileInfo($v_header,
+ $p_file_list[$v_nb_extracted++]);
+ if ($v_result != 1) {
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+ return $v_result;
+ }
+
+ $v_extract = false;
+ }
+
+ // ----- Look for real extraction
+ if ($v_extract)
+ {
+
+ // ----- Go to the file position
+ @rewind($this->zip_fd);
+ if (@fseek($this->zip_fd, $v_header['offset']))
+ {
+ // ----- Close the zip file
+ $this->privCloseFd();
+
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Look for extraction as string
+ if ($p_options[PCLZIP_OPT_EXTRACT_AS_STRING]) {
+
+ $v_string = '';
+
+ // ----- Extracting the file
+ $v_result1 = $this->privExtractFileAsString($v_header, $v_string, $p_options);
+ if ($v_result1 < 1) {
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+ return $v_result1;
+ }
+
+ // ----- Get the only interesting attributes
+ if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted])) != 1)
+ {
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+
+ return $v_result;
+ }
+
+ // ----- Set the file content
+ $p_file_list[$v_nb_extracted]['content'] = $v_string;
+
+ // ----- Next extracted file
+ $v_nb_extracted++;
+
+ // ----- Look for user callback abort
+ if ($v_result1 == 2) {
+ break;
+ }
+ }
+ // ----- Look for extraction in standard output
+ elseif ( (isset($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT]))
+ && ($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT])) {
+ // ----- Extracting the file in standard output
+ $v_result1 = $this->privExtractFileInOutput($v_header, $p_options);
+ if ($v_result1 < 1) {
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+ return $v_result1;
+ }
+
+ // ----- Get the only interesting attributes
+ if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) {
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+ return $v_result;
+ }
+
+ // ----- Look for user callback abort
+ if ($v_result1 == 2) {
+ break;
+ }
+ }
+ // ----- Look for normal extraction
+ else {
+ // ----- Extracting the file
+ $v_result1 = $this->privExtractFile($v_header,
+ $p_path, $p_remove_path,
+ $p_remove_all_path,
+ $p_options);
+ if ($v_result1 < 1) {
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+ return $v_result1;
+ }
+
+ // ----- Get the only interesting attributes
+ if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1)
+ {
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+
+ return $v_result;
+ }
+
+ // ----- Look for user callback abort
+ if ($v_result1 == 2) {
+ break;
+ }
+ }
+ }
+ }
+
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privExtractFile()
+ // Description :
+ // Parameters :
+ // Return Values :
+ //
+ // 1 : ... ?
+ // PCLZIP_ERR_USER_ABORTED(2) : User ask for extraction stop in callback
+ // --------------------------------------------------------------------------------
+ function privExtractFile(&$p_entry, $p_path, $p_remove_path, $p_remove_all_path, &$p_options)
+ {
+ $v_result=1;
+
+ // ----- Read the file header
+ if (($v_result = $this->privReadFileHeader($v_header)) != 1)
+ {
+ // ----- Return
+ return $v_result;
+ }
+
+
+ // ----- Check that the file header is coherent with $p_entry info
+ if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) {
+ // TBC
+ }
+
+ // ----- Look for all path to remove
+ if ($p_remove_all_path == true) {
+ // ----- Look for folder entry that not need to be extracted
+ if (($p_entry['external']&0x00000010)==0x00000010) {
+
+ $p_entry['status'] = "filtered";
+
+ return $v_result;
+ }
+
+ // ----- Get the basename of the path
+ $p_entry['filename'] = basename($p_entry['filename']);
+ }
+
+ // ----- Look for path to remove
+ else if ($p_remove_path != "")
+ {
+ if (PclZipUtilPathInclusion($p_remove_path, $p_entry['filename']) == 2)
+ {
+
+ // ----- Change the file status
+ $p_entry['status'] = "filtered";
+
+ // ----- Return
+ return $v_result;
+ }
+
+ $p_remove_path_size = strlen($p_remove_path);
+ if (substr($p_entry['filename'], 0, $p_remove_path_size) == $p_remove_path)
+ {
+
+ // ----- Remove the path
+ $p_entry['filename'] = substr($p_entry['filename'], $p_remove_path_size);
+
+ }
+ }
+
+ // ----- Add the path
+ if ($p_path != '') {
+ $p_entry['filename'] = $p_path."/".$p_entry['filename'];
+ }
+
+ // ----- Check a base_dir_restriction
+ if (isset($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION])) {
+ $v_inclusion
+ = PclZipUtilPathInclusion($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION],
+ $p_entry['filename']);
+ if ($v_inclusion == 0) {
+
+ PclZip::privErrorLog(PCLZIP_ERR_DIRECTORY_RESTRICTION,
+ "Filename '".$p_entry['filename']."' is "
+ ."outside PCLZIP_OPT_EXTRACT_DIR_RESTRICTION");
+
+ return PclZip::errorCode();
+ }
+ }
+
+ // ----- Look for pre-extract callback
+ if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) {
+
+ // ----- Generate a local information
+ $v_local_header = array();
+ $this->privConvertHeader2FileInfo($p_entry, $v_local_header);
+
+ // ----- Call the callback
+ // Here I do not use call_user_func() because I need to send a reference to the
+ // header.
+// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);');
+ $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header);
+ if ($v_result == 0) {
+ // ----- Change the file status
+ $p_entry['status'] = "skipped";
+ $v_result = 1;
+ }
+
+ // ----- Look for abort result
+ if ($v_result == 2) {
+ // ----- This status is internal and will be changed in 'skipped'
+ $p_entry['status'] = "aborted";
+ $v_result = PCLZIP_ERR_USER_ABORTED;
+ }
+
+ // ----- Update the informations
+ // Only some fields can be modified
+ $p_entry['filename'] = $v_local_header['filename'];
+ }
+
+
+ // ----- Look if extraction should be done
+ if ($p_entry['status'] == 'ok') {
+
+ // ----- Look for specific actions while the file exist
+ if (file_exists($p_entry['filename']))
+ {
+
+ // ----- Look if file is a directory
+ if (is_dir($p_entry['filename']))
+ {
+
+ // ----- Change the file status
+ $p_entry['status'] = "already_a_directory";
+
+ // ----- Look for PCLZIP_OPT_STOP_ON_ERROR
+ // For historical reason first PclZip implementation does not stop
+ // when this kind of error occurs.
+ if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR]))
+ && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) {
+
+ PclZip::privErrorLog(PCLZIP_ERR_ALREADY_A_DIRECTORY,
+ "Filename '".$p_entry['filename']."' is "
+ ."already used by an existing directory");
+
+ return PclZip::errorCode();
+ }
+ }
+ // ----- Look if file is write protected
+ else if (!is_writeable($p_entry['filename']))
+ {
+
+ // ----- Change the file status
+ $p_entry['status'] = "write_protected";
+
+ // ----- Look for PCLZIP_OPT_STOP_ON_ERROR
+ // For historical reason first PclZip implementation does not stop
+ // when this kind of error occurs.
+ if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR]))
+ && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) {
+
+ PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL,
+ "Filename '".$p_entry['filename']."' exists "
+ ."and is write protected");
+
+ return PclZip::errorCode();
+ }
+ }
+
+ // ----- Look if the extracted file is older
+ else if (filemtime($p_entry['filename']) > $p_entry['mtime'])
+ {
+ // ----- Change the file status
+ if ( (isset($p_options[PCLZIP_OPT_REPLACE_NEWER]))
+ && ($p_options[PCLZIP_OPT_REPLACE_NEWER]===true)) {
+ }
+ else {
+ $p_entry['status'] = "newer_exist";
+
+ // ----- Look for PCLZIP_OPT_STOP_ON_ERROR
+ // For historical reason first PclZip implementation does not stop
+ // when this kind of error occurs.
+ if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR]))
+ && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) {
+
+ PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL,
+ "Newer version of '".$p_entry['filename']."' exists "
+ ."and option PCLZIP_OPT_REPLACE_NEWER is not selected");
+
+ return PclZip::errorCode();
+ }
+ }
+ }
+ else {
+ }
+ }
+
+ // ----- Check the directory availability and create it if necessary
+ else {
+ if ((($p_entry['external']&0x00000010)==0x00000010) || (substr($p_entry['filename'], -1) == '/'))
+ $v_dir_to_check = $p_entry['filename'];
+ else if (!strstr($p_entry['filename'], "/"))
+ $v_dir_to_check = "";
+ else
+ $v_dir_to_check = dirname($p_entry['filename']);
+
+ if (($v_result = $this->privDirCheck($v_dir_to_check, (($p_entry['external']&0x00000010)==0x00000010))) != 1) {
+
+ // ----- Change the file status
+ $p_entry['status'] = "path_creation_fail";
+
+ // ----- Return
+ //return $v_result;
+ $v_result = 1;
+ }
+ }
+ }
+
+ // ----- Look if extraction should be done
+ if ($p_entry['status'] == 'ok') {
+
+ // ----- Do the extraction (if not a folder)
+ if (!(($p_entry['external']&0x00000010)==0x00000010))
+ {
+ // ----- Look for not compressed file
+ if ($p_entry['compression'] == 0) {
+
+ // ----- Opening destination file
+ if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0)
+ {
+
+ // ----- Change the file status
+ $p_entry['status'] = "write_error";
+
+ // ----- Return
+ return $v_result;
+ }
+
+
+ // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks
+ $v_size = $p_entry['compressed_size'];
+ while ($v_size != 0)
+ {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @fread($this->zip_fd, $v_read_size);
+ /* Try to speed up the code
+ $v_binary_data = pack('a'.$v_read_size, $v_buffer);
+ @fwrite($v_dest_file, $v_binary_data, $v_read_size);
+ */
+ @fwrite($v_dest_file, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Closing the destination file
+ fclose($v_dest_file);
+
+ // ----- Change the file mtime
+ touch($p_entry['filename'], $p_entry['mtime']);
+
+
+ }
+ else {
+ // ----- TBC
+ // Need to be finished
+ if (($p_entry['flag'] & 1) == 1) {
+ PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, 'File \''.$p_entry['filename'].'\' is encrypted. Encrypted files are not supported.');
+ return PclZip::errorCode();
+ }
+
+
+ // ----- Look for using temporary file to unzip
+ if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF]))
+ && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON])
+ || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD])
+ && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_entry['size'])) ) ) {
+ $v_result = $this->privExtractFileUsingTempFile($p_entry, $p_options);
+ if ($v_result < PCLZIP_ERR_NO_ERROR) {
+ return $v_result;
+ }
+ }
+
+ // ----- Look for extract in memory
+ else {
+
+
+ // ----- Read the compressed file in a buffer (one shot)
+ $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']);
+
+ // ----- Decompress the file
+ $v_file_content = @gzinflate($v_buffer);
+ unset($v_buffer);
+ if ($v_file_content === FALSE) {
+
+ // ----- Change the file status
+ // TBC
+ $p_entry['status'] = "error";
+
+ return $v_result;
+ }
+
+ // ----- Opening destination file
+ if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) {
+
+ // ----- Change the file status
+ $p_entry['status'] = "write_error";
+
+ return $v_result;
+ }
+
+ // ----- Write the uncompressed data
+ @fwrite($v_dest_file, $v_file_content, $p_entry['size']);
+ unset($v_file_content);
+
+ // ----- Closing the destination file
+ @fclose($v_dest_file);
+
+ }
+
+ // ----- Change the file mtime
+ @touch($p_entry['filename'], $p_entry['mtime']);
+ }
+
+ // ----- Look for chmod option
+ if (isset($p_options[PCLZIP_OPT_SET_CHMOD])) {
+
+ // ----- Change the mode of the file
+ @chmod($p_entry['filename'], $p_options[PCLZIP_OPT_SET_CHMOD]);
+ }
+
+ }
+ }
+
+ // ----- Change abort status
+ if ($p_entry['status'] == "aborted") {
+ $p_entry['status'] = "skipped";
+ }
+
+ // ----- Look for post-extract callback
+ elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) {
+
+ // ----- Generate a local information
+ $v_local_header = array();
+ $this->privConvertHeader2FileInfo($p_entry, $v_local_header);
+
+ // ----- Call the callback
+ // Here I do not use call_user_func() because I need to send a reference to the
+ // header.
+// eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);');
+ $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header);
+
+ // ----- Look for abort result
+ if ($v_result == 2) {
+ $v_result = PCLZIP_ERR_USER_ABORTED;
+ }
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privExtractFileUsingTempFile()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privExtractFileUsingTempFile(&$p_entry, &$p_options)
+ {
+ $v_result=1;
+
+ // ----- Creates a temporary file
+ $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz';
+ if (($v_dest_file = @fopen($v_gzip_temp_name, "wb")) == 0) {
+ fclose($v_file);
+ PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode');
+ return PclZip::errorCode();
+ }
+
+
+ // ----- Write gz file format header
+ $v_binary_data = pack('va1a1Va1a1', 0x8b1f, Chr($p_entry['compression']), Chr(0x00), time(), Chr(0x00), Chr(3));
+ @fwrite($v_dest_file, $v_binary_data, 10);
+
+ // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks
+ $v_size = $p_entry['compressed_size'];
+ while ($v_size != 0)
+ {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @fread($this->zip_fd, $v_read_size);
+ //$v_binary_data = pack('a'.$v_read_size, $v_buffer);
+ @fwrite($v_dest_file, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Write gz file format footer
+ $v_binary_data = pack('VV', $p_entry['crc'], $p_entry['size']);
+ @fwrite($v_dest_file, $v_binary_data, 8);
+
+ // ----- Close the temporary file
+ @fclose($v_dest_file);
+
+ // ----- Opening destination file
+ if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) {
+ $p_entry['status'] = "write_error";
+ return $v_result;
+ }
+
+ // ----- Open the temporary gz file
+ if (($v_src_file = @gzopen($v_gzip_temp_name, 'rb')) == 0) {
+ @fclose($v_dest_file);
+ $p_entry['status'] = "read_error";
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode');
+ return PclZip::errorCode();
+ }
+
+
+ // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks
+ $v_size = $p_entry['size'];
+ while ($v_size != 0) {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @gzread($v_src_file, $v_read_size);
+ //$v_binary_data = pack('a'.$v_read_size, $v_buffer);
+ @fwrite($v_dest_file, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+ @fclose($v_dest_file);
+ @gzclose($v_src_file);
+
+ // ----- Delete the temporary file
+ @unlink($v_gzip_temp_name);
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privExtractFileInOutput()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privExtractFileInOutput(&$p_entry, &$p_options)
+ {
+ $v_result=1;
+
+ // ----- Read the file header
+ if (($v_result = $this->privReadFileHeader($v_header)) != 1) {
+ return $v_result;
+ }
+
+
+ // ----- Check that the file header is coherent with $p_entry info
+ if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) {
+ // TBC
+ }
+
+ // ----- Look for pre-extract callback
+ if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) {
+
+ // ----- Generate a local information
+ $v_local_header = array();
+ $this->privConvertHeader2FileInfo($p_entry, $v_local_header);
+
+ // ----- Call the callback
+ // Here I do not use call_user_func() because I need to send a reference to the
+ // header.
+// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);');
+ $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header);
+ if ($v_result == 0) {
+ // ----- Change the file status
+ $p_entry['status'] = "skipped";
+ $v_result = 1;
+ }
+
+ // ----- Look for abort result
+ if ($v_result == 2) {
+ // ----- This status is internal and will be changed in 'skipped'
+ $p_entry['status'] = "aborted";
+ $v_result = PCLZIP_ERR_USER_ABORTED;
+ }
+
+ // ----- Update the informations
+ // Only some fields can be modified
+ $p_entry['filename'] = $v_local_header['filename'];
+ }
+
+ // ----- Trace
+
+ // ----- Look if extraction should be done
+ if ($p_entry['status'] == 'ok') {
+
+ // ----- Do the extraction (if not a folder)
+ if (!(($p_entry['external']&0x00000010)==0x00000010)) {
+ // ----- Look for not compressed file
+ if ($p_entry['compressed_size'] == $p_entry['size']) {
+
+ // ----- Read the file in a buffer (one shot)
+ $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']);
+
+ // ----- Send the file to the output
+ echo $v_buffer;
+ unset($v_buffer);
+ }
+ else {
+
+ // ----- Read the compressed file in a buffer (one shot)
+ $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']);
+
+ // ----- Decompress the file
+ $v_file_content = gzinflate($v_buffer);
+ unset($v_buffer);
+
+ // ----- Send the file to the output
+ echo $v_file_content;
+ unset($v_file_content);
+ }
+ }
+ }
+
+ // ----- Change abort status
+ if ($p_entry['status'] == "aborted") {
+ $p_entry['status'] = "skipped";
+ }
+
+ // ----- Look for post-extract callback
+ elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) {
+
+ // ----- Generate a local information
+ $v_local_header = array();
+ $this->privConvertHeader2FileInfo($p_entry, $v_local_header);
+
+ // ----- Call the callback
+ // Here I do not use call_user_func() because I need to send a reference to the
+ // header.
+// eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);');
+ $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header);
+
+ // ----- Look for abort result
+ if ($v_result == 2) {
+ $v_result = PCLZIP_ERR_USER_ABORTED;
+ }
+ }
+
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privExtractFileAsString()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privExtractFileAsString(&$p_entry, &$p_string, &$p_options)
+ {
+ $v_result=1;
+
+ // ----- Read the file header
+ $v_header = array();
+ if (($v_result = $this->privReadFileHeader($v_header)) != 1)
+ {
+ // ----- Return
+ return $v_result;
+ }
+
+
+ // ----- Check that the file header is coherent with $p_entry info
+ if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) {
+ // TBC
+ }
+
+ // ----- Look for pre-extract callback
+ if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) {
+
+ // ----- Generate a local information
+ $v_local_header = array();
+ $this->privConvertHeader2FileInfo($p_entry, $v_local_header);
+
+ // ----- Call the callback
+ // Here I do not use call_user_func() because I need to send a reference to the
+ // header.
+// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);');
+ $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header);
+ if ($v_result == 0) {
+ // ----- Change the file status
+ $p_entry['status'] = "skipped";
+ $v_result = 1;
+ }
+
+ // ----- Look for abort result
+ if ($v_result == 2) {
+ // ----- This status is internal and will be changed in 'skipped'
+ $p_entry['status'] = "aborted";
+ $v_result = PCLZIP_ERR_USER_ABORTED;
+ }
+
+ // ----- Update the informations
+ // Only some fields can be modified
+ $p_entry['filename'] = $v_local_header['filename'];
+ }
+
+
+ // ----- Look if extraction should be done
+ if ($p_entry['status'] == 'ok') {
+
+ // ----- Do the extraction (if not a folder)
+ if (!(($p_entry['external']&0x00000010)==0x00000010)) {
+ // ----- Look for not compressed file
+ // if ($p_entry['compressed_size'] == $p_entry['size'])
+ if ($p_entry['compression'] == 0) {
+
+ // ----- Reading the file
+ $p_string = @fread($this->zip_fd, $p_entry['compressed_size']);
+ }
+ else {
+
+ // ----- Reading the file
+ $v_data = @fread($this->zip_fd, $p_entry['compressed_size']);
+
+ // ----- Decompress the file
+ if (($p_string = @gzinflate($v_data)) === FALSE) {
+ // TBC
+ }
+ }
+
+ // ----- Trace
+ }
+ else {
+ // TBC : error : can not extract a folder in a string
+ }
+
+ }
+
+ // ----- Change abort status
+ if ($p_entry['status'] == "aborted") {
+ $p_entry['status'] = "skipped";
+ }
+
+ // ----- Look for post-extract callback
+ elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) {
+
+ // ----- Generate a local information
+ $v_local_header = array();
+ $this->privConvertHeader2FileInfo($p_entry, $v_local_header);
+
+ // ----- Swap the content to header
+ $v_local_header['content'] = $p_string;
+ $p_string = '';
+
+ // ----- Call the callback
+ // Here I do not use call_user_func() because I need to send a reference to the
+ // header.
+// eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);');
+ $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header);
+
+ // ----- Swap back the content to header
+ $p_string = $v_local_header['content'];
+ unset($v_local_header['content']);
+
+ // ----- Look for abort result
+ if ($v_result == 2) {
+ $v_result = PCLZIP_ERR_USER_ABORTED;
+ }
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privReadFileHeader()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privReadFileHeader(&$p_header)
+ {
+ $v_result=1;
+
+ // ----- Read the 4 bytes signature
+ $v_binary_data = @fread($this->zip_fd, 4);
+ $v_data = unpack('Vid', $v_binary_data);
+
+ // ----- Check signature
+ if ($v_data['id'] != 0x04034b50)
+ {
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Read the first 42 bytes of the header
+ $v_binary_data = fread($this->zip_fd, 26);
+
+ // ----- Look for invalid block size
+ if (strlen($v_binary_data) != 26)
+ {
+ $p_header['filename'] = "";
+ $p_header['status'] = "invalid_header";
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data));
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Extract the values
+ $v_data = unpack('vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $v_binary_data);
+
+ // ----- Get filename
+ $p_header['filename'] = fread($this->zip_fd, $v_data['filename_len']);
+
+ // ----- Get extra_fields
+ if ($v_data['extra_len'] != 0) {
+ $p_header['extra'] = fread($this->zip_fd, $v_data['extra_len']);
+ }
+ else {
+ $p_header['extra'] = '';
+ }
+
+ // ----- Extract properties
+ $p_header['version_extracted'] = $v_data['version'];
+ $p_header['compression'] = $v_data['compression'];
+ $p_header['size'] = $v_data['size'];
+ $p_header['compressed_size'] = $v_data['compressed_size'];
+ $p_header['crc'] = $v_data['crc'];
+ $p_header['flag'] = $v_data['flag'];
+ $p_header['filename_len'] = $v_data['filename_len'];
+
+ // ----- Recuperate date in UNIX format
+ $p_header['mdate'] = $v_data['mdate'];
+ $p_header['mtime'] = $v_data['mtime'];
+ if ($p_header['mdate'] && $p_header['mtime'])
+ {
+ // ----- Extract time
+ $v_hour = ($p_header['mtime'] & 0xF800) >> 11;
+ $v_minute = ($p_header['mtime'] & 0x07E0) >> 5;
+ $v_seconde = ($p_header['mtime'] & 0x001F)*2;
+
+ // ----- Extract date
+ $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980;
+ $v_month = ($p_header['mdate'] & 0x01E0) >> 5;
+ $v_day = $p_header['mdate'] & 0x001F;
+
+ // ----- Get UNIX date format
+ $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year);
+
+ }
+ else
+ {
+ $p_header['mtime'] = time();
+ }
+
+ // TBC
+ //for(reset($v_data); $key = key($v_data); next($v_data)) {
+ //}
+
+ // ----- Set the stored filename
+ $p_header['stored_filename'] = $p_header['filename'];
+
+ // ----- Set the status field
+ $p_header['status'] = "ok";
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privReadCentralFileHeader()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privReadCentralFileHeader(&$p_header)
+ {
+ $v_result=1;
+
+ // ----- Read the 4 bytes signature
+ $v_binary_data = @fread($this->zip_fd, 4);
+ $v_data = unpack('Vid', $v_binary_data);
+
+ // ----- Check signature
+ if ($v_data['id'] != 0x02014b50)
+ {
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Read the first 42 bytes of the header
+ $v_binary_data = fread($this->zip_fd, 42);
+
+ // ----- Look for invalid block size
+ if (strlen($v_binary_data) != 42)
+ {
+ $p_header['filename'] = "";
+ $p_header['status'] = "invalid_header";
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data));
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Extract the values
+ $p_header = unpack('vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $v_binary_data);
+
+ // ----- Get filename
+ if ($p_header['filename_len'] != 0)
+ $p_header['filename'] = fread($this->zip_fd, $p_header['filename_len']);
+ else
+ $p_header['filename'] = '';
+
+ // ----- Get extra
+ if ($p_header['extra_len'] != 0)
+ $p_header['extra'] = fread($this->zip_fd, $p_header['extra_len']);
+ else
+ $p_header['extra'] = '';
+
+ // ----- Get comment
+ if ($p_header['comment_len'] != 0)
+ $p_header['comment'] = fread($this->zip_fd, $p_header['comment_len']);
+ else
+ $p_header['comment'] = '';
+
+ // ----- Extract properties
+
+ // ----- Recuperate date in UNIX format
+ //if ($p_header['mdate'] && $p_header['mtime'])
+ // TBC : bug : this was ignoring time with 0/0/0
+ if (1)
+ {
+ // ----- Extract time
+ $v_hour = ($p_header['mtime'] & 0xF800) >> 11;
+ $v_minute = ($p_header['mtime'] & 0x07E0) >> 5;
+ $v_seconde = ($p_header['mtime'] & 0x001F)*2;
+
+ // ----- Extract date
+ $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980;
+ $v_month = ($p_header['mdate'] & 0x01E0) >> 5;
+ $v_day = $p_header['mdate'] & 0x001F;
+
+ // ----- Get UNIX date format
+ $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year);
+
+ }
+ else
+ {
+ $p_header['mtime'] = time();
+ }
+
+ // ----- Set the stored filename
+ $p_header['stored_filename'] = $p_header['filename'];
+
+ // ----- Set default status to ok
+ $p_header['status'] = 'ok';
+
+ // ----- Look if it is a directory
+ if (substr($p_header['filename'], -1) == '/') {
+ //$p_header['external'] = 0x41FF0010;
+ $p_header['external'] = 0x00000010;
+ }
+
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privCheckFileHeaders()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // 1 on success,
+ // 0 on error;
+ // --------------------------------------------------------------------------------
+ function privCheckFileHeaders(&$p_local_header, &$p_central_header)
+ {
+ $v_result=1;
+
+ // ----- Check the static values
+ // TBC
+ if ($p_local_header['filename'] != $p_central_header['filename']) {
+ }
+ if ($p_local_header['version_extracted'] != $p_central_header['version_extracted']) {
+ }
+ if ($p_local_header['flag'] != $p_central_header['flag']) {
+ }
+ if ($p_local_header['compression'] != $p_central_header['compression']) {
+ }
+ if ($p_local_header['mtime'] != $p_central_header['mtime']) {
+ }
+ if ($p_local_header['filename_len'] != $p_central_header['filename_len']) {
+ }
+
+ // ----- Look for flag bit 3
+ if (($p_local_header['flag'] & 8) == 8) {
+ $p_local_header['size'] = $p_central_header['size'];
+ $p_local_header['compressed_size'] = $p_central_header['compressed_size'];
+ $p_local_header['crc'] = $p_central_header['crc'];
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privReadEndCentralDir()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privReadEndCentralDir(&$p_central_dir)
+ {
+ $v_result=1;
+
+ // ----- Go to the end of the zip file
+ $v_size = filesize($this->zipname);
+ @fseek($this->zip_fd, $v_size);
+ if (@ftell($this->zip_fd) != $v_size)
+ {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to go to the end of the archive \''.$this->zipname.'\'');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- First try : look if this is an archive with no commentaries (most of the time)
+ // in this case the end of central dir is at 22 bytes of the file end
+ $v_found = 0;
+ if ($v_size > 26) {
+ @fseek($this->zip_fd, $v_size-22);
+ if (($v_pos = @ftell($this->zip_fd)) != ($v_size-22))
+ {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\'');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Read for bytes
+ $v_binary_data = @fread($this->zip_fd, 4);
+ $v_data = @unpack('Vid', $v_binary_data);
+
+ // ----- Check signature
+ if ($v_data['id'] == 0x06054b50) {
+ $v_found = 1;
+ }
+
+ $v_pos = ftell($this->zip_fd);
+ }
+
+ // ----- Go back to the maximum possible size of the Central Dir End Record
+ if (!$v_found) {
+ $v_maximum_size = 65557; // 0xFFFF + 22;
+ if ($v_maximum_size > $v_size)
+ $v_maximum_size = $v_size;
+ @fseek($this->zip_fd, $v_size-$v_maximum_size);
+ if (@ftell($this->zip_fd) != ($v_size-$v_maximum_size))
+ {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\'');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Read byte per byte in order to find the signature
+ $v_pos = ftell($this->zip_fd);
+ $v_bytes = 0x00000000;
+ while ($v_pos < $v_size)
+ {
+ // ----- Read a byte
+ $v_byte = @fread($this->zip_fd, 1);
+
+ // ----- Add the byte
+ //$v_bytes = ($v_bytes << 8) | Ord($v_byte);
+ // Note we mask the old value down such that once shifted we can never end up with more than a 32bit number
+ // Otherwise on systems where we have 64bit integers the check below for the magic number will fail.
+ $v_bytes = ( ($v_bytes & 0xFFFFFF) << 8) | Ord($v_byte);
+
+ // ----- Compare the bytes
+ if ($v_bytes == 0x504b0506)
+ {
+ $v_pos++;
+ break;
+ }
+
+ $v_pos++;
+ }
+
+ // ----- Look if not found end of central dir
+ if ($v_pos == $v_size)
+ {
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Unable to find End of Central Dir Record signature");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+ }
+
+ // ----- Read the first 18 bytes of the header
+ $v_binary_data = fread($this->zip_fd, 18);
+
+ // ----- Look for invalid block size
+ if (strlen($v_binary_data) != 18)
+ {
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid End of Central Dir Record size : ".strlen($v_binary_data));
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Extract the values
+ $v_data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', $v_binary_data);
+
+ // ----- Check the global size
+ if (($v_pos + $v_data['comment_size'] + 18) != $v_size) {
+
+ // ----- Removed in release 2.2 see readme file
+ // The check of the file size is a little too strict.
+ // Some bugs where found when a zip is encrypted/decrypted with 'crypt'.
+ // While decrypted, zip has training 0 bytes
+ if (0) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT,
+ 'The central dir is not at the end of the archive.'
+ .' Some trailing bytes exists after the archive.');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+ }
+
+ // ----- Get comment
+ if ($v_data['comment_size'] != 0) {
+ $p_central_dir['comment'] = fread($this->zip_fd, $v_data['comment_size']);
+ }
+ else
+ $p_central_dir['comment'] = '';
+
+ $p_central_dir['entries'] = $v_data['entries'];
+ $p_central_dir['disk_entries'] = $v_data['disk_entries'];
+ $p_central_dir['offset'] = $v_data['offset'];
+ $p_central_dir['size'] = $v_data['size'];
+ $p_central_dir['disk'] = $v_data['disk'];
+ $p_central_dir['disk_start'] = $v_data['disk_start'];
+
+ // TBC
+ //for(reset($p_central_dir); $key = key($p_central_dir); next($p_central_dir)) {
+ //}
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privDeleteByRule()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privDeleteByRule(&$p_result_list, &$p_options)
+ {
+ $v_result=1;
+ $v_list_detail = array();
+
+ // ----- Open the zip file
+ if (($v_result=$this->privOpenFd('rb')) != 1)
+ {
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Read the central directory informations
+ $v_central_dir = array();
+ if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
+ {
+ $this->privCloseFd();
+ return $v_result;
+ }
+
+ // ----- Go to beginning of File
+ @rewind($this->zip_fd);
+
+ // ----- Scan all the files
+ // ----- Start at beginning of Central Dir
+ $v_pos_entry = $v_central_dir['offset'];
+ @rewind($this->zip_fd);
+ if (@fseek($this->zip_fd, $v_pos_entry))
+ {
+ // ----- Close the zip file
+ $this->privCloseFd();
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Read each entry
+ $v_header_list = array();
+ $j_start = 0;
+ for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++)
+ {
+
+ // ----- Read the file header
+ $v_header_list[$v_nb_extracted] = array();
+ if (($v_result = $this->privReadCentralFileHeader($v_header_list[$v_nb_extracted])) != 1)
+ {
+ // ----- Close the zip file
+ $this->privCloseFd();
+
+ return $v_result;
+ }
+
+
+ // ----- Store the index
+ $v_header_list[$v_nb_extracted]['index'] = $i;
+
+ // ----- Look for the specific extract rules
+ $v_found = false;
+
+ // ----- Look for extract by name rule
+ if ( (isset($p_options[PCLZIP_OPT_BY_NAME]))
+ && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) {
+
+ // ----- Look if the filename is in the list
+ for ($j=0; ($j strlen($p_options[PCLZIP_OPT_BY_NAME][$j]))
+ && (substr($v_header_list[$v_nb_extracted]['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) {
+ $v_found = true;
+ }
+ elseif ( (($v_header_list[$v_nb_extracted]['external']&0x00000010)==0x00000010) /* Indicates a folder */
+ && ($v_header_list[$v_nb_extracted]['stored_filename'].'/' == $p_options[PCLZIP_OPT_BY_NAME][$j])) {
+ $v_found = true;
+ }
+ }
+ // ----- Look for a filename
+ elseif ($v_header_list[$v_nb_extracted]['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) {
+ $v_found = true;
+ }
+ }
+ }
+
+ // ----- Look for extract by ereg rule
+ // ereg() is deprecated with PHP 5.3
+ /*
+ else if ( (isset($p_options[PCLZIP_OPT_BY_EREG]))
+ && ($p_options[PCLZIP_OPT_BY_EREG] != "")) {
+
+ if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header_list[$v_nb_extracted]['stored_filename'])) {
+ $v_found = true;
+ }
+ }
+ */
+
+ // ----- Look for extract by preg rule
+ else if ( (isset($p_options[PCLZIP_OPT_BY_PREG]))
+ && ($p_options[PCLZIP_OPT_BY_PREG] != "")) {
+
+ if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header_list[$v_nb_extracted]['stored_filename'])) {
+ $v_found = true;
+ }
+ }
+
+ // ----- Look for extract by index rule
+ else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX]))
+ && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) {
+
+ // ----- Look if the index is in the list
+ for ($j=$j_start; ($j=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) {
+ $v_found = true;
+ }
+ if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) {
+ $j_start = $j+1;
+ }
+
+ if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) {
+ break;
+ }
+ }
+ }
+ else {
+ $v_found = true;
+ }
+
+ // ----- Look for deletion
+ if ($v_found)
+ {
+ unset($v_header_list[$v_nb_extracted]);
+ }
+ else
+ {
+ $v_nb_extracted++;
+ }
+ }
+
+ // ----- Look if something need to be deleted
+ if ($v_nb_extracted > 0) {
+
+ // ----- Creates a temporay file
+ $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp';
+
+ // ----- Creates a temporary zip archive
+ $v_temp_zip = new PclZip($v_zip_temp_name);
+
+ // ----- Open the temporary zip file in write mode
+ if (($v_result = $v_temp_zip->privOpenFd('wb')) != 1) {
+ $this->privCloseFd();
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Look which file need to be kept
+ for ($i=0; $izip_fd);
+ if (@fseek($this->zip_fd, $v_header_list[$i]['offset'])) {
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $v_temp_zip->privCloseFd();
+ @unlink($v_zip_temp_name);
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Read the file header
+ $v_local_header = array();
+ if (($v_result = $this->privReadFileHeader($v_local_header)) != 1) {
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $v_temp_zip->privCloseFd();
+ @unlink($v_zip_temp_name);
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Check that local file header is same as central file header
+ if ($this->privCheckFileHeaders($v_local_header,
+ $v_header_list[$i]) != 1) {
+ // TBC
+ }
+ unset($v_local_header);
+
+ // ----- Write the file header
+ if (($v_result = $v_temp_zip->privWriteFileHeader($v_header_list[$i])) != 1) {
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $v_temp_zip->privCloseFd();
+ @unlink($v_zip_temp_name);
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Read/write the data block
+ if (($v_result = PclZipUtilCopyBlock($this->zip_fd, $v_temp_zip->zip_fd, $v_header_list[$i]['compressed_size'])) != 1) {
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $v_temp_zip->privCloseFd();
+ @unlink($v_zip_temp_name);
+
+ // ----- Return
+ return $v_result;
+ }
+ }
+
+ // ----- Store the offset of the central dir
+ $v_offset = @ftell($v_temp_zip->zip_fd);
+
+ // ----- Re-Create the Central Dir files header
+ for ($i=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) {
+ $v_temp_zip->privCloseFd();
+ $this->privCloseFd();
+ @unlink($v_zip_temp_name);
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Transform the header to a 'usable' info
+ $v_temp_zip->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]);
+ }
+
+
+ // ----- Zip file comment
+ $v_comment = '';
+ if (isset($p_options[PCLZIP_OPT_COMMENT])) {
+ $v_comment = $p_options[PCLZIP_OPT_COMMENT];
+ }
+
+ // ----- Calculate the size of the central header
+ $v_size = @ftell($v_temp_zip->zip_fd)-$v_offset;
+
+ // ----- Create the central dir footer
+ if (($v_result = $v_temp_zip->privWriteCentralHeader(sizeof($v_header_list), $v_size, $v_offset, $v_comment)) != 1) {
+ // ----- Reset the file list
+ unset($v_header_list);
+ $v_temp_zip->privCloseFd();
+ $this->privCloseFd();
+ @unlink($v_zip_temp_name);
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Close
+ $v_temp_zip->privCloseFd();
+ $this->privCloseFd();
+
+ // ----- Delete the zip file
+ // TBC : I should test the result ...
+ @unlink($this->zipname);
+
+ // ----- Rename the temporary file
+ // TBC : I should test the result ...
+ //@rename($v_zip_temp_name, $this->zipname);
+ PclZipUtilRename($v_zip_temp_name, $this->zipname);
+
+ // ----- Destroy the temporary archive
+ unset($v_temp_zip);
+ }
+
+ // ----- Remove every files : reset the file
+ else if ($v_central_dir['entries'] != 0) {
+ $this->privCloseFd();
+
+ if (($v_result = $this->privOpenFd('wb')) != 1) {
+ return $v_result;
+ }
+
+ if (($v_result = $this->privWriteCentralHeader(0, 0, 0, '')) != 1) {
+ return $v_result;
+ }
+
+ $this->privCloseFd();
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privDirCheck()
+ // Description :
+ // Check if a directory exists, if not it creates it and all the parents directory
+ // which may be useful.
+ // Parameters :
+ // $p_dir : Directory path to check.
+ // Return Values :
+ // 1 : OK
+ // -1 : Unable to create directory
+ // --------------------------------------------------------------------------------
+ function privDirCheck($p_dir, $p_is_dir=false)
+ {
+ $v_result = 1;
+
+
+ // ----- Remove the final '/'
+ if (($p_is_dir) && (substr($p_dir, -1)=='/'))
+ {
+ $p_dir = substr($p_dir, 0, strlen($p_dir)-1);
+ }
+
+ // ----- Check the directory availability
+ if ((is_dir($p_dir)) || ($p_dir == ""))
+ {
+ return 1;
+ }
+
+ // ----- Extract parent directory
+ $p_parent_dir = dirname($p_dir);
+
+ // ----- Just a check
+ if ($p_parent_dir != $p_dir)
+ {
+ // ----- Look for parent directory
+ if ($p_parent_dir != "")
+ {
+ if (($v_result = $this->privDirCheck($p_parent_dir)) != 1)
+ {
+ return $v_result;
+ }
+ }
+ }
+
+ // ----- Create the directory
+ if (!@mkdir($p_dir, 0777))
+ {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_DIR_CREATE_FAIL, "Unable to create directory '$p_dir'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privMerge()
+ // Description :
+ // If $p_archive_to_add does not exist, the function exit with a success result.
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privMerge(&$p_archive_to_add)
+ {
+ $v_result=1;
+
+ // ----- Look if the archive_to_add exists
+ if (!is_file($p_archive_to_add->zipname))
+ {
+
+ // ----- Nothing to merge, so merge is a success
+ $v_result = 1;
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Look if the archive exists
+ if (!is_file($this->zipname))
+ {
+
+ // ----- Do a duplicate
+ $v_result = $this->privDuplicate($p_archive_to_add->zipname);
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Open the zip file
+ if (($v_result=$this->privOpenFd('rb')) != 1)
+ {
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Read the central directory informations
+ $v_central_dir = array();
+ if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
+ {
+ $this->privCloseFd();
+ return $v_result;
+ }
+
+ // ----- Go to beginning of File
+ @rewind($this->zip_fd);
+
+ // ----- Open the archive_to_add file
+ if (($v_result=$p_archive_to_add->privOpenFd('rb')) != 1)
+ {
+ $this->privCloseFd();
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Read the central directory informations
+ $v_central_dir_to_add = array();
+ if (($v_result = $p_archive_to_add->privReadEndCentralDir($v_central_dir_to_add)) != 1)
+ {
+ $this->privCloseFd();
+ $p_archive_to_add->privCloseFd();
+
+ return $v_result;
+ }
+
+ // ----- Go to beginning of File
+ @rewind($p_archive_to_add->zip_fd);
+
+ // ----- Creates a temporay file
+ $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp';
+
+ // ----- Open the temporary file in write mode
+ if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0)
+ {
+ $this->privCloseFd();
+ $p_archive_to_add->privCloseFd();
+
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Copy the files from the archive to the temporary file
+ // TBC : Here I should better append the file and go back to erase the central dir
+ $v_size = $v_central_dir['offset'];
+ while ($v_size != 0)
+ {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = fread($this->zip_fd, $v_read_size);
+ @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Copy the files from the archive_to_add into the temporary file
+ $v_size = $v_central_dir_to_add['offset'];
+ while ($v_size != 0)
+ {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = fread($p_archive_to_add->zip_fd, $v_read_size);
+ @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Store the offset of the central dir
+ $v_offset = @ftell($v_zip_temp_fd);
+
+ // ----- Copy the block of file headers from the old archive
+ $v_size = $v_central_dir['size'];
+ while ($v_size != 0)
+ {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @fread($this->zip_fd, $v_read_size);
+ @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Copy the block of file headers from the archive_to_add
+ $v_size = $v_central_dir_to_add['size'];
+ while ($v_size != 0)
+ {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @fread($p_archive_to_add->zip_fd, $v_read_size);
+ @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Merge the file comments
+ $v_comment = $v_central_dir['comment'].' '.$v_central_dir_to_add['comment'];
+
+ // ----- Calculate the size of the (new) central header
+ $v_size = @ftell($v_zip_temp_fd)-$v_offset;
+
+ // ----- Swap the file descriptor
+ // Here is a trick : I swap the temporary fd with the zip fd, in order to use
+ // the following methods on the temporary fil and not the real archive fd
+ $v_swap = $this->zip_fd;
+ $this->zip_fd = $v_zip_temp_fd;
+ $v_zip_temp_fd = $v_swap;
+
+ // ----- Create the central dir footer
+ if (($v_result = $this->privWriteCentralHeader($v_central_dir['entries']+$v_central_dir_to_add['entries'], $v_size, $v_offset, $v_comment)) != 1)
+ {
+ $this->privCloseFd();
+ $p_archive_to_add->privCloseFd();
+ @fclose($v_zip_temp_fd);
+ $this->zip_fd = null;
+
+ // ----- Reset the file list
+ unset($v_header_list);
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Swap back the file descriptor
+ $v_swap = $this->zip_fd;
+ $this->zip_fd = $v_zip_temp_fd;
+ $v_zip_temp_fd = $v_swap;
+
+ // ----- Close
+ $this->privCloseFd();
+ $p_archive_to_add->privCloseFd();
+
+ // ----- Close the temporary file
+ @fclose($v_zip_temp_fd);
+
+ // ----- Delete the zip file
+ // TBC : I should test the result ...
+ @unlink($this->zipname);
+
+ // ----- Rename the temporary file
+ // TBC : I should test the result ...
+ //@rename($v_zip_temp_name, $this->zipname);
+ PclZipUtilRename($v_zip_temp_name, $this->zipname);
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privDuplicate()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privDuplicate($p_archive_filename)
+ {
+ $v_result=1;
+
+ // ----- Look if the $p_archive_filename exists
+ if (!is_file($p_archive_filename))
+ {
+
+ // ----- Nothing to duplicate, so duplicate is a success.
+ $v_result = 1;
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Open the zip file
+ if (($v_result=$this->privOpenFd('wb')) != 1)
+ {
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Open the temporary file in write mode
+ if (($v_zip_temp_fd = @fopen($p_archive_filename, 'rb')) == 0)
+ {
+ $this->privCloseFd();
+
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive file \''.$p_archive_filename.'\' in binary write mode');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Copy the files from the archive to the temporary file
+ // TBC : Here I should better append the file and go back to erase the central dir
+ $v_size = filesize($p_archive_filename);
+ while ($v_size != 0)
+ {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = fread($v_zip_temp_fd, $v_read_size);
+ @fwrite($this->zip_fd, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Close
+ $this->privCloseFd();
+
+ // ----- Close the temporary file
+ @fclose($v_zip_temp_fd);
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privErrorLog()
+ // Description :
+ // Parameters :
+ // --------------------------------------------------------------------------------
+ function privErrorLog($p_error_code=0, $p_error_string='')
+ {
+ if (PCLZIP_ERROR_EXTERNAL == 1) {
+ PclError($p_error_code, $p_error_string);
+ }
+ else {
+ $this->error_code = $p_error_code;
+ $this->error_string = $p_error_string;
+ }
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privErrorReset()
+ // Description :
+ // Parameters :
+ // --------------------------------------------------------------------------------
+ function privErrorReset()
+ {
+ if (PCLZIP_ERROR_EXTERNAL == 1) {
+ PclErrorReset();
+ }
+ else {
+ $this->error_code = 0;
+ $this->error_string = '';
+ }
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privDisableMagicQuotes()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privDisableMagicQuotes()
+ {
+ $v_result=1;
+
+ // ----- Look if function exists
+ if ( (!function_exists("get_magic_quotes_runtime"))
+ || (!function_exists("set_magic_quotes_runtime"))) {
+ return $v_result;
+ }
+
+ // ----- Look if already done
+ if ($this->magic_quotes_status != -1) {
+ return $v_result;
+ }
+
+ // ----- Get and memorize the magic_quote value
+ $this->magic_quotes_status = @get_magic_quotes_runtime();
+
+ // ----- Disable magic_quotes
+ if ($this->magic_quotes_status == 1) {
+ @set_magic_quotes_runtime(0);
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privSwapBackMagicQuotes()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privSwapBackMagicQuotes()
+ {
+ $v_result=1;
+
+ // ----- Look if function exists
+ if ( (!function_exists("get_magic_quotes_runtime"))
+ || (!function_exists("set_magic_quotes_runtime"))) {
+ return $v_result;
+ }
+
+ // ----- Look if something to do
+ if ($this->magic_quotes_status != -1) {
+ return $v_result;
+ }
+
+ // ----- Swap back magic_quotes
+ if ($this->magic_quotes_status == 1) {
+ @set_magic_quotes_runtime($this->magic_quotes_status);
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ }
+ // End of class
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : PclZipUtilPathReduction()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function PclZipUtilPathReduction($p_dir)
+ {
+ $v_result = "";
+
+ // ----- Look for not empty path
+ if ($p_dir != "") {
+ // ----- Explode path by directory names
+ $v_list = explode("/", $p_dir);
+
+ // ----- Study directories from last to first
+ $v_skip = 0;
+ for ($i=sizeof($v_list)-1; $i>=0; $i--) {
+ // ----- Look for current path
+ if ($v_list[$i] == ".") {
+ // ----- Ignore this directory
+ // Should be the first $i=0, but no check is done
+ }
+ else if ($v_list[$i] == "..") {
+ $v_skip++;
+ }
+ else if ($v_list[$i] == "") {
+ // ----- First '/' i.e. root slash
+ if ($i == 0) {
+ $v_result = "/".$v_result;
+ if ($v_skip > 0) {
+ // ----- It is an invalid path, so the path is not modified
+ // TBC
+ $v_result = $p_dir;
+ $v_skip = 0;
+ }
+ }
+ // ----- Last '/' i.e. indicates a directory
+ else if ($i == (sizeof($v_list)-1)) {
+ $v_result = $v_list[$i];
+ }
+ // ----- Double '/' inside the path
+ else {
+ // ----- Ignore only the double '//' in path,
+ // but not the first and last '/'
+ }
+ }
+ else {
+ // ----- Look for item to skip
+ if ($v_skip > 0) {
+ $v_skip--;
+ }
+ else {
+ $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?"/".$v_result:"");
+ }
+ }
+ }
+
+ // ----- Look for skip
+ if ($v_skip > 0) {
+ while ($v_skip > 0) {
+ $v_result = '../'.$v_result;
+ $v_skip--;
+ }
+ }
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : PclZipUtilPathInclusion()
+ // Description :
+ // This function indicates if the path $p_path is under the $p_dir tree. Or,
+ // said in an other way, if the file or sub-dir $p_path is inside the dir
+ // $p_dir.
+ // The function indicates also if the path is exactly the same as the dir.
+ // This function supports path with duplicated '/' like '//', but does not
+ // support '.' or '..' statements.
+ // Parameters :
+ // Return Values :
+ // 0 if $p_path is not inside directory $p_dir
+ // 1 if $p_path is inside directory $p_dir
+ // 2 if $p_path is exactly the same as $p_dir
+ // --------------------------------------------------------------------------------
+ function PclZipUtilPathInclusion($p_dir, $p_path)
+ {
+ $v_result = 1;
+
+ // ----- Look for path beginning by ./
+ if ( ($p_dir == '.')
+ || ((strlen($p_dir) >=2) && (substr($p_dir, 0, 2) == './'))) {
+ $p_dir = PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_dir, 1);
+ }
+ if ( ($p_path == '.')
+ || ((strlen($p_path) >=2) && (substr($p_path, 0, 2) == './'))) {
+ $p_path = PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_path, 1);
+ }
+
+ // ----- Explode dir and path by directory separator
+ $v_list_dir = explode("/", $p_dir);
+ $v_list_dir_size = sizeof($v_list_dir);
+ $v_list_path = explode("/", $p_path);
+ $v_list_path_size = sizeof($v_list_path);
+
+ // ----- Study directories paths
+ $i = 0;
+ $j = 0;
+ while (($i < $v_list_dir_size) && ($j < $v_list_path_size) && ($v_result)) {
+
+ // ----- Look for empty dir (path reduction)
+ if ($v_list_dir[$i] == '') {
+ $i++;
+ continue;
+ }
+ if ($v_list_path[$j] == '') {
+ $j++;
+ continue;
+ }
+
+ // ----- Compare the items
+ if (($v_list_dir[$i] != $v_list_path[$j]) && ($v_list_dir[$i] != '') && ( $v_list_path[$j] != '')) {
+ $v_result = 0;
+ }
+
+ // ----- Next items
+ $i++;
+ $j++;
+ }
+
+ // ----- Look if everything seems to be the same
+ if ($v_result) {
+ // ----- Skip all the empty items
+ while (($j < $v_list_path_size) && ($v_list_path[$j] == '')) $j++;
+ while (($i < $v_list_dir_size) && ($v_list_dir[$i] == '')) $i++;
+
+ if (($i >= $v_list_dir_size) && ($j >= $v_list_path_size)) {
+ // ----- There are exactly the same
+ $v_result = 2;
+ }
+ else if ($i < $v_list_dir_size) {
+ // ----- The path is shorter than the dir
+ $v_result = 0;
+ }
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : PclZipUtilCopyBlock()
+ // Description :
+ // Parameters :
+ // $p_mode : read/write compression mode
+ // 0 : src & dest normal
+ // 1 : src gzip, dest normal
+ // 2 : src normal, dest gzip
+ // 3 : src & dest gzip
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function PclZipUtilCopyBlock($p_src, $p_dest, $p_size, $p_mode=0)
+ {
+ $v_result = 1;
+
+ if ($p_mode==0)
+ {
+ while ($p_size != 0)
+ {
+ $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @fread($p_src, $v_read_size);
+ @fwrite($p_dest, $v_buffer, $v_read_size);
+ $p_size -= $v_read_size;
+ }
+ }
+ else if ($p_mode==1)
+ {
+ while ($p_size != 0)
+ {
+ $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @gzread($p_src, $v_read_size);
+ @fwrite($p_dest, $v_buffer, $v_read_size);
+ $p_size -= $v_read_size;
+ }
+ }
+ else if ($p_mode==2)
+ {
+ while ($p_size != 0)
+ {
+ $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @fread($p_src, $v_read_size);
+ @gzwrite($p_dest, $v_buffer, $v_read_size);
+ $p_size -= $v_read_size;
+ }
+ }
+ else if ($p_mode==3)
+ {
+ while ($p_size != 0)
+ {
+ $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @gzread($p_src, $v_read_size);
+ @gzwrite($p_dest, $v_buffer, $v_read_size);
+ $p_size -= $v_read_size;
+ }
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : PclZipUtilRename()
+ // Description :
+ // This function tries to do a simple rename() function. If it fails, it
+ // tries to copy the $p_src file in a new $p_dest file and then unlink the
+ // first one.
+ // Parameters :
+ // $p_src : Old filename
+ // $p_dest : New filename
+ // Return Values :
+ // 1 on success, 0 on failure.
+ // --------------------------------------------------------------------------------
+ function PclZipUtilRename($p_src, $p_dest)
+ {
+ $v_result = 1;
+
+ // ----- Try to rename the files
+ if (!@rename($p_src, $p_dest)) {
+
+ // ----- Try to copy & unlink the src
+ if (!@copy($p_src, $p_dest)) {
+ $v_result = 0;
+ }
+ else if (!@unlink($p_src)) {
+ $v_result = 0;
+ }
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : PclZipUtilOptionText()
+ // Description :
+ // Translate option value in text. Mainly for debug purpose.
+ // Parameters :
+ // $p_option : the option value.
+ // Return Values :
+ // The option text value.
+ // --------------------------------------------------------------------------------
+ function PclZipUtilOptionText($p_option)
+ {
+
+ $v_list = get_defined_constants();
+ for (reset($v_list); $v_key = key($v_list); next($v_list)) {
+ $v_prefix = substr($v_key, 0, 10);
+ if (( ($v_prefix == 'PCLZIP_OPT')
+ || ($v_prefix == 'PCLZIP_CB_')
+ || ($v_prefix == 'PCLZIP_ATT'))
+ && ($v_list[$v_key] == $p_option)) {
+ return $v_key;
+ }
+ }
+
+ $v_result = 'Unknown';
+
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : PclZipUtilTranslateWinPath()
+ // Description :
+ // Translate windows path by replacing '\' by '/' and optionally removing
+ // drive letter.
+ // Parameters :
+ // $p_path : path to translate.
+ // $p_remove_disk_letter : true | false
+ // Return Values :
+ // The path translated.
+ // --------------------------------------------------------------------------------
+ function PclZipUtilTranslateWinPath($p_path, $p_remove_disk_letter=true)
+ {
+ if (stristr(php_uname(), 'windows')) {
+ // ----- Look for potential disk letter
+ if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false)) {
+ $p_path = substr($p_path, $v_position+1);
+ }
+ // ----- Change potential windows directory separator
+ if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
+ $p_path = strtr($p_path, '\\', '/');
+ }
+ }
+ return $p_path;
+ }
+ // --------------------------------------------------------------------------------
+
+
+?>
diff --git a/sources/admin/include/photos_add_direct_prepare.inc.php b/sources/admin/include/photos_add_direct_prepare.inc.php
new file mode 100644
index 0000000..117abb8
--- /dev/null
+++ b/sources/admin/include/photos_add_direct_prepare.inc.php
@@ -0,0 +1,296 @@
+assign(
+ array(
+ 'thumbnails' => $page['thumbnails'],
+ )
+ );
+
+ // only display the batch link if we have more than 1 photo
+ if (count($page['thumbnails']) > 1)
+ {
+ $template->assign(
+ array(
+ 'batch_link' => $page['batch_link'],
+ 'batch_label' => sprintf(
+ l10n('Manage this set of %d photos'),
+ count($page['thumbnails'])
+ ),
+ )
+ );
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | Photo selection |
+// +-----------------------------------------------------------------------+
+
+$uploadify_path = PHPWG_ROOT_PATH.'admin/include/uploadify';
+
+$upload_max_filesize = min(
+ get_ini_size('upload_max_filesize'),
+ get_ini_size('post_max_size')
+ );
+
+if ($upload_max_filesize == get_ini_size('upload_max_filesize'))
+{
+ $upload_max_filesize_shorthand = get_ini_size('upload_max_filesize', false);
+}
+else
+{
+ $upload_max_filesize_shorthand = get_ini_size('post_max_filesize', false);
+}
+
+$template->assign(
+ array(
+ 'F_ADD_ACTION'=> PHOTOS_ADD_BASE_URL,
+ 'uploadify_path' => $uploadify_path,
+ 'upload_max_filesize' => $upload_max_filesize,
+ 'upload_max_filesize_shorthand' => $upload_max_filesize_shorthand,
+ )
+ );
+
+// what is the maximum number of pixels permitted by the memory_limit?
+if (pwg_image::get_library() == 'gd')
+{
+ $fudge_factor = 1.7;
+ $available_memory = get_ini_size('memory_limit') - memory_get_usage();
+ $max_upload_width = round(sqrt($available_memory/(2 * $fudge_factor)));
+ $max_upload_height = round(2 * $max_upload_width / 3);
+
+ // we don't want dimensions like 2995x1992 but 3000x2000
+ $max_upload_width = round($max_upload_width/100)*100;
+ $max_upload_height = round($max_upload_height/100)*100;
+
+ $max_upload_resolution = floor($max_upload_width * $max_upload_height / (1000000));
+
+ // no need to display a limitation warning if the limitation is huge like 20MP
+ if ($max_upload_resolution < 25)
+ {
+ $template->assign(
+ array(
+ 'max_upload_width' => $max_upload_width,
+ 'max_upload_height' => $max_upload_height,
+ 'max_upload_resolution' => $max_upload_resolution,
+ )
+ );
+ }
+}
+
+//warn the user if the picture will be resized after upload
+if ($conf['original_resize'])
+{
+ $template->assign(
+ array(
+ 'original_resize_maxwidth' => $conf['original_resize_maxwidth'],
+ 'original_resize_maxheight' => $conf['original_resize_maxheight'],
+ )
+ );
+}
+
+
+$upload_modes = array('html', 'multiple');
+$upload_mode = isset($conf['upload_mode']) ? $conf['upload_mode'] : 'multiple';
+
+if (isset($_GET['upload_mode']) and $upload_mode != $_GET['upload_mode'] and in_array($_GET['upload_mode'], $upload_modes))
+{
+ $upload_mode = $_GET['upload_mode'];
+ conf_update_param('upload_mode', $upload_mode);
+}
+
+// what is the upload switch mode
+$index_of_upload_mode = array_flip($upload_modes);
+$upload_mode_index = $index_of_upload_mode[$upload_mode];
+$upload_switch = $upload_modes[ ($upload_mode_index + 1) % 2 ];
+
+$template->assign(
+ array(
+ 'upload_mode' => $upload_mode,
+ 'form_action' => PHOTOS_ADD_BASE_URL.'&upload_mode='.$upload_mode.'&processed=1',
+ 'switch_url' => PHOTOS_ADD_BASE_URL.'&upload_mode='.$upload_switch,
+ 'upload_id' => md5(rand()),
+ 'session_id' => session_id(),
+ 'pwg_token' => get_pwg_token(),
+ 'another_upload_link' => PHOTOS_ADD_BASE_URL.'&upload_mode='.$upload_mode,
+ )
+ );
+
+$upload_file_types = 'jpeg, png, gif';
+
+if (pwg_image::get_library() == 'ext_imagick')
+{
+ $upload_file_types.= ', tiff';
+ $template->assign('tif_enabled', true);
+}
+
+if ('html' == $upload_mode)
+{
+ $upload_file_types.= ', zip';
+}
+$template->assign(
+ array(
+ 'upload_file_types' => $upload_file_types,
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | Categories |
+// +-----------------------------------------------------------------------+
+
+// we need to know the category in which the last photo was added
+$selected_category = array();
+
+if (isset($_GET['album']))
+{
+ // set the category from get url or ...
+ check_input_parameter('album', $_GET, false, PATTERN_ID);
+
+ // test if album really exists
+ $query = '
+SELECT id
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id = '.$_GET['album'].'
+;';
+ $result = pwg_query($query);
+ if (pwg_db_num_rows($result) == 1)
+ {
+ $selected_category = array($_GET['album']);
+
+ // lets put in the session to persist in case of upload method switch
+ $_SESSION['selected_category'] = $selected_category;
+ }
+ else
+ {
+ fatal_error('[Hacking attempt] the album id = "'.$_GET['album'].'" is not valid');
+ }
+}
+else if (isset($_SESSION['selected_category']))
+{
+ $selected_category = $_SESSION['selected_category'];
+}
+else
+{
+ // we need to know the category in which the last photo was added
+ $query = '
+SELECT category_id
+ FROM '.IMAGES_TABLE.' AS i
+ JOIN '.IMAGE_CATEGORY_TABLE.' AS ic ON image_id = i.id
+ JOIN '.CATEGORIES_TABLE.' AS c ON category_id = c.id
+ ORDER BY i.id DESC
+ LIMIT 1
+;
+';
+ $result = pwg_query($query);
+ if (pwg_db_num_rows($result) > 0)
+ {
+ $row = pwg_db_fetch_assoc($result);
+ $selected_category = array($row['category_id']);
+ }
+}
+
+// existing album
+$query = '
+SELECT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.'
+;';
+
+display_select_cat_wrapper(
+ $query,
+ $selected_category,
+ 'category_options'
+ );
+
+
+// image level options
+$selected_level = isset($_POST['level']) ? $_POST['level'] : 0;
+$template->assign(
+ array(
+ 'level_options'=> get_privacy_level_options(),
+ 'level_options_selected' => array($selected_level)
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | Setup errors/warnings |
+// +-----------------------------------------------------------------------+
+
+// Errors
+$setup_errors = array();
+
+$error_message = ready_for_upload_message();
+if (!empty($error_message))
+{
+ $setup_errors[] = $error_message;
+}
+
+if (!function_exists('gd_info'))
+{
+ $setup_errors[] = l10n('GD library is missing');
+}
+
+$template->assign(
+ array(
+ 'setup_errors'=> $setup_errors,
+ )
+ );
+
+// Warnings
+if (isset($_GET['hide_warnings']))
+{
+ $_SESSION['upload_hide_warnings'] = true;
+}
+
+if (!isset($_SESSION['upload_hide_warnings']))
+{
+ $setup_warnings = array();
+
+ if ($conf['use_exif'] and !function_exists('read_exif_data'))
+ {
+ $setup_warnings[] = l10n('Exif extension not available, admin should disable exif use');
+ }
+
+ if (get_ini_size('upload_max_filesize') > get_ini_size('post_max_size'))
+ {
+ $setup_warnings[] = l10n(
+ 'In your php.ini file, the upload_max_filesize (%sB) is bigger than post_max_size (%sB), you should change this setting',
+ get_ini_size('upload_max_filesize', false),
+ get_ini_size('post_max_size', false)
+ );
+ }
+ $template->assign(
+ array(
+ 'setup_warnings' => $setup_warnings,
+ 'hide_warnings_link' => PHOTOS_ADD_BASE_URL.'&upload_mode='.$upload_mode.'&hide_warnings=1'
+ )
+ );
+}
+
+?>
\ No newline at end of file
diff --git a/sources/admin/include/photos_add_direct_process.inc.php b/sources/admin/include/photos_add_direct_process.inc.php
new file mode 100644
index 0000000..e0a6cba
--- /dev/null
+++ b/sources/admin/include/photos_add_direct_process.inc.php
@@ -0,0 +1,242 @@
+POST'."\n"; print_r($_POST); echo '';
+// echo 'FILES'."\n"; print_r($_FILES); echo ' ';
+// echo 'SESSION'."\n"; print_r($_SESSION); echo ' ';
+// exit();
+
+ // sometimes, you have submitted the form but you have nothing in $_POST
+ // and $_FILES. This may happen when you have an HTML upload and you
+ // exceeded the post_max_size (but not the upload_max_size)
+ if (!isset($_POST['submit_upload']))
+ {
+ $page['errors'][] = l10n(
+ 'The uploaded files exceed the post_max_size directive in php.ini: %sB',
+ ini_get('post_max_size')
+ );
+ }
+ else
+ {
+ $category_id = $_POST['category'];
+ }
+
+ if (isset($_POST['onUploadError']) and is_array($_POST['onUploadError']) and count($_POST['onUploadError']) > 0)
+ {
+ foreach ($_POST['onUploadError'] as $error)
+ {
+ $page['errors'][] = $error;
+ }
+ }
+
+ $image_ids = array();
+
+ if (isset($_FILES) and !empty($_FILES['image_upload']))
+ {
+ $starttime = get_moment();
+
+ foreach ($_FILES['image_upload']['error'] as $idx => $error)
+ {
+ if (UPLOAD_ERR_OK == $error)
+ {
+ $images_to_add = array();
+
+ $extension = pathinfo($_FILES['image_upload']['name'][$idx], PATHINFO_EXTENSION);
+ if ('zip' == strtolower($extension))
+ {
+ $upload_dir = $conf['upload_dir'].'/buffer';
+ prepare_directory($upload_dir);
+
+ $temporary_archive_name = date('YmdHis').'-'.generate_key(10);
+ $archive_path = $upload_dir.'/'.$temporary_archive_name.'.zip';
+
+ move_uploaded_file(
+ $_FILES['image_upload']['tmp_name'][$idx],
+ $archive_path
+ );
+
+ define('PCLZIP_TEMPORARY_DIR', $upload_dir.'/');
+ include_once(PHPWG_ROOT_PATH.'admin/include/pclzip.lib.php');
+ $zip = new PclZip($archive_path);
+ if ($list = $zip->listContent())
+ {
+ $indexes_to_extract = array();
+
+ foreach ($list as $node)
+ {
+ if (1 == $node['folder'])
+ {
+ continue;
+ }
+
+ if (is_valid_image_extension(pathinfo($node['filename'], PATHINFO_EXTENSION)))
+ {
+ $indexes_to_extract[] = $node['index'];
+
+ $images_to_add[] = array(
+ 'source_filepath' => $upload_dir.'/'.$temporary_archive_name.'/'.$node['filename'],
+ 'original_filename' => basename($node['filename']),
+ );
+ }
+ }
+
+ if (count($indexes_to_extract) > 0)
+ {
+ $zip->extract(
+ PCLZIP_OPT_PATH, $upload_dir.'/'.$temporary_archive_name,
+ PCLZIP_OPT_BY_INDEX, $indexes_to_extract,
+ PCLZIP_OPT_ADD_TEMP_FILE_ON
+ );
+ }
+ }
+ }
+ elseif (is_valid_image_extension($extension))
+ {
+ $images_to_add[] = array(
+ 'source_filepath' => $_FILES['image_upload']['tmp_name'][$idx],
+ 'original_filename' => $_FILES['image_upload']['name'][$idx],
+ );
+ }
+
+ foreach ($images_to_add as $image_to_add)
+ {
+ $image_id = add_uploaded_file(
+ $image_to_add['source_filepath'],
+ $image_to_add['original_filename'],
+ array($category_id),
+ $_POST['level']
+ );
+
+ $image_ids[] = $image_id;
+
+ // TODO: if $image_id is not an integer, something went wrong
+ }
+ }
+ else
+ {
+ $error_message = file_upload_error_message($error);
+
+ $page['errors'][] = l10n(
+ 'Error on file "%s" : %s',
+ $_FILES['image_upload']['name'][$idx],
+ $error_message
+ );
+ }
+ }
+
+ $endtime = get_moment();
+ $elapsed = ($endtime - $starttime) * 1000;
+ // printf('%.2f ms', $elapsed);
+
+ } // if (!empty($_FILES))
+
+ if (isset($_POST['upload_id']))
+ {
+ // we're on a multiple upload, with uploadify and so on
+ if (isset($_SESSION['uploads_error'][ $_POST['upload_id'] ]))
+ {
+ foreach ($_SESSION['uploads_error'][ $_POST['upload_id'] ] as $error)
+ {
+ $page['errors'][] = $error;
+ }
+ }
+
+ if (isset($_SESSION['uploads'][ $_POST['upload_id'] ]))
+ {
+ $image_ids = $_SESSION['uploads'][ $_POST['upload_id'] ];
+ }
+ }
+
+ $page['thumbnails'] = array();
+ foreach ($image_ids as $image_id)
+ {
+ // we could return the list of properties from the add_uploaded_file
+ // function, but I like the "double check". And it costs nothing
+ // compared to the upload process.
+ $thumbnail = array();
+
+ $query = '
+SELECT
+ id,
+ file,
+ path
+ FROM '.IMAGES_TABLE.'
+ WHERE id = '.$image_id.'
+;';
+ $image_infos = pwg_db_fetch_assoc(pwg_query($query));
+
+ $thumbnail['file'] = $image_infos['file'];
+
+ $thumbnail['src'] = DerivativeImage::thumb_url($image_infos);
+
+ // TODO: when implementing this plugin in Piwigo core, we should have
+ // a function get_image_name($name, $file) (if name is null, then
+ // compute a temporary name from filename) that would be also used in
+ // picture.php. UPDATE: in fact, "get_name_from_file($file)" already
+ // exists and is used twice (batch_manager_unit + comments, but not in
+ // picture.php I don't know why) with the same pattern if
+ // (empty($name)) {$name = get_name_from_file($file)}, a clean
+ // function get_image_name($name, $file) would be better
+ $thumbnail['title'] = get_name_from_file($image_infos['file']);
+
+ $thumbnail['link'] = get_root_url().'admin.php?page=photo-'.$image_id.'&cat_id='.$category_id;
+
+ $page['thumbnails'][] = $thumbnail;
+ }
+
+ if (!empty($page['thumbnails']))
+ {
+ $page['infos'][] = l10n('%d photos uploaded', count($page['thumbnails']));
+
+ if (0 != $_POST['level'])
+ {
+ $page['infos'][] = l10n(
+ 'Privacy level set to "%s"',
+ l10n(sprintf('Level %d', $_POST['level']))
+ );
+ }
+
+ $query = '
+SELECT
+ COUNT(*)
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ WHERE category_id = '.$category_id.'
+;';
+ list($count) = pwg_db_fetch_row(pwg_query($query));
+ $category_name = get_cat_display_name_from_id($category_id, 'admin.php?page=album-');
+
+ // information
+ $page['infos'][] = l10n(
+ 'Album "%s" now contains %d photos',
+ ''.$category_name.' ',
+ $count
+ );
+
+ $page['batch_link'] = PHOTOS_ADD_BASE_URL.'&batch='.implode(',', $image_ids);
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/sources/admin/include/plugins.class.php b/sources/admin/include/plugins.class.php
new file mode 100644
index 0000000..9862bcc
--- /dev/null
+++ b/sources/admin/include/plugins.class.php
@@ -0,0 +1,671 @@
+plugin_id, $plugin_version, $errors);
+ }
+ }
+ function activate($plugin_version, &$errors=array())
+ {
+ if (is_callable('plugin_activate'))
+ {
+ return plugin_activate($this->plugin_id, $plugin_version, $errors);
+ }
+ }
+ function deactivate()
+ {
+ if (is_callable('plugin_deactivate'))
+ {
+ return plugin_deactivate($this->plugin_id);
+ }
+ }
+ function uninstall()
+ {
+ if (is_callable('plugin_uninstall'))
+ {
+ return plugin_uninstall($this->plugin_id);
+ }
+ }
+}
+
+
+class plugins
+{
+ var $fs_plugins = array();
+ var $db_plugins_by_id = array();
+ var $server_plugins = array();
+ var $default_plugins = array('LocalFilesEditor', 'language_switch', 'c13y_upgrade', 'admin_multi_view');
+
+ /**
+ * Initialize $fs_plugins and $db_plugins_by_id
+ */
+ function plugins()
+ {
+ $this->get_fs_plugins();
+
+ foreach (get_db_plugins() as $db_plugin)
+ {
+ $this->db_plugins_by_id[$db_plugin['id']] = $db_plugin;
+ }
+ }
+
+ /**
+ * Returns the maintain class of a plugin
+ * or build a new class with the procedural methods
+ * @param string $plugin_id
+ */
+ private static function build_maintain_class($plugin_id)
+ {
+ $file_to_include = PHPWG_PLUGINS_PATH . $plugin_id . '/maintain.inc.php';
+ $classname = $plugin_id.'_maintain';
+
+ if (file_exists($file_to_include))
+ {
+ include_once($file_to_include);
+
+ if (class_exists($classname))
+ {
+ $plugin_maintain = new $classname($plugin_id);
+ }
+ else
+ {
+ $plugin_maintain = new DummyPlugin_maintain($plugin_id);
+ }
+ }
+ else
+ {
+ $plugin_maintain = new DummyPlugin_maintain($plugin_id);
+ }
+
+ return $plugin_maintain;
+ }
+
+ /**
+ * Perform requested actions
+ * @param string - action
+ * @param string - plugin id
+ * @param array - errors
+ */
+ function perform_action($action, $plugin_id)
+ {
+ if (isset($this->db_plugins_by_id[$plugin_id]))
+ {
+ $crt_db_plugin = $this->db_plugins_by_id[$plugin_id];
+ }
+
+ $plugin_maintain = self::build_maintain_class($plugin_id);
+
+ $errors = array();
+
+ switch ($action)
+ {
+ case 'install':
+ if (!empty($crt_db_plugin) or !isset($this->fs_plugins[$plugin_id]))
+ {
+ break;
+ }
+
+ $plugin_maintain->install($this->fs_plugins[$plugin_id]['version'], $errors);
+
+ if (empty($errors))
+ {
+ $query = '
+INSERT INTO '. PLUGINS_TABLE .' (id,version)
+ VALUES (\''. $plugin_id .'\', \''. $this->fs_plugins[$plugin_id]['version'] .'\')
+;';
+ pwg_query($query);
+ }
+ break;
+
+ case 'activate':
+ if (!isset($crt_db_plugin))
+ {
+ $errors = $this->perform_action('install', $plugin_id);
+ list($crt_db_plugin) = get_db_plugins(null, $plugin_id);
+ load_conf_from_db();
+ }
+ elseif ($crt_db_plugin['state'] == 'active')
+ {
+ break;
+ }
+
+ if (empty($errors))
+ {
+ $plugin_maintain->activate($crt_db_plugin['version'], $errors);
+ }
+
+ if (empty($errors))
+ {
+ $query = '
+UPDATE '. PLUGINS_TABLE .'
+ SET state=\'active\',
+ version=\''. $this->fs_plugins[$plugin_id]['version'] .'\'
+ WHERE id=\''. $plugin_id .'\'
+;';
+ pwg_query($query);
+ }
+ break;
+
+ case 'deactivate':
+ if (!isset($crt_db_plugin) or $crt_db_plugin['state'] != 'active')
+ {
+ break;
+ }
+
+ $query = '
+UPDATE '. PLUGINS_TABLE .'
+ SET state=\'inactive\'
+ WHERE id=\''. $plugin_id .'\'
+;';
+ pwg_query($query);
+
+ $plugin_maintain->deactivate();
+ break;
+
+ case 'uninstall':
+ if (!isset($crt_db_plugin))
+ {
+ break;
+ }
+ if ($crt_db_plugin['state'] == 'active')
+ {
+ $this->perform_action('deactivate', $plugin_id);
+ }
+
+ $query = '
+DELETE FROM '. PLUGINS_TABLE .'
+ WHERE id=\''. $plugin_id .'\'
+;';
+ pwg_query($query);
+
+ $plugin_maintain->uninstall();
+ break;
+
+ case 'restore':
+ $this->perform_action('uninstall', $plugin_id);
+ unset($this->db_plugins_by_id[$plugin_id]);
+ $errors = $this->perform_action('activate', $plugin_id);
+ break;
+
+ case 'delete':
+ if (!empty($crt_db_plugin))
+ {
+ $this->perform_action('uninstall', $plugin_id);
+ }
+ if (!isset($this->fs_plugins[$plugin_id]))
+ {
+ break;
+ }
+
+ deltree(PHPWG_PLUGINS_PATH . $plugin_id, PHPWG_PLUGINS_PATH . 'trash');
+ break;
+ }
+
+ return $errors;
+ }
+
+ /**
+ * Get plugins defined in the plugin directory
+ */
+ function get_fs_plugins()
+ {
+ $dir = opendir(PHPWG_PLUGINS_PATH);
+ while ($file = readdir($dir))
+ {
+ if ($file!='.' and $file!='..')
+ {
+ $path = PHPWG_PLUGINS_PATH.$file;
+ if (is_dir($path) and !is_link($path)
+ and preg_match('/^[a-zA-Z0-9-_]+$/', $file )
+ and file_exists($path.'/main.inc.php')
+ )
+ {
+ $plugin = array(
+ 'name'=>$file,
+ 'version'=>'0',
+ 'uri'=>'',
+ 'description'=>'',
+ 'author'=>'',
+ );
+ $plg_data = implode( '', file($path.'/main.inc.php') );
+
+ if ( preg_match("|Plugin Name: (.*)|", $plg_data, $val) )
+ {
+ $plugin['name'] = trim( $val[1] );
+ }
+ if (preg_match("|Version: (.*)|", $plg_data, $val))
+ {
+ $plugin['version'] = trim($val[1]);
+ }
+ if ( preg_match("|Plugin URI: (.*)|", $plg_data, $val) )
+ {
+ $plugin['uri'] = trim($val[1]);
+ }
+ if ($desc = load_language('description.txt', $path.'/', array('return' => true)))
+ {
+ $plugin['description'] = trim($desc);
+ }
+ elseif ( preg_match("|Description: (.*)|", $plg_data, $val) )
+ {
+ $plugin['description'] = trim($val[1]);
+ }
+ if ( preg_match("|Author: (.*)|", $plg_data, $val) )
+ {
+ $plugin['author'] = trim($val[1]);
+ }
+ if ( preg_match("|Author URI: (.*)|", $plg_data, $val) )
+ {
+ $plugin['author uri'] = trim($val[1]);
+ }
+ if (!empty($plugin['uri']) and strpos($plugin['uri'] , 'extension_view.php?eid='))
+ {
+ list( , $extension) = explode('extension_view.php?eid=', $plugin['uri']);
+ if (is_numeric($extension)) $plugin['extension'] = $extension;
+ }
+ // IMPORTANT SECURITY !
+ $plugin = array_map('htmlspecialchars', $plugin);
+ $this->fs_plugins[$file] = $plugin;
+ }
+ }
+ }
+ closedir($dir);
+ }
+
+ /**
+ * Sort fs_plugins
+ */
+ function sort_fs_plugins($order='name')
+ {
+ switch ($order)
+ {
+ case 'name':
+ uasort($this->fs_plugins, 'name_compare');
+ break;
+ case 'status':
+ $this->sort_plugins_by_state();
+ break;
+ case 'author':
+ uasort($this->fs_plugins, array($this, 'plugin_author_compare'));
+ break;
+ case 'id':
+ uksort($this->fs_plugins, 'strcasecmp');
+ break;
+ }
+ }
+
+ // Retrieve PEM versions
+ function get_versions_to_check($version=PHPWG_VERSION)
+ {
+ global $conf;
+
+ $versions_to_check = array();
+ $url = PEM_URL . '/api/get_version_list.php?category_id='. $conf['pem_plugins_category'] .'&format=php';
+ if (fetchRemote($url, $result) and $pem_versions = @unserialize($result))
+ {
+ if (!preg_match('/^\d+\.\d+\.\d+$/', $version))
+ {
+ $version = $pem_versions[0]['name'];
+ }
+ $branch = get_branch_from_version($version);
+ foreach ($pem_versions as $pem_version)
+ {
+ if (strpos($pem_version['name'], $branch) === 0)
+ {
+ $versions_to_check[] = $pem_version['id'];
+ }
+ }
+ }
+ return $versions_to_check;
+ }
+
+ /**
+ * Retrieve PEM server datas to $server_plugins
+ */
+ function get_server_plugins($new=false)
+ {
+ global $user, $conf;
+
+ $versions_to_check = $this->get_versions_to_check();
+ if (empty($versions_to_check))
+ {
+ return false;
+ }
+
+ // Plugins to check
+ $plugins_to_check = array();
+ foreach($this->fs_plugins as $fs_plugin)
+ {
+ if (isset($fs_plugin['extension']))
+ {
+ $plugins_to_check[] = $fs_plugin['extension'];
+ }
+ }
+
+ // Retrieve PEM plugins infos
+ $url = PEM_URL . '/api/get_revision_list.php';
+ $get_data = array(
+ 'category_id' => $conf['pem_plugins_category'],
+ 'format' => 'php',
+ 'last_revision_only' => 'true',
+ 'version' => implode(',', $versions_to_check),
+ 'lang' => substr($user['language'], 0, 2),
+ 'get_nb_downloads' => 'true',
+ );
+
+ if (!empty($plugins_to_check))
+ {
+ if ($new)
+ {
+ $get_data['extension_exclude'] = implode(',', $plugins_to_check);
+ }
+ else
+ {
+ $get_data['extension_include'] = implode(',', $plugins_to_check);
+ }
+ }
+ if (fetchRemote($url, $result, $get_data))
+ {
+ $pem_plugins = @unserialize($result);
+ if (!is_array($pem_plugins))
+ {
+ return false;
+ }
+ foreach ($pem_plugins as $plugin)
+ {
+ $this->server_plugins[$plugin['extension_id']] = $plugin;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ function get_incompatible_plugins($actualize=false)
+ {
+ if (isset($_SESSION['incompatible_plugins']) and !$actualize
+ and $_SESSION['incompatible_plugins']['~~expire~~'] > time())
+ {
+ return $_SESSION['incompatible_plugins'];
+ }
+
+ $_SESSION['incompatible_plugins'] = array('~~expire~~' => time() + 300);
+
+ $versions_to_check = $this->get_versions_to_check();
+ if (empty($versions_to_check))
+ {
+ return false;
+ }
+
+ global $conf;
+
+ // Plugins to check
+ $plugins_to_check = array();
+ foreach($this->fs_plugins as $fs_plugin)
+ {
+ if (isset($fs_plugin['extension']))
+ {
+ $plugins_to_check[] = $fs_plugin['extension'];
+ }
+ }
+
+ // Retrieve PEM plugins infos
+ $url = PEM_URL . '/api/get_revision_list.php';
+ $get_data = array(
+ 'category_id' => $conf['pem_plugins_category'],
+ 'format' => 'php',
+ 'version' => implode(',', $versions_to_check),
+ 'extension_include' => implode(',', $plugins_to_check),
+ );
+
+ if (fetchRemote($url, $result, $get_data))
+ {
+ $pem_plugins = @unserialize($result);
+ if (!is_array($pem_plugins))
+ {
+ return false;
+ }
+
+ $server_plugins = array();
+ foreach ($pem_plugins as $plugin)
+ {
+ if (!isset($server_plugins[$plugin['extension_id']]))
+ {
+ $server_plugins[$plugin['extension_id']] = array();
+ }
+ $server_plugins[$plugin['extension_id']][] = $plugin['revision_name'];
+ }
+
+ foreach ($this->fs_plugins as $plugin_id => $fs_plugin)
+ {
+ if (isset($fs_plugin['extension'])
+ and !in_array($plugin_id, $this->default_plugins)
+ and $fs_plugin['version'] != 'auto'
+ and (!isset($server_plugins[$fs_plugin['extension']]) or !in_array($fs_plugin['version'], $server_plugins[$fs_plugin['extension']])))
+ {
+ $_SESSION['incompatible_plugins'][$plugin_id] = $fs_plugin['version'];
+ }
+ }
+ return $_SESSION['incompatible_plugins'];
+ }
+ return false;
+ }
+
+ /**
+ * Sort $server_plugins
+ */
+ function sort_server_plugins($order='date')
+ {
+ switch ($order)
+ {
+ case 'date':
+ krsort($this->server_plugins);
+ break;
+ case 'revision':
+ usort($this->server_plugins, array($this, 'extension_revision_compare'));
+ break;
+ case 'name':
+ uasort($this->server_plugins, array($this, 'extension_name_compare'));
+ break;
+ case 'author':
+ uasort($this->server_plugins, array($this, 'extension_author_compare'));
+ break;
+ case 'downloads':
+ usort($this->server_plugins, array($this, 'extension_downloads_compare'));
+ break;
+ }
+ }
+
+ /**
+ * Extract plugin files from archive
+ * @param string - install or upgrade
+ * @param string - archive URL
+ * @param string - plugin id or extension id
+ */
+ function extract_plugin_files($action, $revision, $dest)
+ {
+ if ($archive = tempnam( PHPWG_PLUGINS_PATH, 'zip'))
+ {
+ $url = PEM_URL . '/download.php';
+ $get_data = array(
+ 'rid' => $revision,
+ 'origin' => 'piwigo_'.$action,
+ );
+
+ if ($handle = @fopen($archive, 'wb') and fetchRemote($url, $handle, $get_data))
+ {
+ fclose($handle);
+ include_once(PHPWG_ROOT_PATH.'admin/include/pclzip.lib.php');
+ $zip = new PclZip($archive);
+ if ($list = $zip->listContent())
+ {
+ foreach ($list as $file)
+ {
+ // we search main.inc.php in archive
+ if (basename($file['filename']) == 'main.inc.php'
+ and (!isset($main_filepath)
+ or strlen($file['filename']) < strlen($main_filepath)))
+ {
+ $main_filepath = $file['filename'];
+ }
+ }
+ if (isset($main_filepath))
+ {
+ $root = dirname($main_filepath); // main.inc.php path in archive
+ if ($action == 'upgrade')
+ {
+ $extract_path = PHPWG_PLUGINS_PATH . $dest;
+ }
+ else
+ {
+ $extract_path = PHPWG_PLUGINS_PATH
+ . ($root == '.' ? 'extension_' . $dest : basename($root));
+ }
+ if($result = $zip->extract(PCLZIP_OPT_PATH, $extract_path,
+ PCLZIP_OPT_REMOVE_PATH, $root,
+ PCLZIP_OPT_REPLACE_NEWER))
+ {
+ foreach ($result as $file)
+ {
+ if ($file['stored_filename'] == $main_filepath)
+ {
+ $status = $file['status'];
+ break;
+ }
+ }
+ if (file_exists($extract_path.'/obsolete.list')
+ and $old_files = file($extract_path.'/obsolete.list', FILE_IGNORE_NEW_LINES)
+ and !empty($old_files))
+ {
+ $old_files[] = 'obsolete.list';
+ foreach($old_files as $old_file)
+ {
+ $path = $extract_path.'/'.$old_file;
+ if (is_file($path))
+ {
+ @unlink($path);
+ }
+ elseif (is_dir($path))
+ {
+ deltree($path, PHPWG_PLUGINS_PATH . 'trash');
+ }
+ }
+ }
+ }
+ else $status = 'extract_error';
+ }
+ else $status = 'archive_error';
+ }
+ else $status = 'archive_error';
+ }
+ else $status = 'dl_archive_error';
+ }
+ else $status = 'temp_path_error';
+
+ @unlink($archive);
+ return $status;
+ }
+
+ function get_merged_extensions($version=PHPWG_VERSION)
+ {
+ $file = PHPWG_ROOT_PATH.'install/obsolete_extensions.list';
+ $merged_extensions = array();
+
+ if (file_exists($file) and $obsolete_ext = file($file, FILE_IGNORE_NEW_LINES) and !empty($obsolete_ext))
+ {
+ foreach ($obsolete_ext as $ext)
+ {
+ if (preg_match('/^(\d+) ?: ?(.*?)$/', $ext, $matches))
+ {
+ $merged_extensions[$matches[1]] = $matches[2];
+ }
+ }
+ }
+ return $merged_extensions;
+ }
+
+ /**
+ * Sort functions
+ */
+ function extension_revision_compare($a, $b)
+ {
+ if ($a['revision_date'] < $b['revision_date']) return 1;
+ else return -1;
+ }
+
+ function extension_name_compare($a, $b)
+ {
+ return strcmp(strtolower($a['extension_name']), strtolower($b['extension_name']));
+ }
+
+ function extension_author_compare($a, $b)
+ {
+ $r = strcasecmp($a['author_name'], $b['author_name']);
+ if ($r == 0) return $this->extension_name_compare($a, $b);
+ else return $r;
+ }
+
+ function plugin_author_compare($a, $b)
+ {
+ $r = strcasecmp($a['author'], $b['author']);
+ if ($r == 0) return name_compare($a, $b);
+ else return $r;
+ }
+
+ function extension_downloads_compare($a, $b)
+ {
+ if ($a['extension_nb_downloads'] < $b['extension_nb_downloads']) return 1;
+ else return -1;
+ }
+
+ function sort_plugins_by_state()
+ {
+ uasort($this->fs_plugins, 'name_compare');
+
+ $active_plugins = array();
+ $inactive_plugins = array();
+ $not_installed = array();
+
+ foreach($this->fs_plugins as $plugin_id => $plugin)
+ {
+ if (isset($this->db_plugins_by_id[$plugin_id]))
+ {
+ $this->db_plugins_by_id[$plugin_id]['state'] == 'active' ?
+ $active_plugins[$plugin_id] = $plugin : $inactive_plugins[$plugin_id] = $plugin;
+ }
+ else
+ {
+ $not_installed[$plugin_id] = $plugin;
+ }
+ }
+ $this->fs_plugins = $active_plugins + $inactive_plugins + $not_installed;
+ }
+}
+?>
\ No newline at end of file
diff --git a/sources/admin/include/tabsheet.class.php b/sources/admin/include/tabsheet.class.php
new file mode 100644
index 0000000..9dc441a
--- /dev/null
+++ b/sources/admin/include/tabsheet.class.php
@@ -0,0 +1,159 @@
+sheets = array();
+ $this->uniqid = null;
+ $this->name = $name;
+ $this->titlename = $titlename;
+ $this->selected = "";
+ }
+
+ function set_id($id)
+ {
+ $this->uniqid = $id;
+ }
+
+ /*
+ add a tab
+ */
+ function add($name, $caption, $url, $selected = false)
+ {
+ if (!isset($this->sheets[$name]))
+ {
+ $this->sheets[$name] = array('caption' => $caption,
+ 'url' => $url);
+ if($selected)
+ {
+ $this->selected=$name;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ remove a tab
+ */
+ function delete($name)
+ {
+ if (isset($this->sheets[$name]))
+ {
+ array_splice($this->sheets, $name, 1);
+
+ if ($this->selected == $name)
+ {
+ $this->selected = "";
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ select a tab to be active
+ */
+ function select($name)
+ {
+ $this->sheets = trigger_event('tabsheet_before_select', $this->sheets, $this->uniqid);
+ if (!array_key_exists($name, $this->sheets))
+ {
+ $keys = array_keys($this->sheets);
+ $name = $keys[0];
+ }
+ $this->selected = $name;
+ }
+
+ /*
+ set $titlename value
+ */
+ function set_titlename($titlename)
+ {
+ $this->titlename = $titlename;
+ return $this->titlename;
+ }
+
+ /*
+ returns $titlename value
+ */
+ function get_titlename()
+ {
+ return $this->titlename;
+ }
+
+ /*
+ returns properties of selected tab
+ */
+ function get_selected()
+ {
+ if (!empty($this->selected))
+ {
+ return $this->sheets[$this->selected];
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /*
+ * Build TabSheet and assign this content to current page
+ *
+ * Fill $this->$name {default value = TABSHEET} with HTML code for tabsheet
+ * Fill $this->titlename {default value = TABSHEET_TITLE} with formated caption of the selected tab
+ */
+ function assign()
+ {
+ global $template;
+
+ $template->set_filename('tabsheet', 'tabsheet.tpl');
+ $template->assign('tabsheet', $this->sheets);
+ $template->assign('tabsheet_selected', $this->selected);
+
+ $selected_tab = $this->get_selected();
+
+ if (isset($selected_tab))
+ {
+ $template->assign(
+ array($this->titlename => '['.$selected_tab['caption'].']'));
+ }
+
+ $template->assign_var_from_handle($this->name, 'tabsheet');
+ $template->clear_assign('tabsheet');
+ }
+}
+
+?>
diff --git a/sources/admin/include/themes.class.php b/sources/admin/include/themes.class.php
new file mode 100644
index 0000000..824ccf4
--- /dev/null
+++ b/sources/admin/include/themes.class.php
@@ -0,0 +1,747 @@
+theme_id, $theme_version, $errors);
+ }
+ }
+ function deactivate()
+ {
+ if (is_callable('theme_deactivate'))
+ {
+ return theme_deactivate($this->theme_id);
+ }
+ }
+ function delete()
+ {
+ if (is_callable('theme_delete'))
+ {
+ return theme_delete($this->theme_id);
+ }
+ }
+}
+
+
+class themes
+{
+ var $fs_themes = array();
+ var $db_themes_by_id = array();
+ var $server_themes = array();
+
+ /**
+ * Initialize $fs_themes and $db_themes_by_id
+ */
+ function themes()
+ {
+ $this->get_fs_themes();
+
+ foreach ($this->get_db_themes() as $db_theme)
+ {
+ $this->db_themes_by_id[$db_theme['id']] = $db_theme;
+ }
+ }
+
+ /**
+ * Returns the maintain class of a theme
+ * or build a new class with the procedural methods
+ * @param string $theme_id
+ */
+ private static function build_maintain_class($theme_id)
+ {
+ $file_to_include = PHPWG_THEMES_PATH.'/'.$theme_id.'/admin/maintain.inc.php';
+ $classname = $theme_id.'_maintain';
+
+ if (file_exists($file_to_include))
+ {
+ include_once($file_to_include);
+
+ if (class_exists($classname))
+ {
+ $theme_maintain = new $classname($theme_id);
+ }
+ else
+ {
+ $theme_maintain = new DummyTheme_maintain($theme_id);
+ }
+ }
+ else
+ {
+ $theme_maintain = new DummyTheme_maintain($theme_id);
+ }
+
+ return $theme_maintain;
+ }
+
+ /**
+ * Perform requested actions
+ * @param string - action
+ * @param string - theme id
+ * @param array - errors
+ */
+ function perform_action($action, $theme_id)
+ {
+ global $conf;
+
+ if (isset($this->db_themes_by_id[$theme_id]))
+ {
+ $crt_db_theme = $this->db_themes_by_id[$theme_id];
+ }
+
+ $theme_maintain = self::build_maintain_class($theme_id);
+
+ $errors = array();
+
+ switch ($action)
+ {
+ case 'activate':
+ if (isset($crt_db_theme))
+ {
+ // the theme is already active
+ break;
+ }
+
+ if ('default' == $theme_id)
+ {
+ // you can't activate the "default" theme
+ break;
+ }
+
+ $missing_parent = $this->missing_parent_theme($theme_id);
+ if (isset($missing_parent))
+ {
+ $errors[] = l10n(
+ 'Impossible to activate this theme, the parent theme is missing: %s',
+ $missing_parent
+ );
+
+ break;
+ }
+
+ if ($this->fs_themes[$theme_id]['mobile']
+ and !empty($conf['mobile_theme'])
+ and $conf['mobile_theme'] != $theme_id)
+ {
+ $errors[] = l10n('You can activate only one mobile theme.');
+ break;
+ }
+
+ $theme_maintain->activate($this->fs_themes[$theme_id]['version'], $errors);
+
+ if (empty($errors))
+ {
+ $query = '
+INSERT INTO '.THEMES_TABLE.'
+ (id, version, name)
+ VALUES(\''.$theme_id.'\',
+ \''.$this->fs_themes[$theme_id]['version'].'\',
+ \''.$this->fs_themes[$theme_id]['name'].'\')
+;';
+ pwg_query($query);
+
+ if ($this->fs_themes[$theme_id]['mobile'])
+ {
+ conf_update_param('mobile_theme', $theme_id);
+ }
+ }
+ break;
+
+ case 'deactivate':
+ if (!isset($crt_db_theme))
+ {
+ // the theme is already inactive
+ break;
+ }
+
+ // you can't deactivate the last theme
+ if (count($this->db_themes_by_id) <= 1)
+ {
+ $errors[] = l10n('Impossible to deactivate this theme, you need at least one theme.');
+ break;
+ }
+
+ if ($theme_id == get_default_theme())
+ {
+ // find a random theme to replace
+ $new_theme = null;
+
+ $query = '
+SELECT id
+ FROM '.THEMES_TABLE.'
+ WHERE id != \''.$theme_id.'\'
+;';
+ $result = pwg_query($query);
+ if (pwg_db_num_rows($result) == 0)
+ {
+ $new_theme = 'default';
+ }
+ else
+ {
+ list($new_theme) = pwg_db_fetch_row($result);
+ }
+
+ $this->set_default_theme($new_theme);
+ }
+
+ $theme_maintain->deactivate();
+
+ $query = '
+DELETE
+ FROM '.THEMES_TABLE.'
+ WHERE id= \''.$theme_id.'\'
+;';
+ pwg_query($query);
+
+ if ($this->fs_themes[$theme_id]['mobile'])
+ {
+ conf_update_param('mobile_theme', '');
+ }
+ break;
+
+ case 'delete':
+ if (!empty($crt_db_theme))
+ {
+ $errors[] = 'CANNOT DELETE - THEME IS INSTALLED';
+ break;
+ }
+ if (!isset($this->fs_themes[$theme_id]))
+ {
+ // nothing to do here
+ break;
+ }
+
+ $children = $this->get_children_themes($theme_id);
+ if (count($children) > 0)
+ {
+ $errors[] = l10n(
+ 'Impossible to delete this theme. Other themes depends on it: %s',
+ implode(', ', $children)
+ );
+ break;
+ }
+
+ $theme_maintain->delete();
+
+ deltree(PHPWG_THEMES_PATH.$theme_id, PHPWG_THEMES_PATH . 'trash');
+ break;
+
+ case 'set_default':
+ // first we need to know which users are using the current default theme
+ $this->set_default_theme($theme_id);
+ break;
+ }
+ return $errors;
+ }
+
+ function missing_parent_theme($theme_id)
+ {
+ if (!isset($this->fs_themes[$theme_id]['parent']))
+ {
+ return null;
+ }
+
+ $parent = $this->fs_themes[$theme_id]['parent'];
+
+ if ('default' == $parent)
+ {
+ return null;
+ }
+
+ if (!isset($this->fs_themes[$parent]))
+ {
+ return $parent;
+ }
+
+ return $this->missing_parent_theme($parent);
+ }
+
+ function get_children_themes($theme_id)
+ {
+ $children = array();
+
+ foreach ($this->fs_themes as $test_child)
+ {
+ if (isset($test_child['parent']) and $test_child['parent'] == $theme_id)
+ {
+ $children[] = $test_child['name'];
+ }
+ }
+
+ return $children;
+ }
+
+ function set_default_theme($theme_id)
+ {
+ global $conf;
+
+ // first we need to know which users are using the current default theme
+ $default_theme = get_default_theme();
+
+ $query = '
+SELECT
+ user_id
+ FROM '.USER_INFOS_TABLE.'
+ WHERE theme = \''.$default_theme.'\'
+;';
+ $user_ids = array_unique(
+ array_merge(
+ array_from_query($query, 'user_id'),
+ array($conf['guest_id'], $conf['default_user_id'])
+ )
+ );
+
+ // $user_ids can't be empty, at least the default user has the default
+ // theme
+
+ $query = '
+UPDATE '.USER_INFOS_TABLE.'
+ SET theme = \''.$theme_id.'\'
+ WHERE user_id IN ('.implode(',', $user_ids).')
+;';
+ pwg_query($query);
+ }
+
+ function get_db_themes($id='')
+ {
+ $query = '
+SELECT
+ *
+ FROM '.THEMES_TABLE;
+
+ $clauses = array();
+ if (!empty($id))
+ {
+ $clauses[] = 'id = \''.$id.'\'';
+ }
+ if (count($clauses) > 0)
+ {
+ $query .= '
+ WHERE '. implode(' AND ', $clauses);
+ }
+
+ $result = pwg_query($query);
+ $themes = array();
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $themes[] = $row;
+ }
+ return $themes;
+ }
+
+
+ /**
+ * Get themes defined in the theme directory
+ */
+ function get_fs_themes()
+ {
+ $dir = opendir(PHPWG_THEMES_PATH);
+
+ while ($file = readdir($dir))
+ {
+ if ($file!='.' and $file!='..')
+ {
+ $path = PHPWG_THEMES_PATH.$file;
+ if (is_dir($path)
+ and preg_match('/^[a-zA-Z0-9-_]+$/', $file)
+ and file_exists($path.'/themeconf.inc.php')
+ )
+ {
+ $theme = array(
+ 'id' => $file,
+ 'name' => $file,
+ 'version' => '0',
+ 'uri' => '',
+ 'description' => '',
+ 'author' => '',
+ 'mobile' => false,
+ );
+ $theme_data = implode( '', file($path.'/themeconf.inc.php') );
+
+ if ( preg_match("|Theme Name: (.*)|", $theme_data, $val) )
+ {
+ $theme['name'] = trim( $val[1] );
+ }
+ if (preg_match("|Version: (.*)|", $theme_data, $val))
+ {
+ $theme['version'] = trim($val[1]);
+ }
+ if ( preg_match("|Theme URI: (.*)|", $theme_data, $val) )
+ {
+ $theme['uri'] = trim($val[1]);
+ }
+ if ($desc = load_language('description.txt', $path.'/', array('return' => true)))
+ {
+ $theme['description'] = trim($desc);
+ }
+ elseif ( preg_match("|Description: (.*)|", $theme_data, $val) )
+ {
+ $theme['description'] = trim($val[1]);
+ }
+ if ( preg_match("|Author: (.*)|", $theme_data, $val) )
+ {
+ $theme['author'] = trim($val[1]);
+ }
+ if ( preg_match("|Author URI: (.*)|", $theme_data, $val) )
+ {
+ $theme['author uri'] = trim($val[1]);
+ }
+ if (!empty($theme['uri']) and strpos($theme['uri'] , 'extension_view.php?eid='))
+ {
+ list( , $extension) = explode('extension_view.php?eid=', $theme['uri']);
+ if (is_numeric($extension)) $theme['extension'] = $extension;
+ }
+ if (preg_match('/["\']parent["\'][^"\']+["\']([^"\']+)["\']/', $theme_data, $val))
+ {
+ $theme['parent'] = $val[1];
+ }
+ if (preg_match('/["\']activable["\'].*?(true|false)/i', $theme_data, $val))
+ {
+ $theme['activable'] = get_boolean($val[1]);
+ }
+ if (preg_match('/["\']mobile["\'].*?(true|false)/i', $theme_data, $val))
+ {
+ $theme['mobile'] = get_boolean($val[1]);
+ }
+
+ // screenshot
+ $screenshot_path = $path.'/screenshot.png';
+ if (file_exists($screenshot_path))
+ {
+ $theme['screenshot'] = $screenshot_path;
+ }
+ else
+ {
+ global $conf;
+ $theme['screenshot'] =
+ PHPWG_ROOT_PATH.'admin/themes/'
+ .$conf['admin_theme']
+ .'/images/missing_screenshot.png'
+ ;
+ }
+
+ $admin_file = $path.'/admin/admin.inc.php';
+ if (file_exists($admin_file))
+ {
+ $theme['admin_uri'] = get_root_url().'admin.php?page=theme&theme='.$file;
+ }
+
+ // IMPORTANT SECURITY !
+ $theme = array_map('htmlspecialchars', $theme);
+ $this->fs_themes[$file] = $theme;
+ }
+ }
+ }
+ closedir($dir);
+ }
+
+ /**
+ * Sort fs_themes
+ */
+ function sort_fs_themes($order='name')
+ {
+ switch ($order)
+ {
+ case 'name':
+ uasort($this->fs_themes, 'name_compare');
+ break;
+ case 'status':
+ $this->sort_themes_by_state();
+ break;
+ case 'author':
+ uasort($this->fs_themes, array($this, 'theme_author_compare'));
+ break;
+ case 'id':
+ uksort($this->fs_themes, 'strcasecmp');
+ break;
+ }
+ }
+
+ /**
+ * Retrieve PEM server datas to $server_themes
+ */
+ function get_server_themes($new=false)
+ {
+ global $user, $conf;
+
+ $get_data = array(
+ 'category_id' => $conf['pem_themes_category'],
+ 'format' => 'php',
+ );
+
+ // Retrieve PEM versions
+ $version = PHPWG_VERSION;
+ $versions_to_check = array();
+ $url = PEM_URL . '/api/get_version_list.php';
+ if (fetchRemote($url, $result, $get_data) and $pem_versions = @unserialize($result))
+ {
+ if (!preg_match('/^\d+\.\d+\.\d+$/', $version))
+ {
+ $version = $pem_versions[0]['name'];
+ }
+ $branch = get_branch_from_version($version);
+ foreach ($pem_versions as $pem_version)
+ {
+ if (strpos($pem_version['name'], $branch) === 0)
+ {
+ $versions_to_check[] = $pem_version['id'];
+ }
+ }
+ }
+ if (empty($versions_to_check))
+ {
+ return false;
+ }
+
+ // Themes to check
+ $themes_to_check = array();
+ foreach($this->fs_themes as $fs_theme)
+ {
+ if (isset($fs_theme['extension']))
+ {
+ $themes_to_check[] = $fs_theme['extension'];
+ }
+ }
+
+ // Retrieve PEM themes infos
+ $url = PEM_URL . '/api/get_revision_list.php';
+ $get_data = array_merge($get_data, array(
+ 'last_revision_only' => 'true',
+ 'version' => implode(',', $versions_to_check),
+ 'lang' => substr($user['language'], 0, 2),
+ 'get_nb_downloads' => 'true',
+ )
+ );
+
+ if (!empty($themes_to_check))
+ {
+ if ($new)
+ {
+ $get_data['extension_exclude'] = implode(',', $themes_to_check);
+ }
+ else
+ {
+ $get_data['extension_include'] = implode(',', $themes_to_check);
+ }
+ }
+ if (fetchRemote($url, $result, $get_data))
+ {
+ $pem_themes = @unserialize($result);
+ if (!is_array($pem_themes))
+ {
+ return false;
+ }
+ foreach ($pem_themes as $theme)
+ {
+ $this->server_themes[$theme['extension_id']] = $theme;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Sort $server_themes
+ */
+ function sort_server_themes($order='date')
+ {
+ switch ($order)
+ {
+ case 'date':
+ krsort($this->server_themes);
+ break;
+ case 'revision':
+ usort($this->server_themes, array($this, 'extension_revision_compare'));
+ break;
+ case 'name':
+ uasort($this->server_themes, array($this, 'extension_name_compare'));
+ break;
+ case 'author':
+ uasort($this->server_themes, array($this, 'extension_author_compare'));
+ break;
+ case 'downloads':
+ usort($this->server_themes, array($this, 'extension_downloads_compare'));
+ break;
+ }
+ }
+
+ /**
+ * Extract theme files from archive
+ *
+ * @param string - install or upgrade
+ * @param string - remote revision identifier (numeric)
+ * @param string - theme id or extension id
+ */
+ function extract_theme_files($action, $revision, $dest)
+ {
+ if ($archive = tempnam( PHPWG_THEMES_PATH, 'zip'))
+ {
+ $url = PEM_URL . '/download.php';
+ $get_data = array(
+ 'rid' => $revision,
+ 'origin' => 'piwigo_'.$action,
+ );
+
+ if ($handle = @fopen($archive, 'wb') and fetchRemote($url, $handle, $get_data))
+ {
+ fclose($handle);
+ include_once(PHPWG_ROOT_PATH.'admin/include/pclzip.lib.php');
+ $zip = new PclZip($archive);
+ if ($list = $zip->listContent())
+ {
+ foreach ($list as $file)
+ {
+ // we search main.inc.php in archive
+ if (basename($file['filename']) == 'themeconf.inc.php'
+ and (!isset($main_filepath)
+ or strlen($file['filename']) < strlen($main_filepath)))
+ {
+ $main_filepath = $file['filename'];
+ }
+ }
+ if (isset($main_filepath))
+ {
+ $root = dirname($main_filepath); // main.inc.php path in archive
+ if ($action == 'upgrade')
+ {
+ $extract_path = PHPWG_THEMES_PATH . $dest;
+ }
+ else
+ {
+ $extract_path = PHPWG_THEMES_PATH . ($root == '.' ? 'extension_' . $dest : basename($root));
+ }
+ if (
+ $result = $zip->extract(
+ PCLZIP_OPT_PATH, $extract_path,
+ PCLZIP_OPT_REMOVE_PATH, $root,
+ PCLZIP_OPT_REPLACE_NEWER
+ )
+ )
+ {
+ foreach ($result as $file)
+ {
+ if ($file['stored_filename'] == $main_filepath)
+ {
+ $status = $file['status'];
+ break;
+ }
+ }
+ if (file_exists($extract_path.'/obsolete.list')
+ and $old_files = file($extract_path.'/obsolete.list', FILE_IGNORE_NEW_LINES)
+ and !empty($old_files))
+ {
+ $old_files[] = 'obsolete.list';
+ foreach($old_files as $old_file)
+ {
+ $path = $extract_path.'/'.$old_file;
+ if (is_file($path))
+ {
+ @unlink($path);
+ }
+ elseif (is_dir($path))
+ {
+ deltree($path, PHPWG_THEMES_PATH . 'trash');
+ }
+ }
+ }
+ }
+ else $status = 'extract_error';
+ }
+ else $status = 'archive_error';
+ }
+ else $status = 'archive_error';
+ }
+ else $status = 'dl_archive_error';
+ }
+ else $status = 'temp_path_error';
+
+ @unlink($archive);
+ return $status;
+ }
+
+ /**
+ * Sort functions
+ */
+ function extension_revision_compare($a, $b)
+ {
+ if ($a['revision_date'] < $b['revision_date']) return 1;
+ else return -1;
+ }
+
+ function extension_name_compare($a, $b)
+ {
+ return strcmp(strtolower($a['extension_name']), strtolower($b['extension_name']));
+ }
+
+ function extension_author_compare($a, $b)
+ {
+ $r = strcasecmp($a['author_name'], $b['author_name']);
+ if ($r == 0) return $this->extension_name_compare($a, $b);
+ else return $r;
+ }
+
+ function theme_author_compare($a, $b)
+ {
+ $r = strcasecmp($a['author'], $b['author']);
+ if ($r == 0) return name_compare($a, $b);
+ else return $r;
+ }
+
+ function extension_downloads_compare($a, $b)
+ {
+ if ($a['extension_nb_downloads'] < $b['extension_nb_downloads']) return 1;
+ else return -1;
+ }
+
+ function sort_themes_by_state()
+ {
+ uasort($this->fs_themes, 'name_compare');
+
+ $active_themes = array();
+ $inactive_themes = array();
+ $not_installed = array();
+
+ foreach($this->fs_themes as $theme_id => $theme)
+ {
+ if (isset($this->db_themes_by_id[$theme_id]))
+ {
+ $this->db_themes_by_id[$theme_id]['state'] == 'active' ?
+ $active_themes[$theme_id] = $theme : $inactive_themes[$theme_id] = $theme;
+ }
+ else
+ {
+ $not_installed[$theme_id] = $theme;
+ }
+ }
+ $this->fs_themes = $active_themes + $inactive_themes + $not_installed;
+ }
+
+}
+?>
\ No newline at end of file
diff --git a/sources/admin/include/updates.class.php b/sources/admin/include/updates.class.php
new file mode 100644
index 0000000..12f5c22
--- /dev/null
+++ b/sources/admin/include/updates.class.php
@@ -0,0 +1,464 @@
+types = array('plugins', 'themes', 'languages');
+
+ if (in_array($page, $this->types))
+ {
+ $this->types = array($page);
+ }
+ $this->default_themes = array('clear', 'dark', 'Sylvia', 'elegant');
+ $this->default_plugins = array('admin_multi_view', 'c13y_upgrade', 'language_switch', 'LocalFilesEditor');
+
+ foreach ($this->types as $type)
+ {
+ include_once(PHPWG_ROOT_PATH.'admin/include/'.$type.'.class.php');
+ $this->$type = new $type();
+ }
+ }
+
+ static function check_piwigo_upgrade()
+ {
+ $_SESSION['need_update'] = null;
+
+ if (preg_match('/(\d+\.\d+)\.(\d+)/', PHPWG_VERSION, $matches)
+ and @fetchRemote(PHPWG_URL.'/download/all_versions.php?rand='.md5(uniqid(rand(), true)), $result))
+ {
+ $all_versions = @explode("\n", $result);
+ $new_version = trim($all_versions[0]);
+ $_SESSION['need_update'] = version_compare(PHPWG_VERSION, $new_version, '<');
+ }
+ }
+
+ function get_server_extensions($version=PHPWG_VERSION)
+ {
+ global $user;
+
+ $get_data = array(
+ 'format' => 'php',
+ );
+
+ // Retrieve PEM versions
+ $versions_to_check = array();
+ $url = PEM_URL . '/api/get_version_list.php';
+ if (fetchRemote($url, $result, $get_data) and $pem_versions = @unserialize($result))
+ {
+ if (!preg_match('/^\d+\.\d+\.\d+$/', $version))
+ {
+ $version = $pem_versions[0]['name'];
+ }
+ $branch = get_branch_from_version($version);
+ foreach ($pem_versions as $pem_version)
+ {
+ if (strpos($pem_version['name'], $branch) === 0)
+ {
+ $versions_to_check[] = $pem_version['id'];
+ }
+ }
+ }
+ if (empty($versions_to_check))
+ {
+ return false;
+ }
+
+ // Extensions to check
+ $ext_to_check = array();
+ foreach ($this->types as $type)
+ {
+ $fs = 'fs_'.$type;
+ foreach ($this->$type->$fs as $ext)
+ {
+ if (isset($ext['extension']))
+ {
+ $ext_to_check[$ext['extension']] = $type;
+ }
+ }
+ }
+
+ // Retrieve PEM plugins infos
+ $url = PEM_URL . '/api/get_revision_list.php';
+ $get_data = array_merge($get_data, array(
+ 'last_revision_only' => 'true',
+ 'version' => implode(',', $versions_to_check),
+ 'lang' => substr($user['language'], 0, 2),
+ 'get_nb_downloads' => 'true',
+ )
+ );
+
+ $post_data = array();
+ if (!empty($ext_to_check))
+ {
+ $post_data['extension_include'] = implode(',', array_keys($ext_to_check));
+ }
+
+ if (fetchRemote($url, $result, $get_data, $post_data))
+ {
+ $pem_exts = @unserialize($result);
+ if (!is_array($pem_exts))
+ {
+ return false;
+ }
+ foreach ($pem_exts as $ext)
+ {
+ if (isset($ext_to_check[$ext['extension_id']]))
+ {
+ $server = 'server_'.$ext_to_check[$ext['extension_id']];
+ $this->$ext_to_check[$ext['extension_id']]->$server += array($ext['extension_id'] => $ext);
+ unset($ext_to_check[$ext['extension_id']]);
+ }
+ }
+ $this->check_missing_extensions($ext_to_check);
+ return true;
+ }
+ return false;
+ }
+
+ // Check all extensions upgrades
+ function check_extensions()
+ {
+ global $conf;
+
+ if (!$this->get_server_extensions())
+ {
+ return false;
+ }
+
+ $_SESSION['extensions_need_update'] = array();
+
+ foreach ($this->types as $type)
+ {
+ $fs = 'fs_'.$type;
+ $server = 'server_'.$type;
+ $server_ext = $this->$type->$server;
+ $fs_ext = $this->$type->$fs;
+
+ $ignore_list = array();
+ $need_upgrade = array();
+
+ foreach($fs_ext as $ext_id => $fs_ext)
+ {
+ if (isset($fs_ext['extension']) and isset($server_ext[$fs_ext['extension']]))
+ {
+ $ext_info = $server_ext[$fs_ext['extension']];
+
+ if (!safe_version_compare($fs_ext['version'], $ext_info['revision_name'], '>='))
+ {
+ if (in_array($ext_id, $conf['updates_ignored'][$type]))
+ {
+ $ignore_list[] = $ext_id;
+ }
+ else
+ {
+ $_SESSION['extensions_need_update'][$type][$ext_id] = $ext_info['revision_name'];
+ }
+ }
+ }
+ }
+ $conf['updates_ignored'][$type] = $ignore_list;
+ }
+ conf_update_param('updates_ignored', pwg_db_real_escape_string(serialize($conf['updates_ignored'])));
+ }
+
+ // Check if extension have been upgraded since last check
+ function check_updated_extensions()
+ {
+ foreach ($this->types as $type)
+ {
+ if (!empty($_SESSION['extensions_need_update'][$type]))
+ {
+ $fs = 'fs_'.$type;
+ foreach($this->$type->$fs as $ext_id => $fs_ext)
+ {
+ if (isset($_SESSION['extensions_need_update'][$type][$ext_id])
+ and safe_version_compare($fs_ext['version'], $_SESSION['extensions_need_update'][$type][$ext_id], '>='))
+ {
+ // Extension have been upgraded
+ $this->check_extensions();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ function check_missing_extensions($missing)
+ {
+ foreach ($missing as $id => $type)
+ {
+ $fs = 'fs_'.$type;
+ $default = 'default_'.$type;
+ foreach ($this->$type->$fs as $ext_id => $ext)
+ {
+ if (isset($ext['extension']) and $id == $ext['extension']
+ and !in_array($ext_id, $this->$default)
+ and !in_array($ext['extension'], $this->merged_extensions))
+ {
+ $this->missing[$type][] = $ext;
+ break;
+ }
+ }
+ }
+ }
+
+ function get_merged_extensions($version)
+ {
+ if (fetchRemote($this->merged_extension_url, $result))
+ {
+ $rows = explode("\n", $result);
+ foreach ($rows as $row)
+ {
+ if (preg_match('/^(\d+\.\d+): *(.*)$/', $row, $match))
+ {
+ if (version_compare($version, $match[1], '>='))
+ {
+ $extensions = explode(',', trim($match[2]));
+ $this->merged_extensions = array_merge($this->merged_extensions, $extensions);
+ }
+ }
+ }
+ }
+ }
+
+ static function process_obsolete_list($file)
+ {
+ if (file_exists(PHPWG_ROOT_PATH.$file)
+ and $old_files = file(PHPWG_ROOT_PATH.$file, FILE_IGNORE_NEW_LINES)
+ and !empty($old_files))
+ {
+ $old_files[] = $file;
+ foreach($old_files as $old_file)
+ {
+ $path = PHPWG_ROOT_PATH.$old_file;
+ if (is_file($path))
+ {
+ @unlink($path);
+ }
+ elseif (is_dir($path))
+ {
+ deltree($path, PHPWG_ROOT_PATH.'_trash');
+ }
+ }
+ }
+ }
+
+ static function dump_database($include_history=false)
+ {
+ global $page, $conf, $cfgBase;
+
+ if (version_compare(PHPWG_VERSION, '2.1', '<'))
+ {
+ $conf['db_base'] = $cfgBase;
+ }
+
+ include(PHPWG_ROOT_PATH.'admin/include/mysqldump.php');
+
+ $path = PHPWG_ROOT_PATH.$conf['data_location'].'update';
+
+ if (@mkgetdir($path)
+ and ($backupFile = tempnam($path, 'sql'))
+ and ($dumper = new MySQLDump($conf['db_base'],$backupFile,false,false)))
+ {
+ foreach (get_defined_constants() as $constant => $value)
+ {
+ if (preg_match('/_TABLE$/', $constant))
+ {
+ $dumper->getTableStructure($value);
+ if ($constant == 'HISTORY_TABLE' and !$include_history)
+ {
+ continue;
+ }
+ $dumper->getTableData($value);
+ }
+ }
+ }
+
+ if (@filesize($backupFile))
+ {
+ $http_headers = array(
+ 'Content-Length: '.@filesize($backupFile),
+ 'Content-Type: text/x-sql',
+ 'Content-Disposition: attachment; filename="database.sql";',
+ 'Content-Transfer-Encoding: binary',
+ );
+
+ foreach ($http_headers as $header) {
+ header($header);
+ }
+
+ @readfile($backupFile);
+ deltree(PHPWG_ROOT_PATH.$conf['data_location'].'update');
+ exit();
+ }
+ else
+ {
+ $page['errors'][] = l10n('Unable to dump database.');
+ }
+ }
+
+ static function upgrade_to($upgrade_to, &$step, $check_current_version=true)
+ {
+ global $page, $conf, $template;
+
+ if ($check_current_version and !version_compare($upgrade_to, PHPWG_VERSION, '>'))
+ {
+ redirect(get_root_url().'admin.php?page=plugin-'.basename(dirname(__FILE__)));
+ }
+
+ if ($step == 2)
+ {
+ preg_match('/(\d+\.\d+)\.(\d+)/', PHPWG_VERSION, $matches);
+ $code = $matches[1].'.x_to_'.$upgrade_to;
+ $dl_code = str_replace(array('.', '_'), '', $code);
+ $remove_path = $code;
+ $obsolete_list = 'obsolete.list';
+ }
+ else
+ {
+ $code = $upgrade_to;
+ $dl_code = $code;
+ $remove_path = version_compare($code, '2.0.8', '>=') ? 'piwigo' : 'piwigo-'.$code;
+ $obsolete_list = PHPWG_ROOT_PATH.'install/obsolete.list';
+ }
+
+ if (empty($page['errors']))
+ {
+ $path = PHPWG_ROOT_PATH.$conf['data_location'].'update';
+ $filename = $path.'/'.$code.'.zip';
+ @mkgetdir($path);
+
+ $chunk_num = 0;
+ $end = false;
+ $zip = @fopen($filename, 'w');
+
+ while (!$end)
+ {
+ $chunk_num++;
+ if (@fetchRemote(PHPWG_URL.'/download/dlcounter.php?code='.$dl_code.'&chunk_num='.$chunk_num, $result)
+ and $input = @unserialize($result))
+ {
+ if (0 == $input['remaining'])
+ {
+ $end = true;
+ }
+ @fwrite($zip, base64_decode($input['data']));
+ }
+ else
+ {
+ $end = true;
+ }
+ }
+ @fclose($zip);
+
+ if (@filesize($filename))
+ {
+ $zip = new PclZip($filename);
+ if ($result = $zip->extract(PCLZIP_OPT_PATH, PHPWG_ROOT_PATH,
+ PCLZIP_OPT_REMOVE_PATH, $remove_path,
+ PCLZIP_OPT_SET_CHMOD, 0755,
+ PCLZIP_OPT_REPLACE_NEWER))
+ {
+ //Check if all files were extracted
+ $error = '';
+ foreach($result as $extract)
+ {
+ if (!in_array($extract['status'], array('ok', 'filtered', 'already_a_directory')))
+ {
+ // Try to change chmod and extract
+ if (@chmod(PHPWG_ROOT_PATH.$extract['filename'], 0777)
+ and ($res = $zip->extract(PCLZIP_OPT_BY_NAME, $remove_path.'/'.$extract['filename'],
+ PCLZIP_OPT_PATH, PHPWG_ROOT_PATH,
+ PCLZIP_OPT_REMOVE_PATH, $remove_path,
+ PCLZIP_OPT_SET_CHMOD, 0755,
+ PCLZIP_OPT_REPLACE_NEWER))
+ and isset($res[0]['status'])
+ and $res[0]['status'] == 'ok')
+ {
+ continue;
+ }
+ else
+ {
+ $error .= $extract['filename'].': '.$extract['status']."\n";
+ }
+ }
+ }
+
+ if (empty($error))
+ {
+ self::process_obsolete_list($obsolete_list);
+ deltree(PHPWG_ROOT_PATH.$conf['data_location'].'update');
+ invalidate_user_cache(true);
+ $template->delete_compiled_templates();
+ unset($_SESSION['need_update']);
+ if ($step == 2)
+ {
+ $page['infos'][] = l10n('Update Complete');
+ $page['infos'][] = $upgrade_to;
+ $step = -1;
+ }
+ else
+ {
+ redirect(PHPWG_ROOT_PATH.'upgrade.php?now=');
+ }
+ }
+ else
+ {
+ file_put_contents(PHPWG_ROOT_PATH.$conf['data_location'].'update/log_error.txt', $error);
+
+ $page['errors'][] = l10n(
+ 'An error has occured during extract. Please check files permissions of your piwigo installation.Click here to show log error .',
+ get_root_url().$conf['data_location'].'update/log_error.txt'
+ );
+ }
+ }
+ else
+ {
+ deltree(PHPWG_ROOT_PATH.$conf['data_location'].'update');
+ $page['errors'][] = l10n('An error has occured during upgrade.');
+ }
+ }
+ else
+ {
+ $page['errors'][] = l10n('Piwigo cannot retrieve upgrade file from server');
+ }
+ }
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/sources/admin/include/uploadify/cancel.png b/sources/admin/include/uploadify/cancel.png
new file mode 100644
index 0000000..1c7d627
Binary files /dev/null and b/sources/admin/include/uploadify/cancel.png differ
diff --git a/sources/admin/include/uploadify/jquery.uploadify.v3.0.0.min.js b/sources/admin/include/uploadify/jquery.uploadify.v3.0.0.min.js
new file mode 100644
index 0000000..e85d864
--- /dev/null
+++ b/sources/admin/include/uploadify/jquery.uploadify.v3.0.0.min.js
@@ -0,0 +1,38 @@
+/*
+SWFUpload: http://www.swfupload.org, http://swfupload.googlecode.com
+
+mmSWFUpload 1.0: Flash upload dialog - http://profandesign.se/swfupload/, http://www.vinterwebb.se/
+
+SWFUpload is (c) 2006-2007 Lars Huring, Olov Nilzén and Mammon Media and is released under the MIT License:
+http://www.opensource.org/licenses/mit-license.php
+
+SWFUpload 2 is (c) 2007-2008 Jake Roberts and is released under the MIT License:
+http://www.opensource.org/licenses/mit-license.php
+*/
+
+var SWFUpload;if(SWFUpload==undefined){SWFUpload=function(a){this.initSWFUpload(a)}}SWFUpload.prototype.initSWFUpload=function(b){try{this.customSettings={};this.settings=b;this.eventQueue=[];this.movieName="SWFUpload_"+SWFUpload.movieCount++;this.movieElement=null;SWFUpload.instances[this.movieName]=this;this.initSettings();this.loadFlash();this.displayDebugInfo()}catch(a){delete SWFUpload.instances[this.movieName];throw a}};SWFUpload.instances={};SWFUpload.movieCount=0;SWFUpload.version="2.2.0 2009-03-25";SWFUpload.QUEUE_ERROR={QUEUE_LIMIT_EXCEEDED:-100,FILE_EXCEEDS_SIZE_LIMIT:-110,ZERO_BYTE_FILE:-120,INVALID_FILETYPE:-130};SWFUpload.UPLOAD_ERROR={HTTP_ERROR:-200,MISSING_UPLOAD_URL:-210,IO_ERROR:-220,SECURITY_ERROR:-230,UPLOAD_LIMIT_EXCEEDED:-240,UPLOAD_FAILED:-250,SPECIFIED_FILE_ID_NOT_FOUND:-260,FILE_VALIDATION_FAILED:-270,FILE_CANCELLED:-280,UPLOAD_STOPPED:-290};SWFUpload.FILE_STATUS={QUEUED:-1,IN_PROGRESS:-2,ERROR:-3,COMPLETE:-4,CANCELLED:-5};SWFUpload.BUTTON_ACTION={SELECT_FILE:-100,SELECT_FILES:-110,START_UPLOAD:-120};SWFUpload.CURSOR={ARROW:-1,HAND:-2};SWFUpload.WINDOW_MODE={WINDOW:"window",TRANSPARENT:"transparent",OPAQUE:"opaque"};SWFUpload.completeURL=function(a){if(typeof(a)!=="string"||a.match(/^https?:\/\//i)||a.match(/^\//)){return a}var c=window.location.protocol+"//"+window.location.hostname+(window.location.port?":"+window.location.port:"");var b=window.location.pathname.lastIndexOf("/");if(b<=0){path="/"}else{path=window.location.pathname.substr(0,b)+"/"}return path+a};SWFUpload.prototype.initSettings=function(){this.ensureDefault=function(b,a){this.settings[b]=(this.settings[b]==undefined)?a:this.settings[b]};this.ensureDefault("upload_url","");this.ensureDefault("preserve_relative_urls",false);this.ensureDefault("file_post_name","Filedata");this.ensureDefault("post_params",{});this.ensureDefault("use_query_string",false);this.ensureDefault("requeue_on_error",false);this.ensureDefault("http_success",[]);this.ensureDefault("assume_success_timeout",0);this.ensureDefault("file_types","*.*");this.ensureDefault("file_types_description","All Files");this.ensureDefault("file_size_limit",0);this.ensureDefault("file_upload_limit",0);this.ensureDefault("file_queue_limit",0);this.ensureDefault("flash_url","swfupload.swf");this.ensureDefault("prevent_swf_caching",true);this.ensureDefault("button_image_url","");this.ensureDefault("button_width",1);this.ensureDefault("button_height",1);this.ensureDefault("button_text","");this.ensureDefault("button_text_style","color: #000000; font-size: 16pt;");this.ensureDefault("button_text_top_padding",0);this.ensureDefault("button_text_left_padding",0);this.ensureDefault("button_action",SWFUpload.BUTTON_ACTION.SELECT_FILES);this.ensureDefault("button_disabled",false);this.ensureDefault("button_placeholder_id","");this.ensureDefault("button_placeholder",null);this.ensureDefault("button_cursor",SWFUpload.CURSOR.ARROW);this.ensureDefault("button_window_mode",SWFUpload.WINDOW_MODE.WINDOW);this.ensureDefault("debug",false);this.settings.debug_enabled=this.settings.debug;this.settings.return_upload_start_handler=this.returnUploadStart;this.ensureDefault("swfupload_loaded_handler",null);this.ensureDefault("file_dialog_start_handler",null);this.ensureDefault("file_queued_handler",null);this.ensureDefault("file_queue_error_handler",null);this.ensureDefault("file_dialog_complete_handler",null);this.ensureDefault("upload_start_handler",null);this.ensureDefault("upload_progress_handler",null);this.ensureDefault("upload_error_handler",null);this.ensureDefault("upload_success_handler",null);this.ensureDefault("upload_complete_handler",null);this.ensureDefault("debug_handler",this.debugMessage);this.ensureDefault("custom_settings",{});this.customSettings=this.settings.custom_settings;if(!!this.settings.prevent_swf_caching){this.settings.flash_url=this.settings.flash_url+(this.settings.flash_url.indexOf("?")<0?"?":"&")+"preventswfcaching="+new Date().getTime()}if(!this.settings.preserve_relative_urls){this.settings.upload_url=SWFUpload.completeURL(this.settings.upload_url);this.settings.button_image_url=SWFUpload.completeURL(this.settings.button_image_url)}delete this.ensureDefault};SWFUpload.prototype.loadFlash=function(){var a,b;if(document.getElementById(this.movieName)!==null){throw"ID "+this.movieName+" is already in use. The Flash Object could not be added"}a=document.getElementById(this.settings.button_placeholder_id)||this.settings.button_placeholder;if(a==undefined){throw"Could not find the placeholder element: "+this.settings.button_placeholder_id}b=document.createElement("div");b.innerHTML=this.getFlashHTML();a.parentNode.replaceChild(b.firstChild,a);if(window[this.movieName]==undefined){window[this.movieName]=this.getMovieElement()}};SWFUpload.prototype.getFlashHTML=function(){return['',' ',' ',' ',' ',' ',' '," "].join("")};SWFUpload.prototype.getFlashVars=function(){var b=this.buildParamString();var a=this.settings.http_success.join(",");return["movieName=",encodeURIComponent(this.movieName),"&uploadURL=",encodeURIComponent(this.settings.upload_url),"&useQueryString=",encodeURIComponent(this.settings.use_query_string),"&requeueOnError=",encodeURIComponent(this.settings.requeue_on_error),"&httpSuccess=",encodeURIComponent(a),"&assumeSuccessTimeout=",encodeURIComponent(this.settings.assume_success_timeout),"¶ms=",encodeURIComponent(b),"&filePostName=",encodeURIComponent(this.settings.file_post_name),"&fileTypes=",encodeURIComponent(this.settings.file_types),"&fileTypesDescription=",encodeURIComponent(this.settings.file_types_description),"&fileSizeLimit=",encodeURIComponent(this.settings.file_size_limit),"&fileUploadLimit=",encodeURIComponent(this.settings.file_upload_limit),"&fileQueueLimit=",encodeURIComponent(this.settings.file_queue_limit),"&debugEnabled=",encodeURIComponent(this.settings.debug_enabled),"&buttonImageURL=",encodeURIComponent(this.settings.button_image_url),"&buttonWidth=",encodeURIComponent(this.settings.button_width),"&buttonHeight=",encodeURIComponent(this.settings.button_height),"&buttonText=",encodeURIComponent(this.settings.button_text),"&buttonTextTopPadding=",encodeURIComponent(this.settings.button_text_top_padding),"&buttonTextLeftPadding=",encodeURIComponent(this.settings.button_text_left_padding),"&buttonTextStyle=",encodeURIComponent(this.settings.button_text_style),"&buttonAction=",encodeURIComponent(this.settings.button_action),"&buttonDisabled=",encodeURIComponent(this.settings.button_disabled),"&buttonCursor=",encodeURIComponent(this.settings.button_cursor)].join("")};SWFUpload.prototype.getMovieElement=function(){if(this.movieElement==undefined){this.movieElement=document.getElementById(this.movieName)}if(this.movieElement===null){throw"Could not find Flash element"}return this.movieElement};SWFUpload.prototype.buildParamString=function(){var c=this.settings.post_params;var b=[];if(typeof(c)==="object"){for(var a in c){if(c.hasOwnProperty(a)){b.push(encodeURIComponent(a.toString())+"="+encodeURIComponent(c[a].toString()))}}}return b.join("&")};SWFUpload.prototype.destroy=function(){try{this.cancelUpload(null,false);var a=null;a=this.getMovieElement();if(a&&typeof(a.CallFunction)==="unknown"){for(var c in a){try{if(typeof(a[c])==="function"){a[c]=null}}catch(e){}}try{a.parentNode.removeChild(a)}catch(b){}}window[this.movieName]=null;SWFUpload.instances[this.movieName]=null;delete SWFUpload.instances[this.movieName];this.movieElement=null;this.settings=null;this.customSettings=null;this.eventQueue=null;this.movieName=null;return true}catch(d){return false}};SWFUpload.prototype.displayDebugInfo=function(){this.debug(["---SWFUpload Instance Info---\n","Version: ",SWFUpload.version,"\n","Movie Name: ",this.movieName,"\n","Settings:\n","\t","upload_url: ",this.settings.upload_url,"\n","\t","flash_url: ",this.settings.flash_url,"\n","\t","use_query_string: ",this.settings.use_query_string.toString(),"\n","\t","requeue_on_error: ",this.settings.requeue_on_error.toString(),"\n","\t","http_success: ",this.settings.http_success.join(", "),"\n","\t","assume_success_timeout: ",this.settings.assume_success_timeout,"\n","\t","file_post_name: ",this.settings.file_post_name,"\n","\t","post_params: ",this.settings.post_params.toString(),"\n","\t","file_types: ",this.settings.file_types,"\n","\t","file_types_description: ",this.settings.file_types_description,"\n","\t","file_size_limit: ",this.settings.file_size_limit,"\n","\t","file_upload_limit: ",this.settings.file_upload_limit,"\n","\t","file_queue_limit: ",this.settings.file_queue_limit,"\n","\t","debug: ",this.settings.debug.toString(),"\n","\t","prevent_swf_caching: ",this.settings.prevent_swf_caching.toString(),"\n","\t","button_placeholder_id: ",this.settings.button_placeholder_id.toString(),"\n","\t","button_placeholder: ",(this.settings.button_placeholder?"Set":"Not Set"),"\n","\t","button_image_url: ",this.settings.button_image_url.toString(),"\n","\t","button_width: ",this.settings.button_width.toString(),"\n","\t","button_height: ",this.settings.button_height.toString(),"\n","\t","button_text: ",this.settings.button_text.toString(),"\n","\t","button_text_style: ",this.settings.button_text_style.toString(),"\n","\t","button_text_top_padding: ",this.settings.button_text_top_padding.toString(),"\n","\t","button_text_left_padding: ",this.settings.button_text_left_padding.toString(),"\n","\t","button_action: ",this.settings.button_action.toString(),"\n","\t","button_disabled: ",this.settings.button_disabled.toString(),"\n","\t","custom_settings: ",this.settings.custom_settings.toString(),"\n","Event Handlers:\n","\t","swfupload_loaded_handler assigned: ",(typeof this.settings.swfupload_loaded_handler==="function").toString(),"\n","\t","file_dialog_start_handler assigned: ",(typeof this.settings.file_dialog_start_handler==="function").toString(),"\n","\t","file_queued_handler assigned: ",(typeof this.settings.file_queued_handler==="function").toString(),"\n","\t","file_queue_error_handler assigned: ",(typeof this.settings.file_queue_error_handler==="function").toString(),"\n","\t","upload_start_handler assigned: ",(typeof this.settings.upload_start_handler==="function").toString(),"\n","\t","upload_progress_handler assigned: ",(typeof this.settings.upload_progress_handler==="function").toString(),"\n","\t","upload_error_handler assigned: ",(typeof this.settings.upload_error_handler==="function").toString(),"\n","\t","upload_success_handler assigned: ",(typeof this.settings.upload_success_handler==="function").toString(),"\n","\t","upload_complete_handler assigned: ",(typeof this.settings.upload_complete_handler==="function").toString(),"\n","\t","debug_handler assigned: ",(typeof this.settings.debug_handler==="function").toString(),"\n"].join(""))};SWFUpload.prototype.addSetting=function(b,c,a){if(c==undefined){return(this.settings[b]=a)}else{return(this.settings[b]=c)}};SWFUpload.prototype.getSetting=function(a){if(this.settings[a]!=undefined){return this.settings[a]}return""};SWFUpload.prototype.callFlash=function(functionName,argumentArray){argumentArray=argumentArray||[];var movieElement=this.getMovieElement();var returnValue,returnString;try{returnString=movieElement.CallFunction(''+__flash__argumentsToXML(argumentArray,0)+" ");returnValue=eval(returnString)}catch(ex){throw"Call to "+functionName+" failed"}if(returnValue!=undefined&&typeof returnValue.post==="object"){returnValue=this.unescapeFilePostParams(returnValue)}return returnValue};SWFUpload.prototype.selectFile=function(){this.callFlash("SelectFile")};SWFUpload.prototype.selectFiles=function(){this.callFlash("SelectFiles")};SWFUpload.prototype.startUpload=function(a){this.callFlash("StartUpload",[a])};SWFUpload.prototype.cancelUpload=function(a,b){if(b!==false){b=true}this.callFlash("CancelUpload",[a,b])};SWFUpload.prototype.stopUpload=function(){this.callFlash("StopUpload")};SWFUpload.prototype.getStats=function(){return this.callFlash("GetStats")};SWFUpload.prototype.setStats=function(a){this.callFlash("SetStats",[a])};SWFUpload.prototype.getFile=function(a){if(typeof(a)==="number"){return this.callFlash("GetFileByIndex",[a])}else{return this.callFlash("GetFile",[a])}};SWFUpload.prototype.addFileParam=function(a,b,c){return this.callFlash("AddFileParam",[a,b,c])};SWFUpload.prototype.removeFileParam=function(a,b){this.callFlash("RemoveFileParam",[a,b])};SWFUpload.prototype.setUploadURL=function(a){this.settings.upload_url=a.toString();this.callFlash("SetUploadURL",[a])};SWFUpload.prototype.setPostParams=function(a){this.settings.post_params=a;this.callFlash("SetPostParams",[a])};SWFUpload.prototype.addPostParam=function(a,b){this.settings.post_params[a]=b;this.callFlash("SetPostParams",[this.settings.post_params])};SWFUpload.prototype.removePostParam=function(a){delete this.settings.post_params[a];this.callFlash("SetPostParams",[this.settings.post_params])};SWFUpload.prototype.setFileTypes=function(a,b){this.settings.file_types=a;this.settings.file_types_description=b;this.callFlash("SetFileTypes",[a,b])};SWFUpload.prototype.setFileSizeLimit=function(a){this.settings.file_size_limit=a;this.callFlash("SetFileSizeLimit",[a])};SWFUpload.prototype.setFileUploadLimit=function(a){this.settings.file_upload_limit=a;this.callFlash("SetFileUploadLimit",[a])};SWFUpload.prototype.setFileQueueLimit=function(a){this.settings.file_queue_limit=a;this.callFlash("SetFileQueueLimit",[a])};SWFUpload.prototype.setFilePostName=function(a){this.settings.file_post_name=a;this.callFlash("SetFilePostName",[a])};SWFUpload.prototype.setUseQueryString=function(a){this.settings.use_query_string=a;this.callFlash("SetUseQueryString",[a])};SWFUpload.prototype.setRequeueOnError=function(a){this.settings.requeue_on_error=a;this.callFlash("SetRequeueOnError",[a])};SWFUpload.prototype.setHTTPSuccess=function(a){if(typeof a==="string"){a=a.replace(" ","").split(",")}this.settings.http_success=a;this.callFlash("SetHTTPSuccess",[a])};SWFUpload.prototype.setAssumeSuccessTimeout=function(a){this.settings.assume_success_timeout=a;this.callFlash("SetAssumeSuccessTimeout",[a])};SWFUpload.prototype.setDebugEnabled=function(a){this.settings.debug_enabled=a;this.callFlash("SetDebugEnabled",[a])};SWFUpload.prototype.setButtonImageURL=function(a){if(a==undefined){a=""}this.settings.button_image_url=a;this.callFlash("SetButtonImageURL",[a])};SWFUpload.prototype.setButtonDimensions=function(c,a){this.settings.button_width=c;this.settings.button_height=a;var b=this.getMovieElement();if(b!=undefined){b.style.width=c+"px";b.style.height=a+"px"}this.callFlash("SetButtonDimensions",[c,a])};SWFUpload.prototype.setButtonText=function(a){this.settings.button_text=a;this.callFlash("SetButtonText",[a])};SWFUpload.prototype.setButtonTextPadding=function(b,a){this.settings.button_text_top_padding=a;this.settings.button_text_left_padding=b;this.callFlash("SetButtonTextPadding",[b,a])};SWFUpload.prototype.setButtonTextStyle=function(a){this.settings.button_text_style=a;this.callFlash("SetButtonTextStyle",[a])};SWFUpload.prototype.setButtonDisabled=function(a){this.settings.button_disabled=a;this.callFlash("SetButtonDisabled",[a])};SWFUpload.prototype.setButtonAction=function(a){this.settings.button_action=a;this.callFlash("SetButtonAction",[a])};SWFUpload.prototype.setButtonCursor=function(a){this.settings.button_cursor=a;this.callFlash("SetButtonCursor",[a])};SWFUpload.prototype.queueEvent=function(b,c){if(c==undefined){c=[]}else{if(!(c instanceof Array)){c=[c]}}var a=this;if(typeof this.settings[b]==="function"){this.eventQueue.push(function(){this.settings[b].apply(this,c)});setTimeout(function(){a.executeNextEvent()},0)}else{if(this.settings[b]!==null){throw"Event handler "+b+" is unknown or is not a function"}}};SWFUpload.prototype.executeNextEvent=function(){var a=this.eventQueue?this.eventQueue.shift():null;if(typeof(a)==="function"){a.apply(this)}};SWFUpload.prototype.unescapeFilePostParams=function(c){var e=/[$]([0-9a-f]{4})/i;var f={};var d;if(c!=undefined){for(var a in c.post){if(c.post.hasOwnProperty(a)){d=a;var b;while((b=e.exec(d))!==null){d=d.replace(b[0],String.fromCharCode(parseInt("0x"+b[1],16)))}f[d]=c.post[a]}}c.post=f}return c};SWFUpload.prototype.testExternalInterface=function(){try{return this.callFlash("TestExternalInterface")}catch(a){return false}};SWFUpload.prototype.flashReady=function(){var a=this.getMovieElement();if(!a){this.debug("Flash called back ready but the flash movie can't be found.");return}this.cleanUp(a);this.queueEvent("swfupload_loaded_handler")};SWFUpload.prototype.cleanUp=function(a){try{if(this.movieElement&&typeof(a.CallFunction)==="unknown"){this.debug("Removing Flash functions hooks (this should only run in IE and should prevent memory leaks)");for(var c in a){try{if(typeof(a[c])==="function"){a[c]=null}}catch(b){}}}}catch(d){}window.__flash__removeCallback=function(e,f){try{if(e){e[f]=null}}catch(g){}}};SWFUpload.prototype.fileDialogStart=function(){this.queueEvent("file_dialog_start_handler")};SWFUpload.prototype.fileQueued=function(a){a=this.unescapeFilePostParams(a);this.queueEvent("file_queued_handler",a)};SWFUpload.prototype.fileQueueError=function(a,c,b){a=this.unescapeFilePostParams(a);this.queueEvent("file_queue_error_handler",[a,c,b])};SWFUpload.prototype.fileDialogComplete=function(b,c,a){this.queueEvent("file_dialog_complete_handler",[b,c,a])};SWFUpload.prototype.uploadStart=function(a){a=this.unescapeFilePostParams(a);this.queueEvent("return_upload_start_handler",a)};SWFUpload.prototype.returnUploadStart=function(a){var b;if(typeof this.settings.upload_start_handler==="function"){a=this.unescapeFilePostParams(a);b=this.settings.upload_start_handler.call(this,a)}else{if(this.settings.upload_start_handler!=undefined){throw"upload_start_handler must be a function"}}if(b===undefined){b=true}b=!!b;this.callFlash("ReturnUploadStart",[b])};SWFUpload.prototype.uploadProgress=function(a,c,b){a=this.unescapeFilePostParams(a);this.queueEvent("upload_progress_handler",[a,c,b])};SWFUpload.prototype.uploadError=function(a,c,b){a=this.unescapeFilePostParams(a);this.queueEvent("upload_error_handler",[a,c,b])};SWFUpload.prototype.uploadSuccess=function(b,a,c){b=this.unescapeFilePostParams(b);this.queueEvent("upload_success_handler",[b,a,c])};SWFUpload.prototype.uploadComplete=function(a){a=this.unescapeFilePostParams(a);this.queueEvent("upload_complete_handler",a)};SWFUpload.prototype.debug=function(a){this.queueEvent("debug_handler",a)};SWFUpload.prototype.debugMessage=function(c){if(this.settings.debug){var a,d=[];if(typeof c==="object"&&typeof c.name==="string"&&typeof c.message==="string"){for(var b in c){if(c.hasOwnProperty(b)){d.push(b+": "+c[b])}}a=d.join("\n")||"";d=a.split("\n");a="EXCEPTION: "+d.join("\nEXCEPTION: ");SWFUpload.Console.writeLine(a)}else{SWFUpload.Console.writeLine(c)}}};SWFUpload.Console={};SWFUpload.Console.writeLine=function(d){var b,a;try{b=document.getElementById("SWFUpload_Console");if(!b){a=document.createElement("form");document.getElementsByTagName("body")[0].appendChild(a);b=document.createElement("textarea");b.id="SWFUpload_Console";b.style.fontFamily="monospace";b.setAttribute("wrap","off");b.wrap="off";b.style.overflow="auto";b.style.width="700px";b.style.height="350px";b.style.margin="5px";a.appendChild(b)}b.value+=d+"\n";b.scrollTop=b.scrollHeight-b.clientHeight}catch(c){alert("Exception: "+c.name+" Message: "+c.message)}};
+
+/*
+Uploadify v3.0.0
+Copyright (c) 2010 Ronnie Garcia
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+if(jQuery)(function(a){a.extend(a.fn,{uploadify:function(b,c){a(this).each(function(){function t(b,c,d){g.queue.queueBytesUploaded+=b.size;a("#"+b.id).find(".data").html(" - "+uploadifyLang["uploadComplete"]);if(g.settings.onUploadSuccess)g.settings.onUploadSuccess(b,c,d)}function s(b){var c=new Date;g.timer=c.getTime();g.bytesLoaded=0;if(g.queue.uploadQueue.length==0){g.queue.uploadSize=b.size}if(g.settings.checkExisting!==false){a.ajax({type:"POST",async:false,url:g.settings.checkExisting,data:{filename:b.name},success:function(c){if(c==1){var d=confirm(uploadifyLang["existsMsg1"]+' "'+b.name+'" '+uploadifyLang["existsMsg2"]+".\n"+uploadifyLang["existsMsg3"]);if(!d){g.cancelUpload(b.id);a("#"+b.id).remove();if(g.queue.uploadQueue.length>0&&g.queue.queueLength>0){if(g.queue.uploadQueue[0]=="*"){g.startUpload()}else{g.startUpload(g.queue.uploadQueue.shift())}}}}}})}if(g.settings.onUploadStart)g.settings.onUploadStart(b)}function r(b,c,d){var e=new Date;var f=e.getTime();var h=f-g.timer;g.timer=f;var i=c-g.bytesLoaded;g.bytesLoaded=c;var j=g.queue.queueBytesUploaded+c;var k=Math.round(c/d*100);var l=0;var m=i/1024/(h/1e3);m=Math.floor(m*10)/10;if(g.queue.averageSpeed>0){g.queue.averageSpeed=(g.queue.averageSpeed+m)/2}else{g.queue.averageSpeed=m}if(m>1e3){l=m*.001;g.queue.averageSpeed=l}var n="KB/s";if(l>0){n="MB/s"}if(a.inArray("onUploadProgress",g.settings.skipDefault)<0){if(g.settings.progressData=="percentage"){a("#"+b.id).find(".data").html(" - "+k+"%")}else if(g.settings.progressData=="speed"){a("#"+b.id).find(".data").html(" - "+g.queue.averageSpeed.toFixed(2)+" "+n)}else if(g.settings.progressData=="all"){a("#"+b.id).find(".data").html(" - "+k+"% - "+g.queue.averageSpeed.toFixed(2)+" "+n)}a("#"+b.id).find(".uploadifyProgressBar").css("width",k+"%")}if(g.settings.onUploadProgress)g.settings.onUploadProgress(b,c,d,j,g.queue.uploadSize)}function q(b,c,d){var e=uploadifyLang["errorString1"];if(c!=SWFUpload.UPLOAD_ERROR.FILE_CANCELLED&&c!=SWFUpload.UPLOAD_ERROR.UPLOAD_STOPPED){a("#"+b.id).addClass("uploadifyError")}a("#"+b.id).find(".uploadifyProgressBar").css("width","1px");switch(c){case SWFUpload.UPLOAD_ERROR.HTTP_ERROR:e=uploadifyLang["errorString2"]+" ("+d+")";break;case SWFUpload.UPLOAD_ERROR.MISSING_UPLOAD_URL:e=uploadifyLang["errorString3"];break;case SWFUpload.UPLOAD_ERROR.IO_ERROR:e=uploadifyLang["errorString4"];break;case SWFUpload.UPLOAD_ERROR.SECURITY_ERROR:e=uploadifyLang["errorString5"];break;case SWFUpload.UPLOAD_ERROR.UPLOAD_LIMIT_EXCEEDED:alert(uploadifyLang["errorString6"]+" ("+d+").");e=uploadifyLang["errorString7"];break;case SWFUpload.UPLOAD_ERROR.UPLOAD_FAILED:e=uploadifyLang["errorString8"];break;case SWFUpload.UPLOAD_ERROR.SPECIFIED_FILE_ID_NOT_FOUND:e=uploadifyLang["errorString9"];break;case SWFUpload.UPLOAD_ERROR.FILE_VALIDATION_FAILED:e=uploadifyLang["errorString10"];break;case SWFUpload.UPLOAD_ERROR.FILE_CANCELLED:e=uploadifyLang["errorString11"];g.queue.queueSize-=b.size;if(b.status==SWFUpload.FILE_STATUS.IN_PROGRESS||a.inArray(b.id,g.queue.uploadQueue)>=0){g.queue.uploadSize-=b.size}delete g.queue.files[b.id];break;case SWFUpload.UPLOAD_ERROR.UPLOAD_STOPPED:e=uploadifyLang["errorString12"];break}if(c!=SWFUpload.UPLOAD_ERROR.SPECIFIED_FILE_ID_NOT_FOUND&&b.status!=SWFUpload.FILE_STATUS.COMPLETE){a("#"+b.id).find(".data").html(" - "+e)}if(g.settings.onUploadError)g.settings.onUploadError(b,c,d,e,g.queue)}function p(b){var c=g.getStats();g.queue.queueLength=c.files_queued;if(g.queue.uploadQueue[0]=="*"){if(g.queue.queueLength>0){g.startUpload()}else{g.queue.uploadQueue=[];if(g.settings.onQueueComplete)g.settings.onQueueComplete(c)}}else{if(g.settings.onUploadComplete)g.settings.onUploadComplete(b,g.queue);if(g.queue.queueLength>0){g.startUpload(g.queue.uploadQueue.shift())}else{g.queue.uploadQueue=[];if(g.settings.onQueueComplete)g.settings.onQueueComplete(c)}}if(a.inArray("onUploadComplete",g.settings.skipDefault)<0){if(g.settings.removeCompleted){switch(b.filestatus){case SWFUpload.FILE_STATUS.COMPLETE:setTimeout(function(){if(a("#"+b.id)){g.queue.queueSize-=b.size;delete g.queue.files[b.id];a("#"+b.id).fadeOut(500,function(){a(this).remove()})}},g.settings.removeTimeout*1e3);break;case SWFUpload.FILE_STATUS.ERROR:if(!g.settings.requeueErrors){setTimeout(function(){if(a("#"+b.id)){g.queue.queueSize-=b.size;delete g.queue.files[b.id];a("#"+b.id).fadeOut(500,function(){a(this).remove()})}},g.settings.removeTimeout*1e3)}break}}}}function o(){var a=g.getStats();if(g.settings.onQueueComplete)g.settings.onQueueComplete(a)}function n(b,c,d){if(a.inArray("onSelectError",g.settings.skipDefault)<0){switch(c){case SWFUpload.QUEUE_ERROR.QUEUE_LIMIT_EXCEEDED:if(g.settings.queueSizeLimit>d){g.queue.errorMsg+="\n"+uploadifyLang["errorMsg2"]+" ("+d+")."}else{g.queue.errorMsg+="\n"+uploadifyLang["errorMsg3"]+" ("+g.settings.queueSizeLimit+")."}break;case SWFUpload.QUEUE_ERROR.FILE_EXCEEDS_SIZE_LIMIT:g.queue.errorMsg+="\n"+uploadifyLang["errorMsg4"]+' "'+b.name+'" '+uploadifyLang["errorMsg5"]+" ("+g.settings.fileSizeLimit+").";break;case SWFUpload.QUEUE_ERROR.ZERO_BYTE_FILE:g.queue.errorMsg+="\n"+uploadifyLang["errorMsg4"]+' "'+b.name+'" '+uploadifyLang["errorMsg6"]+".";break;case SWFUpload.QUEUE_ERROR.FILE_EXCEEDS_SIZE_LIMIT:g.queue.errorMsg+="\n"+uploadifyLang["errorMsg4"]+' "'+b.name+'" '+uploadifyLang["errorMsg7"]+" ("+g.settings.fileTypeDesc+").";break}}if(c!=SWFUpload.QUEUE_ERROR.QUEUE_LIMIT_EXCEEDED){delete g.queue.files[b.id]}if(g.settings.onSelectError)g.settings.onSelectError(b,c,d)}function m(b){if(a.inArray("onSelect",g.settings.skipDefault)<0){var c={};for(var d in g.queue.files){c=g.queue.files[d];if(c.name==b.name){var e=confirm(uploadifyLang["replaceMsg1"]+' "'+b.name+'" '+uploadifyLang["replaceMsg2"]+".\n"+uploadifyLang["replaceMsg3"]);if(!e){g.cancelUpload(b.id);g.queue.filesCancelled++;return false}else{a("#"+c.id).remove();g.cancelUpload(c.id);g.queue.filesReplaced++}}}var f=Math.round(b.size/1024);var h="KB";if(f>1e3){f=Math.round(f/1e3);h="MB"}var i=f.toString().split(".");f=i[0];if(i.length>1){f+="."+i[1].substr(0,2)}f+=h;var j=b.name;if(j.length>18){j=j.substr(0,15)+"..."}a("#"+g.settings.queueID).append('\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
'+j+" ("+f+') \n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
');g.queue.queueSize+=b.size}g.queue.files[b.id]=b;if(g.settings.onSelect)g.settings.onSelect(b)}function l(){g.queue.errorMsg=uploadifyLang["errorMsg1"];g.queue.filesReplaced=0;g.queue.filesCancelled=0;if(g.settings.onDialogOpen)g.settings.onDialogOpen()}function k(b,c,d){var e=g.getStats();g.queue.filesErrored=b-c;g.queue.filesSelected=b;g.queue.filesQueued=c-g.queue.filesCancelled;g.queue.queueLength=d;if(a.inArray("onDialogClose",g.settings.skipDefault)<0){if(g.queue.filesErrored>0){alert(g.queue.errorMsg)}}if(g.settings.onDialogClose)g.settings.onDialogClose(g.queue);if(g.settings.auto)a("#"+g.settings.id).uploadifyUpload("*")}var d=a(this).clone();var e=a.extend({id:a(this).attr("id"),langFile:"uploadifyLang_en.js",swf:"uploadify.swf",uploader:"uploadify.php",auto:false,buttonCursor:"pointer",buttonText:"",cancelImage:"uploadify-cancel.png",checkExisting:"uploadify-check-existing.php",debug:false,fileObjName:"Filedata",fileSizeLimit:0,fileTypeDesc:"",fileTypeExts:"*.*",height:30,method:"post",multi:false,queueID:false,queueSizeLimit:999,removeCompleted:true,removeTimeout:3,requeueErrors:true,postData:{},preventCaching:true,progressData:"all",successTimeout:30,transparent:true,uploadLimit:999,width:120,skipDefault:[],onDialogClose:function(){},onDialogOpen:function(){},onSelect:function(){},onSelectError:function(){},onQueueComplete:function(){},onUploadComplete:function(){},onUploadError:function(){},onUploadProgress:function(){},onUploadStart:function(){},onUploadSuccess:function(){}},b);var f={assume_success_timeout:e.successTimeout,button_placeholder_id:e.id,button_width:e.width,button_height:e.height,button_text:null,button_text_style:null,button_text_top_padding:0,button_text_left_padding:0,button_action:e.multi?SWFUpload.BUTTON_ACTION.SELECT_FILES:SWFUpload.BUTTON_ACTION.SELECT_FILE,button_disabled:false,button_cursor:e.buttonCursor=="arrow"?SWFUpload.CURSOR.ARROW:SWFUpload.CURSOR.HAND,button_window_mode:e.transparent?SWFUpload.WINDOW_MODE.TRANSPARENT:SWFUpload.WINDOW_MODE.OPAQUE,debug:e.debug,requeue_on_error:e.requeueErrors,file_post_name:e.fileObjName,file_size_limit:e.fileSizeLimit,file_types:e.fileTypeExts,file_types_description:e.fileTypeDesc,file_queue_limit:e.queueSizeLimit,file_upload_limit:e.uploadLimit,flash_url:e.swf,prevent_swf_caching:e.preventCaching,post_params:e.postData,upload_url:e.uploader,use_query_string:e.method=="get",file_dialog_complete_handler:k,file_dialog_start_handler:l,file_queued_handler:m,file_queue_error_handler:n,upload_complete_handler:p,upload_error_handler:q,upload_progress_handler:r,upload_start_handler:s,upload_success_handler:t};if(c){f=a.extend(f,c)}f=a.extend(f,e);a.ajaxSetup({async:false});a.getScript(e.langFile);a.ajaxSetup({async:true});if(e.buttonText=="")e.buttonText=uploadifyLang["buttonText"];if(e.fileTypeDesc=="")e.fileTypeDesc=uploadifyLang["fileTypeDesc"];window["uploadify_"+e.id]=new SWFUpload(f);var g=window["uploadify_"+e.id];g.original=d;var h=a("
",{id:e.id,"class":"uploadify",css:{height:e.height+"px",position:"relative",width:e.width+"px"}});a("#"+g.movieName).wrap(h);if(!e.queueID){var i=a("
",{id:e.id+"_queue","class":"uploadifyQueue"});a("#"+e.id).after(i);g.settings.queueID=e.queueID=e.id+"_queue"}g.queue={files:{},filesSelected:0,filesQueued:0,filesReplaced:0,filesCancelled:0,filesErrored:0,averageSpeed:0,queueLength:0,queueSize:0,uploadSize:0,queueBytesUploaded:0,uploadQueue:[],errorMsg:uploadifyLang["errorMsg1"]};var j=a("
",{id:e.id+"_button","class":"uploadifyButton",html:''+e.buttonText+" "});a("#"+e.id).append(j);a("#"+g.movieName).css({position:"absolute","z-index":1,left:"0px"})})},uploadifyCancel:function(b){var c=a(this).selector.replace("#","");var d=window["uploadify_"+c];var e=-1;if(arguments[0]){if(arguments[0]=="*"){a("#"+d.settings.queueID).find(".uploadifyQueueItem").each(function(){e++;d.cancelUpload(a(this).attr("id"));a(this).delay(100*e).fadeOut(500,function(){a(this).remove()})});d.queue.queueSize=0}else{for(var f=0;f $image_id,
+ 'category_id' => $_POST['category_id'],
+ 'thumbnail_url' => $thumbnail_url,
+ );
+
+$output = ob_get_contents();
+ob_end_clean();
+if (!empty($output))
+{
+ add_upload_error($_POST['upload_id'], $output);
+ $return['error_message'] = $output;
+}
+
+echo json_encode($return);
+?>
\ No newline at end of file
diff --git a/sources/admin/include/uploadify/uploadify.swf b/sources/admin/include/uploadify/uploadify.swf
new file mode 100644
index 0000000..e3f7670
Binary files /dev/null and b/sources/admin/include/uploadify/uploadify.swf differ
diff --git a/sources/admin/include/uploadify/uploadifyLang_en.js b/sources/admin/include/uploadify/uploadifyLang_en.js
new file mode 100644
index 0000000..3be8afa
--- /dev/null
+++ b/sources/admin/include/uploadify/uploadifyLang_en.js
@@ -0,0 +1,32 @@
+// this file must be in UTF-8 format
+
+var uploadifyLang = new Array();
+
+uploadifyLang['buttonText'] = 'SELECT FILES';
+uploadifyLang['fileTypeDesc'] = 'All Files (*.*)';
+uploadifyLang['replaceMsg1'] = 'The file named';
+uploadifyLang['replaceMsg2'] = 'is already in the queue';
+uploadifyLang['replaceMsg3'] = 'Do you want to replace the existing item in the queue?';
+uploadifyLang['existsMsg1'] = 'A file with the name';
+uploadifyLang['existsMsg2'] = 'already exists on the server';
+uploadifyLang['existsMsg3'] = 'Would you like to replace the existing file?';
+uploadifyLang['errorMsg1'] = 'Some files were not added to the queue:';
+uploadifyLang['errorMsg2'] = 'The number of files selected exceeds the remaining upload limit';
+uploadifyLang['errorMsg3'] = 'The number of files selected exceeds the queue size limit';
+uploadifyLang['errorMsg4'] = 'The file';
+uploadifyLang['errorMsg5'] = 'exceeds the size limit';
+uploadifyLang['errorMsg6'] = 'is empty';
+uploadifyLang['errorMsg7'] = 'is not an accepted file type';
+uploadifyLang['errorString1'] = 'Error';
+uploadifyLang['errorString2'] = 'HTTP Error';
+uploadifyLang['errorString3'] = 'Missing Upload URL';
+uploadifyLang['errorString4'] = 'IO Error';
+uploadifyLang['errorString5'] = 'Security Error';
+uploadifyLang['errorString6'] = 'The upload limit has been reached';
+uploadifyLang['errorString7'] = 'Exceeds Upload Limit';
+uploadifyLang['errorString8'] = 'Failed';
+uploadifyLang['errorString9'] = 'File ID Not Found';
+uploadifyLang['errorString10'] = 'Validation Error';
+uploadifyLang['errorString11'] = 'Cancelled';
+uploadifyLang['errorString12'] = 'Stopped';
+uploadifyLang['uploadComplete'] = 'Complete';
\ No newline at end of file
diff --git a/sources/admin/index.php b/sources/admin/index.php
new file mode 100644
index 0000000..c8de97f
--- /dev/null
+++ b/sources/admin/index.php
@@ -0,0 +1,30 @@
+
diff --git a/sources/admin/intro.php b/sources/admin/intro.php
new file mode 100644
index 0000000..ee01498
--- /dev/null
+++ b/sources/admin/intro.php
@@ -0,0 +1,279 @@
+ PHPWG_VERSION);
+ $lines = @explode("\r\n", $result);
+
+ // if the current version is a BSF (development branch) build, we check
+ // the first line, for stable versions, we check the second line
+ if (preg_match('/^BSF/', $versions['current']))
+ {
+ $versions['latest'] = trim($lines[0]);
+
+ // because integer are limited to 4,294,967,296 we need to split BSF
+ // versions in date.time
+ foreach ($versions as $key => $value)
+ {
+ $versions[$key] =
+ preg_replace('/BSF_(\d{8})(\d{4})/', '$1.$2', $value);
+ }
+ }
+ else
+ {
+ $versions['latest'] = trim($lines[1]);
+ }
+
+ if ('' == $versions['latest'])
+ {
+ $page['errors'][] = l10n('Check for upgrade failed for unknown reasons.');
+ }
+ // concatenation needed to avoid automatic transformation by release
+ // script generator
+ else if ('%'.'PWGVERSION'.'%' == $versions['current'])
+ {
+ $page['infos'][] = l10n('You are running on development sources, no check possible.');
+ }
+ else if (version_compare($versions['current'], $versions['latest']) < 0)
+ {
+ $page['infos'][] = l10n('A new version of Piwigo is available.');
+ }
+ else
+ {
+ $page['infos'][] = l10n('You are running the latest version of Piwigo.');
+ }
+ }
+}
+// Show phpinfo() output
+else if (isset($_GET['action']) and 'phpinfo' == $_GET['action'])
+{
+ phpinfo();
+ exit();
+}
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+
+$template->set_filenames(array('intro' => 'intro.tpl'));
+
+if ($conf['show_newsletter_subscription']) {
+ $template->assign(
+ array(
+ 'EMAIL' => $user['email'],
+ 'SUBSCRIBE_BASE_URL' => get_newsletter_subscribe_base_url($user['language']),
+ )
+ );
+}
+
+$php_current_timestamp = date("Y-m-d H:i:s");
+$db_version = pwg_get_db_version();
+list($db_current_date) = pwg_db_fetch_row(pwg_query('SELECT now();'));
+
+$query = '
+SELECT COUNT(*)
+ FROM '.IMAGES_TABLE.'
+;';
+list($nb_elements) = pwg_db_fetch_row(pwg_query($query));
+
+$query = '
+SELECT COUNT(*)
+ FROM '.CATEGORIES_TABLE.'
+;';
+list($nb_categories) = pwg_db_fetch_row(pwg_query($query));
+
+$query = '
+SELECT COUNT(*)
+ FROM '.CATEGORIES_TABLE.'
+ WHERE dir IS NULL
+;';
+list($nb_virtual) = pwg_db_fetch_row(pwg_query($query));
+
+$query = '
+SELECT COUNT(*)
+ FROM '.CATEGORIES_TABLE.'
+ WHERE dir IS NOT NULL
+;';
+list($nb_physical) = pwg_db_fetch_row(pwg_query($query));
+
+$query = '
+SELECT COUNT(*)
+ FROM '.IMAGE_CATEGORY_TABLE.'
+;';
+list($nb_image_category) = pwg_db_fetch_row(pwg_query($query));
+
+$query = '
+SELECT COUNT(*)
+ FROM '.TAGS_TABLE.'
+;';
+list($nb_tags) = pwg_db_fetch_row(pwg_query($query));
+
+$query = '
+SELECT COUNT(*)
+ FROM '.IMAGE_TAG_TABLE.'
+;';
+list($nb_image_tag) = pwg_db_fetch_row(pwg_query($query));
+
+$query = '
+SELECT COUNT(*)
+ FROM '.USERS_TABLE.'
+;';
+list($nb_users) = pwg_db_fetch_row(pwg_query($query));
+
+$query = '
+SELECT COUNT(*)
+ FROM '.GROUPS_TABLE.'
+;';
+list($nb_groups) = pwg_db_fetch_row(pwg_query($query));
+
+$query = '
+SELECT COUNT(*)
+ FROM '.RATE_TABLE.'
+;';
+list($nb_rates) = pwg_db_fetch_row(pwg_query($query));
+
+$template->assign(
+ array(
+ 'PHPWG_URL' => PHPWG_URL,
+ 'PWG_VERSION' => PHPWG_VERSION,
+ 'OS' => PHP_OS,
+ 'PHP_VERSION' => phpversion(),
+ 'DB_ENGINE' => 'MySQL',
+ 'DB_VERSION' => $db_version,
+ 'DB_ELEMENTS' => l10n_dec('%d photo', '%d photos', $nb_elements),
+ 'DB_CATEGORIES' =>
+ l10n_dec('%d album including', '%d albums including', $nb_categories).
+ l10n_dec('%d physical', '%d physicals', $nb_physical).
+ l10n_dec(' and %d virtual', ' and %d virtuals', $nb_virtual),
+ 'DB_IMAGE_CATEGORY' => l10n_dec('%d association', '%d associations', $nb_image_category),
+ 'DB_TAGS' => l10n_dec('%d tag', '%d tags', $nb_tags),
+ 'DB_IMAGE_TAG' => l10n_dec('%d association', '%d associations', $nb_image_tag),
+ 'DB_USERS' => l10n_dec('%d user', '%d users', $nb_users),
+ 'DB_GROUPS' => l10n_dec('%d group', '%d groups', $nb_groups),
+ 'DB_RATES' => ($nb_rates == 0) ? l10n('no rate') : l10n('%d rates', $nb_rates),
+ 'U_CHECK_UPGRADE' => PHPWG_ROOT_PATH.'admin.php?action=check_upgrade',
+ 'U_PHPINFO' => PHPWG_ROOT_PATH.'admin.php?action=phpinfo',
+ 'PHP_DATATIME' => $php_current_timestamp,
+ 'DB_DATATIME' => $db_current_date,
+ )
+ );
+
+if ($conf['activate_comments'])
+{
+ $query = '
+SELECT COUNT(*)
+ FROM '.COMMENTS_TABLE.'
+;';
+ list($nb_comments) = pwg_db_fetch_row(pwg_query($query));
+ $template->assign('DB_COMMENTS', l10n_dec('%d comment', '%d comments', $nb_comments));
+}
+
+if ($nb_elements > 0)
+{
+ $query = '
+SELECT MIN(date_available)
+ FROM '.IMAGES_TABLE.'
+;';
+ list($first_date) = pwg_db_fetch_row(pwg_query($query));
+
+ $template->assign(
+ 'first_added',
+ array(
+ 'DB_DATE' =>
+ l10n('first photo added on %s', format_date($first_date))
+ )
+ );
+}
+
+// graphics library
+switch (pwg_image::get_library())
+{
+ case 'imagick':
+ $library = 'ImageMagick';
+ $img = new Imagick();
+ $version = $img->getVersion();
+ if (preg_match('/ImageMagick \d+\.\d+\.\d+-?\d*/', $version['versionString'], $match))
+ {
+ $library = $match[0];
+ }
+ $template->assign('GRAPHICS_LIBRARY', $library);
+ break;
+
+ case 'ext_imagick':
+ $library = 'External ImageMagick';
+ exec($conf['ext_imagick_dir'].'convert -version', $returnarray);
+ if (preg_match('/Version: ImageMagick (\d+\.\d+\.\d+-?\d*)/', $returnarray[0], $match))
+ {
+ $library .= ' ' . $match[1];
+ }
+ $template->assign('GRAPHICS_LIBRARY', $library);
+ break;
+
+ case 'gd':
+ $gd_info = gd_info();
+ $template->assign('GRAPHICS_LIBRARY', 'GD '.@$gd_info['GD Version']);
+ break;
+}
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'intro');
+
+// Check integrity
+$c13y = new check_integrity();
+// add internal checks
+new c13y_internal();
+// check and display
+$c13y->check();
+$c13y->display();
+
+?>
diff --git a/sources/admin/languages.php b/sources/admin/languages.php
new file mode 100644
index 0000000..c901d92
--- /dev/null
+++ b/sources/admin/languages.php
@@ -0,0 +1,48 @@
+set_id('languages');
+$tabsheet->select($page['tab']);
+$tabsheet->assign();
+
+if ($page['tab'] == 'update')
+ include(PHPWG_ROOT_PATH.'admin/updates_ext.php');
+else
+ include(PHPWG_ROOT_PATH.'admin/languages_'.$page['tab'].'.php');
+
+?>
\ No newline at end of file
diff --git a/sources/admin/languages_installed.php b/sources/admin/languages_installed.php
new file mode 100644
index 0000000..eb0a895
--- /dev/null
+++ b/sources/admin/languages_installed.php
@@ -0,0 +1,126 @@
+set_filenames(array('languages' => 'languages_installed.tpl'));
+
+$base_url = get_root_url().'admin.php?page='.$page['page'];
+
+$languages = new languages();
+$languages->get_db_languages();
+
+//--------------------------------------------------perform requested actions
+if (isset($_GET['action']) and isset($_GET['language']))
+{
+ $page['errors'] = $languages->perform_action($_GET['action'], $_GET['language']);
+
+ if (empty($page['errors']))
+ {
+ redirect($base_url);
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | start template output |
+// +-----------------------------------------------------------------------+
+$default_language = get_default_language();
+
+$tpl_languages = array();
+
+foreach($languages->fs_languages as $language_id => $language)
+{
+ $language['u_action'] = add_url_params($base_url, array('language' => $language_id));
+
+ if (in_array($language_id, array_keys($languages->db_languages)))
+ {
+ $language['state'] = 'active';
+ $language['deactivable'] = true;
+
+ if (count($languages->db_languages) <= 1)
+ {
+ $language['deactivable'] = false;
+ $language['deactivate_tooltip'] = l10n('Impossible to deactivate this language, you need at least one language.');
+ }
+
+ if ($language_id == $default_language)
+ {
+ $language['deactivable'] = false;
+ $language['deactivate_tooltip'] = l10n('Impossible to deactivate this language, first set another language as default.');
+ }
+ }
+ else
+ {
+ $language['state'] = 'inactive';
+ }
+
+ if ($language_id == $default_language)
+ {
+ $language['is_default'] = true;
+ array_unshift($tpl_languages, $language);
+ }
+ else
+ {
+ $language['is_default'] = false;
+ $tpl_languages[] = $language;
+ }
+}
+
+$template->assign(
+ array(
+ 'languages' => $tpl_languages,
+ )
+ );
+$template->append('language_states', 'active');
+$template->append('language_states', 'inactive');
+
+
+$missing_language_ids = array_diff(
+ array_keys($languages->db_languages),
+ array_keys($languages->fs_languages)
+ );
+
+foreach($missing_language_ids as $language_id)
+{
+ $query = '
+UPDATE '.USER_INFOS_TABLE.'
+ SET language = \''.get_default_language().'\'
+ WHERE language = \''.$language_id.'\'
+;';
+ pwg_query($query);
+
+ $query = '
+DELETE
+ FROM '.LANGUAGES_TABLE.'
+ WHERE id= \''.$language_id.'\'
+;';
+ pwg_query($query);
+}
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'languages');
+?>
diff --git a/sources/admin/languages_new.php b/sources/admin/languages_new.php
new file mode 100644
index 0000000..bf46cbb
--- /dev/null
+++ b/sources/admin/languages_new.php
@@ -0,0 +1,128 @@
+set_filenames(array('languages' => 'languages_new.tpl'));
+
+$base_url = get_root_url().'admin.php?page='.$page['page'].'&tab='.$page['tab'];
+
+$languages = new languages();
+$languages->get_db_languages();
+
+// +-----------------------------------------------------------------------+
+// | setup check |
+// +-----------------------------------------------------------------------+
+
+$languages_dir = PHPWG_ROOT_PATH.'language';
+if (!is_writable($languages_dir))
+{
+ $page['errors'][] = l10n('Add write access to the "%s" directory', 'language');
+}
+
+// +-----------------------------------------------------------------------+
+// | perform installation |
+// +-----------------------------------------------------------------------+
+
+if (isset($_GET['revision']))
+{
+ if (!is_webmaster())
+ {
+ $page['errors'][] = l10n('Webmaster status is required.');
+ }
+ else
+ {
+ check_pwg_token();
+
+ $install_status = $languages->extract_language_files('install', $_GET['revision']);
+
+ redirect($base_url.'&installstatus='.$install_status);
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | installation result |
+// +-----------------------------------------------------------------------+
+if (isset($_GET['installstatus']))
+{
+ switch ($_GET['installstatus'])
+ {
+ case 'ok':
+ $page['infos'][] = l10n('Language has been successfully installed');
+ break;
+
+ case 'temp_path_error':
+ $page['errors'][] = l10n('Can\'t create temporary file.');
+ break;
+
+ case 'dl_archive_error':
+ $page['errors'][] = l10n('Can\'t download archive.');
+ break;
+
+ case 'archive_error':
+ $page['errors'][] = l10n('Can\'t read or extract archive.');
+ break;
+
+ default:
+ $page['errors'][] = l10n('An error occured during extraction (%s).', htmlspecialchars($_GET['installstatus']));
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | start template output |
+// +-----------------------------------------------------------------------+
+if ($languages->get_server_languages(true))
+{
+ foreach($languages->server_languages as $language)
+ {
+ list($date, ) = explode(' ', $language['revision_date']);
+
+ $url_auto_install = htmlentities($base_url)
+ . '&revision=' . $language['revision_id']
+ . '&pwg_token='.get_pwg_token()
+ ;
+
+ $template->append('languages', array(
+ 'EXT_NAME' => $language['extension_name'],
+ 'EXT_DESC' => $language['extension_description'],
+ 'EXT_URL' => PEM_URL.'/extension_view.php?eid='.$language['extension_id'],
+ 'VERSION' => $language['revision_name'],
+ 'VER_DESC' => $language['revision_description'],
+ 'DATE' => $date,
+ 'AUTHOR' => $language['author_name'],
+ 'URL_INSTALL' => $url_auto_install,
+ 'URL_DOWNLOAD' => $language['download_url'] . '&origin=piwigo_download'));
+ }
+}
+else
+{
+ $page['errors'][] = l10n('Can\'t connect to server.');
+}
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'languages');
+?>
\ No newline at end of file
diff --git a/sources/admin/maintenance.php b/sources/admin/maintenance.php
new file mode 100644
index 0000000..0b3e142
--- /dev/null
+++ b/sources/admin/maintenance.php
@@ -0,0 +1,234 @@
+maintenance();
+ break;
+ }
+ case 'search' :
+ {
+ $query = '
+DELETE
+ FROM '.SEARCH_TABLE.'
+;';
+ pwg_query($query);
+ break;
+ }
+ case 'compiled-templates':
+ {
+ $template->delete_compiled_templates();
+ FileCombiner::clear_combined_files();
+ break;
+ }
+ case 'derivatives':
+ {
+ clear_derivative_cache($_GET['type']);
+ break;
+ }
+ default :
+ {
+ break;
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+
+$template->set_filenames(array('maintenance'=>'maintenance.tpl'));
+
+$url_format = get_root_url().'admin.php?page=maintenance&action=%s&pwg_token='.get_pwg_token();
+
+$purge_urls[l10n('All')] = sprintf($url_format, 'derivatives').'&type=all';
+foreach(ImageStdParams::get_defined_type_map() as $params)
+{
+ $purge_urls[ l10n($params->type) ] = sprintf($url_format, 'derivatives').'&type='.$params->type;
+}
+$purge_urls[ l10n(IMG_CUSTOM) ] = sprintf($url_format, 'derivatives').'&type='.IMG_CUSTOM;
+
+$template->assign(
+ array(
+ 'U_MAINT_CATEGORIES' => sprintf($url_format, 'categories'),
+ 'U_MAINT_IMAGES' => sprintf($url_format, 'images'),
+ 'U_MAINT_ORPHAN_TAGS' => sprintf($url_format, 'delete_orphan_tags'),
+ 'U_MAINT_USER_CACHE' => sprintf($url_format, 'user_cache'),
+ 'U_MAINT_HISTORY_DETAIL' => sprintf($url_format, 'history_detail'),
+ 'U_MAINT_HISTORY_SUMMARY' => sprintf($url_format, 'history_summary'),
+ 'U_MAINT_SESSIONS' => sprintf($url_format, 'sessions'),
+ 'U_MAINT_FEEDS' => sprintf($url_format, 'feeds'),
+ 'U_MAINT_DATABASE' => sprintf($url_format, 'database'),
+ 'U_MAINT_C13Y' => sprintf($url_format, 'c13y'),
+ 'U_MAINT_SEARCH' => sprintf($url_format, 'search'),
+ 'U_MAINT_COMPILED_TEMPLATES' => sprintf($url_format, 'compiled-templates'),
+ 'U_MAINT_DERIVATIVES' => sprintf($url_format, 'derivatives'),
+ 'purge_derivatives' => $purge_urls,
+ 'U_HELP' => get_root_url().'admin/popuphelp.php?page=maintenance',
+ )
+ );
+
+
+if ($conf['gallery_locked'])
+{
+ $template->assign(
+ array(
+ 'U_MAINT_UNLOCK_GALLERY' => sprintf($url_format, 'unlock_gallery'),
+ )
+ );
+}
+else
+{
+ $template->assign(
+ array(
+ 'U_MAINT_LOCK_GALLERY' => sprintf($url_format, 'lock_gallery'),
+ )
+ );
+}
+
+// +-----------------------------------------------------------------------+
+// | Define advanced features |
+// +-----------------------------------------------------------------------+
+
+$advanced_features = array();
+
+//$advanced_features is array of array composed of CAPTION & URL
+$advanced_features = trigger_event(
+ 'get_admin_advanced_features_links',
+ $advanced_features
+ );
+
+$template->assign('advanced_features', $advanced_features);
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'maintenance');
+?>
\ No newline at end of file
diff --git a/sources/admin/menubar.php b/sources/admin/menubar.php
new file mode 100644
index 0000000..69e363c
--- /dev/null
+++ b/sources/admin/menubar.php
@@ -0,0 +1,171 @@
+$pos)
+ {
+ $orders[$id] = $step * ($pos<0 ? -$crt : $crt);
+ $crt++;
+ }
+}
+
+
+global $template;
+
+include_once(PHPWG_ROOT_PATH.'include/block.class.php');
+
+$menu = new BlockManager('menubar');
+$menu->load_registered_blocks();
+$reg_blocks = $menu->get_registered_blocks();
+
+$mb_conf = @$conf[ 'blk_'.$menu->get_id() ];
+if ( is_string($mb_conf) )
+ $mb_conf = unserialize( $mb_conf );
+if ( !is_array($mb_conf) )
+ $mb_conf=array();
+
+foreach ($mb_conf as $id => $pos)
+{
+ if (!isset($reg_blocks[$id]))
+ unset($mb_conf[$id]);
+}
+
+if ( isset($_POST['reset']))
+{
+ $mb_conf = array();
+ $query = '
+UPDATE '.CONFIG_TABLE.'
+ SET value=\'\'
+ WHERE param=\'blk_'.addslashes($menu->get_id()).'\'
+ LIMIT 1';
+ pwg_query($query);
+}
+
+
+$idx=1;
+foreach ($reg_blocks as $id => $block)
+{
+ if ( !isset($mb_conf[$id]) )
+ $mb_conf[$id] = $idx*50;
+ $idx++;
+}
+
+
+if ( isset($_POST['submit']) )
+{
+ foreach ( $mb_conf as $id => $pos )
+ {
+ $hide = isset($_POST['hide_'.$id]);
+ $mb_conf[$id] = ($hide ? -1 : +1)*abs($pos);
+
+ $pos = (int)@$_POST['pos_'.$id];
+ if ($pos>0)
+ $mb_conf[$id] = $mb_conf[$id] > 0 ? $pos : -$pos;
+ }
+ make_consecutive( $mb_conf );
+
+ // BEGIN OPTIM - DONT ASK ABOUT THIS ALGO - but optimizes the size of the array we save in DB
+ /* !!! OPTIM DISABLED UNTIL IT HAS BEEN FIXED !!!
+ $reg_keys = array_keys($reg_blocks);
+ $cnf_keys = array_keys($mb_conf);
+ $best_slice = array( 'len'=>0 );
+ for ($i=0; $i$best_slice['len'])
+ {
+ $best_slice['len'] = 1+$k;
+ $best_slice['start_cnf'] = $j;
+ }
+ }
+ else
+ break;
+ }
+ }
+ }
+ */
+ $mb_conf_db = $mb_conf;
+ /*
+ if ($best_slice['len'])
+ {
+ for ($j=0; $j<$best_slice['start_cnf']; $j++ )
+ {
+ $sign = $mb_conf_db[ $cnf_keys[$j] ] > 0 ? 1 : -1;
+ $mb_conf_db[ $cnf_keys[$j] ] = $sign * ( ($best_slice['start_cnf'])*50 - ($best_slice['start_cnf']-$j) );
+ }
+ for ($j=$best_slice['start_cnf']; $j<$best_slice['start_cnf']+$best_slice['len']; $j++ )
+ {
+ if ($mb_conf_db[ $cnf_keys[$j] ] > 0)
+ unset( $mb_conf_db[ $cnf_keys[$j] ] );
+ }
+ }
+ //var_export( $best_slice ); var_export($mb_conf); var_export($mb_conf_db);
+ // END OPTIM
+ */
+ $query = '
+UPDATE '.CONFIG_TABLE.'
+ SET value=\''.addslashes(serialize($mb_conf_db)).'\'
+ WHERE param=\'blk_'.addslashes($menu->get_id()).'\'
+ ';
+ pwg_query($query);
+
+ $page['infos'][] = l10n('Order of menubar items has been updated successfully.');
+}
+
+make_consecutive( $mb_conf );
+
+foreach ($mb_conf as $id => $pos )
+{
+ $template->append( 'blocks',
+ array(
+ 'pos' => $pos/5,
+ 'reg' => $reg_blocks[$id]
+ )
+ );
+}
+
+$action = get_root_url().'admin.php?page=menubar';
+$template->assign(array('F_ACTION'=>$action));
+
+$template->set_filename( 'menubar_admin_content', 'menubar.tpl' );
+$template->assign_var_from_handle( 'ADMIN_CONTENT', 'menubar_admin_content');
+?>
diff --git a/sources/admin/notification_by_mail.php b/sources/admin/notification_by_mail.php
new file mode 100644
index 0000000..51b2846
--- /dev/null
+++ b/sources/admin/notification_by_mail.php
@@ -0,0 +1,719 @@
+ 0)
+ {
+ $inserts = array();
+ $check_key_list = array();
+
+ while ($nbm_user = pwg_db_fetch_assoc($result))
+ {
+ // Calculate key
+ $nbm_user['check_key'] = find_available_check_key();
+
+ // Save key
+ $check_key_list[] = $nbm_user['check_key'];
+
+ // Insert new nbm_users
+ $inserts[] = array(
+ 'user_id' => $nbm_user['user_id'],
+ 'check_key' => $nbm_user['check_key'],
+ 'enabled' => 'false' // By default if false, set to true with specific functions
+ );
+
+ $page['infos'][] = l10n(
+ 'User %s [%s] added.',
+ stripslashes($nbm_user['username']),
+ $nbm_user['mail_address']
+ );
+ }
+
+ // Insert new nbm_users
+ mass_inserts(USER_MAIL_NOTIFICATION_TABLE, array('user_id', 'check_key', 'enabled'), $inserts);
+ // Update field enabled with specific function
+ $check_key_treated = do_subscribe_unsubscribe_notification_by_mail
+ (
+ true,
+ $conf['nbm_default_value_user_enabled'],
+ $check_key_list
+ );
+
+ // On timeout simulate like tabsheet send
+ if ($env_nbm['is_sendmail_timeout'])
+ {
+ $quoted_check_key_list = quote_check_key_list(array_diff($check_key_list, $check_key_treated));
+ if (count($quoted_check_key_list) != 0 )
+ {
+ $query = 'delete from '.USER_MAIL_NOTIFICATION_TABLE.' where check_key in ('.implode(",", $quoted_check_key_list).');';
+ $result = pwg_query($query);
+
+ redirect($base_url.get_query_string_diff(array(), false), l10n('Operation in progress')."\n".l10n('Please wait...'));
+ }
+ }
+ }
+}
+
+/*
+ * Apply global functions to mail content
+ * return customize mail content rendered
+ */
+function render_global_customize_mail_content($customize_mail_content)
+{
+ global $conf;
+
+ if ($conf['nbm_send_html_mail'] and !(strpos($customize_mail_content, '<') === 0))
+ {
+ // On HTML mail, detects if the content are HTML format.
+ // If it's plain text format, convert content to readable HTML
+ return nl2br(htmlspecialchars($customize_mail_content));
+ }
+ else
+ {
+ return $customize_mail_content;
+ }
+}
+
+/*
+ * Send mail for notification to all users
+ * Return list of "selected" users for 'list_to_send'
+ * Return list of "treated" check_key for 'send'
+ */
+function do_action_send_mail_notification($action = 'list_to_send', $check_key_list = array(), $customize_mail_content = '')
+{
+ global $conf, $page, $user, $lang_info, $lang, $env_nbm;
+ $return_list = array();
+
+ if (in_array($action, array('list_to_send', 'send')))
+ {
+ list($dbnow) = pwg_db_fetch_row(pwg_query('SELECT NOW();'));
+
+ $is_action_send = ($action == 'send');
+
+ // disabled and null mail_address are not selected in the list
+ $data_users = get_user_notifications('send', $check_key_list);
+
+ // List all if it's define on options or on timeout
+ $is_list_all_without_test = ($env_nbm['is_sendmail_timeout'] or $conf['nbm_list_all_enabled_users_to_send']);
+
+ // Check if exist news to list user or send mails
+ if ((!$is_list_all_without_test) or ($is_action_send))
+ {
+ if (count($data_users) > 0)
+ {
+ $datas = array();
+
+ if (!isset($customize_mail_content))
+ {
+ $customize_mail_content = $conf['nbm_complementary_mail_content'];
+ }
+
+ $customize_mail_content =
+ trigger_event('nbm_render_global_customize_mail_content', $customize_mail_content);
+
+
+ // Prepare message after change language
+ if ($is_action_send)
+ {
+ $msg_break_timeout = l10n('Time to send mail is limited. Others mails are skipped.');
+ }
+ else
+ {
+ $msg_break_timeout = l10n('Prepared time for list of users to send mail is limited. Others users are not listed.');
+ }
+
+ // Begin nbm users environment
+ begin_users_env_nbm($is_action_send);
+
+ foreach ($data_users as $nbm_user)
+ {
+ if ((!$is_action_send) and check_sendmail_timeout())
+ {
+ // Stop fill list on 'list_to_send', if the quota is override
+ $page['infos'][] = $msg_break_timeout;
+ break;
+ }
+ if (($is_action_send) and check_sendmail_timeout())
+ {
+ // Stop fill list on 'send', if the quota is override
+ $page['errors'][] = $msg_break_timeout;
+ break;
+ }
+
+ // set env nbm user
+ set_user_on_env_nbm($nbm_user, $is_action_send);
+
+ if ($is_action_send)
+ {
+ set_make_full_url();
+ // Fill return list of "treated" check_key for 'send'
+ $return_list[] = $nbm_user['check_key'];
+
+ if ($conf['nbm_send_detailed_content'])
+ {
+ $news = news($nbm_user['last_send'], $dbnow, false, $conf['nbm_send_html_mail']);
+ $exist_data = count($news) > 0;
+ }
+ else
+ {
+ $exist_data = news_exists($nbm_user['last_send'], $dbnow);
+ }
+
+ if ($exist_data)
+ {
+ $subject = '['.$conf['gallery_title'].'] '.l10n('New photos added');
+
+ // Assign current var for nbm mail
+ assign_vars_nbm_mail_content($nbm_user);
+
+ if (!is_null($nbm_user['last_send']))
+ {
+ $env_nbm['mail_template']->assign
+ (
+ 'content_new_elements_between',
+ array
+ (
+ 'DATE_BETWEEN_1' => $nbm_user['last_send'],
+ 'DATE_BETWEEN_2' => $dbnow,
+ )
+ );
+ }
+ else
+ {
+ $env_nbm['mail_template']->assign
+ (
+ 'content_new_elements_single',
+ array
+ (
+ 'DATE_SINGLE' => $dbnow,
+ )
+ );
+ }
+
+ if ($conf['nbm_send_detailed_content'])
+ {
+ $env_nbm['mail_template']->assign('global_new_lines', $news);
+ }
+
+ $nbm_user_customize_mail_content =
+ trigger_event('nbm_render_user_customize_mail_content',
+ $customize_mail_content, $nbm_user);
+ if (!empty($nbm_user_customize_mail_content))
+ {
+ $env_nbm['mail_template']->assign
+ (
+ 'custom_mail_content', $nbm_user_customize_mail_content
+ );
+ }
+
+ if ($conf['nbm_send_html_mail'] and $conf['nbm_send_recent_post_dates'])
+ {
+ $recent_post_dates = get_recent_post_dates_array(
+ $conf['recent_post_dates']['NBM']);
+ foreach ($recent_post_dates as $date_detail)
+ {
+ $env_nbm['mail_template']->append
+ (
+ 'recent_posts',
+ array
+ (
+ 'TITLE' => get_title_recent_post_date($date_detail),
+ 'HTML_DATA' => get_html_description_recent_post_date($date_detail)
+ )
+ );
+ }
+ }
+
+ $env_nbm['mail_template']->assign
+ (
+ array
+ (
+ 'GOTO_GALLERY_TITLE' => $conf['gallery_title'],
+ 'GOTO_GALLERY_URL' => get_gallery_home_url(),
+ 'SEND_AS_NAME' => $env_nbm['send_as_name'],
+ )
+ );
+
+ $ret = pwg_mail(
+ array(
+ 'name' => stripslashes($nbm_user['username']),
+ 'email' => $nbm_user['mail_address'],
+ ),
+ array(
+ 'from' => $env_nbm['send_as_mail_formated'],
+ 'subject' => $subject,
+ 'email_format' => $env_nbm['email_format'],
+ 'content' => $env_nbm['mail_template']->parse('notification_by_mail', true),
+ 'content_format' => $env_nbm['email_format'],
+ )
+ );
+
+ if ($ret)
+ {
+ inc_mail_sent_success($nbm_user);
+
+ $datas[] = array(
+ 'user_id' => $nbm_user['user_id'],
+ 'last_send' => $dbnow
+ );
+ }
+ else
+ {
+ inc_mail_sent_failed($nbm_user);
+ }
+
+ unset_make_full_url();
+ }
+ }
+ else
+ {
+ if (news_exists($nbm_user['last_send'], $dbnow))
+ {
+ // Fill return list of "selected" users for 'list_to_send'
+ $return_list[] = $nbm_user;
+ }
+ }
+
+ // unset env nbm user
+ unset_user_on_env_nbm();
+ }
+
+ // Restore nbm environment
+ end_users_env_nbm();
+
+ if ($is_action_send)
+ {
+ mass_updates(
+ USER_MAIL_NOTIFICATION_TABLE,
+ array(
+ 'primary' => array('user_id'),
+ 'update' => array('last_send')
+ ),
+ $datas
+ );
+
+ display_counter_info();
+ }
+ }
+ else
+ {
+ if ($is_action_send)
+ {
+ $page['errors'][] = l10n('No user to send notifications by mail.');
+ }
+ }
+ }
+ else
+ {
+ // Quick List, don't check news
+ // Fill return list of "selected" users for 'list_to_send'
+ $return_list = $data_users;
+ }
+ }
+
+ // Return list of "selected" users for 'list_to_send'
+ // Return list of "treated" check_key for 'send'
+ return $return_list;
+}
+
+// +-----------------------------------------------------------------------+
+// | Main |
+// +-----------------------------------------------------------------------+
+if (!isset($_GET['mode']))
+{
+ $page['mode'] = 'send';
+}
+else
+{
+ $page['mode'] = $_GET['mode'];
+}
+
+// +-----------------------------------------------------------------------+
+// | Check Access and exit when user status is not ok |
+// +-----------------------------------------------------------------------+
+check_status(get_tab_status($page['mode']));
+
+
+// +-----------------------------------------------------------------------+
+// | Add event handler |
+// +-----------------------------------------------------------------------+
+add_event_handler('nbm_render_global_customize_mail_content', 'render_global_customize_mail_content');
+trigger_action('nbm_event_handler_added');
+
+
+// +-----------------------------------------------------------------------+
+// | Insert new users with mails |
+// +-----------------------------------------------------------------------+
+if (!isset($_POST) or (count($_POST) ==0))
+{
+ // No insert data in post mode
+ insert_new_data_user_mail_notification();
+}
+
+// +-----------------------------------------------------------------------+
+// | Treatment of tab post |
+// +-----------------------------------------------------------------------+
+switch ($page['mode'])
+{
+ case 'param' :
+ {
+ if (isset($_POST['param_submit']))
+ {
+ $updated_param_count = 0;
+ // Update param
+ $result = pwg_query('select param, value from '.CONFIG_TABLE.' where param like \'nbm\\_%\'');
+ while ($nbm_user = pwg_db_fetch_assoc($result))
+ {
+ if (isset($_POST[$nbm_user['param']]))
+ {
+ $value = $_POST[$nbm_user['param']];
+
+ $query = '
+update
+'.CONFIG_TABLE.'
+set
+ value = \''. str_replace("\'", "''", $value).'\'
+where
+ param = \''.$nbm_user['param'].'\';';
+ pwg_query($query);
+ $updated_param_count += 1;
+ }
+ }
+
+ $page['infos'][] = l10n_dec(
+ '%d parameter was updated.', '%d parameters were updated.',
+ $updated_param_count
+ );
+
+ // Reload conf with new values
+ load_conf_from_db('param like \'nbm\\_%\'');
+ }
+ }
+ case 'subscribe' :
+ {
+ if (isset($_POST['falsify']) and isset($_POST['cat_true']))
+ {
+ $check_key_treated = unsubscribe_notification_by_mail(true, $_POST['cat_true']);
+ do_timeout_treatment('cat_true', $check_key_treated);
+ }
+ else
+ if (isset($_POST['trueify']) and isset($_POST['cat_false']))
+ {
+ $check_key_treated = subscribe_notification_by_mail(true, $_POST['cat_false']);
+ do_timeout_treatment('cat_false', $check_key_treated);
+ }
+ break;
+ }
+
+ case 'send' :
+ {
+ if (isset($_POST['send_submit']) and isset($_POST['send_selection']) and isset($_POST['send_customize_mail_content']))
+ {
+ $check_key_treated = do_action_send_mail_notification('send', $_POST['send_selection'], stripslashes($_POST['send_customize_mail_content']));
+ do_timeout_treatment('send_selection', $check_key_treated);
+ }
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | template initialization |
+// +-----------------------------------------------------------------------+
+$template->set_filenames
+(
+ array
+ (
+ 'double_select' => 'double_select.tpl',
+ 'notification_by_mail'=>'notification_by_mail.tpl'
+ )
+);
+
+$template->assign
+(
+ array
+ (
+ 'U_HELP' => get_root_url().'admin/popuphelp.php?page=notification_by_mail',
+ 'F_ACTION'=> $base_url.get_query_string_diff(array())
+ )
+);
+
+if (is_autorize_status(ACCESS_WEBMASTER))
+{
+ // TabSheet
+ $tabsheet = new tabsheet();
+ $tabsheet->set_id('nbm');
+ $tabsheet->select($page['mode']);
+ $tabsheet->assign();
+}
+
+if ($must_repost)
+{
+ // Get name of submit button
+ $repost_submit_name = '';
+ if (isset($_POST['falsify']))
+ {
+ $repost_submit_name = 'falsify';
+ }
+ elseif (isset($_POST['trueify']))
+ {
+ $repost_submit_name = 'trueify';
+ }
+ elseif (isset($_POST['send_submit']))
+ {
+ $repost_submit_name = 'send_submit';
+ }
+
+ $template->assign('REPOST_SUBMIT_NAME', $repost_submit_name);
+}
+
+switch ($page['mode'])
+{
+ case 'param' :
+ {
+ $template->assign(
+ $page['mode'],
+ array(
+ 'SEND_HTML_MAIL' => $conf['nbm_send_html_mail'],
+ 'SEND_MAIL_AS' => $conf['nbm_send_mail_as'],
+ 'SEND_DETAILED_CONTENT' => $conf['nbm_send_detailed_content'],
+ 'COMPLEMENTARY_MAIL_CONTENT' => $conf['nbm_complementary_mail_content'],
+ 'SEND_RECENT_POST_DATES' => $conf['nbm_send_recent_post_dates'],
+ ));
+ break;
+ }
+
+ case 'subscribe' :
+ {
+ $template->assign( $page['mode'], true );
+
+ $template->assign(
+ array(
+ 'L_CAT_OPTIONS_TRUE' => l10n('Subscribed'),
+ 'L_CAT_OPTIONS_FALSE' => l10n('Unsubscribed')
+ )
+ );
+
+ $data_users = get_user_notifications('subscribe');
+
+ $opt_true = array();
+ $opt_true_selected = array();
+ $opt_false = array();
+ $opt_false_selected = array();
+ foreach ($data_users as $nbm_user)
+ {
+ if (get_boolean($nbm_user['enabled']))
+ {
+ $opt_true[ $nbm_user['check_key'] ] = stripslashes($nbm_user['username']).'['.$nbm_user['mail_address'].']';
+ if ((isset($_POST['falsify']) and isset($_POST['cat_true']) and in_array($nbm_user['check_key'], $_POST['cat_true'])))
+ {
+ $opt_true_selected[] = $nbm_user['check_key'];
+ }
+ }
+ else
+ {
+ $opt_false[ $nbm_user['check_key'] ] = stripslashes($nbm_user['username']).'['.$nbm_user['mail_address'].']';
+ if (isset($_POST['trueify']) and isset($_POST['cat_false']) and in_array($nbm_user['check_key'], $_POST['cat_false']))
+ {
+ $opt_false_selected[] = $nbm_user['check_key'];
+ }
+ }
+ }
+ $template->assign( array(
+ 'category_option_true' => $opt_true,
+ 'category_option_true_selected' => $opt_true_selected,
+ 'category_option_false' => $opt_false,
+ 'category_option_false_selected' => $opt_false_selected,
+ )
+ );
+ $template->assign_var_from_handle('DOUBLE_SELECT', 'double_select');
+ break;
+ }
+
+ case 'send' :
+ {
+ $tpl_var = array('users'=> array() );
+
+ $data_users = do_action_send_mail_notification('list_to_send');
+
+ $tpl_var['CUSTOMIZE_MAIL_CONTENT'] =
+ isset($_POST['send_customize_mail_content'])
+ ? stripslashes($_POST['send_customize_mail_content'])
+ : $conf['nbm_complementary_mail_content'];
+
+ if (count($data_users))
+ {
+ foreach ($data_users as $nbm_user)
+ {
+ if (
+ (!$must_repost) or // Not timeout, normal treatment
+ (($must_repost) and in_array($nbm_user['check_key'], $_POST['send_selection'])) // Must be repost, show only user to send
+ )
+ {
+ $tpl_var['users'][] =
+ array(
+ 'ID' => $nbm_user['check_key'],
+ 'CHECKED' => ( // not check if not selected, on init select stripslashes($nbm_user['username']),
+ 'EMAIL' => $nbm_user['mail_address'],
+ 'LAST_SEND'=> $nbm_user['last_send']
+ );
+ }
+ }
+ }
+ $template->assign($page['mode'], $tpl_var);
+ break;
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | Sending html code |
+// +-----------------------------------------------------------------------+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'notification_by_mail');
+
+?>
diff --git a/sources/admin/permalinks.php b/sources/admin/permalinks.php
new file mode 100644
index 0000000..72f9513
--- /dev/null
+++ b/sources/admin/permalinks.php
@@ -0,0 +1,189 @@
+ $value)
+ {
+ if (!in_array($key, $get_rejects) and $key!=$get_param)
+ {
+ $base_url .= $is_first ? '?' : '&';
+ $is_first = false;
+ $base_url .= $key.'='.urlencode($value);
+ }
+ }
+
+ $ret = array();
+ foreach( $sortable_by as $field)
+ {
+ $url = $base_url;
+ $disp = '↓'; // TODO: an small image is better
+
+ if ( $field !== @$_GET[$get_param] )
+ {
+ if ( !isset($default_field) or $default_field!=$field )
+ { // the first should be the default
+ $url = add_url_params($url, array($get_param=>$field) );
+ }
+ elseif (isset($default_field) and !isset($_GET[$get_param]) )
+ {
+ $ret[] = $field;
+ $disp = ''.$disp.' ';
+ }
+ }
+ else
+ {
+ $ret[] = $field;
+ $disp = ''.$disp.' ';
+ }
+ if ( isset($template_var) )
+ {
+ $template->assign( $template_var.strtoupper($field),
+ ''.$disp.' '
+ );
+ }
+ }
+ return $ret;
+}
+
+if (!defined('PHPWG_ROOT_PATH')) die('Hacking attempt!');
+
+include_once(PHPWG_ROOT_PATH.'admin/include/functions_permalinks.php');
+
+$selected_cat = array();
+if ( isset($_POST['set_permalink']) and $_POST['cat_id']>0 )
+{
+ $permalink = $_POST['permalink'];
+ if ( empty($permalink) )
+ delete_cat_permalink($_POST['cat_id'], isset($_POST['save']) );
+ else
+ set_cat_permalink($_POST['cat_id'], $permalink, isset($_POST['save']) );
+ $selected_cat = array( $_POST['cat_id'] );
+}
+elseif ( isset($_GET['delete_permanent']) )
+{
+ $query = '
+DELETE FROM '.OLD_PERMALINKS_TABLE.'
+ WHERE permalink=\''.$_GET['delete_permanent'].'\'
+ LIMIT 1';
+ $result = pwg_query($query);
+ if (pwg_db_changes($result)==0)
+ {
+ $page['errors'][] = l10n('Cannot delete the old permalink !');
+ }
+}
+
+
+$template->set_filename('permalinks', 'permalinks.tpl' );
+
+// +-----------------------------------------------------------------------+
+// | tabs |
+// +-----------------------------------------------------------------------+
+
+$page['tab'] = 'permalinks';
+include(PHPWG_ROOT_PATH.'admin/include/albums_tab.inc.php');
+
+
+$query = '
+SELECT
+ id, permalink,
+ CONCAT(id, " - ", name, IF(permalink IS NULL, "", " √") ) AS name,
+ uppercats, global_rank
+FROM '.CATEGORIES_TABLE;
+
+display_select_cat_wrapper( $query, $selected_cat, 'categories', false );
+
+
+// --- generate display of active permalinks -----------------------------------
+$sort_by = parse_sort_variables(
+ array('id', 'name', 'permalink'), 'name',
+ 'psf',
+ array('delete_permanent'),
+ 'SORT_' );
+
+$query = '
+SELECT id, permalink, uppercats, global_rank
+ FROM '.CATEGORIES_TABLE.'
+ WHERE permalink IS NOT NULL
+';
+if ( $sort_by[0]=='id' or $sort_by[0]=='permalink' )
+{
+ $query .= ' ORDER BY '.$sort_by[0];
+}
+$categories=array();
+$result=pwg_query($query);
+while ( $row = pwg_db_fetch_assoc($result) )
+{
+ $row['name'] = get_cat_display_name_cache( $row['uppercats'] );
+ $categories[] = $row;
+}
+
+if ( $sort_by[0]=='name')
+{
+ usort($categories, 'global_rank_compare');
+}
+$template->assign( 'permalinks', $categories );
+
+// --- generate display of old permalinks --------------------------------------
+
+$sort_by = parse_sort_variables(
+ array('cat_id','permalink','date_deleted','last_hit','hit'), null,
+ 'dpsf',
+ array('delete_permanent'),
+ 'SORT_OLD_', '#old_permalinks' );
+
+$url_del_base = get_root_url().'admin.php?page=permalinks';
+$query = 'SELECT * FROM '.OLD_PERMALINKS_TABLE;
+if ( count($sort_by) )
+{
+ $query .= ' ORDER BY '.$sort_by[0];
+}
+$result = pwg_query($query);
+$deleted_permalinks=array();
+while ( $row = pwg_db_fetch_assoc($result) )
+{
+ $row['name'] = get_cat_display_name_cache($row['cat_id']);
+ $row['U_DELETE'] =
+ add_url_params(
+ $url_del_base,
+ array( 'delete_permanent'=> $row['permalink'] )
+ );
+ $deleted_permalinks[] = $row;
+}
+$template->assign('deleted_permalinks', $deleted_permalinks);
+$template->assign('U_HELP', get_root_url().'admin/popuphelp.php?page=permalinks');
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'permalinks');
+?>
diff --git a/sources/admin/photo.php b/sources/admin/photo.php
new file mode 100644
index 0000000..b99a76c
--- /dev/null
+++ b/sources/admin/photo.php
@@ -0,0 +1,84 @@
+set_id('photo');
+$tabsheet->select($page['tab']);
+$tabsheet->assign();
+
+// +-----------------------------------------------------------------------+
+// | Load the tab |
+// +-----------------------------------------------------------------------+
+
+if ('properties' == $page['tab'])
+{
+ include(PHPWG_ROOT_PATH.'admin/picture_modify.php');
+}
+elseif ('coi' == $page['tab'])
+{
+ include(PHPWG_ROOT_PATH.'admin/picture_coi.php');
+}
+else
+{
+ include(PHPWG_ROOT_PATH.'admin/photo_'.$page['tab'].'.php');
+}
+?>
\ No newline at end of file
diff --git a/sources/admin/photos_add.php b/sources/admin/photos_add.php
new file mode 100644
index 0000000..72bf991
--- /dev/null
+++ b/sources/admin/photos_add.php
@@ -0,0 +1,89 @@
+set_id('photos_add');
+$tabsheet->select($page['tab']);
+$tabsheet->assign();
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+
+$template->set_filenames(
+ array(
+ 'photos_add' => 'photos_add_'.$page['tab'].'.tpl'
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | Load the tab |
+// +-----------------------------------------------------------------------+
+
+include(PHPWG_ROOT_PATH.'admin/photos_add_'.$page['tab'].'.php');
+?>
\ No newline at end of file
diff --git a/sources/admin/photos_add_applications.php b/sources/admin/photos_add_applications.php
new file mode 100644
index 0000000..82c95fd
--- /dev/null
+++ b/sources/admin/photos_add_applications.php
@@ -0,0 +1,47 @@
+assign(
+ array(
+ 'URL_DOWNLOAD_WINDOWS' => $conf['ploader_download_windows'],
+ 'URL_DOWNLOAD_MAC' => $conf['ploader_download_mac'],
+ 'URL_DOWNLOAD_LINUX' => $conf['ploader_download_linux'],
+ )
+ );
+
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'photos_add');
+?>
\ No newline at end of file
diff --git a/sources/admin/photos_add_direct.php b/sources/admin/photos_add_direct.php
new file mode 100644
index 0000000..10f71e0
--- /dev/null
+++ b/sources/admin/photos_add_direct.php
@@ -0,0 +1,77 @@
+ $user['id'],
+ 'element_id' => $image_id,
+ );
+ }
+ mass_inserts(
+ CADDIE_TABLE,
+ array_keys($inserts[0]),
+ $inserts
+ );
+
+ redirect(get_root_url().'admin.php?page=batch_manager&filter=prefilter-caddie');
+}
+
+// +-----------------------------------------------------------------------+
+// | process form |
+// +-----------------------------------------------------------------------+
+
+include_once(PHPWG_ROOT_PATH.'admin/include/photos_add_direct_process.inc.php');
+
+// +-----------------------------------------------------------------------+
+// | prepare form |
+// +-----------------------------------------------------------------------+
+
+include_once(PHPWG_ROOT_PATH.'admin/include/photos_add_direct_prepare.inc.php');
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'photos_add');
+?>
diff --git a/sources/admin/photos_add_ftp.php b/sources/admin/photos_add_ftp.php
new file mode 100644
index 0000000..5655d0f
--- /dev/null
+++ b/sources/admin/photos_add_ftp.php
@@ -0,0 +1,47 @@
+assign(
+ 'FTP_HELP_CONTENT',
+ load_language(
+ 'help/photos_add_ftp.html',
+ '',
+ array('return'=>true)
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'photos_add');
+?>
\ No newline at end of file
diff --git a/sources/admin/picture_coi.php b/sources/admin/picture_coi.php
new file mode 100644
index 0000000..6ad8fa9
--- /dev/null
+++ b/sources/admin/picture_coi.php
@@ -0,0 +1,113 @@
+sizing->max_crop != 0)
+ {
+ delete_element_derivatives($row, $params->type);
+ }
+ }
+ delete_element_derivatives($row, IMG_CUSTOM);
+ $uid = '&b='.time();
+ $conf['question_mark_in_urls'] = $conf['php_extension_in_urls'] = true;
+ if ($conf['derivative_url_style']==1)
+ {
+ $conf['derivative_url_style']=0; //auto
+ }
+}
+else
+{
+ $uid = '';
+}
+
+$tpl_var = array(
+ 'TITLE' => render_element_name($row),
+ 'ALT' => $row['file'],
+ 'U_IMG' => DerivativeImage::url(IMG_LARGE, $row),
+ );
+
+if (!empty($row['coi']))
+{
+ $tpl_var['coi'] = array(
+ 'l'=> char_to_fraction($row['coi'][0]),
+ 't'=> char_to_fraction($row['coi'][1]),
+ 'r'=> char_to_fraction($row['coi'][2]),
+ 'b'=> char_to_fraction($row['coi'][3]),
+ );
+}
+
+foreach(ImageStdParams::get_defined_type_map() as $params)
+{
+ if ($params->sizing->max_crop != 0)
+ {
+ $derivative = new DerivativeImage($params, new SrcImage($row) );
+ $template->append( 'cropped_derivatives', array(
+ 'U_IMG' => $derivative->get_url().$uid,
+ 'HTM_SIZE' => $derivative->get_size_htm(),
+ ) );
+ }
+}
+
+
+$template->assign($tpl_var);
+$template->set_filename('picture_coi', 'picture_coi.tpl');
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'picture_coi');
+?>
diff --git a/sources/admin/picture_modify.php b/sources/admin/picture_modify.php
new file mode 100644
index 0000000..23b28b7
--- /dev/null
+++ b/sources/admin/picture_modify.php
@@ -0,0 +1,484 @@
+ get_cat_info($_GET['cat_id'])
+ )
+ )
+ );
+ }
+
+ $query = '
+SELECT category_id
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ WHERE image_id = '.$_GET['image_id'].'
+;';
+
+ $authorizeds = array_diff(
+ array_from_query($query, 'category_id'),
+ explode(',', calculate_permissions($user['id'], $user['status']))
+ );
+
+ foreach ($authorizeds as $category_id)
+ {
+ redirect(
+ make_index_url(
+ array(
+ 'category' => get_cat_info($category_id)
+ )
+ )
+ );
+ }
+
+ redirect(make_index_url());
+}
+
+// +-----------------------------------------------------------------------+
+// | synchronize metadata |
+// +-----------------------------------------------------------------------+
+
+if (isset($_GET['sync_metadata']))
+{
+ sync_metadata(array( intval($_GET['image_id'])));
+ $page['infos'][] = l10n('Metadata synchronized from file');
+}
+
+//--------------------------------------------------------- update informations
+
+// first, we verify whether there is a mistake on the given creation date
+if (isset($_POST['date_creation_action'])
+ and 'set' == $_POST['date_creation_action'])
+{
+ if (!is_numeric($_POST['date_creation_year'])
+ or !checkdate(
+ $_POST['date_creation_month'],
+ $_POST['date_creation_day'],
+ $_POST['date_creation_year'])
+ )
+ {
+ $page['errors'][] = l10n('wrong date');
+ }
+}
+
+if (isset($_POST['submit']) and count($page['errors']) == 0)
+{
+ $data = array();
+ $data{'id'} = $_GET['image_id'];
+ $data{'name'} = $_POST['name'];
+ $data{'author'} = $_POST['author'];
+ $data['level'] = $_POST['level'];
+
+ if ($conf['allow_html_descriptions'])
+ {
+ $data{'comment'} = @$_POST['description'];
+ }
+ else
+ {
+ $data{'comment'} = strip_tags(@$_POST['description']);
+ }
+
+ if (!empty($_POST['date_creation_year']))
+ {
+ $data{'date_creation'} =
+ $_POST['date_creation_year']
+ .'-'.$_POST['date_creation_month']
+ .'-'.$_POST['date_creation_day']
+ .' '.$_POST['date_creation_time'];
+ }
+ else
+ {
+ $data{'date_creation'} = null;
+ }
+
+ $data = trigger_change('picture_modify_before_update', $data);
+
+ single_update(
+ IMAGES_TABLE,
+ $data,
+ array('id' => $data['id'])
+ );
+
+ // time to deal with tags
+ $tag_ids = array();
+ if (!empty($_POST['tags']))
+ {
+ $tag_ids = get_tag_ids($_POST['tags']);
+ }
+ set_tags($tag_ids, $_GET['image_id']);
+
+ // association to albums
+ if (!isset($_POST['associate']))
+ {
+ $_POST['associate'] = array();
+ }
+ move_images_to_categories(array($_GET['image_id']), $_POST['associate']);
+
+ invalidate_user_cache();
+
+ // thumbnail for albums
+ if (!isset($_POST['represent']))
+ {
+ $_POST['represent'] = array();
+ }
+
+ $no_longer_thumbnail_for = array_diff($represent_options_selected, $_POST['represent']);
+ if (count($no_longer_thumbnail_for) > 0)
+ {
+ set_random_representant($no_longer_thumbnail_for);
+ }
+
+ $new_thumbnail_for = array_diff($_POST['represent'], $represent_options_selected);
+ if (count($new_thumbnail_for) > 0)
+ {
+ $query = '
+UPDATE '.CATEGORIES_TABLE.'
+ SET representative_picture_id = '.$_GET['image_id'].'
+ WHERE id IN ('.implode(',', $new_thumbnail_for).')
+;';
+ pwg_query($query);
+ }
+
+ $represent_options_selected = $_POST['represent'];
+
+ $page['infos'][] = l10n('Photo informations updated');
+}
+
+// tags
+$query = '
+SELECT
+ id,
+ name
+ FROM '.IMAGE_TAG_TABLE.' AS it
+ JOIN '.TAGS_TABLE.' AS t ON t.id = it.tag_id
+ WHERE image_id = '.$_GET['image_id'].'
+;';
+$tag_selection = get_taglist($query);
+
+$query = '
+SELECT
+ id,
+ name
+ FROM '.TAGS_TABLE.'
+;';
+$tags = get_taglist($query, false);
+
+// retrieving direct information about picture
+$query = '
+SELECT *
+ FROM '.IMAGES_TABLE.'
+ WHERE id = '.$_GET['image_id'].'
+;';
+$row = pwg_db_fetch_assoc(pwg_query($query));
+
+$storage_category_id = null;
+if (!empty($row['storage_category_id']))
+{
+ $storage_category_id = $row['storage_category_id'];
+}
+
+$image_file = $row['file'];
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+
+$template->set_filenames(
+ array(
+ 'picture_modify' => 'picture_modify.tpl'
+ )
+ );
+
+$admin_url_start = $admin_photo_base_url.'-properties';
+$admin_url_start.= isset($_GET['cat_id']) ? '&cat_id='.$_GET['cat_id'] : '';
+
+$template->assign(
+ array(
+ 'tag_selection' => $tag_selection,
+ 'tags' => $tags,
+ 'U_SYNC' => $admin_url_start.'&sync_metadata=1',
+ 'U_DELETE' => $admin_url_start.'&delete=1&pwg_token='.get_pwg_token(),
+
+ 'PATH'=>$row['path'],
+
+ 'TN_SRC' => DerivativeImage::thumb_url($row),
+
+ 'NAME' =>
+ isset($_POST['name']) ?
+ stripslashes($_POST['name']) : @$row['name'],
+
+ 'TITLE' => render_element_name($row),
+
+ 'DIMENSIONS' => @$row['width'].' * '.@$row['height'],
+
+ 'FILESIZE' => @$row['filesize'].' KB',
+
+ 'REGISTRATION_DATE' => format_date($row['date_available']),
+
+ 'AUTHOR' => htmlspecialchars(
+ isset($_POST['author'])
+ ? stripslashes($_POST['author'])
+ : @$row['author']
+ ),
+
+ 'DESCRIPTION' =>
+ htmlspecialchars( isset($_POST['description']) ?
+ stripslashes($_POST['description']) : @$row['comment'] ),
+
+ 'F_ACTION' =>
+ get_root_url().'admin.php'
+ .get_query_string_diff(array('sync_metadata'))
+ )
+ );
+
+$added_by = 'N/A';
+$query = '
+SELECT '.$conf['user_fields']['username'].' AS username
+ FROM '.USERS_TABLE.'
+ WHERE '.$conf['user_fields']['id'].' = '.$row['added_by'].'
+;';
+$result = pwg_query($query);
+while ($user_row = pwg_db_fetch_assoc($result))
+{
+ $row['added_by'] = $user_row['username'];
+}
+
+$intro_vars = array(
+ 'file' => l10n('Original file : %s', $row['file']),
+ 'add_date' => l10n('Posted %s on %s', time_since($row['date_available'], 'year'), format_date($row['date_available'], false, false)),
+ 'added_by' => l10n('Added by %s', $row['added_by']),
+ 'size' => $row['width'].'×'.$row['height'].' pixels, '.sprintf('%.2f', $row['filesize']/1024).'MB',
+ 'stats' => l10n('Visited %d times', $row['hit']),
+ 'id' => l10n('Numeric identifier : %d', $row['id']),
+ );
+
+if ($conf['rate'] and !empty($row['rating_score']))
+{
+ $query = '
+SELECT
+ COUNT(*)
+ FROM '.RATE_TABLE.'
+ WHERE element_id = '.$_GET['image_id'].'
+;';
+ list($row['nb_rates']) = pwg_db_fetch_row(pwg_query($query));
+
+ $intro_vars['stats'].= ', '.sprintf(l10n('Rated %d times, score : %.2f'), $row['nb_rates'], $row['rating_score']);
+}
+
+$template->assign('INTRO', $intro_vars);
+
+
+if (in_array(get_extension($row['path']),$conf['picture_ext']))
+{
+ $template->assign('U_COI', get_root_url().'admin.php?page=picture_coi&image_id='.$_GET['image_id']);
+}
+
+// image level options
+$selected_level = isset($_POST['level']) ? $_POST['level'] : $row['level'];
+$template->assign(
+ array(
+ 'level_options'=> get_privacy_level_options(),
+ 'level_options_selected' => array($selected_level)
+ )
+ );
+
+// creation date
+unset($day, $month, $year);
+
+if (isset($_POST['date_creation_action'])
+ and 'set' == $_POST['date_creation_action'])
+{
+ foreach (array('day', 'month', 'year', 'time') as $varname)
+ {
+ $$varname = $_POST['date_creation_'.$varname];
+ }
+}
+else if (isset($row['date_creation']) and !empty($row['date_creation']))
+{
+ list($year, $month, $day) = explode('-', substr($row['date_creation'],0,10));
+ $time = substr($row['date_creation'],11);
+}
+else
+{
+ list($year, $month, $day) = array('', 0, 0);
+ $time = '00:00:00';
+}
+
+
+$month_list = $lang['month'];
+$month_list[0]='------------';
+ksort($month_list);
+
+$template->assign(
+ array(
+ 'DATE_CREATION_DAY_VALUE' => (int)$day,
+ 'DATE_CREATION_MONTH_VALUE' => (int)$month,
+ 'DATE_CREATION_YEAR_VALUE' => $year,
+ 'DATE_CREATION_TIME_VALUE' => $time,
+ 'month_list' => $month_list,
+ )
+ );
+
+$query = '
+SELECT category_id, uppercats
+ FROM '.IMAGE_CATEGORY_TABLE.' AS ic
+ INNER JOIN '.CATEGORIES_TABLE.' AS c
+ ON c.id = ic.category_id
+ WHERE image_id = '.$_GET['image_id'].'
+;';
+$result = pwg_query($query);
+
+while ($row = pwg_db_fetch_assoc($result))
+{
+ $name =
+ get_cat_display_name_cache(
+ $row['uppercats'],
+ get_root_url().'admin.php?page=album-'
+ );
+
+ if ($row['category_id'] == $storage_category_id)
+ {
+ $template->assign('STORAGE_CATEGORY', $name);
+ }
+ else
+ {
+ $template->append('related_categories', $name);
+ }
+}
+
+// jump to link
+//
+// 1. find all linked categories that are reachable for the current user.
+// 2. if a category is available in the URL, use it if reachable
+// 3. if URL category not available or reachable, use the first reachable
+// linked category
+// 4. if no category reachable, no jumpto link
+
+$query = '
+SELECT category_id
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ WHERE image_id = '.$_GET['image_id'].'
+;';
+
+$authorizeds = array_diff(
+ array_from_query($query, 'category_id'),
+ explode(
+ ',',
+ calculate_permissions($user['id'], $user['status'])
+ )
+ );
+
+if (isset($_GET['cat_id'])
+ and in_array($_GET['cat_id'], $authorizeds))
+{
+ $url_img = make_picture_url(
+ array(
+ 'image_id' => $_GET['image_id'],
+ 'image_file' => $image_file,
+ 'category' => $cache['cat_names'][ $_GET['cat_id'] ],
+ )
+ );
+}
+else
+{
+ foreach ($authorizeds as $category)
+ {
+ $url_img = make_picture_url(
+ array(
+ 'image_id' => $_GET['image_id'],
+ 'image_file' => $image_file,
+ 'category' => $cache['cat_names'][ $category ],
+ )
+ );
+ break;
+ }
+}
+
+if (isset($url_img))
+{
+ $template->assign( 'U_JUMPTO', $url_img );
+}
+
+// associate to albums
+$query = '
+SELECT id
+ FROM '.CATEGORIES_TABLE.'
+ INNER JOIN '.IMAGE_CATEGORY_TABLE.' ON id = category_id
+ WHERE image_id = '.$_GET['image_id'].'
+;';
+$associate_options_selected = array_from_query($query, 'id');
+
+$query = '
+SELECT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.'
+;';
+display_select_cat_wrapper($query, $associate_options_selected, 'associate_options');
+display_select_cat_wrapper($query, $represent_options_selected, 'represent_options');
+
+//----------------------------------------------------------- sending html code
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'picture_modify');
+?>
diff --git a/sources/admin/plugin.php b/sources/admin/plugin.php
new file mode 100644
index 0000000..b636608
--- /dev/null
+++ b/sources/admin/plugin.php
@@ -0,0 +1,62 @@
+
\ No newline at end of file
diff --git a/sources/admin/plugins.php b/sources/admin/plugins.php
new file mode 100644
index 0000000..efe3bd8
--- /dev/null
+++ b/sources/admin/plugins.php
@@ -0,0 +1,48 @@
+set_id('plugins');
+$tabsheet->select($page['tab']);
+$tabsheet->assign();
+
+if ($page['tab'] == 'update')
+ include(PHPWG_ROOT_PATH.'admin/updates_ext.php');
+else
+ include(PHPWG_ROOT_PATH.'admin/plugins_'.$page['tab'].'.php');
+
+?>
\ No newline at end of file
diff --git a/sources/admin/plugins_installed.php b/sources/admin/plugins_installed.php
new file mode 100644
index 0000000..d23e51c
--- /dev/null
+++ b/sources/admin/plugins_installed.php
@@ -0,0 +1,209 @@
+set_filenames(array('plugins' => 'plugins_installed.tpl'));
+
+// should we display details on plugins?
+if (isset($_GET['show_details']))
+{
+ if (1 == $_GET['show_details'])
+ {
+ $show_details = true;
+ }
+ else
+ {
+ $show_details = false;
+ }
+
+ pwg_set_session_var('plugins_show_details', $show_details);
+}
+elseif (null != pwg_get_session_var('plugins_show_details'))
+{
+ $show_details = pwg_get_session_var('plugins_show_details');
+}
+else
+{
+ $show_details = false;
+}
+
+$base_url = get_root_url().'admin.php?page='.$page['page'];
+$pwg_token = get_pwg_token();
+$action_url = $base_url.'&plugin='.'%s'.'&pwg_token='.$pwg_token;
+
+$plugins = new plugins();
+
+//--------------------------------------------------perform requested actions
+if (isset($_GET['action']) and isset($_GET['plugin']))
+{
+ if (!is_webmaster())
+ {
+ $page['errors'][] = l10n('Webmaster status is required.');
+ }
+ else
+ {
+ check_pwg_token();
+
+ $page['errors'] = $plugins->perform_action($_GET['action'], $_GET['plugin']);
+
+ if (empty($page['errors']))
+ {
+ if ($_GET['action'] == 'activate' or $_GET['action'] == 'deactivate')
+ {
+ $template->delete_compiled_templates();
+ }
+ redirect($base_url);
+ }
+ }
+}
+
+//--------------------------------------------------------Incompatible Plugins
+if (isset($_GET['incompatible_plugins']))
+{
+ $incompatible_plugins = array();
+ foreach ($plugins->get_incompatible_plugins() as $plugin => $version)
+ {
+ if ($plugin == '~~expire~~') continue;
+ $incompatible_plugins[] = $plugin;
+
+ }
+ echo json_encode($incompatible_plugins);
+ exit;
+}
+
+// +-----------------------------------------------------------------------+
+// | start template output |
+// +-----------------------------------------------------------------------+
+
+$plugins->sort_fs_plugins('name');
+$merged_extensions = $plugins->get_merged_extensions();
+$merged_plugins = false;
+$tpl_plugins = array();
+$active_plugins = 0;
+
+foreach($plugins->fs_plugins as $plugin_id => $fs_plugin)
+{
+ if (isset($_SESSION['incompatible_plugins'][$plugin_id])
+ and $fs_plugin['version'] != $_SESSION['incompatible_plugins'][$plugin_id])
+ {
+ // Incompatible plugins must be reinitilized
+ unset($_SESSION['incompatible_plugins']);
+ }
+
+ $tpl_plugin = array(
+ 'ID' => $plugin_id,
+ 'NAME' => $fs_plugin['name'],
+ 'VISIT_URL' => $fs_plugin['uri'],
+ 'VERSION' => $fs_plugin['version'],
+ 'DESC' => $fs_plugin['description'],
+ 'AUTHOR' => $fs_plugin['author'],
+ 'AUTHOR_URL' => @$fs_plugin['author uri'],
+ 'U_ACTION' => sprintf($action_url, $plugin_id),
+ );
+
+ if (isset($plugins->db_plugins_by_id[$plugin_id]))
+ {
+ $tpl_plugin['STATE'] = $plugins->db_plugins_by_id[$plugin_id]['state'];
+ }
+ else
+ {
+ $tpl_plugin['STATE'] = 'inactive';
+ }
+
+ if (isset($fs_plugin['extension']) and isset($merged_extensions[$fs_plugin['extension']]))
+ {
+ // Deactivate manually plugin from database
+ $query = 'UPDATE '.PLUGINS_TABLE.' SET state=\'inactive\' WHERE id=\''.$plugin_id.'\'';
+ pwg_query($query);
+
+ $tpl_plugin['STATE'] = 'merged';
+ $tpl_plugin['DESC'] = l10n('THIS PLUGIN IS NOW PART OF PIWIGO CORE! DELETE IT NOW.');
+ $merged_plugins = true;
+ }
+
+ if ($tpl_plugin['STATE'] == 'active')
+ {
+ $active_plugins++;
+ }
+
+ $tpl_plugins[] = $tpl_plugin;
+}
+
+$template->append('plugin_states', 'active');
+$template->append('plugin_states', 'inactive');
+
+if ($merged_plugins)
+{
+ $template->append('plugin_states', 'merged');
+}
+
+$missing_plugin_ids = array_diff(
+ array_keys($plugins->db_plugins_by_id),
+ array_keys($plugins->fs_plugins)
+ );
+
+if (count($missing_plugin_ids) > 0)
+{
+ foreach ($missing_plugin_ids as $plugin_id)
+ {
+ $tpl_plugins[] = array(
+ 'NAME' => $plugin_id,
+ 'VERSION' => $plugins->db_plugins_by_id[$plugin_id]['version'],
+ 'DESC' => l10n('ERROR: THIS PLUGIN IS MISSING BUT IT IS INSTALLED! UNINSTALL IT NOW.'),
+ 'U_ACTION' => sprintf($action_url, $plugin_id),
+ 'STATE' => 'missing',
+ );
+ }
+ $template->append('plugin_states', 'missing');
+}
+
+// sort plugins by state then by name
+function cmp($a, $b)
+{
+ $s = array('merged' => 0, 'missing' => 1, 'active' => 2, 'inactive' => 3);
+
+ if($a['STATE'] == $b['STATE'])
+ return strcasecmp($a['NAME'], $b['NAME']);
+ else
+ return $s[$a['STATE']] >= $s[$b['STATE']];
+}
+usort($tpl_plugins, 'cmp');
+
+$template->assign(
+ array(
+ 'plugins' => $tpl_plugins,
+ 'active_plugins' => $active_plugins,
+ 'PWG_TOKEN' => $pwg_token,
+ 'base_url' => $base_url,
+ 'show_details' => $show_details,
+ )
+ );
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'plugins');
+?>
\ No newline at end of file
diff --git a/sources/admin/plugins_new.php b/sources/admin/plugins_new.php
new file mode 100644
index 0000000..3790774
--- /dev/null
+++ b/sources/admin/plugins_new.php
@@ -0,0 +1,140 @@
+set_filenames(array('plugins' => 'plugins_new.tpl'));
+
+$base_url = get_root_url().'admin.php?page='.$page['page'].'&tab='.$page['tab'];
+
+$plugins = new plugins();
+
+//------------------------------------------------------automatic installation
+if (isset($_GET['revision']) and isset($_GET['extension']))
+{
+ if (!is_webmaster())
+ {
+ $page['errors'][] = l10n('Webmaster status is required.');
+ }
+ else
+ {
+ check_pwg_token();
+
+ $install_status = $plugins->extract_plugin_files('install', $_GET['revision'], $_GET['extension']);
+
+ redirect($base_url.'&installstatus='.$install_status);
+ }
+}
+
+//--------------------------------------------------------------install result
+if (isset($_GET['installstatus']))
+{
+ switch ($_GET['installstatus'])
+ {
+ case 'ok':
+ $page['infos'][] = l10n('Plugin has been successfully copied');
+ $page['infos'][] = l10n('You might go to plugin list to install and activate it.');
+ break;
+
+ case 'temp_path_error':
+ $page['errors'][] = l10n('Can\'t create temporary file.');
+ break;
+
+ case 'dl_archive_error':
+ $page['errors'][] = l10n('Can\'t download archive.');
+ break;
+
+ case 'archive_error':
+ $page['errors'][] = l10n('Can\'t read or extract archive.');
+ break;
+
+ default:
+ $page['errors'][] = l10n('An error occured during extraction (%s).', htmlspecialchars($_GET['installstatus']));
+ $page['errors'][] = l10n('Please check "plugins" folder and sub-folders permissions (CHMOD).');
+ }
+}
+
+//---------------------------------------------------------------Order options
+$template->assign('order_options',
+ array(
+ 'date' => l10n('Post date'),
+ 'revision' => l10n('Last revisions'),
+ 'name' => l10n('Name'),
+ 'author' => l10n('Author'),
+ 'downloads' => l10n('Number of downloads')));
+
+// +-----------------------------------------------------------------------+
+// | start template output |
+// +-----------------------------------------------------------------------+
+if ($plugins->get_server_plugins(true))
+{
+ /* order plugins */
+ if (pwg_get_session_var('plugins_new_order') != null)
+ {
+ $order_selected = pwg_get_session_var('plugins_new_order');
+ $plugins->sort_server_plugins($order_selected);
+ $template->assign('order_selected', $order_selected);
+ }
+ else
+ {
+ $plugins->sort_server_plugins('date');
+ $template->assign('order_selected', 'date');
+ }
+
+ foreach($plugins->server_plugins as $plugin)
+ {
+ $ext_desc = trim($plugin['extension_description'], " \n\r");
+ list($small_desc) = explode("\n", wordwrap($ext_desc, 200));
+
+ $url_auto_install = htmlentities($base_url)
+ . '&revision=' . $plugin['revision_id']
+ . '&extension=' . $plugin['extension_id']
+ . '&pwg_token='.get_pwg_token()
+ ;
+
+ $template->append('plugins', array(
+ 'ID' => $plugin['extension_id'],
+ 'EXT_NAME' => $plugin['extension_name'],
+ 'EXT_URL' => PEM_URL.'/extension_view.php?eid='.$plugin['extension_id'],
+ 'SMALL_DESC' => trim($small_desc, " \r\n"),
+ 'BIG_DESC' => $ext_desc,
+ 'VERSION' => $plugin['revision_name'],
+ 'REVISION_DATE' => preg_replace('/[^0-9]/', '', $plugin['revision_date']),
+ 'AUTHOR' => $plugin['author_name'],
+ 'DOWNLOADS' => $plugin['extension_nb_downloads'],
+ 'URL_INSTALL' => $url_auto_install,
+ 'URL_DOWNLOAD' => $plugin['download_url'] . '&origin=piwigo_download'));
+ }
+}
+else
+{
+ $page['errors'][] = l10n('Can\'t connect to server.');
+}
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'plugins');
+?>
\ No newline at end of file
diff --git a/sources/admin/popuphelp.php b/sources/admin/popuphelp.php
new file mode 100644
index 0000000..dc7290b
--- /dev/null
+++ b/sources/admin/popuphelp.php
@@ -0,0 +1,82 @@
+'.$title.'';
+$page['meta_robots']=array('noindex'=>1, 'nofollow'=>1);
+include(PHPWG_ROOT_PATH.'include/page_header.php');
+
+if
+ (
+ isset($_GET['page'])
+ and preg_match('/^[a-z_]*$/', $_GET['page'])
+ )
+{
+ $help_content =
+ load_language('help/'.$_GET['page'].'.html', '', array('return'=>true) );
+
+ if ($help_content == false)
+ {
+ $help_content = '';
+ }
+
+ $help_content = trigger_event(
+ 'get_popup_help_content', $help_content, $_GET['page']);
+}
+else
+{
+ die('Hacking attempt!');
+}
+
+$template->set_filename('popuphelp','popuphelp.tpl');
+
+$template->assign(
+ array
+ (
+ 'HELP_CONTENT' => $help_content
+ ));
+
+// +-----------------------------------------------------------------------+
+// | html code display |
+// +-----------------------------------------------------------------------+
+
+$template->pparse('popuphelp');
+
+include(PHPWG_ROOT_PATH.'include/page_tail.php');
+
+?>
\ No newline at end of file
diff --git a/sources/admin/profile.php b/sources/admin/profile.php
new file mode 100644
index 0000000..5554fe1
--- /dev/null
+++ b/sources/admin/profile.php
@@ -0,0 +1,47 @@
+set_filename('profile', 'profile.tpl');
+$template->assign_var_from_handle('ADMIN_CONTENT', 'profile');
+?>
diff --git a/sources/admin/rating.php b/sources/admin/rating.php
new file mode 100644
index 0000000..1592125
--- /dev/null
+++ b/sources/admin/rating.php
@@ -0,0 +1,234 @@
+set_id('rating');
+$tabsheet->select('rating');
+$tabsheet->assign();
+
+// +-----------------------------------------------------------------------+
+// | initialization |
+// +-----------------------------------------------------------------------+
+if (isset($_GET['start']) and is_numeric($_GET['start']))
+{
+ $start = $_GET['start'];
+}
+else
+{
+ $start = 0;
+}
+
+$elements_per_page=10;
+if (isset($_GET['display']) and is_numeric($_GET['display']))
+{
+ $elements_per_page = $_GET['display'];
+}
+
+$order_by_index=0;
+if (isset($_GET['order_by']) and is_numeric($_GET['order_by']))
+{
+ $order_by_index = $_GET['order_by'];
+}
+
+$page['user_filter'] = '';
+if (isset($_GET['users']))
+{
+ if ($_GET['users'] == 'user')
+ {
+ $page['user_filter'] = ' AND r.user_id <> '.$conf['guest_id'];
+ }
+ elseif ($_GET['users'] == 'guest')
+ {
+ $page['user_filter'] = ' AND r.user_id = '.$conf['guest_id'];
+ }
+}
+
+$users = array();
+$query = '
+SELECT '.$conf['user_fields']['username'].' as username, '.$conf['user_fields']['id'].' as id
+ FROM '.USERS_TABLE.'
+;';
+$result = pwg_query($query);
+while ($row = pwg_db_fetch_assoc($result))
+{
+ $users[$row['id']]=stripslashes($row['username']);
+}
+
+
+$query = 'SELECT COUNT(DISTINCT(r.element_id))
+FROM '.RATE_TABLE.' AS r
+WHERE 1=1'. $page['user_filter'];
+list($nb_images) = pwg_db_fetch_row(pwg_query($query));
+
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+
+$template->set_filename('rating', 'rating.tpl');
+
+$template->assign(
+ array(
+ 'navbar' => create_navigation_bar(
+ PHPWG_ROOT_PATH.'admin.php'.get_query_string_diff(array('start','del')),
+ $nb_images,
+ $start,
+ $elements_per_page
+ ),
+ 'F_ACTION' => PHPWG_ROOT_PATH.'admin.php',
+ 'DISPLAY' => $elements_per_page,
+ 'NB_ELEMENTS' => $nb_images,
+ )
+ );
+
+
+
+$available_order_by= array(
+ array(l10n('Rate date'), 'recently_rated DESC'),
+ array(l10n('Rating score'), 'score DESC'),
+ array(l10n('Average rate'), 'avg_rates DESC'),
+ array(l10n('Number of rates'), 'nb_rates DESC'),
+ array(l10n('Sum of rates'), 'sum_rates DESC'),
+ array(l10n('File name'), 'file DESC'),
+ array(l10n('Creation date'), 'date_creation DESC'),
+ array(l10n('Post date'), 'date_available DESC'),
+ );
+
+for ($i=0; $iappend(
+ 'order_by_options',
+ $available_order_by[$i][0]
+ );
+}
+$template->assign('order_by_options_selected', array($order_by_index) );
+
+
+$user_options = array(
+ 'all' => l10n('all'),
+ 'user' => l10n('Users'),
+ 'guest' => l10n('Guests'),
+ );
+
+$template->assign('user_options', $user_options );
+$template->assign('user_options_selected', array(@$_GET['users']) );
+
+
+$query = '
+SELECT i.id,
+ i.path,
+ i.file,
+ i.representative_ext,
+ i.rating_score AS score,
+ MAX(r.date) AS recently_rated,
+ ROUND(AVG(r.rate),2) AS avg_rates,
+ COUNT(r.rate) AS nb_rates,
+ SUM(r.rate) AS sum_rates
+ FROM '.RATE_TABLE.' AS r
+ LEFT JOIN '.IMAGES_TABLE.' AS i ON r.element_id = i.id
+ WHERE 1 = 1 ' . $page['user_filter'] . '
+ GROUP BY i.id,
+ i.path,
+ i.file,
+ i.representative_ext,
+ i.rating_score,
+ r.element_id
+ ORDER BY ' . $available_order_by[$order_by_index][1] .'
+ LIMIT '.$elements_per_page.' OFFSET '.$start.'
+;';
+
+$images = array();
+$result = pwg_query($query);
+while ($row = pwg_db_fetch_assoc($result))
+{
+ $images[] = $row;
+}
+
+$template->assign( 'images', array() );
+foreach ($images as $image)
+{
+ $thumbnail_src = DerivativeImage::thumb_url($image);
+
+ $image_url = get_root_url().'admin.php?page=photo-'.$image['id'];
+
+ $query = 'SELECT *
+FROM '.RATE_TABLE.' AS r
+WHERE r.element_id='.$image['id'] . '
+ORDER BY date DESC;';
+ $result = pwg_query($query);
+ $nb_rates = pwg_db_num_rows($result);
+
+ $tpl_image =
+ array(
+ 'id' => $image['id'],
+ 'U_THUMB' => $thumbnail_src,
+ 'U_URL' => $image_url,
+ 'SCORE_RATE' => $image['score'],
+ 'AVG_RATE' => $image['avg_rates'],
+ 'SUM_RATE' => $image['sum_rates'],
+ 'NB_RATES' => (int)$image['nb_rates'],
+ 'NB_RATES_TOTAL' => (int)$nb_rates,
+ 'FILE' => $image['file'],
+ 'rates' => array()
+ );
+
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ if ( isset($users[$row['user_id']]) )
+ {
+ $user_rate = $users[$row['user_id']];
+ }
+ else
+ {
+ $user_rate = '? '. $row['user_id'];
+ }
+ if ( strlen($row['anonymous_id'])>0 )
+ {
+ $user_rate .= '('.$row['anonymous_id'].')';
+ }
+
+ $row['USER'] = $user_rate;
+ $tpl_image['rates'][] = $row;
+ }
+ $template->append( 'images', $tpl_image );
+}
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'rating');
+?>
\ No newline at end of file
diff --git a/sources/admin/rating_user.php b/sources/admin/rating_user.php
new file mode 100644
index 0000000..7465386
--- /dev/null
+++ b/sources/admin/rating_user.php
@@ -0,0 +1,249 @@
+set_id('rating');
+$tabsheet->select('rating_user');
+$tabsheet->assign();
+
+$filter_min_rates = 2;
+if (isset($_GET['f_min_rates']))
+{
+ $filter_min_rates = (int)$_GET['f_min_rates'];
+}
+
+$consensus_top_number = $conf['top_number'];
+if (isset($_GET['consensus_top_number']))
+{
+ $consensus_top_number = (int)$_GET['consensus_top_number'];
+}
+
+// build users
+global $conf;
+$query = 'SELECT DISTINCT
+ u.'.$conf['user_fields']['id'].' AS id,
+ u.'.$conf['user_fields']['username'].' AS name,
+ ui.status
+ FROM '.USERS_TABLE.' AS u INNER JOIN '.USER_INFOS_TABLE.' AS ui
+ ON u.'.$conf['user_fields']['id'].' = ui.user_id';
+
+$users_by_id = array();
+$result = pwg_query($query);
+while ($row = pwg_db_fetch_assoc($result))
+{
+ $users_by_id[(int)$row['id']] = array(
+ 'name' => $row['name'],
+ 'anon' => is_autorize_status(ACCESS_CLASSIC, $row['status']) ? false : true
+ );
+}
+
+$by_user_rating_model = array( 'rates' => array() );
+foreach($conf['rate_items'] as $rate)
+{
+ $by_user_rating_model['rates'][$rate] = array();
+}
+
+// by user aggregation
+$image_ids = array();
+$by_user_ratings = array();
+$query = '
+SELECT * FROM '.RATE_TABLE.' ORDER by date DESC';
+$result = pwg_query($query);
+while ($row = pwg_db_fetch_assoc($result))
+{
+ if (!isset($users_by_id[$row['user_id']]))
+ {
+ $users_by_id[$row['user_id']] = array('name' => '???'.$row['user_id'], 'anon' => false);
+ }
+ $usr = $users_by_id[$row['user_id']];
+ if ($usr['anon'])
+ {
+ $user_key = $usr['name'].'('.$row['anonymous_id'].')';
+ }
+ else
+ {
+ $user_key = $usr['name'];
+ }
+ $rating = & $by_user_ratings[$user_key];
+ if ( is_null($rating) )
+ {
+ $rating = $by_user_rating_model;
+ $rating['uid'] = (int)$row['user_id'];
+ $rating['aid'] = $usr['anon'] ? $row['anonymous_id'] : '';
+ $rating['last_date'] = $row['date'];
+ }
+ $rating['rates'][$row['rate']][] = array(
+ 'id' => $row['element_id'],
+ 'date' => $row['date'],
+ );
+ $image_ids[$row['element_id']] = 1;
+ unset($rating);
+}
+
+// get image tn urls
+$image_urls = array();
+if (count($image_ids) > 0 )
+{
+ $query = 'SELECT id, name, file, path, representative_ext
+ FROM '.IMAGES_TABLE.'
+ WHERE id IN ('.implode(',', array_keys($image_ids)).')';
+ $result = pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $image_urls[ $row['id'] ] = array(
+ 'tn' => DerivativeImage::thumb_url($row),
+ 'page' => make_picture_url( array('image_id'=>$row['id'], 'image_file'=>$row['file']) ),
+ );
+ }
+}
+
+//all image averages
+$query='SELECT element_id,
+ AVG(rate) AS avg
+ FROM '.RATE_TABLE.'
+ GROUP BY element_id';
+$all_img_sum = array();
+$result = pwg_query($query);
+while ($row = pwg_db_fetch_assoc($result))
+{
+ $all_img_sum[(int)$row['element_id']] = array( 'avg'=>(float)$row['avg'] );
+}
+
+$query='SELECT id
+ FROM '.IMAGES_TABLE.'
+ ORDER by rating_score DESC
+ LIMIT '.$consensus_top_number;
+$best_rated = array_flip( array_from_query($query, 'id'));
+
+// by user stats
+foreach($by_user_ratings as $id => &$rating)
+{
+ $c=0; $s=0; $ss=0; $consensus_dev=0; $consensus_dev_top=0; $consensus_dev_top_count=0;
+ foreach($rating['rates'] as $rate => $rates)
+ {
+ $ct = count($rates);
+ $c += $ct;
+ $s += $ct * $rate;
+ $ss += $ct * $rate * $rate;
+ foreach($rates as $id_date)
+ {
+ $dev = abs($rate - $all_img_sum[$id_date['id']]['avg']);
+ $consensus_dev += $dev;
+ if (isset($best_rated[$id_date['id']]))
+ {
+ $consensus_dev_top += $dev;
+ $consensus_dev_top_count++;
+ }
+ }
+ }
+
+ $consensus_dev /= $c;
+ if ($consensus_dev_top_count)
+ $consensus_dev_top /= $consensus_dev_top_count;
+
+ $var = ($ss - $s*$s/$c)/$c;
+ $rating += array(
+ 'id' => $id,
+ 'count' => $c,
+ 'avg' => $s/$c,
+ 'cv' => $s==0 ? -1 : sqrt($var)/($s/$c), // http://en.wikipedia.org/wiki/Coefficient_of_variation
+ 'cd' => $consensus_dev,
+ 'cdtop' => $consensus_dev_top_count ? $consensus_dev_top : ''
+ );
+}
+unset($rating);
+
+// filter
+foreach($by_user_ratings as $id => $rating)
+{
+ if ($rating['count'] <= $filter_min_rates)
+ {
+ unset($by_user_ratings[$id]);
+ }
+}
+
+
+function avg_compare($a, $b)
+{
+ $d = $a['avg'] - $b['avg'];
+ return ($d==0) ? 0 : ($d<0 ? -1 : 1);
+}
+
+function count_compare($a, $b)
+{
+ $d = $a['count'] - $b['count'];
+ return ($d==0) ? 0 : ($d<0 ? -1 : 1);
+}
+
+function cv_compare($a, $b)
+{
+ $d = $b['cv'] - $a['cv']; //desc
+ return ($d==0) ? 0 : ($d<0 ? -1 : 1);
+}
+
+function consensus_dev_compare($a, $b)
+{
+ $d = $b['cd'] - $a['cd']; //desc
+ return ($d==0) ? 0 : ($d<0 ? -1 : 1);
+}
+
+$order_by_index=3;
+if (isset($_GET['order_by']) and is_numeric($_GET['order_by']))
+{
+ $order_by_index = $_GET['order_by'];
+}
+
+$available_order_by= array(
+ array(l10n('Average rate'), 'avg_compare'),
+ array(l10n('Number of rates'), 'count_compare'),
+ array(l10n('Variation'), 'cv_compare'),
+ array(l10n('Consensus deviation'), 'consensus_dev_compare'),
+ );
+
+for ($i=0; $iappend(
+ 'order_by_options',
+ $available_order_by[$i][0]
+ );
+}
+$template->assign('order_by_options_selected', array($order_by_index) );
+
+$x = uasort($by_user_ratings, $available_order_by[$order_by_index][1] );
+
+$template->assign( array(
+ 'F_ACTION' => get_root_url().'admin.php',
+ 'F_MIN_RATES' => $filter_min_rates,
+ 'CONSENSUS_TOP_NUMBER' => $consensus_top_number,
+ 'available_rates' => $conf['rate_items'],
+ 'ratings' => $by_user_ratings,
+ 'image_urls' => $image_urls,
+ 'TN_WIDTH' => 28+2*ImageStdParams::get_by_type(IMG_THUMB)->sizing->ideal_size[0],
+ ) );
+$template->set_filename('rating', 'rating_user.tpl');
+$template->assign_var_from_handle('ADMIN_CONTENT', 'rating');
+
+?>
\ No newline at end of file
diff --git a/sources/admin/site_manager.php b/sources/admin/site_manager.php
new file mode 100644
index 0000000..2d75fea
--- /dev/null
+++ b/sources/admin/site_manager.php
@@ -0,0 +1,187 @@
+set_filenames(array('site_manager'=>'site_manager.tpl'));
+
+// +-----------------------------------------------------------------------+
+// | new site creation form |
+// +-----------------------------------------------------------------------+
+if (isset($_POST['submit']) and !empty($_POST['galleries_url']))
+{
+ $is_remote = url_is_remote( $_POST['galleries_url'] );
+ if ($is_remote)
+ {
+ fatal_error('remote sites not supported');
+ }
+ $url = preg_replace('/[\/]*$/', '', $_POST['galleries_url']);
+ $url.= '/';
+ if ( ! (strpos($url, '.') === 0 ) )
+ {
+ $url = './' . $url;
+ }
+
+ // site must not exists
+ $query = '
+SELECT COUNT(id) AS count
+ FROM '.SITES_TABLE.'
+ WHERE galleries_url = \''.$url.'\'
+;';
+ $row = pwg_db_fetch_assoc(pwg_query($query));
+ if ($row['count'] > 0)
+ {
+ $page['errors'][] = l10n('This site already exists').' ['.$url.']';
+ }
+ if (count($page['errors']) == 0)
+ {
+ if ( ! file_exists($url) )
+ {
+ $page['errors'][] = l10n('Directory does not exist').' ['.$url.']';
+ }
+ }
+
+ if (count($page['errors']) == 0)
+ {
+ $query = '
+INSERT INTO '.SITES_TABLE.'
+ (galleries_url)
+ VALUES
+ (\''.$url.'\')
+;';
+ pwg_query($query);
+ $page['infos'][] = $url.' '.l10n('created');
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | actions on site |
+// +-----------------------------------------------------------------------+
+if (isset($_GET['site']) and is_numeric($_GET['site']))
+{
+ $page['site'] = $_GET['site'];
+}
+if (isset($_GET['action']) and isset($page['site']))
+{
+ $query = '
+SELECT galleries_url
+ FROM '.SITES_TABLE.'
+ WHERE id = '.$page['site'].'
+;';
+ list($galleries_url) = pwg_db_fetch_row(pwg_query($query));
+ switch($_GET['action'])
+ {
+ case 'delete' :
+ {
+ delete_site($page['site']);
+ $page['infos'][] = $galleries_url.' '.l10n('deleted');
+ break;
+ }
+ }
+}
+
+$template->assign(
+ array(
+ 'F_ACTION' => get_root_url().'admin.php'.get_query_string_diff(array('action','site','pwg_token')),
+ 'PWG_TOKEN' => get_pwg_token(),
+ )
+ );
+
+$query = '
+SELECT c.site_id, COUNT(DISTINCT c.id) AS nb_categories, COUNT(i.id) AS nb_images
+ FROM '.CATEGORIES_TABLE.' AS c LEFT JOIN '.IMAGES_TABLE.' AS i
+ ON c.id=i.storage_category_id
+ WHERE c.site_id IS NOT NULL
+ GROUP BY c.site_id
+;';
+$sites_detail = hash_from_query($query, 'site_id');
+
+$query = '
+SELECT *
+ FROM '.SITES_TABLE.'
+;';
+$result = pwg_query($query);
+
+while ($row = pwg_db_fetch_assoc($result))
+{
+ $is_remote = url_is_remote($row['galleries_url']);
+ $base_url = PHPWG_ROOT_PATH.'admin.php';
+ $base_url.= '?page=site_manager';
+ $base_url.= '&site='.$row['id'];
+ $base_url.= '&pwg_token='.get_pwg_token();
+ $base_url.= '&action=';
+
+ $update_url = PHPWG_ROOT_PATH.'admin.php';
+ $update_url.= '?page=site_update';
+ $update_url.= '&site='.$row['id'];
+
+ $tpl_var =
+ array(
+ 'NAME' => $row['galleries_url'],
+ 'TYPE' => l10n( $is_remote ? 'Remote' : 'Local' ),
+ 'CATEGORIES' => (int)@$sites_detail[$row['id']]['nb_categories'],
+ 'IMAGES' => (int)@$sites_detail[$row['id']]['nb_images'],
+ 'U_SYNCHRONIZE' => $update_url
+ );
+
+ if ($row['id'] != 1)
+ {
+ $tpl_var['U_DELETE'] = $base_url.'delete';
+ }
+
+ $plugin_links = array();
+ //$plugin_links is array of array composed of U_HREF, U_HINT & U_CAPTION
+ $plugin_links =
+ trigger_event('get_admins_site_links',
+ $plugin_links, $row['id'], $is_remote);
+ $tpl_var['plugin_links'] = $plugin_links;
+
+ $template->append('sites', $tpl_var);
+}
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'site_manager');
+?>
diff --git a/sources/admin/site_reader_local.php b/sources/admin/site_reader_local.php
new file mode 100644
index 0000000..7e618ca
--- /dev/null
+++ b/sources/admin/site_reader_local.php
@@ -0,0 +1,187 @@
+site_url = $url;
+ global $conf;
+ if (!isset($conf['flip_file_ext']))
+ {
+ $conf['flip_file_ext'] = array_flip($conf['file_ext']);
+ }
+ if (!isset($conf['flip_picture_ext']))
+ {
+ $conf['flip_picture_ext'] = array_flip($conf['picture_ext']);
+ }
+}
+
+/**
+ * Is this local site ok ?
+ *
+ * @return true on success, false otherwise
+ */
+function open()
+{
+ global $errors;
+
+ if (!is_dir($this->site_url))
+ {
+ $errors[] = array(
+ 'path' => $this->site_url,
+ 'type' => 'PWG-ERROR-NO-FS'
+ );
+
+ return false;
+ }
+
+ return true;
+}
+
+// retrieve file system sub-directories fulldirs
+function get_full_directories($basedir)
+{
+ $fs_fulldirs = get_fs_directories($basedir);
+ return $fs_fulldirs;
+}
+
+/**
+ * Returns an array with all file system files according to $conf['file_ext']
+ * and $conf['picture_ext']
+ * @param string $path recurse in this directory
+ * @return array like "pic.jpg"=>array('representative_ext'=>'jpg' ... )
+ */
+function get_elements($path)
+{
+ global $conf;
+
+ $subdirs = array();
+ $fs = array();
+ if (is_dir($path) && $contents = opendir($path) )
+ {
+ while (($node = readdir($contents)) !== false)
+ {
+ if ($node == '.' or $node == '..') continue;
+
+ if (is_file($path.'/'.$node))
+ {
+ $extension = get_extension($node);
+ $filename_wo_ext = get_filename_wo_extension($node);
+
+ if ( isset($conf['flip_file_ext'][$extension]) )
+ {
+ $representative_ext = null;
+ if (! isset($conf['flip_picture_ext'][$extension]) )
+ {
+ $representative_ext = $this->get_representative_ext($path, $filename_wo_ext);
+ }
+ $fs[ $path.'/'.$node ] = array(
+ 'representative_ext' => $representative_ext,
+ );
+ }
+ }
+ else if (is_dir($path.'/'.$node)
+ and $node != 'pwg_high'
+ and $node != 'pwg_representative'
+ and $node != 'thumbnail' )
+ {
+ $subdirs[] = $node;
+ }
+ } //end while readdir
+ closedir($contents);
+
+ foreach ($subdirs as $subdir)
+ {
+ $tmp_fs = $this->get_elements($path.'/'.$subdir);
+ $fs = array_merge($fs, $tmp_fs);
+ }
+ ksort($fs);
+ } //end if is_dir
+ return $fs;
+}
+
+// returns the name of the attributes that are supported for
+// files update/synchronization
+function get_update_attributes()
+{
+ return array('representative_ext');
+}
+
+function get_element_update_attributes($file)
+{
+ global $conf;
+ $data = array();
+
+ $filename = basename($file);
+ $extension = get_extension($filename);
+
+ $representative_ext = null;
+ if (! isset($conf['flip_picture_ext'][$extension]) )
+ {
+ $dirname = dirname($file);
+ $filename_wo_ext = get_filename_wo_extension($filename);
+ $representative_ext = $this->get_representative_ext($dirname, $filename_wo_ext);
+ }
+
+ $data['representative_ext'] = $representative_ext;
+ return $data;
+}
+
+// returns the name of the attributes that are supported for
+// metadata update/synchronization according to configuration
+function get_metadata_attributes()
+{
+ return get_sync_metadata_attributes();
+}
+
+// returns a hash of attributes (metadata+filesize+width,...) for file
+function get_element_metadata($infos)
+{
+ return get_sync_metadata($infos);
+}
+
+
+//-------------------------------------------------- private functions --------
+function get_representative_ext($path, $filename_wo_ext)
+{
+ global $conf;
+ $base_test = $path.'/pwg_representative/'.$filename_wo_ext.'.';
+ foreach ($conf['picture_ext'] as $ext)
+ {
+ $test = $base_test.$ext;
+ if (is_file($test))
+ {
+ return $ext;
+ }
+ }
+ return null;
+}
+
+
+}
+?>
\ No newline at end of file
diff --git a/sources/admin/site_update.php b/sources/admin/site_update.php
new file mode 100644
index 0000000..5bcaea1
--- /dev/null
+++ b/sources/admin/site_update.php
@@ -0,0 +1,925 @@
+ array(
+ l10n('wrong filename'),
+ l10n('The name of directories and files must be composed of letters, numbers, "-", "_" or "."')
+ ),
+ 'PWG-ERROR-NO-FS' => array(
+ l10n('File/directory read error'),
+ l10n('The file or directory cannot be accessed (either it does not exist or the access is denied)')
+ ),
+ );
+$errors = array();
+$infos = array();
+
+if ($site_is_remote)
+{
+ fatal_error('remote sites not supported');
+}
+else
+{
+ include_once( PHPWG_ROOT_PATH.'admin/site_reader_local.php');
+ $site_reader = new LocalSiteReader($site_url);
+}
+
+$general_failure = true;
+if (isset($_POST['submit']))
+{
+ if ($site_reader->open())
+ {
+ $general_failure = false;
+ }
+
+ // shall we simulate only
+ if (isset($_POST['simulate']) and $_POST['simulate'] == 1)
+ {
+ $simulate = true;
+ }
+ else
+ {
+ $simulate = false;
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | directories / categories |
+// +-----------------------------------------------------------------------+
+if (isset($_POST['submit'])
+ and ($_POST['sync'] == 'dirs' or $_POST['sync'] == 'files'))
+{
+ $counts['new_categories'] = 0;
+ $counts['del_categories'] = 0;
+ $counts['del_elements'] = 0;
+ $counts['new_elements'] = 0;
+ $counts['upd_elements'] = 0;
+}
+
+
+if (isset($_POST['submit'])
+ and ($_POST['sync'] == 'dirs' or $_POST['sync'] == 'files')
+ and !$general_failure)
+{
+ $start = get_moment();
+ // which categories to update ?
+ $query = '
+SELECT id, uppercats, global_rank, status, visible
+ FROM '.CATEGORIES_TABLE.'
+ WHERE dir IS NOT NULL
+ AND site_id = '.$site_id;
+ if (isset($_POST['cat']) and is_numeric($_POST['cat']))
+ {
+ if (isset($_POST['subcats-included']) and $_POST['subcats-included'] == 1)
+ {
+ $query.= '
+ AND uppercats '.DB_REGEX_OPERATOR.' \'(^|,)'.$_POST['cat'].'(,|$)\'
+';
+ }
+ else
+ {
+ $query.= '
+ AND id = '.$_POST['cat'].'
+';
+ }
+ }
+ $db_categories = hash_from_query($query, 'id');
+
+ // get categort full directories in an array for comparison with file
+ // system directory tree
+ $db_fulldirs = get_fulldirs(array_keys($db_categories));
+
+ // what is the base directory to search file system sub-directories ?
+ if (isset($_POST['cat']) and is_numeric($_POST['cat']))
+ {
+ $basedir = $db_fulldirs[$_POST['cat']];
+ }
+ else
+ {
+ $basedir = preg_replace('#/*$#', '', $site_url);
+ }
+
+ // we need to have fulldirs as keys to make efficient comparison
+ $db_fulldirs = array_flip($db_fulldirs);
+
+ // finding next rank for each id_uppercat. By default, each category id
+ // has 1 for next rank on its sub-categories to create
+ $next_rank['NULL'] = 1;
+
+ $query = '
+SELECT id
+ FROM '.CATEGORIES_TABLE;
+ $result = pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $next_rank[$row['id']] = 1;
+ }
+
+ // let's see if some categories already have some sub-categories...
+ $query = '
+SELECT id_uppercat, MAX(rank)+1 AS next_rank
+ FROM '.CATEGORIES_TABLE.'
+ GROUP BY id_uppercat';
+ $result = pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ // for the id_uppercat NULL, we write 'NULL' and not the empty string
+ if (!isset($row['id_uppercat']) or $row['id_uppercat'] == '')
+ {
+ $row['id_uppercat'] = 'NULL';
+ }
+ $next_rank[$row['id_uppercat']] = $row['next_rank'];
+ }
+
+ // next category id available
+ $next_id = pwg_db_nextval('id', CATEGORIES_TABLE);
+
+ // retrieve sub-directories fulldirs from the site reader
+ $fs_fulldirs = $site_reader->get_full_directories($basedir);
+
+ // get_full_directories doesn't include the base directory, so if it's a
+ // category directory, we need to include it in our array
+ if (isset($_POST['cat']))
+ {
+ $fs_fulldirs[] = $basedir;
+ }
+ // If $_POST['subcats-included'] != 1 ("Search in sub-albums" is unchecked)
+ // $db_fulldirs doesn't include any subdirectories and $fs_fulldirs does
+ // So $fs_fulldirs will be limited to the selected basedir
+ // (if that one is in $fs_fulldirs)
+ if (!isset($_POST['subcats-included']) or $_POST['subcats-included'] != 1)
+ {
+ $fs_fulldirs = array_intersect($fs_fulldirs, array_keys($db_fulldirs));
+ }
+ $inserts = array();
+ // new categories are the directories not present yet in the database
+ foreach (array_diff($fs_fulldirs, array_keys($db_fulldirs)) as $fulldir)
+ {
+ $dir = basename($fulldir);
+ if (preg_match($conf['sync_chars_regex'], $dir))
+ {
+ $insert = array(
+ 'id' => $next_id++,
+ 'dir' => $dir,
+ 'name' => str_replace('_', ' ', $dir),
+ 'site_id' => $site_id,
+ 'commentable' =>
+ boolean_to_string($conf['newcat_default_commentable']),
+ 'status' => $conf['newcat_default_status'],
+ 'visible' => boolean_to_string($conf['newcat_default_visible']),
+ );
+
+ if (isset($db_fulldirs[dirname($fulldir)]))
+ {
+ $parent = $db_fulldirs[dirname($fulldir)];
+
+ $insert['id_uppercat'] = $parent;
+ $insert['uppercats'] =
+ $db_categories[$parent]['uppercats'].','.$insert['id'];
+ $insert['rank'] = $next_rank[$parent]++;
+ $insert['global_rank'] =
+ $db_categories[$parent]['global_rank'].'.'.$insert['rank'];
+ if ('private' == $db_categories[$parent]['status'])
+ {
+ $insert['status'] = 'private';
+ }
+ if ('false' == $db_categories[$parent]['visible'])
+ {
+ $insert['visible'] = 'false';
+ }
+ }
+ else
+ {
+ $insert['uppercats'] = $insert['id'];
+ $insert{'rank'} = $next_rank['NULL']++;
+ $insert['global_rank'] = $insert['rank'];
+ }
+
+ $inserts[] = $insert;
+ $infos[] = array(
+ 'path' => $fulldir,
+ 'info' => l10n('added'),
+ );
+
+ // add the new category to $db_categories and $db_fulldirs array
+ $db_categories[$insert{'id'}] =
+ array(
+ 'id' => $insert['id'],
+ 'parent' => (isset($parent)) ? $parent : Null,
+ 'status' => $insert['status'],
+ 'visible' => $insert['visible'],
+ 'uppercats' => $insert['uppercats'],
+ 'global_rank' => $insert['global_rank']
+ );
+ $db_fulldirs[$fulldir] = $insert['id'];
+ $next_rank[$insert{'id'}] = 1;
+ }
+ else
+ {
+ $errors[] = array(
+ 'path' => $fulldir,
+ 'type' => 'PWG-UPDATE-1'
+ );
+ }
+ }
+
+ if (count($inserts) > 0)
+ {
+ if (!$simulate)
+ {
+ $dbfields = array(
+ 'id','dir','name','site_id','id_uppercat','uppercats','commentable',
+ 'visible','status','rank','global_rank'
+ );
+ mass_inserts(CATEGORIES_TABLE, $dbfields, $inserts);
+
+ // add default permissions to categories
+ $category_ids = array();
+ $category_up = array();
+ foreach ($inserts as $category)
+ {
+ $category_ids[] = $category['id'];
+ if (!empty($category['id_uppercat']))
+ {
+ $category_up[] = $category['id_uppercat'];
+ }
+ }
+ $category_up=implode(',',array_unique($category_up));
+ if ($conf['inheritance_by_default'])
+ {
+ $query = '
+ SELECT *
+ FROM '.GROUP_ACCESS_TABLE.'
+ WHERE cat_id IN ('.$category_up.')
+ ;';
+ $result = pwg_query($query);
+ if (!empty($result))
+ {
+ $granted_grps = array();
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ if (!isset($granted_grps[$row['cat_id']]))
+ {
+ $granted_grps[$row['cat_id']]=array();
+ }
+ // TODO: explanaition
+ array_push(
+ $granted_grps,
+ array(
+ $row['cat_id'] => array_push($granted_grps[$row['cat_id']],$row['group_id'])
+ )
+ );
+ }
+ }
+ $query = '
+ SELECT *
+ FROM '.USER_ACCESS_TABLE.'
+ WHERE cat_id IN ('.$category_up.')
+ ;';
+ $result = pwg_query($query);
+ if (!empty($result))
+ {
+ $granted_users = array();
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ if (!isset($granted_users[$row['cat_id']]))
+ {
+ $granted_users[$row['cat_id']]=array();
+ }
+ // TODO: explanaition
+ array_push(
+ $granted_users,
+ array(
+ $row['cat_id'] => array_push($granted_users[$row['cat_id']],$row['user_id'])
+ )
+ );
+ }
+ }
+ $insert_granted_users=array();
+ $insert_granted_grps=array();
+ foreach ($category_ids as $ids)
+ {
+ $parent_id=$db_categories[$ids]['parent'];
+ while (in_array($parent_id, $category_ids))
+ {
+ $parent_id= $db_categories[$parent_id]['parent'];
+ }
+ if ($db_categories[$ids]['status']=='private' and !is_null($parent_id))
+ {
+ if (isset($granted_grps[$parent_id]))
+ {
+ foreach ($granted_grps[$parent_id] as $granted_grp)
+ {
+ $insert_granted_grps[] = array(
+ 'group_id' => $granted_grp,
+ 'cat_id' => $ids
+ );
+ }
+ }
+ if (isset($granted_users[$parent_id]))
+ {
+ foreach ($granted_users[$parent_id] as $granted_user)
+ {
+ $insert_granted_users[] = array(
+ 'user_id' => $granted_user,
+ 'cat_id' => $ids
+ );
+ }
+ }
+ foreach (get_admins() as $granted_user)
+ {
+ $insert_granted_users[] = array(
+ 'user_id' => $granted_user,
+ 'cat_id' => $ids
+ );
+ }
+ }
+ }
+ mass_inserts(GROUP_ACCESS_TABLE, array('group_id','cat_id'), $insert_granted_grps);
+ $insert_granted_users=array_unique($insert_granted_users, SORT_REGULAR);
+ mass_inserts(USER_ACCESS_TABLE, array('user_id','cat_id'), $insert_granted_users);
+ }
+ else
+ {
+ add_permission_on_category($category_ids, get_admins());
+ }
+ }
+
+ $counts['new_categories'] = count($inserts);
+ }
+
+ // to delete categories
+ $to_delete = array();
+ $to_delete_derivative_dirs = array();
+
+ foreach (array_diff(array_keys($db_fulldirs), $fs_fulldirs) as $fulldir)
+ {
+ $to_delete[] = $db_fulldirs[$fulldir];
+ unset($db_fulldirs[$fulldir]);
+
+ $infos[] = array(
+ 'path' => $fulldir,
+ 'info' => l10n('deleted')
+ );
+
+ if (substr_compare($fulldir, '../', 0, 3)==0)
+ {
+ $fulldir = substr($fulldir, 3);
+ }
+ $to_delete_derivative_dirs[] = PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR.$fulldir;
+ }
+
+ if (count($to_delete) > 0)
+ {
+ if (!$simulate)
+ {
+ delete_categories($to_delete);
+ foreach($to_delete_derivative_dirs as $to_delete_dir)
+ {
+ if (is_dir($to_delete_dir))
+ {
+ clear_derivative_cache_rec($to_delete_dir, '#.+#');
+ }
+ }
+ }
+ $counts['del_categories'] = count($to_delete);
+ }
+
+ $template->append('footer_elements', '' );
+}
+// +-----------------------------------------------------------------------+
+// | files / elements |
+// +-----------------------------------------------------------------------+
+if (isset($_POST['submit']) and $_POST['sync'] == 'files'
+ and !$general_failure)
+{
+ $start_files = get_moment();
+ $start= $start_files;
+
+ $fs = $site_reader->get_elements($basedir);
+ $template->append('footer_elements', '' );
+
+ $cat_ids = array_diff(array_keys($db_categories), $to_delete);
+
+ $db_elements = array();
+
+ if (count($cat_ids) > 0)
+ {
+ $query = '
+SELECT id, path
+ FROM '.IMAGES_TABLE.'
+ WHERE storage_category_id IN ('
+ .wordwrap(
+ implode(', ', $cat_ids),
+ 160,
+ "\n"
+ ).')';
+ $db_elements = simple_hash_from_query($query, 'id', 'path');
+ }
+
+ // next element id available
+ $next_element_id = pwg_db_nextval('id', IMAGES_TABLE);
+
+ $start = get_moment();
+
+ $inserts = array();
+ $insert_links = array();
+
+ foreach (array_diff(array_keys($fs), $db_elements) as $path)
+ {
+ $insert = array();
+ // storage category must exist
+ $dirname = dirname($path);
+ if (!isset($db_fulldirs[$dirname]))
+ {
+ continue;
+ }
+ $filename = basename($path);
+ if (!preg_match($conf['sync_chars_regex'], $filename))
+ {
+ $errors[] = array(
+ 'path' => $path,
+ 'type' => 'PWG-UPDATE-1'
+ );
+
+ continue;
+ }
+
+ $insert = array(
+ 'id' => $next_element_id++,
+ 'file' => $filename,
+ 'name' => get_name_from_file($filename),
+ 'date_available' => CURRENT_DATE,
+ 'path' => $path,
+ 'representative_ext' => $fs[$path]['representative_ext'],
+ 'storage_category_id' => $db_fulldirs[$dirname],
+ 'added_by' => $user['id'],
+ );
+
+ if ( $_POST['privacy_level']!=0 )
+ {
+ $insert['level'] = $_POST['privacy_level'];
+ }
+
+ $inserts[] = $insert;
+
+ $insert_links[] = array(
+ 'image_id' => $insert['id'],
+ 'category_id' => $insert['storage_category_id'],
+ );
+
+ $infos[] = array(
+ 'path' => $insert['path'],
+ 'info' => l10n('added')
+ );
+
+ $caddiables[] = $insert['id'];
+ }
+
+ if (count($inserts) > 0)
+ {
+ if (!$simulate)
+ {
+ // inserts all new elements
+ mass_inserts(
+ IMAGES_TABLE,
+ array_keys($inserts[0]),
+ $inserts
+ );
+
+ // inserts all links between new elements and their storage category
+ mass_inserts(
+ IMAGE_CATEGORY_TABLE,
+ array_keys($insert_links[0]),
+ $insert_links
+ );
+
+ // add new photos to caddie
+ if (isset($_POST['add_to_caddie']) and $_POST['add_to_caddie'] == 1)
+ {
+ fill_caddie($caddiables);
+ }
+ }
+ $counts['new_elements'] = count($inserts);
+ }
+
+ // delete elements that are in database but not in the filesystem
+ $to_delete_elements = array();
+ foreach (array_diff($db_elements, array_keys($fs)) as $path)
+ {
+ $to_delete_elements[] = array_search($path, $db_elements);
+ $infos[] = array(
+ 'path' => $path,
+ 'info' => l10n('deleted')
+ );
+ }
+ if (count($to_delete_elements) > 0)
+ {
+ if (!$simulate)
+ {
+ delete_elements($to_delete_elements);
+ }
+ $counts['del_elements'] = count($to_delete_elements);
+ }
+
+ $template->append('footer_elements', '' );
+}
+
+// +-----------------------------------------------------------------------+
+// | synchronize files |
+// +-----------------------------------------------------------------------+
+if (isset($_POST['submit'])
+ and ($_POST['sync'] == 'dirs' or $_POST['sync'] == 'files')
+ and !$general_failure )
+{
+ if (!$simulate)
+ {
+ $start = get_moment();
+ update_category('all');
+ $template->append('footer_elements', '' );
+ $start = get_moment();
+ update_global_rank();
+ $template->append('footer_elements', '');
+ }
+
+ if ($_POST['sync'] == 'files')
+ {
+ $start = get_moment();
+ $opts['category_id'] = '';
+ $opts['recursive'] = true;
+ if (isset($_POST['cat']))
+ {
+ $opts['category_id'] = $_POST['cat'];
+ if (!isset($_POST['subcats-included']) or $_POST['subcats-included'] != 1)
+ {
+ $opts['recursive'] = false;
+ }
+ }
+ $files = get_filelist($opts['category_id'], $site_id,
+ $opts['recursive'],
+ false);
+ $template->append('footer_elements', '');
+ $start = get_moment();
+
+ $datas = array();
+ foreach ( $files as $id=>$file )
+ {
+ $file = $file['path'];
+ $data = $site_reader->get_element_update_attributes($file);
+ if ( !is_array($data) )
+ {
+ continue;
+ }
+
+ $data['id']=$id;
+ $datas[] = $data;
+ } // end foreach file
+
+ $counts['upd_elements'] = count($datas);
+ if (!$simulate and count($datas)>0 )
+ {
+ mass_updates(
+ IMAGES_TABLE,
+ // fields
+ array(
+ 'primary' => array('id'),
+ 'update' => $site_reader->get_update_attributes(),
+ ),
+ $datas
+ );
+ }
+ $template->append('footer_elements', '');
+ }// end if sync files
+}
+
+// +-----------------------------------------------------------------------+
+// | synchronize files |
+// +-----------------------------------------------------------------------+
+if (isset($_POST['submit'])
+ and ($_POST['sync'] == 'dirs' or $_POST['sync'] == 'files'))
+{
+ $template->assign(
+ 'update_result',
+ array(
+ 'NB_NEW_CATEGORIES'=>$counts['new_categories'],
+ 'NB_DEL_CATEGORIES'=>$counts['del_categories'],
+ 'NB_NEW_ELEMENTS'=>$counts['new_elements'],
+ 'NB_DEL_ELEMENTS'=>$counts['del_elements'],
+ 'NB_UPD_ELEMENTS'=>$counts['upd_elements'],
+ 'NB_ERRORS'=>count($errors),
+ ));
+}
+
+// +-----------------------------------------------------------------------+
+// | synchronize metadata |
+// +-----------------------------------------------------------------------+
+if (isset($_POST['submit']) and isset($_POST['sync_meta'])
+ and !$general_failure)
+{
+ // sync only never synchronized files ?
+ $opts['only_new'] = isset($_POST['meta_all']) ? false : true;
+ $opts['category_id'] = '';
+ $opts['recursive'] = true;
+
+ if (isset($_POST['cat']))
+ {
+ $opts['category_id'] = $_POST['cat'];
+ // recursive ?
+ if (!isset($_POST['subcats-included']) or $_POST['subcats-included'] != 1)
+ {
+ $opts['recursive'] = false;
+ }
+ }
+ $start = get_moment();
+ $files = get_filelist($opts['category_id'], $site_id,
+ $opts['recursive'],
+ $opts['only_new']);
+
+ $template->append('footer_elements', '');
+
+ $start = get_moment();
+ $datas = array();
+ $tags_of = array();
+
+ foreach ( $files as $id => $element_infos )
+ {
+ $data = $site_reader->get_element_metadata($element_infos);
+
+ if ( is_array($data) )
+ {
+ $data['date_metadata_update'] = CURRENT_DATE;
+ $data['id']=$id;
+ $datas[] = $data;
+
+ foreach (array('keywords', 'tags') as $key)
+ {
+ if (isset($data[$key]))
+ {
+ if (!isset($tags_of[$id]))
+ {
+ $tags_of[$id] = array();
+ }
+
+ foreach (explode(',', $data[$key]) as $tag_name)
+ {
+ $tags_of[$id][] = tag_id_from_tag_name($tag_name);
+ }
+ }
+ }
+ }
+ else
+ {
+ $errors[] = array(
+ 'path' => $element_infos['path'],
+ 'type' => 'PWG-ERROR-NO-FS'
+ );
+ }
+ }
+
+ if (!$simulate)
+ {
+ if (count($datas) > 0)
+ {
+ mass_updates(
+ IMAGES_TABLE,
+ // fields
+ array(
+ 'primary' => array('id'),
+ 'update' => array_unique(
+ array_merge(
+ array_diff(
+ $site_reader->get_metadata_attributes(),
+ // keywords and tags fields are managed separately
+ array('keywords', 'tags')
+ ),
+ array('date_metadata_update'))
+ )
+ ),
+ $datas,
+ isset($_POST['meta_empty_overrides']) ? 0 : MASS_UPDATES_SKIP_EMPTY
+ );
+ }
+ set_tags_of($tags_of);
+ }
+
+ $template->append('footer_elements', '');
+
+ $template->assign(
+ 'metadata_result',
+ array(
+ 'NB_ELEMENTS_DONE' => count($datas),
+ 'NB_ELEMENTS_CANDIDATES' => count($files),
+ 'NB_ERRORS' => count($errors),
+ ));
+}
+
+// +-----------------------------------------------------------------------+
+// | template initialization |
+// +-----------------------------------------------------------------------+
+$template->set_filenames(array('update'=>'site_update.tpl'));
+$result_title = '';
+if (isset($simulate) and $simulate)
+{
+ $result_title.= '['.l10n('Simulation').'] ';
+}
+
+// used_metadata string is displayed to inform admin which metadata will be
+// used from files for synchronization
+$used_metadata = implode( ', ', $site_reader->get_metadata_attributes());
+if ($site_is_remote and !isset($_POST['submit']) )
+{
+ $used_metadata.= ' + ...';
+}
+
+$template->assign(
+ array(
+ 'SITE_URL'=>$site_url,
+ 'U_SITE_MANAGER'=> get_root_url().'admin.php?page=site_manager',
+ 'L_RESULT_UPDATE'=>$result_title.l10n('Search for new images in the directories'),
+ 'L_RESULT_METADATA'=>$result_title.l10n('Metadata synchronization results'),
+ 'METADATA_LIST' => $used_metadata,
+ 'U_HELP' => get_root_url().'admin/popuphelp.php?page=synchronize',
+ ));
+
+// +-----------------------------------------------------------------------+
+// | introduction : choices |
+// +-----------------------------------------------------------------------+
+if (isset($_POST['submit']))
+{
+ $tpl_introduction = array(
+ 'sync' => $_POST['sync'],
+ 'sync_meta' => isset($_POST['sync_meta']) ? true : false,
+ 'display_info' => isset($_POST['display_info']) and $_POST['display_info']==1,
+ 'add_to_caddie' => isset($_POST['add_to_caddie']) and $_POST['add_to_caddie']==1,
+ 'subcats_included' => isset($_POST['subcats-included']) and $_POST['subcats-included']==1,
+ 'privacy_level_selected' => (int)@$_POST['privacy_level'],
+ 'meta_all' => isset($_POST['meta_all']) ? true : false,
+ 'meta_empty_overrides' => isset($_POST['meta_empty_overrides']) ? true : false,
+ );
+
+ if (isset($_POST['cat']) and is_numeric($_POST['cat']))
+ {
+ $cat_selected = array($_POST['cat']);
+ }
+ else
+ {
+ $cat_selected = array();
+ }
+}
+else
+{
+ $tpl_introduction = array(
+ 'sync' => 'dirs',
+ 'sync_meta' => true,
+ 'display_info' => false,
+ 'add_to_caddie' => false,
+ 'subcats_included' => true,
+ 'privacy_level_selected' => 0,
+ 'meta_all' => false,
+ 'meta_empty_overrides' => false,
+ );
+
+ $cat_selected = array();
+
+ if (isset($_GET['cat_id']))
+ {
+ check_input_parameter('cat_id', $_GET, false, PATTERN_ID);
+
+ $cat_selected = array($_GET['cat_id']);
+ $tpl_introduction['sync'] = 'files';
+ }
+}
+
+$tpl_introduction['privacy_level_options'] = get_privacy_level_options();
+
+$template->assign('introduction', $tpl_introduction);
+
+$query = '
+SELECT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.'
+ WHERE site_id = '.$site_id;
+display_select_cat_wrapper($query,
+ $cat_selected,
+ 'category_options',
+ false);
+
+
+if (count($errors) > 0)
+{
+ foreach ($errors as $error)
+ {
+ $template->append(
+ 'sync_errors',
+ array(
+ 'ELEMENT' => $error['path'],
+ 'LABEL' => $error['type'].' ('.$error_labels[$error['type']][0].')'
+ ));
+ }
+
+ foreach ($error_labels as $error_type=>$error_description)
+ {
+ $template->append(
+ 'sync_error_captions',
+ array(
+ 'TYPE' => $error_type,
+ 'LABEL' => $error_description[1]
+ ));
+ }
+}
+
+if (count($infos) > 0
+ and isset($_POST['display_info'])
+ and $_POST['display_info'] == 1)
+{
+ foreach ($infos as $info)
+ {
+ $template->append(
+ 'sync_infos',
+ array(
+ 'ELEMENT' => $info['path'],
+ 'LABEL' => $info['info']
+ ));
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'update');
+?>
diff --git a/sources/admin/stats.php b/sources/admin/stats.php
new file mode 100644
index 0000000..e103c2e
--- /dev/null
+++ b/sources/admin/stats.php
@@ -0,0 +1,502 @@
+ $max_id)
+ {
+ $max_id = $row['max_id'];
+ }
+
+ if ($is_first)
+ {
+ $is_first = false;
+ $first_time_key = $time_keys[3];
+ }
+}
+
+// Only the oldest time_key might be already summarized, so we have to
+// update the 4 corresponding lines instead of simply inserting them.
+//
+// For example, if the oldest unsummarized is 2005.08.25.21, the 4 lines
+// that can be updated are:
+//
+// +---------------+----------+
+// | id | nb_pages |
+// +---------------+----------+
+// | 2005 | 241109 |
+// | 2005-08 | 20133 |
+// | 2005-08-25 | 620 |
+// | 2005-08-25-21 | 151 |
+// +---------------+----------+
+
+
+$updates = array();
+$inserts = array();
+
+if (isset($first_time_key))
+{
+ list($year, $month, $day, $hour) = explode('-', $first_time_key);
+
+ $query = '
+SELECT *
+ FROM '.HISTORY_SUMMARY_TABLE.'
+ WHERE year='.$year.'
+ AND ( month IS NULL
+ OR ( month='.$month.'
+ AND ( day is NULL
+ OR (day='.$day.'
+ AND (hour IS NULL OR hour='.$hour.')
+ )
+ )
+ )
+ )
+;';
+ $result = pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $key = sprintf('%4u', $row['year']);
+ if ( isset($row['month']) )
+ {
+ $key .= sprintf('-%02u', $row['month']);
+ if ( isset($row['day']) )
+ {
+ $key .= sprintf('-%02u', $row['day']);
+ if ( isset($row['hour']) )
+ {
+ $key .= sprintf('-%02u', $row['hour']);
+ }
+ }
+ }
+
+ if (isset($need_update[$key]))
+ {
+ $row['nb_pages'] += $need_update[$key];
+ $updates[] = $row;
+ unset($need_update[$key]);
+ }
+ }
+}
+
+foreach ($need_update as $time_key => $nb_pages)
+{
+ $time_tokens = explode('-', $time_key);
+
+ $inserts[] = array(
+ 'year' => $time_tokens[0],
+ 'month' => @$time_tokens[1],
+ 'day' => @$time_tokens[2],
+ 'hour' => @$time_tokens[3],
+ 'nb_pages' => $nb_pages,
+ );
+}
+
+if (count($updates) > 0)
+{
+ mass_updates(
+ HISTORY_SUMMARY_TABLE,
+ array(
+ 'primary' => array('year','month','day','hour'),
+ 'update' => array('nb_pages'),
+ ),
+ $updates
+ );
+}
+
+if (count($inserts) > 0)
+{
+ mass_inserts(
+ HISTORY_SUMMARY_TABLE,
+ array_keys($inserts[0]),
+ $inserts
+ );
+}
+
+if ($max_id != 0)
+{
+ $query = '
+UPDATE '.HISTORY_TABLE.'
+ SET summarized = \'true\'
+ WHERE summarized = \'false\'
+ AND id <= '.$max_id.'
+;';
+ pwg_query($query);
+}
+
+// +-----------------------------------------------------------------------+
+// | Page parameters check |
+// +-----------------------------------------------------------------------+
+
+foreach (array('day', 'month', 'year') as $key)
+{
+ if (isset($_GET[$key]))
+ {
+ $page[$key] = (int)$_GET[$key];
+ }
+}
+
+if (isset($page['day']))
+{
+ if (!isset($page['month']))
+ {
+ die('month is missing in URL');
+ }
+}
+
+if (isset($page['month']))
+{
+ if (!isset($page['year']))
+ {
+ die('year is missing in URL');
+ }
+}
+
+$summary_lines = get_summary(
+ @$page['year'],
+ @$page['month'],
+ @$page['day']
+ );
+
+// +-----------------------------------------------------------------------+
+// | Display statistics header |
+// +-----------------------------------------------------------------------+
+
+// page title creation
+$title_parts = array();
+
+$url = PHPWG_ROOT_PATH.'admin.php?page=stats';
+
+$title_parts[] = ''.l10n('Overall').' ';
+
+$period_label = l10n('Year');
+
+if (isset($page['year']))
+{
+ $url.= '&year='.$page['year'];
+
+ $title_parts[] = ''.$page['year'].' ';
+
+ $period_label = l10n('Month');
+}
+
+if (isset($page['month']))
+{
+ $url.= '&month='.$page['month'];
+
+ $title_parts[] = ''.$lang['month'][$page['month']].' ';
+
+ $period_label = l10n('Day');
+}
+
+if (isset($page['day']))
+{
+ $url.= '&day='.$page['day'];
+
+ $time = mktime(12, 0, 0, $page['month'], $page['day'], $page['year']);
+
+ $day_title = sprintf(
+ '%u (%s)',
+ $page['day'],
+ $lang['day'][date('w', $time)]
+ );
+
+ $title_parts[] = ''.$day_title.' ';
+
+ $period_label = l10n('Hour');
+}
+
+$template->set_filename('stats', 'stats.tpl');
+
+// TabSheet initialization
+history_tabsheet();
+
+$base_url = get_root_url().'admin.php?page=history';
+
+$template->assign(
+ array(
+ 'L_STAT_TITLE' => implode($conf['level_separator'], $title_parts),
+ 'PERIOD_LABEL' => $period_label,
+ 'U_HELP' => get_root_url().'admin/popuphelp.php?page=history',
+ 'F_ACTION' => $base_url,
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | Display statistic rows |
+// +-----------------------------------------------------------------------+
+
+$max_width = 400;
+
+$datas = array();
+
+if (isset($page['day']))
+{
+ $key = 'hour';
+ $min_x = 0;
+ $max_x = 23;
+}
+elseif (isset($page['month']))
+{
+ $key = 'day';
+ $min_x = 1;
+ $max_x = date(
+ 't',
+ mktime(12, 0, 0, $page['month'], 1, $page['year'])
+ );
+}
+elseif (isset($page['year']))
+{
+ $key = 'month';
+ $min_x = 1;
+ $max_x = 12;
+}
+else
+{
+ $key = 'year';
+}
+
+$max_pages = 1;
+foreach ($summary_lines as $line)
+{
+ if ($line['nb_pages'] > $max_pages)
+ {
+ $max_pages = $line['nb_pages'];
+ }
+
+ $datas[ $line[$key] ] = $line['nb_pages'];
+}
+
+if (!isset($min_x) and !isset($max_x) and count($datas) > 0)
+{
+ $min_x = min(array_keys($datas));
+ $max_x = max(array_keys($datas));
+}
+
+if (count($datas) > 0)
+{
+ for ($i = $min_x; $i <= $max_x; $i++)
+ {
+ if (!isset($datas[$i]))
+ {
+ $datas[$i] = 0;
+ }
+
+ $url = null;
+
+ if (isset($page['day']))
+ {
+ $value = sprintf('%02u', $i);
+ }
+ else if (isset($page['month']))
+ {
+ $url =
+ get_root_url().'admin.php'
+ .'?page=stats'
+ .'&year='.$page['year']
+ .'&month='.$page['month']
+ .'&day='.$i
+ ;
+
+ $time = mktime(12, 0, 0, $page['month'], $i, $page['year']);
+
+ $value = $i.' ('.$lang['day'][date('w', $time)].')';
+ }
+ else if (isset($page['year']))
+ {
+ $url =
+ get_root_url().'admin.php'
+ .'?page=stats'
+ .'&year='.$page['year']
+ .'&month='.$i
+ ;
+
+ $value = $lang['month'][$i];
+ }
+ else
+ {
+ // at least the year is defined
+ $url =
+ get_root_url().'admin.php'
+ .'?page=stats'
+ .'&year='.$i
+ ;
+
+ $value = $i;
+ }
+
+ if ($datas[$i] != 0 and isset($url))
+ {
+ $value = ''.$value.' ';
+ }
+
+ $template->append(
+ 'statrows',
+ array(
+ 'VALUE' => $value,
+ 'PAGES' => $datas[$i],
+ 'WIDTH' => ceil(($datas[$i] * $max_width) / $max_pages ),
+ )
+ );
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | Sending html code |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'stats');
+?>
\ No newline at end of file
diff --git a/sources/admin/tags.php b/sources/admin/tags.php
new file mode 100644
index 0000000..a7c58f7
--- /dev/null
+++ b/sources/admin/tags.php
@@ -0,0 +1,457 @@
+ $tag_id,
+ 'name' => addslashes($tag_name),
+ 'url_name' => trigger_event('render_tag_url', $tag_name),
+ );
+ }
+ }
+ }
+ mass_updates(
+ TAGS_TABLE,
+ array(
+ 'primary' => array('id'),
+ 'update' => array('name', 'url_name'),
+ ),
+ $updates
+ );
+}
+// +-----------------------------------------------------------------------+
+// | dulicate tags |
+// +-----------------------------------------------------------------------+
+
+if (isset($_POST['duplic_submit']))
+{
+ $query = '
+SELECT name
+ FROM '.TAGS_TABLE.'
+;';
+ $existing_names = array_from_query($query, 'name');
+
+
+ $current_name_of = array();
+ $query = '
+SELECT id, name
+ FROM '.TAGS_TABLE.'
+ WHERE id IN ('.$_POST['edit_list'].')
+;';
+ $result = pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $current_name_of[ $row['id'] ] = $row['name'];
+ }
+
+ $updates = array();
+ // we must not rename tag with an already existing name
+ foreach (explode(',', $_POST['edit_list']) as $tag_id)
+ {
+ $tag_name = stripslashes($_POST['tag_name-'.$tag_id]);
+
+ if ($tag_name != $current_name_of[$tag_id])
+ {
+ if (in_array($tag_name, $existing_names))
+ {
+ $page['errors'][] = l10n('Tag "%s" already exists', $tag_name);
+ }
+ else if (!empty($tag_name))
+ {
+ single_insert(
+ TAGS_TABLE,
+ array(
+ 'name' => $tag_name,
+ 'url_name' => trigger_event('render_tag_url', $tag_name),
+ )
+ );
+
+ $query = '
+ SELECT id
+ FROM '.TAGS_TABLE.'
+ WHERE name = \''.$tag_name.'\'
+ ;';
+ $destination_tag = array_from_query($query, 'id');
+ $destination_tag_id = $destination_tag[0];
+
+ $query = '
+ SELECT
+ image_id
+ FROM '.IMAGE_TAG_TABLE.'
+ WHERE tag_id = '.$tag_id.'
+ ;';
+ $destination_tag_image_ids = array_from_query($query, 'image_id');
+
+ $inserts = array();
+ foreach ($destination_tag_image_ids as $image_id)
+ {
+ $inserts[] = array(
+ 'tag_id' => $destination_tag_id,
+ 'image_id' => $image_id
+ );
+ }
+
+ if (count($inserts) > 0)
+ {
+ mass_inserts(
+ IMAGE_TAG_TABLE,
+ array_keys($inserts[0]),
+ $inserts
+ );
+ }
+
+ $page['infos'][] = l10n(
+ 'Tag "%s" is now a duplicate of "%s"',
+ stripslashes($tag_name),
+ $current_name_of[$tag_id]
+ );
+ }
+ }
+ }
+
+ mass_updates(
+ TAGS_TABLE,
+ array(
+ 'primary' => array('id'),
+ 'update' => array('name', 'url_name'),
+ ),
+ $updates
+ );
+}
+
+// +-----------------------------------------------------------------------+
+// | merge tags |
+// +-----------------------------------------------------------------------+
+
+if (isset($_POST['merge_submit']))
+{
+ if (!isset($_POST['destination_tag']))
+ {
+ $page['errors'][] = l10n('No destination tag selected');
+ }
+ else
+ {
+ $destination_tag_id = $_POST['destination_tag'];
+ $tag_ids = explode(',', $_POST['merge_list']);
+
+ if (is_array($tag_ids) and count($tag_ids) > 1)
+ {
+ $name_of_tag = array();
+ $query = '
+SELECT
+ id,
+ name
+ FROM '.TAGS_TABLE.'
+ WHERE id IN ('.implode(',', $tag_ids).')
+;';
+ $result = pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $name_of_tag[ $row['id'] ] = trigger_event('render_tag_name', $row['name'], $row);
+ }
+
+ $tag_ids_to_delete = array_diff(
+ $tag_ids,
+ array($destination_tag_id)
+ );
+
+ $query = '
+SELECT
+ DISTINCT(image_id)
+ FROM '.IMAGE_TAG_TABLE.'
+ WHERE tag_id IN ('.implode(',', $tag_ids_to_delete).')
+;';
+ $image_ids = array_from_query($query, 'image_id');
+
+ delete_tags($tag_ids_to_delete);
+
+ $query = '
+SELECT
+ image_id
+ FROM '.IMAGE_TAG_TABLE.'
+ WHERE tag_id = '.$destination_tag_id.'
+;';
+ $destination_tag_image_ids = array_from_query($query, 'image_id');
+
+ $image_ids_to_link = array_diff(
+ $image_ids,
+ $destination_tag_image_ids
+ );
+
+ $inserts = array();
+ foreach ($image_ids_to_link as $image_id)
+ {
+ $inserts[] = array(
+ 'tag_id' => $destination_tag_id,
+ 'image_id' => $image_id
+ );
+ }
+
+ if (count($inserts) > 0)
+ {
+ mass_inserts(
+ IMAGE_TAG_TABLE,
+ array_keys($inserts[0]),
+ $inserts
+ );
+ }
+
+ $tags_deleted = array();
+ foreach ($tag_ids_to_delete as $tag_id)
+ {
+ $tags_deleted[] = $name_of_tag[$tag_id];
+ }
+
+ $page['infos'][] = l10n(
+ 'Tags %s merged into tag %s ',
+ implode(', ', $tags_deleted),
+ $name_of_tag[$destination_tag_id]
+ );
+ }
+ }
+}
+
+
+// +-----------------------------------------------------------------------+
+// | delete tags |
+// +-----------------------------------------------------------------------+
+
+if (isset($_POST['delete']) and isset($_POST['tags']))
+{
+ $query = '
+SELECT name
+ FROM '.TAGS_TABLE.'
+ WHERE id IN ('.implode(',', $_POST['tags']).')
+;';
+ $tag_names = array_from_query($query, 'name');
+
+ delete_tags($_POST['tags']);
+
+ $page['infos'][] = l10n_dec(
+ 'The following tag was deleted', 'The %d following tags were deleted',
+ count($tag_names)
+ )
+ .' : '.implode(', ', $tag_names);
+}
+
+// +-----------------------------------------------------------------------+
+// | delete orphan tags |
+// +-----------------------------------------------------------------------+
+
+if (isset($_GET['action']) and 'delete_orphans' == $_GET['action'])
+{
+ check_pwg_token();
+
+ delete_orphan_tags();
+ $_SESSION['page_infos'] = array(l10n('Orphan tags deleted'));
+ redirect(get_root_url().'admin.php?page=tags');
+}
+
+// +-----------------------------------------------------------------------+
+// | add a tag |
+// +-----------------------------------------------------------------------+
+
+if (isset($_POST['add']) and !empty($_POST['add_tag']))
+{
+ $ret = create_tag($_POST['add_tag']);
+
+ if (isset($ret['error']))
+ {
+ $page['errors'][] = $ret['error'];
+ }
+ else
+ {
+ $page['infos'][] = $ret['info'];
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+
+$template->set_filenames(array('tags' => 'tags.tpl'));
+
+$template->assign(
+ array(
+ 'F_ACTION' => PHPWG_ROOT_PATH.'admin.php?page=tags',
+ 'PWG_TOKEN' => get_pwg_token(),
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | orphan tags |
+// +-----------------------------------------------------------------------+
+
+$orphan_tags = get_orphan_tags();
+
+$orphan_tag_names = array();
+foreach ($orphan_tags as $tag)
+{
+ $orphan_tag_names[] = trigger_event('render_tag_name', $tag['name'], $tag);
+}
+
+if (count($orphan_tag_names) > 0)
+{
+ $page['warnings'][] = sprintf(
+ l10n('You have %d orphan tags: %s.').' '.l10n('Delete orphan tags').' ',
+ count($orphan_tag_names),
+ implode(', ', $orphan_tag_names),
+ get_root_url().'admin.php?page=tags&action=delete_orphans&pwg_token='.get_pwg_token()
+ );
+}
+
+// +-----------------------------------------------------------------------+
+// | form creation |
+// +-----------------------------------------------------------------------+
+
+
+// tag counters
+$query = '
+SELECT tag_id, COUNT(image_id) AS counter
+ FROM '.IMAGE_TAG_TABLE.'
+ GROUP BY tag_id';
+$tag_counters = simple_hash_from_query($query, 'tag_id', 'counter');
+
+// all tags
+$query = '
+SELECT *
+ FROM '.TAGS_TABLE.'
+;';
+$result = pwg_query($query);
+$all_tags = array();
+while ($tag = pwg_db_fetch_assoc($result))
+{
+ $raw_name = $tag['name'];
+ $tag['name'] = trigger_event('render_tag_name', $raw_name, $tag);
+ $tag['counter'] = intval(@$tag_counters[ $tag['id'] ]);
+ $tag['U_VIEW'] = make_index_url(array('tags'=>array($tag)));
+ $tag['U_EDIT'] = 'admin.php?page=batch_manager&filter=tag-'.$tag['id'];
+
+ $alt_names = trigger_event('get_tag_alt_names', array(), $raw_name);
+ $alt_names = array_diff( array_unique($alt_names), array($tag['name']) );
+ if (count($alt_names))
+ {
+ $tag['alt_names'] = implode(', ', $alt_names);
+ }
+ $all_tags[] = $tag;
+}
+usort($all_tags, 'tag_alpha_compare');
+
+
+
+$template->assign(
+ array(
+ 'all_tags' => $all_tags,
+ )
+ );
+
+if ((isset($_POST['edit']) or isset($_POST['duplicate']) or isset($_POST['merge'])) and isset($_POST['tags']))
+{
+ $list_name = 'EDIT_TAGS_LIST';
+ if (isset($_POST['duplicate']))
+ {
+ $list_name = 'DUPLIC_TAGS_LIST';
+ }
+ elseif (isset($_POST['merge']))
+ {
+ $list_name = 'MERGE_TAGS_LIST';
+ }
+
+ $template->assign($list_name, implode(',', $_POST['tags']));
+
+ $query = '
+SELECT id, name
+ FROM '.TAGS_TABLE.'
+ WHERE id IN ('.implode(',', $_POST['tags']).')
+;';
+ $result = pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $template->append(
+ 'tags',
+ array(
+ 'ID' => $row['id'],
+ 'NAME' => $row['name'],
+ )
+ );
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'tags');
+
+?>
diff --git a/sources/admin/theme.php b/sources/admin/theme.php
new file mode 100644
index 0000000..984ac45
--- /dev/null
+++ b/sources/admin/theme.php
@@ -0,0 +1,53 @@
+fs_themes)))
+{
+ die('Invalid theme');
+}
+
+$filename = PHPWG_THEMES_PATH.$_GET['theme'].'/admin/admin.inc.php';
+if (is_file($filename))
+{
+ include_once($filename);
+}
+else
+{
+ die('Missing file '.$filename);
+}
+?>
\ No newline at end of file
diff --git a/sources/admin/themes.php b/sources/admin/themes.php
new file mode 100644
index 0000000..5359974
--- /dev/null
+++ b/sources/admin/themes.php
@@ -0,0 +1,48 @@
+set_id('themes');
+$tabsheet->select($page['tab']);
+$tabsheet->assign();
+
+if ($page['tab'] == 'update')
+ include(PHPWG_ROOT_PATH.'admin/updates_ext.php');
+else
+ include(PHPWG_ROOT_PATH.'admin/themes_'.$page['tab'].'.php');
+
+?>
\ No newline at end of file
diff --git a/sources/admin/themes/clear/icon/cat_move.png b/sources/admin/themes/clear/icon/cat_move.png
new file mode 100644
index 0000000..b3f9376
Binary files /dev/null and b/sources/admin/themes/clear/icon/cat_move.png differ
diff --git a/sources/admin/themes/clear/icon/category_children.png b/sources/admin/themes/clear/icon/category_children.png
new file mode 100644
index 0000000..7205c3f
Binary files /dev/null and b/sources/admin/themes/clear/icon/category_children.png differ
diff --git a/sources/admin/themes/clear/icon/category_delete.png b/sources/admin/themes/clear/icon/category_delete.png
new file mode 100644
index 0000000..da796da
Binary files /dev/null and b/sources/admin/themes/clear/icon/category_delete.png differ
diff --git a/sources/admin/themes/clear/icon/category_edit.png b/sources/admin/themes/clear/icon/category_edit.png
new file mode 100644
index 0000000..0f3b619
Binary files /dev/null and b/sources/admin/themes/clear/icon/category_edit.png differ
diff --git a/sources/admin/themes/clear/icon/category_elements.png b/sources/admin/themes/clear/icon/category_elements.png
new file mode 100644
index 0000000..1038e51
Binary files /dev/null and b/sources/admin/themes/clear/icon/category_elements.png differ
diff --git a/sources/admin/themes/clear/icon/category_jump-to.png b/sources/admin/themes/clear/icon/category_jump-to.png
new file mode 100644
index 0000000..62db1f4
Binary files /dev/null and b/sources/admin/themes/clear/icon/category_jump-to.png differ
diff --git a/sources/admin/themes/clear/icon/category_permissions.png b/sources/admin/themes/clear/icon/category_permissions.png
new file mode 100644
index 0000000..4780841
Binary files /dev/null and b/sources/admin/themes/clear/icon/category_permissions.png differ
diff --git a/sources/admin/themes/clear/icon/category_representant_random.png b/sources/admin/themes/clear/icon/category_representant_random.png
new file mode 100644
index 0000000..dfa6984
Binary files /dev/null and b/sources/admin/themes/clear/icon/category_representant_random.png differ
diff --git a/sources/admin/themes/clear/icon/check.png b/sources/admin/themes/clear/icon/check.png
new file mode 100644
index 0000000..d9abf27
Binary files /dev/null and b/sources/admin/themes/clear/icon/check.png differ
diff --git a/sources/admin/themes/clear/icon/datepicker.png b/sources/admin/themes/clear/icon/datepicker.png
new file mode 100644
index 0000000..68c5256
Binary files /dev/null and b/sources/admin/themes/clear/icon/datepicker.png differ
diff --git a/sources/admin/themes/clear/icon/delete.png b/sources/admin/themes/clear/icon/delete.png
new file mode 100644
index 0000000..94e2c97
Binary files /dev/null and b/sources/admin/themes/clear/icon/delete.png differ
diff --git a/sources/admin/themes/clear/icon/edit_s.png b/sources/admin/themes/clear/icon/edit_s.png
new file mode 100644
index 0000000..e4951d2
Binary files /dev/null and b/sources/admin/themes/clear/icon/edit_s.png differ
diff --git a/sources/admin/themes/clear/icon/exit.png b/sources/admin/themes/clear/icon/exit.png
new file mode 100644
index 0000000..2aafbb4
Binary files /dev/null and b/sources/admin/themes/clear/icon/exit.png differ
diff --git a/sources/admin/themes/clear/icon/help.png b/sources/admin/themes/clear/icon/help.png
new file mode 100644
index 0000000..b3a7dad
Binary files /dev/null and b/sources/admin/themes/clear/icon/help.png differ
diff --git a/sources/admin/themes/clear/icon/home.png b/sources/admin/themes/clear/icon/home.png
new file mode 100644
index 0000000..16adac2
Binary files /dev/null and b/sources/admin/themes/clear/icon/home.png differ
diff --git a/sources/admin/themes/clear/icon/index.php b/sources/admin/themes/clear/icon/index.php
new file mode 100644
index 0000000..e336595
--- /dev/null
+++ b/sources/admin/themes/clear/icon/index.php
@@ -0,0 +1,30 @@
+
diff --git a/sources/admin/themes/clear/icon/infos.png b/sources/admin/themes/clear/icon/infos.png
new file mode 100644
index 0000000..dc8c8a4
Binary files /dev/null and b/sources/admin/themes/clear/icon/infos.png differ
diff --git a/sources/admin/themes/clear/icon/minus.gif b/sources/admin/themes/clear/icon/minus.gif
new file mode 100644
index 0000000..154e45a
Binary files /dev/null and b/sources/admin/themes/clear/icon/minus.gif differ
diff --git a/sources/admin/themes/clear/icon/next.png b/sources/admin/themes/clear/icon/next.png
new file mode 100644
index 0000000..32587c4
Binary files /dev/null and b/sources/admin/themes/clear/icon/next.png differ
diff --git a/sources/admin/themes/clear/icon/page_end.png b/sources/admin/themes/clear/icon/page_end.png
new file mode 100644
index 0000000..c8f54fb
Binary files /dev/null and b/sources/admin/themes/clear/icon/page_end.png differ
diff --git a/sources/admin/themes/clear/icon/page_top.png b/sources/admin/themes/clear/icon/page_top.png
new file mode 100644
index 0000000..8b18f13
Binary files /dev/null and b/sources/admin/themes/clear/icon/page_top.png differ
diff --git a/sources/admin/themes/clear/icon/permissions.png b/sources/admin/themes/clear/icon/permissions.png
new file mode 100644
index 0000000..2ebc4f6
Binary files /dev/null and b/sources/admin/themes/clear/icon/permissions.png differ
diff --git a/sources/admin/themes/clear/icon/plus.gif b/sources/admin/themes/clear/icon/plus.gif
new file mode 100644
index 0000000..93d2ade
Binary files /dev/null and b/sources/admin/themes/clear/icon/plus.gif differ
diff --git a/sources/admin/themes/clear/icon/preferences.png b/sources/admin/themes/clear/icon/preferences.png
new file mode 100644
index 0000000..3039334
Binary files /dev/null and b/sources/admin/themes/clear/icon/preferences.png differ
diff --git a/sources/admin/themes/clear/icon/prev.png b/sources/admin/themes/clear/icon/prev.png
new file mode 100644
index 0000000..474fbb7
Binary files /dev/null and b/sources/admin/themes/clear/icon/prev.png differ
diff --git a/sources/admin/themes/clear/icon/remove_filter.png b/sources/admin/themes/clear/icon/remove_filter.png
new file mode 100644
index 0000000..c2fa738
Binary files /dev/null and b/sources/admin/themes/clear/icon/remove_filter.png differ
diff --git a/sources/admin/themes/clear/icon/remove_filter_hover.png b/sources/admin/themes/clear/icon/remove_filter_hover.png
new file mode 100644
index 0000000..1f9d975
Binary files /dev/null and b/sources/admin/themes/clear/icon/remove_filter_hover.png differ
diff --git a/sources/admin/themes/clear/icon/toggle_is_default_group.png b/sources/admin/themes/clear/icon/toggle_is_default_group.png
new file mode 100644
index 0000000..0d58e79
Binary files /dev/null and b/sources/admin/themes/clear/icon/toggle_is_default_group.png differ
diff --git a/sources/admin/themes/clear/icon/uncheck.png b/sources/admin/themes/clear/icon/uncheck.png
new file mode 100644
index 0000000..2f7df35
Binary files /dev/null and b/sources/admin/themes/clear/icon/uncheck.png differ
diff --git a/sources/admin/themes/clear/icon/validate_s.png b/sources/admin/themes/clear/icon/validate_s.png
new file mode 100644
index 0000000..400bf8a
Binary files /dev/null and b/sources/admin/themes/clear/icon/validate_s.png differ
diff --git a/sources/admin/themes/clear/icon/virt_category.png b/sources/admin/themes/clear/icon/virt_category.png
new file mode 100644
index 0000000..15f4f22
Binary files /dev/null and b/sources/admin/themes/clear/icon/virt_category.png differ
diff --git a/sources/admin/themes/clear/icon/warning.png b/sources/admin/themes/clear/icon/warning.png
new file mode 100644
index 0000000..060b577
Binary files /dev/null and b/sources/admin/themes/clear/icon/warning.png differ
diff --git a/sources/admin/themes/clear/images/external_active.png b/sources/admin/themes/clear/images/external_active.png
new file mode 100644
index 0000000..9bbccfa
Binary files /dev/null and b/sources/admin/themes/clear/images/external_active.png differ
diff --git a/sources/admin/themes/clear/images/external_inactive.png b/sources/admin/themes/clear/images/external_inactive.png
new file mode 100644
index 0000000..5ed8db2
Binary files /dev/null and b/sources/admin/themes/clear/images/external_inactive.png differ
diff --git a/sources/admin/themes/clear/images/missing_screenshot.png b/sources/admin/themes/clear/images/missing_screenshot.png
new file mode 100644
index 0000000..4c67f61
Binary files /dev/null and b/sources/admin/themes/clear/images/missing_screenshot.png differ
diff --git a/sources/admin/themes/clear/images/piwigo_logo_big.png b/sources/admin/themes/clear/images/piwigo_logo_big.png
new file mode 100644
index 0000000..814c1c9
Binary files /dev/null and b/sources/admin/themes/clear/images/piwigo_logo_big.png differ
diff --git a/sources/admin/themes/clear/images/piwigo_logo_small.png b/sources/admin/themes/clear/images/piwigo_logo_small.png
new file mode 100644
index 0000000..09c679a
Binary files /dev/null and b/sources/admin/themes/clear/images/piwigo_logo_small.png differ
diff --git a/sources/admin/themes/clear/images/quickLocalSync.png b/sources/admin/themes/clear/images/quickLocalSync.png
new file mode 100644
index 0000000..daf75e6
Binary files /dev/null and b/sources/admin/themes/clear/images/quickLocalSync.png differ
diff --git a/sources/admin/themes/clear/images/resizable-e.gif b/sources/admin/themes/clear/images/resizable-e.gif
new file mode 100644
index 0000000..32d05b9
Binary files /dev/null and b/sources/admin/themes/clear/images/resizable-e.gif differ
diff --git a/sources/admin/themes/clear/images/resizable-n.gif b/sources/admin/themes/clear/images/resizable-n.gif
new file mode 100644
index 0000000..6a62dc8
Binary files /dev/null and b/sources/admin/themes/clear/images/resizable-n.gif differ
diff --git a/sources/admin/themes/clear/images/resizable-ne.gif b/sources/admin/themes/clear/images/resizable-ne.gif
new file mode 100644
index 0000000..09ad9fc
Binary files /dev/null and b/sources/admin/themes/clear/images/resizable-ne.gif differ
diff --git a/sources/admin/themes/clear/images/resizable-nw.gif b/sources/admin/themes/clear/images/resizable-nw.gif
new file mode 100644
index 0000000..c33bd0f
Binary files /dev/null and b/sources/admin/themes/clear/images/resizable-nw.gif differ
diff --git a/sources/admin/themes/clear/images/resizable-s.gif b/sources/admin/themes/clear/images/resizable-s.gif
new file mode 100644
index 0000000..b1d72c5
Binary files /dev/null and b/sources/admin/themes/clear/images/resizable-s.gif differ
diff --git a/sources/admin/themes/clear/images/resizable-se.gif b/sources/admin/themes/clear/images/resizable-se.gif
new file mode 100644
index 0000000..ad6bd1f
Binary files /dev/null and b/sources/admin/themes/clear/images/resizable-se.gif differ
diff --git a/sources/admin/themes/clear/images/resizable-sw.gif b/sources/admin/themes/clear/images/resizable-sw.gif
new file mode 100644
index 0000000..5bf25ce
Binary files /dev/null and b/sources/admin/themes/clear/images/resizable-sw.gif differ
diff --git a/sources/admin/themes/clear/images/resizable-w.gif b/sources/admin/themes/clear/images/resizable-w.gif
new file mode 100644
index 0000000..e4ed876
Binary files /dev/null and b/sources/admin/themes/clear/images/resizable-w.gif differ
diff --git a/sources/admin/themes/clear/images/stripe-cat.png b/sources/admin/themes/clear/images/stripe-cat.png
new file mode 100644
index 0000000..126c888
Binary files /dev/null and b/sources/admin/themes/clear/images/stripe-cat.png differ
diff --git a/sources/admin/themes/clear/images/transparent.gif b/sources/admin/themes/clear/images/transparent.gif
new file mode 100644
index 0000000..f419cd4
Binary files /dev/null and b/sources/admin/themes/clear/images/transparent.gif differ
diff --git a/sources/admin/themes/clear/index.php b/sources/admin/themes/clear/index.php
new file mode 100644
index 0000000..d959f2c
--- /dev/null
+++ b/sources/admin/themes/clear/index.php
@@ -0,0 +1,54 @@
+
diff --git a/sources/admin/themes/clear/mail-css.tpl b/sources/admin/themes/clear/mail-css.tpl
new file mode 100644
index 0000000..cfe148b
--- /dev/null
+++ b/sources/admin/themes/clear/mail-css.tpl
@@ -0,0 +1,19 @@
+{* $Id: mail-css.tpl 2526 2008-09-14 00:33:53Z vdigital $ *}
+/* Theme wipi mail css */
+
+body {ldelim} background-color:#111; color:#69c;}
+#the_page {ldelim} background: #111 url({$ROOT_URL}template/{$themeconf.template}/mail/text/html/images/mailbody-bg.png) repeat-y scroll left top;}
+#content {ldelim} background: transparent url({$ROOT_URL}template/{$themeconf.template}/mail/text/html/images/header-bg.png) no-repeat scroll left top;}
+#copyright {ldelim} background: transparent url({$ROOT_URL}template/{$themeconf.template}/mail/text/html/images/footer-bg.png) no-repeat scroll left bottom;
+color: #69c;}
+h2 {ldelim} background-color: #222;color:#eee;background-image: url({$ROOT_URL}template/{$themeconf.template}/themes/{$themeconf.theme}/images/tableh1_bg.png);}
+img {ldelim} margin: 16px; padding:15px;border:1px solid #eee; -moz-border-radius: 4px; border-radius: 4px 4px; }
+img:hover {ldelim} border:1px solid #69c; -moz-border-radius: 4px; border-radius: 4px 4px; }
+a {ldelim} color: #69c; background: transparent; }
+a:hover {ldelim} color: #f92; }
+a.PWG {ldelim} border: 0px; }
+a.PWG .P {ldelim} color : #f92; }
+a.PWG .W {ldelim} color : #aaa; }
+a.PWG .G {ldelim} color : #69c; }
+a.PWG:hover .P {ldelim} color : #69c; }
+a.PWG:hover .G {ldelim} color : #f92; }
diff --git a/sources/admin/themes/clear/theme.css b/sources/admin/themes/clear/theme.css
new file mode 100644
index 0000000..c481771
--- /dev/null
+++ b/sources/admin/themes/clear/theme.css
@@ -0,0 +1,367 @@
+/* generic colors - clear
+.gcText { color: #777777; } used for text
+.gcText2 { color: #777777; } used for table header
+.gcText3 { color: #555555; } used for tabs text
+.gcTextInput { color:#666666; } used for inputs
+.gcLink { color: #005E89; } used for link
+.gcLinkHover { color: #D54E21; } used for link
+.gcBgPage { background-color: #F9F9F9; } used for page background
+.gcBgTabSheet { background-color: #F1F1F1; } used inside tabsheet & menu
+.gcBgTableRow1 { background-color: #DDDDDD; } used in for tables rows background
+.gcBgTableRow2 { background-color: #EEEEEE; } used in tables rows & some widgets background
+.gcBgInput { background-color:#CCCCCC; } used for inputs
+.gcBorder { border-color: #AAAAAA; } used for borders
+.gcBorder2 { border-color: #666666; } used for widgets borders
+.gcBorderInput { border-color:#777777; } used for inputs
+*/
+/* text color */
+
+body, h3, dt, h2, .throw, .content, label , LEGEND {
+ color:#777;
+}
+
+th { color: #888; }
+INPUT, select, textarea { color:#666; background-color: #ccc; }
+option[disabled] { background-color: #ccc; }
+input[type="radio"], input[type="checkbox"] { background-color: transparent; }
+
+
+
+INPUT[type="text"], INPUT[type="password"], INPUT[type="file"] {
+ background-color: #ddd;
+}
+
+SELECT, TEXTAREA {
+ background-color: #ddd;
+}
+
+INPUT:focus, SELECT:focus, TEXTAREA:focus {
+ background-color: #eee;
+ color: #666;
+}
+
+INPUT, SELECT, TEXTAREA { border: 1px solid #999; }
+
+.showInfo { color:#999; }
+.showInfo:hover { color:#333; }
+
+
+#copyright { color:#777; margin:5px auto 0px 240px; text-align:center;
+padding-bottom: 15px; }
+.header_notes {
+ background: transparent url(../default/icon/note.png) no-repeat right top;
+ border: none;
+ min-height: 48px;
+ padding: 15px 60px 0 0;
+ right: 0;
+ width: 550px;
+ top: 70px;
+}
+html, body {
+ min-height: 100%;
+ background-color: #f9f9f9;
+}
+
+/* #the_page { margin: 0; padding: 0; z-index: 1; top:0px; */
+/* padding-top: 0; min-height: 100%; width:100%; position:absolute; left:0px; } */
+h3, .throw, .row1 { background-color: #ddd; }
+.row2 { background-color:#eeeeee; }
+
+#content {
+ background: #f1f1f1;
+ border: 1px solid #aaa;
+ min-height: 467px;
+ margin-left:217px;
+ margin-top: 7px;
+}
+
+.content h3 { font-size:20px; letter-spacing:-0.4px; margin:0 20px 12px 0;
+ text-align:center; background:none; border: 0; }
+.content h3 ~ h3{ margin-top: 40px; }
+.content h4 { color: #aaa; font-size:14px; text-align:center; padding:3px; margin-top: 0; margin-bottom: 10px;}
+
+.content dl, dd { margin:5px; }
+.content div.titrePage { height:40px; }
+
+
+UL.thumbnails span.wrap2:hover { background-color:#7CBA0F; color:#999; }
+UL.thumbnails span.wrap2 {
+ background-color:#ddd;
+}
+
+
+/* borders */
+fieldset { border: 2px solid #ddd; }
+TEXTAREA { cursor:text; font-size: 13px; }
+.tagSelection label { padding-right:12px; }
+.tagSelection LI.tagSelected {background-color:#dbe8f3;}
+.tagSelected LABEL {color:black;}
+
+ul.thumbnails input { color:#666; font-size:10px; margin:0; background-color:#bbb; text-align: center;}
+.throw { font-size: 120%; line-height: 26px; padding-top: 10px; font-weight: bold; }
+label { cursor:pointer }
+.categoryLi, .menuLi { background: #ddd }
+.menuLi_hidden { background: #ccc !important; }
+a.Piwigo {
+ font-family: verdana, arial, helvetica, sans-serif !important;
+ font-size: 11px; font-weight: normal; letter-spacing: 0;
+ color : #0cc; text-decoration: none; border: 0; }
+a.Piwigo:hover span.Piwigo {
+ border-bottom: 1px solid #0cc9c;
+}
+TABLE.table2 { border: 2px solid #dddddd; }
+
+.categoryUl>li:not(.virtual_cat) {
+ background:url(images/stripe-cat.png);
+}
+
+*, *:focus, *:active, input:active, a:active, input:focus, a:focus { outline: none; -moz-outline-width: 0px; }
+A{
+ color:#005E89;
+}
+
+A:hover, A:active {
+ color: #d54e21;
+ cursor: pointer;
+}
+
+.content .navigationBar {color:#999;}
+ul.actions, .content form#waiting {text-align:center;}
+
+.header_msgs {
+ border:0;
+ color:#ccc;
+ font-size:24px;
+ height:30px;
+ margin:0; padding:10px 0 0;
+ text-align:center; width:100%;
+}
+
+/* tabsheets are often used in admin pages => No specific css files */
+#tabsheet { width:auto; margin:-1px; margin-right:-6px; padding:0;
+border:1px solid #f9f9f9; border-bottom:1px solid #aaa; background-color:#f9f9f9;margin-top:-7px;}
+.tabsheet { display:table; white-space:nowrap; padding-left:10px; margin:0; width:auto;
+font-family:verdana,arial,helvetica,sans-serif; font-size:8px;
+list-style-type:none; list-style-image:none; text-decoration:none; }
+.tabsheet li { background:#f1f1f1; float:left; margin:0 6px 0 0; overflow:hidden; text-align:right; border:1px solid #aaa; border-bottom:none; position:relative; top:11px; -moz-border-radius-topleft:5px; -moz-border-radius-topright:5px;-webkit-border-top-left-radius:5px; -webkit-border-top-right-radius:5px;border-radius:5px 5px 0 0;}
+.tabsheet a {
+display:block; font-size:11px; border:0;
+font-weight:bold; overflow:hidden; padding:6px;
+text-align:right; text-decoration:none; margin: 0; }
+.tabsheet a:first-letter { text-transform:capitalize; }
+.tabsheet li.selected_tab {
+ border-bottom:1px solid #f1f1f1;
+}
+.tabsheet li:hover, .tabsheet li.selected_tab {
+margin-top:4px; padding-bottom:3px; padding-top:3px; top:1px;
+ }
+/* .tabsheet a:hover */
+.tabsheet li.selected_tab a { color: #555; }
+.tabsheet a span { font-size:10px; margin-right:2px; padding:0 3px 0 1px; }
+
+.sort { clear: none; }
+/* menubar is on all admin pages => No specific css file */
+#menubar {
+ padding:0; width:207px; z-index:99; text-align: left;margin-top:7px;
+ margin-left:0;
+}
+
+#menubar ul.scroll { overflow-y:auto; max-height:500px;
+ /* Only IE family supports colored scrollbar */
+ scrollbar-face-color: #ddd; scrollbar-shadow-color: #bbb; scrollbar-highlight-color: #fff;
+ scrollbar-3dlight-color: #d6d6d6; scrollbar-darkshadow-color: #ccc; scrollbar-track-color: #eee;
+ scrollbar-arrow-color: #0cccc; }
+#menubar dd { margin: 0; padding: 0;}
+#menubar dl { width: 200px; border:0; margin: 0; padding: 0; display: block; min-height:35px; border:1px solid #ddd; background-color:#f1f1f1; border-left:none; border-right:1px solid #ddd;}
+#menubar dl.first {border-top:none;border-radius:0 6px 0 0;}
+#menubar dl.first dt {border-radius:0 6px 0 0;}
+#menubar dl.last {border-bottom:1px solid #ddd; border-radius:0 0 6px 0;}
+#menubar dt {
+ background-color: #ddd; margin: 0; display: block; font-weight:bold;
+ position:relative; padding: 5px 1px 4px 5px; font-size: 13px; color: #777;
+ cursor:pointer;
+}
+#menubar li { margin: 0; padding-left:10px; }
+#menubar li A { display:block; }
+#menubar li A:hover {color:black; text-decoration:none}
+#menubar li:hover {background-color:#dbe8f3;}
+#menubar ul { color: #ccc; margin:0; line-height: 25px;
+ list-style-type: none; list-style-position: inside; padding: 0; }
+#adminHome {background-color:#ddd;border-radius: 0 6px 6px 0;margin-left:0;margin-bottom:17px;}
+#adminHome:hover {background-color:#d0d0d0;}
+
+/* jQuery tooltips */
+.ui-tooltip {
+ color: #000;
+ background-color: #eee;
+}
+.cluetip-outer { border-color:#999; color: #777; background-color: #eee; }
+.cluetip-title { background-color: #ddd; }
+/* jQuery ui resizable */
+.ui-resizable { position: relative; }
+.ui-wrapper { border: 0; }
+.ui-wrapper input, .ui-wrapper textarea { border: 0; }
+/* jQuery tiptip */
+#tiptip_holder a { color:#42B0FF; }
+
+/* Global handle styles */
+.ui-resizable-handle { position: absolute; display: none; font-size: 0.1px; }
+.ui-resizable .ui-resizable-handle { display: block; }
+body .ui-resizable-disabled .ui-resizable-handle { display: none; } /* use 'body' to make it more specific (css order) */
+body .ui-resizable-autohide .ui-resizable-handle { display: none; } /* use 'body' to make it more specific (css order) */
+.ui-resizable-n { cursor: n-resize; height: 6px; width: 100%; top: 0px; left: 0px; background: transparent url(images/resizable-n.gif) no-repeat scroll center top; }
+.ui-resizable-s { cursor: s-resize; height: 6px; width: 100%; bottom: 0px; left: 0px; background: transparent url(images/resizable-s.gif) no-repeat scroll center top; }
+.ui-resizable-e { cursor: e-resize; width: 6px; right: 0px; top: 0px; height: 100%; background: transparent url(images/resizable-e.gif) no-repeat scroll right center; }
+.ui-resizable-w { cursor: w-resize; width: 6px; left: 0px; top: 0px; height: 100%; background: transparent url(images/resizable-w.gif) no-repeat scroll right center; }
+.ui-resizable-se { cursor: se-resize; width: 9px; height: 9px; right: 0px; bottom: 0px; background: transparent url(images/resizable-se.gif); }
+.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: 0px; bottom: 0px; background: transparent url(images/resizable-sw.gif); }
+.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: 0px; top: 0px; background: transparent url(images/resizable-nw.gif); }
+.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: 0px; top: 0px; background: transparent url(images/resizable-ne.gif); }
+
+#ui-datepicker-div {background-color: #f1f1f1; border:2px solid #aaa;}
+#ui-datepicker-div .ui-datepicker-title {color:black;}
+#ui-datepicker-div .ui-icon-circle-triangle-w {background: transparent url(icon/prev.png) no-repeat;}
+#ui-datepicker-div .ui-icon-circle-triangle-e {background: transparent url(icon/next.png) no-repeat;}
+#ui-datepicker-div .ui-datepicker-control, #ui-datepicker-div a {background-color: #FFFFFF !important;}
+#ui-datepicker-div A.ui-datepicker-prev, #ui-datepicker-div A.ui-datepicker-next {
+ background-color:transparent !important;
+}
+#ui-datepicker-div A.ui-datepicker-prev:hover,
+#ui-datepicker-div A.ui-datepicker-next:hover
+{
+ background-color:#eee !important;border:1px solid #aaa;
+}
+
+#ui-datepicker-div .ui-datepicker-control, #ui-datepicker-div a { background-color: #eee; color: #08f !important;}
+#ui-datepicker-div .ui-datepicker-days-row { background-color: #bbb;}
+#ui-datepicker-div .ui-datepicker-week-end-cell, #ui-datepicker-div .ui-datepicker-week-end-cell a { background-color: #ddd; color: #0cc; border-color: #bbb; border-top-color: #ddd; border-left-color: #ddd;}
+#ui-datepicker-div .ui-datepicker-title-row { background-color: #eee;}
+#ui-datepicker-div .ui-datepicker-title-row .ui-datepicker-week-end-cell a { color: #0cc;}
+#ui-datepicker-div a:hover { color: #0cccc !important; background-color: #eee !important;}
+#ui-datepicker-div .ui-datepicker-header select { background-color:#bbb; color:#666; font-weight:normal;}
+#ui-datepicker-div .ui-datepicker-links { background-color:#ddd;}
+#ui-datepicker-div .ui-datepicker-header { background-color:#bbb;}
+#ui-datepicker-div .ui-datepicker-current-day { background-color: #eee !important; border-color: #bbb; border-top-color: #eee; border-left-color: #eee;}
+#ui-datepicker-div .ui-datepicker-days-cell { background-color:#bbb; border:1px solid #ddd; border-top-color: #bbb; border-left-color: #bbb;}
+img.ui-datepicker-trigger{margin:-2px 10px 1px -2px;}
+
+.ui-slider { background:#fff;border-radius:2px;border:none; }
+.ui-slider .ui-slider-range { background:#aaa;border-radius:2px; }
+
+.ui-slider .ui-slider-handle {
+ background-color: #eee;
+ border: 1px solid #aaa;
+ border-radius: 3px;
+}
+
+INPUT[type="text"].large { width: 317px; }
+
+.bigbutton {background:none;margin-right:-5px; padding: 10px;height:auto;position:static;width:auto;text-align:right;}
+.bigtext {display:none;position:static;}
+.bigbutton input[type="submit"] {display:inline;position:static;height:auto;width:auto;font-size:18px;padding:10px;}
+
+
+/* hacks */
+* html[lang="en"] body .content h2 , *+html[lang="en"] body .content h2 { text-transform:capitalize; } /* IE */
+*+html .bigtext { left: 70px; }
+*+html .bigbutton input, * html .bigbutton input { left:0px; position:relative; top:-40px; }
+* html .bigtext { margin-right: 300px; }
+
+.themeBox {background-color:#ddd;}
+.themeBox IMG {border:1px solid white;}
+.themeName {color:black;}
+#themesContent H3 {border-bottom:1px solid #aaa;}
+.themeDefault {background-color:#dbe8f3;}
+
+#pluginsMenuSeparator {border:1px solid #ddd;}
+
+#pwgHead, #footer {
+ background-color: #ddd;
+ color: #666;
+}
+
+
+#pwgHead A, #footer A {color:#444;}
+#pwgHead A:hover, #footer A:hover {color:black;}
+#footer { background-image: url(images/piwigo_logo_small.png);}
+
+.pluginBox, .pluginMiniBox, .groups li {background-color:#ddd;color:#353535;border-color:#ddd;}
+.pluginBoxNameCell, .pluginMiniBoxNameCell {color:#111;}
+.pluginBox.incompatible, .pluginMiniBox.incompatible {border-color:#a00 !important;}
+.pluginBoxes .merged, .pluginBoxes .missing {background-color:#d99;border:1px solid #a00;}
+
+.languageBox {background-color:#ddd;}
+.languageName {color:black;}
+.languageDefault {background-color:#dbe8f3;}
+
+UL.thumbnails li.rank-of-image {background-color: #ddd;}
+
+#batchManagerGlobal .thumbSelected {background-color:#C2F5C2 !important}
+#batchManagerGlobal #selectedMessage {background-color:#C2F5C2;}
+#filter_dimension_width_info, #filter_dimension_height_info, #filter_dimension_ratio_info {color:#ff7700;font-weight:bold;}
+
+.selectedComment {background-color:#C2F5C2;}
+
+#pwgMain {padding-left:0}
+
+.buttonLike, input[type="submit"], input[type="button"], input[type="reset"] {
+ font-size:12px;
+ font-weight:bold;
+ letter-spacing:1px;
+ border:none;
+ background-color:#666666;
+ color:#fff;
+ padding:2px 5px;
+ -moz-border-radius:5px;
+ -webkit-border-radius:5px;
+ border-radius:5px;
+ margin-left:0;
+}
+
+.buttonLike:hover, input[type="submit"]:hover, input[type="button"]:hover, input[type="reset"]:hover {
+ cursor: pointer;
+ background-color: #ff7700;
+ color: white;
+ text-decoration: none;
+}
+
+p.albumTitle img {margin-bottom:-3px;}
+
+.groups label>p {
+ font-size: 1.2em;
+ color: #111;
+ background-color:#ccc;
+}
+label>p.group_select {
+ color: #000;
+ background-color:#C2F5C2;
+}
+
+.userSeparator {
+ color:#999;
+}
+
+table.dataTable tr.even {
+ background-color: #ddd !important;
+}
+
+table.dataTable tr.even td.sorting_1 {
+ background-color: #ccc !important;
+ color:#333;
+}
+
+table.dataTable tr.odd {
+ background-color: #eee !important;
+}
+
+table.dataTable tr.odd td.sorting_1 {
+ background-color: #ddd !important;
+ color:#333;
+}
+
+table.dataTable thead th {
+ border-color: #aaa !important;
+}
+
+.userPropertiesContainer {border-color:#ddd;}
+.userPrefs {border-color:#ddd;}
diff --git a/sources/admin/themes/clear/themeconf.inc.php b/sources/admin/themes/clear/themeconf.inc.php
new file mode 100644
index 0000000..a73ebbc
--- /dev/null
+++ b/sources/admin/themes/clear/themeconf.inc.php
@@ -0,0 +1,7 @@
+ 'clear',
+ 'parent' => 'default',
+ 'admin_icon_dir' => 'admin/themes/clear/icon',
+);
+?>
diff --git a/sources/admin/themes/default/fix-ie5-ie6.css b/sources/admin/themes/default/fix-ie5-ie6.css
new file mode 100644
index 0000000..e11b2f7
--- /dev/null
+++ b/sources/admin/themes/default/fix-ie5-ie6.css
@@ -0,0 +1,35 @@
+/* Issues in IE from 5 to 6 only not to be used with IE7 */
+
+
+/* to avoid vanishing objects in IE6 */
+H1, #theHeader {
+ width: 100%; /* <- useless but seems to make IE6 happy */
+}
+.content {
+ height: 1em; /* for IE6 it's like min-height */
+}
+
+/* fix quickconnect layout */
+FORM#quickconnect FIELDSET {
+ width: 99%; /* correct an ugly 1 or 2 px misalignement with IE */
+}
+
+/* fix IE with another layout for thumbnails */
+UL.thumbnails SPAN.wrap2 {
+ display: block;
+ position: relative;
+ text-align: left;
+}
+
+UL.thumbnails IMG.thumbnail {
+ position: relative;
+ top: -50%; /* Is this following hacking technic required ? */
+ /*\*//*/
+ margin-top: -40%;
+ /**/
+}
+UL.thumbnails INPUT {
+ position: absolute;
+ left: 2px;
+ top: 2px; /* same as other browsers but not so pretty */
+}
diff --git a/sources/admin/themes/default/fix-ie7.css b/sources/admin/themes/default/fix-ie7.css
new file mode 100644
index 0000000..d61e029
--- /dev/null
+++ b/sources/admin/themes/default/fix-ie7.css
@@ -0,0 +1,11 @@
+/* fix IE7 footer */
+#the_page { min-height:100%; position:relative; padding:0; margin:0; }
+#menubar { margin: 0; }
+#footer { left: 0; }
+
+UL.thumbnails li.rank-of-image {
+ text-align:left;
+}
+.groups li {
+ display: inline;
+}
\ No newline at end of file
diff --git a/sources/admin/themes/default/fontello/LICENSE.txt b/sources/admin/themes/default/fontello/LICENSE.txt
new file mode 100644
index 0000000..8d0e89f
--- /dev/null
+++ b/sources/admin/themes/default/fontello/LICENSE.txt
@@ -0,0 +1,66 @@
+Font license info
+
+
+## Fontelico
+
+ Copyright (C) 2012 by Fontello project
+
+ Author: Crowdsourced, for Fontello project
+ License: SIL (http://scripts.sil.org/OFL)
+ Homepage: http://fontello.com
+
+
+## Elusive
+
+ Copyright (C) 2013 by Aristeides Stathopoulos
+
+ Author: Aristeides Stathopoulos
+ License: SIL (http://scripts.sil.org/OFL)
+ Homepage: http://aristeides.com/
+
+
+## Font Awesome
+
+ Copyright (C) 2012 by Dave Gandy
+
+ Author: Dave Gandy
+ License: SIL ()
+ Homepage: http://fortawesome.github.com/Font-Awesome/
+
+
+## Entypo
+
+ Copyright (C) 2012 by Daniel Bruce
+
+ Author: Daniel Bruce
+ License: CC BY-SA (http://creativecommons.org/licenses/by-sa/2.0/)
+ Homepage: http://www.entypo.com
+
+
+## Typicons
+
+ (c) Stephen Hutchings 2012
+
+ Author: Stephen Hutchings
+ License: CC BY-SA 3.0 (http://creativecommons.org/licenses/by-sa/3.0/)
+ Homepage: http://typicons.com/
+
+
+## Iconic
+
+ Copyright (C) 2012 by P.J. Onori
+
+ Author: P.J. Onori
+ License: SIL (http://scripts.sil.org/OFL)
+ Homepage: http://somerandomdude.com/work/iconic/
+
+
+## MFG Labs
+
+ Copyright (C) 2012 by Daniel Bruce
+
+ Author: MFG Labs
+ License: SIL (http://scripts.sil.org/OFL)
+ Homepage: http://www.mfglabs.com/
+
+
diff --git a/sources/admin/themes/default/fontello/README.txt b/sources/admin/themes/default/fontello/README.txt
new file mode 100644
index 0000000..43e23f2
--- /dev/null
+++ b/sources/admin/themes/default/fontello/README.txt
@@ -0,0 +1,75 @@
+This webfont is generated by http://fontello.com open source project.
+
+
+================================================================================
+Please, note, that you should obey original font licences, used to make this
+webfont pack. Details available in LICENSE.txt file.
+
+- Usually, it's enough to publish content of LICENSE.txt file somewhere on your
+ site in "About" section.
+
+- If your project is open-source, usually, it will be ok to make LICENSE.txt
+ file publically available in your repository.
+
+- Fonts, used in Fontello, don't require to make clickable links on your site.
+ But any kind of additional authors crediting is welcome.
+================================================================================
+
+
+Comments on archive content
+---------------------------
+
+- /font/* - fonts in different formats
+
+- /css/* - different kinds of css, for all situations. Should be ok with
+ twitter bootstrap. Also, you can skip style and assign icon classes
+ directly to text elements, if you don't mind about IE7.
+
+- demo.html - demo file, to show your webfont content
+
+- LICENSE.txt - license info about source fonts, used to build your one.
+
+- config.json - keeps your settings. You can import it back to fontello anytime,
+ to continue your work
+
+
+Why so many CSS files ?
+-----------------------
+
+Because we like to fit all your needs :)
+
+- basic file, .css - is usually enougth, in contains @font-face
+ and character codes definition
+
+- *-ie7.css - if you need IE7 support, but still don't wish to put char codes
+ directly into html
+
+- *-codes.css and *-ie7-codes.css - if you like to use your own @font-face
+ rules, but still wish to benefit of css generation. That can be very
+ convenient for automated assets build systems. When you need to update font -
+ no needs to manually edit files, just override old version with archive
+ content. See fontello source codes for example.
+
+- *-embedded.css - basic css file, but with embedded WOFF font, to avoid
+ CORS issues in Firefox and IE9+, when fonts are hosted on the separate domain.
+ We strongly recommend to resolve this issue by `Access-Control-Allow-Origin`
+ server headers. But if you ok with dirty hack - this file is for you. Note,
+ that data url moved to separate @font-face to avoid problems with
+
+
+
+
+
+
+
+
+
+
+
+
+
+
icon-spin6 0xe81d
+
icon-asl 0xe0ad
+
icon-mail-alt 0xf0e0
+
icon-star 0x2605
+
+
+
icon-star-empty 0x2606
+
icon-user 0x1f464
+
icon-picture 0x1f304
+
icon-cancel-circled 0xe821
+
+
+
icon-plus-circled 0x2795
+
icon-link 0x1f517
+
icon-lock 0x1f512
+
icon-eye 0xe70a
+
+
+
icon-tags 0xe70d
+
icon-code 0xe80a
+
icon-pencil 0x270e
+
icon-chat 0xe720
+
+
+
icon-trash 0xe729
+
icon-folder-open 0xe806
+
icon-menu 0xe809
+
icon-cog 0x2699
+
+
+
icon-cog-alt 0x26ef
+
icon-wrench 0x1f527
+
icon-basket 0xe73d
+
icon-logout 0xe81b
+
+
+
icon-clock 0x1f554
+
icon-block 0x1f6ab
+
icon-move 0xe812
+
icon-cw 0xe80c
+
+
+
icon-arrows-cw 0xe804
+
icon-mail 0x2709
+
icon-exchange 0xe805
+
icon-signal 0xe801
+
+
+
icon-crop 0xf125
+
icon-check 0xe81f
+
icon-check-empty 0xe820
+
icon-tasks 0xf0ae
+
+
+
icon-filter 0xf0b0
+
icon-sitemap 0xf0e8
+
icon-search 0xe811
+
icon-user-add 0xe80b
+
+
+
icon-help-circled 0xe81a
+
icon-info-circled-1 0xe817
+
icon-home 0xe80e
+
icon-flag 0xe81c
+
+
+
icon-upload 0xe813
+
icon-upload-cloud 0xe814
+
icon-tools 0xe803
+
icon-hourglass 0xe81e
+
+
+
icon-network 0xe816
+
icon-brush 0xe807
+
icon-language 0xe800
+
icon-key 0xe815
+
+
+
icon-flow-branch 0xe808
+
icon-puzzle 0xe7b6
+
icon-mail-1 0xe810
+
icon-equalizer 0xe80f
+
+
+
icon-users 0xe802
+
icon-group 0xe80d
+
icon-shuffle 0x1f500
+
+
+
+
+
\ No newline at end of file
diff --git a/sources/admin/themes/default/fontello/font/fontello.eot b/sources/admin/themes/default/fontello/font/fontello.eot
new file mode 100644
index 0000000..030da1c
Binary files /dev/null and b/sources/admin/themes/default/fontello/font/fontello.eot differ
diff --git a/sources/admin/themes/default/fontello/font/fontello.svg b/sources/admin/themes/default/fontello/font/fontello.svg
new file mode 100644
index 0000000..00f80b8
--- /dev/null
+++ b/sources/admin/themes/default/fontello/font/fontello.svg
@@ -0,0 +1,70 @@
+
+
+
+Copyright (C) 2013 by original authors @ fontello.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sources/admin/themes/default/fontello/font/fontello.ttf b/sources/admin/themes/default/fontello/font/fontello.ttf
new file mode 100644
index 0000000..328a9c5
Binary files /dev/null and b/sources/admin/themes/default/fontello/font/fontello.ttf differ
diff --git a/sources/admin/themes/default/fontello/font/fontello.woff b/sources/admin/themes/default/fontello/font/fontello.woff
new file mode 100644
index 0000000..9e17f2e
Binary files /dev/null and b/sources/admin/themes/default/fontello/font/fontello.woff differ
diff --git a/sources/admin/themes/default/icon/cat_move.png b/sources/admin/themes/default/icon/cat_move.png
new file mode 100644
index 0000000..9d6939c
Binary files /dev/null and b/sources/admin/themes/default/icon/cat_move.png differ
diff --git a/sources/admin/themes/default/icon/category_children.png b/sources/admin/themes/default/icon/category_children.png
new file mode 100644
index 0000000..d2c4edd
Binary files /dev/null and b/sources/admin/themes/default/icon/category_children.png differ
diff --git a/sources/admin/themes/default/icon/category_delete.png b/sources/admin/themes/default/icon/category_delete.png
new file mode 100644
index 0000000..b27ef62
Binary files /dev/null and b/sources/admin/themes/default/icon/category_delete.png differ
diff --git a/sources/admin/themes/default/icon/category_edit.png b/sources/admin/themes/default/icon/category_edit.png
new file mode 100644
index 0000000..3ace19d
Binary files /dev/null and b/sources/admin/themes/default/icon/category_edit.png differ
diff --git a/sources/admin/themes/default/icon/category_elements.png b/sources/admin/themes/default/icon/category_elements.png
new file mode 100644
index 0000000..c5e3f92
Binary files /dev/null and b/sources/admin/themes/default/icon/category_elements.png differ
diff --git a/sources/admin/themes/default/icon/category_jump-to.png b/sources/admin/themes/default/icon/category_jump-to.png
new file mode 100644
index 0000000..765bc11
Binary files /dev/null and b/sources/admin/themes/default/icon/category_jump-to.png differ
diff --git a/sources/admin/themes/default/icon/category_permissions.png b/sources/admin/themes/default/icon/category_permissions.png
new file mode 100644
index 0000000..055d9e5
Binary files /dev/null and b/sources/admin/themes/default/icon/category_permissions.png differ
diff --git a/sources/admin/themes/default/icon/category_representant_random.png b/sources/admin/themes/default/icon/category_representant_random.png
new file mode 100644
index 0000000..26fe701
Binary files /dev/null and b/sources/admin/themes/default/icon/category_representant_random.png differ
diff --git a/sources/admin/themes/default/icon/check.png b/sources/admin/themes/default/icon/check.png
new file mode 100644
index 0000000..708003a
Binary files /dev/null and b/sources/admin/themes/default/icon/check.png differ
diff --git a/sources/admin/themes/default/icon/datepicker.png b/sources/admin/themes/default/icon/datepicker.png
new file mode 100644
index 0000000..72e836b
Binary files /dev/null and b/sources/admin/themes/default/icon/datepicker.png differ
diff --git a/sources/admin/themes/default/icon/delete.png b/sources/admin/themes/default/icon/delete.png
new file mode 100644
index 0000000..285e53e
Binary files /dev/null and b/sources/admin/themes/default/icon/delete.png differ
diff --git a/sources/admin/themes/default/icon/edit_s.png b/sources/admin/themes/default/icon/edit_s.png
new file mode 100644
index 0000000..efb6399
Binary files /dev/null and b/sources/admin/themes/default/icon/edit_s.png differ
diff --git a/sources/admin/themes/default/icon/errors.png b/sources/admin/themes/default/icon/errors.png
new file mode 100644
index 0000000..7e6eaec
Binary files /dev/null and b/sources/admin/themes/default/icon/errors.png differ
diff --git a/sources/admin/themes/default/icon/exit.png b/sources/admin/themes/default/icon/exit.png
new file mode 100644
index 0000000..685cc44
Binary files /dev/null and b/sources/admin/themes/default/icon/exit.png differ
diff --git a/sources/admin/themes/default/icon/fcbkcomplete_close.gif b/sources/admin/themes/default/icon/fcbkcomplete_close.gif
new file mode 100644
index 0000000..cc21992
Binary files /dev/null and b/sources/admin/themes/default/icon/fcbkcomplete_close.gif differ
diff --git a/sources/admin/themes/default/icon/help-min.png b/sources/admin/themes/default/icon/help-min.png
new file mode 100644
index 0000000..9a82569
Binary files /dev/null and b/sources/admin/themes/default/icon/help-min.png differ
diff --git a/sources/admin/themes/default/icon/help.png b/sources/admin/themes/default/icon/help.png
new file mode 100644
index 0000000..cd1d209
Binary files /dev/null and b/sources/admin/themes/default/icon/help.png differ
diff --git a/sources/admin/themes/default/icon/home.png b/sources/admin/themes/default/icon/home.png
new file mode 100644
index 0000000..81b6bdd
Binary files /dev/null and b/sources/admin/themes/default/icon/home.png differ
diff --git a/sources/admin/themes/default/icon/index.php b/sources/admin/themes/default/icon/index.php
new file mode 100644
index 0000000..c8de97f
--- /dev/null
+++ b/sources/admin/themes/default/icon/index.php
@@ -0,0 +1,30 @@
+
diff --git a/sources/admin/themes/default/icon/infos.png b/sources/admin/themes/default/icon/infos.png
new file mode 100644
index 0000000..735073d
Binary files /dev/null and b/sources/admin/themes/default/icon/infos.png differ
diff --git a/sources/admin/themes/default/icon/minus.gif b/sources/admin/themes/default/icon/minus.gif
new file mode 100644
index 0000000..154e45a
Binary files /dev/null and b/sources/admin/themes/default/icon/minus.gif differ
diff --git a/sources/admin/themes/default/icon/note.png b/sources/admin/themes/default/icon/note.png
new file mode 100644
index 0000000..5849f79
Binary files /dev/null and b/sources/admin/themes/default/icon/note.png differ
diff --git a/sources/admin/themes/default/icon/page_end.png b/sources/admin/themes/default/icon/page_end.png
new file mode 100644
index 0000000..ac7b25a
Binary files /dev/null and b/sources/admin/themes/default/icon/page_end.png differ
diff --git a/sources/admin/themes/default/icon/page_top.png b/sources/admin/themes/default/icon/page_top.png
new file mode 100644
index 0000000..30c58ed
Binary files /dev/null and b/sources/admin/themes/default/icon/page_top.png differ
diff --git a/sources/admin/themes/default/icon/permissions.png b/sources/admin/themes/default/icon/permissions.png
new file mode 100644
index 0000000..370dddd
Binary files /dev/null and b/sources/admin/themes/default/icon/permissions.png differ
diff --git a/sources/admin/themes/default/icon/plus.gif b/sources/admin/themes/default/icon/plus.gif
new file mode 100644
index 0000000..93d2ade
Binary files /dev/null and b/sources/admin/themes/default/icon/plus.gif differ
diff --git a/sources/admin/themes/default/icon/preferences.png b/sources/admin/themes/default/icon/preferences.png
new file mode 100644
index 0000000..8634b51
Binary files /dev/null and b/sources/admin/themes/default/icon/preferences.png differ
diff --git a/sources/admin/themes/default/icon/remove_filter.png b/sources/admin/themes/default/icon/remove_filter.png
new file mode 100644
index 0000000..c2fa738
Binary files /dev/null and b/sources/admin/themes/default/icon/remove_filter.png differ
diff --git a/sources/admin/themes/default/icon/remove_filter_hover.png b/sources/admin/themes/default/icon/remove_filter_hover.png
new file mode 100644
index 0000000..1f9d975
Binary files /dev/null and b/sources/admin/themes/default/icon/remove_filter_hover.png differ
diff --git a/sources/admin/themes/default/icon/toggle_is_default_group.png b/sources/admin/themes/default/icon/toggle_is_default_group.png
new file mode 100644
index 0000000..7ddce00
Binary files /dev/null and b/sources/admin/themes/default/icon/toggle_is_default_group.png differ
diff --git a/sources/admin/themes/default/icon/uncheck.png b/sources/admin/themes/default/icon/uncheck.png
new file mode 100644
index 0000000..df08cb8
Binary files /dev/null and b/sources/admin/themes/default/icon/uncheck.png differ
diff --git a/sources/admin/themes/default/icon/validate_s.png b/sources/admin/themes/default/icon/validate_s.png
new file mode 100644
index 0000000..adea3d5
Binary files /dev/null and b/sources/admin/themes/default/icon/validate_s.png differ
diff --git a/sources/admin/themes/default/icon/virt_category.png b/sources/admin/themes/default/icon/virt_category.png
new file mode 100644
index 0000000..7957ec7
Binary files /dev/null and b/sources/admin/themes/default/icon/virt_category.png differ
diff --git a/sources/admin/themes/default/icon/warning.png b/sources/admin/themes/default/icon/warning.png
new file mode 100644
index 0000000..060b577
Binary files /dev/null and b/sources/admin/themes/default/icon/warning.png differ
diff --git a/sources/admin/themes/default/icon/warnings.png b/sources/admin/themes/default/icon/warnings.png
new file mode 100644
index 0000000..03454e3
Binary files /dev/null and b/sources/admin/themes/default/icon/warnings.png differ
diff --git a/sources/admin/themes/default/images/ajax-loader-bar.gif b/sources/admin/themes/default/images/ajax-loader-bar.gif
new file mode 100644
index 0000000..d84f653
Binary files /dev/null and b/sources/admin/themes/default/images/ajax-loader-bar.gif differ
diff --git a/sources/admin/themes/default/images/ajax-loader.gif b/sources/admin/themes/default/images/ajax-loader.gif
new file mode 100644
index 0000000..d0bce15
Binary files /dev/null and b/sources/admin/themes/default/images/ajax-loader.gif differ
diff --git a/sources/admin/themes/default/images/logo.png b/sources/admin/themes/default/images/logo.png
new file mode 100644
index 0000000..eb9cc48
Binary files /dev/null and b/sources/admin/themes/default/images/logo.png differ
diff --git a/sources/admin/themes/default/images/pbar-ani.gif b/sources/admin/themes/default/images/pbar-ani.gif
new file mode 100644
index 0000000..cb59a04
Binary files /dev/null and b/sources/admin/themes/default/images/pbar-ani.gif differ
diff --git a/sources/admin/themes/default/index.php b/sources/admin/themes/default/index.php
new file mode 100644
index 0000000..c8de97f
--- /dev/null
+++ b/sources/admin/themes/default/index.php
@@ -0,0 +1,30 @@
+
diff --git a/sources/admin/themes/default/js/common.js b/sources/admin/themes/default/js/common.js
new file mode 100644
index 0000000..0f64353
--- /dev/null
+++ b/sources/admin/themes/default/js/common.js
@@ -0,0 +1,78 @@
+function array_delete(arr, item) {
+ var i = arr.indexOf(item);
+ if (i != -1) arr.splice(i, 1);
+}
+
+function str_repeat(i, m) {
+ for (var o = []; m > 0; o[--m] = i);
+ return o.join('');
+}
+
+if (!Array.prototype.indexOf)
+{
+ Array.prototype.indexOf = function(elt /*, from*/)
+ {
+ var len = this.length;
+
+ var from = Number(arguments[1]) || 0;
+ from = (from < 0)
+ ? Math.ceil(from)
+ : Math.floor(from);
+ if (from < 0)
+ from += len;
+
+ for (; from < len; from++)
+ {
+ if (from in this &&
+ this[from] === elt)
+ return from;
+ }
+ return -1;
+ };
+}
+
+function sprintf() {
+ var i = 0, a, f = arguments[i++], o = [], m, p, c, x, s = '';
+ while (f) {
+ if (m = /^[^\x25]+/.exec(f)) {
+ o.push(m[0]);
+ }
+ else if (m = /^\x25{2}/.exec(f)) {
+ o.push('%');
+ }
+ else if (m = /^\x25(?:(\d+)\$)?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(f)) {
+ if (((a = arguments[m[1] || i++]) == null) || (a == undefined)) {
+ throw('Too few arguments.');
+ }
+ if (/[^s]/.test(m[7]) && (typeof(a) != 'number')) {
+ throw('Expecting number but found ' + typeof(a));
+ }
+
+ switch (m[7]) {
+ case 'b': a = a.toString(2); break;
+ case 'c': a = String.fromCharCode(a); break;
+ case 'd': a = parseInt(a); break;
+ case 'e': a = m[6] ? a.toExponential(m[6]) : a.toExponential(); break;
+ case 'f': a = m[6] ? parseFloat(a).toFixed(m[6]) : parseFloat(a); break;
+ case 'o': a = a.toString(8); break;
+ case 's': a = ((a = String(a)) && m[6] ? a.substring(0, m[6]) : a); break;
+ case 'u': a = Math.abs(a); break;
+ case 'x': a = a.toString(16); break;
+ case 'X': a = a.toString(16).toUpperCase(); break;
+ }
+
+ a = (/[def]/.test(m[7]) && m[2] && a >= 0 ? '+'+ a : a);
+ c = m[3] ? m[3] == '0' ? '0' : m[3].charAt(1) : ' ';
+ x = m[5] - String(a).length - s.length;
+ p = m[5] ? str_repeat(c, x) : '';
+ o.push(s + (m[4] ? a + p : p + a));
+ }
+ else {
+ throw('Huh ?!');
+ }
+
+ f = f.substring(m[0].length);
+ }
+
+ return o.join('');
+}
\ No newline at end of file
diff --git a/sources/admin/themes/default/js/jquery.geoip.js b/sources/admin/themes/default/js/jquery.geoip.js
new file mode 100644
index 0000000..cba374b
--- /dev/null
+++ b/sources/admin/themes/default/js/jquery.geoip.js
@@ -0,0 +1,62 @@
+
+GeoIp = {
+ cache: {},
+ pending: {},
+
+ get: function(ip, callback){
+ if (!GeoIp.storageInit && window.localStorage) {
+ GeoIp.storageInit = true;
+ var cache = localStorage.getItem("freegeoip");
+ if (cache) {
+ cache = JSON.parse(cache);
+ for (var key in cache) {
+ var data = cache[key];
+ if ( (new Date()).getTime() - data.reqTime > 36 * 3600000)
+ delete cache[key];
+ }
+ GeoIp.cache = cache;
+ }
+ jQuery(window).on("unload", function() {
+ localStorage.setItem("freegeoip", JSON.stringify(GeoIp.cache) );
+ } );
+ }
+
+ if (GeoIp.cache.hasOwnProperty(ip))
+ callback(GeoIp.cache[ip]);
+ else if (GeoIp.pending[ip])
+ GeoIp.pending[ip].push(callback);
+ else {
+ GeoIp.pending[ip] = [callback];
+ jQuery.ajax( {
+ url: "http://freegeoip.net/json/" + ip,
+ dataType: "jsonp",
+ cache: true,
+ timeout: 5000,
+ success: function(data) {
+ data.reqTime = (new Date()).getTime();
+ var res=[];
+ if (data.city) res.push(data.city);
+ if (data.region_name) res.push(data.region_name);
+ if (data.country_name) res.push(data.country_name);
+ data.fullName = res.join(", ");
+
+ GeoIp.cache[ip] = data;
+ var callbacks = GeoIp.pending[ip];
+ delete GeoIp.pending[ip];
+ for (var i=0; i
+
+
+
+
+{/if}
\ No newline at end of file
diff --git a/sources/admin/themes/default/print.css b/sources/admin/themes/default/print.css
new file mode 100644
index 0000000..9e2998f
--- /dev/null
+++ b/sources/admin/themes/default/print.css
@@ -0,0 +1,14 @@
+#menubar, .content .navigationBar,
+.navThumb, #addComment {
+ display: none;
+}
+
+BODY {
+ margin: 0;
+ color: #000000;
+ background: #ffffff;
+}
+
+.content {
+ margin: 0;
+}
diff --git a/sources/admin/themes/default/template/admin.tpl b/sources/admin/themes/default/template/admin.tpl
new file mode 100644
index 0000000..7fc9dc1
--- /dev/null
+++ b/sources/admin/themes/default/template/admin.tpl
@@ -0,0 +1,139 @@
+{combine_script id='jquery.ui.accordion' load='header'}{*we load in the header because the accordion is on every admin page and usually all admin pages use the same header combined script but not the same footer script*}
+{footer_script require='jquery.ui.accordion'}
+jQuery(document).ready(function(){ldelim}
+ jQuery('#menubar').accordion({ldelim}
+ header: "dt.rdion",
+ event: "click",
+ heightStyle: "content",
+ active: {$ACTIVE_MENU}
+ });
+});
+{/footer_script}
+
+
+
+
+
+ {if isset($TABSHEET)}
+ {$TABSHEET}
+ {/if}
+ {if isset($U_HELP)}
+ {combine_script id='core.scripts' load='async' path='themes/default/js/scripts.js'}
+
+
+
+ {/if}
+
+ {if isset($errors)}
+
+
+ {foreach from=$errors item=error}
+ {$error}
+ {/foreach}
+
+
+ {/if}
+
+ {if isset($infos)}
+
+
+ {foreach from=$infos item=info}
+ {$info}
+ {/foreach}
+
+
+ {/if}
+
+ {if isset($warnings)}
+
+
+ {foreach from=$warnings item=warning}
+ {$warning}
+ {/foreach}
+
+
+ {/if}
+
+ {$ADMIN_CONTENT}
+
diff --git a/sources/admin/themes/default/template/album_notification.tpl b/sources/admin/themes/default/template/album_notification.tpl
new file mode 100644
index 0000000..32aa8e4
--- /dev/null
+++ b/sources/admin/themes/default/template/album_notification.tpl
@@ -0,0 +1,40 @@
+
+
{$CATEGORIES_NAV} › {'Edit album'|@translate} {$TABSHEET_TITLE}
+
+
+
diff --git a/sources/admin/themes/default/template/batch_manager_global.tpl b/sources/admin/themes/default/template/batch_manager_global.tpl
new file mode 100644
index 0000000..46e2aa7
--- /dev/null
+++ b/sources/admin/themes/default/template/batch_manager_global.tpl
@@ -0,0 +1,949 @@
+{include file='include/tag_selection.inc.tpl'}
+{include file='include/datepicker.inc.tpl'}
+{include file='include/colorbox.inc.tpl'}
+{include file='include/add_album.inc.tpl'}
+
+{combine_script id='common' load='footer' path='admin/themes/default/js/common.js'}
+{combine_script id='jquery.ui.slider' require='jquery.ui' load='footer' path='themes/default/js/ui/minified/jquery.ui.slider.min.js'}
+{combine_css path="themes/default/js/ui/theme/jquery.ui.slider.css"}
+
+{footer_script}{literal}
+ pwg_initialization_datepicker("#date_creation_day", "#date_creation_month", "#date_creation_year", "#date_creation_linked_date", "#date_creation_action_set");
+{/literal}{/footer_script}
+
+{footer_script}{literal}
+/* Shift-click: select all photos between the click and the shift+click */
+jQuery(document).ready(function() {
+ var last_clicked=0;
+ var last_clickedstatus=true;
+ jQuery.fn.enableShiftClick = function() {
+ var inputs = [];
+ var count=0;
+ this.find('input[type=checkbox]').each(function() {
+ var pos=count;
+ inputs[count++]=this;
+ $(this).bind("shclick", function (dummy,event) {
+ if (event.shiftKey) {
+ var first = last_clicked;
+ var last = pos;
+ if (first > last) {
+ first=pos;
+ last=last_clicked;
+ }
+
+ for (var i=first; i<=last;i++) {
+ input = $(inputs[i]);
+ $(input).prop('checked', last_clickedstatus);
+ if (last_clickedstatus)
+ {
+ $(input).siblings("span.wrap2").addClass("thumbSelected");
+ }
+ else
+ {
+ $(input).siblings("span.wrap2").removeClass("thumbSelected");
+ }
+ }
+ }
+ else {
+ last_clicked = pos;
+ last_clickedstatus = this.checked;
+ }
+ return true;
+ });
+ $(this).click(function(event) { $(this).triggerHandler("shclick",event)});
+ });
+ }
+ $('ul.thumbnails').enableShiftClick();
+});
+{/literal}{/footer_script}
+
+{combine_css path='themes/default/js/plugins/jquery.tokeninput.css'}
+{combine_script id='jquery.tokeninput' load='footer' require='jquery' path='themes/default/js/plugins/jquery.tokeninput.js'}
+{combine_script id='jquery.progressBar' load='footer' path='themes/default/js/plugins/jquery.progressbar.min.js'}
+{combine_script id='jquery.ajaxmanager' load='footer' path='themes/default/js/plugins/jquery.ajaxmanager.js'}
+
+{footer_script require='jquery.tokeninput'}
+jQuery(document).ready(function() {ldelim}
+ jQuery("a.preview-box").colorbox();
+
+ var tag_src = [{foreach from=$tags item=tag name=tags}{ldelim}name:"{$tag.name|@escape:'javascript'}",id:"{$tag.id}"{rdelim}{if !$smarty.foreach.tags.last},{/if}{/foreach}];
+ jQuery("#tags").tokenInput(
+ tag_src,
+ {ldelim}
+ hintText: '{'Type in a search term'|@translate}',
+ noResultsText: '{'No results'|@translate}',
+ searchingText: '{'Searching...'|@translate}',
+ newText: ' ({'new'|@translate})',
+ animateDropdown: false,
+ preventDuplicates: true,
+ allowFreeTagging: true
+ }
+ );
+
+ jQuery("#tagsFilter").tokenInput(
+ tag_src,
+ {ldelim}
+ hintText: '{'Type in a search term'|@translate}',
+ noResultsText: '{'No results'|@translate}',
+ searchingText: '{'Searching...'|@translate}',
+ animateDropdown: false,
+ preventDuplicates: true,
+ allowFreeTagging: false
+ }
+ );
+
+});
+{/footer_script}
+
+{footer_script}
+var nb_thumbs_page = {$nb_thumbs_page};
+var nb_thumbs_set = {$nb_thumbs_set};
+var are_you_sure = "{'Are you sure?'|@translate|@escape:'javascript'}";
+var applyOnDetails_pattern = "{'on the %d selected photos'|@translate}";
+var all_elements = [{if !empty($all_elements)}{','|@implode:$all_elements}{/if}];
+var derivatives = {ldelim}
+ elements: null,
+ done: 0,
+ total: 0,
+
+ finished: function() {ldelim}
+ return derivatives.done == derivatives.total && derivatives.elements && derivatives.elements.length==0;
+ }
+};
+
+var selectedMessage_pattern = "{'%d of %d photos selected'|@translate}";
+var selectedMessage_none = "{'No photo selected, %d photos in current set'|@translate}";
+var selectedMessage_all = "{'All %d photos are selected'|@translate}";
+
+var width_str = '{'Width'|@translate}';
+var height_str = '{'Height'|@translate}';
+var max_width_str = '{'Maximum width'|@translate}';
+var max_height_str = '{'Maximum height'|@translate}';
+{literal}
+
+function progress(success) {
+ jQuery('#progressBar').progressBar(derivatives.done, {
+ max: derivatives.total,
+ textFormat: 'fraction',
+ boxImage: 'themes/default/images/progressbar.gif',
+ barImage: 'themes/default/images/progressbg_orange.gif'
+ });
+ if (success !== undefined) {
+ var type = success ? 'regenerateSuccess': 'regenerateError',
+ s = jQuery('[name="'+type+'"]').val();
+ jQuery('[name="'+type+'"]').val(++s);
+ }
+
+ if (derivatives.finished()) {
+ jQuery('#applyAction').click();
+ }
+}
+
+$(document).ready(function() {
+ function checkPermitAction() {
+ var nbSelected = 0;
+ if ($("input[name=setSelected]").is(':checked')) {
+ nbSelected = nb_thumbs_set;
+ }
+ else {
+ nbSelected = $(".thumbnails input[type=checkbox]").filter(':checked').length;
+ }
+
+ if (nbSelected == 0) {
+ $("#permitAction").hide();
+ $("#forbidAction").show();
+ }
+ else {
+ $("#permitAction").show();
+ $("#forbidAction").hide();
+ }
+
+ $("#applyOnDetails").text(
+ sprintf(
+ applyOnDetails_pattern,
+ nbSelected
+ )
+ );
+
+ // display the number of currently selected photos in the "Selection" fieldset
+ if (nbSelected == 0) {
+ $("#selectedMessage").text(
+ sprintf(
+ selectedMessage_none,
+ nb_thumbs_set
+ )
+ );
+ }
+ else if (nbSelected == nb_thumbs_set) {
+ $("#selectedMessage").text(
+ sprintf(
+ selectedMessage_all,
+ nb_thumbs_set
+ )
+ );
+ }
+ else {
+ $("#selectedMessage").text(
+ sprintf(
+ selectedMessage_pattern,
+ nbSelected,
+ nb_thumbs_set
+ )
+ );
+ }
+ }
+
+ $('.thumbnails img').tipTip({
+ 'delay' : 0,
+ 'fadeIn' : 200,
+ 'fadeOut' : 200
+ });
+
+ $("[id^=action_]").hide();
+
+ $("select[name=selectAction]").change(function () {
+ $("[id^=action_]").hide();
+ $("#action_"+$(this).prop("value")).show();
+
+ /* make sure the #albumSelect is on the right select box so that the */
+ /* "add new album" popup fills the right select box */
+ if ("associate" == $(this).prop("value") || "move" == $(this).prop("value")) {
+ jQuery("#albumSelect").removeAttr("id");
+ jQuery("#action_"+$(this).prop("value")+" select").attr("id", "albumSelect");
+ }
+
+ if ($(this).val() != -1) {
+ $("#applyActionBlock").show();
+ }
+ else {
+ $("#applyActionBlock").hide();
+ }
+ });
+
+ $(".wrap1 label").click(function (event) {
+ $("input[name=setSelected]").prop('checked', false);
+
+ var wrap2 = $(this).children(".wrap2");
+ var checkbox = $(this).children("input[type=checkbox]");
+
+ checkbox.triggerHandler("shclick",event);
+
+ if ($(checkbox).is(':checked')) {
+ $(wrap2).addClass("thumbSelected");
+ }
+ else {
+ $(wrap2).removeClass('thumbSelected');
+ }
+
+ checkPermitAction();
+ });
+
+ $("#selectAll").click(function () {
+ $("input[name=setSelected]").prop('checked', false);
+ selectPageThumbnails();
+ checkPermitAction();
+ return false;
+ });
+
+ function selectPageThumbnails() {
+ $(".thumbnails label").each(function() {
+ var wrap2 = $(this).children(".wrap2");
+ var checkbox = $(this).children("input[type=checkbox]");
+
+ $(checkbox).prop('checked', true);
+ $(wrap2).addClass("thumbSelected");
+ });
+ }
+
+ $("#selectNone").click(function () {
+ $("input[name=setSelected]").prop('checked', false);
+
+ $(".thumbnails label").each(function() {
+ var wrap2 = $(this).children(".wrap2");
+ var checkbox = $(this).children("input[type=checkbox]");
+
+ $(checkbox).prop('checked', false);
+ $(wrap2).removeClass("thumbSelected");
+ });
+ checkPermitAction();
+ return false;
+ });
+
+ $("#selectInvert").click(function () {
+ $("input[name=setSelected]").prop('checked', false);
+
+ $(".thumbnails label").each(function() {
+ var wrap2 = $(this).children(".wrap2");
+ var checkbox = $(this).children("input[type=checkbox]");
+
+ $(checkbox).prop('checked', !$(checkbox).is(':checked'));
+
+ if ($(checkbox).is(':checked')) {
+ $(wrap2).addClass("thumbSelected");
+ }
+ else {
+ $(wrap2).removeClass('thumbSelected');
+ }
+ });
+ checkPermitAction();
+ return false;
+ });
+
+ $("#selectSet").click(function () {
+ selectPageThumbnails();
+ $("input[name=setSelected]").prop('checked', true);
+ checkPermitAction();
+ return false;
+ });
+
+ $("input[name=remove_author]").click(function () {
+ if ($(this).is(':checked')) {
+ $("input[name=author]").hide();
+ }
+ else {
+ $("input[name=author]").show();
+ }
+ });
+
+ $("input[name=remove_title]").click(function () {
+ if ($(this).is(':checked')) {
+ $("input[name=title]").hide();
+ }
+ else {
+ $("input[name=title]").show();
+ }
+ });
+
+ $("input[name=remove_date_creation]").click(function () {
+ if ($(this).is(':checked')) {
+ $("#set_date_creation").hide();
+ }
+ else {
+ $("#set_date_creation").show();
+ }
+ });
+
+ $(".removeFilter").click(function () {
+ var filter = $(this).parent('li').attr("id");
+ filter_disable(filter);
+
+ return false;
+ });
+
+ function filter_enable(filter) {
+ /* show the filter*/
+ $("#"+filter).show();
+
+ /* check the checkbox to declare we use this filter */
+ $("input[type=checkbox][name="+filter+"_use]").prop("checked", true);
+
+ /* forbid to select this filter in the addFilter list */
+ $("#addFilter").children("option[value="+filter+"]").attr("disabled", "disabled");
+ }
+
+ $("#addFilter").change(function () {
+ var filter = $(this).prop("value");
+ filter_enable(filter);
+ $(this).prop("value", -1);
+ });
+
+ function filter_disable(filter) {
+ /* hide the filter line */
+ $("#"+filter).hide();
+
+ /* uncheck the checkbox to declare we do not use this filter */
+ $("input[name="+filter+"_use]").prop("checked", false);
+
+ /* give the possibility to show it again */
+ $("#addFilter").children("option[value="+filter+"]").removeAttr("disabled");
+ }
+
+ $("#removeFilters").click(function() {
+ $("#filterList li").each(function() {
+ var filter = $(this).attr("id");
+ filter_disable(filter);
+ });
+ return false;
+ });
+
+ jQuery('#applyAction').click(function() {
+ var action = jQuery('[name="selectAction"]').val();
+ if (action == 'delete_derivatives') {
+ var d_count = $('#action_delete_derivatives input[type=checkbox]').filter(':checked').length
+ , e_count = $('input[name="setSelected"]').is(':checked') ? nb_thumbs_set : $('.thumbnails input[type=checkbox]').filter(':checked').length;
+ if (d_count*e_count > 500)
+ return confirm(are_you_sure);
+ }
+
+ if (action != 'generate_derivatives'
+ || derivatives.finished() )
+ {
+ return true;
+ }
+
+ jQuery('.bulkAction').hide();
+
+ var queuedManager = jQuery.manageAjax.create('queued', {
+ queue: true,
+ cacheResponse: false,
+ maxRequests: 1
+ });
+
+ derivatives.elements = [];
+ if (jQuery('input[name="setSelected"]').is(':checked'))
+ derivatives.elements = all_elements;
+ else
+ jQuery('.thumbnails input[type=checkbox]').each(function() {
+ if (jQuery(this).is(':checked')) {
+ derivatives.elements.push(jQuery(this).val());
+ }
+ });
+
+ jQuery('#applyActionBlock').hide();
+ jQuery('select[name="selectAction"]').hide();
+ jQuery('#regenerationMsg').show();
+
+ progress();
+ getDerivativeUrls();
+ return false;
+ });
+
+ function getDerivativeUrls() {
+ var ids = derivatives.elements.splice(0, 500);
+ var params = {max_urls: 100000, ids: ids, types: []};
+ jQuery("#action_generate_derivatives input").each( function(i, t) {
+ if ($(t).is(":checked"))
+ params.types.push( t.value );
+ } );
+
+ jQuery.ajax( {
+ type: "POST",
+ url: 'ws.php?format=json&method=pwg.getMissingDerivatives',
+ data: params,
+ dataType: "json",
+ success: function(data) {
+ if (!data.stat || data.stat != "ok") {
+ return;
+ }
+ derivatives.total += data.result.urls.length;
+ progress();
+ for (var i=0; i < data.result.urls.length; i++) {
+ jQuery.manageAjax.add("queued", {
+ type: 'GET',
+ url: data.result.urls[i] + "&ajaxload=true",
+ dataType: 'json',
+ success: ( function(data) { derivatives.done++; progress(true) }),
+ error: ( function(data) { derivatives.done++; progress(false) })
+ });
+ }
+ if (derivatives.elements.length)
+ setTimeout( getDerivativeUrls, 25 * (derivatives.total-derivatives.done));
+ }
+ } );
+ }
+
+ checkPermitAction();
+
+ /* dimensions sliders */
+ /**
+ * find the key from a value in the startStopValues array
+ */
+ function getSliderKeyFromValue(value, values) {
+ for (var key in values) {
+ if (values[key] == value) {
+ return key;
+ }
+ }
+
+ return 0;
+ }
+
+{/literal}
+ var dimension_values = {ldelim}
+ 'width':[{$dimensions.widths}],
+ 'height':[{$dimensions.heights}],
+ 'ratio':[{$dimensions.ratios}]
+ };
+
+ $("#filter_dimension_width_slider").slider({ldelim}
+ range: true,
+ min: 0,
+ max: dimension_values['width'].length - 1,
+ values: [
+ getSliderKeyFromValue({$dimensions.selected.min_width}, dimension_values['width']),
+ getSliderKeyFromValue({$dimensions.selected.max_width}, dimension_values['width'])
+ ],
+ slide: function(event, ui) {ldelim}
+ $("input[name='filter_dimension_min_width']").val(dimension_values['width'][ui.values[0]]);
+ $("input[name='filter_dimension_max_width']").val(dimension_values['width'][ui.values[1]]);
+
+ $("#filter_dimension_width_info").html(sprintf(
+ "{'between %d and %d pixels'|@translate}",
+ dimension_values['width'][ui.values[0]],
+ dimension_values['width'][ui.values[1]]
+ ));
+ },
+ change: function(event, ui) {ldelim}
+ $("input[name='filter_dimension_min_width']").val(dimension_values['width'][ui.values[0]]);
+ $("input[name='filter_dimension_max_width']").val(dimension_values['width'][ui.values[1]]);
+
+ $("#filter_dimension_width_info").html(sprintf(
+ "{'between %d and %d pixels'|@translate}",
+ dimension_values['width'][ui.values[0]],
+ dimension_values['width'][ui.values[1]]
+ ));
+ }
+ });
+
+ $("#filter_dimension_height_slider").slider({ldelim}
+ range: true,
+ min: 0,
+ max: dimension_values['height'].length - 1,
+ values: [
+ getSliderKeyFromValue({$dimensions.selected.min_height}, dimension_values['height']),
+ getSliderKeyFromValue({$dimensions.selected.max_height}, dimension_values['height'])
+ ],
+ slide: function(event, ui) {ldelim}
+ $("input[name='filter_dimension_min_height']").val(dimension_values['height'][ui.values[0]]);
+ $("input[name='filter_dimension_max_height']").val(dimension_values['height'][ui.values[1]]);
+
+ $("#filter_dimension_height_info").html(sprintf(
+ "{'between %d and %d pixels'|@translate}",
+ dimension_values['height'][ui.values[0]],
+ dimension_values['height'][ui.values[1]]
+ ));
+ },
+ change: function(event, ui) {ldelim}
+ $("input[name='filter_dimension_min_height']").val(dimension_values['height'][ui.values[0]]);
+ $("input[name='filter_dimension_max_height']").val(dimension_values['height'][ui.values[1]]);
+
+ $("#filter_dimension_height_info").html(sprintf(
+ "{'between %d and %d pixels'|@translate}",
+ dimension_values['height'][ui.values[0]],
+ dimension_values['height'][ui.values[1]]
+ ));
+ }
+ });
+
+ $("#filter_dimension_ratio_slider").slider({ldelim}
+ range: true,
+ min: 0,
+ max: dimension_values['ratio'].length - 1,
+ values: [
+ getSliderKeyFromValue({$dimensions.selected.min_ratio}, dimension_values['ratio']),
+ getSliderKeyFromValue({$dimensions.selected.max_ratio}, dimension_values['ratio'])
+ ],
+ slide: function(event, ui) {ldelim}
+ $("input[name='filter_dimension_min_ratio']").val(dimension_values['ratio'][ui.values[0]]);
+ $("input[name='filter_dimension_max_ratio']").val(dimension_values['ratio'][ui.values[1]]);
+
+ $("#filter_dimension_ratio_info").html(sprintf(
+ "{'between %.2f and %.2f'|@translate}",
+ dimension_values['ratio'][ui.values[0]],
+ dimension_values['ratio'][ui.values[1]]
+ ));
+ },
+ change: function(event, ui) {ldelim}
+ $("input[name='filter_dimension_min_ratio']").val(dimension_values['ratio'][ui.values[0]]);
+ $("input[name='filter_dimension_max_ratio']").val(dimension_values['ratio'][ui.values[1]]);
+
+ $("#filter_dimension_ratio_info").html(sprintf(
+ "{'between %.2f and %.2f'|@translate}",
+ dimension_values['ratio'][ui.values[0]],
+ dimension_values['ratio'][ui.values[1]]
+ ));
+ }
+ });
+
+ $("a.dimensions-choice").click(function() {ldelim}
+ var type = jQuery(this).data("type");
+ var min = jQuery(this).data("min");
+ var max = jQuery(this).data("max");
+
+ $("#filter_dimension_"+ type +"_slider").slider("values", 0,
+ getSliderKeyFromValue(min, dimension_values[type])
+ );
+
+ $("#filter_dimension_"+type+"_slider").slider("values", 1,
+ getSliderKeyFromValue(max, dimension_values[type])
+ );
+ });
+});
+
+{/footer_script}
+
+
+
+
{'Batch Manager'|@translate}
+
+
+
+
+
+ {'Filter'|@translate}
+
+
+
+
+
+ {'Add a filter'|@translate}
+ ------------------
+ {'Predefined filter'|@translate}
+ {'Album'|@translate}
+ {'Tags'|@translate}
+ {'Privacy level'|@translate}
+ {'Dimensions'|@translate}
+
+ {'Remove all filters'|@translate}
+
+
+
+
+
+
+
+
+
+
+ {'Selection'|@translate}
+
+ {if !empty($thumbnails)}
+
+ {'Select:'|@translate}
+{if $nb_thumbs_set > $nb_thumbs_page}
+ {'The whole page'|@translate} ,
+ {'The whole set'|@translate} ,
+{else}
+ {'All'|@translate} ,
+{/if}
+ {'None'|@translate} ,
+ {'Invert'|@translate}
+
+
+
+
+
+
+
+
+ {if !empty($navbar) }
+
+
+
+ {include file='navigation_bar.tpl'|@get_extent:'navbar'}
+
+
+
+
+ {/if}
+
+ {else}
+ {'No photo in the current set.'|@translate}
+ {/if}
+
+
+
+
+ {'Action'|@translate}
+ {'No photo selected, no action possible.'|@translate}
+
+
+
+ {'Choose an action'|@translate}
+ ------------------
+ {'Delete selected photos'|@translate}
+ {'Associate to album'|@translate}
+ {'Move to album'|@translate}
+ {if !empty($dissociate_options)}
+ {'Dissociate from album'|@translate}
+ {/if}
+ {'Add tags'|@translate}
+ {if !empty($DEL_TAG_SELECTION)}
+ {'remove tags'|@translate}
+ {/if}
+ {'Set author'|@translate}
+ {'Set title'|@translate}
+ {'Set creation date'|@translate}
+ {'Who can see these photos?'|@translate}
+ {'Synchronize metadata'|@translate}
+ {if ($IN_CADDIE)}
+ {'Remove from caddie'|@translate}
+ {else}
+ {'Add to caddie'|@translate}
+ {/if}
+ {'Delete multiple size images'|@translate}
+ {'Generate multiple size images'|@translate}
+ {if !empty($element_set_global_plugins_actions)}
+ {foreach from=$element_set_global_plugins_actions item=action}
+ {$action.NAME}
+ {/foreach}
+ {/if}
+
+
+
+
+
{'Are you sure?'|@translate}
+
+
+
+
+
+
+
+
+
+
+
+
+ {if !empty($dissociate_options)}{html_options options=$dissociate_options }{/if}
+
+
+
+
+
+
+
+
+
+
+
+
+{if !empty($DEL_TAG_SELECTION)}{$DEL_TAG_SELECTION}{/if}
+
+
+
+
+ {'remove author'|@translate}
+ {assign var='authorDefaultValue' value='Type here the author name'|@translate}
+
+
+
+
+
+ {'remove title'|@translate}
+ {assign var='titleDefaultValue' value='Type here the title'|@translate}
+
+
+
+
+
+
+
+
+
+ {html_options options=$level_options selected=$level_options_selected}
+
+
+
+
+
+
+
+
+
+
{'All'|@translate} ,
+
{'None'|@translate}
+
+ {foreach from=$generate_derivatives_types key=type item=disp}
+
{$disp}
+ {/foreach}
+ {footer_script}
+ function selectGenerateDerivAll() {ldelim}
+ $("#action_generate_derivatives input[type=checkbox]").prop("checked", true);
+ }
+ function selectGenerateDerivNone() {ldelim}
+ $("#action_generate_derivatives input[type=checkbox]").prop("checked", false);
+ }
+ {/footer_script}
+
+
+
+
+
{'All'|@translate} ,
+
{'None'|@translate}
+
+ {foreach from=$del_derivatives_types key=type item=disp}
+
{$disp}
+ {/foreach}
+ {footer_script}
+ function selectDelDerivAll() {ldelim}
+ $("#action_delete_derivatives input[type=checkbox]").prop("checked", true);
+ }
+ function selectDelDerivNone() {ldelim}
+ $("#action_delete_derivatives input[type=checkbox]").prop("checked", false);
+ }
+ {/footer_script}
+
+
+
+
+
{'Generate multiple size images'|@translate}
+
+
+
+
+
+
+{if !empty($element_set_global_plugins_actions)}
+ {foreach from=$element_set_global_plugins_actions item=action}
+
+ {if !empty($action.CONTENT)}{$action.CONTENT}{/if}
+
+ {/foreach}
+{/if}
+
+
+
+
+
+
+
+
+
+
diff --git a/sources/admin/themes/default/template/batch_manager_unit.tpl b/sources/admin/themes/default/template/batch_manager_unit.tpl
new file mode 100644
index 0000000..859a9b6
--- /dev/null
+++ b/sources/admin/themes/default/template/batch_manager_unit.tpl
@@ -0,0 +1,145 @@
+{include file='include/autosize.inc.tpl'}
+{include file='include/datepicker.inc.tpl'}
+{include file='include/colorbox.inc.tpl'}
+
+{combine_css path='themes/default/js/plugins/jquery.tokeninput.css'}
+{combine_script id='jquery.tokeninput' load='async' require='jquery' path='themes/default/js/plugins/jquery.tokeninput.js'}
+{footer_script require='jquery.tokeninput'}
+jQuery(document).ready(function() {ldelim}
+ jQuery('select[name|="tags"]').tokenInput(
+ [{foreach from=$tags item=tag name=tags}{ldelim}name:"{$tag.name|@escape:'javascript'}",id:"{$tag.id}"{rdelim}{if !$smarty.foreach.tags.last},{/if}{/foreach}],
+ {ldelim}
+ hintText: '{'Type in a search term'|@translate}',
+ noResultsText: '{'No results'|@translate}',
+ searchingText: '{'Searching...'|@translate}',
+ newText: ' ({'new'|@translate})',
+ animateDropdown: false,
+ preventDuplicates: true,
+ allowFreeTagging: true
+ }
+ );
+
+ jQuery("a.preview-box").colorbox();
+});
+{/footer_script}
+
+{'Batch Manager'|@translate}
+
+
+
+ {'Display options'|@translate}
+ {'photos per page'|@translate} :
+ 5
+ | 10
+ | 50
+ | {'all'|@translate}
+
+
+
+
+{if !empty($navbar) }{include file='navigation_bar.tpl'|@get_extent:'navbar'}{/if}
+
+{if !empty($elements) }
+
+{foreach from=$elements item=element}
+
+ {$element.LEGEND}
+
+
+
+
+ {'Edit'|@translate}
+
+
+
+
+
+{/foreach}
+
+{if !empty($navbar)}{include file='navigation_bar.tpl'|@get_extent:'navbar'}{/if}
+
+
+
+
+
+{/if}
+
+
+
+{footer_script}
+{literal}$(document).ready(function() {
+ $(".elementEdit img")
+ .fadeTo("slow", 0.6) // Opacity on page load
+ .hover(function(){
+ $(this).fadeTo("slow", 1.0); // Opacity on hover
+ },function(){
+ $(this).fadeTo("slow", 0.6); // Opacity on mouseout
+ });
+});{/literal}
+{/footer_script}
diff --git a/sources/admin/themes/default/template/cat_list.tpl b/sources/admin/themes/default/template/cat_list.tpl
new file mode 100644
index 0000000..5fe54b9
--- /dev/null
+++ b/sources/admin/themes/default/template/cat_list.tpl
@@ -0,0 +1,142 @@
+{footer_script require='jquery.ui.sortable'}{literal}
+jQuery(document).ready(function(){
+ jQuery(".drag_button").show();
+ jQuery(".categoryLi").css("cursor","move");
+ jQuery(".categoryUl").sortable({
+ axis: "y",
+ opacity: 0.8,
+ update : function() {
+ jQuery("#manualOrder").show();
+ jQuery("#notManualOrder").hide();
+ jQuery("#formAutoOrder").hide();
+ jQuery("#formCreateAlbum").hide();
+ }
+ });
+
+ jQuery("#categoryOrdering").submit(function(){
+ ar = jQuery('.categoryUl').sortable('toArray');
+ for(i=0;i{$CATEGORIES_NAV} › {'Album list management'|@translate}
+
+ {'create a new album'|@translate}
+ {if count($categories)}| {'apply automatic sort order'|@translate} {/if}
+ {if ($PARENT_EDIT)}| {'edit'|@translate} {/if}
+
+
+
+ {'create a new album'|@translate}
+
+
+
+ {'Album name'|@translate}
+
+
+
+
+
+ {'Cancel'|@translate}
+
+
+
+{if count($categories)}
+
+
+ {'Automatic sort order'|@translate}
+
+
+ {'Sort order'|@translate}
+ {'ascending'|@translate}
+ {'descending'|@translate}
+
+
+
+ {'Apply to sub-albums'|@translate}
+
+
+
+
+ {'Cancel'|@translate}
+
+
+
+{/if}
+
+
+
+
+
+ {'... or '|@translate} {'cancel manual order'|@translate}
+
+
+{if count($categories)}
+
+{/if}
+
diff --git a/sources/admin/themes/default/template/cat_modify.tpl b/sources/admin/themes/default/template/cat_modify.tpl
new file mode 100644
index 0000000..52632ae
--- /dev/null
+++ b/sources/admin/themes/default/template/cat_modify.tpl
@@ -0,0 +1,104 @@
+
+
{$CATEGORIES_NAV} › {'Edit album'|@translate} {$TABSHEET_TITLE}
+
+
+
+
+
+ {'Informations'|@translate}
+
+
+
+
+
+
+ {'Properties'|@translate}
+
+ {'Name'|@translate}
+
+
+
+
+
+ {'Description'|@translate}
+
+
+
+
+{if isset($move_cat_options) }
+
+ {'Parent album'|@translate}
+
+
+ ------------
+ {html_options options=$move_cat_options selected=$move_cat_options_selected }
+
+
+{/if}
+
+
+ {'Lock'|@translate}
+
+ {html_radios name='visible' values=['true','false'] output=['No'|translate,'Yes'|translate] selected=$CAT_VISIBLE}
+
+
+ {if isset($CAT_COMMENTABLE)}
+
+ {'Comments'|@translate}
+
+ {html_radios name='commentable' values=['false','true'] output=['No'|translate,'Yes'|translate] selected=$CAT_COMMENTABLE}
+
+ {/if}
+
+
+
+
+
+
+
diff --git a/sources/admin/themes/default/template/cat_move.tpl b/sources/admin/themes/default/template/cat_move.tpl
new file mode 100644
index 0000000..d8a9c94
--- /dev/null
+++ b/sources/admin/themes/default/template/cat_move.tpl
@@ -0,0 +1,32 @@
+
+
{'Move albums'|@translate}
+
+
+
+
+ {'Move albums'|@translate}
+
+
+ {'Virtual albums to move'|@translate}
+
+
+ {html_options options=$category_to_move_options}
+
+
+
+
+ {'New parent album'|@translate}
+
+
+ ------------
+ {html_options options=$category_parent_options}
+
+
+
+
+
+
+
+
+
+
diff --git a/sources/admin/themes/default/template/cat_options.tpl b/sources/admin/themes/default/template/cat_options.tpl
new file mode 100644
index 0000000..9c25c1e
--- /dev/null
+++ b/sources/admin/themes/default/template/cat_options.tpl
@@ -0,0 +1,11 @@
+
+
{'Properties'|@translate} {$TABSHEET_TITLE}
+
+
+
+
+ {$L_SECTION}
+ {$DOUBLE_SELECT}
+
+
+
diff --git a/sources/admin/themes/default/template/cat_perm.tpl b/sources/admin/themes/default/template/cat_perm.tpl
new file mode 100644
index 0000000..0415c87
--- /dev/null
+++ b/sources/admin/themes/default/template/cat_perm.tpl
@@ -0,0 +1,157 @@
+{combine_script id='jquery.chosen' load='footer' path='themes/default/js/plugins/chosen.jquery.min.js'}
+{combine_css path="themes/default/js/plugins/chosen.css"}
+
+{footer_script}{literal}
+jQuery(document).ready(function() {
+ jQuery(".chzn-select").chosen();
+
+ function checkStatusOptions() {
+ if (jQuery("input[name=status]:checked").val() == "private") {
+ jQuery("#privateOptions, #applytoSubAction").show();
+ }
+ else {
+ jQuery("#privateOptions, #applytoSubAction").hide();
+ }
+ }
+
+ checkStatusOptions();
+ jQuery("#selectStatus").change(function() {
+ checkStatusOptions();
+ });
+
+ jQuery("#indirectPermissionsDetailsShow").click(function(){
+ jQuery("#indirectPermissionsDetailsShow").hide();
+ jQuery("#indirectPermissionsDetailsHide").show();
+ jQuery("#indirectPermissionsDetails").show();
+ return false;
+ });
+
+ jQuery("#indirectPermissionsDetailsHide").click(function(){
+ jQuery("#indirectPermissionsDetailsShow").show();
+ jQuery("#indirectPermissionsDetailsHide").hide();
+ jQuery("#indirectPermissionsDetails").hide();
+ return false;
+ });
+});
+{/literal}{/footer_script}
+
+
+
{$CATEGORIES_NAV} › {'Edit album'|@translate} {$TABSHEET_TITLE}
+
+
+
+
+
+ {'Access type'|@translate}
+
+
+ {'public'|@translate} : {'any visitor can see this album'|@translate}
+
+ {'private'|@translate} : {'visitors need to login and have the appropriate permissions to see this album'|@translate}
+
+
+
+
+ {'Groups and users'|@translate}
+
+
+{if count($groups) > 0}
+ {'Permission granted for groups'|@translate}
+
+
+ {html_options options=$groups selected=$groups_selected}
+
+{else}
+ {'There is no group in this gallery.'|@translate} {'Group management'|@translate}
+{/if}
+
+
+
+ {'Permission granted for users'|@translate}
+
+
+ {html_options options=$users selected=$users_selected}
+
+
+
+{if isset($nb_users_granted_indirect)}
+
+ {'%u users have automatic permission because they belong to a granted group.'|@translate:$nb_users_granted_indirect}
+ {'hide details'|@translate}
+ {'show details'|@translate}
+
+
+ {foreach from=$user_granted_indirect_groups item=group_details}
+ {$group_details.group_name} : {$group_details.group_users}
+ {/foreach}
+
+
+{/if}
+
+{*
+ {'Groups'|@translate}
+
+
+ {'Permission granted'|@translate}
+
+
+
+
+
+ {'Permission denied'|@translate}
+
+
+ {'Apply to sub-albums'|@translate}
+
+
+ {'Users'|@translate}
+
+
+ {'Permission granted'|@translate}
+
+
+
+
+
+ {'Permission granted thanks to a group'|@translate}
+ {if isset($user_granted_indirects) }
+
+ {foreach from=$user_granted_indirects item=user_group}
+ {$user_group.USER} ({$user_group.GROUP})
+ {/foreach}
+
+ {/if}
+
+
+
+ {'Permission denied'|@translate}
+
+
+ {'Apply to sub-albums'|@translate}
+
+*}
+
+
+
+
+ {'Apply to sub-albums'|@translate}
+
+
+
+
diff --git a/sources/admin/themes/default/template/check_integrity.tpl b/sources/admin/themes/default/template/check_integrity.tpl
new file mode 100644
index 0000000..f11e76e
--- /dev/null
+++ b/sources/admin/themes/default/template/check_integrity.tpl
@@ -0,0 +1,109 @@
+
+ {'Check integrity'|@translate}
+
+
+
+
+
+
+ {literal}
+
+ {/literal}
+ {if $c13y_show_submit_ignore}
+ {'Check all'|@translate}
+ / {'Uncheck all'|@translate}
+ {/if}
+ {if isset($c13y_do_check)}
+ / {'Check automatic corrections'|@translate}
+ {/if}
+
+
+
+ {if $c13y_show_submit_automatic_correction}
+
+ {/if}
+ {if $c13y_show_submit_ignore}
+
+ {/if}
+
+
+
+
+
+
+
diff --git a/sources/admin/themes/default/template/comments.tpl b/sources/admin/themes/default/template/comments.tpl
new file mode 100644
index 0000000..3b3a404
--- /dev/null
+++ b/sources/admin/themes/default/template/comments.tpl
@@ -0,0 +1,89 @@
+{footer_script}{literal}
+jQuery(document).ready(function(){
+ function highlighComments() {
+ jQuery(".checkComment").each(function() {
+ var parent = jQuery(this).parent('tr');
+ if (jQuery(this).children("input[type=checkbox]").is(':checked')) {
+ jQuery(parent).addClass('selectedComment');
+ }
+ else {
+ jQuery(parent).removeClass('selectedComment');
+ }
+ });
+ }
+
+ jQuery(".checkComment").click(function(event) {
+ var checkbox = jQuery(this).children("input[type=checkbox]");
+ if (event.target.type !== 'checkbox') {
+ jQuery(checkbox).prop('checked', !jQuery(checkbox).prop('checked'));
+ }
+ highlighComments();
+ });
+
+ jQuery("#commentSelectAll").click(function () {
+ jQuery(".checkComment input[type=checkbox]").prop('checked', true);
+ highlighComments();
+ return false;
+ });
+
+ jQuery("#commentSelectNone").click(function () {
+ jQuery(".checkComment input[type=checkbox]").prop('checked', false);
+ highlighComments();
+ return false;
+ });
+
+ jQuery("#commentSelectInvert").click(function () {
+ jQuery(".checkComment input[type=checkbox]").each(function() {
+ jQuery(this).prop('checked', !$(this).prop('checked'));
+ });
+ highlighComments();
+ return false;
+ });
+
+});
+{/literal}{/footer_script}
+
+{'User comments'|@translate} {$TABSHEET_TITLE}
+
+
+
+
+
+{if !empty($comments) }
+
+{/if}
diff --git a/sources/admin/themes/default/template/configuration.tpl b/sources/admin/themes/default/template/configuration.tpl
new file mode 100644
index 0000000..d3309e8
--- /dev/null
+++ b/sources/admin/themes/default/template/configuration.tpl
@@ -0,0 +1,852 @@
+{footer_script}
+(function(){
+ var targets = {
+ 'input[name="rate"]' : '#rate_anonymous',
+ 'input[name="allow_user_registration"]' : '#email_admin_on_new_user',
+ 'input[name="comments_validation"]' : '#email_admin_on_comment_validation',
+ 'input[name="user_can_edit_comment"]' : '#email_admin_on_comment_edition',
+ 'input[name="user_can_delete_comment"]' : '#email_admin_on_comment_deletion',
+ };
+
+ for (selector in targets) {
+ var target = targets[selector];
+
+ jQuery(target).toggle(jQuery(selector).is(':checked'));
+
+ (function(target){
+ jQuery(selector).on('change', function() {
+ jQuery(target).toggle($(this).is(':checked'));
+ });
+ })(target);
+ };
+}());
+{/footer_script}
+
+{'Piwigo configuration'|@translate} {$TABSHEET_TITLE}
+
+{if !isset($default)}
+
+{/if}
+
+
+{if isset($main)}
+
+ {'Basic settings'|translate}
+
+
+{if !isset($ORDER_BY_IS_CUSTOM)}
+{footer_script require='jquery'}
+(function(){
+var max_fields = Math.ceil({$main.order_by_options|@count}/2);
+
+function updateFilters() {
+ var $selects = jQuery('#order_filters select');
+
+ jQuery('#order_filters .addFilter').toggle($selects.length <= max_fields);
+ jQuery('#order_filters .removeFilter').css('display', '').filter(':first').css('display', 'none');
+
+ $selects.find('option').removeAttr('disabled');
+ $selects.each(function() {
+ $selects.not(this).find('option[value="'+ jQuery(this).val() +'"]').attr('disabled', 'disabled');
+ });
+}
+
+jQuery('#order_filters').on('click', '.removeFilter', function() {
+ jQuery(this).parent('span.filter').remove();
+ updateFilters();
+});
+
+jQuery('#order_filters').on('change', 'select', updateFilters);
+
+jQuery('#order_filters .addFilter').click(function() {
+ jQuery(this).prev('span.filter').clone().insertBefore(jQuery(this));
+ jQuery(this).prev('span.filter').children('select').val('');
+ updateFilters();
+});
+
+updateFilters();
+}());
+{/footer_script}
+{/if}
+
+
+
+ {'Permissions'|translate}
+
+
+{footer_script require='jquery'}
+jQuery("#activate_comments").change(function(){
+ if ($(this).is(':checked')) {
+ jQuery("#comments_param_warp").show();
+ } else {
+ jQuery("#comments_param_warp").hide();
+ }
+});
+{/footer_script}
+
+
+
+ {'Miscellaneous'|translate}
+
+
+{/if}
+
+{if isset($comments)}
+
+{/if}
+
+{if isset($sizes)}
+
+{footer_script}
+(function(){
+ var labelMaxWidth = "{'Maximum width'|@translate}",
+ labelWidth = "{'Width'|@translate}",
+ labelMaxHeight = "{'Maximum height'|@translate}",
+ labelHeight = "{'Height'|@translate}";
+
+ function toggleResizeFields(size) {
+ var checkbox = jQuery("#original_resize");
+ var needToggle = jQuery("#sizeEdit-original");
+
+ if (jQuery(checkbox).is(':checked')) {
+ needToggle.show();
+ }
+ else {
+ needToggle.hide();
+ }
+ }
+
+ toggleResizeFields("original");
+ jQuery("#original_resize").click(function () {
+ toggleResizeFields("original");
+ });
+
+ jQuery("a[id^='sizeEditOpen-']").click(function(){
+ var sizeName = jQuery(this).attr("id").split("-")[1];
+ jQuery("#sizeEdit-"+sizeName).toggle();
+ jQuery(this).hide();
+ return false;
+ });
+
+ jQuery(".cropToggle").click(function() {
+ var labelBoxWidth = jQuery(this).parents('table.sizeEditForm').find('td.sizeEditWidth');
+ var labelBoxHeight = jQuery(this).parents('table.sizeEditForm').find('td.sizeEditHeight');
+
+ if (jQuery(this).is(':checked')) {
+ jQuery(labelBoxWidth).html(labelWidth);
+ jQuery(labelBoxHeight).html(labelHeight);
+ }
+ else {
+ jQuery(labelBoxWidth).html(labelMaxWidth);
+ jQuery(labelBoxHeight).html(labelMaxHeight);
+ }
+ });
+
+ jQuery("#showDetails").click(function() {
+ jQuery(".sizeDetails").show();
+ jQuery(this).css("visibility", "hidden");
+ return false;
+ });
+}());
+{/footer_script}
+
+{html_style}{literal}
+.sizeEnable {width:50px;}
+.sizeEditForm {margin:0 0 10px 20px;}
+.sizeEdit {display:none;}
+#sizesConf table {margin:0;}
+.showDetails {padding:0;}
+.sizeDetails {display:none;margin-left:10px;}
+.sizeEditOpen {margin-left:10px;}
+{/literal}{/html_style}
+
+
+ {'Original Size'|@translate}
+{if $is_gd}
+
+ {'Resize after upload disabled due to the use of GD as graphic library'|@translate}
+
+
+
+
+
+{else}
+
+
+
+ {'Resize after upload'|@translate}
+
+
+
+
+{/if}
+
+
+
+ {'Multiple Size'|@translate}
+
+
+
+
+
+
+ {'Image Quality'|@translate}
+ %
+ {if isset($ferrors.resize_quality)}! {/if}
+
+
+ {'Reset to default values'|@translate}
+
+
+{if !empty($custom_derivatives)}
+
+ {'custom'|@translate}
+
+
+{/if}
+
+
+{/if}
+
+{if isset($watermark)}
+
+{footer_script}
+(function(){
+ function onWatermarkChange() {
+ var val = jQuery("#wSelect").val();
+ if (val.length) {
+ jQuery("#wImg").attr('src', '{$ROOT_URL}'+val).show();
+ }
+ else {
+ jQuery("#wImg").hide();
+ }
+ }
+
+ onWatermarkChange();
+
+ jQuery("#wSelect").bind("change", onWatermarkChange);
+
+ if (jQuery("input[name='w[position]']:checked").val() == 'custom') {
+ jQuery("#positionCustomDetails").show();
+ }
+
+ jQuery("input[name='w[position]']").change(function(){
+ if (jQuery(this).val() == 'custom') {
+ jQuery("#positionCustomDetails").show();
+ }
+ else {
+ jQuery("#positionCustomDetails").hide();
+ }
+ });
+
+ jQuery(".addWatermarkOpen").click(function(){
+ jQuery("#addWatermark, #selectWatermark").toggle();
+ return false;
+ });
+}());
+{/footer_script}
+
+
+
+
+
+
+{/if} {* end of watermark section *}
+
+{if isset($display)}
+
+ {'Main Page'|@translate}
+
+
+
+
+ {'Photo Page'|@translate}
+
+
+
+
+ {'Photo Properties'|@translate}
+
+
+{/if}
+
+
+
+{if !isset($default)}
+
+
+
+
+{/if}
+
+{if isset($default)}
+
+{$PROFILE_CONTENT}
+
+{/if}
diff --git a/sources/admin/themes/default/template/double_select.tpl b/sources/admin/themes/default/template/double_select.tpl
new file mode 100644
index 0000000..62e99fb
--- /dev/null
+++ b/sources/admin/themes/default/template/double_select.tpl
@@ -0,0 +1,22 @@
+
+{include file='include/dbselect.inc.tpl'}
+
+
diff --git a/sources/admin/themes/default/template/element_set_ranks.tpl b/sources/admin/themes/default/template/element_set_ranks.tpl
new file mode 100644
index 0000000..dfdafdc
--- /dev/null
+++ b/sources/admin/themes/default/template/element_set_ranks.tpl
@@ -0,0 +1,94 @@
+{footer_script require='jquery.ui.sortable'}{literal}
+jQuery(document).ready(function() {
+ function checkOrderOptions() {
+ jQuery("#image_order_user_define_options").hide();
+ if (jQuery("input[name=image_order_choice]:checked").val() == "user_define") {
+ jQuery("#image_order_user_define_options").show();
+ }
+ }
+
+ jQuery('ul.thumbnails').sortable( {
+ revert: true, opacity: 0.7,
+ handle: jQuery('.rank-of-image').add('.rank-of-image img'),
+ update: function() {
+ jQuery(this).find('li').each(function(i) {
+ jQuery(this).find("input[name^=rank_of_image]").each(function() {
+ jQuery(this).attr('value', (i+1)*10)
+ });
+ });
+
+ jQuery('#image_order_rank').prop('checked', true);
+ checkOrderOptions();
+ }
+ });
+
+ jQuery("input[name=image_order_choice]").click(function () {
+ checkOrderOptions();
+ });
+
+ checkOrderOptions();
+});
+jQuery(document).ready(function() {
+jQuery('.thumbnail').tipTip({
+'delay' : 0,
+'fadeIn' : 200,
+'fadeOut' : 200
+});
+});
+{/literal}{/footer_script}
+
+
+
{$CATEGORIES_NAV} › {'Edit album'|@translate} {$TABSHEET_TITLE}
+
+
+
+{if !empty($thumbnails)}
+
+
+ {'Manual order'|@translate}
+ {if !empty($thumbnails)}
+ {'Drag to re-order'|@translate}
+
+ {/if}
+
+{/if}
+
+
+ {'Sort order'|@translate}
+
+
+ {'Use the default photo sort order (defined in the configuration file)'|@translate}
+
+
+
+ {'manual order'|@translate}
+
+
+
+ {'automatic order'|@translate}
+
+ {foreach from=$image_order item=order}
+
+
+ {html_options options=$image_order_options selected=$order}
+
+
+ {/foreach}
+
+
+
+
+
+
+
+ {'Apply to sub-albums'|@translate}
+
+
+
diff --git a/sources/admin/themes/default/template/extend_for_templates.tpl b/sources/admin/themes/default/template/extend_for_templates.tpl
new file mode 100644
index 0000000..818096b
--- /dev/null
+++ b/sources/admin/themes/default/template/extend_for_templates.tpl
@@ -0,0 +1,35 @@
+
{'Extend for templates'|@translate}
+
+{if isset($extents)}
+{'Replacement of original templates by customized templates from template-extension subfolder'|@translate}
+
+
+
+ {'Replacers (customized templates)'|@translate}
+ {'Original templates'|@translate}
+ {'Optional URL keyword'|@translate}
+ {'Bound Theme'|@translate}
+
+ {foreach from=$extents item=tpl name=extent_loop}
+
+
+
+ {$tpl.replacer}
+
+
+ {html_options name='original[]' output=$tpl.original_tpl values=$tpl.original_tpl selected=$tpl.selected_tpl}
+
+
+ {html_options name='url[]' output=$tpl.url_parameter values=$tpl.url_parameter selected=$tpl.selected_url}
+
+
+ {html_options name='bound[]' output=$tpl.bound_tpl values=$tpl.bound_tpl selected=$tpl.selected_bound}
+
+
+ {/foreach}
+
+
+
+
+
+{/if}
diff --git a/sources/admin/themes/default/template/footer.tpl b/sources/admin/themes/default/template/footer.tpl
new file mode 100644
index 0000000..2704ef5
--- /dev/null
+++ b/sources/admin/themes/default/template/footer.tpl
@@ -0,0 +1,83 @@
+{*
+ Warning : This is the admin pages footer only
+ don't be confusing with the public page footer
+*}
+
+{if isset($footer_elements)}
+{foreach from=$footer_elements item=v}
+{$v}
+{/foreach}
+{/if}
+{if isset($debug.QUERIES_LIST)}
+
+{$debug.QUERIES_LIST}
+
+{/if}
+
+
+
+
+
+{combine_script id='jquery.tipTip' load='async' path='themes/default/js/plugins/jquery.tipTip.minified.js'}
+{footer_script require='jquery.tipTip'}
+jQuery(document).ready(function() {ldelim}
+ jQuery('#pwgHead A[title], #footer A[title], .themeActions .tiptip, .languageActions .tiptip').tipTip({ldelim}
+ 'delay' : 0,
+ 'fadeIn' : 200,
+ 'fadeOut' : 200
+ });
+});
+{/footer_script}
+
+
+{get_combined_scripts load='footer'}
+
+
+{literal}
+
+{/literal}
+
+{if not $ENABLE_SYNCHRONIZATION}
+{literal}
+
+{/literal}
+{/if}
+
+