'.
+ l10n('No tag defined. Use Administration>Pictures>Tags').
+ '
';
+ }
+
+ $template->append(
+ 'elements',
+ array(
+ 'ID' => $row['id'],
+ 'TN_SRC' => $src,
+ 'LEGEND' =>
+ !empty($row['name']) ?
+ $row['name'] : get_name_from_file($row['file']),
+ 'U_EDIT' =>
+ PHPWG_ROOT_PATH.'admin.php?page=picture_modify'.
+ '&image_id='.$row['id'],
+ 'NAME' => @$row['name'],
+ 'AUTHOR' => @$row['author'],
+ 'DESCRIPTION' => @$row['comment'],
+ 'DATE_CREATION_YEAR' => $year,
+ 'DATE_CREATION_MONTH' => (int)$month,
+ 'DATE_CREATION_DAY' => (int)$day,
+
+ 'TAG_SELECTION' => $tag_selection,
+ )
+ );
+ }
+
+ $template->assign('ELEMENT_IDS', implode(',', $element_ids));
+}
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'element_set_unit');
+?>
\ No newline at end of file
diff --git a/BSF/admin/group_list.php b/BSF/admin/group_list.php
new file mode 100644
index 000000000..323674bc1
--- /dev/null
+++ b/BSF/admin/group_list.php
@@ -0,0 +1,207 @@
+set_filenames(array('group_list' => 'admin/group_list.tpl'));
+
+$template->assign(
+ array(
+ 'F_ADD_ACTION' => get_root_url().'admin.php?page=group_list',
+ 'U_HELP' => get_root_url().'popuphelp.php?page=group_list',
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | 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=';
+$members_url = $admin_url.'user_list&group=';
+$toggle_is_default_url = $admin_url.'group_list&toggle_is_default=';
+
+while ($row = mysql_fetch_array($result))
+{
+ $query = '
+SELECT COUNT(*)
+ FROM '.USER_GROUP_TABLE.'
+ WHERE group_id = '.$row['id'].'
+;';
+ list($counter) = mysql_fetch_row(pwg_query($query));
+
+ $template->append(
+ 'groups',
+ array(
+ 'NAME' => $row['name'],
+ 'IS_DEFAULT' => (get_boolean($row['is_default']) ? ' ['.l10n('is_default_group').']' : ''),
+ 'MEMBERS' => l10n_dec('%d member', '%d members', $counter),
+ 'U_MEMBERS' => $members_url.$row['id'],
+ 'U_DELETE' => $del_url.$row['id'],
+ 'U_PERM' => $perm_url.$row['id'],
+ 'U_ISDEFAULT' => $toggle_is_default_url.$row['id']
+ )
+ );
+}
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'group_list');
+
+?>
diff --git a/BSF/admin/group_perm.php b/BSF/admin/group_perm.php
new file mode 100644
index 000000000..430730001
--- /dev/null
+++ b/BSF/admin/group_perm.php
@@ -0,0 +1,185 @@
+ 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 = mysql_fetch_array($result))
+ {
+ array_push($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 = mysql_fetch_array($result))
+ {
+ array_push($authorized_ids, $row['cat_id']);
+ }
+
+ $inserts = array();
+ $to_autorize_ids = array_diff($private_uppercats, $authorized_ids);
+ foreach ($to_autorize_ids as $to_autorize_id)
+ {
+ array_push(
+ $inserts,
+ array(
+ 'group_id' => $page['group'],
+ 'cat_id' => $to_autorize_id
+ )
+ );
+ }
+
+ mass_inserts(GROUP_ACCESS_TABLE, array('group_id','cat_id'), $inserts);
+}
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+
+$template->set_filenames(
+ array(
+ 'group_perm' => 'admin/group_perm.tpl',
+ 'double_select' => 'admin/double_select.tpl'
+ )
+ );
+
+$template->assign(
+ array(
+ 'TITLE' =>
+ sprintf(
+ 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 = mysql_fetch_array($result))
+{
+ array_push($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/BSF/admin/help.php b/BSF/admin/help.php
new file mode 100644
index 000000000..051a10073
--- /dev/null
+++ b/BSF/admin/help.php
@@ -0,0 +1,32 @@
+assign('ADMIN_CONTENT', load_language('help.html','','',true) );
+?>
diff --git a/BSF/admin/history.php b/BSF/admin/history.php
new file mode 100644
index 000000000..c064e7516
--- /dev/null
+++ b/BSF/admin/history.php
@@ -0,0 +1,685 @@
+'; print_r($search); echo '';
+
+ 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 = mysql_insert_id();
+
+ redirect(
+ PHPWG_ROOT_PATH.'admin.php?page=history&search_id='.$search_id
+ );
+ }
+ else
+ {
+ array_push($page['errors'], l10n('search_one_clause_at_least'));
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+
+$template->set_filename('history', 'admin/history.tpl');
+
+// TabSheet initialization
+history_tabsheet();
+
+$template->assign(
+ array(
+ 'U_HELP' => PHPWG_ROOT_PATH.'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) = mysql_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 = mysql_insert_id();
+
+ 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();
+ $tag_ids = array();
+
+ 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']))
+ {
+ foreach (explode(',', $row['tag_ids']) as $tag_id)
+ {
+ array_push($tag_ids, $tag_id);
+ }
+ }
+
+ array_push(
+ $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 = mysql_fetch_array($result))
+ {
+ $username_of[$row['id']] = $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,
+ high_filesize,
+ file,
+ path,
+ tn_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();
+ $high_filesize_of_image = array();
+ $file_of_image = array();
+ $path_of_image = array();
+ $tn_ext_of_image = array();
+
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ {
+ $label_of_image[ $row['id'] ] = $row['label'];
+
+ if (isset($row['filesize']))
+ {
+ $filesize_of_image[ $row['id'] ] = $row['filesize'];
+ }
+
+ if (isset($row['high_filesize']))
+ {
+ $high_filesize_of_image[ $row['id'] ] = $row['high_filesize'];
+ }
+
+ $file_of_image[ $row['id'] ] = $row['file'];
+ $path_of_image[ $row['id'] ] = $row['path'];
+ $tn_ext_of_image[ $row['id'] ] = $row['tn_ext'];
+ }
+
+ // echo '
'; print_r($high_filesize_of_image); echo '
';
+ }
+
+ if (count($tag_ids) > 0)
+ {
+ $tag_ids = array_unique($tag_ids);
+
+ $query = '
+SELECT
+ id,
+ name
+ FROM '.TAGS_TABLE.'
+ WHERE id IN ('.implode(', ', $tag_ids).')
+;';
+ $name_of_tag = array();
+
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ {
+ $name_of_tag[ $row['id'] ] = $row['name'];
+ }
+ }
+
+ $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)
+ {
+ // FIXME when we watch the representative of a non image element, it is
+ // the not the representative filesize that is counted (as it is
+ // unknown) but the non image element filesize. Proposed solution: add
+ // #images.representative_filesize and add 'representative' in the
+ // choices of #history.image_type.
+
+ if (isset($line['image_type']))
+ {
+ if ($line['image_type'] == 'high')
+ {
+ if (isset($high_filesize_of_image[$line['image_id']]))
+ {
+ $summary['total_filesize']+=
+ $high_filesize_of_image[$line['image_id']];
+ }
+ }
+ else
+ {
+ 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(
+ '/(\d+)/e',
+ '$name_of_tag["$1"]',
+ str_replace(
+ ',',
+ ', ',
+ $line['tag_ids']
+ )
+ );
+ }
+
+ $image_string = '';
+ if (isset($line['image_id']))
+ {
+ $picture_url = make_picture_url(
+ array(
+ 'image_id' => $line['image_id'],
+ )
+ );
+
+ $element = array(
+ 'id' => $line['image_id'],
+ 'file' => $file_of_image[$line['image_id']],
+ 'path' => $path_of_image[$line['image_id']],
+ 'tn_ext' => $tn_ext_of_image[$line['image_id']],
+ );
+
+ $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 ($page['search']['fields']['display_thumbnail'])
+ {
+ 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'].' KB',
+ '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']
+ ),
+ )
+ );
+}
+
+// +-----------------------------------------------------------------------+
+// | 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('NAV_BAR', $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['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('history_display_thumbnail', $display_thumbnails[2]);
+}
+
+
+$month_list = $lang['month'];
+$month_list[0]='------------';
+ksort($month_list);
+
+$template->assign(
+ array(
+ 'IMAGE_ID' => @$form['image_id'],
+ 'FILENAME' => @$form['filename'],
+
+ '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(
+ array(
+ 'display_thumbnail_values' => $display_thumbnails,
+ 'display_thumbnail_selected' => array($form['display_thumbnail']),
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | html code display |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'history');
+?>
\ No newline at end of file
diff --git a/BSF/admin/images/index.php b/BSF/admin/images/index.php
new file mode 100644
index 000000000..c15b15795
--- /dev/null
+++ b/BSF/admin/images/index.php
@@ -0,0 +1,30 @@
+
diff --git a/BSF/admin/include/c13y_internal.class.php b/BSF/admin/include/c13y_internal.class.php
new file mode 100644
index 000000000..2e2c309b5
--- /dev/null
+++ b/BSF/admin/include/c13y_internal.class.php
@@ -0,0 +1,249 @@
+ 'PHP', 'current' => phpversion(), 'required' => REQUIRED_PHP_VERSION);
+
+ list($mysql_version) = mysql_fetch_row(pwg_query('SELECT VERSION();'));
+ $check_list[] = array('type' => 'MySQL', 'current' => $mysql_version, 'required' => REQUIRED_MYSQL_VERSION);
+
+ foreach ($check_list as $elem)
+ {
+ if (version_compare($elem['current'], $elem['required'], '<'))
+ {
+ $c13y->add_anomaly(
+ sprintf(l10n('c13y_version_anomaly'), $elem['type'], $elem['current'], $elem['required']),
+ null,
+ null,
+ l10n('c13y_version_correction')
+ .' '.
+ $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('c13y_exif_anomaly'), '$conf[\''.$value.'\']'),
+ null,
+ null,
+ sprintf(l10n('c13y_exif_correction'), '$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' => 'c13y_guest_non_existent',
+ 'l10n_bad_status' => 'c13y_bad_guest_status');
+
+ if ($conf['guest_id'] != $conf['default_user_id'])
+ {
+ $c13y_users[$conf['default_user_id']] = array(
+ 'password' => null,
+ 'l10n_non_existent' => 'c13y_default_non_existent');
+ }
+
+ $c13y_users[$conf['webmaster_id']] = array(
+ 'status' => 'webmaster',
+ 'l10n_non_existent' => 'c13y_webmaster_non_existent',
+ 'l10n_bad_status' => 'c13y_bad_webmaster_status');
+
+ $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 = mysql_fetch_array($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' => $name,
+ 'password' => $password
+ ),
+ );
+ mass_inserts(USERS_TABLE, array_keys($inserts[0]), $inserts);
+
+ create_user_infos($id);
+
+ $page['infos'][] = sprintf(l10n('c13y_user_created'), $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('c13y_user_status_updated'), get_username($id));
+
+ $result = true;
+ }
+ break;
+ }
+ }
+
+ return $result;
+ }
+}
+
+?>
diff --git a/BSF/admin/include/check_integrity.class.php b/BSF/admin/include/check_integrity.class.php
new file mode 100644
index 000000000..b6f3ddd6a
--- /dev/null
+++ b/BSF/admin/include/check_integrity.class.php
@@ -0,0 +1,345 @@
+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('c13y_anomaly_count', 'c13y_anomalies_count',
+ count($this->retrieve_list));
+ }
+
+ // Treatments
+ if (!is_adviser())
+ {
+ 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('c13y_anomaly_corrected_count', 'c13y_anomalies_corrected_count',
+ $corrected_count);
+ }
+ if ($not_corrected_count > 0)
+ {
+ $page['errors'][] =
+ l10n_dec('c13y_anomaly_not_corrected_count', 'c13y_anomalies_not_corrected_count',
+ $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('c13y_anomaly_ignored_count', 'c13y_anomalies_ignored_count',
+ $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' => 'admin/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('c13y_more_info'),
+ sprintf($link_fmt, $pwg_links['FORUM'], l10n('c13y_more_info_forum')),
+ sprintf($link_fmt, $pwg_links['WIKI'], l10n('c13y_more_info_wiki'))
+ );
+ }
+
+}
+
+?>
diff --git a/BSF/admin/include/functions.php b/BSF/admin/include/functions.php
new file mode 100644
index 000000000..328a36ab7
--- /dev/null
+++ b/BSF/admin/include/functions.php
@@ -0,0 +1,1872 @@
+ 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);
+ }
+ }
+}
+
+/**
+ * returns an array containing sub-directories which can be a category,
+ * recursive by default
+ *
+ * directories nammed "thumbnail", "pwg_high" or "pwg_representative" are
+ * omitted
+ *
+ * @param string $basedir
+ * @return array
+ */
+function get_fs_directories($path, $recursive = true)
+{
+ $dirs = array();
+
+ if (is_dir($path))
+ {
+ if ($contents = opendir($path))
+ {
+ while (($node = readdir($contents)) !== false)
+ {
+ if (is_dir($path.'/'.$node)
+ and $node != '.'
+ and $node != '..'
+ and $node != '.svn'
+ and $node != 'thumbnail'
+ and $node != 'pwg_high'
+ and $node != 'pwg_representative')
+ {
+ array_push($dirs, $path.'/'.$node);
+ if ($recursive)
+ {
+ $dirs = array_merge($dirs, get_fs_directories($path.'/'.$node));
+ }
+ }
+ }
+ }
+ }
+
+ return $dirs;
+}
+
+/**
+ * inserts multiple lines in a table
+ *
+ * @param string table_name
+ * @param array dbfields
+ * @param array inserts
+ * @return void
+ */
+function mass_inserts($table_name, $dbfields, $datas)
+{
+ if (count($datas) != 0)
+ {
+ $first = true;
+
+ $query = 'SHOW VARIABLES LIKE \'max_allowed_packet\';';
+ list(, $packet_size) = mysql_fetch_row(pwg_query($query));
+ $packet_size = $packet_size - 2000; // The last list of values MUST not exceed 2000 character*/
+ $query = '';
+
+ foreach ($datas as $insert)
+ {
+ if (strlen($query) >= $packet_size)
+ {
+ $query .= '
+;';
+ pwg_query($query);
+ $first = true;
+ }
+
+ if ($first)
+ {
+ $query = '
+INSERT INTO '.$table_name.'
+ ('.implode(',', $dbfields).')
+ VALUES';
+ $first = false;
+ }
+ else
+ {
+ $query .= '
+ , ';
+ }
+
+ $query .= '(';
+ foreach ($dbfields as $field_id => $dbfield)
+ {
+ if ($field_id > 0)
+ {
+ $query .= ',';
+ }
+
+ if (!isset($insert[$dbfield]) or $insert[$dbfield] === '')
+ {
+ $query .= 'NULL';
+ }
+ else
+ {
+ $query .= "'".$insert[$dbfield]."'";
+ }
+ }
+ $query .= ')';
+ }
+
+ $query .= '
+;';
+ pwg_query($query);
+ }
+}
+
+/**
+ * updates multiple lines in a table
+ *
+ * @param string table_name
+ * @param array dbfields
+ * @param array datas
+ * @return void
+ */
+function mass_updates($tablename, $dbfields, $datas)
+{
+ if (count($datas) != 0)
+ {
+ // depending on the MySQL version, we use the multi table update or N
+ // update queries
+ if (count($datas) < 10 or version_compare(mysql_get_server_info(), '4.0.4') < 0)
+ {
+ // MySQL is prior to version 4.0.4, multi table update feature is not
+ // available
+ foreach ($datas as $data)
+ {
+ $query = '
+UPDATE '.$tablename.'
+ SET ';
+ $is_first = true;
+ foreach ($dbfields['update'] as $key)
+ {
+ if (!$is_first)
+ {
+ $query.= ",\n ";
+ }
+ $query.= $key.' = ';
+ if (isset($data[$key]) and $data[$key] != '')
+ {
+ $query.= '\''.$data[$key].'\'';
+ }
+ else
+ {
+ $query.= 'NULL';
+ }
+ $is_first = false;
+ }
+ $query.= '
+ WHERE ';
+
+ $is_first = true;
+ foreach ($dbfields['primary'] as $key)
+ {
+ if (!$is_first)
+ {
+ $query.= ' AND ';
+ }
+ if ( isset($data[$key]) )
+ {
+ $query.= $key.' = \''.$data[$key].'\'';
+ }
+ else
+ {
+ $query.= $key.' IS NULL';
+ }
+ $is_first = false;
+ }
+ $query.= '
+;';
+ pwg_query($query);
+ }
+ }
+ else
+ {
+ // creation of the temporary table
+ $query = '
+SHOW FULL COLUMNS FROM '.$tablename.'
+;';
+ $result = pwg_query($query);
+ $columns = array();
+ $all_fields = array_merge($dbfields['primary'], $dbfields['update']);
+ while ($row = mysql_fetch_array($result))
+ {
+ if (in_array($row['Field'], $all_fields))
+ {
+ $column = $row['Field'];
+ $column.= ' '.$row['Type'];
+
+ $nullable = true;
+ if (!isset($row['Null']) or $row['Null'] == '' or $row['Null']=='NO')
+ {
+ $column.= ' NOT NULL';
+ $nullable = false;
+ }
+ if (isset($row['Default']))
+ {
+ $column.= " default '".$row['Default']."'";
+ }
+ elseif ($nullable)
+ {
+ $column.= " default NULL";
+ }
+ if (isset($row['Collation']) and $row['Collation'] != 'NULL')
+ {
+ $column.= " collate '".$row['Collation']."'";
+ }
+ array_push($columns, $column);
+ }
+ }
+
+ $temporary_tablename = $tablename.'_'.micro_seconds();
+
+ $query = '
+ CREATE TABLE '.$temporary_tablename.'
+ (
+ '.implode(",\n", $columns).',
+ UNIQUE KEY the_key ('.implode(',', $dbfields['primary']).')
+ )
+;';
+
+ pwg_query($query);
+ mass_inserts($temporary_tablename, $all_fields, $datas);
+ // update of images table by joining with temporary table
+ $query = '
+UPDATE '.$tablename.' AS t1, '.$temporary_tablename.' AS t2
+ SET '.
+ implode(
+ "\n , ",
+ array_map(
+ create_function('$s', 'return "t1.$s = t2.$s";'),
+ $dbfields['update']
+ )
+ ).'
+ WHERE '.
+ implode(
+ "\n AND ",
+ array_map(
+ create_function('$s', 'return "t1.$s = t2.$s";'),
+ $dbfields['primary']
+ )
+ ).'
+ ;';
+ pwg_query($query);
+ $query = '
+DROP TABLE '.$temporary_tablename.'
+;';
+ pwg_query($query);
+ }
+ }
+}
+
+/**
+ * order categories (update categories.rank and global_rank database fields)
+ * so that rank field are consecutive integers starting at 1 for each child
+ * @return void
+ */
+function update_global_rank()
+{
+ $query = '
+SELECT id, if(id_uppercat is null,\'\',id_uppercat) AS id_uppercat, uppercats, rank, global_rank
+ FROM '.CATEGORIES_TABLE.'
+ ORDER BY id_uppercat,rank,name
+;';
+
+ $cat_map = array();
+
+ $current_rank = 0;
+ $current_uppercat = '';
+
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($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();
+
+ foreach( $cat_map as $id=>$cat )
+ {
+ $new_global_rank = preg_replace(
+ '/(\d+)/e',
+ "\$cat_map['$1']['rank']",
+ 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,
+ );
+ }
+ }
+
+ 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 array categories
+ * @param string value
+ * @return void
+ */
+function set_cat_visible($categories, $value)
+{
+ if (!in_array($value, array('true', 'false')))
+ {
+ return false;
+ }
+
+ // unlocking a category => all its parent categories become unlocked
+ if ($value == 'true')
+ {
+ $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
+ if ($value == 'false')
+ {
+ $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 array categories
+ * @param string value
+ * @return void
+ */
+function set_cat_status($categories, $value)
+{
+ if (!in_array($value, array('public', 'private')))
+ {
+ 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);
+ }
+}
+
+/**
+ * returns all uppercats category ids of the given category ids
+ *
+ * @param array cat_ids
+ * @return array
+ */
+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 = mysql_fetch_array($result))
+ {
+ $uppercats = array_merge($uppercats,
+ explode(',', $row['uppercats']));
+ }
+ $uppercats = array_unique($uppercats);
+
+ return $uppercats;
+}
+
+/**
+ * set a new random representant to the categories
+ *
+ * @param array 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 RAND()
+ LIMIT 0,1
+;';
+ list($representative) = mysql_fetch_array(pwg_query($query));
+
+ array_push(
+ $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 array cat_ids
+ * @return array
+ */
+function get_fulldirs($cat_ids)
+{
+ if (count($cat_ids) == 0)
+ {
+ return array();
+ }
+
+ // caching directories of existing categories
+ $query = '
+SELECT id, dir
+ FROM '.CATEGORIES_TABLE.'
+ WHERE dir IS NOT NULL
+;';
+ $result = pwg_query($query);
+ $cat_dirs = array();
+ while ($row = mysql_fetch_array($result))
+ {
+ $cat_dirs[$row['id']] = $row['dir'];
+ }
+
+ // caching galleries_url
+ $query = '
+SELECT id, galleries_url
+ FROM '.SITES_TABLE.'
+;';
+ $result = pwg_query($query);
+ $galleries_url = array();
+ while ($row = mysql_fetch_array($result))
+ {
+ $galleries_url[$row['id']] = $row['galleries_url'];
+ }
+
+ // categories : id, site_id, uppercats
+ $categories = array();
+
+ $query = '
+SELECT id, uppercats, site_id
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id IN (
+'.wordwrap(implode(', ', $cat_ids), 80, "\n").')
+;';
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push($categories, $row);
+ }
+
+ // filling $cat_fulldirs
+ $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('/(\d+)/e',
+ "\$cat_dirs['$1']",
+ $uppercats);
+ }
+
+ 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 (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')
+ {
+ array_push($fs['thumbnails'], $path.'/'.$node);
+ }
+ else if (basename($path) == 'pwg_representative')
+ {
+ array_push($fs['representatives'], $path.'/'.$node);
+ }
+ else
+ {
+ array_push($fs['elements'], $path.'/'.$node);
+ }
+ }
+// else if (in_array($extension, $conf['file_ext']))
+ else if (isset($conf['flip_file_ext'][$extension]))
+ {
+ array_push($fs['elements'], $path.'/'.$node);
+ }
+ }
+ else if (is_dir($path.'/'.$node)
+ and $node != '.'
+ and $node != '..'
+ and $node != 'pwg_high'
+ and $recursive)
+ {
+ array_push($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;
+}
+
+/**
+ * stupidly returns the current microsecond since Unix epoch
+ */
+function micro_seconds()
+{
+ $t1 = explode(' ', microtime());
+ $t2 = explode('.', $t1[0]);
+ $t2 = $t1[1].substr($t2[1], 0, 6);
+ return $t2;
+}
+
+/**
+ * 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.
+ *
+ * @return void
+ */
+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
+ *
+ * @return void
+ */
+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)
+ {
+ array_push($upper_list, $uppercat);
+ $uppercat = $cat_map[$uppercat]['id_uppercat'];
+ }
+
+ $new_uppercats = implode(',', array_reverse($upper_list));
+ if ($new_uppercats != $cat['uppercats'])
+ {
+ array_push(
+ $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
+ *
+ * @return void
+ */
+function update_path()
+{
+ $query = '
+SELECT DISTINCT(storage_category_id)
+ FROM '.IMAGES_TABLE.'
+;';
+ $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 = CONCAT(\''.$fulldirs[$cat_id].'\',\'/\',file)
+ WHERE storage_category_id = '.$cat_id.'
+;';
+ pwg_query($query);
+ }
+}
+
+/**
+ * update images.average_rate field
+ * param int $element_id optional, otherwise applies to all
+ * @return void
+ */
+function update_average_rate( $element_id=-1 )
+{
+ $query = '
+SELECT element_id,
+ ROUND(AVG(rate),2) AS average_rate
+ FROM '.RATE_TABLE;
+ if ( $element_id != -1 )
+ {
+ $query .= ' WHERE element_id=' . $element_id;
+ }
+ $query .= ' GROUP BY element_id;';
+
+ $result = pwg_query($query);
+
+ $datas = array();
+
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push(
+ $datas,
+ array(
+ 'id' => $row['element_id'],
+ 'average_rate' => $row['average_rate']
+ )
+ );
+ }
+
+ mass_updates(
+ IMAGES_TABLE,
+ array(
+ 'primary' => array('id'),
+ 'update' => array('average_rate')
+ ),
+ $datas
+ );
+
+ $query='
+SELECT id FROM '.IMAGES_TABLE .'
+ LEFT JOIN '.RATE_TABLE.' ON id=element_id
+ WHERE element_id IS NULL AND average_rate IS NOT NULL';
+ if ( $element_id != -1 )
+ {
+ $query .= ' AND id=' . $element_id;
+ }
+ $to_update = array_from_query( $query, 'id');
+
+ if ( !empty($to_update) )
+ {
+ $query='
+UPDATE '.IMAGES_TABLE .'
+ SET average_rate=NULL
+ WHERE id IN (' . implode(',',$to_update) . ')';
+ pwg_query($query);
+ }
+}
+
+/**
+ * change the parent category of the given categories. The categories are
+ * supposed virtual.
+ *
+ * @param array category identifiers
+ * @param int parent category identifier
+ * @return void
+ */
+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 = mysql_fetch_array($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) = mysql_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))
+ {
+ array_push(
+ $page['errors'],
+ l10n('You cannot move a category in its own sub category')
+ );
+ 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) = mysql_fetch_row(pwg_query($query));
+ }
+
+ if ('private' == $parent_status)
+ {
+ foreach ($categories as $cat_id => $category)
+ {
+ switch ($category['status'])
+ {
+ case 'public' :
+ {
+ set_cat_status(array($cat_id), 'private');
+ break;
+ }
+ case 'private' :
+ {
+ $subcats = get_subcat_ids(array($cat_id));
+
+ foreach ($tables as $table => $field)
+ {
+ $query = '
+SELECT '.$field.'
+ FROM '.$table.'
+ WHERE cat_id = '.$cat_id.'
+;';
+ $category_access = array_from_query($query, $field);
+
+ $query = '
+SELECT '.$field.'
+ FROM '.$table.'
+ WHERE cat_id = '.$new_parent.'
+;';
+ $parent_access = array_from_query($query, $field);
+
+ $to_delete = array_diff($parent_access, $category_access);
+
+ if (count($to_delete) > 0)
+ {
+ $query = '
+DELETE FROM '.$table.'
+ WHERE '.$field.' IN ('.implode(',', $to_delete).')
+ AND cat_id IN ('.implode(',', $subcats).')
+;';
+ pwg_query($query);
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ array_push(
+ $page['infos'],
+ l10n_dec(
+ '%d category moved', '%d categories moved',
+ count($categories)
+ )
+ );
+}
+
+/**
+ * create a virtual category
+ *
+ * @param string category name
+ * @param int parent category id
+ * @return array with ('info' and 'id') or ('error') key
+ */
+function create_virtual_category($category_name, $parent_id=null)
+{
+ global $conf;
+
+ // is the given category name only containing blank spaces ?
+ if (preg_match('/^\s*$/', $category_name))
+ {
+ return array('error' => l10n('cat_error_name'));
+ }
+
+ $parent_id = !empty($parent_id) ? $parent_id : 'NULL';
+
+ $query = '
+SELECT MAX(rank)
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id_uppercat '.(is_numeric($parent_id) ? '= '.$parent_id : 'IS NULL').'
+;';
+ list($current_rank) = mysql_fetch_array(pwg_query($query));
+
+ $insert = array(
+ 'name' => $category_name,
+ 'rank' => ++$current_rank,
+ 'commentable' => boolean_to_string($conf['newcat_default_commentable']),
+ 'uploadable' => 'false',
+ );
+
+ if ($parent_id != 'NULL')
+ {
+ $query = '
+SELECT id, uppercats, global_rank, visible, status
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id = '.$parent_id.'
+;';
+ $parent = mysql_fetch_array(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';
+ }
+ else
+ {
+ $insert{'visible'} = boolean_to_string($conf['newcat_default_visible']);
+ }
+
+ // 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';
+ }
+ else
+ {
+ $insert{'status'} = $conf['newcat_default_status'];
+ }
+ }
+ else
+ {
+ $insert{'visible'} = boolean_to_string($conf['newcat_default_visible']);
+ $insert{'status'} = $conf['newcat_default_status'];
+ $insert{'global_rank'} = $insert{'rank'};
+ }
+
+ // we have then to add the virtual category
+ mass_inserts(
+ CATEGORIES_TABLE,
+ array(
+ 'site_id', 'name', 'id_uppercat', 'rank', 'commentable',
+ 'uploadable', 'visible', 'status', 'global_rank',
+ ),
+ array($insert)
+ );
+
+ $inserted_id = mysql_insert_id();
+
+ $query = '
+UPDATE
+ '.CATEGORIES_TABLE.'
+ SET uppercats = \''.
+ (isset($parent) ? $parent{'uppercats'}.',' : '').
+ $inserted_id.
+ '\'
+ WHERE id = '.$inserted_id.'
+;';
+ pwg_query($query);
+
+ return array(
+ 'info' => l10n('cat_virtual_added'),
+ 'id' => $inserted_id,
+ );
+}
+
+/**
+ * Set tags to an image. Warning: given tags are all tags associated to the
+ * image, not additionnal tags.
+ *
+ * @param array tag ids
+ * @param int image id
+ * @return void
+ */
+function set_tags($tags, $image_id)
+{
+ $query = '
+DELETE
+ FROM '.IMAGE_TAG_TABLE.'
+ WHERE image_id = '.$image_id.'
+;';
+ pwg_query($query);
+
+ if (count($tags) > 0)
+ {
+ $inserts = array();
+ foreach ($tags as $tag_id)
+ {
+ array_push(
+ $inserts,
+ array(
+ 'tag_id' => $tag_id,
+ 'image_id' => $image_id
+ )
+ );
+ }
+ mass_inserts(
+ IMAGE_TAG_TABLE,
+ array_keys($inserts[0]),
+ $inserts
+ );
+ }
+}
+
+/**
+ * Add new tags to a set of images.
+ *
+ * @param array tag ids
+ * @param array image ids
+ * @return void
+ */
+function add_tags($tags, $images)
+{
+ if (count($tags) == 0 or count($tags) == 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 ($tags as $tag_id)
+ {
+ array_push(
+ $inserts,
+ array(
+ 'image_id' => $image_id,
+ 'tag_id' => $tag_id,
+ )
+ );
+ }
+ }
+ mass_inserts(
+ IMAGE_TAG_TABLE,
+ array_keys($inserts[0]),
+ $inserts
+ );
+}
+
+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];
+ }
+
+ // 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)
+ {
+ mass_inserts(
+ TAGS_TABLE,
+ array('name', 'url_name'),
+ array(
+ array(
+ 'name' => $tag_name,
+ 'url_name' => str2url($tag_name),
+ )
+ )
+ );
+
+ $page['tag_id_from_tag_name_cache'][$tag_name] = mysql_insert_id();
+ }
+ else
+ {
+ $page['tag_id_from_tag_name_cache'][$tag_name] = $existing_tags[0];
+ }
+
+ return $page['tag_id_from_tag_name_cache'][$tag_name];
+}
+
+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 ($tag_ids as $tag_id)
+ {
+ array_push(
+ $inserts,
+ array(
+ 'image_id' => $image_id,
+ 'tag_id' => $tag_id,
+ )
+ );
+ }
+ }
+
+ mass_inserts(
+ IMAGE_TAG_TABLE,
+ array_keys($inserts[0]),
+ $inserts
+ );
+ }
+}
+
+/**
+ * Do maintenance on all PWG tables
+ *
+ * @return nono
+ */
+function do_maintenance_all_tables()
+{
+ global $prefixeTable;
+
+ $all_tables = array();
+
+ // List all tables
+ $query = 'SHOW TABLES LIKE \''.$prefixeTable.'%\';';
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push($all_tables, $row[0]);
+ }
+
+ // Repair all tables
+ $query = 'REPAIR TABLE '.implode(', ', $all_tables).';';
+ pwg_query($query);
+
+ // Re-Order all tables
+ foreach ($all_tables as $table_name)
+ {
+ $all_primary_key = array();
+
+ $query = 'DESC '.$table_name.';';
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ {
+ if ($row['Key'] == 'PRI')
+ {
+ array_push($all_primary_key, $row['Field']);
+ }
+ }
+
+ if (count($all_primary_key) != 0)
+ {
+ $query = 'ALTER TABLE '.$table_name.' ORDER BY '.implode(', ', $all_primary_key).';';
+ pwg_query($query);
+ }
+ }
+
+ // Optimize all tables
+ $query = 'OPTIMIZE TABLE '.implode(', ', $all_tables).';';
+ pwg_query($query);
+
+}
+
+/**
+ * Associate a list of images to a list of categories.
+ *
+ * The function will not duplicate links
+ *
+ * @param array images
+ * @param array categories
+ * @return void
+ */
+function associate_images_to_categories($images, $categories)
+{
+ if (count($images) == 0
+ or count($categories) == 0)
+ {
+ return false;
+ }
+
+ $query = '
+DELETE
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ WHERE image_id IN ('.implode(',', $images).')
+ AND category_id IN ('.implode(',', $categories).')
+;';
+ pwg_query($query);
+
+ $inserts = array();
+ foreach ($categories as $category_id)
+ {
+ foreach ($images as $image_id)
+ {
+ array_push(
+ $inserts,
+ array(
+ 'image_id' => $image_id,
+ 'category_id' => $category_id,
+ )
+ );
+ }
+ }
+
+ mass_inserts(
+ IMAGE_CATEGORY_TABLE,
+ array_keys($inserts[0]),
+ $inserts
+ );
+
+ update_category($categories);
+}
+
+/**
+ * Associate images associated to a list of source categories to a list of
+ * destination categories.
+ *
+ * @param array sources
+ * @param array destinations
+ * @return void
+ */
+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);
+}
+
+/**
+ * Create an XML file with Piwigo informations about a list of
+ * pictures.
+ *
+ * The goal of the export feature is to make easier the reading of
+ * informations related to pictures outside of Piwigo.
+ *
+ * @param array image_ids
+ */
+function export_pwg_data($image_ids)
+{
+ global $conf;
+
+ if (count($image_ids) == 0)
+ {
+ return;
+ }
+
+ $fp = fopen($conf['export_file'], 'w');
+ $xml_string = ''."\n";
+
+ $query = '
+SELECT tag_id,
+ image_id
+ FROM '.IMAGE_TAG_TABLE.'
+ WHERE image_id IN ('.implode(',', $image_ids).')
+;';
+ $result = pwg_query($query);
+ $tags_of = array();
+ $all_tag_ids = array();
+ $tag_name_of = array();
+
+ if (mysql_num_rows($result))
+ {
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push($all_tag_ids, $row['tag_id']);
+
+ if (!isset($tags_of[ $row['image_id'] ])) {
+ $tags_of[ $row['image_id'] ] = array();
+ }
+
+ array_push(
+ $tags_of[ $row['image_id'] ],
+ $row['tag_id']
+ );
+ }
+
+ $all_tag_ids = array_unique($all_tag_ids);
+
+ $query = '
+SELECT id,
+ name
+ FROM '.TAGS_TABLE.'
+ WHERE id IN ('.implode(',', $all_tag_ids).')
+;';
+ $result = pwg_query($query);
+
+ while ($row = mysql_fetch_array($result))
+ {
+ $tag_name_of[ $row['id'] ] = $row['name'];
+ }
+ }
+
+ $query = '
+SELECT id,
+ path
+ FROM '.IMAGES_TABLE.'
+ WHERE id IN ('.implode(',', $image_ids).')
+;';
+ $result = pwg_query($query);
+
+ while ($row = mysql_fetch_array($result))
+ {
+ $xml_string.= " \n";
+ $xml_string.= " ".$row['id']."\n";
+ $xml_string.= " ".$row['path']."\n";
+
+ foreach ($tags_of[ $row['id'] ] as $tag_id)
+ {
+ $xml_string.= " ".$tag_name_of[$tag_id]."\n";
+ }
+
+ $xml_string.= " \n";
+ }
+
+ $xml_string.= '';
+ fwrite($fp, $xml_string);
+ fclose($fp);
+}
+
+/**
+ * Refer main Piwigo URLs (currently PHPWG_DOMAIN domain)
+ *
+ * @param void
+ * @return array like $conf['links']
+ */
+function pwg_URL()
+{
+ global $lang_info;
+ $urls = array(
+ 'WIKI' => 'http://'.PHPWG_DOMAIN.'/doc/',
+ 'HOME' => 'http://'.PHPWG_DOMAIN.'/',
+ 'DEMO' => 'http://demo.'.PHPWG_DOMAIN.'/',
+ 'FORUM' => 'http://forum.'.PHPWG_DOMAIN.'/',
+ 'BUGS' => 'http://bugs.'.PHPWG_DOMAIN.'/',
+ 'EXTENSIONS' => 'http://'.PHPWG_DOMAIN.'/ext',
+ );
+ if ( isset($lang_info['code']) and
+ in_array($lang_info['code'], array('fr','en')) )
+ { /* current wiki languages are French or English */
+ $urls['WIKI'] .= 'doku.php?id='.$lang_info['code'].':start';
+ $urls['HOME'] .= '?lang='.$lang_info['code'];
+ }
+ return $urls;
+}
+
+/**
+ * Invalidates cahed data (permissions and category counts) for all users.
+ */
+function invalidate_user_cache()
+{
+ $query = '
+UPDATE '.USER_CACHE_TABLE.'
+ SET need_update = \'true\'
+;';
+ pwg_query($query);
+ trigger_action('invalidate_user_cache');
+}
+
+/**
+ * adds the caracter set to a create table sql query.
+ * all CREATE TABLE queries must call this function
+ * @param string query - the sql query
+ */
+function create_table_add_character_set($query)
+{
+ defined('DB_CHARSET') or die('create_table_add_character_set DB_CHARSET undefined');
+ if ('DB_CHARSET'!='')
+ {
+ if ( version_compare(mysql_get_server_info(), '4.1.0', '<') )
+ {
+ return $query;
+ }
+ $charset_collate = " DEFAULT CHARACTER SET ".DB_CHARSET;
+ if ('DB_COLLATE'!='')
+ {
+ $charset_collate .= " COLLATE ".DB_COLLATE;
+ }
+ $query=trim($query);
+ $query=trim($query, ';');
+ if (preg_match('/^CREATE\s+TABLE/i',$query))
+ {
+ $query.=$charset_collate;
+ }
+ $query .= ';';
+ }
+ return $query;
+}
+
+/**
+ * Returns array use on template with html_options method
+ * @param Min and Max access to use
+ * @return array of user access level
+ */
+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;
+}
+
+?>
\ No newline at end of file
diff --git a/BSF/admin/include/functions_history.inc.php b/BSF/admin/include/functions_history.inc.php
new file mode 100644
index 000000000..aa4a03fc8
--- /dev/null
+++ b/BSF/admin/include/functions_history.inc.php
@@ -0,0 +1,179 @@
+add('stats', l10n('Statistics'), $link_start.'stats');
+ $tabsheet->add('history', l10n('Search'), $link_start.'history');
+ // TabSheet selection
+ $tabsheet->select($page['page']);
+ // Assign tabsheet to template
+ $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']))
+ {
+ array_push(
+ $clauses,
+ "date >= '".$search['fields']['date-after']."'"
+ );
+ }
+
+ if (isset($search['fields']['date-before']))
+ {
+ array_push(
+ $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."'";
+ }
+
+ array_push($local_clauses, $clause);
+ }
+ }
+
+ if (count($local_clauses) > 0)
+ {
+ array_push(
+ $clauses,
+ implode(' OR ', $local_clauses)
+ );
+ }
+ }
+
+ if (isset($search['fields']['user'])
+ and $search['fields']['user'] != -1)
+ {
+ array_push(
+ $clauses,
+ 'user_id = '.$search['fields']['user']
+ );
+ }
+
+ if (isset($search['fields']['image_id']))
+ {
+ array_push(
+ $clauses,
+ 'image_id = '.$search['fields']['image_id']
+ );
+ }
+
+ if (isset($search['fields']['filename']))
+ {
+ if (count($search['image_ids']) == 0)
+ {
+ // a clause that is always false
+ array_push($clauses, '1 = 2 ');
+ }
+ else
+ {
+ array_push(
+ $clauses,
+ 'image_id IN ('.implode(', ', $search['image_ids']).')'
+ );
+ }
+ }
+
+ $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 '.$page['start'].', '.$conf['nb_logs_page'].'
+
+ $result = pwg_query($query);
+
+ while ($row = mysql_fetch_assoc($result))
+ {
+ array_push($data, $row);
+ }
+
+ return $data;
+}
+
+add_event_handler('get_history', 'get_history', EVENT_HANDLER_PRIORITY_NEUTRAL, 3);
+trigger_action('functions_history_included');
+
+?>
diff --git a/BSF/admin/include/functions_metadata.php b/BSF/admin/include/functions_metadata.php
new file mode 100644
index 000000000..e805aeba6
--- /dev/null
+++ b/BSF/admin/include/functions_metadata.php
@@ -0,0 +1,298 @@
+ $value)
+ {
+ if (in_array($pwg_key, $page['datefields']))
+ {
+ if (preg_match('/(\d{4})(\d{2})(\d{2})/', $value, $matches))
+ {
+ $iptc[$pwg_key] = $matches[1].'-'.$matches[2].'-'.$matches[3];
+ }
+ }
+ }
+
+ if (isset($iptc['keywords']))
+ {
+ // official keywords separator is the comma
+ $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, $page;
+
+ $exif = get_exif_data($file, $conf['use_exif_mapping']);
+
+ foreach ($exif as $pwg_key => $value)
+ {
+ if (in_array($pwg_key, $page['datefields']))
+ {
+ if (preg_match('/^(\d{4}).(\d{2}).(\d{2})/', $value, $matches))
+ {
+ $exif[$pwg_key] = $matches[1].'-'.$matches[2].'-'.$matches[3];
+ }
+ }
+ $exif[$pwg_key] = addslashes($exif[$pwg_key]);
+ }
+
+ return $exif;
+}
+
+function update_metadata($files)
+{
+ global $conf;
+
+ if (!defined('CURRENT_DATE'))
+ {
+ define('CURRENT_DATE', date('Y-m-d'));
+ }
+
+ $datas = array();
+ $tags_of = array();
+ $has_high_images = array();
+
+ $image_ids = array();
+ foreach ($files as $id => $file)
+ {
+ array_push($image_ids, $id);
+ }
+
+ $query = '
+SELECT id
+ FROM '.IMAGES_TABLE.'
+ WHERE has_high = \'true\'
+ AND id IN (
+'.wordwrap(implode(', ', $image_ids), 80, "\n").'
+)
+;';
+
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push($has_high_images, $row['id']);
+ }
+
+ foreach ($files as $id => $file)
+ {
+ $data = array();
+ $data['id'] = $id;
+ $data['filesize'] = floor(filesize($file)/1024);
+
+ if ($image_size = @getimagesize($file))
+ {
+ $data['width'] = $image_size[0];
+ $data['height'] = $image_size[1];
+ }
+
+ if (in_array($id, $has_high_images))
+ {
+ $high_file = dirname($file).'/pwg_high/'.basename($file);
+
+ $data['high_filesize'] = floor(filesize($high_file)/1024);
+ }
+
+ if ($conf['use_exif'])
+ {
+ $exif = get_sync_exif_data($file);
+ }
+
+ if ($conf['use_iptc'])
+ {
+ $iptc = get_sync_iptc_data($file);
+ if (count($iptc) > 0)
+ {
+ foreach (array_keys($iptc) as $key)
+ {
+ if ($key == 'keywords' or $key == 'tags')
+ {
+ if (!isset($tags_of[$id]))
+ {
+ $tags_of[$id] = array();
+ }
+
+ foreach (explode(',', $iptc[$key]) as $tag_name)
+ {
+ array_push(
+ $tags_of[$id],
+ tag_id_from_tag_name($tag_name)
+ );
+ }
+ }
+ }
+ }
+ }
+
+ $data['date_metadata_update'] = CURRENT_DATE;
+
+ array_push($datas, $data);
+ }
+
+ if (count($datas) > 0)
+ {
+ $update_fields =
+ array(
+ 'filesize',
+ 'width',
+ 'height',
+ 'high_filesize',
+ 'date_metadata_update'
+ );
+
+ if ($conf['use_exif'])
+ {
+ $update_fields =
+ array_merge(
+ $update_fields,
+ array_keys($conf['use_exif_mapping'])
+ );
+ }
+
+ if ($conf['use_iptc'])
+ {
+ $update_fields =
+ array_merge(
+ $update_fields,
+ array_diff(
+ array_keys($conf['use_iptc_mapping']),
+ array('tags', 'keywords')
+ )
+ );
+ }
+
+ mass_updates(
+ IMAGES_TABLE,
+ array(
+ 'primary' => array('id'),
+ 'update' => array_unique($update_fields)
+ ),
+ $datas
+ );
+ }
+
+ 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 REGEXP \'(^|,)'.$category_id.'(,|$)\'
+';
+ }
+ else
+ {
+ $query.= '
+ AND id = '.$category_id.'
+';
+ }
+ }
+ $query.= '
+;';
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push($cat_ids, $row['id']);
+ }
+
+ if (count($cat_ids) == 0)
+ {
+ return array();
+ }
+
+ $files = array();
+
+ $query = '
+SELECT id, path
+ FROM '.IMAGES_TABLE.'
+ WHERE storage_category_id IN ('.implode(',', $cat_ids).')';
+ if ($only_new)
+ {
+ $query.= '
+ AND date_metadata_update IS NULL
+';
+ }
+ $query.= '
+;';
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ {
+ $files[$row['id']] = $row['path'];
+ }
+
+ return $files;
+}
+?>
\ No newline at end of file
diff --git a/BSF/admin/include/functions_notification_by_mail.inc.php b/BSF/admin/include/functions_notification_by_mail.inc.php
new file mode 100644
index 000000000..c7c9f1942
--- /dev/null
+++ b/BSF/admin/include/functions_notification_by_mail.inc.php
@@ -0,0 +1,522 @@
+ 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) = mysql_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 = mysql_fetch_array($result))
+ {
+ array_push($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('nbm_msg_mail_sent_to');
+ $env_nbm['msg_error'] = l10n('nbm_msg_error_sending_email_to');
+ }
+}
+
+/*
+ * 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)
+ {
+ $nbm_user['template'] = $user['template'];
+ $nbm_user['theme'] = $user['theme'];
+ $env_nbm['mail_template'] =
+ get_mail_template($env_nbm['email_format'],
+ array('template' => $nbm_user['template'], 'theme' => $nbm_user['theme']));
+ $env_nbm['mail_template']->set_filename('notification_by_mail', 'admin/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;
+ array_push($page['infos'], sprintf($env_nbm['msg_info'], $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;
+ array_push($page['errors'], sprintf($env_nbm['msg_error'], $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)
+ {
+ array_push($page['errors'], l10n_dec('nbm_msg_n_mail_not_send', 'nbm_msg_n_mails_not_send', $env_nbm['error_on_mail_count']));
+ if ($env_nbm['sent_mail_count'] != 0)
+ array_push($page['infos'], l10n_dec('nbm_msg_n_mail_sent', 'nbm_msg_n_mails_sent', $env_nbm['sent_mail_count']));
+ }
+ else
+ {
+ if ($env_nbm['sent_mail_count'] == 0)
+ array_push($page['infos'], l10n('nbm_no_mail_to_send'));
+ else
+ array_push($page['infos'], l10n_dec('nbm_msg_n_mail_sent', 'nbm_msg_n_mails_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' => $nbm_user['username'],
+
+ 'SEND_AS_NAME' => $env_nbm['send_as_name'],
+
+ 'UNSUBSCRIBE_LINK' => add_url_params(get_root_url().'nbm.php', array('unsubscribe' => $nbm_user['check_key'])),
+ 'SUBSCRIBE_LINK' => add_url_params(get_root_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;
+
+ $check_key_treated = array();
+ $updated_data_count = 0;
+ $error_on_updated_data_count = 0;
+
+ if ($is_subscribe)
+ {
+ $msg_info = l10n('nbm_user_change_enabled_true');
+ $msg_error = l10n('nbm_user_not_change_enabled_true');
+ }
+ else
+ {
+ $msg_info = l10n('nbm_user_change_enabled_false');
+ $msg_error = l10n('nbm_user_not_change_enabled_false');
+ }
+
+ 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('nbm_break_timeout_send_mail');
+
+ // 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
+ array_push($page['errors'], $msg_break_timeout);
+ break;
+ }
+
+ // Fill return list
+ array_push($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('nbm_object_subscribe'): l10n('nbm_object_unsubscribe'));
+
+ // 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( $section_action_by, true );
+
+ if (pwg_mail
+ (
+ format_email($nbm_user['username'], $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'],
+ 'template' => $nbm_user['template'],
+ 'theme' => $nbm_user['theme']
+ )
+ ))
+ {
+ 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)
+ {
+ array_push
+ (
+ $updates,
+ array
+ (
+ 'check_key' => $nbm_user['check_key'],
+ 'enabled' => $enabled_value
+ )
+ );
+ $updated_data_count += 1;
+ array_push($page['infos'], sprintf($msg_info, $nbm_user['username'], $nbm_user['mail_address']));
+ }
+ else
+ {
+ $error_on_updated_data_count += 1;
+ array_push($page['errors'], sprintf($msg_error, $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
+ );
+
+ }
+
+ array_push($page['infos'], l10n_dec('nbm_user_change_enabled_updated_data_count', 'nbm_users_change_enabled_updated_data_count', $updated_data_count));
+ if ($error_on_updated_data_count != 0)
+ {
+ array_push($page['errors'],
+ l10n_dec('nbm_user_change_enabled_error_on_updated_data_count',
+ 'nbm_users_change_enabled_error_on_updated_data_count',
+ $error_on_updated_data_count));
+ }
+
+ 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);
+}
+
+?>
diff --git a/BSF/admin/include/functions_permalinks.php b/BSF/admin/include/functions_permalinks.php
new file mode 100644
index 000000000..4b19f02e1
--- /dev/null
+++ b/BSF/admin/include/functions_permalinks.php
@@ -0,0 +1,204 @@
+
diff --git a/BSF/admin/include/functions_plugins.inc.php b/BSF/admin/include/functions_plugins.inc.php
new file mode 100644
index 000000000..1395f34e0
--- /dev/null
+++ b/BSF/admin/include/functions_plugins.inc.php
@@ -0,0 +1,64 @@
+add('plugins_list', l10n('plugins_tab_list'), $link.'plugins_list');
+ $tabsheet->add('plugins_update', l10n('plugins_tab_update'), $link.'plugins_update');
+ $tabsheet->add('plugins_new', l10n('plugins_tab_new'), $link.'plugins_new');
+ $tabsheet->select($selected);
+ $tabsheet->assign();
+}
+?>
\ No newline at end of file
diff --git a/BSF/admin/include/functions_upgrade.php b/BSF/admin/include/functions_upgrade.php
new file mode 100644
index 000000000..7e9a93aef
--- /dev/null
+++ b/BSF/admin/include/functions_upgrade.php
@@ -0,0 +1,98 @@
+
+define(\'PHPWG_IN_UPGRADE\', true);
+
+if you want to upgrade';
+ die($message);
+ }
+}
+
+// concerning upgrade, we use the default tables
+function prepare_conf_upgrade()
+{
+ global $prefixeTable;
+
+ // $conf is not used for users tables
+ // define cannot be re-defined
+ define('CATEGORIES_TABLE', $prefixeTable.'categories');
+ define('COMMENTS_TABLE', $prefixeTable.'comments');
+ define('CONFIG_TABLE', $prefixeTable.'config');
+ define('FAVORITES_TABLE', $prefixeTable.'favorites');
+ define('GROUP_ACCESS_TABLE', $prefixeTable.'group_access');
+ define('GROUPS_TABLE', $prefixeTable.'groups');
+ define('HISTORY_TABLE', $prefixeTable.'history');
+ define('HISTORY_SUMMARY_TABLE', $prefixeTable.'history_summary');
+ define('IMAGE_CATEGORY_TABLE', $prefixeTable.'image_category');
+ define('IMAGES_TABLE', $prefixeTable.'images');
+ define('SESSIONS_TABLE', $prefixeTable.'sessions');
+ define('SITES_TABLE', $prefixeTable.'sites');
+ define('USER_ACCESS_TABLE', $prefixeTable.'user_access');
+ define('USER_GROUP_TABLE', $prefixeTable.'user_group');
+ define('USERS_TABLE', $prefixeTable.'users');
+ define('USER_INFOS_TABLE', $prefixeTable.'user_infos');
+ define('USER_FEED_TABLE', $prefixeTable.'user_feed');
+ define('WAITING_TABLE', $prefixeTable.'waiting');
+ define('RATE_TABLE', $prefixeTable.'rate');
+ define('USER_CACHE_TABLE', $prefixeTable.'user_cache');
+ define('USER_CACHE_CATEGORIES_TABLE', $prefixeTable.'user_cache_categories');
+ define('CADDIE_TABLE', $prefixeTable.'caddie');
+ define('UPGRADE_TABLE', $prefixeTable.'upgrade');
+ define('SEARCH_TABLE', $prefixeTable.'search');
+ define('USER_MAIL_NOTIFICATION_TABLE', $prefixeTable.'user_mail_notification');
+ define('TAGS_TABLE', $prefixeTable.'tags');
+ define('IMAGE_TAG_TABLE', $prefixeTable.'image_tag');
+ define('PLUGINS_TABLE', $prefixeTable.'plugins');
+ define('WEB_SERVICES_ACCESS_TABLE', $prefixeTable.'ws_access');
+ define('OLD_PERMALINKS_TABLE', $prefixeTable.'old_permalinks');
+}
+
+// Create empty local files to avoid log errors
+function create_empty_local_files()
+{
+ $files =
+ array (
+ PHPWG_ROOT_PATH . 'template-common/local-layout.css',
+ PHPWG_ROOT_PATH . 'template/yoga/local-layout.css'
+ );
+
+ foreach ($files as $path)
+ {
+ if (!file_exists ($path))
+ {
+ $file = @fopen($path, "w");
+ @fwrite($file , '/* You can modify this file */');
+ @fclose($file);
+ }
+ }
+}
+
+?>
diff --git a/BSF/admin/include/functions_waiting.inc.php b/BSF/admin/include/functions_waiting.inc.php
new file mode 100644
index 000000000..3e7c3d8b6
--- /dev/null
+++ b/BSF/admin/include/functions_waiting.inc.php
@@ -0,0 +1,41 @@
+add('comments', l10n('comments'), $link_start.'comments');
+ $tabsheet->add('upload', l10n('Pictures'), $link_start.'upload');
+ // TabSheet selection
+ $tabsheet->select($page['page']);
+ // Assign tabsheet to template
+ $tabsheet->assign();
+}
+
+?>
diff --git a/BSF/admin/include/index.php b/BSF/admin/include/index.php
new file mode 100644
index 000000000..c15b15795
--- /dev/null
+++ b/BSF/admin/include/index.php
@@ -0,0 +1,30 @@
+
diff --git a/BSF/admin/include/pclzip.lib.php b/BSF/admin/include/pclzip.lib.php
new file mode 100644
index 000000000..8b3c62d38
--- /dev/null
+++ b/BSF/admin/include/pclzip.lib.php
@@ -0,0 +1,5872 @@
+zipname = $p_zipname;
+ $this->zip_fd = 0;
+ $this->magic_quotes_status = -1;
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 1);
+ 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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, 'PclZip::create', "filelist='$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();
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "$v_size arguments passed to the method");
+
+ // ----- 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)) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Variable list of options detected");
+
+ // ----- 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_CRYPT => 'optional'
+ ));
+ if ($v_result != 1) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0);
+ return 0;
+ }
+ }
+
+ // ----- Look for 2 args
+ // Here we need to support the first historic synopsis of the
+ // method.
+ else {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Static synopsis");
+
+ // ----- 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");
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return 0;
+ }
+ }
+ }
+
+ // ----- 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");
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0);
+ 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 {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Ignore an empty filename");
+ }
+ }
+ }
+
+ // ----- 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) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0);
+ return 0;
+ }
+ }
+
+ // ----- Expand the filelist (expand directories)
+ $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options);
+ if ($v_result != 1) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0);
+ return 0;
+ }
+
+ // ----- Call the create fct
+ $v_result = $this->privCreate($v_filedescr_list, $p_result_list, $v_options);
+ if ($v_result != 1) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0);
+ return 0;
+ }
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $p_result_list);
+ 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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, 'PclZip::add', "filelist='$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();
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "$v_size arguments passed to the method");
+
+ // ----- 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)) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Variable list of options detected");
+
+ // ----- 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_CRYPT => 'optional'
+ ));
+ if ($v_result != 1) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0);
+ return 0;
+ }
+ }
+
+ // ----- Look for 2 args
+ // Here we need to support the first historic synopsis of the
+ // method.
+ else {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Static synopsis");
+
+ // ----- 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return 0;
+ }
+ }
+ }
+
+ // ----- 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");
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0);
+ 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) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0);
+ return 0;
+ }
+ }
+
+ // ----- Expand the filelist (expand directories)
+ $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options);
+ if ($v_result != 1) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0);
+ return 0;
+ }
+
+ // ----- Call the create fct
+ $v_result = $this->privAdd($v_filedescr_list, $p_result_list, $v_options);
+ if ($v_result != 1) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0);
+ return 0;
+ }
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $p_result_list);
+ 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 exists 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()
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, 'PclZip::listContent', "");
+ $v_result=1;
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Check archive
+ if (!$this->privCheckFormat()) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0);
+ return(0);
+ }
+
+ // ----- Call the extracting fct
+ $p_list = array();
+ if (($v_result = $this->privList($p_list)) != 1)
+ {
+ unset($p_list);
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0, PclZip::errorInfo());
+ return(0);
+ }
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $p_list);
+ 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()
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::extract", "");
+ $v_result=1;
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Check archive
+ if (!$this->privCheckFormat()) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0);
+ 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();
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "$v_size arguments passed to the method");
+
+ // ----- 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)) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Variable list of options");
+
+ // ----- 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'
+ ));
+ if ($v_result != 1) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0);
+ 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 {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Static synopsis");
+
+ // ----- 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0, PclZip::errorInfo());
+ return 0;
+ }
+ }
+ }
+
+ // ----- Trace
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "path='$v_path', remove_path='$v_remove_path', remove_all_path='".($v_remove_path?'true':'false')."'");
+
+ // ----- 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);
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0, PclZip::errorInfo());
+ return(0);
+ }
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $p_list);
+ 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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::extractByIndex", "index='$p_index', ...");
+ $v_result=1;
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Check archive
+ if (!$this->privCheckFormat()) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0);
+ 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();
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "$v_size arguments passed to the method");
+
+ // ----- 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)) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Variable list of options");
+
+ // ----- 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'
+ ));
+ if ($v_result != 1) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0);
+ 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;
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Option PCLZIP_OPT_EXTRACT_AS_STRING not set.");
+ }
+ else {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Option PCLZIP_OPT_EXTRACT_AS_STRING set.");
+ }
+ }
+
+ // ----- Look for 2 args
+ // Here we need to support the first historic synopsis of the
+ // method.
+ else {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Static synopsis");
+
+ // ----- 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return 0;
+ }
+ }
+ }
+
+ // ----- Trace
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "index='$p_index', path='$v_path', remove_path='$v_remove_path', remove_all_path='".($v_remove_path?'true':'false')."'");
+
+ // ----- 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) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0);
+ return 0;
+ }
+ $v_options[PCLZIP_OPT_BY_INDEX] = $v_options_trick[PCLZIP_OPT_BY_INDEX];
+
+ // ----- Call the extracting fct
+ if (($v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, $v_remove_all_path, $v_options)) < 1) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0, PclZip::errorInfo());
+ return(0);
+ }
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $p_list);
+ 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()
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::delete", "");
+ $v_result=1;
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Check archive
+ if (!$this->privCheckFormat()) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0);
+ return(0);
+ }
+
+ // ----- Set default values
+ $v_options = array();
+
+ // ----- Look for variable options arguments
+ $v_size = func_num_args();
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "$v_size arguments passed to the method");
+
+ // ----- 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) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0);
+ 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);
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0, PclZip::errorInfo());
+ return(0);
+ }
+
+ // ----- Magic quotes trick
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_list);
+ return $v_list;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : deleteByIndex()
+ // Description :
+ // ***** Deprecated *****
+ // delete(PCLZIP_OPT_BY_INDEX, $p_index) should be prefered.
+ // --------------------------------------------------------------------------------
+ function deleteByIndex($p_index)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::deleteByIndex", "index='$p_index'");
+
+ $p_list = $this->delete(PCLZIP_OPT_BY_INDEX, $p_index);
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $p_list);
+ 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()
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::properties", "");
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Magic quotes trick
+ $this->privDisableMagicQuotes();
+
+ // ----- Check archive
+ if (!$this->privCheckFormat()) {
+ $this->privSwapBackMagicQuotes();
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0);
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode");
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), 0);
+ return 0;
+ }
+
+ // ----- Read the central directory informations
+ $v_central_dir = array();
+ if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
+ {
+ $this->privSwapBackMagicQuotes();
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0);
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_prop);
+ 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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::duplicate", "");
+ $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'))
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "The parameter is valid PclZip object '".$p_archive->zipname."'");
+
+ // ----- 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))
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "The parameter is a filename '$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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::merge", "");
+ $v_result = 1;
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Check archive
+ if (!$this->privCheckFormat()) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0);
+ 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'))
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "The parameter is valid PclZip object");
+
+ // ----- 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))
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "The parameter is a filename");
+
+ // ----- 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privCheckFormat", "");
+ $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."'");
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, false, PclZip::errorInfo());
+ 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."'");
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, false, PclZip::errorInfo());
+ return(false);
+ }
+
+ // ----- Check the magic code
+ // TBC
+
+ // ----- Check the central header
+ // TBC
+
+ // ----- Check each file header
+ // TBC
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privParseOptions", "");
+ $v_result=1;
+
+ // ----- Read the options
+ $i=0;
+ while ($i<$p_size) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Looking for table index $i, option = '".PclZipUtilOptionText($p_options_list[$i])."(".$p_options_list[$i].")'");
+
+ // ----- 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value
+ $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "".PclZipUtilOptionText($p_options_list[$i])." = '".$v_result_list[$p_options_list[$i]]."'");
+ $i++;
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ 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);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "".PclZipUtilOptionText($p_options_list[$i])." = '".$v_result_list[$p_options_list[$i]]."'");
+ $i++;
+ }
+ else {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "".PclZipUtilOptionText($p_options_list[$i])." set with an empty value is ignored.");
+ }
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+ ////--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "".PclZipUtilOptionText($p_options_list[$i])." = '".$v_result_list[$p_options_list[$i]]."'");
+ $i++;
+ break;
+
+ // ----- Look for options that request an EREG or PREG expression
+ case PCLZIP_OPT_BY_EREG :
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "".PclZipUtilOptionText($p_options_list[$i])." = '".$v_result_list[$p_options_list[$i]]."'");
+ $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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "".PclZipUtilOptionText($p_options_list[$i])." = '".$v_result_list[$p_options_list[$i]]."'");
+ $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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value
+ $v_work_list = array();
+ if (is_string($p_options_list[$i+1])) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Index value is a 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])) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Index value is an 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])) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Index value is an array");
+ $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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value
+ $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "".PclZipUtilOptionText($p_options_list[$i])." = '".$v_result_list[$p_options_list[$i]]."'");
+ $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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value
+ $v_function_name = $p_options_list[$i+1];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "call-back ".PclZipUtilOptionText($p_options_list[$i])." = '".$v_function_name."'");
+
+ // ----- 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ 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') {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Detect a mandatory option : ".PclZipUtilOptionText($key)."(".$key.")");
+ // ----- Look if present
+ if (!isset($v_result_list[$key])) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")");
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+ }
+ }
+ }
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privFileDescrParseAtt", "");
+ $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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ 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)."'");
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+
+ $p_filedescr['filename'] = PclZipUtilPathReduction($v_value);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "".PclZipUtilOptionText($v_key)." = '".$v_value."'");
+
+ if ($p_filedescr['filename'] == '') {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty filename for attribute '".PclZipUtilOptionText($v_key)."'");
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ 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)."'");
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+
+ $p_filedescr['new_short_name'] = PclZipUtilPathReduction($v_value);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "".PclZipUtilOptionText($v_key)." = '".$v_value."'");
+
+ if ($p_filedescr['new_short_name'] == '') {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty short filename for attribute '".PclZipUtilOptionText($v_key)."'");
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ 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)."'");
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+
+ $p_filedescr['new_full_name'] = PclZipUtilPathReduction($v_value);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "".PclZipUtilOptionText($v_key)." = '".$v_value."'");
+
+ if ($p_filedescr['new_full_name'] == '') {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty full filename for attribute '".PclZipUtilOptionText($v_key)."'");
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ 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)."'");
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+
+ $p_filedescr['comment'] = $v_value;
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "".PclZipUtilOptionText($v_key)." = '".$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)."'");
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+
+ $p_filedescr['mtime'] = $v_value;
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "".PclZipUtilOptionText($v_key)." = '".$v_value."'");
+ break;
+
+ case PCLZIP_ATT_FILE_CONTENT :
+ $p_filedescr['content'] = $v_value;
+ ////--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "".PclZipUtilOptionText($v_key)." = '".$v_value."'");
+ break;
+
+ default :
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER,
+ "Unknown parameter '".$v_key."'");
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ 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') {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Detect a mandatory option : ".PclZipUtilOptionText($key)."(".$key.")");
+ // ----- Look if present
+ if (!isset($p_file_list[$key])) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")");
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+ }
+ }
+ }
+
+ // end foreach
+ }
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privFileDescrExpand()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // 1 on success.
+ // 0 on failure.
+ // --------------------------------------------------------------------------------
+ function privFileDescrExpand(&$p_filedescr_list, &$p_options)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privFileDescrExpand", "");
+ $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) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Looking for '".$v_item_handler."' in the directory");
+
+ // ----- 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']) {
+ 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 {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Unable to open dir '".$v_descr['filename']."' in read mode. Skipped.");
+ // 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) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Concat the resulting list
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Merging result list (size '".sizeof($v_result_list)."') with dirlist (size '".sizeof($v_dirlist_descr)."')");
+ $v_result_list = array_merge($v_result_list, $v_dirlist_descr);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "merged result list is size '".sizeof($v_result_list)."'");
+ }
+ else {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Nothing in this folder to expand.");
+ }
+
+ // ----- Free local array
+ unset($v_dirlist_descr);
+ }
+ }
+
+ // ----- Get the result list
+ $p_filedescr_list = $v_result_list;
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privCreate()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privCreate($p_filedescr_list, &$p_result_list, &$p_options)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privCreate", "list");
+ $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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privAdd()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privAdd($p_filedescr_list, &$p_result_list, &$p_options)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privAdd", "list");
+ $v_result=1;
+ $v_list_detail = array();
+
+ // ----- Look if the archive exists or is empty
+ if ((!is_file($this->zipname)) || (filesize($this->zipname) == 0))
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Archive does not exist, or is empty, create it.");
+
+ // ----- Do a create
+ $v_result = $this->privCreate($p_filedescr_list, $p_result_list, $p_options);
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // ----- Magic quotes trick
+ $this->privDisableMagicQuotes();
+
+ // ----- Open the zip file
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode");
+ if (($v_result=$this->privOpenFd('rb')) != 1)
+ {
+ // ----- Magic quotes trick
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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();
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Go to beginning of File
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in file : ".ftell($this->zip_fd)."'");
+ @rewind($this->zip_fd);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in file : ".ftell($this->zip_fd)."'");
+
+ // ----- Creates a temporay file
+ $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp';
+
+ // ----- Open the temporary file in write mode
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ 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);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes");
+ $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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Store the offset of the central dir
+ $v_offset = @ftell($this->zip_fd);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "New offset of central dir : $v_offset");
+
+ // ----- 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);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes");
+ $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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privOpenFd()
+ // Description :
+ // Parameters :
+ // --------------------------------------------------------------------------------
+ function privOpenFd($p_mode)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privOpenFd", 'mode='.$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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+
+ // ----- Open the zip file
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Open file in '.$p_mode.' mode');
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privCloseFd()
+ // Description :
+ // Parameters :
+ // --------------------------------------------------------------------------------
+ function privCloseFd()
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privCloseFd", "");
+ $v_result=1;
+
+ if ($this->zip_fd != 0)
+ @fclose($this->zip_fd);
+ $this->zip_fd = 0;
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privAddList", "list");
+ $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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privAddFileList", "filedescr_list");
+ $v_result=1;
+ $v_header = array();
+
+ // ----- Recuperate the current number of elt in list
+ $v_nb = sizeof($p_result_list);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Before add, list have ".$v_nb." elements");
+
+ // ----- Loop on the files
+ for ($j=0; ($jprivAddFile($p_filedescr_list[$j], $v_header,
+ $p_options);
+ if ($v_result != 1) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Store the file infos
+ $p_result_list[$v_nb++] = $v_header;
+ }
+ }
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "After add, list have ".$v_nb." elements");
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privAddFile()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privAddFile($p_filedescr, &$p_header, &$p_options)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privAddFile", "filename='".$p_filedescr['filename']."'");
+ $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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+
+ // ----- Look for a stored different filename
+ /* TBC : Removed
+ if (isset($p_filedescr['stored_filename'])) {
+ $v_stored_filename = $p_filedescr['stored_filename'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, 'Stored filename is NOT the same "'.$v_stored_filename.'"');
+ }
+ else {
+ $v_stored_filename = $p_filedescr['stored_filename'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, 'Stored filename is the same');
+ }
+ */
+
+ // ----- 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']);
+ }
+
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Header external extension '".sprintf("0x%X",$p_header['external'])."'");
+
+ // ----- Look for filetime
+ if (isset($p_filedescr['mtime'])) {
+ $p_header['mtime'] = $p_filedescr['mtime'];
+ }
+ else if ($p_filedescr['type'] == 'virtual_file') {
+ $p_header['mtime'] = mktime();
+ }
+ 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])) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "A pre-callback '".$p_options[PCLZIP_CB_PRE_ADD]."()') is defined for the extraction");
+
+ // ----- 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);');
+ 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']);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "New stored filename is '".$p_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 (is_file($p_filename))
+ if ( ($p_filedescr['type'] == 'file')
+ || ($p_filedescr['type'] == 'virtual_file')) {
+
+ // ----- Get content from real file
+ if ($p_filedescr['type'] == 'file') {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "'".$p_filename."' is a file");
+
+ // ----- 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");
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+
+ // ----- Read the file content
+ $v_content = @fread($v_file, $p_header['size']);
+
+ // ----- Close the file
+ @fclose($v_file);
+ }
+ else if ($p_filedescr['type'] == 'virtual_file') {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Add by string");
+ $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]) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "File will not be compressed");
+ // ----- Set header parameters
+ $p_header['compressed_size'] = $p_header['size'];
+ $p_header['compression'] = 0;
+ }
+
+ // ----- Look for normal compression
+ else {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "File will be compressed");
+ // ----- Compress the content
+ $v_content = @gzdeflate($v_content);
+
+ // ----- Set header parameters
+ $p_header['compressed_size'] = strlen($v_content);
+ $p_header['compression'] = 8;
+ }
+
+ // ----- Look for encryption
+ /*
+ if ((isset($p_options[PCLZIP_OPT_CRYPT]))
+ && ($p_options[PCLZIP_OPT_CRYPT] != "")) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "File need to be crypted ....");
+
+ // Should be a random header
+ $v_header = 'xxxxxxxxxxxx';
+ $v_content_compressed = PclZipUtilZipEncrypt($v_content_compressed,
+ $p_header['compressed_size'],
+ $v_header,
+ $p_header['crc'],
+ "test");
+
+ $p_header['compressed_size'] += 12;
+ $p_header['flag'] = 1;
+
+ // ----- Add the header to the data
+ $v_content_compressed = $v_header.$v_content_compressed;
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Size after header : ".strlen($v_content_compressed)."");
+ }
+ */
+
+ // ----- Call the header generation
+ if (($v_result = $this->privWriteFileHeader($p_header)) != 1) {
+ @fclose($v_file);
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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') {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "'".$p_filename."' is a 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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ }
+ }
+
+ // ----- Look for post-add callback
+ if (isset($p_options[PCLZIP_CB_POST_ADD])) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "A post-callback '".$p_options[PCLZIP_CB_POST_ADD]."()') is defined for the extraction");
+
+ // ----- 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);');
+ if ($v_result == 0) {
+ // ----- Ignored
+ $v_result = 1;
+ }
+
+ // ----- Update the informations
+ // Nothing can be modified
+ }
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privCalculateStoredFilename", "filename='".$p_filedescr['filename']."'");
+ $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 = '';
+ }
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Remove path ='".$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'])) {
+ $v_stored_filename = $p_filedescr['new_full_name'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Changing full name of '".$p_filename."' for '".$v_stored_filename."'");
+ }
+
+ // ----- Look for path and/or short name change
+ else {
+
+ // ----- Look for short name change
+ 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'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Changing short name of '".$p_filename."' for '".$v_stored_filename."'");
+ }
+ 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);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Remove all path selected change '".$p_filename."' for '".$v_stored_filename."'");
+ }
+ // ----- Look for partial path remove
+ else if ($p_remove_dir != "") {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Partial path to remove");
+ 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 = "";
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Path to remove is the current folder");
+ }
+ else {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Remove path '$p_remove_dir' in file '$v_stored_filename'");
+ $v_stored_filename = substr($v_stored_filename,
+ strlen($p_remove_dir));
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Result is '$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;
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Add path '$p_add_dir' in file '$p_filename' = '$v_stored_filename'");
+ }
+ }
+
+ // ----- Filename (reduce the path of stored name)
+ $v_stored_filename = PclZipUtilPathReduction($v_stored_filename);
+ $p_filedescr['stored_filename'] = $v_stored_filename;
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Stored filename will be '".$p_filedescr['stored_filename']."', strlen ".strlen($p_filedescr['stored_filename']));
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privWriteFileHeader()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privWriteFileHeader(&$p_header)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privWriteFileHeader", 'file="'.$p_header['filename'].'", stored as "'.$p_header['stored_filename'].'"');
+ $v_result=1;
+
+ // ----- Store the offset position of the file
+ $p_header['offset'] = ftell($this->zip_fd);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, 'File offset of the header :'.$p_header['offset']);
+
+ // ----- Transform UNIX mtime to DOS format mdate/mtime
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Date : \''.date("d/m/y H:i:s", $p_header['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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privWriteCentralFileHeader()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privWriteCentralFileHeader(&$p_header)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privWriteCentralFileHeader", 'file="'.$p_header['filename'].'", stored as "'.$p_header['stored_filename'].'"');
+ $v_result=1;
+
+ // TBC
+ //for(reset($p_header); $key = key($p_header); next($p_header)) {
+ // //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "header[$key] = ".$p_header[$key]);
+ //}
+
+ // ----- Transform UNIX mtime to DOS format mdate/mtime
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Date : \''.date("d/m/y H:i:s", $p_header['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'];
+
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Comment size : \''.$p_header['comment_len'].'\'');
+
+ // ----- 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privWriteCentralHeader()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privWriteCentralHeader($p_nb_entries, $p_size, $p_offset, $p_comment)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privWriteCentralHeader", 'nb_entries='.$p_nb_entries.', size='.$p_size.', offset='.$p_offset.', comment="'.$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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privList()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privList(&$p_list)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privList", "list");
+ $v_result=1;
+
+ // ----- Magic quotes trick
+ $this->privDisableMagicQuotes();
+
+ // ----- Open the zip file
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode");
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+
+ // ----- Read the central directory informations
+ $v_central_dir = array();
+ if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
+ {
+ $this->privSwapBackMagicQuotes();
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Go to beginning of Central Dir
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Offset : ".$v_central_dir['offset']."'");
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Position in file : ".ftell($this->zip_fd)."'");
+ @rewind($this->zip_fd);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Position in file : ".ftell($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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Position in file : ".ftell($this->zip_fd)."'");
+
+ // ----- 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();
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privConvertHeader2FileInfo", "Filename='".$p_header['filename']."'");
+ $v_result=1;
+
+ // ----- Get the interesting attributes
+ $p_info['filename'] = $p_header['filename'];
+ $p_info['stored_filename'] = $p_header['stored_filename'];
+ $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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privExtractByRule", "path='$p_path', remove_path='$p_remove_path', remove_all_path='".($p_remove_all_path?'true':'false')."'");
+ $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) == "/")
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Destination path [$p_path] ends by '/'");
+ $p_path = substr($p_path, 0, strlen($p_path)-1);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Modified to [$p_path]");
+ }
+ }
+
+ // ----- 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
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode");
+ if (($v_result = $this->privOpenFd('rb')) != 1)
+ {
+ $this->privSwapBackMagicQuotes();
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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();
+
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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++)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Read next file header entry : '$i'");
+
+ // ----- Read next Central dir entry
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Position before rewind : ".ftell($this->zip_fd)."'");
+ @rewind($this->zip_fd);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Position after rewind : ".ftell($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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Position after fseek : ".ftell($this->zip_fd)."'");
+
+ // ----- Read the file header
+ $v_header = array();
+ if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1)
+ {
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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)) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract with rule 'ByName'");
+
+ // ----- 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])) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "The directory is in the file path");
+ $v_extract = true;
+ }
+ }
+ // ----- Look for a filename
+ elseif ($v_header['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "The file is the right one.");
+ $v_extract = true;
+ }
+ }
+ }
+
+ // ----- Look for extract by ereg rule
+ else if ( (isset($p_options[PCLZIP_OPT_BY_EREG]))
+ && ($p_options[PCLZIP_OPT_BY_EREG] != "")) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract by ereg '".$p_options[PCLZIP_OPT_BY_EREG]."'");
+
+ if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header['stored_filename'])) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Filename match the regular expression");
+ $v_extract = true;
+ }
+ }
+
+ // ----- Look for extract by preg rule
+ else if ( (isset($p_options[PCLZIP_OPT_BY_PREG]))
+ && ($p_options[PCLZIP_OPT_BY_PREG] != "")) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract with rule 'ByEreg'");
+
+ if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header['stored_filename'])) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Filename match the regular expression");
+ $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)) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract with rule 'ByIndex'");
+
+ // ----- 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'])) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Found as part of an index range");
+ $v_extract = true;
+ }
+ if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Do not look this index range for next loop");
+ $j_start = $j+1;
+ }
+
+ if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Index range is greater than index, stop loop");
+ break;
+ }
+ }
+ }
+
+ // ----- Look for no rule, which means extract all the archive
+ else {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract with no rule (extract all)");
+ $v_extract = true;
+ }
+
+ // ----- Check compression method
+ if ( ($v_extract)
+ && ( ($v_header['compression'] != 8)
+ && ($v_header['compression'] != 0))) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Unsupported compression method (".$v_header['compression'].")");
+ $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)) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "PCLZIP_OPT_STOP_ON_ERROR is selected, extraction will be stopped");
+
+ $this->privSwapBackMagicQuotes();
+
+ PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_COMPRESSION,
+ "Filename '".$v_header['stored_filename']."' is "
+ ."compressed by an unsupported compression "
+ ."method (".$v_header['compression'].") ");
+
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+ }
+
+ // ----- Check encrypted files
+ if (($v_extract) && (($v_header['flag'] & 1) == 1)) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Unsupported file encryption");
+ $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)) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "PCLZIP_OPT_STOP_ON_ERROR is selected, extraction will be stopped");
+
+ $this->privSwapBackMagicQuotes();
+
+ PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION,
+ "Unsupported encryption for "
+ ." filename '".$v_header['stored_filename']
+ ."'");
+
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+ }
+
+ // ----- Look for real extraction
+ if (($v_extract) && ($v_header['status'] != 'ok')) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "No need for extract");
+ $v_result = $this->privConvertHeader2FileInfo($v_header,
+ $p_file_list[$v_nb_extracted++]);
+ if ($v_result != 1) {
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ $v_extract = false;
+ }
+
+ // ----- Look for real extraction
+ if ($v_extract)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting file '".$v_header['filename']."', index '$i'");
+
+ // ----- Go to the file position
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position before rewind : ".ftell($this->zip_fd)."'");
+ @rewind($this->zip_fd);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position after rewind : ".ftell($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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position after fseek : ".ftell($this->zip_fd)."'");
+
+ // ----- Look for extraction as string
+ if ($p_options[PCLZIP_OPT_EXTRACT_AS_STRING]) {
+
+ // ----- Extracting the file
+ $v_result1 = $this->privExtractFileAsString($v_header, $v_string);
+ if ($v_result1 < 1) {
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result1);
+ 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();
+
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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();
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result1);
+ 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();
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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();
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result1);
+ 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();
+
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Look for user callback abort
+ if ($v_result1 == 2) {
+ break;
+ }
+ }
+ }
+ }
+
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, 'PclZip::privExtractFile', "path='$p_path', remove_path='$p_remove_path', remove_all_path='".($p_remove_all_path?'true':'false')."'");
+ $v_result=1;
+
+ // ----- Read the file header
+ if (($v_result = $this->privReadFileHeader($v_header)) != 1)
+ {
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Found file '".$v_header['filename']."', size '".$v_header['size']."'");
+
+ // ----- 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) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "The entry is a folder : need to be filtered");
+
+ $p_entry['status'] = "filtered";
+
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "All path is removed");
+ // ----- Get the basename of the path
+ $p_entry['filename'] = basename($p_entry['filename']);
+ }
+
+ // ----- Look for path to remove
+ else if ($p_remove_path != "")
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Look for some path to remove");
+ if (PclZipUtilPathInclusion($p_remove_path, $p_entry['filename']) == 2)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "The folder is the same as the removed path '".$p_entry['filename']."'");
+
+ // ----- Change the file status
+ $p_entry['status'] = "filtered";
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ $p_remove_path_size = strlen($p_remove_path);
+ if (substr($p_entry['filename'], 0, $p_remove_path_size) == $p_remove_path)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Found path '$p_remove_path' to remove in file '".$p_entry['filename']."'");
+
+ // ----- Remove the path
+ $p_entry['filename'] = substr($p_entry['filename'], $p_remove_path_size);
+
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Resulting file is '".$p_entry['filename']."'");
+ }
+ }
+
+ // ----- 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])) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Check the extract directory restriction");
+ $v_inclusion
+ = PclZipUtilPathInclusion($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION],
+ $p_entry['filename']);
+ if ($v_inclusion == 0) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "PCLZIP_OPT_EXTRACT_DIR_RESTRICTION is selected, file is outside restriction");
+
+ PclZip::privErrorLog(PCLZIP_ERR_DIRECTORY_RESTRICTION,
+ "Filename '".$p_entry['filename']."' is "
+ ."outside PCLZIP_OPT_EXTRACT_DIR_RESTRICTION");
+
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+ }
+
+ // ----- Look for pre-extract callback
+ if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "A pre-callback '".$p_options[PCLZIP_CB_PRE_EXTRACT]."()') is defined for the extraction");
+
+ // ----- 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);');
+ if ($v_result == 0) {
+ // ----- Change the file status
+ $p_entry['status'] = "skipped";
+ $v_result = 1;
+ }
+
+ // ----- Look for abort result
+ if ($v_result == 2) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "User callback abort the extraction");
+ // ----- 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'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "New filename is '".$p_entry['filename']."'");
+ }
+
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting file (with path) '".$p_entry['filename']."', size '$v_header[size]'");
+
+ // ----- 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']))
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "File '".$p_entry['filename']."' already exists");
+
+ // ----- Look if file is a directory
+ if (is_dir($p_entry['filename']))
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Existing file '".$p_entry['filename']."' is a directory");
+
+ // ----- 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)) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "PCLZIP_OPT_STOP_ON_ERROR is selected, extraction will be stopped");
+
+ PclZip::privErrorLog(PCLZIP_ERR_ALREADY_A_DIRECTORY,
+ "Filename '".$p_entry['filename']."' is "
+ ."already used by an existing directory");
+
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+ }
+ // ----- Look if file is write protected
+ else if (!is_writeable($p_entry['filename']))
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Existing file '".$p_entry['filename']."' is write protected");
+
+ // ----- 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)) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "PCLZIP_OPT_STOP_ON_ERROR is selected, extraction will be stopped");
+
+ PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL,
+ "Filename '".$p_entry['filename']."' exists "
+ ."and is write protected");
+
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+ }
+
+ // ----- Look if the extracted file is older
+ else if (filemtime($p_entry['filename']) > $p_entry['mtime'])
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Existing file '".$p_entry['filename']."' is newer (".date("l dS of F Y h:i:s A", filemtime($p_entry['filename'])).") than the extracted file (".date("l dS of F Y h:i:s A", $p_entry['mtime']).")");
+ // ----- Change the file status
+ if ( (isset($p_options[PCLZIP_OPT_REPLACE_NEWER]))
+ && ($p_options[PCLZIP_OPT_REPLACE_NEWER]===true)) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "PCLZIP_OPT_REPLACE_NEWER is selected, file will be replaced");
+ }
+ else {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "File will not be replaced");
+ $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)) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "PCLZIP_OPT_STOP_ON_ERROR is selected, extraction will be stopped");
+
+ PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL,
+ "Newer version of '".$p_entry['filename']."' exists "
+ ."and option PCLZIP_OPT_REPLACE_NEWER is not selected");
+
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+ }
+ }
+ else {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Existing file '".$p_entry['filename']."' is older than the extrated one - will be replaced by the extracted one (".date("l dS of F Y h:i:s A", filemtime($p_entry['filename'])).") than the extracted file (".date("l dS of F Y h:i:s A", $p_entry['mtime']).")");
+ }
+ }
+
+ // ----- 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) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Unable to create path for '".$p_entry['filename']."'");
+
+ // ----- Change the file status
+ $p_entry['status'] = "path_creation_fail";
+
+ // ----- Return
+ ////--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ //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) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting an un-compressed file");
+
+ // ----- Opening destination file
+ if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Error while opening '".$p_entry['filename']."' in write binary mode");
+
+ // ----- Change the file status
+ $p_entry['status'] = "write_error";
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Read '".$p_entry['size']."' bytes");
+
+ // ----- 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);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Read $v_read_size bytes");
+ $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 {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting a compressed file (Compression method ".$p_entry['compression'].")");
+ // ----- TBC
+ // Need to be finished
+ if (($p_entry['flag'] & 1) == 1) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "File is encrypted");
+ /*
+ // ----- Read the encryption header
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Read 12 encryption header bytes");
+ $v_encryption_header = @fread($this->zip_fd, 12);
+
+ // ----- Read the encrypted & compressed file in a buffer
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Read '".($p_entry['compressed_size']-12)."' compressed & encrypted bytes");
+ $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']-12);
+
+ // ----- Decrypt the buffer
+ $this->privDecrypt($v_encryption_header, $v_buffer,
+ $p_entry['compressed_size']-12, $p_entry['crc']);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Buffer is '".$v_buffer."'");
+ */
+ }
+ else {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Read '".$p_entry['compressed_size']."' compressed bytes");
+ // ----- 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) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Unable to inflate compressed file");
+
+ // ----- Change the file status
+ // TBC
+ $p_entry['status'] = "error";
+
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Opening destination file
+ if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Error while opening '".$p_entry['filename']."' in write binary mode");
+
+ // ----- Change the file status
+ $p_entry['status'] = "write_error";
+
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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])) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "chmod option activated '".$p_options[PCLZIP_OPT_SET_CHMOD]."'");
+
+ // ----- Change the mode of the file
+ @chmod($p_entry['filename'], $p_options[PCLZIP_OPT_SET_CHMOD]);
+ }
+
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extraction done");
+ }
+ }
+
+ // ----- 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])) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "A post-callback '".$p_options[PCLZIP_CB_POST_EXTRACT]."()') is defined for the extraction");
+
+ // ----- 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);');
+
+ // ----- Look for abort result
+ if ($v_result == 2) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "User callback abort the extraction");
+ $v_result = PCLZIP_ERR_USER_ABORTED;
+ }
+ }
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privExtractFileInOutput()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privExtractFileInOutput(&$p_entry, &$p_options)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, 'PclZip::privExtractFileInOutput', "");
+ $v_result=1;
+
+ // ----- Read the file header
+ if (($v_result = $this->privReadFileHeader($v_header)) != 1) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Found file '".$v_header['filename']."', size '".$v_header['size']."'");
+
+ // ----- 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])) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "A pre-callback '".$p_options[PCLZIP_CB_PRE_EXTRACT]."()') is defined for the extraction");
+
+ // ----- 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);');
+ if ($v_result == 0) {
+ // ----- Change the file status
+ $p_entry['status'] = "skipped";
+ $v_result = 1;
+ }
+
+ // ----- Look for abort result
+ if ($v_result == 2) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "User callback abort the extraction");
+ // ----- 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'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "New filename is '".$p_entry['filename']."'");
+ }
+
+ // ----- Trace
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting file (with path) '".$p_entry['filename']."', size '$v_header[size]'");
+
+ // ----- 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']) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting an un-compressed file");
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Reading '".$p_entry['size']."' bytes");
+
+ // ----- 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 {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting a compressed file");
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Reading '".$p_entry['size']."' bytes");
+
+ // ----- 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);
+ }
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extraction done");
+ }
+ }
+
+ // ----- 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])) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "A post-callback '".$p_options[PCLZIP_CB_POST_EXTRACT]."()') is defined for the extraction");
+
+ // ----- 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);');
+
+ // ----- Look for abort result
+ if ($v_result == 2) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "User callback abort the extraction");
+ $v_result = PCLZIP_ERR_USER_ABORTED;
+ }
+ }
+
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privExtractFileAsString()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privExtractFileAsString(&$p_entry, &$p_string)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, 'PclZip::privExtractFileAsString', "p_entry['filename']='".$p_entry['filename']."'");
+ $v_result=1;
+
+ // ----- Read the file header
+ $v_header = array();
+ if (($v_result = $this->privReadFileHeader($v_header)) != 1)
+ {
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Found file '".$v_header['filename']."', size '".$v_header['size']."'");
+
+ // ----- Check that the file header is coherent with $p_entry info
+ if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) {
+ // TBC
+ }
+
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting file in string (with path) '".$p_entry['filename']."', size '$v_header[size]'");
+
+ // ----- 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) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting an un-compressed file");
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Reading '".$p_entry['size']."' bytes");
+
+ // ----- Reading the file
+ $p_string = @fread($this->zip_fd, $p_entry['compressed_size']);
+ }
+ else {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting a compressed file (compression method '".$p_entry['compression']."')");
+
+ // ----- 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
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extraction done");
+ }
+ else {
+ // TBC : error : can not extract a folder in a string
+ }
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privReadFileHeader()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privReadFileHeader(&$p_header)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privReadFileHeader", "");
+ $v_result=1;
+
+ // ----- Read the 4 bytes signature
+ $v_binary_data = @fread($this->zip_fd, 4);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Binary data is : '".sprintf("%08x", $v_binary_data)."'");
+ $v_data = unpack('Vid', $v_binary_data);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Binary signature is : '".sprintf("0x%08x", $v_data['id'])."'");
+
+ // ----- Check signature
+ if ($v_data['id'] != 0x04034b50)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Invalid File header");
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure');
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ 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";
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Invalid block size : ".strlen($v_binary_data));
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data));
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+
+ // ----- Extract the values
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Header : '".$v_binary_data."'");
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Header (Hex) : '".bin2hex($v_binary_data)."'");
+ $v_data = unpack('vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $v_binary_data);
+
+ // ----- Get filename
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "File name length : ".$v_data['filename_len']);
+ $p_header['filename'] = fread($this->zip_fd, $v_data['filename_len']);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Filename : \''.$p_header['filename'].'\'');
+
+ // ----- Get extra_fields
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extra field length : ".$v_data['extra_len']);
+ if ($v_data['extra_len'] != 0) {
+ $p_header['extra'] = fread($this->zip_fd, $v_data['extra_len']);
+ }
+ else {
+ $p_header['extra'] = '';
+ }
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Extra field : \''.bin2hex($p_header['extra']).'\'');
+
+ // ----- Extract properties
+ $p_header['version_extracted'] = $v_data['version'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Version need to extract : ('.$p_header['version_extracted'].') \''.($p_header['version_extracted']/10).'.'.($p_header['version_extracted']%10).'\'');
+ $p_header['compression'] = $v_data['compression'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Compression method : \''.$p_header['compression'].'\'');
+ $p_header['size'] = $v_data['size'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Size : \''.$p_header['size'].'\'');
+ $p_header['compressed_size'] = $v_data['compressed_size'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Compressed Size : \''.$p_header['compressed_size'].'\'');
+ $p_header['crc'] = $v_data['crc'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'CRC : \''.sprintf("0x%X", $p_header['crc']).'\'');
+ $p_header['flag'] = $v_data['flag'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Flag : \''.$p_header['flag'].'\'');
+ $p_header['filename_len'] = $v_data['filename_len'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Filename_len : \''.$p_header['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);
+
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Date : \''.date("d/m/y H:i:s", $p_header['mtime']).'\'');
+ }
+ else
+ {
+ $p_header['mtime'] = time();
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Date is actual : \''.date("d/m/y H:i:s", $p_header['mtime']).'\'');
+ }
+
+ // TBC
+ //for(reset($v_data); $key = key($v_data); next($v_data)) {
+ // //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Attribut[$key] = ".$v_data[$key]);
+ //}
+
+ // ----- Set the stored filename
+ $p_header['stored_filename'] = $p_header['filename'];
+
+ // ----- Set the status field
+ $p_header['status'] = "ok";
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privReadCentralFileHeader()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privReadCentralFileHeader(&$p_header)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privReadCentralFileHeader", "");
+ $v_result=1;
+
+ // ----- Read the 4 bytes signature
+ $v_binary_data = @fread($this->zip_fd, 4);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Binary data is : '".sprintf("%08x", $v_binary_data)."'");
+ $v_data = unpack('Vid', $v_binary_data);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Binary signature is : '".sprintf("0x%08x", $v_data['id'])."'");
+
+ // ----- Check signature
+ if ($v_data['id'] != 0x02014b50)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Invalid Central Dir File signature");
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure');
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ 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";
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Invalid block size : ".strlen($v_binary_data));
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data));
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+
+ // ----- Extract the values
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Header : '".$v_binary_data."'");
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Header (Hex) : '".bin2hex($v_binary_data)."'");
+ $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
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "File name length : ".$p_header['filename_len']);
+ if ($p_header['filename_len'] != 0)
+ $p_header['filename'] = fread($this->zip_fd, $p_header['filename_len']);
+ else
+ $p_header['filename'] = '';
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Filename : \''.$p_header['filename'].'\'');
+
+ // ----- Get extra
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Extra length : ".$p_header['extra_len']);
+ if ($p_header['extra_len'] != 0)
+ $p_header['extra'] = fread($this->zip_fd, $p_header['extra_len']);
+ else
+ $p_header['extra'] = '';
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Extra : \''.$p_header['extra'].'\'');
+
+ // ----- Get comment
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Comment length : ".$p_header['comment_len']);
+ if ($p_header['comment_len'] != 0)
+ $p_header['comment'] = fread($this->zip_fd, $p_header['comment_len']);
+ else
+ $p_header['comment'] = '';
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Comment : \''.$p_header['comment'].'\'');
+
+ // ----- Extract properties
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Version : \''.($p_header['version']/10).'.'.($p_header['version']%10).'\'');
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Version need to extract : \''.($p_header['version_extracted']/10).'.'.($p_header['version_extracted']%10).'\'');
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Size : \''.$p_header['size'].'\'');
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Compressed Size : \''.$p_header['compressed_size'].'\'');
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'CRC : \''.sprintf("0x%X", $p_header['crc']).'\'');
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Flag : \''.$p_header['flag'].'\'');
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Offset : \''.$p_header['offset'].'\'');
+
+ // ----- 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);
+
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Date : \''.date("d/m/y H:i:s", $p_header['mtime']).'\'');
+ }
+ else
+ {
+ $p_header['mtime'] = time();
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Date is actual : \''.date("d/m/y H:i:s", $p_header['mtime']).'\'');
+ }
+
+ // ----- 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
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Internal (Hex) : '".sprintf("Ox%04X", $p_header['internal'])."'");
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "External (Hex) : '".sprintf("Ox%04X", $p_header['external'])."' (".(($p_header['external']&0x00000010)==0x00000010?'is a folder':'is a file').')');
+ if (substr($p_header['filename'], -1) == '/') {
+ //$p_header['external'] = 0x41FF0010;
+ $p_header['external'] = 0x00000010;
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Force folder external : \''.sprintf("Ox%04X", $p_header['external']).'\'');
+ }
+
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Header of filename : \''.$p_header['filename'].'\'');
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privCheckFileHeaders()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // 1 on success,
+ // 0 on error;
+ // --------------------------------------------------------------------------------
+ function privCheckFileHeaders(&$p_local_header, &$p_central_header)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privCheckFileHeaders", "");
+ $v_result=1;
+
+ // ----- Check the static values
+ // TBC
+ if ($p_local_header['filename'] != $p_central_header['filename']) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Bad check "filename" : TBC To Be Completed');
+ }
+ if ($p_local_header['version_extracted'] != $p_central_header['version_extracted']) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Bad check "version_extracted" : TBC To Be Completed');
+ }
+ if ($p_local_header['flag'] != $p_central_header['flag']) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Bad check "flag" : TBC To Be Completed');
+ }
+ if ($p_local_header['compression'] != $p_central_header['compression']) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Bad check "compression" : TBC To Be Completed');
+ }
+ if ($p_local_header['mtime'] != $p_central_header['mtime']) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Bad check "mtime" : TBC To Be Completed');
+ }
+ if ($p_local_header['filename_len'] != $p_central_header['filename_len']) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Bad check "filename_len" : TBC To Be Completed');
+ }
+
+ // ----- Look for flag bit 3
+ if (($p_local_header['flag'] & 8) == 8) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Purpose bit flag bit 3 set !');
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'File size, compression size and crc found in central header');
+ $p_local_header['size'] = $p_central_header['size'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Size : \''.$p_local_header['size'].'\'');
+ $p_local_header['compressed_size'] = $p_central_header['compressed_size'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Compressed Size : \''.$p_local_header['compressed_size'].'\'');
+ $p_local_header['crc'] = $p_central_header['crc'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'CRC : \''.sprintf("0x%X", $p_local_header['crc']).'\'');
+ }
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privReadEndCentralDir()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privReadEndCentralDir(&$p_central_dir)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privReadEndCentralDir", "");
+ $v_result=1;
+
+ // ----- Go to the end of the zip file
+ $v_size = filesize($this->zipname);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Size of the file :$v_size");
+ @fseek($this->zip_fd, $v_size);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Position at end of zip file : \''.ftell($this->zip_fd).'\'');
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ 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) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Look for central dir with no comment');
+ @fseek($this->zip_fd, $v_size-22);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Position after min central position : \''.ftell($this->zip_fd).'\'');
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+
+ // ----- Read for bytes
+ $v_binary_data = @fread($this->zip_fd, 4);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Binary data is : '".sprintf("%08x", $v_binary_data)."'");
+ $v_data = @unpack('Vid', $v_binary_data);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Binary signature is : '".sprintf("0x%08x", $v_data['id'])."'");
+
+ // ----- Check signature
+ if ($v_data['id'] == 0x06054b50) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Found central dir at the default position.");
+ $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) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Start extended search of end central dir');
+ $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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Position after max central position : \''.ftell($this->zip_fd).'\'');
+
+ // ----- 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);
+
+ // ----- Compare the bytes
+ if ($v_bytes == 0x504b0506)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Found End Central Dir signature at position : \''.ftell($this->zip_fd).'\'');
+ $v_pos++;
+ break;
+ }
+
+ $v_pos++;
+ }
+
+ // ----- Look if not found end of central dir
+ if ($v_pos == $v_size)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Unable to find End of Central Dir Record signature");
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Unable to find End of Central Dir Record signature");
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ 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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Invalid End of Central Dir Record size : ".strlen($v_binary_data));
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid End of Central Dir Record size : ".strlen($v_binary_data));
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+
+ // ----- Extract the values
+ ////--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Central Dir Record : '".$v_binary_data."'");
+ ////--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Central Dir Record (Hex) : '".bin2hex($v_binary_data)."'");
+ $v_data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', $v_binary_data);
+
+ // ----- Check the global size
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Comment length : ".$v_data['comment_size']);
+ if (($v_pos + $v_data['comment_size'] + 18) != $v_size) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "The central dir is not at the end of the archive. Some trailing bytes exists after the archive.");
+
+ // ----- 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+ }
+
+ // ----- Get comment
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Comment size : \''.$v_data['comment_size'].'\'');
+ if ($v_data['comment_size'] != 0) {
+ $p_central_dir['comment'] = fread($this->zip_fd, $v_data['comment_size']);
+ }
+ else
+ $p_central_dir['comment'] = '';
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Comment : \''.$p_central_dir['comment'].'\'');
+
+ $p_central_dir['entries'] = $v_data['entries'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Nb of entries : \''.$p_central_dir['entries'].'\'');
+ $p_central_dir['disk_entries'] = $v_data['disk_entries'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Nb of entries for this disk : \''.$p_central_dir['disk_entries'].'\'');
+ $p_central_dir['offset'] = $v_data['offset'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Offset of Central Dir : \''.$p_central_dir['offset'].'\'');
+ $p_central_dir['size'] = $v_data['size'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Size of Central Dir : \''.$p_central_dir['size'].'\'');
+ $p_central_dir['disk'] = $v_data['disk'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Disk number : \''.$p_central_dir['disk'].'\'');
+ $p_central_dir['disk_start'] = $v_data['disk_start'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Start disk number : \''.$p_central_dir['disk_start'].'\'');
+
+ // TBC
+ //for(reset($p_central_dir); $key = key($p_central_dir); next($p_central_dir)) {
+ // //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "central_dir[$key] = ".$p_central_dir[$key]);
+ //}
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privDeleteByRule()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privDeleteByRule(&$p_result_list, &$p_options)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privDeleteByRule", "");
+ $v_result=1;
+ $v_list_detail = array();
+
+ // ----- Open the zip file
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode");
+ if (($v_result=$this->privOpenFd('rb')) != 1)
+ {
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Read the central directory informations
+ $v_central_dir = array();
+ if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
+ {
+ $this->privCloseFd();
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Go to beginning of File
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in file : ".ftell($this->zip_fd)."'");
+ @rewind($this->zip_fd);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in file : ".ftell($this->zip_fd)."'");
+
+ // ----- Scan all the files
+ // ----- Start at beginning of Central Dir
+ $v_pos_entry = $v_central_dir['offset'];
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position before rewind : ".ftell($this->zip_fd)."'");
+ @rewind($this->zip_fd);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position after rewind : ".ftell($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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position after fseek : ".ftell($this->zip_fd)."'");
+
+ // ----- Read each entry
+ $v_header_list = array();
+ $j_start = 0;
+ for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Read next file header entry (index '$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();
+
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Filename (index '$i') : '".$v_header_list[$v_nb_extracted]['stored_filename']."'");
+
+ // ----- 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)) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract with rule 'ByName'");
+
+ // ----- 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])) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "The directory is in the file path");
+ $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])) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "The entry is the searched directory");
+ $v_found = true;
+ }
+ }
+ // ----- Look for a filename
+ elseif ($v_header_list[$v_nb_extracted]['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "The file is the right one.");
+ $v_found = true;
+ }
+ }
+ }
+
+ // ----- Look for extract by ereg rule
+ else if ( (isset($p_options[PCLZIP_OPT_BY_EREG]))
+ && ($p_options[PCLZIP_OPT_BY_EREG] != "")) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract by ereg '".$p_options[PCLZIP_OPT_BY_EREG]."'");
+
+ if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header_list[$v_nb_extracted]['stored_filename'])) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Filename match the regular expression");
+ $v_found = true;
+ }
+ }
+
+ // ----- Look for extract by preg rule
+ else if ( (isset($p_options[PCLZIP_OPT_BY_PREG]))
+ && ($p_options[PCLZIP_OPT_BY_PREG] != "")) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract with rule 'ByEreg'");
+
+ if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header_list[$v_nb_extracted]['stored_filename'])) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Filename match the regular expression");
+ $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)) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract with rule 'ByIndex'");
+
+ // ----- 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'])) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Found as part of an index range");
+ $v_found = true;
+ }
+ if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Do not look this index range for next loop");
+ $j_start = $j+1;
+ }
+
+ if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Index range is greater than index, stop loop");
+ break;
+ }
+ }
+ }
+ else {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "No argument mean remove all file");
+ $v_found = true;
+ }
+
+ // ----- Look for deletion
+ if ($v_found)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "File '".$v_header_list[$v_nb_extracted]['stored_filename']."', index '$i' need to be deleted");
+ unset($v_header_list[$v_nb_extracted]);
+ }
+ else
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "File '".$v_header_list[$v_nb_extracted]['stored_filename']."', index '$i' will not be deleted");
+ $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
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary write mode");
+ if (($v_result = $v_temp_zip->privOpenFd('wb')) != 1) {
+ $this->privCloseFd();
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Look which file need to be kept
+ for ($i=0; $izip_fd)."'");
+ @rewind($this->zip_fd);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position after rewind : ".ftell($this->zip_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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position after fseek : ".ftell($this->zip_fd)."'");
+
+ // ----- 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Offset for this file is '".$v_header_list[$i]['offset']."'");
+
+ // ----- 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ }
+
+ // ----- Store the offset of the central dir
+ $v_offset = @ftell($v_temp_zip->zip_fd);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "New offset of central dir : $v_offset");
+
+ // ----- Re-Create the Central Dir files header
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Creates the new central directory");
+ for ($i=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) {
+ $v_temp_zip->privCloseFd();
+ $this->privCloseFd();
+ @unlink($v_zip_temp_name);
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Transform the header to a 'usable' info
+ $v_temp_zip->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]);
+ }
+
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Creates the central directory footer");
+
+ // ----- 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ if (($v_result = $this->privWriteCentralHeader(0, 0, 0, '')) != 1) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ $this->privCloseFd();
+ }
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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;
+
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privDirCheck", "entry='$p_dir', is_dir='".($p_is_dir?"true":"false")."'");
+
+ // ----- Remove the final '/'
+ if (($p_is_dir) && (substr($p_dir, -1)=='/'))
+ {
+ $p_dir = substr($p_dir, 0, strlen($p_dir)-1);
+ }
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Looking for entry '$p_dir'");
+
+ // ----- Check the directory availability
+ if ((is_dir($p_dir)) || ($p_dir == ""))
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, "'$p_dir' is a directory");
+ return 1;
+ }
+
+ // ----- Extract parent directory
+ $p_parent_dir = dirname($p_dir);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Parent directory is '$p_parent_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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ }
+ }
+
+ // ----- Create the directory
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Create directory '$p_dir'");
+ if (!@mkdir($p_dir, 0777))
+ {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_DIR_CREATE_FAIL, "Unable to create directory '$p_dir'");
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ return PclZip::errorCode();
+ }
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result, "Directory '$p_dir' created");
+ 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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privMerge", "archive='".$p_archive_to_add->zipname."'");
+ $v_result=1;
+
+ // ----- Look if the archive_to_add exists
+ if (!is_file($p_archive_to_add->zipname))
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Archive to add does not exist. End of merge.");
+
+ // ----- Nothing to merge, so merge is a success
+ $v_result = 1;
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Look if the archive exists
+ if (!is_file($this->zipname))
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Archive does not exist, duplicate the archive_to_add.");
+
+ // ----- Do a duplicate
+ $v_result = $this->privDuplicate($p_archive_to_add->zipname);
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Open the zip file
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode");
+ if (($v_result=$this->privOpenFd('rb')) != 1)
+ {
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Read the central directory informations
+ $v_central_dir = array();
+ if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
+ {
+ $this->privCloseFd();
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Go to beginning of File
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in zip : ".ftell($this->zip_fd)."'");
+ @rewind($this->zip_fd);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in zip : ".ftell($this->zip_fd)."'");
+
+ // ----- Open the archive_to_add file
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open archive_to_add in binary read mode");
+ if (($v_result=$p_archive_to_add->privOpenFd('rb')) != 1)
+ {
+ $this->privCloseFd();
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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();
+
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Go to beginning of File
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in archive_to_add : ".ftell($p_archive_to_add->zip_fd)."'");
+ @rewind($p_archive_to_add->zip_fd);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in archive_to_add : ".ftell($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
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ 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);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes");
+ $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);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes");
+ $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);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "New offset of central dir : $v_offset");
+
+ // ----- 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);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes");
+ $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);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes");
+ $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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privDuplicate()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privDuplicate($p_archive_filename)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privDuplicate", "archive_filename='$p_archive_filename'");
+ $v_result=1;
+
+ // ----- Look if the $p_archive_filename exists
+ if (!is_file($p_archive_filename))
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Archive to duplicate does not exist. End of duplicate.");
+
+ // ----- Nothing to duplicate, so duplicate is a success.
+ $v_result = 1;
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Open the zip file
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode");
+ if (($v_result=$this->privOpenFd('wb')) != 1)
+ {
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Open the temporary file in write mode
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
+ 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);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Read $v_read_size bytes");
+ $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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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 : privDecrypt()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privDecrypt($p_encryption_header, &$p_buffer, $p_size, $p_crc)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, 'PclZip::privDecrypt', "size=".$p_size."");
+ $v_result=1;
+
+ // ----- To Be Modified ;-)
+ $v_pwd = "test";
+
+ $p_buffer = PclZipUtilZipDecrypt($p_buffer, $p_size, $p_encryption_header,
+ $p_crc, $v_pwd);
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privDisableMagicQuotes()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privDisableMagicQuotes()
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, 'PclZip::privDisableMagicQuotes', "");
+ $v_result=1;
+
+ // ----- Look if function exists
+ if ( (!function_exists("get_magic_quotes_runtime"))
+ || (!function_exists("set_magic_quotes_runtime"))) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Functions *et_magic_quotes_runtime are not supported");
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Look if already done
+ if ($this->magic_quotes_status != -1) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "magic_quote already disabled");
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Get and memorize the magic_quote value
+ $this->magic_quotes_status = @get_magic_quotes_runtime();
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Current magic_quotes_runtime status is '".($this->magic_quotes_status==0?'disable':'enable')."'");
+
+ // ----- Disable magic_quotes
+ if ($this->magic_quotes_status == 1) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Disable magic_quotes");
+ @set_magic_quotes_runtime(0);
+ }
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privSwapBackMagicQuotes()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privSwapBackMagicQuotes()
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, 'PclZip::privSwapBackMagicQuotes', "");
+ $v_result=1;
+
+ // ----- Look if function exists
+ if ( (!function_exists("get_magic_quotes_runtime"))
+ || (!function_exists("set_magic_quotes_runtime"))) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Functions *et_magic_quotes_runtime are not supported");
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Look if something to do
+ if ($this->magic_quotes_status != -1) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "magic_quote not modified");
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+
+ // ----- Swap back magic_quotes
+ if ($this->magic_quotes_status == 1) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Enable back magic_quotes");
+ @set_magic_quotes_runtime($this->magic_quotes_status);
+ }
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ }
+ // End of class
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : PclZipUtilPathReduction()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function PclZipUtilPathReduction($p_dir)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZipUtilPathReduction", "dir='$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;
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Invalid path is unchanged");
+ $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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZipUtilPathInclusion", "dir='$p_dir', path='$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);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Replacing ./ by full path in p_dir '".$p_dir."'");
+ }
+ if ( ($p_path == '.')
+ || ((strlen($p_path) >=2) && (substr($p_path, 0, 2) == './'))) {
+ $p_path = PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_path, 1);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Replacing ./ by full path in p_path '".$p_path."'");
+ }
+
+ // ----- 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)) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Working on dir($i)='".$v_list_dir[$i]."' and path($j)='".$v_list_path[$j]."'");
+
+ // ----- 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] != '')) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Items ($i,$j) are different");
+ $v_result = 0;
+ }
+
+ // ----- Next items
+ $i++;
+ $j++;
+ }
+
+ // ----- Look if everything seems to be the same
+ if ($v_result) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Look for tie break");
+ // ----- 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++;
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Looking on dir($i)='".($i < $v_list_dir_size?$v_list_dir[$i]:'')."' and path($j)='".($j < $v_list_path_size?$v_list_path[$j]:'')."'");
+
+ 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
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZipUtilCopyBlock", "size=$p_size, mode=$p_mode");
+ $v_result = 1;
+
+ if ($p_mode==0)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Src offset before read :".(@ftell($p_src)));
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Dest offset before write :".(@ftell($p_dest)));
+ while ($p_size != 0)
+ {
+ $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes");
+ $v_buffer = @fread($p_src, $v_read_size);
+ @fwrite($p_dest, $v_buffer, $v_read_size);
+ $p_size -= $v_read_size;
+ }
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Src offset after read :".(@ftell($p_src)));
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Dest offset after write :".(@ftell($p_dest)));
+ }
+ else if ($p_mode==1)
+ {
+ while ($p_size != 0)
+ {
+ $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes");
+ $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);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes");
+ $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);
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes");
+ $v_buffer = @gzread($p_src, $v_read_size);
+ @gzwrite($p_dest, $v_buffer, $v_read_size);
+ $p_size -= $v_read_size;
+ }
+ }
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZipUtilRename", "source=$p_src, destination=$p_dest");
+ $v_result = 1;
+
+ // ----- Try to rename the files
+ if (!@rename($p_src, $p_dest)) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Fail to rename file, try copy+unlink");
+
+ // ----- Try to copy & unlink the src
+ if (!@copy($p_src, $p_dest)) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Fail to copy file");
+ $v_result = 0;
+ }
+ else if (!@unlink($p_src)) {
+ //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Fail to unlink old filename");
+ $v_result = 0;
+ }
+ }
+
+ // ----- Return
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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)
+ {
+ //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZipUtilOptionText", "option='".$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)) {
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_key);
+ return $v_key;
+ }
+ }
+
+ $v_result = 'Unknown';
+
+ //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
+ 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/BSF/admin/include/plugins.class.php b/BSF/admin/include/plugins.class.php
new file mode 100644
index 000000000..de189ffc9
--- /dev/null
+++ b/BSF/admin/include/plugins.class.php
@@ -0,0 +1,484 @@
+get_fs_plugins();
+
+ foreach (get_db_plugins() as $db_plugin)
+ {
+ $this->db_plugins_by_id[$db_plugin['id']] = $db_plugin;
+ }
+ }
+
+ /**
+ * 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];
+ }
+ $file_to_include = PHPWG_PLUGINS_PATH . $plugin_id . '/maintain.inc.php';
+
+ $errors = array();
+
+ switch ($action)
+ {
+ case 'install':
+ if (!empty($crt_db_plugin))
+ {
+ array_push($errors, 'CANNOT INSTALL - ALREADY INSTALLED');
+ break;
+ }
+ if (!isset($this->fs_plugins[$plugin_id]))
+ {
+ array_push($errors, 'CANNOT INSTALL - NO SUCH PLUGIN');
+ break;
+ }
+ if (file_exists($file_to_include))
+ {
+ include_once($file_to_include);
+ if (function_exists('plugin_install'))
+ {
+ plugin_install($plugin_id, $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))
+ {
+ array_push($errors, 'CANNOT ACTIVATE - NOT INSTALLED');
+ break;
+ }
+ if ($crt_db_plugin['state'] != 'inactive')
+ {
+ array_push($errors, 'invalid current state ' . $crt_db_plugin['state']);
+ break;
+ }
+ if (file_exists($file_to_include))
+ {
+ include_once($file_to_include);
+ if (function_exists('plugin_activate'))
+ {
+ plugin_activate($plugin_id, $crt_db_plugin['version'], $errors);
+ }
+ }
+ if (empty($errors))
+ {
+ $query = '
+UPDATE ' . PLUGINS_TABLE . ' SET state="active" WHERE id="' . $plugin_id . '"';
+ pwg_query($query);
+ }
+ break;
+
+ case 'deactivate':
+ if (!isset($crt_db_plugin))
+ {
+ die ('CANNOT DEACTIVATE - NOT INSTALLED');
+ }
+ if ($crt_db_plugin['state'] != 'active')
+ {
+ die('invalid current state ' . $crt_db_plugin['state']);
+ }
+ $query = '
+UPDATE ' . PLUGINS_TABLE . ' SET state="inactive" WHERE id="' . $plugin_id . '"';
+ pwg_query($query);
+ if (file_exists($file_to_include))
+ {
+ include_once($file_to_include);
+ if (function_exists('plugin_deactivate'))
+ {
+ plugin_deactivate($plugin_id);
+ }
+ }
+ break;
+
+ case 'uninstall':
+ if (!isset($crt_db_plugin))
+ {
+ die ('CANNOT UNINSTALL - NOT INSTALLED');
+ }
+ $query = '
+DELETE FROM ' . PLUGINS_TABLE . ' WHERE id="' . $plugin_id . '"';
+ pwg_query($query);
+ if (file_exists($file_to_include))
+ {
+ include_once($file_to_include);
+ if (function_exists('plugin_uninstall'))
+ {
+ plugin_uninstall($plugin_id);
+ }
+ }
+ break;
+
+ case 'delete':
+ if (!empty($crt_db_plugin))
+ {
+ array_push($errors, 'CANNOT DELETE - PLUGIN IS INSTALLED');
+ break;
+ }
+ if (!isset($this->fs_plugins[$plugin_id]))
+ {
+ array_push($errors, 'CANNOT DELETE - NO SUCH PLUGIN');
+ break;
+ }
+ if (!$this->deltree(PHPWG_PLUGINS_PATH . $plugin_id))
+ {
+ $this->send_to_trash(PHPWG_PLUGINS_PATH . $plugin_id);
+ }
+ 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 ( 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 server datas to $server_plugins
+ */
+ function get_server_plugins($new=false)
+ {
+ foreach($this->fs_plugins as $fs_plugin)
+ {
+ if (isset($fs_plugin['extension']))
+ {
+ $plugins_to_check[] = $fs_plugin['extension'];
+ }
+ }
+ $url = PEM_URL . '/uptodate.php?version=' . rawurlencode(PHPWG_VERSION) . '&extensions=' . implode(',', $plugins_to_check);
+ $url .= $new ? '&newext=Plugin' : '';
+
+ if (!empty($plugins_to_check) and $source = @file_get_contents($url))
+ {
+ $this->server_plugins = @unserialize($source);
+ }
+ }
+
+ /**
+ * 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;
+ }
+ }
+
+ /**
+ * Extract plugin files from archive
+ * @param string - install or upgrade
+ * @param string - archive URL
+ * @param string - destination path
+ */
+ function extract_plugin_files($action, $source, $dest)
+ {
+ if ($archive = tempnam( PHPWG_PLUGINS_PATH, 'zip'))
+ {
+ if (@copy(PEM_URL . str_replace(' ', '%20', $source), $archive))
+ {
+ include(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;
+ }
+ }
+ }
+ 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;
+ }
+
+ /**
+ * delete $path directory
+ * @param string - path
+ */
+ function deltree($path)
+ {
+ if (is_dir($path))
+ {
+ $fh = opendir($path);
+ while ($file = readdir($fh))
+ {
+ if ($file != '.' and $file != '..')
+ {
+ $pathfile = $path . '/' . $file;
+ if (is_dir($pathfile))
+ {
+ $this->deltree($pathfile);
+ }
+ else
+ {
+ @unlink($pathfile);
+ }
+ }
+ }
+ closedir($fh);
+ return @rmdir($path);
+ }
+ }
+
+ /**
+ * send $path to trash directory
+ * @param string - path
+ */
+ function send_to_trash($path)
+ {
+ $trash_path = PHPWG_PLUGINS_PATH . 'trash';
+ if (!is_dir($trash_path))
+ {
+ @mkdir($trash_path);
+ $file = @fopen($trash_path . '/.htaccess', 'w');
+ @fwrite($file, 'deny from all');
+ @fclose($file);
+ }
+ while ($r = $trash_path . '/' . md5(uniqid(rand(), true)))
+ {
+ if (!is_dir($r))
+ {
+ @rename($path, $r);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Sort functions
+ */
+ function plugin_version_compare($a, $b)
+ {
+ $pattern = array('/([a-z])/ei', '/\.+/', '/\.\Z|\A\./');
+ $replacement = array( "'.'.intval('\\1', 36).'.'", '.', '');
+
+ $array = preg_replace($pattern, $replacement, array($a['version'], $b['version']));
+
+ return version_compare($array[0], $array[1], '>=');
+ }
+
+ function extension_revision_compare($a, $b)
+ {
+ if ($a['date'] < $b['date']) return 1;
+ else return -1;
+ }
+
+ function extension_name_compare($a, $b)
+ {
+ return strcmp(strtolower($a['ext_name']), strtolower($b['ext_name']));
+ }
+
+ function extension_author_compare($a, $b)
+ {
+ $r = strcasecmp($a['author'], $b['author']);
+ 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 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/BSF/admin/include/tabsheet.class.php b/BSF/admin/include/tabsheet.class.php
new file mode 100644
index 000000000..c295f0ae0
--- /dev/null
+++ b/BSF/admin/include/tabsheet.class.php
@@ -0,0 +1,146 @@
+sheets = array();
+ $this->name = $name;
+ $this->titlename = $titlename;
+ $this->selected = "";
+ }
+
+ /*
+ 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->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', 'admin/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/BSF/admin/index.php b/BSF/admin/index.php
new file mode 100644
index 000000000..c15b15795
--- /dev/null
+++ b/BSF/admin/index.php
@@ -0,0 +1,30 @@
+
diff --git a/BSF/admin/intro.php b/BSF/admin/intro.php
new file mode 100644
index 000000000..ed378d2d5
--- /dev/null
+++ b/BSF/admin/intro.php
@@ -0,0 +1,285 @@
+ PHPWG_VERSION);
+ $lines = @file(PHPWG_URL.'/latest_version');
+
+ // 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'})
+ {
+ array_push(
+ $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'})
+ {
+ array_push(
+ $page['infos'],
+ l10n('You are running on development sources, no check possible.')
+ );
+ }
+ else if (version_compare($versions{'current'}, $versions{'latest'}) < 0)
+ {
+ array_push(
+ $page['infos'],
+ l10n('A new version of Piwigo is available.')
+ );
+ }
+ else
+ {
+ array_push(
+ $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' => 'admin/intro.tpl'));
+
+$php_current_timestamp = date("Y-m-d H:i:s");
+list($mysql_version, $db_current_timestamp) = mysql_fetch_row(pwg_query('SELECT VERSION(), CURRENT_TIMESTAMP;'));
+
+$query = '
+SELECT COUNT(*)
+ FROM '.IMAGES_TABLE.'
+;';
+list($nb_elements) = mysql_fetch_row(pwg_query($query));
+
+$query = '
+SELECT COUNT(*)
+ FROM '.CATEGORIES_TABLE.'
+;';
+list($nb_categories) = mysql_fetch_row(pwg_query($query));
+
+$query = '
+SELECT COUNT(*)
+ FROM '.CATEGORIES_TABLE.'
+ WHERE dir IS NULL
+;';
+list($nb_virtual) = mysql_fetch_row(pwg_query($query));
+
+$query = '
+SELECT COUNT(*)
+ FROM '.CATEGORIES_TABLE.'
+ WHERE dir IS NOT NULL
+;';
+list($nb_physical) = mysql_fetch_row(pwg_query($query));
+
+$query = '
+SELECT COUNT(*)
+ FROM '.IMAGE_CATEGORY_TABLE.'
+;';
+list($nb_image_category) = mysql_fetch_row(pwg_query($query));
+
+$query = '
+SELECT COUNT(*)
+ FROM '.TAGS_TABLE.'
+;';
+list($nb_tags) = mysql_fetch_row(pwg_query($query));
+
+$query = '
+SELECT COUNT(*)
+ FROM '.IMAGE_TAG_TABLE.'
+;';
+list($nb_image_tag) = mysql_fetch_row(pwg_query($query));
+
+$query = '
+SELECT COUNT(*)
+ FROM '.USERS_TABLE.'
+;';
+list($nb_users) = mysql_fetch_row(pwg_query($query));
+
+$query = '
+SELECT COUNT(*)
+ FROM '.GROUPS_TABLE.'
+;';
+list($nb_groups) = mysql_fetch_row(pwg_query($query));
+
+$query = '
+SELECT COUNT(*)
+ FROM '.COMMENTS_TABLE.'
+;';
+list($nb_comments) = mysql_fetch_row(pwg_query($query));
+
+$template->assign(
+ array(
+ 'PWG_VERSION' => PHPWG_VERSION,
+ 'OS' => PHP_OS,
+ 'PHP_VERSION' => phpversion(),
+ 'MYSQL_VERSION' => $mysql_version,
+ 'DB_ELEMENTS' => l10n_dec('%d element', '%d elements', $nb_elements),
+ 'DB_CATEGORIES' =>
+ l10n_dec('cat_inclu_part1_S', 'cat_inclu_part1_P',
+ $nb_categories).
+ l10n_dec('cat_inclu_part2_S', 'cat_inclu_part2_P',
+ $nb_physical).
+ l10n_dec('cat_inclu_part3_S', 'cat_inclu_part3_P',
+ $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_COMMENTS' => l10n_dec('%d comment', '%d comments', $nb_comments),
+ '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_timestamp,
+ )
+ );
+
+if ($nb_elements > 0)
+{
+ $query = '
+SELECT MIN(date_available)
+ FROM '.IMAGES_TABLE.'
+;';
+ list($first_date) = mysql_fetch_row(pwg_query($query));
+
+ $template->assign(
+ 'first_added',
+ array(
+ 'DB_DATE' =>
+ sprintf(
+ l10n('first element added on %s'),
+ format_date($first_date, 'mysql_datetime')
+ )
+ )
+ );
+}
+
+// waiting elements
+$query = '
+SELECT COUNT(*)
+ FROM '.WAITING_TABLE.'
+ WHERE validated=\'false\'
+;';
+list($nb_waiting) = mysql_fetch_row(pwg_query($query));
+
+if ($nb_waiting > 0)
+{
+ $template->assign(
+ 'waiting',
+ array(
+ 'URL' => PHPWG_ROOT_PATH.'admin.php?page=upload',
+ 'INFO' => sprintf(l10n('%d waiting for validation'), $nb_waiting)
+ )
+ );
+}
+
+// unvalidated comments
+$query = '
+SELECT COUNT(*)
+ FROM '.COMMENTS_TABLE.'
+ WHERE validated=\'false\'
+;';
+list($nb_comments) = mysql_fetch_row(pwg_query($query));
+
+if ($nb_comments > 0)
+{
+ $template->assign(
+ 'unvalidated',
+ array(
+ 'URL' => PHPWG_ROOT_PATH.'admin.php?page=comments',
+ 'INFO' => sprintf(l10n('%d waiting for validation'), $nb_comments)
+ )
+ );
+}
+
+// +-----------------------------------------------------------------------+
+// | 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/BSF/admin/maintenance.php b/BSF/admin/maintenance.php
new file mode 100644
index 000000000..69268cf2c
--- /dev/null
+++ b/BSF/admin/maintenance.php
@@ -0,0 +1,142 @@
+maintenance();
+ break;
+ }
+ case 'compiled-templates' :
+ {
+ $template->delete_compiled_templates();
+ break;
+ }
+ default :
+ {
+ break;
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+
+$template->set_filenames(array('maintenance'=>'admin/maintenance.tpl'));
+
+$start_url = get_root_url().'admin.php?page=maintenance&action=';
+
+$template->assign(
+ array(
+ 'U_MAINT_CATEGORIES' => $start_url.'categories',
+ 'U_MAINT_IMAGES' => $start_url.'images',
+ 'U_MAINT_HISTORY_DETAIL' => $start_url.'history_detail',
+ 'U_MAINT_HISTORY_SUMMARY' => $start_url.'history_summary',
+ 'U_MAINT_SESSIONS' => $start_url.'sessions',
+ 'U_MAINT_FEEDS' => $start_url.'feeds',
+ 'U_MAINT_DATABASE' => $start_url.'database',
+ 'U_MAINT_C13Y' => $start_url.'c13y',
+ 'U_MAINT_COMPILED_TEMPLATES' => $start_url.'compiled-templates',
+ 'U_HELP' => PHPWG_ROOT_PATH.'popuphelp.php?page=maintenance',
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'maintenance');
+?>
\ No newline at end of file
diff --git a/BSF/admin/notification_by_mail.php b/BSF/admin/notification_by_mail.php
new file mode 100644
index 000000000..fc0a2d637
--- /dev/null
+++ b/BSF/admin/notification_by_mail.php
@@ -0,0 +1,738 @@
+ 0)
+ {
+ $inserts = array();
+ $check_key_list = array();
+
+ while ($nbm_user = mysql_fetch_array($result))
+ {
+ // Calculate key
+ $nbm_user['check_key'] = find_available_check_key();
+
+ // Save key
+ array_push($check_key_list, $nbm_user['check_key']);
+
+ // Insert new nbm_users
+ array_push
+ (
+ $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
+ )
+ );
+
+ array_push
+ (
+ $page['infos'],
+ sprintf(
+ l10n('nbm_user_x_added'),
+ $nbm_user['username'],
+ get_email_address_as_display_text($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('nbm_redirect_msg'));
+ }
+ }
+ }
+}
+
+/*
+ * 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) = mysql_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('nbm_break_timeout_send_mail');
+ }
+ else
+ {
+ $msg_break_timeout = l10n('nbm_break_timeout_list_user');
+ }
+
+ // 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
+ array_push($page['infos'], $msg_break_timeout);
+ break;
+ }
+ if (($is_action_send) and check_sendmail_timeout())
+ {
+ // Stop fill list on 'send', if the quota is override
+ array_push($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'
+ array_push($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('nbm_object_news');
+
+ // 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' => $conf['gallery_url'],
+ 'SEND_AS_NAME' => $env_nbm['send_as_name'],
+ )
+ );
+
+ if (pwg_mail
+ (
+ format_email($nbm_user['username'], $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'],
+ 'template' => $nbm_user['template'],
+ 'theme' => $nbm_user['theme']
+ )
+ ))
+ {
+ inc_mail_sent_success($nbm_user);
+
+ $data = array('user_id' => $nbm_user['user_id'],
+ 'last_send' => $dbnow);
+ array_push($datas, $data);
+ }
+ 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'
+ array_push($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)
+ {
+ array_push($page['errors'], l10n('nbm_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']) and !is_adviser())
+ {
+ $updated_param_count = 0;
+ // Update param
+ $result = pwg_query('select param, value from '.CONFIG_TABLE.' where param like \'nbm\\_%\'');
+ while ($nbm_user = mysql_fetch_array($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;
+ }
+ }
+
+ array_push($page['infos'],
+ l10n_dec('nbm_updated_param_count', 'nbm_updated_params_count',
+ $updated_param_count));
+
+ // Reload conf with new values
+ load_conf_from_db('param like \'nbm\\_%\'');
+ }
+ }
+ case 'subscribe' :
+ {
+ if (!is_adviser())
+ {
+ 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']) and !is_adviser())
+ {
+ $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' => 'admin/double_select.tpl',
+ 'notification_by_mail'=>'admin/notification_by_mail.tpl'
+ )
+);
+
+$template->assign
+(
+ array
+ (
+ 'U_HELP' => add_url_params(get_root_url().'popuphelp.php', array('page' => 'notification_by_mail')),
+ 'F_ACTION'=> $base_url.get_query_string_diff(array())
+ )
+);
+
+if (is_autorize_status(ACCESS_WEBMASTER))
+{
+ // TabSheet
+ $tabsheet = new tabsheet();
+ // TabSheet initialization
+ $tabsheet->add('param', l10n('nbm_param_mode'),
+ add_url_params($base_url.get_query_string_diff(array('mode', 'select')),
+ array('mode' => 'param')));
+ $tabsheet->add('subscribe', l10n('nbm_subscribe_mode'),
+ add_url_params($base_url.get_query_string_diff(array('mode', 'select')),
+ array('mode' => 'subscribe')));
+ $tabsheet->add('send', l10n('nbm_send_mode'),
+ add_url_params($base_url.get_query_string_diff(array('mode', 'select')),
+ array('mode' => 'send')));
+ // TabSheet selection
+ $tabsheet->select($page['mode']);
+ // Assign tabsheet to template
+ $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('nbm_subscribe_col'),
+ 'L_CAT_OPTIONS_FALSE' => l10n('nbm_unsubscribe_col')
+ )
+ );
+
+ $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'] ] = $nbm_user['username'].'['.get_email_address_as_display_text($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'] ] = $nbm_user['username'].'['.get_email_address_as_display_text($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 $nbm_user['username'],
+ 'EMAIL' => get_email_address_as_display_text($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');
+
+?>
\ No newline at end of file
diff --git a/BSF/admin/permalinks.php b/BSF/admin/permalinks.php
new file mode 100644
index 000000000..ecae18965
--- /dev/null
+++ b/BSF/admin/permalinks.php
@@ -0,0 +1,179 @@
+ $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]) )
+ {
+ array_push($ret, $field);
+ $disp = ''.$disp.'';
+ }
+ }
+ else
+ {
+ array_push($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 and !is_adviser() )
+{
+ $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']) and !is_adviser() )
+{
+ $query = '
+DELETE FROM '.OLD_PERMALINKS_TABLE.'
+ WHERE permalink="'.$_GET['delete_permanent'].'"
+ LIMIT 1';
+ pwg_query($query);
+ if (mysql_affected_rows()==0)
+ array_push($page['errors'], 'Cannot delete the old permalink !');
+}
+
+
+$template->set_filename('permalinks', 'admin/permalinks.tpl' );
+
+$query = '
+SELECT
+ id,
+ 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=mysql_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=mysql_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().'popuphelp.php?page=permalinks');
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'permalinks');
+?>
diff --git a/BSF/admin/picture_modify.php b/BSF/admin/picture_modify.php
new file mode 100644
index 000000000..2fbaf901b
--- /dev/null
+++ b/BSF/admin/picture_modify.php
@@ -0,0 +1,437 @@
+ $path));
+
+ array_push($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 (!checkdate(
+ $_POST['date_creation_month'],
+ $_POST['date_creation_day'],
+ $_POST['date_creation_year'])
+ )
+ {
+ array_push($page['errors'], l10n('err_date'));
+ }
+}
+
+if (isset($_POST['submit']) and count($page['errors']) == 0 and !is_adviser())
+{
+ $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 (isset($_POST['date_creation_action']))
+ {
+ if ('set' == $_POST['date_creation_action'])
+ {
+ $data{'date_creation'} = $_POST['date_creation_year']
+ .'-'.$_POST['date_creation_month']
+ .'-'.$_POST['date_creation_day'];
+ }
+ else if ('unset' == $_POST['date_creation_action'])
+ {
+ $data{'date_creation'} = '';
+ }
+ }
+
+ mass_updates(
+ IMAGES_TABLE,
+ array(
+ 'primary' => array('id'),
+ 'update' => array_diff(array_keys($data), array('id'))
+ ),
+ array($data)
+ );
+
+ set_tags(
+ isset($_POST['tags']) ? $_POST['tags'] : array(),
+ $_GET['image_id']
+ );
+
+ array_push($page['infos'], l10n('Picture informations updated'));
+}
+// associate the element to other categories than its storage category
+if (isset($_POST['associate'])
+ and isset($_POST['cat_dissociated'])
+ and count($_POST['cat_dissociated']) > 0
+ and !is_adviser()
+ )
+{
+ associate_images_to_categories(
+ array($_GET['image_id']),
+ $_POST['cat_dissociated']
+ );
+}
+// dissociate the element from categories (but not from its storage category)
+if (isset($_POST['dissociate'])
+ and isset($_POST['cat_associated'])
+ and count($_POST['cat_associated']) > 0
+ and !is_adviser()
+ )
+{
+ $query = '
+DELETE FROM '.IMAGE_CATEGORY_TABLE.'
+ WHERE image_id = '.$_GET['image_id'].'
+ AND category_id IN ('.implode(',', $_POST['cat_associated']).')
+';
+ pwg_query($query);
+
+ update_category($_POST['cat_associated']);
+}
+// elect the element to represent the given categories
+if (isset($_POST['elect'])
+ and isset($_POST['cat_dismissed'])
+ and count($_POST['cat_dismissed']) > 0
+ and !is_adviser()
+ )
+{
+ $datas = array();
+ foreach ($_POST['cat_dismissed'] as $category_id)
+ {
+ array_push($datas,
+ array('id' => $category_id,
+ 'representative_picture_id' => $_GET['image_id']));
+ }
+ $fields = array('primary' => array('id'),
+ 'update' => array('representative_picture_id'));
+ mass_updates(CATEGORIES_TABLE, $fields, $datas);
+}
+// dismiss the element as representant of the given categories
+if (isset($_POST['dismiss'])
+ and isset($_POST['cat_elected'])
+ and count($_POST['cat_elected']) > 0
+ and !is_adviser()
+ )
+{
+ set_random_representant($_POST['cat_elected']);
+}
+
+// retrieving direct information about picture
+$query = '
+SELECT *
+ FROM '.IMAGES_TABLE.'
+ WHERE id = '.$_GET['image_id'].'
+;';
+$row = mysql_fetch_array(pwg_query($query));
+
+$storage_category_id = $row['storage_category_id'];
+$image_file = $row['file'];
+
+// tags
+$query = '
+SELECT tag_id
+ FROM '.IMAGE_TAG_TABLE.'
+ WHERE image_id = '.$_GET['image_id'].'
+;';
+$selected_tags = array_from_query($query, 'tag_id');
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+
+$template->set_filenames(
+ array(
+ 'picture_modify' => 'admin/picture_modify.tpl'
+ )
+ );
+
+$all_tags = get_all_tags();
+
+if (count($all_tags) > 0)
+{
+ $tag_selection = get_html_tag_selection(
+ $all_tags,
+ 'tags',
+ $selected_tags
+ );
+}
+else
+{
+ $tag_selection =
+ '
'.
+ l10n('No tag defined. Use Administration>Pictures>Tags').
+ '
';
+}
+
+$template->assign(
+ array(
+ 'U_SYNC' =>
+ get_root_url().'admin.php?page=picture_modify'.
+ '&image_id='.$_GET['image_id'].
+ (isset($_GET['cat_id']) ? '&cat_id='.$_GET['cat_id'] : '').
+ '&sync_metadata=1',
+
+ 'PATH'=>$row['path'],
+
+ 'TN_SRC' => get_thumbnail_url($row),
+
+ 'NAME' =>
+ isset($_POST['name']) ?
+ stripslashes($_POST['name']) : @$row['name'],
+
+ 'DIMENSIONS' => @$row['width'].' * '.@$row['height'],
+
+ 'FILESIZE' => @$row['filesize'].' KB',
+
+ 'REGISTRATION_DATE' =>
+ format_date($row['date_available'], 'mysql_datetime', false),
+
+ 'AUTHOR' => isset($_POST['author']) ? $_POST['author'] : @$row['author'],
+
+ 'TAG_SELECTION' => $tag_selection,
+
+ 'DESCRIPTION' =>
+ htmlspecialchars( isset($_POST['description']) ?
+ stripslashes($_POST['description']) : @$row['comment'] ),
+
+ 'F_ACTION' =>
+ get_root_url().'admin.php'
+ .get_query_string_diff(array('sync_metadata'))
+ )
+ );
+
+if ($row['has_high'] == 'true')
+{
+ $template->assign(
+ 'HIGH_FILESIZE',
+ isset($row['high_filesize'])
+ ? $row['high_filesize'].' KB'
+ : l10n('unknown')
+ );
+}
+
+// image level options
+$tpl_options = array();
+foreach ($conf['available_permission_levels'] as $level)
+{
+ $tpl_options[$level] = l10n( sprintf('Level %d', $level) ).' ('.$level.')';
+}
+$selected_level = isset($_POST['level']) ? $_POST['level'] : $row['level'];
+$template->assign(
+ array(
+ 'level_options'=> $tpl_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') as $varname)
+ {
+ $$varname = $_POST['date_creation_'.$varname];
+ }
+}
+else if (isset($row['date_creation']) and !empty($row['date_creation']))
+{
+ list($year, $month, $day) = explode('-', $row['date_creation']);
+}
+else
+{
+ list($year, $month, $day) = array('', 0, 0);
+}
+
+
+$month_list = $lang['month'];
+$month_list[0]='------------';
+ksort($month_list);
+
+$template->assign(
+ array(
+ 'DATE_CREATION_DAY_VALUE' => $day,
+ 'DATE_CREATION_MONTH_VALUE' => $month,
+ 'DATE_CREATION_YEAR_VALUE' => $year,
+ '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 = mysql_fetch_array($result))
+{
+ $name =
+ get_cat_display_name_cache(
+ $row['uppercats'],
+ get_root_url().'admin.php?page=cat_modify&cat_id=',
+ false
+ );
+
+ 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 another category ?
+$query = '
+SELECT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.'
+ INNER JOIN '.IMAGE_CATEGORY_TABLE.' ON id = category_id
+ WHERE image_id = '.$_GET['image_id'].'
+ AND id != '.$storage_category_id.'
+;';
+display_select_cat_wrapper($query, array(), 'associated_options');
+
+$result = pwg_query($query);
+$associateds = array($storage_category_id);
+while ($row = mysql_fetch_array($result))
+{
+ array_push($associateds, $row['id']);
+}
+$query = '
+SELECT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id NOT IN ('.implode(',', $associateds).')
+;';
+display_select_cat_wrapper($query, array(), 'dissociated_options');
+
+// representing
+$query = '
+SELECT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.'
+ WHERE representative_picture_id = '.$_GET['image_id'].'
+;';
+display_select_cat_wrapper($query, array(), 'elected_options');
+
+$query = '
+SELECT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.'
+ WHERE representative_picture_id != '.$_GET['image_id'].'
+ OR representative_picture_id IS NULL
+;';
+display_select_cat_wrapper($query, array(), 'dismissed_options');
+
+//----------------------------------------------------------- sending html code
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'picture_modify');
+?>
diff --git a/BSF/admin/plugin.php b/BSF/admin/plugin.php
new file mode 100644
index 000000000..3b4055230
--- /dev/null
+++ b/BSF/admin/plugin.php
@@ -0,0 +1,62 @@
+
\ No newline at end of file
diff --git a/BSF/admin/plugins_list.php b/BSF/admin/plugins_list.php
new file mode 100644
index 000000000..ca8033164
--- /dev/null
+++ b/BSF/admin/plugins_list.php
@@ -0,0 +1,154 @@
+set_filenames(array('plugins' => 'admin/plugins_list.tpl'));
+
+$order = isset($_GET['order']) ? $_GET['order'] : 'name';
+$base_url = get_root_url().'admin.php?page='.$page['page'].'&order='.$order;
+
+$plugins = new plugins();
+
+//--------------------------------------------------perform requested actions
+if (isset($_GET['action']) and isset($_GET['plugin']) and !is_adviser())
+{
+ $page['errors'] =
+ $plugins->perform_action($_GET['action'], $_GET['plugin']);
+
+ if (empty($page['errors'])) redirect($base_url);
+}
+
+//--------------------------------------------------------------------Tabsheet
+set_plugins_tabsheet($page['page']);
+
+//---------------------------------------------------------------Order options
+$link = get_root_url().'admin.php?page='.$page['page'].'&order=';
+$template->assign('order_options',
+ array(
+ $link.'name' => l10n('Name'),
+ $link.'status' => l10n('Status'),
+ $link.'author' => l10n('Author'),
+ $link.'id' => 'Id'));
+$template->assign('order_selected', $link.$order);
+
+// +-----------------------------------------------------------------------+
+// | start template output |
+// +-----------------------------------------------------------------------+
+$plugins->sort_fs_plugins($order);
+
+foreach($plugins->fs_plugins as $plugin_id => $fs_plugin)
+{
+ $display_name = $fs_plugin['name'];
+ if (!empty($fs_plugin['uri']))
+ {
+ $display_name = ''
+ . $display_name . '';
+ }
+ $desc = $fs_plugin['description'];
+ if (!empty($fs_plugin['author']))
+ {
+ $desc .= ' (';
+ if (!empty($fs_plugin['author uri']))
+ {
+ $desc .= ''
+ . $fs_plugin['author'] . '';
+ }
+ else
+ {
+ $desc .= $fs_plugin['author'];
+ }
+ $desc .= ')';
+ }
+ $tpl_plugin =
+ array('NAME' => $display_name,
+ 'VERSION' => $fs_plugin['version'],
+ 'DESCRIPTION' => $desc);
+
+ $action_url = $base_url.'&plugin='.$plugin_id;
+
+ if (isset($plugins->db_plugins_by_id[$plugin_id]))
+ {
+ $tpl_plugin['STATE'] = $plugins->db_plugins_by_id[$plugin_id]['state'];
+ switch ($plugins->db_plugins_by_id[$plugin_id]['state'])
+ {
+ case 'active':
+ $tpl_plugin['actions'][] =
+ array('U_ACTION' => $action_url . '&action=deactivate',
+ 'L_ACTION' => l10n('Deactivate'));
+ break;
+
+ case 'inactive':
+ $tpl_plugin['actions'][] =
+ array('U_ACTION' => $action_url . '&action=activate',
+ 'L_ACTION' => l10n('Activate'));
+ $tpl_plugin['actions'][] =
+ array('U_ACTION' => $action_url . '&action=uninstall',
+ 'L_ACTION' => l10n('Uninstall'),
+ 'CONFIRM' => l10n('Are you sure?'));
+ break;
+ }
+ }
+ else
+ {
+ $tpl_plugin['actions'][] =
+ array('U_ACTION' => $action_url . '&action=install',
+ 'L_ACTION' => l10n('Install'),
+ 'CONFIRM' => l10n('Are you sure?'));
+ $tpl_plugin['actions'][] =
+ array('U_ACTION' => $action_url . '&action=delete',
+ 'L_ACTION' => l10n('plugins_delete'),
+ 'CONFIRM' => l10n('plugins_confirm_delete'));
+ }
+ $template->append('plugins', $tpl_plugin);
+}
+
+$missing_plugin_ids = array_diff(
+ array_keys($plugins->db_plugins_by_id), array_keys($plugins->fs_plugins)
+ );
+
+foreach($missing_plugin_ids as $plugin_id)
+{
+ $action_url = $base_url.'&plugin='.$plugin_id;
+
+ $template->append( 'plugins',
+ array(
+ 'NAME' => $plugin_id,
+ 'VERSION' => $plugins->db_plugins_by_id[$plugin_id]['version'],
+ 'DESCRIPTION' => "ERROR: THIS PLUGIN IS MISSING BUT IT IS INSTALLED! UNINSTALL IT NOW !",
+ 'actions' => array ( array (
+ 'U_ACTION' => $action_url . '&action=uninstall',
+ 'L_ACTION' => l10n('Uninstall'),
+ ) )
+ )
+ );
+}
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'plugins');
+?>
\ No newline at end of file
diff --git a/BSF/admin/plugins_new.php b/BSF/admin/plugins_new.php
new file mode 100644
index 000000000..31171b443
--- /dev/null
+++ b/BSF/admin/plugins_new.php
@@ -0,0 +1,139 @@
+set_filenames(array('plugins' => 'admin/plugins_new.tpl'));
+
+$order = isset($_GET['order']) ? $_GET['order'] : 'date';
+$base_url = get_root_url().'admin.php?page='.$page['page'].'&order='.$order;
+
+$plugins = new plugins();
+
+//------------------------------------------------------automatic installation
+if (isset($_GET['install']) and isset($_GET['extension']) and !is_adviser())
+{
+ $install_status =
+ $plugins->extract_plugin_files('install', $_GET['install'], $_GET['extension']);
+
+ redirect($base_url.'&installstatus='.$install_status);
+}
+
+//--------------------------------------------------------------install result
+if (isset($_GET['installstatus']))
+{
+ switch ($_GET['installstatus'])
+ {
+ case 'ok':
+ array_push($page['infos'],
+ l10n('plugins_install_ok'),
+ l10n('plugins_install_need_activate'));
+ break;
+
+ case 'temp_path_error':
+ array_push($page['errors'], l10n('plugins_temp_path_error'));
+ break;
+
+ case 'dl_archive_error':
+ array_push($page['errors'], l10n('plugins_dl_archive_error'));
+ break;
+
+ case 'archive_error':
+ array_push($page['errors'], l10n('plugins_archive_error'));
+ break;
+
+ default:
+ array_push($page['errors'],
+ sprintf(l10n('plugins_extract_error'), $_GET['installstatus']),
+ l10n('plugins_check_chmod'));
+ }
+}
+
+//--------------------------------------------------------------------Tabsheet
+set_plugins_tabsheet($page['page']);
+
+//---------------------------------------------------------------Order options
+$link = get_root_url().'admin.php?page='.$page['page'].'&order=';
+$template->assign('order_options',
+ array(
+ $link.'date' => l10n('Post date'),
+ $link.'revision' => l10n('plugins_revisions'),
+ $link.'name' => l10n('Name'),
+ $link.'author' => l10n('Author')));
+$template->assign('order_selected', $link.$order);
+
+// +-----------------------------------------------------------------------+
+// | start template output |
+// +-----------------------------------------------------------------------+
+$plugins->get_server_plugins(true);
+
+if (is_array($plugins->server_plugins))
+{
+ $plugins->sort_server_plugins($order);
+
+ foreach($plugins->server_plugins as $plugin)
+ {
+ $ext_desc = nl2br(htmlspecialchars(strip_tags(
+ utf8_encode($plugin['ext_description']))));
+
+ $ver_desc = sprintf(l10n('plugins_description'),
+ $plugin['version'],
+ date('Y-m-d', $plugin['date']),
+ nl2br(htmlspecialchars(strip_tags(
+ utf8_encode($plugin['description'])))));
+
+ $url_auto_install = htmlentities($base_url)
+ . '&extension=' . $plugin['id_extension']
+ . '&install=%2Fupload%2Fextension-' . $plugin['id_extension']
+ . '%2Frevision-' . $plugin['id_revision'] . '%2F'
+ . str_replace(' ', '%20',$plugin['url']);
+
+ $url_download = PEM_URL .'/upload/extension-'.$plugin['id_extension']
+ . '/revision-' . $plugin['id_revision']
+ . '/' . str_replace(' ', '%20',$plugin['url']);
+
+ $template->append('plugins',
+ array('EXT_NAME' => $plugin['ext_name'],
+ 'EXT_URL' => PEM_URL.'/extension_view.php?eid='.$plugin['id_extension'],
+ 'EXT_DESC' => $ext_desc,
+ 'VERSION' => $plugin['version'],
+ 'VERSION_URL' => PEM_URL.'/revision_view.php?rid='.$plugin['id_revision'],
+ 'DATE' => date('Y-m-d', $plugin['date']),
+ 'VER_DESC' => $ver_desc,
+ 'AUTHOR' => $plugin['author'],
+ 'URL_INSTALL' => $url_auto_install,
+ 'URL_DOWNLOAD' => $url_download));
+ }
+}
+else
+{
+ array_push($page['errors'], l10n('plugins_server_error'));
+}
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'plugins');
+?>
\ No newline at end of file
diff --git a/BSF/admin/plugins_update.php b/BSF/admin/plugins_update.php
new file mode 100644
index 000000000..bdb8c5b9d
--- /dev/null
+++ b/BSF/admin/plugins_update.php
@@ -0,0 +1,169 @@
+set_filenames(array('plugins' => 'admin/plugins_update.tpl'));
+
+$base_url = get_root_url().'admin.php?page='.$page['page'];
+
+$plugins = new plugins();
+
+//-----------------------------------------------------------automatic upgrade
+if (isset($_GET['upgrade']) and isset($_GET['plugin']) and !is_adviser())
+{
+ $plugin_id = $_GET['plugin'];
+
+ if (isset($plugins->db_plugins_by_id[$plugin_id])
+ and $plugins->db_plugins_by_id[$plugin_id]['state'] == 'active')
+ {
+ $plugins->perform_action('deactivate', $plugin_id);
+
+ redirect($base_url
+ . '&upgrade=' . $_GET['upgrade']
+ . '&plugin=' . $plugin_id
+ . '&reactivate=true');
+ }
+
+ $upgrade_status =
+ $plugins->extract_plugin_files('upgrade', $_GET['upgrade'], $plugin_id);
+
+ if (isset($_GET['reactivate']))
+ {
+ $plugins->perform_action('activate', $plugin_id);
+ }
+ redirect($base_url.'&plugin='.$plugin_id.'&upgradestatus='.$upgrade_status);
+}
+
+//--------------------------------------------------------------upgrade result
+if (isset($_GET['upgradestatus']) and isset($_GET['plugin']))
+{
+ switch ($_GET['upgradestatus'])
+ {
+ case 'ok':
+ array_push($page['infos'],
+ sprintf(
+ l10n('plugins_upgrade_ok'),
+ $plugins->fs_plugins[$_GET['plugin']]['name']));
+ break;
+
+ case 'temp_path_error':
+ array_push($page['errors'], l10n('plugins_temp_path_error'));
+ break;
+
+ case 'dl_archive_error':
+ array_push($page['errors'], l10n('plugins_dl_archive_error'));
+ break;
+
+ case 'archive_error':
+ array_push($page['errors'], l10n('plugins_archive_error'));
+ break;
+
+ default:
+ array_push($page['errors'],
+ sprintf(l10n('plugins_extract_error'), $_GET['installstatus']),
+ l10n('plugins_check_chmod'));
+ }
+}
+
+//--------------------------------------------------------------------Tabsheet
+set_plugins_tabsheet($page['page']);
+
+// +-----------------------------------------------------------------------+
+// | start template output |
+// +-----------------------------------------------------------------------+
+$plugins->get_server_plugins();
+
+if (is_array($plugins->server_plugins))
+{
+ foreach($plugins->fs_plugins as $plugin_id => $fs_plugin)
+ {
+ if (isset($fs_plugin['extension'])
+ and isset($plugins->server_plugins[$fs_plugin['extension']]))
+ {
+ $plugin_info = $plugins->server_plugins[$fs_plugin['extension']];
+
+ $ext_desc = nl2br(htmlspecialchars(strip_tags(
+ utf8_encode($plugin_info['ext_description']))));
+
+ $ver_desc = sprintf(l10n('plugins_description'),
+ $plugin_info['version'],
+ date('Y-m-d', $plugin_info['date']),
+ nl2br(htmlspecialchars(strip_tags(
+ utf8_encode($plugin_info['description'])))));
+
+ if ($plugins->plugin_version_compare($fs_plugin, $plugin_info))
+ {
+ // Plugin is up to date
+ $template->append('plugins_uptodate',
+ array('URL' => $fs_plugin['uri'],
+ 'NAME' => $fs_plugin['name'],
+ 'EXT_DESC' => $ext_desc,
+ 'VERSION' => $fs_plugin['version']));
+ }
+ else
+ {
+ // Plugin need upgrade
+ $url_auto_update = $base_url
+ . '&plugin=' . $plugin_id
+ . '&upgrade=%2Fupload%2Fextension-' . $fs_plugin['extension']
+ . '%2Frevision-' . $plugin_info['id_revision']
+ . '%2F' . str_replace(' ', '%20',$plugin_info['url']);
+
+ $url_download = PEM_URL.'/upload/extension-'. $fs_plugin['extension']
+ . '/revision-' . $plugin_info['id_revision']
+ . '/' . str_replace(' ', '%20',$plugin_info['url']);
+
+ $template->append('plugins_not_uptodate',
+ array('EXT_NAME' => $fs_plugin['name'],
+ 'EXT_URL' => $fs_plugin['uri'],
+ 'EXT_DESC' => $ext_desc,
+ 'VERSION' => $fs_plugin['version'],
+ 'VERSION_URL' => PEM_URL.'/revision_view.php?rid='.$plugin_info['id_revision'],
+ 'NEW_VERSION' => $plugin_info['version'],
+ 'NEW_VER_DESC' => $ver_desc,
+ 'URL_UPDATE' => $url_auto_update,
+ 'URL_DOWNLOAD' => $url_download));
+ }
+ }
+ else
+ {
+ // Can't check plugin
+ $template->append('plugins_cant_check',
+ array('NAME' => $fs_plugin['name'],
+ 'VERSION' => $fs_plugin['version']));
+ }
+ }
+}
+else
+{
+ array_push($page['errors'], l10n('plugins_server_error'));
+}
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'plugins');
+?>
\ No newline at end of file
diff --git a/BSF/admin/profile.php b/BSF/admin/profile.php
new file mode 100644
index 000000000..7c4244494
--- /dev/null
+++ b/BSF/admin/profile.php
@@ -0,0 +1,46 @@
+set_filename('profile', 'admin/profile.tpl');
+$template->assign_var_from_handle('ADMIN_CONTENT', 'profile');
+?>
diff --git a/BSF/admin/rating.php b/BSF/admin/rating.php
new file mode 100644
index 000000000..18557b329
--- /dev/null
+++ b/BSF/admin/rating.php
@@ -0,0 +1,258 @@
+ '.$conf['guest_id'];
+ }
+ elseif ($_GET['users'] == 'guest')
+ {
+ $page['user_filter'] = ' AND r.user_id = '.$conf['guest_id'];
+ }
+}
+
+if (isset($_GET['del']) and !is_adviser())
+{
+ $del_params = urldecode( $_GET['del'] );
+ parse_str($del_params, $vars);
+ if ( !is_numeric($vars['e']) or !is_numeric($vars['u']) )
+ {
+ die('Hacking attempt');
+ }
+ $query = '
+DELETE FROM '. RATE_TABLE .'
+WHERE element_id=' . $vars['e'] . '
+AND user_id=' . $vars['u'] . '
+AND anonymous_id=\'' . $vars['a'] . '\'
+;';
+ pwg_query($query);
+ update_average_rate( $vars['e'] );
+}
+
+$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 = mysql_fetch_array($result))
+{
+ $users[$row['id']]=$row['username'];
+}
+
+
+$query = 'SELECT COUNT(DISTINCT(i.id))
+FROM '.RATE_TABLE.' AS r, '.IMAGES_TABLE.' AS i
+WHERE r.element_id=i.id'. $page['user_filter'] .
+';';
+list($nb_images) = mysql_fetch_row(pwg_query($query));
+
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+
+$template->set_filename('rating', 'admin/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('Average rate'), 'average_rate DESC'),
+ array(l10n('Number of rates'), 'nb_rates DESC'),
+ array(l10n('Sum of rates'), 'sum_rates DESC'),
+ array(l10n('Controversy'), 'std_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.tn_ext,
+ i.average_rate,
+ i.storage_category_id,
+ MAX(r.date) AS recently_rated,
+ COUNT(r.rate) AS nb_rates,
+ SUM(r.rate) AS sum_rates,
+ ROUND(STD(r.rate),2) AS std_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 r.element_id
+ ORDER BY ' . $available_order_by[$order_by_index][1] .'
+ LIMIT '.$start.','.$elements_per_page.'
+;';
+
+$images = array();
+$result = pwg_query($query);
+while ($row = mysql_fetch_assoc($result))
+{
+ array_push($images, $row);
+}
+
+$template->assign( 'images', array() );
+foreach ($images as $image)
+{
+ $thumbnail_src = get_thumbnail_url($image);
+
+ $image_url = PHPWG_ROOT_PATH.'admin.php?page=picture_modify'.
+ '&image_id='.$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 = mysql_num_rows($result);
+
+ $tpl_image =
+ array(
+ 'U_THUMB' => $thumbnail_src,
+ 'U_URL' => $image_url,
+ 'AVG_RATE' => $image['average_rate'],
+ 'STD_RATE' => $image['std_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 = mysql_fetch_array($result))
+ {
+
+ $url_del = PHPWG_ROOT_PATH.'admin.php'.
+ get_query_string_diff(array('del'));
+
+ $del_param = 'e='.$image['id'].
+ '&u='.$row['user_id'].
+ '&a='.$row['anonymous_id'];
+
+ $url_del .= '&del='.urlencode(urlencode($del_param));
+
+ if ( isset($users[$row['user_id']]) )
+ {
+ $user = $users[$row['user_id']];
+ }
+ else
+ {
+ $user = '? '. $row['user_id'];
+ }
+ if ( strlen($row['anonymous_id'])>0 )
+ {
+ $user .= '('.$row['anonymous_id'].')';
+ }
+
+ $tpl_image['rates'][] =
+ array(
+ 'DATE' => format_date($row['date']),
+ 'RATE' => $row['rate'],
+ 'USER' => $user,
+ 'U_DELETE' => $url_del
+ );
+ }
+ $template->append( 'images', $tpl_image );
+}
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'rating');
+?>
diff --git a/BSF/admin/site_manager.php b/BSF/admin/site_manager.php
new file mode 100644
index 000000000..def9fe579
--- /dev/null
+++ b/BSF/admin/site_manager.php
@@ -0,0 +1,307 @@
+append(
+ 'remote_output',
+ array(
+ 'CLASS' => 'remote'.ucfirst(strtolower($matches[1])),
+ 'CONTENT' => $line
+ )
+ );
+ }
+ }
+ }
+ else
+ {
+ array_push($page['errors'], l10n('site_err_remote_file_not_found'));
+ }
+}
+
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+$template->set_filenames(array('site_manager'=>'admin/site_manager.tpl'));
+
+// +-----------------------------------------------------------------------+
+// | new site creation form |
+// +-----------------------------------------------------------------------+
+if (isset($_POST['submit']) and !empty($_POST['galleries_url'])
+ and !is_adviser() )
+{
+ $is_remote = url_is_remote( $_POST['galleries_url'] );
+ $url = preg_replace('/[\/]*$/', '', $_POST['galleries_url']);
+ $url.= '/';
+ if (! $is_remote)
+ {
+ if ( ! (strpos($url, '.') === 0 ) )
+ {
+ $url = './' . $url;
+ }
+ }
+
+ // site must not exists
+ $query = '
+SELECT COUNT(id) AS count
+ FROM '.SITES_TABLE.'
+ WHERE galleries_url = \''.$url.'\'
+;';
+ $row = mysql_fetch_array(pwg_query($query));
+ if ($row['count'] > 0)
+ {
+ array_push($page['errors'],
+ l10n('site_already_exists').' ['.$url.']');
+ }
+ if (count($page['errors']) == 0)
+ {
+ if ($is_remote)
+ {
+ if ( ! isset($_POST['no_check']) )
+ {
+ $clf_url = $url.'create_listing_file.php';
+ $clf_url.= '?action=test';
+ $clf_url.= '&version='.PHPWG_VERSION;
+ if ( ($lines = @file($clf_url)) !== false)
+ {
+ $first_line = strip_tags($lines[0]);
+ if (!preg_match('/^PWG-INFO-2:/', $first_line))
+ {
+ array_push($page['errors'],
+ l10n('site_err').' : '.$first_line);
+ }
+ }
+ else
+ {
+ array_push($page['errors'], l10n('site_err_remote_file_not_found') );
+ }
+ }
+ }
+ else
+ { // local directory
+ if ( ! file_exists($url) )
+ {
+ array_push($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);
+ array_push($page['infos'],
+ $url.' '.l10n('site_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']) and !is_adviser())
+{
+ $query = '
+SELECT galleries_url
+ FROM '.SITES_TABLE.'
+ WHERE id = '.$page['site'].'
+;';
+ list($galleries_url) = mysql_fetch_array(pwg_query($query));
+ switch($_GET['action'])
+ {
+ case 'generate' :
+ {
+ $title = $galleries_url.' : '.l10n('remote_site_generate');
+ remote_output($galleries_url.'create_listing_file.php?action=generate');
+ break;
+ }
+ case 'test' :
+ {
+ $title = $galleries_url.' : '.l10n('remote_site_test');
+ remote_output($galleries_url.'create_listing_file.php?action=test&version='.PHPWG_VERSION);
+ break;
+ }
+ case 'clean' :
+ {
+ $title = $galleries_url.' : '.l10n('remote_site_clean');
+ remote_output($galleries_url.'create_listing_file.php?action=clean');
+ break;
+ }
+ case 'delete' :
+ {
+ delete_site($page['site']);
+ array_push($page['infos'],
+ $galleries_url.' '.l10n('site_deleted'));
+ break;
+ }
+ }
+}
+
+$template->assign( array(
+ 'U_HELP' => get_root_url().'popuphelp.php?page=site_manager',
+ 'F_ACTION' => get_root_url().'admin.php'
+ .get_query_string_diff( array('action','site') )
+ ) );
+
+// +-----------------------------------------------------------------------+
+// | remote sites list |
+// +-----------------------------------------------------------------------+
+
+if ( is_file(PHPWG_ROOT_PATH.'listing.xml') )
+{
+ $xml_content = getXmlCode(PHPWG_ROOT_PATH.'listing.xml');
+ $local_listing_site_url = getAttribute(
+ getChild($xml_content, 'informations'),
+ 'url'
+ );
+ if ( !url_is_remote($local_listing_site_url) )
+ {
+ $local_listing_site_url = null;
+ }
+}
+
+$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 = mysql_fetch_array($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.= '&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 ? 'site_remote' : 'site_local' ),
+ 'CATEGORIES' => (int)@$sites_detail[$row['id']]['nb_categories'],
+ 'IMAGES' => (int)@$sites_detail[$row['id']]['nb_images'],
+ 'U_SYNCHRONIZE' => $update_url
+ );
+
+ if ($is_remote)
+ {
+ $tpl_var['remote'] =
+ array(
+ 'U_TEST' => $base_url.'test',
+ 'U_GENERATE' => $row['galleries_url'].'create_listing_file.php?action=generate',
+ 'U_CLEAN' => $base_url.'clean',
+ );
+ }
+
+ 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);
+
+ if ( isset($local_listing_site_url) and
+ $row['galleries_url']==$local_listing_site_url )
+ {
+ $local_listing_site_id = $row['id'];
+ $template->assign( 'local_listing',
+ array(
+ 'URL' => $local_listing_site_url,
+ 'U_SYNCHRONIZE' => $update_url.'&local_listing=1'
+ )
+ );
+ }
+}
+
+if ( isset($local_listing_site_url) and !isset($local_listing_site_id) )
+{
+ $template->assign( 'local_listing',
+ array(
+ 'URL' => $local_listing_site_url,
+ 'CREATE' => true
+ )
+ );
+}
+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'site_manager');
+?>
diff --git a/BSF/admin/site_reader_local.php b/BSF/admin/site_reader_local.php
new file mode 100644
index 000000000..6d6d252ee
--- /dev/null
+++ b/BSF/admin/site_reader_local.php
@@ -0,0 +1,265 @@
+site_url = $url;
+}
+
+/**
+ * Is this local site ok ?
+ *
+ * @return true on success, false otherwise
+ */
+function open()
+{
+ global $errors;
+
+ if (!is_dir($this->site_url))
+ {
+ array_push(
+ $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('tn_ext'=>'jpg' ... )
+ */
+function get_elements($path)
+{
+ global $conf;
+ if (!isset($conf['flip_file_ext']))
+ {
+ $conf['flip_file_ext'] = array_flip($conf['file_ext']);
+ }
+
+ $subdirs = array();
+ $fs = array();
+ if (is_dir($path) && $contents = opendir($path) )
+ {
+ while (($node = readdir($contents)) !== false)
+ {
+ if (is_file($path.'/'.$node))
+ {
+ $extension = get_extension($node);
+ $filename_wo_ext = get_filename_wo_extension($node);
+
+ if ( isset($conf['flip_file_ext'][$extension]) )
+ {
+ $tn_ext = $this->get_tn_ext($path, $filename_wo_ext);
+ $fs[ $path.'/'.$node ] = array(
+ 'tn_ext' => $tn_ext,
+ );
+ }
+ }
+ elseif (is_dir($path.'/'.$node)
+ and $node != '.'
+ and $node != '..'
+ and $node != 'pwg_high'
+ and $node != 'pwg_representative'
+ and $node != 'thumbnail' )
+ {
+ array_push($subdirs, $node);
+ }
+ } //end while readdir
+ closedir($contents);
+
+ foreach ($subdirs as $subdir)
+ {
+ $tmp_fs = $this->get_elements($path.'/'.$subdir);
+ $fs = array_merge($fs, $tmp_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('tn_ext', 'has_high', 'representative_ext');
+}
+
+function get_element_update_attributes($file)
+{
+ global $conf;
+ $data = array();
+
+ $filename = basename($file);
+ $dirname = dirname($file);
+ $filename_wo_ext = get_filename_wo_extension($filename);
+ $extension = get_extension($filename);
+
+ $data['tn_ext'] = $this->get_tn_ext($dirname, $filename_wo_ext);
+ $data['has_high'] = $this->get_has_high($dirname, $filename);
+
+ if ( !isset($conf['flip_picture_ext'][$extension]) )
+ {
+ $data['representative_ext'] = $this->get_representative_ext(
+ $dirname, $filename_wo_ext
+ );
+ }
+ return $data;
+}
+
+// returns the name of the attributes that are supported for
+// metadata update/synchronization according to configuration
+function get_metadata_attributes()
+{
+ global $conf;
+
+ $update_fields = array('filesize', 'width', 'height', 'high_filesize');
+
+ if ($conf['use_exif'])
+ {
+ $update_fields =
+ array_merge(
+ $update_fields,
+ array_keys($conf['use_exif_mapping'])
+ );
+ }
+
+ if ($conf['use_iptc'])
+ {
+ $update_fields =
+ array_merge(
+ $update_fields,
+ array_keys($conf['use_iptc_mapping'])
+ );
+ }
+
+ return $update_fields;
+}
+
+// returns a hash of attributes (metadata+filesize+width,...) for file
+function get_element_metadata($file, $has_high = false)
+{
+ global $conf;
+ if (!is_file($file))
+ {
+ return null;
+ }
+
+ $data = array();
+
+ $data['filesize'] = floor(filesize($file)/1024);
+
+ if ($image_size = @getimagesize($file))
+ {
+ $data['width'] = $image_size[0];
+ $data['height'] = $image_size[1];
+ }
+
+ if ($has_high)
+ {
+ $high_file = dirname($file).'/pwg_high/'.basename($file);
+
+ $data['high_filesize'] = floor(filesize($high_file)/1024);
+ }
+
+ if ($conf['use_exif'])
+ {
+ $data = array_merge($data, get_sync_exif_data($file) );
+ }
+
+ if ($conf['use_iptc'])
+ {
+ $data = array_merge($data, get_sync_iptc_data($file) );
+ }
+
+ return $data;
+}
+
+
+//-------------------------------------------------- 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;
+}
+
+function get_tn_ext($path, $filename_wo_ext)
+{
+ global $conf;
+
+ $base_test =
+ $path.'/thumbnail/'.$conf['prefix_thumbnail'].$filename_wo_ext.'.';
+
+ foreach ($conf['picture_ext'] as $ext)
+ {
+ $test = $base_test.$ext;
+ if (is_file($test))
+ {
+ return $ext;
+ }
+ }
+
+ return null;
+}
+
+function get_has_high($path, $filename)
+{
+ if (is_file($path.'/pwg_high/'.$filename))
+ {
+ return 'true';
+ }
+
+ return null;
+}
+
+}
+?>
\ No newline at end of file
diff --git a/BSF/admin/site_reader_remote.php b/BSF/admin/site_reader_remote.php
new file mode 100644
index 000000000..31ae3e792
--- /dev/null
+++ b/BSF/admin/site_reader_remote.php
@@ -0,0 +1,250 @@
+site_url = $url;
+ $this->insert_attributes = array(
+ 'tn_ext',
+ );
+ $this->update_attributes = array(
+ 'tn_ext', 'representative_ext', 'has_high',
+ );
+ $this->metadata_attributes = array(
+ 'filesize', 'width', 'height', 'high_filesize'
+ );
+
+ if (!isset($listing_url))
+ {
+ $this->listing_url = $this->site_url.'/listing.xml';
+ }
+ else
+ {
+ $this->listing_url = $listing_url;
+ }
+}
+
+/**
+ * Is this remote site ok ?
+ *
+ * @return true on success, false otherwise
+ */
+function open()
+{
+ global $errors;
+
+ if (@fopen($this->listing_url, 'r'))
+ {
+ $this->site_dirs = array();
+ $this->site_files = array();
+ $xml_content = getXmlCode($this->listing_url);
+ $info_xml_element = getChild($xml_content, 'informations');
+ if (getAttribute($info_xml_element , 'phpwg_version') != PHPWG_VERSION)
+ {
+ array_push(
+ $errors,
+ array(
+ 'path' => $this->listing_url,
+ 'type' => 'PWG-ERROR-VERSION'
+ )
+ );
+
+ return false;
+ }
+
+ $additional_metadata = getAttribute($info_xml_element, 'metadata');
+
+ if ($additional_metadata != '')
+ {
+ $this->metadata_attributes = array_merge(
+ $this->metadata_attributes,
+ explode(',', $additional_metadata)
+ );
+ }
+
+ $this->build_structure($xml_content, '', 0);
+
+ return true;
+ }
+ else
+ {
+ array_push(
+ $errors,
+ array(
+ 'path' => $this->listing_url,
+ 'type' => 'PWG-ERROR-NOLISTING'
+ )
+ );
+
+ return false;
+ }
+}
+
+// retrieve xml sub-directories fulldirs
+function get_full_directories($basedir)
+{
+ $dirs = array();
+ foreach ( array_keys($this->site_dirs) as $dir)
+ {
+ $full_dir = $this->site_url . $dir;
+ if ($full_dir != $basedir
+ and strpos($full_dir, $basedir) === 0
+ )
+ {
+ array_push($dirs, $full_dir);
+ }
+ }
+ return $dirs;
+}
+
+/**
+ * Returns a hash with all elements (images and files) inside the full $path
+ * according to listing.xml
+ * @param string $path recurse in this directory only
+ * @return array like "pic.jpg"=>array('tn_ext'=>'jpg' ... )
+ */
+function get_elements($path)
+{
+ $elements = array();
+ foreach ( $this->site_dirs as $dir=>$files)
+ {
+ $full_dir = $this->site_url . $dir;
+ if (strpos($full_dir, $path) === 0)
+ {
+ foreach ($files as $file)
+ {
+ $data = $this->get_element_attributes(
+ $file,
+ $this->insert_attributes
+ );
+ $elements[$file] = $data;
+ }
+ }
+ }
+
+ return $elements;
+}
+
+// returns the name of the attributes that are supported for
+// files update/synchronization
+function get_update_attributes()
+{
+ return $this->update_attributes;
+}
+
+function get_element_update_attributes($file)
+{
+ return $this->get_element_attributes(
+ $file,
+ $this->update_attributes
+ );
+}
+
+// returns the name of the attributes that are supported for
+// metadata update/synchronization according to listing.xml
+function get_metadata_attributes()
+{
+ return $this->metadata_attributes;
+}
+
+// returns a hash of attributes (metadata+width,...) for file
+function get_element_metadata($file, $has_high = false)
+{
+ return $this->get_element_attributes(
+ $file,
+ $this->metadata_attributes
+ );
+}
+
+//-------------------------------------------------- private functions --------
+/**
+ * Returns a hash of image/file attributes
+ * @param string $file fully qualified file name
+ * @param array $attributes specifies which attributes to retrieve
+ * returned
+*/
+function get_element_attributes($file, $attributes)
+{
+ $xml_element = $this->site_files[$file];
+ if (!isset($xml_element))
+ {
+ return null;
+ }
+ $data = array();
+ foreach($attributes as $att)
+ {
+ if (getAttribute($xml_element, $att) != '')
+ {
+ $val = getAttribute($xml_element, $att);
+ $data[$att] = addslashes($val);
+ }
+ }
+ return $data;
+}
+
+// recursively parse the xml_content for later usage
+function build_structure($xml_content, $basedir, $level)
+{
+ $temp_dirs = getChildren($xml_content, 'dir'.$level);
+ foreach ($temp_dirs as $temp_dir)
+ {
+ $dir_name = $basedir;
+ if ($dir_name != '' )
+ {
+ $dir_name .= '/';
+ }
+ $dir_name .= getAttribute($temp_dir, 'name');
+ $this->site_dirs[ $dir_name ] = array();
+ $this->build_structure($temp_dir, $dir_name, $level+1);
+ }
+
+ if ($basedir != '')
+ {
+ $xml_elements = getChildren(
+ getChild($xml_content, 'root'),
+ 'element'
+ );
+ foreach ($xml_elements as $xml_element)
+ {
+ $path = getAttribute($xml_element, 'path');
+ $this->site_files[$path] = $xml_element;
+ array_push($this->site_dirs[$basedir], $path);
+ }
+ }
+}
+
+}
+
+?>
diff --git a/BSF/admin/site_update.php b/BSF/admin/site_update.php
new file mode 100644
index 000000000..e6fb6d4ef
--- /dev/null
+++ b/BSF/admin/site_update.php
@@ -0,0 +1,991 @@
+ array(
+ l10n('update_wrong_dirname_short'),
+ l10n('update_wrong_dirname_info')
+ ),
+ 'PWG-UPDATE-2' => array(
+ l10n('update_missing_tn_short'),
+ l10n('update_missing_tn_info').implode(',', $conf['picture_ext'])
+ ),
+ 'PWG-ERROR-NO-FS' => array(
+ l10n('update_missing_file_or_dir'),
+ l10n('update_missing_file_or_dir_info')
+ ),
+ 'PWG-ERROR-VERSION' => array(
+ l10n('update_err_pwg_version_differs'),
+ l10n('update_err_pwg_version_differs_info')
+ ),
+ 'PWG-ERROR-NOLISTING' => array(
+ l10n('update_err_remote_listing_not_found'),
+ l10n('update_err_remote_listing_not_found_info')
+ )
+ );
+$errors = array();
+$infos = array();
+
+if ($site_is_remote)
+{
+ include_once(PHPWG_ROOT_PATH.'admin/site_reader_remote.php');
+ $local_listing = null;
+ if ( isset($_GET['local_listing'])
+ and $_GET['local_listing'] )
+ {
+ $local_listing = PHPWG_ROOT_PATH.'listing.xml';
+ }
+ $site_reader = new RemoteSiteReader($site_url, $local_listing);
+}
+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 (!isset($conf['flip_picture_ext']))
+ {
+ $conf['flip_picture_ext'] = array_flip($conf['picture_ext']);
+ }
+ if ($site_reader->open())
+ {
+ $general_failure = false;
+ }
+
+ // shall we simulate only
+ if ((isset($_POST['simulate']) and $_POST['simulate'] == 1) or is_adviser())
+ {
+ $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 ?
+ $cat_ids = array();
+
+ $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 REGEXP \'(^|,)'.$_POST['cat'].'(,|$)\'
+';
+ }
+ else
+ {
+ $query.= '
+ AND id = '.$_POST['cat'].'
+';
+ }
+ }
+ $query.= '
+;';
+ $result = pwg_query($query);
+
+ $db_categories = array();
+ while ($row = mysql_fetch_array($result))
+ {
+ $db_categories[$row['id']] = $row;
+ }
+
+ // 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 = mysql_fetch_array($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 = mysql_fetch_array($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
+ $query = '
+SELECT IF(MAX(id)+1 IS NULL, 1, MAX(id)+1) AS next_id
+ FROM '.CATEGORIES_TABLE.'
+;';
+ list($next_id) = mysql_fetch_array(pwg_query($query));
+
+ // 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']))
+ {
+ array_push($fs_fulldirs, $basedir);
+ }
+ // If $_POST['subcats-included'] != 1 ("Search in subcategories" 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('/^[a-zA-Z0-9-_.]+$/', $dir))
+ {
+ $insert = array(
+ 'id' => $next_id++,
+ 'dir' => $dir,
+ 'name' => str_replace('_', ' ', $dir),
+ 'site_id' => $site_id,
+ 'commentable' =>
+ boolean_to_string($conf['newcat_default_commentable']),
+ 'uploadable' => $site_is_remote
+ ? 'false'
+ : boolean_to_string($conf['newcat_default_uploadable']),
+ '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'};
+ }
+
+ array_push($inserts, $insert);
+ array_push(
+ $infos,
+ array(
+ 'path' => $fulldir,
+ 'info' => l10n('update_research_added')
+ )
+ );
+
+ // add the new category to $db_categories and $db_fulldirs array
+ $db_categories[$insert{'id'}] =
+ array(
+ 'id' => $insert{'id'},
+ '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
+ {
+ array_push(
+ $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',
+ 'uploadable','visible','status','rank','global_rank'
+ );
+ mass_inserts(CATEGORIES_TABLE, $dbfields, $inserts);
+ }
+
+ $counts['new_categories'] = count($inserts);
+ }
+
+ // to delete categories
+ $to_delete = array();
+ foreach (array_diff(array_keys($db_fulldirs), $fs_fulldirs) as $fulldir)
+ {
+ array_push($to_delete, $db_fulldirs[$fulldir]);
+ unset($db_fulldirs[$fulldir]);
+ array_push($infos, array('path' => $fulldir,
+ 'info' => l10n('update_research_deleted')));
+ }
+ if (count($to_delete) > 0)
+ {
+ if (!$simulate)
+ {
+ delete_categories($to_delete);
+ }
+ $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();
+ $db_unvalidated = array();
+
+ if (count($cat_ids) > 0)
+ {
+ $query = '
+SELECT id, path
+ FROM '.IMAGES_TABLE.'
+ WHERE storage_category_id IN ('
+ .wordwrap(
+ implode(', ', $cat_ids),
+ 80,
+ "\n"
+ ).')
+;';
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ {
+ $db_elements[$row['id']] = $row['path'];
+ }
+
+ // searching the unvalidated waiting elements (they must not be taken into
+ // account)
+ $query = '
+SELECT file,storage_category_id
+ FROM '.WAITING_TABLE.'
+ WHERE storage_category_id IN (
+'.wordwrap(implode(', ', $cat_ids), 80, "\n").')
+ AND validated = \'false\'
+;';
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push(
+ $db_unvalidated,
+ array_search(
+ $row['storage_category_id'],
+ $db_fulldirs)
+ .'/'.$row['file']
+ );
+ }
+ }
+
+ // next element id available
+ $query = '
+SELECT IF(MAX(id)+1 IS NULL, 1, MAX(id)+1) AS next_element_id
+ FROM '.IMAGES_TABLE.'
+;';
+ list($next_element_id) = mysql_fetch_array(pwg_query($query));
+
+ $start = get_moment();
+
+ $inserts = array();
+ $insert_links = array();
+
+ foreach (array_diff(array_keys($fs), $db_elements, $db_unvalidated) as $path)
+ {
+ $insert = array();
+ // storage category must exist
+ $dirname = dirname($path);
+ if (!isset($db_fulldirs[$dirname]))
+ {
+ continue;
+ }
+ $filename = basename($path);
+ if (!preg_match('/^[a-zA-Z0-9-_.]+$/', $filename))
+ {
+ array_push(
+ $errors,
+ array(
+ 'path' => $path,
+ 'type' => 'PWG-UPDATE-1'
+ )
+ );
+
+ continue;
+ }
+
+ if ( isset( $conf['flip_picture_ext'][get_extension($filename)] )
+ and !isset($fs[$path]['tn_ext']) )
+ { // For a picture thumbnail is mandatory and for non picture element,
+ // thumbnail and representative are optionnal
+ array_push(
+ $errors,
+ array(
+ 'path' => $path,
+ 'type' => 'PWG-UPDATE-2'
+ )
+ );
+ }
+ else
+ {
+ $insert = array(
+ 'id' => $next_element_id++,
+ 'file' => $filename,
+ 'date_available' => CURRENT_DATE,
+ 'path' => $path,
+ 'tn_ext' => isset($fs[$path]['tn_ext'])
+ ? $fs[$path]['tn_ext']
+ : null,
+ 'storage_category_id' => $db_fulldirs[$dirname],
+ );
+
+ if ( $_POST['privacy_level']!=0 )
+ {
+ $insert['level'] = $_POST['privacy_level'];
+ }
+
+ array_push(
+ $inserts,
+ $insert
+ );
+
+ array_push(
+ $insert_links,
+ array(
+ 'image_id' => $insert{'id'},
+ 'category_id' => $insert['storage_category_id'],
+ )
+ );
+
+ array_push(
+ $infos,
+ array(
+ 'path' => $insert{'path'},
+ 'info' => l10n('update_research_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 elements 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)
+ {
+ array_push($to_delete_elements, array_search($path, $db_elements));
+ array_push($infos, array('path' => $path,
+ 'info' => l10n('update_research_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', '' );
+
+ // retrieving informations given by uploaders
+ if (!$simulate and count($cat_ids) > 0)
+ {
+ $query = '
+SELECT id,file,storage_category_id,infos
+ FROM '.WAITING_TABLE.'
+ WHERE storage_category_id IN (
+'.wordwrap(implode(', ', $cat_ids), 80, "\n").')
+ AND validated = \'true\'
+;';
+ $result = pwg_query($query);
+
+ $datas = array();
+ $fields =
+ array(
+ 'primary' => array('id'),
+ 'update' => array('date_creation', 'author', 'name', 'comment')
+ );
+
+ $waiting_to_delete = array();
+
+ while ($row = mysql_fetch_array($result))
+ {
+ $data = array();
+
+ $query = '
+SELECT id
+ FROM '.IMAGES_TABLE.'
+ WHERE storage_category_id = '.$row['storage_category_id'].'
+ AND file = \''.$row['file'].'\'
+;';
+ list($data['id']) = mysql_fetch_array(pwg_query($query));
+
+ foreach ($fields['update'] as $field)
+ {
+ $data[$field] = addslashes( getAttribute($row['infos'], $field) );
+ }
+
+ array_push($datas, $data);
+ array_push($waiting_to_delete, $row['id']);
+ }
+
+ if (count($datas) > 0)
+ {
+ mass_updates(IMAGES_TABLE, $fields, $datas);
+
+ // delete now useless waiting elements
+ $query = '
+DELETE
+ FROM '.WAITING_TABLE.'
+ WHERE id IN ('.implode(',', $waiting_to_delete).')
+;';
+ pwg_query($query);
+ }
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | 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 )
+ {
+ $data = $site_reader->get_element_update_attributes($file);
+ if ( !is_array($data) )
+ {
+ continue;
+ }
+ $extension = get_extension($file);
+ if ( isset($conf['flip_picture_ext'][$extension]) )
+ {
+ if ( !isset($data['tn_ext']) )
+ {
+ array_push(
+ $errors,
+ array(
+ 'path' => $file,
+ 'type' => 'PWG-UPDATE-2'
+ )
+ );
+ continue;
+ }
+ }
+
+ $data['id']=$id;
+ array_push($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 preg_match('/^metadata/', $_POST['sync'])
+ and !$general_failure)
+{
+ // sync only never synchronized files ?
+ if ($_POST['sync'] == 'metadata_new')
+ {
+ $opts['only_new'] = true;
+ }
+ else
+ {
+ $opts['only_new'] = false;
+ }
+ $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();
+
+ $has_high_images = array();
+
+ $image_ids = array();
+ foreach ($files as $id => $file)
+ {
+ array_push($image_ids, $id);
+ }
+
+ if (count($image_ids) > 0)
+ {
+ $query = '
+SELECT id
+ FROM '.IMAGES_TABLE.'
+ WHERE has_high = \'true\'
+ AND id IN (
+'.wordwrap(implode(', ', $image_ids), 80, "\n").'
+)
+;';
+
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push($has_high_images, $row['id']);
+ }
+ }
+
+ foreach ( $files as $id=>$file )
+ {
+ $data = $site_reader->get_element_metadata(
+ $file,
+ in_array($id, $has_high_images)
+ );
+
+ if ( is_array($data) )
+ {
+ $data['date_metadata_update'] = CURRENT_DATE;
+ $data['id']=$id;
+ array_push($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)
+ {
+ array_push(
+ $tags_of[$id],
+ tag_id_from_tag_name($tag_name)
+ );
+ }
+ }
+ }
+ }
+ else
+ {
+ array_push($errors, array('path' => $file, '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
+ );
+ }
+ 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'=>'admin/site_update.tpl'));
+$result_title = '';
+if (isset($simulate) and $simulate)
+{
+ $result_title.= l10n('update_simulation_title').' ';
+}
+
+// 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('update_part_research'),
+ 'L_RESULT_METADATA'=>$result_title.l10n('update_result_metadata'),
+ 'METADATA_LIST' => $used_metadata,
+ 'U_HELP' => get_root_url().'popuphelp.php?page=synchronize',
+ ));
+
+// +-----------------------------------------------------------------------+
+// | introduction : choices |
+// +-----------------------------------------------------------------------+
+if (!isset($_POST['submit']) or (isset($simulate) and $simulate))
+{
+ if (isset($simulate) and $simulate)
+ {
+ $tpl_introduction = array(
+ 'sync' => $_POST['sync'],
+ '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'],
+ );
+
+ if (isset($_POST['cat']) and is_numeric($_POST['cat']))
+ {
+ $cat_selected = array($_POST['cat']);
+ }
+ else
+ {
+ $cat_selected = array();
+ }
+ }
+ else
+ {
+ $tpl_introduction = array(
+ 'sync' => 'dirs',
+ 'display_info' => false,
+ 'add_to_caddie' => false,
+ 'subcats_included' => true,
+ 'privacy_level_selected' => 0,
+ );
+
+ $cat_selected = array();
+ }
+
+ $tpl_introduction['privacy_level_options']=array();
+ foreach ($conf['available_permission_levels'] as $level)
+ {
+ $tpl_introduction['privacy_level_options'][$level] = l10n( sprintf('Level %d', $level) );
+ }
+
+ $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/BSF/admin/stats.php b/BSF/admin/stats.php
new file mode 100644
index 000000000..9892127c8
--- /dev/null
+++ b/BSF/admin/stats.php
@@ -0,0 +1,514 @@
+ $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 = mysql_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];
+ array_push($updates, $row);
+ unset($need_update[$key]);
+ }
+ }
+}
+
+foreach ($need_update as $time_key => $nb_pages)
+{
+ $time_tokens = explode('-', $time_key);
+
+ array_push(
+ $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';
+
+array_push(
+ $title_parts,
+ ''.l10n('Overall').''
+ );
+
+$period_label = l10n('Year');
+
+if (isset($page['year']))
+{
+ $url.= '&year='.$page['year'];
+
+ array_push(
+ $title_parts,
+ ''.$page['year'].''
+ );
+
+ $period_label = l10n('Month');
+}
+
+if (isset($page['month']))
+{
+ $url.= '&month='.$page['month'];
+
+ array_push(
+ $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)]
+ );
+
+ array_push(
+ $title_parts,
+ ''.$day_title.''
+ );
+
+ $period_label = l10n('Hour');
+}
+
+$template->set_filename('stats', 'admin/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().'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/BSF/admin/tags.php b/BSF/admin/tags.php
new file mode 100644
index 000000000..36e668a3d
--- /dev/null
+++ b/BSF/admin/tags.php
@@ -0,0 +1,246 @@
+ $tag_id,
+ 'name' => addslashes($tag_name),
+ 'url_name' => str2url($tag_name),
+ )
+ );
+ }
+ }
+ }
+ mass_updates(
+ TAGS_TABLE,
+ array(
+ 'primary' => array('id'),
+ 'update' => array('name', 'url_name'),
+ ),
+ $updates
+ );
+}
+
+// +-----------------------------------------------------------------------+
+// | delete tags |
+// +-----------------------------------------------------------------------+
+
+if (isset($_POST['delete']) and isset($_POST['tags']) and !is_adviser())
+{
+ $query = '
+SELECT name
+ FROM '.TAGS_TABLE.'
+ WHERE id IN ('.implode(',', $_POST['tags']).')
+;';
+ $tag_names = array_from_query($query, 'name');
+
+ $query = '
+DELETE
+ FROM '.IMAGE_TAG_TABLE.'
+ WHERE tag_id IN ('.implode(',', $_POST['tags']).')
+;';
+ pwg_query($query);
+
+ $query = '
+DELETE
+ FROM '.TAGS_TABLE.'
+ WHERE id IN ('.implode(',', $_POST['tags']).')
+;';
+ pwg_query($query);
+
+ array_push(
+ $page['infos'],
+ l10n_dec(
+ 'The %d following tag were deleted',
+ 'The %d following tags were deleted',
+ count($tag_names)).' : '.
+ implode(', ', $tag_names)
+ );
+}
+
+// +-----------------------------------------------------------------------+
+// | add a tag |
+// +-----------------------------------------------------------------------+
+
+if (isset($_POST['add']) and !empty($_POST['add_tag']) and !is_adviser())
+{
+ $tag_name = $_POST['add_tag'];
+
+ // 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)
+ {
+ mass_inserts(
+ TAGS_TABLE,
+ array('name', 'url_name'),
+ array(
+ array(
+ 'name' => $tag_name,
+ 'url_name' => str2url($tag_name),
+ )
+ )
+ );
+
+ array_push(
+ $page['infos'],
+ sprintf(
+ l10n('Tag "%s" was added'),
+ stripslashes($tag_name)
+ )
+ );
+ }
+ else
+ {
+ array_push(
+ $page['errors'],
+ sprintf(
+ l10n('Tag "%s" already exists'),
+ stripslashes($tag_name)
+ )
+ );
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+
+$template->set_filenames(array('tags' => 'admin/tags.tpl'));
+
+$template->assign(
+ array(
+ 'F_ACTION' => PHPWG_ROOT_PATH.'admin.php?page=tags'
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | form creation |
+// +-----------------------------------------------------------------------+
+
+$template->assign(
+ array(
+ 'TAG_SELECTION' => get_html_tag_selection(
+ get_all_tags(),
+ 'tags'
+ ),
+ )
+ );
+
+if (isset($_POST['edit']) and isset($_POST['tags']))
+{
+ $template->assign(
+ array(
+ 'EDIT_TAGS_LIST' => implode(',', $_POST['tags']),
+ )
+ );
+
+ $query = '
+SELECT id, name
+ FROM '.TAGS_TABLE.'
+ WHERE id IN ('.implode(',', $_POST['tags']).')
+;';
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ {
+ $name_of[ $row['id'] ] = $row['name'];
+ }
+
+ foreach ($_POST['tags'] as $tag_id)
+ {
+ $template->append(
+ 'tags',
+ array(
+ 'ID' => $tag_id,
+ 'NAME' => $name_of[$tag_id],
+ )
+ );
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | sending html code |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'tags');
+
+?>
diff --git a/BSF/admin/thumbnail.php b/BSF/admin/thumbnail.php
new file mode 100644
index 000000000..57ecf9cdb
--- /dev/null
+++ b/BSF/admin/thumbnail.php
@@ -0,0 +1,352 @@
+ 1 ) or ( $ratioHeight > 1 ) )
+ {
+ if ( $ratioWidth < $ratioHeight)
+ {
+ $destWidth = $srcWidth/$ratioHeight;
+ $destHeight = $newHeight;
+ }
+ else
+ {
+ $destWidth = $newWidth;
+ $destHeight = $srcHeight/$ratioWidth;
+ }
+ }
+ else
+ {
+ $destWidth = $srcWidth;
+ $destHeight = $srcHeight;
+ }
+ // according to the GD version installed on the server
+ if ( $_POST['gd'] == 2 )
+ {
+ // GD 2.0 or more recent -> good results (but slower)
+ $destImage = imagecreatetruecolor( $destWidth, $destHeight);
+ imagecopyresampled( $destImage, $srcImage, 0, 0, 0, 0,
+ $destWidth,$destHeight,$srcWidth,$srcHeight );
+ }
+ else
+ {
+ // GD prior to version 2 -> pretty bad results :-/ (but fast)
+ $destImage = imagecreate( $destWidth, $destHeight);
+ imagecopyresized( $destImage, $srcImage, 0, 0, 0, 0,
+ $destWidth,$destHeight,$srcWidth,$srcHeight );
+ }
+
+ if (($tndir = mkget_thumbnail_dir($dirname, $page['errors'])) == false)
+ {
+ return false;
+ }
+
+ $dest_file = $tndir.'/'.$conf['prefix_thumbnail'];
+ $dest_file.= get_filename_wo_extension($filename);
+ $dest_file.= '.'.$tn_ext;
+
+ // creation and backup of final picture
+ if (!is_writable($tndir))
+ {
+ array_push($page['errors'], '['.$tndir.'] : '.l10n('no_write_access'));
+ return false;
+ }
+ imagejpeg($destImage, $dest_file);
+ // freeing memory ressources
+ imagedestroy( $srcImage );
+ imagedestroy( $destImage );
+
+ list($tn_width, $tn_height) = getimagesize($dest_file);
+ $tn_size = floor(filesize($dest_file) / 1024).' KB';
+
+ $info = array( 'path' => $path,
+ 'tn_file' => $dest_file,
+ 'tn_width' => $tn_width,
+ 'tn_height' => $tn_height,
+ 'tn_size' => $tn_size );
+ return $info;
+ }
+ // error
+ else
+ {
+ echo l10n('tn_no_support')." ";
+ if ( isset( $extenstion ) )
+ {
+ echo l10n('tn_format').' '.$extension;
+ }
+ else
+ {
+ echo l10n('tn_thisformat');
+ }
+ exit();
+ }
+}
+
+$pictures = array();
+$stats = array();
+// +-----------------------------------------------------------------------+
+// | template initialization |
+// +-----------------------------------------------------------------------+
+$template->set_filenames( array('thumbnail'=>'admin/thumbnail.tpl') );
+
+$template->assign(array(
+ 'U_HELP' => PHPWG_ROOT_PATH.'popuphelp.php?page=thumbnail',
+ ));
+// +-----------------------------------------------------------------------+
+// | search pictures without thumbnails |
+// +-----------------------------------------------------------------------+
+$wo_thumbnails = array();
+$thumbnalized = array();
+
+
+// what is the directory to search in ?
+$query = '
+SELECT galleries_url FROM '.SITES_TABLE.'
+ WHERE galleries_url NOT LIKE "http://%"
+;';
+$result = pwg_query($query);
+while ( $row=mysql_fetch_assoc($result) )
+{
+ $basedir = preg_replace('#/*$#', '', $row['galleries_url']);
+ $fs = get_fs($basedir);
+
+ // because isset is one hundred time faster than in_array
+ $fs['thumbnails'] = array_flip($fs['thumbnails']);
+
+ foreach ($fs['elements'] as $path)
+ {
+ // only pictures need thumbnails
+ if (in_array(get_extension($path), $conf['picture_ext']))
+ {
+ $dirname = dirname($path);
+ $filename = basename($path);
+
+ // only files matching the authorized filename pattern can be considered
+ // as "without thumbnail"
+ if (!preg_match('/^[a-zA-Z0-9-_.]+$/', $filename))
+ {
+ continue;
+ }
+
+ // searching the element
+ $filename_wo_ext = get_filename_wo_extension($filename);
+ $tn_ext = '';
+ $base_test = $dirname.'/thumbnail/';
+ $base_test.= $conf['prefix_thumbnail'].$filename_wo_ext.'.';
+ foreach ($conf['picture_ext'] as $ext)
+ {
+ if (isset($fs['thumbnails'][$base_test.$ext]))
+ {
+ $tn_ext = $ext;
+ break;
+ }
+ }
+
+ if (empty($tn_ext))
+ {
+ array_push($wo_thumbnails, $path);
+ }
+ }
+ } // next element
+} // next site id
+// +-----------------------------------------------------------------------+
+// | thumbnails creation |
+// +-----------------------------------------------------------------------+
+if (isset($_POST['submit']))
+{
+ $times = array();
+ $infos = array();
+
+ // checking criteria
+ if (!ereg('^[0-9]{2,3}$', $_POST['width']) or $_POST['width'] < 10)
+ {
+ array_push($page['errors'], l10n('tn_err_width').' 10');
+ }
+ if (!ereg('^[0-9]{2,3}$', $_POST['height']) or $_POST['height'] < 10)
+ {
+ array_push($page['errors'], l10n('tn_err_height').' 10');
+ }
+
+ // picture miniaturization
+ if (count($page['errors']) == 0)
+ {
+ $num = 1;
+ foreach ($wo_thumbnails as $path)
+ {
+ if (is_numeric($_POST['n']) and $num > $_POST['n'])
+ {
+ break;
+ }
+
+ $starttime = get_moment();
+ if ($info = RatioResizeImg($path,$_POST['width'],$_POST['height'],'jpg'))
+ {
+ $endtime = get_moment();
+ $info['time'] = ($endtime - $starttime) * 1000;
+ array_push($infos, $info);
+ array_push($times, $info['time']);
+ array_push($thumbnalized, $path);
+ $num++;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if (count($infos) > 0)
+ {
+ $sum = array_sum($times);
+ $average = $sum / count($times);
+ sort($times, SORT_NUMERIC);
+ $max = array_pop($times);
+ if (count($thumbnalized) == 1)
+ {
+ $min = $max;
+ }
+ else
+ {
+ $min = array_shift($times);
+ }
+
+ $tpl_var =
+ array(
+ 'TN_NB'=>count($infos),
+ 'TN_TOTAL'=>number_format($sum, 2, '.', ' ').' ms',
+ 'TN_MAX'=>number_format($max, 2, '.', ' ').' ms',
+ 'TN_MIN'=>number_format($min, 2, '.', ' ').' ms',
+ 'TN_AVERAGE'=>number_format($average, 2, '.', ' ').' ms',
+ 'elements' => array()
+ );
+
+ foreach ($infos as $i => $info)
+ {
+ $tpl_var['elements'][] =
+ array(
+ 'PATH'=>$info['path'],
+ 'TN_FILE_IMG'=>$info['tn_file'],
+ 'TN_FILESIZE_IMG'=>$info['tn_size'],
+ 'TN_WIDTH_IMG'=>$info['tn_width'],
+ 'TN_HEIGHT_IMG'=>$info['tn_height'],
+ 'GEN_TIME'=>number_format($info['time'], 2, '.', ' ').' ms',
+ );
+ }
+ $template->assign('results', $tpl_var);
+ }
+ }
+}
+// +-----------------------------------------------------------------------+
+// | form & pictures without thumbnails display |
+// +-----------------------------------------------------------------------+
+$remainings = array_diff($wo_thumbnails, $thumbnalized);
+
+if (count($remainings) > 0)
+{
+ $form_url = get_root_url().'admin.php?page=thumbnail';
+ $gd = !empty($_POST['gd']) ? $_POST['gd'] : 2;
+ $width = !empty($_POST['width']) ? $_POST['width'] : $conf['tn_width'];
+ $height = !empty($_POST['height']) ? $_POST['height'] : $conf['tn_height'];
+ $n = !empty($_POST['n']) ? $_POST['n'] : 5;
+
+ $template->assign(
+ 'params',
+ array(
+ 'F_ACTION'=> $form_url,
+ 'GD_SELECTED' => $gd,
+ 'N_SELECTED' => $n,
+ 'WIDTH_TN'=>$width,
+ 'HEIGHT_TN'=>$height
+ ));
+
+ $template->assign(
+ 'TOTAL_NB_REMAINING',
+ count($remainings));
+
+ foreach ($remainings as $path)
+ {
+ list($width, $height) = getimagesize($path);
+ $size = floor(filesize($path) / 1024).' KB';
+
+ $template->append(
+ 'remainings',
+ array(
+ 'PATH'=>$path,
+ 'FILESIZE_IMG'=>$size,
+ 'WIDTH_IMG'=>$width,
+ 'HEIGHT_IMG'=>$height,
+ ));
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | return to admin |
+// +-----------------------------------------------------------------------+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'thumbnail');
+?>
diff --git a/BSF/admin/upload.php b/BSF/admin/upload.php
new file mode 100644
index 000000000..6e258f05a
--- /dev/null
+++ b/BSF/admin/upload.php
@@ -0,0 +1,209 @@
+ 0)
+ {
+ $query = '
+UPDATE '.WAITING_TABLE.'
+ SET validated = \'true\'
+ WHERE id IN ('.implode(',', $to_validate).')
+;';
+ pwg_query($query);
+
+ array_push(
+ $page['infos'],
+ sprintf(
+ l10n('%d waiting pictures validated'),
+ count($to_validate)
+ )
+ );
+ }
+
+ if (count($to_reject) > 0)
+ {
+ // The uploaded element was refused, we have to delete its reference in
+ // the database and to delete the element as well.
+ $query = '
+SELECT id, storage_category_id, file, tn_ext
+ FROM '.WAITING_TABLE.'
+ WHERE id IN ('.implode(',', $to_reject).')
+;';
+ $result = pwg_query($query);
+ while($row = mysql_fetch_array($result))
+ {
+ $dir = get_complete_dir($row['storage_category_id']);
+ unlink($dir.$row['file']);
+ $element_info = array(
+ 'path' => $dir.$row['file'],
+ 'tn_ext' =>
+ (isset($row['tn_ext']) and $row['tn_ext']!='') ? $row['tn_ext']:'jpg'
+ );
+ $tn_path = get_thumbnail_path( $element_info );
+
+ if ( @is_file($tn_path) )
+ {
+ unlink( $tn_path );
+ }
+ }
+
+ $query = '
+DELETE
+ FROM '.WAITING_TABLE.'
+ WHERE id IN ('.implode(',', $to_reject).')
+;';
+ pwg_query($query);
+
+ array_push(
+ $page['infos'],
+ sprintf(
+ l10n('%d waiting pictures rejected'),
+ count($to_reject)
+ )
+ );
+ }
+}
+
+//----------------------------------------------------- template initialization
+$template->set_filenames(array('upload'=>'admin/upload.tpl'));
+
+// TabSheet initialization
+waiting_tabsheet();
+
+$template->assign(array(
+ 'F_ACTION'=>str_replace( '&', '&', $_SERVER['REQUEST_URI'])
+ ));
+
+//---------------------------------------------------------------- form display
+$cat_names = array();
+$list = array();
+
+$query = 'SELECT * FROM '.WAITING_TABLE;
+$query.= " WHERE validated = 'false'";
+$query.= ' ORDER BY storage_category_id';
+$query.= ';';
+$result = pwg_query( $query );
+while ( $row = mysql_fetch_array( $result ) )
+{
+ if ( !isset( $cat_names[$row['storage_category_id']] ) )
+ {
+ $cat = get_cat_info( $row['storage_category_id'] );
+ $cat_names[$row['storage_category_id']] = array();
+ $cat_names[$row['storage_category_id']]['dir'] =
+ PHPWG_ROOT_PATH.get_complete_dir( $row['storage_category_id'] );
+ $cat_names[$row['storage_category_id']]['display_name'] =
+ get_cat_display_name($cat['upper_names']);
+ }
+ $preview_url = PHPWG_ROOT_PATH.$cat_names[$row['storage_category_id']]['dir'].$row['file'];
+
+ $tpl_var =
+ array(
+ 'CATEGORY_IMG'=>$cat_names[$row['storage_category_id']]['display_name'],
+ 'ID_IMG'=>$row['id'],
+ 'DATE_IMG' => date('Y-m-d H:i:s', $row['date']),
+ 'FILE_TITLE'=>$row['file'],
+ 'FILE_IMG' =>
+ (strlen($row['file']) > 10) ?
+ (substr($row['file'], 0, 10)).'...' : $row['file'],
+ 'PREVIEW_URL_IMG'=>$preview_url,
+ 'UPLOAD_EMAIL'=>get_email_address_as_display_text($row['mail_address']),
+ 'UPLOAD_USERNAME'=>$row['username']
+ );
+
+ // is there an existing associated thumnail ?
+ if ( !empty( $row['tn_ext'] ))
+ {
+ $thumbnail = $conf['prefix_thumbnail'];
+ $thumbnail.= get_filename_wo_extension( $row['file'] );
+ $thumbnail.= '.'.$row['tn_ext'];
+ $url = $cat_names[$row['storage_category_id']]['dir'];
+ $url.= 'thumbnail/'.$thumbnail;
+
+ $tpl_var['thumbnail'] =
+ array(
+ 'PREVIEW_URL_TN_IMG' => $url,
+ 'FILE_TN_IMG' =>
+ (strlen($thumbnail) > 10) ?
+ (substr($thumbnail, 0, 10)).'...' : $thumbnail,
+ 'FILE_TN_TITLE' => $thumbnail
+ );
+ }
+ $template->append('pictures', $tpl_var);
+ array_push($list, $row['id']);
+}
+
+$template->assign('LIST',implode(',', $list) );
+
+//----------------------------------------------------------- sending html code
+$template->assign_var_from_handle('ADMIN_CONTENT', 'upload');
+?>
diff --git a/BSF/admin/user_list.php b/BSF/admin/user_list.php
new file mode 100644
index 000000000..635424b6a
--- /dev/null
+++ b/BSF/admin/user_list.php
@@ -0,0 +1,688 @@
+ 0';
+ if (isset($filter['username']))
+ {
+ $query.= '
+ AND u.'.$conf['user_fields']['username'].' LIKE \''.$filter['username'].'\'';
+ }
+ if (isset($filter['group']))
+ {
+ $query.= '
+ AND ug.group_id = '.$filter['group'];
+ }
+ if (isset($filter['status']))
+ {
+ $query.= '
+ AND ui.status = \''.$filter['status']."'";
+ }
+ $query.= '
+ ORDER BY '.$order_by.' '.$direction.'
+;';
+
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ {
+ $user = $row;
+ $user['groups'] = array();
+
+ array_push($users, $user);
+ }
+
+ // add group lists
+ $user_ids = array();
+ foreach ($users as $i => $user)
+ {
+ $user_ids[$i] = $user['id'];
+ }
+ $user_nums = array_flip($user_ids);
+
+ if (count($user_ids) > 0)
+ {
+ $query = '
+SELECT user_id, group_id
+ FROM '.USER_GROUP_TABLE.'
+ WHERE user_id IN ('.implode(',', $user_ids).')
+;';
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push(
+ $users[$user_nums[$row['user_id']]]['groups'],
+ $row['group_id']
+ );
+ }
+ }
+
+ return $users;
+}
+
+// +-----------------------------------------------------------------------+
+// | initialization |
+// +-----------------------------------------------------------------------+
+
+if (!defined('PHPWG_ROOT_PATH'))
+{
+ die('Hacking attempt!');
+}
+
+include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
+
+// +-----------------------------------------------------------------------+
+// | Check Access and exit when user status is not ok |
+// +-----------------------------------------------------------------------+
+check_status(ACCESS_ADMINISTRATOR);
+
+$page['order_by_items'] = array(
+ 'id' => l10n('registration_date'),
+ 'username' => l10n('Username'),
+ 'level' => l10n('Privacy level'),
+ 'language' => l10n('language'),
+ );
+
+$page['direction_items'] = array(
+ 'asc' => l10n('ascending'),
+ 'desc' => l10n('descending')
+ );
+
+// +-----------------------------------------------------------------------+
+// | add a user |
+// +-----------------------------------------------------------------------+
+
+if (isset($_POST['submit_add']))
+{
+ $page['errors'] = register_user(
+ $_POST['login'], $_POST['password'], $_POST['email'], false);
+
+ if (count($page['errors']) == 0)
+ {
+ array_push(
+ $page['infos'],
+ sprintf(
+ l10n('user "%s" added'),
+ $_POST['login']
+ )
+ );
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | user list |
+// +-----------------------------------------------------------------------+
+
+$page['filtered_users'] = get_filtered_user_list();
+
+// +-----------------------------------------------------------------------+
+// | selected users |
+// +-----------------------------------------------------------------------+
+
+if (isset($_POST['delete']) or isset($_POST['pref_submit']))
+{
+ $collection = array();
+
+ switch ($_POST['target'])
+ {
+ case 'all' :
+ {
+ foreach($page['filtered_users'] as $local_user)
+ {
+ array_push($collection, $local_user['id']);
+ }
+ break;
+ }
+ case 'selection' :
+ {
+ if (isset($_POST['selection']))
+ {
+ $collection = $_POST['selection'];
+ }
+ break;
+ }
+ }
+
+ if (count($collection) == 0)
+ {
+ array_push($page['errors'], l10n('Select at least one user'));
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | delete users |
+// +-----------------------------------------------------------------------+
+if (isset($_POST['delete']) and count($collection) > 0)
+{
+ if (in_array($conf['guest_id'], $collection))
+ {
+ array_push($page['errors'], l10n('Guest cannot be deleted'));
+ }
+ if (($conf['guest_id'] != $conf['default_user_id']) and
+ in_array($conf['default_user_id'], $collection))
+ {
+ array_push($page['errors'], l10n('Default user cannot be deleted'));
+ }
+ if (in_array($conf['webmaster_id'], $collection))
+ {
+ array_push($page['errors'], l10n('Webmaster cannot be deleted'));
+ }
+ if (in_array($user['id'], $collection))
+ {
+ array_push($page['errors'], l10n('You cannot delete your account'));
+ }
+
+ if (count($page['errors']) == 0)
+ {
+ if (isset($_POST['confirm_deletion']) and 1 == $_POST['confirm_deletion'])
+ {
+ foreach ($collection as $user_id)
+ {
+ delete_user($user_id);
+ }
+ array_push(
+ $page['infos'],
+ l10n_dec(
+ '%d user deleted', '%d users deleted',
+ count($collection)
+ )
+ );
+ foreach ($page['filtered_users'] as $filter_key => $filter_user)
+ {
+ if (in_array($filter_user['id'], $collection))
+ {
+ unset($page['filtered_users'][$filter_key]);
+ }
+ }
+ }
+ else
+ {
+ array_push($page['errors'], l10n('You need to confirm deletion'));
+ }
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | preferences form submission |
+// +-----------------------------------------------------------------------+
+
+if (isset($_POST['pref_submit']) and count($collection) > 0)
+{
+ if (-1 != $_POST['associate'])
+ {
+ $datas = array();
+
+ $query = '
+SELECT user_id
+ FROM '.USER_GROUP_TABLE.'
+ WHERE group_id = '.$_POST['associate'].'
+;';
+ $associated = array_from_query($query, 'user_id');
+
+ $associable = array_diff($collection, $associated);
+
+ if (count($associable) > 0)
+ {
+ foreach ($associable as $item)
+ {
+ array_push($datas,
+ array('group_id'=>$_POST['associate'],
+ 'user_id'=>$item));
+ }
+
+ mass_inserts(USER_GROUP_TABLE,
+ array('group_id', 'user_id'),
+ $datas);
+ }
+ }
+
+ if (-1 != $_POST['dissociate'])
+ {
+ $query = '
+DELETE FROM '.USER_GROUP_TABLE.'
+ WHERE group_id = '.$_POST['dissociate'].'
+ AND user_id IN ('.implode(',', $collection).')
+';
+ pwg_query($query);
+ }
+
+ // properties to set for the collection (a user list)
+ $datas = array();
+ $dbfields = array('primary' => array('user_id'), 'update' => array());
+
+ $formfields =
+ array('nb_image_line', 'nb_line_page', 'template', 'language',
+ 'recent_period', 'maxwidth', 'expand', 'show_nb_comments',
+ 'show_nb_hits', 'maxheight', 'status', 'enabled_high',
+ 'level');
+
+ $true_false_fields = array('expand', 'show_nb_comments',
+ 'show_nb_hits', 'enabled_high');
+ if ($conf['allow_adviser'])
+ {
+ array_push($formfields, 'adviser');
+ array_push($true_false_fields, 'adviser');
+ }
+
+ foreach ($formfields as $formfield)
+ {
+ // special for true/false fields
+ if (in_array($formfield, $true_false_fields))
+ {
+ $test = $formfield;
+ }
+ else
+ {
+ $test = $formfield.'_action';
+ }
+
+ if ($_POST[$test] != 'leave')
+ {
+ array_push($dbfields['update'], $formfield);
+ }
+ }
+
+ // updating elements is useful only if needed...
+ if (count($dbfields['update']) > 0)
+ {
+ $datas = array();
+
+ foreach ($collection as $user_id)
+ {
+ $data = array();
+ $data['user_id'] = $user_id;
+
+ // TODO : verify if submited values are semanticaly correct
+ foreach ($dbfields['update'] as $dbfield)
+ {
+ // if the action is 'unset', the key won't be in row and
+ // mass_updates function will set this field to NULL
+ if (in_array($dbfield, $true_false_fields)
+ or 'set' == $_POST[$dbfield.'_action'])
+ {
+ $data[$dbfield] = $_POST[$dbfield];
+ }
+ }
+
+ // special users checks
+ if
+ (
+ ($conf['webmaster_id'] == $user_id) or
+ ($conf['guest_id'] == $user_id) or
+ ($conf['default_user_id'] == $user_id)
+ )
+ {
+ // status must not be changed
+ if (isset($data['status']))
+ {
+ if ($conf['webmaster_id'] == $user_id)
+ {
+ $data['status'] = 'webmaster';
+ }
+ else
+ {
+ $data['status'] = 'guest';
+ }
+ }
+
+ // could not be adivser
+ if (isset($data['adviser']))
+ {
+ $data['adviser'] = 'false';
+ }
+ }
+
+ array_push($datas, $data);
+ }
+
+ mass_updates(USER_INFOS_TABLE, $dbfields, $datas);
+ }
+
+ redirect(
+ get_root_url().
+ 'admin.php'.
+ get_query_string_diff(array(), false)
+ );
+}
+
+// +-----------------------------------------------------------------------+
+// | groups list |
+// +-----------------------------------------------------------------------+
+
+$groups[-1] = '------------';
+
+$query = '
+SELECT id, name
+ FROM '.GROUPS_TABLE.'
+ ORDER BY name ASC
+;';
+$result = pwg_query($query);
+
+while ($row = mysql_fetch_array($result))
+{
+ $groups[$row['id']] = $row['name'];
+}
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+
+$template->set_filenames(array('user_list'=>'admin/user_list.tpl'));
+
+$base_url = PHPWG_ROOT_PATH.'admin.php?page=user_list';
+
+if (isset($_GET['start']) and is_numeric($_GET['start']))
+{
+ $start = $_GET['start'];
+}
+else
+{
+ $start = 0;
+}
+
+$template->assign(
+ array(
+ 'U_HELP' => get_root_url().'popuphelp.php?page=user_list',
+
+ 'F_ADD_ACTION' => $base_url,
+ 'F_USERNAME' => @htmlentities($_GET['username']),
+ 'F_FILTER_ACTION' => get_root_url().'admin.php'
+ ));
+
+// Hide radio-button if not allow to assign adviser
+if ($conf['allow_adviser'])
+{
+ $template->assign('adviser', true);
+}
+
+// Filter status options
+$status_options[-1] = '------------';
+foreach (get_enums(USER_INFOS_TABLE, 'status') as $status)
+{
+ $status_options[$status] = l10n('user_status_'.$status);
+}
+$template->assign('status_options', $status_options);
+$template->assign('status_selected',
+ isset($_GET['status']) ? $_GET['status'] : '');
+
+// Filter group options
+$template->assign('group_options', $groups);
+$template->assign('group_selected',
+ isset($_GET['group']) ? $_GET['group'] : '');
+
+// Filter order options
+$template->assign('order_options', $page['order_by_items']);
+$template->assign('order_selected',
+ isset($_GET['order_by']) ? $_GET['order_by'] : '');
+
+// Filter direction options
+$template->assign('direction_options', $page['direction_items']);
+$template->assign('direction_selected',
+ isset($_GET['direction']) ? $_GET['direction'] : '');
+
+
+if (isset($_POST['pref_submit']))
+{
+ $template->assign(
+ array(
+ 'NB_IMAGE_LINE' => $_POST['nb_image_line'],
+ 'NB_LINE_PAGE' => $_POST['nb_line_page'],
+ 'MAXWIDTH' => $_POST['maxwidth'],
+ 'MAXHEIGHT' => $_POST['maxheight'],
+ 'RECENT_PERIOD' => $_POST['recent_period'],
+ ));
+}
+else
+{
+ $default_user = get_default_user_info(true);
+ $template->assign(
+ array(
+ 'NB_IMAGE_LINE' => $default_user['nb_image_line'],
+ 'NB_LINE_PAGE' => $default_user['nb_line_page'],
+ 'MAXWIDTH' => $default_user['maxwidth'],
+ 'MAXHEIGHT' => $default_user['maxheight'],
+ 'RECENT_PERIOD' => $default_user['recent_period'],
+ ));
+}
+
+// Template Options
+$template->assign('template_options', get_pwg_themes());
+$template->assign('template_selected',
+ isset($_POST['pref_submit']) ? $_POST['template'] : get_default_template());
+
+// Language options
+$template->assign('language_options', get_languages());
+$template->assign('language_selected',
+ isset($_POST['pref_submit']) ? $_POST['language'] : get_default_language());
+
+// Status options
+foreach (get_enums(USER_INFOS_TABLE, 'status') as $status)
+{
+ // Only status <= can be assign
+ if (is_autorize_status(get_access_type_status($status)))
+ {
+ $pref_status_options[$status] = l10n('user_status_'.$status);
+ }
+}
+$template->assign('pref_status_options', $pref_status_options);
+$template->assign('pref_status_selected',
+ isset($_POST['pref_submit']) ? $_POST['status'] : 'normal');
+
+// associate and dissociate options
+$template->assign('association_options', $groups);
+$template->assign('associate_selected',
+ isset($_POST['pref_submit']) ? $_POST['associate'] : '');
+$template->assign('dissociate_selected',
+ isset($_POST['pref_submit']) ? $_POST['dissociate'] : '');
+
+
+// user level options
+foreach ($conf['available_permission_levels'] as $level)
+{
+ $level_options[$level] = l10n(sprintf('Level %d', $level));
+}
+$template->assign('level_options', $level_options);
+$template->assign('level_selected',
+ isset($_POST['pref_submit']) ? $_POST['level'] : $default_user['level']);
+
+// +-----------------------------------------------------------------------+
+// | navigation bar |
+// +-----------------------------------------------------------------------+
+
+$url = PHPWG_ROOT_PATH.'admin.php'.get_query_string_diff(array('start'));
+
+$navbar = create_navigation_bar(
+ $url,
+ count($page['filtered_users']),
+ $start,
+ $conf['users_page']
+ );
+
+$template->assign('NAVBAR', $navbar);
+
+// +-----------------------------------------------------------------------+
+// | user list |
+// +-----------------------------------------------------------------------+
+
+$profile_url = get_root_url().'admin.php?page=profile&user_id=';
+$perm_url = get_root_url().'admin.php?page=user_perm&user_id=';
+
+$visible_user_list = array();
+foreach ($page['filtered_users'] as $num => $local_user)
+{
+ // simulate LIMIT $start, $conf['users_page']
+ if ($num < $start)
+ {
+ continue;
+ }
+ if ($num >= $start + $conf['users_page'])
+ {
+ break;
+ }
+
+ $visible_user_list[] = $local_user;
+}
+
+// allow plugins to fill template var plugin_user_list_column_titles and
+// plugin_columns/plugin_actions for each user in the list
+$visible_user_list = trigger_event('loc_visible_user_list', $visible_user_list);
+
+foreach ($visible_user_list as $local_user)
+{
+ $groups_string = preg_replace(
+ '/(\d+)/e',
+ "\$groups['$1']",
+ implode(
+ ', ',
+ $local_user['groups']
+ )
+ );
+
+ if (isset($_POST['pref_submit'])
+ and isset($_POST['selection'])
+ and in_array($local_user['id'], $_POST['selection']))
+ {
+ $checked = 'checked="checked"';
+ }
+ else
+ {
+ $checked = '';
+ }
+
+ $properties = array();
+ if ( $local_user['level'] != 0 )
+ {
+ $properties[] = l10n( sprintf('Level %d', $local_user['level']) );
+ }
+ $properties[] =
+ (isset($local_user['enabled_high']) and ($local_user['enabled_high'] == 'true'))
+ ? l10n('is_high_enabled') : l10n('is_high_disabled');
+
+ $template->append(
+ 'users',
+ array(
+ 'ID' => $local_user['id'],
+ 'CHECKED' => $checked,
+ 'U_PROFILE' => $profile_url.$local_user['id'],
+ 'U_PERM' => $perm_url.$local_user['id'],
+ 'USERNAME' => $local_user['username']
+ .($local_user['id'] == $conf['guest_id']
+ ? ' ['.l10n('is_the_guest').']' : '')
+ .($local_user['id'] == $conf['default_user_id']
+ ? ' ['.l10n('is_the_default').']' : ''),
+ 'STATUS' => l10n('user_status_'.
+ $local_user['status']).(($local_user['adviser'] == 'true')
+ ? ' ['.l10n('adviser').']' : ''),
+ 'EMAIL' => get_email_address_as_display_text($local_user['email']),
+ 'GROUPS' => $groups_string,
+ 'PROPERTIES' => implode( ', ', $properties),
+ 'plugin_columns' => isset($local_user['plugin_columns']) ? $local_user['plugin_columns'] : array(),
+ 'plugin_actions' => isset($local_user['plugin_actions']) ? $local_user['plugin_actions'] : array(),
+ )
+ );
+}
+
+// +-----------------------------------------------------------------------+
+// | html code display |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'user_list');
+?>
diff --git a/BSF/admin/user_perm.php b/BSF/admin/user_perm.php
new file mode 100644
index 000000000..f7002455a
--- /dev/null
+++ b/BSF/admin/user_perm.php
@@ -0,0 +1,223 @@
+ 0)
+{
+ // if you forbid access to a category, all sub-categories become
+ // automatically forbidden
+ $subcats = get_subcat_ids($_POST['cat_true']);
+ $query = '
+DELETE FROM '.USER_ACCESS_TABLE.'
+ WHERE user_id = '.$page['user'].'
+ 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 = mysql_fetch_array($result))
+ {
+ array_push($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 '.USER_ACCESS_TABLE.'
+ WHERE user_id = '.$page['user'].'
+;';
+ $result = pwg_query($query);
+
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push($authorized_ids, $row['cat_id']);
+ }
+
+ $inserts = array();
+ $to_autorize_ids = array_diff($private_uppercats, $authorized_ids);
+ foreach ($to_autorize_ids as $to_autorize_id)
+ {
+ array_push($inserts, array('user_id' => $page['user'],
+ 'cat_id' => $to_autorize_id));
+ }
+
+ mass_inserts(USER_ACCESS_TABLE, array('user_id','cat_id'), $inserts);
+}
+
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+
+$template->set_filenames(
+ array(
+ 'user_perm' => 'admin/user_perm.tpl',
+ 'double_select' => 'admin/double_select.tpl'
+ )
+ );
+
+$template->assign(
+ array(
+ 'TITLE' =>
+ sprintf(
+ l10n('Manage permissions for user "%s"'),
+ get_username($page['user']
+ )
+ ),
+ 'L_CAT_OPTIONS_TRUE'=>l10n('authorized'),
+ 'L_CAT_OPTIONS_FALSE'=>l10n('forbidden'),
+
+ 'F_ACTION' =>
+ PHPWG_ROOT_PATH.
+ 'admin.php?page=user_perm'.
+ '&user_id='.$page['user']
+ )
+ );
+
+
+// retrieve category ids authorized to the groups the user belongs to
+$group_authorized = array();
+
+$query = '
+SELECT DISTINCT cat_id, c.uppercats, c.global_rank
+ FROM '.USER_GROUP_TABLE.' AS ug
+ INNER JOIN '.GROUP_ACCESS_TABLE.' AS ga
+ ON ug.group_id = ga.group_id
+ INNER JOIN '.CATEGORIES_TABLE.' AS c
+ ON c.id = ga.cat_id
+ WHERE ug.user_id = '.$page['user'].'
+;';
+$result = pwg_query($query);
+
+if (mysql_num_rows($result) > 0)
+{
+ $cats = array();
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push($cats, $row);
+ array_push($group_authorized, $row['cat_id']);
+ }
+ usort($cats, 'global_rank_compare');
+
+ foreach ($cats as $category)
+ {
+ $template->append(
+ 'categories_because_of_groups',
+ get_cat_display_name_cache($category['uppercats'], null, false)
+ );
+ }
+}
+
+// only private categories are listed
+$query_true = '
+SELECT id,name,uppercats,global_rank
+ FROM '.CATEGORIES_TABLE.' INNER JOIN '.USER_ACCESS_TABLE.' ON cat_id = id
+ WHERE status = \'private\'
+ AND user_id = '.$page['user'];
+if (count($group_authorized) > 0)
+{
+ $query_true.= '
+ AND cat_id NOT IN ('.implode(',', $group_authorized).')';
+}
+$query_true.= '
+;';
+display_select_cat_wrapper($query_true,array(),'category_option_true');
+
+$result = pwg_query($query_true);
+$authorized_ids = array();
+while ($row = mysql_fetch_array($result))
+{
+ array_push($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).')';
+}
+if (count($group_authorized) > 0)
+{
+ $query_false.= '
+ AND id NOT IN ('.implode(',', $group_authorized).')';
+}
+$query_false.= '
+;';
+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', 'user_perm');
+?>
diff --git a/BSF/admin/ws_checker.php b/BSF/admin/ws_checker.php
new file mode 100644
index 000000000..7da8fac10
--- /dev/null
+++ b/BSF/admin/ws_checker.php
@@ -0,0 +1,334 @@
+ $id)
+ {
+ $next_less_1 = (isset($result[$k + 1]))? $result[$k + 1] - 1:-1;
+ if ( $id == $next_less_1 and end($serial)=='-' )
+ { // nothing to do
+ }
+ elseif ( $id == $next_less_1 )
+ {
+ $serial[]=$id;
+ $serial[]='-';
+ }
+ else
+ {
+ $serial[]=$id; // end serie or non serie
+ }
+ }
+ $null = array_shift($serial); // remove first value
+ $list .= array_shift($serial); // add the real first one
+ $separ = ',';
+ foreach ($serial as $id)
+ {
+ $list .= ($id=='-') ? '' : $separ . $id;
+ $separ = ($id=='-') ? '-':','; // add comma except if hyphen
+ }
+ }
+ return $list;
+}
+
+// +-----------------------------------------------------------------------+
+// | Check Access and exit when user status is not ok |
+// +-----------------------------------------------------------------------+
+check_status(ACCESS_ADMINISTRATOR);
+
+// accepted queries
+$req_type_list = official_req();
+
+//--------------------------------------------------------- update informations
+$chk_partner = '';
+// Is a new access required?
+
+if (isset($_POST['wsa_submit']))
+{
+// Check $_post (Some values are commented - maybe a future use)
+$add_partner = htmlspecialchars( $_POST['add_partner'], ENT_QUOTES);
+$add_target = check_target( $_POST['add_target']) ;
+$add_end = ( is_numeric($_POST['add_end']) ) ? $_POST['add_end']:0;
+$add_request = htmlspecialchars( $_POST['add_request'], ENT_QUOTES);
+$add_limit = ( is_numeric($_POST['add_limit']) ) ? $_POST['add_limit']:1;
+$add_comment = htmlspecialchars( $_POST['add_comment'], ENT_QUOTES);
+if ( strlen($add_partner) < 8 )
+{ // TODO What? Complete with some MD5...
+}
+ $query = '
+INSERT INTO '.WEB_SERVICES_ACCESS_TABLE.'
+( `name` , `access` , `start` , `end` , `request` , `limit` , `comment` )
+VALUES (' . "
+ '$add_partner', '$add_target',
+ NOW(),
+ ADDDATE( NOW(), INTERVAL $add_end DAY),
+ '$add_request', '$add_limit', '$add_comment' );";
+
+ pwg_query($query);
+ $chk_partner = $add_partner;
+
+ $template->append(
+ 'update_results',
+ l10n('ws_adding_legend').l10n('ws_success_upd')
+ );
+}
+
+// Next, Update selected access
+if (isset($_POST['wsu_submit']))
+{
+ $upd_end = ( is_numeric($_POST['upd_end']) ) ? $_POST['upd_end']:0;
+ $settxt = ' end = ADDDATE(NOW(), INTERVAL '. $upd_end .' DAY)';
+
+ if ((isset($_POST['selection'])) and (trim($settxt) != ''))
+ {
+ $uid = (int) $_POST['selection'];
+ $query = '
+ UPDATE '.WEB_SERVICES_ACCESS_TABLE.'
+ SET '.$settxt.'
+ WHERE id = '.$uid.'; ';
+ pwg_query($query);
+ $template->append(
+ 'update_results',
+ l10n('ws_update_legend').l10n('ws_success_upd')
+ );
+ } else {
+ $template->append(
+ 'update_results',
+ l10n('ws_update_legend').l10n('ws_failed_upd')
+ );
+ }
+}
+// Next, Delete selected access
+
+if (isset($_POST['wsX_submit']))
+{
+ if ((isset($_POST['delete_confirmation']))
+ and (isset($_POST['selection'])))
+ {
+ $uid = (int) $_POST['selection'];
+ $query = 'DELETE FROM '.WEB_SERVICES_ACCESS_TABLE.'
+ WHERE id = '.$uid.'; ';
+ pwg_query($query);
+ $template->append(
+ 'update_results',
+ l10n('ws_delete_legend').l10n('ws_success_upd')
+ );
+ } else {
+ $template->append(
+ 'update_results',
+ l10n('Not selected / Not confirmed').l10n('ws_failed_upd')
+ );
+ }
+}
+
+
+
+$template->assign(
+ array(
+ 'U_HELP' => get_root_url().'popuphelp.php?page=web_service',
+ )
+ );
+
+// Build where
+$where = '';
+$order = ' ORDER BY `id` DESC' ;
+
+$query = '
+SELECT *
+ FROM '.WEB_SERVICES_ACCESS_TABLE.'
+WHERE 1=1 '
+.$where.
+' '
+.$order.
+';';
+$result = pwg_query($query);
+$acc_list = mysql_num_rows($result);
+$result = pwg_query($query);
+// +-----------------------------------------------------------------------+
+// | template init |
+// +-----------------------------------------------------------------------+
+
+$template->set_filenames(
+ array(
+ 'ws_checker' => 'admin/ws_checker.tpl'
+ )
+ );
+
+
+// Access List
+while ($row = mysql_fetch_array($result))
+{
+ $chk_partner = ( $chk_partner == '' ) ? $row['name'] : $chk_partner;
+ $template->append(
+ 'access_list',
+ array(
+ 'ID' => $row['id'],
+ 'NAME' =>
+ (is_adviser()) ? '*********' : $row['name'],
+ 'TARGET' => $row['access'],
+ 'END' => $row['end'],
+ 'REQUEST' => $row['request'],
+ 'LIMIT' => $row['limit'],
+ 'COMMENT' => $row['comment'],
+ )
+ );
+}
+
+$template->assign('add_requests', $req_type_list);
+
+$template->assign('add_limits', $conf['ws_allowed_limit'] );
+
+// Postponed Start Date
+// By default 0, 1, 2, 3, 5, 7, 14 or 30 days
+/*foreach ($conf['ws_postponed_start'] as $value) {
+ $template->assign_block_vars(
+ 'add_start',
+ array(
+ 'VALUE'=> $value,
+ 'CONTENT' => $value,
+ 'SELECTED' => ($conf['ws_postponed_start'][0] == $value) ? $selected:'',
+ )
+ );
+}*/
+
+// Durations (Allowed Web Services Period)
+// By default 10, 5, 2, 1 year(s) or 6, 3, 1 month(s) or 15, 10, 7, 5, 1, 0 day(s)
+$template->assign('add_ends', $conf['ws_durations']);
+
+if ( $chk_partner !== '' )
+{
+ if (function_exists('curl_init'))
+ {
+ $request = get_absolute_root_url().'ws.php?method=pwg.getVersion&format=rest&'
+ . "partner=$chk_partner" ;
+ $session = curl_init($request);
+ curl_setopt ($session, CURLOPT_POST, true);
+ curl_setopt($session, CURLOPT_HEADER, true);
+ curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
+ $response = curl_exec($session);
+ curl_close($session);
+ $status_code = array();
+ preg_match('/\d\d\d/', $response, $status_code);
+ switch( $status_code[0] ) {
+ case 200:
+ $ws_status = l10n('Web Services under control');
+ break;
+ case 503:
+ $ws_status = 'Piwigo Web Services failed and returned an '
+ . 'HTTP status of 503. Service is unavailable. An internal '
+ . 'problem prevented us from returning data to you.';
+ break;
+ case 403:
+ $ws_status = 'Piwigo Web Services failed and returned an '
+ . 'HTTP status of 403. Access is forbidden. You do not have '
+ . 'permission to access this resource, or are over '
+ . 'your rate limit.';
+ break;
+ case 400:
+ // You may want to fall through here and read the specific XML error
+ $ws_status = 'Piwigo Web Services failed and returned an '
+ . 'HTTP status of 400. Bad request. The parameters passed '
+ . 'to the service did not match as expected. The exact '
+ . 'error is returned in the XML response.';
+ break;
+ default:
+ $ws_status = 'Piwigo Web Services returned an unexpected HTTP '
+ . 'status of:' . $status_code[0];
+ }
+ }
+ else
+ {
+ $ws_status = 'Cannot check - curl not installed';
+ }
+ $template->assign( 'WS_STATUS', $ws_status );
+}
+
+//----------------------------------------------------------- sending html code
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'ws_checker');
+
+include_once(PHPWG_ROOT_PATH.'include/ws_core.inc.php');
+?>
diff --git a/BSF/category.php b/BSF/category.php
new file mode 100644
index 000000000..303cc903f
--- /dev/null
+++ b/BSF/category.php
@@ -0,0 +1,65 @@
+
diff --git a/BSF/comments.php b/BSF/comments.php
new file mode 100644
index 000000000..555986328
--- /dev/null
+++ b/BSF/comments.php
@@ -0,0 +1,394 @@
+ l10n('descending'),
+ 'ASC' => l10n('ascending')
+ );
+
+// sort_by : database fields proposed for sorting comments list
+$sort_by = array(
+ 'date' => l10n('comment date'),
+ 'image_id' => l10n('picture')
+ );
+
+// items_number : list of number of items to display per page
+$items_number = array(5,10,20,50,'all');
+
+// since when display comments ?
+//
+$since_options = array(
+ 1 => array('label' => l10n('today'),
+ 'clause' => 'date > SUBDATE(CURDATE(), INTERVAL 1 DAY)'),
+ 2 => array('label' => sprintf(l10n('last %d days'), 7),
+ 'clause' => 'date > SUBDATE(CURDATE(), INTERVAL 7 DAY)'),
+ 3 => array('label' => sprintf(l10n('last %d days'), 30),
+ 'clause' => 'date > SUBDATE(CURDATE(), INTERVAL 30 DAY)'),
+ 4 => array('label' => l10n('the beginning'),
+ 'clause' => '1=1') // stupid but generic
+ );
+
+$page['since'] = isset($_GET['since']) ? $_GET['since'] : 4;
+
+// on which field sorting
+//
+$page['sort_by'] = 'date';
+// if the form was submitted, it overloads default behaviour
+if (isset($_GET['sort_by']))
+{
+ $page['sort_by'] = $_GET['sort_by'];
+}
+
+// order to sort
+//
+$page['sort_order'] = 'DESC';
+// if the form was submitted, it overloads default behaviour
+if (isset($_GET['sort_order']))
+{
+ $page['sort_order'] = $_GET['sort_order'];
+}
+
+// number of items to display
+//
+$page['items_number'] = 10;
+if (isset($_GET['items_number']))
+{
+ $page['items_number'] = $_GET['items_number'];
+}
+
+$page['where_clauses'] = array();
+
+// which category to filter on ?
+if (isset($_GET['cat']) and 0 != $_GET['cat'])
+{
+ $page['where_clauses'][] =
+ 'category_id IN ('.implode(',', get_subcat_ids(array($_GET['cat']))).')';
+}
+
+// search a particular author
+if (isset($_GET['author']) and !empty($_GET['author']))
+{
+ $page['where_clauses'][] = 'com.author = \''.$_GET['author'].'\'';
+}
+
+// search a substring among comments content
+if (isset($_GET['keyword']) and !empty($_GET['keyword']))
+{
+ $page['where_clauses'][] =
+ '('.
+ implode(' AND ',
+ array_map(
+ create_function(
+ '$s',
+ 'return "content LIKE \'%$s%\'";'
+ ),
+ preg_split('/[\s,;]+/', $_GET['keyword'] )
+ )
+ ).
+ ')';
+}
+
+$page['where_clauses'][] = $since_options[$page['since']]['clause'];
+
+// which status to filter on ?
+if ( !is_admin() )
+{
+ $page['where_clauses'][] = 'validated="true"';
+}
+
+$page['where_clauses'][] = get_sql_condition_FandF
+ (
+ array
+ (
+ 'forbidden_categories' => 'category_id',
+ 'visible_categories' => 'category_id',
+ 'visible_images' => 'ic.image_id'
+ ),
+ '', true
+ );
+
+// +-----------------------------------------------------------------------+
+// | comments management |
+// +-----------------------------------------------------------------------+
+if (isset($_GET['delete']) and is_numeric($_GET['delete'])
+ and !is_adviser() )
+{// comments deletion
+ check_status(ACCESS_ADMINISTRATOR);
+ $query = '
+DELETE FROM '.COMMENTS_TABLE.'
+ WHERE id='.$_GET['delete'].'
+;';
+ pwg_query($query);
+}
+
+if (isset($_GET['validate']) and is_numeric($_GET['validate'])
+ and !is_adviser() )
+{ // comments validation
+ check_status(ACCESS_ADMINISTRATOR);
+ $query = '
+UPDATE '.COMMENTS_TABLE.'
+ SET validated = \'true\'
+ , validation_date = NOW()
+ WHERE id='.$_GET['validate'].'
+;';
+ pwg_query($query);
+}
+
+// +-----------------------------------------------------------------------+
+// | page header and options |
+// +-----------------------------------------------------------------------+
+
+$title= l10n('User comments');
+$page['body_id'] = 'theCommentsPage';
+
+$template->set_filenames(array('comments'=>'comments.tpl'));
+$template->assign(
+ array(
+ 'F_ACTION'=>PHPWG_ROOT_PATH.'comments.php',
+ 'F_KEYWORD'=>@htmlspecialchars(stripslashes($_GET['keyword'])),
+ 'F_AUTHOR'=>@htmlspecialchars(stripslashes($_GET['author'])),
+ )
+ );
+
+// +-----------------------------------------------------------------------+
+// | form construction |
+// +-----------------------------------------------------------------------+
+
+// Search in a particular category
+$blockname = 'categories';
+
+$query = '
+SELECT id, name, uppercats, global_rank
+ FROM '.CATEGORIES_TABLE.'
+'.get_sql_condition_FandF
+ (
+ array
+ (
+ 'forbidden_categories' => 'id',
+ 'visible_categories' => 'id'
+ ),
+ 'WHERE'
+ ).'
+;';
+display_select_cat_wrapper($query, array(@$_GET['cat']), $blockname, true);
+
+// Filter on recent comments...
+$tpl_var=array();
+foreach ($since_options as $id => $option)
+{
+ $tpl_var[ $id ] = $option['label'];
+}
+$template->assign( 'since_options', $tpl_var);
+$template->assign( 'since_options_selected', $page['since']);
+
+// Sort by
+$template->assign( 'sort_by_options', $sort_by);
+$template->assign( 'sort_by_options_selected', $page['sort_by']);
+
+// Sorting order
+$template->assign( 'sort_order_options', $sort_order);
+$template->assign( 'sort_order_options_selected', $page['sort_order']);
+
+
+// Number of items
+$blockname = 'items_number_option';
+$tpl_var=array();
+foreach ($items_number as $option)
+{
+ $tpl_var[ $option ] = is_numeric($option) ? $option : l10n($option);
+}
+$template->assign( 'item_number_options', $tpl_var);
+$template->assign( 'item_number_options_selected', $page['items_number']);
+
+
+// +-----------------------------------------------------------------------+
+// | navigation bar |
+// +-----------------------------------------------------------------------+
+
+if (isset($_GET['start']) and is_numeric($_GET['start']))
+{
+ $start = $_GET['start'];
+}
+else
+{
+ $start = 0;
+}
+
+$query = '
+SELECT COUNT(DISTINCT(id))
+ FROM '.IMAGE_CATEGORY_TABLE.' AS ic
+ INNER JOIN '.COMMENTS_TABLE.' AS com
+ ON ic.image_id = com.image_id
+ WHERE '.implode('
+ AND ', $page['where_clauses']).'
+;';
+list($counter) = mysql_fetch_row(pwg_query($query));
+
+$url = PHPWG_ROOT_PATH
+ .'comments.php'
+ .get_query_string_diff(array('start','delete','validate'));
+
+$navbar = create_navigation_bar($url,
+ $counter,
+ $start,
+ $page['items_number'],
+ '');
+
+$template->assign('NAVBAR', $navbar);
+
+// +-----------------------------------------------------------------------+
+// | last comments display |
+// +-----------------------------------------------------------------------+
+
+$comments = array();
+$element_ids = array();
+$category_ids = array();
+
+$query = '
+SELECT com.id AS comment_id
+ , com.image_id
+ , ic.category_id
+ , com.author
+ , com.date
+ , com.content
+ , com.id AS comment_id
+ , com.validated
+ FROM '.IMAGE_CATEGORY_TABLE.' AS ic
+ INNER JOIN '.COMMENTS_TABLE.' AS com
+ ON ic.image_id = com.image_id
+ WHERE '.implode('
+ AND ', $page['where_clauses']).'
+ GROUP BY comment_id
+ ORDER BY '.$page['sort_by'].' '.$page['sort_order'];
+if ('all' != $page['items_number'])
+{
+ $query.= '
+ LIMIT '.$start.','.$page['items_number'];
+}
+$query.= '
+;';
+$result = pwg_query($query);
+while ($row = mysql_fetch_assoc($result))
+{
+ array_push($comments, $row);
+ array_push($element_ids, $row['image_id']);
+ array_push($category_ids, $row['category_id']);
+}
+
+if (count($comments) > 0)
+{
+ // retrieving element informations
+ $elements = array();
+ $query = '
+SELECT id, name, file, path, tn_ext
+ FROM '.IMAGES_TABLE.'
+ WHERE id IN ('.implode(',', $element_ids).')
+;';
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_assoc($result))
+ {
+ $elements[$row['id']] = $row;
+ }
+
+ // retrieving category informations
+ $query = '
+SELECT id, name, permalink, uppercats
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id IN ('.implode(',', $category_ids).')
+;';
+ $categories = hash_from_query($query, 'id');
+
+ foreach ($comments as $comment)
+ {
+ if (!empty($elements[$comment['image_id']]['name']))
+ {
+ $name=$elements[$comment['image_id']]['name'];
+ }
+ else
+ {
+ $name=get_name_from_file($elements[$comment['image_id']]['file']);
+ }
+
+ // source of the thumbnail picture
+ $thumbnail_src = get_thumbnail_url( $elements[$comment['image_id']] );
+
+ // link to the full size picture
+ $url = make_picture_url(
+ array(
+ 'category' => $categories[ $comment['category_id'] ],
+ 'image_id' => $comment['image_id'],
+ 'image_file' => $elements[$comment['image_id']]['file'],
+ )
+ );
+
+ $author = $comment['author'];
+ if (empty($comment['author']))
+ {
+ $author = l10n('guest');
+ }
+
+ $tpl_comment =
+ array(
+ 'U_PICTURE' => $url,
+ 'TN_SRC' => $thumbnail_src,
+ 'ALT' => $name,
+ 'AUTHOR' => trigger_event('render_comment_author', $author),
+ 'DATE'=>format_date($comment['date'],'mysql_datetime',true),
+ 'CONTENT'=>trigger_event('render_comment_content',$comment['content']),
+ );
+
+ if ( is_admin() )
+ {
+ $url = get_root_url().'comments.php'.get_query_string_diff(array('delete','validate'));
+ $tpl_comment['U_DELETE'] = add_url_params($url,
+ array('delete'=>$comment['comment_id'])
+ );
+
+ if ($comment['validated'] != 'true')
+ {
+ $tpl_comment['U_VALIDATE'] = add_url_params($url,
+ array('validate'=>$comment['comment_id'])
+ );
+ }
+ }
+ $template->append('comments', $tpl_comment);
+ }
+}
+// +-----------------------------------------------------------------------+
+// | html code display |
+// +-----------------------------------------------------------------------+
+include(PHPWG_ROOT_PATH.'include/page_header.php');
+$template->pparse('comments');
+include(PHPWG_ROOT_PATH.'include/page_tail.php');
+?>
\ No newline at end of file
diff --git a/BSF/doc/COPYING b/BSF/doc/COPYING
new file mode 100644
index 000000000..5b6e7c66c
--- /dev/null
+++ b/BSF/doc/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ , 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/BSF/doc/ChangeLog b/BSF/doc/ChangeLog
new file mode 100644
index 000000000..7c9c084d7
--- /dev/null
+++ b/BSF/doc/ChangeLog
@@ -0,0 +1,822 @@
+2005-11-07 Pierrick LE GALL
+
+ * bug 180 fixed: remote_site.tpl doesn't validate.
+
+ * translation bug fixed: generate_file_listing.php doesn't exist,
+ it's create_listing_file.php !
+
+2005-11-07 Pierrick LE GALL
+
+ * bug 184 fixed: for the third time...
+
+2005-11-07 Pierrick LE GALL
+
+ * improvement: use a "filter" form for adding user comments.
+
+2005-11-05 Pierrick LE GALL
+
+ * bug 186 fixed: useless template theme icons deleted. Remaining
+ icons in GIF converted to PNG.
+
+2005-10-31 Pierrick LE GALL
+
+ * bug 185 fixed: errors in language items.
+
+2005-10-31 Pierrick LE GALL
+
+ * modification: use "filter" class for quickconnect box (with some
+ custom modifications)
+
+ * bug fixed: the total number of pictures had disappeared
+
+2005-10-30 volcom
+
+ * bug 190 fixed: issue in stats.tpl display
+ * bug fixed: labels of monthly, daily graph fixed
+
+2005-10-26 chrisaga
+
+ * bug 177 fixed: for Safari (was fixed for Konqueror only)
+
+2005-10-25 Pierrick LE GALL
+
+ * bug 183 fixed: Warning when trying to associate an image with an
+ already associated category. Just coded the TODO instructions I
+ had let some time ago.
+
+2005-10-23 chrisaga
+
+ * bug 184 fixed: .png button left aligned in IE
+
+2005-10-23 Pierrick LE GALL
+
+ * bug 182 fixed: newly created user is not displayed in the user
+ list.
+
+2005-10-23 Pierrick LE GALL
+
+ * bug fixed: during file tree synchronization, warning on finding
+ the next rank of category which has no sub-categories yet.
+
+2005-10-23 chrisaga
+
+ * bug fixed: cat-list.tpl validate now
+
+ * improvement: removed some unnecessary css rules from old tests in
+ FROM design in file default-layout.css
+
+2005-10-23 Pierrick LE GALL
+
+ * bug 181 fixed: "Parameters not created in phpwebgallery_config
+ during upgrade". gallery_title and gallery_description had not
+ been added to config table. (use|show)_(exif|iptc) and
+ authorize_remembering have also been removed...
+
+2005-10-22 chrisaga
+
+ * bug 177 fixed: icon tools wrong position in Safari and Konqueror
+ Needed a little trick to fix it. Put the trick in a fix-khtml.css
+ Loading in header.tpl and protected from IE, Geko and Opera,
+ just in case (couldn't found a safari css-only filter wich validate)
+
+ * bug fixed: issue in the FORM in remote_site.tpl which prevented
+ HTML validation (still something to do with the TABLE).
+
+2005-10-22 Pierrick LE GALL
+
+ * bug fixed: link on a single day in
+ Administration>General>History was missing
+
+2005-10-22 Pierrick LE GALL
+
+ * bug 173 fixed: due to phpBB user identifiers management, the
+ method to find the next user identifier has changer to MAX+1.
+
+ * improvement: information message when new user added
+
+ * bug fixed: language item "Username" used instead of "login".
+
+2005-10-20 chrisaga
+
+ * bug 176 fixed: need a javascript to handle transparent PNG
+ background in IE (tribute to Bob Osola
+ http://homepage.ntlworld.com/bobosola/index.htm)
+
+ * display: fixed another IE childselector bug in Categories
+ Management admin page and improved display of this page in all
+ browsers too.
+
+2005-10-18 Pierrick LE GALL
+
+ * bug 174 fixed: use_exif_mapping configuration parameter was not
+ used to list database fields to update from EXIF values.
+
+2005-10-18 Pierrick LE GALL
+
+ * bug 172 fixed: crash when changing password with an external
+ users table. The same kind of correction was also made in
+ picture.php and register.php.
+
+2005-10-18 Pierrick LE GALL
+
+ * bug 159 and 166 fixed: parameter "options" for mail() function
+ disabled by default.
+
+2005-10-18 chrisaga
+
+ * display : smaller font-size and, as requested, fonts more like 1.4
+
+ * bug 165 fixed : La largeur par dĂŠfaut en PIXELS pas en Kb
+
+2005-10-18 chrisaga
+
+ * display : in group_list.tpl - wrong form class, mixed html tags, ...
+
+ * display : suppress bullets in admin->synchronize and checkbox
+ under select
+
+ * display : set standard font size to 12pt
+
+2005-10-17 Pierrick LE GALL
+
+ * improvement: use new function get_language_filepath in about.php
+ (complement of bug 168 correction)
+
+2005-10-17 Pierrick LE GALL
+
+ * bug 154 fixed: "Calendar category, too precise dates". Bug came
+ from the bigger precision of creation_date and available_date in
+ images tables. Need to retrieve only the year-month-day
+ information.
+
+2005-10-17 volcom
+
+ * new: history details by month,day
+
+2005-10-15 Pierrick LE GALL
+
+ * modification: localized labels in Administration>Pictures>Caddie
+
+ * translation: bug fixed on special categories title in French
+
+ * translation: all help pages were translated in french
+
+2005-10-09 Pierrick LE GALL
+
+ * new: "quick start" section in Administration>General>Instructions
+
+2005-10-08 Pierrick LE GALL
+
+ * translation: new items related to configuration parameters
+ gallery_title and gallery_description in GUI.
+
+2005-10-08 Pierrick LE GALL
+
+ * modification: configuration parameters gallery_title and
+ gallery_description are GUI managed in
+ Administration>Configuration>General
+
+ * bug fixed: configuration parameters (use|show)_(exif|iptc) and
+ authorize_remembering should have been deleted from config table
+ some time ago.
+
+2005-10-08 Pierrick LE GALL
+
+ * new: mass virtual categories movement manager in
+ Administration>Categories>Move screen.
+
+2005-10-05 Pierrick LE GALL
+
+ * bug 160 fixed: (part one of the bug) hard coded column name of
+ users table had to be replaced by the configurable column
+ name. This correction was made by a full rewrite of filtered users
+ list management. The other bug (not submited in bugtracker) that
+ needed this rewrite was that when you choose "all" as target for
+ mass users modification, you expected to apply modification on
+ filtered users, not all users.
+
+ * bug 160 fixed: (part two of the bug) hard coded column name for
+ primary key in mass_updates function.
+
+ * modification: configuration parameter users_page is now located
+ in the correct file (include/config_default.inc.php instead of
+ admin/user_list.php)
+
+2005-09-27 Pierrick LE GALL
+
+ * bug 168 fixed: crash when language file does not
+ exists. Constant PHPWG_DEFAULT_LANGUAGE added. New function
+ get_language_filepath always used to find language files.
+
+2005-09-26 Pierrick LE GALL
+
+ * bug 158 fixed: display error on "double selects" screens (with
+ MSIE, strange behavior). VDigital modification optimizes width
+ usage :-)
+
+2005-09-25 Pierrick LE GALL
+
+ * bug 163 fixed: link error when cookies disabled in admin/stats
+
+2005-09-24 Pierrick LE GALL
+
+ * bug fixed: HTML error on nested UL in categories menu.
+
+ * bug 161 fixed: guest_id not taken into account for history table
+ insert.
+
+ * bug 164 fixed: category inserted in history table becomes too
+ long if HTML tags are kept.
+
+2005-09-24 Pierrick LE GALL
+
+ * bug 162 fixed: division by zero when trying to view "all" items
+ in admin/element_set_(global|unit)
+
+ * bug 156 fixed: error when selection is empty was not catched.
+
+2005-09-21 Pierrick LE GALL
+
+ * bug 151 fixed: default maxheight was not proposed as "set to"
+ option in admin/user_list
+
+2005-09-21 Pierrick LE GALL
+
+ * bug 150 fixed: in category permissions management, wrong column
+ name when using an external users table.
+
+2005-09-20 Pierrick LE GALL
+
+ * update: upgrade from 1.4.0 or 1.4.1. Upgrade from 1.3.x is not
+ available anymore.
+
+ * update: README files updated for very near branch 1.5 :-)
+
+ * new: file tools/config_local.inc.php as example for optional
+ include/config_local.inc.php
+
+ * bug fixed: configuration parameter show_picture_name_on_title
+ was useless
+
+2005-09-18 Pierrick LE GALL
+
+ * bug 111 fixed: "Can't add virtual category when cookie
+ disabled". Correction reported from branch 1.4.
+
+ * bug 109 fixed : "disabled "best rated" menu item when rating is
+ not enabled". Correction reported from branch 1.4.
+
+ * bug 95 fixed : "default maxwidth and maxheight not registered
+ ". Correction reported from branch 1.4.
+
+2005-09-18 Pierrick LE GALL
+
+ * bug 107 fixed: crash when logging visit on a category containing
+ "'" character. Correction reported from branch 1.4
+
+ * bug fixed: in admin/stats, incorrect SQL queries gave no result.
+
+2005-09-18 Pierrick LE GALL
+
+ * bug 101 fixed: "problem with IE search on keywords". correction
+ reported from branch 1.4
+
+2005-09-17 Pierrick LE GALL
+
+ * new: automatic new password sent by mail when requested by user
+
+2005-09-17 Pierrick LE GALL
+
+ * improvement: add information about how to use an external table
+ for users
+
+ * bug fixed: uses user configuration fields for filtering in
+ admin/user_list
+
+ * bug fixed: don't try to create feeds automatically during
+ sync_users (when line in user_infos is missing)
+
+ * bug fixed: create_user_infos function gives status admin if user
+ id matches $conf['webmaster_id']
+
+2005-09-17 Pierrick LE GALL
+
+ * new: the number of elements waiting for validation is notified
+ in RSS feed.
+
+2005-09-17 Pierrick LE GALL
+
+ * translation: common and admin strings translated in french from
+ english files
+
+2005-09-14 Pierrick LE GALL
+
+ * improvement: long localized messages are in HTML files instead
+ of $lang array. This is the case of admin/help and about pages.
+
+ * deletion: of unused functions (ts_to_mysqldt, is_image,
+ TN_exists, check_date_format, date_convert,
+ get_category_directories, get_used_metadata_list, array_remove,
+ pwg_write_debug, get_group_restrictions,
+ get_all_group_restrictions, is_group_allowed, style_select,
+ deprecated_getAttribute).
+
+ * new: many new contextual help pages to replace descriptions
+ previously included in pages.
+
+ * modification: reorganisation of language files. Deletion of
+ unused language keys, alphabetical sort. No faq.lang.php anymore
+ (replaced by help.html). Only done for en_UK.iso-8859-1.
+
+2005-09-03 Pierrick LE GALL
+
+ * bug fixed : no display of first added element date in
+ admin/intro if no element in the gallery.
+
+2005-09-03 Pierrick LE GALL
+
+ * new : template yoga, made by yoDan, helped by chrisaga for
+ integration becomes the official template.
+
+2005-09-03 Pierrick LE GALL
+
+ * modification : less configuration parameters in administration
+ screen. These parameters are move to
+ include/config_default.inc.php.
+
+ * new : ability to add a single picture to caddie from picture.php
+
+ * new : contextual help, only a few pages are available.
+
+ * new : ability to delete users from admin/user_list
+
+ * modification : reorganization of configuration file
+
+ * new : configuration parameter use_exif_mapping
+
+ * improvement : MOD hidemail added to standard
+
+2005-08-26 Pierrick LE GALL
+
+ * new : HTML BODY identifier to let CSS stylesheets manage
+ specific behaviour.
+
+ * deletion : admin/search useless
+
+ * improvement : in admin/user_list, special behaviour for
+ true/false fields (expand, show_comments)
+
+ * new : gallery_title and gallery_description are displayed at the
+ top of each page.
+
+ * improvement : simplification in HTML for categories menu.
+
+ * improvement : standardization of presentation in all public
+ pages (identification, registration, search, profile,
+ notification, comments, etc.)
+
+2005-08-25 Pierrick LE GALL
+
+ * deletion : no mail notification anymore. Feature replaced by RSS
+ feed notification.
+
+ * improvement : on waiting pictures management. Ability to
+ validate all or reject all in one clic.
+
+2005-08-21 Pierrick LE GALL
+
+ * modification : adaptation of template variables and blocks in
+ comments page to display comment by comment instead of picture by
+ picture.
+
+2005-08-20 Pierrick LE GALL
+
+ * improvement : dedicated page for user comments validation/reject
+ in administration. (screen is not shared with public part of the
+ gallery). Ability to validate all or reject all in one clic.
+
+2005-08-19 Pierrick LE GALL
+
+ * improvement : less compact presentation of screen
+ admin/element_set_unit.
+
+2005-08-19 Pierrick LE GALL
+
+ * improvement : standardization of categories navigation bar on
+ top of administration screen element_set_(global|unit),
+ cat_modify, cat_list, cat_perm.
+
+ * new : direct link to admin/cat_modify from category.php.
+
+2005-08-19 Pierrick LE GALL
+
+ * modification : RSS feed work only with a given feed
+ identifier. Thus we can avoid fixed frequency notification to
+ concentrate on variable frequency notification, which is much more
+ interesting. To do this, feeds have moved to a dedicated table
+ allowing each user (including guest user) to have more than on
+ feed.
+
+2005-08-18 Pierrick LE GALL
+
+ * bug 133 fixed : (report from branch 1.4) Deleting user favorites
+ is too restrictive. Instead of deleting a favorite because it
+ belongs to at least one forbidden category, a favorite is deleted
+ if it belongs to no authorized category (which was the expected
+ behaviour).
+
+2005-08-18 Pierrick LE GALL
+
+ * bug 134 fixed : "Nb of images incorectly rendered in "tool tip"
+ near category in category menu". Correction reported (and
+ improved) from branch 1.4
+
+2005-08-18 Pierrick LE GALL
+
+ * improvement : screen admin/picture_modify
+ rewritten. Presentation copied from admin/cat_modify : fieldsets
+ regroup fields. Ability to synchronize metadata for the displayed
+ item.
+
+ * bug 110 fixed : "return to element view from element edition
+ fails depending on permissions". If a reachable (for the connected
+ admin) category is available, a "jump to" link is displayed, by
+ default, using the category given in URL.
+
+ * bug fixed : in mass_updates function, the first item of
+ $fields['update'] has not always 0 for id (as in any array).
+
+ * modification : get_keywords function understands spaces as
+ separator, allow less than 3 chars keywords, allow quotes.
+
+ * new : ability to allow HTML in picture or category description
+ (false by default)
+
+2005-08-17 Pierrick LE GALL
+
+ * improvement : in admin/user_perm, already authorized categories
+ thanks to group associations are displayed and not taken into
+ account for user specific permissions.
+
+2005-08-17 Pierrick LE GALL
+
+ * modification : major simplification of admin.php. Titles are
+ managed by included page, localized items are managed directly in
+ the template.
+
+ * new : sub template admin/double_select is included in templates
+ admin/cat_options, admin/user_perm and admin/group_perm. I haven't
+ been able to use it in admin/picture_modify because it seems
+ impossible to have two instance of the same sub-template without
+ interfering.
+
+ * modification : bug 99, in profile manager, no auto submit when
+ changing language (useless and generate accessibility problem).
+
+ * improvement : HTML semantically correct for administration menu,
+ simpler syntax, less tags, correct tags (dl/dt/dd instead of
+ div/div).
+
+ * modification : number of waiting elements and unvalidated
+ comments are displayed in admin/intro instead of administration
+ menu (with a link to the dedicated pages).
+
+ * deletion : no link to profile from admin/user_list anymore (no
+ need).
+
+2005-08-16 Pierrick LE GALL
+
+ * modification : admin/group_list screen completely rewrite to
+ present the list of existing groups and a form to add a new
+ group. Here you can delete a group, go to permissions management
+ for a group, go to member list of a group (on admin/user_list with
+ a filter on group).
+
+ * modification : admin/user_perm and admin/group_perm are not
+ directly reachable by the admin menu anymore. Only the user/group
+ list lets you reach user/group permissions management screen.
+
+2005-08-15 Pierrick LE GALL
+
+ * new : introduction page to administration section. This page
+ gives informations about PhpWebGallery version, PHP version, MySQL
+ version, gallery database informations (number of categories,
+ elements, users, comments). Ability to request phpwebgallery.net
+ for upgrade.
+
+ * deletion : of obsolete admin/admin_phpinfo.php page replaced by
+ a link in introduction page.
+
+2005-08-14 Pierrick LE GALL
+
+ * modification : simplification of HTML/CSS code for double select
+ screen to manage categories properties (admin/cat_options)
+
+2005-08-14 Pierrick LE GALL
+
+ * new : ability to set an element as representant of its category
+ directly from picture.php screen.
+
+ * improvement : dedicated icon to add elements into caddie. Ths
+ icon is displayed in the category title bar.
+
+2005-08-14 Pierrick LE GALL
+
+ * new : maintenance screen in administration. There you can update
+ categories informations (number of images, date of the last added
+ element), update images informations (path, average rate), purge
+ obsolete sessions, purge history.
+
+ * new : ability to have random representative for categories. This
+ configuration parameter is set to false by default.
+
+ * new : ability to set an element as representative of a category
+ without belonging to the category. Thus, administrator can choose
+ representative even for empty categories.
+
+ * improvement : semantically superior design for category edition
+ screen by regrouping fields in fieldsets. The improved screen
+ contains action buttons as in category list screen.
+
+ * new : ability to move a virtual category (ie change its parent
+ category).
+
+ * bug fixed : the sync_users function checks all user children
+ tables (access, cache, group association).
+
+2005-08-08 Pierrick LE GALL
+
+ * new : external authentication in another users table. Previous
+ users table is divided between users (common properties with any
+ web application) and user_infos (phpwebgallery specific
+ informations). External table and fields can be configured.
+
+ * modification : profile.php is not reachable through
+ administration anymore (not useful).
+
+ * modification : in profile.php, current password is mandatory
+ only if user tries to change his password. Username can't be
+ changed.
+
+ * deletion : of obsolete functions get_user_restrictions,
+ update_user_restrictions, get_user_all_restrictions,
+ is_user_allowed, update_user
+
+ * modification : $user['forbidden_categories'] equals at least
+ "-1" so that category_id NOT IN ($user['forbidden_categories'])
+ can always be used.
+
+ * modification : user_forbidden table becomes user_cache so that
+ not only restriction informations can be stored in this table.
+
+2005-07-17 Pierrick LE GALL
+
+ * improvement : in admin/element_set_global, javascript is not
+ used anymore to select an item with its thumbnail
+
+2005-07-17 Pierrick LE GALL
+
+ * bug fixed : in admin/user_list, if target is "selection" and
+ that no user is selected, an error occurs
+
+2005-07-17 Pierrick LE GALL
+
+ * new feature : use Apache authentication. If
+ $conf['apache_authentication'] is set true : if no user matches
+ $_SERVER['REMOTE_USER'] in "users" table, PWG automatically
+ creates one. This way, users can customize the behaviour of the
+ application.
+
+ * template : new organisation of identification menu
+ (category.php). Simplification is required for Apache
+ authentication (no logout link even if user is externally logged
+ in)
+
+ * new : usernames can contain quotes (required because Apache
+ authentication authorized quotes in usernames)
+
+2005-07-17 Pierrick LE GALL
+
+ * new configuration parameter : hide thumbnail captions on main
+ page with $conf['show_thumbnail_caption']
+
+ * new configuration parameter : hide picture name in title on
+ picture presentation page with $conf['show_picture_name_on_title']
+
+ * template : new CSS classes to manage picture.php title
+
+2005-07-16 Pierrick LE GALL
+
+ * new feature : RSS notification feed. Feed generator is an
+ external tool (FeedCreator class v1.7.2). New file feed.php
+
+ * new database field : comments.validation_date (datetime). This
+ field is required for notification feed.
+
+ * new database field : users.feed_id (varchar(50)). users.feed_id
+ is an alias of users.id but is much more complicated to find (50
+ characters, figures or letters, case sensitive) : the purpose is
+ to keep it secret (as far as possible).
+
+ * new database field : users.last_feed_check (datetime)
+
+ * new database field : users.registration_date (datetime)
+
+ * bug fixed : no need to add the (unavailable) session id to
+ install.php in the installation form.
+
+ * modified database field : images.date_available become more
+ precise (date to datetime). This precision is needed for
+ notification feed.
+
+ * new index : comments_i1 (validation_date). Might be useful for
+ feed queries.
+
+ * new index : comments_i2 (image_id). Useful each time you want to
+ have informations about an element and its associated comments.
+
+ * version 9.11 of mysqldump outputs database field names and table
+ names with backquote "`" (didn't find how to take them off)
+
+2005-06-30 Pierrick LE GALL
+
+ * category permissions management comes back! (it disappeared in
+ branch 1.4) This time, it is designed to support better long users
+ list. On this screen, for a particular category, admin can say
+ which groups and users are permitted.
+
+2005-06-30 Pierrick LE GALL
+
+ * users managment : change display of filter (according to filter
+ on user comments)
+
+2005-06-30 Pierrick LE GALL
+
+ * categories management : new display with icon for actions
+ (delete, sub-categories, elements, edit, jump to, permissions)
+
+ * categories management : semantic HTML layout (using common
+ lists)
+
+ * categories management : new way to order categories of the same
+ level : a text field let the admin reorder all categories at once.
+
+2005-06-25 Pierrick LE GALL
+
+ * new feature : ability to add links on the main page (see
+ include/config_default.inc.php)
+
+2005-06-21 Pierrick LE GALL
+
+ * comments page rewritten : comments are displayed one by one,
+ with filters and display options available. The list of comments
+ is paginated.
+
+2005-06-21 Pierrick LE GALL
+
+ * direct communication between templates and language items,
+ without needing a mapping in the PHP code.
+
+2005-06-21 Pierrick LE GALL
+
+ * new function get_name_from_file to centralize the construction
+ of displayed name from the filename
+
+ * new function l10n which returns the corresponding value from
+ $lang if existing. Else, the key is returned. This means that if a
+ language item is not translated, the key is displayed instead
+ (better than nothing).
+
+2005-06-11 Pierrick LE GALL
+
+ * errors and informations boxes : management centralized in
+ admin.php, $errors and $infos arrays replaced by $page['errors']
+ and $page['infos'], special management for admin/update.php (more
+ complex management)
+
+2005-06-11 Pierrick LE GALL
+
+ * bug 96 (informations given by uploaders are lost) correction
+ reported from branch 1.4
+
+2005-05-10 Pierrick LE GALL
+
+ * user list : links to profile page and permissions page are
+ represented by icons (more compact)
+
+ * user list : ability to associate to a group or to dissociate
+ from a group a list of selected users
+
+ * user list : ability to set user properties in "batch" mode (a
+ selection of users at once)
+
+ * user list : alternate background color for each line
+
+2005-04-30 Pierrick LE GALL
+
+ * user list updated : ability to filter list on status. Function
+ get_enums comes back to retrieve the list of possible status in
+ the database.
+
+2005-04-28 Pierrick LE GALL
+
+ * user list updated : ability to filter list on group
+
+2005-04-25 Pierrick LE GALL
+
+ * include/config.inc.php becomes include/config_default.inc.php :
+ this file should not be modified. A new file
+ include/config_local.inc.php can be used for overwriting
+ configuration parameters
+
+2005-04-25 Pierrick LE GALL
+
+ * come back to previous version of include/constants.php :
+ %PWGVERSION% was not a mistake
+
+2005-04-25 Pierrick LE GALL
+
+ * profile.php is no longer used for listing users :
+ admin/user_list.php comes back (as in 1.3 branch)
+
+ * user list updated : ability to filter list on username
+
+2005-04-25 Gweltas
+
+ * ability to install even if file include/mysql.inc.php doesn't
+ exist
+
+ * monthly statistics
+
+ * installation labels updated (english only)
+
+2005-04-16 Pierrick LE GALL
+
+ * elements batch management : element_set page becomes the
+ frontend to element_set_global and element_set_unit, infos_images
+ (after a long time of use) become deprecated : the more powerful
+ element_set is used instead. Consequently, batch management
+ concerns caddie but also "normal categories".
+
+ * refactoring code in admin.php to include the sub-file (clearer)
+
+ * caddie : function fill_caddie replaces the code in category.php
+ and can be used in admin/element_set.php
+
+ * caddie : caddie table is added in delete_elements function
+
+2005-04-16 Pierrick LE GALL
+
+ * elements batch management : in addition to global mode, a unit
+ mode is added : ability to manage a set of elements, element by
+ element. This screen is very close to the existing "infos_images"
+ (which will soon disappear).
+
+ * elements batch management : in screen element_set_global, the
+ display options are displayed at the top as in element_set_unit
+
+2005-04-11 Pierrick LE GALL
+
+ * functions get_day_list and get_month_list moved from search.php
+ to include/functions.inc.php : these functions are now also used
+ in admin/element_set_global.php
+
+ * elements batch management improved : ability to set the number
+ of elements to display per line, ability to set {author, name,
+ creation date} fields, ability to add and remove keywords, ability
+ to take selected elements out of caddie
+
+2005-03-31 Pierrick LE GALL
+
+ * apply category name and element name separation in calendar
+ special category
+
+ * change method of counting total number of viewable pictures :
+ pictures linked to more than one category are not counted twice.
+
+2005-03-26 Pierrick LE GALL
+
+ * "add to caddie" link is visible only when categories contains
+ elements. Only admin users can see this link
+
+ * elements batch management : impossible to create a link between
+ category and element that already exists
+
+ * logical separation of category name under thumbnail and element
+ name
+
+2005-03-25 Pierrick LE GALL
+
+ * application version is defined at build or release
+ creation. This way, include/constants.php won't change at each
+ build.
+
+2005-03-25 Pierrick LE GALL
+
+ * typo fixed : wrong block name closed in template
+
+2005-03-25 Pierrick LE GALL
+
+ * new feature : caddie. The purpose is batch management,
+ especially concerning elements to categories associations.This is
+ the very first release, needs many improvements.
diff --git a/BSF/doc/README_en.txt b/BSF/doc/README_en.txt
new file mode 100644
index 000000000..91b5eecc8
--- /dev/null
+++ b/BSF/doc/README_en.txt
@@ -0,0 +1,105 @@
+=============
+Piwigo
+=============
+
+http://piwigo.org
+
+
+Installation
+============
+
+1. extract files from the downloaded file (using tar or unzip command, or
+ softwares like 7-zip or winzip)
+
+2. place de source files on your website in the directory of your choice
+ ("gallery" for example)
+
+3. go to the URL http://your.domain/gallery/install.php and follow the
+ instructions of installation
+
+Upgrade
+=======
+
+1. elements to save :
+
+ - file "include/mysql.inc.php"
+ - file "include/config_local.inc.php"
+ - file "template-common/local-layout.css"
+ - file "template/yoga/local-layout.css"
+ - directory "galleries"
+ - any optional theme or extension you have intalled
+ - your database (create a dump, using PhpMyAdmin for instance)
+
+2. delete all files and directories of your previous installation (but not
+ the previous listed elements)
+
+3. extract files from the downloaded file (using tar or unzip command, or
+ softwares like 7-zip or winzip)
+
+4. upload all the new version files to your website but the previous listed
+ elements. The only elements coming from the previous installed version
+ are the elements listed above.
+
+5. go to the URL http://your.domain/gallery/upgrade.php and follow the
+ instructions
+
+How to start
+============
+
+Once installed or upgraded, your gallery is ready to run. Start by
+displaying the installation directory in your browser :
+
+http://your.domain/gallery
+
+Then identify as an administrator. A new link in Identification menu of main
+page will appear : Administration. Enter the administration panel.
+
+In the administration panel, take all your time for reading instructions
+explaining how to use your gallery.
+
+Communication
+=============
+
+Newsletter
+----------
+
+https://gna.org/mail/?group=phpwebgallery
+
+It is *highly* recommended to subscribe to Piwigo newsletter. This is
+extremely low-traffic, but will provide you with announcements of new
+Piwigo releases and serious bug notification. You will find available
+mailing lists at this URL :
+
+No spam, no commercial use.
+
+Freshmeat
+---------
+
+http://freshmeat.net/projects/phpwebgallery
+
+Want to stay informed at each release, stable and development
+release. Development releases notification are not send in the newsletter.
+
+Bugtracker
+----------
+
+http://bugs.phpwebgallery.net
+
+Bugs and change requests tracking. The best way to have your bug corrected:
+it won't be forgotten (as in the forum).
+
+Wiki
+----
+
+http://phpwebgallery.net/doc
+
+Wiki documentation: everyone can participate to improve documentation
+content.
+
+Message board
+-------------
+
+http://forum.phpwebgallery.net
+
+All communications (installation help, technical discussions) that can't be
+done in other channels.
diff --git a/BSF/doc/README_fr.txt b/BSF/doc/README_fr.txt
new file mode 100644
index 000000000..2915c1253
--- /dev/null
+++ b/BSF/doc/README_fr.txt
@@ -0,0 +1,116 @@
+=============
+Piwigo
+=============
+
+http://piwigo.org
+
+Installation
+============
+
+1. décompresser ŕ l'aide de winzip par exemple (winrar, winace et beaucoup
+ d'autres le permettent également) le fichier téléchargé.
+
+2. placer les fichiers décompressés sur votre serveur web dans le répertoire
+ de votre choix ("galerie" par exemple)
+
+3. se rendre ŕ l'URL http://votre.domaine/galerie/install.php et suivre les
+ instructions
+
+Mise ŕ jour
+===========
+
+1. éléments ŕ sauvegarder :
+
+ - fichier "include/mysql.inc.php"
+ - fichier "include/config_local.inc.php"
+ - fichier "template-common/local-layout.css"
+ - fichier "template/yoga/local-layout.css"
+ - répertoire "galleries"
+ - éventuellement thčmes supplémentaires et extensions
+ - votre base de données (en créant un dump, avec PhpMyAdmin par exemple)
+
+2. supprimer tous les fichiers et répertoires de la précédente installation
+ (sauf les éléments listés ci-dessus)
+
+3. décompresser ŕ l'aide de winzip par exemple (winrar, winace et beaucoup
+ d'autres le permettent également) le fichier téléchargé.
+
+4. placer tous les fichiers de la nouvelle version sur votre site web sauf
+ pour les élements listés ci-dessus. Les seuls éléments venant de la
+ précédente installation sont ceux listés ci-dessus.
+
+5. se rendre ŕ l'URL http://votre.domaine/galerie/upgrade.php et suivre les
+ instructions
+
+Comment commencer
+=================
+
+Une fois installée ou mise ŕ jour, votre galerie est pręte ŕ
+fonctionner. Commencez par vous rendre sur le répertoire d'installation dans
+votre navigateur :
+
+http://votre.domaine/galerie
+
+Ensuite, identifiez-vous en tant qu'un administrateur. Un nouveau lien dans
+le menu d'identification de la page principale va apparaître :
+Administration. Suivre ce lien :-)
+
+Dans la zone d'administration, prenez tout le temps nécessaire pour
+consulter les instructions, expliquant comment utiliser votre galerie.
+
+Communication
+=============
+
+Newsletter
+----------
+
+https://gna.org/mail/?group=phpwebgallery
+
+Il est *fortement* recommandé de souscrire ŕ la newsletter de
+Piwigo. Trčs peu de mails sont envoyés, mais les informations sont
+importantes : nouvelles versions de l'application, notification de bugs
+importants (relatifs ŕ la sécurité). Vous trouverez les listes de
+discussions disponibles sur la page suivante :
+
+Pas de spam, pas d'utilisation commerciale.
+
+Freshmeat
+---------
+
+http://freshmeat.net/projects/phpwebgallery
+
+Permet d'ętre au courant des sorties de toutes les releases, et en
+exclusivité les builds de la branche de développement (ce qui n'est pas
+prévu sur les mailing lists "announce").
+
+Outil de suivi de bogues
+------------------------
+
+http://bugs.phpwebgallery.net
+
+Gestion des bugs, mais aussi demande de nouvelles fonctionnalités. Rien de
+plus efficace pour qu'un bug soit corrigé : tant qu'il ne l'est pas, la
+"fiche" reste lŕ ŕ attendre, on ne l'oublie pas comme un topic sur le
+forum.
+
+Les demandes d'évolutions sont également gérées dans cet outil. Ce n'est pas
+forcément idéal car il ne s'agit pas de la męme chose, mais le suivi du dev
+d'une nouvelle fonctionnalité peut se modéliser de la męme façon que le
+suivi de la correction d'un bug.
+
+Wiki
+----
+
+http://phpwebgallery.net/doc
+
+Documentation suivant le systčme du wiki. Chacun peut participer ŕ
+l'amélioration de la doc.
+
+Forum de discussion
+-------------------
+
+http://forum.phpwebgallery.net
+
+Un forum est disponible et recommandé pour toutes les questions autres que
+les demandes d'évolution et rapport de bogue (installation, discussions
+techniques).
diff --git a/BSF/doc/index.php b/BSF/doc/index.php
new file mode 100644
index 000000000..c15b15795
--- /dev/null
+++ b/BSF/doc/index.php
@@ -0,0 +1,30 @@
+
diff --git a/BSF/feed.php b/BSF/feed.php
new file mode 100644
index 000000000..3c435cff1
--- /dev/null
+++ b/BSF/feed.php
@@ -0,0 +1,202 @@
+'.generate_key(50).'';
+if ( !empty($feed_id) )
+{
+ $query = '
+SELECT user_id,
+ last_check
+ FROM '.USER_FEED_TABLE.'
+ WHERE id = \''.$feed_id.'\'
+;';
+ $feed_row = mysql_fetch_assoc(pwg_query($query));
+ if ( empty($feed_row) )
+ {
+ page_not_found('Unknown/missing feed identifier');
+ }
+ if ($feed_row['user_id']!=$user['id'])
+ { // new user
+ $user = build_user( $feed_row['user_id'], true );
+ }
+}
+else
+{
+ $image_only = true;
+ if (!is_a_guest())
+ {// auto session was created - so switch to guest
+ $user = build_user( $conf['guest_id'], true );
+ }
+}
+
+// Check the status now after the user has been loaded
+check_status(ACCESS_GUEST);
+
+list($dbnow) = mysql_fetch_row(pwg_query('SELECT NOW();'));
+
+include_once(PHPWG_ROOT_PATH.'include/feedcreator.class.php');
+
+set_make_full_url();
+
+$rss = new UniversalFeedCreator();
+$rss->encoding=get_pwg_charset();
+$rss->title = $conf['gallery_title'];
+$rss->title.= ' (as '.$user['username'].')';
+
+$rss->link = $conf['gallery_url'];
+
+// +-----------------------------------------------------------------------+
+// | Feed creation |
+// +-----------------------------------------------------------------------+
+
+$news = array();
+if (!$image_only)
+{
+ $news = news($feed_row['last_check'], $dbnow, true, true);
+
+ if (count($news) > 0)
+ {
+ $item = new FeedItem();
+ $item->title = sprintf(l10n('New on %s'),
+ format_date($dbnow, 'mysql_datetime') );
+ $item->link = $conf['gallery_url'];
+
+ // content creation
+ $item->description = '
';
+ $item->descriptionHtmlSyndicated = true;
+
+ $item->date = mysqldt_to_ts($dbnow);
+ $item->author = 'Piwigo notifier';
+ $item->guid= sprintf('%s', $dbnow);;
+
+ $rss->addItem($item);
+
+ $query = '
+UPDATE '.USER_FEED_TABLE.'
+ SET last_check = \''.$dbnow.'\'
+ WHERE id = \''.$feed_id.'\'
+;';
+ pwg_query($query);
+ }
+}
+
+if ( !empty($feed_id) and empty($news) )
+{// update the last check from time to time to avoid deletion by maintenance tasks
+ if ( !isset($feed_row['last_check'])
+ or time()-mysqldt_to_ts($feed_row['last_check']) > 30*24*3600 )
+ {
+ $query = '
+UPDATE '.USER_FEED_TABLE.'
+ SET last_check = DATE_ADD(\''.$dbnow.'\', INTERVAL -15 DAY )
+ WHERE id = \''.$feed_id.'\'
+;';
+ pwg_query($query);
+ }
+}
+
+$dates = get_recent_post_dates_array($conf['recent_post_dates']['RSS']);
+
+foreach($dates as $date_detail)
+{ // for each recent post date we create a feed item
+ $item = new FeedItem();
+ $date = $date_detail['date_available'];
+ $item->title = get_title_recent_post_date($date_detail);
+ $item->link = make_index_url(
+ array(
+ 'chronology_field' => 'posted',
+ 'chronology_style'=> 'monthly',
+ 'chronology_view' => 'calendar',
+ 'chronology_date' => explode('-', substr($date,0,10) )
+ )
+ );
+
+ $item->description .=
+ ''.$conf['gallery_title'].' ';
+
+ $item->description .= get_html_description_recent_post_date($date_detail);
+
+ $item->descriptionHtmlSyndicated = true;
+
+ $item->date = mysqldt_to_ts($date);
+ $item->author = 'Piwigo notifier';
+ $item->guid= sprintf('%s', 'pics-'.$date);;
+
+ $rss->addItem($item);
+}
+
+$fileName= $conf['local_data_dir'].'/tmp';
+@mkdir($fileName); // just in case
+$fileName.='/feed.xml';
+// send XML feed
+echo $rss->saveFeed('RSS2.0', $fileName, true);
+?>
\ No newline at end of file
diff --git a/BSF/galleries/.cvsignore b/BSF/galleries/.cvsignore
new file mode 100644
index 000000000..72e8ffc0d
--- /dev/null
+++ b/BSF/galleries/.cvsignore
@@ -0,0 +1 @@
+*
diff --git a/BSF/galleries/index.php b/BSF/galleries/index.php
new file mode 100644
index 000000000..c15b15795
--- /dev/null
+++ b/BSF/galleries/index.php
@@ -0,0 +1,30 @@
+
diff --git a/BSF/identification.php b/BSF/identification.php
new file mode 100644
index 000000000..877e51a65
--- /dev/null
+++ b/BSF/identification.php
@@ -0,0 +1,93 @@
+set_filenames( array('identification'=>'identification.tpl') );
+
+$template->assign(
+ array(
+ 'U_LOST_PASSWORD' => get_root_url().'password.php',
+ 'U_REDIRECT' => $redirect_to,
+
+ 'F_LOGIN_ACTION' => get_root_url().'identification.php',
+ 'authorize_remembering' => $conf['authorize_remembering'],
+ ));
+
+if ($conf['allow_user_registration'])
+{
+ $template->assign('U_REGISTER', get_root_url().'register.php' );
+}
+
+//-------------------------------------------------------------- errors display
+if ( sizeof( $errors ) != 0 )
+{
+ $template->assign('errors', $errors);
+}
+
+//----------------------------------------------------------- html code display
+$template->pparse('identification');
+include(PHPWG_ROOT_PATH.'include/page_tail.php');
+?>
diff --git a/BSF/include/calendar_base.class.php b/BSF/include/calendar_base.class.php
new file mode 100644
index 000000000..bd3d2b180
--- /dev/null
+++ b/BSF/include/calendar_base.class.php
@@ -0,0 +1,351 @@
+date_field = 'date_available';
+ }
+ else
+ {
+ $this->date_field = 'date_creation';
+ }
+ $this->inner_sql = $inner_sql;
+ }
+
+ function get_display_name()
+ {
+ global $conf, $page;
+ $res = '';
+
+ for ($i=0; $i$chronology_date ),
+ array( 'start' )
+ );
+ $res .=
+ ''
+ .$this->get_date_component_label($i, $page['chronology_date'][$i])
+ .'';
+ }
+ else
+ {
+ $res .=
+ ''
+ .$this->get_date_component_label($i, $page['chronology_date'][$i])
+ .'';
+ }
+ }
+ return $res;
+ }
+
+//--------------------------------------------------------- private members ---
+ /**
+ * Returns a display name for a date component optionally using labels
+ */
+ function get_date_component_label($level, $date_component)
+ {
+ $label = $date_component;
+ if (isset($this->calendar_levels[$level]['labels'][$date_component]))
+ {
+ $label = $this->calendar_levels[$level]['labels'][$date_component];
+ }
+ elseif ('any' === $date_component )
+ {
+ $label = l10n('calendar_any');
+ }
+ return $label;
+ }
+
+ /**
+ * Gets a nice display name for a date to be shown in previos/next links.
+ */
+ function get_date_nice_name($date)
+ {
+ $date_components = explode('-', $date);
+ $res = '';
+ for ($i=count($date_components)-1; $i>=0; $i--)
+ {
+ if ('any' !== $date_components[$i])
+ {
+ $label = $this->get_date_component_label($i, $date_components[$i] );
+ if ( $res!='' )
+ {
+ $res .= ' ';
+ }
+ $res .= $label;
+ }
+ }
+ return $res;
+ }
+
+ /**
+ * Creates a calendar navigation bar.
+ *
+ * @param array date_components
+ * @param array items - hash of items to put in the bar (e.g. 2005,2006)
+ * @param string class_prefix - html class attribute prefix for span elements
+ * @param bool show_any - adds any link to the end of the bar
+ * @param bool show_empty - shows all labels even those without items
+ * @param array labels - optional labels for items (e.g. Jan,Feb,...)
+ * @return string the navigation bar
+ */
+ function get_nav_bar_from_items($date_components, $items,
+ $class_prefix, $show_any,
+ $show_empty=false, $labels=null)
+ {
+ global $conf, $page;
+
+ $nav_bar = '';
+
+ if ($conf['calendar_show_empty'] and $show_empty and !empty($labels) )
+ {
+ foreach ($labels as $item => $label)
+ {
+ if ( ! isset($items[$item]) )
+ {
+ $items[$item] = -1;
+ }
+ }
+ ksort($items);
+ }
+
+ foreach ($items as $item => $nb_images)
+ {
+ $label = $item;
+ if (isset($labels[$item]))
+ {
+ $label = $labels[$item];
+ }
+ if ($nb_images==-1)
+ {
+ $nav_bar .= '';
+ $nav_bar .= $label;
+ }
+ else
+ {
+ $nav_bar .= '';
+ $url = duplicate_index_url(
+ array('chronology_date'=>array_merge($date_components,array($item))),
+ array( 'start' )
+ );
+ $nav_bar .= '';
+ $nav_bar .= $label;
+ $nav_bar .= '';
+ }
+ if ($nb_images > 0)
+ {
+ $nav_bar .= '('.$nb_images.')';
+ }
+ $nav_bar.= '';
+ }
+
+ if ($conf['calendar_show_any'] and $show_any and count($items)>1 and
+ count($date_components)calendar_levels)-1 )
+ {
+ $label = l10n('calendar_any');
+ $nav_bar .= '';
+ $url = duplicate_index_url(
+ array('chronology_date'=>array_merge($date_components,array('any'))),
+ array( 'start' )
+ );
+ $nav_bar .= '';
+ $nav_bar .= $label;
+ $nav_bar .= '';
+ $nav_bar.= '';
+ }
+ return $nav_bar;
+ }
+
+ /**
+ * Creates a calendar navigation bar for a given level.
+ *
+ * @param int level - the level (0-year,1-month/week,2-day)
+ * @return void
+ */
+ function build_nav_bar($level, $labels=null)
+ {
+ global $template, $conf, $page;
+
+ $query = '
+SELECT DISTINCT('.$this->calendar_levels[$level]['sql']
+ .') as period';
+ $query.= $this->inner_sql;
+ $query.= $this->get_date_where($level);
+ $query.= '
+ GROUP BY period
+;';
+
+ $level_items = array();
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ {
+ $level_items[$row['period']] = 0;
+ }
+
+ if ( count($level_items)==1 and
+ count($page['chronology_date'])calendar_levels)-1)
+ {
+ if ( ! isset($page['chronology_date'][$level]) )
+ {
+ list($key) = array_keys($level_items);
+ $page['chronology_date'][$level] = (int)$key;
+
+ if ( $levelcalendar_levels)-1 )
+ {
+ return;
+ }
+ }
+ }
+
+ $dates = $page['chronology_date'];
+ while ($levelget_nav_bar_from_items(
+ $dates,
+ $level_items,
+ 'calItem',
+ true,
+ true,
+ isset($labels) ? $labels : $this->calendar_levels[$level]['labels']
+ );
+
+ $template->append(
+ 'chronology_navigation_bars',
+ array(
+ 'CONTENT' => $nav_bar,
+ )
+ );
+ }
+
+ /**
+ * Assigns the next/previous link to the template with regards to
+ * the currently choosen date.
+ */
+ function build_next_prev()
+ {
+ global $template, $page;
+ $prev = $next =null;
+ if ( empty($page['chronology_date']) )
+ return;
+ $query = 'SELECT CONCAT_WS("-"';
+ for ($i=0; $icalendar_levels[$i]['sql'];
+ }
+ }
+ $current = implode('-', $page['chronology_date'] );
+
+ $query.=') as period' . $this->inner_sql .'
+AND ' . $this->date_field . ' IS NOT NULL
+GROUP BY period';
+
+ $upper_items = array_from_query( $query, 'period');
+
+ usort($upper_items, 'version_compare');
+ $upper_items_rank = array_flip($upper_items);
+ if ( !isset($upper_items_rank[$current]) )
+ {
+ array_push($upper_items, $current);// just in case (external link)
+ usort($upper_items, 'version_compare');
+ $upper_items_rank = array_flip($upper_items);
+ }
+ $current_rank = $upper_items_rank[$current];
+
+ $tpl_var = array();
+
+ if ( $current_rank>0 )
+ { // has previous
+ $prev = $upper_items[$current_rank-1];
+ $chronology_date = explode('-', $prev);
+ $tpl_var['previous'] =
+ array(
+ 'LABEL' => $this->get_date_nice_name($prev),
+ 'URL' => duplicate_index_url(
+ array('chronology_date'=>$chronology_date), array('start')
+ )
+ );
+ }
+
+ if ( $current_rank < count($upper_items)-1 )
+ { // has next
+ $next = $upper_items[$current_rank+1];
+ $chronology_date = explode('-', $next);
+ $tpl_var['next'] =
+ array(
+ 'LABEL' => $this->get_date_nice_name($next),
+ 'URL' => duplicate_index_url(
+ array('chronology_date'=>$chronology_date), array('start')
+ )
+ );
+ }
+
+ if ( !empty($tpl_var) )
+ {
+ $existing = & $template->get_template_vars('chronology_navigation_bars');
+ if ( !empty($existing) )
+ {
+ $existing[ sizeof($existing)-1 ] =
+ array_merge( $existing[ sizeof($existing)-1 ], $tpl_var);
+ }
+ else
+ {
+ $template->append( 'chronology_navigation_bars', $tpl_var );
+ }
+ }
+ }
+}
+?>
diff --git a/BSF/include/calendar_monthly.class.php b/BSF/include/calendar_monthly.class.php
new file mode 100644
index 000000000..b29997cef
--- /dev/null
+++ b/BSF/include/calendar_monthly.class.php
@@ -0,0 +1,515 @@
+calendar_levels = array(
+ array(
+ 'sql'=> 'YEAR('.$this->date_field.')',
+ 'labels' => null
+ ),
+ array(
+ 'sql'=> 'MONTH('.$this->date_field.')',
+ 'labels' => $lang['month']
+ ),
+ array(
+ 'sql'=> 'DAYOFMONTH('.$this->date_field.')',
+ 'labels' => null
+ ),
+ );
+ }
+
+/**
+ * Generate navigation bars for category page
+ * @return boolean false to indicate that thumbnails
+ * where not included here, true otherwise
+ */
+function generate_category_content()
+{
+ global $conf, $page;
+
+ $view_type = $page['chronology_view'];
+ if ($view_type==CAL_VIEW_CALENDAR)
+ {
+ global $template;
+ $tpl_var = array();
+ if ( count($page['chronology_date'])==0 )
+ {//case A: no year given - display all years+months
+ if ($this->build_global_calendar($tpl_var))
+ {
+ $template->assign('chronology_calendar', $tpl_var);
+ return true;
+ }
+ }
+
+ if ( count($page['chronology_date'])==1 )
+ {//case B: year given - display all days in given year
+ if ($this->build_year_calendar($tpl_var))
+ {
+ $template->assign('chronology_calendar', $tpl_var);
+ $this->build_nav_bar(CYEAR); // years
+ return true;
+ }
+ }
+
+ if ( count($page['chronology_date'])==2 )
+ {//case C: year+month given - display a nice month calendar
+ if ( $this->build_month_calendar($tpl_var) )
+ {
+ $template->assign('chronology_calendar', $tpl_var);
+ }
+ $this->build_next_prev();
+ return true;
+ }
+ }
+
+ if ($view_type==CAL_VIEW_LIST or count($page['chronology_date'])==3)
+ {
+ if ( count($page['chronology_date'])==0 )
+ {
+ $this->build_nav_bar(CYEAR); // years
+ }
+ if ( count($page['chronology_date'])==1)
+ {
+ $this->build_nav_bar(CMONTH); // month
+ }
+ if ( count($page['chronology_date'])==2 )
+ {
+ $day_labels = range( 1, $this->get_all_days_in_month(
+ $page['chronology_date'][CYEAR] ,$page['chronology_date'][CMONTH] ) );
+ array_unshift($day_labels, 0);
+ unset( $day_labels[0] );
+ $this->build_nav_bar( CDAY, $day_labels ); // days
+ }
+ $this->build_next_prev();
+ }
+ return false;
+}
+
+
+/**
+ * Returns a sql where subquery for the date field
+ * @param int max_levels return the where up to this level
+ * (e.g. 2=only year and month)
+ * @return string
+ */
+function get_date_where($max_levels=3)
+{
+ global $page;
+ $date = $page['chronology_date'];
+ while (count($date)>$max_levels)
+ {
+ array_pop($date);
+ }
+ $res = '';
+ if (isset($date[CYEAR]) and $date[CYEAR]!=='any')
+ {
+ $b = $date[CYEAR] . '-';
+ $e = $date[CYEAR] . '-';
+ if (isset($date[CMONTH]) and $date[CMONTH]!=='any')
+ {
+ $b .= $date[CMONTH] . '-';
+ $e .= $date[CMONTH] . '-';
+ if (isset($date[CDAY]) and $date[CDAY]!=='any')
+ {
+ $b .= $date[CDAY];
+ $e .= $date[CDAY];
+ }
+ else
+ {
+ $b .= '01';
+ $e .= '31';
+ }
+ }
+ else
+ {
+ $b .= '01-01';
+ $e .= '12-31';
+ if (isset($date[CMONTH]) and $date[CMONTH]!=='any')
+ {
+ $res .= ' AND '.$this->calendar_levels[CMONTH]['sql'].'='.$date[CMONTH];
+ }
+ if (isset($date[CDAY]) and $date[CDAY]!=='any')
+ {
+ $res .= ' AND '.$this->calendar_levels[CDAY]['sql'].'='.$date[CDAY];
+ }
+ }
+ $res = " AND $this->date_field BETWEEN '$b' AND '$e 23:59:59'" . $res;
+ }
+ else
+ {
+ $res = ' AND '.$this->date_field.' IS NOT NULL';
+ if (isset($date[CMONTH]) and $date[CMONTH]!=='any')
+ {
+ $res .= ' AND '.$this->calendar_levels[CMONTH]['sql'].'='.$date[CMONTH];
+ }
+ if (isset($date[CDAY]) and $date[CDAY]!=='any')
+ {
+ $res .= ' AND '.$this->calendar_levels[CDAY]['sql'].'='.$date[CDAY];
+ }
+ }
+ return $res;
+}
+
+
+
+//--------------------------------------------------------- private members ---
+
+// returns an array with alll the days in a given month
+function get_all_days_in_month($year, $month)
+{
+ $md= array(1=>31,28,31,30,31,30,31,31,30,31,30,31);
+
+ if ( is_numeric($year) and $month==2)
+ {
+ $nb_days = $md[2];
+ if ( ($year%4==0) and ( ($year%100!=0) or ($year%400!=0) ) )
+ {
+ $nb_days++;
+ }
+ }
+ elseif ( is_numeric($month) )
+ {
+ $nb_days = $md[ $month ];
+ }
+ else
+ {
+ $nb_days = 31;
+ }
+ return $nb_days;
+}
+
+function build_global_calendar(&$tpl_var)
+{
+ global $page;
+ assert( count($page['chronology_date']) == 0 );
+ $query='SELECT DISTINCT(DATE_FORMAT('.$this->date_field.',"%Y%m")) as period,
+ COUNT( DISTINCT(id) ) as count';
+ $query.= $this->inner_sql;
+ $query.= $this->get_date_where();
+ $query.= '
+ GROUP BY period
+ ORDER BY YEAR('.$this->date_field.') DESC, MONTH('.$this->date_field.')';
+
+ $result = pwg_query($query);
+ $items=array();
+ while ($row = mysql_fetch_array($result))
+ {
+ $y = substr($row['period'], 0, 4);
+ $m = (int)substr($row['period'], 4, 2);
+ if ( ! isset($items[$y]) )
+ {
+ $items[$y] = array('nb_images'=>0, 'children'=>array() );
+ }
+ $items[$y]['children'][$m] = $row['count'];
+ $items[$y]['nb_images'] += $row['count'];
+ }
+ //echo ('
'. var_export($items, true) . '
');
+ if (count($items)==1)
+ {// only one year exists so bail out to year view
+ list($y) = array_keys($items);
+ $page['chronology_date'][CYEAR] = $y;
+ return false;
+ }
+
+ global $lang;
+ foreach ( $items as $year=>$year_data)
+ {
+ $chronology_date = array( $year );
+ $url = duplicate_index_url( array('chronology_date'=>$chronology_date) );
+
+ $nav_bar = $this->get_nav_bar_from_items( $chronology_date,
+ $year_data['children'], 'calCal', false, false, $lang['month'] );
+
+ $tpl_var['calendar_bars'][] =
+ array(
+ 'U_HEAD' => $url,
+ 'NB_IMAGES' => $year_data['nb_images'],
+ 'HEAD_LABEL' => $year,
+ 'NAV_BAR' => $nav_bar,
+ );
+ }
+ return true;
+}
+
+function build_year_calendar(&$tpl_var)
+{
+ global $page;
+ assert( count($page['chronology_date']) == 1 );
+ $query='SELECT DISTINCT(DATE_FORMAT('.$this->date_field.',"%m%d")) as period,
+ COUNT( DISTINCT(id) ) as count';
+ $query.= $this->inner_sql;
+ $query.= $this->get_date_where();
+ $query.= '
+ GROUP BY period';
+
+ $result = pwg_query($query);
+ $items=array();
+ while ($row = mysql_fetch_array($result))
+ {
+ $m = (int)substr($row['period'], 0, 2);
+ $d = substr($row['period'], 2, 2);
+ if ( ! isset($items[$m]) )
+ {
+ $items[$m] = array('nb_images'=>0, 'children'=>array() );
+ }
+ $items[$m]['children'][$d] = $row['count'];
+ $items[$m]['nb_images'] += $row['count'];
+ }
+ if (count($items)==1)
+ { // only one month exists so bail out to month view
+ list($m) = array_keys($items);
+ $page['chronology_date'][CMONTH] = $m;
+ return false;
+ }
+ global $lang;
+ foreach ( $items as $month=>$month_data)
+ {
+ $chronology_date = array( $page['chronology_date'][CYEAR], $month );
+ $url = duplicate_index_url( array('chronology_date'=>$chronology_date) );
+
+ $nav_bar = $this->get_nav_bar_from_items( $chronology_date,
+ $month_data['children'], 'calCal', false );
+
+ $tpl_var['calendar_bars'][] =
+ array(
+ 'U_HEAD' => $url,
+ 'NB_IMAGES' => $month_data['nb_images'],
+ 'HEAD_LABEL' => $lang['month'][$month],
+ 'NAV_BAR' => $nav_bar,
+ );
+ }
+ return true;
+
+}
+
+function build_month_calendar(&$tpl_var)
+{
+ global $page;
+ $query='SELECT DISTINCT(DAYOFMONTH('.$this->date_field.')) as period,
+ COUNT( DISTINCT(id) ) as count';
+ $query.= $this->inner_sql;
+ $query.= $this->get_date_where();
+ $query.= '
+ GROUP BY period';
+
+ $items=array();
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ {
+ $d = (int)$row['period'];
+ $items[$d] = array('nb_images'=>$row['count']);
+ }
+
+ foreach ( $items as $day=>$data)
+ {
+ $page['chronology_date'][CDAY]=$day;
+ $query = '
+SELECT id, file,tn_ext,path, width, height, DAYOFWEEK('.$this->date_field.')-1 as dow';
+ $query.= $this->inner_sql;
+ $query.= $this->get_date_where();
+ $query.= '
+ ORDER BY RAND()
+ LIMIT 0,1';
+ unset ( $page['chronology_date'][CDAY] );
+
+ $row = mysql_fetch_assoc(pwg_query($query));
+ $items[$day]['tn_url'] = get_thumbnail_url($row);
+ $items[$day]['file'] = $row['file'];
+ $items[$day]['path'] = $row['path'];
+ $items[$day]['tn_ext'] = @$row['tn_ext'];
+ $items[$day]['width'] = $row['width'];
+ $items[$day]['height'] = $row['height'];
+ $items[$day]['dow'] = $row['dow'];
+ }
+
+ global $lang, $conf;
+
+ if ( !empty($items)
+ and $conf['calendar_month_cell_width']>0
+ and $conf['calendar_month_cell_height']>0)
+ {
+ list($known_day) = array_keys($items);
+ $known_dow = $items[$known_day]['dow'];
+ $first_day_dow = ($known_dow-($known_day-1))%7;
+ if ($first_day_dow<0)
+ {
+ $first_day_dow += 7;
+ }
+ //first_day_dow = week day corresponding to the first day of this month
+ $wday_labels = $lang['day'];
+
+ // BEGIN - pass now in week starting Monday
+ if ($first_day_dow==0)
+ {
+ $first_day_dow = 6;
+ }
+ else
+ {
+ $first_day_dow -= 1;
+ }
+ array_push( $wday_labels, array_shift($wday_labels) );
+ // END - pass now in week starting Monday
+
+ $cell_width = $conf['calendar_month_cell_width'];
+ $cell_height = $conf['calendar_month_cell_height'];
+
+ $tpl_weeks = array();
+ $tpl_crt_week = array();
+
+ //fill the empty days in the week before first day of this month
+ for ($i=0; $i<$first_day_dow; $i++)
+ {
+ $tpl_crt_week[] = array();
+ }
+
+ for ( $day = 1;
+ $day <= $this->get_all_days_in_month(
+ $page['chronology_date'][CYEAR], $page['chronology_date'][CMONTH]
+ );
+ $day++)
+ {
+ $dow = ($first_day_dow + $day-1)%7;
+ if ($dow==0 and $day!=1)
+ {
+ $tpl_weeks[] = $tpl_crt_week; // add finished week to week list
+ $tpl_crt_week = array(); // start new week
+ }
+
+ if ( !isset($items[$day]) )
+ {// empty day
+ $tpl_crt_week[] =
+ array(
+ 'DAY' => $day
+ );
+ }
+ else
+ {
+ $thumb = get_thumbnail_path($items[$day]);
+ $tn_size = @getimagesize($thumb);
+
+ $tn_width = $tn_size[0];
+ $tn_height = $tn_size[1];
+
+ // now need to fit the thumbnail of size tn_size within
+ // a cell of size cell_size by playing with CSS position (left/top)
+ // and the width and height of .
+ $ratio_w = $tn_width/$cell_width;
+ $ratio_h = $tn_height/$cell_height;
+
+ $pos_top=$pos_left=0;
+ $css_style = '';
+
+ if ( $ratio_w>1 and $ratio_h>1)
+ {// cell completely smaller than the thumbnail so we will let the browser
+ // resize the thumbnail
+ if ($ratio_w > $ratio_h )
+ {// thumbnail ratio compared to cell -> wide format
+ $css_style = 'height:'.$cell_height.'px;';
+ $browser_img_width = $cell_height*$tn_width/$tn_height;
+ $pos_left = ($browser_img_width-$cell_width)/2;
+ }
+ else
+ {
+ $css_style = 'width:'.$cell_width.'px;';
+ $browser_img_height = $cell_width*$tn_height/$tn_width;
+ $pos_top = ($browser_img_height-$cell_height)/2;
+ }
+ }
+ else
+ {
+ $pos_left = ($tn_width-$cell_width)/2;
+ $pos_top = ($tn_height-$cell_height)/2;
+ }
+
+ if ( round($pos_left)!=0)
+ {
+ $css_style.='left:'.round(-$pos_left).'px;';
+ }
+ if ( round($pos_top)!=0)
+ {
+ $css_style.='top:'.round(-$pos_top).'px;';
+ }
+ $url = duplicate_index_url(
+ array(
+ 'chronology_date' =>
+ array(
+ $page['chronology_date'][CYEAR],
+ $page['chronology_date'][CMONTH],
+ $day
+ )
+ )
+ );
+
+ $tpl_crt_week[] =
+ array(
+ 'DAY' => $day,
+ 'DOW' => $dow,
+ 'NB_ELEMENTS' => $items[$day]['nb_images'],
+ 'IMAGE' => $items[$day]['tn_url'],
+ 'U_IMG_LINK' => $url,
+ 'IMAGE_STYLE' => $css_style,
+ 'IMAGE_ALT' => $items[$day]['file'],
+ );
+ }
+ }
+ //fill the empty days in the week after the last day of this month
+ while ( $dow<6 )
+ {
+ $tpl_crt_week[] = array();
+ $dow++;
+ }
+ $tpl_weeks[] = $tpl_crt_week;
+
+ $tpl_var['month_view'] =
+ array(
+ 'CELL_WIDTH' => $cell_width,
+ 'CELL_HEIGHT' => $cell_height,
+ 'wday_labels' => $wday_labels,
+ 'weeks' => $tpl_weeks,
+ );
+ }
+
+ return true;
+}
+
+}
+?>
diff --git a/BSF/include/calendar_weekly.class.php b/BSF/include/calendar_weekly.class.php
new file mode 100644
index 000000000..2077bc594
--- /dev/null
+++ b/BSF/include/calendar_weekly.class.php
@@ -0,0 +1,136 @@
+calendar_levels = array(
+ array(
+ 'sql'=> 'YEAR('.$this->date_field.')',
+ 'labels' => null
+ ),
+ array(
+ 'sql'=> 'WEEK('.$this->date_field.')+1',
+ 'labels' => $week_no_labels,
+ ),
+ array(
+ 'sql'=> 'DAYOFWEEK('.$this->date_field.')-1',
+ 'labels' => $lang['day']
+ ),
+ );
+ //Comment next lines for week starting on Sunday or if MySQL version<4.0.17
+ //WEEK(date,5) = "0-53 - Week 1=the first week with a Monday in this year"
+ $this->calendar_levels[CWEEK]['sql'] = 'WEEK('.$this->date_field.',5)+1';
+ $this->calendar_levels[CDAY]['sql'] = 'WEEKDAY('.$this->date_field.')';
+ array_push( $this->calendar_levels[CDAY]['labels'],
+ array_shift( $this->calendar_levels[CDAY]['labels'] ) );
+ }
+
+/**
+ * Generate navigation bars for category page
+ * @return boolean false to indicate that thumbnails where not included here
+ */
+function generate_category_content()
+{
+ global $conf, $page;
+
+ if ( count($page['chronology_date'])==0 )
+ {
+ $this->build_nav_bar(CYEAR); // years
+ }
+ if ( count($page['chronology_date'])==1 )
+ {
+ $this->build_nav_bar(CWEEK, array()); // week nav bar 1-53
+ }
+ if ( count($page['chronology_date'])==2 )
+ {
+ $this->build_nav_bar(CDAY); // days nav bar Mon-Sun
+ }
+ $this->build_next_prev();
+ return false;
+}
+
+
+/**
+ * Returns a sql where subquery for the date field
+ * @param int max_levels return the where up to this level
+ * (e.g. 2=only year and week in year)
+ * @return string
+ */
+function get_date_where($max_levels=3)
+{
+ global $page;
+ $date = $page['chronology_date'];
+ while (count($date)>$max_levels)
+ {
+ array_pop($date);
+ }
+ $res = '';
+ if (isset($date[CYEAR]) and $date[CYEAR]!=='any')
+ {
+ $y = $date[CYEAR];
+ $res = " AND $this->date_field BETWEEN '$y-01-01' AND '$y-12-31 23:59:59'";
+ }
+
+ if (isset($date[CWEEK]) and $date[CWEEK]!=='any')
+ {
+ $res .= ' AND '.$this->calendar_levels[CWEEK]['sql'].'='.$date[CWEEK];
+ }
+ if (isset($date[CDAY]) and $date[CDAY]!=='any')
+ {
+ $res .= ' AND '.$this->calendar_levels[CDAY]['sql'].'='.$date[CDAY];
+ }
+ if (empty($res))
+ {
+ $res = ' AND '.$this->date_field.' IS NOT NULL';
+ }
+ return $res;
+}
+
+}
+
+?>
diff --git a/BSF/include/category_cats.inc.php b/BSF/include/category_cats.inc.php
new file mode 100644
index 000000000..e7f9a168a
--- /dev/null
+++ b/BSF/include/category_cats.inc.php
@@ -0,0 +1,298 @@
+= SUBDATE(
+ CURRENT_DATE,INTERVAL '.$user['recent_period'].' DAY
+ )
+'.get_sql_condition_FandF
+ (
+ array
+ (
+ 'visible_categories' => 'id',
+ ),
+ 'AND'
+ ).'
+;';
+}
+else
+{
+ // $user['forbidden_categories'] including with USER_CACHE_CATEGORIES_TABLE
+ $query = '
+SELECT
+ id, name, permalink, representative_picture_id, comment, nb_images, uppercats,
+ date_last, max_date_last, count_images, count_categories
+ FROM '.CATEGORIES_TABLE.' INNER JOIN '.USER_CACHE_CATEGORIES_TABLE.'
+ ON id = cat_id and user_id = '.$user['id'].'
+ WHERE id_uppercat '.
+ (!isset($page['category']) ? 'is NULL' : '= '.$page['category']['id']).'
+'.get_sql_condition_FandF
+ (
+ array
+ (
+ 'visible_categories' => 'id',
+ ),
+ 'AND'
+ ).'
+ ORDER BY rank
+;';
+}
+
+$result = pwg_query($query);
+$categories = array();
+$category_ids = array();
+$image_ids = array();
+
+while ($row = mysql_fetch_assoc($result))
+{
+ $row['is_child_date_last'] = @$row['max_date_last']>@$row['date_last'];
+
+ if (isset($row['representative_picture_id'])
+ and is_numeric($row['representative_picture_id']))
+ { // if a representative picture is set, it has priority
+ $image_id = $row['representative_picture_id'];
+ }
+ else if ($conf['allow_random_representative'])
+ {// searching a random representant among elements in sub-categories
+ $query = '
+SELECT image_id
+ FROM '.CATEGORIES_TABLE.' AS c INNER JOIN '.IMAGE_CATEGORY_TABLE.' AS ic
+ ON ic.category_id = c.id';
+ $query.= '
+ WHERE (c.id='.$row['id'].' OR uppercats LIKE \''.$row['uppercats'].',%\')'
+ .get_sql_condition_FandF
+ (
+ array
+ (
+ 'forbidden_categories' => 'c.id',
+ 'visible_categories' => 'c.id',
+ 'visible_images' => 'image_id'
+ ),
+ "\n AND"
+ ).'
+ ORDER BY RAND()
+ LIMIT 0,1
+;';
+ $subresult = pwg_query($query);
+ if (mysql_num_rows($subresult) > 0)
+ {
+ list($image_id) = mysql_fetch_row($subresult);
+ }
+ }
+ else
+ { // searching a random representant among representant of sub-categories
+ $query = '
+SELECT representative_picture_id
+ FROM '.CATEGORIES_TABLE.' INNER JOIN '.USER_CACHE_CATEGORIES_TABLE.'
+ ON id = cat_id and user_id = '.$user['id'].'
+ WHERE uppercats LIKE \''.$row['uppercats'].',%\'
+ AND representative_picture_id IS NOT NULL'
+ .get_sql_condition_FandF
+ (
+ array
+ (
+ 'visible_categories' => 'id',
+ ),
+ "\n AND"
+ ).'
+ ORDER BY RAND()
+ LIMIT 0,1
+;';
+ $subresult = pwg_query($query);
+ if (mysql_num_rows($subresult) > 0)
+ {
+ list($image_id) = mysql_fetch_row($subresult);
+ }
+ }
+
+ if (isset($image_id))
+ {
+ $row['representative_picture_id'] = $image_id;
+ array_push($image_ids, $image_id);
+ array_push($categories, $row);
+ array_push($category_ids, $row['id']);
+ }
+ unset($image_id);
+}
+
+if ($conf['display_fromto'])
+{
+ $dates_of_category = array();
+ if (count($category_ids) > 0)
+ {
+ $query = '
+SELECT
+ category_id,
+ MIN(date_creation) AS date_creation_min,
+ MAX(date_creation) AS date_creation_max
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ INNER JOIN '.IMAGES_TABLE.' ON image_id = id
+ WHERE category_id IN ('.implode(',', $category_ids).')
+'.get_sql_condition_FandF
+ (
+ array
+ (
+ 'visible_categories' => 'category_id',
+ 'visible_images' => 'id'
+ ),
+ 'AND'
+ ).'
+ GROUP BY category_id
+;';
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ {
+ $dates_of_category[ $row['category_id'] ] = array(
+ 'from' => $row['date_creation_min'],
+ 'to' => $row['date_creation_max'],
+ );
+ }
+ }
+}
+
+if ($page['section']=='recent_cats')
+{
+ usort($categories, 'global_rank_compare');
+}
+if (count($categories) > 0)
+{
+ $thumbnail_src_of = array();
+
+ $query = '
+SELECT id, path, tn_ext
+ FROM '.IMAGES_TABLE.'
+ WHERE id IN ('.implode(',', $image_ids).')
+;';
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_assoc($result))
+ {
+ $thumbnail_src_of[$row['id']] = get_thumbnail_url($row);
+ }
+}
+
+if (count($categories) > 0)
+{
+ // Update filtered data
+ if (function_exists('update_cats_with_filtered_data'))
+ {
+ update_cats_with_filtered_data($categories);
+ }
+
+ $template->set_filename('index_category_thumbnails', 'mainpage_categories.tpl');
+
+ trigger_action('loc_begin_index_category_thumbnails', $categories);
+
+ foreach ($categories as $category)
+ {
+ if ($page['section']=='recent_cats')
+ {
+ $name = get_cat_display_name_cache($category['uppercats'], null, false);
+ }
+ else
+ {
+ $name = $category['name'];
+ }
+
+ $icon_ts = get_icon($category['max_date_last'], $category['is_child_date_last']);
+
+ $tpl_var =
+ array(
+ 'ID' => $category['id'],
+ 'TN_SRC' => $thumbnail_src_of[$category['representative_picture_id']],
+ 'ALT' => $category['name'],
+ 'ICON' => $icon_ts,
+
+ 'URL' => make_index_url(
+ array(
+ 'category' => $category
+ )
+ ),
+ 'CAPTION_NB_IMAGES' => get_display_images_count
+ (
+ $category['nb_images'],
+ $category['count_images'],
+ $category['count_categories'],
+ true,
+ ' '
+ ),
+ 'DESCRIPTION' =>
+ trigger_event('render_category_literal_description',
+ trigger_event('render_category_description',
+ @$category['comment'],
+ 'subcatify_category_description')),
+ 'NAME' => $name,
+ );
+
+ if ($conf['display_fromto'])
+ {
+ if (isset($dates_of_category[ $category['id'] ]))
+ {
+ $from = $dates_of_category[ $category['id'] ]['from'];
+ $to = $dates_of_category[ $category['id'] ]['to'];
+
+ if (!empty($from))
+ {
+ $info = '';
+
+ if ($from == $to)
+ {
+ $info = format_date($from);
+ }
+ else
+ {
+ $info = sprintf(
+ l10n('from %s to %s'),
+ format_date($from),
+ format_date($to)
+ );
+ }
+ $tpl_var['INFO_DATES'] = $info;
+ }
+ }
+ }//fromto
+
+ //plugins need to add/modify sth in this loop ?
+ $tpl_var = trigger_event('loc_index_category_thumbnail',
+ $tpl_var, $category );
+
+ $template->append( 'category_thumbnails', $tpl_var);
+ }
+
+ trigger_action('loc_end_index_category_thumbnails', $categories);
+ $template->assign_var_from_handle('CATEGORIES', 'index_category_thumbnails');
+}
+?>
diff --git a/BSF/include/category_default.inc.php b/BSF/include/category_default.inc.php
new file mode 100644
index 000000000..8dad04d90
--- /dev/null
+++ b/BSF/include/category_default.inc.php
@@ -0,0 +1,170 @@
+ 0)
+{
+ $query = '
+SELECT *
+ FROM '.IMAGES_TABLE.'
+ WHERE id IN ('.implode(',', $selection).')
+;';
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_assoc($result))
+ {
+ $row['rank'] = $page['rank_of'][ $row['id'] ];
+
+ array_push($pictures, $row);
+ }
+
+ usort($pictures, 'rank_compare');
+}
+
+if (count($pictures) > 0)
+{
+ // define category slideshow url
+ $row = reset($pictures);
+ $page['cat_slideshow_url'] =
+ add_url_params(
+ duplicate_picture_url(
+ array(
+ 'image_id' => $row['id'],
+ 'image_file' => $row['file']
+ ),
+ array('start')
+ ),
+ array('slideshow' =>
+ (isset($_GET['slideshow']) ? $_GET['slideshow']
+ : '' ))
+ );
+
+ if ($user['show_nb_comments'])
+ {
+ $query = '
+SELECT image_id, COUNT(*) AS nb_comments
+ FROM '.COMMENTS_TABLE.'
+ WHERE validated = \'true\'
+ AND image_id IN ('.implode(',', $selection).')
+ GROUP BY image_id
+;';
+ $nb_comments_of = simple_hash_from_query($query, 'image_id', 'nb_comments');
+ }
+}
+
+// template thumbnail initialization
+$template->set_filenames( array( 'index_thumbnails' => 'thumbnails.tpl',));
+
+trigger_action('loc_begin_index_thumbnails', $pictures);
+
+foreach ($pictures as $row)
+{
+ $thumbnail_url = get_thumbnail_url($row);
+
+ // link on picture.php page
+ $url = duplicate_picture_url(
+ array(
+ 'image_id' => $row['id'],
+ 'image_file' => $row['file']
+ ),
+ array('start')
+ );
+
+ $tpl_var =
+ array(
+ 'ID' => $row['id'],
+ 'IMAGE' => $thumbnail_url,
+ 'IMAGE_ALT' => $row['file'],
+ 'IMAGE_TITLE' => get_thumbnail_title($row),
+ 'IMAGE_TS' => get_icon($row['date_available']),
+ 'U_IMG_LINK' => $url,
+ );
+
+ if ($user['show_nb_hits'])
+ {
+ $tpl_var['NB_HITS'] = $row['hit'];
+ }
+
+ if ($conf['show_thumbnail_caption'])
+ {
+ // name of the picture
+ if (isset($row['name']) and $row['name'] != '')
+ {
+ $name = $row['name'];
+ }
+ else
+ {
+ $name = str_replace('_', ' ', get_filename_wo_extension($row['file']));
+ }
+
+ switch ($page['section'])
+ {
+ case 'best_rated' :
+ {
+ $name = '('.$row['average_rate'].') '.$name;
+ break;
+ }
+ case 'most_visited' :
+ {
+ if ( !$user['show_nb_hits']) {
+ $name = '('.$row['hit'].') '.$name;
+ }
+ break;
+ }
+ }
+
+ $tpl_var['ELEMENT_NAME'] = $name;
+ }
+
+ if ( isset($nb_comments_of) )
+ {
+ $row['nb_comments'] = isset($nb_comments_of[$row['id']])
+ ? (int)$nb_comments_of[$row['id']] : 0;
+ $tpl_var['NB_COMMENTS'] = $row['nb_comments'];
+ }
+
+ //plugins need to add/modify sth in this loop ?
+ $tpl_var = trigger_event('loc_index_thumbnail', $tpl_var, $row);
+
+ $template->append('thumbnails', $tpl_var);
+}
+
+trigger_action('loc_end_index_thumbnails', $pictures);
+$template->assign_var_from_handle('THUMBNAILS', 'index_thumbnails');
+
+pwg_debug('end include/category_default.inc.php');
+?>
diff --git a/BSF/include/class_smtp_mail.inc.php b/BSF/include/class_smtp_mail.inc.php
new file mode 100644
index 000000000..06ef69efd
--- /dev/null
+++ b/BSF/include/class_smtp_mail.inc.php
@@ -0,0 +1,157 @@
+host = $host;
+ $this->user = $user;
+ $this->password = $password;
+ $this->email_webmaster = $email_webmaster;
+ }
+
+ // Adaptation of server_parse
+ function server_parse($expected_response)
+ {
+ if ($this->no_error)
+ {
+ $server_response = '';
+ while (substr($server_response, 3, 1) != ' ')
+ {
+ if (!($server_response = fgets($this->socket, 256)))
+ {
+ trigger_error('Couldn\'t get mail server response codes.', E_USER_WARNING);
+ $this->no_error = false;
+ }
+ }
+ }
+
+ if ($this->no_error)
+ {
+ if (!(substr($server_response, 0, 3) == $expected_response))
+ {
+ trigger_error('Unable to send e-mail. Error message reported by the SMTP server: "'.$server_response.'"', E_USER_WARNING);
+ $this->no_error = false;
+ }
+ }
+ return $this->no_error;
+ }
+
+ function server_write($s)
+ {
+ $this->no_error = $this->no_error && (fwrite($this->socket, $s) !== false);
+ return $this->no_error;
+ }
+
+ // Adaptation of pun_mail
+ function mail($to, $subject, $message, $headers = '')
+ {
+ $this->no_error = true;
+
+ $recipients = explode(',', $to);
+
+ // Are we using port 25 or a custom port?
+ if (strpos($this->host, ':') !== false)
+ {
+ list($smtp_host, $smtp_port) = explode(':', $this->host);
+ }
+ else
+ {
+ $smtp_host = $this->host;
+ $smtp_port = 25;
+ }
+
+ if ($this->socket = fsockopen($smtp_host, $smtp_port, $errno, $errstr, 15))
+ {
+ $this->server_parse('220');
+
+ if (!empty($this->user) && !empty($this->password))
+ {
+ $this->server_write('EHLO '.$smtp_host."\r\n");
+ $this->server_parse('250');
+
+ $this->server_write('AUTH LOGIN'."\r\n");
+ $this->server_parse('334');
+
+ $this->server_write(base64_encode($this->user)."\r\n");
+ $this->no_error = $this->no_error && $this->no_error = $this->server_parse('334');
+
+ $this->server_write(base64_encode($this->password)."\r\n");
+ $this->server_parse('235');
+ }
+ else
+ {
+ $this->server_write('HELO '.$smtp_host."\r\n");
+ $this->server_parse('250');
+ }
+
+ $this->server_write('MAIL FROM: <'.$this->email_webmaster.'>'."\r\n");
+ $this->server_parse('250');
+
+ if (preg_match('/^\s*to\s*:.*/mi', $headers) === 0)
+ {
+ $to_header = 'To: '.implode(',', array_map(create_function('$email','return "<".$email.">";'), $recipients));
+ }
+ else
+ {
+ $to_header = '';
+ }
+
+ @reset($recipients);
+ while (list(, $email) = @each($recipients))
+ {
+ $this->server_write('RCPT TO: <'.$email.'>'."\r\n");
+ $this->server_parse('250');
+ }
+
+ $this->server_write('DATA'."\r\n");
+ $this->server_parse('354');
+
+ $this->server_write('Subject: '.$subject."\r\n".(empty($to_header) ? "" : $to_header."\r\n").$headers."\r\n\r\n".$message."\r\n");
+ $this->server_write('.'."\r\n");
+ $this->server_parse('250');
+
+ $this->server_write('QUIT'."\r\n");
+ fclose($this->socket);
+ }
+ else
+ {
+ trigger_error('Could not connect to smtp host "'.$this->host.'" ('.$errno.') ('.$errstr.')', E_USER_WARNING);
+ $this->no_error = false;;
+ }
+
+ return $this->no_error;
+ }
+}
+
+?>
diff --git a/BSF/include/common.inc.php b/BSF/include/common.inc.php
new file mode 100644
index 000000000..45c6a3845
--- /dev/null
+++ b/BSF/include/common.inc.php
@@ -0,0 +1,283 @@
+= 5.1.0RC1
+ 'hash_hmac', //(hash) - enabled by default as of PHP 5.1.2
+ 'preg_last_error', // PHP 5 >= 5.2.0
+ 'file_put_contents', //PHP5
+ ) as $func)
+{
+ if (!function_exists($func))
+ {
+ include_once(PHPWG_ROOT_PATH . 'include/php_compat/'.$func.'.php');
+ }
+}
+
+include(PHPWG_ROOT_PATH . 'include/config_default.inc.php');
+@include(PHPWG_ROOT_PATH. 'include/config_local.inc.php');
+include(PHPWG_ROOT_PATH . 'include/constants.php');
+include(PHPWG_ROOT_PATH . 'include/functions.inc.php');
+include(PHPWG_ROOT_PATH . 'include/template.class.php');
+
+// Database connection
+mysql_connect( $cfgHote, $cfgUser, $cfgPassword )
+or die ( "Could not connect to database server" );
+mysql_select_db( $cfgBase )
+or die ( "Could not connect to database" );
+
+defined('PWG_CHARSET') and defined('DB_CHARSET')
+ or die('PWG_CHARSET and/or DB_CHARSET is not defined');
+if ( version_compare(mysql_get_server_info(), '4.1.0', '>=') )
+{
+ if (DB_CHARSET!='')
+ {
+ pwg_query('SET NAMES "'.DB_CHARSET.'"');
+ }
+}
+else
+{
+ if ( strtolower(PWG_CHARSET)!='iso-8859-1' )
+ {
+ die('PWG supports only iso-8859-1 charset on MySql version '.mysql_get_server_info());
+ }
+}
+
+//
+// Setup gallery wide options, if this fails then we output a CRITICAL_ERROR
+// since basic gallery information is not available
+//
+load_conf_from_db();
+load_plugins();
+
+include(PHPWG_ROOT_PATH.'include/user.inc.php');
+
+
+// language files
+load_language('common.lang');
+if (defined('IN_ADMIN') and IN_ADMIN)
+{
+ load_language('admin.lang');
+}
+trigger_action('loading_lang');
+load_language('local.lang');
+
+// only now we can set the localized username of the guest user (and not in
+// include/user.inc.php)
+if (is_a_guest())
+{
+ $user['username'] = l10n('guest');
+}
+
+// template instance
+if
+ (
+ defined('IN_ADMIN') and IN_ADMIN and
+ isset($user['admin_template']) and
+ isset($user['admin_theme'])
+ )
+{
+ // Admin template
+ $template = new Template(PHPWG_ROOT_PATH.'template/'.$user['admin_template'], $user['admin_theme'] );
+}
+else
+{
+ // Classic template
+ $template = new Template(PHPWG_ROOT_PATH.'template/'.$user['template'], $user['theme'] );
+}
+
+if (isset($user['internal_status']['guest_must_be_guest'])
+ and
+ $user['internal_status']['guest_must_be_guest'] === true)
+{
+ $header_msgs[] = l10n('guest_must_be_guest');
+}
+
+if ($conf['gallery_locked'])
+{
+ $header_msgs[] = l10n('gallery_locked_message');
+
+ if ( script_basename() != 'identification' and !is_admin() )
+ {
+ set_status_header(503, 'Service Unavailable');
+ @header('Retry-After: 900');
+ echo l10n('gallery_locked_message')
+ .'.';
+ exit();
+ }
+}
+
+if ($conf['check_upgrade_feed']
+ and defined('PHPWG_IN_UPGRADE')
+ and PHPWG_IN_UPGRADE)
+{
+
+ // 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?
+ if (count(array_diff($existing, $applied)) > 0)
+ {
+ $header_msgs[] = 'Some database upgrades are missing, '
+ .'upgrade now';
+ }
+}
+
+if (is_adviser())
+{
+ $header_msgs[] = l10n('adviser_mode_enabled');
+}
+
+if (count($header_msgs) > 0)
+{
+ $template->assign('header_msgs', $header_msgs);
+ $header_msgs=array();
+}
+
+if (!empty($conf['filter_pages']) and get_filter_page_value('used'))
+{
+ include(PHPWG_ROOT_PATH.'include/functions_filter.inc.php');
+ include(PHPWG_ROOT_PATH.'include/filter.inc.php');
+}
+else
+{
+ $filter['enabled'] = false;
+}
+
+if (isset($conf['header_notes']))
+{
+ $header_notes = array_merge($header_notes, $conf['header_notes']);
+}
+
+// default event handlers
+add_event_handler('render_category_literal_description', 'render_category_literal_description');
+add_event_handler('render_category_description', 'render_category_description');
+add_event_handler('render_comment_content', 'htmlspecialchars');
+add_event_handler('render_comment_content', 'parse_comment_content');
+add_event_handler('render_comment_author', 'strip_tags');
+trigger_action('init');
+?>
diff --git a/BSF/include/config_default.inc.php b/BSF/include/config_default.inc.php
new file mode 100644
index 000000000..cb78729ef
--- /dev/null
+++ b/BSF/include/config_default.inc.php
@@ -0,0 +1,700 @@
+ 'PWG website',
+// 'http://forum.phpwebgallery.net' => 'PWG forum',
+// 'http://phpwebgallery.net/doc' => 'PWG wiki'
+// );
+//
+// Advenced use:
+// You can also used special options. Instead to pass a string like parameter value
+// you can pass a array with different optional parameter values
+// $conf['links'] = array(
+// 'http://piwigo.org' => array('label' => 'PWG website', 'new_window' => false, 'eval_visible' => 'return true;'),
+// 'http://forum.phpwebgallery.net' => array('label' => 'For ADMIN', 'new_window' => true, 'eval_visible' => 'return is_admin();'),
+// 'http://phpwebgallery.net/doc' => array('label' => 'For Guest', 'new_window' => true, 'eval_visible' => 'return is_a_guest();'),
+// 'http://download.gna.org/phpwebgallery/' =>
+// array('label' => 'PopUp', 'new_window' => true,
+// 'nw_name' => 'PopUp', 'nw_features' => 'width=800,height=450,location=no,status=no,toolbar=no,scrollbars=no,menubar=no'),
+// );
+// Parameters:
+// 'label':
+// Label to display for the link, must be defined
+// 'new_window':
+// If true open link on tab/window
+// [Default value is true if it's not defined]
+// 'nw_name':
+// Name use when new_window is true
+// [Default value is '' if it's not defined]
+// 'nw_features':
+// features use when new_window is true
+// [Default value is '' if it's not defined]
+// 'eval_visible':
+// It's php code witch must return if the link is visible or not
+// [Default value is true if it's not defined]
+//
+// Equivalence:
+// $conf['links'] = array(
+// 'http://piwigo.org' => 'PWG website',
+// );
+// $conf['links'] = array(
+// 'http://piwigo.org' => array('label' => 'PWG website', 'new_window' => false, 'visible' => 'return true;'),
+// );
+//
+// If the array is empty, the "Links" box won't be displayed on the main
+// page.
+$conf['links'] = array();
+
+// random_index_redirect: list of 'internal' links to use when no section is defined on index.php.
+// An example is the best than a long explanation :
+//
+// for each link is associated a php condition
+// '' condition is equivalent to 'return true;'
+// $conf['random_index_redirect'] = array(
+// PHPWG_ROOT_PATH.'index.php?/best_rated' => 'return true;',
+// PHPWG_ROOT_PATH.'index.php?/recent_pics' => 'return is_a_guest();',
+// PHPWG_ROOT_PATH.'random.php' => '',
+// PHPWG_ROOT_PATH.'index.php?/categories' => '',
+// );
+$conf['random_index_redirect'] = array();
+
+// List of notes to display on all header page
+// example $conf['header_notes'] = array('Test', 'Hello');
+$conf['header_notes'] = array();
+
+// show_thumbnail_caption : on thumbnails page, show thumbnail captions ?
+$conf['show_thumbnail_caption'] = true;
+
+// show_picture_name_on_title : on picture presentation page, show picture
+// name ?
+$conf['show_picture_name_on_title'] = true;
+
+// display_fromto: in subcatify mode, display the date creation bounds of a
+// category.
+$conf['display_fromto'] = false;
+
+// allow_random_representative : do you wish Piwigo to search among
+// categories elements a new representative at each reload ?
+//
+// If false, an element is randomly or manually chosen to represent its
+// category and remains the representative as long as an admin does not
+// change it.
+//
+// Warning : setting this parameter to true is CPU consuming. Each time you
+// change the value of this parameter from false to true, an administrator
+// must update categories informations in screen [Admin > General >
+// Maintenance].
+$conf['allow_random_representative'] = false;
+
+// allow_html_descriptions : authorize administrators to use HTML in
+// category and element description.
+$conf['allow_html_descriptions'] = true;
+
+// prefix_thumbnail : string before filename. Thumbnail's prefix must only
+// contain characters among : a to z (case insensitive), "-" or "_".
+$conf['prefix_thumbnail'] = 'TN-';
+
+// users_page: how many users to display in screen
+// Administration>Identification>Users?
+$conf['users_page'] = 20;
+
+// image level permissions available in the admin interface
+$conf['available_permission_levels'] = array(0,1,2,4,8);
+
+// mail_options: only set it true if you have a send mail warning with
+// "options" parameter missing on mail() function execution.
+$conf['mail_options'] = false;
+
+// send_bcc_mail_webmaster: send bcc mail to webmaster. Set true for debug
+// or test.
+$conf['send_bcc_mail_webmaster'] = false;
+
+// default_email_format:
+// Define the default email format use to send email
+// Value could be text/plain or text/html
+$conf['default_email_format'] = 'text/html';
+
+// define the name of sender mail:
+// If value is empty, gallery title is used
+$conf['mail_sender_name'] = '';
+
+// smtp configuration
+// (work if fsockopen function is allowed for smtp port)
+// smtp_host: smtp server host
+// if null, regular mail function is used
+// format: hoststring[:port]
+// exemple: smtp.pwg.net:21
+// smtp_user/smtp_password: user & password for smtp identication
+$conf['smtp_host'] = '';
+$conf['smtp_user'] = '';
+$conf['smtp_password'] = '';
+
+
+// check_upgrade_feed: check if there are database upgrade required. Set to
+// true, a message will strongly encourage you to upgrade your database if
+// needed.
+//
+// This configuration parameter is set to true in BSF branch and to false
+// elsewhere.
+$conf['check_upgrade_feed'] = true;
+
+// rate_items: available rates for a picture
+$conf['rate_items'] = array(0,1,2,3,4,5);
+
+// Define default method to use ('http' or 'html' in order to do redirect)
+$conf['default_redirect_method'] = 'http';
+
+// +-----------------------------------------------------------------------+
+// | metadata |
+// +-----------------------------------------------------------------------+
+
+// show_iptc: Show IPTC metadata on picture.php if asked by user
+$conf['show_iptc'] = false;
+
+// show_iptc_mapping : is used for showing IPTC metadata on picture.php
+// page. For each key of the array, you need to have the same key in the
+// $lang array. For example, if my first key is 'iptc_keywords' (associated
+// to '2#025') then you need to have $lang['iptc_keywords'] set in
+// language/$user['language']/common.lang.php. If you don't have the lang
+// var set, the key will be simply displayed
+//
+// To know how to associated iptc_field with their meaning, use
+// tools/metadata.php
+$conf['show_iptc_mapping'] = array(
+ 'iptc_keywords' => '2#025',
+ 'iptc_caption_writer' => '2#122',
+ 'iptc_byline_title' => '2#085',
+ 'iptc_caption' => '2#120'
+ );
+
+// use_iptc: Use IPTC data during database synchronization with files
+// metadata
+$conf['use_iptc'] = false;
+
+// use_iptc_mapping : in which IPTC fields will Piwigo find image
+// information ? This setting is used during metadata synchronisation. It
+// associates a piwigo_images column name to a IPTC key
+$conf['use_iptc_mapping'] = array(
+ 'keywords' => '2#025',
+ 'date_creation' => '2#055',
+ 'author' => '2#122',
+ 'name' => '2#005',
+ 'comment' => '2#120'
+ );
+
+// show_exif: Show EXIF metadata on picture.php (table or line presentation
+// avalaible)
+$conf['show_exif'] = true;
+
+// show_exif_fields : in EXIF fields, you can choose to display fields in
+// sub-arrays, for example ['COMPUTED']['ApertureFNumber']. for this, add
+// 'COMPUTED;ApertureFNumber' in $conf['show_exif_fields']
+//
+// The key displayed in picture.php will be $lang['exif_field_Make'] for
+// example and if it exists. For compound fields, only take into account the
+// last part : for key 'COMPUTED;ApertureFNumber', you need
+// $lang['exif_field_ApertureFNumber']
+//
+// for PHP version newer than 4.1.2 :
+// $conf['show_exif_fields'] = array('CameraMake','CameraModel','DateTime');
+//
+$conf['show_exif_fields'] = array(
+ 'Make',
+ 'Model',
+ 'DateTimeOriginal',
+ 'COMPUTED;ApertureFNumber'
+ );
+
+// use_exif: Use EXIF data during database synchronization with files
+// metadata
+$conf['use_exif'] = true;
+
+// use_exif_mapping: same behaviour as use_iptc_mapping
+$conf['use_exif_mapping'] = array(
+ 'date_creation' => 'DateTimeOriginal'
+ );
+
+// +-----------------------------------------------------------------------+
+// | sessions |
+// +-----------------------------------------------------------------------+
+
+// session_use_cookies: specifies to use cookie to store
+// the session id on client side
+$conf['session_use_cookies'] = true;
+
+// session_use_only_cookies: specifies to only use cookie to store
+// the session id on client side
+$conf['session_use_only_cookies'] = true;
+
+// session_use_trans_sid: do not use transparent session id support
+$conf['session_use_trans_sid'] = false;
+
+// session_name: specifies the name of the session which is used as cookie name
+$conf['session_name'] = 'pwg_id';
+
+// session_save_handler: comment the line below
+// to use file handler for sessions.
+$conf['session_save_handler'] = 'db';
+
+// authorize_remembering : permits user to stay logged for a long time. It
+// creates a cookie on client side.
+$conf['authorize_remembering'] = true;
+
+// remember_me_name: specifies the name of the cookie used to stay logged
+$conf['remember_me_name'] = 'pwg_remember';
+
+// remember_me_length : time of validity for "remember me" cookies, in
+// seconds.
+$conf['remember_me_length'] = 5184000;
+
+// session_length : time of validity for normal session, in seconds.
+$conf['session_length'] = 3600;
+
+// +-----------------------------------------------------------------------+
+// | debug |
+// +-----------------------------------------------------------------------+
+
+// show_queries : for debug purpose, show queries and execution times
+$conf['show_queries'] = false;
+
+// show_gt : display generation time at the bottom of each page
+$conf['show_gt'] = true;
+
+// debug_l10n : display a warning message each time an unset language key is
+// accessed
+$conf['debug_l10n'] = false;
+
+// activate template debugging - a new window will appear
+$conf['debug_template'] = false;
+
+// die_on_sql_error: if an SQL query fails, should everything stop?
+$conf['die_on_sql_error'] = true;
+
+// +-----------------------------------------------------------------------+
+// | authentication |
+// +-----------------------------------------------------------------------+
+
+// apache_authentication : use Apache authentication as reference instead of
+// users table ?
+$conf['apache_authentication'] = false;
+
+// users_table: which table is the reference for users? Can be a different
+// table than Piwigo table
+//
+// If you decide to use another table than the default one, you need to
+// prepare your database by deleting some datas :
+//
+// delete from piwigo_user_access;
+// delete from piwigo_user_cache;
+// delete from piwigo_user_feed;
+// delete from piwigo_user_group;
+// delete from piwigo_user_infos;
+// delete from piwigo_sessions;
+// delete from piwigo_rate;
+// update piwigo_images set average_rate = null;
+// delete from piwigo_caddie;
+// delete from piwigo_favorites;
+//
+// All informations contained in these tables and column are related to
+// piwigo_users table.
+$conf['users_table'] = $prefixeTable.'users';
+
+// Other tables can be changed, if you define associated constants
+// Example:
+// define('USER_INFOS_TABLE', 'pwg_main'.'user_infos');
+
+
+// user_fields : mapping between generic field names and table specific
+// field names. For example, in PWG, the mail address is names
+// "mail_address" and in punbb, it's called "email".
+$conf['user_fields'] = array(
+ 'id' => 'id',
+ 'username' => 'username',
+ 'password' => 'password',
+ 'email' => 'mail_address'
+ );
+
+// pass_convert : function to crypt or hash the clear user password to store
+// it in the database
+$conf['pass_convert'] = create_function('$s', 'return md5($s);');
+
+// guest_id : id of the anonymous user
+$conf['guest_id'] = 2;
+// default_user_id : id of user used for default value
+$conf['default_user_id'] = $conf['guest_id'];
+
+// webmaster_id : webmaster'id.
+$conf['webmaster_id'] = 1;
+
+// allow to use adviser mode
+$conf['allow_adviser'] = false;
+
+// does the guest have access ?
+// (not a security feature, set your categories "private" too)
+// If false it'll be redirected from index.php to identification.php
+$conf['guest_access'] = true;
+
+// +-----------------------------------------------------------------------+
+// | upload |
+// +-----------------------------------------------------------------------+
+
+// upload_maxfilesize: maximum filesize for the uploaded pictures. In
+// kilobytes.
+$conf['upload_maxfilesize'] = 200;
+
+// upload_maxheight: maximum height authorized for the uploaded images. In
+// pixels.
+$conf['upload_maxheight'] = 800;
+
+// upload_maxwidth: maximum width authorized for the uploaded images. In
+// pixels.
+$conf['upload_maxwidth'] = 800;
+
+// upload_maxheight_thumbnail: maximum height authorized for the uploaded
+// thumbnails
+$conf['upload_maxheight_thumbnail'] = 128;
+
+// upload_maxwidth_thumbnail: maximum width authorized for the uploaded
+// thumbnails
+$conf['upload_maxwidth_thumbnail'] = 128;
+
+// +-----------------------------------------------------------------------+
+// | history |
+// +-----------------------------------------------------------------------+
+
+// nb_logs_page : how many logs to display on a page
+$conf['nb_logs_page'] = 300;
+
+// +-----------------------------------------------------------------------+
+// | urls |
+// +-----------------------------------------------------------------------+
+
+// question_mark_in_urls : the generated urls contain a ? sign. This can be
+// changed to false only if the server translates PATH_INFO variable
+// (depends on the server AcceptPathInfo directive configuration)
+$conf['question_mark_in_urls'] = true;
+
+// php_extension_in_urls : if true, the urls generated for picture and
+// category will not contain the .php extension. This will work only if
+// .htaccess defines Options +MultiViews parameter or url rewriting rules
+// are active.
+$conf['php_extension_in_urls'] = true;
+
+// category_url_style : one of 'id' (default) or 'id-name'. 'id-name'
+// means that an simplified ascii represntation of the category name will
+// appear in the url
+$conf['category_url_style'] = 'id';
+
+// picture_url_style : one of 'id' (default), 'id-file' or 'file'. 'id-file'
+// or 'file' mean that the file name (without extension will appear in the
+// url). Note that one aditionnal sql query will occur if 'file' is choosen.
+// Note that you might experience navigation issues if you choose 'file'
+// and your file names are not unique
+$conf['picture_url_style'] = 'id';
+
+// tag_url_style : one of 'id-tag' (default), 'id' or 'tag'.
+// Note that if you choose 'tag' and the url (ascii) representation of your
+// tags is not unique, all tags with the same url representation will be shown
+$conf['tag_url_style'] = 'id-tag';
+
+// +-----------------------------------------------------------------------+
+// | tags |
+// +-----------------------------------------------------------------------+
+
+// full_tag_cloud_items_number: number of tags to show in the full tag
+// cloud. Only the most represented tags will be shown
+$conf['full_tag_cloud_items_number'] = 200;
+
+// menubar_tag_cloud_items_number: number of tags to show in the tag
+// cloud in the menubar. Only the most represented tags will be shown
+$conf['menubar_tag_cloud_items_number'] = 100;
+
+// content_tag_cloud_items_number: number of tags to show in the tag
+// cloud on the content page. Only the most represented tags will be shown
+$conf['content_tag_cloud_items_number'] = 12;
+
+// tags_levels: number of levels to use for display. Each level is bind to a
+// CSS class tagLevelX.
+$conf['tags_levels'] = 5;
+
+// +-----------------------------------------------------------------------+
+// | Notification by mail |
+// +-----------------------------------------------------------------------+
+
+// Default Value for nbm user
+$conf['nbm_default_value_user_enabled'] = false;
+
+// Search list user to send quickly (List all without to check news)
+// More quickly but less fun to use
+$conf['nbm_list_all_enabled_users_to_send'] = false;
+
+// Max time used on one pass in order to send mails.
+// Timeout delay ratio.
+$conf['nbm_max_treatment_timeout_percent'] = 0.8;
+
+// If timeout cannot be compite with nbm_max_treatment_timeout_percent,
+// nbm_treatment_timeout_default is used by default
+$conf['nbm_treatment_timeout_default'] = 20;
+
+// Parameters used in get_recent_post_dates for the 2 kind of notification
+$conf['recent_post_dates'] = array(
+ 'RSS' => array('max_dates' => 5, 'max_elements' => 6, 'max_cats' => 6),
+ 'NBM' => array('max_dates' => 7, 'max_elements' => 3, 'max_cats' => 9)
+ );
+
+// +-----------------------------------------------------------------------+
+// | Set admin layout |
+// +-----------------------------------------------------------------------+
+
+$conf['admin_layout'] = 'yoga/admin';
+
+// should we load the active plugins ? true=Yes, false=No
+$conf['enable_plugins']=true;
+
+// Web services are allowed (true) or completely forbidden (false)
+$conf['allow_web_services'] = true;
+
+// Maximum number of images to be returned foreach call to the web service
+$conf['ws_max_images_per_page'] = 500;
+
+// On Access control false / Admim Web Service need Php cURL extension
+// Controls are done on public basis or
+// if connected on member authorization basis
+$conf['ws_access_control'] = false;
+
+// Additionnal controls are made based on Web Service Access Table
+// Max returned rows number ( > 0 )
+$conf['ws_allowed_limit'] = array(1,2,3,5,10,25);
+
+// By default can be delayed by 0, 1, 2, 3, 5, 7, 14 or 30 days
+// 0 it's Now(), don't remove that one
+$conf['ws_postponed_start'] = array(0,1,2,3,5,7,14,30); /* In days */
+
+// By default 10, 5, 2, 1 year(s) or 6, 3, 1 month(s)
+// or 15, 10, 7, 5, 1, 0 day(s)
+// 0 it's temporary closed (Useful for one access)
+$conf['ws_durations'] = array(3650,1825,730,365,182,91,30,15,10,7,5,1,0);
+
+// +-----------------------------------------------------------------------+
+// | Filter |
+// +-----------------------------------------------------------------------+
+// $conf['filter_pages'] contains configuration for each pages
+// o If values are not defined for a specific page, default value are used
+// o Array is composed by the basename of each page without extention
+// o List of value names:
+// - used: filter function are used
+// (if false nothing is done [start, cancel, stop, ...]
+// - cancel: cancel current started filter
+// - add_notes: add notes about current started filter on the header
+// o Empty configuration in order to disable completely filter functions
+// No filter, No icon,...
+// $conf['filter_pages'] = array();
+$conf['filter_pages'] = array
+ (
+ // Default page
+ 'default' => array(
+ 'used' => true, 'cancel' => false, 'add_notes' => false),
+ // Real pages
+ 'index' => array('add_notes' => true),
+ 'tags' => array('add_notes' => true),
+ 'search' => array('add_notes' => true),
+ 'comments' => array('add_notes' => true),
+ 'admin' => array('used' => false),
+ 'feed' => array('used' => false),
+ 'notification' => array('used' => false),
+ 'nbm' => array('used' => false),
+ 'popuphelp' => array('used' => false),
+ 'profile' => array('used' => false),
+ 'web_service' => array('used' => false),
+ 'ws' => array('used' => false),
+ 'identification' => array('cancel' => true),
+ 'install' => array('cancel' => true),
+ 'password' => array('cancel' => true),
+ 'register' => array('cancel' => true),
+ 'upgrade_feed' => array('cancel' => true),
+ );
+
+// +-----------------------------------------------------------------------+
+// | Slideshow |
+// +-----------------------------------------------------------------------+
+// slideshow_period : waiting time in seconds before loading a new page
+// during automated slideshow
+// slideshow_period_min, slideshow_period_max are bounds of slideshow_period
+// slideshow_period_step is the step of navigation between min and max
+$conf['slideshow_period_min'] = 1;
+$conf['slideshow_period_max'] = 10;
+$conf['slideshow_period_step'] = 1;
+$conf['slideshow_period'] = 4;
+
+// slideshow_repeat : slideshow loops on pictures
+$conf['slideshow_repeat'] = true;
+
+// $conf['light_slideshow'] indicates to use slideshow.tpl in state of
+// picture.tpl for slideshow
+// Take care to have slideshow.tpl in all available templates
+// Or set it false.
+// Check if Picture's plugins are compliant with it
+// Every plugin from 1.7 would be design to manage light_slideshow case.
+$conf['light_slideshow'] = true;
+
+// the local data directory is used to store data such as compiled templates
+// or other plugin variables etc
+$conf['local_data_dir'] = dirname(dirname(__FILE__)).'/_data';
+
+// if true, some language strings are replaced during template compilation
+// (insted of template output). this results in better performance. however
+// any change in the language file will not be propagated until you purge
+// the compiled templates from the admin / maintenance menu
+$conf['compiled_template_cache_language'] = false;
+
+?>
\ No newline at end of file
diff --git a/BSF/include/constants.php b/BSF/include/constants.php
new file mode 100644
index 000000000..42854a9d4
--- /dev/null
+++ b/BSF/include/constants.php
@@ -0,0 +1,112 @@
+
diff --git a/BSF/include/feedcreator.class.php b/BSF/include/feedcreator.class.php
new file mode 100644
index 000000000..f274b8417
--- /dev/null
+++ b/BSF/include/feedcreator.class.php
@@ -0,0 +1,1541 @@
+useCached(); // use cached version if age<1 hour
+$rss->title = "PHP news";
+$rss->description = "daily news from the PHP scripting world";
+
+//optional
+$rss->descriptionTruncSize = 500;
+$rss->descriptionHtmlSyndicated = true;
+
+$rss->link = "http://www.dailyphp.net/news";
+$rss->syndicationURL = "http://www.dailyphp.net/".$_SERVER["PHP_SELF"];
+
+$image = new FeedImage();
+$image->title = "dailyphp.net logo";
+$image->url = "http://www.dailyphp.net/images/logo.gif";
+$image->link = "http://www.dailyphp.net";
+$image->description = "Feed provided by dailyphp.net. Click to visit.";
+
+//optional
+$image->descriptionTruncSize = 500;
+$image->descriptionHtmlSyndicated = true;
+
+$rss->image = $image;
+
+// get your news items from somewhere, e.g. your database:
+mysql_select_db($dbHost, $dbUser, $dbPass);
+$res = mysql_query("SELECT * FROM news ORDER BY newsdate DESC");
+while ($data = mysql_fetch_object($res)) {
+ $item = new FeedItem();
+ $item->title = $data->title;
+ $item->link = $data->url;
+ $item->description = $data->short;
+
+ //optional
+ item->descriptionTruncSize = 500;
+ item->descriptionHtmlSyndicated = true;
+
+ $item->date = $data->newsdate;
+ $item->source = "http://www.dailyphp.net";
+ $item->author = "John Doe";
+
+ $rss->addItem($item);
+}
+
+// valid format strings are: RSS0.91, RSS1.0, RSS2.0, PIE0.1 (deprecated),
+// MBOX, OPML, ATOM, ATOM0.3, HTML, JS
+echo $rss->saveFeed("RSS1.0", "news/feed.xml");
+
+
+***************************************************************************
+* A little setup *
+**************************************************************************/
+
+// your local timezone, set to "" to disable or for GMT
+define("TIME_ZONE","+00:00");
+
+
+
+
+/**
+ * Version string.
+ **/
+define("FEEDCREATOR_VERSION", "FeedCreator 1.7.2");
+
+
+
+/**
+ * A FeedItem is a part of a FeedCreator feed.
+ *
+ * @author Kai Blankenhorn
+ * @since 1.3
+ */
+class FeedItem extends HtmlDescribable {
+ /**
+ * Mandatory attributes of an item.
+ */
+ var $title, $description, $link;
+
+ /**
+ * Optional attributes of an item.
+ */
+ var $author, $authorEmail, $image, $category, $comments, $guid, $source, $creator;
+
+ /**
+ * Publishing date of an item. May be in one of the following formats:
+ *
+ * RFC 822:
+ * "Mon, 20 Jan 03 18:05:41 +0400"
+ * "20 Jan 03 18:05:41 +0000"
+ *
+ * ISO 8601:
+ * "2003-01-20T18:05:41+04:00"
+ *
+ * Unix:
+ * 1043082341
+ */
+ var $date;
+
+ /**
+ * Any additional elements to include as an assiciated array. All $key => $value pairs
+ * will be included unencoded in the feed item in the form
+ * <$key>$value$key>
+ * Again: No encoding will be used! This means you can invalidate or enhance the feed
+ * if $value contains markup. This may be abused to embed tags not implemented by
+ * the FeedCreator class used.
+ */
+ var $additionalElements = Array();
+
+ // on hold
+ // var $source;
+}
+
+
+
+/**
+ * An FeedImage may be added to a FeedCreator feed.
+ * @author Kai Blankenhorn
+ * @since 1.3
+ */
+class FeedImage extends HtmlDescribable {
+ /**
+ * Mandatory attributes of an image.
+ */
+ var $title, $url, $link;
+
+ /**
+ * Optional attributes of an image.
+ */
+ var $width, $height, $description;
+}
+
+
+
+/**
+ * An HtmlDescribable is an item within a feed that can have a description that may
+ * include HTML markup.
+ */
+class HtmlDescribable {
+ /**
+ * Indicates whether the description field should be rendered in HTML.
+ */
+ var $descriptionHtmlSyndicated;
+
+ /**
+ * Indicates whether and to how many characters a description should be truncated.
+ */
+ var $descriptionTruncSize;
+
+ /**
+ * Returns a formatted description field, depending on descriptionHtmlSyndicated and
+ * $descriptionTruncSize properties
+ * @return string the formatted description
+ */
+ function getDescription() {
+ $descriptionField = new FeedHtmlField($this->description);
+ $descriptionField->syndicateHtml = $this->descriptionHtmlSyndicated;
+ $descriptionField->truncSize = $this->descriptionTruncSize;
+ return $descriptionField->output();
+ }
+
+}
+
+
+/**
+ * An FeedHtmlField describes and generates
+ * a feed, item or image html field (probably a description). Output is
+ * generated based on $truncSize, $syndicateHtml properties.
+ * @author Pascal Van Hecke
+ * @version 1.6
+ */
+class FeedHtmlField {
+ /**
+ * Mandatory attributes of a FeedHtmlField.
+ */
+ var $rawFieldContent;
+
+ /**
+ * Optional attributes of a FeedHtmlField.
+ *
+ */
+ var $truncSize, $syndicateHtml;
+
+ /**
+ * Creates a new instance of FeedHtmlField.
+ * @param $string: if given, sets the rawFieldContent property
+ */
+ function FeedHtmlField($parFieldContent) {
+ if ($parFieldContent) {
+ $this->rawFieldContent = $parFieldContent;
+ }
+ }
+
+
+ /**
+ * Creates the right output, depending on $truncSize, $syndicateHtml properties.
+ * @return string the formatted field
+ */
+ function output() {
+ // when field available and syndicated in html we assume
+ // - valid html in $rawFieldContent and we enclose in CDATA tags
+ // - no truncation (truncating risks producing invalid html)
+ if (!$this->rawFieldContent) {
+ $result = "";
+ } elseif ($this->syndicateHtml) {
+ $result = "rawFieldContent."]]>";
+ } else {
+ if ($this->truncSize and is_int($this->truncSize)) {
+ $result = FeedCreator::iTrunc(htmlspecialchars($this->rawFieldContent),$this->truncSize);
+ } else {
+ $result = htmlspecialchars($this->rawFieldContent);
+ }
+ }
+ return $result;
+ }
+
+}
+
+
+
+/**
+ * UniversalFeedCreator lets you choose during runtime which
+ * format to build.
+ * For general usage of a feed class, see the FeedCreator class
+ * below or the example above.
+ *
+ * @since 1.3
+ * @author Kai Blankenhorn
+ */
+class UniversalFeedCreator extends FeedCreator {
+ var $_feed;
+
+ function _setFormat($format) {
+ switch (strtoupper($format)) {
+
+ case "2.0":
+ // fall through
+ case "RSS2.0":
+ $this->_feed = new RSSCreator20();
+ break;
+
+ case "1.0":
+ // fall through
+ case "RSS1.0":
+ $this->_feed = new RSSCreator10();
+ break;
+
+ case "0.91":
+ // fall through
+ case "RSS0.91":
+ $this->_feed = new RSSCreator091();
+ break;
+
+ case "PIE0.1":
+ $this->_feed = new PIECreator01();
+ break;
+
+ case "MBOX":
+ $this->_feed = new MBOXCreator();
+ break;
+
+ case "OPML":
+ $this->_feed = new OPMLCreator();
+ break;
+
+ case "ATOM":
+ // fall through: always the latest ATOM version
+
+ case "ATOM0.3":
+ $this->_feed = new AtomCreator03();
+ break;
+
+ case "HTML":
+ $this->_feed = new HTMLCreator();
+ break;
+
+ case "JS":
+ // fall through
+ case "JAVASCRIPT":
+ $this->_feed = new JSCreator();
+ break;
+
+ default:
+ $this->_feed = new RSSCreator091();
+ break;
+ }
+
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ // prevent overwriting of properties "contentType", "encoding"; do not copy "_feed" itself
+ if (!in_array($key, array("_feed", "contentType"/*PWG, "encoding"*/))) {
+ $this->_feed->{$key} = $this->{$key};
+ }
+ }
+ }
+
+ /**
+ * Creates a syndication feed based on the items previously added.
+ *
+ * @see FeedCreator::addItem()
+ * @param string format format the feed should comply to. Valid values are:
+ * "PIE0.1", "mbox", "RSS0.91", "RSS1.0", "RSS2.0", "OPML", "ATOM0.3", "HTML", "JS"
+ * @return string the contents of the feed.
+ */
+ function createFeed($format = "RSS0.91") {
+ $this->_setFormat($format);
+ return $this->_feed->createFeed();
+ }
+
+
+
+ /**
+ * Saves this feed as a file on the local disk. After the file is saved, an HTTP redirect
+ * header may be sent to redirect the use to the newly created file.
+ * @since 1.4
+ *
+ * @param string format format the feed should comply to. Valid values are:
+ * "PIE0.1" (deprecated), "mbox", "RSS0.91", "RSS1.0", "RSS2.0", "OPML", "ATOM", "ATOM0.3", "HTML", "JS"
+ * @param string filename optional the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()).
+ * @param boolean displayContents optional send the content of the file or not. If true, the file will be sent in the body of the response.
+ */
+ function saveFeed($format="RSS0.91", $filename="", $displayContents=true) {
+ $this->_setFormat($format);
+ $this->_feed->saveFeed($filename, $displayContents);
+ }
+
+
+ /**
+ * Turns on caching and checks if there is a recent version of this feed in the cache.
+ * If there is, an HTTP redirect header is sent.
+ * To effectively use caching, you should create the FeedCreator object and call this method
+ * before anything else, especially before you do the time consuming task to build the feed
+ * (web fetching, for example).
+ *
+ * @param string format format the feed should comply to. Valid values are:
+ * "PIE0.1" (deprecated), "mbox", "RSS0.91", "RSS1.0", "RSS2.0", "OPML", "ATOM0.3".
+ * @param filename string optional the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()).
+ * @param timeout int optional the timeout in seconds before a cached version is refreshed (defaults to 3600 = 1 hour)
+ */
+ function useCached($format="RSS0.91", $filename="", $timeout=3600) {
+ $this->_setFormat($format);
+ $this->_feed->useCached($filename, $timeout);
+ }
+
+}
+
+
+/**
+ * FeedCreator is the abstract base implementation for concrete
+ * implementations that implement a specific format of syndication.
+ *
+ * @abstract
+ * @author Kai Blankenhorn
+ * @since 1.4
+ */
+class FeedCreator extends HtmlDescribable {
+
+ /**
+ * Mandatory attributes of a feed.
+ */
+ var $title, $description, $link;
+
+
+ /**
+ * Optional attributes of a feed.
+ */
+ var $syndicationURL, $image, $language, $copyright, $pubDate, $lastBuildDate, $editor, $editorEmail, $webmaster, $category, $docs, $ttl, $rating, $skipHours, $skipDays;
+
+ /**
+ * The url of the external xsl stylesheet used to format the naked rss feed.
+ * Ignored in the output when empty.
+ */
+ var $xslStyleSheet = "";
+
+
+ /**
+ * @access private
+ */
+ var $items = Array();
+
+
+ /**
+ * This feed's MIME content type.
+ * @since 1.4
+ * @access private
+ */
+ var $contentType = "application/xml";
+
+
+ /**
+ * This feed's character encoding.
+ * @since 1.6.1
+ **/
+ var $encoding = "ISO-8859-1";
+
+
+ /**
+ * Any additional elements to include as an assiciated array. All $key => $value pairs
+ * will be included unencoded in the feed in the form
+ * <$key>$value$key>
+ * Again: No encoding will be used! This means you can invalidate or enhance the feed
+ * if $value contains markup. This may be abused to embed tags not implemented by
+ * the FeedCreator class used.
+ */
+ var $additionalElements = Array();
+
+
+ /**
+ * Adds an FeedItem to the feed.
+ *
+ * @param object FeedItem $item The FeedItem to add to the feed.
+ * @access public
+ */
+ function addItem($item) {
+ $this->items[] = $item;
+ }
+
+
+ /**
+ * Truncates a string to a certain length at the most sensible point.
+ * First, if there's a '.' character near the end of the string, the string is truncated after this character.
+ * If there is no '.', the string is truncated after the last ' ' character.
+ * If the string is truncated, " ..." is appended.
+ * If the string is already shorter than $length, it is returned unchanged.
+ *
+ * @static
+ * @param string string A string to be truncated.
+ * @param int length the maximum length the string should be truncated to
+ * @return string the truncated string
+ */
+ function iTrunc($string, $length) {
+ if (strlen($string)<=$length) {
+ return $string;
+ }
+
+ $pos = strrpos($string,".");
+ if ($pos>=$length-4) {
+ $string = substr($string,0,$length-4);
+ $pos = strrpos($string,".");
+ }
+ if ($pos>=$length*0.4) {
+ return substr($string,0,$pos+1)." ...";
+ }
+
+ $pos = strrpos($string," ");
+ if ($pos>=$length-4) {
+ $string = substr($string,0,$length-4);
+ $pos = strrpos($string," ");
+ }
+ if ($pos>=$length*0.4) {
+ return substr($string,0,$pos)." ...";
+ }
+
+ return substr($string,0,$length-4)." ...";
+
+ }
+
+
+ /**
+ * Creates a comment indicating the generator of this feed.
+ * The format of this comment seems to be recognized by
+ * Syndic8.com.
+ */
+ function _createGeneratorComment() {
+ return "\n";
+ }
+
+
+ /**
+ * Creates a string containing all additional elements specified in
+ * $additionalElements.
+ * @param elements array an associative array containing key => value pairs
+ * @param indentString string a string that will be inserted before every generated line
+ * @return string the XML tags corresponding to $additionalElements
+ */
+ function _createAdditionalElements($elements, $indentString="") {
+ $ae = "";
+ if (is_array($elements)) {
+ foreach($elements AS $key => $value) {
+ $ae.= $indentString."<$key>$value$key>\n";
+ }
+ }
+ return $ae;
+ }
+
+ function _createStylesheetReferences() {
+ $xml = "";
+ if (isset($this->cssStyleSheet)) $xml .= "cssStyleSheet."\" type=\"text/css\"?>\n";
+ if ($this->xslStyleSheet) $xml .= "xslStyleSheet."\" type=\"text/xsl\"?>\n";
+ return $xml;
+ }
+
+
+ /**
+ * Builds the feed's text.
+ * @abstract
+ * @return string the feed's complete text
+ */
+ function createFeed() {
+ }
+
+ /**
+ * Generate a filename for the feed cache file. The result will be $_SERVER["PHP_SELF"] with the extension changed to .xml.
+ * For example:
+ *
+ * echo $_SERVER["PHP_SELF"]."\n";
+ * echo FeedCreator::_generateFilename();
+ *
+ * would produce:
+ *
+ * /rss/latestnews.php
+ * latestnews.xml
+ *
+ * @return string the feed cache filename
+ * @since 1.4
+ * @access private
+ */
+ function _generateFilename() {
+ $fileInfo = pathinfo($_SERVER["PHP_SELF"]);
+ return substr($fileInfo["basename"],0,-(strlen($fileInfo["extension"])+1)).".xml";
+ }
+
+
+ /**
+ * @since 1.4
+ * @access private
+ */
+ function _redirect($filename) {
+ // attention, heavily-commented-out-area
+
+ // maybe use this in addition to file time checking
+ //Header("Expires: ".date("r",time()+$this->_timeout));
+
+ /* no caching at all, doesn't seem to work as good:
+ Header("Cache-Control: no-cache");
+ Header("Pragma: no-cache");
+ */
+
+ // HTTP redirect, some feed readers' simple HTTP implementations don't follow it
+ //Header("Location: ".$filename);
+
+ Header("Content-Type: ".$this->contentType."; charset=".$this->encoding."; filename=".basename($filename));
+ Header("Content-Disposition: inline; filename=".basename($filename));
+ readfile($filename, "r");
+ die();
+ }
+
+ /**
+ * Turns on caching and checks if there is a recent version of this feed in the cache.
+ * If there is, an HTTP redirect header is sent.
+ * To effectively use caching, you should create the FeedCreator object and call this method
+ * before anything else, especially before you do the time consuming task to build the feed
+ * (web fetching, for example).
+ * @since 1.4
+ * @param filename string optional the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()).
+ * @param timeout int optional the timeout in seconds before a cached version is refreshed (defaults to 3600 = 1 hour)
+ */
+ function useCached($filename="", $timeout=3600) {
+ $this->_timeout = $timeout;
+ if ($filename=="") {
+ $filename = $this->_generateFilename();
+ }
+ if (file_exists($filename) AND (time()-filemtime($filename) < $timeout)) {
+ $this->_redirect($filename);
+ }
+ }
+
+
+ /**
+ * Saves this feed as a file on the local disk. After the file is saved, a redirect
+ * header may be sent to redirect the user to the newly created file.
+ * @since 1.4
+ *
+ * @param filename string optional the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()).
+ * @param redirect boolean optional send an HTTP redirect header or not. If true, the user will be automatically redirected to the created file.
+ */
+ function saveFeed($filename="", $displayContents=true) {
+ if ($filename=="") {
+ $filename = $this->_generateFilename();
+ }
+ $feedFile = fopen($filename, "w+");
+ if ($feedFile) {
+ fputs($feedFile,$this->createFeed());
+ fclose($feedFile);
+ if ($displayContents) {
+ $this->_redirect($filename);
+ }
+ } else {
+ echo " Error creating feed file, please check write permissions. ";
+ }
+ }
+
+}
+
+
+/**
+ * FeedDate is an internal class that stores a date for a feed or feed item.
+ * Usually, you won't need to use this.
+ */
+class FeedDate {
+ var $unix;
+
+ /**
+ * Creates a new instance of FeedDate representing a given date.
+ * Accepts RFC 822, ISO 8601 date formats as well as unix time stamps.
+ * @param mixed $dateString optional the date this FeedDate will represent. If not specified, the current date and time is used.
+ */
+ function FeedDate($dateString="") {
+ if ($dateString=="") $dateString = date("r");
+
+ if (is_integer($dateString)) {
+ $this->unix = $dateString;
+ return;
+ }
+ if (preg_match("~(?:(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),\\s+)?(\\d{1,2})\\s+([a-zA-Z]{3})\\s+(\\d{4})\\s+(\\d{2}):(\\d{2}):(\\d{2})\\s+(.*)~",$dateString,$matches)) {
+ $months = Array("Jan"=>1,"Feb"=>2,"Mar"=>3,"Apr"=>4,"May"=>5,"Jun"=>6,"Jul"=>7,"Aug"=>8,"Sep"=>9,"Oct"=>10,"Nov"=>11,"Dec"=>12);
+ $this->unix = mktime($matches[4],$matches[5],$matches[6],$months[$matches[2]],$matches[1],$matches[3]);
+ if (substr($matches[7],0,1)=='+' OR substr($matches[7],0,1)=='-') {
+ $tzOffset = (substr($matches[7],0,3) * 60 + substr($matches[7],-2)) * 60;
+ } else {
+ if (strlen($matches[7])==1) {
+ $oneHour = 3600;
+ $ord = ord($matches[7]);
+ if ($ord < ord("M")) {
+ $tzOffset = (ord("A") - $ord - 1) * $oneHour;
+ } elseif ($ord >= ord("M") AND $matches[7]!="Z") {
+ $tzOffset = ($ord - ord("M")) * $oneHour;
+ } elseif ($matches[7]=="Z") {
+ $tzOffset = 0;
+ }
+ }
+ switch ($matches[7]) {
+ case "UT":
+ case "GMT": $tzOffset = 0;
+ }
+ }
+ $this->unix += $tzOffset;
+ return;
+ }
+ if (preg_match("~(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(.*)~",$dateString,$matches)) {
+ $this->unix = mktime($matches[4],$matches[5],$matches[6],$matches[2],$matches[3],$matches[1]);
+ if (substr($matches[7],0,1)=='+' OR substr($matches[7],0,1)=='-') {
+ $tzOffset = (substr($matches[7],0,3) * 60 + substr($matches[7],-2)) * 60;
+ } else {
+ if ($matches[7]=="Z") {
+ $tzOffset = 0;
+ }
+ }
+ $this->unix += $tzOffset;
+ return;
+ }
+ $this->unix = 0;
+ }
+
+ /**
+ * Gets the date stored in this FeedDate as an RFC 822 date.
+ *
+ * @return a date in RFC 822 format
+ */
+ function rfc822() {
+ //return gmdate("r",$this->unix);
+ $date = gmdate("D, d M Y H:i:s", $this->unix);
+ if (TIME_ZONE!="") $date .= " ".str_replace(":","",TIME_ZONE);
+ return $date;
+ }
+
+ /**
+ * Gets the date stored in this FeedDate as an ISO 8601 date.
+ *
+ * @return a date in ISO 8601 format
+ */
+ function iso8601() {
+ $date = gmdate("Y-m-d\TH:i:sO",$this->unix);
+ $date = substr($date,0,22) . ':' . substr($date,-2);
+ if (TIME_ZONE!="") $date = str_replace("+00:00",TIME_ZONE,$date);
+ return $date;
+ }
+
+ /**
+ * Gets the date stored in this FeedDate as unix time stamp.
+ *
+ * @return a date as a unix time stamp
+ */
+ function unix() {
+ return $this->unix;
+ }
+}
+
+
+/**
+ * RSSCreator10 is a FeedCreator that implements RDF Site Summary (RSS) 1.0.
+ *
+ * @see http://www.purl.org/rss/1.0/
+ * @since 1.3
+ * @author Kai Blankenhorn
+ */
+class RSSCreator10 extends FeedCreator {
+
+ /**
+ * Builds the RSS feed's text. The feed will be compliant to RDF Site Summary (RSS) 1.0.
+ * The feed will contain all items previously added in the same order.
+ * @return string the feed's complete text
+ */
+ function createFeed() {
+ $feed = "encoding."\"?>\n";
+ $feed.= $this->_createGeneratorComment();
+ if ($this->cssStyleSheet=="") {
+ $cssStyleSheet = "http://www.w3.org/2000/08/w3c-synd/style.css";
+ }
+ $feed.= $this->_createStylesheetReferences();
+ $feed.= "\n";
+ $feed.= " syndicationURL."\">\n";
+ $feed.= " ".htmlspecialchars($this->title)."\n";
+ $feed.= " ".htmlspecialchars($this->description)."\n";
+ $feed.= " ".$this->link."\n";
+ if ($this->image!=null) {
+ $feed.= " image->url."\" />\n";
+ }
+ $now = new FeedDate();
+ $feed.= " ".htmlspecialchars($now->iso8601())."\n";
+ $feed.= " \n";
+ $feed.= " \n";
+ for ($i=0;$iitems);$i++) {
+ $feed.= " items[$i]->link)."\"/>\n";
+ }
+ $feed.= " \n";
+ $feed.= " \n";
+ $feed.= " \n";
+ if ($this->image!=null) {
+ $feed.= " image->url."\">\n";
+ $feed.= " ".$this->image->title."\n";
+ $feed.= " ".$this->image->link."\n";
+ $feed.= " ".$this->image->url."\n";
+ $feed.= " \n";
+ }
+ $feed.= $this->_createAdditionalElements($this->additionalElements, " ");
+
+ for ($i=0;$iitems);$i++) {
+ $feed.= " items[$i]->link)."\">\n";
+ //$feed.= " Posting\n";
+ $feed.= " text/html\n";
+ if ($this->items[$i]->date!=null) {
+ $itemDate = new FeedDate($this->items[$i]->date);
+ $feed.= " ".htmlspecialchars($itemDate->iso8601())."\n";
+ }
+ if ($this->items[$i]->source!="") {
+ $feed.= " ".htmlspecialchars($this->items[$i]->source)."\n";
+ }
+ if ($this->items[$i]->author!="") {
+ $feed.= " ".htmlspecialchars($this->items[$i]->author)."\n";
+ }
+ $feed.= " ".htmlspecialchars(strip_tags(strtr($this->items[$i]->title,"\n\r"," ")))."\n";
+ $feed.= " ".htmlspecialchars($this->items[$i]->link)."\n";
+ $feed.= " ".htmlspecialchars($this->items[$i]->description)."\n";
+ $feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " ");
+ $feed.= " \n";
+ }
+ $feed.= "\n";
+ return $feed;
+ }
+}
+
+
+
+/**
+ * RSSCreator091 is a FeedCreator that implements RSS 0.91 Spec, revision 3.
+ *
+ * @see http://my.netscape.com/publish/formats/rss-spec-0.91.html
+ * @since 1.3
+ * @author Kai Blankenhorn
+ */
+class RSSCreator091 extends FeedCreator {
+
+ /**
+ * Stores this RSS feed's version number.
+ * @access private
+ */
+ var $RSSVersion;
+
+ function RSSCreator091() {
+ $this->_setRSSVersion("0.91");
+ $this->contentType = "application/rss+xml";
+ }
+
+ /**
+ * Sets this RSS feed's version number.
+ * @access private
+ */
+ function _setRSSVersion($version) {
+ $this->RSSVersion = $version;
+ }
+
+ /**
+ * Builds the RSS feed's text. The feed will be compliant to RDF Site Summary (RSS) 1.0.
+ * The feed will contain all items previously added in the same order.
+ * @return string the feed's complete text
+ */
+ function createFeed() {
+ $feed = "encoding."\"?>\n";
+ $feed.= $this->_createGeneratorComment();
+ $feed.= $this->_createStylesheetReferences();
+ $feed.= "RSSVersion."\">\n";
+ $feed.= " \n";
+ $feed.= " ".FeedCreator::iTrunc(htmlspecialchars($this->title),100)."\n";
+ $this->descriptionTruncSize = 500;
+ $feed.= " ".$this->getDescription()."\n";
+ $feed.= " ".$this->link."\n";
+ $now = new FeedDate();
+ $feed.= " ".htmlspecialchars($now->rfc822())."\n";
+ $feed.= " ".FEEDCREATOR_VERSION."\n";
+
+ if ($this->image!=null) {
+ $feed.= " \n";
+ $feed.= " ".$this->image->url."\n";
+ $feed.= " ".FeedCreator::iTrunc(htmlspecialchars($this->image->title),100)."\n";
+ $feed.= " ".$this->image->link."\n";
+ if ($this->image->width!="") {
+ $feed.= " ".$this->image->width."\n";
+ }
+ if ($this->image->height!="") {
+ $feed.= " ".$this->image->height."\n";
+ }
+ if ($this->image->description!="") {
+ $feed.= " ".$this->image->getDescription()."\n";
+ }
+ $feed.= " \n";
+ }
+ if ($this->language!="") {
+ $feed.= " ".$this->language."\n";
+ }
+ if ($this->copyright!="") {
+ $feed.= " ".FeedCreator::iTrunc(htmlspecialchars($this->copyright),100)."\n";
+ }
+ if ($this->editor!="") {
+ $feed.= " ".FeedCreator::iTrunc(htmlspecialchars($this->editor),100)."\n";
+ }
+ if ($this->webmaster!="") {
+ $feed.= " ".FeedCreator::iTrunc(htmlspecialchars($this->webmaster),100)."\n";
+ }
+ if ($this->pubDate!="") {
+ $pubDate = new FeedDate($this->pubDate);
+ $feed.= " ".htmlspecialchars($pubDate->rfc822())."\n";
+ }
+ if ($this->category!="") {
+ $feed.= " ".htmlspecialchars($this->category)."\n";
+ }
+ if ($this->docs!="") {
+ $feed.= " ".FeedCreator::iTrunc(htmlspecialchars($this->docs),500)."\n";
+ }
+ if ($this->ttl!="") {
+ $feed.= " ".htmlspecialchars($this->ttl)."\n";
+ }
+ if ($this->rating!="") {
+ $feed.= " ".FeedCreator::iTrunc(htmlspecialchars($this->rating),500)."\n";
+ }
+ if ($this->skipHours!="") {
+ $feed.= " ".htmlspecialchars($this->skipHours)."\n";
+ }
+ if ($this->skipDays!="") {
+ $feed.= " ".htmlspecialchars($this->skipDays)."\n";
+ }
+ $feed.= $this->_createAdditionalElements($this->additionalElements, " ");
+
+ for ($i=0;$iitems);$i++) {
+ $feed.= " \n";
+ $feed.= " ".FeedCreator::iTrunc(htmlspecialchars(strip_tags($this->items[$i]->title)),100)."\n";
+ $feed.= " ".htmlspecialchars($this->items[$i]->link)."\n";
+ $feed.= " ".$this->items[$i]->getDescription()."\n";
+
+ if ($this->items[$i]->author!="") {
+ $feed.= " ".htmlspecialchars($this->items[$i]->author)."\n";
+ }
+ /*
+ // on hold
+ if ($this->items[$i]->source!="") {
+ $feed.= " ".htmlspecialchars($this->items[$i]->source)."\n";
+ }
+ */
+ if ($this->items[$i]->category!="") {
+ $feed.= " ".htmlspecialchars($this->items[$i]->category)."\n";
+ }
+ if ($this->items[$i]->comments!="") {
+ $feed.= " ".htmlspecialchars($this->items[$i]->comments)."\n";
+ }
+ if ($this->items[$i]->date!="") {
+ $itemDate = new FeedDate($this->items[$i]->date);
+ $feed.= " ".htmlspecialchars($itemDate->rfc822())."\n";
+ }
+ if ($this->items[$i]->guid!="") {
+ $feed.= " ".htmlspecialchars($this->items[$i]->guid)."\n";
+ }
+ $feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " ");
+ $feed.= " \n";
+ }
+ $feed.= " \n";
+ $feed.= "\n";
+ return $feed;
+ }
+}
+
+
+
+/**
+ * RSSCreator20 is a FeedCreator that implements RDF Site Summary (RSS) 2.0.
+ *
+ * @see http://backend.userland.com/rss
+ * @since 1.3
+ * @author Kai Blankenhorn
+ */
+class RSSCreator20 extends RSSCreator091 {
+
+ function RSSCreator20() {
+ parent::_setRSSVersion("2.0");
+ }
+
+}
+
+
+/**
+ * PIECreator01 is a FeedCreator that implements the emerging PIE specification,
+ * as in http://intertwingly.net/wiki/pie/Syntax.
+ *
+ * @deprecated
+ * @since 1.3
+ * @author Scott Reynen and Kai Blankenhorn
+ */
+class PIECreator01 extends FeedCreator {
+
+ function PIECreator01() {
+ $this->encoding = "utf-8";
+ }
+
+ function createFeed() {
+ $feed = "encoding."\"?>\n";
+ $feed.= $this->_createStylesheetReferences();
+ $feed.= "\n";
+ $feed.= " ".FeedCreator::iTrunc(htmlspecialchars($this->title),100)."\n";
+ $this->truncSize = 500;
+ $feed.= " ".$this->getDescription()."\n";
+ $feed.= " ".$this->link."\n";
+ for ($i=0;$iitems);$i++) {
+ $feed.= " \n";
+ $feed.= " ".FeedCreator::iTrunc(htmlspecialchars(strip_tags($this->items[$i]->title)),100)."\n";
+ $feed.= " ".htmlspecialchars($this->items[$i]->link)."\n";
+ $itemDate = new FeedDate($this->items[$i]->date);
+ $feed.= " ".htmlspecialchars($itemDate->iso8601())."\n";
+ $feed.= " ".htmlspecialchars($itemDate->iso8601())."\n";
+ $feed.= " ".htmlspecialchars($itemDate->iso8601())."\n";
+ $feed.= " ".htmlspecialchars($this->items[$i]->guid)."\n";
+ if ($this->items[$i]->author!="") {
+ $feed.= " \n";
+ $feed.= " ".htmlspecialchars($this->items[$i]->author)."\n";
+ if ($this->items[$i]->authorEmail!="") {
+ $feed.= " ".$this->items[$i]->authorEmail."\n";
+ }
+ $feed.=" \n";
+ }
+ $feed.= " \n";
+ $feed.= "
".$this->items[$i]->getDescription()."
\n";
+ $feed.= " \n";
+ $feed.= " \n";
+ }
+ $feed.= "\n";
+ return $feed;
+ }
+}
+
+
+/**
+ * AtomCreator03 is a FeedCreator that implements the atom specification,
+ * as in http://www.intertwingly.net/wiki/pie/FrontPage.
+ * Please note that just by using AtomCreator03 you won't automatically
+ * produce valid atom files. For example, you have to specify either an editor
+ * for the feed or an author for every single feed item.
+ *
+ * Some elements have not been implemented yet. These are (incomplete list):
+ * author URL, item author's email and URL, item contents, alternate links,
+ * other link content types than text/html. Some of them may be created with
+ * AtomCreator03::additionalElements.
+ *
+ * @see FeedCreator#additionalElements
+ * @since 1.6
+ * @author Kai Blankenhorn , Scott Reynen
+ */
+class AtomCreator03 extends FeedCreator {
+
+ function AtomCreator03() {
+ $this->contentType = "application/atom+xml";
+ $this->encoding = "utf-8";
+ }
+
+ function createFeed() {
+ $feed = "encoding."\"?>\n";
+ $feed.= $this->_createGeneratorComment();
+ $feed.= $this->_createStylesheetReferences();
+ $feed.= "language!="") {
+ $feed.= " xml:lang=\"".$this->language."\"";
+ }
+ $feed.= ">\n";
+ $feed.= " ".htmlspecialchars($this->title)."\n";
+ $feed.= " ".htmlspecialchars($this->description)."\n";
+ $feed.= " link)."\"/>\n";
+ $feed.= " ".htmlspecialchars($this->link)."\n";
+ $now = new FeedDate();
+ $feed.= " ".htmlspecialchars($now->iso8601())."\n";
+ if ($this->editor!="") {
+ $feed.= " \n";
+ $feed.= " ".$this->editor."\n";
+ if ($this->editorEmail!="") {
+ $feed.= " ".$this->editorEmail."\n";
+ }
+ $feed.= " \n";
+ }
+ $feed.= " ".FEEDCREATOR_VERSION."\n";
+ $feed.= $this->_createAdditionalElements($this->additionalElements, " ");
+ for ($i=0;$iitems);$i++) {
+ $feed.= " \n";
+ $feed.= " ".htmlspecialchars(strip_tags($this->items[$i]->title))."\n";
+ $feed.= " items[$i]->link)."\"/>\n";
+ if ($this->items[$i]->date=="") {
+ $this->items[$i]->date = time();
+ }
+ $itemDate = new FeedDate($this->items[$i]->date);
+ $feed.= " ".htmlspecialchars($itemDate->iso8601())."\n";
+ $feed.= " ".htmlspecialchars($itemDate->iso8601())."\n";
+ $feed.= " ".htmlspecialchars($itemDate->iso8601())."\n";
+ $feed.= " ".htmlspecialchars($this->items[$i]->link)."\n";
+ $feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " ");
+ if ($this->items[$i]->author!="") {
+ $feed.= " \n";
+ $feed.= " ".htmlspecialchars($this->items[$i]->author)."\n";
+ $feed.= " \n";
+ }
+ if ($this->items[$i]->description!="") {
+ $feed.= " ".htmlspecialchars($this->items[$i]->description)."\n";
+ }
+ $feed.= " \n";
+ }
+ $feed.= "\n";
+ return $feed;
+ }
+}
+
+
+/**
+ * MBOXCreator is a FeedCreator that implements the mbox format
+ * as described in http://www.qmail.org/man/man5/mbox.html
+ *
+ * @since 1.3
+ * @author Kai Blankenhorn
+ */
+class MBOXCreator extends FeedCreator {
+
+ function MBOXCreator() {
+ $this->contentType = "text/plain";
+ $this->encoding = "ISO-8859-15";
+ }
+
+ function qp_enc($input = "", $line_max = 76) {
+ $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
+ $lines = preg_split("/(?:\r\n|\r|\n)/", $input);
+ $eol = "\r\n";
+ $escape = "=";
+ $output = "";
+ while( list(, $line) = each($lines) ) {
+ //$line = rtrim($line); // remove trailing white space -> no =20\r\n necessary
+ $linlen = strlen($line);
+ $newline = "";
+ for($i = 0; $i < $linlen; $i++) {
+ $c = substr($line, $i, 1);
+ $dec = ord($c);
+ if ( ($dec == 32) && ($i == ($linlen - 1)) ) { // convert space at eol only
+ $c = "=20";
+ } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required
+ $h2 = floor($dec/16); $h1 = floor($dec%16);
+ $c = $escape.$hex["$h2"].$hex["$h1"];
+ }
+ if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted
+ $output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay
+ $newline = "";
+ }
+ $newline .= $c;
+ } // end of for
+ $output .= $newline.$eol;
+ }
+ return trim($output);
+ }
+
+
+ /**
+ * Builds the MBOX contents.
+ * @return string the feed's complete text
+ */
+ function createFeed() {
+ for ($i=0;$iitems);$i++) {
+ if ($this->items[$i]->author!="") {
+ $from = $this->items[$i]->author;
+ } else {
+ $from = $this->title;
+ }
+ $itemDate = new FeedDate($this->items[$i]->date);
+ $feed.= "From ".strtr(MBOXCreator::qp_enc($from)," ","_")." ".date("D M d H:i:s Y",$itemDate->unix())."\n";
+ $feed.= "Content-Type: text/plain;\n";
+ $feed.= " charset=\"".$this->encoding."\"\n";
+ $feed.= "Content-Transfer-Encoding: quoted-printable\n";
+ $feed.= "Content-Type: text/plain\n";
+ $feed.= "From: \"".MBOXCreator::qp_enc($from)."\"\n";
+ $feed.= "Date: ".$itemDate->rfc822()."\n";
+ $feed.= "Subject: ".MBOXCreator::qp_enc(FeedCreator::iTrunc($this->items[$i]->title,100))."\n";
+ $feed.= "\n";
+ $body = chunk_split(MBOXCreator::qp_enc($this->items[$i]->description));
+ $feed.= preg_replace("~\nFrom ([^\n]*)(\n?)~","\n>From $1$2\n",$body);
+ $feed.= "\n";
+ $feed.= "\n";
+ }
+ return $feed;
+ }
+
+ /**
+ * Generate a filename for the feed cache file. Overridden from FeedCreator to prevent XML data types.
+ * @return string the feed cache filename
+ * @since 1.4
+ * @access private
+ */
+ function _generateFilename() {
+ $fileInfo = pathinfo($_SERVER["PHP_SELF"]);
+ return substr($fileInfo["basename"],0,-(strlen($fileInfo["extension"])+1)).".mbox";
+ }
+}
+
+
+/**
+ * OPMLCreator is a FeedCreator that implements OPML 1.0.
+ *
+ * @see http://opml.scripting.com/spec
+ * @author Dirk Clemens, Kai Blankenhorn
+ * @since 1.5
+ */
+class OPMLCreator extends FeedCreator {
+
+ function OPMLCreator() {
+ $this->encoding = "utf-8";
+ }
+
+ function createFeed() {
+ $feed = "encoding."\"?>\n";
+ $feed.= $this->_createGeneratorComment();
+ $feed.= $this->_createStylesheetReferences();
+ $feed.= "\n";
+ $feed.= " \n";
+ $feed.= " ".htmlspecialchars($this->title)."\n";
+ if ($this->pubDate!="") {
+ $date = new FeedDate($this->pubDate);
+ $feed.= " ".$date->rfc822()."\n";
+ }
+ if ($this->lastBuildDate!="") {
+ $date = new FeedDate($this->lastBuildDate);
+ $feed.= " ".$date->rfc822()."\n";
+ }
+ if ($this->editor!="") {
+ $feed.= " ".$this->editor."\n";
+ }
+ if ($this->editorEmail!="") {
+ $feed.= " ".$this->editorEmail."\n";
+ }
+ $feed.= " \n";
+ $feed.= " \n";
+ for ($i=0;$iitems);$i++) {
+ $feed.= " items[$i]->title,"\n\r"," ")));
+ $feed.= " title=\"".$title."\"";
+ $feed.= " text=\"".$title."\"";
+ //$feed.= " description=\"".htmlspecialchars($this->items[$i]->description)."\"";
+ $feed.= " url=\"".htmlspecialchars($this->items[$i]->link)."\"";
+ $feed.= "/>\n";
+ }
+ $feed.= " \n";
+ $feed.= "\n";
+ return $feed;
+ }
+}
+
+
+
+/**
+ * HTMLCreator is a FeedCreator that writes an HTML feed file to a specific
+ * location, overriding the createFeed method of the parent FeedCreator.
+ * The HTML produced can be included over http by scripting languages, or serve
+ * as the source for an IFrame.
+ * All output by this class is embedded in tags to enable formatting
+ * using CSS.
+ *
+ * @author Pascal Van Hecke
+ * @since 1.7
+ */
+class HTMLCreator extends FeedCreator {
+
+ var $contentType = "text/html";
+
+ /**
+ * Contains HTML to be output at the start of the feed's html representation.
+ */
+ var $header;
+
+ /**
+ * Contains HTML to be output at the end of the feed's html representation.
+ */
+ var $footer ;
+
+ /**
+ * Contains HTML to be output between entries. A separator is only used in
+ * case of multiple entries.
+ */
+ var $separator;
+
+ /**
+ * Used to prefix the stylenames to make sure they are unique
+ * and do not clash with stylenames on the users' page.
+ */
+ var $stylePrefix;
+
+ /**
+ * Determines whether the links open in a new window or not.
+ */
+ var $openInNewWindow = true;
+
+ var $imageAlign ="right";
+
+ /**
+ * In case of very simple output you may want to get rid of the style tags,
+ * hence this variable. There's no equivalent on item level, but of course you can
+ * add strings to it while iterating over the items ($this->stylelessOutput .= ...)
+ * and when it is non-empty, ONLY the styleless output is printed, the rest is ignored
+ * in the function createFeed().
+ */
+ var $stylelessOutput ="";
+
+ /**
+ * Writes the HTML.
+ * @return string the scripts's complete text
+ */
+ function createFeed() {
+ // if there is styleless output, use the content of this variable and ignore the rest
+ if ($this->stylelessOutput!="") {
+ return $this->stylelessOutput;
+ }
+
+ //if no stylePrefix is set, generate it yourself depending on the script name
+ if ($this->stylePrefix=="") {
+ $this->stylePrefix = str_replace(".", "_", $this->_generateFilename())."_";
+ }
+
+ //set an openInNewWindow_token_to be inserted or not
+ if ($this->openInNewWindow) {
+ $targetInsert = " target='_blank'";
+ }
+
+ // use this array to put the lines in and implode later with "document.write" javascript
+ $feedArray = array();
+ if ($this->image!=null) {
+ $imageStr = "".
+ "image->width) {
+ $imageStr .=" width='".$this->image->width. "' ";
+ }
+ if ($this->image->height) {
+ $imageStr .=" height='".$this->image->height."' ";
+ }
+ $imageStr .="/>";
+ $feedArray[] = $imageStr;
+ }
+
+ if ($this->title) {
+ $feedArray[] = "
';
+
+ if ($conf['die_on_sql_error'])
+ {
+ die($error);
+ }
+ else
+ {
+ echo $error;
+ }
+}
+
+/**
+ * creates an array based on a query, this function is a very common pattern
+ * used here
+ *
+ * @param string $query
+ * @param string $fieldname
+ * @return array
+ */
+function array_from_query($query, $fieldname)
+{
+ $array = array();
+
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push($array, $row[$fieldname]);
+ }
+
+ return $array;
+}
+
+/**
+ * fill the current user caddie with given elements, if not already in
+ * caddie
+ *
+ * @param array elements_id
+ */
+function fill_caddie($elements_id)
+{
+ global $user;
+
+ include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
+
+ $query = '
+SELECT element_id
+ FROM '.CADDIE_TABLE.'
+ WHERE user_id = '.$user['id'].'
+;';
+ $in_caddie = array_from_query($query, 'element_id');
+
+ $caddiables = array_diff($elements_id, $in_caddie);
+
+ $datas = array();
+
+ foreach ($caddiables as $caddiable)
+ {
+ array_push($datas, array('element_id' => $caddiable,
+ 'user_id' => $user['id']));
+ }
+
+ if (count($caddiables) > 0)
+ {
+ mass_inserts(CADDIE_TABLE, array('element_id','user_id'), $datas);
+ }
+}
+
+/**
+ * returns the element name from its filename
+ *
+ * @param string filename
+ * @return string name
+ */
+function get_name_from_file($filename)
+{
+ return str_replace('_',' ',get_filename_wo_extension($filename));
+}
+
+/**
+ * returns the corresponding value from $lang if existing. Else, the key is
+ * returned
+ *
+ * @param string key
+ * @return string
+ */
+function l10n($key)
+{
+ global $lang, $conf;
+
+ if ($conf['debug_l10n'] and !isset($lang[$key]) and !empty($key))
+ {
+ trigger_error('[l10n] language key "'.$key.'" is not defined', E_USER_NOTICE);
+ }
+
+ return isset($lang[$key]) ? $lang[$key] : $key;
+}
+
+/**
+ * returns the prinft value for strings including %d
+ * return is concorded with decimal value (singular, plural)
+ *
+ * @param singular string key
+ * @param plural string key
+ * @param decimal value
+ * @return string
+ */
+function l10n_dec($singular_fmt_key, $plural_fmt_key, $decimal)
+{
+ global $lang_info;
+
+ return
+ sprintf(
+ l10n((
+ (($decimal > 1) or ($decimal == 0 and $lang_info['zero_plural']))
+ ? $plural_fmt_key
+ : $singular_fmt_key
+ )), $decimal);
+}
+/*
+ * returns a single element to use with l10n_args
+ *
+ * @param string key: translation key
+ * @param array/string/../number args:
+ * arguments to use on sprintf($key, args)
+ * if args is a array, each values are used on sprintf
+ * @return string
+ */
+function get_l10n_args($key, $args)
+{
+ if (is_array($args))
+ {
+ $key_arg = array_merge(array($key), $args);
+ }
+ else
+ {
+ $key_arg = array($key, $args);
+ }
+ return array('key_args' => $key_arg);
+}
+
+/*
+ * returns a string with formated with l10n_args elements
+ *
+ * @param element/array $key_args: element or array of l10n_args elements
+ * @param $sep: if $key_args is array,
+ * separator is used when translated l10n_args elements are concated
+ * @return string
+ */
+function l10n_args($key_args, $sep = "\n")
+{
+ if (is_array($key_args))
+ {
+ foreach ($key_args as $key => $element)
+ {
+ if (isset($result))
+ {
+ $result .= $sep;
+ }
+ else
+ {
+ $result = '';
+ }
+
+ if ($key === 'key_args')
+ {
+ array_unshift($element, l10n(array_shift($element)));
+ $result .= call_user_func_array('sprintf', $element);
+ }
+ else
+ {
+ $result .= l10n_args($element, $sep);
+ }
+ }
+ }
+ else
+ {
+ die('l10n_args: Invalid arguments');
+ }
+
+ return $result;
+}
+
+/**
+ * returns the corresponding value from $themeconf if existing. Else, the
+ * key is returned
+ *
+ * @param string key
+ * @return string
+ */
+function get_themeconf($key)
+{
+ global $template;
+
+ return $template->get_themeconf($key);
+}
+
+/**
+ * Returns webmaster mail address depending on $conf['webmaster_id']
+ *
+ * @return string
+ */
+function get_webmaster_mail_address()
+{
+ global $conf;
+
+ $query = '
+SELECT '.$conf['user_fields']['email'].'
+ FROM '.USERS_TABLE.'
+ WHERE '.$conf['user_fields']['id'].' = '.$conf['webmaster_id'].'
+;';
+ list($email) = mysql_fetch_array(pwg_query($query));
+
+ return $email;
+}
+
+/**
+ * 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))
+ {
+ array_push($available_upgrade_ids, $match[1]);
+ }
+ }
+ }
+ natcasesort($available_upgrade_ids);
+
+ return $available_upgrade_ids;
+}
+
+/**
+ * Add configuration parameters from database to global $conf array
+ *
+ * @return void
+ */
+function load_conf_from_db($condition = '')
+{
+ global $conf;
+
+ $query = '
+SELECT param, value
+ FROM '.CONFIG_TABLE.'
+ '.(!empty($condition) ? 'WHERE '.$condition : '').'
+;';
+ $result = pwg_query($query);
+
+ if ((mysql_num_rows($result) == 0) and !empty($condition))
+ {
+ die('No configuration data');
+ }
+
+ while ($row = mysql_fetch_array($result))
+ {
+ $conf[ $row['param'] ] = isset($row['value']) ? $row['value'] : '';
+
+ // If the field is true or false, the variable is transformed into a
+ // boolean value.
+ if ($conf[$row['param']] == 'true' or $conf[$row['param']] == 'false')
+ {
+ $conf[ $row['param'] ] = get_boolean($conf[ $row['param'] ]);
+ }
+ }
+}
+
+/**
+ * Prepends and appends a string at each value of the given array.
+ *
+ * @param array
+ * @param string prefix to each array values
+ * @param string suffix to each array values
+ */
+function prepend_append_array_items($array, $prepend_str, $append_str)
+{
+ array_walk(
+ $array,
+ create_function('&$s', '$s = "'.$prepend_str.'".$s."'.$append_str.'";')
+ );
+
+ return $array;
+}
+
+/**
+ * creates an hashed based on a query, this function is a very common
+ * pattern used here. Among the selected columns fetched, choose one to be
+ * the key, another one to be the value.
+ *
+ * @param string $query
+ * @param string $keyname
+ * @param string $valuename
+ * @return array
+ */
+function simple_hash_from_query($query, $keyname, $valuename)
+{
+ $array = array();
+
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ {
+ $array[ $row[$keyname] ] = $row[$valuename];
+ }
+
+ return $array;
+}
+
+/**
+ * creates an hashed based on a query, this function is a very common
+ * pattern used here. The key is given as parameter, the value is an associative
+ * array.
+ *
+ * @param string $query
+ * @param string $keyname
+ * @return array
+ */
+function hash_from_query($query, $keyname)
+{
+ $array = array();
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_assoc($result))
+ {
+ $array[ $row[$keyname] ] = $row;
+ }
+ return $array;
+}
+
+/**
+ * Return basename of the current script
+ * Lower case convertion is applied on return value
+ * Return value is without file extention ".php"
+ *
+ * @param void
+ *
+ * @return script basename
+ */
+function script_basename()
+{
+ global $conf;
+
+ foreach (array('SCRIPT_NAME', 'SCRIPT_FILENAME', 'PHP_SELF') as $value)
+ {
+ $continue = !empty($_SERVER[$value]);
+ if ($continue)
+ {
+ $filename = strtolower($_SERVER[$value]);
+
+ if ($conf['php_extension_in_urls'])
+ {
+ $continue = get_extension($filename) === 'php';
+ }
+
+ if ($continue)
+ {
+ $basename = basename($filename, '.php');
+ $continue = !empty($basename);
+ }
+
+ if ($continue)
+ {
+ return $basename;
+ }
+ }
+ }
+
+ return '';
+}
+
+/**
+ * Return value for the current page define on $conf['filter_pages']
+ * Îf value is not defined, default value are returned
+ *
+ * @param value name
+ *
+ * @return filter page value
+ */
+function get_filter_page_value($value_name)
+{
+ global $conf;
+
+ $page_name = script_basename();
+
+ if (isset($conf['filter_pages'][$page_name][$value_name]))
+ {
+ return $conf['filter_pages'][$page_name][$value_name];
+ }
+ else if (isset($conf['filter_pages']['default'][$value_name]))
+ {
+ return $conf['filter_pages']['default'][$value_name];
+ }
+ else
+ {
+ return null;
+ }
+}
+
+/**
+ * returns the character set of data sent to browsers / received from forms
+ */
+function get_pwg_charset()
+{
+ defined('PWG_CHARSET') or die('load_language PWG_CHARSET undefined');
+ return PWG_CHARSET;
+}
+
+/**
+ * includes a language file or returns the content of a language file
+ * availability of the file
+ *
+ * in descending order of preference:
+ * param language, user language, default language
+ * Piwigo default language.
+ *
+ * @param string filename
+ * @param string dirname
+ * @param string language
+ * @param bool return_content - if true the file content is returned otherwise
+ * the file is evaluated as php
+ * @return boolean success status or a string if return_content is true
+ */
+function load_language($filename, $dirname = '', $language = '',
+ $return_content=false, $target_charset=null)
+{
+ global $user;
+
+ if (!$return_content)
+ {
+ $filename .= '.php'; //MAYBE to do .. load .po and .mo localization files
+ }
+ if (empty($dirname))
+ {
+ $dirname = PHPWG_ROOT_PATH;
+ }
+ $dirname .= 'language/';
+
+ $languages = array();
+ if ( !empty($language) )
+ {
+ $languages[] = $language;
+ }
+ if ( !empty($user['language']) )
+ {
+ $languages[] = $user['language'];
+ }
+ if ( defined('PHPWG_INSTALLED') )
+ {
+ $languages[] = get_default_language();
+ }
+ $languages[] = PHPWG_DEFAULT_LANGUAGE;
+ $languages = array_unique($languages);
+
+ if ( empty($target_charset) )
+ {
+ $target_charset = get_pwg_charset();
+ }
+ $target_charset = strtolower($target_charset);
+ $source_charset = '';
+ $source_file = '';
+ foreach ($languages as $language)
+ {
+ $dir = $dirname.$language;
+
+ // exact charset match - no conversion required
+ $f = $dir.'.'.$target_charset.'/'.$filename;
+ if (file_exists($f))
+ {
+ $source_file = $f;
+ break;
+ }
+
+ // UTF-8 ?
+ $f = $dir.'/'.$filename;
+ if (file_exists($f))
+ {
+ $source_charset = 'utf-8';
+ $source_file = $f;
+ break;
+ }
+
+ if ($target_charset=='utf-8')
+ { // we accept conversion from ISO-8859-1 to UTF-8
+ $f = $dir.'.iso-8859-1/'.$filename;
+ if (file_exists($f))
+ {
+ $source_charset = 'iso-8859-1';
+ $source_file = $f;
+ break;
+ }
+ }
+ }
+
+ if ( !empty($source_file) )
+ {
+ if (!$return_content)
+ {
+ @include($source_file);
+ $load_lang = @$lang;
+ $load_lang_info = @$lang_info;
+
+ global $lang, $lang_info;
+ if ( !isset($lang) ) $lang=array();
+ if ( !isset($lang_info) ) $lang_info=array();
+
+ if ( !empty($source_charset) and $source_charset!=$target_charset)
+ {
+ if ( is_array($load_lang) )
+ {
+ foreach ($load_lang as $k => $v)
+ {
+ if ( is_array($v) )
+ {
+ $func = create_function('$v', 'return convert_charset($v, "'.$source_charset.'","'.$target_charset.'");' );
+ $lang[$k] = array_map($func, $v);
+ }
+ else
+ $lang[$k] = convert_charset($v, $source_charset, $target_charset);
+ }
+ }
+ if ( is_array($load_lang_info) )
+ {
+ foreach ($load_lang_info as $k => $v)
+ {
+ $lang_info[$k] = convert_charset($v, $source_charset, $target_charset);
+ }
+ }
+ }
+ else
+ {
+ $lang = array_merge( $lang, (array)$load_lang );
+ $lang_info = array_merge( $lang_info, (array)$load_lang_info );
+ }
+ return true;
+ }
+ else
+ {
+ $content = @file_get_contents($source_file);
+ if ( !empty($source_charset) and $source_charset!=$target_charset)
+ {
+ $content = convert_charset($content, $source_charset, $target_charset);
+ }
+ return $content;
+ }
+ }
+ return false;
+}
+
+/**
+ * converts a string from a character set to another character set
+ * @param string str the string to be converted
+ * @param string source_charset the character set in which the string is encoded
+ * @param string dest_charset the destination character set
+ */
+function convert_charset($str, $source_charset, $dest_charset)
+{
+ if ($source_charset==$dest_charset)
+ return $str;
+ if ($source_charset=='iso-8859-1' and $dest_charset=='utf-8')
+ {
+ return utf8_encode($str);
+ }
+ if ($source_charset=='utf-8' and $dest_charset=='iso-8859-1')
+ {
+ return utf8_decode($str);
+ }
+ if (function_exists('iconv'))
+ {
+ return iconv($source_charset, $dest_charset, $str);
+ }
+ if (function_exists('mb_convert_encoding'))
+ {
+ return mb_convert_encoding( $str, $dest_charset, $source_charset );
+ }
+ return $str; //???
+}
+?>
\ No newline at end of file
diff --git a/BSF/include/functions_calendar.inc.php b/BSF/include/functions_calendar.inc.php
new file mode 100644
index 000000000..baa72b927
--- /dev/null
+++ b/BSF/include/functions_calendar.inc.php
@@ -0,0 +1,295 @@
+ 'id'
+ ),
+ 'AND', false
+ );
+ }
+ else
+ {
+ $inner_sql .= '
+ '.get_sql_condition_FandF
+ (
+ array
+ (
+ 'forbidden_categories' => 'category_id',
+ 'visible_categories' => 'category_id',
+ 'visible_images' => 'id'
+ ),
+ 'WHERE', true
+ );
+ }
+ }
+ else
+ {
+ if ( empty($page['items']) )
+ {
+ return; // nothing to do
+ }
+ $inner_sql .= '
+WHERE id IN (' . implode(',',$page['items']) .')';
+ }
+
+//-------------------------------------- initialize the calendar parameters ---
+ pwg_debug('start initialize_calendar');
+
+ $fields = array(
+ // Created
+ 'created' => array(
+ 'label' => l10n('Creation date'),
+ ),
+ // Posted
+ 'posted' => array(
+ 'label' => l10n('Post date'),
+ ),
+ );
+
+ $styles = array(
+ // Monthly style
+ 'monthly' => array(
+ 'include' => 'calendar_monthly.class.php',
+ 'view_calendar' => true,
+ ),
+ // Weekly style
+ 'weekly' => array(
+ 'include' => 'calendar_weekly.class.php',
+ 'view_calendar' => false,
+ ),
+ );
+
+ $views = array(CAL_VIEW_LIST,CAL_VIEW_CALENDAR);
+
+ // Retrieve calendar field
+ if ( !isset( $fields[ $page['chronology_field'] ] ) )
+ {
+ die('bad chronology field');
+ }
+
+ // Retrieve style
+ if ( !isset( $styles[ $page['chronology_style'] ] ) )
+ {
+ $page['chronology_style'] = 'monthly';
+ }
+ $cal_style = $page['chronology_style'];
+ include(PHPWG_ROOT_PATH.'include/'. $styles[$cal_style]['include']);
+ $calendar = new Calendar();
+
+ // Retrieve view
+
+ if ( !isset($page['chronology_view']) or
+ !in_array( $page['chronology_view'], $views ) )
+ {
+ $page['chronology_view'] = CAL_VIEW_LIST;
+ }
+
+ if ( CAL_VIEW_CALENDAR==$page['chronology_view'] and
+ !$styles[$cal_style]['view_calendar'] )
+ {
+
+ $page['chronology_view'] = CAL_VIEW_LIST;
+ }
+
+ // perform a sanity check on $requested
+ if (!isset($page['chronology_date']))
+ {
+ $page['chronology_date'] = array();
+ }
+ while ( count($page['chronology_date']) > 3)
+ {
+ array_pop($page['chronology_date']);
+ }
+
+ $any_count = 0;
+ for ($i = 0; $i < count($page['chronology_date']); $i++)
+ {
+ if ($page['chronology_date'][$i] == 'any')
+ {
+ if ($page['chronology_view'] == CAL_VIEW_CALENDAR)
+ {// we dont allow any in calendar view
+ while ($i < count($page['chronology_date']))
+ {
+ array_pop($page['chronology_date']);
+ }
+ break;
+ }
+ $any_count++;
+ }
+ elseif ($page['chronology_date'][$i] == '')
+ {
+ while ($i < count($page['chronology_date']))
+ {
+ array_pop($page['chronology_date']);
+ }
+ }
+ else
+ {
+ $page['chronology_date'][$i] = (int)$page['chronology_date'][$i];
+ }
+ }
+ if ($any_count == 3)
+ {
+ array_pop($page['chronology_date']);
+ }
+
+ $calendar->initialize($inner_sql);
+
+ //echo ('
'. var_export($calendar, true) . '
');
+
+ $must_show_list = true; // true until calendar generates its own display
+ if (script_basename() != 'picture') // basename without file extention
+ {
+ if ($calendar->generate_category_content())
+ {
+ $page['items'] = array();
+ $must_show_list = false;
+ }
+
+ $page['comment'] = '';
+ $template->assign('FILE_CHRONOLOGY_VIEW', 'month_calendar.tpl');
+
+ foreach ($styles as $style => $style_data)
+ {
+ foreach ($views as $view)
+ {
+ if ( $style_data['view_calendar'] or $view != CAL_VIEW_CALENDAR)
+ {
+ $selected = false;
+
+ if ($style!=$cal_style)
+ {
+ $chronology_date = array();
+ if ( isset($page['chronology_date'][0]) )
+ {
+ array_push($chronology_date, $page['chronology_date'][0]);
+ }
+ }
+ else
+ {
+ $chronology_date = $page['chronology_date'];
+ }
+ $url = duplicate_index_url(
+ array(
+ 'chronology_style' => $style,
+ 'chronology_view' => $view,
+ 'chronology_date' => $chronology_date,
+ )
+ );
+
+ if ($style==$cal_style and $view==$page['chronology_view'] )
+ {
+ $selected = true;
+ }
+
+ $template->append(
+ 'chronology_views',
+ array(
+ 'VALUE' => $url,
+ 'CONTENT' => l10n('chronology_'.$style.'_'.$view),
+ 'SELECTED' => $selected,
+ )
+ );
+ }
+ }
+ }
+ $url = duplicate_index_url(
+ array(), array('start', 'chronology_date')
+ );
+ $calendar_title = ''
+ .$fields[$page['chronology_field']]['label'].'';
+ $calendar_title.= $calendar->get_display_name();
+ $template->assign('chronology',
+ array(
+ 'TITLE' => $calendar_title
+ )
+ );
+ } // end category calling
+
+ if ($must_show_list)
+ {
+ $query = 'SELECT DISTINCT(id)';
+ $query .= $calendar->inner_sql.'
+ '.$calendar->get_date_where();
+ if ( isset($page['super_order_by']) )
+ {
+ $query .= '
+ '.$conf['order_by'];
+ }
+ else
+ {
+ if ( count($page['chronology_date'])==0
+ or in_array('any', $page['chronology_date']) )
+ {// selected period is very big so we show newest first
+ $order = ' DESC, ';
+ }
+ else
+ {// selected period is small (month,week) so we show oldest first
+ $order = ' ASC, ';
+ }
+ $order_by = str_replace(
+ 'ORDER BY ',
+ 'ORDER BY '.$calendar->date_field.$order, $conf['order_by']
+ );
+ $query .= '
+ '.$order_by;
+ }
+ $page['items'] = array_from_query($query, 'id');
+ }
+ pwg_debug('end initialize_calendar');
+}
+
+?>
\ No newline at end of file
diff --git a/BSF/include/functions_category.inc.php b/BSF/include/functions_category.inc.php
new file mode 100644
index 000000000..721cc0038
--- /dev/null
+++ b/BSF/include/functions_category.inc.php
@@ -0,0 +1,509 @@
+ restriction)
+ if (in_array($category_id, explode(',', $user['forbidden_categories'])))
+ {
+ access_denied();
+ }
+}
+
+function get_categories_menu()
+{
+ global $page, $user, $filter;
+
+ $query = '
+SELECT ';
+ // From CATEGORIES_TABLE
+ $query.= '
+ id, name, permalink, nb_images, global_rank,';
+ // From USER_CACHE_CATEGORIES_TABLE
+ $query.= '
+ date_last, max_date_last, count_images, count_categories';
+
+ // $user['forbidden_categories'] including with USER_CACHE_CATEGORIES_TABLE
+ $query.= '
+FROM '.CATEGORIES_TABLE.' INNER JOIN '.USER_CACHE_CATEGORIES_TABLE.'
+ ON id = cat_id and user_id = '.$user['id'];
+
+ // Always expand when filter is activated
+ if (!$user['expand'] and !$filter['enabled'])
+ {
+ $where = '
+(id_uppercat is NULL';
+ if (isset($page['category']))
+ {
+ $where .= ' OR id_uppercat IN ('.$page['category']['uppercats'].')';
+ }
+ $where .= ')';
+ }
+ else
+ {
+ $where = '
+ '.get_sql_condition_FandF
+ (
+ array
+ (
+ 'visible_categories' => 'id',
+ ),
+ null,
+ true
+ );
+ }
+
+ $where = trigger_event('get_categories_menu_sql_where',
+ $where, $user['expand'], $filter['enabled'] );
+
+ $query.= '
+WHERE '.$where.'
+;';
+
+ $result = pwg_query($query);
+ $cats = array();
+ while ($row = mysql_fetch_assoc($result))
+ {
+ array_push($cats, $row);
+ }
+ usort($cats, 'global_rank_compare');
+
+ // Update filtered data
+ if (function_exists('update_cats_with_filtered_data'))
+ {
+ update_cats_with_filtered_data($cats);
+ }
+
+ return get_html_menu_category($cats, @$page['category'] );
+}
+
+
+/**
+ * Retrieve informations about a category in the database
+ *
+ * Returns an array with following keys :
+ *
+ * - comment
+ * - dir : directory, might be empty for virtual categories
+ * - name : an array with indexes from 0 (lowest cat name) to n (most
+ * uppercat name findable)
+ * - nb_images
+ * - id_uppercat
+ * - site_id
+ * -
+ *
+ * @param int category id
+ * @return array
+ */
+function get_cat_info( $id )
+{
+ $query = '
+SELECT *
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id = '.$id.'
+;';
+ $cat = mysql_fetch_assoc(pwg_query($query));
+ if (empty($cat))
+ return null;
+
+ foreach ($cat as $k => $v)
+ {
+ // If the field is true or false, the variable is transformed into a
+ // boolean value.
+ if ($cat[$k] == 'true' or $cat[$k] == 'false')
+ {
+ $cat[$k] = get_boolean( $cat[$k] );
+ }
+ }
+
+ $upper_ids = explode(',', $cat['uppercats']);
+ if ( count($upper_ids)==1 )
+ {// no need to make a query for level 1
+ $cat['upper_names'] = array(
+ array(
+ 'id' => $cat['id'],
+ 'name' => $cat['name'],
+ 'permalink' => $cat['permalink'],
+ )
+ );
+ }
+ else
+ {
+ $names = array();
+ $query = '
+ SELECT id, name, permalink
+ FROM '.CATEGORIES_TABLE.'
+ WHERE id IN ('.$cat['uppercats'].')
+ ;';
+ $names = hash_from_query($query, 'id');
+
+ // category names must be in the same order than uppercats list
+ $cat['upper_names'] = array();
+ foreach ($upper_ids as $cat_id)
+ {
+ array_push( $cat['upper_names'], $names[$cat_id]);
+ }
+ }
+ return $cat;
+}
+
+// get_complete_dir returns the concatenation of get_site_url and
+// get_local_dir
+// Example : "pets > 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 = mysql_fetch_array( 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 = mysql_fetch_array( $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 = mysql_fetch_array(pwg_query($query));
+ return $row['galleries_url'];
+}
+
+// returns an array of image orders available for users/visitors
+function get_category_preferred_image_orders()
+{
+ global $conf;
+ return array(
+ array(l10n('default_sort'), '', true),
+ array(l10n('Average rate'), 'average_rate DESC', $conf['rate']),
+ array(l10n('most_visited_cat'), 'hit DESC', true),
+ array(l10n('Creation date'), 'date_creation DESC', true),
+ array(l10n('Post date'), 'date_available DESC', true),
+ array(l10n('File name'), 'file ASC', true)
+ );
+}
+
+function display_select_categories($categories,
+ $selecteds,
+ $blockname,
+ $fullname = true)
+{
+ global $template;
+
+ $tpl_cats = array();
+ foreach ($categories as $category)
+ {
+ if ($fullname)
+ {
+ $option = get_cat_display_name_cache($category['uppercats'],
+ null,
+ false);
+ }
+ else
+ {
+ $option = str_repeat(' ',
+ (3 * substr_count($category['global_rank'], '.')));
+ $option.= '- '.$category['name'];
+ }
+ $tpl_cats[ $category['id'] ] = $option;
+ }
+
+ $template->assign( $blockname, $tpl_cats);
+ $template->assign( $blockname.'_selected', $selecteds);
+}
+
+function display_select_cat_wrapper($query, $selecteds, $blockname,
+ $fullname = true)
+{
+ $result = pwg_query($query);
+ $categories = array();
+ if (!empty($result))
+ {
+ while ($row = mysql_fetch_assoc($result))
+ {
+ array_push($categories, $row);
+ }
+ }
+ usort($categories, 'global_rank_compare');
+ display_select_categories($categories, $selecteds, $blockname, $fullname);
+}
+
+/**
+ * returns all subcategory identifiers of given category ids
+ *
+ * @param array ids
+ * @return array
+ */
+function get_subcat_ids($ids)
+{
+ $query = '
+SELECT DISTINCT(id)
+ FROM '.CATEGORIES_TABLE.'
+ WHERE ';
+ foreach ($ids as $num => $category_id)
+ {
+ is_numeric($category_id)
+ or trigger_error(
+ 'get_subcat_ids expecting numeric, not '.gettype($category_id),
+ E_USER_WARNING
+ );
+ if ($num > 0)
+ {
+ $query.= '
+ OR ';
+ }
+ $query.= 'uppercats REGEXP \'(^|,)'.$category_id.'(,|$)\'';
+ }
+ $query.= '
+;';
+ $result = pwg_query($query);
+
+ $subcats = array();
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push($subcats, $row['id']);
+ }
+ return $subcats;
+}
+
+/** finds a matching category id from a potential list of permalinks
+ * @param array permalinks example: holiday holiday/france holiday/france/paris
+ * @param int idx - output of the index in $permalinks that matches
+ * return category id or null if no match
+ */
+function get_cat_id_from_permalinks( $permalinks, &$idx )
+{
+ $in = '';
+ foreach($permalinks as $permalink)
+ {
+ if ( !empty($in) ) $in.=', ';
+ $in .= '"'.$permalink.'"';
+ }
+ $query ='
+SELECT cat_id AS id, permalink, 1 AS is_old
+ FROM '.OLD_PERMALINKS_TABLE.'
+ WHERE permalink IN ('.$in.')
+UNION
+SELECT id, permalink, 0 AS is_old
+ FROM '.CATEGORIES_TABLE.'
+ WHERE permalink IN ('.$in.')
+;';
+ $perma_hash = hash_from_query($query, 'permalink');
+
+ if ( empty($perma_hash) )
+ return null;
+ for ($i=count($permalinks)-1; $i>=0; $i--)
+ {
+ if ( isset( $perma_hash[ $permalinks[$i] ] ) )
+ {
+ $idx = $i;
+ $cat_id = $perma_hash[ $permalinks[$i] ]['id'];
+ if ($perma_hash[ $permalinks[$i] ]['is_old'])
+ {
+ $query='
+UPDATE '.OLD_PERMALINKS_TABLE.' SET last_hit=NOW(), hit=hit+1
+ WHERE permalink="'.$permalinks[$i].'" AND cat_id='.$cat_id.'
+ LIMIT 1';
+ pwg_query($query);
+ }
+ return $cat_id;
+ }
+ }
+ return null;
+}
+
+function global_rank_compare($a, $b)
+{
+ return strnatcasecmp($a['global_rank'], $b['global_rank']);
+}
+
+function rank_compare($a, $b)
+{
+ if ($a['rank'] == $b['rank'])
+ {
+ return 0;
+ }
+
+ return ($a['rank'] < $b['rank']) ? -1 : 1;
+}
+
+/**
+ * returns display text for information images of category
+ *
+ * @param array categories
+ * @return string
+ */
+function get_display_images_count($cat_nb_images, $cat_count_images, $cat_count_categories, $short_message = true, $Separator = '\n')
+{
+ $display_text = '';
+
+ if ($cat_count_images > 0)
+ {
+ if ($cat_nb_images > 0 and $cat_nb_images < $cat_count_images)
+ {
+ $display_text.= get_display_images_count($cat_nb_images, $cat_nb_images, 0, $short_message, $Separator).$Separator;
+ $cat_count_images-= $cat_nb_images;
+ $cat_nb_images = 0;
+ }
+
+ //at least one image direct or indirect
+ $display_text.= l10n_dec('%d element', '%d elements', $cat_count_images);
+
+ if ($cat_count_categories == 0 or $cat_nb_images == $cat_count_images)
+ {
+ //no descendant categories or descendants do not contain images
+ if (! $short_message)
+ {
+ $display_text.= ' '.l10n('images_available_cpl');
+ }
+ }
+ else
+ {
+ $display_text.= ' '.l10n_dec('images_available_cat', 'images_available_cats', $cat_count_categories);
+ }
+ }
+
+ return $display_text;
+}
+
+/**
+ * returns the link of upload menu
+ *
+ * @param null
+ * @return string or null
+ */
+function get_upload_menu_link()
+{
+ global $conf, $page, $user;
+
+ $show_link = false;
+ $arg_link = null;
+
+ if (is_autorize_status($conf['upload_user_access']))
+ {
+ if (isset($page['category']) and $page['category']['uploadable'] )
+ {
+ // upload a picture in the category
+ $show_link = true;
+ $arg_link = 'cat='.$page['category']['id'];
+ }
+ else
+ if ($conf['upload_link_everytime'])
+ {
+ // upload a picture in the category
+ $query = '
+SELECT
+ 1
+FROM '.CATEGORIES_TABLE.' INNER JOIN '.USER_CACHE_CATEGORIES_TABLE.'
+ ON id = cat_id and user_id = '.$user['id'].'
+WHERE
+ uploadable = \'true\'
+ '.get_sql_condition_FandF
+ (
+ array
+ (
+ 'visible_categories' => 'id',
+ ),
+ 'AND'
+ ).'
+LIMIT 1';
+
+ $show_link = mysql_num_rows(pwg_query($query)) <> 0;
+ }
+ }
+ if ($show_link)
+ {
+ return get_root_url().'upload.php'.(empty($arg_link) ? '' : '?'.$arg_link);
+ }
+ else
+ {
+ return;
+ }
+}
+
+?>
diff --git a/BSF/include/functions_comment.inc.php b/BSF/include/functions_comment.inc.php
new file mode 100644
index 000000000..6e6498c38
--- /dev/null
+++ b/BSF/include/functions_comment.inc.php
@@ -0,0 +1,228 @@
+$conf['comment_spam_max_links'] )
+ return $my_action;
+
+ return $action;
+}
+
+
+add_event_handler('user_comment_check', 'user_comment_check',
+ EVENT_HANDLER_PRIORITY_NEUTRAL, 2);
+
+/**
+ * Tries to insert a user comment in the database and returns one of :
+ * validate, moderate, reject
+ * @param array comm contains author, content, image_id
+ * @param string key secret key sent back to the browser
+ * @param array infos out array of messages
+ */
+function insert_user_comment( &$comm, $key, &$infos )
+{
+ global $conf, $user;
+
+ $comm = array_merge( $comm,
+ array(
+ 'ip' => $_SERVER['REMOTE_ADDR'],
+ 'agent' => $_SERVER['HTTP_USER_AGENT']
+ )
+ );
+
+ $infos = array();
+ if (!$conf['comments_validation'] or is_admin())
+ {
+ $comment_action='validate'; //one of validate, moderate, reject
+ }
+ else
+ {
+ $comment_action='moderate'; //one of validate, moderate, reject
+ }
+
+ // display author field if the user status is guest or generic
+ if (!is_classic_user())
+ {
+ if ( empty($comm['author']) )
+ {
+ $comm['author'] = 'guest';
+ }
+ // if a guest try to use the name of an already existing user, he must be
+ // rejected
+ if ( $comm['author'] != 'guest' )
+ {
+ $query = '
+SELECT COUNT(*) AS user_exists
+ FROM '.USERS_TABLE.'
+ WHERE '.$conf['user_fields']['username']." = '".addslashes($comm['author'])."'";
+ $row = mysql_fetch_assoc( pwg_query( $query ) );
+ if ( $row['user_exists'] == 1 )
+ {
+ array_push($infos, l10n('comment_user_exists') );
+ $comment_action='reject';
+ }
+ }
+ }
+ else
+ {
+ $comm['author'] = $user['username'];
+ }
+ if ( empty($comm['content']) )
+ { // empty comment content
+ $comment_action='reject';
+ }
+
+ $key = explode( ':', @$key );
+ if ( count($key)!=2
+ or $key[0]>time()-2 // page must have been retrieved more than 2 sec ago
+ or $key[0]" ) returns "Joe"
+//
+// It also works with strings containing themself sub-tags :
+// JeanBillie ->
+// JeanBillie
+function getContent( $element )
+{
+ // deleting start of the tag
+ $content = preg_replace( '/^<[^>]+>/', '', $element );
+ // deleting end of the tag
+ $content = preg_replace( '/<\/[^>]+>$/', '', $content );
+ // replacing multiple instance of space character
+ $content = preg_replace( '/\s+/', ' ', $content );
+
+ return $content;
+}
+
+// The function get Attribute returns the value corresponding to the
+// attribute $attribute for the tag $element.
+function getAttribute( $element, $attribute )
+{
+// echo htmlentities($element).'
';
+ $regex = '/^<\w+[^>]*\b'.$attribute.'\s*=\s*"('.VAL_REG.')"/i';
+ if ( preg_match( $regex, $element, $out ) )
+ {
+ return html_entity_decode($out[1], ENT_QUOTES);
+ }
+ else return '';
+}
+
+// The function encode Attribute returns the xml attribute $attribute="$value"
+function encodeAttribute( $attribute, $value )
+{
+ return $attribute.'="'.htmlspecialchars($value, ENT_QUOTES).'" ';
+}
+
+// The function getChild returns the first child
+// exemple : getChild( "
XXX
YYY
", "tr" )
+// returns "
XXX
"
+function getChild( $document, $node )
+{
+ $regex = '/<'.$node.'(\s+'.ATT_REG.'="'.VAL_REG.'")*';
+ $regex.= '(\s*\/>|>.*<\/'.$node.'>)/U';
+
+ if
+ (
+ preg_match( $regex, $document, $out )
+ or
+ preg_last_error() == PREG_NO_ERROR
+ )
+ {
+ return $out[0];
+ }
+ else
+ {
+ die('getChild: error ['.preg_last_error().'] with preg_match function');
+ }
+}
+
+// getChildren returns a list of the children identified by the $node
+// example :
+// getChild( "
XXX
YYY
", "tr" )
+// returns an array with :
+// $array[0] equals "
XXX
"
+// $array[1] equals "
YYY
"
+function getChildren( $document, $node )
+{
+ $regex = '/<'.$node.'(\s+'.ATT_REG.'="'.VAL_REG.'")*';
+ $regex.= '(\s*\/>|>.*<\/'.$node.'>)/U';
+
+ if
+ (
+ preg_match_all( $regex, $document, $out )
+ or
+ preg_last_error() == PREG_NO_ERROR
+ )
+ {
+ return $out[0];
+ }
+ else
+ {
+ die('getChild: error ['.preg_last_error().'] with preg_match_all function');
+ }
+}
+
+// get_CodeXML places the content of a text file in a PHP variable and
+// return it. If the file can't be opened, returns false.
+function getXmlCode( $filename )
+{
+ if (function_exists('ini_set'))
+ {
+ // limit must be growed with php5 and "big" listing file
+ ini_set("pcre.backtrack_limit", pow(2, 32));
+ }
+
+ $file = fopen( $filename, 'r' );
+ if ( !$file )
+ {
+ return false;
+ }
+
+ $xml_content = '';
+ while ( !feof( $file ) )
+ {
+ $xml_content .= fgets( $file, 1024 );
+ }
+ fclose( $file );
+ $xml_content = str_replace( "\n", '', $xml_content );
+ $xml_content = str_replace( "\t", '', $xml_content );
+
+ return $xml_content;
+}
+?>
diff --git a/BSF/include/index.php b/BSF/include/index.php
new file mode 100644
index 000000000..c15b15795
--- /dev/null
+++ b/BSF/include/index.php
@@ -0,0 +1,30 @@
+
diff --git a/BSF/include/menubar.inc.php b/BSF/include/menubar.inc.php
new file mode 100644
index 000000000..03e941226
--- /dev/null
+++ b/BSF/include/menubar.inc.php
@@ -0,0 +1,311 @@
+set_filenames(
+ array(
+ 'menubar' => 'menubar.tpl',
+ )
+ );
+
+trigger_action('loc_begin_menubar');
+
+$template->assign(
+ array(
+ 'NB_PICTURE' => $user['nb_total_images'],
+ 'MENU_CATEGORIES_CONTENT' => get_categories_menu(),
+ 'U_CATEGORIES' => make_index_url(array('section' => 'categories')),
+ 'U_LOST_PASSWORD' => get_root_url().'password.php',
+ 'U_UPLOAD' => get_upload_menu_link()
+ )
+ );
+
+//-------------------------------------------------------------- external links
+foreach ($conf['links'] as $url => $url_data)
+{
+ if (!is_array($url_data))
+ {
+ $url_data = array('label' => $url_data);
+ }
+
+ if
+ (
+ (!isset($url_data['eval_visible']))
+ or
+ (eval($url_data['eval_visible']))
+ )
+ {
+ $tpl_var = array(
+ 'URL' => $url,
+ 'LABEL' => $url_data['label']
+ );
+
+ if (!isset($url_data['new_window']) or $url_data['new_window'])
+ {
+ $tpl_var['new_window'] =
+ array(
+ 'NAME' => (isset($url_data['nw_name']) ? $url_data['nw_name'] : ''),
+ 'FEATURES' => (isset($url_data['nw_features']) ? $url_data['nw_features'] : '')
+ );
+ }
+ $template->append('links', $tpl_var);
+ }
+}
+
+//------------------------------------------------------------------------ filter
+if (!empty($conf['filter_pages']) and get_filter_page_value('used'))
+{
+ if ($filter['enabled'])
+ {
+ $template->assign(
+ 'U_STOP_FILTER',
+ add_url_params(make_index_url(array()), array('filter' => 'stop'))
+ );
+ }
+ else
+ {
+ $template->assign(
+ 'U_START_FILTER',
+ add_url_params(make_index_url(array()), array('filter' => 'start-recent-'.$user['recent_period']))
+ );
+ }
+}
+
+//------------------------------------------------------------------------ tags
+if ('tags' == @$page['section'])
+{
+ // display tags associated to currently tagged items, less current tags
+ $tags = array();
+ if ( !empty($page['items']) )
+ {
+ $tags = get_common_tags($page['items'],
+ $conf['menubar_tag_cloud_items_number'], $page['tag_ids']);
+ }
+
+ $tags = add_level_to_tags($tags);
+
+ foreach ($tags as $tag)
+ {
+ $template->append(
+ 'related_tags',
+ array(
+ 'U_TAG' => make_index_url(
+ array(
+ 'tags' => array($tag)
+ )
+ ),
+
+ 'NAME' => $tag['name'],
+
+ 'CLASS' => 'tagLevel'.$tag['level'],
+
+ 'add' => array(
+
+ 'URL' => make_index_url(
+ array(
+ 'tags' => array_merge(
+ $page['tags'],
+ array($tag)
+ )
+ )
+ ),
+ 'COUNTER' => $tag['counter'],
+ )
+ )
+ );
+ }
+}
+//---------------------------------------------------------- special categories
+// favorites categories
+if ( !is_a_guest() )
+{
+ $template->append(
+ 'special_categories',
+ array(
+ 'URL' => make_index_url(array('section' => 'favorites')),
+ 'TITLE' => l10n('favorite_cat_hint'),
+ 'NAME' => l10n('favorite_cat')
+ ));
+}
+// most visited
+$template->append(
+ 'special_categories',
+ array(
+ 'URL' => make_index_url(array('section' => 'most_visited')),
+ 'TITLE' => l10n('most_visited_cat_hint'),
+ 'NAME' => l10n('most_visited_cat')
+ ));
+// best rated
+if ($conf['rate'])
+{
+ $template->append(
+ 'special_categories',
+ array(
+ 'URL' => make_index_url(array('section' => 'best_rated')),
+ 'TITLE' => l10n('best_rated_cat_hint'),
+ 'NAME' => l10n('best_rated_cat')
+ )
+ );
+}
+// random
+$template->append(
+ 'special_categories',
+ array(
+ 'URL' => get_root_url().'random.php',
+ 'TITLE' => l10n('random_cat_hint'),
+ 'NAME' => l10n('random_cat'),
+ 'REL'=> 'rel="nofollow"'
+ ));
+
+// recent pics
+$template->append(
+ 'special_categories',
+ array(
+ 'URL' => make_index_url(array('section' => 'recent_pics')),
+ 'TITLE' => l10n('recent_pics_cat_hint'),
+ 'NAME' => l10n('recent_pics_cat'),
+ ));
+// recent cats
+$template->append(
+ 'special_categories',
+ array(
+ 'URL' => make_index_url(array('section' => 'recent_cats')),
+ 'TITLE' => l10n('recent_cats_cat_hint'),
+ 'NAME' => l10n('recent_cats_cat'),
+ ));
+
+// calendar
+$template->append(
+ 'special_categories',
+ array(
+ 'URL' =>
+ make_index_url(
+ array(
+ 'chronology_field' => ($conf['calendar_datefield']=='date_available'
+ ? 'posted' : 'created'),
+ 'chronology_style'=> 'monthly',
+ 'chronology_view' => 'calendar'
+ )
+ ),
+ 'TITLE' => l10n('calendar_hint'),
+ 'NAME' => l10n('calendar'),
+ 'REL'=> 'rel="nofollow"'
+ )
+ );
+//--------------------------------------------------------------------- summary
+
+if (is_a_guest())
+{
+ $template->assign(
+ array(
+ 'U_IDENTIFY' => get_root_url().'identification.php',
+ 'AUTHORIZE_REMEMBERING' => $conf['authorize_remembering']
+ )
+ );
+
+ if ($conf['allow_user_registration'])
+ {
+ $template->assign( 'U_REGISTER', get_root_url().'register.php');
+ }
+}
+else
+{
+ $template->assign('USERNAME', $user['username']);
+
+ if (is_autorize_status(ACCESS_CLASSIC))
+ {
+ $template->assign('U_PROFILE', get_root_url().'profile.php');
+ }
+
+ // the logout link has no meaning with Apache authentication : it is not
+ // possible to logout with this kind of authentication.
+ if (!$conf['apache_authentication'])
+ {
+ $template->assign('U_LOGOUT', get_root_url().'?act=logout');
+ }
+
+ if (is_admin())
+ {
+ $template->assign('U_ADMIN', get_root_url().'admin.php');
+ }
+}
+
+// tags link
+$template->append(
+ 'summaries',
+ array(
+ 'TITLE' => l10n('See available tags'),
+ 'NAME' => l10n('Tags'),
+ 'U_SUMMARY'=> get_root_url().'tags.php',
+ )
+ );
+
+// search link
+$template->append(
+ 'summaries',
+ array(
+ 'TITLE'=>l10n('hint_search'),
+ 'NAME'=>l10n('Search'),
+ 'U_SUMMARY'=> get_root_url().'search.php',
+ 'REL'=> 'rel="search"'
+ )
+ );
+
+// comments link
+$template->append(
+ 'summaries',
+ array(
+ 'TITLE'=>l10n('hint_comments'),
+ 'NAME'=>l10n('comments'),
+ 'U_SUMMARY'=> get_root_url().'comments.php',
+ )
+ );
+
+// about link
+$template->append(
+ 'summaries',
+ array(
+ 'TITLE' => l10n('about_page_title'),
+ 'NAME' => l10n('About'),
+ 'U_SUMMARY' => get_root_url().'about.php',
+ )
+ );
+
+// notification
+$template->append(
+ 'summaries',
+ array(
+ 'TITLE'=>l10n('RSS feed'),
+ 'NAME'=>l10n('Notification'),
+ 'U_SUMMARY'=> get_root_url().'notification.php',
+ 'REL'=> 'rel="nofollow"'
+ )
+ );
+
+trigger_action('loc_end_menubar');
+$template->assign_var_from_handle('MENUBAR', 'menubar');
+
+?>
diff --git a/BSF/include/page_header.php b/BSF/include/page_header.php
new file mode 100644
index 000000000..102e2bdd8
--- /dev/null
+++ b/BSF/include/page_header.php
@@ -0,0 +1,90 @@
+set_filenames(array('header'=>'header.tpl'));
+
+trigger_action('loc_begin_page_header');
+
+$template->assign(
+ array(
+ 'GALLERY_TITLE' =>
+ isset($page['gallery_title']) ?
+ $page['gallery_title'] : $conf['gallery_title'],
+
+ 'PAGE_BANNER' =>
+ trigger_event('render_page_banner',
+ isset($page['page_banner']) ?
+ $page['page_banner'] : $conf['page_banner']),
+
+ 'BODY_ID' =>
+ isset($page['body_id']) ?
+ $page['body_id'] : '',
+
+ 'CONTENT_ENCODING' => get_pwg_charset(),
+ 'PAGE_TITLE' => strip_tags($title),
+ 'LANG'=>$lang_info['code'],
+ 'DIR'=>$lang_info['direction'],
+
+ 'U_HOME' => make_index_url(),
+ ));
+
+
+// Header notes
+if ( !empty($header_notes) )
+{
+ $template->assign('header_notes',$header_notes);
+}
+
+if ( !empty($page['meta_robots']) )
+{
+ $template->append('head_elements',
+ ''
+ );
+}
+
+// refresh
+if ( isset( $refresh ) and intval($refresh) >= 0
+ and isset( $url_link ) and isset( $redirect_msg ) )
+{
+ $template->assign(
+ array(
+ 'REDIRECT_MSG' => $redirect_msg,
+ 'page_refresh' => array(
+ 'TIME' => $refresh,
+ 'U_REFRESH' => $url_link
+ )
+ ));
+}
+
+trigger_action('loc_end_page_header');
+
+header('Content-Type: text/html; charset='.get_pwg_charset());
+$template->parse('header');
+
+trigger_action('loc_after_page_header');
+?>
\ No newline at end of file
diff --git a/BSF/include/page_tail.php b/BSF/include/page_tail.php
new file mode 100644
index 000000000..670d23a2e
--- /dev/null
+++ b/BSF/include/page_tail.php
@@ -0,0 +1,74 @@
+set_filenames(array('tail'=>'footer.tpl'));
+
+trigger_action('loc_begin_page_tail');
+
+$template->assign(
+ array(
+ 'VERSION' => $conf['show_version'] ? PHPWG_VERSION : '',
+ 'PHPWG_URL' => PHPWG_URL,
+ ));
+
+//--------------------------------------------------------------------- contact
+
+if (!is_a_guest())
+{
+ $template->assign(
+ 'CONTACT_MAIL', get_webmaster_mail_address()
+ );
+}
+
+//------------------------------------------------------------- generation time
+$debug_vars = array();
+
+if ($conf['show_queries'])
+{
+ $debug_vars = array_merge($debug_vars, array('QUERIES_LIST' => $debug) );
+}
+
+if ($conf['show_gt'])
+{
+ if (!isset($page['count_queries']))
+ {
+ $page['count_queries'] = 0;
+ $page['queries_time'] = 0;
+ }
+ $time = get_elapsed_time($t2, get_moment());
+
+ $debug_vars = array_merge($debug_vars,
+ array('TIME' => $time,
+ 'NB_QUERIES' => $page['count_queries'],
+ 'SQL_TIME' => number_format($page['queries_time'],3,'.',' ').' s')
+ );
+}
+
+$template->assign('debug', $debug_vars );
+
+trigger_action('loc_end_page_tail');
+//
+// Generate the page
+//
+$template->parse('tail');
+$template->p();
+?>
\ No newline at end of file
diff --git a/BSF/include/php_compat/array_intersect_key.php b/BSF/include/php_compat/array_intersect_key.php
new file mode 100644
index 000000000..748b8f6f1
--- /dev/null
+++ b/BSF/include/php_compat/array_intersect_key.php
@@ -0,0 +1,35 @@
+= 5.1.0RC1
+function array_intersect_key()
+{
+ $args = func_get_args();
+ if (count($args) < 2) {
+ trigger_error('Wrong parameter count for array_intersect_key()', E_USER_WARNING);
+ return;
+ }
+
+ // Check arrays
+ $array_count = count($args);
+ for ($i = 0; $i !== $array_count; $i++) {
+ if (!is_array($args[$i])) {
+ trigger_error('array_intersect_key() Argument #' . ($i + 1) . ' is not an array', E_USER_WARNING);
+ return;
+ }
+ }
+
+ // Compare entries
+ $result = array();
+ foreach ($args[0] as $key1 => $value1) {
+ for ($i = 1; $i !== $array_count; $i++) {
+ foreach ($args[$i] as $key2 => $value2) {
+ if ((string) $key1 === (string) $key2) {
+ $result[$key1] = $value1;
+ }
+ }
+ }
+ }
+
+ return $result;
+}
+?>
\ No newline at end of file
diff --git a/BSF/include/php_compat/file_put_contents.php b/BSF/include/php_compat/file_put_contents.php
new file mode 100644
index 000000000..679d9c984
--- /dev/null
+++ b/BSF/include/php_compat/file_put_contents.php
@@ -0,0 +1,14 @@
+
\ No newline at end of file
diff --git a/BSF/include/php_compat/hash_hmac.php b/BSF/include/php_compat/hash_hmac.php
new file mode 100644
index 000000000..5f05e370c
--- /dev/null
+++ b/BSF/include/php_compat/hash_hmac.php
@@ -0,0 +1,25 @@
+'H32','sha1'=>'H40');
+ if ( !isset($p[$algo]) or !function_exists($algo) )
+ {
+ $algo = 'md5';
+ }
+ if(strlen($key)>64) $key=pack($p[$algo],$algo($key));
+ if(strlen($key)<64) $key=str_pad($key,64,chr(0));
+
+ $ipad=substr($key,0,64) ^ str_repeat(chr(0x36),64);
+ $opad=substr($key,0,64) ^ str_repeat(chr(0x5C),64);
+
+ $ret = $algo($opad.pack($p[$algo],$algo($ipad.$data)));
+ if ($raw_output)
+ {
+ $ret = pack('H*', $ret);
+ }
+ return $ret;
+}
+?>
\ No newline at end of file
diff --git a/BSF/include/php_compat/index.php b/BSF/include/php_compat/index.php
new file mode 100644
index 000000000..c15b15795
--- /dev/null
+++ b/BSF/include/php_compat/index.php
@@ -0,0 +1,30 @@
+
diff --git a/BSF/include/php_compat/preg_last_error.php b/BSF/include/php_compat/preg_last_error.php
new file mode 100644
index 000000000..c564aeba9
--- /dev/null
+++ b/BSF/include/php_compat/preg_last_error.php
@@ -0,0 +1,41 @@
+= 5.2.0
+if (!defined('PREG_NO_ERROR'))
+ define('PREG_NO_ERROR', 0);
+if (!defined('PREG_INTERNAL_ERROR'))
+ define('PREG_INTERNAL_ERROR', 1);
+if (!defined('PREG_BACKTRACK_LIMIT_ERROR'))
+ define('PREG_BACKTRACK_LIMIT_ERROR', 2);
+if (!defined('PREG_RECURSION_LIMIT_ERROR'))
+ define('PREG_RECURSION_LIMIT_ERROR', 3);
+if (!defined('PREG_BAD_UTF8_ERROR'))
+ define('PREG_BAD_UTF8_ERROR', 4);
+
+function preg_last_error()
+{
+ return PREG_NO_ERROR;
+}
+?>
\ No newline at end of file
diff --git a/BSF/include/picture_comment.inc.php b/BSF/include/picture_comment.inc.php
new file mode 100644
index 000000000..ad0438d96
--- /dev/null
+++ b/BSF/include/picture_comment.inc.php
@@ -0,0 +1,183 @@
+ trim( stripslashes(@$_POST['author']) ),
+ 'content' => trim( stripslashes($_POST['content']) ),
+ 'image_id' => $page['image_id'],
+ );
+
+ include_once(PHPWG_ROOT_PATH.'include/functions_comment.inc.php');
+
+ $comment_action = insert_user_comment($comm, @$_POST['key'], $infos );
+
+ switch ($comment_action)
+ {
+ case 'moderate':
+ array_push( $infos, l10n('comment_to_validate') );
+ case 'validate':
+ array_push( $infos, l10n('comment_added'));
+ break;
+ case 'reject':
+ set_status_header(403);
+ array_push($infos, l10n('comment_not_added') );
+ break;
+ default:
+ trigger_error('Invalid comment action '.$comment_action, E_USER_WARNING);
+ }
+
+ $template->assign(
+ ($comment_action=='reject') ? 'errors' : 'infos',
+ $infos
+ );
+
+ // allow plugins to notify what's going on
+ trigger_action( 'user_comment_insertion',
+ array_merge($comm, array('action'=>$comment_action) )
+ );
+}
+elseif ( isset($_POST['content']) )
+{
+ set_status_header(403);
+ die('ugly spammer');
+}
+
+if ($page['show_comments'])
+{
+ // number of comment for this picture
+ $query = 'SELECT COUNT(*) AS nb_comments';
+ $query.= ' FROM '.COMMENTS_TABLE.' WHERE image_id = '.$page['image_id'];
+ $query.= " AND validated = 'true'";
+ $query.= ';';
+ $row = mysql_fetch_array( pwg_query( $query ) );
+
+ // navigation bar creation
+ if (!isset($page['start']))
+ {
+ $page['start'] = 0;
+ }
+
+ $navigation_bar = create_navigation_bar(
+ duplicate_picture_url(array(), array('start')),
+ $row['nb_comments'],
+ $page['start'],
+ $conf['nb_comment_page'],
+ true // We want a clean URL
+ );
+
+ $template->assign(
+ array(
+ 'COMMENT_COUNT' => $row['nb_comments'],
+ 'COMMENT_NAV_BAR' => $navigation_bar,
+ )
+ );
+
+ if ($row['nb_comments'] > 0)
+ {
+ $query = '
+SELECT id,author,date,image_id,content
+ FROM '.COMMENTS_TABLE.'
+ WHERE image_id = '.$page['image_id'].'
+ AND validated = \'true\'
+ ORDER BY date ASC
+ LIMIT '.$page['start'].', '.$conf['nb_comment_page'].'
+;';
+ $result = pwg_query( $query );
+
+ while ($row = mysql_fetch_array($result))
+ {
+ $tpl_comment =
+ array(
+ 'AUTHOR' => trigger_event('render_comment_author',
+ empty($row['author'])
+ ? l10n('guest')
+ : $row['author']),
+
+ 'DATE' => format_date(
+ $row['date'],
+ 'mysql_datetime',
+ true),
+
+ 'CONTENT' => trigger_event('render_comment_content',$row['content']),
+ );
+
+ if (is_admin())
+ {
+ $tpl_comment['U_DELETE'] =
+ add_url_params(
+ $url_self,
+ array(
+ 'action'=>'delete_comment',
+ 'comment_to_delete'=>$row['id']
+ )
+ );
+ }
+ $template->append('comments', $tpl_comment);
+ }
+ }
+
+ if (!is_a_guest()
+ or (is_a_guest() and $conf['comments_forall']))
+ {
+ include_once(PHPWG_ROOT_PATH.'include/functions_comment.inc.php');
+ $key = get_comment_post_key($page['image_id']);
+ $content = '';
+ if ('reject'===@$comment_action)
+ {
+ $content = htmlspecialchars($comm['content']);
+ }
+ $template->assign('comment_add',
+ array(
+ 'F_ACTION' => $url_self,
+ 'KEY' => $key,
+ 'CONTENT' => $content,
+ 'SHOW_AUTHOR' => !is_classic_user()
+ ));
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/BSF/include/picture_metadata.inc.php b/BSF/include/picture_metadata.inc.php
new file mode 100644
index 000000000..bc6410897
--- /dev/null
+++ b/BSF/include/picture_metadata.inc.php
@@ -0,0 +1,99 @@
+ 'EXIF Metadata',
+ 'lines' => array(),
+ );
+
+ foreach ($conf['show_exif_fields'] as $field)
+ {
+ if (strpos($field, ';') === false)
+ {
+ if (isset($exif[$field]))
+ {
+ $key = $field;
+ if (isset($lang['exif_field_'.$field]))
+ {
+ $key = $lang['exif_field_'.$field];
+ }
+ $tpl_meta['lines'][$key] = $exif[$field];
+ }
+ }
+ else
+ {
+ $tokens = explode(';', $field);
+ if (isset($exif[$tokens[0]][$tokens[1]]))
+ {
+ $key = $tokens[1];
+ if (isset($lang['exif_field_'.$tokens[1]]))
+ {
+ $key = $lang['exif_field_'.$tokens[1]];
+ }
+ $tpl_meta['lines'][$key] = $exif[$tokens[0]][$tokens[1]];
+ }
+ }
+ }
+ $template->append('metadata', $tpl_meta);
+ }
+}
+
+if ($conf['show_iptc'])
+{
+ $iptc = get_iptc_data($picture['current']['image_path'],
+ $conf['show_iptc_mapping']);
+
+ if (count($iptc) > 0)
+ {
+ $tpl_meta = array(
+ 'TITLE' => 'IPTC Metadata',
+ 'lines' => array(),
+ );
+
+ foreach ($iptc as $field => $value)
+ {
+ $key = $field;
+ if (isset($lang[$field]))
+ {
+ $key = $lang[$field];
+ }
+ $tpl_meta['lines'][$key] = $value;
+ }
+ $template->append('metadata', $tpl_meta);
+ }
+}
+
+
+?>
diff --git a/BSF/include/picture_rate.inc.php b/BSF/include/picture_rate.inc.php
new file mode 100644
index 000000000..aa1071452
--- /dev/null
+++ b/BSF/include/picture_rate.inc.php
@@ -0,0 +1,91 @@
+ no rate -> no need to query db
+ $row = array( 'count'=>0, 'average'=>NULL, 'std'=>NULL );
+ }
+ $template->assign('rate_summary', $row);
+
+ $user_rate = null;
+ if ($conf['rate_anonymous'] or is_autorize_status(ACCESS_CLASSIC) )
+ {
+ if ($row['count']>0)
+ {
+ $query = 'SELECT rate
+ FROM '.RATE_TABLE.'
+ WHERE element_id = '.$page['image_id'] . '
+ AND user_id = '.$user['id'] ;
+
+ if ( !is_autorize_status(ACCESS_CLASSIC) )
+ {
+ $ip_components = explode('.', $_SERVER['REMOTE_ADDR']);
+ if ( count($ip_components)>3 )
+ {
+ array_pop($ip_components);
+ }
+ $anonymous_id = implode ('.', $ip_components);
+ $query .= ' AND anonymous_id = \''.$anonymous_id . '\'';
+ }
+
+ $result = pwg_query($query);
+ if (mysql_num_rows($result) > 0)
+ {
+ $row = mysql_fetch_array($result);
+ $user_rate = $row['rate'];
+ }
+ }
+
+ $template->assign(
+ 'rating',
+ array(
+ 'F_ACTION' => add_url_params(
+ $url_self,
+ array('action'=>'rate')
+ ),
+ 'USER_RATE'=> $user_rate,
+ 'marks' => $conf['rate_items']
+ )
+ );
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/BSF/include/section_init.inc.php b/BSF/include/section_init.inc.php
new file mode 100644
index 000000000..ffd6cac1a
--- /dev/null
+++ b/BSF/include/section_init.inc.php
@@ -0,0 +1,620 @@
+ 'categories',
+// 'category' => array('id'=>12, ...),
+// 'start' => 24
+// );
+
+$page['items'] = array();
+
+// some ISPs set PATH_INFO to empty string or to SCRIPT_FILENAME while in the
+// default apache implementation it is not set
+if ( $conf['question_mark_in_urls']==false and
+ isset($_SERVER["PATH_INFO"]) and !empty($_SERVER["PATH_INFO"]) )
+{
+ $rewritten = $_SERVER["PATH_INFO"];
+ $rewritten = str_replace('//', '/', $rewritten);
+ $path_count = count( explode('/', $rewritten) );
+ $page['root_path'] = PHPWG_ROOT_PATH.str_repeat('../', $path_count-1);
+}
+else
+{
+ $rewritten = '';
+ foreach (array_keys($_GET) as $keynum => $key)
+ {
+ $rewritten = $key;
+ break;
+ }
+ $page['root_path'] = PHPWG_ROOT_PATH;
+}
+
+// deleting first "/" if displayed
+$tokens = explode(
+ '/',
+ preg_replace('#^/#', '', $rewritten)
+ );
+// $tokens = array(
+// 0 => category,
+// 1 => 12-foo,
+// 2 => start-24
+// );
+
+$next_token = 0;
+if (script_basename() == 'picture') // basename without file extention
+{ // the first token must be the identifier for the picture
+ if ( isset($_GET['image_id'])
+ and isset($_GET['cat']) and is_numeric($_GET['cat']) )
+ {// url compatibility with versions below 1.6
+ $url = make_picture_url( array(
+ 'section' => 'categories',
+ 'category' => get_cat_info($_GET['cat']),
+ 'image_id' => $_GET['image_id']
+ ) );
+ redirect($url);
+ }
+ $token = $tokens[$next_token];
+ $next_token++;
+ if ( is_numeric($token) )
+ {
+ $page['image_id'] = $token;
+ }
+ else
+ {
+ preg_match('/^(\d+-)?(.*)?$/', $token, $matches);
+ if (isset($matches[1]) and is_numeric($matches[1]=rtrim($matches[1],'-')) )
+ {
+ $page['image_id'] = $matches[1];
+ if ( !empty($matches[2]) )
+ {
+ $page['image_file'] = $matches[2];
+ }
+ }
+ else
+ {
+ if ( !empty($matches[2]) )
+ {
+ $page['image_file'] = $matches[2];
+ }
+ else
+ {
+ bad_request('picture identifier is missing');
+ }
+ }
+ }
+}
+
+$page = array_merge( $page, parse_section_url( $tokens, $next_token) );
+if ( !isset($page['section']) )
+{
+ $page['section'] = 'categories';
+
+ switch (script_basename())
+ {
+ case 'picture':
+ break;
+ case 'index':
+ {
+ // No section defined, go to selected url
+ if (!empty($conf['random_index_redirect']) and empty($tokens[$next_token]) )
+ {
+ $random_index_redirect = array();
+ foreach ($conf['random_index_redirect'] as $random_url => $random_url_condition)
+ {
+ if (empty($random_url_condition) or eval($random_url_condition))
+ {
+ $random_index_redirect[] = $random_url;
+ }
+ }
+ if (!empty($random_index_redirect))
+ {
+ redirect($random_index_redirect[mt_rand(0, count($random_index_redirect)-1)]);
+ }
+ }
+ break;
+ }
+ default:
+ trigger_error('script_basename "'.script_basename().'" unknown',
+ E_USER_WARNING);
+ }
+}
+
+
+$page = array_merge( $page, parse_well_known_params_url( $tokens, $next_token) );
+
+
+if ( script_basename()=='picture' and 'categories'==$page['section'] and
+ !isset($page['category']) and !isset($page['chronology_field']) )
+{ //access a picture only by id, file or id-file without given section
+ $page['flat']=true;
+}
+
+// $page['nb_image_page'] is the number of picture to display on this page
+// By default, it is the same as the $user['nb_image_page']
+$page['nb_image_page'] = $user['nb_image_page'];
+
+if (pwg_get_session_var('image_order',0) > 0)
+{
+ $orders = get_category_preferred_image_orders();
+
+ $conf['order_by'] = str_replace(
+ 'ORDER BY ',
+ 'ORDER BY '.$orders[ pwg_get_session_var('image_order',0) ][1].',',
+ $conf['order_by']
+ );
+ $page['super_order_by'] = true;
+}
+
+$forbidden = get_sql_condition_FandF(
+ array
+ (
+ 'forbidden_categories' => 'category_id',
+ 'visible_categories' => 'category_id',
+ 'visible_images' => 'id'
+ ),
+ 'AND'
+ );
+
+// +-----------------------------------------------------------------------+
+// | category |
+// +-----------------------------------------------------------------------+
+if ('categories' == $page['section'])
+{
+ if (isset($page['category']))
+ {
+ $page = array_merge(
+ $page,
+ array(
+ 'comment' =>
+ trigger_event(
+ 'render_category_description',
+ $page['category']['comment'],
+ 'main_page_category_description'
+ ),
+ 'title' =>
+ get_cat_display_name($page['category']['upper_names'], '', false),
+ )
+ );
+ }
+ else
+ {
+ $page['title'] = l10n('no_category');
+ }
+
+ if
+ (
+ (!isset($page['chronology_field'])) and
+ (
+ (isset($page['category'])) or
+ (isset($page['flat']))
+ )
+ )
+ {
+ if ( !empty($page['category']['image_order']) and !isset($page['super_order_by']) )
+ {
+ $conf[ 'order_by' ] = ' ORDER BY '.$page['category']['image_order'];
+ }
+
+ if (isset($page['flat']))
+ {// flat categories mode
+ if ( isset($page['category']) )
+ { // get all allowed sub-categories
+ $query = '
+SELECT id
+ FROM '.CATEGORIES_TABLE.'
+ WHERE
+ uppercats LIKE "'.$page['category']['uppercats'].',%" '
+ .get_sql_condition_FandF(
+ array
+ (
+ 'forbidden_categories' => 'id',
+ 'visible_categories' => 'id',
+ ),
+ "\n AND"
+ );
+ $subcat_ids = array_from_query($query, 'id');
+ $subcat_ids[] = $page['category']['id'];
+ $where_sql = 'category_id IN ('.implode(',',$subcat_ids).')';
+ // remove categories from forbidden because just checked above
+ $forbidden = get_sql_condition_FandF(
+ array( 'visible_images' => 'id' ),
+ 'AND'
+ );
+ }
+ else
+ {
+ $where_sql = '1=1';
+ }
+ }
+ else
+ {// Normal mode
+ $where_sql = 'category_id = '.$page['category']['id'];
+ }
+
+ // Main query
+ $query = '
+SELECT DISTINCT(image_id)
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ INNER JOIN '.IMAGES_TABLE.' ON id = image_id
+ WHERE
+ '.$where_sql.'
+'.$forbidden.'
+ '.$conf['order_by'].'
+;';
+
+ $page['items'] = array_from_query($query, 'image_id');
+ } //otherwise the calendar will requery all subitems
+}
+// special sections
+else
+{
+// +-----------------------------------------------------------------------+
+// | tags section |
+// +-----------------------------------------------------------------------+
+ if ($page['section'] == 'tags')
+ {
+ $page['tag_ids'] = array();
+ foreach ($page['tags'] as $tag)
+ {
+ array_push($page['tag_ids'], $tag['id']);
+ }
+
+ $items = get_image_ids_for_tags($page['tag_ids']);
+
+ // permissions depends on category, so to only keep images that are
+ // reachable to the connected user, we need to check category
+ // associations
+ if (!empty($items) )
+ {
+ $query = '
+SELECT DISTINCT image_id
+ FROM '.IMAGE_CATEGORY_TABLE.' INNER JOIN '.IMAGES_TABLE.' ON image_id=id
+ WHERE image_id IN ('.implode(',', $items).')
+ '.$forbidden.
+ $conf['order_by'].'
+;';
+ $items = array_from_query($query, 'image_id');
+ }
+
+ $title = get_tags_content_title();
+
+ $page = array_merge(
+ $page,
+ array(
+ 'title' => $title,
+ 'items' => $items,
+ )
+ );
+ }
+// +-----------------------------------------------------------------------+
+// | search section |
+// +-----------------------------------------------------------------------+
+ if ($page['section'] == 'search')
+ {
+ include_once( PHPWG_ROOT_PATH .'include/functions_search.inc.php' );
+
+ $search_result = get_search_results($page['search']);
+ if ( !empty($search_result['items']) and !isset($search_result['as_is']) )
+ {
+ $query = '
+SELECT DISTINCT(id)
+ FROM '.IMAGES_TABLE.'
+ INNER JOIN '.IMAGE_CATEGORY_TABLE.' AS ic ON id = ic.image_id
+ WHERE id IN ('.implode(',', $search_result['items']).')
+ '.$forbidden.'
+ '.$conf['order_by'].'
+;';
+ $page['items'] = array_from_query($query, 'id');
+ }
+ else
+ {
+ $page['items'] = $search_result['items'];
+ if ( isset($search_result['qs']) )
+ {//save the details of the query search
+ $page['qsearch_details'] = $search_result['qs'];
+ }
+ }
+
+ $page = array_merge(
+ $page,
+ array(
+ 'title' => ''
+ .l10n('search_result').'',
+ )
+ );
+ }
+// +-----------------------------------------------------------------------+
+// | favorite section |
+// +-----------------------------------------------------------------------+
+ else if ($page['section'] == 'favorites')
+ {
+ check_user_favorites();
+
+ $query = '
+SELECT image_id
+ FROM '.FAVORITES_TABLE.'
+ INNER JOIN '.IMAGES_TABLE.' ON image_id = id
+ WHERE user_id = '.$user['id'].'
+'.get_sql_condition_FandF
+ (
+ array
+ (
+ 'visible_images' => 'image_id'
+ ),
+ 'AND'
+ ).'
+ '.$conf['order_by'].'
+;';
+
+ $page = array_merge(
+ $page,
+ array(
+ 'title' => l10n('favorites'),
+ 'items' => array_from_query($query, 'image_id'),
+ )
+ );
+ }
+// +-----------------------------------------------------------------------+
+// | recent pictures section |
+// +-----------------------------------------------------------------------+
+ else if ($page['section'] == 'recent_pics')
+ {
+ $query = '
+SELECT DISTINCT(id)
+ FROM '.IMAGES_TABLE.'
+ INNER JOIN '.IMAGE_CATEGORY_TABLE.' AS ic ON id = ic.image_id
+ WHERE
+ date_available >= SUBDATE(
+ CURRENT_DATE,INTERVAL '.$user['recent_period'].' DAY)
+ '.$forbidden.'
+ '.$conf['order_by'].'
+;';
+
+ $page = array_merge(
+ $page,
+ array(
+ 'title' => ''
+ .l10n('recent_pics_cat').'',
+ 'items' => array_from_query($query, 'id'),
+ )
+ );
+ }
+// +-----------------------------------------------------------------------+
+// | recently updated categories section |
+// +-----------------------------------------------------------------------+
+ else if ($page['section'] == 'recent_cats')
+ {
+ $page = array_merge(
+ $page,
+ array(
+ 'title' => l10n('recent_cats_cat'),
+ )
+ );
+ }
+// +-----------------------------------------------------------------------+
+// | most visited section |
+// +-----------------------------------------------------------------------+
+ else if ($page['section'] == 'most_visited')
+ {
+ $page['super_order_by'] = true;
+ $conf['order_by'] = ' ORDER BY hit DESC, file ASC';
+ $query = '
+SELECT DISTINCT(id)
+ FROM '.IMAGES_TABLE.'
+ INNER JOIN '.IMAGE_CATEGORY_TABLE.' AS ic ON id = ic.image_id
+ WHERE hit > 0
+ '.$forbidden.'
+ '.$conf['order_by'].'
+ LIMIT 0, '.$conf['top_number'].'
+;';
+
+ $page = array_merge(
+ $page,
+ array(
+ 'title' => ''
+ .$conf['top_number'].' '.l10n('most_visited_cat').'',
+ 'items' => array_from_query($query, 'id'),
+ )
+ );
+ }
+// +-----------------------------------------------------------------------+
+// | best rated section |
+// +-----------------------------------------------------------------------+
+ else if ($page['section'] == 'best_rated')
+ {
+ $page['super_order_by'] = true;
+ $conf['order_by'] = ' ORDER BY average_rate DESC, id ASC';
+
+ $query ='
+SELECT DISTINCT(id)
+ FROM '.IMAGES_TABLE.'
+ INNER JOIN '.IMAGE_CATEGORY_TABLE.' AS ic ON id = ic.image_id
+ WHERE average_rate IS NOT NULL
+ '.$forbidden.'
+ '.$conf['order_by'].'
+ LIMIT 0, '.$conf['top_number'].'
+;';
+ $page = array_merge(
+ $page,
+ array(
+ 'title' => ''
+ .$conf['top_number'].' '.l10n('best_rated_cat').'',
+ 'items' => array_from_query($query, 'id'),
+ )
+ );
+ }
+// +-----------------------------------------------------------------------+
+// | list section |
+// +-----------------------------------------------------------------------+
+ else if ($page['section'] == 'list')
+ {
+ $query ='
+SELECT DISTINCT(id)
+ FROM '.IMAGES_TABLE.'
+ INNER JOIN '.IMAGE_CATEGORY_TABLE.' AS ic ON id = ic.image_id
+ WHERE image_id IN ('.implode(',', $page['list']).')
+ '.$forbidden.'
+ '.$conf['order_by'].'
+;';
+
+ $page = array_merge(
+ $page,
+ array(
+ 'title' => ''
+ .l10n('random_cat').'',
+ 'items' => array_from_query($query, 'id'),
+ )
+ );
+ }
+}
+
+// +-----------------------------------------------------------------------+
+// | chronology |
+// +-----------------------------------------------------------------------+
+
+if (isset($page['chronology_field']))
+{
+ include_once( PHPWG_ROOT_PATH.'include/functions_calendar.inc.php' );
+ initialize_calendar();
+}
+
+if (script_basename() == 'picture'
+ and !isset($page['image_id']) )
+{
+ if ( !empty($page['items']) )
+ {
+ $query = '
+SELECT id,file
+ FROM '.IMAGES_TABLE .'
+ WHERE file LIKE "' . $page['image_file'] . '.%" ESCAPE "|"';
+ if ( count($page['items']) < 500)
+ {// for very large item sets do not add IN - because slow
+ $query .= '
+ AND id IN ('.implode(',',$page['items']).')
+ LIMIT 0,1';
+ }
+ $result = pwg_query($query);
+ switch (mysql_num_rows($result))
+ {
+ case 0: break;
+ case 1:
+ list($page['image_id'], $page['image_file']) = mysql_fetch_row($result);
+ break;
+ default: // more than 1 file name match
+ while ($row = mysql_fetch_row($result) )
+ {
+ if ( in_array($row[0], $page['items']) )
+ {
+ list($page['image_id'], $page['image_file']) = $row;
+ break;
+ }
+ }
+ }
+ }
+ if ( !isset($page['image_id']) )
+ {
+ $page['image_id'] = -1; // will fail in picture.php
+ }
+}
+
+// add meta robots noindex, nofollow to avoid unnecesary robot crawls
+$page['meta_robots']=array();
+if ( isset($page['chronology_field'])
+ or ( isset($page['flat']) and isset($page['category']) )
+ or 'list'==$page['section'] or 'recent_pics'==$page['section'] )
+{
+ $page['meta_robots']=array('noindex'=>1, 'nofollow'=>1);
+}
+elseif ('tags' == $page['section'])
+{
+ if ( count($page['tag_ids'])>1 )
+ {
+ $page['meta_robots']=array('noindex'=>1, 'nofollow'=>1);
+ }
+}
+elseif ('recent_cats'==$page['section'])
+{
+ $page['meta_robots']['noindex']=1;
+}
+elseif ('search'==$page['section'])
+{
+ $page['meta_robots']['nofollow']=1;
+}
+if ( $filter['enabled'] )
+{
+ $page['meta_robots']['noindex']=1;
+}
+
+// see if we need a redirect because of a permalink
+if ( 'categories'==$page['section'] and isset($page['category']) )
+{
+ $need_redirect=false;
+ if ( empty($page['category']['permalink']) )
+ {
+ if ( $conf['category_url_style'] == 'id-name' and
+ @$page['hit_by']['cat_url_name'] !== str2url($page['category']['name']) )
+ {
+ $need_redirect=true;
+ }
+ }
+ else
+ {
+ if ( $page['category']['permalink'] !== @$page['hit_by']['cat_permalink'] )
+ {
+ $need_redirect=true;
+ }
+ }
+
+ if ($need_redirect)
+ {
+ $redirect_url = ( script_basename()=='picture'
+ ? duplicate_picture_url()
+ : duplicate_index_url()
+ );
+ if (!headers_sent())
+ { // this is a permanent redirection
+ set_status_header(301);
+ redirect_http( $redirect_url );
+ }
+ redirect( $redirect_url );
+ }
+ unset( $need_redirect, $page['hit_by'] );
+}
+
+trigger_action('loc_end_section_init');
+?>
\ No newline at end of file
diff --git a/BSF/include/smarty/COPYING.lib b/BSF/include/smarty/COPYING.lib
new file mode 100644
index 000000000..3b204400c
--- /dev/null
+++ b/BSF/include/smarty/COPYING.lib
@@ -0,0 +1,458 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
diff --git a/BSF/include/smarty/NEWS b/BSF/include/smarty/NEWS
new file mode 100644
index 000000000..fa550ff18
--- /dev/null
+++ b/BSF/include/smarty/NEWS
@@ -0,0 +1,1024 @@
+Version 2.6.19 (Feb 11th, 2008)
+-------------------------------
+
+- fix regex_replace allowing \0 in the search string (c960657,
+ monte)
+- add append feature to {capture} (jablko, monte)
+- fix when (un)registering filters with the same method name but different class
+ name (danilo)
+- fix calling registered objects' methods with an empty argument list
+ (marcello, messju)
+
+Version 2.6.18 (Mar 7th, 2007)
+------------------------------
+
+- fix html_select_date separator when parts are missing (hayk,
+ monte)
+- fix broken detection of non-cached blocks introduced in 2.6.17
+ (messju)
+
+Version 2.6.17 (Mar 5th, 2007)
+------------------------------
+
+- fix php handling (monte, boots, danilo)
+- fix handling of plugin tags directly followed by an else tag (Fahr, danilo)
+- fix handling of $etc in the truncate modifier when $etc is longer
+ than $length (Sylvinus, messju)
+- fix handling of %I with mysql timestamps in the date_format modifier
+ (danilo, boots)
+- update smarty_core_write_file() and smarty_modifier_date_format() to better
+ recognize Windows (boots, danilo)
+- emulate %h, %n, %r, %R, %t in the date_format modifier on Windows
+ (danilo, boots)
+
+Version 2.6.16 (Dec 1st, 2006)
+------------------------------
+
+- fixed replacement bug in trimwhitespace output filter that was introduced
+ in the last release (Spuerhund, boots)
+
+Version 2.6.15 (Nov 30th, 2006)
+-------------------------------
+
+- change file writing semantics in smarty_core_write_file() to unlink() only
+ when rename() fails or a Windows system is detected (c960657, boots)
+- update debug.tpl to xhtml 1.1 compliance, fix javascript escaping in debug
+ output and apply a Smarty based color scheme (cybot, boots)
+- enhance reporting precision of debug_print_var modifier (cybot, boots)
+- make html_select_date work consistently with 0000-00-00 00:00:00 and
+ 0000-00-00 inputs (cybot, boots)
+- fix wrong handling of insert's name attribute. (messju)
+- fix false replacement of "$t" inside double quotes (checat, messju)
+- added support for column headings and caption element to html_table and
+ updated the output to use thead/tbody elements (boots)
+- fixed ordering of replacements in trimwhitespace output filter (Getty, boots)
+- update mailto function plugin to work around a firefox/thunderbird
+ escaping bug (elijahlofgren, boots)
+- emulate %l in the date_format modifier on windows (boots)
+- fix handling of apostrophes in capitalize modifier (Alec Smecher, boots)
+
+Version 2.6.14 (May 28th, 2006)
+-------------------------------
+
+- fix compiler bug allowing php tags in secure templates
+ (boots,monte)
+- un-hide hidden xml open tags (boots)
+- fix handling of block-methods of registered objects (El Hombre Gris,
+ messju)
+
+Version 2.6.13 (March 9th, 2006)
+--------------------------------
+
+ - update regex_replace, removing possible use of "e" modifier
+
+Version 2.6.12 (Jan 18th, 2006)
+-------------------------------
+
+ - fix improper use of references in the compiler handling cached
+ attributes and in compiled code handling block plugins (messju)
+ - make Smarty::_read_file() work on latest php (messju)
+ - fixed improper tokenization of certain inline math expressions (boots)
+
+Version 2.6.11 (Dec 14, 2005)
+-----------------------------
+
+ - fixed code generation of non-cacheable blocks to play well with php's
+ "Alternative syntax for control structures" (kihara, messju)
+ - fix handling of multiple identical inserts in one display()-call (messju)
+ - replace {} string access with equivalent substr() to avoid E_STRICT
+ warnings in PHP 5.1 (boots)
+ - return valid reference in get_config_vars() when given var is
+ non-existant (Thomas Schulz, boots)
+ - plugin html_image: fix incorrect secure_dir error when
+ file doesn't exist (monte)
+ - plugin html_image: add path_prefix param (monte)
+ - add char_set parameter to escape modifier (Loading, monte)
+ - fix notice in debug security check (Drakla, monte)
+ - return valid reference in get_template_vars() when given var is
+ non-existant (monte)
+ - add escape type "urlpathinfo" to escape modifier (monte)
+
+Version 2.6.10 (Aug 5, 2005)
+----------------------------
+
+ - allow secure_dir to be a filename, not just
+ a directory name (monte)
+ - set debug.tpl as a secure_dir, not the entire
+ SMARTY_DIR (monte)
+ - fix proper escaping for literal strings in
+ Smarty_Compiler::_parse_var_props() (boots, messju)
+ - remove ambiguity for numeric values passed to smarty_make_timestamp()
+ (and thus the date_format modifier). numeric values are treated as
+ timestamps now. (andreas, messju)
+ - add passthru attribute feature to html_select_date (Sedgar,
+ monte)
+ - add "middle" parameter to truncate (monte)
+ - make form input label ids optional (monte)
+ - add error message for empty if/elseif statements (eykanal,
+ monte)
+ - cast selected value to string for comparison in html_radios
+ (Exeption, monte)
+ - updated html_select_date's year_as_text-feature to be xhtml compliant
+ (Mark West, messju)
+ - fix handling of selected month html_select_date (Yuri Weseman, messju)
+
+Version 2.6.9 (Mar 31, 2005)
+----------------------------
+
+ - disallow variable function calls in {if} statements (messju, monte)
+ - disallow variable function calls in {math} equations (messju, monte)
+
+Version 2.6.8 (Mar 21, 2005)
+----------------------------
+
+ - remove e-modifier from regex_replace modifier (messju)
+ - remove cast of object to array in foreach's from-attribute (messju)
+ - add "null" as a valid token for {if} when security is enabled (messju)
+ - add javascript_charcode encoding option to mailto function
+ (monte)
+ - add ids to html_radios labels (monte, menulis)
+ - fix handling of strip-tags with non-default delimiters (Mark West, messju)
+
+Version 2.6.7 (Feb 3, 2005)
+---------------------------
+
+ - fix handling of hashed opening php-tags inside strip-blocks (messju)
+ - removed border tag from html_image function (monte)
+ - change escape:url use rawurlencode() instead of urlencode() (messju)
+ - make $smarty.const.FOO compile to "FOO", and not to "constant('foo')".
+ this is less code and a little faster execution. note that undefined
+ constants are now displayed as the constant's name. (messju)
+ - make block functions and registered objects' block methods use a
+ local variable for block_content instead of a property of $smarty (messju)
+ - fix escaping in the generated code that calls smarty_core_load_plugins
+ (jes5199, messju)
+ - fix invalid HTML issue with popup (Stefanos Harhalakis,
+ Monte)
+ - fixed {popup} to properly handle inarray and function parameters and added
+ support for mouseoff and followmouse options (boots)
+
+Version 2.6.6 (Oct 13, 2004)
+----------------------------
+
+ - fixed nocache-handling with nested includes (Lars Jankowfsky, messju)
+ - moved /libs/core to /libs/internals (boots)
+ - fixed more parsing problems (messju)
+
+Version 2.6.5 (Sept 13, 2004)
+-----------------------------
+
+ - fixed some parsing problems with object calls introduced
+ in 2.6.4 (Monte)
+ - add $smarty->security_settings['ALLOW_CONSTANTS']. note: this
+ defaults to false which means you have to allow them explicitly
+ in your secured templates from now on! (messju)
+
+Version 2.6.4 (Sept 7, 2004)
+----------------------------
+
+ - add $smarty.ldelim and $smarty.rdelim to smarty special var (Monte)
+ - fall back to old uniqid()-behaviour when tempnam() fails in
+ core.write_file.php (messju)
+ - fix capitalize modifier, don't rely on buggy ucwords (Monte)
+ - make html_select_date work with negative timestamps, also
+ force year range to include given date unless explicitly
+ set (Garo, Monte)
+ - fix bug with {fetch}, passing user/pass in url did not work
+ (Monte)
+ - fix occasional wrong error messages on mismatched tags when
+ {else}, {elseif}, {foreachelse} or {sectionelse} is involved (messju)
+ - fix handling of methods arguments (messju, Manfred Wischin)
+ - remove touch() call that made the compiled-template's timestamp the
+ same as the source-template's one. (messju)
+ - add assign attribute to html_checkboxes and html_radios
+ (pcg, Monte)
+ - remove non-xhtml conformant tag from mailto function
+ (tacker, Monte)
+ - handle date_format codes %e, %T and %D for windows (tip,
+ Monte)
+ - fix unnecessary call to smarty_core_get_include_path() inside
+ Smarty::_get_auto_filename() (c960657, messju)
+ - add error-messages when anything else than an identifier is passed
+ to foreach's key- or item-attribute (messju)
+ - fix handling of digits inside tagnames (messju)
+ - fix escaping of backslashes in Smarty_Compiler::_quote_replace() (messju)
+
+Version 2.6.3 (June 16, 2004)
+-----------------------------
+
+ - added escapement of '' to '<\/' in escape:javascript
+ modifier (c960657, Monte)
+ - added obfuscation of protocol-string in {mailto} when using hex-
+ encoding (bharat, messju)
+ - enhanced auto-generated filenames for templates_c and cache (messju)
+ - add 'nonstd' to escape modifier for escaping non-std chars,
+ such as ms doc quote (Monte)
+ - adjusted textformat to not output wrap chars after last para
+ (Monte)
+ - use tempnam() instead of unqid() to create better temporary files in
+ smarty_core_write_file() (xces, messju)
+ - add 'mail' to escape modifier for safe display of e-mail
+ addresses (Monte)
+ - add cycle function attribute "reset" to english docs (Monte)
+ - enhanced support of numeric constants as variable-expressions (messju)
+ - add case decentity to smarty_modifier_escape() (Konstantin A. Pelepelin,
+ messju)
+ - make smarty_core_write_compiled_include() php5-aware (messju)
+ - removed unused functionality to load a subset of lines from a file (messju)
+ - fix is_secure() should only check if a file is_readable, not if
+ the directory where it is in is readable (sagi, messju)
+ - fix problem displaying debug console when $default_resource_type
+ is not "file:" (c960657, Monte)
+ - fix permission handling with security and config_load (messju)
+ - removed '.' from the list of default template locations in
+ _parse_resource_name() (messju)
+ - fix avoid warning with template_exists() on an absolute paths (messju)
+ - fix parameters passed to resource's secure()-function (messju)
+ - fix handling of integer values like width and delay im
+ smarty_function_popup() (messju)
+
+Version 2.6.2 (Feb 17, 2004)
+----------------------------
+
+ - fix allow empty years, months and days in html_select_date's
+ time-attribute (messju)
+ - fix YES and NO should not be booleanized inside triple-quotes in a
+ config-file (messju)
+ - fix accidently slurped line following a triple-quoted value in a
+ config-file (messju)
+ - change default for use_sub_dirs to false (messju)
+ - fix quoting of values in smarty_function_popup() (messju)
+ - fix handling of hidden sections in Config_File (messju)
+ - add handling of resources for {config_load} (messju)
+ - fix bug when using arrays with tr_attr and td_attr in {html_table} (messju)
+ - add unit testing to cvs core (Monte)
+
+Version 2.6.1 (Jan 16, 2004)
+----------------------------
+
+ - rename $smarty->tpl_error_reporting to $smarty->error_reporting
+ (messju)
+ - fix interpretation of $smarty->security in {html_image} (messju)
+ - add caching of requested paths to _assemble_plugin_filepath() (messju)
+ - fix handling of comments inside {php}- and {literal}-blocks (messju)
+ - fix bug handling triple-quotes in config-files (BRDude, messju)
+ - change default of request_use_auto_globals to true - $_SERVER is
+ now preferred over $HTTP_SERVER_VARS (messju)
+ - re-add support for $SCRIPT_NAME (messju)
+ - reactivate $smarty->default_modifiers (messju)
+ - add cookie persistance to debug console (Monte)
+ - allow single-digit days and months without smarty_make_timestamp()
+ in html_select_date (messju)
+ - fix headers sent erroneously with cache_modified_check and fetch()
+ (wphilips, messju)
+ - fix config_file path bug (Marc Cabadas, Monte)
+ - fix 'is even by' and 'is odd by' logic (Monte)
+ - add day_empty, month_empty, year_empty and all_empty attributes to
+ html_select_date (messju)
+ - add table of explanation for {if} qualifiers in docs (boots)
+ - fix bug when comparing array-keys to "selected" in html_options
+ and html_checkboxes (messju)
+ - add better checks for correctly nested tags when compiling (messju)
+ - remove {$SCRIPT_NAME}. use {$smarty.server.SCRIPT_NAME} instead (messju)
+ - remove $global_assign. assign global variables explicitly instead (messju)
+ - fix example for count_characters in docs (boots)
+ - add section new basic syntax section "Escaping Smarty Parsing" in docs (boots)
+ - fix error handler call in config_load (boots)
+ - remove warning in debug_print_var on php-resources (messju)
+ - move function.assign.php to compiler.assign.php (messju)
+ - add property $tpl_error_reporting (messju)
+ - remove property $undefined. "null" is used literally instead (messju)
+
+Version 2.6.0 (Nov 19, 2003)
+----------------------------
+
+ - move Smarty::quote_replace() to Smarty_Compiler::_quote_replace() (messju)
+ - remove import of of attributes of {include_php} to php's namespace.
+ use $params[name] instead (messju)
+
+Version 2.6.0-RC3 (Nov 13, 2003)
+--------------------------------
+
+ - fix handling of $var.key inside [] (messju)
+ - fix handling of assign inside {insert}-tags (messju)
+ - fix handling if [...] inside triple-quotes in config-files (messju)
+ - fix handling of simple-math-operators inside modifiers (Dominik, messju)
+ - fix handling of trailing-slashes in open_basedir in
+ smarty_core_create_dir_structure() (packman, messju)
+
+Version 2.6.0-RC2 (Oct 8, 2003)
+-------------------------------
+
+ - apply modifiers only once to section-loop and foreach-from attrs (messju)
+ - remove use of _smarty_cached_paths-files (messju)
+ - remove Smarty::_plugin_implementation_exists() - use is_callable() (messju)
+ - ignore {strip}/{/strip) inside {strip}-blocks (messju)
+ - fixed removal of leading/trailing newlines in {strip}-blocks (messju)
+ - fixed proper escaping of " and ' with escape:javascript (messju)
+ - fixed bug in traversal of $smarty->plugins_dir-array. now the
+ first matching plugin is taken (messju)
+ - moved {strip} back into the compiler (messju)
+ - fixed config_load: handling of section-attribute and use of
+ multiple config-files in one template (atu, messju)
+
+Version 2.6.0-RC1 (August 11, 2003)
+-----------------------------------
+
+ - fixed status-header for cache_modified_check under cgi-sapi (messju)
+ - added optional parameter $cache_attrs to register_function() and
+ register_block(). $cache_attrs is an array containing attribute-
+ names that should be cached on calls to functions that have
+ $cacheable set to false. (messju)
+ - enabled registration of class-methods as callbacks for the register_*-
+ functions (use: array('classname', 'method_name')) as callback) (messju)
+ - added filepath caching (Monte)
+ - added optional assign-attribute to {capture}-tag (messju)
+ - added $cacheable-parameter to register_compiler_function() (messju)
+ - added $cacheable-parameter with default=true to register_function()
+ and register_block() (messju)
+ - add math speedup to core (Dominik, Monte)
+ - fix newlines for tags without template output (Monte)
+ - added config-option "request_use_auto_globals" to make auto-globals be
+ used as request vars instead of HTTP_*_VARS (messju)
+ - speed up config_load, simplify compiling (Monte)
+ - added block-methods for registered objects (Bharat Mediratta, messju)
+ - ignore one char resource names like c:foo.tpl (Monte)
+ - added default_resource_type feature (Monte)
+ - fix bug where config file starts with hidden section (boots, Monte)
+ - add discrete error checking pertaining to $cache_dir
+ and $compile_dir, their existance and writability (Monte)
+ - fixed behaviour of start=... for {counter} (messju)
+ - fixed assign for {counter} (messju)
+ - added params vdir, hdir and inner to html_table to allow looping
+ over the data in various directions (messju)
+ - allow spaces in literal tags (Paul Lockaby, Monte)
+ - speed up compiled templates, hardcode plugin filepaths
+ instead of dynamically calculate at runtime. (Monte)
+ - abstract many core components from Smarty.class.php,
+ speeding up core class instantiation (Monte)
+ - fixed bug in _create_dir_structure() when used with open_basedir-
+ restriction and relative paths (messju)
+ - use DIRECTORY_SEPARATOR exclusively, keep DIR_SEP for BC (Monte)
+ - changed "link" to "href" in html_image. "link" is still working
+ but deprecated (messju)
+ - html_image always renders an alt-tag now (default alt="") (messju)
+ - fixed assign attribute for multiple counters (messju)
+ - added simple math operators to variables (Monte)
+ - enabled array(&$obj. 'source', 'timestamp', 'secure', 'trusted')
+ as callback for register_resource() (messju);
+ - enabled array(&$obj, 'method') as callback for
+ $default_template_handler_func (messju)
+ - remove unnecessary close/open tags from compiled templates
+ (Monte)
+ - fixed errornous creation of '//' in image_path in html_image (messju)
+ - fix escapement of special chars for key vals in debug
+ console (Monte)
+ - fixed debug timing logic for config_load (Tom Sommer, Monte)
+ - all in-code doc comments converted to phpDocumentor format (Greg)
+ - moved strip from smarty core to plugin (Monte)
+ - moved config_load from smarty core to plugin (Monte)
+ - added &$repeat-parameter to block-functions (messju)
+ - enabled hex-constants in function.math.php (messju)
+ - enabled hex-constants (0x...) as function-attributes, inside if-statements
+ and as modifier-parameters (messju)
+ - fixed bug with passing $smarty as reference in Smarty.compiler.class
+ (messju)
+ - corrected output with {strip} and PHP tag newlines (Monte)
+ - added possibility to register function-callbacks as "array(&$obj, 'method)"
+ this affects register_function(), -block, -compiler_function, -modifier,
+ -prefilter, -postfilter, -outputfilter-functions() and $cache_handler_func
+ (messju)
+ - added to html_checkboxes and html_radios (Philippe, messju)
+ - added "labels"-options to turn off labels in html_checkboxes and _radios
+ (messju)
+
+Version 2.5.0 (April 11, 2003)
+------------------------------
+
+ - fixed bug with default modifier when passing integer 0
+ (Monte)
+ - change backtic syntax from $`foo` to `$foo` (Monte)
+ - recognize $foo[][] syntax inside embedded quotes without
+ backtics (Monte)
+ - name=123 is passed as an integer (not a string) to plugins now (messju)
+ - $length is now propagated to sub-values in debug_print_var (messju)
+
+Version 2.5.0-RC2 (March 26, 2003)
+----------------------------------
+
+ - made clear_cache() ignore compile_id, when clearing cache-groups (this
+ is when no $tpl_file is supplied) (messju)
+ - made onmouseout XHTML-compliant in function.popup.php (messju)
+ - applied local-var-naming-scheme to fetch() (messju)
+ - renamed $localvars to $_localvars in cache-file-handling-functions,
+ added _get_auto_id()-function (messju)
+ - swapped compile_id and cache_id in read_cache_file and write_cache_file
+ (messju)
+ - reverted patch for cache-file-handling (messju)
+ - made html_radios and html_checkboxes accept "selected" instead
+ of "checked" optionally. (messju)
+ - made compile_id ignored in clear_cache, made order of
+ auto_file_name $cache_id.$compile_id again, applied the the new
+ variable-naming-scheme for cache_file_handing functions (messju)
+ - removed notice of undefined var in _rm_auto() (messju)
+ - added warning message when an array is passed as
+ the "checked" value of html_radios (Monte)
+ - fixed errormessage in _compile_smarty_ref() (messju)
+ - updated docs for html_image "name" -> "file" (messju)
+ - fixed bug with html_options-optgroups (Nichlas Löfdahl, messju)
+ - cleaned up calls to readdir() (messju)
+ - fixed bug with passing multiple modifiers to a parameter
+ (Monte)
+ - updated docs for html_checkboxes, html_options and html_radios (messju)
+ - fixed wrong default "name" attribute for html_options (messju)
+ - html_checkboxes now expect the options as attribute "options" instead
+ of "checkboxes. html_radios expect "options" instead of "radios".
+ cleaned up indentiation (messju)
+ - fixed too greedy str_replace in trimwhitespace outputfilter (messju)
+ - html_checkboxes and html_radios passthru all unknown paramters now
+ additionally their output is now XHTML compliant (messju)
+ - html_options passthru all unknown paramters now (messju)
+ - fix link functionality of html_image, also make
+ output XHTML compatible (Hinrich Donner, Monte)
+ - append "@" to default modifier vars/args
+ supress possible warnings (Monte)
+ - fix problem with escaped double quotes (Monte)
+ - fix html_radios to not return an array (Monte)
+ - fixed length in modifier.truncate.php (messju)
+ - fixed handling of '$'-signs in trimwhitespace outputfilter (messju)
+ - fix bug that makes config files recompile every time
+ (Nagger, Monte)
+ - add dpi functionality to html_image, change "name"
+ parameter to "file" (Thomas Shulz, Monte)
+ - fix height/width parameter index in html_image (Gerard,
+ Monte)
+ - get rid of unsetting name and script attributes
+ to insert tag (Thomas Schulz, Monte)
+ - changed argument order of string_format modifier back,
+ was right in the first place (Monte)
+
+Version 2.5.0-RC1 (March 5, 2003)
+---------------------------------
+
+ - fixed notice in popup function (Nagger, Monte)
+ - fix "once" var compiling for include_php (Monte)
+ - added nl2br modifier to distribution (Monte)
+ - added html_image to distribution (Monte)
+ - added cat modifier to distribution (Monte)
+ - added html_table to distribution (Monte)
+ - added << >> <> support to if statments (SMK, Monte)
+ - fix _assign_smarty_interface to not overwrite keys
+ other than 'request' (Jerome Poudevigne, Monte)
+ - added html_checkboxes to distribution (Christopher Kvarme, Monte)
+ - added html_radios to distribution (Christopher Kvarme, Monte)
+ - fixed string_format modifier args (wrong order) (Paul
+ Lockaby, Monte)
+ - use tmp file for file writes, avoid file lock race (Monte)
+ - support syntax "$`smarty.config.foo`.tpl" for embedded
+ vars in quotes, and allow full dollar var syntax (Monte)
+ - add $smarty.config.varname variable for accessing config vars (Paul
+ Lockaby, Monte)
+ - silence PHP warnings in function.fetch.php (Eduardo,
+ Monte)
+ - added get_config_vars(), same basic functionality as
+ get_template_vars() (Monte)
+ - update get_template_vars() to be able to get
+ individual vars (Monte)
+ - fix minor logic in _fetch_template_info (Dennis Gearon,
+ Monte)
+ - fix cache groups with compile_id set (Monte)
+ - add support for merging appended vars (messju, Monte)
+ - allow null as function attribute value
+ (André Rabold, Monte)
+ - support $foo->bar[index] syntax (Monte)
+ - add get_registered_object function (messju, Monte)
+ - treat unrecognized param attribute syntax as string (Monte)
+ - support $smarty.const.$foo syntax (messju, Monte)
+ - remove E_NOTICE warnings from debug.tpl,
+ escape modifier (Kanstantin, Monte)
+ - don't count non-ascii chars in count_words modifier
+ (Kanstantin, Monte)
+ - clean up param calls to _parse_var and _parse_attrs (Monte)
+ - define $template_source var, elude possible warning
+ (Monte)
+ - fix syntax problem with evaluating PHP constants (Monte)
+ - add @ and === as valid if statement tokens (Monte)
+ - enable error messages for config_load errors,
+ use $this->config_class for loading class name (Monte)
+ - fix html_options to not escape already escaped entities (Monte)
+ - send Last-Modified header on cache creation (Monte)
+ - check strict syntax of function attributes (Monte)
+ - dropped support for modifers on object parameters,
+ added support for objects as modifier parameters (Monte)
+ - fixed bug with decimal numbers in if statements (Monte)
+
+Version 2.4.2 (Feb 11, 2003)
+----------------------------
+ - support embedded variables in objects (Monte)
+ - fix bug with objects with no properties (M Mohr, Monte)
+ - support full dollar var syntax in quoted text (Monte)
+ - fixed bug in $smarty.const.FOO introduced in 2.4.1 (M
+ Mohr, Monte)
+
+Version 2.4.1 (Feb 6, 2003)
+---------------------------
+
+ - ignore case in IF statements (Rainer Collet, Monte)
+ - treat undefined constants as null (Ferdinand Beyer, Monte)
+ - fix problem with inserts and nested fetches
+ (Rainer Collet, Monte)
+ - added support for passing params to include_php
+ (Tim Riley, Monte)
+ - added support for math operators in if statements (Monte)
+ - added support for $foo->bar[$x].blah syntax (Monte)
+
+Version 2.4.0 (Feb 2, 2003)
+---------------------------
+
+ - fix known problems with php tag handling in templates
+ (recursion, echoing xml tags) (Monte)
+ - add support for object registration (Monte)
+ - add debug template to secure_dir, add template_dir
+ to secure_dir by default (Ferdinand Beyer, Monte)
+ - added support for assigned object access (Monte)
+ - fixed bug with directories named '0' (Frank Bauer, Monte)
+ - add javascript parameter to escape modifier (Monte)
+ - added calling function line numbers to syntax error
+ messages in compiler (Monte)
+ - added support for modifiers to function calls (Monte)
+ - support return value for custom functions
+ instead of echoing (but echo still works) (Monte)
+ - added direct access to constants
+ via $smarty.const.FOO (Monte)
+ - added support for passing modifiers
+ to static values (Monte)
+ - fix up regex code in compiler, more accurate and
+ maintainable (Monte)
+ - added day_value_format to html_select_date (Marcus
+ Bointon, Monte)
+ - assigned variables are no longer in global
+ namespace, saving extract() calls and speeding
+ up fetch() and display() linearly with no. of
+ assigned variables (Monte)
+ - added trimwhitespace output filter to dist. (Monte)
+ - fix popup function to allow newlines in text (Monte)
+ - escape html entities in html_options (Monte)
+ - fixed bug with label for html_options (Monte)
+ - added config_load API function (Monte)
+ - added caching to config file loading (Monte)
+ - added "extra" parameter to mailto function (Monte,
+ Massimiliano Perantoni)
+ - added mailto plugin to dist. (Monte)
+
+Version 2.3.1 (Nov 19, 2002)
+----------------------------
+
+ - added optgroup support to html_options (Monte, Robert
+ Amos)
+ - set mtime on compile files so they match source
+ files (Monte, Peter Bowen)
+ - added proper support for open_basedir setting
+ (Monte, Alessandro Astarita)
+ - added strip variable modifier, updated docs (Monte)
+ - fixed access to $smarty.x variables as arrays. (Andrei)
+ - fixed errors with example setup docs (Monte, Matthew
+ Hagerty)
+ - added textformat block function (Monte)
+
+Version 2.3.0 (Aug 7, 2002)
+---------------------------
+
+ - added assign_by_ref() and append_by_ref() functions
+ (Bob Silva, Monte)
+ - changed default warning type for plugin errors from
+ E_USER_WARNING to E_USER_ERROR (Monte)
+ - added $all_extra, $hour_extra, $minute_extra,
+ $second_extra and $meridian_extra parameters to
+ html_select_time function (Rainer Collet, Monte)
+ - update debug console to print objects (Simon Willison,
+ Monte)
+ - fix Config_File class to not error when there are no
+ sections (Peter Kmet, Monte)
+ - add default modifier logic (Monte)
+ - updated popup_init to be xhtml compliant (Tom Oram, Monte)
+ - fix filename bug with windows (Gary Loescher, Monte)
+ - add ability to supply expire time in seconds when clearing
+ cache or compile files (Monte)
+ - add {debug} plugin to distribution (Monte)
+ - fixed bug with insert tags, loading from "script" attribute
+ when caching is enabled (Monte)
+ - fix bug with debug_tpl file path with Windows (.SMK., Monte)
+ - fix append() function with string/array problem (Monte)
+
+Version 2.2.0 (July 11, 2002)
+-----------------------------
+
+ - make debug.tpl work with any delimiter (Monte)
+ - change logic in assign() and append() to test var names
+ against != '' instead of empty() (Monte)
+ - fix PHP notice in append() function (Monte)
+ - allow $plugins_dir to be an array of directories
+ (Andreas Kossmeier, Monte)
+ - move debug.tpl to SMARTY_DIR, add to constructor (Monte)
+ - fixed warning message in function.assign_debug_info (Monte)
+ - fixed $template_dir, $compile_dir, $cache_dir, $config_dir,
+ $plugin_dir to respect include_path (Monte)
+ - fixed warning message with output filter array (Monte)
+ - add optional 2nd parameter to date_format, used as
+ the default date if the passed date is empty (Monte)
+ - gave $reset a default value in cycle plugin (Monte)
+ - fixed warnings with html_select_date and timestamp
+ functions (Monte)
+ - added support for sub directory exlusion format (Monte)
+ - added support for grouping by cache_id, compile_id
+ and segments thereof (Monte)
+ - changed cache and compile files to human readable
+ format (Monte)
+ - remove overlib.js file from distribution (Monte)
+ - fixed bug with 304 Not Modified response sending
+ content (Monte)
+ - fixed cycle function to respect delimiter after
+ initial setting (Monte)
+ - update $GLOBALS references to work properly with
+ track_globals settings (Michal Prinke, Monte)
+ - fixed bug in math function with call to assign
+ (Grigory V. Kareev, Monte)
+ - optimized for loops with count() function calls (Monte)
+ - add month_value_format attribute to html_select_date
+ plugin (Gary Loescher, Monte)
+ - made it possible to use simple variables inside [] for
+ indexing. (Andrei)
+ - added "once" attribute to {include_php}. (Monte)
+
+Version 2.1.1
+-------------
+ - added cycle function. (Monte)
+ - fixed bug with resource testing, and include_path. (Monte)
+ - fixed a bug with register_outputfilter function. (Monte)
+
+Version 2.1.0
+-------------
+
+ - introduced output filters. (Andrei)
+ - changed the way filters are loaded, added load_filter()
+ API function and $autoload_filters variable. (Andrei)
+ - added caching logic for expire times per cache file
+ (Norbert Rocher, Monte)
+ - fixed html_select_date when field separator is "/"
+ (Roberto Berto, Monte)
+ - added YYYY-MM-DD format support to html_select_date
+ (Jan Rosier, Monte)
+ - fixed cache_lifetime logic bug, also made -1 = never
+ expire (Monte)
+ - fixed directory separator issue for Windows. (Andrei)
+ - added ability to use simple variables as array indices or
+ object properties. (Andrei)
+ - added ability to unregister pre/postfilters plugins at
+ runtime. (Andrei)
+ - added 'htmlall' attribute to escape modifier. (Monte)
+ - added template_exists() API function. (Andrei)
+ - fixed a problem with using dynamic values for 'file'
+ attribute of {include_php} tag. (Andrei)
+ - added $smarty.template variable. (Andrei)
+ - fixed several plugins that would not work if the plugin
+ directory was not the default one. (Andrei)
+ - implemented support for block functions. (Andrei)
+ - made it possible to assign variables in pre/postfilter
+ plugins. (Andrei)
+
+Version 2.0.1
+-------------
+ - rename plugin .make_timestamp.php to shared.make_timestamp.php.
+ (Monte)
+ - changed crc32() generated values, replace '-' with 'N'. (Monte)
+ - added support for +/- N syntax in html_select_date year values.
+ (Monte)
+ - fixed behavior of inserts with script attribute. (Andrei)
+ - fixed bug with $smarty.cookies and $smarty.server. (Andrei)
+ - wordwrap and indent are missing from 2.0 release, now fixed.
+ (Monte)
+ - removed show_info_header and show_info_include variables. (Monte)
+
+Version 2.0.0
+-------------
+ - added "eval" function plugin for evaluating variables as
+ templates. (Monte)
+ - removed $tpl_file_ext class variable, no longer used. (Monte)
+ - added "hex" and "hexentity" escape types to escape modifier.
+ (Monte)
+ - removed dependency on PEAR. (Andrei)
+ - update popup_init to accept src attribute. (Monte, Duncan Forrest)
+ - implemented several optimizations, speeding up Smarty
+ significantly in most cases. (Andrei,Monte)
+ - implemented plugin architecture. (Andrei)
+ - added wordwrap and indent modifiers. (Monte)
+ - added support for 'If-Modified-Since' headers for cached content.
+ (Monte)
+ - removed insert_tag_check class variable, no longer needed. (Monte)
+ - optimized cache fetches by scanning for insert tags only if they
+ exist. (Monte)
+ - fixed bugs in overlib. (Monte, Duncan Forrest)
+ - fixed a problem with compile_id usage. (Andrei)
+ - fixed problem with using assigned vars with {include_php ...}
+ filepath. (Monte)
+
+Version 1.5.2
+-------------
+ - added Smarty object as fifth argument for template resource functions.
+ (Monte)
+ - fixed a bug with incorrectly combined cache and compile id in
+ clear_cache(). (Andrei)
+ - fixed bug in smarty_make_timestamp introduced in PHP 4.1.0. (Monte)
+ - fixed bug with cached insert debug timing. (Monte)
+ - added 'script' attribute to {insert..} which specifies the script that
+ the insert function can be found in. (Andrei)
+ - added default template function handler. (Monte)
+
+Version 1.5.1
+-------------
+ - removed error message from the generic _read_file() method, the caller
+ should take care of that. (Andrei)
+ - fixed a bug with incorrectly combined cache and compile id. (Andrei)
+
+Version 1.5.0
+-------------
+ - added include_php built-in function, documented. (Monte)
+ - added trusted_dir functionality, documented. (Monte)
+ - consolidated secure_dir tests to one function. (Monte)
+ - prepended _smarty_ to variable names in fetch() class function to avoid
+ namespace conflicts. (Monte)
+ - introduced $compile_id class variable that can be used to set persistent
+ compile identifier across multiple display calls, documented. (Andrei)
+ - fixed bug with concatenated null cache and compile identifiers. (Andrei)
+ - added $smarty.section.* syntax for accessing section properties,
+ documented. (Andrei)
+ - added custom cache handling function ability, documented. (Monte)
+ - added assign attribute to include, include_php, insert, fetch, math, and
+ counter functions, documented. (Monte)
+ - fixed bug with fetch testing for local file when http address. (Monte)
+ - fixed bug with counter and skipval setting. (Monte)
+ - made {config_load ...} merge globals from each config file only once per
+ scope, thus avoiding several problems. (Andrei)
+ - added {foreach ...} tag that can be used to iterate through
+ non-sequential and associative arrays, documented. (Andrei)
+ - speeded up section property access a bit. (Andrei)
+ - removed $smarty variable from storage used by normal template variables,
+ to prevent any problems. (Andrei)
+ - fixed a bug that could cause parse error with quotes inside literal
+ blocks. (Andrei, Alexander Belonosov)
+ - added 'field_array' attribute to html_select_time function, documented.
+ (Andrei, Michael Caplan)
+ - documented {section} "max" attribute. (Monte)
+ - fixed notice message in Smarty_Compiler.class.php. (Monte)
+ - fixed bug with clear_cache introduced in 1.4.6, third parameter should
+ default to null. (Monte)
+ - updated Config_File class to support '\' path separator in OS/2. (Monte,
+ Francesco Cipriani)
+ - removed secure_ext setting (not used). (Monte)
+ - made cache reading process more efficient. (Monte)
+ - fixed bug, is_cached() now supports new 1.4.6 caching behavior. (Monte)
+ - update FAQ with mailing list Reply-To header FAQ. (Monte)
+ - supress error messages for fopen(), fix cache to regenerate if cache
+ file is not available (i.e. cluster race condition). (Monte)
+ - added index key example to QUICKSTART guide. (Monte)
+
+Version 1.4.6
+-------------
+ - fixed bug with {assign ...} when passing an empty value. (Monte)
+ - add more warning message fixes. (Monte, Tara Johnson)
+ - documentation updates. (Monte)
+ - update fetch function to give proper warning when fetching a non-readable
+ or non-existant file. (Monte)
+ - fixed problem with newline at the end of included templates (Monte, Andrei)
+ - added feature to regenerate cache if compile_check is enabled and an
+ involved template or config file gets modified. (Monte)
+ - added DEBUG execution times to included files: REQUIRES updated debug.tpl
+ file! (Monte)
+ - added support for hidden config variables that cannot be read by
+ templates. (Andrei)
+ - added execution time to DEBUG console, total and inserts. (Monte)
+ - fixed bug where DEBUG console would not appear with cached content. (Monte)
+ - added support for postfilter functions that are applied to compiled
+ template right after compilation. (Andrei)
+ - fixed the name of clear_compile_tpl() API function to clear_compiled_tpl.
+ (Andrei)
+ - added fix for removing comments so that the line numbers are reported
+ correctly in case of errors. (patch from Anders Janson)
+ - made html_options output xhtml compatible code. (Monte, Arnaud Limbourg)
+
+Version 1.4.5
+-------------
+ - update FAQ with index of questions at the top
+ - update overlib to 3.50, adjust addon code so that the overlib.js
+ file isn't modified, and not using the mini one. (Monte)
+ - added many more options to html_select_date. (Alexander Skwar, Andrei)
+ - added support for generating different compiled templates from the same
+ source template. (Hans-Peter Oeri, Andrei)
+ - modified Smarty to pass itself to insert functions as the second
+ parameter. (Andrei)
+ - modified Smarty to pass itself to prefilter functions as the second
+ parameter. (Andrei)
+ - fixed syntax error when including a non-existant template with security
+ enabled. (Monte)
+ - fixed comments handling to allow commenting out template blocks. (Andrei)
+ - implemented named capture buffers, with results accessible via
+ $smarty.capture.. (Andrei)
+ - added ability to index arrays directly by numbers. (Andrei)
+ - fixed bug with SMARTY_DIR not prepended to Config_File include. (Monte)
+
+Version 1.4.4
+-------------
+ - fixed problem with including insecure templates with security enabled.
+ (Monte)
+ - numerous documentation updates. (Monte)
+ - added ENT_QUOTES to escapement of html. (Monte, Sam Beckwith)
+ - implemented access to request variables via auto-assigned $smarty
+ template variable. (Andrei)
+ - fixed a bug with parsing function arguments inside {if} tags if a comma
+ was present. (Andrei)
+ - updated debug console with config file vars. (Monte)
+ - added SMARTY_DIR constant as an alternative to relying on include_path.
+ (Monte)
+ - added popup_init and popup functions (requires overlib.js). (Monte)
+ - updated debug console with config file vars. (Monte)
+ - added debugging url control. (Monte)
+ - added 'quotes' type to escape modifier. (Monte, Mike Krus)
+ - added 'total' and 'iteration' section properties. (Andrei)
+ - added 'start', 'max', and 'step' section attributes/properties. (Andrei)
+ - fixed a bug with security checking of functions inside {if} tags.
+ (Andrei)
+ - fixed a bug in Config_File that would incorrectly booleanize values that
+ weren't really booleans. (Andrei)
+
+Version 1.4.3
+-------------
+ - added regex_replace modifier, documented. (Monte)
+ - added debugging console feature and custom function assign_debug_info,
+ documented. (Monte)
+ - added 'scope' attribute for {config_load}, 'global' is now deprecated but
+ is still supported. (Andrei)
+ - reduced template symbol table pollution by moving config array into the
+ class itself. (Andrei)
+ - fixed a bug with passing quoted arguments to modifiers inside {if}
+ statements. (Andrei, Sam Beckwith)
+ - added security features for third party template editing, documented
+ (Monte)
+ - added assign custom function, documented. (Monte)
+ - fixed bug with template header using version instead of _version. (Monte)
+ - fixed a problem with putting $ followed by numbers inside {strip} and
+ {/strip} tags. (Andrei)
+ - fixed Config_File class to allow empty config paths (defaults to current
+ directory). (Andrei)
+
+Version 1.4.2
+-------------
+ - move $version to internal variable, remove from docs. (Monte)
+ - cleaned up compiled templates global scope by moving some variables into
+ the class itself. (Andrei)
+ - fixed a bug that would not allow referring to a section in the including
+ file from the included file. (Andrei)
+ - configs directory missing from 1.4.1 release, added back in. (Monte)
+ - added windows include_path setup instructions to FAQ & QUICKSTART.
+ (Monte)
+
+Version 1.4.1
+-------------
+ - fix LOCK_EX logic for all windows platforms (Monte)
+ - fixed indexing by section properties with the new syntax. (Andrei)
+ - updated Smarty to use absolute paths when requiring/including Smarty
+ components. (Andrei, John Lim)
+
+Version 1.4.0
+-------------
+ - added {capture}{/capture} function, documented (Monte)
+ - added {counter} function, documented (Monte)
+
+Version 1.4.0b2
+---------------
+ - fixed issue in Config_File.class with referencing blank sections (Andrei)
+ - fixed problem with passing variables to included files (Andrei)
+ - fixed resource path recognition for windows (Monte)
+
+Version 1.4.0b1
+---------------
+ - added "componentized templates" tip into documentation (Monte)
+ - added {php}{/php} tags for embedding php code into templates (Monte)
+ - changed default value of $show_info_header to false (Monte)
+ - implemented '->' syntax for accessing properties of objects passed to the
+ template. (Andrei)
+ - allowed custom functions to receive Smarty object as the second
+ parameter; this can be used to dynamically change template variables, for
+ example. (Andrei)
+ - added custom compiler functions support, register_compiler_function() and
+ unregister_compiler_function() API functions. (Andrei, Ivo Jansch).
+ - updated GLOBAL_ASSIGN to take SCRIPT_NAME from HTTP_SERVER_VARS
+ instead of global variable. You can also assign several variables
+ in one shot with an array. (Monte, Roman Neuhauser)
+ - added template prefilters, register_prefilter() and
+ unregister_prefilter() API functions. (Monte)
+ - added RELEASE_NOTES file to distribution. (Monte)
+ - moved CREDITS out of manual into its own file. (Monte)
+ - added register_resource() and unregister_resource() API functions. (Monte)
+ - changed the syntax of indexing template variables, thus supporting
+ structures of arbitrary complexity; supplied fix_vars.php script to fix
+ old syntax. (Andrei)
+ - added $insert_tag_check to speed up cached pages if {insert ...} is not
+ used. (Monte)
+ - added $compiler_class variable to allow specifying a different compiler
+ class. (Andrei)
+ - changed Smarty to compile templates at runtime, allowing for arbitrary
+ template resources. (Monte)
+ - added fix for LOCK_EX under Windows and changed a couple of file
+ permissions for security. (Monte, Fernando Nunes)
+ - allow arbitrary date strings to date_format, html_select_date and
+ html_select_time (Monte)
+
+Version 1.3.2
+-------------
+ - fixed a bug that caused some nested includes to loop infinitely. (Andrei)
+ - added optional HTML header to output. (Monte)
+ - significantly improved config_load performance. (Andrei)
+ - added format attribute to math function. (Monte)
+ - added html_select_time custom function. (Andrei)
+ - fixed minor PHP warning when attempting to unset an unset variable
+ (Monte)
+ - added count_characters, count_words, count_sentences, count_paragraphs
+ modifiers (Monte)
+
+Version 1.3.1pl1
+--------------
+ - bug fix, recovered missing _syntax_error function (Monte)
+
+Version 1.3.1
+-------------
+ - document first, last, index_prev, index_next (Monte)
+ - added 'first' and 'last' section properties. (Andrei)
+ - split out compiling code to separate class for faster template execution
+ time (Monte)
+ - fixed a couple of minor PHP warnings (Monte)
+ - added and documented unregister_modifier() and unregister_function() API
+ calls. (Monte)
+ - added and documented 'fetch' and 'math' functions. (Monte)
+ - added ability to index looped variables by section properties, e.g.
+ $foo.index_prev/bar. (Andrei)
+ - added index_prev and index_next section properties. (Andrei)
+ - fixed issue with php executing in literal blocks. (Monte)
+
+Version 1.3.0
+-------------
+ - moved license from GPL to LGPL (Monte)
+ - implemented workaround for PHP "feature" that eats carriage returns
+ if the PHP tag is at the end of the line. (Andrei)
+ - removed $allow_php, added $php_handling logic (Monte)
+ - added file locking to prevent reader/writer problem. (Andrei)
+ - made Smarty catch unimplemented modifiers and custom functions and output
+ error messages during compilation instead of failing during run time.
+ (Andrei)
+ - removed short-tags at the top of the smarty scripts (Monte)
+ - added register_function() and register_modifier() API calls to make
+ registering stuff easier. (Andrei)
+ - added template results caching capability. (Monte, Andrei)
+ - added optional 'options' attribute to html_options custom function
+ that allows passing associative arrays for values/output. (Andrei)
+ - modifier arguments can now contain '|' and ':' characters inside quoted
+ strings. (Andrei)
+
+Version 1.2.2
+-------------
+ - fixed bug that would not respect nested template directories and would
+ put all compiled files into top-level one. (Andrei)
+ - fixed bug using $PHP_VERSION instead of environment var PHP_VERSION.
+ (Monte)
+ - a couple small warning fixes. (Monte)
+
+Version 1.2.1
+-------------
+ - added $compile_dir, removed $compile_dir_ext, simplified usage. (Monte)
+ - added tips & tricks chapter to documentation. (Monte)
+ - misc documentation updates. (Monte)
+
+Version 1.2.0
+-------------
+ - updated documentation (Monte)
+ - added file and line number information to syntax error messages. (Andrei)
+ - added ability to index template vars by a key. (Andrei)
+
+Version 1.1.0
+-------------
+ - misc documentation changes, official stable release
+
+Version 1.0b
+------------
+ - fixed the bug that prevented using non-array values for 'loop' attribute.
+ (Andrei)
+ - many misc documentation changes & additions (Monte)
+
+Version 1.0a
+------------
+ - fixed bug that caused templates to recompile every time (Monte)
+
+Version 1.0
+------------
+ - initial release
+
+/* vim: set et tw=64 ft=changelog: */
diff --git a/BSF/include/smarty/README b/BSF/include/smarty/README
new file mode 100644
index 000000000..6e7c93c56
--- /dev/null
+++ b/BSF/include/smarty/README
@@ -0,0 +1,85 @@
+NAME:
+
+ Smarty - the PHP compiling template engine
+
+VERSION: 2.6.19
+
+AUTHORS:
+
+ Monte Ohrt
+ Andrei Zmievski
+
+MAILING LISTS:
+
+ We have a few mailing lists. "discussion" for you to share your ideas or ask
+ questions, "developers" for those interested in the development efforts of Smarty,
+ and "svn" for those that would like to track the updates made in the svn
+ repository.
+
+ send a blank e-mail message to:
+ smarty-discussion-subscribe@googlecode.com(subscribe to the general discussion list)
+ smarty-discussion-unsubscribe@googlecode.com (unsubscribe from the general discussion list)
+ smarty-discussion-digest-subscribe@googlecode.com (subscribe to digest)
+ smarty-discussion-digest-unsubscribe@googlecode.com (unsubscribe from digest)
+ smarty-developers-subscribe@googlecode.com (subscribe to the dev list)
+ smarty-developers-unsubscribe@googlecode.com (unsubscribe from the dev list)
+ smarty-svn-subscribe@googlecode.com (subscribe to the svn list)
+ smarty-svn-unsubscribe@googlecode.com (unsubscribe from the svn list)
+
+ You can also browse the mailing list archives at
+ http://groups.google.com/group/smarty-discussion
+ http://groups.google.com/group/smarty-developers
+
+ and the OLD list archives at
+ http://marc.theaimsgroup.com/?l=smarty&r=1&w=2
+
+SYNOPSIS:
+
+ require("Smarty.class.php");
+
+ $smarty = new Smarty;
+
+ $smarty->assign("Title","My Homepage");
+ $smarty->assign("Names",array("John","Gary","Gregg","James"));
+
+ $smarty->display("index.tpl");
+
+
+DESCRIPTION:
+
+ What is Smarty?
+
+ Smarty is a template engine for PHP. Many other template engines for PHP
+ provide basic variable substitution and dynamic block functionality.
+ Smarty takes a step further to be a "smart" template engine, adding
+ features such as configuration files, template functions, and variable
+ modifiers, and making all of this functionality as easy as possible to
+ use for both programmers and template designers. Smarty also converts
+ the templates into PHP scripts, eliminating the need to parse the
+ templates on every invocation. This makes Smarty extremely scalable and
+ manageable for large application needs.
+
+ Some of Smarty's features:
+
+ * it is extremely fast
+ * no template parsing overhead, only compiles once.
+ * it is smart about recompiling only the template files that have
+ changed.
+ * the template language is remarkably extensible via the plugin
+ architecture.
+ * configurable template delimiter tag syntax, so you can use
+ {}, {{}}, , or whatever you like.
+ * built-in caching of template output.
+ * arbitrary template sources (filesystem, databases, etc.)
+ * template if/elseif/else/endif constructs are passed to the PHP parser,
+ so the if syntax can be as simple or as complex as you like.
+ * unlimited nesting of sections, conditionals, etc. allowed
+ * it is possible to embed PHP code right in your template files,
+ although not recommended and doubtfully needed since the engine
+ is so customizable.
+ * and many more.
+
+COPYRIGHT:
+ Copyright (c) 2001-2005 New Digital Group, Inc. All rights reserved.
+ This software is released under the GNU Lesser General Public License.
+ Please read the disclaimer at the top of the Smarty.class.php file.
diff --git a/BSF/include/smarty/libs/Config_File.class.php b/BSF/include/smarty/libs/Config_File.class.php
new file mode 100644
index 000000000..89ba41c45
--- /dev/null
+++ b/BSF/include/smarty/libs/Config_File.class.php
@@ -0,0 +1,389 @@
+
+ * @access public
+ * @package Smarty
+ */
+
+/* $Id$ */
+
+/**
+ * Config file reading class
+ * @package Smarty
+ */
+class Config_File {
+ /**#@+
+ * Options
+ * @var boolean
+ */
+ /**
+ * Controls whether variables with the same name overwrite each other.
+ */
+ var $overwrite = true;
+
+ /**
+ * Controls whether config values of on/true/yes and off/false/no get
+ * converted to boolean values automatically.
+ */
+ var $booleanize = true;
+
+ /**
+ * Controls whether hidden config sections/vars are read from the file.
+ */
+ var $read_hidden = true;
+
+ /**
+ * Controls whether or not to fix mac or dos formatted newlines.
+ * If set to true, \r or \r\n will be changed to \n.
+ */
+ var $fix_newlines = true;
+ /**#@-*/
+
+ /** @access private */
+ var $_config_path = "";
+ var $_config_data = array();
+ /**#@-*/
+
+ /**
+ * Constructs a new config file class.
+ *
+ * @param string $config_path (optional) path to the config files
+ */
+ function Config_File($config_path = NULL)
+ {
+ if (isset($config_path))
+ $this->set_path($config_path);
+ }
+
+
+ /**
+ * Set the path where configuration files can be found.
+ *
+ * @param string $config_path path to the config files
+ */
+ function set_path($config_path)
+ {
+ if (!empty($config_path)) {
+ if (!is_string($config_path) || !file_exists($config_path) || !is_dir($config_path)) {
+ $this->_trigger_error_msg("Bad config file path '$config_path'");
+ return;
+ }
+ if(substr($config_path, -1) != DIRECTORY_SEPARATOR) {
+ $config_path .= DIRECTORY_SEPARATOR;
+ }
+
+ $this->_config_path = $config_path;
+ }
+ }
+
+
+ /**
+ * Retrieves config info based on the file, section, and variable name.
+ *
+ * @param string $file_name config file to get info for
+ * @param string $section_name (optional) section to get info for
+ * @param string $var_name (optional) variable to get info for
+ * @return string|array a value or array of values
+ */
+ function get($file_name, $section_name = NULL, $var_name = NULL)
+ {
+ if (empty($file_name)) {
+ $this->_trigger_error_msg('Empty config file name');
+ return;
+ } else {
+ $file_name = $this->_config_path . $file_name;
+ if (!isset($this->_config_data[$file_name]))
+ $this->load_file($file_name, false);
+ }
+
+ if (!empty($var_name)) {
+ if (empty($section_name)) {
+ return $this->_config_data[$file_name]["vars"][$var_name];
+ } else {
+ if(isset($this->_config_data[$file_name]["sections"][$section_name]["vars"][$var_name]))
+ return $this->_config_data[$file_name]["sections"][$section_name]["vars"][$var_name];
+ else
+ return array();
+ }
+ } else {
+ if (empty($section_name)) {
+ return (array)$this->_config_data[$file_name]["vars"];
+ } else {
+ if(isset($this->_config_data[$file_name]["sections"][$section_name]["vars"]))
+ return (array)$this->_config_data[$file_name]["sections"][$section_name]["vars"];
+ else
+ return array();
+ }
+ }
+ }
+
+
+ /**
+ * Retrieves config info based on the key.
+ *
+ * @param $file_name string config key (filename/section/var)
+ * @return string|array same as get()
+ * @uses get() retrieves information from config file and returns it
+ */
+ function &get_key($config_key)
+ {
+ list($file_name, $section_name, $var_name) = explode('/', $config_key, 3);
+ $result = &$this->get($file_name, $section_name, $var_name);
+ return $result;
+ }
+
+ /**
+ * Get all loaded config file names.
+ *
+ * @return array an array of loaded config file names
+ */
+ function get_file_names()
+ {
+ return array_keys($this->_config_data);
+ }
+
+
+ /**
+ * Get all section names from a loaded file.
+ *
+ * @param string $file_name config file to get section names from
+ * @return array an array of section names from the specified file
+ */
+ function get_section_names($file_name)
+ {
+ $file_name = $this->_config_path . $file_name;
+ if (!isset($this->_config_data[$file_name])) {
+ $this->_trigger_error_msg("Unknown config file '$file_name'");
+ return;
+ }
+
+ return array_keys($this->_config_data[$file_name]["sections"]);
+ }
+
+
+ /**
+ * Get all global or section variable names.
+ *
+ * @param string $file_name config file to get info for
+ * @param string $section_name (optional) section to get info for
+ * @return array an array of variables names from the specified file/section
+ */
+ function get_var_names($file_name, $section = NULL)
+ {
+ if (empty($file_name)) {
+ $this->_trigger_error_msg('Empty config file name');
+ return;
+ } else if (!isset($this->_config_data[$file_name])) {
+ $this->_trigger_error_msg("Unknown config file '$file_name'");
+ return;
+ }
+
+ if (empty($section))
+ return array_keys($this->_config_data[$file_name]["vars"]);
+ else
+ return array_keys($this->_config_data[$file_name]["sections"][$section]["vars"]);
+ }
+
+
+ /**
+ * Clear loaded config data for a certain file or all files.
+ *
+ * @param string $file_name file to clear config data for
+ */
+ function clear($file_name = NULL)
+ {
+ if ($file_name === NULL)
+ $this->_config_data = array();
+ else if (isset($this->_config_data[$file_name]))
+ $this->_config_data[$file_name] = array();
+ }
+
+
+ /**
+ * Load a configuration file manually.
+ *
+ * @param string $file_name file name to load
+ * @param boolean $prepend_path whether current config path should be
+ * prepended to the filename
+ */
+ function load_file($file_name, $prepend_path = true)
+ {
+ if ($prepend_path && $this->_config_path != "")
+ $config_file = $this->_config_path . $file_name;
+ else
+ $config_file = $file_name;
+
+ ini_set('track_errors', true);
+ $fp = @fopen($config_file, "r");
+ if (!is_resource($fp)) {
+ $this->_trigger_error_msg("Could not open config file '$config_file'");
+ return false;
+ }
+
+ $contents = ($size = filesize($config_file)) ? fread($fp, $size) : '';
+ fclose($fp);
+
+ $this->_config_data[$config_file] = $this->parse_contents($contents);
+ return true;
+ }
+
+ /**
+ * Store the contents of a file manually.
+ *
+ * @param string $config_file file name of the related contents
+ * @param string $contents the file-contents to parse
+ */
+ function set_file_contents($config_file, $contents)
+ {
+ $this->_config_data[$config_file] = $this->parse_contents($contents);
+ return true;
+ }
+
+ /**
+ * parse the source of a configuration file manually.
+ *
+ * @param string $contents the file-contents to parse
+ */
+ function parse_contents($contents)
+ {
+ if($this->fix_newlines) {
+ // fix mac/dos formatted newlines
+ $contents = preg_replace('!\r\n?!', "\n", $contents);
+ }
+
+ $config_data = array();
+ $config_data['sections'] = array();
+ $config_data['vars'] = array();
+
+ /* reference to fill with data */
+ $vars =& $config_data['vars'];
+
+ /* parse file line by line */
+ preg_match_all('!^.*\r?\n?!m', $contents, $match);
+ $lines = $match[0];
+ for ($i=0, $count=count($lines); $i<$count; $i++) {
+ $line = $lines[$i];
+ if (empty($line)) continue;
+
+ if ( substr($line, 0, 1) == '[' && preg_match('!^\[(.*?)\]!', $line, $match) ) {
+ /* section found */
+ if (substr($match[1], 0, 1) == '.') {
+ /* hidden section */
+ if ($this->read_hidden) {
+ $section_name = substr($match[1], 1);
+ } else {
+ /* break reference to $vars to ignore hidden section */
+ unset($vars);
+ $vars = array();
+ continue;
+ }
+ } else {
+ $section_name = $match[1];
+ }
+ if (!isset($config_data['sections'][$section_name]))
+ $config_data['sections'][$section_name] = array('vars' => array());
+ $vars =& $config_data['sections'][$section_name]['vars'];
+ continue;
+ }
+
+ if (preg_match('/^\s*(\.?\w+)\s*=\s*(.*)/s', $line, $match)) {
+ /* variable found */
+ $var_name = rtrim($match[1]);
+ if (strpos($match[2], '"""') === 0) {
+ /* handle multiline-value */
+ $lines[$i] = substr($match[2], 3);
+ $var_value = '';
+ while ($i<$count) {
+ if (($pos = strpos($lines[$i], '"""')) === false) {
+ $var_value .= $lines[$i++];
+ } else {
+ /* end of multiline-value */
+ $var_value .= substr($lines[$i], 0, $pos);
+ break;
+ }
+ }
+ $booleanize = false;
+
+ } else {
+ /* handle simple value */
+ $var_value = preg_replace('/^([\'"])(.*)\1$/', '\2', rtrim($match[2]));
+ $booleanize = $this->booleanize;
+
+ }
+ $this->_set_config_var($vars, $var_name, $var_value, $booleanize);
+ }
+ /* else unparsable line / means it is a comment / means ignore it */
+ }
+ return $config_data;
+ }
+
+ /**#@+ @access private */
+ /**
+ * @param array &$container
+ * @param string $var_name
+ * @param mixed $var_value
+ * @param boolean $booleanize determines whether $var_value is converted to
+ * to true/false
+ */
+ function _set_config_var(&$container, $var_name, $var_value, $booleanize)
+ {
+ if (substr($var_name, 0, 1) == '.') {
+ if (!$this->read_hidden)
+ return;
+ else
+ $var_name = substr($var_name, 1);
+ }
+
+ if (!preg_match("/^[a-zA-Z_]\w*$/", $var_name)) {
+ $this->_trigger_error_msg("Bad variable name '$var_name'");
+ return;
+ }
+
+ if ($booleanize) {
+ if (preg_match("/^(on|true|yes)$/i", $var_value))
+ $var_value = true;
+ else if (preg_match("/^(off|false|no)$/i", $var_value))
+ $var_value = false;
+ }
+
+ if (!isset($container[$var_name]) || $this->overwrite)
+ $container[$var_name] = $var_value;
+ else {
+ settype($container[$var_name], 'array');
+ $container[$var_name][] = $var_value;
+ }
+ }
+
+ /**
+ * @uses trigger_error() creates a PHP warning/error
+ * @param string $error_msg
+ * @param integer $error_type one of
+ */
+ function _trigger_error_msg($error_msg, $error_type = E_USER_WARNING)
+ {
+ trigger_error("Config_File error: $error_msg", $error_type);
+ }
+ /**#@-*/
+}
+
+?>
diff --git a/BSF/include/smarty/libs/Smarty.class.php b/BSF/include/smarty/libs/Smarty.class.php
new file mode 100644
index 000000000..6b893dec8
--- /dev/null
+++ b/BSF/include/smarty/libs/Smarty.class.php
@@ -0,0 +1,1968 @@
+
+ * @author Andrei Zmievski
+ * @package Smarty
+ * @version 2.6.19
+ */
+
+/* $Id$ */
+
+/**
+ * DIR_SEP isn't used anymore, but third party apps might
+ */
+if(!defined('DIR_SEP')) {
+ define('DIR_SEP', DIRECTORY_SEPARATOR);
+}
+
+/**
+ * set SMARTY_DIR to absolute path to Smarty library files.
+ * if not defined, include_path will be used. Sets SMARTY_DIR only if user
+ * application has not already defined it.
+ */
+
+if (!defined('SMARTY_DIR')) {
+ define('SMARTY_DIR', dirname(__FILE__) . DIRECTORY_SEPARATOR);
+}
+
+if (!defined('SMARTY_CORE_DIR')) {
+ define('SMARTY_CORE_DIR', SMARTY_DIR . 'internals' . DIRECTORY_SEPARATOR);
+}
+
+define('SMARTY_PHP_PASSTHRU', 0);
+define('SMARTY_PHP_QUOTE', 1);
+define('SMARTY_PHP_REMOVE', 2);
+define('SMARTY_PHP_ALLOW', 3);
+
+/**
+ * @package Smarty
+ */
+class Smarty
+{
+ /**#@+
+ * Smarty Configuration Section
+ */
+
+ /**
+ * The name of the directory where templates are located.
+ *
+ * @var string
+ */
+ var $template_dir = 'templates';
+
+ /**
+ * The directory where compiled templates are located.
+ *
+ * @var string
+ */
+ var $compile_dir = 'templates_c';
+
+ /**
+ * The directory where config files are located.
+ *
+ * @var string
+ */
+ var $config_dir = 'configs';
+
+ /**
+ * An array of directories searched for plugins.
+ *
+ * @var array
+ */
+ var $plugins_dir = array('plugins');
+
+ /**
+ * If debugging is enabled, a debug console window will display
+ * when the page loads (make sure your browser allows unrequested
+ * popup windows)
+ *
+ * @var boolean
+ */
+ var $debugging = false;
+
+ /**
+ * When set, smarty does uses this value as error_reporting-level.
+ *
+ * @var boolean
+ */
+ var $error_reporting = null;
+
+ /**
+ * This is the path to the debug console template. If not set,
+ * the default one will be used.
+ *
+ * @var string
+ */
+ var $debug_tpl = '';
+
+ /**
+ * This determines if debugging is enable-able from the browser.
+ *
+ *
NONE => no debugging control allowed
+ *
URL => enable debugging when SMARTY_DEBUG is found in the URL.
+ *
+ * @link http://www.foo.dom/index.php?SMARTY_DEBUG
+ * @var string
+ */
+ var $debugging_ctrl = 'NONE';
+
+ /**
+ * This tells Smarty whether to check for recompiling or not. Recompiling
+ * does not need to happen unless a template or config file is changed.
+ * Typically you enable this during development, and disable for
+ * production.
+ *
+ * @var boolean
+ */
+ var $compile_check = true;
+
+ /**
+ * This forces templates to compile every time. Useful for development
+ * or debugging.
+ *
+ * @var boolean
+ */
+ var $force_compile = false;
+
+ /**
+ * This enables template caching.
+ *
+ *
0 = no caching
+ *
1 = use class cache_lifetime value
+ *
2 = use cache_lifetime in cache file
+ *
+ * @var integer
+ */
+ var $caching = 0;
+
+ /**
+ * The name of the directory for cache files.
+ *
+ * @var string
+ */
+ var $cache_dir = 'cache';
+
+ /**
+ * This is the number of seconds cached content will persist.
+ *
+ *
0 = always regenerate cache
+ *
-1 = never expires
+ *
+ *
+ * @var integer
+ */
+ var $cache_lifetime = 3600;
+
+ /**
+ * Only used when $caching is enabled. If true, then If-Modified-Since headers
+ * are respected with cached content, and appropriate HTTP headers are sent.
+ * This way repeated hits to a cached page do not send the entire page to the
+ * client every time.
+ *
+ * @var boolean
+ */
+ var $cache_modified_check = false;
+
+ /**
+ * This determines how Smarty handles "" tags in templates.
+ * possible values:
+ *
+ *
SMARTY_PHP_PASSTHRU -> print tags as plain text
+ *
SMARTY_PHP_QUOTE -> escape tags as entities
+ *
SMARTY_PHP_REMOVE -> remove php tags
+ *
SMARTY_PHP_ALLOW -> execute php tags
+ *
+ *
+ * @var integer
+ */
+ var $php_handling = SMARTY_PHP_PASSTHRU;
+
+ /**
+ * This enables template security. When enabled, many things are restricted
+ * in the templates that normally would go unchecked. This is useful when
+ * untrusted parties are editing templates and you want a reasonable level
+ * of security. (no direct execution of PHP in templates for example)
+ *
+ * @var boolean
+ */
+ var $security = false;
+
+ /**
+ * This is the list of template directories that are considered secure. This
+ * is used only if {@link $security} is enabled. One directory per array
+ * element. {@link $template_dir} is in this list implicitly.
+ *
+ * @var array
+ */
+ var $secure_dir = array();
+
+ /**
+ * These are the security settings for Smarty. They are used only when
+ * {@link $security} is enabled.
+ *
+ * @var array
+ */
+ var $security_settings = array(
+ 'PHP_HANDLING' => false,
+ 'IF_FUNCS' => array('array', 'list',
+ 'isset', 'empty',
+ 'count', 'sizeof',
+ 'in_array', 'is_array',
+ 'true', 'false', 'null'),
+ 'INCLUDE_ANY' => false,
+ 'PHP_TAGS' => false,
+ 'MODIFIER_FUNCS' => array('count'),
+ 'ALLOW_CONSTANTS' => false
+ );
+
+ /**
+ * This is an array of directories where trusted php scripts reside.
+ * {@link $security} is disabled during their inclusion/execution.
+ *
+ * @var array
+ */
+ var $trusted_dir = array();
+
+ /**
+ * The left delimiter used for the template tags.
+ *
+ * @var string
+ */
+ var $left_delimiter = '{';
+
+ /**
+ * The right delimiter used for the template tags.
+ *
+ * @var string
+ */
+ var $right_delimiter = '}';
+
+ /**
+ * The order in which request variables are registered, similar to
+ * variables_order in php.ini E = Environment, G = GET, P = POST,
+ * C = Cookies, S = Server
+ *
+ * @var string
+ */
+ var $request_vars_order = 'EGPCS';
+
+ /**
+ * Indicates wether $HTTP_*_VARS[] (request_use_auto_globals=false)
+ * are uses as request-vars or $_*[]-vars. note: if
+ * request_use_auto_globals is true, then $request_vars_order has
+ * no effect, but the php-ini-value "gpc_order"
+ *
+ * @var boolean
+ */
+ var $request_use_auto_globals = true;
+
+ /**
+ * Set this if you want different sets of compiled files for the same
+ * templates. This is useful for things like different languages.
+ * Instead of creating separate sets of templates per language, you
+ * set different compile_ids like 'en' and 'de'.
+ *
+ * @var string
+ */
+ var $compile_id = null;
+
+ /**
+ * This tells Smarty whether or not to use sub dirs in the cache/ and
+ * templates_c/ directories. sub directories better organized, but
+ * may not work well with PHP safe mode enabled.
+ *
+ * @var boolean
+ *
+ */
+ var $use_sub_dirs = false;
+
+ /**
+ * This is a list of the modifiers to apply to all template variables.
+ * Put each modifier in a separate array element in the order you want
+ * them applied. example: array('escape:"htmlall"');
+ *
+ * @var array
+ */
+ var $default_modifiers = array();
+
+ /**
+ * This is the resource type to be used when not specified
+ * at the beginning of the resource path. examples:
+ * $smarty->display('file:index.tpl');
+ * $smarty->display('db:index.tpl');
+ * $smarty->display('index.tpl'); // will use default resource type
+ * {include file="file:index.tpl"}
+ * {include file="db:index.tpl"}
+ * {include file="index.tpl"} {* will use default resource type *}
+ *
+ * @var array
+ */
+ var $default_resource_type = 'file';
+
+ /**
+ * The function used for cache file handling. If not set, built-in caching is used.
+ *
+ * @var null|string function name
+ */
+ var $cache_handler_func = null;
+
+ /**
+ * This indicates which filters are automatically loaded into Smarty.
+ *
+ * @var array array of filter names
+ */
+ var $autoload_filters = array();
+
+ /**#@+
+ * @var boolean
+ */
+ /**
+ * This tells if config file vars of the same name overwrite each other or not.
+ * if disabled, same name variables are accumulated in an array.
+ */
+ var $config_overwrite = true;
+
+ /**
+ * This tells whether or not to automatically booleanize config file variables.
+ * If enabled, then the strings "on", "true", and "yes" are treated as boolean
+ * true, and "off", "false" and "no" are treated as boolean false.
+ */
+ var $config_booleanize = true;
+
+ /**
+ * This tells whether hidden sections [.foobar] are readable from the
+ * tempalates or not. Normally you would never allow this since that is
+ * the point behind hidden sections: the application can access them, but
+ * the templates cannot.
+ */
+ var $config_read_hidden = false;
+
+ /**
+ * This tells whether or not automatically fix newlines in config files.
+ * It basically converts \r (mac) or \r\n (dos) to \n
+ */
+ var $config_fix_newlines = true;
+ /**#@-*/
+
+ /**
+ * If a template cannot be found, this PHP function will be executed.
+ * Useful for creating templates on-the-fly or other special action.
+ *
+ * @var string function name
+ */
+ var $default_template_handler_func = '';
+
+ /**
+ * The file that contains the compiler class. This can a full
+ * pathname, or relative to the php_include path.
+ *
+ * @var string
+ */
+ var $compiler_file = 'Smarty_Compiler.class.php';
+
+ /**
+ * The class used for compiling templates.
+ *
+ * @var string
+ */
+ var $compiler_class = 'Smarty_Compiler';
+
+ /**
+ * The class used to load config vars.
+ *
+ * @var string
+ */
+ var $config_class = 'Config_File';
+
+/**#@+
+ * END Smarty Configuration Section
+ * There should be no need to touch anything below this line.
+ * @access private
+ */
+ /**
+ * where assigned template vars are kept
+ *
+ * @var array
+ */
+ var $_tpl_vars = array();
+
+ /**
+ * stores run-time $smarty.* vars
+ *
+ * @var null|array
+ */
+ var $_smarty_vars = null;
+
+ /**
+ * keeps track of sections
+ *
+ * @var array
+ */
+ var $_sections = array();
+
+ /**
+ * keeps track of foreach blocks
+ *
+ * @var array
+ */
+ var $_foreach = array();
+
+ /**
+ * keeps track of tag hierarchy
+ *
+ * @var array
+ */
+ var $_tag_stack = array();
+
+ /**
+ * configuration object
+ *
+ * @var Config_file
+ */
+ var $_conf_obj = null;
+
+ /**
+ * loaded configuration settings
+ *
+ * @var array
+ */
+ var $_config = array(array('vars' => array(), 'files' => array()));
+
+ /**
+ * md5 checksum of the string 'Smarty'
+ *
+ * @var string
+ */
+ var $_smarty_md5 = 'f8d698aea36fcbead2b9d5359ffca76f';
+
+ /**
+ * Smarty version number
+ *
+ * @var string
+ */
+ var $_version = '2.6.19';
+
+ /**
+ * current template inclusion depth
+ *
+ * @var integer
+ */
+ var $_inclusion_depth = 0;
+
+ /**
+ * for different compiled templates
+ *
+ * @var string
+ */
+ var $_compile_id = null;
+
+ /**
+ * text in URL to enable debug mode
+ *
+ * @var string
+ */
+ var $_smarty_debug_id = 'SMARTY_DEBUG';
+
+ /**
+ * debugging information for debug console
+ *
+ * @var array
+ */
+ var $_smarty_debug_info = array();
+
+ /**
+ * info that makes up a cache file
+ *
+ * @var array
+ */
+ var $_cache_info = array();
+
+ /**
+ * default file permissions
+ *
+ * @var integer
+ */
+ var $_file_perms = 0644;
+
+ /**
+ * default dir permissions
+ *
+ * @var integer
+ */
+ var $_dir_perms = 0771;
+
+ /**
+ * registered objects
+ *
+ * @var array
+ */
+ var $_reg_objects = array();
+
+ /**
+ * table keeping track of plugins
+ *
+ * @var array
+ */
+ var $_plugins = array(
+ 'modifier' => array(),
+ 'function' => array(),
+ 'block' => array(),
+ 'compiler' => array(),
+ 'prefilter' => array(),
+ 'postfilter' => array(),
+ 'outputfilter' => array(),
+ 'resource' => array(),
+ 'insert' => array());
+
+
+ /**
+ * cache serials
+ *
+ * @var array
+ */
+ var $_cache_serials = array();
+
+ /**
+ * name of optional cache include file
+ *
+ * @var string
+ */
+ var $_cache_include = null;
+
+ /**
+ * indicate if the current code is used in a compiled
+ * include
+ *
+ * @var string
+ */
+ var $_cache_including = false;
+
+ /**#@-*/
+ /**
+ * The class constructor.
+ */
+ function Smarty()
+ {
+ $this->assign('SCRIPT_NAME', isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME']
+ : @$GLOBALS['HTTP_SERVER_VARS']['SCRIPT_NAME']);
+ }
+
+ /**
+ * assigns values to template variables
+ *
+ * @param array|string $tpl_var the template variable name(s)
+ * @param mixed $value the value to assign
+ */
+ function assign($tpl_var, $value = null)
+ {
+ if (is_array($tpl_var)){
+ foreach ($tpl_var as $key => $val) {
+ if ($key != '') {
+ $this->_tpl_vars[$key] = $val;
+ }
+ }
+ } else {
+ if ($tpl_var != '')
+ $this->_tpl_vars[$tpl_var] = $value;
+ }
+ }
+
+ /**
+ * assigns values to template variables by reference
+ *
+ * @param string $tpl_var the template variable name
+ * @param mixed $value the referenced value to assign
+ */
+ function assign_by_ref($tpl_var, &$value)
+ {
+ if ($tpl_var != '')
+ $this->_tpl_vars[$tpl_var] = &$value;
+ }
+
+ /**
+ * appends values to template variables
+ *
+ * @param array|string $tpl_var the template variable name(s)
+ * @param mixed $value the value to append
+ */
+ function append($tpl_var, $value=null, $merge=false)
+ {
+ if (is_array($tpl_var)) {
+ // $tpl_var is an array, ignore $value
+ foreach ($tpl_var as $_key => $_val) {
+ if ($_key != '') {
+ if(!@is_array($this->_tpl_vars[$_key])) {
+ settype($this->_tpl_vars[$_key],'array');
+ }
+ if($merge && is_array($_val)) {
+ foreach($_val as $_mkey => $_mval) {
+ $this->_tpl_vars[$_key][$_mkey] = $_mval;
+ }
+ } else {
+ $this->_tpl_vars[$_key][] = $_val;
+ }
+ }
+ }
+ } else {
+ if ($tpl_var != '' && isset($value)) {
+ if(!@is_array($this->_tpl_vars[$tpl_var])) {
+ settype($this->_tpl_vars[$tpl_var],'array');
+ }
+ if($merge && is_array($value)) {
+ foreach($value as $_mkey => $_mval) {
+ $this->_tpl_vars[$tpl_var][$_mkey] = $_mval;
+ }
+ } else {
+ $this->_tpl_vars[$tpl_var][] = $value;
+ }
+ }
+ }
+ }
+
+ /**
+ * appends values to template variables by reference
+ *
+ * @param string $tpl_var the template variable name
+ * @param mixed $value the referenced value to append
+ */
+ function append_by_ref($tpl_var, &$value, $merge=false)
+ {
+ if ($tpl_var != '' && isset($value)) {
+ if(!@is_array($this->_tpl_vars[$tpl_var])) {
+ settype($this->_tpl_vars[$tpl_var],'array');
+ }
+ if ($merge && is_array($value)) {
+ foreach($value as $_key => $_val) {
+ $this->_tpl_vars[$tpl_var][$_key] = &$value[$_key];
+ }
+ } else {
+ $this->_tpl_vars[$tpl_var][] = &$value;
+ }
+ }
+ }
+
+
+ /**
+ * clear the given assigned template variable.
+ *
+ * @param string $tpl_var the template variable to clear
+ */
+ function clear_assign($tpl_var)
+ {
+ if (is_array($tpl_var))
+ foreach ($tpl_var as $curr_var)
+ unset($this->_tpl_vars[$curr_var]);
+ else
+ unset($this->_tpl_vars[$tpl_var]);
+ }
+
+
+ /**
+ * Registers custom function to be used in templates
+ *
+ * @param string $function the name of the template function
+ * @param string $function_impl the name of the PHP function to register
+ */
+ function register_function($function, $function_impl, $cacheable=true, $cache_attrs=null)
+ {
+ $this->_plugins['function'][$function] =
+ array($function_impl, null, null, false, $cacheable, $cache_attrs);
+
+ }
+
+ /**
+ * Unregisters custom function
+ *
+ * @param string $function name of template function
+ */
+ function unregister_function($function)
+ {
+ unset($this->_plugins['function'][$function]);
+ }
+
+ /**
+ * Registers object to be used in templates
+ *
+ * @param string $object name of template object
+ * @param object &$object_impl the referenced PHP object to register
+ * @param null|array $allowed list of allowed methods (empty = all)
+ * @param boolean $smarty_args smarty argument format, else traditional
+ * @param null|array $block_functs list of methods that are block format
+ */
+ function register_object($object, &$object_impl, $allowed = array(), $smarty_args = true, $block_methods = array())
+ {
+ settype($allowed, 'array');
+ settype($smarty_args, 'boolean');
+ $this->_reg_objects[$object] =
+ array(&$object_impl, $allowed, $smarty_args, $block_methods);
+ }
+
+ /**
+ * Unregisters object
+ *
+ * @param string $object name of template object
+ */
+ function unregister_object($object)
+ {
+ unset($this->_reg_objects[$object]);
+ }
+
+
+ /**
+ * Registers block function to be used in templates
+ *
+ * @param string $block name of template block
+ * @param string $block_impl PHP function to register
+ */
+ function register_block($block, $block_impl, $cacheable=true, $cache_attrs=null)
+ {
+ $this->_plugins['block'][$block] =
+ array($block_impl, null, null, false, $cacheable, $cache_attrs);
+ }
+
+ /**
+ * Unregisters block function
+ *
+ * @param string $block name of template function
+ */
+ function unregister_block($block)
+ {
+ unset($this->_plugins['block'][$block]);
+ }
+
+ /**
+ * Registers compiler function
+ *
+ * @param string $function name of template function
+ * @param string $function_impl name of PHP function to register
+ */
+ function register_compiler_function($function, $function_impl, $cacheable=true)
+ {
+ $this->_plugins['compiler'][$function] =
+ array($function_impl, null, null, false, $cacheable);
+ }
+
+ /**
+ * Unregisters compiler function
+ *
+ * @param string $function name of template function
+ */
+ function unregister_compiler_function($function)
+ {
+ unset($this->_plugins['compiler'][$function]);
+ }
+
+ /**
+ * Registers modifier to be used in templates
+ *
+ * @param string $modifier name of template modifier
+ * @param string $modifier_impl name of PHP function to register
+ */
+ function register_modifier($modifier, $modifier_impl)
+ {
+ $this->_plugins['modifier'][$modifier] =
+ array($modifier_impl, null, null, false);
+ }
+
+ /**
+ * Unregisters modifier
+ *
+ * @param string $modifier name of template modifier
+ */
+ function unregister_modifier($modifier)
+ {
+ unset($this->_plugins['modifier'][$modifier]);
+ }
+
+ /**
+ * Registers a resource to fetch a template
+ *
+ * @param string $type name of resource
+ * @param array $functions array of functions to handle resource
+ */
+ function register_resource($type, $functions)
+ {
+ if (count($functions)==4) {
+ $this->_plugins['resource'][$type] =
+ array($functions, false);
+
+ } elseif (count($functions)==5) {
+ $this->_plugins['resource'][$type] =
+ array(array(array(&$functions[0], $functions[1])
+ ,array(&$functions[0], $functions[2])
+ ,array(&$functions[0], $functions[3])
+ ,array(&$functions[0], $functions[4]))
+ ,false);
+
+ } else {
+ $this->trigger_error("malformed function-list for '$type' in register_resource");
+
+ }
+ }
+
+ /**
+ * Unregisters a resource
+ *
+ * @param string $type name of resource
+ */
+ function unregister_resource($type)
+ {
+ unset($this->_plugins['resource'][$type]);
+ }
+
+ /**
+ * Registers a prefilter function to apply
+ * to a template before compiling
+ *
+ * @param callback $function
+ */
+ function register_prefilter($function)
+ {
+ $this->_plugins['prefilter'][$this->_get_filter_name($function)]
+ = array($function, null, null, false);
+ }
+
+ /**
+ * Unregisters a prefilter function
+ *
+ * @param callback $function
+ */
+ function unregister_prefilter($function)
+ {
+ unset($this->_plugins['prefilter'][$this->_get_filter_name($function)]);
+ }
+
+ /**
+ * Registers a postfilter function to apply
+ * to a compiled template after compilation
+ *
+ * @param callback $function
+ */
+ function register_postfilter($function)
+ {
+ $this->_plugins['postfilter'][$this->_get_filter_name($function)]
+ = array($function, null, null, false);
+ }
+
+ /**
+ * Unregisters a postfilter function
+ *
+ * @param callback $function
+ */
+ function unregister_postfilter($function)
+ {
+ unset($this->_plugins['postfilter'][$this->_get_filter_name($function)]);
+ }
+
+ /**
+ * Registers an output filter function to apply
+ * to a template output
+ *
+ * @param callback $function
+ */
+ function register_outputfilter($function)
+ {
+ $this->_plugins['outputfilter'][$this->_get_filter_name($function)]
+ = array($function, null, null, false);
+ }
+
+ /**
+ * Unregisters an outputfilter function
+ *
+ * @param callback $function
+ */
+ function unregister_outputfilter($function)
+ {
+ unset($this->_plugins['outputfilter'][$this->_get_filter_name($function)]);
+ }
+
+ /**
+ * load a filter of specified type and name
+ *
+ * @param string $type filter type
+ * @param string $name filter name
+ */
+ function load_filter($type, $name)
+ {
+ switch ($type) {
+ case 'output':
+ $_params = array('plugins' => array(array($type . 'filter', $name, null, null, false)));
+ require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
+ smarty_core_load_plugins($_params, $this);
+ break;
+
+ case 'pre':
+ case 'post':
+ if (!isset($this->_plugins[$type . 'filter'][$name]))
+ $this->_plugins[$type . 'filter'][$name] = false;
+ break;
+ }
+ }
+
+ /**
+ * clear cached content for the given template and cache id
+ *
+ * @param string $tpl_file name of template file
+ * @param string $cache_id name of cache_id
+ * @param string $compile_id name of compile_id
+ * @param string $exp_time expiration time
+ * @return boolean
+ */
+ function clear_cache($tpl_file = null, $cache_id = null, $compile_id = null, $exp_time = null)
+ {
+
+ if (!isset($compile_id))
+ $compile_id = $this->compile_id;
+
+ if (!isset($tpl_file))
+ $compile_id = null;
+
+ $_auto_id = $this->_get_auto_id($cache_id, $compile_id);
+
+ if (!empty($this->cache_handler_func)) {
+ return call_user_func_array($this->cache_handler_func,
+ array('clear', &$this, &$dummy, $tpl_file, $cache_id, $compile_id, $exp_time));
+ } else {
+ $_params = array('auto_base' => $this->cache_dir,
+ 'auto_source' => $tpl_file,
+ 'auto_id' => $_auto_id,
+ 'exp_time' => $exp_time);
+ require_once(SMARTY_CORE_DIR . 'core.rm_auto.php');
+ return smarty_core_rm_auto($_params, $this);
+ }
+
+ }
+
+
+ /**
+ * clear the entire contents of cache (all templates)
+ *
+ * @param string $exp_time expire time
+ * @return boolean results of {@link smarty_core_rm_auto()}
+ */
+ function clear_all_cache($exp_time = null)
+ {
+ return $this->clear_cache(null, null, null, $exp_time);
+ }
+
+
+ /**
+ * test to see if valid cache exists for this template
+ *
+ * @param string $tpl_file name of template file
+ * @param string $cache_id
+ * @param string $compile_id
+ * @return string|false results of {@link _read_cache_file()}
+ */
+ function is_cached($tpl_file, $cache_id = null, $compile_id = null)
+ {
+ if (!$this->caching)
+ return false;
+
+ if (!isset($compile_id))
+ $compile_id = $this->compile_id;
+
+ $_params = array(
+ 'tpl_file' => $tpl_file,
+ 'cache_id' => $cache_id,
+ 'compile_id' => $compile_id
+ );
+ require_once(SMARTY_CORE_DIR . 'core.read_cache_file.php');
+ return smarty_core_read_cache_file($_params, $this);
+ }
+
+
+ /**
+ * clear all the assigned template variables.
+ *
+ */
+ function clear_all_assign()
+ {
+ $this->_tpl_vars = array();
+ }
+
+ /**
+ * clears compiled version of specified template resource,
+ * or all compiled template files if one is not specified.
+ * This function is for advanced use only, not normally needed.
+ *
+ * @param string $tpl_file
+ * @param string $compile_id
+ * @param string $exp_time
+ * @return boolean results of {@link smarty_core_rm_auto()}
+ */
+ function clear_compiled_tpl($tpl_file = null, $compile_id = null, $exp_time = null)
+ {
+ if (!isset($compile_id)) {
+ $compile_id = $this->compile_id;
+ }
+ $_params = array('auto_base' => $this->compile_dir,
+ 'auto_source' => $tpl_file,
+ 'auto_id' => $compile_id,
+ 'exp_time' => $exp_time,
+ 'extensions' => array('.inc', '.php'));
+ require_once(SMARTY_CORE_DIR . 'core.rm_auto.php');
+ return smarty_core_rm_auto($_params, $this);
+ }
+
+ /**
+ * Checks whether requested template exists.
+ *
+ * @param string $tpl_file
+ * @return boolean
+ */
+ function template_exists($tpl_file)
+ {
+ $_params = array('resource_name' => $tpl_file, 'quiet'=>true, 'get_source'=>false);
+ return $this->_fetch_resource_info($_params);
+ }
+
+ /**
+ * Returns an array containing template variables
+ *
+ * @param string $name
+ * @param string $type
+ * @return array
+ */
+ function &get_template_vars($name=null)
+ {
+ if(!isset($name)) {
+ return $this->_tpl_vars;
+ } elseif(isset($this->_tpl_vars[$name])) {
+ return $this->_tpl_vars[$name];
+ } else {
+ // var non-existant, return valid reference
+ $_tmp = null;
+ return $_tmp;
+ }
+ }
+
+ /**
+ * Returns an array containing config variables
+ *
+ * @param string $name
+ * @param string $type
+ * @return array
+ */
+ function &get_config_vars($name=null)
+ {
+ if(!isset($name) && is_array($this->_config[0])) {
+ return $this->_config[0]['vars'];
+ } else if(isset($this->_config[0]['vars'][$name])) {
+ return $this->_config[0]['vars'][$name];
+ } else {
+ // var non-existant, return valid reference
+ $_tmp = null;
+ return $_tmp;
+ }
+ }
+
+ /**
+ * trigger Smarty error
+ *
+ * @param string $error_msg
+ * @param integer $error_type
+ */
+ function trigger_error($error_msg, $error_type = E_USER_WARNING)
+ {
+ trigger_error("Smarty error: $error_msg", $error_type);
+ }
+
+
+ /**
+ * executes & displays the template results
+ *
+ * @param string $resource_name
+ * @param string $cache_id
+ * @param string $compile_id
+ */
+ function display($resource_name, $cache_id = null, $compile_id = null)
+ {
+ $this->fetch($resource_name, $cache_id, $compile_id, true);
+ }
+
+ /**
+ * executes & returns or displays the template results
+ *
+ * @param string $resource_name
+ * @param string $cache_id
+ * @param string $compile_id
+ * @param boolean $display
+ */
+ function fetch($resource_name, $cache_id = null, $compile_id = null, $display = false)
+ {
+ static $_cache_info = array();
+
+ $_smarty_old_error_level = $this->debugging ? error_reporting() : error_reporting(isset($this->error_reporting)
+ ? $this->error_reporting : error_reporting() & ~E_NOTICE);
+
+ if (!$this->debugging && $this->debugging_ctrl == 'URL') {
+ $_query_string = $this->request_use_auto_globals ? $_SERVER['QUERY_STRING'] : $GLOBALS['HTTP_SERVER_VARS']['QUERY_STRING'];
+ if (@strstr($_query_string, $this->_smarty_debug_id)) {
+ if (@strstr($_query_string, $this->_smarty_debug_id . '=on')) {
+ // enable debugging for this browser session
+ @setcookie('SMARTY_DEBUG', true);
+ $this->debugging = true;
+ } elseif (@strstr($_query_string, $this->_smarty_debug_id . '=off')) {
+ // disable debugging for this browser session
+ @setcookie('SMARTY_DEBUG', false);
+ $this->debugging = false;
+ } else {
+ // enable debugging for this page
+ $this->debugging = true;
+ }
+ } else {
+ $this->debugging = (bool)($this->request_use_auto_globals ? @$_COOKIE['SMARTY_DEBUG'] : @$GLOBALS['HTTP_COOKIE_VARS']['SMARTY_DEBUG']);
+ }
+ }
+
+ if ($this->debugging) {
+ // capture time for debugging info
+ $_params = array();
+ require_once(SMARTY_CORE_DIR . 'core.get_microtime.php');
+ $_debug_start_time = smarty_core_get_microtime($_params, $this);
+ $this->_smarty_debug_info[] = array('type' => 'template',
+ 'filename' => $resource_name,
+ 'depth' => 0);
+ $_included_tpls_idx = count($this->_smarty_debug_info) - 1;
+ }
+
+ if (!isset($compile_id)) {
+ $compile_id = $this->compile_id;
+ }
+
+ $this->_compile_id = $compile_id;
+ $this->_inclusion_depth = 0;
+
+ if ($this->caching) {
+ // save old cache_info, initialize cache_info
+ array_push($_cache_info, $this->_cache_info);
+ $this->_cache_info = array();
+ $_params = array(
+ 'tpl_file' => $resource_name,
+ 'cache_id' => $cache_id,
+ 'compile_id' => $compile_id,
+ 'results' => null
+ );
+ require_once(SMARTY_CORE_DIR . 'core.read_cache_file.php');
+ if (smarty_core_read_cache_file($_params, $this)) {
+ $_smarty_results = $_params['results'];
+ if (!empty($this->_cache_info['insert_tags'])) {
+ $_params = array('plugins' => $this->_cache_info['insert_tags']);
+ require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
+ smarty_core_load_plugins($_params, $this);
+ $_params = array('results' => $_smarty_results);
+ require_once(SMARTY_CORE_DIR . 'core.process_cached_inserts.php');
+ $_smarty_results = smarty_core_process_cached_inserts($_params, $this);
+ }
+ if (!empty($this->_cache_info['cache_serials'])) {
+ $_params = array('results' => $_smarty_results);
+ require_once(SMARTY_CORE_DIR . 'core.process_compiled_include.php');
+ $_smarty_results = smarty_core_process_compiled_include($_params, $this);
+ }
+
+
+ if ($display) {
+ if ($this->debugging)
+ {
+ // capture time for debugging info
+ $_params = array();
+ require_once(SMARTY_CORE_DIR . 'core.get_microtime.php');
+ $this->_smarty_debug_info[$_included_tpls_idx]['exec_time'] = smarty_core_get_microtime($_params, $this) - $_debug_start_time;
+ require_once(SMARTY_CORE_DIR . 'core.display_debug_console.php');
+ $_smarty_results .= smarty_core_display_debug_console($_params, $this);
+ }
+ if ($this->cache_modified_check) {
+ $_server_vars = ($this->request_use_auto_globals) ? $_SERVER : $GLOBALS['HTTP_SERVER_VARS'];
+ $_last_modified_date = @substr($_server_vars['HTTP_IF_MODIFIED_SINCE'], 0, strpos($_server_vars['HTTP_IF_MODIFIED_SINCE'], 'GMT') + 3);
+ $_gmt_mtime = gmdate('D, d M Y H:i:s', $this->_cache_info['timestamp']).' GMT';
+ if (@count($this->_cache_info['insert_tags']) == 0
+ && !$this->_cache_serials
+ && $_gmt_mtime == $_last_modified_date) {
+ if (php_sapi_name()=='cgi')
+ header('Status: 304 Not Modified');
+ else
+ header('HTTP/1.1 304 Not Modified');
+
+ } else {
+ header('Last-Modified: '.$_gmt_mtime);
+ echo $_smarty_results;
+ }
+ } else {
+ echo $_smarty_results;
+ }
+ error_reporting($_smarty_old_error_level);
+ // restore initial cache_info
+ $this->_cache_info = array_pop($_cache_info);
+ return true;
+ } else {
+ error_reporting($_smarty_old_error_level);
+ // restore initial cache_info
+ $this->_cache_info = array_pop($_cache_info);
+ return $_smarty_results;
+ }
+ } else {
+ $this->_cache_info['template'][$resource_name] = true;
+ if ($this->cache_modified_check && $display) {
+ header('Last-Modified: '.gmdate('D, d M Y H:i:s', time()).' GMT');
+ }
+ }
+ }
+
+ // load filters that are marked as autoload
+ if (count($this->autoload_filters)) {
+ foreach ($this->autoload_filters as $_filter_type => $_filters) {
+ foreach ($_filters as $_filter) {
+ $this->load_filter($_filter_type, $_filter);
+ }
+ }
+ }
+
+ $_smarty_compile_path = $this->_get_compile_path($resource_name);
+
+ // if we just need to display the results, don't perform output
+ // buffering - for speed
+ $_cache_including = $this->_cache_including;
+ $this->_cache_including = false;
+ if ($display && !$this->caching && count($this->_plugins['outputfilter']) == 0) {
+ if ($this->_is_compiled($resource_name, $_smarty_compile_path)
+ || $this->_compile_resource($resource_name, $_smarty_compile_path))
+ {
+ include($_smarty_compile_path);
+ }
+ } else {
+ ob_start();
+ if ($this->_is_compiled($resource_name, $_smarty_compile_path)
+ || $this->_compile_resource($resource_name, $_smarty_compile_path))
+ {
+ include($_smarty_compile_path);
+ }
+ $_smarty_results = ob_get_contents();
+ ob_end_clean();
+
+ foreach ((array)$this->_plugins['outputfilter'] as $_output_filter) {
+ $_smarty_results = call_user_func_array($_output_filter[0], array($_smarty_results, &$this));
+ }
+ }
+
+ if ($this->caching) {
+ $_params = array('tpl_file' => $resource_name,
+ 'cache_id' => $cache_id,
+ 'compile_id' => $compile_id,
+ 'results' => $_smarty_results);
+ require_once(SMARTY_CORE_DIR . 'core.write_cache_file.php');
+ smarty_core_write_cache_file($_params, $this);
+ require_once(SMARTY_CORE_DIR . 'core.process_cached_inserts.php');
+ $_smarty_results = smarty_core_process_cached_inserts($_params, $this);
+
+ if ($this->_cache_serials) {
+ // strip nocache-tags from output
+ $_smarty_results = preg_replace('!(\{/?nocache\:[0-9a-f]{32}#\d+\})!s'
+ ,''
+ ,$_smarty_results);
+ }
+ // restore initial cache_info
+ $this->_cache_info = array_pop($_cache_info);
+ }
+ $this->_cache_including = $_cache_including;
+
+ if ($display) {
+ if (isset($_smarty_results)) { echo $_smarty_results; }
+ }
+
+ if ($this->debugging) {
+ // capture time for debugging info
+ $_params = array();
+ require_once(SMARTY_CORE_DIR . 'core.get_microtime.php');
+ $this->_smarty_debug_info[$_included_tpls_idx]['exec_time'] = (smarty_core_get_microtime($_params, $this) - $_debug_start_time);
+ }
+
+ if ($display) {
+ if ($this->debugging) {
+ // capture time for debugging info
+ $_params = array();
+ require_once(SMARTY_CORE_DIR . 'core.display_debug_console.php');
+ echo smarty_core_display_debug_console($_params, $this);
+ }
+ error_reporting($_smarty_old_error_level);
+ return;
+ } else {
+ error_reporting($_smarty_old_error_level);
+ if (isset($_smarty_results)) { return $_smarty_results; }
+ }
+ }
+
+ /**
+ * load configuration values
+ *
+ * @param string $file
+ * @param string $section
+ * @param string $scope
+ */
+ function config_load($file, $section = null, $scope = 'global')
+ {
+ require_once($this->_get_plugin_filepath('function', 'config_load'));
+ smarty_function_config_load(array('file' => $file, 'section' => $section, 'scope' => $scope), $this);
+ }
+
+ /**
+ * return a reference to a registered object
+ *
+ * @param string $name
+ * @return object
+ */
+ function &get_registered_object($name) {
+ if (!isset($this->_reg_objects[$name]))
+ $this->_trigger_fatal_error("'$name' is not a registered object");
+
+ if (!is_object($this->_reg_objects[$name][0]))
+ $this->_trigger_fatal_error("registered '$name' is not an object");
+
+ return $this->_reg_objects[$name][0];
+ }
+
+ /**
+ * clear configuration values
+ *
+ * @param string $var
+ */
+ function clear_config($var = null)
+ {
+ if(!isset($var)) {
+ // clear all values
+ $this->_config = array(array('vars' => array(),
+ 'files' => array()));
+ } else {
+ unset($this->_config[0]['vars'][$var]);
+ }
+ }
+
+ /**
+ * get filepath of requested plugin
+ *
+ * @param string $type
+ * @param string $name
+ * @return string|false
+ */
+ function _get_plugin_filepath($type, $name)
+ {
+ $_params = array('type' => $type, 'name' => $name);
+ require_once(SMARTY_CORE_DIR . 'core.assemble_plugin_filepath.php');
+ return smarty_core_assemble_plugin_filepath($_params, $this);
+ }
+
+ /**
+ * test if resource needs compiling
+ *
+ * @param string $resource_name
+ * @param string $compile_path
+ * @return boolean
+ */
+ function _is_compiled($resource_name, $compile_path)
+ {
+ if (!$this->force_compile && file_exists($compile_path)) {
+ if (!$this->compile_check) {
+ // no need to check compiled file
+ return true;
+ } else {
+ // get file source and timestamp
+ $_params = array('resource_name' => $resource_name, 'get_source'=>false);
+ if (!$this->_fetch_resource_info($_params)) {
+ return false;
+ }
+ if ($_params['resource_timestamp'] <= filemtime($compile_path)) {
+ // template not expired, no recompile
+ return true;
+ } else {
+ // compile template
+ return false;
+ }
+ }
+ } else {
+ // compiled template does not exist, or forced compile
+ return false;
+ }
+ }
+
+ /**
+ * compile the template
+ *
+ * @param string $resource_name
+ * @param string $compile_path
+ * @return boolean
+ */
+ function _compile_resource($resource_name, $compile_path)
+ {
+
+ $_params = array('resource_name' => $resource_name);
+ if (!$this->_fetch_resource_info($_params)) {
+ return false;
+ }
+
+ $_source_content = $_params['source_content'];
+ $_cache_include = substr($compile_path, 0, -4).'.inc';
+
+ if ($this->_compile_source($resource_name, $_source_content, $_compiled_content, $_cache_include)) {
+ // if a _cache_serial was set, we also have to write an include-file:
+ if ($this->_cache_include_info) {
+ require_once(SMARTY_CORE_DIR . 'core.write_compiled_include.php');
+ smarty_core_write_compiled_include(array_merge($this->_cache_include_info, array('compiled_content'=>$_compiled_content, 'resource_name'=>$resource_name)), $this);
+ }
+
+ $_params = array('compile_path'=>$compile_path, 'compiled_content' => $_compiled_content);
+ require_once(SMARTY_CORE_DIR . 'core.write_compiled_resource.php');
+ smarty_core_write_compiled_resource($_params, $this);
+
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+
+ /**
+ * compile the given source
+ *
+ * @param string $resource_name
+ * @param string $source_content
+ * @param string $compiled_content
+ * @return boolean
+ */
+ function _compile_source($resource_name, &$source_content, &$compiled_content, $cache_include_path=null)
+ {
+ if (file_exists(SMARTY_DIR . $this->compiler_file)) {
+ require_once(SMARTY_DIR . $this->compiler_file);
+ } else {
+ // use include_path
+ require_once($this->compiler_file);
+ }
+
+
+ $smarty_compiler = new $this->compiler_class;
+
+ $smarty_compiler->template_dir = $this->template_dir;
+ $smarty_compiler->compile_dir = $this->compile_dir;
+ $smarty_compiler->plugins_dir = $this->plugins_dir;
+ $smarty_compiler->config_dir = $this->config_dir;
+ $smarty_compiler->force_compile = $this->force_compile;
+ $smarty_compiler->caching = $this->caching;
+ $smarty_compiler->php_handling = $this->php_handling;
+ $smarty_compiler->left_delimiter = $this->left_delimiter;
+ $smarty_compiler->right_delimiter = $this->right_delimiter;
+ $smarty_compiler->_version = $this->_version;
+ $smarty_compiler->security = $this->security;
+ $smarty_compiler->secure_dir = $this->secure_dir;
+ $smarty_compiler->security_settings = $this->security_settings;
+ $smarty_compiler->trusted_dir = $this->trusted_dir;
+ $smarty_compiler->use_sub_dirs = $this->use_sub_dirs;
+ $smarty_compiler->_reg_objects = &$this->_reg_objects;
+ $smarty_compiler->_plugins = &$this->_plugins;
+ $smarty_compiler->_tpl_vars = &$this->_tpl_vars;
+ $smarty_compiler->default_modifiers = $this->default_modifiers;
+ $smarty_compiler->compile_id = $this->_compile_id;
+ $smarty_compiler->_config = $this->_config;
+ $smarty_compiler->request_use_auto_globals = $this->request_use_auto_globals;
+
+ if (isset($cache_include_path) && isset($this->_cache_serials[$cache_include_path])) {
+ $smarty_compiler->_cache_serial = $this->_cache_serials[$cache_include_path];
+ }
+ $smarty_compiler->_cache_include = $cache_include_path;
+
+
+ $_results = $smarty_compiler->_compile_file($resource_name, $source_content, $compiled_content);
+
+ if ($smarty_compiler->_cache_serial) {
+ $this->_cache_include_info = array(
+ 'cache_serial'=>$smarty_compiler->_cache_serial
+ ,'plugins_code'=>$smarty_compiler->_plugins_code
+ ,'include_file_path' => $cache_include_path);
+
+ } else {
+ $this->_cache_include_info = null;
+
+ }
+
+ return $_results;
+ }
+
+ /**
+ * Get the compile path for this resource
+ *
+ * @param string $resource_name
+ * @return string results of {@link _get_auto_filename()}
+ */
+ function _get_compile_path($resource_name)
+ {
+ return $this->_get_auto_filename($this->compile_dir, $resource_name,
+ $this->_compile_id) . '.php';
+ }
+
+ /**
+ * fetch the template info. Gets timestamp, and source
+ * if get_source is true
+ *
+ * sets $source_content to the source of the template, and
+ * $resource_timestamp to its time stamp
+ * @param string $resource_name
+ * @param string $source_content
+ * @param integer $resource_timestamp
+ * @param boolean $get_source
+ * @param boolean $quiet
+ * @return boolean
+ */
+
+ function _fetch_resource_info(&$params)
+ {
+ if(!isset($params['get_source'])) { $params['get_source'] = true; }
+ if(!isset($params['quiet'])) { $params['quiet'] = false; }
+
+ $_return = false;
+ $_params = array('resource_name' => $params['resource_name']) ;
+ if (isset($params['resource_base_path']))
+ $_params['resource_base_path'] = $params['resource_base_path'];
+ else
+ $_params['resource_base_path'] = $this->template_dir;
+
+ if ($this->_parse_resource_name($_params)) {
+ $_resource_type = $_params['resource_type'];
+ $_resource_name = $_params['resource_name'];
+ switch ($_resource_type) {
+ case 'file':
+ if ($params['get_source']) {
+ $params['source_content'] = $this->_read_file($_resource_name);
+ }
+ $params['resource_timestamp'] = filemtime($_resource_name);
+ $_return = is_file($_resource_name);
+ break;
+
+ default:
+ // call resource functions to fetch the template source and timestamp
+ if ($params['get_source']) {
+ $_source_return = isset($this->_plugins['resource'][$_resource_type]) &&
+ call_user_func_array($this->_plugins['resource'][$_resource_type][0][0],
+ array($_resource_name, &$params['source_content'], &$this));
+ } else {
+ $_source_return = true;
+ }
+
+ $_timestamp_return = isset($this->_plugins['resource'][$_resource_type]) &&
+ call_user_func_array($this->_plugins['resource'][$_resource_type][0][1],
+ array($_resource_name, &$params['resource_timestamp'], &$this));
+
+ $_return = $_source_return && $_timestamp_return;
+ break;
+ }
+ }
+
+ if (!$_return) {
+ // see if we can get a template with the default template handler
+ if (!empty($this->default_template_handler_func)) {
+ if (!is_callable($this->default_template_handler_func)) {
+ $this->trigger_error("default template handler function \"$this->default_template_handler_func\" doesn't exist.");
+ } else {
+ $_return = call_user_func_array(
+ $this->default_template_handler_func,
+ array($_params['resource_type'], $_params['resource_name'], &$params['source_content'], &$params['resource_timestamp'], &$this));
+ }
+ }
+ }
+
+ if (!$_return) {
+ if (!$params['quiet']) {
+ $this->trigger_error('unable to read resource: "' . $params['resource_name'] . '"');
+ }
+ } else if ($_return && $this->security) {
+ require_once(SMARTY_CORE_DIR . 'core.is_secure.php');
+ if (!smarty_core_is_secure($_params, $this)) {
+ if (!$params['quiet'])
+ $this->trigger_error('(secure mode) accessing "' . $params['resource_name'] . '" is not allowed');
+ $params['source_content'] = null;
+ $params['resource_timestamp'] = null;
+ return false;
+ }
+ }
+ return $_return;
+ }
+
+
+ /**
+ * parse out the type and name from the resource
+ *
+ * @param string $resource_base_path
+ * @param string $resource_name
+ * @param string $resource_type
+ * @param string $resource_name
+ * @return boolean
+ */
+
+ function _parse_resource_name(&$params)
+ {
+
+ // split tpl_path by the first colon
+ $_resource_name_parts = explode(':', $params['resource_name'], 2);
+
+ if (count($_resource_name_parts) == 1) {
+ // no resource type given
+ $params['resource_type'] = $this->default_resource_type;
+ $params['resource_name'] = $_resource_name_parts[0];
+ } else {
+ if(strlen($_resource_name_parts[0]) == 1) {
+ // 1 char is not resource type, but part of filepath
+ $params['resource_type'] = $this->default_resource_type;
+ $params['resource_name'] = $params['resource_name'];
+ } else {
+ $params['resource_type'] = $_resource_name_parts[0];
+ $params['resource_name'] = $_resource_name_parts[1];
+ }
+ }
+
+ if ($params['resource_type'] == 'file') {
+ if (!preg_match('/^([\/\\\\]|[a-zA-Z]:[\/\\\\])/', $params['resource_name'])) {
+ // relative pathname to $params['resource_base_path']
+ // use the first directory where the file is found
+ foreach ((array)$params['resource_base_path'] as $_curr_path) {
+ $_fullpath = $_curr_path . DIRECTORY_SEPARATOR . $params['resource_name'];
+ if (file_exists($_fullpath) && is_file($_fullpath)) {
+ $params['resource_name'] = $_fullpath;
+ return true;
+ }
+ // didn't find the file, try include_path
+ $_params = array('file_path' => $_fullpath);
+ require_once(SMARTY_CORE_DIR . 'core.get_include_path.php');
+ if(smarty_core_get_include_path($_params, $this)) {
+ $params['resource_name'] = $_params['new_file_path'];
+ return true;
+ }
+ }
+ return false;
+ } else {
+ /* absolute path */
+ return file_exists($params['resource_name']);
+ }
+ } elseif (empty($this->_plugins['resource'][$params['resource_type']])) {
+ $_params = array('type' => $params['resource_type']);
+ require_once(SMARTY_CORE_DIR . 'core.load_resource_plugin.php');
+ smarty_core_load_resource_plugin($_params, $this);
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Handle modifiers
+ *
+ * @param string|null $modifier_name
+ * @param array|null $map_array
+ * @return string result of modifiers
+ */
+ function _run_mod_handler()
+ {
+ $_args = func_get_args();
+ list($_modifier_name, $_map_array) = array_splice($_args, 0, 2);
+ list($_func_name, $_tpl_file, $_tpl_line) =
+ $this->_plugins['modifier'][$_modifier_name];
+
+ $_var = $_args[0];
+ foreach ($_var as $_key => $_val) {
+ $_args[0] = $_val;
+ $_var[$_key] = call_user_func_array($_func_name, $_args);
+ }
+ return $_var;
+ }
+
+ /**
+ * Remove starting and ending quotes from the string
+ *
+ * @param string $string
+ * @return string
+ */
+ function _dequote($string)
+ {
+ if ((substr($string, 0, 1) == "'" || substr($string, 0, 1) == '"') &&
+ substr($string, -1) == substr($string, 0, 1))
+ return substr($string, 1, -1);
+ else
+ return $string;
+ }
+
+
+ /**
+ * read in a file
+ *
+ * @param string $filename
+ * @return string
+ */
+ function _read_file($filename)
+ {
+ if ( file_exists($filename) && ($fd = @fopen($filename, 'rb')) ) {
+ $contents = '';
+ while (!feof($fd)) {
+ $contents .= fread($fd, 8192);
+ }
+ fclose($fd);
+ return $contents;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * get a concrete filename for automagically created content
+ *
+ * @param string $auto_base
+ * @param string $auto_source
+ * @param string $auto_id
+ * @return string
+ * @staticvar string|null
+ * @staticvar string|null
+ */
+ function _get_auto_filename($auto_base, $auto_source = null, $auto_id = null)
+ {
+ $_compile_dir_sep = $this->use_sub_dirs ? DIRECTORY_SEPARATOR : '^';
+ $_return = $auto_base . DIRECTORY_SEPARATOR;
+
+ if(isset($auto_id)) {
+ // make auto_id safe for directory names
+ $auto_id = str_replace('%7C',$_compile_dir_sep,(urlencode($auto_id)));
+ // split into separate directories
+ $_return .= $auto_id . $_compile_dir_sep;
+ }
+
+ if(isset($auto_source)) {
+ // make source name safe for filename
+ $_filename = urlencode(basename($auto_source));
+ $_crc32 = sprintf('%08X', crc32($auto_source));
+ // prepend %% to avoid name conflicts with
+ // with $params['auto_id'] names
+ $_crc32 = substr($_crc32, 0, 2) . $_compile_dir_sep .
+ substr($_crc32, 0, 3) . $_compile_dir_sep . $_crc32;
+ $_return .= '%%' . $_crc32 . '%%' . $_filename;
+ }
+
+ return $_return;
+ }
+
+ /**
+ * unlink a file, possibly using expiration time
+ *
+ * @param string $resource
+ * @param integer $exp_time
+ */
+ function _unlink($resource, $exp_time = null)
+ {
+ if(isset($exp_time)) {
+ if(time() - @filemtime($resource) >= $exp_time) {
+ return @unlink($resource);
+ }
+ } else {
+ return @unlink($resource);
+ }
+ }
+
+ /**
+ * returns an auto_id for auto-file-functions
+ *
+ * @param string $cache_id
+ * @param string $compile_id
+ * @return string|null
+ */
+ function _get_auto_id($cache_id=null, $compile_id=null) {
+ if (isset($cache_id))
+ return (isset($compile_id)) ? $cache_id . '|' . $compile_id : $cache_id;
+ elseif(isset($compile_id))
+ return $compile_id;
+ else
+ return null;
+ }
+
+ /**
+ * trigger Smarty plugin error
+ *
+ * @param string $error_msg
+ * @param string $tpl_file
+ * @param integer $tpl_line
+ * @param string $file
+ * @param integer $line
+ * @param integer $error_type
+ */
+ function _trigger_fatal_error($error_msg, $tpl_file = null, $tpl_line = null,
+ $file = null, $line = null, $error_type = E_USER_ERROR)
+ {
+ if(isset($file) && isset($line)) {
+ $info = ' ('.basename($file).", line $line)";
+ } else {
+ $info = '';
+ }
+ if (isset($tpl_line) && isset($tpl_file)) {
+ $this->trigger_error('[in ' . $tpl_file . ' line ' . $tpl_line . "]: $error_msg$info", $error_type);
+ } else {
+ $this->trigger_error($error_msg . $info, $error_type);
+ }
+ }
+
+
+ /**
+ * callback function for preg_replace, to call a non-cacheable block
+ * @return string
+ */
+ function _process_compiled_include_callback($match) {
+ $_func = '_smarty_tplfunc_'.$match[2].'_'.$match[3];
+ ob_start();
+ $_func($this);
+ $_ret = ob_get_contents();
+ ob_end_clean();
+ return $_ret;
+ }
+
+
+ /**
+ * called for included templates
+ *
+ * @param string $_smarty_include_tpl_file
+ * @param string $_smarty_include_vars
+ */
+
+ // $_smarty_include_tpl_file, $_smarty_include_vars
+
+ function _smarty_include($params)
+ {
+ if ($this->debugging) {
+ $_params = array();
+ require_once(SMARTY_CORE_DIR . 'core.get_microtime.php');
+ $debug_start_time = smarty_core_get_microtime($_params, $this);
+ $this->_smarty_debug_info[] = array('type' => 'template',
+ 'filename' => $params['smarty_include_tpl_file'],
+ 'depth' => ++$this->_inclusion_depth);
+ $included_tpls_idx = count($this->_smarty_debug_info) - 1;
+ }
+
+ $this->_tpl_vars = array_merge($this->_tpl_vars, $params['smarty_include_vars']);
+
+ // config vars are treated as local, so push a copy of the
+ // current ones onto the front of the stack
+ array_unshift($this->_config, $this->_config[0]);
+
+ $_smarty_compile_path = $this->_get_compile_path($params['smarty_include_tpl_file']);
+
+
+ if ($this->_is_compiled($params['smarty_include_tpl_file'], $_smarty_compile_path)
+ || $this->_compile_resource($params['smarty_include_tpl_file'], $_smarty_compile_path))
+ {
+ include($_smarty_compile_path);
+ }
+
+ // pop the local vars off the front of the stack
+ array_shift($this->_config);
+
+ $this->_inclusion_depth--;
+
+ if ($this->debugging) {
+ // capture time for debugging info
+ $_params = array();
+ require_once(SMARTY_CORE_DIR . 'core.get_microtime.php');
+ $this->_smarty_debug_info[$included_tpls_idx]['exec_time'] = smarty_core_get_microtime($_params, $this) - $debug_start_time;
+ }
+
+ if ($this->caching) {
+ $this->_cache_info['template'][$params['smarty_include_tpl_file']] = true;
+ }
+ }
+
+
+ /**
+ * get or set an array of cached attributes for function that is
+ * not cacheable
+ * @return array
+ */
+ function &_smarty_cache_attrs($cache_serial, $count) {
+ $_cache_attrs =& $this->_cache_info['cache_attrs'][$cache_serial][$count];
+
+ if ($this->_cache_including) {
+ /* return next set of cache_attrs */
+ $_return = current($_cache_attrs);
+ next($_cache_attrs);
+ return $_return;
+
+ } else {
+ /* add a reference to a new set of cache_attrs */
+ $_cache_attrs[] = array();
+ return $_cache_attrs[count($_cache_attrs)-1];
+
+ }
+
+ }
+
+
+ /**
+ * wrapper for include() retaining $this
+ * @return mixed
+ */
+ function _include($filename, $once=false, $params=null)
+ {
+ if ($once) {
+ return include_once($filename);
+ } else {
+ return include($filename);
+ }
+ }
+
+
+ /**
+ * wrapper for eval() retaining $this
+ * @return mixed
+ */
+ function _eval($code, $params=null)
+ {
+ return eval($code);
+ }
+
+ /**
+ * Extracts the filter name from the given callback
+ *
+ * @param callback $function
+ * @return string
+ */
+ function _get_filter_name($function)
+ {
+ if (is_array($function)) {
+ $_class_name = (is_object($function[0]) ?
+ get_class($function[0]) : $function[0]);
+ return $_class_name . '_' . $function[1];
+ }
+ else {
+ return $function;
+ }
+ }
+
+ /**#@-*/
+
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/Smarty_Compiler.class.php b/BSF/include/smarty/libs/Smarty_Compiler.class.php
new file mode 100644
index 000000000..d5f877f7b
--- /dev/null
+++ b/BSF/include/smarty/libs/Smarty_Compiler.class.php
@@ -0,0 +1,2325 @@
+
+ * @author Andrei Zmievski
+ * @version 2.6.19
+ * @copyright 2001-2005 New Digital Group, Inc.
+ * @package Smarty
+ */
+
+/* $Id$ */
+
+/**
+ * Template compiling class
+ * @package Smarty
+ */
+class Smarty_Compiler extends Smarty {
+
+ // internal vars
+ /**#@+
+ * @access private
+ */
+ var $_folded_blocks = array(); // keeps folded template blocks
+ var $_current_file = null; // the current template being compiled
+ var $_current_line_no = 1; // line number for error messages
+ var $_capture_stack = array(); // keeps track of nested capture buffers
+ var $_plugin_info = array(); // keeps track of plugins to load
+ var $_init_smarty_vars = false;
+ var $_permitted_tokens = array('true','false','yes','no','on','off','null');
+ var $_db_qstr_regexp = null; // regexps are setup in the constructor
+ var $_si_qstr_regexp = null;
+ var $_qstr_regexp = null;
+ var $_func_regexp = null;
+ var $_reg_obj_regexp = null;
+ var $_var_bracket_regexp = null;
+ var $_num_const_regexp = null;
+ var $_dvar_guts_regexp = null;
+ var $_dvar_regexp = null;
+ var $_cvar_regexp = null;
+ var $_svar_regexp = null;
+ var $_avar_regexp = null;
+ var $_mod_regexp = null;
+ var $_var_regexp = null;
+ var $_parenth_param_regexp = null;
+ var $_func_call_regexp = null;
+ var $_obj_ext_regexp = null;
+ var $_obj_start_regexp = null;
+ var $_obj_params_regexp = null;
+ var $_obj_call_regexp = null;
+ var $_cacheable_state = 0;
+ var $_cache_attrs_count = 0;
+ var $_nocache_count = 0;
+ var $_cache_serial = null;
+ var $_cache_include = null;
+
+ var $_strip_depth = 0;
+ var $_additional_newline = "\n";
+
+ /**#@-*/
+ /**
+ * The class constructor.
+ */
+ function Smarty_Compiler()
+ {
+ // matches double quoted strings:
+ // "foobar"
+ // "foo\"bar"
+ $this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
+
+ // matches single quoted strings:
+ // 'foobar'
+ // 'foo\'bar'
+ $this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
+
+ // matches single or double quoted strings
+ $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';
+
+ // matches bracket portion of vars
+ // [0]
+ // [foo]
+ // [$bar]
+ $this->_var_bracket_regexp = '\[\$?[\w\.]+\]';
+
+ // matches numerical constants
+ // 30
+ // -12
+ // 13.22
+ $this->_num_const_regexp = '(?:\-?\d+(?:\.\d+)?)';
+
+ // matches $ vars (not objects):
+ // $foo
+ // $foo.bar
+ // $foo.bar.foobar
+ // $foo[0]
+ // $foo[$bar]
+ // $foo[5][blah]
+ // $foo[5].bar[$foobar][4]
+ $this->_dvar_math_regexp = '(?:[\+\*\/\%]|(?:-(?!>)))';
+ $this->_dvar_math_var_regexp = '[\$\w\.\+\-\*\/\%\d\>\[\]]';
+ $this->_dvar_guts_regexp = '\w+(?:' . $this->_var_bracket_regexp
+ . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?';
+ $this->_dvar_regexp = '\$' . $this->_dvar_guts_regexp;
+
+ // matches config vars:
+ // #foo#
+ // #foobar123_foo#
+ $this->_cvar_regexp = '\#\w+\#';
+
+ // matches section vars:
+ // %foo.bar%
+ $this->_svar_regexp = '\%\w+\.\w+\%';
+
+ // matches all valid variables (no quotes, no modifiers)
+ $this->_avar_regexp = '(?:' . $this->_dvar_regexp . '|'
+ . $this->_cvar_regexp . '|' . $this->_svar_regexp . ')';
+
+ // matches valid variable syntax:
+ // $foo
+ // $foo
+ // #foo#
+ // #foo#
+ // "text"
+ // "text"
+ $this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')';
+
+ // matches valid object call (one level of object nesting allowed in parameters):
+ // $foo->bar
+ // $foo->bar()
+ // $foo->bar("text")
+ // $foo->bar($foo, $bar, "text")
+ // $foo->bar($foo, "foo")
+ // $foo->bar->foo()
+ // $foo->bar->foo->bar()
+ // $foo->bar($foo->bar)
+ // $foo->bar($foo->bar())
+ // $foo->bar($foo->bar($blah,$foo,44,"foo",$foo[0].bar))
+ $this->_obj_ext_regexp = '\->(?:\$?' . $this->_dvar_guts_regexp . ')';
+ $this->_obj_restricted_param_regexp = '(?:'
+ . '(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')(?:' . $this->_obj_ext_regexp . '(?:\((?:(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')'
+ . '(?:\s*,\s*(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . '))*)?\))?)*)';
+ $this->_obj_single_param_regexp = '(?:\w+|' . $this->_obj_restricted_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
+ . $this->_var_regexp . $this->_obj_restricted_param_regexp . ')))*)';
+ $this->_obj_params_regexp = '\((?:' . $this->_obj_single_param_regexp
+ . '(?:\s*,\s*' . $this->_obj_single_param_regexp . ')*)?\)';
+ $this->_obj_start_regexp = '(?:' . $this->_dvar_regexp . '(?:' . $this->_obj_ext_regexp . ')+)';
+ $this->_obj_call_regexp = '(?:' . $this->_obj_start_regexp . '(?:' . $this->_obj_params_regexp . ')?(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?)';
+
+ // matches valid modifier syntax:
+ // |foo
+ // |@foo
+ // |foo:"bar"
+ // |foo:$bar
+ // |foo:"bar":$foobar
+ // |foo|bar
+ // |foo:$foo->bar
+ $this->_mod_regexp = '(?:\|@?\w+(?::(?:\w+|' . $this->_num_const_regexp . '|'
+ . $this->_obj_call_regexp . '|' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'))*)';
+
+ // matches valid function name:
+ // foo123
+ // _foo_bar
+ $this->_func_regexp = '[a-zA-Z_]\w*';
+
+ // matches valid registered object:
+ // foo->bar
+ $this->_reg_obj_regexp = '[a-zA-Z_]\w*->[a-zA-Z_]\w*';
+
+ // matches valid parameter values:
+ // true
+ // $foo
+ // $foo|bar
+ // #foo#
+ // #foo#|bar
+ // "text"
+ // "text"|bar
+ // $foo->bar
+ $this->_param_regexp = '(?:\s*(?:' . $this->_obj_call_regexp . '|'
+ . $this->_var_regexp . '|' . $this->_num_const_regexp . '|\w+)(?>' . $this->_mod_regexp . '*)\s*)';
+
+ // matches valid parenthesised function parameters:
+ //
+ // "text"
+ // $foo, $bar, "text"
+ // $foo|bar, "foo"|bar, $foo->bar($foo)|bar
+ $this->_parenth_param_regexp = '(?:\((?:\w+|'
+ . $this->_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
+ . $this->_param_regexp . ')))*)?\))';
+
+ // matches valid function call:
+ // foo()
+ // foo_bar($foo)
+ // _foo_bar($foo,"bar")
+ // foo123($foo,$foo->bar(),"foo")
+ $this->_func_call_regexp = '(?:' . $this->_func_regexp . '\s*(?:'
+ . $this->_parenth_param_regexp . '))';
+ }
+
+ /**
+ * compile a resource
+ *
+ * sets $compiled_content to the compiled source
+ * @param string $resource_name
+ * @param string $source_content
+ * @param string $compiled_content
+ * @return true
+ */
+ function _compile_file($resource_name, $source_content, &$compiled_content)
+ {
+
+ if ($this->security) {
+ // do not allow php syntax to be executed unless specified
+ if ($this->php_handling == SMARTY_PHP_ALLOW &&
+ !$this->security_settings['PHP_HANDLING']) {
+ $this->php_handling = SMARTY_PHP_PASSTHRU;
+ }
+ }
+
+ $this->_load_filters();
+
+ $this->_current_file = $resource_name;
+ $this->_current_line_no = 1;
+ $ldq = preg_quote($this->left_delimiter, '~');
+ $rdq = preg_quote($this->right_delimiter, '~');
+
+ // run template source through prefilter functions
+ if (count($this->_plugins['prefilter']) > 0) {
+ foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
+ if ($prefilter === false) continue;
+ if ($prefilter[3] || is_callable($prefilter[0])) {
+ $source_content = call_user_func_array($prefilter[0],
+ array($source_content, &$this));
+ $this->_plugins['prefilter'][$filter_name][3] = true;
+ } else {
+ $this->_trigger_fatal_error("[plugin] prefilter '$filter_name' is not implemented");
+ }
+ }
+ }
+
+ /* fetch all special blocks */
+ $search = "~{$ldq}\*(.*?)\*{$rdq}|{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}|{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}~s";
+
+ preg_match_all($search, $source_content, $match, PREG_SET_ORDER);
+ $this->_folded_blocks = $match;
+ reset($this->_folded_blocks);
+
+ /* replace special blocks by "{php}" */
+ $source_content = preg_replace($search.'e', "'"
+ . $this->_quote_replace($this->left_delimiter) . 'php'
+ . "' . str_repeat(\"\n\", substr_count('\\0', \"\n\")) .'"
+ . $this->_quote_replace($this->right_delimiter)
+ . "'"
+ , $source_content);
+
+ /* Gather all template tags. */
+ preg_match_all("~{$ldq}\s*(.*?)\s*{$rdq}~s", $source_content, $_match);
+ $template_tags = $_match[1];
+ /* Split content by template tags to obtain non-template content. */
+ $text_blocks = preg_split("~{$ldq}.*?{$rdq}~s", $source_content);
+
+ /* loop through text blocks */
+ for ($curr_tb = 0, $for_max = count($text_blocks); $curr_tb < $for_max; $curr_tb++) {
+ /* match anything resembling php tags */
+ if (preg_match_all('~(<\?(?:\w+|=)?|\?>|language\s*=\s*[\"\']?\s*php\s*[\"\']?)~is', $text_blocks[$curr_tb], $sp_match)) {
+ /* replace tags with placeholders to prevent recursive replacements */
+ $sp_match[1] = array_unique($sp_match[1]);
+ usort($sp_match[1], '_smarty_sort_length');
+ for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
+ $text_blocks[$curr_tb] = str_replace($sp_match[1][$curr_sp],'%%%SMARTYSP'.$curr_sp.'%%%',$text_blocks[$curr_tb]);
+ }
+ /* process each one */
+ for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
+ if ($this->php_handling == SMARTY_PHP_PASSTHRU) {
+ /* echo php contents */
+ $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', ''."\n", $text_blocks[$curr_tb]);
+ } else if ($this->php_handling == SMARTY_PHP_QUOTE) {
+ /* quote php tags */
+ $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', htmlspecialchars($sp_match[1][$curr_sp]), $text_blocks[$curr_tb]);
+ } else if ($this->php_handling == SMARTY_PHP_REMOVE) {
+ /* remove php tags */
+ $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '', $text_blocks[$curr_tb]);
+ } else {
+ /* SMARTY_PHP_ALLOW, but echo non php starting tags */
+ $sp_match[1][$curr_sp] = preg_replace('~(<\?(?!php|=|$))~i', ''."\n", $sp_match[1][$curr_sp]);
+ $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', $sp_match[1][$curr_sp], $text_blocks[$curr_tb]);
+ }
+ }
+ }
+ }
+
+ /* Compile the template tags into PHP code. */
+ $compiled_tags = array();
+ for ($i = 0, $for_max = count($template_tags); $i < $for_max; $i++) {
+ $this->_current_line_no += substr_count($text_blocks[$i], "\n");
+ $compiled_tags[] = $this->_compile_tag($template_tags[$i]);
+ $this->_current_line_no += substr_count($template_tags[$i], "\n");
+ }
+ if (count($this->_tag_stack)>0) {
+ list($_open_tag, $_line_no) = end($this->_tag_stack);
+ $this->_syntax_error("unclosed tag \{$_open_tag} (opened line $_line_no).", E_USER_ERROR, __FILE__, __LINE__);
+ return;
+ }
+
+ /* Reformat $text_blocks between 'strip' and '/strip' tags,
+ removing spaces, tabs and newlines. */
+ $strip = false;
+ for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
+ if ($compiled_tags[$i] == '{strip}') {
+ $compiled_tags[$i] = '';
+ $strip = true;
+ /* remove leading whitespaces */
+ $text_blocks[$i + 1] = ltrim($text_blocks[$i + 1]);
+ }
+ if ($strip) {
+ /* strip all $text_blocks before the next '/strip' */
+ for ($j = $i + 1; $j < $for_max; $j++) {
+ /* remove leading and trailing whitespaces of each line */
+ $text_blocks[$j] = preg_replace('![\t ]*[\r\n]+[\t ]*!', '', $text_blocks[$j]);
+ if ($compiled_tags[$j] == '{/strip}') {
+ /* remove trailing whitespaces from the last text_block */
+ $text_blocks[$j] = rtrim($text_blocks[$j]);
+ }
+ $text_blocks[$j] = ""\'", "\\"=>"\\\\")) . "'; ?>";
+ if ($compiled_tags[$j] == '{/strip}') {
+ $compiled_tags[$j] = "\n"; /* slurped by php, but necessary
+ if a newline is following the closing strip-tag */
+ $strip = false;
+ $i = $j;
+ break;
+ }
+ }
+ }
+ }
+ $compiled_content = '';
+
+ $tag_guard = '%%%SMARTYOTG' . md5(uniqid(rand(), true)) . '%%%';
+
+ /* Interleave the compiled contents and text blocks to get the final result. */
+ for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
+ if ($compiled_tags[$i] == '') {
+ // tag result empty, remove first newline from following text block
+ $text_blocks[$i+1] = preg_replace('~^(\r\n|\r|\n)~', '', $text_blocks[$i+1]);
+ }
+ // replace legit PHP tags with placeholder
+ $text_blocks[$i] = str_replace('', $tag_guard, $text_blocks[$i]);
+ $compiled_tags[$i] = str_replace('', $tag_guard, $compiled_tags[$i]);
+
+ $compiled_content .= $text_blocks[$i] . $compiled_tags[$i];
+ }
+ $compiled_content .= str_replace('', $tag_guard, $text_blocks[$i]);
+
+ // escape php tags created by interleaving
+ $compiled_content = str_replace('', "\n", $compiled_content);
+ $compiled_content = preg_replace("~(?\n", $compiled_content);
+
+ // recover legit tags
+ $compiled_content = str_replace($tag_guard, '', $compiled_content);
+
+ // remove \n from the end of the file, if any
+ if (strlen($compiled_content) && (substr($compiled_content, -1) == "\n") ) {
+ $compiled_content = substr($compiled_content, 0, -1);
+ }
+
+ if (!empty($this->_cache_serial)) {
+ $compiled_content = "_cache_serials['".$this->_cache_include."'] = '".$this->_cache_serial."'; ?>" . $compiled_content;
+ }
+
+ // run compiled template through postfilter functions
+ if (count($this->_plugins['postfilter']) > 0) {
+ foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
+ if ($postfilter === false) continue;
+ if ($postfilter[3] || is_callable($postfilter[0])) {
+ $compiled_content = call_user_func_array($postfilter[0],
+ array($compiled_content, &$this));
+ $this->_plugins['postfilter'][$filter_name][3] = true;
+ } else {
+ $this->_trigger_fatal_error("Smarty plugin error: postfilter '$filter_name' is not implemented");
+ }
+ }
+ }
+
+ // put header at the top of the compiled template
+ $template_header = "_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n";
+ $template_header .= " compiled from ".strtr(urlencode($resource_name), array('%2F'=>'/', '%3A'=>':'))." */ ?>\n";
+
+ /* Emit code to load needed plugins. */
+ $this->_plugins_code = '';
+ if (count($this->_plugin_info)) {
+ $_plugins_params = "array('plugins' => array(";
+ foreach ($this->_plugin_info as $plugin_type => $plugins) {
+ foreach ($plugins as $plugin_name => $plugin_info) {
+ $_plugins_params .= "array('$plugin_type', '$plugin_name', '" . strtr($plugin_info[0], array("'" => "\\'", "\\" => "\\\\")) . "', $plugin_info[1], ";
+ $_plugins_params .= $plugin_info[2] ? 'true),' : 'false),';
+ }
+ }
+ $_plugins_params .= '))';
+ $plugins_code = "\n";
+ $template_header .= $plugins_code;
+ $this->_plugin_info = array();
+ $this->_plugins_code = $plugins_code;
+ }
+
+ if ($this->_init_smarty_vars) {
+ $template_header .= "\n";
+ $this->_init_smarty_vars = false;
+ }
+
+ $compiled_content = $template_header . $compiled_content;
+ return true;
+ }
+
+ /**
+ * Compile a template tag
+ *
+ * @param string $template_tag
+ * @return string
+ */
+ function _compile_tag($template_tag)
+ {
+ /* Matched comment. */
+ if (substr($template_tag, 0, 1) == '*' && substr($template_tag, -1) == '*')
+ return '';
+
+ /* Split tag into two three parts: command, command modifiers and the arguments. */
+ if(! preg_match('~^(?:(' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp
+ . '|\/?' . $this->_reg_obj_regexp . '|\/?' . $this->_func_regexp . ')(' . $this->_mod_regexp . '*))
+ (?:\s+(.*))?$
+ ~xs', $template_tag, $match)) {
+ $this->_syntax_error("unrecognized tag: $template_tag", E_USER_ERROR, __FILE__, __LINE__);
+ }
+
+ $tag_command = $match[1];
+ $tag_modifier = isset($match[2]) ? $match[2] : null;
+ $tag_args = isset($match[3]) ? $match[3] : null;
+
+ if (preg_match('~^' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '$~', $tag_command)) {
+ /* tag name is a variable or object */
+ $_return = $this->_parse_var_props($tag_command . $tag_modifier);
+ return "" . $this->_additional_newline;
+ }
+
+ /* If the tag name is a registered object, we process it. */
+ if (preg_match('~^\/?' . $this->_reg_obj_regexp . '$~', $tag_command)) {
+ return $this->_compile_registered_object_tag($tag_command, $this->_parse_attrs($tag_args), $tag_modifier);
+ }
+
+ switch ($tag_command) {
+ case 'include':
+ return $this->_compile_include_tag($tag_args);
+
+ case 'include_php':
+ return $this->_compile_include_php_tag($tag_args);
+
+ case 'if':
+ $this->_push_tag('if');
+ return $this->_compile_if_tag($tag_args);
+
+ case 'else':
+ list($_open_tag) = end($this->_tag_stack);
+ if ($_open_tag != 'if' && $_open_tag != 'elseif')
+ $this->_syntax_error('unexpected {else}', E_USER_ERROR, __FILE__, __LINE__);
+ else
+ $this->_push_tag('else');
+ return '';
+
+ case 'elseif':
+ list($_open_tag) = end($this->_tag_stack);
+ if ($_open_tag != 'if' && $_open_tag != 'elseif')
+ $this->_syntax_error('unexpected {elseif}', E_USER_ERROR, __FILE__, __LINE__);
+ if ($_open_tag == 'if')
+ $this->_push_tag('elseif');
+ return $this->_compile_if_tag($tag_args, true);
+
+ case '/if':
+ $this->_pop_tag('if');
+ return '';
+
+ case 'capture':
+ return $this->_compile_capture_tag(true, $tag_args);
+
+ case '/capture':
+ return $this->_compile_capture_tag(false);
+
+ case 'ldelim':
+ return $this->left_delimiter;
+
+ case 'rdelim':
+ return $this->right_delimiter;
+
+ case 'section':
+ $this->_push_tag('section');
+ return $this->_compile_section_start($tag_args);
+
+ case 'sectionelse':
+ $this->_push_tag('sectionelse');
+ return "";
+ break;
+
+ case '/section':
+ $_open_tag = $this->_pop_tag('section');
+ if ($_open_tag == 'sectionelse')
+ return "";
+ else
+ return "";
+
+ case 'foreach':
+ $this->_push_tag('foreach');
+ return $this->_compile_foreach_start($tag_args);
+ break;
+
+ case 'foreachelse':
+ $this->_push_tag('foreachelse');
+ return "";
+
+ case '/foreach':
+ $_open_tag = $this->_pop_tag('foreach');
+ if ($_open_tag == 'foreachelse')
+ return "";
+ else
+ return "";
+ break;
+
+ case 'strip':
+ case '/strip':
+ if (substr($tag_command, 0, 1)=='/') {
+ $this->_pop_tag('strip');
+ if (--$this->_strip_depth==0) { /* outermost closing {/strip} */
+ $this->_additional_newline = "\n";
+ return '{' . $tag_command . '}';
+ }
+ } else {
+ $this->_push_tag('strip');
+ if ($this->_strip_depth++==0) { /* outermost opening {strip} */
+ $this->_additional_newline = "";
+ return '{' . $tag_command . '}';
+ }
+ }
+ return '';
+
+ case 'php':
+ /* handle folded tags replaced by {php} */
+ list(, $block) = each($this->_folded_blocks);
+ $this->_current_line_no += substr_count($block[0], "\n");
+ /* the number of matched elements in the regexp in _compile_file()
+ determins the type of folded tag that was found */
+ switch (count($block)) {
+ case 2: /* comment */
+ return '';
+
+ case 3: /* literal */
+ return ""\'", "\\"=>"\\\\")) . "'; ?>" . $this->_additional_newline;
+
+ case 4: /* php */
+ if ($this->security && !$this->security_settings['PHP_TAGS']) {
+ $this->_syntax_error("(secure mode) php tags not permitted", E_USER_WARNING, __FILE__, __LINE__);
+ return;
+ }
+ return '';
+ }
+ break;
+
+ case 'insert':
+ return $this->_compile_insert_tag($tag_args);
+
+ default:
+ if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) {
+ return $output;
+ } else if ($this->_compile_block_tag($tag_command, $tag_args, $tag_modifier, $output)) {
+ return $output;
+ } else if ($this->_compile_custom_tag($tag_command, $tag_args, $tag_modifier, $output)) {
+ return $output;
+ } else {
+ $this->_syntax_error("unrecognized tag '$tag_command'", E_USER_ERROR, __FILE__, __LINE__);
+ }
+
+ }
+ }
+
+
+ /**
+ * compile the custom compiler tag
+ *
+ * sets $output to the compiled custom compiler tag
+ * @param string $tag_command
+ * @param string $tag_args
+ * @param string $output
+ * @return boolean
+ */
+ function _compile_compiler_tag($tag_command, $tag_args, &$output)
+ {
+ $found = false;
+ $have_function = true;
+
+ /*
+ * First we check if the compiler function has already been registered
+ * or loaded from a plugin file.
+ */
+ if (isset($this->_plugins['compiler'][$tag_command])) {
+ $found = true;
+ $plugin_func = $this->_plugins['compiler'][$tag_command][0];
+ if (!is_callable($plugin_func)) {
+ $message = "compiler function '$tag_command' is not implemented";
+ $have_function = false;
+ }
+ }
+ /*
+ * Otherwise we need to load plugin file and look for the function
+ * inside it.
+ */
+ else if ($plugin_file = $this->_get_plugin_filepath('compiler', $tag_command)) {
+ $found = true;
+
+ include_once $plugin_file;
+
+ $plugin_func = 'smarty_compiler_' . $tag_command;
+ if (!is_callable($plugin_func)) {
+ $message = "plugin function $plugin_func() not found in $plugin_file\n";
+ $have_function = false;
+ } else {
+ $this->_plugins['compiler'][$tag_command] = array($plugin_func, null, null, null, true);
+ }
+ }
+
+ /*
+ * True return value means that we either found a plugin or a
+ * dynamically registered function. False means that we didn't and the
+ * compiler should now emit code to load custom function plugin for this
+ * tag.
+ */
+ if ($found) {
+ if ($have_function) {
+ $output = call_user_func_array($plugin_func, array($tag_args, &$this));
+ if($output != '') {
+ $output = '_push_cacheable_state('compiler', $tag_command)
+ . $output
+ . $this->_pop_cacheable_state('compiler', $tag_command) . ' ?>';
+ }
+ } else {
+ $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+
+ /**
+ * compile block function tag
+ *
+ * sets $output to compiled block function tag
+ * @param string $tag_command
+ * @param string $tag_args
+ * @param string $tag_modifier
+ * @param string $output
+ * @return boolean
+ */
+ function _compile_block_tag($tag_command, $tag_args, $tag_modifier, &$output)
+ {
+ if (substr($tag_command, 0, 1) == '/') {
+ $start_tag = false;
+ $tag_command = substr($tag_command, 1);
+ } else
+ $start_tag = true;
+
+ $found = false;
+ $have_function = true;
+
+ /*
+ * First we check if the block function has already been registered
+ * or loaded from a plugin file.
+ */
+ if (isset($this->_plugins['block'][$tag_command])) {
+ $found = true;
+ $plugin_func = $this->_plugins['block'][$tag_command][0];
+ if (!is_callable($plugin_func)) {
+ $message = "block function '$tag_command' is not implemented";
+ $have_function = false;
+ }
+ }
+ /*
+ * Otherwise we need to load plugin file and look for the function
+ * inside it.
+ */
+ else if ($plugin_file = $this->_get_plugin_filepath('block', $tag_command)) {
+ $found = true;
+
+ include_once $plugin_file;
+
+ $plugin_func = 'smarty_block_' . $tag_command;
+ if (!function_exists($plugin_func)) {
+ $message = "plugin function $plugin_func() not found in $plugin_file\n";
+ $have_function = false;
+ } else {
+ $this->_plugins['block'][$tag_command] = array($plugin_func, null, null, null, true);
+
+ }
+ }
+
+ if (!$found) {
+ return false;
+ } else if (!$have_function) {
+ $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
+ return true;
+ }
+
+ /*
+ * Even though we've located the plugin function, compilation
+ * happens only once, so the plugin will still need to be loaded
+ * at runtime for future requests.
+ */
+ $this->_add_plugin('block', $tag_command);
+
+ if ($start_tag)
+ $this->_push_tag($tag_command);
+ else
+ $this->_pop_tag($tag_command);
+
+ if ($start_tag) {
+ $output = '_push_cacheable_state('block', $tag_command);
+ $attrs = $this->_parse_attrs($tag_args);
+ $_cache_attrs='';
+ $arg_list = $this->_compile_arg_list('block', $tag_command, $attrs, $_cache_attrs);
+ $output .= "$_cache_attrs\$this->_tag_stack[] = array('$tag_command', array(".implode(',', $arg_list).')); ';
+ $output .= '$_block_repeat=true;' . $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], null, $this, $_block_repeat);';
+ $output .= 'while ($_block_repeat) { ob_start(); ?>';
+ } else {
+ $output = '_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], $_block_content, $this, $_block_repeat)';
+ if ($tag_modifier != '') {
+ $this->_parse_modifiers($_out_tag_text, $tag_modifier);
+ }
+ $output .= '$_block_repeat=false;echo ' . $_out_tag_text . '; } ';
+ $output .= " array_pop(\$this->_tag_stack); " . $this->_pop_cacheable_state('block', $tag_command) . '?>';
+ }
+
+ return true;
+ }
+
+
+ /**
+ * compile custom function tag
+ *
+ * @param string $tag_command
+ * @param string $tag_args
+ * @param string $tag_modifier
+ * @return string
+ */
+ function _compile_custom_tag($tag_command, $tag_args, $tag_modifier, &$output)
+ {
+ $found = false;
+ $have_function = true;
+
+ /*
+ * First we check if the custom function has already been registered
+ * or loaded from a plugin file.
+ */
+ if (isset($this->_plugins['function'][$tag_command])) {
+ $found = true;
+ $plugin_func = $this->_plugins['function'][$tag_command][0];
+ if (!is_callable($plugin_func)) {
+ $message = "custom function '$tag_command' is not implemented";
+ $have_function = false;
+ }
+ }
+ /*
+ * Otherwise we need to load plugin file and look for the function
+ * inside it.
+ */
+ else if ($plugin_file = $this->_get_plugin_filepath('function', $tag_command)) {
+ $found = true;
+
+ include_once $plugin_file;
+
+ $plugin_func = 'smarty_function_' . $tag_command;
+ if (!function_exists($plugin_func)) {
+ $message = "plugin function $plugin_func() not found in $plugin_file\n";
+ $have_function = false;
+ } else {
+ $this->_plugins['function'][$tag_command] = array($plugin_func, null, null, null, true);
+
+ }
+ }
+
+ if (!$found) {
+ return false;
+ } else if (!$have_function) {
+ $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
+ return true;
+ }
+
+ /* declare plugin to be loaded on display of the template that
+ we compile right now */
+ $this->_add_plugin('function', $tag_command);
+
+ $_cacheable_state = $this->_push_cacheable_state('function', $tag_command);
+ $attrs = $this->_parse_attrs($tag_args);
+ $_cache_attrs = '';
+ $arg_list = $this->_compile_arg_list('function', $tag_command, $attrs, $_cache_attrs);
+
+ $output = $this->_compile_plugin_call('function', $tag_command).'(array('.implode(',', $arg_list)."), \$this)";
+ if($tag_modifier != '') {
+ $this->_parse_modifiers($output, $tag_modifier);
+ }
+
+ if($output != '') {
+ $output = '_pop_cacheable_state('function', $tag_command) . "?>" . $this->_additional_newline;
+ }
+
+ return true;
+ }
+
+ /**
+ * compile a registered object tag
+ *
+ * @param string $tag_command
+ * @param array $attrs
+ * @param string $tag_modifier
+ * @return string
+ */
+ function _compile_registered_object_tag($tag_command, $attrs, $tag_modifier)
+ {
+ if (substr($tag_command, 0, 1) == '/') {
+ $start_tag = false;
+ $tag_command = substr($tag_command, 1);
+ } else {
+ $start_tag = true;
+ }
+
+ list($object, $obj_comp) = explode('->', $tag_command);
+
+ $arg_list = array();
+ if(count($attrs)) {
+ $_assign_var = false;
+ foreach ($attrs as $arg_name => $arg_value) {
+ if($arg_name == 'assign') {
+ $_assign_var = $arg_value;
+ unset($attrs['assign']);
+ continue;
+ }
+ if (is_bool($arg_value))
+ $arg_value = $arg_value ? 'true' : 'false';
+ $arg_list[] = "'$arg_name' => $arg_value";
+ }
+ }
+
+ if($this->_reg_objects[$object][2]) {
+ // smarty object argument format
+ $args = "array(".implode(',', (array)$arg_list)."), \$this";
+ } else {
+ // traditional argument format
+ $args = implode(',', array_values($attrs));
+ if (empty($args)) {
+ $args = '';
+ }
+ }
+
+ $prefix = '';
+ $postfix = '';
+ $newline = '';
+ if(!is_object($this->_reg_objects[$object][0])) {
+ $this->_trigger_fatal_error("registered '$object' is not an object" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
+ } elseif(!empty($this->_reg_objects[$object][1]) && !in_array($obj_comp, $this->_reg_objects[$object][1])) {
+ $this->_trigger_fatal_error("'$obj_comp' is not a registered component of object '$object'", $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
+ } elseif(method_exists($this->_reg_objects[$object][0], $obj_comp)) {
+ // method
+ if(in_array($obj_comp, $this->_reg_objects[$object][3])) {
+ // block method
+ if ($start_tag) {
+ $prefix = "\$this->_tag_stack[] = array('$obj_comp', $args); ";
+ $prefix .= "\$_block_repeat=true; \$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], null, \$this, \$_block_repeat); ";
+ $prefix .= "while (\$_block_repeat) { ob_start();";
+ $return = null;
+ $postfix = '';
+ } else {
+ $prefix = "\$_obj_block_content = ob_get_contents(); ob_end_clean(); \$_block_repeat=false;";
+ $return = "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], \$_obj_block_content, \$this, \$_block_repeat)";
+ $postfix = "} array_pop(\$this->_tag_stack);";
+ }
+ } else {
+ // non-block method
+ $return = "\$this->_reg_objects['$object'][0]->$obj_comp($args)";
+ }
+ } else {
+ // property
+ $return = "\$this->_reg_objects['$object'][0]->$obj_comp";
+ }
+
+ if($return != null) {
+ if($tag_modifier != '') {
+ $this->_parse_modifiers($return, $tag_modifier);
+ }
+
+ if(!empty($_assign_var)) {
+ $output = "\$this->assign('" . $this->_dequote($_assign_var) ."', $return);";
+ } else {
+ $output = 'echo ' . $return . ';';
+ $newline = $this->_additional_newline;
+ }
+ } else {
+ $output = '';
+ }
+
+ return '" . $newline;
+ }
+
+ /**
+ * Compile {insert ...} tag
+ *
+ * @param string $tag_args
+ * @return string
+ */
+ function _compile_insert_tag($tag_args)
+ {
+ $attrs = $this->_parse_attrs($tag_args);
+ $name = $this->_dequote($attrs['name']);
+
+ if (empty($name)) {
+ return $this->_syntax_error("missing insert name", E_USER_ERROR, __FILE__, __LINE__);
+ }
+
+ if (!preg_match('~^\w+$~', $name)) {
+ return $this->_syntax_error("'insert: 'name' must be an insert function name", E_USER_ERROR, __FILE__, __LINE__);
+ }
+
+ if (!empty($attrs['script'])) {
+ $delayed_loading = true;
+ } else {
+ $delayed_loading = false;
+ }
+
+ foreach ($attrs as $arg_name => $arg_value) {
+ if (is_bool($arg_value))
+ $arg_value = $arg_value ? 'true' : 'false';
+ $arg_list[] = "'$arg_name' => $arg_value";
+ }
+
+ $this->_add_plugin('insert', $name, $delayed_loading);
+
+ $_params = "array('args' => array(".implode(', ', (array)$arg_list)."))";
+
+ return "" . $this->_additional_newline;
+ }
+
+ /**
+ * Compile {include ...} tag
+ *
+ * @param string $tag_args
+ * @return string
+ */
+ function _compile_include_tag($tag_args)
+ {
+ $attrs = $this->_parse_attrs($tag_args);
+ $arg_list = array();
+
+ if (empty($attrs['file'])) {
+ $this->_syntax_error("missing 'file' attribute in include tag", E_USER_ERROR, __FILE__, __LINE__);
+ }
+
+ foreach ($attrs as $arg_name => $arg_value) {
+ if ($arg_name == 'file') {
+ $include_file = $arg_value;
+ continue;
+ } else if ($arg_name == 'assign') {
+ $assign_var = $arg_value;
+ continue;
+ }
+ if (is_bool($arg_value))
+ $arg_value = $arg_value ? 'true' : 'false';
+ $arg_list[] = "'$arg_name' => $arg_value";
+ }
+
+ $output = '_tpl_vars;\n";
+
+
+ $_params = "array('smarty_include_tpl_file' => " . $include_file . ", 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))";
+ $output .= "\$this->_smarty_include($_params);\n" .
+ "\$this->_tpl_vars = \$_smarty_tpl_vars;\n" .
+ "unset(\$_smarty_tpl_vars);\n";
+
+ if (isset($assign_var)) {
+ $output .= "\$this->assign(" . $assign_var . ", ob_get_contents()); ob_end_clean();\n";
+ }
+
+ $output .= ' ?>';
+
+ return $output;
+
+ }
+
+ /**
+ * Compile {include ...} tag
+ *
+ * @param string $tag_args
+ * @return string
+ */
+ function _compile_include_php_tag($tag_args)
+ {
+ $attrs = $this->_parse_attrs($tag_args);
+
+ if (empty($attrs['file'])) {
+ $this->_syntax_error("missing 'file' attribute in include_php tag", E_USER_ERROR, __FILE__, __LINE__);
+ }
+
+ $assign_var = (empty($attrs['assign'])) ? '' : $this->_dequote($attrs['assign']);
+ $once_var = (empty($attrs['once']) || $attrs['once']=='false') ? 'false' : 'true';
+
+ $arg_list = array();
+ foreach($attrs as $arg_name => $arg_value) {
+ if($arg_name != 'file' AND $arg_name != 'once' AND $arg_name != 'assign') {
+ if(is_bool($arg_value))
+ $arg_value = $arg_value ? 'true' : 'false';
+ $arg_list[] = "'$arg_name' => $arg_value";
+ }
+ }
+
+ $_params = "array('smarty_file' => " . $attrs['file'] . ", 'smarty_assign' => '$assign_var', 'smarty_once' => $once_var, 'smarty_include_vars' => array(".implode(',', $arg_list)."))";
+
+ return "" . $this->_additional_newline;
+ }
+
+
+ /**
+ * Compile {section ...} tag
+ *
+ * @param string $tag_args
+ * @return string
+ */
+ function _compile_section_start($tag_args)
+ {
+ $attrs = $this->_parse_attrs($tag_args);
+ $arg_list = array();
+
+ $output = '_syntax_error("missing section name", E_USER_ERROR, __FILE__, __LINE__);
+ }
+
+ $output .= "unset(\$this->_sections[$section_name]);\n";
+ $section_props = "\$this->_sections[$section_name]";
+
+ foreach ($attrs as $attr_name => $attr_value) {
+ switch ($attr_name) {
+ case 'loop':
+ $output .= "{$section_props}['loop'] = is_array(\$_loop=$attr_value) ? count(\$_loop) : max(0, (int)\$_loop); unset(\$_loop);\n";
+ break;
+
+ case 'show':
+ if (is_bool($attr_value))
+ $show_attr_value = $attr_value ? 'true' : 'false';
+ else
+ $show_attr_value = "(bool)$attr_value";
+ $output .= "{$section_props}['show'] = $show_attr_value;\n";
+ break;
+
+ case 'name':
+ $output .= "{$section_props}['$attr_name'] = $attr_value;\n";
+ break;
+
+ case 'max':
+ case 'start':
+ $output .= "{$section_props}['$attr_name'] = (int)$attr_value;\n";
+ break;
+
+ case 'step':
+ $output .= "{$section_props}['$attr_name'] = ((int)$attr_value) == 0 ? 1 : (int)$attr_value;\n";
+ break;
+
+ default:
+ $this->_syntax_error("unknown section attribute - '$attr_name'", E_USER_ERROR, __FILE__, __LINE__);
+ break;
+ }
+ }
+
+ if (!isset($attrs['show']))
+ $output .= "{$section_props}['show'] = true;\n";
+
+ if (!isset($attrs['loop']))
+ $output .= "{$section_props}['loop'] = 1;\n";
+
+ if (!isset($attrs['max']))
+ $output .= "{$section_props}['max'] = {$section_props}['loop'];\n";
+ else
+ $output .= "if ({$section_props}['max'] < 0)\n" .
+ " {$section_props}['max'] = {$section_props}['loop'];\n";
+
+ if (!isset($attrs['step']))
+ $output .= "{$section_props}['step'] = 1;\n";
+
+ if (!isset($attrs['start']))
+ $output .= "{$section_props}['start'] = {$section_props}['step'] > 0 ? 0 : {$section_props}['loop']-1;\n";
+ else {
+ $output .= "if ({$section_props}['start'] < 0)\n" .
+ " {$section_props}['start'] = max({$section_props}['step'] > 0 ? 0 : -1, {$section_props}['loop'] + {$section_props}['start']);\n" .
+ "else\n" .
+ " {$section_props}['start'] = min({$section_props}['start'], {$section_props}['step'] > 0 ? {$section_props}['loop'] : {$section_props}['loop']-1);\n";
+ }
+
+ $output .= "if ({$section_props}['show']) {\n";
+ if (!isset($attrs['start']) && !isset($attrs['step']) && !isset($attrs['max'])) {
+ $output .= " {$section_props}['total'] = {$section_props}['loop'];\n";
+ } else {
+ $output .= " {$section_props}['total'] = min(ceil(({$section_props}['step'] > 0 ? {$section_props}['loop'] - {$section_props}['start'] : {$section_props}['start']+1)/abs({$section_props}['step'])), {$section_props}['max']);\n";
+ }
+ $output .= " if ({$section_props}['total'] == 0)\n" .
+ " {$section_props}['show'] = false;\n" .
+ "} else\n" .
+ " {$section_props}['total'] = 0;\n";
+
+ $output .= "if ({$section_props}['show']):\n";
+ $output .= "
+ for ({$section_props}['index'] = {$section_props}['start'], {$section_props}['iteration'] = 1;
+ {$section_props}['iteration'] <= {$section_props}['total'];
+ {$section_props}['index'] += {$section_props}['step'], {$section_props}['iteration']++):\n";
+ $output .= "{$section_props}['rownum'] = {$section_props}['iteration'];\n";
+ $output .= "{$section_props}['index_prev'] = {$section_props}['index'] - {$section_props}['step'];\n";
+ $output .= "{$section_props}['index_next'] = {$section_props}['index'] + {$section_props}['step'];\n";
+ $output .= "{$section_props}['first'] = ({$section_props}['iteration'] == 1);\n";
+ $output .= "{$section_props}['last'] = ({$section_props}['iteration'] == {$section_props}['total']);\n";
+
+ $output .= "?>";
+
+ return $output;
+ }
+
+
+ /**
+ * Compile {foreach ...} tag.
+ *
+ * @param string $tag_args
+ * @return string
+ */
+ function _compile_foreach_start($tag_args)
+ {
+ $attrs = $this->_parse_attrs($tag_args);
+ $arg_list = array();
+
+ if (empty($attrs['from'])) {
+ return $this->_syntax_error("foreach: missing 'from' attribute", E_USER_ERROR, __FILE__, __LINE__);
+ }
+ $from = $attrs['from'];
+
+ if (empty($attrs['item'])) {
+ return $this->_syntax_error("foreach: missing 'item' attribute", E_USER_ERROR, __FILE__, __LINE__);
+ }
+ $item = $this->_dequote($attrs['item']);
+ if (!preg_match('~^\w+$~', $item)) {
+ return $this->_syntax_error("foreach: 'item' must be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
+ }
+
+ if (isset($attrs['key'])) {
+ $key = $this->_dequote($attrs['key']);
+ if (!preg_match('~^\w+$~', $key)) {
+ return $this->_syntax_error("foreach: 'key' must to be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
+ }
+ $key_part = "\$this->_tpl_vars['$key'] => ";
+ } else {
+ $key = null;
+ $key_part = '';
+ }
+
+ if (isset($attrs['name'])) {
+ $name = $attrs['name'];
+ } else {
+ $name = null;
+ }
+
+ $output = '_foreach[$name]";
+ $output .= "{$foreach_props} = array('total' => count(\$_from), 'iteration' => 0);\n";
+ $output .= "if ({$foreach_props}['total'] > 0):\n";
+ $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
+ $output .= " {$foreach_props}['iteration']++;\n";
+ } else {
+ $output .= "if (count(\$_from)):\n";
+ $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
+ }
+ $output .= '?>';
+
+ return $output;
+ }
+
+
+ /**
+ * Compile {capture} .. {/capture} tags
+ *
+ * @param boolean $start true if this is the {capture} tag
+ * @param string $tag_args
+ * @return string
+ */
+
+ function _compile_capture_tag($start, $tag_args = '')
+ {
+ $attrs = $this->_parse_attrs($tag_args);
+
+ if ($start) {
+ $buffer = isset($attrs['name']) ? $attrs['name'] : "'default'";
+ $assign = isset($attrs['assign']) ? $attrs['assign'] : null;
+ $append = isset($attrs['append']) ? $attrs['append'] : null;
+
+ $output = "";
+ $this->_capture_stack[] = array($buffer, $assign, $append);
+ } else {
+ list($buffer, $assign, $append) = array_pop($this->_capture_stack);
+ $output = "_smarty_vars['capture'][$buffer] = ob_get_contents(); ";
+ if (isset($assign)) {
+ $output .= " \$this->assign($assign, ob_get_contents());";
+ }
+ if (isset($append)) {
+ $output .= " \$this->append($append, ob_get_contents());";
+ }
+ $output .= "ob_end_clean(); ?>";
+ }
+
+ return $output;
+ }
+
+ /**
+ * Compile {if ...} tag
+ *
+ * @param string $tag_args
+ * @param boolean $elseif if true, uses elseif instead of if
+ * @return string
+ */
+ function _compile_if_tag($tag_args, $elseif = false)
+ {
+
+ /* Tokenize args for 'if' tag. */
+ preg_match_all('~(?>
+ ' . $this->_obj_call_regexp . '(?:' . $this->_mod_regexp . '*)? | # valid object call
+ ' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)? | # var or quoted string
+ \-?0[xX][0-9a-fA-F]+|\-?\d+(?:\.\d+)?|\.\d+|!==|===|==|!=|<>|<<|>>|<=|>=|\&\&|\|\||\(|\)|,|\!|\^|=|\&|\~|<|>|\||\%|\+|\-|\/|\*|\@ | # valid non-word token
+ \b\w+\b | # valid word token
+ \S+ # anything else
+ )~x', $tag_args, $match);
+
+ $tokens = $match[0];
+
+ if(empty($tokens)) {
+ $_error_msg = $elseif ? "'elseif'" : "'if'";
+ $_error_msg .= ' statement requires arguments';
+ $this->_syntax_error($_error_msg, E_USER_ERROR, __FILE__, __LINE__);
+ }
+
+
+ // make sure we have balanced parenthesis
+ $token_count = array_count_values($tokens);
+ if(isset($token_count['(']) && $token_count['('] != $token_count[')']) {
+ $this->_syntax_error("unbalanced parenthesis in if statement", E_USER_ERROR, __FILE__, __LINE__);
+ }
+
+ $is_arg_stack = array();
+
+ for ($i = 0; $i < count($tokens); $i++) {
+
+ $token = &$tokens[$i];
+
+ switch (strtolower($token)) {
+ case '!':
+ case '%':
+ case '!==':
+ case '==':
+ case '===':
+ case '>':
+ case '<':
+ case '!=':
+ case '<>':
+ case '<<':
+ case '>>':
+ case '<=':
+ case '>=':
+ case '&&':
+ case '||':
+ case '|':
+ case '^':
+ case '&':
+ case '~':
+ case ')':
+ case ',':
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '@':
+ break;
+
+ case 'eq':
+ $token = '==';
+ break;
+
+ case 'ne':
+ case 'neq':
+ $token = '!=';
+ break;
+
+ case 'lt':
+ $token = '<';
+ break;
+
+ case 'le':
+ case 'lte':
+ $token = '<=';
+ break;
+
+ case 'gt':
+ $token = '>';
+ break;
+
+ case 'ge':
+ case 'gte':
+ $token = '>=';
+ break;
+
+ case 'and':
+ $token = '&&';
+ break;
+
+ case 'or':
+ $token = '||';
+ break;
+
+ case 'not':
+ $token = '!';
+ break;
+
+ case 'mod':
+ $token = '%';
+ break;
+
+ case '(':
+ array_push($is_arg_stack, $i);
+ break;
+
+ case 'is':
+ /* If last token was a ')', we operate on the parenthesized
+ expression. The start of the expression is on the stack.
+ Otherwise, we operate on the last encountered token. */
+ if ($tokens[$i-1] == ')')
+ $is_arg_start = array_pop($is_arg_stack);
+ else
+ $is_arg_start = $i-1;
+ /* Construct the argument for 'is' expression, so it knows
+ what to operate on. */
+ $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));
+
+ /* Pass all tokens from next one until the end to the
+ 'is' expression parsing function. The function will
+ return modified tokens, where the first one is the result
+ of the 'is' expression and the rest are the tokens it
+ didn't touch. */
+ $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));
+
+ /* Replace the old tokens with the new ones. */
+ array_splice($tokens, $is_arg_start, count($tokens), $new_tokens);
+
+ /* Adjust argument start so that it won't change from the
+ current position for the next iteration. */
+ $i = $is_arg_start;
+ break;
+
+ default:
+ if(preg_match('~^' . $this->_func_regexp . '$~', $token) ) {
+ // function call
+ if($this->security &&
+ !in_array($token, $this->security_settings['IF_FUNCS'])) {
+ $this->_syntax_error("(secure mode) '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
+ }
+ } elseif(preg_match('~^' . $this->_var_regexp . '$~', $token) && (strpos('+-*/^%&|', substr($token, -1)) === false) && isset($tokens[$i+1]) && $tokens[$i+1] == '(') {
+ // variable function call
+ $this->_syntax_error("variable function call '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
+ } elseif(preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)$~', $token)) {
+ // object or variable
+ $token = $this->_parse_var_props($token);
+ } elseif(is_numeric($token)) {
+ // number, skip it
+ } else {
+ $this->_syntax_error("unidentified token '$token'", E_USER_ERROR, __FILE__, __LINE__);
+ }
+ break;
+ }
+ }
+
+ if ($elseif)
+ return '';
+ else
+ return '';
+ }
+
+
+ function _compile_arg_list($type, $name, $attrs, &$cache_code) {
+ $arg_list = array();
+
+ if (isset($type) && isset($name)
+ && isset($this->_plugins[$type])
+ && isset($this->_plugins[$type][$name])
+ && empty($this->_plugins[$type][$name][4])
+ && is_array($this->_plugins[$type][$name][5])
+ ) {
+ /* we have a list of parameters that should be cached */
+ $_cache_attrs = $this->_plugins[$type][$name][5];
+ $_count = $this->_cache_attrs_count++;
+ $cache_code = "\$_cache_attrs =& \$this->_smarty_cache_attrs('$this->_cache_serial','$_count');";
+
+ } else {
+ /* no parameters are cached */
+ $_cache_attrs = null;
+ }
+
+ foreach ($attrs as $arg_name => $arg_value) {
+ if (is_bool($arg_value))
+ $arg_value = $arg_value ? 'true' : 'false';
+ if (is_null($arg_value))
+ $arg_value = 'null';
+ if ($_cache_attrs && in_array($arg_name, $_cache_attrs)) {
+ $arg_list[] = "'$arg_name' => (\$this->_cache_including) ? \$_cache_attrs['$arg_name'] : (\$_cache_attrs['$arg_name']=$arg_value)";
+ } else {
+ $arg_list[] = "'$arg_name' => $arg_value";
+ }
+ }
+ return $arg_list;
+ }
+
+ /**
+ * Parse is expression
+ *
+ * @param string $is_arg
+ * @param array $tokens
+ * @return array
+ */
+ function _parse_is_expr($is_arg, $tokens)
+ {
+ $expr_end = 0;
+ $negate_expr = false;
+
+ if (($first_token = array_shift($tokens)) == 'not') {
+ $negate_expr = true;
+ $expr_type = array_shift($tokens);
+ } else
+ $expr_type = $first_token;
+
+ switch ($expr_type) {
+ case 'even':
+ if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
+ $expr_end++;
+ $expr_arg = $tokens[$expr_end++];
+ $expr = "!(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
+ } else
+ $expr = "!(1 & $is_arg)";
+ break;
+
+ case 'odd':
+ if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
+ $expr_end++;
+ $expr_arg = $tokens[$expr_end++];
+ $expr = "(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
+ } else
+ $expr = "(1 & $is_arg)";
+ break;
+
+ case 'div':
+ if (@$tokens[$expr_end] == 'by') {
+ $expr_end++;
+ $expr_arg = $tokens[$expr_end++];
+ $expr = "!($is_arg % " . $this->_parse_var_props($expr_arg) . ")";
+ } else {
+ $this->_syntax_error("expecting 'by' after 'div'", E_USER_ERROR, __FILE__, __LINE__);
+ }
+ break;
+
+ default:
+ $this->_syntax_error("unknown 'is' expression - '$expr_type'", E_USER_ERROR, __FILE__, __LINE__);
+ break;
+ }
+
+ if ($negate_expr) {
+ $expr = "!($expr)";
+ }
+
+ array_splice($tokens, 0, $expr_end, $expr);
+
+ return $tokens;
+ }
+
+
+ /**
+ * Parse attribute string
+ *
+ * @param string $tag_args
+ * @return array
+ */
+ function _parse_attrs($tag_args)
+ {
+
+ /* Tokenize tag attributes. */
+ preg_match_all('~(?:' . $this->_obj_call_regexp . '|' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+)
+ )+ |
+ [=]
+ ~x', $tag_args, $match);
+ $tokens = $match[0];
+
+ $attrs = array();
+ /* Parse state:
+ 0 - expecting attribute name
+ 1 - expecting '='
+ 2 - expecting attribute value (not '=') */
+ $state = 0;
+
+ foreach ($tokens as $token) {
+ switch ($state) {
+ case 0:
+ /* If the token is a valid identifier, we set attribute name
+ and go to state 1. */
+ if (preg_match('~^\w+$~', $token)) {
+ $attr_name = $token;
+ $state = 1;
+ } else
+ $this->_syntax_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__);
+ break;
+
+ case 1:
+ /* If the token is '=', then we go to state 2. */
+ if ($token == '=') {
+ $state = 2;
+ } else
+ $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
+ break;
+
+ case 2:
+ /* If token is not '=', we set the attribute value and go to
+ state 0. */
+ if ($token != '=') {
+ /* We booleanize the token if it's a non-quoted possible
+ boolean value. */
+ if (preg_match('~^(on|yes|true)$~', $token)) {
+ $token = 'true';
+ } else if (preg_match('~^(off|no|false)$~', $token)) {
+ $token = 'false';
+ } else if ($token == 'null') {
+ $token = 'null';
+ } else if (preg_match('~^' . $this->_num_const_regexp . '|0[xX][0-9a-fA-F]+$~', $token)) {
+ /* treat integer literally */
+ } else if (!preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . ')*$~', $token)) {
+ /* treat as a string, double-quote it escaping quotes */
+ $token = '"'.addslashes($token).'"';
+ }
+
+ $attrs[$attr_name] = $token;
+ $state = 0;
+ } else
+ $this->_syntax_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__);
+ break;
+ }
+ $last_token = $token;
+ }
+
+ if($state != 0) {
+ if($state == 1) {
+ $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
+ } else {
+ $this->_syntax_error("missing attribute value", E_USER_ERROR, __FILE__, __LINE__);
+ }
+ }
+
+ $this->_parse_vars_props($attrs);
+
+ return $attrs;
+ }
+
+ /**
+ * compile multiple variables and section properties tokens into
+ * PHP code
+ *
+ * @param array $tokens
+ */
+ function _parse_vars_props(&$tokens)
+ {
+ foreach($tokens as $key => $val) {
+ $tokens[$key] = $this->_parse_var_props($val);
+ }
+ }
+
+ /**
+ * compile single variable and section properties token into
+ * PHP code
+ *
+ * @param string $val
+ * @param string $tag_attrs
+ * @return string
+ */
+ function _parse_var_props($val)
+ {
+ $val = trim($val);
+
+ if(preg_match('~^(' . $this->_obj_call_regexp . '|' . $this->_dvar_regexp . ')(' . $this->_mod_regexp . '*)$~', $val, $match)) {
+ // $ variable or object
+ $return = $this->_parse_var($match[1]);
+ $modifiers = $match[2];
+ if (!empty($this->default_modifiers) && !preg_match('~(^|\|)smarty:nodefaults($|\|)~',$modifiers)) {
+ $_default_mod_string = implode('|',(array)$this->default_modifiers);
+ $modifiers = empty($modifiers) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers;
+ }
+ $this->_parse_modifiers($return, $modifiers);
+ return $return;
+ } elseif (preg_match('~^' . $this->_db_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
+ // double quoted text
+ preg_match('~^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
+ $return = $this->_expand_quoted_text($match[1]);
+ if($match[2] != '') {
+ $this->_parse_modifiers($return, $match[2]);
+ }
+ return $return;
+ }
+ elseif(preg_match('~^' . $this->_num_const_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
+ // numerical constant
+ preg_match('~^(' . $this->_num_const_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
+ if($match[2] != '') {
+ $this->_parse_modifiers($match[1], $match[2]);
+ return $match[1];
+ }
+ }
+ elseif(preg_match('~^' . $this->_si_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
+ // single quoted text
+ preg_match('~^(' . $this->_si_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
+ if($match[2] != '') {
+ $this->_parse_modifiers($match[1], $match[2]);
+ return $match[1];
+ }
+ }
+ elseif(preg_match('~^' . $this->_cvar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
+ // config var
+ return $this->_parse_conf_var($val);
+ }
+ elseif(preg_match('~^' . $this->_svar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
+ // section var
+ return $this->_parse_section_prop($val);
+ }
+ elseif(!in_array($val, $this->_permitted_tokens) && !is_numeric($val)) {
+ // literal string
+ return $this->_expand_quoted_text('"' . strtr($val, array('\\' => '\\\\', '"' => '\\"')) .'"');
+ }
+ return $val;
+ }
+
+ /**
+ * expand quoted text with embedded variables
+ *
+ * @param string $var_expr
+ * @return string
+ */
+ function _expand_quoted_text($var_expr)
+ {
+ // if contains unescaped $, expand it
+ if(preg_match_all('~(?:\`(?_dvar_guts_regexp . '(?:' . $this->_obj_ext_regexp . ')*\`)|(?:(?_parse_var(str_replace('`','',$_var)) . ')."';
+ }
+ $var_expr = strtr($var_expr, $_replace);
+ $_return = preg_replace('~\.""|(?_dvar_math_regexp.'|'.$this->_qstr_regexp.')~', $var_expr, -1, PREG_SPLIT_DELIM_CAPTURE);
+
+ if(count($_math_vars) > 1) {
+ $_first_var = "";
+ $_complete_var = "";
+ $_output = "";
+ // simple check if there is any math, to stop recursion (due to modifiers with "xx % yy" as parameter)
+ foreach($_math_vars as $_k => $_math_var) {
+ $_math_var = $_math_vars[$_k];
+
+ if(!empty($_math_var) || is_numeric($_math_var)) {
+ // hit a math operator, so process the stuff which came before it
+ if(preg_match('~^' . $this->_dvar_math_regexp . '$~', $_math_var)) {
+ $_has_math = true;
+ if(!empty($_complete_var) || is_numeric($_complete_var)) {
+ $_output .= $this->_parse_var($_complete_var);
+ }
+
+ // just output the math operator to php
+ $_output .= $_math_var;
+
+ if(empty($_first_var))
+ $_first_var = $_complete_var;
+
+ $_complete_var = "";
+ } else {
+ $_complete_var .= $_math_var;
+ }
+ }
+ }
+ if($_has_math) {
+ if(!empty($_complete_var) || is_numeric($_complete_var))
+ $_output .= $this->_parse_var($_complete_var);
+
+ // get the modifiers working (only the last var from math + modifier is left)
+ $var_expr = $_complete_var;
+ }
+ }
+
+ // prevent cutting of first digit in the number (we _definitly_ got a number if the first char is a digit)
+ if(is_numeric(substr($var_expr, 0, 1)))
+ $_var_ref = $var_expr;
+ else
+ $_var_ref = substr($var_expr, 1);
+
+ if(!$_has_math) {
+
+ // get [foo] and .foo and ->foo and (...) pieces
+ preg_match_all('~(?:^\w+)|' . $this->_obj_params_regexp . '|(?:' . $this->_var_bracket_regexp . ')|->\$?\w+|\.\$?\w+|\S+~', $_var_ref, $match);
+
+ $_indexes = $match[0];
+ $_var_name = array_shift($_indexes);
+
+ /* Handle $smarty.* variable references as a special case. */
+ if ($_var_name == 'smarty') {
+ /*
+ * If the reference could be compiled, use the compiled output;
+ * otherwise, fall back on the $smarty variable generated at
+ * run-time.
+ */
+ if (($smarty_ref = $this->_compile_smarty_ref($_indexes)) !== null) {
+ $_output = $smarty_ref;
+ } else {
+ $_var_name = substr(array_shift($_indexes), 1);
+ $_output = "\$this->_smarty_vars['$_var_name']";
+ }
+ } elseif(is_numeric($_var_name) && is_numeric(substr($var_expr, 0, 1))) {
+ // because . is the operator for accessing arrays thru inidizes we need to put it together again for floating point numbers
+ if(count($_indexes) > 0)
+ {
+ $_var_name .= implode("", $_indexes);
+ $_indexes = array();
+ }
+ $_output = $_var_name;
+ } else {
+ $_output = "\$this->_tpl_vars['$_var_name']";
+ }
+
+ foreach ($_indexes as $_index) {
+ if (substr($_index, 0, 1) == '[') {
+ $_index = substr($_index, 1, -1);
+ if (is_numeric($_index)) {
+ $_output .= "[$_index]";
+ } elseif (substr($_index, 0, 1) == '$') {
+ if (strpos($_index, '.') !== false) {
+ $_output .= '[' . $this->_parse_var($_index) . ']';
+ } else {
+ $_output .= "[\$this->_tpl_vars['" . substr($_index, 1) . "']]";
+ }
+ } else {
+ $_var_parts = explode('.', $_index);
+ $_var_section = $_var_parts[0];
+ $_var_section_prop = isset($_var_parts[1]) ? $_var_parts[1] : 'index';
+ $_output .= "[\$this->_sections['$_var_section']['$_var_section_prop']]";
+ }
+ } else if (substr($_index, 0, 1) == '.') {
+ if (substr($_index, 1, 1) == '$')
+ $_output .= "[\$this->_tpl_vars['" . substr($_index, 2) . "']]";
+ else
+ $_output .= "['" . substr($_index, 1) . "']";
+ } else if (substr($_index,0,2) == '->') {
+ if(substr($_index,2,2) == '__') {
+ $this->_syntax_error('call to internal object members is not allowed', E_USER_ERROR, __FILE__, __LINE__);
+ } elseif($this->security && substr($_index, 2, 1) == '_') {
+ $this->_syntax_error('(secure) call to private object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
+ } elseif (substr($_index, 2, 1) == '$') {
+ if ($this->security) {
+ $this->_syntax_error('(secure) call to dynamic object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
+ } else {
+ $_output .= '->{(($_var=$this->_tpl_vars[\''.substr($_index,3).'\']) && substr($_var,0,2)!=\'__\') ? $_var : $this->trigger_error("cannot access property \\"$_var\\"")}';
+ }
+ } else {
+ $_output .= $_index;
+ }
+ } elseif (substr($_index, 0, 1) == '(') {
+ $_index = $this->_parse_parenth_args($_index);
+ $_output .= $_index;
+ } else {
+ $_output .= $_index;
+ }
+ }
+ }
+
+ return $_output;
+ }
+
+ /**
+ * parse arguments in function call parenthesis
+ *
+ * @param string $parenth_args
+ * @return string
+ */
+ function _parse_parenth_args($parenth_args)
+ {
+ preg_match_all('~' . $this->_param_regexp . '~',$parenth_args, $match);
+ $orig_vals = $match = $match[0];
+ $this->_parse_vars_props($match);
+ $replace = array();
+ for ($i = 0, $count = count($match); $i < $count; $i++) {
+ $replace[$orig_vals[$i]] = $match[$i];
+ }
+ return strtr($parenth_args, $replace);
+ }
+
+ /**
+ * parse configuration variable expression into PHP code
+ *
+ * @param string $conf_var_expr
+ */
+ function _parse_conf_var($conf_var_expr)
+ {
+ $parts = explode('|', $conf_var_expr, 2);
+ $var_ref = $parts[0];
+ $modifiers = isset($parts[1]) ? $parts[1] : '';
+
+ $var_name = substr($var_ref, 1, -1);
+
+ $output = "\$this->_config[0]['vars']['$var_name']";
+
+ $this->_parse_modifiers($output, $modifiers);
+
+ return $output;
+ }
+
+ /**
+ * parse section property expression into PHP code
+ *
+ * @param string $section_prop_expr
+ * @return string
+ */
+ function _parse_section_prop($section_prop_expr)
+ {
+ $parts = explode('|', $section_prop_expr, 2);
+ $var_ref = $parts[0];
+ $modifiers = isset($parts[1]) ? $parts[1] : '';
+
+ preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match);
+ $section_name = $match[1];
+ $prop_name = $match[2];
+
+ $output = "\$this->_sections['$section_name']['$prop_name']";
+
+ $this->_parse_modifiers($output, $modifiers);
+
+ return $output;
+ }
+
+
+ /**
+ * parse modifier chain into PHP code
+ *
+ * sets $output to parsed modified chain
+ * @param string $output
+ * @param string $modifier_string
+ */
+ function _parse_modifiers(&$output, $modifier_string)
+ {
+ preg_match_all('~\|(@?\w+)((?>:(?:'. $this->_qstr_regexp . '|[^|]+))*)~', '|' . $modifier_string, $_match);
+ list(, $_modifiers, $modifier_arg_strings) = $_match;
+
+ for ($_i = 0, $_for_max = count($_modifiers); $_i < $_for_max; $_i++) {
+ $_modifier_name = $_modifiers[$_i];
+
+ if($_modifier_name == 'smarty') {
+ // skip smarty modifier
+ continue;
+ }
+
+ preg_match_all('~:(' . $this->_qstr_regexp . '|[^:]+)~', $modifier_arg_strings[$_i], $_match);
+ $_modifier_args = $_match[1];
+
+ if (substr($_modifier_name, 0, 1) == '@') {
+ $_map_array = false;
+ $_modifier_name = substr($_modifier_name, 1);
+ } else {
+ $_map_array = true;
+ }
+
+ if (empty($this->_plugins['modifier'][$_modifier_name])
+ && !$this->_get_plugin_filepath('modifier', $_modifier_name)
+ && function_exists($_modifier_name)) {
+ if ($this->security && !in_array($_modifier_name, $this->security_settings['MODIFIER_FUNCS'])) {
+ $this->_trigger_fatal_error("[plugin] (secure mode) modifier '$_modifier_name' is not allowed" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
+ } else {
+ $this->_plugins['modifier'][$_modifier_name] = array($_modifier_name, null, null, false);
+ }
+ }
+ $this->_add_plugin('modifier', $_modifier_name);
+
+ $this->_parse_vars_props($_modifier_args);
+
+ if($_modifier_name == 'default') {
+ // supress notifications of default modifier vars and args
+ if(substr($output, 0, 1) == '$') {
+ $output = '@' . $output;
+ }
+ if(isset($_modifier_args[0]) && substr($_modifier_args[0], 0, 1) == '$') {
+ $_modifier_args[0] = '@' . $_modifier_args[0];
+ }
+ }
+ if (count($_modifier_args) > 0)
+ $_modifier_args = ', '.implode(', ', $_modifier_args);
+ else
+ $_modifier_args = '';
+
+ if ($_map_array) {
+ $output = "((is_array(\$_tmp=$output)) ? \$this->_run_mod_handler('$_modifier_name', true, \$_tmp$_modifier_args) : " . $this->_compile_plugin_call('modifier', $_modifier_name) . "(\$_tmp$_modifier_args))";
+
+ } else {
+
+ $output = $this->_compile_plugin_call('modifier', $_modifier_name)."($output$_modifier_args)";
+
+ }
+ }
+ }
+
+
+ /**
+ * add plugin
+ *
+ * @param string $type
+ * @param string $name
+ * @param boolean? $delayed_loading
+ */
+ function _add_plugin($type, $name, $delayed_loading = null)
+ {
+ if (!isset($this->_plugin_info[$type])) {
+ $this->_plugin_info[$type] = array();
+ }
+ if (!isset($this->_plugin_info[$type][$name])) {
+ $this->_plugin_info[$type][$name] = array($this->_current_file,
+ $this->_current_line_no,
+ $delayed_loading);
+ }
+ }
+
+
+ /**
+ * Compiles references of type $smarty.foo
+ *
+ * @param string $indexes
+ * @return string
+ */
+ function _compile_smarty_ref(&$indexes)
+ {
+ /* Extract the reference name. */
+ $_ref = substr($indexes[0], 1);
+ foreach($indexes as $_index_no=>$_index) {
+ if (substr($_index, 0, 1) != '.' && $_index_no<2 || !preg_match('~^(\.|\[|->)~', $_index)) {
+ $this->_syntax_error('$smarty' . implode('', array_slice($indexes, 0, 2)) . ' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
+ }
+ }
+
+ switch ($_ref) {
+ case 'now':
+ $compiled_ref = 'time()';
+ $_max_index = 1;
+ break;
+
+ case 'foreach':
+ array_shift($indexes);
+ $_var = $this->_parse_var_props(substr($indexes[0], 1));
+ $_propname = substr($indexes[1], 1);
+ $_max_index = 1;
+ switch ($_propname) {
+ case 'index':
+ array_shift($indexes);
+ $compiled_ref = "(\$this->_foreach[$_var]['iteration']-1)";
+ break;
+
+ case 'first':
+ array_shift($indexes);
+ $compiled_ref = "(\$this->_foreach[$_var]['iteration'] <= 1)";
+ break;
+
+ case 'last':
+ array_shift($indexes);
+ $compiled_ref = "(\$this->_foreach[$_var]['iteration'] == \$this->_foreach[$_var]['total'])";
+ break;
+
+ case 'show':
+ array_shift($indexes);
+ $compiled_ref = "(\$this->_foreach[$_var]['total'] > 0)";
+ break;
+
+ default:
+ unset($_max_index);
+ $compiled_ref = "\$this->_foreach[$_var]";
+ }
+ break;
+
+ case 'section':
+ array_shift($indexes);
+ $_var = $this->_parse_var_props(substr($indexes[0], 1));
+ $compiled_ref = "\$this->_sections[$_var]";
+ break;
+
+ case 'get':
+ $compiled_ref = ($this->request_use_auto_globals) ? '$_GET' : "\$GLOBALS['HTTP_GET_VARS']";
+ break;
+
+ case 'post':
+ $compiled_ref = ($this->request_use_auto_globals) ? '$_POST' : "\$GLOBALS['HTTP_POST_VARS']";
+ break;
+
+ case 'cookies':
+ $compiled_ref = ($this->request_use_auto_globals) ? '$_COOKIE' : "\$GLOBALS['HTTP_COOKIE_VARS']";
+ break;
+
+ case 'env':
+ $compiled_ref = ($this->request_use_auto_globals) ? '$_ENV' : "\$GLOBALS['HTTP_ENV_VARS']";
+ break;
+
+ case 'server':
+ $compiled_ref = ($this->request_use_auto_globals) ? '$_SERVER' : "\$GLOBALS['HTTP_SERVER_VARS']";
+ break;
+
+ case 'session':
+ $compiled_ref = ($this->request_use_auto_globals) ? '$_SESSION' : "\$GLOBALS['HTTP_SESSION_VARS']";
+ break;
+
+ /*
+ * These cases are handled either at run-time or elsewhere in the
+ * compiler.
+ */
+ case 'request':
+ if ($this->request_use_auto_globals) {
+ $compiled_ref = '$_REQUEST';
+ break;
+ } else {
+ $this->_init_smarty_vars = true;
+ }
+ return null;
+
+ case 'capture':
+ return null;
+
+ case 'template':
+ $compiled_ref = "'$this->_current_file'";
+ $_max_index = 1;
+ break;
+
+ case 'version':
+ $compiled_ref = "'$this->_version'";
+ $_max_index = 1;
+ break;
+
+ case 'const':
+ if ($this->security && !$this->security_settings['ALLOW_CONSTANTS']) {
+ $this->_syntax_error("(secure mode) constants not permitted",
+ E_USER_WARNING, __FILE__, __LINE__);
+ return;
+ }
+ array_shift($indexes);
+ if (preg_match('!^\.\w+$!', $indexes[0])) {
+ $compiled_ref = '@' . substr($indexes[0], 1);
+ } else {
+ $_val = $this->_parse_var_props(substr($indexes[0], 1));
+ $compiled_ref = '@constant(' . $_val . ')';
+ }
+ $_max_index = 1;
+ break;
+
+ case 'config':
+ $compiled_ref = "\$this->_config[0]['vars']";
+ $_max_index = 3;
+ break;
+
+ case 'ldelim':
+ $compiled_ref = "'$this->left_delimiter'";
+ break;
+
+ case 'rdelim':
+ $compiled_ref = "'$this->right_delimiter'";
+ break;
+
+ default:
+ $this->_syntax_error('$smarty.' . $_ref . ' is an unknown reference', E_USER_ERROR, __FILE__, __LINE__);
+ break;
+ }
+
+ if (isset($_max_index) && count($indexes) > $_max_index) {
+ $this->_syntax_error('$smarty' . implode('', $indexes) .' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
+ }
+
+ array_shift($indexes);
+ return $compiled_ref;
+ }
+
+ /**
+ * compiles call to plugin of type $type with name $name
+ * returns a string containing the function-name or method call
+ * without the paramter-list that would have follow to make the
+ * call valid php-syntax
+ *
+ * @param string $type
+ * @param string $name
+ * @return string
+ */
+ function _compile_plugin_call($type, $name) {
+ if (isset($this->_plugins[$type][$name])) {
+ /* plugin loaded */
+ if (is_array($this->_plugins[$type][$name][0])) {
+ return ((is_object($this->_plugins[$type][$name][0][0])) ?
+ "\$this->_plugins['$type']['$name'][0][0]->" /* method callback */
+ : (string)($this->_plugins[$type][$name][0][0]).'::' /* class callback */
+ ). $this->_plugins[$type][$name][0][1];
+
+ } else {
+ /* function callback */
+ return $this->_plugins[$type][$name][0];
+
+ }
+ } else {
+ /* plugin not loaded -> auto-loadable-plugin */
+ return 'smarty_'.$type.'_'.$name;
+
+ }
+ }
+
+ /**
+ * load pre- and post-filters
+ */
+ function _load_filters()
+ {
+ if (count($this->_plugins['prefilter']) > 0) {
+ foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
+ if ($prefilter === false) {
+ unset($this->_plugins['prefilter'][$filter_name]);
+ $_params = array('plugins' => array(array('prefilter', $filter_name, null, null, false)));
+ require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
+ smarty_core_load_plugins($_params, $this);
+ }
+ }
+ }
+ if (count($this->_plugins['postfilter']) > 0) {
+ foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
+ if ($postfilter === false) {
+ unset($this->_plugins['postfilter'][$filter_name]);
+ $_params = array('plugins' => array(array('postfilter', $filter_name, null, null, false)));
+ require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
+ smarty_core_load_plugins($_params, $this);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Quote subpattern references
+ *
+ * @param string $string
+ * @return string
+ */
+ function _quote_replace($string)
+ {
+ return strtr($string, array('\\' => '\\\\', '$' => '\\$'));
+ }
+
+ /**
+ * display Smarty syntax error
+ *
+ * @param string $error_msg
+ * @param integer $error_type
+ * @param string $file
+ * @param integer $line
+ */
+ function _syntax_error($error_msg, $error_type = E_USER_ERROR, $file=null, $line=null)
+ {
+ $this->_trigger_fatal_error("syntax error: $error_msg", $this->_current_file, $this->_current_line_no, $file, $line, $error_type);
+ }
+
+
+ /**
+ * check if the compilation changes from cacheable to
+ * non-cacheable state with the beginning of the current
+ * plugin. return php-code to reflect the transition.
+ * @return string
+ */
+ function _push_cacheable_state($type, $name) {
+ $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
+ if ($_cacheable
+ || 0<$this->_cacheable_state++) return '';
+ if (!isset($this->_cache_serial)) $this->_cache_serial = md5(uniqid('Smarty'));
+ $_ret = 'if ($this->caching && !$this->_cache_including): echo \'{nocache:'
+ . $this->_cache_serial . '#' . $this->_nocache_count
+ . '}\'; endif;';
+ return $_ret;
+ }
+
+
+ /**
+ * check if the compilation changes from non-cacheable to
+ * cacheable state with the end of the current plugin return
+ * php-code to reflect the transition.
+ * @return string
+ */
+ function _pop_cacheable_state($type, $name) {
+ $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
+ if ($_cacheable
+ || --$this->_cacheable_state>0) return '';
+ return 'if ($this->caching && !$this->_cache_including): echo \'{/nocache:'
+ . $this->_cache_serial . '#' . ($this->_nocache_count++)
+ . '}\'; endif;';
+ }
+
+
+ /**
+ * push opening tag-name, file-name and line-number on the tag-stack
+ * @param string the opening tag's name
+ */
+ function _push_tag($open_tag)
+ {
+ array_push($this->_tag_stack, array($open_tag, $this->_current_line_no));
+ }
+
+ /**
+ * pop closing tag-name
+ * raise an error if this stack-top doesn't match with the closing tag
+ * @param string the closing tag's name
+ * @return string the opening tag's name
+ */
+ function _pop_tag($close_tag)
+ {
+ $message = '';
+ if (count($this->_tag_stack)>0) {
+ list($_open_tag, $_line_no) = array_pop($this->_tag_stack);
+ if ($close_tag == $_open_tag) {
+ return $_open_tag;
+ }
+ if ($close_tag == 'if' && ($_open_tag == 'else' || $_open_tag == 'elseif' )) {
+ return $this->_pop_tag($close_tag);
+ }
+ if ($close_tag == 'section' && $_open_tag == 'sectionelse') {
+ $this->_pop_tag($close_tag);
+ return $_open_tag;
+ }
+ if ($close_tag == 'foreach' && $_open_tag == 'foreachelse') {
+ $this->_pop_tag($close_tag);
+ return $_open_tag;
+ }
+ if ($_open_tag == 'else' || $_open_tag == 'elseif') {
+ $_open_tag = 'if';
+ } elseif ($_open_tag == 'sectionelse') {
+ $_open_tag = 'section';
+ } elseif ($_open_tag == 'foreachelse') {
+ $_open_tag = 'foreach';
+ }
+ $message = " expected {/$_open_tag} (opened line $_line_no).";
+ }
+ $this->_syntax_error("mismatched tag {/$close_tag}.$message",
+ E_USER_ERROR, __FILE__, __LINE__);
+ }
+
+}
+
+/**
+ * compare to values by their string length
+ *
+ * @access private
+ * @param string $a
+ * @param string $b
+ * @return 0|-1|1
+ */
+function _smarty_sort_length($a, $b)
+{
+ if($a == $b)
+ return 0;
+
+ if(strlen($a) == strlen($b))
+ return ($a > $b) ? -1 : 1;
+
+ return (strlen($a) > strlen($b)) ? -1 : 1;
+}
+
+
+/* vim: set et: */
+
+?>
diff --git a/BSF/include/smarty/libs/debug.tpl b/BSF/include/smarty/libs/debug.tpl
new file mode 100644
index 000000000..c05ef5d0b
--- /dev/null
+++ b/BSF/include/smarty/libs/debug.tpl
@@ -0,0 +1,157 @@
+{* Smarty *}
+{* debug.tpl, last updated version 2.1.0 *}
+{assign_debug_info}
+{capture assign=debug_output}
+
+
+
+ Smarty Debug Console
+{literal}
+
+{/literal}
+
+
+
+
Smarty Debug Console
+
+
included templates & config files (load time in seconds)