aboutsummaryrefslogtreecommitdiffstats
path: root/BSF/include
diff options
context:
space:
mode:
Diffstat (limited to 'BSF/include')
-rw-r--r--BSF/include/calendar_base.class.php351
-rw-r--r--BSF/include/calendar_monthly.class.php515
-rw-r--r--BSF/include/calendar_weekly.class.php136
-rw-r--r--BSF/include/category_cats.inc.php298
-rw-r--r--BSF/include/category_default.inc.php170
-rw-r--r--BSF/include/class_smtp_mail.inc.php157
-rw-r--r--BSF/include/common.inc.php283
-rw-r--r--BSF/include/config_default.inc.php700
-rw-r--r--BSF/include/constants.php112
-rw-r--r--BSF/include/feedcreator.class.php1541
-rw-r--r--BSF/include/filter.inc.php141
-rw-r--r--BSF/include/functions.inc.php1557
-rw-r--r--BSF/include/functions_calendar.inc.php295
-rw-r--r--BSF/include/functions_category.inc.php509
-rw-r--r--BSF/include/functions_comment.inc.php228
-rw-r--r--BSF/include/functions_cookie.inc.php115
-rw-r--r--BSF/include/functions_filter.inc.php63
-rw-r--r--BSF/include/functions_group.inc.php26
-rw-r--r--BSF/include/functions_html.inc.php751
-rw-r--r--BSF/include/functions_mail.inc.php822
-rw-r--r--BSF/include/functions_metadata.inc.php142
-rw-r--r--BSF/include/functions_notification.inc.php608
-rw-r--r--BSF/include/functions_picture.inc.php330
-rw-r--r--BSF/include/functions_plugins.inc.php284
-rw-r--r--BSF/include/functions_rate.inc.php134
-rw-r--r--BSF/include/functions_search.inc.php560
-rw-r--r--BSF/include/functions_session.inc.php225
-rw-r--r--BSF/include/functions_tag.inc.php323
-rw-r--r--BSF/include/functions_url.inc.php740
-rw-r--r--BSF/include/functions_user.inc.php1340
-rw-r--r--BSF/include/functions_xml.inc.php141
-rw-r--r--BSF/include/index.php30
-rw-r--r--BSF/include/menubar.inc.php311
-rw-r--r--BSF/include/page_header.php90
-rw-r--r--BSF/include/page_tail.php74
-rw-r--r--BSF/include/php_compat/array_intersect_key.php35
-rw-r--r--BSF/include/php_compat/file_put_contents.php14
-rw-r--r--BSF/include/php_compat/hash_hmac.php25
-rw-r--r--BSF/include/php_compat/index.php30
-rw-r--r--BSF/include/php_compat/preg_last_error.php41
-rw-r--r--BSF/include/picture_comment.inc.php183
-rw-r--r--BSF/include/picture_metadata.inc.php99
-rw-r--r--BSF/include/picture_rate.inc.php91
-rw-r--r--BSF/include/section_init.inc.php620
-rw-r--r--BSF/include/smarty/COPYING.lib458
-rw-r--r--BSF/include/smarty/NEWS1024
-rw-r--r--BSF/include/smarty/README85
-rw-r--r--BSF/include/smarty/libs/Config_File.class.php389
-rw-r--r--BSF/include/smarty/libs/Smarty.class.php1968
-rw-r--r--BSF/include/smarty/libs/Smarty_Compiler.class.php2325
-rw-r--r--BSF/include/smarty/libs/debug.tpl157
-rw-r--r--BSF/include/smarty/libs/internals/core.assemble_plugin_filepath.php67
-rw-r--r--BSF/include/smarty/libs/internals/core.assign_smarty_interface.php43
-rw-r--r--BSF/include/smarty/libs/internals/core.create_dir_structure.php79
-rw-r--r--BSF/include/smarty/libs/internals/core.display_debug_console.php61
-rw-r--r--BSF/include/smarty/libs/internals/core.get_include_path.php44
-rw-r--r--BSF/include/smarty/libs/internals/core.get_microtime.php23
-rw-r--r--BSF/include/smarty/libs/internals/core.get_php_resource.php80
-rw-r--r--BSF/include/smarty/libs/internals/core.is_secure.php59
-rw-r--r--BSF/include/smarty/libs/internals/core.is_trusted.php47
-rw-r--r--BSF/include/smarty/libs/internals/core.load_plugins.php125
-rw-r--r--BSF/include/smarty/libs/internals/core.load_resource_plugin.php74
-rw-r--r--BSF/include/smarty/libs/internals/core.process_cached_inserts.php71
-rw-r--r--BSF/include/smarty/libs/internals/core.process_compiled_include.php37
-rw-r--r--BSF/include/smarty/libs/internals/core.read_cache_file.php101
-rw-r--r--BSF/include/smarty/libs/internals/core.rm_auto.php71
-rw-r--r--BSF/include/smarty/libs/internals/core.rmdir.php54
-rw-r--r--BSF/include/smarty/libs/internals/core.run_insert_handler.php71
-rw-r--r--BSF/include/smarty/libs/internals/core.smarty_include_php.php50
-rw-r--r--BSF/include/smarty/libs/internals/core.write_cache_file.php96
-rw-r--r--BSF/include/smarty/libs/internals/core.write_compiled_include.php91
-rw-r--r--BSF/include/smarty/libs/internals/core.write_compiled_resource.php35
-rw-r--r--BSF/include/smarty/libs/internals/core.write_file.php54
-rw-r--r--BSF/include/smarty/libs/plugins/block.textformat.php103
-rw-r--r--BSF/include/smarty/libs/plugins/compiler.assign.php40
-rw-r--r--BSF/include/smarty/libs/plugins/function.assign_debug_info.php40
-rw-r--r--BSF/include/smarty/libs/plugins/function.config_load.php142
-rw-r--r--BSF/include/smarty/libs/plugins/function.counter.php80
-rw-r--r--BSF/include/smarty/libs/plugins/function.cycle.php102
-rw-r--r--BSF/include/smarty/libs/plugins/function.debug.php35
-rw-r--r--BSF/include/smarty/libs/plugins/function.eval.php49
-rw-r--r--BSF/include/smarty/libs/plugins/function.fetch.php221
-rw-r--r--BSF/include/smarty/libs/plugins/function.html_checkboxes.php143
-rw-r--r--BSF/include/smarty/libs/plugins/function.html_image.php142
-rw-r--r--BSF/include/smarty/libs/plugins/function.html_options.php122
-rw-r--r--BSF/include/smarty/libs/plugins/function.html_radios.php156
-rw-r--r--BSF/include/smarty/libs/plugins/function.html_select_date.php331
-rw-r--r--BSF/include/smarty/libs/plugins/function.html_select_time.php194
-rw-r--r--BSF/include/smarty/libs/plugins/function.html_table.php177
-rw-r--r--BSF/include/smarty/libs/plugins/function.mailto.php165
-rw-r--r--BSF/include/smarty/libs/plugins/function.math.php84
-rw-r--r--BSF/include/smarty/libs/plugins/function.popup.php119
-rw-r--r--BSF/include/smarty/libs/plugins/function.popup_init.php40
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.capitalize.php43
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.cat.php33
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.count_characters.php32
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.count_paragraphs.php29
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.count_sentences.php29
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.count_words.php33
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.date_format.php58
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.debug_print_var.php90
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.default.php32
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.escape.php93
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.indent.php28
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.lower.php26
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.nl2br.php35
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.regex_replace.php37
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.replace.php30
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.spacify.php30
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.string_format.php29
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.strip.php33
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.strip_tags.php32
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.truncate.php50
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.upper.php26
-rw-r--r--BSF/include/smarty/libs/plugins/modifier.wordwrap.php29
-rw-r--r--BSF/include/smarty/libs/plugins/outputfilter.trimwhitespace.php75
-rw-r--r--BSF/include/smarty/libs/plugins/shared.escape_special_chars.php31
-rw-r--r--BSF/include/smarty/libs/plugins/shared.make_timestamp.php46
-rw-r--r--BSF/include/template.class.php385
-rw-r--r--BSF/include/upload.class.php41
-rw-r--r--BSF/include/user.inc.php74
-rw-r--r--BSF/include/ws_core.inc.php619
-rw-r--r--BSF/include/ws_functions.inc.php1103
-rw-r--r--BSF/include/ws_protocols/index.php30
-rw-r--r--BSF/include/ws_protocols/json_encoder.php87
-rw-r--r--BSF/include/ws_protocols/php_encoder.php54
-rw-r--r--BSF/include/ws_protocols/rest_encoder.php281
-rw-r--r--BSF/include/ws_protocols/rest_handler.php57
-rw-r--r--BSF/include/ws_protocols/xmlrpc_encoder.php115
129 files changed, 29419 insertions, 0 deletions
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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+/**
+ * Base class for monthly and weekly calendar styles
+ */
+class CalendarBase
+{
+ // db column on which this calendar works
+ var $date_field;
+ // used for queries (INNER JOIN or normal)
+ var $inner_sql;
+ //
+ var $calendar_levels;
+
+ /**
+ * Initialize the calendar
+ * @param string inner_sql used for queries (INNER JOIN or normal)
+ */
+ function initialize($inner_sql)
+ {
+ global $page;
+ if ($page['chronology_field']=='posted')
+ {
+ $this->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<count($page['chronology_date']); $i++)
+ {
+ $res .= $conf['level_separator'];
+ if ( isset($page['chronology_date'][$i+1]) )
+ {
+ $chronology_date = array_slice($page['chronology_date'],0, $i+1);
+ $url = duplicate_index_url(
+ array( 'chronology_date'=>$chronology_date ),
+ array( 'start' )
+ );
+ $res .=
+ '<a href="'.$url.'">'
+ .$this->get_date_component_label($i, $page['chronology_date'][$i])
+ .'</a>';
+ }
+ else
+ {
+ $res .=
+ '<span class="calInHere">'
+ .$this->get_date_component_label($i, $page['chronology_date'][$i])
+ .'</span>';
+ }
+ }
+ 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 .= '<span class="'.$class_prefix.'Empty">';
+ $nav_bar .= $label;
+ }
+ else
+ {
+ $nav_bar .= '<span class="'.$class_prefix.'">';
+ $url = duplicate_index_url(
+ array('chronology_date'=>array_merge($date_components,array($item))),
+ array( 'start' )
+ );
+ $nav_bar .= '<a href="'.$url.'">';
+ $nav_bar .= $label;
+ $nav_bar .= '</a>';
+ }
+ if ($nb_images > 0)
+ {
+ $nav_bar .= '('.$nb_images.')';
+ }
+ $nav_bar.= '</span>';
+ }
+
+ if ($conf['calendar_show_any'] and $show_any and count($items)>1 and
+ count($date_components)<count($this->calendar_levels)-1 )
+ {
+ $label = l10n('calendar_any');
+ $nav_bar .= '<span class="'.$class_prefix.'">';
+ $url = duplicate_index_url(
+ array('chronology_date'=>array_merge($date_components,array('any'))),
+ array( 'start' )
+ );
+ $nav_bar .= '<a href="'.$url.'">';
+ $nav_bar .= $label;
+ $nav_bar .= '</a>';
+ $nav_bar.= '</span>';
+ }
+ 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'])<count($this->calendar_levels)-1)
+ {
+ if ( ! isset($page['chronology_date'][$level]) )
+ {
+ list($key) = array_keys($level_items);
+ $page['chronology_date'][$level] = (int)$key;
+
+ if ( $level<count($page['chronology_date']) and
+ $level!=count($this->calendar_levels)-1 )
+ {
+ return;
+ }
+ }
+ }
+
+ $dates = $page['chronology_date'];
+ while ($level<count($dates))
+ {
+ array_pop($dates);
+ }
+
+ $nav_bar = $this->get_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; $i<count($page['chronology_date']); $i++)
+ {
+ if ( 'any' === $page['chronology_date'][$i] )
+ {
+ $query .= ','.'"any"';
+ }
+ else
+ {
+ $query .= ','.$this->calendar_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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+include_once(PHPWG_ROOT_PATH.'include/calendar_base.class.php');
+
+define ('CYEAR', 0);
+define ('CMONTH', 1);
+define ('CDAY', 2);
+
+/**
+ * Monthly calendar style (composed of years/months and days)
+ */
+class Calendar extends CalendarBase
+{
+
+ /**
+ * Initialize the calendar
+ * @param string inner_sql used for queries (INNER JOIN or normal)
+ */
+ function initialize($inner_sql)
+ {
+ parent::initialize($inner_sql);
+ global $lang;
+ $this->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 ('<pre>'. var_export($items, true) . '</pre>');
+ 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 <img>.
+ $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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+include_once(PHPWG_ROOT_PATH.'include/calendar_base.class.php');
+
+define ('CYEAR', 0);
+define ('CWEEK', 1);
+define ('CDAY', 2);
+
+/**
+ * Weekly calendar style (composed of years/week in years and days in week)
+ */
+class Calendar extends CalendarBase
+{
+
+ /**
+ * Initialize the calendar
+ * @param string inner_sql used for queries (INNER JOIN or normal)
+ */
+ function initialize($inner_sql)
+ {
+ parent::initialize($inner_sql);
+ global $lang;
+ $week_no_labels=array();
+ for ($i=1; $i<=53; $i++)
+ {
+ $week_no_labels[$i] = sprintf( l10n("Week %d"), $i);
+ //$week_no_labels[$i] = $i;
+ }
+
+ $this->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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+/**
+ * This file is included by the main page to show thumbnails for a category
+ * that have only subcategories or to show recent categories
+ *
+ */
+
+if ($page['section']=='recent_cats')
+{
+ // $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, global_rank
+ FROM '.CATEGORIES_TABLE.' INNER JOIN '.USER_CACHE_CATEGORIES_TABLE.'
+ ON id = cat_id and user_id = '.$user['id'].'
+ WHERE date_last >= 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,
+ '<br />'
+ ),
+ '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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+/**
+ * This file is included by the main page to show thumbnails for the default
+ * case
+ *
+ */
+
+$page['rank_of'] = array_flip($page['items']);
+
+$pictures = array();
+
+$selection = array_slice(
+ $page['items'],
+ $page['start'],
+ $page['nb_image_page']
+ );
+
+if (count($selection) > 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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+// These function were originally a part of the punBB.
+
+class smtp_mail
+{
+ var $socket;
+ var $no_error;
+ var $host;
+ var $user;
+ var $password;
+ var $email_webmaster;
+
+ function smtp_mail($host, $user, $password, $email_webmaster)
+ {
+ $this->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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+if (!defined('PHPWG_ROOT_PATH'))
+{
+ die('Hacking attempt!');
+}
+// determine the initial instant to indicate the generation time of this page
+$t1 = explode( ' ', microtime() );
+$t2 = explode( '.', $t1[0] );
+$t2 = $t1[1].'.'.$t2[1];
+
+set_magic_quotes_runtime(0); // Disable magic_quotes_runtime
+
+//
+// addslashes to vars if magic_quotes_gpc is off this is a security
+// precaution to prevent someone trying to break out of a SQL statement.
+//
+if( !get_magic_quotes_gpc() )
+{
+ if( is_array( $_GET ) )
+ {
+ while( list($k, $v) = each($_GET) )
+ {
+ if( is_array($_GET[$k]) )
+ {
+ while( list($k2, $v2) = each($_GET[$k]) )
+ {
+ $_GET[$k][$k2] = addslashes($v2);
+ }
+ @reset($_GET[$k]);
+ }
+ else
+ {
+ $_GET[$k] = addslashes($v);
+ }
+ }
+ @reset($_GET);
+ }
+
+ if( is_array($_POST) )
+ {
+ while( list($k, $v) = each($_POST) )
+ {
+ if( is_array($_POST[$k]) )
+ {
+ while( list($k2, $v2) = each($_POST[$k]) )
+ {
+ $_POST[$k][$k2] = addslashes($v2);
+ }
+ @reset($_POST[$k]);
+ }
+ else
+ {
+ $_POST[$k] = addslashes($v);
+ }
+ }
+ @reset($_POST);
+ }
+
+ if( is_array($_COOKIE) )
+ {
+ while( list($k, $v) = each($_COOKIE) )
+ {
+ if( is_array($_COOKIE[$k]) )
+ {
+ while( list($k2, $v2) = each($_COOKIE[$k]) )
+ {
+ $_COOKIE[$k][$k2] = addslashes($v2);
+ }
+ @reset($_COOKIE[$k]);
+ }
+ else
+ {
+ $_COOKIE[$k] = addslashes($v);
+ }
+ }
+ @reset($_COOKIE);
+ }
+}
+
+//
+// Define some basic configuration arrays this also prevents malicious
+// rewriting of language and otherarray values via URI params
+//
+$conf = array();
+$page = array();
+$user = array();
+$lang = array();
+$header_msgs = array();
+$header_notes = array();
+$filter = array();
+
+@include(PHPWG_ROOT_PATH .'include/mysql.inc.php');
+if (!defined('PHPWG_INSTALLED'))
+{
+ header('Location: install.php');
+ exit;
+}
+
+foreach( array(
+ 'array_intersect_key', //PHP 5 >= 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')
+ .'<a href="'.get_absolute_root_url(false).'identification.php">.</a>';
+ 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, '
+ .'<a href="'.get_absolute_root_url(false).'upgrade_feed.php">upgrade now</a>';
+ }
+}
+
+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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+/**
+ * configuration page
+ *
+ * Set configuration parameters that are not in the table config. In the
+ * application, configuration parameters are considered in the same way
+ * coming from config table or config_default.inc.php.
+ *
+ * It is recommended to let config_default.inc.php as provided and to
+ * overwrite configuration in your local configuration file
+ * config_local.inc.php. See tools/config_local.inc.php as an example.
+ *
+ * Why having some parameters in config table and others in
+ * config_*.inc.php? Modifying config_*.inc.php is a "hard" task for low
+ * skilled users, they need a GUI for this : admin/configuration. But only
+ * parameters that might be modified by low skilled users are in config
+ * table, other parameters are in config_*.inc.php
+ */
+
+// +-----------------------------------------------------------------------+
+// | misc |
+// +-----------------------------------------------------------------------+
+
+// order_by : how to change the order of display for images in a category ?
+//
+// There are several fields that can order the display :
+//
+// - date_available : the date of the adding to the gallery
+// - file : the name of the file
+// - id : element identifier
+// - date_creation : date of element creation
+//
+// Once you've chosen which field(s) to use for ordering, you must chose the
+// ascending or descending order for each field. examples :
+//
+// 1. $conf['order_by'] = " order by date_available desc, file asc";
+// will order pictures by date_available descending & by filename ascending
+//
+// 2. $conf['order_by'] = " order by file asc";
+// will only order pictures by file ascending without taking into account
+// the date_available
+$conf['order_by'] = ' ORDER BY date_available DESC, file ASC, id ASC';
+
+// file_ext : file extensions (case sensitive) authorized
+$conf['file_ext'] = array('jpg','JPG','jpeg','JPEG',
+ 'png','PNG','gif','GIF','mpg','zip',
+ 'avi','mp3','ogg');
+
+// picture_ext : file extensions for picture file, must be a subset of
+// file_ext
+$conf['picture_ext'] = array('jpg','JPG','jpeg','JPEG',
+ 'png','PNG','gif','GIF');
+
+// top_number : number of element to display for "best rated" and "most
+// visited" categories
+$conf['top_number'] = 15;
+
+// anti-flood_time : number of seconds between 2 comments : 0 to disable
+$conf['anti-flood_time'] = 60;
+
+// qualified spam comments are not registered (false will register them
+// but they will require admin validation)
+$conf['comment_spam_reject'] = true;
+
+// maximum number of links in a comment before it is qualified spam
+$conf['comment_spam_max_links'] = 3;
+
+// calendar_datefield : date field of table "images" used for calendar
+// catgory
+$conf['calendar_datefield'] = 'date_creation';
+
+// calendar_show_any : the calendar shows an aditional 'any' button in the
+// year/month/week/day navigation bars
+$conf['calendar_show_any'] = true;
+
+// calendar_show_empty : the calendar shows month/weeks/days even if there are
+//no elements for these
+$conf['calendar_show_empty'] = true;
+
+// calendar_month_cell_width, calendar_month_cell_height : define the
+// width and the height of a cell in the monthly calendar when viewing a
+// given month. a value of 0 means that the pretty view is not shown.
+// a good suggestion would be to have the width and the height equal
+// and smaller than tn_width and tn_height.
+$conf['calendar_month_cell_width'] =80;
+$conf['calendar_month_cell_height']=80;
+
+// newcat_default_commentable : at creation, must a category be commentable
+// or not ?
+$conf['newcat_default_commentable'] = true;
+
+// newcat_default_uploadable : at creation, must a category be uploadable or
+// not ?
+$conf['newcat_default_uploadable'] = false;
+
+// newcat_default_visible : at creation, must a category be visible or not ?
+// Warning : if the parent category is invisible, the category is
+// automatically create invisible. (invisible = locked)
+$conf['newcat_default_visible'] = true;
+
+// newcat_default_status : at creation, must a category be public or private
+// ? Warning : if the parent category is private, the category is
+// automatically create private.
+$conf['newcat_default_status'] = 'public';
+
+// level_separator : character string used for separating a category level
+// to the sub level. Suggestions : ' / ', ' &raquo; ', ' &rarr; ', ' - ',
+// ' &gt;'
+$conf['level_separator'] = ' / ';
+
+// paginate_pages_around : on paginate navigation bar, how many pages
+// display before and after the current page ?
+$conf['paginate_pages_around'] = 2;
+
+// tn_width : default width for thumbnails creation
+$conf['tn_width'] = 128;
+
+// tn_height : default height for thumbnails creation
+$conf['tn_height'] = 128;
+
+// show_version : shall the version of Piwigo be displayed at the
+// bottom of each page ?
+$conf['show_version'] = true;
+
+
+// links : list of external links to add in the menu. An example is the best
+// than a long explanation :
+//
+// Simple use:
+// for each link is associated a label
+// $conf['links'] = array(
+// 'http://piwigo.org' => '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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+// Default settings
+define('PHPWG_VERSION', 'Butterfly');
+define('PHPWG_DOMAIN', 'phpwebgallery.net');
+define('PHPWG_URL', 'http://www.'.PHPWG_DOMAIN);
+define('PEM_URL', 'http://' . PHPWG_DOMAIN . '/ext');
+define('PHPWG_DEFAULT_LANGUAGE', 'en_UK');
+define('PHPWG_DEFAULT_TEMPLATE', 'yoga/clear');
+
+// Required versions
+define('REQUIRED_PHP_VERSION', '5.0.0');
+define('REQUIRED_MYSQL_VERSION', '4.1.22');
+
+// Error codes
+define('GENERAL_MESSAGE', 200);
+define('GENERAL_ERROR', 202);
+define('CRITICAL_MESSAGE', 203);
+define('CRITICAL_ERROR', 204);
+
+// Access codes
+define('ACCESS_FREE', 0);
+define('ACCESS_GUEST', 1);
+define('ACCESS_CLASSIC', 2);
+define('ACCESS_ADMINISTRATOR', 3);
+define('ACCESS_WEBMASTER', 4);
+define('ACCESS_CLOSED', 5);
+
+// Table names
+if (!defined('CATEGORIES_TABLE'))
+ define('CATEGORIES_TABLE', $prefixeTable.'categories');
+if (!defined('COMMENTS_TABLE'))
+ define('COMMENTS_TABLE', $prefixeTable.'comments');
+if (!defined('CONFIG_TABLE'))
+ define('CONFIG_TABLE', $prefixeTable.'config');
+if (!defined('FAVORITES_TABLE'))
+ define('FAVORITES_TABLE', $prefixeTable.'favorites');
+if (!defined('GROUP_ACCESS_TABLE'))
+ define('GROUP_ACCESS_TABLE', $prefixeTable.'group_access');
+if (!defined('GROUPS_TABLE'))
+ define('GROUPS_TABLE', $prefixeTable.'groups');
+if (!defined('HISTORY_TABLE'))
+ define('HISTORY_TABLE', $prefixeTable.'history');
+if (!defined('HISTORY_SUMMARY_TABLE'))
+ define('HISTORY_SUMMARY_TABLE', $prefixeTable.'history_summary');
+if (!defined('IMAGE_CATEGORY_TABLE'))
+ define('IMAGE_CATEGORY_TABLE', $prefixeTable.'image_category');
+if (!defined('IMAGES_TABLE'))
+ define('IMAGES_TABLE', $prefixeTable.'images');
+if (!defined('SESSIONS_TABLE'))
+ define('SESSIONS_TABLE', $prefixeTable.'sessions');
+if (!defined('SITES_TABLE'))
+ define('SITES_TABLE', $prefixeTable.'sites');
+if (!defined('USER_ACCESS_TABLE'))
+ define('USER_ACCESS_TABLE', $prefixeTable.'user_access');
+if (!defined('USER_GROUP_TABLE'))
+ define('USER_GROUP_TABLE', $prefixeTable.'user_group');
+if (!defined('USERS_TABLE'))
+ define('USERS_TABLE', $conf['users_table']);
+if (!defined('USER_INFOS_TABLE'))
+ define('USER_INFOS_TABLE', $prefixeTable.'user_infos');
+if (!defined('USER_FEED_TABLE'))
+ define('USER_FEED_TABLE', $prefixeTable.'user_feed');
+if (!defined('WAITING_TABLE'))
+ define('WAITING_TABLE', $prefixeTable.'waiting');
+if (!defined('RATE_TABLE'))
+ define('RATE_TABLE', $prefixeTable.'rate');
+if (!defined('USER_CACHE_TABLE'))
+ define('USER_CACHE_TABLE', $prefixeTable.'user_cache');
+if (!defined('USER_CACHE_CATEGORIES_TABLE'))
+ define('USER_CACHE_CATEGORIES_TABLE', $prefixeTable.'user_cache_categories');
+if (!defined('CADDIE_TABLE'))
+ define('CADDIE_TABLE', $prefixeTable.'caddie');
+if (!defined('UPGRADE_TABLE'))
+ define('UPGRADE_TABLE', $prefixeTable.'upgrade');
+if (!defined('SEARCH_TABLE'))
+ define('SEARCH_TABLE', $prefixeTable.'search');
+if (!defined('USER_MAIL_NOTIFICATION_TABLE'))
+ define('USER_MAIL_NOTIFICATION_TABLE', $prefixeTable.'user_mail_notification');
+if (!defined('TAGS_TABLE'))
+ define('TAGS_TABLE', $prefixeTable.'tags');
+if (!defined('IMAGE_TAG_TABLE'))
+ define('IMAGE_TAG_TABLE', $prefixeTable.'image_tag');
+if (!defined('PLUGINS_TABLE'))
+ define('PLUGINS_TABLE', $prefixeTable.'plugins');
+if (!defined('WEB_SERVICES_ACCESS_TABLE'))
+ define('WEB_SERVICES_ACCESS_TABLE', $prefixeTable.'ws_access');
+if (!defined('OLD_PERMALINKS_TABLE'))
+ define('OLD_PERMALINKS_TABLE', $prefixeTable.'old_permalinks');
+
+?>
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 @@
+<?php
+/***************************************************************************
+
+FeedCreator class v1.7.2
+originally (c) Kai Blankenhorn
+www.bitfolge.de
+kaib@bitfolge.de
+v1.3 work by Scott Reynen (scott@randomchaos.com) and Kai Blankenhorn
+v1.5 OPML support by Dirk Clemens
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+****************************************************************************
+
+
+Changelog:
+
+v1.7.2 10-11-04
+ license changed to LGPL
+
+v1.7.1
+ fixed a syntax bug
+ fixed left over debug code
+
+v1.7 07-18-04
+ added HTML and JavaScript feeds (configurable via CSS) (thanks to Pascal Van Hecke)
+ added HTML descriptions for all feed formats (thanks to Pascal Van Hecke)
+ added a switch to select an external stylesheet (thanks to Pascal Van Hecke)
+ changed default content-type to application/xml
+ added character encoding setting
+ fixed numerous smaller bugs (thanks to Sören Fuhrmann of golem.de)
+ improved changing ATOM versions handling (thanks to August Trometer)
+ improved the UniversalFeedCreator's useCached method (thanks to Sören Fuhrmann of golem.de)
+ added charset output in HTTP headers (thanks to Sören Fuhrmann of golem.de)
+ added Slashdot namespace to RSS 1.0 (thanks to Sören Fuhrmann of golem.de)
+
+v1.6 05-10-04
+ added stylesheet to RSS 1.0 feeds
+ fixed generator comment (thanks Kevin L. Papendick and Tanguy Pruvot)
+ fixed RFC822 date bug (thanks Tanguy Pruvot)
+ added TimeZone customization for RFC8601 (thanks Tanguy Pruvot)
+ fixed Content-type could be empty (thanks Tanguy Pruvot)
+ fixed author/creator in RSS1.0 (thanks Tanguy Pruvot)
+
+v1.6 beta 02-28-04
+ added Atom 0.3 support (not all features, though)
+ improved OPML 1.0 support (hopefully - added more elements)
+ added support for arbitrary additional elements (use with caution)
+ code beautification :-)
+ considered beta due to some internal changes
+
+v1.5.1 01-27-04
+ fixed some RSS 1.0 glitches (thanks to Stéphane Vanpoperynghe)
+ fixed some inconsistencies between documentation and code (thanks to Timothy Martin)
+
+v1.5 01-06-04
+ added support for OPML 1.0
+ added more documentation
+
+v1.4 11-11-03
+ optional feed saving and caching
+ improved documentation
+ minor improvements
+
+v1.3 10-02-03
+ renamed to FeedCreator, as it not only creates RSS anymore
+ added support for mbox
+ tentative support for echo/necho/atom/pie/???
+
+v1.2 07-20-03
+ intelligent auto-truncating of RSS 0.91 attributes
+ don't create some attributes when they're not set
+ documentation improved
+ fixed a real and a possible bug with date conversions
+ code cleanup
+
+v1.1 06-29-03
+ added images to feeds
+ now includes most RSS 0.91 attributes
+ added RSS 2.0 feeds
+
+v1.0 06-24-03
+ initial release
+
+
+
+***************************************************************************/
+
+/*** GENERAL USAGE *********************************************************
+
+include("feedcreator.class.php");
+
+$rss = new UniversalFeedCreator();
+$rss->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 <kaib@bitfolge.de>
+ * @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 <kaib@bitfolge.de>
+ * @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 <feedcreator.class.php@vanhecke.info>
+ * @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 = "<![CDATA[".$this->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 <kaib@bitfolge.de>
+ */
+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 <kaib@bitfolge.de>
+ * @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 "<!-- generator=\"".FEEDCREATOR_VERSION."\" -->\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 .= "<?xml-stylesheet href=\"".$this->cssStyleSheet."\" type=\"text/css\"?>\n";
+ if ($this->xslStyleSheet) $xml .= "<?xml-stylesheet href=\"".$this->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 "<br /><b>Error creating feed file, please check write permissions.</b><br />";
+ }
+ }
+
+}
+
+
+/**
+ * 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 <kaib@bitfolge.de>
+ */
+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 = "<?xml version=\"1.0\" encoding=\"".$this->encoding."\"?>\n";
+ $feed.= $this->_createGeneratorComment();
+ if ($this->cssStyleSheet=="") {
+ $cssStyleSheet = "http://www.w3.org/2000/08/w3c-synd/style.css";
+ }
+ $feed.= $this->_createStylesheetReferences();
+ $feed.= "<rdf:RDF\n";
+ $feed.= " xmlns=\"http://purl.org/rss/1.0/\"\n";
+ $feed.= " xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n";
+ $feed.= " xmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n";
+ $feed.= " xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n";
+ $feed.= " <channel rdf:about=\"".$this->syndicationURL."\">\n";
+ $feed.= " <title>".htmlspecialchars($this->title)."</title>\n";
+ $feed.= " <description>".htmlspecialchars($this->description)."</description>\n";
+ $feed.= " <link>".$this->link."</link>\n";
+ if ($this->image!=null) {
+ $feed.= " <image rdf:resource=\"".$this->image->url."\" />\n";
+ }
+ $now = new FeedDate();
+ $feed.= " <dc:date>".htmlspecialchars($now->iso8601())."</dc:date>\n";
+ $feed.= " <items>\n";
+ $feed.= " <rdf:Seq>\n";
+ for ($i=0;$i<count($this->items);$i++) {
+ $feed.= " <rdf:li rdf:resource=\"".htmlspecialchars($this->items[$i]->link)."\"/>\n";
+ }
+ $feed.= " </rdf:Seq>\n";
+ $feed.= " </items>\n";
+ $feed.= " </channel>\n";
+ if ($this->image!=null) {
+ $feed.= " <image rdf:about=\"".$this->image->url."\">\n";
+ $feed.= " <title>".$this->image->title."</title>\n";
+ $feed.= " <link>".$this->image->link."</link>\n";
+ $feed.= " <url>".$this->image->url."</url>\n";
+ $feed.= " </image>\n";
+ }
+ $feed.= $this->_createAdditionalElements($this->additionalElements, " ");
+
+ for ($i=0;$i<count($this->items);$i++) {
+ $feed.= " <item rdf:about=\"".htmlspecialchars($this->items[$i]->link)."\">\n";
+ //$feed.= " <dc:type>Posting</dc:type>\n";
+ $feed.= " <dc:format>text/html</dc:format>\n";
+ if ($this->items[$i]->date!=null) {
+ $itemDate = new FeedDate($this->items[$i]->date);
+ $feed.= " <dc:date>".htmlspecialchars($itemDate->iso8601())."</dc:date>\n";
+ }
+ if ($this->items[$i]->source!="") {
+ $feed.= " <dc:source>".htmlspecialchars($this->items[$i]->source)."</dc:source>\n";
+ }
+ if ($this->items[$i]->author!="") {
+ $feed.= " <dc:creator>".htmlspecialchars($this->items[$i]->author)."</dc:creator>\n";
+ }
+ $feed.= " <title>".htmlspecialchars(strip_tags(strtr($this->items[$i]->title,"\n\r"," ")))."</title>\n";
+ $feed.= " <link>".htmlspecialchars($this->items[$i]->link)."</link>\n";
+ $feed.= " <description>".htmlspecialchars($this->items[$i]->description)."</description>\n";
+ $feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " ");
+ $feed.= " </item>\n";
+ }
+ $feed.= "</rdf:RDF>\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 <kaib@bitfolge.de>
+ */
+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 = "<?xml version=\"1.0\" encoding=\"".$this->encoding."\"?>\n";
+ $feed.= $this->_createGeneratorComment();
+ $feed.= $this->_createStylesheetReferences();
+ $feed.= "<rss version=\"".$this->RSSVersion."\">\n";
+ $feed.= " <channel>\n";
+ $feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars($this->title),100)."</title>\n";
+ $this->descriptionTruncSize = 500;
+ $feed.= " <description>".$this->getDescription()."</description>\n";
+ $feed.= " <link>".$this->link."</link>\n";
+ $now = new FeedDate();
+ $feed.= " <lastBuildDate>".htmlspecialchars($now->rfc822())."</lastBuildDate>\n";
+ $feed.= " <generator>".FEEDCREATOR_VERSION."</generator>\n";
+
+ if ($this->image!=null) {
+ $feed.= " <image>\n";
+ $feed.= " <url>".$this->image->url."</url>\n";
+ $feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars($this->image->title),100)."</title>\n";
+ $feed.= " <link>".$this->image->link."</link>\n";
+ if ($this->image->width!="") {
+ $feed.= " <width>".$this->image->width."</width>\n";
+ }
+ if ($this->image->height!="") {
+ $feed.= " <height>".$this->image->height."</height>\n";
+ }
+ if ($this->image->description!="") {
+ $feed.= " <description>".$this->image->getDescription()."</description>\n";
+ }
+ $feed.= " </image>\n";
+ }
+ if ($this->language!="") {
+ $feed.= " <language>".$this->language."</language>\n";
+ }
+ if ($this->copyright!="") {
+ $feed.= " <copyright>".FeedCreator::iTrunc(htmlspecialchars($this->copyright),100)."</copyright>\n";
+ }
+ if ($this->editor!="") {
+ $feed.= " <managingEditor>".FeedCreator::iTrunc(htmlspecialchars($this->editor),100)."</managingEditor>\n";
+ }
+ if ($this->webmaster!="") {
+ $feed.= " <webMaster>".FeedCreator::iTrunc(htmlspecialchars($this->webmaster),100)."</webMaster>\n";
+ }
+ if ($this->pubDate!="") {
+ $pubDate = new FeedDate($this->pubDate);
+ $feed.= " <pubDate>".htmlspecialchars($pubDate->rfc822())."</pubDate>\n";
+ }
+ if ($this->category!="") {
+ $feed.= " <category>".htmlspecialchars($this->category)."</category>\n";
+ }
+ if ($this->docs!="") {
+ $feed.= " <docs>".FeedCreator::iTrunc(htmlspecialchars($this->docs),500)."</docs>\n";
+ }
+ if ($this->ttl!="") {
+ $feed.= " <ttl>".htmlspecialchars($this->ttl)."</ttl>\n";
+ }
+ if ($this->rating!="") {
+ $feed.= " <rating>".FeedCreator::iTrunc(htmlspecialchars($this->rating),500)."</rating>\n";
+ }
+ if ($this->skipHours!="") {
+ $feed.= " <skipHours>".htmlspecialchars($this->skipHours)."</skipHours>\n";
+ }
+ if ($this->skipDays!="") {
+ $feed.= " <skipDays>".htmlspecialchars($this->skipDays)."</skipDays>\n";
+ }
+ $feed.= $this->_createAdditionalElements($this->additionalElements, " ");
+
+ for ($i=0;$i<count($this->items);$i++) {
+ $feed.= " <item>\n";
+ $feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars(strip_tags($this->items[$i]->title)),100)."</title>\n";
+ $feed.= " <link>".htmlspecialchars($this->items[$i]->link)."</link>\n";
+ $feed.= " <description>".$this->items[$i]->getDescription()."</description>\n";
+
+ if ($this->items[$i]->author!="") {
+ $feed.= " <author>".htmlspecialchars($this->items[$i]->author)."</author>\n";
+ }
+ /*
+ // on hold
+ if ($this->items[$i]->source!="") {
+ $feed.= " <source>".htmlspecialchars($this->items[$i]->source)."</source>\n";
+ }
+ */
+ if ($this->items[$i]->category!="") {
+ $feed.= " <category>".htmlspecialchars($this->items[$i]->category)."</category>\n";
+ }
+ if ($this->items[$i]->comments!="") {
+ $feed.= " <comments>".htmlspecialchars($this->items[$i]->comments)."</comments>\n";
+ }
+ if ($this->items[$i]->date!="") {
+ $itemDate = new FeedDate($this->items[$i]->date);
+ $feed.= " <pubDate>".htmlspecialchars($itemDate->rfc822())."</pubDate>\n";
+ }
+ if ($this->items[$i]->guid!="") {
+ $feed.= " <guid isPermaLink=\"false\">".htmlspecialchars($this->items[$i]->guid)."</guid>\n";
+ }
+ $feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " ");
+ $feed.= " </item>\n";
+ }
+ $feed.= " </channel>\n";
+ $feed.= "</rss>\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 <kaib@bitfolge.de>
+ */
+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 <scott@randomchaos.com> and Kai Blankenhorn <kaib@bitfolge.de>
+ */
+class PIECreator01 extends FeedCreator {
+
+ function PIECreator01() {
+ $this->encoding = "utf-8";
+ }
+
+ function createFeed() {
+ $feed = "<?xml version=\"1.0\" encoding=\"".$this->encoding."\"?>\n";
+ $feed.= $this->_createStylesheetReferences();
+ $feed.= "<feed version=\"0.1\" xmlns=\"http://example.com/newformat#\">\n";
+ $feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars($this->title),100)."</title>\n";
+ $this->truncSize = 500;
+ $feed.= " <subtitle>".$this->getDescription()."</subtitle>\n";
+ $feed.= " <link>".$this->link."</link>\n";
+ for ($i=0;$i<count($this->items);$i++) {
+ $feed.= " <entry>\n";
+ $feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars(strip_tags($this->items[$i]->title)),100)."</title>\n";
+ $feed.= " <link>".htmlspecialchars($this->items[$i]->link)."</link>\n";
+ $itemDate = new FeedDate($this->items[$i]->date);
+ $feed.= " <created>".htmlspecialchars($itemDate->iso8601())."</created>\n";
+ $feed.= " <issued>".htmlspecialchars($itemDate->iso8601())."</issued>\n";
+ $feed.= " <modified>".htmlspecialchars($itemDate->iso8601())."</modified>\n";
+ $feed.= " <id>".htmlspecialchars($this->items[$i]->guid)."</id>\n";
+ if ($this->items[$i]->author!="") {
+ $feed.= " <author>\n";
+ $feed.= " <name>".htmlspecialchars($this->items[$i]->author)."</name>\n";
+ if ($this->items[$i]->authorEmail!="") {
+ $feed.= " <email>".$this->items[$i]->authorEmail."</email>\n";
+ }
+ $feed.=" </author>\n";
+ }
+ $feed.= " <content type=\"text/html\" xml:lang=\"en-us\">\n";
+ $feed.= " <div xmlns=\"http://www.w3.org/1999/xhtml\">".$this->items[$i]->getDescription()."</div>\n";
+ $feed.= " </content>\n";
+ $feed.= " </entry>\n";
+ }
+ $feed.= "</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 <kaib@bitfolge.de>, Scott Reynen <scott@randomchaos.com>
+ */
+class AtomCreator03 extends FeedCreator {
+
+ function AtomCreator03() {
+ $this->contentType = "application/atom+xml";
+ $this->encoding = "utf-8";
+ }
+
+ function createFeed() {
+ $feed = "<?xml version=\"1.0\" encoding=\"".$this->encoding."\"?>\n";
+ $feed.= $this->_createGeneratorComment();
+ $feed.= $this->_createStylesheetReferences();
+ $feed.= "<feed version=\"0.3\" xmlns=\"http://purl.org/atom/ns#\"";
+ if ($this->language!="") {
+ $feed.= " xml:lang=\"".$this->language."\"";
+ }
+ $feed.= ">\n";
+ $feed.= " <title>".htmlspecialchars($this->title)."</title>\n";
+ $feed.= " <tagline>".htmlspecialchars($this->description)."</tagline>\n";
+ $feed.= " <link rel=\"alternate\" type=\"text/html\" href=\"".htmlspecialchars($this->link)."\"/>\n";
+ $feed.= " <id>".htmlspecialchars($this->link)."</id>\n";
+ $now = new FeedDate();
+ $feed.= " <modified>".htmlspecialchars($now->iso8601())."</modified>\n";
+ if ($this->editor!="") {
+ $feed.= " <author>\n";
+ $feed.= " <name>".$this->editor."</name>\n";
+ if ($this->editorEmail!="") {
+ $feed.= " <email>".$this->editorEmail."</email>\n";
+ }
+ $feed.= " </author>\n";
+ }
+ $feed.= " <generator>".FEEDCREATOR_VERSION."</generator>\n";
+ $feed.= $this->_createAdditionalElements($this->additionalElements, " ");
+ for ($i=0;$i<count($this->items);$i++) {
+ $feed.= " <entry>\n";
+ $feed.= " <title>".htmlspecialchars(strip_tags($this->items[$i]->title))."</title>\n";
+ $feed.= " <link rel=\"alternate\" type=\"text/html\" href=\"".htmlspecialchars($this->items[$i]->link)."\"/>\n";
+ if ($this->items[$i]->date=="") {
+ $this->items[$i]->date = time();
+ }
+ $itemDate = new FeedDate($this->items[$i]->date);
+ $feed.= " <created>".htmlspecialchars($itemDate->iso8601())."</created>\n";
+ $feed.= " <issued>".htmlspecialchars($itemDate->iso8601())."</issued>\n";
+ $feed.= " <modified>".htmlspecialchars($itemDate->iso8601())."</modified>\n";
+ $feed.= " <id>".htmlspecialchars($this->items[$i]->link)."</id>\n";
+ $feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " ");
+ if ($this->items[$i]->author!="") {
+ $feed.= " <author>\n";
+ $feed.= " <name>".htmlspecialchars($this->items[$i]->author)."</name>\n";
+ $feed.= " </author>\n";
+ }
+ if ($this->items[$i]->description!="") {
+ $feed.= " <summary>".htmlspecialchars($this->items[$i]->description)."</summary>\n";
+ }
+ $feed.= " </entry>\n";
+ }
+ $feed.= "</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 <kaib@bitfolge.de>
+ */
+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;$i<count($this->items);$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 = "<?xml version=\"1.0\" encoding=\"".$this->encoding."\"?>\n";
+ $feed.= $this->_createGeneratorComment();
+ $feed.= $this->_createStylesheetReferences();
+ $feed.= "<opml xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n";
+ $feed.= " <head>\n";
+ $feed.= " <title>".htmlspecialchars($this->title)."</title>\n";
+ if ($this->pubDate!="") {
+ $date = new FeedDate($this->pubDate);
+ $feed.= " <dateCreated>".$date->rfc822()."</dateCreated>\n";
+ }
+ if ($this->lastBuildDate!="") {
+ $date = new FeedDate($this->lastBuildDate);
+ $feed.= " <dateModified>".$date->rfc822()."</dateModified>\n";
+ }
+ if ($this->editor!="") {
+ $feed.= " <ownerName>".$this->editor."</ownerName>\n";
+ }
+ if ($this->editorEmail!="") {
+ $feed.= " <ownerEmail>".$this->editorEmail."</ownerEmail>\n";
+ }
+ $feed.= " </head>\n";
+ $feed.= " <body>\n";
+ for ($i=0;$i<count($this->items);$i++) {
+ $feed.= " <outline type=\"rss\" ";
+ $title = htmlspecialchars(strip_tags(strtr($this->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.= " </body>\n";
+ $feed.= "</opml>\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 <div></div> 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 = "<a href='".$this->image->link."'".$targetInsert.">".
+ "<img src='".$this->image->url."' border='0' alt='".
+ FeedCreator::iTrunc(htmlspecialchars($this->image->title),100).
+ "' align='".$this->imageAlign."' ";
+ if ($this->image->width) {
+ $imageStr .=" width='".$this->image->width. "' ";
+ }
+ if ($this->image->height) {
+ $imageStr .=" height='".$this->image->height."' ";
+ }
+ $imageStr .="/></a>";
+ $feedArray[] = $imageStr;
+ }
+
+ if ($this->title) {
+ $feedArray[] = "<div class='".$this->stylePrefix."title'><a href='".$this->link."' ".$targetInsert." class='".$this->stylePrefix."title'>".
+ FeedCreator::iTrunc(htmlspecialchars($this->title),100)."</a></div>";
+ }
+ if ($this->getDescription()) {
+ $feedArray[] = "<div class='".$this->stylePrefix."description'>".
+ str_replace("]]>", "", str_replace("<![CDATA[", "", $this->getDescription())).
+ "</div>";
+ }
+
+ if ($this->header) {
+ $feedArray[] = "<div class='".$this->stylePrefix."header'>".$this->header."</div>";
+ }
+
+ for ($i=0;$i<count($this->items);$i++) {
+ if ($this->separator and $i > 0) {
+ $feedArray[] = "<div class='".$this->stylePrefix."separator'>".$this->separator."</div>";
+ }
+
+ if ($this->items[$i]->title) {
+ if ($this->items[$i]->link) {
+ $feedArray[] =
+ "<div class='".$this->stylePrefix."item_title'><a href='".$this->items[$i]->link."' class='".$this->stylePrefix.
+ "item_title'".$targetInsert.">".FeedCreator::iTrunc(htmlspecialchars(strip_tags($this->items[$i]->title)),100).
+ "</a></div>";
+ } else {
+ $feedArray[] =
+ "<div class='".$this->stylePrefix."item_title'>".
+ FeedCreator::iTrunc(htmlspecialchars(strip_tags($this->items[$i]->title)),100).
+ "</div>";
+ }
+ }
+ if ($this->items[$i]->getDescription()) {
+ $feedArray[] =
+ "<div class='".$this->stylePrefix."item_description'>".
+ str_replace("]]>", "", str_replace("<![CDATA[", "", $this->items[$i]->getDescription())).
+ "</div>";
+ }
+ }
+ if ($this->footer) {
+ $feedArray[] = "<div class='".$this->stylePrefix."footer'>".$this->footer."</div>";
+ }
+
+ $feed= "".join($feedArray, "\r\n");
+ return $feed;
+ }
+
+ /**
+ * Overrrides parent to produce .html extensions
+ *
+ * @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)).".html";
+ }
+}
+
+
+/**
+ * JSCreator is a class that writes a js file to a specific
+ * location, overriding the createFeed method of the parent HTMLCreator.
+ *
+ * @author Pascal Van Hecke
+ */
+class JSCreator extends HTMLCreator {
+ var $contentType = "text/javascript";
+
+ /**
+ * writes the javascript
+ * @return string the scripts's complete text
+ */
+ function createFeed()
+ {
+ $feed = parent::createFeed();
+ $feedArray = explode("\n",$feed);
+
+ $jsFeed = "";
+ foreach ($feedArray as $value) {
+ $jsFeed .= "document.write('".trim(addslashes($value))."');\n";
+ }
+ return $jsFeed;
+ }
+
+ /**
+ * Overrrides parent to produce .js extensions
+ *
+ * @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)).".js";
+ }
+
+}
+
+
+
+/*** TEST SCRIPT *********************************************************
+
+//include("feedcreator.class.php");
+
+$rss = new UniversalFeedCreator();
+$rss->useCached();
+$rss->title = "PHP news";
+$rss->description = "daily news from the PHP scripting world";
+
+//optional
+//$rss->descriptionTruncSize = 500;
+//$rss->descriptionHtmlSyndicated = true;
+//$rss->xslStyleSheet = "http://feedster.com/rss20.xsl";
+
+$rss->link = "http://www.dailyphp.net/news";
+$rss->feedURL = "http://www.dailyphp.net/".$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 = "This is an the test title of an item";
+ $item->link = "http://localhost/item/";
+ $item->description = "<b>description in </b><br/>HTML";
+
+ //optional
+ //item->descriptionTruncSize = 500;
+ $item->descriptionHtmlSyndicated = true;
+
+ $item->date = time();
+ $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, MBOX, OPML, ATOM0.3, HTML, JS
+echo $rss->saveFeed("RSS0.91", "feed.xml");
+
+
+
+***************************************************************************/
+
+?>
diff --git a/BSF/include/filter.inc.php b/BSF/include/filter.inc.php
new file mode 100644
index 000000000..b77ddca98
--- /dev/null
+++ b/BSF/include/filter.inc.php
@@ -0,0 +1,141 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+// $filter['enabled']: Filter is enabled
+// $filter['check_key']: Check key to valitade computed filter data
+// $filter['recent_period']: Recent period used to computed filter data
+// $filter['categories']: Computed data of filtered categories
+// $filter['visible_categories']:
+// List of visible categories (count(visible) < count(forbidden) more often)
+// $filter['visible_images']: List of visible images
+
+if (!get_filter_page_value('cancel'))
+{
+ if (isset($_GET['filter']))
+ {
+ $filter['matches'] = array();
+ $filter['enabled'] =
+ preg_match('/^start-recent-(\d+)$/', $_GET['filter'], $filter['matches']) === 1;
+ }
+ else
+ {
+ $filter['enabled'] = pwg_get_session_var('filter_enabled', false);
+ }
+}
+else
+{
+ $filter['enabled'] = false;
+}
+
+if ($filter['enabled'])
+{
+ if (isset($filter['matches']))
+ {
+ $filter['recent_period'] = $filter['matches'][1];
+ }
+ else
+ {
+ $filter['recent_period'] = pwg_get_session_var('filter_recent_period', $user['recent_period']);
+ }
+
+ if (
+ // New filter
+ !pwg_get_session_var('filter_enabled', false) or
+ // Cache data updated
+ $user['need_update_done'] or
+ // Date, period, user are changed
+ (pwg_get_session_var('filter_check_key', '') != get_filter_check_key())
+ )
+ {
+ // Need to compute dats
+ $filter['check_key'] = get_filter_check_key();
+ $filter['categories'] = get_computed_categories($user, (int)$filter['recent_period']);
+
+ $filter['visible_categories'] = implode(',', array_keys($filter['categories']));
+ if (empty($filter['visible_categories']))
+ {
+ // Must be not empty
+ $filter['visible_categories'] = -1;
+ }
+
+ $query ='
+SELECT
+ distinct image_id
+FROM '.
+ IMAGE_CATEGORY_TABLE.' INNER JOIN '.IMAGES_TABLE.' ON image_id = id
+WHERE ';
+ if (!empty($filter['visible_categories']))
+ {
+ $query.= '
+ category_id IN ('.$filter['visible_categories'].') and';
+ }
+ $query.= '
+ date_available >= SUBDATE(
+ CURRENT_DATE,INTERVAL '.$filter['recent_period'].' DAY)';
+
+ $filter['visible_images'] = implode(',', array_from_query($query, 'image_id'));
+
+ if (empty($filter['visible_images']))
+ {
+ // Must be not empty
+ $filter['visible_images'] = -1;
+ }
+
+ // Save filter data on session
+ pwg_set_session_var('filter_enabled', $filter['enabled']);
+ pwg_set_session_var('filter_check_key', $filter['check_key']);
+ pwg_set_session_var('filter_recent_period', $filter['recent_period']);
+ pwg_set_session_var('filter_categories', serialize($filter['categories']));
+ pwg_set_session_var('filter_visible_categories', $filter['visible_categories']);
+ pwg_set_session_var('filter_visible_images', $filter['visible_images']);
+
+ }
+ else
+ {
+ // Read only data
+ $filter['check_key'] = pwg_get_session_var('filter_check_key', '');
+ $filter['categories'] = unserialize(pwg_get_session_var('filter_categories', serialize(array())));
+ $filter['visible_categories'] = pwg_get_session_var('filter_visible_categories', '');
+ $filter['visible_images'] = pwg_get_session_var('filter_visible_images', '');
+ }
+
+ if (get_filter_page_value('add_notes'))
+ {
+ $header_notes[] = l10n_dec('note_filter_day', 'note_filter_days', $filter['recent_period']);
+ }
+}
+else
+{
+ if (pwg_get_session_var('filter_enabled', false))
+ {
+ pwg_unset_session_var('filter_enabled');
+ pwg_unset_session_var('filter_check_key');
+ pwg_unset_session_var('filter_recent_period');
+ pwg_unset_session_var('filter_categories');
+ pwg_unset_session_var('filter_visible_categories');
+ pwg_unset_session_var('filter_visible_images');
+ }
+}
+
+
+?>
diff --git a/BSF/include/functions.inc.php b/BSF/include/functions.inc.php
new file mode 100644
index 000000000..a1b589999
--- /dev/null
+++ b/BSF/include/functions.inc.php
@@ -0,0 +1,1557 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+include_once( PHPWG_ROOT_PATH .'include/functions_user.inc.php' );
+include_once( PHPWG_ROOT_PATH .'include/functions_cookie.inc.php' );
+include_once( PHPWG_ROOT_PATH .'include/functions_session.inc.php' );
+include_once( PHPWG_ROOT_PATH .'include/functions_category.inc.php' );
+include_once( PHPWG_ROOT_PATH .'include/functions_xml.inc.php' );
+include_once( PHPWG_ROOT_PATH .'include/functions_group.inc.php' );
+include_once( PHPWG_ROOT_PATH .'include/functions_html.inc.php' );
+include_once( PHPWG_ROOT_PATH .'include/functions_tag.inc.php' );
+include_once( PHPWG_ROOT_PATH .'include/functions_url.inc.php' );
+include_once( PHPWG_ROOT_PATH .'include/functions_plugins.inc.php' );
+
+//----------------------------------------------------------- generic functions
+
+/**
+ * returns an array containing the possible values of an enum field
+ *
+ * @param string tablename
+ * @param string fieldname
+ */
+function get_enums($table, $field)
+{
+ // retrieving the properties of the table. Each line represents a field :
+ // columns are 'Field', 'Type'
+ $result = pwg_query('desc '.$table);
+ while ($row = mysql_fetch_array($result))
+ {
+ // we are only interested in the the field given in parameter for the
+ // function
+ if ($row['Field'] == $field)
+ {
+ // retrieving possible values of the enum field
+ // enum('blue','green','black')
+ $options = explode(',', substr($row['Type'], 5, -1));
+ foreach ($options as $i => $option)
+ {
+ $options[$i] = str_replace("'", '',$option);
+ }
+ }
+ }
+ mysql_free_result($result);
+ return $options;
+}
+
+// get_boolean transforms a string to a boolean value. If the string is
+// "false" (case insensitive), then the boolean value false is returned. In
+// any other case, true is returned.
+function get_boolean( $string )
+{
+ $boolean = true;
+ if ( preg_match( '/^false$/i', $string ) )
+ {
+ $boolean = false;
+ }
+ return $boolean;
+}
+
+/**
+ * returns boolean string 'true' or 'false' if the given var is boolean
+ *
+ * @param mixed $var
+ * @return mixed
+ */
+function boolean_to_string($var)
+{
+ if (is_bool($var))
+ {
+ if ($var)
+ {
+ return 'true';
+ }
+ else
+ {
+ return 'false';
+ }
+ }
+ else
+ {
+ return $var;
+ }
+}
+
+// The function get_moment returns a float value coresponding to the number
+// of seconds since the unix epoch (1st January 1970) and the microseconds
+// are precised : e.g. 1052343429.89276600
+function get_moment()
+{
+ $t1 = explode( ' ', microtime() );
+ $t2 = explode( '.', $t1[0] );
+ $t2 = $t1[1].'.'.$t2[1];
+ return $t2;
+}
+
+// The function get_elapsed_time returns the number of seconds (with 3
+// decimals precision) between the start time and the end time given.
+function get_elapsed_time( $start, $end )
+{
+ return number_format( $end - $start, 3, '.', ' ').' s';
+}
+
+// - The replace_space function replaces space and '-' characters
+// by their HTML equivalent &nbsb; and &minus;
+// - The function does not replace characters in HTML tags
+// - This function was created because IE5 does not respect the
+// CSS "white-space: nowrap;" property unless space and minus
+// characters are replaced like this function does.
+// - Example :
+// <div class="foo">My friend</div>
+// ( 01234567891111111111222222222233 )
+// ( 0123456789012345678901 )
+// becomes :
+// <div class="foo">My&nbsp;friend</div>
+function replace_space( $string )
+{
+ //return $string;
+ $return_string = '';
+ // $remaining is the rest of the string where to replace spaces characters
+ $remaining = $string;
+ // $start represents the position of the next '<' character
+ // $end represents the position of the next '>' character
+ $start = 0;
+ $end = 0;
+ $start = strpos ( $remaining, '<' ); // -> 0
+ $end = strpos ( $remaining, '>' ); // -> 16
+ // as long as a '<' and his friend '>' are found, we loop
+ while ( is_numeric( $start ) and is_numeric( $end ) )
+ {
+ // $treatment is the part of the string to treat
+ // In the first loop of our example, this variable is empty, but in the
+ // second loop, it equals 'My friend'
+ $treatment = substr ( $remaining, 0, $start );
+ // Replacement of ' ' by his equivalent '&nbsp;'
+ $treatment = str_replace( ' ', '&nbsp;', $treatment );
+ $treatment = str_replace( '-', '&minus;', $treatment );
+ // composing the string to return by adding the treated string and the
+ // following HTML tag -> 'My&nbsp;friend</div>'
+ $return_string.= $treatment.substr( $remaining, $start, $end-$start+1 );
+ // the remaining string is deplaced to the part after the '>' of this
+ // loop
+ $remaining = substr ( $remaining, $end + 1, strlen( $remaining ) );
+ $start = strpos ( $remaining, '<' );
+ $end = strpos ( $remaining, '>' );
+ }
+ $treatment = str_replace( ' ', '&nbsp;', $remaining );
+ $treatment = str_replace( '-', '&minus;', $treatment );
+ $return_string.= $treatment;
+
+ return $return_string;
+}
+
+// get_extension returns the part of the string after the last "."
+function get_extension( $filename )
+{
+ return substr( strrchr( $filename, '.' ), 1, strlen ( $filename ) );
+}
+
+// get_filename_wo_extension returns the part of the string before the last
+// ".".
+// get_filename_wo_extension( 'test.tar.gz' ) -> 'test.tar'
+function get_filename_wo_extension( $filename )
+{
+ $pos = strrpos( $filename, '.' );
+ return ($pos===false) ? $filename : substr( $filename, 0, $pos);
+}
+
+/**
+ * returns an array contening sub-directories, excluding "CVS"
+ *
+ * @param string $dir
+ * @return array
+ */
+function get_dirs($directory)
+{
+ $sub_dirs = array();
+
+ if ($opendir = opendir($directory))
+ {
+ while ($file = readdir($opendir))
+ {
+ if ($file != '.'
+ and $file != '..'
+ and is_dir($directory.'/'.$file)
+ and $file != 'CVS'
+ and $file != '.svn')
+ {
+ array_push($sub_dirs, $file);
+ }
+ }
+ }
+ return $sub_dirs;
+}
+
+/**
+ * returns thumbnail directory name of input diretoty name
+ * make thumbnail directory is necessary
+ * set error messages on array messages
+ *
+ * @param:
+ * string $dirname
+ * arrayy $errors
+ * @return bool false on error else string directory name
+ */
+function mkget_thumbnail_dir($dirname, &$errors)
+{
+ $tndir = $dirname.'/thumbnail';
+ if (!is_dir($tndir))
+ {
+ if (!is_writable($dirname))
+ {
+ array_push($errors,
+ '['.$dirname.'] : '.l10n('no_write_access'));
+ return false;
+ }
+ umask(0000);
+ mkdir($tndir, 0777);
+ }
+
+ return $tndir;
+}
+
+// The get_picture_size function return an array containing :
+// - $picture_size[0] : final width
+// - $picture_size[1] : final height
+// The final dimensions are calculated thanks to the original dimensions and
+// the maximum dimensions given in parameters. get_picture_size respects
+// the width/height ratio
+function get_picture_size( $original_width, $original_height,
+ $max_width, $max_height )
+{
+ $width = $original_width;
+ $height = $original_height;
+ $is_original_size = true;
+
+ if ( $max_width != "" )
+ {
+ if ( $original_width > $max_width )
+ {
+ $width = $max_width;
+ $height = floor( ( $width * $original_height ) / $original_width );
+ }
+ }
+ if ( $max_height != "" )
+ {
+ if ( $original_height > $max_height )
+ {
+ $height = $max_height;
+ $width = floor( ( $height * $original_width ) / $original_height );
+ $is_original_size = false;
+ }
+ }
+ if ( is_numeric( $max_width ) and is_numeric( $max_height )
+ and $max_width != 0 and $max_height != 0 )
+ {
+ $ratioWidth = $original_width / $max_width;
+ $ratioHeight = $original_height / $max_height;
+ if ( ( $ratioWidth > 1 ) or ( $ratioHeight > 1 ) )
+ {
+ if ( $ratioWidth < $ratioHeight )
+ {
+ $width = floor( $original_width / $ratioHeight );
+ $height = $max_height;
+ }
+ else
+ {
+ $width = $max_width;
+ $height = floor( $original_height / $ratioWidth );
+ }
+ $is_original_size = false;
+ }
+ }
+ $picture_size = array();
+ $picture_size[0] = $width;
+ $picture_size[1] = $height;
+ return $picture_size;
+}
+
+/* Returns true if the string appears to be encoded in UTF-8. (from wordpress)
+ * @param string Str
+ */
+function seems_utf8($Str) { # by bmorel at ssi dot fr
+ for ($i=0; $i<strlen($Str); $i++) {
+ if (ord($Str[$i]) < 0x80) continue; # 0bbbbbbb
+ elseif ((ord($Str[$i]) & 0xE0) == 0xC0) $n=1; # 110bbbbb
+ elseif ((ord($Str[$i]) & 0xF0) == 0xE0) $n=2; # 1110bbbb
+ elseif ((ord($Str[$i]) & 0xF8) == 0xF0) $n=3; # 11110bbb
+ elseif ((ord($Str[$i]) & 0xFC) == 0xF8) $n=4; # 111110bb
+ elseif ((ord($Str[$i]) & 0xFE) == 0xFC) $n=5; # 1111110b
+ else return false; # Does not match any model
+ for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
+ if ((++$i == strlen($Str)) || ((ord($Str[$i]) & 0xC0) != 0x80))
+ return false;
+ }
+ }
+ return true;
+}
+
+/* Remove accents from a UTF-8 or ISO-859-1 string (from wordpress)
+ * @param string sstring - an UTF-8 or ISO-8859-1 string
+ */
+function remove_accents($string)
+{
+ if ( !preg_match('/[\x80-\xff]/', $string) )
+ return $string;
+
+ if (seems_utf8($string)) {
+ $chars = array(
+ // Decompositions for Latin-1 Supplement
+ chr(195).chr(128) => 'A', chr(195).chr(129) => 'A',
+ chr(195).chr(130) => 'A', chr(195).chr(131) => 'A',
+ chr(195).chr(132) => 'A', chr(195).chr(133) => 'A',
+ chr(195).chr(135) => 'C', chr(195).chr(136) => 'E',
+ chr(195).chr(137) => 'E', chr(195).chr(138) => 'E',
+ chr(195).chr(139) => 'E', chr(195).chr(140) => 'I',
+ chr(195).chr(141) => 'I', chr(195).chr(142) => 'I',
+ chr(195).chr(143) => 'I', chr(195).chr(145) => 'N',
+ chr(195).chr(146) => 'O', chr(195).chr(147) => 'O',
+ chr(195).chr(148) => 'O', chr(195).chr(149) => 'O',
+ chr(195).chr(150) => 'O', chr(195).chr(153) => 'U',
+ chr(195).chr(154) => 'U', chr(195).chr(155) => 'U',
+ chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y',
+ chr(195).chr(159) => 's', chr(195).chr(160) => 'a',
+ chr(195).chr(161) => 'a', chr(195).chr(162) => 'a',
+ chr(195).chr(163) => 'a', chr(195).chr(164) => 'a',
+ chr(195).chr(165) => 'a', chr(195).chr(167) => 'c',
+ chr(195).chr(168) => 'e', chr(195).chr(169) => 'e',
+ chr(195).chr(170) => 'e', chr(195).chr(171) => 'e',
+ chr(195).chr(172) => 'i', chr(195).chr(173) => 'i',
+ chr(195).chr(174) => 'i', chr(195).chr(175) => 'i',
+ chr(195).chr(177) => 'n', chr(195).chr(178) => 'o',
+ chr(195).chr(179) => 'o', chr(195).chr(180) => 'o',
+ chr(195).chr(181) => 'o', chr(195).chr(182) => 'o',
+ chr(195).chr(182) => 'o', chr(195).chr(185) => 'u',
+ chr(195).chr(186) => 'u', chr(195).chr(187) => 'u',
+ chr(195).chr(188) => 'u', chr(195).chr(189) => 'y',
+ chr(195).chr(191) => 'y',
+ // Decompositions for Latin Extended-A
+ chr(196).chr(128) => 'A', chr(196).chr(129) => 'a',
+ chr(196).chr(130) => 'A', chr(196).chr(131) => 'a',
+ chr(196).chr(132) => 'A', chr(196).chr(133) => 'a',
+ chr(196).chr(134) => 'C', chr(196).chr(135) => 'c',
+ chr(196).chr(136) => 'C', chr(196).chr(137) => 'c',
+ chr(196).chr(138) => 'C', chr(196).chr(139) => 'c',
+ chr(196).chr(140) => 'C', chr(196).chr(141) => 'c',
+ chr(196).chr(142) => 'D', chr(196).chr(143) => 'd',
+ chr(196).chr(144) => 'D', chr(196).chr(145) => 'd',
+ chr(196).chr(146) => 'E', chr(196).chr(147) => 'e',
+ chr(196).chr(148) => 'E', chr(196).chr(149) => 'e',
+ chr(196).chr(150) => 'E', chr(196).chr(151) => 'e',
+ chr(196).chr(152) => 'E', chr(196).chr(153) => 'e',
+ chr(196).chr(154) => 'E', chr(196).chr(155) => 'e',
+ chr(196).chr(156) => 'G', chr(196).chr(157) => 'g',
+ chr(196).chr(158) => 'G', chr(196).chr(159) => 'g',
+ chr(196).chr(160) => 'G', chr(196).chr(161) => 'g',
+ chr(196).chr(162) => 'G', chr(196).chr(163) => 'g',
+ chr(196).chr(164) => 'H', chr(196).chr(165) => 'h',
+ chr(196).chr(166) => 'H', chr(196).chr(167) => 'h',
+ chr(196).chr(168) => 'I', chr(196).chr(169) => 'i',
+ chr(196).chr(170) => 'I', chr(196).chr(171) => 'i',
+ chr(196).chr(172) => 'I', chr(196).chr(173) => 'i',
+ chr(196).chr(174) => 'I', chr(196).chr(175) => 'i',
+ chr(196).chr(176) => 'I', chr(196).chr(177) => 'i',
+ chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij',
+ chr(196).chr(180) => 'J', chr(196).chr(181) => 'j',
+ chr(196).chr(182) => 'K', chr(196).chr(183) => 'k',
+ chr(196).chr(184) => 'k', chr(196).chr(185) => 'L',
+ chr(196).chr(186) => 'l', chr(196).chr(187) => 'L',
+ chr(196).chr(188) => 'l', chr(196).chr(189) => 'L',
+ chr(196).chr(190) => 'l', chr(196).chr(191) => 'L',
+ chr(197).chr(128) => 'l', chr(197).chr(129) => 'L',
+ chr(197).chr(130) => 'l', chr(197).chr(131) => 'N',
+ chr(197).chr(132) => 'n', chr(197).chr(133) => 'N',
+ chr(197).chr(134) => 'n', chr(197).chr(135) => 'N',
+ chr(197).chr(136) => 'n', chr(197).chr(137) => 'N',
+ chr(197).chr(138) => 'n', chr(197).chr(139) => 'N',
+ chr(197).chr(140) => 'O', chr(197).chr(141) => 'o',
+ chr(197).chr(142) => 'O', chr(197).chr(143) => 'o',
+ chr(197).chr(144) => 'O', chr(197).chr(145) => 'o',
+ chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe',
+ chr(197).chr(148) => 'R',chr(197).chr(149) => 'r',
+ chr(197).chr(150) => 'R',chr(197).chr(151) => 'r',
+ chr(197).chr(152) => 'R',chr(197).chr(153) => 'r',
+ chr(197).chr(154) => 'S',chr(197).chr(155) => 's',
+ chr(197).chr(156) => 'S',chr(197).chr(157) => 's',
+ chr(197).chr(158) => 'S',chr(197).chr(159) => 's',
+ chr(197).chr(160) => 'S', chr(197).chr(161) => 's',
+ chr(197).chr(162) => 'T', chr(197).chr(163) => 't',
+ chr(197).chr(164) => 'T', chr(197).chr(165) => 't',
+ chr(197).chr(166) => 'T', chr(197).chr(167) => 't',
+ chr(197).chr(168) => 'U', chr(197).chr(169) => 'u',
+ chr(197).chr(170) => 'U', chr(197).chr(171) => 'u',
+ chr(197).chr(172) => 'U', chr(197).chr(173) => 'u',
+ chr(197).chr(174) => 'U', chr(197).chr(175) => 'u',
+ chr(197).chr(176) => 'U', chr(197).chr(177) => 'u',
+ chr(197).chr(178) => 'U', chr(197).chr(179) => 'u',
+ chr(197).chr(180) => 'W', chr(197).chr(181) => 'w',
+ chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y',
+ chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z',
+ chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z',
+ chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z',
+ chr(197).chr(190) => 'z', chr(197).chr(191) => 's',
+ // Euro Sign
+ chr(226).chr(130).chr(172) => 'E',
+ // GBP (Pound) Sign
+ chr(194).chr(163) => '');
+
+ $string = strtr($string, $chars);
+ } else {
+ // Assume ISO-8859-1 if not UTF-8
+ $chars['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158)
+ .chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194)
+ .chr(195).chr(196).chr(197).chr(199).chr(200).chr(201).chr(202)
+ .chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210)
+ .chr(211).chr(212).chr(213).chr(214).chr(216).chr(217).chr(218)
+ .chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227)
+ .chr(228).chr(229).chr(231).chr(232).chr(233).chr(234).chr(235)
+ .chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243)
+ .chr(244).chr(245).chr(246).chr(248).chr(249).chr(250).chr(251)
+ .chr(252).chr(253).chr(255);
+
+ $chars['out'] = "EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy";
+
+ $string = strtr($string, $chars['in'], $chars['out']);
+ $double_chars['in'] = array(chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254));
+ $double_chars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th');
+ $string = str_replace($double_chars['in'], $double_chars['out'], $string);
+ }
+
+ return $string;
+}
+
+/**
+ * simplify a string to insert it into an URL
+ *
+ * @param string
+ * @return string
+ */
+function str2url($str)
+{
+ $str = remove_accents($str);
+ $str = preg_replace('/[^a-z0-9_\s\'\:\/\[\],-]/','',strtolower($str));
+ $str = preg_replace('/[\s\'\:\/\[\],-]+/',' ',trim($str));
+ $res = str_replace(' ','_',$str);
+
+ return $res;
+}
+
+//-------------------------------------------- Piwigo specific functions
+
+/**
+ * returns an array with a list of {language_code => language_name}
+ *
+ * @returns array
+ */
+function get_languages($target_charset = null)
+{
+ if ( empty($target_charset) )
+ {
+ $target_charset = get_pwg_charset();
+ }
+ $target_charset = strtolower($target_charset);
+
+ $dir = opendir(PHPWG_ROOT_PATH.'language');
+ $languages = array();
+
+ while ($file = readdir($dir))
+ {
+ $path = PHPWG_ROOT_PATH.'language/'.$file;
+ if (is_dir($path) and !is_link($path) and file_exists($path.'/iso.txt'))
+ {
+ list($language_name) = @file($path.'/iso.txt');
+
+ $langdef = explode('.',$file);
+ if (count($langdef)>1) // (langCode,encoding)
+ {
+ $langdef[1] = strtolower($langdef[1]);
+
+ if (
+ $target_charset==$langdef[1]
+ or
+ ($target_charset=='utf-8' and $langdef[1]=='iso-8859-1')
+ or
+ ($target_charset=='iso-8859-1' and
+ in_array( substr($langdef[0],2), array('en','fr','de','es','it','nl')))
+ )
+ {
+ $language_name = convert_charset($language_name,
+ $langdef[1], $target_charset);
+ $languages[ $langdef[0] ] = $language_name;
+ }
+ else
+ continue; // the language encoding is not compatible with our charset
+ }
+ else
+ { // UTF-8
+ $language_name = convert_charset($language_name,
+ 'utf-8', $target_charset);
+ $languages[$file] = $language_name;
+ }
+ }
+ }
+ closedir($dir);
+ @asort($languages);
+ @reset($languages);
+
+ return $languages;
+}
+
+function pwg_log($image_id = null, $image_type = null)
+{
+ global $conf, $user, $page;
+
+ $do_log = true;
+ if (!$conf['log'])
+ {
+ $do_log = false;
+ }
+ if (is_admin() and !$conf['history_admin'])
+ {
+ $do_log = false;
+ }
+ if (is_a_guest() and !$conf['history_guest'])
+ {
+ $do_log = false;
+ }
+
+ $do_log = trigger_event('pwg_log_allowed', $do_log, $image_id, $image_type);
+
+ if (!$do_log)
+ {
+ return false;
+ }
+
+ $tags_string = null;
+ if (isset($page['section']) and $page['section'] == 'tags')
+ {
+ $tag_ids = array();
+ foreach ($page['tags'] as $tag)
+ {
+ array_push($tag_ids, $tag['id']);
+ }
+
+ $tags_string = implode(',', $tag_ids);
+ }
+
+ $query = '
+INSERT INTO '.HISTORY_TABLE.'
+ (
+ date,
+ time,
+ user_id,
+ IP,
+ section,
+ category_id,
+ image_id,
+ image_type,
+ tag_ids
+ )
+ VALUES
+ (
+ CURDATE(),
+ CURTIME(),
+ '.$user['id'].',
+ \''.$_SERVER['REMOTE_ADDR'].'\',
+ '.(isset($page['section']) ? "'".$page['section']."'" : 'NULL').',
+ '.(isset($page['category']['id']) ? $page['category']['id'] : 'NULL').',
+ '.(isset($image_id) ? $image_id : 'NULL').',
+ '.(isset($image_type) ? "'".$image_type."'" : 'NULL').',
+ '.(isset($tags_string) ? "'".$tags_string."'" : 'NULL').'
+ )
+;';
+ pwg_query($query);
+
+ return true;
+}
+
+// format_date returns a formatted date for display. The date given in
+// argument can be a unixdate (number of seconds since the 01.01.1970) or an
+// american format (2003-09-15). By option, you can show the time. The
+// output is internationalized.
+//
+// format_date( "2003-09-15", 'us', true ) -> "Monday 15 September 2003 21:52"
+function format_date($date, $type = 'us', $show_time = false)
+{
+ global $lang;
+
+ list($year,$month,$day,$hour,$minute,$second) = array(0,0,0,0,0,0);
+
+ switch ( $type )
+ {
+ case 'us' :
+ {
+ list($year,$month,$day) = explode('-', $date);
+ break;
+ }
+ case 'unix' :
+ {
+ list($year,$month,$day,$hour,$minute) =
+ explode('.', date('Y.n.j.G.i', $date));
+ break;
+ }
+ case 'mysql_datetime' :
+ {
+ preg_match('/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/',
+ $date, $out);
+ list($year,$month,$day,$hour,$minute,$second) =
+ array($out[1],$out[2],$out[3],$out[4],$out[5],$out[6]);
+ break;
+ }
+ }
+ $formated_date = '';
+ // before 1970, Microsoft Windows can't mktime
+ if ($year >= 1970)
+ {
+ // we ask midday because Windows think it's prior to midnight with a
+ // zero and refuse to work
+ $formated_date.= $lang['day'][date('w', mktime(12,0,0,$month,$day,$year))];
+ }
+ $formated_date.= ' '.$day;
+ $formated_date.= ' '.$lang['month'][(int)$month];
+ $formated_date.= ' '.$year;
+ if ($show_time)
+ {
+ $formated_date.= ' '.$hour.':'.$minute;
+ }
+
+ return $formated_date;
+}
+
+function pwg_query($query)
+{
+ global $conf,$page,$debug,$t2;
+
+ $start = get_moment();
+ $result = mysql_query($query) or my_error($query."\n");
+
+ $time = get_moment() - $start;
+
+ if (!isset($page['count_queries']))
+ {
+ $page['count_queries'] = 0;
+ $page['queries_time'] = 0;
+ }
+
+ $page['count_queries']++;
+ $page['queries_time']+= $time;
+
+ if ($conf['show_queries'])
+ {
+ $output = '';
+ $output.= '<pre>['.$page['count_queries'].'] ';
+ $output.= "\n".$query;
+ $output.= "\n".'(this query time : ';
+ $output.= '<b>'.number_format($time, 3, '.', ' ').' s)</b>';
+ $output.= "\n".'(total SQL time : ';
+ $output.= number_format($page['queries_time'], 3, '.', ' ').' s)';
+ $output.= "\n".'(total time : ';
+ $output.= number_format( ($time+$start-$t2), 3, '.', ' ').' s)';
+ if ( $result!=null and preg_match('/\s*SELECT\s+/i',$query) )
+ {
+ $output.= "\n".'(num rows : ';
+ $output.= mysql_num_rows($result).' )';
+ }
+ elseif ( $result!=null
+ and preg_match('/\s*INSERT|UPDATE|REPLACE|DELETE\s+/i',$query) )
+ {
+ $output.= "\n".'(affected rows : ';
+ $output.= mysql_affected_rows().' )';
+ }
+ $output.= "</pre>\n";
+
+ $debug .= $output;
+ }
+
+ return $result;
+}
+
+function pwg_debug( $string )
+{
+ global $debug,$t2,$page;
+
+ $now = explode( ' ', microtime() );
+ $now2 = explode( '.', $now[0] );
+ $now2 = $now[1].'.'.$now2[1];
+ $time = number_format( $now2 - $t2, 3, '.', ' ').' s';
+ $debug .= '<p>';
+ $debug.= '['.$time.', ';
+ $debug.= $page['count_queries'].' queries] : '.$string;
+ $debug.= "</p>\n";
+}
+
+/**
+ * Redirects to the given URL (HTTP method)
+ *
+ * Note : once this function called, the execution doesn't go further
+ * (presence of an exit() instruction.
+ *
+ * @param string $url
+ * @return void
+ */
+function redirect_http( $url )
+{
+ if (ob_get_length () !== FALSE)
+ {
+ ob_clean();
+ }
+ // default url is on html format
+ $url = html_entity_decode($url);
+ header('Request-URI: '.$url);
+ header('Content-Location: '.$url);
+ header('Location: '.$url);
+ exit();
+}
+
+/**
+ * Redirects to the given URL (HTML method)
+ *
+ * Note : once this function called, the execution doesn't go further
+ * (presence of an exit() instruction.
+ *
+ * @param string $url
+ * @param string $title_msg
+ * @param integer $refreh_time
+ * @return void
+ */
+function redirect_html( $url , $msg = '', $refresh_time = 0)
+{
+ global $user, $template, $lang_info, $conf, $lang, $t2, $page, $debug;
+
+ if (!isset($lang_info))
+ {
+ $user = build_user( $conf['guest_id'], true);
+ load_language('common.lang');
+ trigger_action('loading_lang');
+ load_language('local.lang');
+ list($tmpl, $thm) = explode('/', get_default_template());
+ $template = new Template(PHPWG_ROOT_PATH.'template/'.$tmpl, $thm);
+ }
+ else
+ {
+ $template = new Template(PHPWG_ROOT_PATH.'template/'.$user['template'], $user['theme']);
+ }
+
+ if (empty($msg))
+ {
+ $redirect_msg = l10n('redirect_msg');
+ }
+ else
+ {
+ $redirect_msg = $msg;
+ }
+ $redirect_msg = nl2br($redirect_msg);
+
+ $refresh = $refresh_time;
+ $url_link = $url;
+ $title = 'redirection';
+
+ $template->set_filenames( array( 'redirect' => 'redirect.tpl' ) );
+
+ include( PHPWG_ROOT_PATH.'include/page_header.php' );
+
+ $template->set_filenames( array( 'redirect' => 'redirect.tpl' ) );
+ $template->parse('redirect');
+
+ include( PHPWG_ROOT_PATH.'include/page_tail.php' );
+
+ exit();
+}
+
+/**
+ * Redirects to the given URL (Switch to HTTP method or HTML method)
+ *
+ * Note : once this function called, the execution doesn't go further
+ * (presence of an exit() instruction.
+ *
+ * @param string $url
+ * @param string $title_msg
+ * @param integer $refreh_time
+ * @return void
+ */
+function redirect( $url , $msg = '', $refresh_time = 0)
+{
+ global $conf;
+
+ // with RefeshTime <> 0, only html must be used
+ if ($conf['default_redirect_method']=='http'
+ and $refresh_time==0
+ and !headers_sent()
+ )
+ {
+ redirect_http($url);
+ }
+ else
+ {
+ redirect_html($url, $msg, $refresh_time);
+ }
+}
+
+/**
+ * returns $_SERVER['QUERY_STRING'] whitout keys given in parameters
+ *
+ * @param array $rejects
+ * @param boolean $escape - if true escape & to &amp; (for html)
+ * @returns string
+ */
+function get_query_string_diff($rejects=array(), $escape=true)
+{
+ $query_string = '';
+
+ $str = $_SERVER['QUERY_STRING'];
+ parse_str($str, $vars);
+
+ $is_first = true;
+ foreach ($vars as $key => $value)
+ {
+ if (!in_array($key, $rejects))
+ {
+ $query_string.= $is_first ? '?' : ($escape ? '&amp;' : '&' );
+ $is_first = false;
+ $query_string.= $key.'='.$value;
+ }
+ }
+
+ return $query_string;
+}
+
+function url_is_remote($url)
+{
+ if ( strncmp($url, 'http://', 7)==0
+ or strncmp($url, 'https://', 8)==0 )
+ {
+ return true;
+ }
+ return false;
+}
+
+/**
+ * returns available template/theme
+ */
+function get_pwg_themes()
+{
+ global $conf;
+ $themes = array();
+
+ $template_dir = PHPWG_ROOT_PATH.'template';
+
+ foreach (get_dirs($template_dir) as $template)
+ {
+ foreach (get_dirs($template_dir.'/'.$template.'/theme') as $theme)
+ {
+ if ( ($template.'/'.$theme) != $conf['admin_layout'] )
+ array_push($themes, $template.'/'.$theme);
+ }
+ }
+
+ return $themes;
+}
+
+/* Returns the PATH to the thumbnail to be displayed. If the element does not
+ * have a thumbnail, the default mime image path is returned. The PATH can be
+ * used in the php script, but not sent to the browser.
+ * @param array element_info assoc array containing element info from db
+ * at least 'path', 'tn_ext' and 'id' should be present
+ */
+function get_thumbnail_path($element_info)
+{
+ $path = get_thumbnail_location($element_info);
+ if ( !url_is_remote($path) )
+ {
+ $path = PHPWG_ROOT_PATH.$path;
+ }
+ return $path;
+}
+
+/* Returns the URL of the thumbnail to be displayed. If the element does not
+ * have a thumbnail, the default mime image url is returned. The URL can be
+ * sent to the browser, but not used in the php script.
+ * @param array element_info assoc array containing element info from db
+ * at least 'path', 'tn_ext' and 'id' should be present
+ */
+function get_thumbnail_url($element_info)
+{
+ $path = get_thumbnail_location($element_info);
+ if ( !url_is_remote($path) )
+ {
+ $path = embellish_url(get_root_url().$path);
+ }
+
+ // plugins want another url ?
+ $path = trigger_event('get_thumbnail_url', $path, $element_info);
+ return $path;
+}
+
+/* returns the relative path of the thumnail with regards to to the root
+of piwigo (not the current page!).This function is not intended to be
+called directly from code.*/
+function get_thumbnail_location($element_info)
+{
+ global $conf;
+ if ( !empty( $element_info['tn_ext'] ) )
+ {
+ $path = substr_replace(
+ get_filename_wo_extension($element_info['path']),
+ '/thumbnail/'.$conf['prefix_thumbnail'],
+ strrpos($element_info['path'],'/'),
+ 1
+ );
+ $path.= '.'.$element_info['tn_ext'];
+ }
+ else
+ {
+ $path = get_themeconf('mime_icon_dir')
+ .strtolower(get_extension($element_info['path'])).'.png';
+ }
+
+ // plugins want another location ?
+ $path = trigger_event( 'get_thumbnail_location', $path, $element_info);
+ return $path;
+}
+
+/* returns the title of the thumnail */
+function get_thumbnail_title($element_info)
+{
+ // message in title for the thumbnail
+ if (isset($element_info['file']))
+ {
+ $thumbnail_title = $element_info['file'];
+ }
+ else
+ {
+ $thumbnail_title = '';
+ }
+
+ if (!empty($element_info['filesize']))
+ {
+ $thumbnail_title .= ' : '.l10n_dec('%d Kb', '%d Kb', $element_info['filesize']);
+ }
+
+ return $thumbnail_title;
+}
+
+// my_error returns (or send to standard output) the message concerning the
+// error occured for the last mysql query.
+function my_error($header)
+{
+ global $conf;
+
+ $error = '<pre>';
+ $error.= $header;
+ $error.= '[mysql error '.mysql_errno().'] ';
+ $error.= mysql_error();
+ $error.= '</pre>';
+
+ 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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+define('CAL_VIEW_LIST', 'list');
+define('CAL_VIEW_CALENDAR', 'calendar');
+
+function initialize_calendar()
+{
+ global $page, $conf, $user, $template, $filter;
+
+//------------------ initialize the condition on items to take into account ---
+ $inner_sql = ' FROM ' . IMAGES_TABLE;
+
+ if ($page['section']=='categories')
+ { // we will regenerate the items by including subcats elements
+ $page['items'] = array();
+ $inner_sql .= '
+INNER JOIN '.IMAGE_CATEGORY_TABLE.' ON id = image_id';
+
+ if ( isset($page['category']) )
+ {
+ $sub_ids = array_diff(
+ get_subcat_ids(array($page['category']['id'])),
+ explode(',', $user['forbidden_categories'])
+ );
+
+ if (empty($sub_ids))
+ {
+ return; // nothing to do
+ }
+ $inner_sql .= '
+WHERE category_id IN ('.implode(',',$sub_ids).')';
+ $inner_sql .= '
+ '.get_sql_condition_FandF
+ (
+ array
+ (
+ 'visible_images' => '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 ('<pre>'. var_export($calendar, true) . '</pre>');
+
+ $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 = '<a href="'.$url.'">'
+ .$fields[$page['chronology_field']]['label'].'</a>';
+ $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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+/**
+ * Provides functions to handle categories.
+ *
+ *
+ */
+
+/**
+ * Is the category accessible to the connected user ?
+ *
+ * Note : if the user is not authorized to see this category, page creation
+ * ends (exit command in this function)
+ *
+ * @param int category id to verify
+ * @return void
+ */
+function check_restrictions($category_id)
+{
+ global $user;
+
+ // $filter['visible_categories'] and $filter['visible_images']
+ // are not used because it's not necessary (filter <> restriction)
+ if (in_array($category_id, explode(',', $user['forbidden_categories'])))
+ {
+ 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('&nbsp;',
+ (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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+/**
+ * returns a "secret key" that is to be sent back when a user enters a comment
+ */
+function get_comment_post_key($image_id)
+{
+ global $conf;
+
+ $time = time();
+
+ return sprintf(
+ '%s:%s',
+ $time,
+ hash_hmac(
+ 'md5',
+ $time.':'.$image_id,
+ $conf['secret_key']
+ )
+ );
+}
+
+//returns string action to perform on a new comment: validate, moderate, reject
+function user_comment_check($action, $comment)
+{
+ global $conf,$user;
+
+ if ($action=='reject')
+ return $action;
+
+ $my_action = $conf['comment_spam_reject'] ? 'reject':'moderate';
+
+ if ($action==$my_action)
+ return $action;
+
+ // we do here only BASIC spam check (plugins can do more)
+ if ( !is_a_guest() )
+ return $action;
+
+ $link_count = preg_match_all( '/https?:\/\//',
+ $comment['content'], $matches);
+
+ if ( strpos($comment['author'], 'http://')!==false )
+ {
+ $link_count++;
+ }
+
+ if ( $link_count>$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]<time()-3600 // 60 minutes expiration
+ or hash_hmac(
+ 'md5', $key[0].':'.$comm['image_id'], $conf['secret_key']
+ ) != $key[1]
+ )
+ {
+ $comment_action='reject';
+ }
+
+ if ($comment_action!='reject' and $conf['anti-flood_time']>0 )
+ { // anti-flood system
+ $reference_date = time() - $conf['anti-flood_time'];
+ $query = '
+SELECT id FROM '.COMMENTS_TABLE.'
+ WHERE date > FROM_UNIXTIME('.$reference_date.')
+ AND author = "'.addslashes($comm['author']).'"';
+ if ( mysql_num_rows( pwg_query( $query ) ) > 0 )
+ {
+ array_push( $infos, l10n('comment_anti-flood') );
+ $comment_action='reject';
+ }
+ }
+
+ // perform more spam check
+ $comment_action = trigger_event('user_comment_check',
+ $comment_action, $comm
+ );
+
+ if ( $comment_action!='reject' )
+ {
+ $query = '
+INSERT INTO '.COMMENTS_TABLE.'
+ (author, content, date, validated, validation_date, image_id)
+ VALUES (
+ "'.addslashes($comm['author']).'",
+ "'.addslashes($comm['content']).'",
+ NOW(),
+ "'.($comment_action=='validate' ? 'true':'false').'",
+ '.($comment_action=='validate' ? 'NOW()':'NULL').',
+ '.$comm['image_id'].'
+ )
+';
+
+ pwg_query($query);
+
+ $comm['id'] = mysql_insert_id();
+
+ if
+ (
+ ($comment_action=='validate' and $conf['email_admin_on_comment'])
+ or
+ ($comment_action!='validate' and $conf['email_admin_on_comment_validation'])
+ )
+ {
+ include_once(PHPWG_ROOT_PATH.'include/functions_mail.inc.php');
+
+ $del_url =
+ get_absolute_root_url().'comments.php?delete='.$comm['id'];
+
+ $keyargs_content = array
+ (
+ get_l10n_args('Author: %s', $comm['author']),
+ get_l10n_args('Comment: %s', $comm['content']),
+ get_l10n_args('', ''),
+ get_l10n_args('Delete: %s', $del_url)
+ );
+
+ if ($comment_action!='validate')
+ {
+ $keyargs_content[] =
+ get_l10n_args('', '');
+ $keyargs_content[] =
+ get_l10n_args('Validate: %s',
+ get_absolute_root_url().'comments.php?validate='.$comm['id']);
+ }
+
+ pwg_mail_notification_admins
+ (
+ get_l10n_args('Comment by %s', $comm['author']),
+ $keyargs_content
+ );
+ }
+ }
+ return $comment_action;
+}
+
+?> \ No newline at end of file
diff --git a/BSF/include/functions_cookie.inc.php b/BSF/include/functions_cookie.inc.php
new file mode 100644
index 000000000..159a5b538
--- /dev/null
+++ b/BSF/include/functions_cookie.inc.php
@@ -0,0 +1,115 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+// cookie_path returns the path to use for the Piwigo cookie.
+// If Piwigo is installed on :
+// http://domain.org/meeting/gallery/category.php
+// cookie_path will return : "/meeting/gallery"
+function cookie_path()
+{
+ if ( isset($_SERVER['REDIRECT_SCRIPT_NAME']) and
+ !empty($_SERVER['REDIRECT_SCRIPT_NAME']) )
+ {
+ $scr = $_SERVER['REDIRECT_SCRIPT_NAME'];
+ }
+ else if ( isset($_SERVER['REDIRECT_URL']) )
+ {
+ // mod_rewrite is activated for upper level directories. we must set the
+ // cookie to the path shown in the browser otherwise it will be discarded.
+ if
+ (
+ isset($_SERVER['PATH_INFO']) and !empty($_SERVER['PATH_INFO']) and
+ ($_SERVER['REDIRECT_URL'] !== $_SERVER['PATH_INFO']) and
+ (substr($_SERVER['REDIRECT_URL'],-strlen($_SERVER['PATH_INFO']))
+ == $_SERVER['PATH_INFO'])
+ )
+ {
+ $scr = substr($_SERVER['REDIRECT_URL'], 0,
+ strlen($_SERVER['REDIRECT_URL'])-strlen($_SERVER['PATH_INFO']));
+ }
+ else
+ {
+ $scr = $_SERVER['REDIRECT_URL'];
+ }
+ }
+ else
+ {
+ $scr = $_SERVER['SCRIPT_NAME'];
+ }
+
+ $scr = substr($scr,0,strrpos( $scr,'/'));
+
+ // add a trailing '/' if needed
+ if ((strlen($scr) == 0) or ($scr{strlen($scr)-1} !== '/'))
+ {
+ $scr .= '/';
+ }
+
+ if ( substr(PHPWG_ROOT_PATH,0,3)=='../')
+ { // this is maybe a plugin inside pwg directory
+ // TODO - what if it is an external script outside PWG ?
+ $scr = $scr.PHPWG_ROOT_PATH;
+ while (1)
+ {
+ $new = preg_replace('#[^/]+/\.\.(/|$)#', '', $scr);
+ if ($new==$scr)
+ {
+ break;
+ }
+ $scr=$new;
+ }
+ }
+ return $scr;
+}
+
+/**
+ * persistently stores a variable in pwg cookie
+ * @return boolean true on success
+ * @see pwg_get_cookie_var
+ */
+function pwg_set_cookie_var($var, $value)
+{
+ $_COOKIE['pwg_'.$var] = $value;
+ return
+ setcookie('pwg_'.$var, $value,
+ strtotime('+10 years'), cookie_path());
+}
+
+/**
+ * retrieves the value of a persistent variable in pwg cookie
+ * @return mixed
+ * @see pwg_set_cookie_var
+ */
+function pwg_get_cookie_var($var, $default = null)
+{
+ if (isset($_COOKIE['pwg_'.$var]))
+ {
+ return $_COOKIE['pwg_'.$var];
+ }
+ else
+ {
+ return $default;
+ }
+}
+
+?>
diff --git a/BSF/include/functions_filter.inc.php b/BSF/include/functions_filter.inc.php
new file mode 100644
index 000000000..24efb0994
--- /dev/null
+++ b/BSF/include/functions_filter.inc.php
@@ -0,0 +1,63 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+
+/**
+ * Get a check key for filtered data
+ * Check key are composed of elements witch force to compute data
+ *
+ * @param null
+ * @return strinf check_key
+ */
+function get_filter_check_key()
+{
+ global $user, $filter;
+
+ return $user['id'].$filter['recent_period'].date('Ymd');
+}
+
+/**
+ * update data of categories with filtered values
+ *
+ * @param array list of categories
+ * @return null
+ */
+function update_cats_with_filtered_data(&$cats)
+{
+ global $filter;
+
+ if ($filter['enabled'])
+ {
+ $upd_fields = array('date_last', 'max_date_last', 'count_images', 'count_categories', 'nb_images');
+
+ foreach ($cats as $cat_id => $category)
+ {
+ foreach ($upd_fields as $upd_field)
+ {
+ $cats[$cat_id][$upd_field] = $filter['categories'][$category['id']][$upd_field];
+ }
+ }
+ }
+}
+
+?>
diff --git a/BSF/include/functions_group.inc.php b/BSF/include/functions_group.inc.php
new file mode 100644
index 000000000..a5c07b5b5
--- /dev/null
+++ b/BSF/include/functions_group.inc.php
@@ -0,0 +1,26 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+// with 1.5 preparation, no group dedicated function is used.
+
+?>
diff --git a/BSF/include/functions_html.inc.php b/BSF/include/functions_html.inc.php
new file mode 100644
index 000000000..b51f6f9df
--- /dev/null
+++ b/BSF/include/functions_html.inc.php
@@ -0,0 +1,751 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+function get_icon($date, $is_child_date = false)
+{
+ global $page, $user;
+
+ if (empty($date))
+ {
+ return '';
+ }
+
+ if (isset($page['get_icon_cache'][$date]))
+ {
+ if (! $page['get_icon_cache'][$date] )
+ return '';
+ return $page['get_icon_cache']['_icons_'][$is_child_date];
+ }
+
+ if (!isset($page['get_icon_cache']['sql_recent_date']))
+ {
+ // Use MySql date in order to standardize all recent "actions/queries"
+ list($page['get_icon_cache']['sql_recent_date']) =
+ mysql_fetch_array(pwg_query('select SUBDATE(
+ CURRENT_DATE,INTERVAL '.$user['recent_period'].' DAY)'));
+ }
+
+ $page['get_icon_cache'][$date] = false;
+ if ( $date > $page['get_icon_cache']['sql_recent_date'] )
+ {
+ if ( !isset($page['get_icon_cache']['_icons_'] ) )
+ {
+ $icons = array(false => 'recent', true => 'recent_by_child' );
+ $title = sprintf(
+ l10n('elements posted during the last %d days'),
+ $user['recent_period']
+ );
+ foreach ($icons as $key => $icon)
+ {
+ $icon_url = get_themeconf('icon_dir').'/'.$icon.'.png';
+ $size = getimagesize( PHPWG_ROOT_PATH.$icon_url );
+ $icon_url = get_root_url().$icon_url;
+ $output = '<img title="'.$title.'" src="'.$icon_url.'" class="icon" style="border:0;';
+ $output.= 'height:'.$size[1].'px;width:'.$size[0].'px" alt="(!)" />';
+ $page['get_icon_cache']['_icons_'][$key] = $output;
+ }
+ }
+ $page['get_icon_cache'][$date] = true;
+ }
+
+ if (! $page['get_icon_cache'][$date] )
+ return '';
+ return $page['get_icon_cache']['_icons_'][$is_child_date];
+}
+
+function create_navigation_bar(
+ $url, $nb_element, $start, $nb_element_page, $clean_url = false
+ )
+{
+ global $conf;
+
+ $pages_around = $conf['paginate_pages_around'];
+ $start_str = $clean_url ? '/start-' :
+ ( ( strstr($url, '?')===false ? '?':'&amp;') . 'start=' );
+
+ $navbar = '';
+
+ // current page detection
+ if (!isset($start)
+ or !is_numeric($start)
+ or (is_numeric($start) and $start < 0))
+ {
+ $start = 0;
+ }
+
+ // navigation bar useful only if more than one page to display !
+ if ($nb_element > $nb_element_page)
+ {
+ // current page and last page
+ $cur_page = ceil($start / $nb_element_page) + 1;
+ $maximum = ceil($nb_element / $nb_element_page);
+
+ // link to first page ?
+ if ($cur_page != 1)
+ {
+ $navbar.=
+ '<a href="'.$url.'" rel="first">'
+ .l10n('first_page')
+ .'</a>';
+ }
+ else
+ {
+ $navbar.= l10n('first_page');
+ }
+ $navbar.= ' | ';
+ // link on previous page ?
+ if ($start != 0)
+ {
+ $previous = $start - $nb_element_page;
+
+ $navbar.=
+ '<a href="'
+ .$url.($previous > 0 ? $start_str.$previous : '')
+ .'" rel="prev">'
+ .l10n('previous_page')
+ .'</a>';
+ }
+ else
+ {
+ $navbar.= l10n('previous_page');
+ }
+ $navbar.= ' |';
+
+ if ($cur_page > $pages_around + 1)
+ {
+ $navbar.= '&nbsp;<a href="'.$url.'">1</a>';
+
+ if ($cur_page > $pages_around + 2)
+ {
+ $navbar.= ' ...';
+ }
+ }
+
+ // inspired from punbb source code
+ for ($i = $cur_page - $pages_around, $stop = $cur_page + $pages_around + 1;
+ $i < $stop;
+ $i++)
+ {
+ if ($i < 1 or $i > $maximum)
+ {
+ continue;
+ }
+ else if ($i != $cur_page)
+ {
+ $temp_start = ($i - 1) * $nb_element_page;
+
+ $navbar.=
+ '&nbsp;'
+ .'<a href="'.$url
+ .($temp_start > 0 ? $start_str.$temp_start : '')
+ .'">'
+ .$i
+ .'</a>';
+ }
+ else
+ {
+ $navbar.=
+ '&nbsp;'
+ .'<span class="pageNumberSelected">'
+ .$i
+ .'</span>';
+ }
+ }
+
+ if ($cur_page < ($maximum - $pages_around))
+ {
+ $temp_start = ($maximum - 1) * $nb_element_page;
+
+ if ($cur_page < ($maximum - $pages_around - 1))
+ {
+ $navbar.= ' ...';
+ }
+
+ $navbar.= ' <a href="'.$url.$start_str.$temp_start.'">'.$maximum.'</a>';
+ }
+
+ $navbar.= ' | ';
+ // link on next page ?
+ if ($nb_element > $nb_element_page
+ and $start + $nb_element_page < $nb_element)
+ {
+ $next = $start + $nb_element_page;
+
+ $navbar.=
+ '<a href="'.$url.$start_str.$next.'" rel="next">'
+ .l10n('next_page')
+ .'</a>';
+ }
+ else
+ {
+ $navbar.= l10n('next_page');
+ }
+
+ $navbar.= ' | ';
+ // link to last page ?
+ if ($cur_page != $maximum)
+ {
+ $temp_start = ($maximum - 1) * $nb_element_page;
+
+ $navbar.=
+ '<a href="'.$url.$start_str.$temp_start.'" rel="last">'
+ .l10n('last_page')
+ .'</a>';
+ }
+ else
+ {
+ $navbar.= l10n('last_page');
+ }
+ }
+ return $navbar;
+}
+
+/**
+ * returns the list of categories as a HTML string
+ *
+ * categories string returned contains categories as given in the input
+ * array $cat_informations. $cat_informations array must be an array
+ * of array( id=>?, name=>?, permalink=>?). If url input parameter is null,
+ * returns only the categories name without links.
+ *
+ * @param array cat_informations
+ * @param string url
+ * @param boolean replace_space
+ * @return string
+ */
+function get_cat_display_name($cat_informations,
+ $url = '',
+ $replace_space = true)
+{
+ global $conf;
+
+ $output = '';
+ $is_first = true;
+ foreach ($cat_informations as $cat)
+ {
+ is_array($cat) or trigger_error(
+ 'get_cat_display_name wrong type for category ', E_USER_WARNING
+ );
+ if ($is_first)
+ {
+ $is_first = false;
+ }
+ else
+ {
+ $output.= $conf['level_separator'];
+ }
+
+ if ( !isset($url) )
+ {
+ $output.= $cat['name'];
+ }
+ elseif ($url == '')
+ {
+ $output.= '<a href="'
+ .make_index_url(
+ array(
+ 'category' => $cat,
+ )
+ )
+ .'">';
+ $output.= $cat['name'].'</a>';
+ }
+ else
+ {
+ $output.= '<a href="'.PHPWG_ROOT_PATH.$url.$cat['id'].'">';
+ $output.= $cat['name'].'</a>';
+ }
+ }
+ if ($replace_space)
+ {
+ return replace_space($output);
+ }
+ else
+ {
+ return $output;
+ }
+}
+
+/**
+ * returns the list of categories as a HTML string, with cache of names
+ *
+ * categories string returned contains categories as given in the input
+ * array $cat_informations. $uppercats is the list of category ids to
+ * display in the right order. If url input parameter is empty, returns only
+ * the categories name without links.
+ *
+ * @param string uppercats
+ * @param string url
+ * @param boolean replace_space
+ * @return string
+ */
+function get_cat_display_name_cache($uppercats,
+ $url = '',
+ $replace_space = true)
+{
+ global $cache, $conf;
+
+ if (!isset($cache['cat_names']))
+ {
+ $query = '
+SELECT id, name, permalink
+ FROM '.CATEGORIES_TABLE.'
+;';
+ $cache['cat_names'] = hash_from_query($query, 'id');
+ }
+
+ $output = '';
+ $is_first = true;
+ foreach (explode(',', $uppercats) as $category_id)
+ {
+ $cat = $cache['cat_names'][$category_id];
+
+ if ($is_first)
+ {
+ $is_first = false;
+ }
+ else
+ {
+ $output.= $conf['level_separator'];
+ }
+
+ if ( !isset($url) )
+ {
+ $output.= $cat['name'];
+ }
+ elseif ($url == '')
+ {
+ $output.= '
+<a href="'
+ .make_index_url(
+ array(
+ 'category' => $cat,
+ )
+ )
+ .'">'.$cat['name'].'</a>';
+ }
+ else
+ {
+ $output.= '
+<a href="'.PHPWG_ROOT_PATH.$url.$category_id.'">'.$cat['name'].'</a>';
+ }
+ }
+ if ($replace_space)
+ {
+ return replace_space($output);
+ }
+ else
+ {
+ return $output;
+ }
+}
+
+/**
+ * returns the HTML code for a category item in the menu (for the main page)
+ *
+ * HTML code generated uses logical list tags ul and each category is an
+ * item li. The paramter given is the category informations as an array,
+ * used keys are : id, name, nb_images, max_date_last, date_last
+ * count_images, count_categories
+ *
+ * @param array categories
+ * @return string
+ */
+function get_html_menu_category($categories, $selected_category)
+{
+ $ref_level = 0;
+ $level = 0;
+
+ $menu = trigger_event('get_html_menu_category', '',
+ $categories, $selected_category);
+ if (strlen($menu))
+ {
+ return $menu;
+ }
+
+ foreach ($categories as $category)
+ {
+ $level = substr_count($category['global_rank'], '.') + 1;
+ if ($level > $ref_level)
+ {
+ $menu.= "\n<ul>";
+ }
+ else if ($level == $ref_level)
+ {
+ $menu.= "\n</li>";
+ }
+ else if ($level < $ref_level)
+ {
+ // we may have to close more than one level at the same time...
+ $menu.= "\n</li>";
+ $menu.= str_repeat("\n</ul></li>",($ref_level-$level));
+ }
+ $ref_level = $level;
+
+ $menu.= "\n\n".'<li';
+ if ($category['id'] == @$selected_category['id'])
+ {
+ $menu.= ' class="selected"';
+ }
+ $menu.= '>';
+
+ $url = make_index_url(
+ array(
+ 'category' => $category
+ )
+ );
+
+ $title = get_display_images_count
+ (
+ $category['nb_images'],
+ $category['count_images'],
+ $category['count_categories'],
+ false,
+ ' / '
+ );
+
+ $menu.= '<a href="'.$url.'"';
+ if ($selected_category!=null
+ and $category['id'] == $selected_category['id_uppercat'])
+ {
+ $menu.= ' rel="up"';
+ }
+ $menu.= ' title="'.$title.'">'.$category['name'].'</a>';
+
+ if ( $category['count_images']>0 )
+ {// at least one direct or indirect image
+ $menu.= "\n".'<span class="';
+ // at least one image in this category -> class menuInfoCat
+ $menu.= ($category['nb_images'] > 0 ? "menuInfoCat"
+ : "menuInfoCatByChild").'"';
+ $menu.= ' title=" '.$title.'">';
+ // show total number of images
+ $menu.= '['.$category['count_images'].']';
+ $menu.= '</span>';
+ }
+ $child_date_last = @$category['max_date_last']> @$category['date_last'];
+ $menu.= get_icon($category['max_date_last'], $child_date_last);
+ }
+
+ $menu.= str_repeat("\n</li></ul>",($level));
+
+ return $menu;
+}
+
+/**
+ * returns HTMLized comment contents retrieved from database
+ *
+ * newlines becomes br tags, _word_ becomes underline, /word/ becomes
+ * italic, *word* becomes bolded
+ *
+ * @param string content
+ * @return string
+ */
+function parse_comment_content($content)
+{
+ $pattern = '/(https?:\/\/\S*)/';
+ $replacement = '<a href="$1" rel="nofollow">$1</a>';
+ $content = preg_replace($pattern, $replacement, $content);
+
+ $content = nl2br($content);
+
+ // replace _word_ by an underlined word
+ $pattern = '/\b_(\S*)_\b/';
+ $replacement = '<span style="text-decoration:underline;">$1</span>';
+ $content = preg_replace($pattern, $replacement, $content);
+
+ // replace *word* by a bolded word
+ $pattern = '/\b\*(\S*)\*\b/';
+ $replacement = '<span style="font-weight:bold;">$1</span>';
+ $content = preg_replace($pattern, $replacement, $content);
+
+ // replace /word/ by an italic word
+ $pattern = "/\/(\S*)\/(\s)/";
+ $replacement = '<span style="font-style:italic;">$1$2</span>';
+ $content = preg_replace($pattern, $replacement, $content);
+
+ $content = '<div>'.$content.'</div>';
+ return $content;
+}
+
+function get_cat_display_name_from_id($cat_id,
+ $url = '',
+ $replace_space = true)
+{
+ $cat_info = get_cat_info($cat_id);
+ return get_cat_display_name($cat_info['upper_names'], $url, $replace_space);
+}
+
+/**
+ * Returns an HTML list of tags. It can be a multi select field or a list of
+ * checkboxes.
+ *
+ * @param string HTML field name
+ * @param array selected tag ids
+ * @return array
+ */
+function get_html_tag_selection(
+ $tags,
+ $fieldname,
+ $selecteds = array(),
+ $forbidden_categories = null
+ )
+{
+ global $conf;
+
+ if (count ($tags) == 0 )
+ {
+ return '';
+ }
+ $output = '<ul class="tagSelection">';
+ foreach ($tags as $tag)
+ {
+ $output.=
+ '<li>'
+ .'<label>'
+ .'<input type="checkbox" name="'.$fieldname.'[]"'
+ .' value="'.$tag['id'].'"'
+ ;
+
+ if (in_array($tag['id'], $selecteds))
+ {
+ $output.= ' checked="checked"';
+ }
+
+ $output.=
+ ' />'
+ .' '. $tag['name']
+ .'</label>'
+ .'</li>'
+ ."\n"
+ ;
+ }
+ $output.= '</ul>';
+
+ return $output;
+}
+
+function name_compare($a, $b)
+{
+ return strcmp(strtolower($a['name']), strtolower($b['name']));
+}
+
+/**
+ * exits the current script (either exit or redirect)
+ */
+function access_denied()
+{
+ global $user;
+
+ $login_url =
+ get_root_url().'identification.php?redirect='
+ .urlencode(urlencode($_SERVER['REQUEST_URI']));
+
+ if ( isset($user) and !is_a_guest() )
+ {
+ echo '<div style="text-align:center;">'.l10n('access_forbiden').'<br />';
+ echo '<a href="'.get_root_url().'identification.php">'.l10n('identification').'</a>&nbsp;';
+ echo '<a href="'.make_index_url().'">'.l10n('home').'</a></div>';
+ exit();
+ }
+ else
+ {
+ set_status_header(401);
+ redirect_html($login_url);
+ }
+}
+
+/**
+ * exits the current script with 403 code
+ * @param string msg a message to display
+ * @param string alternate_url redirect to this url
+ */
+function page_forbidden($msg, $alternate_url=null)
+{
+ set_status_header(403);
+ if ($alternate_url==null)
+ $alternate_url = make_index_url();
+ redirect_html( $alternate_url,
+ '<div style="text-align:left; margin-left:5em;margin-bottom:5em;">
+<h1 style="text-align:left; font-size:36px;">Forbidden</h1><br/>'
+.$msg.'</div>',
+ 5 );
+}
+
+/**
+ * exits the current script with 400 code
+ * @param string msg a message to display
+ * @param string alternate_url redirect to this url
+ */
+function bad_request($msg, $alternate_url=null)
+{
+ set_status_header(400);
+ if ($alternate_url==null)
+ $alternate_url = make_index_url();
+ redirect_html( $alternate_url,
+ '<div style="text-align:left; margin-left:5em;margin-bottom:5em;">
+<h1 style="text-align:left; font-size:36px;">Bad request</h1><br/>'
+.$msg.'</div>',
+ 5 );
+}
+
+/**
+ * exits the current script with 404 code when a page cannot be found
+ * @param string msg a message to display
+ * @param string alternate_url redirect to this url
+ */
+function page_not_found($msg, $alternate_url=null)
+{
+ set_status_header(404);
+ if ($alternate_url==null)
+ $alternate_url = make_index_url();
+ redirect_html( $alternate_url,
+ '<div style="text-align:left; margin-left:5em;margin-bottom:5em;">
+<h1 style="text-align:left; font-size:36px;">Page not found</h1><br/>'
+.$msg.'</div>',
+ 5 );
+}
+
+/* returns the title to be displayed above thumbnails on tag page
+ */
+function get_tags_content_title()
+{
+ global $page;
+ $title = count($page['tags']) > 1 ? l10n('Tags') : l10n('Tag');
+ $title.= ' ';
+
+ for ($i=0; $i<count($page['tags']); $i++)
+ {
+ $title.= $i>0 ? ' + ' : '';
+
+ $title.=
+ '<a href="'
+ .make_index_url(
+ array(
+ 'tags' => array( $page['tags'][$i] )
+ )
+ )
+ .'" title="'
+ .l10n('See elements linked to this tag only')
+ .'">'
+ .$page['tags'][$i]['name']
+ .'</a>';
+
+ if ( count($page['tags'])>2 )
+ {
+ $other_tags = $page['tags'];
+ unset ( $other_tags[$i] );
+ $title.=
+ '<a href="'
+ .make_index_url(
+ array(
+ 'tags' => $other_tags
+ )
+ )
+ .'" style="border:none;" title="'
+ .l10n('remove this tag')
+ .'"><img src="'
+ .get_root_url().get_themeconf('icon_dir').'/remove_s.png'
+ .'" alt="x" style="vertical-align:bottom;" class="button"/>'
+ .'</a>';
+ }
+
+ }
+ return $title;
+}
+
+/**
+ Sets the http status header (200,401,...)
+ */
+function set_status_header($code, $text='')
+{
+ if (empty($text))
+ {
+ switch ($code)
+ {
+ case 200: $text='OK';break;
+ case 301: $text='Moved permanently';break;
+ case 302: $text='Moved temporarily';break;
+ case 304: $text='Not modified';break;
+ case 400: $text='Bad request';break;
+ case 401: $text='Authorization required';break;
+ case 403: $text='Forbidden';break;
+ case 404: $text='Not found';break;
+ case 500: $text='Server error';break;
+ case 503: $text='Service unavailable';break;
+ }
+ }
+ $protocol = $_SERVER["SERVER_PROTOCOL"];
+ if ( ('HTTP/1.1' != $protocol) && ('HTTP/1.0' != $protocol) )
+ $protocol = 'HTTP/1.0';
+
+ if ( version_compare( phpversion(), '4.3.0', '>=' ) )
+ {
+ header( "$protocol $code $text", true, $code );
+ }
+ else
+ {
+ header( "$protocol $code $text" );
+ }
+ trigger_action('set_status_header', $code, $text);
+}
+
+/** returns the category comment for rendering in html.
+ * this is an event handler. don't call directly
+ */
+function render_category_description($desc)
+{
+ global $conf;
+ if ( !( $conf['allow_html_descriptions'] and
+ preg_match('/<(div|br|img|script).*>/i', $desc) ) )
+ {
+ $desc = nl2br($desc);
+ }
+ return $desc;
+}
+
+/** returns the category comment for rendering in html textual mode (subcatify)
+ * this is an event handler. don't call directly
+ */
+function render_category_literal_description($desc)
+{
+ return strip_tags($desc, '<span><p><a><br><b><i><small><big><strong><em>');
+}
+
+/** returns the argument_ids array with new sequenced keys based on related
+ * names. Sequence is not case sensitive.
+ * Warning: By definition, this function breaks original keys
+ */
+function order_by_name($element_ids,$name)
+{
+ $ordered_element_ids = array();
+ foreach ($element_ids as $k_id => $element_id)
+ {
+ $key = strtolower($name[$element_id]) .'-'. $name[$element_id] .'-'. $k_id;
+ $ordered_element_ids[$key] = $element_id;
+ }
+ ksort($ordered_element_ids);
+ return $ordered_element_ids;
+}
+
+?>
diff --git a/BSF/include/functions_mail.inc.php b/BSF/include/functions_mail.inc.php
new file mode 100644
index 000000000..9e7f9024d
--- /dev/null
+++ b/BSF/include/functions_mail.inc.php
@@ -0,0 +1,822 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+// +-----------------------------------------------------------------------+
+// | functions |
+// +-----------------------------------------------------------------------+
+
+
+/**
+ * Encodes a string using Q form if required (RFC2045)
+ * mail headers MUST contain only US-ASCII characters
+ */
+function encode_mime_header($str)
+{
+ $x = preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
+ if ($x==0)
+ {
+ return $str;
+ }
+ // Replace every high ascii, control =, ? and _ characters
+ $str = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e',
+ "'='.sprintf('%02X', ord('\\1'))", $str);
+
+ // Replace every spaces to _ (more readable than =20)
+ $str = str_replace(" ", "_", $str);
+
+ global $lang_info;
+ return '=?'.get_pwg_charset().'?Q?'.$str.'?=';
+}
+
+/*
+ * Returns the name of the mail sender :
+ *
+ * @return string
+ */
+function get_mail_sender_name()
+{
+ global $conf;
+
+ return (empty($conf['mail_sender_name']) ? $conf['gallery_title'] : $conf['mail_sender_name']);
+}
+
+/*
+ * Returns an array of mail configuration parameters :
+ *
+ * - mail_options: see $conf['mail_options']
+ * - send_bcc_mail_webmaster: see $conf['send_bcc_mail_webmaster']
+ * - email_webmaster: mail corresponding to $conf['webmaster_id']
+ * - formated_email_webmaster: the name of webmaster is $conf['gallery_title']
+ * - text_footer: Piwigo and version
+ *
+ * @return array
+ */
+function get_mail_configuration()
+{
+ global $conf;
+
+ $conf_mail = array(
+ 'mail_options' => $conf['mail_options'],
+ 'send_bcc_mail_webmaster' => $conf['send_bcc_mail_webmaster'],
+ 'default_email_format' => $conf['default_email_format'],
+ 'use_smtp' => !empty($conf['smtp_host']),
+ 'smtp_host' => $conf['smtp_host'],
+ 'smtp_user' => $conf['smtp_user'],
+ 'smtp_password' => $conf['smtp_password']
+ );
+
+ // we have webmaster id among user list, what's his email address ?
+ $conf_mail['email_webmaster'] = get_webmaster_mail_address();
+
+ // name of the webmaster is the title of the gallery
+ $conf_mail['formated_email_webmaster'] =
+ format_email(get_mail_sender_name(), $conf_mail['email_webmaster']);
+
+ $conf_mail['boundary_key'] = generate_key(32);
+
+ return $conf_mail;
+}
+
+/**
+ * Returns an email address with an associated real name
+ *
+ * @param string name
+ * @param string email
+ */
+function format_email($name, $email)
+{
+ // Spring cleaning
+ $cvt_email = trim(preg_replace('#[\n\r]+#s', '', $email));
+ $cvt_name = trim(preg_replace('#[\n\r]+#s', '', $name));
+
+ if ($cvt_name!="")
+ {
+ $cvt_name = encode_mime_header(
+ '"'
+ .addcslashes($cvt_name,'"')
+ .'"');
+ $cvt_name .= ' ';
+ }
+
+ if (strpos($cvt_email, '<') === false)
+ {
+ return $cvt_name.'<'.$cvt_email.'>';
+ }
+ else
+ {
+ return $cvt_name.$cvt_email;
+ }
+}
+
+/**
+ * Returns an email address list with minimal email string
+ *
+ * @param string with email list (email separated by comma)
+ */
+function get_strict_email_list($email_list)
+{
+ $result = array();
+ $list = explode(',', $email_list);
+ foreach ($list as $email)
+ {
+ if (strpos($email, '<') !== false)
+ {
+ $email = preg_replace('/.*<(.*)>.*/i', '$1', $email);
+ }
+ $result[] = trim($email);
+ }
+
+ return implode(',', $result);
+}
+
+/**
+ * Returns an completed array template/theme
+ * completed with get_default_template()
+ *
+ * @params:
+ * - args: incompleted array of template/theme
+ * o template: template to use [default get_default_template()]
+ * o theme: template to use [default get_default_template()]
+ */
+function get_array_template_theme($args = array())
+{
+ global $conf;
+
+ $res = array();
+
+ if (empty($args['template']) or empty($args['theme']))
+ {
+ list($res['template'], $res['theme']) = explode('/', get_default_template());
+ }
+
+ if (!empty($args['template']))
+ {
+ $res['template'] = $args['template'];
+ }
+
+ if (!empty($args['theme']))
+ {
+ $res['theme'] = $args['theme'];
+ }
+
+ return $res;
+}
+
+/**
+ * Return an new mail template
+ *
+ * @params:
+ * - email_format: mail format
+ * - args: function params of mail function:
+ * o template: template to use [default get_default_template()]
+ * o theme: template to use [default get_default_template()]
+ */
+function & get_mail_template($email_format, $args = array())
+{
+ $args = get_array_template_theme($args);
+
+ $mail_template = new Template(PHPWG_ROOT_PATH.'template/'.$args['template'], $args['theme']);
+ $mail_template->set_template_dir(PHPWG_ROOT_PATH.'template/'.$args['template'].'/mail/'.$email_format);
+ return $mail_template;
+}
+
+/**
+ * Return string email format (html or not)
+ *
+ * @param string format
+ */
+function get_str_email_format($is_html)
+{
+ return ($is_html ? 'text/html' : 'text/plain');
+}
+
+/*
+ * Switch language to param language
+ * All entries are push on language stack
+ *
+ * @param string language
+ */
+function switch_lang_to($language)
+{
+ global $switch_lang, $user, $lang, $lang_info;
+
+ if (count($switch_lang['stack']) == 0)
+ {
+ $prev_language = $user['language'];
+ }
+ else
+ {
+ $prev_language = end($switch_lang['stack']);
+ }
+
+ $switch_lang['stack'][] = $language;
+
+ if ($prev_language != $language)
+ {
+ if (!isset($switch_lang['language'][$prev_language]))
+ {
+ $switch_lang[$prev_language]['lang_info'] = $lang_info;
+ $switch_lang[$prev_language]['lang'] = $lang;
+ }
+
+ if (!isset($switch_lang['language'][$language]))
+ {
+ // Re-Init language arrays
+ $lang_info = array();
+ $lang = array();
+
+ // language files
+ load_language('common.lang', '', $language);
+ // No test admin because script is checked admin (user selected no)
+ // Translations are in admin file too
+ load_language('admin.lang', '', $language);
+ trigger_action('loading_lang');
+ load_language('local.lang', '', $language);
+
+ $switch_lang[$language]['lang_info'] = $lang_info;
+ $switch_lang[$language]['lang'] = $lang;
+ }
+ else
+ {
+ $lang_info = $switch_lang[$language]['lang_info'];
+ $lang = $switch_lang[$language]['lang'];
+ }
+
+ $user['language'] = $language;
+ }
+}
+
+/*
+ * Switch back language pushed with switch_lang_to function
+ *
+ * @param: none
+ */
+function switch_lang_back()
+{
+ global $switch_lang, $user, $lang, $lang_info;
+
+ $last_language = array_pop($switch_lang['stack']);
+
+ if (count($switch_lang['stack']) > 0)
+ {
+ $language = end($switch_lang['stack']);
+ }
+ else
+ {
+ $language = $user['language'];
+ }
+
+ if ($last_language != $language)
+ {
+ if (!isset($switch_lang['language'][$language]))
+ {
+ $lang_info = $switch_lang[$language]['lang_info'];
+ $lang = $switch_lang[$language]['lang'];
+ }
+ $user['language'] = $language;
+ }
+}
+
+/**
+ * Returns email of all administrator
+ *
+ * @return string
+ */
+/*
+ * send en notification email to all administrators
+ * if a administrator is doing action,
+ * he's be removed to email list
+ *
+ * @param:
+ * - keyargs_subject: mail subject on l10n_args format
+ * - keyargs_content: mail content on l10n_args format
+ *
+ * @return boolean (Ok or not)
+ */
+function pwg_mail_notification_admins($keyargs_subject, $keyargs_content)
+{
+ // Check arguments
+ if
+ (
+ empty($keyargs_subject) or
+ empty($keyargs_content)
+ )
+ {
+ return false;
+ }
+
+ global $conf, $user;
+ $return = true;
+
+ $admins = array();
+
+ $query = '
+select
+ U.'.$conf['user_fields']['username'].' as username,
+ U.'.$conf['user_fields']['email'].' as mail_address
+from
+ '.USERS_TABLE.' as U,
+ '.USER_INFOS_TABLE.' as I
+where
+ I.user_id = U.'.$conf['user_fields']['id'].' and
+ I.status in (\'webmaster\', \'admin\') and
+ I.adviser = \'false\' and
+ '.$conf['user_fields']['email'].' is not null and
+ I.user_id <> '.$user['id'].'
+order by
+ username
+';
+
+ $datas = pwg_query($query);
+ if (!empty($datas))
+ {
+ while ($admin = mysql_fetch_array($datas))
+ {
+ if (!empty($admin['mail_address']))
+ {
+ array_push($admins, format_email($admin['username'], $admin['mail_address']));
+ }
+ }
+ }
+
+ if (count($admins) > 0)
+ {
+ $keyargs_content_admin_info = array
+ (
+ get_l10n_args('Connected user: %s', $user['username']),
+ get_l10n_args('IP: %s', $_SERVER['REMOTE_ADDR']),
+ get_l10n_args('Browser: %s', $_SERVER['HTTP_USER_AGENT'])
+ );
+
+ switch_lang_to(get_default_language());
+
+ $return = pwg_mail
+ (
+ '',
+ array
+ (
+ 'Bcc' => $admins,
+ 'subject' => '['.$conf['gallery_title'].'] '.l10n_args($keyargs_subject),
+ 'content' =>
+ l10n_args($keyargs_content)."\n\n"
+ .l10n_args($keyargs_content_admin_info)."\n",
+ 'content_format' => 'text/plain'
+ )
+ ) and $return;
+
+ switch_lang_back();
+ }
+
+ return $return;
+}
+
+/*
+ * send en email to user's group
+ *
+ * @param:
+ * - group_id: mail are sent to group with this Id
+ * - email_format: mail format
+ * - keyargs_subject: mail subject on l10n_args format
+ * - dirname: short name of directory including template
+ * - tpl_shortname: short template name without extension
+ * - assign_vars: array used to assign_vars to mail template
+ * - language_selected: send mail only to user with this selected language
+ *
+ * @return boolean (Ok or not)
+ */
+function pwg_mail_group(
+ $group_id, $email_format, $keyargs_subject,
+ $dirname, $tpl_shortname,
+ $assign_vars = array(), $language_selected = '')
+{
+ // Check arguments
+ if
+ (
+ empty($group_id) or
+ empty($email_format) or
+ empty($keyargs_subject) or
+ empty($tpl_shortname)
+ )
+ {
+ return false;
+ }
+
+ global $conf;
+ $return = true;
+
+ $query = '
+SELECT
+ distinct language, template
+FROM
+ '.USER_GROUP_TABLE.' as ug
+ INNER JOIN '.USERS_TABLE.' as u ON '.$conf['user_fields']['id'].' = ug.user_id
+ INNER JOIN '.USER_INFOS_TABLE.' as ui ON ui.user_id = ug.user_id
+WHERE
+ '.$conf['user_fields']['email'].' IS NOT NULL
+ AND group_id = '.$group_id;
+
+ if (!empty($language_selected))
+ {
+ $query .= '
+ AND language = \''.$language_selected.'\'';
+ }
+
+ $query .= '
+;';
+
+ $result = pwg_query($query);
+
+ if (mysql_num_rows($result) > 0)
+ {
+ $list = array();
+ while ($row = mysql_fetch_array($result))
+ {
+ $row['template_theme'] = $row['template'];
+ list($row['template'], $row['theme']) = explode('/', $row['template_theme']);
+ $list[] = $row;
+ }
+
+ foreach ($list as $elem)
+ {
+ $query = '
+SELECT
+ u.'.$conf['user_fields']['username'].' as username,
+ u.'.$conf['user_fields']['email'].' as mail_address
+FROM
+ '.USER_GROUP_TABLE.' as ug
+ INNER JOIN '.USERS_TABLE.' as u ON '.$conf['user_fields']['id'].' = ug.user_id
+ INNER JOIN '.USER_INFOS_TABLE.' as ui ON ui.user_id = ug.user_id
+WHERE
+ '.$conf['user_fields']['email'].' IS NOT NULL
+ AND group_id = '.$group_id.'
+ AND language = \''.$elem['language'].'\'
+ AND template = \''.$elem['template_theme'].'\'
+;';
+
+ $result = pwg_query($query);
+
+ if (mysql_num_rows($result) > 0)
+ {
+ $Bcc = array();
+ while ($row = mysql_fetch_array($result))
+ {
+ if (!empty($row['mail_address']))
+ {
+ array_push($Bcc, format_email($row['username'], $row['mail_address']));
+ }
+ }
+
+ if (count($Bcc) > 0)
+ {
+ switch_lang_to($elem['language']);
+
+ $mail_template = get_mail_template($email_format, $elem);
+ $mail_template->set_filename($tpl_shortname,
+ (empty($dirname) ? '' : $dirname.'/').$tpl_shortname.'.tpl');
+
+ $mail_template->assign(
+ trigger_event('mail_group_assign_vars', $assign_vars));
+
+ $return = pwg_mail
+ (
+ '',
+ array
+ (
+ 'Bcc' => $Bcc,
+ 'subject' => l10n_args($keyargs_subject),
+ 'email_format' => $email_format,
+ 'content' => $mail_template->parse($tpl_shortname, true),
+ 'content_format' => $email_format,
+ 'template' => $elem['template'],
+ 'theme' => $elem['theme']
+ )
+ ) and $return;
+
+ switch_lang_back();
+ }
+ }
+ }
+ }
+
+ return $return;
+}
+
+/*
+ * sends an email, using Piwigo specific informations
+ *
+ * @param:
+ * - to: receiver(s) of the mail (list separated by comma).
+ * - args: function params of mail function:
+ * o from: sender [default value webmaster email]
+ * o Cc: array of carbon copy receivers of the mail. [default value empty]
+ * o Bcc: array of blind carbon copy receivers of the mail. [default value empty]
+ * o subject [default value 'Piwigo']
+ * o content: content of mail [default value '']
+ * o content_format: format of mail content [default value 'text/plain']
+ * o email_format: global mail format [default value $conf_mail['default_email_format']]
+ * o template: template to use [default get_default_template()]
+ * o theme: template to use [default get_default_template()]
+ *
+ * @return boolean (Ok or not)
+ */
+function pwg_mail($to, $args = array())
+{
+ global $conf, $conf_mail, $lang_info, $page;
+
+ if (empty($to) and empty($args['Cc']) and empty($args['Bcc']))
+ {
+ return true;
+ }
+
+ if (!isset($conf_mail))
+ {
+ $conf_mail = get_mail_configuration();
+ }
+
+ if (empty($args['email_format']))
+ {
+ $args['email_format'] = $conf_mail['default_email_format'];
+ }
+
+ // Compute root_path in order have complete path
+ if ($args['email_format'] == 'text/html')
+ {
+ set_make_full_url();
+ }
+
+ if (empty($args['from']))
+ {
+ $args['from'] = $conf_mail['formated_email_webmaster'];
+ }
+ else
+ {
+ $args['from'] = format_email('', $args['from']);
+ }
+
+ if (empty($args['subject']))
+ {
+ $args['subject'] = 'Piwigo';
+ }
+ // Spring cleaning
+ $cvt_subject = trim(preg_replace('#[\n\r]+#s', '', $args['subject']));
+ // Ascii convertion
+ $cvt_subject = encode_mime_header($cvt_subject);
+
+ if (!isset($args['content']))
+ {
+ $args['content'] = '';
+ }
+
+ if (empty($args['content_format']))
+ {
+ $args['content_format'] = 'text/plain';
+ }
+
+ if ($conf_mail['send_bcc_mail_webmaster'])
+ {
+ $args['Bcc'][] = $conf_mail['formated_email_webmaster'];
+ }
+
+ if (($args['content_format'] == 'text/html') and ($args['email_format'] == 'text/plain'))
+ {
+ // Todo find function to convert html text to plain text
+ return false;
+ }
+
+ $args = array_merge($args, get_array_template_theme($args));
+
+ $headers = 'From: '.$args['from']."\n";
+ $headers.= 'Reply-To: '.$args['from']."\n";
+ if (empty($to))
+ {
+ $headers.= 'To: undisclosed-recipients: ;'."\n";
+ }
+ else
+ {
+ $headers.= 'To: '.$to."\n";
+ }
+
+ if (!empty($args['Cc']))
+ {
+ $headers.= 'Cc: '.implode(',', $args['Cc'])."\n";
+ }
+
+ if (!empty($args['Bcc']))
+ {
+ $headers.= 'Bcc: '.implode(',', $args['Bcc'])."\n";
+ }
+
+ $headers.= 'Content-Type: multipart/alternative;'."\n";
+ $headers.= ' boundary="---='.$conf_mail['boundary_key'].'";'."\n";
+ $headers.= ' reply-type=original'."\n";
+ $headers.= 'MIME-Version: 1.0'."\n";
+ $headers.= 'X-Mailer: Piwigo Mailer'."\n";
+
+ $content = '';
+
+ // key compose of indexes witch allow ti cache mail data
+ $cache_key = $args['email_format'].'-'.$lang_info['code'].'-'.$args['template'].'-'.$args['theme'];
+
+ if (!isset($conf_mail[$cache_key]))
+ {
+ if (!isset($mail_template))
+ {
+ $mail_template = get_mail_template($args['email_format']);
+ }
+
+ $mail_template->set_filename('mail_header', 'header.tpl');
+ $mail_template->set_filename('mail_footer', 'footer.tpl');
+
+ $mail_template->assign(
+ array(
+ //Header
+ 'BOUNDARY_KEY' => $conf_mail['boundary_key'],
+ 'CONTENT_TYPE' => $args['email_format'],
+ 'CONTENT_ENCODING' => get_pwg_charset(),
+ 'LANG' => $lang_info['code'],
+ 'DIR' => $lang_info['direction'],
+
+ // Footer
+ 'GALLERY_URL' =>
+ isset($page['gallery_url']) ?
+ $page['gallery_url'] : $conf['gallery_url'],
+ 'GALLERY_TITLE' =>
+ isset($page['gallery_title']) ?
+ $page['gallery_title'] : $conf['gallery_title'],
+ 'VERSION' => $conf['show_version'] ? PHPWG_VERSION : '',
+ 'PHPWG_URL' => PHPWG_URL,
+
+ 'TITLE_MAIL' => urlencode(l10n('title_send_mail')),
+ 'MAIL' => get_webmaster_mail_address()
+ ));
+
+ if ($args['email_format'] == 'text/html')
+ {
+ if (is_file($mail_template->get_template_dir().'/global-mail-css.tpl'))
+ {
+ $mail_template->set_filename('css', 'global-mail-css.tpl');
+ $mail_template->assign_var_from_handle('GLOBAL_MAIL_CSS', 'css');
+ }
+
+ $root_abs_path = dirname(dirname(__FILE__));
+
+ $file = $root_abs_path.'/template/'.$args['template'].'/theme/'.$args['theme'].'/mail-css.tpl';
+ if (is_file($file))
+ {
+ $mail_template->set_filename('css', $file);
+ $mail_template->assign_var_from_handle('MAIL_CSS', 'css');
+ }
+
+ $file = $root_abs_path.'/template-common/local-mail-css.tpl';
+ if (is_file($file))
+ {
+ $mail_template->set_filename('css', $file);
+ $mail_template->assign_var_from_handle('LOCAL_MAIL_CSS', 'css');
+ }
+ }
+
+ // what are displayed on the header of each mail ?
+ $conf_mail[$cache_key]['header'] =
+ $mail_template->parse('mail_header', true);
+
+ // what are displayed on the footer of each mail ?
+ $conf_mail[$cache_key]['footer'] =
+ $mail_template->parse('mail_footer', true);
+ }
+
+ // Header
+ $content.= $conf_mail[$cache_key]['header'];
+
+ // Content
+ if (($args['content_format'] == 'text/plain') and ($args['email_format'] == 'text/html'))
+ {
+ $content.= '<p>'.
+ nl2br(
+ preg_replace("/(http:\/\/)([^\s,]*)/i",
+ "<a href='$1$2'>$1$2</a>",
+ htmlspecialchars($args['content']))).
+ '</p>';
+ }
+ else
+ {
+ $content.= $args['content'];
+ }
+
+ // Footer
+ $content.= $conf_mail[$cache_key]['footer'];
+
+ // Close boundary
+ $content.= "\n".'-----='.$conf_mail['boundary_key'].'--'."\n";
+
+ // Undo Compute root_path in order have complete path
+ if ($args['email_format'] == 'text/html')
+ {
+ unset_make_full_url();
+ }
+
+ return
+ trigger_event('send_mail',
+ false, /* Result */
+ trigger_event('send_mail_to', get_strict_email_list($to)),
+ trigger_event('send_mail_subject', $cvt_subject),
+ trigger_event('send_mail_content', $content),
+ trigger_event('send_mail_headers', $headers),
+ $args
+ );
+}
+
+/*
+ * pwg sendmail
+ *
+ * @param:
+ * - result of other sendmail
+ * - to: Receiver or receiver(s) of the mail.
+ * - subject [default value 'Piwigo']
+ * - content: content of mail
+ * - headers: headers of mail
+ *
+ * @return boolean (Ok or not)
+ */
+function pwg_send_mail($result, $to, $subject, $content, $headers)
+{
+ if (!$result)
+ {
+ global $conf_mail;
+
+ if ($conf_mail['use_smtp'])
+ {
+ include_once( PHPWG_ROOT_PATH.'include/class_smtp_mail.inc.php' );
+ $smtp_mail = new smtp_mail(
+ $conf_mail['smtp_host'], $conf_mail['smtp_user'], $conf_mail['smtp_password'],
+ $conf_mail['email_webmaster']);
+ return $smtp_mail->mail($to, $subject, $content, $headers);
+ }
+ else
+ {
+ if ($conf_mail['mail_options'])
+ {
+ $options = '-f '.$conf_mail['email_webmaster'];
+ return mail($to, $subject, $content, $headers, $options);
+ }
+ else
+ {
+ return mail($to, $subject, $content, $headers);
+ }
+ }
+ }
+ else
+ {
+ return $result;
+ }
+}
+
+/*Testing block*/
+/*function pwg_send_mail_test($result, $to, $subject, $content, $headers, $args)
+{
+ global $conf, $user, $lang_info;
+ $dir = $conf['local_data_dir'].'/tmp';
+ @mkdir( $dir );
+ $filename = $dir.'/mail.'.$user['username'].'.'.$lang_info['code'].'.'.$args['template'].'.'.$args['theme'];
+ if ($args['content_format'] == 'text/plain')
+ {
+ $filename .= '.txt';
+ }
+ else
+ {
+ $filename .= '.html';
+ }
+ $file = fopen($filename, 'w+');
+ fwrite($file, $to ."\n");
+ fwrite($file, $subject ."\n");
+ fwrite($file, $headers);
+ fwrite($file, $content);
+ fclose($file);
+ return $result;
+}
+add_event_handler('send_mail', 'pwg_send_mail_test', EVENT_HANDLER_PRIORITY_NEUTRAL+10, 6);*/
+
+
+add_event_handler('send_mail', 'pwg_send_mail', EVENT_HANDLER_PRIORITY_NEUTRAL, 5);
+trigger_action('functions_mail_included');
+
+?>
diff --git a/BSF/include/functions_metadata.inc.php b/BSF/include/functions_metadata.inc.php
new file mode 100644
index 000000000..df89eea9c
--- /dev/null
+++ b/BSF/include/functions_metadata.inc.php
@@ -0,0 +1,142 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+/**
+ * returns informations from IPTC metadata, mapping is done at the beginning
+ * of the function
+ *
+ * @param string $filename
+ * @return array
+ */
+function get_iptc_data($filename, $map)
+{
+ $result = array();
+
+ $imginfo = array();
+ if (false == @getimagesize($filename, $imginfo) )
+ {
+ return $result;
+ }
+
+ if (isset($imginfo['APP13']))
+ {
+ $iptc = iptcparse($imginfo['APP13']);
+ if (is_array($iptc))
+ {
+ $rmap = array_flip($map);
+ foreach (array_keys($rmap) as $iptc_key)
+ {
+ if (isset($iptc[$iptc_key][0]))
+ {
+ if ($iptc_key == '2#025')
+ {
+ $value = implode(',',
+ array_map('clean_iptc_value',$iptc[$iptc_key]));
+ }
+ else
+ {
+ $value = clean_iptc_value($iptc[$iptc_key][0]);
+ }
+
+ foreach (array_keys($map, $iptc_key) as $pwg_key)
+ {
+ $result[$pwg_key] = $value;
+ }
+ }
+ }
+ }
+ }
+ return $result;
+}
+
+/**
+ * return a cleaned IPTC value
+ *
+ * @param string value
+ * @return string
+ */
+function clean_iptc_value($value)
+{
+ // strip leading zeros (weird Kodak Scanner software)
+ while ( isset($value[0]) and $value[0] == chr(0))
+ {
+ $value = substr($value, 1);
+ }
+ // remove binary nulls
+ $value = str_replace(chr(0x00), ' ', $value);
+
+ if ( preg_match('/[\x80-\xff]/', $value) )
+ {
+ // apparently mac uses some MacRoman crap encoding. I don't know
+ // how to detect it so a plugin should do the trick.
+ $value = trigger_event('clean_iptc_value', $value);
+ $is_utf8 = seems_utf8($value);
+ $value = convert_charset( $value,
+ $is_utf8 ? 'utf-8' : 'iso-8859-1',
+ get_pwg_charset() );
+ }
+ return $value;
+}
+
+/**
+ * returns informations from EXIF metadata, mapping is done at the beginning
+ * of the function
+ *
+ * @param string $filename
+ * @return array
+ */
+function get_exif_data($filename, $map)
+{
+ $result = array();
+
+ if (!function_exists('read_exif_data'))
+ {
+ die('Exif extension not available, admin should disable exif use');
+ }
+
+ // Read EXIF data
+ if ($exif = @read_exif_data($filename))
+ {
+ foreach ($map as $key => $field)
+ {
+ if (strpos($field, ';') === false)
+ {
+ if (isset($exif[$field]))
+ {
+ $result[$key] = $exif[$field];
+ }
+ }
+ else
+ {
+ $tokens = explode(';', $field);
+ if (isset($exif[$tokens[0]][$tokens[1]]))
+ {
+ $result[$key] = $exif[$tokens[0]][$tokens[1]];
+ }
+ }
+ }
+ }
+
+ return $result;
+}
+?> \ No newline at end of file
diff --git a/BSF/include/functions_notification.inc.php b/BSF/include/functions_notification.inc.php
new file mode 100644
index 000000000..259a34613
--- /dev/null
+++ b/BSF/include/functions_notification.inc.php
@@ -0,0 +1,608 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+// +-----------------------------------------------------------------------+
+// | functions |
+// +-----------------------------------------------------------------------+
+
+/*
+ * get standard sql where in order to
+ * restict an filter caregories and images
+ *
+ * IMAGE_CATEGORY_TABLE muste named ic in the query
+ *
+ * @param none
+ *
+ * @return string sql where
+ */
+function get_std_sql_where_restrict_filter($prefix_condition, $force_one_condition = false)
+{
+ return get_sql_condition_FandF
+ (
+ array
+ (
+ 'forbidden_categories' => 'ic.category_id',
+ 'visible_categories' => 'ic.category_id',
+ 'visible_images' => 'ic.image_id'
+ ),
+ $prefix_condition,
+ $force_one_condition
+ );
+}
+
+/*
+ * Execute custom notification query
+ *
+ * @param string action ('count' or 'info')
+ * @param string type of query ('new_comments', 'unvalidated_comments', 'new_elements', 'updated_categories', 'new_users', 'waiting_elements')
+ * @param string start (mysql datetime format)
+ * @param string end (mysql datetime format)
+ *
+ * @return integer for action count
+ * array for info
+ */
+function custom_notification_query($action, $type, $start, $end)
+{
+ global $user;
+
+ switch($type)
+ {
+ case 'new_comments':
+ $query = '
+ FROM '.COMMENTS_TABLE.' AS c
+ , '.IMAGE_CATEGORY_TABLE.' AS ic
+ WHERE c.image_id = ic.image_id
+ AND c.validation_date > \''.$start.'\'
+ AND c.validation_date <= \''.$end.'\'
+ '.get_std_sql_where_restrict_filter('AND').'
+;';
+ break;
+ case 'unvalidated_comments':
+ $query = '
+ FROM '.COMMENTS_TABLE.'
+ WHERE date> \''.$start.'\' AND date <= \''.$end.'\'
+ AND validated = \'false\'
+;';
+ break;
+ case 'new_elements':
+ $query = '
+ FROM '.IMAGES_TABLE.' INNER JOIN '.IMAGE_CATEGORY_TABLE.' AS ic ON image_id = id
+ WHERE date_available > \''.$start.'\'
+ AND date_available <= \''.$end.'\'
+ '.get_std_sql_where_restrict_filter('AND').'
+;';
+ break;
+ case 'updated_categories':
+ $query = '
+ FROM '.IMAGES_TABLE.' INNER JOIN '.IMAGE_CATEGORY_TABLE.' AS ic ON image_id = id
+ WHERE date_available > \''.$start.'\'
+ AND date_available <= \''.$end.'\'
+ '.get_std_sql_where_restrict_filter('AND').'
+;';
+ break;
+ case 'new_users':
+ $query = '
+ FROM '.USER_INFOS_TABLE.'
+ WHERE registration_date > \''.$start.'\'
+ AND registration_date <= \''.$end.'\'
+;';
+ break;
+ case 'waiting_elements':
+ $query = '
+ FROM '.WAITING_TABLE.'
+ WHERE validated = \'false\'
+;';
+ break;
+ default:
+ // stop this function and return nothing
+ return;
+ break;
+ }
+
+ switch($action)
+ {
+ case 'count':
+ switch($type)
+ {
+ case 'new_comments':
+ $field_id = 'c.id';
+ break;
+ case 'unvalidated_comments':
+ $field_id = 'id';
+ break;
+ case 'new_elements':
+ $field_id = 'image_id';
+ break;
+ case 'updated_categories':
+ $field_id = 'category_id';
+ break;
+ case 'new_users':
+ $field_id = 'user_id';
+ break;
+ case 'waiting_elements':
+ $field_id = 'id';
+ break;
+ }
+ $query = 'SELECT count(distinct '.$field_id.') as CountId
+'.$query;
+ list($count) = mysql_fetch_array(pwg_query($query));
+ return $count;
+
+ break;
+ case 'info':
+ switch($type)
+ {
+ case 'new_comments':
+ $fields = array('c.id');
+ break;
+ case 'unvalidated_comments':
+ $fields = array('id');
+ break;
+ case 'new_elements':
+ $fields = array('image_id');
+ break;
+ case 'updated_categories':
+ $fields = array('category_id');
+ break;
+ case 'new_users':
+ $fields = array('user_id');
+ break;
+ case 'waiting_elements':
+ $fields = array('id');
+ break;
+ }
+
+ $query = 'SELECT distinct '.implode(', ', $fields).'
+'.$query;
+ $result = pwg_query($query);
+
+ $infos = array();
+
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push($infos, $row);
+ }
+
+ return $infos;
+
+ break;
+ }
+
+ //return is done on previous switch($action)
+}
+
+/**
+ * new comments between two dates, according to authorized categories
+ *
+ * @param string start (mysql datetime format)
+ * @param string end (mysql datetime format)
+ * @param string forbidden categories (comma separated)
+ * @return count comment ids
+ */
+function nb_new_comments($start, $end)
+{
+ return custom_notification_query('count', 'new_comments', $start, $end);
+}
+
+/**
+ * new comments between two dates, according to authorized categories
+ *
+ * @param string start (mysql datetime format)
+ * @param string end (mysql datetime format)
+ * @param string forbidden categories (comma separated)
+ * @return array comment ids
+ */
+function new_comments($start, $end)
+{
+ return custom_notification_query('info', 'new_comments', $start, $end);
+}
+
+/**
+ * unvalidated at a precise date
+ *
+ * Comments that are registered and not validated yet on a precise date
+ *
+ * @param string start (mysql datetime format)
+ * @param string end (mysql datetime format)
+ * @return count comment ids
+ */
+function nb_unvalidated_comments($start, $end)
+{
+ return custom_notification_query('count', 'unvalidated_comments', $start, $end);
+}
+
+
+/**
+ * new elements between two dates, according to authorized categories
+ *
+ * @param string start (mysql datetime format)
+ * @param string end (mysql datetime format)
+ * @param string forbidden categories (comma separated)
+ * @return count element ids
+ */
+function nb_new_elements($start, $end)
+{
+ return custom_notification_query('count', 'new_elements', $start, $end);
+}
+
+/**
+ * new elements between two dates, according to authorized categories
+ *
+ * @param string start (mysql datetime format)
+ * @param string end (mysql datetime format)
+ * @param string forbidden categories (comma separated)
+ * @return array element ids
+ */
+function new_elements($start, $end)
+{
+ return custom_notification_query('info', 'new_elements', $start, $end);
+}
+
+/**
+ * updated categories between two dates, according to authorized categories
+ *
+ * @param string start (mysql datetime format)
+ * @param string end (mysql datetime format)
+ * @param string forbidden categories (comma separated)
+ * @return count element ids
+ */
+function nb_updated_categories($start, $end)
+{
+ return custom_notification_query('count', 'updated_categories', $start, $end);
+}
+
+/**
+ * updated categories between two dates, according to authorized categories
+ *
+ * @param string start (mysql datetime format)
+ * @param string end (mysql datetime format)
+ * @param string forbidden categories (comma separated)
+ * @return array element ids
+ */
+function updated_categories($start, $end)
+{
+ return custom_notification_query('info', 'updated_categories', $start, $end);
+}
+
+/**
+ * new registered users between two dates
+ *
+ * @param string start (mysql datetime format)
+ * @param string end (mysql datetime format)
+ * @return count user ids
+ */
+function nb_new_users($start, $end)
+{
+ return custom_notification_query('count', 'new_users', $start, $end);
+}
+
+/**
+ * new registered users between two dates
+ *
+ * @param string start (mysql datetime format)
+ * @param string end (mysql datetime format)
+ * @return array user ids
+ */
+function new_users($start, $end)
+{
+ return custom_notification_query('info', 'new_users', $start, $end);
+}
+
+/**
+ * currently waiting pictures
+ *
+ * @return count waiting ids
+ */
+function nb_waiting_elements()
+{
+ return custom_notification_query('count', 'waiting_elements', '', '');
+}
+
+/**
+ * currently waiting pictures
+ *
+ * @return array waiting ids
+ */
+function waiting_elements()
+{
+ return custom_notification_query('info', 'waiting_elements', $start, $end);
+}
+
+/**
+ * There are new between two dates ?
+ *
+ * Informations : number of new comments, number of new elements, number of
+ * updated categories. Administrators are also informed about : number of
+ * unvalidated comments, number of new users (TODO : number of unvalidated
+ * elements)
+ *
+ * @param string start date (mysql datetime format)
+ * @param string end date (mysql datetime format)
+ *
+ * @return boolean : true if exist news else false
+ */
+function news_exists($start, $end)
+{
+ return (
+ (nb_new_comments($start, $end) > 0) or
+ (nb_new_elements($start, $end) > 0) or
+ (nb_updated_categories($start, $end) > 0) or
+ ((is_admin()) and (nb_unvalidated_comments($start, $end) > 0)) or
+ ((is_admin()) and (nb_new_users($start, $end) > 0)) or
+ ((is_admin()) and (nb_waiting_elements() > 0))
+ );
+}
+
+/**
+ * Formats a news line and adds it to the array (e.g. '5 new elements')
+ */
+function add_news_line(&$news, $count, $singular_fmt_key, $plural_fmt_key, $url='', $add_url=false)
+{
+ if ($count > 0)
+ {
+ $line = l10n_dec($singular_fmt_key, $plural_fmt_key, $count);
+ if ($add_url and !empty($url) )
+ {
+ $line = '<a href="'.$url.'">'.$line.'</a>';
+ }
+ array_push($news, $line);
+ }
+}
+
+/**
+ * What's new between two dates ?
+ *
+ * Informations : number of new comments, number of new elements, number of
+ * updated categories. Administrators are also informed about : number of
+ * unvalidated comments, number of new users (TODO : number of unvalidated
+ * elements)
+ *
+ * @param string start date (mysql datetime format)
+ * @param string end date (mysql datetime format)
+ * @param bool exclude_img_cats if true, no info about new images/categories
+ * @param bool add_url add html A link around news
+ *
+ * @return array of news
+ */
+function news($start, $end, $exclude_img_cats=false, $add_url=false)
+{
+ $news = array();
+
+ if (!$exclude_img_cats)
+ {
+ add_news_line( $news,
+ nb_new_elements($start, $end), '%d new element', '%d new elements',
+ make_index_url(array('section'=>'recent_pics')), $add_url );
+ }
+
+ if (!$exclude_img_cats)
+ {
+ add_news_line( $news,
+ nb_updated_categories($start, $end), '%d category updated', '%d categories updated',
+ make_index_url(array('section'=>'recent_cats')), $add_url );
+ }
+
+ add_news_line( $news,
+ nb_new_comments($start, $end), '%d new comment', '%d new comments',
+ get_root_url().'comments.php', $add_url );
+
+ if (is_admin())
+ {
+ add_news_line( $news,
+ nb_unvalidated_comments($start, $end), '%d comment to validate', '%d comments to validate',
+ get_root_url().'admin.php?page=comments', $add_url );
+
+ add_news_line( $news,
+ nb_new_users($start, $end), '%d new user', '%d new users',
+ get_root_url().'admin.php?page=user_list', $add_url );
+
+ add_news_line( $news,
+ nb_waiting_elements(), '%d waiting element', '%d waiting elements',
+ get_root_url().'admin.php?page=upload', $add_url );
+ }
+
+ return $news;
+}
+
+/**
+ * returns information about recently published elements grouped by post date
+ * @param int max_dates maximum returned number of recent dates
+ * @param int max_elements maximum returned number of elements per date
+ * @param int max_cats maximum returned number of categories per date
+ */
+function get_recent_post_dates($max_dates, $max_elements, $max_cats)
+{
+ global $conf, $user;
+
+ $where_sql = get_std_sql_where_restrict_filter('WHERE', true);
+
+ $query = '
+SELECT date_available,
+ COUNT(DISTINCT id) nb_elements,
+ COUNT(DISTINCT category_id) nb_cats
+ FROM '.IMAGES_TABLE.' INNER JOIN '.IMAGE_CATEGORY_TABLE.' AS ic ON id=image_id
+ '.$where_sql.'
+ GROUP BY date_available
+ ORDER BY date_available DESC
+ LIMIT 0,'.$max_dates.'
+;';
+ $result = pwg_query($query);
+ $dates = array();
+ while ($row = mysql_fetch_assoc($result))
+ {
+ array_push($dates, $row);
+ }
+
+ for ($i=0; $i<count($dates); $i++)
+ {
+ if ($max_elements>0)
+ { // get some thumbnails ...
+ $query = '
+SELECT DISTINCT id, path, name, tn_ext, file
+ FROM '.IMAGES_TABLE.' INNER JOIN '.IMAGE_CATEGORY_TABLE.' AS ic ON id=image_id
+ '.$where_sql.'
+ AND date_available="'.$dates[$i]['date_available'].'"
+ AND tn_ext IS NOT NULL
+ ORDER BY RAND(NOW())
+ LIMIT 0,'.$max_elements.'
+;';
+ $dates[$i]['elements'] = array();
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_assoc($result))
+ {
+ array_push($dates[$i]['elements'], $row);
+ }
+ }
+
+ if ($max_cats>0)
+ {// get some categories ...
+ $query = '
+SELECT DISTINCT c.uppercats, COUNT(DISTINCT i.id) img_count
+ FROM '.IMAGES_TABLE.' i INNER JOIN '.IMAGE_CATEGORY_TABLE.' AS ic ON i.id=image_id
+ INNER JOIN '.CATEGORIES_TABLE.' c ON c.id=category_id
+ '.$where_sql.'
+ AND date_available="'.$dates[$i]['date_available'].'"
+ GROUP BY category_id
+ ORDER BY img_count DESC
+ LIMIT 0,'.$max_cats.'
+;';
+ $dates[$i]['categories'] = array();
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_assoc($result))
+ {
+ array_push($dates[$i]['categories'], $row);
+ }
+ }
+ }
+ return $dates;
+}
+
+/*
+ Call function get_recent_post_dates but
+ the parameters to be passed to the function, as an indexed array.
+
+*/
+function get_recent_post_dates_array($args)
+{
+ return
+ get_recent_post_dates
+ (
+ (empty($args['max_dates']) ? 3 : $args['max_dates']),
+ (empty($args['max_elements']) ? 3 : $args['max_elements']),
+ (empty($args['max_cats']) ? 3 : $args['max_cats'])
+ );
+}
+
+
+/**
+ * returns html description about recently published elements grouped by post date
+ * @param $date_detail: selected date computed by get_recent_post_dates function
+ */
+function get_html_description_recent_post_date($date_detail)
+{
+ global $conf;
+
+ $description = '';
+
+ $description .=
+ '<li>'
+ .l10n_dec('%d new element', '%d new elements', $date_detail['nb_elements'])
+ .' ('
+ .'<a href="'.make_index_url(array('section'=>'recent_pics')).'">'
+ .l10n('recent_pics_cat').'</a>'
+ .')'
+ .'</li><br/>';
+
+ foreach($date_detail['elements'] as $element)
+ {
+ $tn_src = get_thumbnail_url($element);
+ $description .= '<a href="'.
+ make_picture_url(array(
+ 'image_id' => $element['id'],
+ 'image_file' => $element['file'],
+ ))
+ .'"><img src="'.$tn_src.'"/></a>';
+ }
+ $description .= '...<br/>';
+
+ $description .=
+ '<li>'
+ .l10n_dec('%d category updated', '%d categories updated',
+ $date_detail['nb_cats'])
+ .'</li>';
+
+ $description .= '<ul>';
+ foreach($date_detail['categories'] as $cat)
+ {
+ $description .=
+ '<li>'
+ .get_cat_display_name_cache($cat['uppercats'])
+ .' ('.
+ l10n_dec('%d new element',
+ '%d new elements', $cat['img_count']).')'
+ .'</li>';
+ }
+ $description .= '</ul>';
+
+ return $description;
+}
+
+/**
+ * explodes a MySQL datetime format (2005-07-14 23:01:37) in fields "year",
+ * "month", "day", "hour", "minute", "second".
+ *
+ * @param string mysql datetime format
+ * @return array
+ */
+function explode_mysqldt($mysqldt)
+{
+ $date = array();
+ list($date['year'],
+ $date['month'],
+ $date['day'],
+ $date['hour'],
+ $date['minute'],
+ $date['second'])
+ = preg_split('/[-: ]/', $mysqldt);
+
+ return $date;
+}
+
+/**
+ * returns title about recently published elements grouped by post date
+ * @param $date_detail: selected date computed by get_recent_post_dates function
+ */
+function get_title_recent_post_date($date_detail)
+{
+ global $lang;
+
+ $date = $date_detail['date_available'];
+ $exploded_date = explode_mysqldt($date);
+
+ $title = l10n_dec('%d new element', '%d new elements', $date_detail['nb_elements']);
+ $title .= ' ('.$lang['month'][(int)$exploded_date['month']].' '.$exploded_date['day'].')';
+
+ return $title;
+}
+
+?> \ No newline at end of file
diff --git a/BSF/include/functions_picture.inc.php b/BSF/include/functions_picture.inc.php
new file mode 100644
index 000000000..1b6403ff3
--- /dev/null
+++ b/BSF/include/functions_picture.inc.php
@@ -0,0 +1,330 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+/**
+ * @param element_info array containing element information from db;
+ * at least 'id', 'path' should be present
+ */
+function get_element_path($element_info)
+{
+ $path = get_element_location($element_info);
+ if ( !url_is_remote($path) )
+ {
+ $path = PHPWG_ROOT_PATH.$path;
+ }
+ return $path;
+}
+
+/*
+ * @param element_info array containing element information from db;
+ * at least 'id', 'path' should be present
+ */
+function get_element_url($element_info)
+{
+ $url = get_element_location($element_info);
+ if ( !url_is_remote($url) )
+ {
+ $url = embellish_url(get_root_url().$url);
+ }
+ // plugins want another url ?
+ return trigger_event('get_element_url', $url, $element_info);
+}
+
+/**
+ * Returns the relative path of the element with regards to to the root
+ * of PWG (not the current page). This function is not intended to be
+ * called directly from code.
+ * @param element_info array containing element information from db;
+ * at least 'id', 'path' should be present
+ */
+function get_element_location($element_info)
+{
+ // maybe a cached watermark ?
+ return trigger_event('get_element_location',
+ $element_info['path'], $element_info);
+}
+
+
+/**
+ * Returns the PATH to the image to be displayed in the picture page. If the
+ * element is not a picture, then the representative image or the default
+ * mime image. The path can be used in the php script, but not sent to the
+ * browser.
+ * @param element_info array containing element information from db;
+ * at least 'id', 'path', 'representative_ext' should be present
+ */
+function get_image_path($element_info)
+{
+ global $conf;
+ $ext = get_extension($element_info['path']);
+ if (in_array($ext, $conf['picture_ext']))
+ {
+ if (isset($element_info['element_path']) )
+ {
+ return $element_info['element_path'];
+ }
+ return get_element_path($element_info);
+ }
+
+ $path = get_image_location($element_info);
+ if ( !url_is_remote($path) )
+ {
+ $path = PHPWG_ROOT_PATH.$path;
+ }
+ return $path;
+}
+
+/**
+ * Returns the URL of the image to be displayed in the picture page. If the
+ * element is not a picture, then the representative image or the default
+ * mime image. The URL can't be used in the php script, but can be sent to the
+ * browser.
+ * @param element_info array containing element information from db;
+ * at least 'id', 'path', 'representative_ext' should be present
+ */
+function get_image_url($element_info)
+{
+ global $conf;
+ $ext = get_extension($element_info['path']);
+ if (in_array($ext, $conf['picture_ext']))
+ {
+ if (isset($element_info['element_url']) )
+ {
+ return $element_info['element_url'];
+ }
+ return get_element_url($element_info);
+ }
+
+ $url = get_image_location($element_info);
+ if ( !url_is_remote($url) )
+ {
+ $url = embellish_url(get_root_url().$url);
+ }
+ return $url;
+}
+
+/**
+ * Returns the relative path of the image (element/representative/mimetype)
+ * with regards to the root of PWG (not the current page). This function
+ * is not intended to be called directly from code.
+ * @param element_info array containing element information from db;
+ * at least 'id', 'path', 'representative_ext' should be present
+ */
+function get_image_location($element_info)
+{
+ if (isset($element_info['representative_ext'])
+ and $element_info['representative_ext'] != '')
+ {
+ $pi = pathinfo($element_info['path']);
+ $file_wo_ext = get_filename_wo_extension($pi['basename']);
+ $path =
+ $pi['dirname'].'/pwg_representative/'
+ .$file_wo_ext.'.'.$element_info['representative_ext'];
+ }
+ else
+ {
+ $ext = get_extension($element_info['path']);
+ $path = get_themeconf('mime_icon_dir');
+ $path.= strtolower($ext).'.png';
+ if ( !file_exists(PHPWG_ROOT_PATH.$path)
+ and !empty($element_info['tn_ext']) )
+ {
+ $path = get_thumbnail_location($element_info);
+ }
+ }
+
+ // plugins want another location ?
+ return trigger_event( 'get_image_location', $path, $element_info);
+}
+
+
+/*
+ * @param element_info array containing element information from db;
+ * at least 'id', 'path', 'has_high' should be present
+ */
+function get_high_path($element_info)
+{
+ $path = get_high_location($element_info);
+ if (!empty($path) and !url_is_remote($path) )
+ {
+ $path = PHPWG_ROOT_PATH.$path;
+ }
+ return $path;
+}
+
+/**
+ * @param element_info array containing element information from db;
+ * at least 'id', 'path', 'has_high' should be present
+ */
+function get_high_url($element_info)
+{
+ $url = get_high_location($element_info);
+ if (!empty($url) and !url_is_remote($url) )
+ {
+ $url = embellish_url(get_root_url().$url);
+ }
+ // plugins want another url ?
+ return trigger_event('get_high_url', $url, $element_info);
+}
+
+/**
+ * @param element_info array containing element information from db;
+ * at least 'id', 'path', 'has_high' should be present
+ */
+function get_high_location($element_info)
+{
+ $location = '';
+ if ($element_info['has_high'] == 'true')
+ {
+ $pi = pathinfo($element_info['path']);
+ $location=$pi['dirname'].'/pwg_high/'.$pi['basename'];
+ }
+ return trigger_event( 'get_high_location', $location, $element_info);
+}
+
+
+/**
+ * @param what_part string one of 't' (thumbnail), 'e' (element), 'i' (image),
+ * 'h' (high resolution image)
+ * @param element_info array containing element information from db;
+ * at least 'id', 'path' should be present
+ */
+function get_download_url($what_part, $element_info)
+{
+ $url = get_root_url().'action.php';
+ $url = add_url_params($url,
+ array(
+ 'id' => $element_info['id'],
+ 'part' => $what_part,
+ )
+ );
+ return trigger_event( 'get_download_url', $url, $element_info);
+}
+
+/*
+ * get slideshow default params into array
+ *
+ * @param void
+ *
+ * @return slideshow default values into array
+ */
+function get_default_slideshow_params()
+{
+ global $conf;
+
+ return array(
+ 'period' => $conf['slideshow_period'],
+ 'repeat' => $conf['slideshow_repeat'],
+ 'play' => true,
+ );
+}
+
+/*
+ * check and correct slideshow params from array
+ *
+ * @param array of params
+ *
+ * @return slideshow corrected values into array
+ */
+function correct_slideshow_params($params = array())
+{
+ global $conf;
+
+ if ($params['period'] < $conf['slideshow_period_min'])
+ {
+ $params['period'] = $conf['slideshow_period_min'];
+ }
+ else if ($params['period'] > $conf['slideshow_period_max'])
+ {
+ $params['period'] = $conf['slideshow_period_max'];
+ }
+
+ return $params;
+}
+
+/*
+ * Decode slideshow string params into array
+ *
+ * @param string params like ""
+ *
+ * @return slideshow values into array
+ */
+function decode_slideshow_params($encode_params = null)
+{
+ global $conf;
+
+ $result = get_default_slideshow_params();
+
+ if (is_numeric($encode_params))
+ {
+ $result['period'] = $encode_params;
+ }
+ else
+ {
+ $matches = array();
+ if (preg_match_all('/([a-z]+)-(\d+)/', $encode_params, $matches))
+ {
+ $matchcount = count($matches[1]);
+ for ($i = 0; $i < $matchcount; $i++)
+ {
+ $result[$matches[1][$i]] = $matches[2][$i];
+ }
+ }
+
+ if (preg_match_all('/([a-z]+)-(true|false)/', $encode_params, $matches))
+ {
+ $matchcount = count($matches[1]);
+ for ($i = 0; $i < $matchcount; $i++)
+ {
+ $result[$matches[1][$i]] = get_boolean($matches[2][$i]);
+ }
+ }
+ }
+
+ return correct_slideshow_params($result);
+}
+
+/*
+ * Encode slideshow array params into array
+ *
+ * @param array params
+ *
+ * @return slideshow values into string
+ */
+function encode_slideshow_params($decode_params = array())
+{
+ global $conf;
+
+ $params = array_diff_assoc(correct_slideshow_params($decode_params), get_default_slideshow_params());
+ $result = '';
+
+ foreach ($params as $name => $value)
+ {
+ // boolean_to_string return $value, if it's not a bool
+ $result .= '+'.$name.'-'.boolean_to_string($value);
+ }
+
+ return $result;
+}
+
+?>
diff --git a/BSF/include/functions_plugins.inc.php b/BSF/include/functions_plugins.inc.php
new file mode 100644
index 000000000..e2beb18ac
--- /dev/null
+++ b/BSF/include/functions_plugins.inc.php
@@ -0,0 +1,284 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+/*
+Events and event handlers are the core of Piwigo plugin management.
+Plugins are addons that are found in plugins subdirectory. If activated, PWG
+will include the index.php of each plugin.
+Events are triggered by PWG core code. Plugins (or even PWG itself) can
+register their functions to handle these events. An event is identified by a
+string.
+*/
+
+define('PHPWG_PLUGINS_PATH', PHPWG_ROOT_PATH.'plugins/');
+
+define('EVENT_HANDLER_PRIORITY_NEUTRAL', 50);
+
+/* Register a event handler.
+ * @param string $event the name of the event to listen to
+ * @param mixed $func the function that will handle the event
+ * @param int $priority optional priority (greater priority will
+ * be executed at last)
+*/
+function add_event_handler($event, $func,
+ $priority=EVENT_HANDLER_PRIORITY_NEUTRAL, $accepted_args=1)
+{
+ global $pwg_event_handlers;
+
+ if ( isset($pwg_event_handlers[$event][$priority]) )
+ {
+ foreach($pwg_event_handlers[$event][$priority] as $handler)
+ {
+ if ( $handler['function'] == $func )
+ {
+ return false;
+ }
+ }
+ }
+
+ $pwg_event_handlers[$event][$priority][] =
+ array(
+ 'function'=>$func,
+ 'accepted_args'=>$accepted_args);
+ ksort( $pwg_event_handlers[$event] );
+ return true;
+}
+
+/* Register a event handler.
+ * @param string $event the name of the event to listen to
+ * @param mixed $func the function that needs removal
+ * @param int $priority optional priority (greater priority will
+ * be executed at last)
+*/
+function remove_event_handler($event, $func,
+ $priority=EVENT_HANDLER_PRIORITY_NEUTRAL)
+{
+ global $pwg_event_handlers;
+
+ if (!isset( $pwg_event_handlers[$event][$priority] ) )
+ {
+ return false;
+ }
+ for ($i=0; $i<count($pwg_event_handlers[$event][$priority]); $i++)
+ {
+ if ($pwg_event_handlers[$event][$priority][$i]['function']==$func)
+ {
+ unset($pwg_event_handlers[$event][$priority][$i]);
+ $pwg_event_handlers[$event][$priority] =
+ array_values($pwg_event_handlers[$event][$priority]);
+
+ if ( empty($pwg_event_handlers[$event][$priority]) )
+ {
+ unset( $pwg_event_handlers[$event][$priority] );
+ if (empty( $pwg_event_handlers[$event] ) )
+ {
+ unset( $pwg_event_handlers[$event] );
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+/* Triggers an event and calls all registered event handlers
+ * @param string $event name of the event
+ * @param mixed $data data to pass to handlers
+*/
+function trigger_event($event, $data=null)
+{
+ global $pwg_event_handlers;
+
+ // just for debugging
+ trigger_action('pre_trigger_event',
+ array('event'=>$event, 'data'=>$data) );
+
+ if ( !isset($pwg_event_handlers[$event]) )
+ {
+ trigger_action('post_trigger_event',
+ array('event'=>$event, 'data'=>$data) );
+ return $data;
+ }
+ $args = array_slice(func_get_args(), 2);
+
+ foreach ($pwg_event_handlers[$event] as $priority => $handlers)
+ {
+ if ( !is_null($handlers) )
+ {
+ foreach($handlers as $handler)
+ {
+ $all_args = array_merge( array($data), $args);
+ $function_name = $handler['function'];
+ $accepted_args = $handler['accepted_args'];
+
+ if ( $accepted_args == 1 )
+ $the_args = array($data);
+ elseif ( $accepted_args > 1 )
+ $the_args = array_slice($all_args, 0, $accepted_args);
+ elseif ( $accepted_args == 0 )
+ $the_args = NULL;
+ else
+ $the_args = $all_args;
+
+ $data = call_user_func_array($function_name, $the_args);
+ }
+ }
+ }
+ trigger_action('post_trigger_event',
+ array('event'=>$event, 'data'=>$data) );
+ return $data;
+}
+
+function trigger_action($event, $data=null)
+{
+ global $pwg_event_handlers;
+ if ($event!='pre_trigger_event'
+ and $event!='post_trigger_event'
+ and $event!='trigger_action')
+ {// special case for debugging - avoid recursive calls
+ trigger_action('trigger_action',
+ array('event'=>$event, 'data'=>$data) );
+ }
+
+ if ( !isset($pwg_event_handlers[$event]) )
+ {
+ return;
+ }
+ $args = array_slice(func_get_args(), 2);
+
+ foreach ($pwg_event_handlers[$event] as $priority => $handlers)
+ {
+ if ( !is_null($handlers) )
+ {
+ foreach($handlers as $handler)
+ {
+ $all_args = array_merge( array($data), $args);
+ $function_name = $handler['function'];
+ $accepted_args = $handler['accepted_args'];
+
+ if ( $accepted_args == 1 )
+ $the_args = array($data);
+ elseif ( $accepted_args > 1 )
+ $the_args = array_slice($all_args, 0, $accepted_args);
+ elseif ( $accepted_args == 0 )
+ $the_args = NULL;
+ else
+ $the_args = $all_args;
+
+ call_user_func_array($function_name, $the_args);
+ }
+ }
+ }
+}
+
+/** Saves some data with the associated plugim id. It can be retrieved later (
+ * during this script lifetime) using get_plugin_data
+ * @param string plugin_id
+ * @param mixed data
+ * returns true on success, false otherwise
+ */
+function set_plugin_data($plugin_id, &$data)
+{
+ global $pwg_loaded_plugins;
+ if ( isset($pwg_loaded_plugins[$plugin_id]) )
+ {
+ $pwg_loaded_plugins[$plugin_id]['plugin_data'] = &$data;
+ return true;
+ }
+ return false;
+}
+
+/** Retrieves plugin data saved previously with set_plugin_data
+ * @param string plugin_id
+ */
+function &get_plugin_data($plugin_id)
+{
+ global $pwg_loaded_plugins;
+ if ( isset($pwg_loaded_plugins[$plugin_id]) )
+ {
+ return $pwg_loaded_plugins[$plugin_id]['plugin_data'];
+ }
+ return null;
+}
+
+/* Returns an array of plugins defined in the database
+ * @param string $state optional filter on this state
+ * @param string $id optional returns only data about given plugin
+*/
+function get_db_plugins($state='', $id='')
+{
+ $query = '
+SELECT * FROM '.PLUGINS_TABLE;
+ if (!empty($state) or !empty($id) )
+ {
+ $query .= '
+WHERE 1=1';
+ if (!empty($state))
+ {
+ $query .= '
+ AND state="'.$state.'"';
+ }
+ if (!empty($id))
+ {
+ $query .= '
+ AND id="'.$id.'"';
+ }
+ }
+
+ $result = pwg_query($query);
+ $plugins = array();
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push($plugins, $row);
+ }
+ return $plugins;
+}
+
+
+function load_plugin($plugin)
+{
+ $file_name = PHPWG_PLUGINS_PATH.$plugin['id'].'/main.inc.php';
+ if ( file_exists($file_name) )
+ {
+ global $pwg_loaded_plugins;
+ $pwg_loaded_plugins[ $plugin['id'] ] = $plugin;
+ include_once( $file_name );
+ }
+}
+
+/*loads all the plugins on startup*/
+function load_plugins()
+{
+ global $conf, $pwg_loaded_plugins;
+ $pwg_loaded_plugins = array();
+ if ($conf['enable_plugins'])
+ {
+ $plugins = get_db_plugins('active');
+ foreach( $plugins as $plugin)
+ {// include main from a function to avoid using same function context
+ load_plugin($plugin);
+ }
+ trigger_action('plugins_loaded');
+ }
+}
+?> \ No newline at end of file
diff --git a/BSF/include/functions_rate.inc.php b/BSF/include/functions_rate.inc.php
new file mode 100644
index 000000000..ad3de1b2f
--- /dev/null
+++ b/BSF/include/functions_rate.inc.php
@@ -0,0 +1,134 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+/**
+ * rate a picture by a user
+ *
+ * @param int image identifier
+ * @param int rate
+ * @return void
+ */
+function rate_picture($image_id, $rate)
+{
+ global $conf, $user;
+
+ if (!isset($rate)
+ or !$conf['rate']
+ or !in_array($rate, $conf['rate_items']))
+ {
+ return;
+ }
+
+ $user_anonymous = is_autorize_status(ACCESS_CLASSIC) ? false : true;
+
+ if ($user_anonymous and !$conf['rate_anonymous'])
+ {
+ return;
+ }
+
+ $ip_components = explode('.', $_SERVER["REMOTE_ADDR"]);
+ if (count($ip_components) > 3)
+ {
+ array_pop($ip_components);
+ }
+ $anonymous_id = implode ('.', $ip_components);
+
+ if ($user_anonymous)
+ {
+ $save_anonymous_id = pwg_get_cookie_var('anonymous_rater', $anonymous_id);
+
+ if ($anonymous_id != $save_anonymous_id)
+ { // client has changed his IP adress or he's trying to fool us
+ $query = '
+SELECT element_id
+ FROM '.RATE_TABLE.'
+ WHERE user_id = '.$user['id'].'
+ AND anonymous_id = \''.$anonymous_id.'\'
+;';
+ $already_there = array_from_query($query, 'element_id');
+
+ if (count($already_there) > 0)
+ {
+ $query = '
+DELETE
+ FROM '.RATE_TABLE.'
+ WHERE user_id = '.$user['id'].'
+ AND anonymous_id = \''.$save_anonymous_id.'\'
+ AND element_id IN ('.implode(',', $already_there).')
+;';
+ pwg_query($query);
+ }
+
+ $query = '
+UPDATE '.RATE_TABLE.'
+ SET anonymous_id = \'' .$anonymous_id.'\'
+ WHERE user_id = '.$user['id'].'
+ AND anonymous_id = \'' . $save_anonymous_id.'\'
+;';
+ pwg_query($query);
+ } // end client changed ip
+
+ pwg_set_cookie_var('anonymous_rater', $anonymous_id);
+ } // end anonymous user
+
+ $query = '
+DELETE
+ FROM '.RATE_TABLE.'
+ WHERE element_id = '.$image_id.'
+ AND user_id = '.$user['id'].'
+';
+ if (isset($user_anonymous))
+ {
+ $query.= ' AND anonymous_id = \''.$anonymous_id.'\'';
+ }
+ pwg_query($query);
+ $query = '
+INSERT
+ INTO '.RATE_TABLE.'
+ (user_id,anonymous_id,element_id,rate,date)
+ VALUES
+ ('
+ .$user['id'].','
+ .'\''.$anonymous_id.'\','
+ .$image_id.','
+ .$rate
+ .',NOW())
+;';
+ pwg_query($query);
+
+ // update of images.average_rate field
+ $query = '
+SELECT ROUND(AVG(rate),2) AS average_rate
+ FROM '.RATE_TABLE.'
+ WHERE element_id = '.$image_id.'
+;';
+ $row = mysql_fetch_array(pwg_query($query));
+ $query = '
+UPDATE '.IMAGES_TABLE.'
+ SET average_rate = '.$row['average_rate'].'
+ WHERE id = '.$image_id.'
+;';
+ pwg_query($query);
+}
+
+?> \ No newline at end of file
diff --git a/BSF/include/functions_search.inc.php b/BSF/include/functions_search.inc.php
new file mode 100644
index 000000000..7069bd5c9
--- /dev/null
+++ b/BSF/include/functions_search.inc.php
@@ -0,0 +1,560 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+
+/**
+ * returns search rules stored into a serialized array in "search"
+ * table. Each search rules set is numericaly identified.
+ *
+ * @param int search_id
+ * @return array
+ */
+function get_search_array($search_id)
+{
+ if (!is_numeric($search_id))
+ {
+ die('Search id must be an integer');
+ }
+
+ $query = '
+SELECT rules
+ FROM '.SEARCH_TABLE.'
+ WHERE id = '.$search_id.'
+;';
+ list($serialized_rules) = mysql_fetch_row(pwg_query($query));
+
+ return unserialize($serialized_rules);
+}
+
+/**
+ * returns the SQL clause from a search identifier
+ *
+ * Search rules are stored in search table as a serialized array. This array
+ * need to be transformed into an SQL clause to be used in queries.
+ *
+ * @param array search
+ * @return string
+ */
+function get_sql_search_clause($search)
+{
+ // SQL where clauses are stored in $clauses array during query
+ // construction
+ $clauses = array();
+
+ foreach (array('file','name','comment','author') as $textfield)
+ {
+ if (isset($search['fields'][$textfield]))
+ {
+ $local_clauses = array();
+ foreach ($search['fields'][$textfield]['words'] as $word)
+ {
+ array_push($local_clauses, $textfield." LIKE '%".$word."%'");
+ }
+
+ // adds brackets around where clauses
+ $local_clauses = prepend_append_array_items($local_clauses, '(', ')');
+
+ array_push(
+ $clauses,
+ implode(
+ ' '.$search['fields'][$textfield]['mode'].' ',
+ $local_clauses
+ )
+ );
+ }
+ }
+
+ if (isset($search['fields']['allwords']))
+ {
+ $fields = array('file', 'name', 'comment', 'author');
+ // in the OR mode, request bust be :
+ // ((field1 LIKE '%word1%' OR field2 LIKE '%word1%')
+ // OR (field1 LIKE '%word2%' OR field2 LIKE '%word2%'))
+ //
+ // in the AND mode :
+ // ((field1 LIKE '%word1%' OR field2 LIKE '%word1%')
+ // AND (field1 LIKE '%word2%' OR field2 LIKE '%word2%'))
+ $word_clauses = array();
+ foreach ($search['fields']['allwords']['words'] as $word)
+ {
+ $field_clauses = array();
+ foreach ($fields as $field)
+ {
+ array_push($field_clauses, $field." LIKE '%".$word."%'");
+ }
+ // adds brackets around where clauses
+ array_push(
+ $word_clauses,
+ implode(
+ "\n OR ",
+ $field_clauses
+ )
+ );
+ }
+
+ array_walk(
+ $word_clauses,
+ create_function('&$s','$s="(".$s.")";')
+ );
+
+ array_push(
+ $clauses,
+ "\n ".
+ implode(
+ "\n ".
+ $search['fields']['allwords']['mode'].
+ "\n ",
+ $word_clauses
+ )
+ );
+ }
+
+ foreach (array('date_available', 'date_creation') as $datefield)
+ {
+ if (isset($search['fields'][$datefield]))
+ {
+ array_push(
+ $clauses,
+ $datefield." = '".$search['fields'][$datefield]['date']."'"
+ );
+ }
+
+ foreach (array('after','before') as $suffix)
+ {
+ $key = $datefield.'-'.$suffix;
+
+ if (isset($search['fields'][$key]))
+ {
+ array_push(
+ $clauses,
+
+ $datefield.
+ ($suffix == 'after' ? ' >' : ' <').
+ ($search['fields'][$key]['inc'] ? '=' : '').
+ " '".$search['fields'][$key]['date']."'"
+
+ );
+ }
+ }
+ }
+
+ if (isset($search['fields']['cat']))
+ {
+ if ($search['fields']['cat']['sub_inc'])
+ {
+ // searching all the categories id of sub-categories
+ $cat_ids = get_subcat_ids($search['fields']['cat']['words']);
+ }
+ else
+ {
+ $cat_ids = $search['fields']['cat']['words'];
+ }
+
+ $local_clause = 'category_id IN ('.implode(',', $cat_ids).')';
+ array_push($clauses, $local_clause);
+ }
+
+ // adds brackets around where clauses
+ $clauses = prepend_append_array_items($clauses, '(', ')');
+
+ $where_separator =
+ implode(
+ "\n ".$search['mode'].' ',
+ $clauses
+ );
+
+ $search_clause = $where_separator;
+
+ return $search_clause;
+}
+
+/**
+ * returns the list of items corresponding to the advanced search array
+ *
+ * @param array search
+ * @return array
+ */
+function get_regular_search_results($search)
+{
+ $items = array();
+
+ $search_clause = get_sql_search_clause($search);
+
+ if (!empty($search_clause))
+ {
+ $query = '
+SELECT DISTINCT(id)
+ FROM '.IMAGES_TABLE.'
+ INNER JOIN '.IMAGE_CATEGORY_TABLE.' AS ic ON id = ic.image_id
+ WHERE '.$search_clause.'
+;';
+ $items = array_from_query($query, 'id');
+ }
+
+ if (isset($search['fields']['tags']))
+ {
+ $tag_items = get_image_ids_for_tags(
+ $search['fields']['tags']['words'],
+ $search['fields']['tags']['mode']
+ );
+
+ switch ($search['mode'])
+ {
+ case 'AND':
+ {
+ if (empty($search_clause))
+ {
+ $items = $tag_items;
+ }
+ else
+ {
+ $items = array_intersect($items, $tag_items);
+ }
+ break;
+ }
+ case 'OR':
+ {
+ $items = array_unique(
+ array_merge(
+ $items,
+ $tag_items
+ )
+ );
+ break;
+ }
+ }
+ }
+
+ return $items;
+}
+
+/**
+ * returns the LIKE sql clause corresponding to the quick search query $q
+ * and the field $field. example q='john bill', field='file' will return
+ * file LIKE '%john%' OR file LIKE '%bill%'. Special characters for MySql full
+ * text search (+,<,>,~) are omitted. The query can contain a phrase:
+ * 'Pierre "New York"' will return LIKE '%Pierre%' OR LIKE '%New York%'.
+ * @param string q
+ * @param string field
+ * @return string
+ */
+function get_qsearch_like_clause($q, $field)
+{
+ $q = stripslashes($q);
+ $tokens = array();
+ $token_modifiers = array();
+ $crt_token = "";
+ $crt_token_modifier = "";
+ $state = 0;
+
+ for ($i=0; $i<strlen($q); $i++)
+ {
+ $ch = $q[$i];
+ switch ($state)
+ {
+ case 0:
+ if ($ch=='"')
+ {
+ if (strlen($crt_token))
+ {
+ $tokens[] = $crt_token;
+ $token_modifiers[] = $crt_token_modifier;
+ $crt_token = "";
+ $crt_token_modifier = "";
+ }
+ $state=1;
+ }
+ elseif ( $ch=='*' )
+ { // wild card
+ $crt_token .= '%';
+ }
+ elseif ( strcspn($ch, '+-><~')==0 )
+ { //special full text modifier
+ if (strlen($crt_token))
+ {
+ $tokens[] = $crt_token;
+ $token_modifiers[] = $crt_token_modifier;
+ $crt_token = "";
+ $crt_token_modifier = "";
+ }
+ $crt_token_modifier .= $ch;
+ }
+ elseif (preg_match('/[\s,.;!\?]+/', $ch))
+ { // white space
+ if (strlen($crt_token))
+ {
+ $tokens[] = $crt_token;
+ $token_modifiers[] = $crt_token_modifier;
+ $crt_token = "";
+ $crt_token_modifier = "";
+ }
+ }
+ else
+ {
+ $crt_token .= $ch;
+ }
+ break;
+ case 1: // qualified with quotes
+ switch ($ch)
+ {
+ case '"':
+ $tokens[] = $crt_token;
+ $token_modifiers[] = $crt_token_modifier;
+ $crt_token = "";
+ $crt_token_modifier = "";
+ $state=0;
+ break;
+ default:
+ $crt_token .= $ch;
+ }
+ break;
+ }
+ }
+ if (strlen($crt_token))
+ {
+ $tokens[] = $crt_token;
+ $token_modifiers[] = $crt_token_modifier;
+ }
+
+ $clauses = array();
+ for ($i=0; $i<count($tokens); $i++)
+ {
+ $tokens[$i] = trim($tokens[$i], '%');
+ if (strstr($token_modifiers[$i], '-')!==false)
+ continue;
+ if ( strlen($tokens[$i])==0)
+ continue;
+ $clauses[] = $field.' LIKE "%'.addslashes($tokens[$i]).'%"';
+ }
+
+ return count($clauses) ? '('.implode(' OR ', $clauses).')' : null;
+}
+
+
+/**
+ * returns the search results corresponding to a quick/query search.
+ * A quick/query search returns many items (search is not strict), but results
+ * are sorted by relevance unless $page['super_order_by'] is set. Returns:
+ * array (
+ * 'items' => array(85,68,79...)
+ * 'as_is' => 1 (indicates the caller that items are ordered and permissions checked
+ * 'qs' => array(
+ * 'matching_tags' => array of matching tags
+ * 'matching_cats' => array of matching categories
+ * 'matching_cats_no_images' =>array(99) - matching categories without images
+ * ))
+ *
+ * @param string q
+ * @param string images_where optional aditional restriction on images table
+ * @return array
+ */
+function get_quick_search_results($q, $images_where='')
+{
+ global $page;
+ $search_results =
+ array(
+ 'items' => array(),
+ 'as_is' => 1,
+ 'qs' => array('q'=>stripslashes($q)),
+ );
+ $q = trim($q);
+ if (empty($q))
+ {
+ return $search_results;
+ }
+ $q_like_field = '@@__db_field__@@'; //something never in a search
+ $q_like_clause = get_qsearch_like_clause($q, $q_like_field );
+
+
+ // Step 1 - first we find matches in #images table ===========================
+ $where_clauses='MATCH(i.name, i.comment) AGAINST( "'.$q.'" IN BOOLEAN MODE)';
+ if (!empty($q_like_clause))
+ {
+ $where_clauses .= '
+ OR '. str_replace($q_like_field, 'file', $q_like_clause);
+ $where_clauses = '('.$where_clauses.')';
+ }
+ $where_clauses = array($where_clauses);
+ if (!empty($images_where))
+ {
+ $where_clauses[]='('.$images_where.')';
+ }
+ $where_clauses[] .= get_sql_condition_FandF
+ (
+ array( 'visible_images' => 'i.id' ), null, true
+ );
+ $query = '
+SELECT i.id,
+ MATCH(i.name, i.comment) AGAINST( "'.$q.'" IN BOOLEAN MODE) AS weight
+ FROM '.IMAGES_TABLE.' i
+ WHERE '.implode("\n AND ", $where_clauses);
+
+ $by_weights=array();
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ { // weight is important when sorting images by relevance
+ if ($row['weight'])
+ {
+ $by_weights[(int)$row['id']] = 2*$row['weight'];
+ }
+ else
+ {//full text does not match but file name match
+ $by_weights[(int)$row['id']] = 2;
+ }
+ }
+
+
+ // Step 2 - search tags corresponding to the query $q ========================
+ if (!empty($q_like_clause))
+ { // search name and url name (without accents)
+ $query = '
+SELECT id, name, url_name
+ FROM '.TAGS_TABLE.'
+ WHERE ('.str_replace($q_like_field, 'CONVERT(name, CHAR)', $q_like_clause).'
+ OR '.str_replace($q_like_field, 'url_name', $q_like_clause).')';
+ $tags = hash_from_query($query, 'id');
+ if ( !empty($tags) )
+ { // we got some tags; get the images
+ $search_results['qs']['matching_tags']=$tags;
+ $query = '
+SELECT image_id, COUNT(tag_id) AS weight
+ FROM '.IMAGE_TAG_TABLE.'
+ WHERE tag_id IN ('.implode(',',array_keys($tags)).')
+ GROUP BY image_id';
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_assoc($result))
+ { // weight is important when sorting images by relevance
+ $image_id=(int)$row['image_id'];
+ @$by_weights[$image_id] += $row['weight'];
+ }
+ }
+ }
+
+
+ // Step 3 - search categories corresponding to the query $q ==================
+ global $user;
+ $query = '
+SELECT id, name, permalink, nb_images
+ FROM '.CATEGORIES_TABLE.'
+ INNER JOIN '.USER_CACHE_CATEGORIES_TABLE.' ON id=cat_id
+ WHERE user_id='.$user['id'].'
+ AND MATCH(name, comment) AGAINST( "'.$q.'" IN BOOLEAN MODE)'.
+ get_sql_condition_FandF (
+ array( 'visible_categories' => 'cat_id' ), "\n AND"
+ );
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_assoc($result))
+ { // weight is important when sorting images by relevance
+ if ($row['nb_images']==0)
+ {
+ $search_results['qs']['matching_cats_no_images'][] = $row;
+ }
+ else
+ {
+ $search_results['qs']['matching_cats'][$row['id']] = $row;
+ }
+ }
+
+ if ( empty($by_weights) and empty($search_results['qs']['matching_cats']) )
+ {
+ return $search_results;
+ }
+
+ // Step 4 - now we have $by_weights ( array image id => weight ) that need
+ // permission checks and/or matching categories to get images from
+ $where_clauses = array();
+ if ( !empty($by_weights) )
+ {
+ $where_clauses[]='i.id IN ('
+ . implode(',', array_keys($by_weights)) . ')';
+ }
+ if ( !empty($search_results['qs']['matching_cats']) )
+ {
+ $where_clauses[]='category_id IN ('.
+ implode(',',array_keys($search_results['qs']['matching_cats'])).')';
+ }
+ $where_clauses = array( '('.implode("\n OR ",$where_clauses).')' );
+ if (!empty($images_where))
+ {
+ $where_clauses[]='('.$images_where.')';
+ }
+ $where_clauses[] = get_sql_condition_FandF(
+ array
+ (
+ 'forbidden_categories' => 'category_id',
+ 'visible_categories' => 'category_id',
+ 'visible_images' => 'i.id'
+ ),
+ null,true
+ );
+
+ global $conf;
+ $query = '
+SELECT DISTINCT(id)
+ FROM '.IMAGES_TABLE.' i
+ INNER JOIN '.IMAGE_CATEGORY_TABLE.' AS ic ON id = ic.image_id
+ WHERE '.implode("\n AND ", $where_clauses)."\n".
+ $conf['order_by'];
+
+ $allowed_images = array_from_query( $query, 'id');
+
+ if ( isset($page['super_order_by']) or empty($by_weights) )
+ {
+ $search_results['items'] = $allowed_images;
+ return $search_results;
+ }
+
+ $allowed_images = array_flip( $allowed_images );
+ $divisor = 5.0 * count($allowed_images);
+ foreach ($allowed_images as $id=>$rank )
+ {
+ $weight = isset($by_weights[$id]) ? $by_weights[$id] : 1;
+ $weight -= $rank/$divisor;
+ $allowed_images[$id] = $weight;
+ }
+ arsort($allowed_images, SORT_NUMERIC);
+ $search_results['items'] = array_keys($allowed_images);
+ return $search_results;
+}
+
+/**
+ * returns an array of 'items' corresponding to the search id
+ *
+ * @param int search id
+ * @param string images_where optional aditional restriction on images table
+ * @return array
+ */
+function get_search_results($search_id, $images_where='')
+{
+ $search = get_search_array($search_id);
+ if ( !isset($search['q']) )
+ {
+ $result['items'] = get_regular_search_results($search);
+ return $result;
+ }
+ else
+ {
+ return get_quick_search_results($search['q'], $images_where);
+ }
+}
+?> \ No newline at end of file
diff --git a/BSF/include/functions_session.inc.php b/BSF/include/functions_session.inc.php
new file mode 100644
index 000000000..f17f2377a
--- /dev/null
+++ b/BSF/include/functions_session.inc.php
@@ -0,0 +1,225 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+// The function generate_key creates a string with pseudo random characters.
+// the size of the string depends on the $conf['session_id_size'].
+// Characters used are a-z A-Z and numerical values. Examples :
+// "Er4Tgh6", "Rrp08P", "54gj"
+// input : none (using global variable)
+// output : $key
+function generate_key($size)
+{
+ global $conf;
+
+ $md5 = md5(substr(microtime(), 2, 6));
+ $init = '';
+ for ( $i = 0; $i < strlen( $md5 ); $i++ )
+ {
+ if ( is_numeric( $md5[$i] ) ) $init.= $md5[$i];
+ }
+ $init = substr( $init, 0, 8 );
+ mt_srand( $init );
+ $key = '';
+ for ( $i = 0; $i < $size; $i++ )
+ {
+ $c = mt_rand( 0, 2 );
+ if ( $c == 0 ) $key .= chr( mt_rand( 65, 90 ) );
+ else if ( $c == 1 ) $key .= chr( mt_rand( 97, 122 ) );
+ else $key .= mt_rand( 0, 9 );
+ }
+ return $key;
+}
+
+if (isset($conf['session_save_handler'])
+ and ($conf['session_save_handler'] == 'db')
+ and defined('PHPWG_INSTALLED'))
+{
+ session_set_save_handler('pwg_session_open',
+ 'pwg_session_close',
+ 'pwg_session_read',
+ 'pwg_session_write',
+ 'pwg_session_destroy',
+ 'pwg_session_gc'
+ );
+ if ( function_exists('ini_set') )
+ {
+ ini_set('session.use_cookies', $conf['session_use_cookies']);
+ ini_set('session.use_only_cookies', $conf['session_use_only_cookies']);
+ ini_set('session.use_trans_sid', intval($conf['session_use_trans_sid']));
+ }
+ session_name($conf['session_name']);
+ session_set_cookie_params(0, cookie_path());
+}
+
+/**
+ * returns true; used when the session_start() function is called
+ *
+ * @params not use but useful for php engine
+ */
+function pwg_session_open($path, $name)
+{
+ return true;
+}
+
+/**
+ * returns true; used when the session is closed (unset($_SESSION))
+ *
+ */
+function pwg_session_close()
+{
+ return true;
+}
+
+/**
+ * this function returns
+ * a string corresponding to the value of the variable save in the session
+ * or an empty string when the variable doesn't exist
+ *
+ * @param string session id
+ */
+function pwg_session_read($session_id)
+{
+ $query = '
+SELECT data
+ FROM '.SESSIONS_TABLE.'
+ WHERE id = \''.$session_id.'\'
+;';
+ $result = pwg_query($query);
+ if ($result)
+ {
+ $row = mysql_fetch_assoc($result);
+ return $row['data'];
+ }
+ else
+ {
+ return '';
+ }
+}
+
+/**
+ * returns true; writes set a variable in the active session
+ *
+ * @param string session id
+ * @data string value of date to be saved
+ */
+function pwg_session_write($session_id, $data)
+{
+ $query = '
+UPDATE '.SESSIONS_TABLE.'
+ SET expiration = now(),
+ data = \''.$data.'\'
+ WHERE id = \''.$session_id.'\'
+;';
+ pwg_query($query);
+ if ( mysql_affected_rows()>0 )
+ {
+ return true;
+ }
+ $query = '
+INSERT INTO '.SESSIONS_TABLE.'
+ (id,data,expiration)
+ VALUES(\''.$session_id.'\',\''.$data.'\',now())
+;';
+ mysql_query($query);
+ return true;
+}
+
+/**
+ * returns true; delete the active session
+ *
+ * @param string session id
+ */
+function pwg_session_destroy($session_id)
+{
+ $query = '
+DELETE
+ FROM '.SESSIONS_TABLE.'
+ WHERE id = \''.$session_id.'\'
+;';
+ pwg_query($query);
+ return true;
+}
+
+/**
+ * returns true; delete expired sessions
+ * called each time a session is closed.
+ */
+function pwg_session_gc()
+{
+ global $conf;
+
+ $query = '
+DELETE
+ FROM '.SESSIONS_TABLE.'
+ WHERE UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(expiration) > '
+ .$conf['session_length'].'
+;';
+ pwg_query($query);
+ return true;
+}
+
+
+/**
+ * persistently stores a variable for the current session
+ * currently we use standard php sessions but it might change
+ * @return boolean true on success
+ * @see pwg_get_session_var, pwg_unset_session_var
+ */
+function pwg_set_session_var($var, $value)
+{
+ if ( !isset($_SESSION) )
+ return false;
+ $_SESSION['pwg_'.$var] = $value;
+ return true;
+}
+
+/**
+ * retrieves the value of a persistent variable for the current session
+ * currently we use standard php sessions but it might change
+ * @return mixed
+ * @see pwg_set_session_var, pwg_unset_session_var
+ */
+function pwg_get_session_var($var, $default = null)
+{
+ if (isset( $_SESSION['pwg_'.$var] ) )
+ {
+ return $_SESSION['pwg_'.$var];
+ }
+ return $default;
+}
+
+/**
+ * deletes a persistent variable for the current session
+ * currently we use standard php sessions but it might change
+ * @return boolean true on success
+ * @see pwg_set_session_var, pwg_get_session_var
+ */
+function pwg_unset_session_var($var)
+{
+ if ( !isset($_SESSION) )
+ return false;
+ unset( $_SESSION['pwg_'.$var] );
+ return true;
+}
+
+?>
diff --git a/BSF/include/functions_tag.inc.php b/BSF/include/functions_tag.inc.php
new file mode 100644
index 000000000..7bd5d8408
--- /dev/null
+++ b/BSF/include/functions_tag.inc.php
@@ -0,0 +1,323 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+
+/**
+ * Tags available. Each return tag is represented as an array with its id,
+ * its name, its weight (count), its url name. Tags are not sorted.
+ *
+ * The returned list can be a subset of all existing tags due to
+ * permissions, only if a list of forbidden categories is provided
+ *
+ * @param array forbidden categories
+ * @return array
+ */
+function get_available_tags()
+{
+ // we can find top fatter tags among reachable images
+ $query = '
+SELECT tag_id, COUNT(DISTINCT(it.image_id)) counter
+ FROM '.IMAGE_CATEGORY_TABLE.' ic
+ INNER JOIN '.IMAGE_TAG_TABLE.' it ON ic.image_id=it.image_id'.get_sql_condition_FandF
+ (
+ array
+ (
+ 'forbidden_categories' => 'category_id',
+ 'visible_categories' => 'category_id',
+ 'visible_images' => 'ic.image_id'
+ ),
+ '
+ WHERE'
+ ).'
+ GROUP BY tag_id
+ ORDER BY NULL';
+ $tag_counters = simple_hash_from_query($query, 'tag_id', 'counter');
+
+ if ( empty($tag_counters) )
+ {
+ return array();
+ }
+
+ $query = '
+SELECT id, name, url_name
+ FROM '.TAGS_TABLE;
+ $result = pwg_query($query);
+ $tags = array();
+ while ($row = mysql_fetch_assoc($result))
+ {
+ $counter = @$tag_counters[ $row['id'] ];
+ if ( $counter )
+ {
+ $row['counter'] = $counter;
+ array_push($tags, $row);
+ }
+ }
+ return $tags;
+}
+
+/**
+ * All tags, even tags associated to no image.
+ *
+ * @return array
+ */
+function get_all_tags()
+{
+ $query = '
+SELECT id,
+ name,
+ url_name
+ FROM '.TAGS_TABLE.'
+;';
+ $result = pwg_query($query);
+ $tags = array();
+ while ($row = mysql_fetch_assoc($result))
+ {
+ array_push($tags, $row);
+ }
+
+ usort($tags, 'name_compare');
+
+ return $tags;
+}
+
+/**
+ * Giving a set of tags with a counter for each one, calculate the display
+ * level of each tag.
+ *
+ * The level of each tag depends on the average count of tags. This
+ * calcylation method avoid having very different levels for tags having
+ * nearly the same count when set are small.
+ *
+ * @param array tags
+ * @return array
+ */
+function add_level_to_tags($tags)
+{
+ global $conf;
+
+ if (count($tags) == 0)
+ {
+ return $tags;
+ }
+
+ $total_count = 0;
+
+ foreach ($tags as $tag)
+ {
+ $total_count+= $tag['counter'];
+ }
+
+ // average count of available tags will determine the level of each tag
+ $tag_average_count = $total_count / count($tags);
+
+ // tag levels threshold calculation: a tag with an average rate must have
+ // the middle level.
+ for ($i = 1; $i < $conf['tags_levels']; $i++)
+ {
+ $threshold_of_level[$i] =
+ 2 * $i * $tag_average_count / $conf['tags_levels'];
+ }
+
+ // display sorted tags
+ foreach (array_keys($tags) as $k)
+ {
+ $tags[$k]['level'] = 1;
+
+ // based on threshold, determine current tag level
+ for ($i = $conf['tags_levels'] - 1; $i >= 1; $i--)
+ {
+ if ($tags[$k]['counter'] > $threshold_of_level[$i])
+ {
+ $tags[$k]['level'] = $i + 1;
+ break;
+ }
+ }
+ }
+
+ return $tags;
+}
+
+/**
+ * return the list of image ids corresponding to given tags. AND & OR mode
+ * supported.
+ *
+ * @param array tag ids
+ * @param string mode
+ * @return array
+ */
+function get_image_ids_for_tags($tag_ids, $mode = 'AND')
+{
+ switch ($mode)
+ {
+ case 'AND':
+ {
+ // strategy is to list images associated to each tag
+ $tag_images = array();
+
+ foreach ($tag_ids as $tag_id)
+ {
+ $query = '
+SELECT image_id
+ FROM '.IMAGE_TAG_TABLE.'
+ WHERE tag_id = '.$tag_id.'
+;';
+ $tag_images[$tag_id] = array_from_query($query, 'image_id');
+ }
+
+ // then we calculate the intersection, the images that are associated to
+ // every tags
+ $items = array_shift($tag_images);
+ foreach ($tag_images as $images)
+ {
+ $items = array_intersect($items, $images);
+ }
+
+ return array_unique($items);
+ break;
+ }
+ case 'OR':
+ {
+ $query = '
+SELECT DISTINCT image_id
+ FROM '.IMAGE_TAG_TABLE.'
+ WHERE tag_id IN ('.implode(',', $tag_ids).')
+;';
+ return array_from_query($query, 'image_id');
+ break;
+ }
+ default:
+ {
+ die('get_image_ids_for_tags: unknown mode, only AND & OR are supported');
+ }
+ }
+}
+
+/**
+ * return a list of tags corresponding to given items.
+ *
+ * @param array items
+ * @param array max_tags
+ * @param array excluded_tag_ids
+ * @return array
+ */
+function get_common_tags($items, $max_tags, $excluded_tag_ids=null)
+{
+ if (empty($items))
+ {
+ return array();
+ }
+ $query = '
+SELECT id, name, url_name, count(*) counter
+ FROM '.IMAGE_TAG_TABLE.'
+ INNER JOIN '.TAGS_TABLE.' ON tag_id = id
+ WHERE image_id IN ('.implode(',', $items).')';
+ if (!empty($excluded_tag_ids))
+ {
+ $query.='
+ AND tag_id NOT IN ('.implode(',', $excluded_tag_ids).')';
+ }
+ $query .='
+ GROUP BY tag_id';
+ if ($max_tags>0)
+ {
+ $query .= '
+ ORDER BY counter DESC
+ LIMIT 0,'.$max_tags;
+ }
+ else
+ {
+ $query .= '
+ ORDER BY NULL';
+ }
+
+ $result = pwg_query($query);
+ $tags = array();
+ while($row = mysql_fetch_assoc($result))
+ {
+ array_push($tags, $row);
+ }
+ usort($tags, 'name_compare');
+ return $tags;
+}
+
+/**
+ * return a list of tags corresponding to any of ids, url_names, names
+ *
+ * @param array ids
+ * @param array url_names
+ * @param array names
+ * @return array
+ */
+function find_tags($ids, $url_names=array(), $names=array() )
+{
+ $where_clauses = array();
+ if ( !empty($ids) )
+ {
+ $where_clauses[] = 'id IN ('.implode(',', $ids).')';
+ }
+ if ( !empty($url_names) )
+ {
+ $where_clauses[] =
+ 'url_name IN ('.
+ implode(
+ ',',
+ array_map(
+ create_function('$s', 'return "\'".$s."\'";'),
+ $url_names
+ )
+ )
+ .')';
+ }
+ if ( !empty($names) )
+ {
+ $where_clauses[] =
+ 'name IN ('.
+ implode(
+ ',',
+ array_map(
+ create_function('$s', 'return "\'".$s."\'";'),
+ $names
+ )
+ )
+ .')';
+ }
+ if (empty($where_clauses))
+ {
+ return array();
+ }
+
+ $query = '
+SELECT id, url_name, name
+ FROM '.TAGS_TABLE.'
+ WHERE '. implode( '
+ OR ', $where_clauses);
+
+ $result = pwg_query($query);
+ $tags = array();
+ while ($row = mysql_fetch_assoc($result))
+ {
+ array_push($tags, $row);
+ }
+ return $tags;
+}
+?> \ No newline at end of file
diff --git a/BSF/include/functions_url.inc.php b/BSF/include/functions_url.inc.php
new file mode 100644
index 000000000..fa7114dea
--- /dev/null
+++ b/BSF/include/functions_url.inc.php
@@ -0,0 +1,740 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+
+/**
+ * returns a prefix for each url link on displayed page
+ * and return an empty string for current path
+ * @return string
+ */
+function get_root_url()
+{
+ global $page;
+ if ( isset($page['root_path']) )
+ {
+ $root_url = $page['root_path'];
+ }
+ else
+ {// TODO - add HERE the possibility to call PWG functions from external scripts
+ $root_url = PHPWG_ROOT_PATH;
+ }
+ if ( dirname($root_url)!='.' )
+ {
+ return $root_url;
+ }
+ else
+ {
+ return (string)substr($root_url, 2);
+ }
+}
+
+/**
+ * returns the absolute url to the root of PWG
+ * @param boolean with_scheme if false - does not add http://toto.com
+ */
+function get_absolute_root_url($with_scheme=true)
+{
+ // TODO - add HERE the possibility to call PWG functions from external scripts
+ $url = '';
+ if ($with_scheme)
+ {
+ if (empty($_SERVER['HTTPS']))
+ {
+ $url .= 'http://';
+ }
+ else
+ {
+ $url .= 'https://';
+ }
+ $url .= $_SERVER['HTTP_HOST'];
+ if ($_SERVER['SERVER_PORT'] != 80)
+ {
+ $url_port = ':'.$_SERVER['SERVER_PORT'];
+ if (strrchr($url, ':') != $url_port)
+ {
+ $url .= $url_port;
+ }
+ }
+ }
+ $url .= cookie_path();
+ return $url;
+}
+
+/**
+ * adds one or more _GET style parameters to an url
+ * example: add_url_params('/x', array('a'=>'b')) returns /x?a=b
+ * add_url_params('/x?cat_id=10', array('a'=>'b')) returns /x?cat_id=10&amp;a=b
+ * @param string url
+ * @param array params
+ * @return string
+ */
+function add_url_params($url, $params)
+{
+ if ( !empty($params) )
+ {
+ assert( is_array($params) );
+ $is_first = true;
+ foreach($params as $param=>$val)
+ {
+ if ($is_first)
+ {
+ $is_first = false;
+ $url .= ( strstr($url, '?')===false ) ? '?' :'&amp;';
+ }
+ else
+ {
+ $url .= '&amp;';
+ }
+ $url .= $param;
+ if (isset($val))
+ {
+ $url .= '='.$val;
+ }
+ }
+ }
+ return $url;
+}
+
+/**
+ * build an index URL for a specific section
+ *
+ * @param array
+ * @return string
+ */
+function make_index_url($params = array())
+{
+ global $conf;
+ $url = get_root_url().'index';
+ if ($conf['php_extension_in_urls'])
+ {
+ $url .= '.php';
+ }
+ if ($conf['question_mark_in_urls'])
+ {
+ $url .= '?';
+ }
+ $url.= make_section_in_url($params);
+ $url = add_well_known_params_in_url($url, $params);
+ return $url;
+}
+
+/**
+ * build an index URL with current page parameters, but with redefinitions
+ * and removes.
+ *
+ * duplicate_index_url( array(
+ * 'category' => array('id'=>12, 'name'=>'toto'),
+ * array('start')
+ * ) will create an index URL on the current section (categories), but on
+ * a redefined category and without the start URL parameter.
+ *
+ * @param array redefined keys
+ * @param array removed keys
+ * @return string
+ */
+function duplicate_index_url($redefined = array(), $removed = array())
+{
+ return make_index_url(
+ params_for_duplication($redefined, $removed)
+ );
+}
+
+/**
+ * returns $page global array with key redefined and key removed
+ *
+ * @param array redefined keys
+ * @param array removed keys
+ * @return array
+ */
+function params_for_duplication($redefined, $removed)
+{
+ global $page;
+
+ if (count($removed) > 0)
+ {
+ $params = array();
+
+ foreach ($page as $page_item_key => $page_item_value)
+ {
+ if (!in_array($page_item_key, $removed))
+ {
+ $params[$page_item_key] = $page_item_value;
+ }
+ }
+ }
+ else
+ {
+ $params = $page;
+ }
+
+ foreach ($redefined as $redefined_param => $redefined_value)
+ {
+ $params[$redefined_param] = $redefined_value;
+ }
+
+ return $params;
+}
+
+/**
+ * create a picture URL with current page parameters, but with redefinitions
+ * and removes. See duplicate_index_url.
+ *
+ * @param array redefined keys
+ * @param array removed keys
+ * @return string
+ */
+function duplicate_picture_url($redefined = array(), $removed = array())
+{
+ return make_picture_url(
+ params_for_duplication($redefined, $removed)
+ );
+}
+
+/**
+ * create a picture URL on a specific section for a specific picture
+ *
+ * @param array
+ * @return string
+ */
+function make_picture_url($params)
+{
+ global $conf;
+ if (!isset($params['image_id']))
+ {
+ die('make_picture_url: image_id is a required parameter');
+ }
+
+ $url = get_root_url().'picture';
+ if ($conf['php_extension_in_urls'])
+ {
+ $url .= '.php';
+ }
+ if ($conf['question_mark_in_urls'])
+ {
+ $url .= '?';
+ }
+ $url.= '/';
+ switch ( $conf['picture_url_style'] )
+ {
+ case 'id-file':
+ $url .= $params['image_id'];
+ if ( isset($params['image_file']) )
+ {
+ $url .= '-'.get_filename_wo_extension($params['image_file']);
+ }
+ break;
+ case 'file':
+ if ( isset($params['image_file']) )
+ {
+ $fname_wo_ext = get_filename_wo_extension($params['image_file']);
+ if (! preg_match('/^\d+(-|$)/', $fname_wo_ext) )
+ {
+ $url .= $fname_wo_ext;
+ break;
+ }
+ }
+ default:
+ $url .= $params['image_id'];
+ }
+ if ( !isset($params['category'] ) )
+ {// make urls shorter ...
+ unset( $params['flat'] );
+ }
+ $url .= make_section_in_url($params);
+ $url = add_well_known_params_in_url($url, $params);
+ return $url;
+}
+
+/**
+ *adds to the url the chronology and start parameters
+*/
+function add_well_known_params_in_url($url, $params)
+{
+ if ( isset($params['chronology_field']) )
+ {
+ $url .= '/'. $params['chronology_field'];
+ $url .= '-'. $params['chronology_style'];
+ if ( isset($params['chronology_view']) )
+ {
+ $url .= '-'. $params['chronology_view'];
+ }
+ if ( !empty($params['chronology_date']) )
+ {
+ $url .= '-'. implode('-', $params['chronology_date'] );
+ }
+ }
+
+ if (isset($params['flat']))
+ {
+ $url.= '/flat';
+ }
+
+ if (isset($params['start']) and $params['start'] > 0)
+ {
+ $url.= '/start-'.$params['start'];
+ }
+ return $url;
+}
+
+/**
+ * return the section token of an index or picture URL.
+ *
+ * Depending on section, other parameters are required (see function code
+ * for details)
+ *
+ * @param array
+ * @return string
+ */
+function make_section_in_url($params)
+{
+ global $conf;
+ $section_string = '';
+
+ $section_of = array(
+ 'category' => 'categories',
+ 'tags' => 'tags',
+ 'list' => 'list',
+ 'search' => 'search',
+ );
+
+ foreach ($section_of as $param => $section)
+ {
+ if (isset($params[$param]))
+ {
+ $params['section'] = $section;
+ }
+ }
+
+ if (!isset($params['section']))
+ {
+ $params['section'] = 'none';
+ }
+
+ switch($params['section'])
+ {
+ case 'categories' :
+ {
+ if (!isset($params['category']))
+ {
+ $section_string.= '/categories';
+ }
+ else
+ {
+ is_array($params['category']) or trigger_error(
+ 'make_section_in_url wrong type for category', E_USER_WARNING
+ );
+ is_numeric($params['category']['id']) or trigger_error(
+ 'make_section_in_url category id not numeric', E_USER_WARNING
+ );
+ isset($params['category']['name']) or trigger_error(
+ 'make_section_in_url category name not set', E_USER_WARNING
+ );
+
+ array_key_exists('permalink', $params['category']) or trigger_error(
+ 'make_section_in_url category permalink not set', E_USER_WARNING
+ );
+
+ $section_string.= '/category/';
+ if ( empty($params['category']['permalink']) )
+ {
+ $section_string.= $params['category']['id'];
+ if ( $conf['category_url_style']=='id-name' )
+ {
+ $section_string.= '-'.str2url($params['category']['name']);
+ }
+ }
+ else
+ {
+ $section_string.= $params['category']['permalink'];
+ }
+ }
+
+ break;
+ }
+ case 'tags' :
+ {
+ if (!isset($params['tags']) or count($params['tags']) == 0)
+ {
+ die('make_section_in_url: require at least one tag');
+ }
+
+ $section_string.= '/tags';
+
+ foreach ($params['tags'] as $tag)
+ {
+ switch ( $conf['tag_url_style'] )
+ {
+ case 'id':
+ $section_string.= '/'.$tag['id'];
+ break;
+ case 'tag':
+ if (isset($tag['url_name']) and !is_numeric($tag['url_name']) )
+ {
+ $section_string.= '/'.$tag['url_name'];
+ break;
+ }
+ default:
+ $section_string.= '/'.$tag['id'];
+ if (isset($tag['url_name']))
+ {
+ $section_string.= '-'.$tag['url_name'];
+ }
+ }
+ }
+
+ break;
+ }
+ case 'search' :
+ {
+ if (!isset($params['search']))
+ {
+ die('make_section_in_url: require a search identifier');
+ }
+
+ $section_string.= '/search/'.$params['search'];
+
+ break;
+ }
+ case 'list' :
+ {
+ if (!isset($params['list']))
+ {
+ die('make_section_in_url: require a list of items');
+ }
+
+ $section_string.= '/list/'.implode(',', $params['list']);
+
+ break;
+ }
+ case 'none' :
+ {
+ break;
+ }
+ default :
+ {
+ $section_string.= '/'.$params['section'];
+ }
+ }
+
+ return $section_string;
+}
+
+/**
+ * the reverse of make_section_in_url
+ * returns the 'section' (categories/tags/...) and the data associated with it
+ *
+ * Depending on section, other parameters are returned (category/tags/list/...)
+ *
+ * @param array of url tokens to parse
+ * @param int the index in the array of url tokens; in/out
+ * @return array
+ */
+function parse_section_url( $tokens, &$next_token)
+{
+ $page=array();
+ if (0 === strpos(@$tokens[$next_token], 'categor'))
+ {
+ $page['section'] = 'categories';
+ $next_token++;
+
+ if (isset($tokens[$next_token]) )
+ {
+ if (preg_match('/^(\d+)(?:-(.+))?$/', $tokens[$next_token], $matches))
+ {
+ if ( isset($matches[2]) )
+ $page['hit_by']['cat_url_name'] = $matches[2];
+ $page['category'] = $matches[1];
+ $next_token++;
+ }
+ else
+ {// try a permalink
+ $maybe_permalinks = array();
+ $current_token = $next_token;
+ while ( isset($tokens[$current_token])
+ and strpos($tokens[$current_token], 'created-')!==0
+ and strpos($tokens[$current_token], 'posted-')!==0
+ and strpos($tokens[$next_token], 'start-')!==0
+ and $tokens[$current_token] != 'flat')
+ {
+ if (empty($maybe_permalinks))
+ {
+ array_push($maybe_permalinks, $tokens[$current_token]);
+ }
+ else
+ {
+ array_push($maybe_permalinks,
+ $maybe_permalinks[count($maybe_permalinks)-1]
+ . '/' . $tokens[$current_token]
+ );
+ }
+ $current_token++;
+ }
+
+ if ( count($maybe_permalinks) )
+ {
+ $cat_id = get_cat_id_from_permalinks($maybe_permalinks, $perma_index);
+ if ( isset($cat_id) )
+ {
+ $next_token += $perma_index+1;
+ $page['category'] = $cat_id;
+ $page['hit_by']['cat_permalink'] = $maybe_permalinks[$perma_index];
+ }
+ else
+ {
+ page_not_found('Permalink for album not found');
+ }
+ }
+ }
+ }
+
+ if (isset($page['category']))
+ {
+ $result = get_cat_info($page['category']);
+ if (empty($result))
+ {
+ page_not_found('Requested category does not exist' );
+ }
+ $page['category']=$result;
+ }
+ }
+ else if (0 === strpos(@$tokens[$next_token], 'tag'))
+ {
+ $page['section'] = 'tags';
+ $page['tags'] = array();
+
+ $next_token++;
+ $i = $next_token;
+
+ $requested_tag_ids = array();
+ $requested_tag_url_names = array();
+
+ while (isset($tokens[$i]))
+ {
+ if ( preg_match('/^(created-|posted-|start-(\d)+)/', $tokens[$i]) )
+ break;
+
+ if ( preg_match('/^(\d+)(?:-(.*))?/', $tokens[$i], $matches) )
+ {
+ array_push($requested_tag_ids, $matches[1]);
+ }
+ else
+ {
+ array_push($requested_tag_url_names, $tokens[$i]);
+ }
+ $i++;
+ }
+ $next_token = $i;
+
+ if ( empty($requested_tag_ids) && empty($requested_tag_url_names) )
+ {
+ bad_request('at least one tag required');
+ }
+
+ $page['tags'] = find_tags($requested_tag_ids, $requested_tag_url_names);
+ if ( empty($page['tags']) )
+ {
+ page_not_found('Requested tag does not exist', get_root_url().'tags.php' );
+ }
+ }
+ else if (0 === strpos(@$tokens[$next_token], 'fav'))
+ {
+ $page['section'] = 'favorites';
+ $next_token++;
+ }
+ else if ('most_visited' == @$tokens[$next_token])
+ {
+ $page['section'] = 'most_visited';
+ $next_token++;
+ }
+ else if ('best_rated' == @$tokens[$next_token])
+ {
+ $page['section'] = 'best_rated';
+ $next_token++;
+ }
+ else if ('recent_pics' == @$tokens[$next_token])
+ {
+ $page['section'] = 'recent_pics';
+ $next_token++;
+ }
+ else if ('recent_cats' == @$tokens[$next_token])
+ {
+ $page['section'] = 'recent_cats';
+ $next_token++;
+ }
+ else if ('search' == @$tokens[$next_token])
+ {
+ $page['section'] = 'search';
+ $next_token++;
+
+ preg_match('/(\d+)/', @$tokens[$next_token], $matches);
+ if (!isset($matches[1]))
+ {
+ bad_request('search identifier is missing');
+ }
+ $page['search'] = $matches[1];
+ $next_token++;
+ }
+ else if ('list' == @$tokens[$next_token])
+ {
+ $page['section'] = 'list';
+ $next_token++;
+
+ $page['list'] = array();
+
+ // No pictures
+ if (empty($tokens[$next_token]))
+ {
+ // Add dummy element list
+ array_push($page['list'], -1);
+ }
+ // With pictures list
+ else
+ {
+ if (!preg_match('/^\d+(,\d+)*$/', $tokens[$next_token]))
+ {
+ bad_request('wrong format on list GET parameter');
+ }
+ foreach (explode(',', $tokens[$next_token]) as $image_id)
+ {
+ array_push($page['list'], $image_id);
+ }
+ }
+ $next_token++;
+ }
+ return $page;
+}
+
+/**
+ * the reverse of add_well_known_params_in_url
+ * parses start, flat and chronology from url tokens
+*/
+function parse_well_known_params_url($tokens, &$i)
+{
+ $page = array();
+ while (isset($tokens[$i]))
+ {
+ if (preg_match('/^start-(\d+)/', $tokens[$i], $matches))
+ {
+ $page['start'] = $matches[1];
+ }
+
+ if ( 'flat' == $tokens[$i] )
+ {
+ // indicate a special list of images
+ $page['flat'] = true;
+ }
+
+ if (preg_match('/^(posted|created)/', $tokens[$i] ))
+ {
+ $chronology_tokens = explode('-', $tokens[$i] );
+
+ $page['chronology_field'] = $chronology_tokens[0];
+
+ array_shift($chronology_tokens);
+ $page['chronology_style'] = $chronology_tokens[0];
+
+ array_shift($chronology_tokens);
+ if ( count($chronology_tokens)>0 )
+ {
+ if ('list'==$chronology_tokens[0] or
+ 'calendar'==$chronology_tokens[0])
+ {
+ $page['chronology_view'] = $chronology_tokens[0];
+ array_shift($chronology_tokens);
+ }
+ $page['chronology_date'] = $chronology_tokens;
+ }
+ }
+ $i++;
+ }
+ return $page;
+}
+
+/**
+ * Indicate to build url with full path
+ *
+ * @param null
+ * @return null
+ */
+function set_make_full_url()
+{
+ global $page;
+
+ if (!isset($page['save_root_path']))
+ {
+ if (isset($page['root_path']))
+ {
+ $page['save_root_path']['path'] = $page['root_path'];
+ }
+ $page['save_root_path']['count'] = 1;
+ $page['root_path'] = get_absolute_root_url();
+ }
+ else
+ {
+ $page['save_root_path']['count'] += 1;
+ }
+}
+
+/**
+ * Restore old parameter to build url with full path
+ *
+ * @param null
+ * @return null
+ */
+function unset_make_full_url()
+{
+ global $page;
+
+ if (isset($page['save_root_path']))
+ {
+ if ($page['save_root_path']['count'] == 1)
+ {
+ if (isset($page['save_root_path']['path']))
+ {
+ $page['root_path'] = $page['save_root_path']['path'];
+ }
+ else
+ {
+ unset($page['root_path']);
+ }
+ unset($page['save_root_path']);
+ }
+ else
+ {
+ $page['save_root_path']['count'] -= 1;
+ }
+ }
+}
+
+/**
+ * Embellish the url argument
+ *
+ * @param $url
+ * @return $url embellished
+ */
+function embellish_url($url)
+{
+ return str_replace('/./', '/', $url);
+}
+
+?> \ No newline at end of file
diff --git a/BSF/include/functions_user.inc.php b/BSF/include/functions_user.inc.php
new file mode 100644
index 000000000..d2c9530e2
--- /dev/null
+++ b/BSF/include/functions_user.inc.php
@@ -0,0 +1,1340 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+// validate_mail_address:
+// o verifies whether the given mail address has the
+// right format. ie someone@domain.com "someone" can contain ".", "-" or
+// even "_". Exactly as "domain". The extension doesn't have to be
+// "com". The mail address can also be empty.
+// o check if address could be empty
+// o check if address is not used by a other user
+// If the mail address doesn't correspond, an error message is returned.
+//
+function validate_mail_address($user_id, $mail_address)
+{
+ global $conf;
+
+ if (empty($mail_address) and
+ !($conf['obligatory_user_mail_address'] and
+ in_array(script_basename(), array('register', 'profile'))))
+ {
+ return '';
+ }
+
+ $regex = '/^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)*\.[a-z]+$/';
+ if ( !preg_match( $regex, $mail_address ) )
+ {
+ return l10n('reg_err_mail_address');
+ }
+
+ if (defined("PHPWG_INSTALLED") and !empty($mail_address))
+ {
+ $query = '
+select count(*)
+from '.USERS_TABLE.'
+where upper('.$conf['user_fields']['email'].') = upper(\''.$mail_address.'\')
+'.(is_numeric($user_id) ? 'and '.$conf['user_fields']['id'].' != \''.$user_id.'\'' : '').'
+;';
+ list($count) = mysql_fetch_array(pwg_query($query));
+ if ($count != 0)
+ {
+ return l10n('reg_err_mail_address_dbl');
+ }
+ }
+}
+
+function register_user($login, $password, $mail_address,
+ $with_notification = true, $errors = array())
+{
+ global $conf;
+
+ if ($login == '')
+ {
+ array_push($errors, l10n('reg_err_login1'));
+ }
+ if (ereg("^.* $", $login))
+ {
+ array_push($errors, l10n('reg_err_login2'));
+ }
+ if (ereg("^ .*$", $login))
+ {
+ array_push($errors, l10n('reg_err_login3'));
+ }
+ if (get_userid($login))
+ {
+ array_push($errors, l10n('reg_err_login5'));
+ }
+ $mail_error = validate_mail_address(null, $mail_address);
+ if ('' != $mail_error)
+ {
+ array_push($errors, $mail_error);
+ }
+
+ $errors = trigger_event('register_user_check',
+ $errors,
+ array(
+ 'username'=>$login,
+ 'password'=>$password,
+ 'email'=>$mail_address,
+ )
+ );
+
+ // if no error until here, registration of the user
+ if (count($errors) == 0)
+ {
+ // what will be the inserted id ?
+ $query = '
+SELECT MAX('.$conf['user_fields']['id'].') + 1
+ FROM '.USERS_TABLE.'
+;';
+ list($next_id) = mysql_fetch_array(pwg_query($query));
+
+ $insert =
+ array(
+ $conf['user_fields']['id'] => $next_id,
+ $conf['user_fields']['username'] => mysql_escape_string($login),
+ $conf['user_fields']['password'] => $conf['pass_convert']($password),
+ $conf['user_fields']['email'] => $mail_address
+ );
+
+ include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
+ mass_inserts(USERS_TABLE, array_keys($insert), array($insert));
+
+ // Assign by default groups
+ {
+ $query = '
+SELECT id
+ FROM '.GROUPS_TABLE.'
+ WHERE is_default = \''.boolean_to_string(true).'\'
+ ORDER BY id ASC
+;';
+ $result = pwg_query($query);
+
+ $inserts = array();
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push
+ (
+ $inserts,
+ array
+ (
+ 'user_id' => $next_id,
+ 'group_id' => $row['id']
+ )
+ );
+ }
+ }
+
+ if (count($inserts) != 0)
+ {
+ include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
+ mass_inserts(USER_GROUP_TABLE, array('user_id', 'group_id'), $inserts);
+ }
+
+ create_user_infos($next_id);
+
+ if ($with_notification and $conf['email_admin_on_new_user'])
+ {
+ include_once(PHPWG_ROOT_PATH.'include/functions_mail.inc.php');
+ $admin_url = get_absolute_root_url()
+ .'admin.php?page=user_list&username='.$login;
+
+ $keyargs_content = array
+ (
+ get_l10n_args('User: %s', $login),
+ get_l10n_args('Email: %s', $_POST['mail_address']),
+ get_l10n_args('', ''),
+ get_l10n_args('Admin: %s', $admin_url)
+ );
+
+ pwg_mail_notification_admins
+ (
+ get_l10n_args('Registration of %s', $login),
+ $keyargs_content
+ );
+ }
+
+ trigger_action('register_user',
+ array(
+ 'id'=>$next_id,
+ 'username'=>$login,
+ 'email'=>$mail_address,
+ )
+ );
+ }
+
+ return $errors;
+}
+
+function build_user( $user_id, $use_cache )
+{
+ global $conf;
+
+ $user['id'] = $user_id;
+ $user = array_merge( $user, getuserdata($user_id, $use_cache) );
+
+ if ($user['id'] == $conf['guest_id'] and $user['status'] <> 'guest')
+ {
+ $user['status'] = 'guest';
+ $user['internal_status']['guest_must_be_guest'] = true;
+ }
+
+ // calculation of the number of picture to display per page
+ $user['nb_image_page'] = $user['nb_image_line'] * $user['nb_line_page'];
+
+ if (is_admin($user['status']))
+ {
+ list($user['admin_template'], $user['admin_theme']) =
+ explode ('/', $conf['admin_layout']);
+ }
+
+ list($user['template'], $user['theme']) = explode('/', $user['template']);
+
+ return $user;
+}
+
+/**
+ * find informations related to the user identifier
+ *
+ * @param int user identifier
+ * @param boolean use_cache
+ * @param array
+ */
+function getuserdata($user_id, $use_cache)
+{
+ global $conf;
+
+ $userdata = array();
+
+ $query = '
+SELECT ';
+ $is_first = true;
+ foreach ($conf['user_fields'] as $pwgfield => $dbfield)
+ {
+ if ($is_first)
+ {
+ $is_first = false;
+ }
+ else
+ {
+ $query.= '
+ , ';
+ }
+ $query.= $dbfield.' AS '.$pwgfield;
+ }
+ $query.= '
+ FROM '.USERS_TABLE.'
+ WHERE '.$conf['user_fields']['id'].' = \''.$user_id.'\'
+;';
+
+ $row = mysql_fetch_array(pwg_query($query));
+
+ while (true)
+ {
+ $query = '
+SELECT ui.*, uc.*
+ FROM '.USER_INFOS_TABLE.' AS ui LEFT JOIN '.USER_CACHE_TABLE.' AS uc
+ ON ui.user_id = uc.user_id
+ WHERE ui.user_id = \''.$user_id.'\'
+;';
+ $result = pwg_query($query);
+ if (mysql_num_rows($result) > 0)
+ {
+ break;
+ }
+ else
+ {
+ create_user_infos($user_id);
+ }
+ }
+
+ $row = array_merge($row, mysql_fetch_array($result));
+
+ foreach ($row as $key => $value)
+ {
+ if (!is_numeric($key))
+ {
+ // If the field is true or false, the variable is transformed into a
+ // boolean value.
+ if ($value == 'true' or $value == 'false')
+ {
+ $userdata[$key] = get_boolean($value);
+ }
+ else
+ {
+ $userdata[$key] = $value;
+ }
+ }
+ }
+
+ if ($use_cache)
+ {
+ if (!isset($userdata['need_update'])
+ or !is_bool($userdata['need_update'])
+ or $userdata['need_update'] == true)
+ {
+ $userdata['forbidden_categories'] =
+ calculate_permissions($userdata['id'], $userdata['status']);
+
+ /* now we build the list of forbidden images (this list does not contain
+ images that are not in at least an authorized category)*/
+ $query = '
+SELECT DISTINCT(id)
+ FROM '.IMAGES_TABLE.' INNER JOIN '.IMAGE_CATEGORY_TABLE.' ON id=image_id
+ WHERE category_id NOT IN ('.$userdata['forbidden_categories'].')
+ AND level>'.$userdata['level'];
+ $forbidden_ids = array_from_query($query, 'id');
+
+ if ( empty($forbidden_ids) )
+ {
+ array_push( $forbidden_ids, 0 );
+ }
+ $userdata['image_access_type'] = 'NOT IN'; //TODO maybe later
+ $userdata['image_access_list'] = implode(',',$forbidden_ids);
+
+ update_user_cache_categories($userdata);
+
+ // Set need update are done
+ $userdata['need_update'] = false;
+
+ // Indicate update done
+ $userdata['need_update_done'] = true;
+
+ $query = '
+SELECT COUNT(DISTINCT(image_id)) as total
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ WHERE category_id NOT IN ('.$userdata['forbidden_categories'].')
+ AND image_id '.$userdata['image_access_type'].' ('.$userdata['image_access_list'].')
+;';
+ list($userdata['nb_total_images']) = mysql_fetch_array(pwg_query($query));
+
+ // update user cache
+ $query = '
+DELETE FROM '.USER_CACHE_TABLE.'
+ WHERE user_id = '.$userdata['id'].'
+;';
+ pwg_query($query);
+
+ $query = '
+INSERT INTO '.USER_CACHE_TABLE.'
+ (user_id, need_update, forbidden_categories, nb_total_images,
+ image_access_type, image_access_list)
+ VALUES
+ ('.$userdata['id'].',\''.boolean_to_string($userdata['need_update']).'\',\''
+ .$userdata['forbidden_categories'].'\','.$userdata['nb_total_images'].',"'
+ .$userdata['image_access_type'].'","'.$userdata['image_access_list'].'")
+;';
+ pwg_query($query);
+ }
+ else
+ {
+ // Indicate update not done
+ $userdata['need_update_done'] = false;
+ }
+ }
+
+ return $userdata;
+}
+
+/*
+ * deletes favorites of the current user if he's not allowed to see them
+ *
+ * @return void
+ */
+function check_user_favorites()
+{
+ global $user;
+
+ if ($user['forbidden_categories'] == '')
+ {
+ return;
+ }
+
+ // $filter['visible_categories'] and $filter['visible_images']
+ // must be not used because filter <> restriction
+ // retrieving images allowed : belonging to at least one authorized
+ // category
+ $query = '
+SELECT DISTINCT f.image_id
+ FROM '.FAVORITES_TABLE.' AS f INNER JOIN '.IMAGE_CATEGORY_TABLE.' AS ic
+ ON f.image_id = ic.image_id
+ WHERE f.user_id = '.$user['id'].'
+'.get_sql_condition_FandF
+ (
+ array
+ (
+ 'forbidden_categories' => 'ic.category_id',
+ ),
+ 'AND'
+ ).'
+;';
+ $result = pwg_query($query);
+ $authorizeds = array();
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push($authorizeds, $row['image_id']);
+ }
+
+ $query = '
+SELECT image_id
+ FROM '.FAVORITES_TABLE.'
+ WHERE user_id = '.$user['id'].'
+;';
+ $result = pwg_query($query);
+ $favorites = array();
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push($favorites, $row['image_id']);
+ }
+
+ $to_deletes = array_diff($favorites, $authorizeds);
+
+ if (count($to_deletes) > 0)
+ {
+ $query = '
+DELETE FROM '.FAVORITES_TABLE.'
+ WHERE image_id IN ('.implode(',', $to_deletes).')
+ AND user_id = '.$user['id'].'
+;';
+ pwg_query($query);
+ }
+}
+
+/**
+ * calculates the list of forbidden categories for a given user
+ *
+ * Calculation is based on private categories minus categories authorized to
+ * the groups the user belongs to minus the categories directly authorized
+ * to the user. The list contains at least -1 to be compliant with queries
+ * such as "WHERE category_id NOT IN ($forbidden_categories)"
+ *
+ * @param int user_id
+ * @param string user_status
+ * @return string forbidden_categories
+ */
+function calculate_permissions($user_id, $user_status)
+{
+ $private_array = array();
+ $authorized_array = array();
+
+ $query = '
+SELECT id
+ FROM '.CATEGORIES_TABLE.'
+ WHERE status = \'private\'
+;';
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push($private_array, $row['id']);
+ }
+
+ // retrieve category ids directly authorized to the user
+ $query = '
+SELECT cat_id
+ FROM '.USER_ACCESS_TABLE.'
+ WHERE user_id = '.$user_id.'
+;';
+ $authorized_array = array_from_query($query, 'cat_id');
+
+ // retrieve category ids authorized to the groups the user belongs to
+ $query = '
+SELECT cat_id
+ FROM '.USER_GROUP_TABLE.' AS ug INNER JOIN '.GROUP_ACCESS_TABLE.' AS ga
+ ON ug.group_id = ga.group_id
+ WHERE ug.user_id = '.$user_id.'
+;';
+ $authorized_array =
+ array_merge(
+ $authorized_array,
+ array_from_query($query, 'cat_id')
+ );
+
+ // uniquify ids : some private categories might be authorized for the
+ // groups and for the user
+ $authorized_array = array_unique($authorized_array);
+
+ // only unauthorized private categories are forbidden
+ $forbidden_array = array_diff($private_array, $authorized_array);
+
+ // if user is not an admin, locked categories are forbidden
+ if (!is_admin($user_status))
+ {
+ $query = '
+SELECT id
+ FROM '.CATEGORIES_TABLE.'
+ WHERE visible = \'false\'
+;';
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_array($result))
+ {
+ array_push($forbidden_array, $row['id']);
+ }
+ $forbidden_array = array_unique($forbidden_array);
+ }
+
+ if ( empty($forbidden_array) )
+ {// at least, the list contains 0 value. This category does not exists so
+ // where clauses such as "WHERE category_id NOT IN(0)" will always be
+ // true.
+ array_push($forbidden_array, 0);
+ }
+
+ return implode(',', $forbidden_array);
+}
+
+/**
+ * compute data of categories branches (one branch only)
+ */
+function compute_branch_cat_data(&$cats, &$list_cat_id, &$level, &$ref_level)
+{
+ $date = '';
+ $count_images = 0;
+ $count_categories = 0;
+ do
+ {
+ $cat_id = array_pop($list_cat_id);
+ if (!is_null($cat_id))
+ {
+ // Count images and categories
+ $cats[$cat_id]['count_images'] += $count_images;
+ $cats[$cat_id]['count_categories'] += $count_categories;
+ $count_images = $cats[$cat_id]['count_images'];
+ $count_categories = $cats[$cat_id]['count_categories'] + 1;
+
+ if ((empty($cats[$cat_id]['max_date_last'])) or ($cats[$cat_id]['max_date_last'] < $date))
+ {
+ $cats[$cat_id]['max_date_last'] = $date;
+ }
+ else
+ {
+ $date = $cats[$cat_id]['max_date_last'];
+ }
+ $ref_level = substr_count($cats[$cat_id]['global_rank'], '.') + 1;
+ }
+ else
+ {
+ $ref_level = 0;
+ }
+ } while ($level <= $ref_level);
+
+ // Last cat updating must be added to list for next branch
+ if ($ref_level <> 0)
+ {
+ array_push($list_cat_id, $cat_id);
+ }
+}
+
+/**
+ * compute data of categories branches
+ */
+function compute_categories_data(&$cats)
+{
+ $ref_level = 0;
+ $level = 0;
+ $list_cat_id = array();
+
+ foreach ($cats as $id => $category)
+ {
+ // Compute
+ $level = substr_count($category['global_rank'], '.') + 1;
+ if ($level > $ref_level)
+ {
+ array_push($list_cat_id, $id);
+ }
+ else
+ {
+ compute_branch_cat_data($cats, $list_cat_id, $level, $ref_level);
+ array_push($list_cat_id, $id);
+ }
+ $ref_level = $level;
+ }
+
+ $level = 1;
+ compute_branch_cat_data($cats, $list_cat_id, $level, $ref_level);
+}
+
+/**
+ * get computed array of categories
+ *
+ * @param array userdata
+ * @param int filter_days number of recent days to filter on or null
+ * @return array
+ */
+function get_computed_categories($userdata, $filter_days=null)
+{
+ $query = 'SELECT c.id cat_id, global_rank';
+ // Count by date_available to avoid count null
+ $query .= ',
+ MAX(date_available) date_last, COUNT(date_available) nb_images
+FROM '.CATEGORIES_TABLE.' as c
+ LEFT JOIN '.IMAGE_CATEGORY_TABLE.' AS ic ON ic.category_id = c.id
+ LEFT JOIN '.IMAGES_TABLE.' AS i
+ ON ic.image_id = i.id
+ AND i.level<='.$userdata['level'];
+
+ if ( isset($filter_days) )
+ {
+ $query .= ' AND i.date_available > SUBDATE(CURRENT_DATE,INTERVAL '.$filter_days.' DAY)';
+ }
+
+ if ( !empty($userdata['forbidden_categories']) )
+ {
+ $query.= '
+ WHERE c.id NOT IN ('.$userdata['forbidden_categories'].')';
+ }
+
+ $query.= '
+ GROUP BY c.id';
+
+ $result = pwg_query($query);
+
+ $cats = array();
+ while ($row = mysql_fetch_assoc($result))
+ {
+ $row['user_id'] = $userdata['id'];
+ $row['count_categories'] = 0;
+ $row['count_images'] = (int)$row['nb_images'];
+ $row['max_date_last'] = $row['date_last'];
+
+ $cats += array($row['cat_id'] => $row);
+ }
+ usort($cats, 'global_rank_compare');
+
+ compute_categories_data($cats);
+
+ if ( isset($filter_days) )
+ {
+ $cat_tmp = $cats;
+ $cats = array();
+
+ foreach ($cat_tmp as $category)
+ {
+ if (!empty($category['max_date_last']))
+ {
+ // Re-init counters
+ $category['count_categories'] = 0;
+ $category['count_images'] = (int)$category['nb_images'];
+ // Keep category
+ $cats[$category['cat_id']] = $category;
+ }
+ }
+ // Compute a second time
+ compute_categories_data($cats);
+ }
+ return $cats;
+}
+
+/**
+ * update data of user_cache_categories
+ *
+ * @param array userdata
+ * @return null
+ */
+function update_user_cache_categories($userdata)
+{
+ // delete user cache
+ $query = '
+DELETE FROM '.USER_CACHE_CATEGORIES_TABLE.'
+ WHERE user_id = '.$userdata['id'].'
+;';
+ pwg_query($query);
+
+ $cats = get_computed_categories($userdata, null);
+
+ include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
+ mass_inserts
+ (
+ USER_CACHE_CATEGORIES_TABLE,
+ array
+ (
+ 'user_id', 'cat_id',
+ 'date_last', 'max_date_last', 'nb_images', 'count_images', 'count_categories'
+ ),
+ $cats
+ );
+}
+
+/**
+ * returns the username corresponding to the given user identifier if exists
+ *
+ * @param int user_id
+ * @return mixed
+ */
+function get_username($user_id)
+{
+ global $conf;
+
+ $query = '
+SELECT '.$conf['user_fields']['username'].'
+ FROM '.USERS_TABLE.'
+ WHERE '.$conf['user_fields']['id'].' = '.intval($user_id).'
+;';
+ $result = pwg_query($query);
+ if (mysql_num_rows($result) > 0)
+ {
+ list($username) = mysql_fetch_row($result);
+ }
+ else
+ {
+ return false;
+ }
+
+ return $username;
+}
+
+/**
+ * returns user identifier thanks to his name, false if not found
+ *
+ * @param string username
+ * @param int user identifier
+ */
+function get_userid($username)
+{
+ global $conf;
+
+ $username = mysql_escape_string($username);
+
+ $query = '
+SELECT '.$conf['user_fields']['id'].'
+ FROM '.USERS_TABLE.'
+ WHERE '.$conf['user_fields']['username'].' = \''.$username.'\'
+;';
+ $result = pwg_query($query);
+
+ if (mysql_num_rows($result) == 0)
+ {
+ return false;
+ }
+ else
+ {
+ list($user_id) = mysql_fetch_row($result);
+ return $user_id;
+ }
+}
+
+/**
+ * search an available feed_id
+ *
+ * @return string feed identifier
+ */
+function find_available_feed_id()
+{
+ while (true)
+ {
+ $key = generate_key(50);
+ $query = '
+SELECT COUNT(*)
+ FROM '.USER_FEED_TABLE.'
+ WHERE id = \''.$key.'\'
+;';
+ list($count) = mysql_fetch_row(pwg_query($query));
+ if (0 == $count)
+ {
+ return $key;
+ }
+ }
+}
+
+/*
+ * Returns a array with default user value
+ *
+ * @param convert_str allows to convert string value if necessary
+ */
+function get_default_user_info($convert_str = true)
+{
+ global $page, $conf;
+
+ if (!isset($page['cache_default_user']))
+ {
+ $query = 'select * from '.USER_INFOS_TABLE.
+ ' where user_id = '.$conf['default_user_id'].';';
+
+ $result = pwg_query($query);
+ $page['cache_default_user'] = mysql_fetch_assoc($result);
+
+ if ($page['cache_default_user'] !== false)
+ {
+ unset($page['cache_default_user']['user_id']);
+ unset($page['cache_default_user']['status']);
+ unset($page['cache_default_user']['registration_date']);
+ }
+ }
+
+ if (is_array($page['cache_default_user']) and $convert_str)
+ {
+ $default_user = array();
+ foreach ($page['cache_default_user'] as $name => $value)
+ {
+ // If the field is true or false, the variable is transformed into a
+ // boolean value.
+ if ($value == 'true' or $value == 'false')
+ {
+ $default_user[$name] = get_boolean($value);
+ }
+ else
+ {
+ $default_user[$name] = $value;
+ }
+ }
+ return $default_user;
+ }
+ else
+ {
+ return $page['cache_default_user'];
+ }
+}
+
+/*
+ * Returns a default user value
+ *
+ * @param value_name: name of value
+ * @param sos_value: value used if don't exist value
+ */
+function get_default_user_value($value_name, $sos_value)
+{
+ $default_user = get_default_user_info(true);
+ if ($default_user === false or !isset($default_user[$value_name]))
+ {
+ return $sos_value;
+ }
+ else
+ {
+ return $default_user[$value_name];
+ }
+}
+
+/*
+ * Returns the default template value
+ *
+ */
+function get_default_template()
+{
+ return get_default_user_value('template', PHPWG_DEFAULT_TEMPLATE);
+}
+
+/*
+ * Returns the default language value
+ *
+ */
+function get_default_language()
+{
+ return get_default_user_value('language', PHPWG_DEFAULT_LANGUAGE);
+}
+
+/**
+ * add user informations based on default values
+ *
+ * @param int user_id / array of user_if
+ * @param array of values used to override default user values
+ */
+function create_user_infos($arg_id, $override_values = null)
+{
+ global $conf;
+
+ if (is_array($arg_id))
+ {
+ $user_ids = $arg_id;
+ }
+ else
+ {
+ $user_ids = array();
+ if (is_numeric($arg_id))
+ {
+ $user_ids[] = $arg_id;
+ }
+ }
+
+ if (!empty($user_ids))
+ {
+ $inserts = array();
+ list($dbnow) = mysql_fetch_row(pwg_query('SELECT NOW();'));
+
+ $default_user = get_default_user_info(false);
+ if ($default_user === false)
+ {
+ // Default on structure are used
+ $default_user = array();
+ }
+
+ if (!is_null($override_values))
+ {
+ $default_user = array_merge($default_user, $override_values);
+ }
+
+ foreach ($user_ids as $user_id)
+ {
+ $level= isset($default_user['level']) ? $default_user['level'] : 0;
+ if ($user_id == $conf['webmaster_id'])
+ {
+ $status = 'webmaster';
+ $level = max( $conf['available_permission_levels'] );
+ }
+ else if (($user_id == $conf['guest_id']) or
+ ($user_id == $conf['default_user_id']))
+ {
+ $status = 'guest';
+ }
+ else
+ {
+ $status = 'normal';
+ }
+
+ $insert = array_merge(
+ $default_user,
+ array(
+ 'user_id' => $user_id,
+ 'status' => $status,
+ 'registration_date' => $dbnow,
+ 'level' => $level
+ ));
+
+ array_push($inserts, $insert);
+ }
+
+ include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
+ mass_inserts(USER_INFOS_TABLE, array_keys($inserts[0]), $inserts);
+
+ }
+}
+
+/**
+ * returns the groupname corresponding to the given group identifier if
+ * exists
+ *
+ * @param int group_id
+ * @return mixed
+ */
+function get_groupname($group_id)
+{
+ $query = '
+SELECT name
+ FROM '.GROUPS_TABLE.'
+ WHERE id = '.intval($group_id).'
+;';
+ $result = pwg_query($query);
+ if (mysql_num_rows($result) > 0)
+ {
+ list($groupname) = mysql_fetch_row($result);
+ }
+ else
+ {
+ return false;
+ }
+
+ return $groupname;
+}
+
+
+/**
+ * returns the auto login key or false on error
+ * @param int user_id
+ * @param string [out] username
+*/
+function calculate_auto_login_key($user_id, &$username)
+{
+ global $conf;
+ $query = '
+SELECT '.$conf['user_fields']['username'].' AS username
+ , '.$conf['user_fields']['password'].' AS password
+FROM '.USERS_TABLE.'
+WHERE '.$conf['user_fields']['id'].' = '.$user_id;
+ $result = pwg_query($query);
+ if (mysql_num_rows($result) > 0)
+ {
+ $row = mysql_fetch_assoc($result);
+ $username = $row['username'];
+ $data = $row['username'].$row['password'];
+ $key = base64_encode(
+ pack('H*', sha1($data))
+ .hash_hmac('md5', $data, $conf['secret_key'],true)
+ );
+ return $key;
+ }
+ return false;
+}
+
+/*
+ * Performs all required actions for user login
+ * @param int user_id
+ * @param bool remember_me
+ * @return void
+*/
+function log_user($user_id, $remember_me)
+{
+ global $conf, $user;
+
+ if ($remember_me and $conf['authorize_remembering'])
+ {
+ $key = calculate_auto_login_key($user_id, $username);
+ if ($key!==false)
+ {
+ $cookie = array('id' => (int)$user_id, 'key' => $key);
+ setcookie($conf['remember_me_name'],
+ serialize($cookie),
+ time()+$conf['remember_me_length'],
+ cookie_path()
+ );
+ }
+ }
+ else
+ { // make sure we clean any remember me ...
+ setcookie($conf['remember_me_name'], '', 0, cookie_path());
+ }
+ if ( session_id()!="" )
+ { // we regenerate the session for security reasons
+ // see http://www.acros.si/papers/session_fixation.pdf
+ session_regenerate_id();
+ }
+ else
+ {
+ session_start();
+ }
+ $_SESSION['pwg_uid'] = (int)$user_id;
+
+ $user['id'] = $_SESSION['pwg_uid'];
+}
+
+/*
+ * Performs auto-connexion when cookie remember_me exists
+ * @return true/false
+*/
+function auto_login() {
+ global $conf;
+
+ if ( isset( $_COOKIE[$conf['remember_me_name']] ) )
+ {
+ $cookie = unserialize(stripslashes($_COOKIE[$conf['remember_me_name']]));
+ if ($cookie!==false and is_numeric(@$cookie['id']) )
+ {
+ $key = calculate_auto_login_key( $cookie['id'], $username );
+ if ($key!==false and $key===$cookie['key'])
+ {
+ log_user($cookie['id'], true);
+ trigger_action('login_success', $username);
+ return true;
+ }
+ }
+ setcookie($conf['remember_me_name'], '', 0, cookie_path());
+ }
+ return false;
+}
+
+/**
+ * Tries to login a user given username and password (must be MySql escaped)
+ * return true on success
+ */
+function try_log_user($username, $password, $remember_me)
+{
+ global $conf;
+ // retrieving the encrypted password of the login submitted
+ $query = '
+SELECT '.$conf['user_fields']['id'].' AS id,
+ '.$conf['user_fields']['password'].' AS password
+ FROM '.USERS_TABLE.'
+ WHERE '.$conf['user_fields']['username'].' = \''.$username.'\'
+;';
+ $row = mysql_fetch_assoc(pwg_query($query));
+ if ($row['password'] == $conf['pass_convert']($password))
+ {
+ log_user($row['id'], $remember_me);
+ trigger_action('login_success', $username);
+ return true;
+ }
+ trigger_action('login_failure', $username);
+ return false;
+}
+
+/*
+ * Return user status used in this library
+ * @return string
+*/
+function get_user_status($user_status)
+{
+ global $user;
+
+ if (empty($user_status))
+ {
+ if (isset($user['status']))
+ {
+ $user_status = $user['status'];
+ }
+ else
+ {
+ // swicth to default value
+ $user_status = '';
+ }
+ }
+ return $user_status;
+}
+
+/*
+ * Return access_type definition of user
+ * Test does with user status
+ * @return bool
+*/
+function get_access_type_status($user_status='')
+{
+ global $conf;
+
+ switch (get_user_status($user_status))
+ {
+ case 'guest':
+ {
+ $access_type_status =
+ ($conf['guest_access'] ? ACCESS_GUEST : ACCESS_FREE);
+ break;
+ }
+ case 'generic':
+ {
+ $access_type_status = ACCESS_GUEST;
+ break;
+ }
+ case 'normal':
+ {
+ $access_type_status = ACCESS_CLASSIC;
+ break;
+ }
+ case 'admin':
+ {
+ $access_type_status = ACCESS_ADMINISTRATOR;
+ break;
+ }
+ case 'webmaster':
+ {
+ $access_type_status = ACCESS_WEBMASTER;
+ break;
+ }
+ default:
+ {
+ $access_type_status = ACCESS_FREE;
+ break;
+ }
+ }
+
+ return $access_type_status;
+}
+
+/*
+ * Return if user have access to access_type definition
+ * Test does with user status
+ * @return bool
+*/
+function is_autorize_status($access_type, $user_status = '')
+{
+ return (get_access_type_status($user_status) >= $access_type);
+}
+
+/*
+ * Check if user have access to access_type definition
+ * Stop action if there are not access
+ * Test does with user status
+ * @return none
+*/
+function check_status($access_type, $user_status = '')
+{
+ if (!is_autorize_status($access_type, $user_status))
+ {
+ access_denied();
+ }
+}
+
+/*
+ * Return if user is generic
+ * @return bool
+*/
+ function is_generic($user_status = '')
+{
+ return get_user_status($user_status) == 'generic';
+}
+
+/*
+ * Return if user is only a guest
+ * @return bool
+*/
+ function is_a_guest($user_status = '')
+{
+ return get_user_status($user_status) == 'guest';
+}
+
+/*
+ * Return if user is, at least, a classic user
+ * @return bool
+*/
+ function is_classic_user($user_status = '')
+{
+ return is_autorize_status(ACCESS_CLASSIC, $user_status);
+}
+
+/*
+ * Return if user is, at least, an administrator
+ * @return bool
+*/
+ function is_admin($user_status = '')
+{
+ return is_autorize_status(ACCESS_ADMINISTRATOR, $user_status);
+}
+
+/*
+ * Return if current user is an adviser
+ * @return bool
+*/
+function is_adviser()
+{
+ global $user;
+
+ return ($user['adviser'] == 'true');
+}
+
+/*
+ * Return mail address as display text
+ * @return string
+*/
+function get_email_address_as_display_text($email_address)
+{
+ global $conf;
+
+ if (!isset($email_address) or (trim($email_address) == ''))
+ {
+ return '';
+ }
+ else
+ {
+ if (defined('IN_ADMIN') and is_adviser())
+ {
+ return 'adviser.mode@'.$_SERVER['SERVER_NAME'];
+ }
+ else
+ {
+ return $email_address;
+ }
+ }
+}
+
+/*
+ * Compute sql where condition with restrict and filter data. "FandF" means
+ * Forbidden and Filters.
+ *
+ * @param array condition_fields: read function body
+ * @param string prefix_condition: prefixes sql if condition is not empty
+ * @param boolean force_one_condition: use at least "1 = 1"
+ *
+ * @return string sql where/conditions
+ */
+function get_sql_condition_FandF(
+ $condition_fields,
+ $prefix_condition = null,
+ $force_one_condition = false
+ )
+{
+ global $user, $filter;
+
+ $sql_list = array();
+
+ foreach ($condition_fields as $condition => $field_name)
+ {
+ switch($condition)
+ {
+ case 'forbidden_categories':
+ {
+ if (!empty($user['forbidden_categories']))
+ {
+ $sql_list[] =
+ $field_name.' NOT IN ('.$user['forbidden_categories'].')';
+ }
+ break;
+ }
+ case 'visible_categories':
+ {
+ if (!empty($filter['visible_categories']))
+ {
+ $sql_list[] =
+ $field_name.' IN ('.$filter['visible_categories'].')';
+ }
+ break;
+ }
+ case 'visible_images':
+ if (!empty($filter['visible_images']))
+ {
+ $sql_list[] =
+ $field_name.' IN ('.$filter['visible_images'].')';
+ }
+ // note there is no break - visible include forbidden
+ case 'forbidden_images':
+ if (
+ !empty($user['image_access_list'])
+ or $user['image_access_type']!='NOT IN'
+ )
+ {
+ $table_prefix=null;
+ if ($field_name=='id')
+ {
+ $table_prefix = '';
+ }
+ elseif ($field_name=='i.id')
+ {
+ $table_prefix = 'i.';
+ }
+ if ( isset($table_prefix) )
+ {
+ $sql_list[]=$table_prefix.'level<='.$user['level'];
+ }
+ else
+ {
+ $sql_list[]=$field_name.' '.$user['image_access_type']
+ .' ('.$user['image_access_list'].')';
+ }
+ }
+ break;
+ default:
+ {
+ die('Unknow condition');
+ break;
+ }
+ }
+ }
+
+ if (count($sql_list) > 0)
+ {
+ $sql = '('.implode(' AND ', $sql_list).')';
+ }
+ else
+ {
+ if ($force_one_condition)
+ {
+ $sql = '1 = 1';
+ }
+ else
+ {
+ $sql = '';
+ }
+ }
+
+ if (isset($prefix_condition) and !empty($sql))
+ {
+ $sql = $prefix_condition.' '.$sql;
+ }
+
+ return $sql;
+}
+
+?> \ No newline at end of file
diff --git a/BSF/include/functions_xml.inc.php b/BSF/include/functions_xml.inc.php
new file mode 100644
index 000000000..1a849d434
--- /dev/null
+++ b/BSF/include/functions_xml.inc.php
@@ -0,0 +1,141 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+define( 'ATT_REG', '\w+' );
+define( 'VAL_REG', '[^"]*' );
+
+//------------------------------------------------------------------- functions
+// getContent returns the content of a tag
+//
+// example : getContent( "<name>Joe</name>" ) returns "Joe"
+//
+// It also works with strings containing themself sub-tags :
+// <perso><name>Jean</name><firstname>Billie</fisrtname></perso> ->
+// <name>Jean</name><firstname>Billie</firstname>
+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).'<br /><br />';
+ $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( "<table><tr>XXX</tr><tr>YYY</tr></table>", "tr" )
+// returns "<tr>XXX</tr>"
+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( "<table><tr>XXX</tr><tr>YYY</tr></table>", "tr" )
+// returns an array with :
+// $array[0] equals "<tr>XXX</tr>"
+// $array[1] equals "<tr>YYY</tr>"
+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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+// Recursive call
+$url = '../';
+header( 'Request-URI: '.$url );
+header( 'Content-Location: '.$url );
+header( 'Location: '.$url );
+exit();
+?>
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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+/**
+ * This file is included by the main page to show the menu bar
+ *
+ */
+$template->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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+//
+// Start output of page
+//
+$template->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',
+ '<meta name="robots" content="'
+ .implode(',', array_keys($page['meta_robots']))
+ .'">'
+ );
+}
+
+// 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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+$template->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 @@
+<?php
+// http://www.php.net/manual/en/function.array-intersect-key.php
+// PHP 5 >= 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 @@
+<?php
+//php 5
+function file_put_contents($filename, $data)
+{
+ $fp = fopen($filename, 'w');
+ if ($fp)
+ {
+ $ret = fwrite($fp, $data);
+ fclose($fp);
+ return $ret;
+ }
+ return false;
+}
+?> \ 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 @@
+<?php
+//(hash) - enabled by default as of PHP 5.1.2
+function hash_hmac($algo, $data, $key, $raw_output=false)
+{
+ /* md5 and sha1 only */
+ $algo=strtolower($algo);
+ $p=array('md5'=>'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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+// Recursive call
+$url = '../';
+header( 'Request-URI: '.$url );
+header( 'Content-Location: '.$url );
+header( 'Location: '.$url );
+exit();
+?>
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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+// http://www.php.net/manual/fr/function.preg-last-error.php
+// PHP 5 >= 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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+/**
+ * This file is included by the picture page to manage user comments
+ *
+ */
+
+// the picture is commentable if it belongs at least to one category which
+// is commentable
+$page['show_comments'] = false;
+foreach ($related_categories as $category)
+{
+ if ($category['commentable'] == 'true')
+ {
+ $page['show_comments'] = true;
+ break;
+ }
+}
+
+if ( $page['show_comments'] and isset( $_POST['content'] ) )
+{
+ if ( is_a_guest() and !$conf['comments_forall'] )
+ {
+ die ('Session expired');
+ }
+
+ $comm = array(
+ 'author' => 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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+/**
+ * This file is included by the picture page to manage picture metadata
+ *
+ */
+
+include_once(PHPWG_ROOT_PATH.'/include/functions_metadata.inc.php');
+if (($conf['show_exif']) and (function_exists('read_exif_data')))
+{
+ if ($exif = @read_exif_data($picture['current']['image_path']))
+ {
+ $exif = trigger_event('format_exif_data', $exif, $picture['current'] );
+
+ $tpl_meta = array(
+ 'TITLE' => '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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+/**
+ * This file is included by the picture page to manage rates
+ *
+ */
+
+if ($conf['rate'])
+{
+ if ( NULL != $picture['current']['average_rate'] )
+ {
+ $query = '
+SELECT COUNT(rate) AS count
+ , ROUND(AVG(rate),2) AS average
+ , ROUND(STD(rate),2) AS std
+ FROM '.RATE_TABLE.'
+ WHERE element_id = '.$picture['current']['id'].'
+;';
+ $row = mysql_fetch_array(pwg_query($query));
+ }
+ else
+ { // avg rate null -> 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 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+/**
+ * This included page checks section related parameter and provides
+ * following informations:
+ *
+ * - $page['title']
+ *
+ * - $page['items']: ordered list of items to display
+ *
+ */
+
+// "index.php?/category/12-foo/start-24" or
+// "index.php/category/12-foo/start-24"
+// must return :
+//
+// array(
+// 'section' => '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' => '<a href="'.duplicate_index_url(array('start'=>0)).'">'
+ .l10n('search_result').'</a>',
+ )
+ );
+ }
+// +-----------------------------------------------------------------------+
+// | 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' => '<a href="'.duplicate_index_url(array('start'=>0)).'">'
+ .l10n('recent_pics_cat').'</a>',
+ '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' => '<a href="'.duplicate_index_url(array('start'=>0)).'">'
+ .$conf['top_number'].' '.l10n('most_visited_cat').'</a>',
+ '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' => '<a href="'.duplicate_index_url(array('start'=>0)).'">'
+ .$conf['top_number'].' '.l10n('best_rated_cat').'</a>',
+ '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' => '<a href="'.duplicate_index_url(array('start'=>0)).'">'
+ .l10n('random_cat').'</a>',
+ '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 <labels> 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.<name>. (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 <monte at ohrt dot com>
+ Andrei Zmievski <andrei@php.net>
+
+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 @@
+<?php
+
+/**
+ * Config_File class.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * @link http://smarty.php.net/
+ * @version 2.6.19
+ * @copyright Copyright: 2001-2005 New Digital Group, Inc.
+ * @author Andrei Zmievski <andrei@php.net>
+ * @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 @@
+<?php
+
+/**
+ * Project: Smarty: the PHP compiling template engine
+ * File: Smarty.class.php
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * For questions, help, comments, discussion, etc., please join the
+ * Smarty mailing list. Send a blank e-mail to
+ * smarty-general-subscribe@lists.php.net
+ *
+ * @link http://smarty.php.net/
+ * @copyright 2001-2005 New Digital Group, Inc.
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @author Andrei Zmievski <andrei@php.net>
+ * @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.
+ * <ul>
+ * <li>NONE => no debugging control allowed</li>
+ * <li>URL => enable debugging when SMARTY_DEBUG is found in the URL.</li>
+ * </ul>
+ * @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.
+ * <ul>
+ * <li>0 = no caching</li>
+ * <li>1 = use class cache_lifetime value</li>
+ * <li>2 = use cache_lifetime in cache file</li>
+ * </ul>
+ * @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.
+ * <ul>
+ * <li>0 = always regenerate cache</li>
+ * <li>-1 = never expires</li>
+ * </ul>
+ *
+ * @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 "<?php ... ?>" tags in templates.
+ * possible values:
+ * <ul>
+ * <li>SMARTY_PHP_PASSTHRU -> print tags as plain text</li>
+ * <li>SMARTY_PHP_QUOTE -> escape tags as entities</li>
+ * <li>SMARTY_PHP_REMOVE -> remove php tags</li>
+ * <li>SMARTY_PHP_ALLOW -> execute php tags</li>
+ * </ul>
+ *
+ * @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: <code>array('escape:"htmlall"');</code>
+ *
+ * @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 @@
+<?php
+
+/**
+ * Project: Smarty: the PHP compiling template engine
+ * File: Smarty_Compiler.class.php
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * @link http://smarty.php.net/
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @author Andrei Zmievski <andrei@php.net>
+ * @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.'%%%', '<?php echo \''.str_replace("'", "\'", $sp_match[1][$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', '<?php echo \'\\1\'?>'."\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] = "<?php echo '" . strtr($text_blocks[$j], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>";
+ 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('<?', "<?php echo '<?' ?>\n", $compiled_content);
+ $compiled_content = preg_replace("~(?<!')language\s*=\s*[\"\']?\s*php\s*[\"\']?~", "<?php echo 'language=php' ?>\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 = "<?php \$this->_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 = "<?php /* Smarty version ".$this->_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 = "<?php require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');\nsmarty_core_load_plugins($_plugins_params, \$this); ?>\n";
+ $template_header .= $plugins_code;
+ $this->_plugin_info = array();
+ $this->_plugins_code = $plugins_code;
+ }
+
+ if ($this->_init_smarty_vars) {
+ $template_header .= "<?php require_once(SMARTY_CORE_DIR . 'core.assign_smarty_interface.php');\nsmarty_core_assign_smarty_interface(null, \$this); ?>\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 "<?php echo $_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 '<?php else: ?>';
+
+ 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 '<?php endif; ?>';
+
+ 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 "<?php endfor; else: ?>";
+ break;
+
+ case '/section':
+ $_open_tag = $this->_pop_tag('section');
+ if ($_open_tag == 'sectionelse')
+ return "<?php endif; ?>";
+ else
+ return "<?php endfor; endif; ?>";
+
+ case 'foreach':
+ $this->_push_tag('foreach');
+ return $this->_compile_foreach_start($tag_args);
+ break;
+
+ case 'foreachelse':
+ $this->_push_tag('foreachelse');
+ return "<?php endforeach; else: ?>";
+
+ case '/foreach':
+ $_open_tag = $this->_pop_tag('foreach');
+ if ($_open_tag == 'foreachelse')
+ return "<?php endif; unset(\$_from); ?>";
+ else
+ return "<?php endforeach; endif; unset(\$_from); ?>";
+ 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 "<?php echo '" . strtr($block[2], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>" . $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 '<?php ' . $block[3] .' ?>';
+ }
+ 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 = '<?php ' . $this->_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 = '<?php ' . $this->_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 = '<?php $_block_content = ob_get_contents(); ob_end_clean(); ';
+ $_out_tag_text = $this->_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 = '<?php ' . $_cacheable_state . $_cache_attrs . 'echo ' . $output . ';'
+ . $this->_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 '<?php ' . $prefix . $output . $postfix . "?>" . $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 "<?php require_once(SMARTY_CORE_DIR . 'core.run_insert_handler.php');\necho smarty_core_run_insert_handler($_params, \$this); ?>" . $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 = '<?php ';
+
+ if (isset($assign_var)) {
+ $output .= "ob_start();\n";
+ }
+
+ $output .=
+ "\$_smarty_tpl_vars = \$this->_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 "<?php require_once(SMARTY_CORE_DIR . 'core.smarty_include_php.php');\nsmarty_core_smarty_include_php($_params, \$this); ?>" . $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 = '<?php ';
+ $section_name = $attrs['name'];
+ if (empty($section_name)) {
+ $this->_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 = '<?php ';
+ $output .= "\$_from = $from; if (!is_array(\$_from) && !is_object(\$_from)) { settype(\$_from, 'array'); }";
+ if (isset($name)) {
+ $foreach_props = "\$this->_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 = "<?php ob_start(); ?>";
+ $this->_capture_stack[] = array($buffer, $assign, $append);
+ } else {
+ list($buffer, $assign, $append) = array_pop($this->_capture_stack);
+ $output = "<?php \$this->_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 '<?php elseif ('.implode(' ', $tokens).'): ?>';
+ else
+ return '<?php if ('.implode(' ', $tokens).'): ?>';
+ }
+
+
+ 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('~(?:\`(?<!\\\\)\$' . $this->_dvar_guts_regexp . '(?:' . $this->_obj_ext_regexp . ')*\`)|(?:(?<!\\\\)\$\w+(\[[a-zA-Z0-9]+\])*)~', $var_expr, $_match)) {
+ $_match = $_match[0];
+ $_replace = array();
+ foreach($_match as $_var) {
+ $_replace[$_var] = '".(' . $this->_parse_var(str_replace('`','',$_var)) . ')."';
+ }
+ $var_expr = strtr($var_expr, $_replace);
+ $_return = preg_replace('~\.""|(?<!\\\\)""\.~', '', $var_expr);
+ } else {
+ $_return = $var_expr;
+ }
+ // replace double quoted literal string with single quotes
+ $_return = preg_replace('~^"([\s\w]+)"$~',"'\\1'",$_return);
+ return $_return;
+ }
+
+ /**
+ * parse variable expression into PHP code
+ *
+ * @param string $var_expr
+ * @param string $output
+ * @return string
+ */
+ function _parse_var($var_expr)
+ {
+ $_has_math = false;
+ $_math_vars = preg_split('~('.$this->_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}
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+<head>
+ <title>Smarty Debug Console</title>
+{literal}
+<style type="text/css">
+/* <![CDATA[ */
+body, h1, h2, td, th, p {
+ font-family: sans-serif;
+ font-weight: normal;
+ font-size: 0.9em;
+ margin: 1px;
+ padding: 0;
+}
+
+h1 {
+ margin: 0;
+ text-align: left;
+ padding: 2px;
+ background-color: #f0c040;
+ color: black;
+ font-weight: bold;
+ font-size: 1.2em;
+ }
+
+h2 {
+ background-color: #9B410E;
+ color: white;
+ text-align: left;
+ font-weight: bold;
+ padding: 2px;
+ border-top: 1px solid black;
+}
+
+body {
+ background: black;
+}
+
+p, table, div {
+ background: #f0ead8;
+}
+
+p {
+ margin: 0;
+ font-style: italic;
+ text-align: center;
+}
+
+table {
+ width: 100%;
+}
+
+th, td {
+ font-family: monospace;
+ vertical-align: top;
+ text-align: left;
+ width: 50%;
+}
+
+td {
+ color: green;
+}
+
+.odd {
+ background-color: #eeeeee;
+}
+
+.even {
+ background-color: #fafafa;
+}
+
+.exectime {
+ font-size: 0.8em;
+ font-style: italic;
+}
+
+#table_assigned_vars th {
+ color: blue;
+}
+
+#table_config_vars th {
+ color: maroon;
+}
+/* ]]> */
+</style>
+{/literal}
+</head>
+<body>
+
+<h1>Smarty Debug Console</h1>
+
+<h2>included templates &amp; config files (load time in seconds)</h2>
+
+<div>
+{section name=templates loop=$_debug_tpls}
+ {section name=indent loop=$_debug_tpls[templates].depth}&nbsp;&nbsp;&nbsp;{/section}
+ <font color={if $_debug_tpls[templates].type eq "template"}brown{elseif $_debug_tpls[templates].type eq "insert"}black{else}green{/if}>
+ {$_debug_tpls[templates].filename|escape:html}</font>
+ {if isset($_debug_tpls[templates].exec_time)}
+ <span class="exectime">
+ ({$_debug_tpls[templates].exec_time|string_format:"%.5f"})
+ {if %templates.index% eq 0}(total){/if}
+ </span>
+ {/if}
+ <br />
+{sectionelse}
+ <p>no templates included</p>
+{/section}
+</div>
+
+<h2>assigned template variables</h2>
+
+<table id="table_assigned_vars">
+ {section name=vars loop=$_debug_keys}
+ <tr class="{cycle values="odd,even"}">
+ <th>{ldelim}${$_debug_keys[vars]|escape:'html'}{rdelim}</th>
+ <td>{$_debug_vals[vars]|@debug_print_var}</td></tr>
+ {sectionelse}
+ <tr><td><p>no template variables assigned</p></td></tr>
+ {/section}
+</table>
+
+<h2>assigned config file variables (outer template scope)</h2>
+
+<table id="table_config_vars">
+ {section name=config_vars loop=$_debug_config_keys}
+ <tr class="{cycle values="odd,even"}">
+ <th>{ldelim}#{$_debug_config_keys[config_vars]|escape:'html'}#{rdelim}</th>
+ <td>{$_debug_config_vals[config_vars]|@debug_print_var}</td></tr>
+ {sectionelse}
+ <tr><td><p>no config vars assigned</p></td></tr>
+ {/section}
+</table>
+</body>
+</html>
+{/capture}
+{if isset($_smarty_debug_output) and $_smarty_debug_output eq "html"}
+ {$debug_output}
+{else}
+<script type="text/javascript">
+// <![CDATA[
+ if ( self.name == '' ) {ldelim}
+ var title = 'Console';
+ {rdelim}
+ else {ldelim}
+ var title = 'Console_' + self.name;
+ {rdelim}
+ _smarty_console = window.open("",title.value,"width=680,height=600,resizable,scrollbars=yes");
+ _smarty_console.document.write('{$debug_output|escape:'javascript'}');
+ _smarty_console.document.close();
+// ]]>
+</script>
+{/if} \ No newline at end of file
diff --git a/BSF/include/smarty/libs/internals/core.assemble_plugin_filepath.php b/BSF/include/smarty/libs/internals/core.assemble_plugin_filepath.php
new file mode 100644
index 000000000..690d3ddbc
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.assemble_plugin_filepath.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * assemble filepath of requested plugin
+ *
+ * @param string $type
+ * @param string $name
+ * @return string|false
+ */
+function smarty_core_assemble_plugin_filepath($params, &$smarty)
+{
+ static $_filepaths_cache = array();
+
+ $_plugin_filename = $params['type'] . '.' . $params['name'] . '.php';
+ if (isset($_filepaths_cache[$_plugin_filename])) {
+ return $_filepaths_cache[$_plugin_filename];
+ }
+ $_return = false;
+
+ foreach ((array)$smarty->plugins_dir as $_plugin_dir) {
+
+ $_plugin_filepath = $_plugin_dir . DIRECTORY_SEPARATOR . $_plugin_filename;
+
+ // see if path is relative
+ if (!preg_match("/^([\/\\\\]|[a-zA-Z]:[\/\\\\])/", $_plugin_dir)) {
+ $_relative_paths[] = $_plugin_dir;
+ // relative path, see if it is in the SMARTY_DIR
+ if (@is_readable(SMARTY_DIR . $_plugin_filepath)) {
+ $_return = SMARTY_DIR . $_plugin_filepath;
+ break;
+ }
+ }
+ // try relative to cwd (or absolute)
+ if (@is_readable($_plugin_filepath)) {
+ $_return = $_plugin_filepath;
+ break;
+ }
+ }
+
+ if($_return === false) {
+ // still not found, try PHP include_path
+ if(isset($_relative_paths)) {
+ foreach ((array)$_relative_paths as $_plugin_dir) {
+
+ $_plugin_filepath = $_plugin_dir . DIRECTORY_SEPARATOR . $_plugin_filename;
+
+ $_params = array('file_path' => $_plugin_filepath);
+ require_once(SMARTY_CORE_DIR . 'core.get_include_path.php');
+ if(smarty_core_get_include_path($_params, $smarty)) {
+ $_return = $_params['new_file_path'];
+ break;
+ }
+ }
+ }
+ }
+ $_filepaths_cache[$_plugin_filename] = $_return;
+ return $_return;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/internals/core.assign_smarty_interface.php b/BSF/include/smarty/libs/internals/core.assign_smarty_interface.php
new file mode 100644
index 000000000..7e65a73ec
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.assign_smarty_interface.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Smarty assign_smarty_interface core plugin
+ *
+ * Type: core<br>
+ * Name: assign_smarty_interface<br>
+ * Purpose: assign the $smarty interface variable
+ * @param array Format: null
+ * @param Smarty
+ */
+function smarty_core_assign_smarty_interface($params, &$smarty)
+{
+ if (isset($smarty->_smarty_vars) && isset($smarty->_smarty_vars['request'])) {
+ return;
+ }
+
+ $_globals_map = array('g' => 'HTTP_GET_VARS',
+ 'p' => 'HTTP_POST_VARS',
+ 'c' => 'HTTP_COOKIE_VARS',
+ 's' => 'HTTP_SERVER_VARS',
+ 'e' => 'HTTP_ENV_VARS');
+
+ $_smarty_vars_request = array();
+
+ foreach (preg_split('!!', strtolower($smarty->request_vars_order)) as $_c) {
+ if (isset($_globals_map[$_c])) {
+ $_smarty_vars_request = array_merge($_smarty_vars_request, $GLOBALS[$_globals_map[$_c]]);
+ }
+ }
+ $_smarty_vars_request = @array_merge($_smarty_vars_request, $GLOBALS['HTTP_SESSION_VARS']);
+
+ $smarty->_smarty_vars['request'] = $_smarty_vars_request;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/internals/core.create_dir_structure.php b/BSF/include/smarty/libs/internals/core.create_dir_structure.php
new file mode 100644
index 000000000..3eecc4972
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.create_dir_structure.php
@@ -0,0 +1,79 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * create full directory structure
+ *
+ * @param string $dir
+ */
+
+// $dir
+
+function smarty_core_create_dir_structure($params, &$smarty)
+{
+ if (!file_exists($params['dir'])) {
+ $_open_basedir_ini = ini_get('open_basedir');
+
+ if (DIRECTORY_SEPARATOR=='/') {
+ /* unix-style paths */
+ $_dir = $params['dir'];
+ $_dir_parts = preg_split('!/+!', $_dir, -1, PREG_SPLIT_NO_EMPTY);
+ $_new_dir = (substr($_dir, 0, 1)=='/') ? '/' : getcwd().'/';
+ if($_use_open_basedir = !empty($_open_basedir_ini)) {
+ $_open_basedirs = explode(':', $_open_basedir_ini);
+ }
+
+ } else {
+ /* other-style paths */
+ $_dir = str_replace('\\','/', $params['dir']);
+ $_dir_parts = preg_split('!/+!', $_dir, -1, PREG_SPLIT_NO_EMPTY);
+ if (preg_match('!^((//)|([a-zA-Z]:/))!', $_dir, $_root_dir)) {
+ /* leading "//" for network volume, or "[letter]:/" for full path */
+ $_new_dir = $_root_dir[1];
+ /* remove drive-letter from _dir_parts */
+ if (isset($_root_dir[3])) array_shift($_dir_parts);
+
+ } else {
+ $_new_dir = str_replace('\\', '/', getcwd()).'/';
+
+ }
+
+ if($_use_open_basedir = !empty($_open_basedir_ini)) {
+ $_open_basedirs = explode(';', str_replace('\\', '/', $_open_basedir_ini));
+ }
+
+ }
+
+ /* all paths use "/" only from here */
+ foreach ($_dir_parts as $_dir_part) {
+ $_new_dir .= $_dir_part;
+
+ if ($_use_open_basedir) {
+ // do not attempt to test or make directories outside of open_basedir
+ $_make_new_dir = false;
+ foreach ($_open_basedirs as $_open_basedir) {
+ if (substr($_new_dir, 0, strlen($_open_basedir)) == $_open_basedir) {
+ $_make_new_dir = true;
+ break;
+ }
+ }
+ } else {
+ $_make_new_dir = true;
+ }
+
+ if ($_make_new_dir && !file_exists($_new_dir) && !@mkdir($_new_dir, $smarty->_dir_perms) && !is_dir($_new_dir)) {
+ $smarty->trigger_error("problem creating directory '" . $_new_dir . "'");
+ return false;
+ }
+ $_new_dir .= '/';
+ }
+ }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/internals/core.display_debug_console.php b/BSF/include/smarty/libs/internals/core.display_debug_console.php
new file mode 100644
index 000000000..1a80f3909
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.display_debug_console.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Smarty debug_console function plugin
+ *
+ * Type: core<br>
+ * Name: display_debug_console<br>
+ * Purpose: display the javascript debug console window
+ * @param array Format: null
+ * @param Smarty
+ */
+function smarty_core_display_debug_console($params, &$smarty)
+{
+ // we must force compile the debug template in case the environment
+ // changed between separate applications.
+
+ if(empty($smarty->debug_tpl)) {
+ // set path to debug template from SMARTY_DIR
+ $smarty->debug_tpl = SMARTY_DIR . 'debug.tpl';
+ if($smarty->security && is_file($smarty->debug_tpl)) {
+ $smarty->secure_dir[] = realpath($smarty->debug_tpl);
+ }
+ $smarty->debug_tpl = 'file:' . SMARTY_DIR . 'debug.tpl';
+ }
+
+ $_ldelim_orig = $smarty->left_delimiter;
+ $_rdelim_orig = $smarty->right_delimiter;
+
+ $smarty->left_delimiter = '{';
+ $smarty->right_delimiter = '}';
+
+ $_compile_id_orig = $smarty->_compile_id;
+ $smarty->_compile_id = null;
+
+ $_compile_path = $smarty->_get_compile_path($smarty->debug_tpl);
+ if ($smarty->_compile_resource($smarty->debug_tpl, $_compile_path))
+ {
+ ob_start();
+ $smarty->_include($_compile_path);
+ $_results = ob_get_contents();
+ ob_end_clean();
+ } else {
+ $_results = '';
+ }
+
+ $smarty->_compile_id = $_compile_id_orig;
+
+ $smarty->left_delimiter = $_ldelim_orig;
+ $smarty->right_delimiter = $_rdelim_orig;
+
+ return $_results;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/internals/core.get_include_path.php b/BSF/include/smarty/libs/internals/core.get_include_path.php
new file mode 100644
index 000000000..43432412b
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.get_include_path.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Get path to file from include_path
+ *
+ * @param string $file_path
+ * @param string $new_file_path
+ * @return boolean
+ * @staticvar array|null
+ */
+
+// $file_path, &$new_file_path
+
+function smarty_core_get_include_path(&$params, &$smarty)
+{
+ static $_path_array = null;
+
+ if(!isset($_path_array)) {
+ $_ini_include_path = ini_get('include_path');
+
+ if(strstr($_ini_include_path,';')) {
+ // windows pathnames
+ $_path_array = explode(';',$_ini_include_path);
+ } else {
+ $_path_array = explode(':',$_ini_include_path);
+ }
+ }
+ foreach ($_path_array as $_include_path) {
+ if (@is_readable($_include_path . DIRECTORY_SEPARATOR . $params['file_path'])) {
+ $params['new_file_path'] = $_include_path . DIRECTORY_SEPARATOR . $params['file_path'];
+ return true;
+ }
+ }
+ return false;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/internals/core.get_microtime.php b/BSF/include/smarty/libs/internals/core.get_microtime.php
new file mode 100644
index 000000000..f1a28e042
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.get_microtime.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Get seconds and microseconds
+ * @return double
+ */
+function smarty_core_get_microtime($params, &$smarty)
+{
+ $mtime = microtime();
+ $mtime = explode(" ", $mtime);
+ $mtime = (double)($mtime[1]) + (double)($mtime[0]);
+ return ($mtime);
+}
+
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/internals/core.get_php_resource.php b/BSF/include/smarty/libs/internals/core.get_php_resource.php
new file mode 100644
index 000000000..786d4e78e
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.get_php_resource.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Retrieves PHP script resource
+ *
+ * sets $php_resource to the returned resource
+ * @param string $resource
+ * @param string $resource_type
+ * @param $php_resource
+ * @return boolean
+ */
+
+function smarty_core_get_php_resource(&$params, &$smarty)
+{
+
+ $params['resource_base_path'] = $smarty->trusted_dir;
+ $smarty->_parse_resource_name($params, $smarty);
+
+ /*
+ * Find out if the resource exists.
+ */
+
+ if ($params['resource_type'] == 'file') {
+ $_readable = false;
+ if(file_exists($params['resource_name']) && is_readable($params['resource_name'])) {
+ $_readable = true;
+ } else {
+ // test for file in include_path
+ $_params = array('file_path' => $params['resource_name']);
+ require_once(SMARTY_CORE_DIR . 'core.get_include_path.php');
+ if(smarty_core_get_include_path($_params, $smarty)) {
+ $_include_path = $_params['new_file_path'];
+ $_readable = true;
+ }
+ }
+ } else if ($params['resource_type'] != 'file') {
+ $_template_source = null;
+ $_readable = is_callable($smarty->_plugins['resource'][$params['resource_type']][0][0])
+ && call_user_func_array($smarty->_plugins['resource'][$params['resource_type']][0][0],
+ array($params['resource_name'], &$_template_source, &$smarty));
+ }
+
+ /*
+ * Set the error function, depending on which class calls us.
+ */
+ if (method_exists($smarty, '_syntax_error')) {
+ $_error_funcc = '_syntax_error';
+ } else {
+ $_error_funcc = 'trigger_error';
+ }
+
+ if ($_readable) {
+ if ($smarty->security) {
+ require_once(SMARTY_CORE_DIR . 'core.is_trusted.php');
+ if (!smarty_core_is_trusted($params, $smarty)) {
+ $smarty->$_error_funcc('(secure mode) ' . $params['resource_type'] . ':' . $params['resource_name'] . ' is not trusted');
+ return false;
+ }
+ }
+ } else {
+ $smarty->$_error_funcc($params['resource_type'] . ':' . $params['resource_name'] . ' is not readable');
+ return false;
+ }
+
+ if ($params['resource_type'] == 'file') {
+ $params['php_resource'] = $params['resource_name'];
+ } else {
+ $params['php_resource'] = $_template_source;
+ }
+ return true;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/internals/core.is_secure.php b/BSF/include/smarty/libs/internals/core.is_secure.php
new file mode 100644
index 000000000..d54abd432
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.is_secure.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * determines if a resource is secure or not.
+ *
+ * @param string $resource_type
+ * @param string $resource_name
+ * @return boolean
+ */
+
+// $resource_type, $resource_name
+
+function smarty_core_is_secure($params, &$smarty)
+{
+ if (!$smarty->security || $smarty->security_settings['INCLUDE_ANY']) {
+ return true;
+ }
+
+ if ($params['resource_type'] == 'file') {
+ $_rp = realpath($params['resource_name']);
+ if (isset($params['resource_base_path'])) {
+ foreach ((array)$params['resource_base_path'] as $curr_dir) {
+ if ( ($_cd = realpath($curr_dir)) !== false &&
+ strncmp($_rp, $_cd, strlen($_cd)) == 0 &&
+ substr($_rp, strlen($_cd), 1) == DIRECTORY_SEPARATOR ) {
+ return true;
+ }
+ }
+ }
+ if (!empty($smarty->secure_dir)) {
+ foreach ((array)$smarty->secure_dir as $curr_dir) {
+ if ( ($_cd = realpath($curr_dir)) !== false) {
+ if($_cd == $_rp) {
+ return true;
+ } elseif (strncmp($_rp, $_cd, strlen($_cd)) == 0 &&
+ substr($_rp, strlen($_cd), 1) == DIRECTORY_SEPARATOR) {
+ return true;
+ }
+ }
+ }
+ }
+ } else {
+ // resource is not on local file system
+ return call_user_func_array(
+ $smarty->_plugins['resource'][$params['resource_type']][0][2],
+ array($params['resource_name'], &$smarty));
+ }
+
+ return false;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/internals/core.is_trusted.php b/BSF/include/smarty/libs/internals/core.is_trusted.php
new file mode 100644
index 000000000..429973158
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.is_trusted.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * determines if a resource is trusted or not
+ *
+ * @param string $resource_type
+ * @param string $resource_name
+ * @return boolean
+ */
+
+ // $resource_type, $resource_name
+
+function smarty_core_is_trusted($params, &$smarty)
+{
+ $_smarty_trusted = false;
+ if ($params['resource_type'] == 'file') {
+ if (!empty($smarty->trusted_dir)) {
+ $_rp = realpath($params['resource_name']);
+ foreach ((array)$smarty->trusted_dir as $curr_dir) {
+ if (!empty($curr_dir) && is_readable ($curr_dir)) {
+ $_cd = realpath($curr_dir);
+ if (strncmp($_rp, $_cd, strlen($_cd)) == 0
+ && substr($_rp, strlen($_cd), 1) == DIRECTORY_SEPARATOR ) {
+ $_smarty_trusted = true;
+ break;
+ }
+ }
+ }
+ }
+
+ } else {
+ // resource is not on local file system
+ $_smarty_trusted = call_user_func_array($smarty->_plugins['resource'][$params['resource_type']][0][3],
+ array($params['resource_name'], $smarty));
+ }
+
+ return $_smarty_trusted;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/internals/core.load_plugins.php b/BSF/include/smarty/libs/internals/core.load_plugins.php
new file mode 100644
index 000000000..6db1dc51d
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.load_plugins.php
@@ -0,0 +1,125 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Load requested plugins
+ *
+ * @param array $plugins
+ */
+
+// $plugins
+
+function smarty_core_load_plugins($params, &$smarty)
+{
+
+ foreach ($params['plugins'] as $_plugin_info) {
+ list($_type, $_name, $_tpl_file, $_tpl_line, $_delayed_loading) = $_plugin_info;
+ $_plugin = &$smarty->_plugins[$_type][$_name];
+
+ /*
+ * We do not load plugin more than once for each instance of Smarty.
+ * The following code checks for that. The plugin can also be
+ * registered dynamically at runtime, in which case template file
+ * and line number will be unknown, so we fill them in.
+ *
+ * The final element of the info array is a flag that indicates
+ * whether the dynamically registered plugin function has been
+ * checked for existence yet or not.
+ */
+ if (isset($_plugin)) {
+ if (empty($_plugin[3])) {
+ if (!is_callable($_plugin[0])) {
+ $smarty->_trigger_fatal_error("[plugin] $_type '$_name' is not implemented", $_tpl_file, $_tpl_line, __FILE__, __LINE__);
+ } else {
+ $_plugin[1] = $_tpl_file;
+ $_plugin[2] = $_tpl_line;
+ $_plugin[3] = true;
+ if (!isset($_plugin[4])) $_plugin[4] = true; /* cacheable */
+ }
+ }
+ continue;
+ } else if ($_type == 'insert') {
+ /*
+ * For backwards compatibility, we check for insert functions in
+ * the symbol table before trying to load them as a plugin.
+ */
+ $_plugin_func = 'insert_' . $_name;
+ if (function_exists($_plugin_func)) {
+ $_plugin = array($_plugin_func, $_tpl_file, $_tpl_line, true, false);
+ continue;
+ }
+ }
+
+ $_plugin_file = $smarty->_get_plugin_filepath($_type, $_name);
+
+ if (! $_found = ($_plugin_file != false)) {
+ $_message = "could not load plugin file '$_type.$_name.php'\n";
+ }
+
+ /*
+ * If plugin file is found, it -must- provide the properly named
+ * plugin function. In case it doesn't, simply output the error and
+ * do not fall back on any other method.
+ */
+ if ($_found) {
+ include_once $_plugin_file;
+
+ $_plugin_func = 'smarty_' . $_type . '_' . $_name;
+ if (!function_exists($_plugin_func)) {
+ $smarty->_trigger_fatal_error("[plugin] function $_plugin_func() not found in $_plugin_file", $_tpl_file, $_tpl_line, __FILE__, __LINE__);
+ continue;
+ }
+ }
+ /*
+ * In case of insert plugins, their code may be loaded later via
+ * 'script' attribute.
+ */
+ else if ($_type == 'insert' && $_delayed_loading) {
+ $_plugin_func = 'smarty_' . $_type . '_' . $_name;
+ $_found = true;
+ }
+
+ /*
+ * Plugin specific processing and error checking.
+ */
+ if (!$_found) {
+ if ($_type == 'modifier') {
+ /*
+ * In case modifier falls back on using PHP functions
+ * directly, we only allow those specified in the security
+ * context.
+ */
+ if ($smarty->security && !in_array($_name, $smarty->security_settings['MODIFIER_FUNCS'])) {
+ $_message = "(secure mode) modifier '$_name' is not allowed";
+ } else {
+ if (!function_exists($_name)) {
+ $_message = "modifier '$_name' is not implemented";
+ } else {
+ $_plugin_func = $_name;
+ $_found = true;
+ }
+ }
+ } else if ($_type == 'function') {
+ /*
+ * This is a catch-all situation.
+ */
+ $_message = "unknown tag - '$_name'";
+ }
+ }
+
+ if ($_found) {
+ $smarty->_plugins[$_type][$_name] = array($_plugin_func, $_tpl_file, $_tpl_line, true, true);
+ } else {
+ // output error
+ $smarty->_trigger_fatal_error('[plugin] ' . $_message, $_tpl_file, $_tpl_line, __FILE__, __LINE__);
+ }
+ }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/internals/core.load_resource_plugin.php b/BSF/include/smarty/libs/internals/core.load_resource_plugin.php
new file mode 100644
index 000000000..a7d37d1af
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.load_resource_plugin.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * load a resource plugin
+ *
+ * @param string $type
+ */
+
+// $type
+
+function smarty_core_load_resource_plugin($params, &$smarty)
+{
+ /*
+ * Resource plugins are not quite like the other ones, so they are
+ * handled differently. The first element of plugin info is the array of
+ * functions provided by the plugin, the second one indicates whether
+ * all of them exist or not.
+ */
+
+ $_plugin = &$smarty->_plugins['resource'][$params['type']];
+ if (isset($_plugin)) {
+ if (!$_plugin[1] && count($_plugin[0])) {
+ $_plugin[1] = true;
+ foreach ($_plugin[0] as $_plugin_func) {
+ if (!is_callable($_plugin_func)) {
+ $_plugin[1] = false;
+ break;
+ }
+ }
+ }
+
+ if (!$_plugin[1]) {
+ $smarty->_trigger_fatal_error("[plugin] resource '" . $params['type'] . "' is not implemented", null, null, __FILE__, __LINE__);
+ }
+
+ return;
+ }
+
+ $_plugin_file = $smarty->_get_plugin_filepath('resource', $params['type']);
+ $_found = ($_plugin_file != false);
+
+ if ($_found) { /*
+ * If the plugin file is found, it -must- provide the properly named
+ * plugin functions.
+ */
+ include_once($_plugin_file);
+
+ /*
+ * Locate functions that we require the plugin to provide.
+ */
+ $_resource_ops = array('source', 'timestamp', 'secure', 'trusted');
+ $_resource_funcs = array();
+ foreach ($_resource_ops as $_op) {
+ $_plugin_func = 'smarty_resource_' . $params['type'] . '_' . $_op;
+ if (!function_exists($_plugin_func)) {
+ $smarty->_trigger_fatal_error("[plugin] function $_plugin_func() not found in $_plugin_file", null, null, __FILE__, __LINE__);
+ return;
+ } else {
+ $_resource_funcs[] = $_plugin_func;
+ }
+ }
+
+ $smarty->_plugins['resource'][$params['type']] = array($_resource_funcs, true);
+ }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/internals/core.process_cached_inserts.php b/BSF/include/smarty/libs/internals/core.process_cached_inserts.php
new file mode 100644
index 000000000..1d78edd93
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.process_cached_inserts.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Replace cached inserts with the actual results
+ *
+ * @param string $results
+ * @return string
+ */
+function smarty_core_process_cached_inserts($params, &$smarty)
+{
+ preg_match_all('!'.$smarty->_smarty_md5.'{insert_cache (.*)}'.$smarty->_smarty_md5.'!Uis',
+ $params['results'], $match);
+ list($cached_inserts, $insert_args) = $match;
+
+ for ($i = 0, $for_max = count($cached_inserts); $i < $for_max; $i++) {
+ if ($smarty->debugging) {
+ $_params = array();
+ require_once(SMARTY_CORE_DIR . 'core.get_microtime.php');
+ $debug_start_time = smarty_core_get_microtime($_params, $smarty);
+ }
+
+ $args = unserialize($insert_args[$i]);
+ $name = $args['name'];
+
+ if (isset($args['script'])) {
+ $_params = array('resource_name' => $smarty->_dequote($args['script']));
+ require_once(SMARTY_CORE_DIR . 'core.get_php_resource.php');
+ if(!smarty_core_get_php_resource($_params, $smarty)) {
+ return false;
+ }
+ $resource_type = $_params['resource_type'];
+ $php_resource = $_params['php_resource'];
+
+
+ if ($resource_type == 'file') {
+ $smarty->_include($php_resource, true);
+ } else {
+ $smarty->_eval($php_resource);
+ }
+ }
+
+ $function_name = $smarty->_plugins['insert'][$name][0];
+ if (empty($args['assign'])) {
+ $replace = $function_name($args, $smarty);
+ } else {
+ $smarty->assign($args['assign'], $function_name($args, $smarty));
+ $replace = '';
+ }
+
+ $params['results'] = substr_replace($params['results'], $replace, strpos($params['results'], $cached_inserts[$i]), strlen($cached_inserts[$i]));
+ if ($smarty->debugging) {
+ $_params = array();
+ require_once(SMARTY_CORE_DIR . 'core.get_microtime.php');
+ $smarty->_smarty_debug_info[] = array('type' => 'insert',
+ 'filename' => 'insert_'.$name,
+ 'depth' => $smarty->_inclusion_depth,
+ 'exec_time' => smarty_core_get_microtime($_params, $smarty) - $debug_start_time);
+ }
+ }
+
+ return $params['results'];
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/internals/core.process_compiled_include.php b/BSF/include/smarty/libs/internals/core.process_compiled_include.php
new file mode 100644
index 000000000..d539423bf
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.process_compiled_include.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Replace nocache-tags by results of the corresponding non-cacheable
+ * functions and return it
+ *
+ * @param string $compiled_tpl
+ * @param string $cached_source
+ * @return string
+ */
+
+function smarty_core_process_compiled_include($params, &$smarty)
+{
+ $_cache_including = $smarty->_cache_including;
+ $smarty->_cache_including = true;
+
+ $_return = $params['results'];
+
+ foreach ($smarty->_cache_info['cache_serials'] as $_include_file_path=>$_cache_serial) {
+ $smarty->_include($_include_file_path, true);
+ }
+
+ foreach ($smarty->_cache_serials as $_include_file_path=>$_cache_serial) {
+ $_return = preg_replace_callback('!(\{nocache\:('.$_cache_serial.')#(\d+)\})!s',
+ array(&$smarty, '_process_compiled_include_callback'),
+ $_return);
+ }
+ $smarty->_cache_including = $_cache_including;
+ return $_return;
+}
+
+?>
diff --git a/BSF/include/smarty/libs/internals/core.read_cache_file.php b/BSF/include/smarty/libs/internals/core.read_cache_file.php
new file mode 100644
index 000000000..c60e113a7
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.read_cache_file.php
@@ -0,0 +1,101 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * read a cache file, determine if it needs to be
+ * regenerated or not
+ *
+ * @param string $tpl_file
+ * @param string $cache_id
+ * @param string $compile_id
+ * @param string $results
+ * @return boolean
+ */
+
+// $tpl_file, $cache_id, $compile_id, &$results
+
+function smarty_core_read_cache_file(&$params, &$smarty)
+{
+ static $content_cache = array();
+
+ if ($smarty->force_compile) {
+ // force compile enabled, always regenerate
+ return false;
+ }
+
+ if (isset($content_cache[$params['tpl_file'].','.$params['cache_id'].','.$params['compile_id']])) {
+ list($params['results'], $smarty->_cache_info) = $content_cache[$params['tpl_file'].','.$params['cache_id'].','.$params['compile_id']];
+ return true;
+ }
+
+ if (!empty($smarty->cache_handler_func)) {
+ // use cache_handler function
+ call_user_func_array($smarty->cache_handler_func,
+ array('read', &$smarty, &$params['results'], $params['tpl_file'], $params['cache_id'], $params['compile_id'], null));
+ } else {
+ // use local cache file
+ $_auto_id = $smarty->_get_auto_id($params['cache_id'], $params['compile_id']);
+ $_cache_file = $smarty->_get_auto_filename($smarty->cache_dir, $params['tpl_file'], $_auto_id);
+ $params['results'] = $smarty->_read_file($_cache_file);
+ }
+
+ if (empty($params['results'])) {
+ // nothing to parse (error?), regenerate cache
+ return false;
+ }
+
+ $_contents = $params['results'];
+ $_info_start = strpos($_contents, "\n") + 1;
+ $_info_len = (int)substr($_contents, 0, $_info_start - 1);
+ $_cache_info = unserialize(substr($_contents, $_info_start, $_info_len));
+ $params['results'] = substr($_contents, $_info_start + $_info_len);
+
+ if ($smarty->caching == 2 && isset ($_cache_info['expires'])){
+ // caching by expiration time
+ if ($_cache_info['expires'] > -1 && (time() > $_cache_info['expires'])) {
+ // cache expired, regenerate
+ return false;
+ }
+ } else {
+ // caching by lifetime
+ if ($smarty->cache_lifetime > -1 && (time() - $_cache_info['timestamp'] > $smarty->cache_lifetime)) {
+ // cache expired, regenerate
+ return false;
+ }
+ }
+
+ if ($smarty->compile_check) {
+ $_params = array('get_source' => false, 'quiet'=>true);
+ foreach (array_keys($_cache_info['template']) as $_template_dep) {
+ $_params['resource_name'] = $_template_dep;
+ if (!$smarty->_fetch_resource_info($_params) || $_cache_info['timestamp'] < $_params['resource_timestamp']) {
+ // template file has changed, regenerate cache
+ return false;
+ }
+ }
+
+ if (isset($_cache_info['config'])) {
+ $_params = array('resource_base_path' => $smarty->config_dir, 'get_source' => false, 'quiet'=>true);
+ foreach (array_keys($_cache_info['config']) as $_config_dep) {
+ $_params['resource_name'] = $_config_dep;
+ if (!$smarty->_fetch_resource_info($_params) || $_cache_info['timestamp'] < $_params['resource_timestamp']) {
+ // config file has changed, regenerate cache
+ return false;
+ }
+ }
+ }
+ }
+
+ $content_cache[$params['tpl_file'].','.$params['cache_id'].','.$params['compile_id']] = array($params['results'], $_cache_info);
+
+ $smarty->_cache_info = $_cache_info;
+ return true;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/internals/core.rm_auto.php b/BSF/include/smarty/libs/internals/core.rm_auto.php
new file mode 100644
index 000000000..b251f6491
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.rm_auto.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * delete an automagically created file by name and id
+ *
+ * @param string $auto_base
+ * @param string $auto_source
+ * @param string $auto_id
+ * @param integer $exp_time
+ * @return boolean
+ */
+
+// $auto_base, $auto_source = null, $auto_id = null, $exp_time = null
+
+function smarty_core_rm_auto($params, &$smarty)
+{
+ if (!@is_dir($params['auto_base']))
+ return false;
+
+ if(!isset($params['auto_id']) && !isset($params['auto_source'])) {
+ $_params = array(
+ 'dirname' => $params['auto_base'],
+ 'level' => 0,
+ 'exp_time' => $params['exp_time']
+ );
+ require_once(SMARTY_CORE_DIR . 'core.rmdir.php');
+ $_res = smarty_core_rmdir($_params, $smarty);
+ } else {
+ $_tname = $smarty->_get_auto_filename($params['auto_base'], $params['auto_source'], $params['auto_id']);
+
+ if(isset($params['auto_source'])) {
+ if (isset($params['extensions'])) {
+ $_res = false;
+ foreach ((array)$params['extensions'] as $_extension)
+ $_res |= $smarty->_unlink($_tname.$_extension, $params['exp_time']);
+ } else {
+ $_res = $smarty->_unlink($_tname, $params['exp_time']);
+ }
+ } elseif ($smarty->use_sub_dirs) {
+ $_params = array(
+ 'dirname' => $_tname,
+ 'level' => 1,
+ 'exp_time' => $params['exp_time']
+ );
+ require_once(SMARTY_CORE_DIR . 'core.rmdir.php');
+ $_res = smarty_core_rmdir($_params, $smarty);
+ } else {
+ // remove matching file names
+ $_handle = opendir($params['auto_base']);
+ $_res = true;
+ while (false !== ($_filename = readdir($_handle))) {
+ if($_filename == '.' || $_filename == '..') {
+ continue;
+ } elseif (substr($params['auto_base'] . DIRECTORY_SEPARATOR . $_filename, 0, strlen($_tname)) == $_tname) {
+ $_res &= (bool)$smarty->_unlink($params['auto_base'] . DIRECTORY_SEPARATOR . $_filename, $params['exp_time']);
+ }
+ }
+ }
+ }
+
+ return $_res;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/internals/core.rmdir.php b/BSF/include/smarty/libs/internals/core.rmdir.php
new file mode 100644
index 000000000..2166c44d2
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.rmdir.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * delete a dir recursively (level=0 -> keep root)
+ * WARNING: no tests, it will try to remove what you tell it!
+ *
+ * @param string $dirname
+ * @param integer $level
+ * @param integer $exp_time
+ * @return boolean
+ */
+
+// $dirname, $level = 1, $exp_time = null
+
+function smarty_core_rmdir($params, &$smarty)
+{
+ if(!isset($params['level'])) { $params['level'] = 1; }
+ if(!isset($params['exp_time'])) { $params['exp_time'] = null; }
+
+ if($_handle = @opendir($params['dirname'])) {
+
+ while (false !== ($_entry = readdir($_handle))) {
+ if ($_entry != '.' && $_entry != '..') {
+ if (@is_dir($params['dirname'] . DIRECTORY_SEPARATOR . $_entry)) {
+ $_params = array(
+ 'dirname' => $params['dirname'] . DIRECTORY_SEPARATOR . $_entry,
+ 'level' => $params['level'] + 1,
+ 'exp_time' => $params['exp_time']
+ );
+ smarty_core_rmdir($_params, $smarty);
+ }
+ else {
+ $smarty->_unlink($params['dirname'] . DIRECTORY_SEPARATOR . $_entry, $params['exp_time']);
+ }
+ }
+ }
+ closedir($_handle);
+ }
+
+ if ($params['level']) {
+ return @rmdir($params['dirname']);
+ }
+ return (bool)$_handle;
+
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/internals/core.run_insert_handler.php b/BSF/include/smarty/libs/internals/core.run_insert_handler.php
new file mode 100644
index 000000000..71c384508
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.run_insert_handler.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Handle insert tags
+ *
+ * @param array $args
+ * @return string
+ */
+function smarty_core_run_insert_handler($params, &$smarty)
+{
+
+ require_once(SMARTY_CORE_DIR . 'core.get_microtime.php');
+ if ($smarty->debugging) {
+ $_params = array();
+ $_debug_start_time = smarty_core_get_microtime($_params, $smarty);
+ }
+
+ if ($smarty->caching) {
+ $_arg_string = serialize($params['args']);
+ $_name = $params['args']['name'];
+ if (!isset($smarty->_cache_info['insert_tags'][$_name])) {
+ $smarty->_cache_info['insert_tags'][$_name] = array('insert',
+ $_name,
+ $smarty->_plugins['insert'][$_name][1],
+ $smarty->_plugins['insert'][$_name][2],
+ !empty($params['args']['script']) ? true : false);
+ }
+ return $smarty->_smarty_md5."{insert_cache $_arg_string}".$smarty->_smarty_md5;
+ } else {
+ if (isset($params['args']['script'])) {
+ $_params = array('resource_name' => $smarty->_dequote($params['args']['script']));
+ require_once(SMARTY_CORE_DIR . 'core.get_php_resource.php');
+ if(!smarty_core_get_php_resource($_params, $smarty)) {
+ return false;
+ }
+
+ if ($_params['resource_type'] == 'file') {
+ $smarty->_include($_params['php_resource'], true);
+ } else {
+ $smarty->_eval($_params['php_resource']);
+ }
+ unset($params['args']['script']);
+ }
+
+ $_funcname = $smarty->_plugins['insert'][$params['args']['name']][0];
+ $_content = $_funcname($params['args'], $smarty);
+ if ($smarty->debugging) {
+ $_params = array();
+ require_once(SMARTY_CORE_DIR . 'core.get_microtime.php');
+ $smarty->_smarty_debug_info[] = array('type' => 'insert',
+ 'filename' => 'insert_'.$params['args']['name'],
+ 'depth' => $smarty->_inclusion_depth,
+ 'exec_time' => smarty_core_get_microtime($_params, $smarty) - $_debug_start_time);
+ }
+
+ if (!empty($params['args']["assign"])) {
+ $smarty->assign($params['args']["assign"], $_content);
+ } else {
+ return $_content;
+ }
+ }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/internals/core.smarty_include_php.php b/BSF/include/smarty/libs/internals/core.smarty_include_php.php
new file mode 100644
index 000000000..30c6e7654
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.smarty_include_php.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * called for included php files within templates
+ *
+ * @param string $smarty_file
+ * @param string $smarty_assign variable to assign the included template's
+ * output into
+ * @param boolean $smarty_once uses include_once if this is true
+ * @param array $smarty_include_vars associative array of vars from
+ * {include file="blah" var=$var}
+ */
+
+// $file, $assign, $once, $_smarty_include_vars
+
+function smarty_core_smarty_include_php($params, &$smarty)
+{
+ $_params = array('resource_name' => $params['smarty_file']);
+ require_once(SMARTY_CORE_DIR . 'core.get_php_resource.php');
+ smarty_core_get_php_resource($_params, $smarty);
+ $_smarty_resource_type = $_params['resource_type'];
+ $_smarty_php_resource = $_params['php_resource'];
+
+ if (!empty($params['smarty_assign'])) {
+ ob_start();
+ if ($_smarty_resource_type == 'file') {
+ $smarty->_include($_smarty_php_resource, $params['smarty_once'], $params['smarty_include_vars']);
+ } else {
+ $smarty->_eval($_smarty_php_resource, $params['smarty_include_vars']);
+ }
+ $smarty->assign($params['smarty_assign'], ob_get_contents());
+ ob_end_clean();
+ } else {
+ if ($_smarty_resource_type == 'file') {
+ $smarty->_include($_smarty_php_resource, $params['smarty_once'], $params['smarty_include_vars']);
+ } else {
+ $smarty->_eval($_smarty_php_resource, $params['smarty_include_vars']);
+ }
+ }
+}
+
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/internals/core.write_cache_file.php b/BSF/include/smarty/libs/internals/core.write_cache_file.php
new file mode 100644
index 000000000..72f785b74
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.write_cache_file.php
@@ -0,0 +1,96 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Prepend the cache information to the cache file
+ * and write it
+ *
+ * @param string $tpl_file
+ * @param string $cache_id
+ * @param string $compile_id
+ * @param string $results
+ * @return true|null
+ */
+
+ // $tpl_file, $cache_id, $compile_id, $results
+
+function smarty_core_write_cache_file($params, &$smarty)
+{
+
+ // put timestamp in cache header
+ $smarty->_cache_info['timestamp'] = time();
+ if ($smarty->cache_lifetime > -1){
+ // expiration set
+ $smarty->_cache_info['expires'] = $smarty->_cache_info['timestamp'] + $smarty->cache_lifetime;
+ } else {
+ // cache will never expire
+ $smarty->_cache_info['expires'] = -1;
+ }
+
+ // collapse nocache.../nocache-tags
+ if (preg_match_all('!\{(/?)nocache\:[0-9a-f]{32}#\d+\}!', $params['results'], $match, PREG_PATTERN_ORDER)) {
+ // remove everything between every pair of outermost noache.../nocache-tags
+ // and replace it by a single nocache-tag
+ // this new nocache-tag will be replaced by dynamic contents in
+ // smarty_core_process_compiled_includes() on a cache-read
+
+ $match_count = count($match[0]);
+ $results = preg_split('!(\{/?nocache\:[0-9a-f]{32}#\d+\})!', $params['results'], -1, PREG_SPLIT_DELIM_CAPTURE);
+
+ $level = 0;
+ $j = 0;
+ for ($i=0, $results_count = count($results); $i < $results_count && $j < $match_count; $i++) {
+ if ($results[$i] == $match[0][$j]) {
+ // nocache tag
+ if ($match[1][$j]) { // closing tag
+ $level--;
+ unset($results[$i]);
+ } else { // opening tag
+ if ($level++ > 0) unset($results[$i]);
+ }
+ $j++;
+ } elseif ($level > 0) {
+ unset($results[$i]);
+ }
+ }
+ $params['results'] = implode('', $results);
+ }
+ $smarty->_cache_info['cache_serials'] = $smarty->_cache_serials;
+
+ // prepend the cache header info into cache file
+ $_cache_info = serialize($smarty->_cache_info);
+ $params['results'] = strlen($_cache_info) . "\n" . $_cache_info . $params['results'];
+
+ if (!empty($smarty->cache_handler_func)) {
+ // use cache_handler function
+ call_user_func_array($smarty->cache_handler_func,
+ array('write', &$smarty, &$params['results'], $params['tpl_file'], $params['cache_id'], $params['compile_id'], null));
+ } else {
+ // use local cache file
+
+ if(!@is_writable($smarty->cache_dir)) {
+ // cache_dir not writable, see if it exists
+ if(!@is_dir($smarty->cache_dir)) {
+ $smarty->trigger_error('the $cache_dir \'' . $smarty->cache_dir . '\' does not exist, or is not a directory.', E_USER_ERROR);
+ return false;
+ }
+ $smarty->trigger_error('unable to write to $cache_dir \'' . realpath($smarty->cache_dir) . '\'. Be sure $cache_dir is writable by the web server user.', E_USER_ERROR);
+ return false;
+ }
+
+ $_auto_id = $smarty->_get_auto_id($params['cache_id'], $params['compile_id']);
+ $_cache_file = $smarty->_get_auto_filename($smarty->cache_dir, $params['tpl_file'], $_auto_id);
+ $_params = array('filename' => $_cache_file, 'contents' => $params['results'], 'create_dirs' => true);
+ require_once(SMARTY_CORE_DIR . 'core.write_file.php');
+ smarty_core_write_file($_params, $smarty);
+ return true;
+ }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/internals/core.write_compiled_include.php b/BSF/include/smarty/libs/internals/core.write_compiled_include.php
new file mode 100644
index 000000000..c14adb5f4
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.write_compiled_include.php
@@ -0,0 +1,91 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Extract non-cacheable parts out of compiled template and write it
+ *
+ * @param string $compile_path
+ * @param string $template_compiled
+ * @return boolean
+ */
+
+function smarty_core_write_compiled_include($params, &$smarty)
+{
+ $_tag_start = 'if \(\$this->caching && \!\$this->_cache_including\)\: echo \'\{nocache\:('.$params['cache_serial'].')#(\d+)\}\'; endif;';
+ $_tag_end = 'if \(\$this->caching && \!\$this->_cache_including\)\: echo \'\{/nocache\:(\\2)#(\\3)\}\'; endif;';
+
+ preg_match_all('!('.$_tag_start.'(.*)'.$_tag_end.')!Us',
+ $params['compiled_content'], $_match_source, PREG_SET_ORDER);
+
+ // no nocache-parts found: done
+ if (count($_match_source)==0) return;
+
+ // convert the matched php-code to functions
+ $_include_compiled = "<?php /* Smarty version ".$smarty->_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n";
+ $_include_compiled .= " compiled from " . strtr(urlencode($params['resource_name']), array('%2F'=>'/', '%3A'=>':')) . " */\n\n";
+
+ $_compile_path = $params['include_file_path'];
+
+ $smarty->_cache_serials[$_compile_path] = $params['cache_serial'];
+ $_include_compiled .= "\$this->_cache_serials['".$_compile_path."'] = '".$params['cache_serial']."';\n\n?>";
+
+ $_include_compiled .= $params['plugins_code'];
+ $_include_compiled .= "<?php";
+
+ $this_varname = ((double)phpversion() >= 5.0) ? '_smarty' : 'this';
+ for ($_i = 0, $_for_max = count($_match_source); $_i < $_for_max; $_i++) {
+ $_match =& $_match_source[$_i];
+ $source = $_match[4];
+ if ($this_varname == '_smarty') {
+ /* rename $this to $_smarty in the sourcecode */
+ $tokens = token_get_all('<?php ' . $_match[4]);
+
+ /* remove trailing <?php */
+ $open_tag = '';
+ while ($tokens) {
+ $token = array_shift($tokens);
+ if (is_array($token)) {
+ $open_tag .= $token[1];
+ } else {
+ $open_tag .= $token;
+ }
+ if ($open_tag == '<?php ') break;
+ }
+
+ for ($i=0, $count = count($tokens); $i < $count; $i++) {
+ if (is_array($tokens[$i])) {
+ if ($tokens[$i][0] == T_VARIABLE && $tokens[$i][1] == '$this') {
+ $tokens[$i] = '$' . $this_varname;
+ } else {
+ $tokens[$i] = $tokens[$i][1];
+ }
+ }
+ }
+ $source = implode('', $tokens);
+ }
+
+ /* add function to compiled include */
+ $_include_compiled .= "
+function _smarty_tplfunc_$_match[2]_$_match[3](&\$$this_varname)
+{
+$source
+}
+
+";
+ }
+ $_include_compiled .= "\n\n?>\n";
+
+ $_params = array('filename' => $_compile_path,
+ 'contents' => $_include_compiled, 'create_dirs' => true);
+
+ require_once(SMARTY_CORE_DIR . 'core.write_file.php');
+ smarty_core_write_file($_params, $smarty);
+ return true;
+}
+
+
+?>
diff --git a/BSF/include/smarty/libs/internals/core.write_compiled_resource.php b/BSF/include/smarty/libs/internals/core.write_compiled_resource.php
new file mode 100644
index 000000000..b902eff3c
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.write_compiled_resource.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * write the compiled resource
+ *
+ * @param string $compile_path
+ * @param string $compiled_content
+ * @return true
+ */
+function smarty_core_write_compiled_resource($params, &$smarty)
+{
+ if(!@is_writable($smarty->compile_dir)) {
+ // compile_dir not writable, see if it exists
+ if(!@is_dir($smarty->compile_dir)) {
+ $smarty->trigger_error('the $compile_dir \'' . $smarty->compile_dir . '\' does not exist, or is not a directory.', E_USER_ERROR);
+ return false;
+ }
+ $smarty->trigger_error('unable to write to $compile_dir \'' . realpath($smarty->compile_dir) . '\'. Be sure $compile_dir is writable by the web server user.', E_USER_ERROR);
+ return false;
+ }
+
+ $_params = array('filename' => $params['compile_path'], 'contents' => $params['compiled_content'], 'create_dirs' => true);
+ require_once(SMARTY_CORE_DIR . 'core.write_file.php');
+ smarty_core_write_file($_params, $smarty);
+ return true;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/internals/core.write_file.php b/BSF/include/smarty/libs/internals/core.write_file.php
new file mode 100644
index 000000000..8a3a3b398
--- /dev/null
+++ b/BSF/include/smarty/libs/internals/core.write_file.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * write out a file to disk
+ *
+ * @param string $filename
+ * @param string $contents
+ * @param boolean $create_dirs
+ * @return boolean
+ */
+function smarty_core_write_file($params, &$smarty)
+{
+ $_dirname = dirname($params['filename']);
+
+ if ($params['create_dirs']) {
+ $_params = array('dir' => $_dirname);
+ require_once(SMARTY_CORE_DIR . 'core.create_dir_structure.php');
+ smarty_core_create_dir_structure($_params, $smarty);
+ }
+
+ // write to tmp file, then rename it to avoid file locking race condition
+ $_tmp_file = tempnam($_dirname, 'wrt');
+
+ if (!($fd = @fopen($_tmp_file, 'wb'))) {
+ $_tmp_file = $_dirname . DIRECTORY_SEPARATOR . uniqid('wrt');
+ if (!($fd = @fopen($_tmp_file, 'wb'))) {
+ $smarty->trigger_error("problem writing temporary file '$_tmp_file'");
+ return false;
+ }
+ }
+
+ fwrite($fd, $params['contents']);
+ fclose($fd);
+
+ if (DIRECTORY_SEPARATOR == '\\' || !@rename($_tmp_file, $params['filename'])) {
+ // On platforms and filesystems that cannot overwrite with rename()
+ // delete the file before renaming it -- because windows always suffers
+ // this, it is short-circuited to avoid the initial rename() attempt
+ @unlink($params['filename']);
+ @rename($_tmp_file, $params['filename']);
+ }
+ @chmod($params['filename'], $smarty->_file_perms);
+
+ return true;
+}
+
+/* vim: set expandtab: */
+
+?> \ No newline at end of file
diff --git a/BSF/include/smarty/libs/plugins/block.textformat.php b/BSF/include/smarty/libs/plugins/block.textformat.php
new file mode 100644
index 000000000..8cd010acb
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/block.textformat.php
@@ -0,0 +1,103 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Smarty {textformat}{/textformat} block plugin
+ *
+ * Type: block function<br>
+ * Name: textformat<br>
+ * Purpose: format text a certain way with preset styles
+ * or custom wrap/indent settings<br>
+ * @link http://smarty.php.net/manual/en/language.function.textformat.php {textformat}
+ * (Smarty online manual)
+ * @param array
+ * <pre>
+ * Params: style: string (email)
+ * indent: integer (0)
+ * wrap: integer (80)
+ * wrap_char string ("\n")
+ * indent_char: string (" ")
+ * wrap_boundary: boolean (true)
+ * </pre>
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string contents of the block
+ * @param Smarty clever simulation of a method
+ * @return string string $content re-formatted
+ */
+function smarty_block_textformat($params, $content, &$smarty)
+{
+ if (is_null($content)) {
+ return;
+ }
+
+ $style = null;
+ $indent = 0;
+ $indent_first = 0;
+ $indent_char = ' ';
+ $wrap = 80;
+ $wrap_char = "\n";
+ $wrap_cut = false;
+ $assign = null;
+
+ foreach ($params as $_key => $_val) {
+ switch ($_key) {
+ case 'style':
+ case 'indent_char':
+ case 'wrap_char':
+ case 'assign':
+ $$_key = (string)$_val;
+ break;
+
+ case 'indent':
+ case 'indent_first':
+ case 'wrap':
+ $$_key = (int)$_val;
+ break;
+
+ case 'wrap_cut':
+ $$_key = (bool)$_val;
+ break;
+
+ default:
+ $smarty->trigger_error("textformat: unknown attribute '$_key'");
+ }
+ }
+
+ if ($style == 'email') {
+ $wrap = 72;
+ }
+
+ // split into paragraphs
+ $_paragraphs = preg_split('![\r\n][\r\n]!',$content);
+ $_output = '';
+
+ for($_x = 0, $_y = count($_paragraphs); $_x < $_y; $_x++) {
+ if ($_paragraphs[$_x] == '') {
+ continue;
+ }
+ // convert mult. spaces & special chars to single space
+ $_paragraphs[$_x] = preg_replace(array('!\s+!','!(^\s+)|(\s+$)!'), array(' ',''), $_paragraphs[$_x]);
+ // indent first line
+ if($indent_first > 0) {
+ $_paragraphs[$_x] = str_repeat($indent_char, $indent_first) . $_paragraphs[$_x];
+ }
+ // wordwrap sentences
+ $_paragraphs[$_x] = wordwrap($_paragraphs[$_x], $wrap - $indent, $wrap_char, $wrap_cut);
+ // indent lines
+ if($indent > 0) {
+ $_paragraphs[$_x] = preg_replace('!^!m', str_repeat($indent_char, $indent), $_paragraphs[$_x]);
+ }
+ }
+ $_output = implode($wrap_char . $wrap_char, $_paragraphs);
+
+ return $assign ? $smarty->assign($assign, $_output) : $_output;
+
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/compiler.assign.php b/BSF/include/smarty/libs/plugins/compiler.assign.php
new file mode 100644
index 000000000..abef377f8
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/compiler.assign.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Smarty {assign} compiler function plugin
+ *
+ * Type: compiler function<br>
+ * Name: assign<br>
+ * Purpose: assign a value to a template variable
+ * @link http://smarty.php.net/manual/en/language.custom.functions.php#LANGUAGE.FUNCTION.ASSIGN {assign}
+ * (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com> (initial author)
+ * @author messju mohr <messju at lammfellpuschen dot de> (conversion to compiler function)
+ * @param string containing var-attribute and value-attribute
+ * @param Smarty_Compiler
+ */
+function smarty_compiler_assign($tag_attrs, &$compiler)
+{
+ $_params = $compiler->_parse_attrs($tag_attrs);
+
+ if (!isset($_params['var'])) {
+ $compiler->_syntax_error("assign: missing 'var' parameter", E_USER_WARNING);
+ return;
+ }
+
+ if (!isset($_params['value'])) {
+ $compiler->_syntax_error("assign: missing 'value' parameter", E_USER_WARNING);
+ return;
+ }
+
+ return "\$this->assign({$_params['var']}, {$_params['value']});";
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/function.assign_debug_info.php b/BSF/include/smarty/libs/plugins/function.assign_debug_info.php
new file mode 100644
index 000000000..654049876
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/function.assign_debug_info.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Smarty {assign_debug_info} function plugin
+ *
+ * Type: function<br>
+ * Name: assign_debug_info<br>
+ * Purpose: assign debug info to the template<br>
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param array unused in this plugin, this plugin uses {@link Smarty::$_config},
+ * {@link Smarty::$_tpl_vars} and {@link Smarty::$_smarty_debug_info}
+ * @param Smarty
+ */
+function smarty_function_assign_debug_info($params, &$smarty)
+{
+ $assigned_vars = $smarty->_tpl_vars;
+ ksort($assigned_vars);
+ if (@is_array($smarty->_config[0])) {
+ $config_vars = $smarty->_config[0];
+ ksort($config_vars);
+ $smarty->assign("_debug_config_keys", array_keys($config_vars));
+ $smarty->assign("_debug_config_vals", array_values($config_vars));
+ }
+
+ $included_templates = $smarty->_smarty_debug_info;
+
+ $smarty->assign("_debug_keys", array_keys($assigned_vars));
+ $smarty->assign("_debug_vals", array_values($assigned_vars));
+
+ $smarty->assign("_debug_tpls", $included_templates);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/function.config_load.php b/BSF/include/smarty/libs/plugins/function.config_load.php
new file mode 100644
index 000000000..db89f638c
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/function.config_load.php
@@ -0,0 +1,142 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Smarty {config_load} function plugin
+ *
+ * Type: function<br>
+ * Name: config_load<br>
+ * Purpose: load config file vars
+ * @link http://smarty.php.net/manual/en/language.function.config.load.php {config_load}
+ * (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @author messju mohr <messju at lammfellpuschen dot de> (added use of resources)
+ * @param array Format:
+ * <pre>
+ * array('file' => required config file name,
+ * 'section' => optional config file section to load
+ * 'scope' => local/parent/global
+ * 'global' => overrides scope, setting to parent if true)
+ * </pre>
+ * @param Smarty
+ */
+function smarty_function_config_load($params, &$smarty)
+{
+ if ($smarty->debugging) {
+ $_params = array();
+ require_once(SMARTY_CORE_DIR . 'core.get_microtime.php');
+ $_debug_start_time = smarty_core_get_microtime($_params, $smarty);
+ }
+
+ $_file = isset($params['file']) ? $smarty->_dequote($params['file']) : null;
+ $_section = isset($params['section']) ? $smarty->_dequote($params['section']) : null;
+ $_scope = isset($params['scope']) ? $smarty->_dequote($params['scope']) : 'global';
+ $_global = isset($params['global']) ? $smarty->_dequote($params['global']) : false;
+
+ if (!isset($_file) || strlen($_file) == 0) {
+ $smarty->trigger_error("missing 'file' attribute in config_load tag", E_USER_ERROR, __FILE__, __LINE__);
+ }
+
+ if (isset($_scope)) {
+ if ($_scope != 'local' &&
+ $_scope != 'parent' &&
+ $_scope != 'global') {
+ $smarty->trigger_error("invalid 'scope' attribute value", E_USER_ERROR, __FILE__, __LINE__);
+ }
+ } else {
+ if ($_global) {
+ $_scope = 'parent';
+ } else {
+ $_scope = 'local';
+ }
+ }
+
+ $_params = array('resource_name' => $_file,
+ 'resource_base_path' => $smarty->config_dir,
+ 'get_source' => false);
+ $smarty->_parse_resource_name($_params);
+ $_file_path = $_params['resource_type'] . ':' . $_params['resource_name'];
+ if (isset($_section))
+ $_compile_file = $smarty->_get_compile_path($_file_path.'|'.$_section);
+ else
+ $_compile_file = $smarty->_get_compile_path($_file_path);
+
+ if($smarty->force_compile || !file_exists($_compile_file)) {
+ $_compile = true;
+ } elseif ($smarty->compile_check) {
+ $_params = array('resource_name' => $_file,
+ 'resource_base_path' => $smarty->config_dir,
+ 'get_source' => false);
+ $_compile = $smarty->_fetch_resource_info($_params) &&
+ $_params['resource_timestamp'] > filemtime($_compile_file);
+ } else {
+ $_compile = false;
+ }
+
+ if($_compile) {
+ // compile config file
+ if(!is_object($smarty->_conf_obj)) {
+ require_once SMARTY_DIR . $smarty->config_class . '.class.php';
+ $smarty->_conf_obj = new $smarty->config_class();
+ $smarty->_conf_obj->overwrite = $smarty->config_overwrite;
+ $smarty->_conf_obj->booleanize = $smarty->config_booleanize;
+ $smarty->_conf_obj->read_hidden = $smarty->config_read_hidden;
+ $smarty->_conf_obj->fix_newlines = $smarty->config_fix_newlines;
+ }
+
+ $_params = array('resource_name' => $_file,
+ 'resource_base_path' => $smarty->config_dir,
+ $_params['get_source'] = true);
+ if (!$smarty->_fetch_resource_info($_params)) {
+ return;
+ }
+ $smarty->_conf_obj->set_file_contents($_file, $_params['source_content']);
+ $_config_vars = array_merge($smarty->_conf_obj->get($_file),
+ $smarty->_conf_obj->get($_file, $_section));
+ if(function_exists('var_export')) {
+ $_output = '<?php $_config_vars = ' . var_export($_config_vars, true) . '; ?>';
+ } else {
+ $_output = '<?php $_config_vars = unserialize(\'' . strtr(serialize($_config_vars),array('\''=>'\\\'', '\\'=>'\\\\')) . '\'); ?>';
+ }
+ $_params = (array('compile_path' => $_compile_file, 'compiled_content' => $_output, 'resource_timestamp' => $_params['resource_timestamp']));
+ require_once(SMARTY_CORE_DIR . 'core.write_compiled_resource.php');
+ smarty_core_write_compiled_resource($_params, $smarty);
+ } else {
+ include($_compile_file);
+ }
+
+ if ($smarty->caching) {
+ $smarty->_cache_info['config'][$_file] = true;
+ }
+
+ $smarty->_config[0]['vars'] = @array_merge($smarty->_config[0]['vars'], $_config_vars);
+ $smarty->_config[0]['files'][$_file] = true;
+
+ if ($_scope == 'parent') {
+ $smarty->_config[1]['vars'] = @array_merge($smarty->_config[1]['vars'], $_config_vars);
+ $smarty->_config[1]['files'][$_file] = true;
+ } else if ($_scope == 'global') {
+ for ($i = 1, $for_max = count($smarty->_config); $i < $for_max; $i++) {
+ $smarty->_config[$i]['vars'] = @array_merge($smarty->_config[$i]['vars'], $_config_vars);
+ $smarty->_config[$i]['files'][$_file] = true;
+ }
+ }
+
+ if ($smarty->debugging) {
+ $_params = array();
+ require_once(SMARTY_CORE_DIR . 'core.get_microtime.php');
+ $smarty->_smarty_debug_info[] = array('type' => 'config',
+ 'filename' => $_file.' ['.$_section.'] '.$_scope,
+ 'depth' => $smarty->_inclusion_depth,
+ 'exec_time' => smarty_core_get_microtime($_params, $smarty) - $_debug_start_time);
+ }
+
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/function.counter.php b/BSF/include/smarty/libs/plugins/function.counter.php
new file mode 100644
index 000000000..1f26db5fb
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/function.counter.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {counter} function plugin
+ *
+ * Type: function<br>
+ * Name: counter<br>
+ * Purpose: print out a counter value
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @link http://smarty.php.net/manual/en/language.function.counter.php {counter}
+ * (Smarty online manual)
+ * @param array parameters
+ * @param Smarty
+ * @return string|null
+ */
+function smarty_function_counter($params, &$smarty)
+{
+ static $counters = array();
+
+ $name = (isset($params['name'])) ? $params['name'] : 'default';
+ if (!isset($counters[$name])) {
+ $counters[$name] = array(
+ 'start'=>1,
+ 'skip'=>1,
+ 'direction'=>'up',
+ 'count'=>1
+ );
+ }
+ $counter =& $counters[$name];
+
+ if (isset($params['start'])) {
+ $counter['start'] = $counter['count'] = (int)$params['start'];
+ }
+
+ if (!empty($params['assign'])) {
+ $counter['assign'] = $params['assign'];
+ }
+
+ if (isset($counter['assign'])) {
+ $smarty->assign($counter['assign'], $counter['count']);
+ }
+
+ if (isset($params['print'])) {
+ $print = (bool)$params['print'];
+ } else {
+ $print = empty($counter['assign']);
+ }
+
+ if ($print) {
+ $retval = $counter['count'];
+ } else {
+ $retval = null;
+ }
+
+ if (isset($params['skip'])) {
+ $counter['skip'] = $params['skip'];
+ }
+
+ if (isset($params['direction'])) {
+ $counter['direction'] = $params['direction'];
+ }
+
+ if ($counter['direction'] == "down")
+ $counter['count'] -= $counter['skip'];
+ else
+ $counter['count'] += $counter['skip'];
+
+ return $retval;
+
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/function.cycle.php b/BSF/include/smarty/libs/plugins/function.cycle.php
new file mode 100644
index 000000000..fe78bb87d
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/function.cycle.php
@@ -0,0 +1,102 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Smarty {cycle} function plugin
+ *
+ * Type: function<br>
+ * Name: cycle<br>
+ * Date: May 3, 2002<br>
+ * Purpose: cycle through given values<br>
+ * Input:
+ * - name = name of cycle (optional)
+ * - values = comma separated list of values to cycle,
+ * or an array of values to cycle
+ * (this can be left out for subsequent calls)
+ * - reset = boolean - resets given var to true
+ * - print = boolean - print var or not. default is true
+ * - advance = boolean - whether or not to advance the cycle
+ * - delimiter = the value delimiter, default is ","
+ * - assign = boolean, assigns to template var instead of
+ * printed.
+ *
+ * Examples:<br>
+ * <pre>
+ * {cycle values="#eeeeee,#d0d0d0d"}
+ * {cycle name=row values="one,two,three" reset=true}
+ * {cycle name=row}
+ * </pre>
+ * @link http://smarty.php.net/manual/en/language.function.cycle.php {cycle}
+ * (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @author credit to Mark Priatel <mpriatel@rogers.com>
+ * @author credit to Gerard <gerard@interfold.com>
+ * @author credit to Jason Sweat <jsweat_php@yahoo.com>
+ * @version 1.3
+ * @param array
+ * @param Smarty
+ * @return string|null
+ */
+function smarty_function_cycle($params, &$smarty)
+{
+ static $cycle_vars;
+
+ $name = (empty($params['name'])) ? 'default' : $params['name'];
+ $print = (isset($params['print'])) ? (bool)$params['print'] : true;
+ $advance = (isset($params['advance'])) ? (bool)$params['advance'] : true;
+ $reset = (isset($params['reset'])) ? (bool)$params['reset'] : false;
+
+ if (!in_array('values', array_keys($params))) {
+ if(!isset($cycle_vars[$name]['values'])) {
+ $smarty->trigger_error("cycle: missing 'values' parameter");
+ return;
+ }
+ } else {
+ if(isset($cycle_vars[$name]['values'])
+ && $cycle_vars[$name]['values'] != $params['values'] ) {
+ $cycle_vars[$name]['index'] = 0;
+ }
+ $cycle_vars[$name]['values'] = $params['values'];
+ }
+
+ $cycle_vars[$name]['delimiter'] = (isset($params['delimiter'])) ? $params['delimiter'] : ',';
+
+ if(is_array($cycle_vars[$name]['values'])) {
+ $cycle_array = $cycle_vars[$name]['values'];
+ } else {
+ $cycle_array = explode($cycle_vars[$name]['delimiter'],$cycle_vars[$name]['values']);
+ }
+
+ if(!isset($cycle_vars[$name]['index']) || $reset ) {
+ $cycle_vars[$name]['index'] = 0;
+ }
+
+ if (isset($params['assign'])) {
+ $print = false;
+ $smarty->assign($params['assign'], $cycle_array[$cycle_vars[$name]['index']]);
+ }
+
+ if($print) {
+ $retval = $cycle_array[$cycle_vars[$name]['index']];
+ } else {
+ $retval = null;
+ }
+
+ if($advance) {
+ if ( $cycle_vars[$name]['index'] >= count($cycle_array) -1 ) {
+ $cycle_vars[$name]['index'] = 0;
+ } else {
+ $cycle_vars[$name]['index']++;
+ }
+ }
+
+ return $retval;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/function.debug.php b/BSF/include/smarty/libs/plugins/function.debug.php
new file mode 100644
index 000000000..43452307b
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/function.debug.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {debug} function plugin
+ *
+ * Type: function<br>
+ * Name: debug<br>
+ * Date: July 1, 2002<br>
+ * Purpose: popup debug window
+ * @link http://smarty.php.net/manual/en/language.function.debug.php {debug}
+ * (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @version 1.0
+ * @param array
+ * @param Smarty
+ * @return string output from {@link Smarty::_generate_debug_output()}
+ */
+function smarty_function_debug($params, &$smarty)
+{
+ if (isset($params['output'])) {
+ $smarty->assign('_smarty_debug_output', $params['output']);
+ }
+ require_once(SMARTY_CORE_DIR . 'core.display_debug_console.php');
+ return smarty_core_display_debug_console(null, $smarty);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/function.eval.php b/BSF/include/smarty/libs/plugins/function.eval.php
new file mode 100644
index 000000000..ff0472de2
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/function.eval.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {eval} function plugin
+ *
+ * Type: function<br>
+ * Name: eval<br>
+ * Purpose: evaluate a template variable as a template<br>
+ * @link http://smarty.php.net/manual/en/language.function.eval.php {eval}
+ * (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param array
+ * @param Smarty
+ */
+function smarty_function_eval($params, &$smarty)
+{
+
+ if (!isset($params['var'])) {
+ $smarty->trigger_error("eval: missing 'var' parameter");
+ return;
+ }
+
+ if($params['var'] == '') {
+ return;
+ }
+
+ $smarty->_compile_source('evaluated template', $params['var'], $_var_compiled);
+
+ ob_start();
+ $smarty->_eval('?>' . $_var_compiled);
+ $_contents = ob_get_contents();
+ ob_end_clean();
+
+ if (!empty($params['assign'])) {
+ $smarty->assign($params['assign'], $_contents);
+ } else {
+ return $_contents;
+ }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/function.fetch.php b/BSF/include/smarty/libs/plugins/function.fetch.php
new file mode 100644
index 000000000..81b1bfc6b
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/function.fetch.php
@@ -0,0 +1,221 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {fetch} plugin
+ *
+ * Type: function<br>
+ * Name: fetch<br>
+ * Purpose: fetch file, web or ftp data and display results
+ * @link http://smarty.php.net/manual/en/language.function.fetch.php {fetch}
+ * (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param array
+ * @param Smarty
+ * @return string|null if the assign parameter is passed, Smarty assigns the
+ * result to a template variable
+ */
+function smarty_function_fetch($params, &$smarty)
+{
+ if (empty($params['file'])) {
+ $smarty->_trigger_fatal_error("[plugin] parameter 'file' cannot be empty");
+ return;
+ }
+
+ $content = '';
+ if ($smarty->security && !preg_match('!^(http|ftp)://!i', $params['file'])) {
+ $_params = array('resource_type' => 'file', 'resource_name' => $params['file']);
+ require_once(SMARTY_CORE_DIR . 'core.is_secure.php');
+ if(!smarty_core_is_secure($_params, $smarty)) {
+ $smarty->_trigger_fatal_error('[plugin] (secure mode) fetch \'' . $params['file'] . '\' is not allowed');
+ return;
+ }
+
+ // fetch the file
+ if($fp = @fopen($params['file'],'r')) {
+ while(!feof($fp)) {
+ $content .= fgets ($fp,4096);
+ }
+ fclose($fp);
+ } else {
+ $smarty->_trigger_fatal_error('[plugin] fetch cannot read file \'' . $params['file'] . '\'');
+ return;
+ }
+ } else {
+ // not a local file
+ if(preg_match('!^http://!i',$params['file'])) {
+ // http fetch
+ if($uri_parts = parse_url($params['file'])) {
+ // set defaults
+ $host = $server_name = $uri_parts['host'];
+ $timeout = 30;
+ $accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*";
+ $agent = "Smarty Template Engine ".$smarty->_version;
+ $referer = "";
+ $uri = !empty($uri_parts['path']) ? $uri_parts['path'] : '/';
+ $uri .= !empty($uri_parts['query']) ? '?' . $uri_parts['query'] : '';
+ $_is_proxy = false;
+ if(empty($uri_parts['port'])) {
+ $port = 80;
+ } else {
+ $port = $uri_parts['port'];
+ }
+ if(!empty($uri_parts['user'])) {
+ $user = $uri_parts['user'];
+ }
+ if(!empty($uri_parts['pass'])) {
+ $pass = $uri_parts['pass'];
+ }
+ // loop through parameters, setup headers
+ foreach($params as $param_key => $param_value) {
+ switch($param_key) {
+ case "file":
+ case "assign":
+ case "assign_headers":
+ break;
+ case "user":
+ if(!empty($param_value)) {
+ $user = $param_value;
+ }
+ break;
+ case "pass":
+ if(!empty($param_value)) {
+ $pass = $param_value;
+ }
+ break;
+ case "accept":
+ if(!empty($param_value)) {
+ $accept = $param_value;
+ }
+ break;
+ case "header":
+ if(!empty($param_value)) {
+ if(!preg_match('![\w\d-]+: .+!',$param_value)) {
+ $smarty->_trigger_fatal_error("[plugin] invalid header format '".$param_value."'");
+ return;
+ } else {
+ $extra_headers[] = $param_value;
+ }
+ }
+ break;
+ case "proxy_host":
+ if(!empty($param_value)) {
+ $proxy_host = $param_value;
+ }
+ break;
+ case "proxy_port":
+ if(!preg_match('!\D!', $param_value)) {
+ $proxy_port = (int) $param_value;
+ } else {
+ $smarty->_trigger_fatal_error("[plugin] invalid value for attribute '".$param_key."'");
+ return;
+ }
+ break;
+ case "agent":
+ if(!empty($param_value)) {
+ $agent = $param_value;
+ }
+ break;
+ case "referer":
+ if(!empty($param_value)) {
+ $referer = $param_value;
+ }
+ break;
+ case "timeout":
+ if(!preg_match('!\D!', $param_value)) {
+ $timeout = (int) $param_value;
+ } else {
+ $smarty->_trigger_fatal_error("[plugin] invalid value for attribute '".$param_key."'");
+ return;
+ }
+ break;
+ default:
+ $smarty->_trigger_fatal_error("[plugin] unrecognized attribute '".$param_key."'");
+ return;
+ }
+ }
+ if(!empty($proxy_host) && !empty($proxy_port)) {
+ $_is_proxy = true;
+ $fp = fsockopen($proxy_host,$proxy_port,$errno,$errstr,$timeout);
+ } else {
+ $fp = fsockopen($server_name,$port,$errno,$errstr,$timeout);
+ }
+
+ if(!$fp) {
+ $smarty->_trigger_fatal_error("[plugin] unable to fetch: $errstr ($errno)");
+ return;
+ } else {
+ if($_is_proxy) {
+ fputs($fp, 'GET ' . $params['file'] . " HTTP/1.0\r\n");
+ } else {
+ fputs($fp, "GET $uri HTTP/1.0\r\n");
+ }
+ if(!empty($host)) {
+ fputs($fp, "Host: $host\r\n");
+ }
+ if(!empty($accept)) {
+ fputs($fp, "Accept: $accept\r\n");
+ }
+ if(!empty($agent)) {
+ fputs($fp, "User-Agent: $agent\r\n");
+ }
+ if(!empty($referer)) {
+ fputs($fp, "Referer: $referer\r\n");
+ }
+ if(isset($extra_headers) && is_array($extra_headers)) {
+ foreach($extra_headers as $curr_header) {
+ fputs($fp, $curr_header."\r\n");
+ }
+ }
+ if(!empty($user) && !empty($pass)) {
+ fputs($fp, "Authorization: BASIC ".base64_encode("$user:$pass")."\r\n");
+ }
+
+ fputs($fp, "\r\n");
+ while(!feof($fp)) {
+ $content .= fgets($fp,4096);
+ }
+ fclose($fp);
+ $csplit = split("\r\n\r\n",$content,2);
+
+ $content = $csplit[1];
+
+ if(!empty($params['assign_headers'])) {
+ $smarty->assign($params['assign_headers'],split("\r\n",$csplit[0]));
+ }
+ }
+ } else {
+ $smarty->_trigger_fatal_error("[plugin] unable to parse URL, check syntax");
+ return;
+ }
+ } else {
+ // ftp fetch
+ if($fp = @fopen($params['file'],'r')) {
+ while(!feof($fp)) {
+ $content .= fgets ($fp,4096);
+ }
+ fclose($fp);
+ } else {
+ $smarty->_trigger_fatal_error('[plugin] fetch cannot read file \'' . $params['file'] .'\'');
+ return;
+ }
+ }
+
+ }
+
+
+ if (!empty($params['assign'])) {
+ $smarty->assign($params['assign'],$content);
+ } else {
+ return $content;
+ }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/function.html_checkboxes.php b/BSF/include/smarty/libs/plugins/function.html_checkboxes.php
new file mode 100644
index 000000000..ed8ad7f33
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/function.html_checkboxes.php
@@ -0,0 +1,143 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {html_checkboxes} function plugin
+ *
+ * File: function.html_checkboxes.php<br>
+ * Type: function<br>
+ * Name: html_checkboxes<br>
+ * Date: 24.Feb.2003<br>
+ * Purpose: Prints out a list of checkbox input types<br>
+ * Input:<br>
+ * - name (optional) - string default "checkbox"
+ * - values (required) - array
+ * - options (optional) - associative array
+ * - checked (optional) - array default not set
+ * - separator (optional) - ie <br> or &nbsp;
+ * - output (optional) - the output next to each checkbox
+ * - assign (optional) - assign the output as an array to this variable
+ * Examples:
+ * <pre>
+ * {html_checkboxes values=$ids output=$names}
+ * {html_checkboxes values=$ids name='box' separator='<br>' output=$names}
+ * {html_checkboxes values=$ids checked=$checked separator='<br>' output=$names}
+ * </pre>
+ * @link http://smarty.php.net/manual/en/language.function.html.checkboxes.php {html_checkboxes}
+ * (Smarty online manual)
+ * @author Christopher Kvarme <christopher.kvarme@flashjab.com>
+ * @author credits to Monte Ohrt <monte at ohrt dot com>
+ * @version 1.0
+ * @param array
+ * @param Smarty
+ * @return string
+ * @uses smarty_function_escape_special_chars()
+ */
+function smarty_function_html_checkboxes($params, &$smarty)
+{
+ require_once $smarty->_get_plugin_filepath('shared','escape_special_chars');
+
+ $name = 'checkbox';
+ $values = null;
+ $options = null;
+ $selected = null;
+ $separator = '';
+ $labels = true;
+ $output = null;
+
+ $extra = '';
+
+ foreach($params as $_key => $_val) {
+ switch($_key) {
+ case 'name':
+ case 'separator':
+ $$_key = $_val;
+ break;
+
+ case 'labels':
+ $$_key = (bool)$_val;
+ break;
+
+ case 'options':
+ $$_key = (array)$_val;
+ break;
+
+ case 'values':
+ case 'output':
+ $$_key = array_values((array)$_val);
+ break;
+
+ case 'checked':
+ case 'selected':
+ $selected = array_map('strval', array_values((array)$_val));
+ break;
+
+ case 'checkboxes':
+ $smarty->trigger_error('html_checkboxes: the use of the "checkboxes" attribute is deprecated, use "options" instead', E_USER_WARNING);
+ $options = (array)$_val;
+ break;
+
+ case 'assign':
+ break;
+
+ default:
+ if(!is_array($_val)) {
+ $extra .= ' '.$_key.'="'.smarty_function_escape_special_chars($_val).'"';
+ } else {
+ $smarty->trigger_error("html_checkboxes: extra attribute '$_key' cannot be an array", E_USER_NOTICE);
+ }
+ break;
+ }
+ }
+
+ if (!isset($options) && !isset($values))
+ return ''; /* raise error here? */
+
+ settype($selected, 'array');
+ $_html_result = array();
+
+ if (isset($options)) {
+
+ foreach ($options as $_key=>$_val)
+ $_html_result[] = smarty_function_html_checkboxes_output($name, $_key, $_val, $selected, $extra, $separator, $labels);
+
+
+ } else {
+ foreach ($values as $_i=>$_key) {
+ $_val = isset($output[$_i]) ? $output[$_i] : '';
+ $_html_result[] = smarty_function_html_checkboxes_output($name, $_key, $_val, $selected, $extra, $separator, $labels);
+ }
+
+ }
+
+ if(!empty($params['assign'])) {
+ $smarty->assign($params['assign'], $_html_result);
+ } else {
+ return implode("\n",$_html_result);
+ }
+
+}
+
+function smarty_function_html_checkboxes_output($name, $value, $output, $selected, $extra, $separator, $labels) {
+ $_output = '';
+ if ($labels) $_output .= '<label>';
+ $_output .= '<input type="checkbox" name="'
+ . smarty_function_escape_special_chars($name) . '[]" value="'
+ . smarty_function_escape_special_chars($value) . '"';
+
+ if (in_array((string)$value, $selected)) {
+ $_output .= ' checked="checked"';
+ }
+ $_output .= $extra . ' />' . $output;
+ if ($labels) $_output .= '</label>';
+ $_output .= $separator;
+
+ return $_output;
+}
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/function.html_image.php b/BSF/include/smarty/libs/plugins/function.html_image.php
new file mode 100644
index 000000000..9abae72ef
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/function.html_image.php
@@ -0,0 +1,142 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {html_image} function plugin
+ *
+ * Type: function<br>
+ * Name: html_image<br>
+ * Date: Feb 24, 2003<br>
+ * Purpose: format HTML tags for the image<br>
+ * Input:<br>
+ * - file = file (and path) of image (required)
+ * - height = image height (optional, default actual height)
+ * - width = image width (optional, default actual width)
+ * - basedir = base directory for absolute paths, default
+ * is environment variable DOCUMENT_ROOT
+ * - path_prefix = prefix for path output (optional, default empty)
+ *
+ * Examples: {html_image file="/images/masthead.gif"}
+ * Output: <img src="/images/masthead.gif" width=400 height=23>
+ * @link http://smarty.php.net/manual/en/language.function.html.image.php {html_image}
+ * (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @author credits to Duda <duda@big.hu> - wrote first image function
+ * in repository, helped with lots of functionality
+ * @version 1.0
+ * @param array
+ * @param Smarty
+ * @return string
+ * @uses smarty_function_escape_special_chars()
+ */
+function smarty_function_html_image($params, &$smarty)
+{
+ require_once $smarty->_get_plugin_filepath('shared','escape_special_chars');
+
+ $alt = '';
+ $file = '';
+ $height = '';
+ $width = '';
+ $extra = '';
+ $prefix = '';
+ $suffix = '';
+ $path_prefix = '';
+ $server_vars = ($smarty->request_use_auto_globals) ? $_SERVER : $GLOBALS['HTTP_SERVER_VARS'];
+ $basedir = isset($server_vars['DOCUMENT_ROOT']) ? $server_vars['DOCUMENT_ROOT'] : '';
+ foreach($params as $_key => $_val) {
+ switch($_key) {
+ case 'file':
+ case 'height':
+ case 'width':
+ case 'dpi':
+ case 'path_prefix':
+ case 'basedir':
+ $$_key = $_val;
+ break;
+
+ case 'alt':
+ if(!is_array($_val)) {
+ $$_key = smarty_function_escape_special_chars($_val);
+ } else {
+ $smarty->trigger_error("html_image: extra attribute '$_key' cannot be an array", E_USER_NOTICE);
+ }
+ break;
+
+ case 'link':
+ case 'href':
+ $prefix = '<a href="' . $_val . '">';
+ $suffix = '</a>';
+ break;
+
+ default:
+ if(!is_array($_val)) {
+ $extra .= ' '.$_key.'="'.smarty_function_escape_special_chars($_val).'"';
+ } else {
+ $smarty->trigger_error("html_image: extra attribute '$_key' cannot be an array", E_USER_NOTICE);
+ }
+ break;
+ }
+ }
+
+ if (empty($file)) {
+ $smarty->trigger_error("html_image: missing 'file' parameter", E_USER_NOTICE);
+ return;
+ }
+
+ if (substr($file,0,1) == '/') {
+ $_image_path = $basedir . $file;
+ } else {
+ $_image_path = $file;
+ }
+
+ if(!isset($params['width']) || !isset($params['height'])) {
+ if(!$_image_data = @getimagesize($_image_path)) {
+ if(!file_exists($_image_path)) {
+ $smarty->trigger_error("html_image: unable to find '$_image_path'", E_USER_NOTICE);
+ return;
+ } else if(!is_readable($_image_path)) {
+ $smarty->trigger_error("html_image: unable to read '$_image_path'", E_USER_NOTICE);
+ return;
+ } else {
+ $smarty->trigger_error("html_image: '$_image_path' is not a valid image file", E_USER_NOTICE);
+ return;
+ }
+ }
+ if ($smarty->security &&
+ ($_params = array('resource_type' => 'file', 'resource_name' => $_image_path)) &&
+ (require_once(SMARTY_CORE_DIR . 'core.is_secure.php')) &&
+ (!smarty_core_is_secure($_params, $smarty)) ) {
+ $smarty->trigger_error("html_image: (secure) '$_image_path' not in secure directory", E_USER_NOTICE);
+ }
+
+ if(!isset($params['width'])) {
+ $width = $_image_data[0];
+ }
+ if(!isset($params['height'])) {
+ $height = $_image_data[1];
+ }
+
+ }
+
+ if(isset($params['dpi'])) {
+ if(strstr($server_vars['HTTP_USER_AGENT'], 'Mac')) {
+ $dpi_default = 72;
+ } else {
+ $dpi_default = 96;
+ }
+ $_resize = $dpi_default/$params['dpi'];
+ $width = round($width * $_resize);
+ $height = round($height * $_resize);
+ }
+
+ return $prefix . '<img src="'.$path_prefix.$file.'" alt="'.$alt.'" width="'.$width.'" height="'.$height.'"'.$extra.' />' . $suffix;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/function.html_options.php b/BSF/include/smarty/libs/plugins/function.html_options.php
new file mode 100644
index 000000000..cebadde47
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/function.html_options.php
@@ -0,0 +1,122 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {html_options} function plugin
+ *
+ * Type: function<br>
+ * Name: html_options<br>
+ * Input:<br>
+ * - name (optional) - string default "select"
+ * - values (required if no options supplied) - array
+ * - options (required if no values supplied) - associative array
+ * - selected (optional) - string default not set
+ * - output (required if not options supplied) - array
+ * Purpose: Prints the list of <option> tags generated from
+ * the passed parameters
+ * @link http://smarty.php.net/manual/en/language.function.html.options.php {html_image}
+ * (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param array
+ * @param Smarty
+ * @return string
+ * @uses smarty_function_escape_special_chars()
+ */
+function smarty_function_html_options($params, &$smarty)
+{
+ require_once $smarty->_get_plugin_filepath('shared','escape_special_chars');
+
+ $name = null;
+ $values = null;
+ $options = null;
+ $selected = array();
+ $output = null;
+
+ $extra = '';
+
+ foreach($params as $_key => $_val) {
+ switch($_key) {
+ case 'name':
+ $$_key = (string)$_val;
+ break;
+
+ case 'options':
+ $$_key = (array)$_val;
+ break;
+
+ case 'values':
+ case 'output':
+ $$_key = array_values((array)$_val);
+ break;
+
+ case 'selected':
+ $$_key = array_map('strval', array_values((array)$_val));
+ break;
+
+ default:
+ if(!is_array($_val)) {
+ $extra .= ' '.$_key.'="'.smarty_function_escape_special_chars($_val).'"';
+ } else {
+ $smarty->trigger_error("html_options: extra attribute '$_key' cannot be an array", E_USER_NOTICE);
+ }
+ break;
+ }
+ }
+
+ if (!isset($options) && !isset($values))
+ return ''; /* raise error here? */
+
+ $_html_result = '';
+
+ if (isset($options)) {
+
+ foreach ($options as $_key=>$_val)
+ $_html_result .= smarty_function_html_options_optoutput($_key, $_val, $selected);
+
+ } else {
+
+ foreach ($values as $_i=>$_key) {
+ $_val = isset($output[$_i]) ? $output[$_i] : '';
+ $_html_result .= smarty_function_html_options_optoutput($_key, $_val, $selected);
+ }
+
+ }
+
+ if(!empty($name)) {
+ $_html_result = '<select name="' . $name . '"' . $extra . '>' . "\n" . $_html_result . '</select>' . "\n";
+ }
+
+ return $_html_result;
+
+}
+
+function smarty_function_html_options_optoutput($key, $value, $selected) {
+ if(!is_array($value)) {
+ $_html_result = '<option label="' . smarty_function_escape_special_chars($value) . '" value="' .
+ smarty_function_escape_special_chars($key) . '"';
+ if (in_array((string)$key, $selected))
+ $_html_result .= ' selected="selected"';
+ $_html_result .= '>' . smarty_function_escape_special_chars($value) . '</option>' . "\n";
+ } else {
+ $_html_result = smarty_function_html_options_optgroup($key, $value, $selected);
+ }
+ return $_html_result;
+}
+
+function smarty_function_html_options_optgroup($key, $values, $selected) {
+ $optgroup_html = '<optgroup label="' . smarty_function_escape_special_chars($key) . '">' . "\n";
+ foreach ($values as $key => $value) {
+ $optgroup_html .= smarty_function_html_options_optoutput($key, $value, $selected);
+ }
+ $optgroup_html .= "</optgroup>\n";
+ return $optgroup_html;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/function.html_radios.php b/BSF/include/smarty/libs/plugins/function.html_radios.php
new file mode 100644
index 000000000..7503cfaca
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/function.html_radios.php
@@ -0,0 +1,156 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {html_radios} function plugin
+ *
+ * File: function.html_radios.php<br>
+ * Type: function<br>
+ * Name: html_radios<br>
+ * Date: 24.Feb.2003<br>
+ * Purpose: Prints out a list of radio input types<br>
+ * Input:<br>
+ * - name (optional) - string default "radio"
+ * - values (required) - array
+ * - options (optional) - associative array
+ * - checked (optional) - array default not set
+ * - separator (optional) - ie <br> or &nbsp;
+ * - output (optional) - the output next to each radio button
+ * - assign (optional) - assign the output as an array to this variable
+ * Examples:
+ * <pre>
+ * {html_radios values=$ids output=$names}
+ * {html_radios values=$ids name='box' separator='<br>' output=$names}
+ * {html_radios values=$ids checked=$checked separator='<br>' output=$names}
+ * </pre>
+ * @link http://smarty.php.net/manual/en/language.function.html.radios.php {html_radios}
+ * (Smarty online manual)
+ * @author Christopher Kvarme <christopher.kvarme@flashjab.com>
+ * @author credits to Monte Ohrt <monte at ohrt dot com>
+ * @version 1.0
+ * @param array
+ * @param Smarty
+ * @return string
+ * @uses smarty_function_escape_special_chars()
+ */
+function smarty_function_html_radios($params, &$smarty)
+{
+ require_once $smarty->_get_plugin_filepath('shared','escape_special_chars');
+
+ $name = 'radio';
+ $values = null;
+ $options = null;
+ $selected = null;
+ $separator = '';
+ $labels = true;
+ $label_ids = false;
+ $output = null;
+ $extra = '';
+
+ foreach($params as $_key => $_val) {
+ switch($_key) {
+ case 'name':
+ case 'separator':
+ $$_key = (string)$_val;
+ break;
+
+ case 'checked':
+ case 'selected':
+ if(is_array($_val)) {
+ $smarty->trigger_error('html_radios: the "' . $_key . '" attribute cannot be an array', E_USER_WARNING);
+ } else {
+ $selected = (string)$_val;
+ }
+ break;
+
+ case 'labels':
+ case 'label_ids':
+ $$_key = (bool)$_val;
+ break;
+
+ case 'options':
+ $$_key = (array)$_val;
+ break;
+
+ case 'values':
+ case 'output':
+ $$_key = array_values((array)$_val);
+ break;
+
+ case 'radios':
+ $smarty->trigger_error('html_radios: the use of the "radios" attribute is deprecated, use "options" instead', E_USER_WARNING);
+ $options = (array)$_val;
+ break;
+
+ case 'assign':
+ break;
+
+ default:
+ if(!is_array($_val)) {
+ $extra .= ' '.$_key.'="'.smarty_function_escape_special_chars($_val).'"';
+ } else {
+ $smarty->trigger_error("html_radios: extra attribute '$_key' cannot be an array", E_USER_NOTICE);
+ }
+ break;
+ }
+ }
+
+ if (!isset($options) && !isset($values))
+ return ''; /* raise error here? */
+
+ $_html_result = array();
+
+ if (isset($options)) {
+
+ foreach ($options as $_key=>$_val)
+ $_html_result[] = smarty_function_html_radios_output($name, $_key, $_val, $selected, $extra, $separator, $labels, $label_ids);
+
+ } else {
+
+ foreach ($values as $_i=>$_key) {
+ $_val = isset($output[$_i]) ? $output[$_i] : '';
+ $_html_result[] = smarty_function_html_radios_output($name, $_key, $_val, $selected, $extra, $separator, $labels, $label_ids);
+ }
+
+ }
+
+ if(!empty($params['assign'])) {
+ $smarty->assign($params['assign'], $_html_result);
+ } else {
+ return implode("\n",$_html_result);
+ }
+
+}
+
+function smarty_function_html_radios_output($name, $value, $output, $selected, $extra, $separator, $labels, $label_ids) {
+ $_output = '';
+ if ($labels) {
+ if($label_ids) {
+ $_id = smarty_function_escape_special_chars(preg_replace('![^\w\-\.]!', '_', $name . '_' . $value));
+ $_output .= '<label for="' . $_id . '">';
+ } else {
+ $_output .= '<label>';
+ }
+ }
+ $_output .= '<input type="radio" name="'
+ . smarty_function_escape_special_chars($name) . '" value="'
+ . smarty_function_escape_special_chars($value) . '"';
+
+ if ($labels && $label_ids) $_output .= ' id="' . $_id . '"';
+
+ if ((string)$value==$selected) {
+ $_output .= ' checked="checked"';
+ }
+ $_output .= $extra . ' />' . $output;
+ if ($labels) $_output .= '</label>';
+ $_output .= $separator;
+
+ return $_output;
+}
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/function.html_select_date.php b/BSF/include/smarty/libs/plugins/function.html_select_date.php
new file mode 100644
index 000000000..e5eb18307
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/function.html_select_date.php
@@ -0,0 +1,331 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Smarty {html_select_date} plugin
+ *
+ * Type: function<br>
+ * Name: html_select_date<br>
+ * Purpose: Prints the dropdowns for date selection.
+ *
+ * ChangeLog:<br>
+ * - 1.0 initial release
+ * - 1.1 added support for +/- N syntax for begin
+ * and end year values. (Monte)
+ * - 1.2 added support for yyyy-mm-dd syntax for
+ * time value. (Jan Rosier)
+ * - 1.3 added support for choosing format for
+ * month values (Gary Loescher)
+ * - 1.3.1 added support for choosing format for
+ * day values (Marcus Bointon)
+ * - 1.3.2 support negative timestamps, force year
+ * dropdown to include given date unless explicitly set (Monte)
+ * - 1.3.4 fix behaviour of 0000-00-00 00:00:00 dates to match that
+ * of 0000-00-00 dates (cybot, boots)
+ * @link http://smarty.php.net/manual/en/language.function.html.select.date.php {html_select_date}
+ * (Smarty online manual)
+ * @version 1.3.4
+ * @author Andrei Zmievski
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param array
+ * @param Smarty
+ * @return string
+ */
+function smarty_function_html_select_date($params, &$smarty)
+{
+ require_once $smarty->_get_plugin_filepath('shared','escape_special_chars');
+ require_once $smarty->_get_plugin_filepath('shared','make_timestamp');
+ require_once $smarty->_get_plugin_filepath('function','html_options');
+ /* Default values. */
+ $prefix = "Date_";
+ $start_year = strftime("%Y");
+ $end_year = $start_year;
+ $display_days = true;
+ $display_months = true;
+ $display_years = true;
+ $month_format = "%B";
+ /* Write months as numbers by default GL */
+ $month_value_format = "%m";
+ $day_format = "%02d";
+ /* Write day values using this format MB */
+ $day_value_format = "%d";
+ $year_as_text = false;
+ /* Display years in reverse order? Ie. 2000,1999,.... */
+ $reverse_years = false;
+ /* Should the select boxes be part of an array when returned from PHP?
+ e.g. setting it to "birthday", would create "birthday[Day]",
+ "birthday[Month]" & "birthday[Year]". Can be combined with prefix */
+ $field_array = null;
+ /* <select size>'s of the different <select> tags.
+ If not set, uses default dropdown. */
+ $day_size = null;
+ $month_size = null;
+ $year_size = null;
+ /* Unparsed attributes common to *ALL* the <select>/<input> tags.
+ An example might be in the template: all_extra ='class ="foo"'. */
+ $all_extra = null;
+ /* Separate attributes for the tags. */
+ $day_extra = null;
+ $month_extra = null;
+ $year_extra = null;
+ /* Order in which to display the fields.
+ "D" -> day, "M" -> month, "Y" -> year. */
+ $field_order = 'MDY';
+ /* String printed between the different fields. */
+ $field_separator = "\n";
+ $time = time();
+ $all_empty = null;
+ $day_empty = null;
+ $month_empty = null;
+ $year_empty = null;
+ $extra_attrs = '';
+
+ foreach ($params as $_key=>$_value) {
+ switch ($_key) {
+ case 'prefix':
+ case 'time':
+ case 'start_year':
+ case 'end_year':
+ case 'month_format':
+ case 'day_format':
+ case 'day_value_format':
+ case 'field_array':
+ case 'day_size':
+ case 'month_size':
+ case 'year_size':
+ case 'all_extra':
+ case 'day_extra':
+ case 'month_extra':
+ case 'year_extra':
+ case 'field_order':
+ case 'field_separator':
+ case 'month_value_format':
+ case 'month_empty':
+ case 'day_empty':
+ case 'year_empty':
+ $$_key = (string)$_value;
+ break;
+
+ case 'all_empty':
+ $$_key = (string)$_value;
+ $day_empty = $month_empty = $year_empty = $all_empty;
+ break;
+
+ case 'display_days':
+ case 'display_months':
+ case 'display_years':
+ case 'year_as_text':
+ case 'reverse_years':
+ $$_key = (bool)$_value;
+ break;
+
+ default:
+ if(!is_array($_value)) {
+ $extra_attrs .= ' '.$_key.'="'.smarty_function_escape_special_chars($_value).'"';
+ } else {
+ $smarty->trigger_error("html_select_date: extra attribute '$_key' cannot be an array", E_USER_NOTICE);
+ }
+ break;
+ }
+ }
+
+ if (preg_match('!^-\d+$!', $time)) {
+ // negative timestamp, use date()
+ $time = date('Y-m-d', $time);
+ }
+ // If $time is not in format yyyy-mm-dd
+ if (preg_match('/^(\d{0,4}-\d{0,2}-\d{0,2})/', $time, $found)) {
+ $time = $found[1];
+ } else {
+ // use smarty_make_timestamp to get an unix timestamp and
+ // strftime to make yyyy-mm-dd
+ $time = strftime('%Y-%m-%d', smarty_make_timestamp($time));
+ }
+ // Now split this in pieces, which later can be used to set the select
+ $time = explode("-", $time);
+
+ // make syntax "+N" or "-N" work with start_year and end_year
+ if (preg_match('!^(\+|\-)\s*(\d+)$!', $end_year, $match)) {
+ if ($match[1] == '+') {
+ $end_year = strftime('%Y') + $match[2];
+ } else {
+ $end_year = strftime('%Y') - $match[2];
+ }
+ }
+ if (preg_match('!^(\+|\-)\s*(\d+)$!', $start_year, $match)) {
+ if ($match[1] == '+') {
+ $start_year = strftime('%Y') + $match[2];
+ } else {
+ $start_year = strftime('%Y') - $match[2];
+ }
+ }
+ if (strlen($time[0]) > 0) {
+ if ($start_year > $time[0] && !isset($params['start_year'])) {
+ // force start year to include given date if not explicitly set
+ $start_year = $time[0];
+ }
+ if($end_year < $time[0] && !isset($params['end_year'])) {
+ // force end year to include given date if not explicitly set
+ $end_year = $time[0];
+ }
+ }
+
+ $field_order = strtoupper($field_order);
+
+ $html_result = $month_result = $day_result = $year_result = "";
+
+ $field_separator_count = -1;
+ if ($display_months) {
+ $field_separator_count++;
+ $month_names = array();
+ $month_values = array();
+ if(isset($month_empty)) {
+ $month_names[''] = $month_empty;
+ $month_values[''] = '';
+ }
+ for ($i = 1; $i <= 12; $i++) {
+ $month_names[$i] = strftime($month_format, mktime(0, 0, 0, $i, 1, 2000));
+ $month_values[$i] = strftime($month_value_format, mktime(0, 0, 0, $i, 1, 2000));
+ }
+
+ $month_result .= '<select name=';
+ if (null !== $field_array){
+ $month_result .= '"' . $field_array . '[' . $prefix . 'Month]"';
+ } else {
+ $month_result .= '"' . $prefix . 'Month"';
+ }
+ if (null !== $month_size){
+ $month_result .= ' size="' . $month_size . '"';
+ }
+ if (null !== $month_extra){
+ $month_result .= ' ' . $month_extra;
+ }
+ if (null !== $all_extra){
+ $month_result .= ' ' . $all_extra;
+ }
+ $month_result .= $extra_attrs . '>'."\n";
+
+ $month_result .= smarty_function_html_options(array('output' => $month_names,
+ 'values' => $month_values,
+ 'selected' => (int)$time[1] ? strftime($month_value_format, mktime(0, 0, 0, (int)$time[1], 1, 2000)) : '',
+ 'print_result' => false),
+ $smarty);
+ $month_result .= '</select>';
+ }
+
+ if ($display_days) {
+ $field_separator_count++;
+ $days = array();
+ if (isset($day_empty)) {
+ $days[''] = $day_empty;
+ $day_values[''] = '';
+ }
+ for ($i = 1; $i <= 31; $i++) {
+ $days[] = sprintf($day_format, $i);
+ $day_values[] = sprintf($day_value_format, $i);
+ }
+
+ $day_result .= '<select name=';
+ if (null !== $field_array){
+ $day_result .= '"' . $field_array . '[' . $prefix . 'Day]"';
+ } else {
+ $day_result .= '"' . $prefix . 'Day"';
+ }
+ if (null !== $day_size){
+ $day_result .= ' size="' . $day_size . '"';
+ }
+ if (null !== $all_extra){
+ $day_result .= ' ' . $all_extra;
+ }
+ if (null !== $day_extra){
+ $day_result .= ' ' . $day_extra;
+ }
+ $day_result .= $extra_attrs . '>'."\n";
+ $day_result .= smarty_function_html_options(array('output' => $days,
+ 'values' => $day_values,
+ 'selected' => $time[2],
+ 'print_result' => false),
+ $smarty);
+ $day_result .= '</select>';
+ }
+
+ if ($display_years) {
+ $field_separator_count++;
+ if (null !== $field_array){
+ $year_name = $field_array . '[' . $prefix . 'Year]';
+ } else {
+ $year_name = $prefix . 'Year';
+ }
+ if ($year_as_text) {
+ $year_result .= '<input type="text" name="' . $year_name . '" value="' . $time[0] . '" size="4" maxlength="4"';
+ if (null !== $all_extra){
+ $year_result .= ' ' . $all_extra;
+ }
+ if (null !== $year_extra){
+ $year_result .= ' ' . $year_extra;
+ }
+ $year_result .= ' />';
+ } else {
+ $years = range((int)$start_year, (int)$end_year);
+ if ($reverse_years) {
+ rsort($years, SORT_NUMERIC);
+ } else {
+ sort($years, SORT_NUMERIC);
+ }
+ $yearvals = $years;
+ if(isset($year_empty)) {
+ array_unshift($years, $year_empty);
+ array_unshift($yearvals, '');
+ }
+ $year_result .= '<select name="' . $year_name . '"';
+ if (null !== $year_size){
+ $year_result .= ' size="' . $year_size . '"';
+ }
+ if (null !== $all_extra){
+ $year_result .= ' ' . $all_extra;
+ }
+ if (null !== $year_extra){
+ $year_result .= ' ' . $year_extra;
+ }
+ $year_result .= $extra_attrs . '>'."\n";
+ $year_result .= smarty_function_html_options(array('output' => $years,
+ 'values' => $yearvals,
+ 'selected' => $time[0],
+ 'print_result' => false),
+ $smarty);
+ $year_result .= '</select>';
+ }
+ }
+
+ // Loop thru the field_order field
+ for ($i = 0; $i <= 2; $i++){
+ $c = substr($field_order, $i, 1);
+ switch ($c){
+ case 'D':
+ $html_result .= $day_result;
+ break;
+
+ case 'M':
+ $html_result .= $month_result;
+ break;
+
+ case 'Y':
+ $html_result .= $year_result;
+ break;
+ }
+ // Add the field seperator
+ if($i < $field_separator_count) {
+ $html_result .= $field_separator;
+ }
+ }
+
+ return $html_result;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/function.html_select_time.php b/BSF/include/smarty/libs/plugins/function.html_select_time.php
new file mode 100644
index 000000000..2e5be7efe
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/function.html_select_time.php
@@ -0,0 +1,194 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {html_select_time} function plugin
+ *
+ * Type: function<br>
+ * Name: html_select_time<br>
+ * Purpose: Prints the dropdowns for time selection
+ * @link http://smarty.php.net/manual/en/language.function.html.select.time.php {html_select_time}
+ * (Smarty online manual)
+ * @author Roberto Berto <roberto@berto.net>
+ * @credits Monte Ohrt <monte AT ohrt DOT com>
+ * @param array
+ * @param Smarty
+ * @return string
+ * @uses smarty_make_timestamp()
+ */
+function smarty_function_html_select_time($params, &$smarty)
+{
+ require_once $smarty->_get_plugin_filepath('shared','make_timestamp');
+ require_once $smarty->_get_plugin_filepath('function','html_options');
+ /* Default values. */
+ $prefix = "Time_";
+ $time = time();
+ $display_hours = true;
+ $display_minutes = true;
+ $display_seconds = true;
+ $display_meridian = true;
+ $use_24_hours = true;
+ $minute_interval = 1;
+ $second_interval = 1;
+ /* Should the select boxes be part of an array when returned from PHP?
+ e.g. setting it to "birthday", would create "birthday[Hour]",
+ "birthday[Minute]", "birthday[Seconds]" & "birthday[Meridian]".
+ Can be combined with prefix. */
+ $field_array = null;
+ $all_extra = null;
+ $hour_extra = null;
+ $minute_extra = null;
+ $second_extra = null;
+ $meridian_extra = null;
+
+ foreach ($params as $_key=>$_value) {
+ switch ($_key) {
+ case 'prefix':
+ case 'time':
+ case 'field_array':
+ case 'all_extra':
+ case 'hour_extra':
+ case 'minute_extra':
+ case 'second_extra':
+ case 'meridian_extra':
+ $$_key = (string)$_value;
+ break;
+
+ case 'display_hours':
+ case 'display_minutes':
+ case 'display_seconds':
+ case 'display_meridian':
+ case 'use_24_hours':
+ $$_key = (bool)$_value;
+ break;
+
+ case 'minute_interval':
+ case 'second_interval':
+ $$_key = (int)$_value;
+ break;
+
+ default:
+ $smarty->trigger_error("[html_select_time] unknown parameter $_key", E_USER_WARNING);
+ }
+ }
+
+ $time = smarty_make_timestamp($time);
+
+ $html_result = '';
+
+ if ($display_hours) {
+ $hours = $use_24_hours ? range(0, 23) : range(1, 12);
+ $hour_fmt = $use_24_hours ? '%H' : '%I';
+ for ($i = 0, $for_max = count($hours); $i < $for_max; $i++)
+ $hours[$i] = sprintf('%02d', $hours[$i]);
+ $html_result .= '<select name=';
+ if (null !== $field_array) {
+ $html_result .= '"' . $field_array . '[' . $prefix . 'Hour]"';
+ } else {
+ $html_result .= '"' . $prefix . 'Hour"';
+ }
+ if (null !== $hour_extra){
+ $html_result .= ' ' . $hour_extra;
+ }
+ if (null !== $all_extra){
+ $html_result .= ' ' . $all_extra;
+ }
+ $html_result .= '>'."\n";
+ $html_result .= smarty_function_html_options(array('output' => $hours,
+ 'values' => $hours,
+ 'selected' => strftime($hour_fmt, $time),
+ 'print_result' => false),
+ $smarty);
+ $html_result .= "</select>\n";
+ }
+
+ if ($display_minutes) {
+ $all_minutes = range(0, 59);
+ for ($i = 0, $for_max = count($all_minutes); $i < $for_max; $i+= $minute_interval)
+ $minutes[] = sprintf('%02d', $all_minutes[$i]);
+ $selected = intval(floor(strftime('%M', $time) / $minute_interval) * $minute_interval);
+ $html_result .= '<select name=';
+ if (null !== $field_array) {
+ $html_result .= '"' . $field_array . '[' . $prefix . 'Minute]"';
+ } else {
+ $html_result .= '"' . $prefix . 'Minute"';
+ }
+ if (null !== $minute_extra){
+ $html_result .= ' ' . $minute_extra;
+ }
+ if (null !== $all_extra){
+ $html_result .= ' ' . $all_extra;
+ }
+ $html_result .= '>'."\n";
+
+ $html_result .= smarty_function_html_options(array('output' => $minutes,
+ 'values' => $minutes,
+ 'selected' => $selected,
+ 'print_result' => false),
+ $smarty);
+ $html_result .= "</select>\n";
+ }
+
+ if ($display_seconds) {
+ $all_seconds = range(0, 59);
+ for ($i = 0, $for_max = count($all_seconds); $i < $for_max; $i+= $second_interval)
+ $seconds[] = sprintf('%02d', $all_seconds[$i]);
+ $selected = intval(floor(strftime('%S', $time) / $second_interval) * $second_interval);
+ $html_result .= '<select name=';
+ if (null !== $field_array) {
+ $html_result .= '"' . $field_array . '[' . $prefix . 'Second]"';
+ } else {
+ $html_result .= '"' . $prefix . 'Second"';
+ }
+
+ if (null !== $second_extra){
+ $html_result .= ' ' . $second_extra;
+ }
+ if (null !== $all_extra){
+ $html_result .= ' ' . $all_extra;
+ }
+ $html_result .= '>'."\n";
+
+ $html_result .= smarty_function_html_options(array('output' => $seconds,
+ 'values' => $seconds,
+ 'selected' => $selected,
+ 'print_result' => false),
+ $smarty);
+ $html_result .= "</select>\n";
+ }
+
+ if ($display_meridian && !$use_24_hours) {
+ $html_result .= '<select name=';
+ if (null !== $field_array) {
+ $html_result .= '"' . $field_array . '[' . $prefix . 'Meridian]"';
+ } else {
+ $html_result .= '"' . $prefix . 'Meridian"';
+ }
+
+ if (null !== $meridian_extra){
+ $html_result .= ' ' . $meridian_extra;
+ }
+ if (null !== $all_extra){
+ $html_result .= ' ' . $all_extra;
+ }
+ $html_result .= '>'."\n";
+
+ $html_result .= smarty_function_html_options(array('output' => array('AM', 'PM'),
+ 'values' => array('am', 'pm'),
+ 'selected' => strtolower(strftime('%p', $time)),
+ 'print_result' => false),
+ $smarty);
+ $html_result .= "</select>\n";
+ }
+
+ return $html_result;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/function.html_table.php b/BSF/include/smarty/libs/plugins/function.html_table.php
new file mode 100644
index 000000000..32aeba83c
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/function.html_table.php
@@ -0,0 +1,177 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {html_table} function plugin
+ *
+ * Type: function<br>
+ * Name: html_table<br>
+ * Date: Feb 17, 2003<br>
+ * Purpose: make an html table from an array of data<br>
+ * Input:<br>
+ * - loop = array to loop through
+ * - cols = number of columns, comma separated list of column names
+ * or array of column names
+ * - rows = number of rows
+ * - table_attr = table attributes
+ * - th_attr = table heading attributes (arrays are cycled)
+ * - tr_attr = table row attributes (arrays are cycled)
+ * - td_attr = table cell attributes (arrays are cycled)
+ * - trailpad = value to pad trailing cells with
+ * - caption = text for caption element
+ * - vdir = vertical direction (default: "down", means top-to-bottom)
+ * - hdir = horizontal direction (default: "right", means left-to-right)
+ * - inner = inner loop (default "cols": print $loop line by line,
+ * $loop will be printed column by column otherwise)
+ *
+ *
+ * Examples:
+ * <pre>
+ * {table loop=$data}
+ * {table loop=$data cols=4 tr_attr='"bgcolor=red"'}
+ * {table loop=$data cols="first,second,third" tr_attr=$colors}
+ * </pre>
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @author credit to Messju Mohr <messju at lammfellpuschen dot de>
+ * @author credit to boots <boots dot smarty at yahoo dot com>
+ * @version 1.1
+ * @link http://smarty.php.net/manual/en/language.function.html.table.php {html_table}
+ * (Smarty online manual)
+ * @param array
+ * @param Smarty
+ * @return string
+ */
+function smarty_function_html_table($params, &$smarty)
+{
+ $table_attr = 'border="1"';
+ $tr_attr = '';
+ $th_attr = '';
+ $td_attr = '';
+ $cols = $cols_count = 3;
+ $rows = 3;
+ $trailpad = '&nbsp;';
+ $vdir = 'down';
+ $hdir = 'right';
+ $inner = 'cols';
+ $caption = '';
+
+ if (!isset($params['loop'])) {
+ $smarty->trigger_error("html_table: missing 'loop' parameter");
+ return;
+ }
+
+ foreach ($params as $_key=>$_value) {
+ switch ($_key) {
+ case 'loop':
+ $$_key = (array)$_value;
+ break;
+
+ case 'cols':
+ if (is_array($_value) && !empty($_value)) {
+ $cols = $_value;
+ $cols_count = count($_value);
+ } elseif (!is_numeric($_value) && is_string($_value) && !empty($_value)) {
+ $cols = explode(',', $_value);
+ $cols_count = count($cols);
+ } elseif (!empty($_value)) {
+ $cols_count = (int)$_value;
+ } else {
+ $cols_count = $cols;
+ }
+ break;
+
+ case 'rows':
+ $$_key = (int)$_value;
+ break;
+
+ case 'table_attr':
+ case 'trailpad':
+ case 'hdir':
+ case 'vdir':
+ case 'inner':
+ case 'caption':
+ $$_key = (string)$_value;
+ break;
+
+ case 'tr_attr':
+ case 'td_attr':
+ case 'th_attr':
+ $$_key = $_value;
+ break;
+ }
+ }
+
+ $loop_count = count($loop);
+ if (empty($params['rows'])) {
+ /* no rows specified */
+ $rows = ceil($loop_count/$cols_count);
+ } elseif (empty($params['cols'])) {
+ if (!empty($params['rows'])) {
+ /* no cols specified, but rows */
+ $cols_count = ceil($loop_count/$rows);
+ }
+ }
+
+ $output = "<table $table_attr>\n";
+
+ if (!empty($caption)) {
+ $output .= '<caption>' . $caption . "</caption>\n";
+ }
+
+ if (is_array($cols)) {
+ $cols = ($hdir == 'right') ? $cols : array_reverse($cols);
+ $output .= "<thead><tr>\n";
+
+ for ($r=0; $r<$cols_count; $r++) {
+ $output .= '<th' . smarty_function_html_table_cycle('th', $th_attr, $r) . '>';
+ $output .= $cols[$r];
+ $output .= "</th>\n";
+ }
+ $output .= "</tr></thead>\n";
+ }
+
+ $output .= "<tbody>\n";
+ for ($r=0; $r<$rows; $r++) {
+ $output .= "<tr" . smarty_function_html_table_cycle('tr', $tr_attr, $r) . ">\n";
+ $rx = ($vdir == 'down') ? $r*$cols_count : ($rows-1-$r)*$cols_count;
+
+ for ($c=0; $c<$cols_count; $c++) {
+ $x = ($hdir == 'right') ? $rx+$c : $rx+$cols_count-1-$c;
+ if ($inner!='cols') {
+ /* shuffle x to loop over rows*/
+ $x = floor($x/$cols_count) + ($x%$cols_count)*$rows;
+ }
+
+ if ($x<$loop_count) {
+ $output .= "<td" . smarty_function_html_table_cycle('td', $td_attr, $c) . ">" . $loop[$x] . "</td>\n";
+ } else {
+ $output .= "<td" . smarty_function_html_table_cycle('td', $td_attr, $c) . ">$trailpad</td>\n";
+ }
+ }
+ $output .= "</tr>\n";
+ }
+ $output .= "</tbody>\n";
+ $output .= "</table>\n";
+
+ return $output;
+}
+
+function smarty_function_html_table_cycle($name, $var, $no) {
+ if(!is_array($var)) {
+ $ret = $var;
+ } else {
+ $ret = $var[$no % count($var)];
+ }
+
+ return ($ret) ? ' '.$ret : '';
+}
+
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/function.mailto.php b/BSF/include/smarty/libs/plugins/function.mailto.php
new file mode 100644
index 000000000..20e9ed984
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/function.mailto.php
@@ -0,0 +1,165 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {mailto} function plugin
+ *
+ * Type: function<br>
+ * Name: mailto<br>
+ * Date: May 21, 2002
+ * Purpose: automate mailto address link creation, and optionally
+ * encode them.<br>
+ * Input:<br>
+ * - address = e-mail address
+ * - text = (optional) text to display, default is address
+ * - encode = (optional) can be one of:
+ * * none : no encoding (default)
+ * * javascript : encode with javascript
+ * * javascript_charcode : encode with javascript charcode
+ * * hex : encode with hexidecimal (no javascript)
+ * - cc = (optional) address(es) to carbon copy
+ * - bcc = (optional) address(es) to blind carbon copy
+ * - subject = (optional) e-mail subject
+ * - newsgroups = (optional) newsgroup(s) to post to
+ * - followupto = (optional) address(es) to follow up to
+ * - extra = (optional) extra tags for the href link
+ *
+ * Examples:
+ * <pre>
+ * {mailto address="me@domain.com"}
+ * {mailto address="me@domain.com" encode="javascript"}
+ * {mailto address="me@domain.com" encode="hex"}
+ * {mailto address="me@domain.com" subject="Hello to you!"}
+ * {mailto address="me@domain.com" cc="you@domain.com,they@domain.com"}
+ * {mailto address="me@domain.com" extra='class="mailto"'}
+ * </pre>
+ * @link http://smarty.php.net/manual/en/language.function.mailto.php {mailto}
+ * (Smarty online manual)
+ * @version 1.2
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @author credits to Jason Sweat (added cc, bcc and subject functionality)
+ * @param array
+ * @param Smarty
+ * @return string
+ */
+function smarty_function_mailto($params, &$smarty)
+{
+ $extra = '';
+
+ if (empty($params['address'])) {
+ $smarty->trigger_error("mailto: missing 'address' parameter");
+ return;
+ } else {
+ $address = $params['address'];
+ }
+
+ $text = $address;
+
+ // netscape and mozilla do not decode %40 (@) in BCC field (bug?)
+ // so, don't encode it.
+ $search = array('%40', '%2C');
+ $replace = array('@', ',');
+ $mail_parms = array();
+ foreach ($params as $var=>$value) {
+ switch ($var) {
+ case 'cc':
+ case 'bcc':
+ case 'followupto':
+ if (!empty($value))
+ $mail_parms[] = $var.'='.str_replace($search,$replace,rawurlencode($value));
+ break;
+
+ case 'subject':
+ case 'newsgroups':
+ $mail_parms[] = $var.'='.rawurlencode($value);
+ break;
+
+ case 'extra':
+ case 'text':
+ $$var = $value;
+
+ default:
+ }
+ }
+
+ $mail_parm_vals = '';
+ for ($i=0; $i<count($mail_parms); $i++) {
+ $mail_parm_vals .= (0==$i) ? '?' : '&';
+ $mail_parm_vals .= $mail_parms[$i];
+ }
+ $address .= $mail_parm_vals;
+
+ $encode = (empty($params['encode'])) ? 'none' : $params['encode'];
+ if (!in_array($encode,array('javascript','javascript_charcode','hex','none')) ) {
+ $smarty->trigger_error("mailto: 'encode' parameter must be none, javascript or hex");
+ return;
+ }
+
+ if ($encode == 'javascript' ) {
+ $string = 'document.write(\'<a href="mailto:'.$address.'" '.$extra.'>'.$text.'</a>\');';
+
+ $js_encode = '';
+ for ($x=0; $x < strlen($string); $x++) {
+ $js_encode .= '%' . bin2hex($string[$x]);
+ }
+
+ return '<script type="text/javascript">eval(unescape(\''.$js_encode.'\'))</script>';
+
+ } elseif ($encode == 'javascript_charcode' ) {
+ $string = '<a href="mailto:'.$address.'" '.$extra.'>'.$text.'</a>';
+
+ for($x = 0, $y = strlen($string); $x < $y; $x++ ) {
+ $ord[] = ord($string[$x]);
+ }
+
+ $_ret = "<script type=\"text/javascript\" language=\"javascript\">\n";
+ $_ret .= "<!--\n";
+ $_ret .= "{document.write(String.fromCharCode(";
+ $_ret .= implode(',',$ord);
+ $_ret .= "))";
+ $_ret .= "}\n";
+ $_ret .= "//-->\n";
+ $_ret .= "</script>\n";
+
+ return $_ret;
+
+
+ } elseif ($encode == 'hex') {
+
+ preg_match('!^(.*)(\?.*)$!',$address,$match);
+ if(!empty($match[2])) {
+ $smarty->trigger_error("mailto: hex encoding does not work with extra attributes. Try javascript.");
+ return;
+ }
+ $address_encode = '';
+ for ($x=0; $x < strlen($address); $x++) {
+ if(preg_match('!\w!',$address[$x])) {
+ $address_encode .= '%' . bin2hex($address[$x]);
+ } else {
+ $address_encode .= $address[$x];
+ }
+ }
+ $text_encode = '';
+ for ($x=0; $x < strlen($text); $x++) {
+ $text_encode .= '&#x' . bin2hex($text[$x]).';';
+ }
+
+ $mailto = "&#109;&#97;&#105;&#108;&#116;&#111;&#58;";
+ return '<a href="'.$mailto.$address_encode.'" '.$extra.'>'.$text_encode.'</a>';
+
+ } else {
+ // no encoding
+ return '<a href="mailto:'.$address.'" '.$extra.'>'.$text.'</a>';
+
+ }
+
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/function.math.php b/BSF/include/smarty/libs/plugins/function.math.php
new file mode 100644
index 000000000..71672fea4
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/function.math.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {math} function plugin
+ *
+ * Type: function<br>
+ * Name: math<br>
+ * Purpose: handle math computations in template<br>
+ * @link http://smarty.php.net/manual/en/language.function.math.php {math}
+ * (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param array
+ * @param Smarty
+ * @return string
+ */
+function smarty_function_math($params, &$smarty)
+{
+ // be sure equation parameter is present
+ if (empty($params['equation'])) {
+ $smarty->trigger_error("math: missing equation parameter");
+ return;
+ }
+
+ $equation = $params['equation'];
+
+ // make sure parenthesis are balanced
+ if (substr_count($equation,"(") != substr_count($equation,")")) {
+ $smarty->trigger_error("math: unbalanced parenthesis");
+ return;
+ }
+
+ // match all vars in equation, make sure all are passed
+ preg_match_all("!(?:0x[a-fA-F0-9]+)|([a-zA-Z][a-zA-Z0-9_]+)!",$equation, $match);
+ $allowed_funcs = array('int','abs','ceil','cos','exp','floor','log','log10',
+ 'max','min','pi','pow','rand','round','sin','sqrt','srand','tan');
+
+ foreach($match[1] as $curr_var) {
+ if ($curr_var && !in_array($curr_var, array_keys($params)) && !in_array($curr_var, $allowed_funcs)) {
+ $smarty->trigger_error("math: function call $curr_var not allowed");
+ return;
+ }
+ }
+
+ foreach($params as $key => $val) {
+ if ($key != "equation" && $key != "format" && $key != "assign") {
+ // make sure value is not empty
+ if (strlen($val)==0) {
+ $smarty->trigger_error("math: parameter $key is empty");
+ return;
+ }
+ if (!is_numeric($val)) {
+ $smarty->trigger_error("math: parameter $key: is not numeric");
+ return;
+ }
+ $equation = preg_replace("/\b$key\b/", " \$params['$key'] ", $equation);
+ }
+ }
+
+ eval("\$smarty_math_result = ".$equation.";");
+
+ if (empty($params['format'])) {
+ if (empty($params['assign'])) {
+ return $smarty_math_result;
+ } else {
+ $smarty->assign($params['assign'],$smarty_math_result);
+ }
+ } else {
+ if (empty($params['assign'])){
+ printf($params['format'],$smarty_math_result);
+ } else {
+ $smarty->assign($params['assign'],sprintf($params['format'],$smarty_math_result));
+ }
+ }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/function.popup.php b/BSF/include/smarty/libs/plugins/function.popup.php
new file mode 100644
index 000000000..3a76b785a
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/function.popup.php
@@ -0,0 +1,119 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {popup} function plugin
+ *
+ * Type: function<br>
+ * Name: popup<br>
+ * Purpose: make text pop up in windows via overlib
+ * @link http://smarty.php.net/manual/en/language.function.popup.php {popup}
+ * (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param array
+ * @param Smarty
+ * @return string
+ */
+function smarty_function_popup($params, &$smarty)
+{
+ $append = '';
+ foreach ($params as $_key=>$_value) {
+ switch ($_key) {
+ case 'text':
+ case 'trigger':
+ case 'function':
+ case 'inarray':
+ $$_key = (string)$_value;
+ if ($_key == 'function' || $_key == 'inarray')
+ $append .= ',' . strtoupper($_key) . ",'$_value'";
+ break;
+
+ case 'caption':
+ case 'closetext':
+ case 'status':
+ $append .= ',' . strtoupper($_key) . ",'" . str_replace("'","\'",$_value) . "'";
+ break;
+
+ case 'fgcolor':
+ case 'bgcolor':
+ case 'textcolor':
+ case 'capcolor':
+ case 'closecolor':
+ case 'textfont':
+ case 'captionfont':
+ case 'closefont':
+ case 'fgbackground':
+ case 'bgbackground':
+ case 'caparray':
+ case 'capicon':
+ case 'background':
+ case 'frame':
+ $append .= ',' . strtoupper($_key) . ",'$_value'";
+ break;
+
+ case 'textsize':
+ case 'captionsize':
+ case 'closesize':
+ case 'width':
+ case 'height':
+ case 'border':
+ case 'offsetx':
+ case 'offsety':
+ case 'snapx':
+ case 'snapy':
+ case 'fixx':
+ case 'fixy':
+ case 'padx':
+ case 'pady':
+ case 'timeout':
+ case 'delay':
+ $append .= ',' . strtoupper($_key) . ",$_value";
+ break;
+
+ case 'sticky':
+ case 'left':
+ case 'right':
+ case 'center':
+ case 'above':
+ case 'below':
+ case 'noclose':
+ case 'autostatus':
+ case 'autostatuscap':
+ case 'fullhtml':
+ case 'hauto':
+ case 'vauto':
+ case 'mouseoff':
+ case 'followmouse':
+ case 'closeclick':
+ if ($_value) $append .= ',' . strtoupper($_key);
+ break;
+
+ default:
+ $smarty->trigger_error("[popup] unknown parameter $_key", E_USER_WARNING);
+ }
+ }
+
+ if (empty($text) && !isset($inarray) && empty($function)) {
+ $smarty->trigger_error("overlib: attribute 'text' or 'inarray' or 'function' required");
+ return false;
+ }
+
+ if (empty($trigger)) { $trigger = "onmouseover"; }
+
+ $retval = $trigger . '="return overlib(\''.preg_replace(array("!'!","![\r\n]!"),array("\'",'\r'),$text).'\'';
+ $retval .= $append . ');"';
+ if ($trigger == 'onmouseover')
+ $retval .= ' onmouseout="nd();"';
+
+
+ return $retval;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/function.popup_init.php b/BSF/include/smarty/libs/plugins/function.popup_init.php
new file mode 100644
index 000000000..93cb45450
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/function.popup_init.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty {popup_init} function plugin
+ *
+ * Type: function<br>
+ * Name: popup_init<br>
+ * Purpose: initialize overlib
+ * @link http://smarty.php.net/manual/en/language.function.popup.init.php {popup_init}
+ * (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param array
+ * @param Smarty
+ * @return string
+ */
+function smarty_function_popup_init($params, &$smarty)
+{
+ $zindex = 1000;
+
+ if (!empty($params['zindex'])) {
+ $zindex = $params['zindex'];
+ }
+
+ if (!empty($params['src'])) {
+ return '<div id="overDiv" style="position:absolute; visibility:hidden; z-index:'.$zindex.';"></div>' . "\n"
+ . '<script type="text/javascript" language="JavaScript" src="'.$params['src'].'"></script>' . "\n";
+ } else {
+ $smarty->trigger_error("popup_init: missing src parameter");
+ }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.capitalize.php b/BSF/include/smarty/libs/plugins/modifier.capitalize.php
new file mode 100644
index 000000000..4a611d9f0
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.capitalize.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty capitalize modifier plugin
+ *
+ * Type: modifier<br>
+ * Name: capitalize<br>
+ * Purpose: capitalize words in the string
+ * @link http://smarty.php.net/manual/en/language.modifiers.php#LANGUAGE.MODIFIER.CAPITALIZE
+ * capitalize (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string
+ * @return string
+ */
+function smarty_modifier_capitalize($string, $uc_digits = false)
+{
+ smarty_modifier_capitalize_ucfirst(null, $uc_digits);
+ return preg_replace_callback('!\'?\b\w(\w|\')*\b!', 'smarty_modifier_capitalize_ucfirst', $string);
+}
+
+function smarty_modifier_capitalize_ucfirst($string, $uc_digits = null)
+{
+ static $_uc_digits = false;
+
+ if(isset($uc_digits)) {
+ $_uc_digits = $uc_digits;
+ return;
+ }
+
+ if(substr($string[0],0,1) != "'" && !preg_match("!\d!",$string[0]) || $_uc_digits)
+ return ucfirst($string[0]);
+ else
+ return $string[0];
+}
+
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.cat.php b/BSF/include/smarty/libs/plugins/modifier.cat.php
new file mode 100644
index 000000000..2e37940d4
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.cat.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty cat modifier plugin
+ *
+ * Type: modifier<br>
+ * Name: cat<br>
+ * Date: Feb 24, 2003
+ * Purpose: catenate a value to a variable
+ * Input: string to catenate
+ * Example: {$var|cat:"foo"}
+ * @link http://smarty.php.net/manual/en/language.modifier.cat.php cat
+ * (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @version 1.0
+ * @param string
+ * @param string
+ * @return string
+ */
+function smarty_modifier_cat($string, $cat)
+{
+ return $string . $cat;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.count_characters.php b/BSF/include/smarty/libs/plugins/modifier.count_characters.php
new file mode 100644
index 000000000..5ed9a87cd
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.count_characters.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty count_characters modifier plugin
+ *
+ * Type: modifier<br>
+ * Name: count_characteres<br>
+ * Purpose: count the number of characters in a text
+ * @link http://smarty.php.net/manual/en/language.modifier.count.characters.php
+ * count_characters (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string
+ * @param boolean include whitespace in the character count
+ * @return integer
+ */
+function smarty_modifier_count_characters($string, $include_spaces = false)
+{
+ if ($include_spaces)
+ return(strlen($string));
+
+ return preg_match_all("/[^\s]/",$string, $match);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.count_paragraphs.php b/BSF/include/smarty/libs/plugins/modifier.count_paragraphs.php
new file mode 100644
index 000000000..e0e274da7
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.count_paragraphs.php
@@ -0,0 +1,29 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty count_paragraphs modifier plugin
+ *
+ * Type: modifier<br>
+ * Name: count_paragraphs<br>
+ * Purpose: count the number of paragraphs in a text
+ * @link http://smarty.php.net/manual/en/language.modifier.count.paragraphs.php
+ * count_paragraphs (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string
+ * @return integer
+ */
+function smarty_modifier_count_paragraphs($string)
+{
+ // count \r or \n characters
+ return count(preg_split('/[\r\n]+/', $string));
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.count_sentences.php b/BSF/include/smarty/libs/plugins/modifier.count_sentences.php
new file mode 100644
index 000000000..f66ea1703
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.count_sentences.php
@@ -0,0 +1,29 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty count_sentences modifier plugin
+ *
+ * Type: modifier<br>
+ * Name: count_sentences
+ * Purpose: count the number of sentences in a text
+ * @link http://smarty.php.net/manual/en/language.modifier.count.paragraphs.php
+ * count_sentences (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string
+ * @return integer
+ */
+function smarty_modifier_count_sentences($string)
+{
+ // find periods with a word before but not after.
+ return preg_match_all('/[^\s]\.(?!\w)/', $string, $match);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.count_words.php b/BSF/include/smarty/libs/plugins/modifier.count_words.php
new file mode 100644
index 000000000..9d339f542
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.count_words.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty count_words modifier plugin
+ *
+ * Type: modifier<br>
+ * Name: count_words<br>
+ * Purpose: count the number of words in a text
+ * @link http://smarty.php.net/manual/en/language.modifier.count.words.php
+ * count_words (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string
+ * @return integer
+ */
+function smarty_modifier_count_words($string)
+{
+ // split text by ' ',\r,\n,\f,\t
+ $split_array = preg_split('/\s+/',$string);
+ // count matches that contain alphanumerics
+ $word_count = preg_grep('/[a-zA-Z0-9\\x80-\\xff]/', $split_array);
+
+ return count($word_count);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.date_format.php b/BSF/include/smarty/libs/plugins/modifier.date_format.php
new file mode 100644
index 000000000..8cf7d5e14
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.date_format.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Include the {@link shared.make_timestamp.php} plugin
+ */
+require_once $smarty->_get_plugin_filepath('shared', 'make_timestamp');
+/**
+ * Smarty date_format modifier plugin
+ *
+ * Type: modifier<br>
+ * Name: date_format<br>
+ * Purpose: format datestamps via strftime<br>
+ * Input:<br>
+ * - string: input date string
+ * - format: strftime format for output
+ * - default_date: default date if $string is empty
+ * @link http://smarty.php.net/manual/en/language.modifier.date.format.php
+ * date_format (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string
+ * @param string
+ * @param string
+ * @return string|void
+ * @uses smarty_make_timestamp()
+ */
+function smarty_modifier_date_format($string, $format = '%b %e, %Y', $default_date = '')
+{
+ if ($string != '') {
+ $timestamp = smarty_make_timestamp($string);
+ } elseif ($default_date != '') {
+ $timestamp = smarty_make_timestamp($default_date);
+ } else {
+ return;
+ }
+ if (DIRECTORY_SEPARATOR == '\\') {
+ $_win_from = array('%D', '%h', '%n', '%r', '%R', '%t', '%T');
+ $_win_to = array('%m/%d/%y', '%b', "\n", '%I:%M:%S %p', '%H:%M', "\t", '%H:%M:%S');
+ if (strpos($format, '%e') !== false) {
+ $_win_from[] = '%e';
+ $_win_to[] = sprintf('%\' 2d', date('j', $timestamp));
+ }
+ if (strpos($format, '%l') !== false) {
+ $_win_from[] = '%l';
+ $_win_to[] = sprintf('%\' 2d', date('h', $timestamp));
+ }
+ $format = str_replace($_win_from, $_win_to, $format);
+ }
+ return strftime($format, $timestamp);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.debug_print_var.php b/BSF/include/smarty/libs/plugins/modifier.debug_print_var.php
new file mode 100644
index 000000000..e4f7bc0cc
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.debug_print_var.php
@@ -0,0 +1,90 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty debug_print_var modifier plugin
+ *
+ * Type: modifier<br>
+ * Name: debug_print_var<br>
+ * Purpose: formats variable contents for display in the console
+ * @link http://smarty.php.net/manual/en/language.modifier.debug.print.var.php
+ * debug_print_var (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param array|object
+ * @param integer
+ * @param integer
+ * @return string
+ */
+function smarty_modifier_debug_print_var($var, $depth = 0, $length = 40)
+{
+ $_replace = array(
+ "\n" => '<i>\n</i>',
+ "\r" => '<i>\r</i>',
+ "\t" => '<i>\t</i>'
+ );
+
+ switch (gettype($var)) {
+ case 'array' :
+ $results = '<b>Array (' . count($var) . ')</b>';
+ foreach ($var as $curr_key => $curr_val) {
+ $results .= '<br>' . str_repeat('&nbsp;', $depth * 2)
+ . '<b>' . strtr($curr_key, $_replace) . '</b> =&gt; '
+ . smarty_modifier_debug_print_var($curr_val, ++$depth, $length);
+ $depth--;
+ }
+ break;
+ case 'object' :
+ $object_vars = get_object_vars($var);
+ $results = '<b>' . get_class($var) . ' Object (' . count($object_vars) . ')</b>';
+ foreach ($object_vars as $curr_key => $curr_val) {
+ $results .= '<br>' . str_repeat('&nbsp;', $depth * 2)
+ . '<b> -&gt;' . strtr($curr_key, $_replace) . '</b> = '
+ . smarty_modifier_debug_print_var($curr_val, ++$depth, $length);
+ $depth--;
+ }
+ break;
+ case 'boolean' :
+ case 'NULL' :
+ case 'resource' :
+ if (true === $var) {
+ $results = 'true';
+ } elseif (false === $var) {
+ $results = 'false';
+ } elseif (null === $var) {
+ $results = 'null';
+ } else {
+ $results = htmlspecialchars((string) $var);
+ }
+ $results = '<i>' . $results . '</i>';
+ break;
+ case 'integer' :
+ case 'float' :
+ $results = htmlspecialchars((string) $var);
+ break;
+ case 'string' :
+ $results = strtr($var, $_replace);
+ if (strlen($var) > $length ) {
+ $results = substr($var, 0, $length - 3) . '...';
+ }
+ $results = htmlspecialchars('"' . $results . '"');
+ break;
+ case 'unknown type' :
+ default :
+ $results = strtr((string) $var, $_replace);
+ if (strlen($results) > $length ) {
+ $results = substr($results, 0, $length - 3) . '...';
+ }
+ $results = htmlspecialchars($results);
+ }
+
+ return $results;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.default.php b/BSF/include/smarty/libs/plugins/modifier.default.php
new file mode 100644
index 000000000..70011fd6b
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.default.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty default modifier plugin
+ *
+ * Type: modifier<br>
+ * Name: default<br>
+ * Purpose: designate default value for empty variables
+ * @link http://smarty.php.net/manual/en/language.modifier.default.php
+ * default (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string
+ * @param string
+ * @return string
+ */
+function smarty_modifier_default($string, $default = '')
+{
+ if (!isset($string) || $string === '')
+ return $default;
+ else
+ return $string;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.escape.php b/BSF/include/smarty/libs/plugins/modifier.escape.php
new file mode 100644
index 000000000..a2f52b232
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.escape.php
@@ -0,0 +1,93 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty escape modifier plugin
+ *
+ * Type: modifier<br>
+ * Name: escape<br>
+ * Purpose: Escape the string according to escapement type
+ * @link http://smarty.php.net/manual/en/language.modifier.escape.php
+ * escape (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string
+ * @param html|htmlall|url|quotes|hex|hexentity|javascript
+ * @return string
+ */
+function smarty_modifier_escape($string, $esc_type = 'html', $char_set = 'ISO-8859-1')
+{
+ switch ($esc_type) {
+ case 'html':
+ return htmlspecialchars($string, ENT_QUOTES, $char_set);
+
+ case 'htmlall':
+ return htmlentities($string, ENT_QUOTES, $char_set);
+
+ case 'url':
+ return rawurlencode($string);
+
+ case 'urlpathinfo':
+ return str_replace('%2F','/',rawurlencode($string));
+
+ case 'quotes':
+ // escape unescaped single quotes
+ return preg_replace("%(?<!\\\\)'%", "\\'", $string);
+
+ case 'hex':
+ // escape every character into hex
+ $return = '';
+ for ($x=0; $x < strlen($string); $x++) {
+ $return .= '%' . bin2hex($string[$x]);
+ }
+ return $return;
+
+ case 'hexentity':
+ $return = '';
+ for ($x=0; $x < strlen($string); $x++) {
+ $return .= '&#x' . bin2hex($string[$x]) . ';';
+ }
+ return $return;
+
+ case 'decentity':
+ $return = '';
+ for ($x=0; $x < strlen($string); $x++) {
+ $return .= '&#' . ord($string[$x]) . ';';
+ }
+ return $return;
+
+ case 'javascript':
+ // escape quotes and backslashes, newlines, etc.
+ return strtr($string, array('\\'=>'\\\\',"'"=>"\\'",'"'=>'\\"',"\r"=>'\\r',"\n"=>'\\n','</'=>'<\/'));
+
+ case 'mail':
+ // safe way to display e-mail address on a web page
+ return str_replace(array('@', '.'),array(' [AT] ', ' [DOT] '), $string);
+
+ case 'nonstd':
+ // escape non-standard chars, such as ms document quotes
+ $_res = '';
+ for($_i = 0, $_len = strlen($string); $_i < $_len; $_i++) {
+ $_ord = ord(substr($string, $_i, 1));
+ // non-standard char, escape it
+ if($_ord >= 126){
+ $_res .= '&#' . $_ord . ';';
+ }
+ else {
+ $_res .= substr($string, $_i, 1);
+ }
+ }
+ return $_res;
+
+ default:
+ return $string;
+ }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.indent.php b/BSF/include/smarty/libs/plugins/modifier.indent.php
new file mode 100644
index 000000000..394147a29
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.indent.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty indent modifier plugin
+ *
+ * Type: modifier<br>
+ * Name: indent<br>
+ * Purpose: indent lines of text
+ * @link http://smarty.php.net/manual/en/language.modifier.indent.php
+ * indent (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string
+ * @param integer
+ * @param string
+ * @return string
+ */
+function smarty_modifier_indent($string,$chars=4,$char=" ")
+{
+ return preg_replace('!^!m',str_repeat($char,$chars),$string);
+}
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.lower.php b/BSF/include/smarty/libs/plugins/modifier.lower.php
new file mode 100644
index 000000000..20e7a8d39
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.lower.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty lower modifier plugin
+ *
+ * Type: modifier<br>
+ * Name: lower<br>
+ * Purpose: convert string to lowercase
+ * @link http://smarty.php.net/manual/en/language.modifier.lower.php
+ * lower (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string
+ * @return string
+ */
+function smarty_modifier_lower($string)
+{
+ return strtolower($string);
+}
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.nl2br.php b/BSF/include/smarty/libs/plugins/modifier.nl2br.php
new file mode 100644
index 000000000..d6fabff64
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.nl2br.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty plugin
+ *
+ * Type: modifier<br>
+ * Name: nl2br<br>
+ * Date: Feb 26, 2003
+ * Purpose: convert \r\n, \r or \n to <<br>>
+ * Input:<br>
+ * - contents = contents to replace
+ * - preceed_test = if true, includes preceeding break tags
+ * in replacement
+ * Example: {$text|nl2br}
+ * @link http://smarty.php.net/manual/en/language.modifier.nl2br.php
+ * nl2br (Smarty online manual)
+ * @version 1.0
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string
+ * @return string
+ */
+function smarty_modifier_nl2br($string)
+{
+ return nl2br($string);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.regex_replace.php b/BSF/include/smarty/libs/plugins/modifier.regex_replace.php
new file mode 100644
index 000000000..d1f1545d8
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.regex_replace.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty regex_replace modifier plugin
+ *
+ * Type: modifier<br>
+ * Name: regex_replace<br>
+ * Purpose: regular expression search/replace
+ * @link http://smarty.php.net/manual/en/language.modifier.regex.replace.php
+ * regex_replace (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string
+ * @param string|array
+ * @param string|array
+ * @return string
+ */
+function smarty_modifier_regex_replace($string, $search, $replace)
+{
+ if (($pos = strpos($search,"\0")) !== false)
+ $search = substr($search,0,$pos);
+ if (preg_match('!([a-zA-Z\s]+)$!s', $search, $match) && (strpos($match[1], 'e') !== false)) {
+ /* remove eval-modifier from $search */
+ $search = substr($search, 0, -strlen($match[1])) . preg_replace('![e\s]+!', '', $match[1]);
+ }
+
+ return preg_replace($search, $replace, $string);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.replace.php b/BSF/include/smarty/libs/plugins/modifier.replace.php
new file mode 100644
index 000000000..df041c884
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.replace.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty replace modifier plugin
+ *
+ * Type: modifier<br>
+ * Name: replace<br>
+ * Purpose: simple search/replace
+ * @link http://smarty.php.net/manual/en/language.modifier.replace.php
+ * replace (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string
+ * @param string
+ * @param string
+ * @return string
+ */
+function smarty_modifier_replace($string, $search, $replace)
+{
+ return str_replace($search, $replace, $string);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.spacify.php b/BSF/include/smarty/libs/plugins/modifier.spacify.php
new file mode 100644
index 000000000..c2423f4f2
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.spacify.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty spacify modifier plugin
+ *
+ * Type: modifier<br>
+ * Name: spacify<br>
+ * Purpose: add spaces between characters in a string
+ * @link http://smarty.php.net/manual/en/language.modifier.spacify.php
+ * spacify (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string
+ * @param string
+ * @return string
+ */
+function smarty_modifier_spacify($string, $spacify_char = ' ')
+{
+ return implode($spacify_char,
+ preg_split('//', $string, -1, PREG_SPLIT_NO_EMPTY));
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.string_format.php b/BSF/include/smarty/libs/plugins/modifier.string_format.php
new file mode 100644
index 000000000..9e051a578
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.string_format.php
@@ -0,0 +1,29 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty string_format modifier plugin
+ *
+ * Type: modifier<br>
+ * Name: string_format<br>
+ * Purpose: format strings via sprintf
+ * @link http://smarty.php.net/manual/en/language.modifier.string.format.php
+ * string_format (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string
+ * @param string
+ * @return string
+ */
+function smarty_modifier_string_format($string, $format)
+{
+ return sprintf($format, $string);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.strip.php b/BSF/include/smarty/libs/plugins/modifier.strip.php
new file mode 100644
index 000000000..cc5c453c8
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.strip.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty strip modifier plugin
+ *
+ * Type: modifier<br>
+ * Name: strip<br>
+ * Purpose: Replace all repeated spaces, newlines, tabs
+ * with a single space or supplied replacement string.<br>
+ * Example: {$var|strip} {$var|strip:"&nbsp;"}
+ * Date: September 25th, 2002
+ * @link http://smarty.php.net/manual/en/language.modifier.strip.php
+ * strip (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @version 1.0
+ * @param string
+ * @param string
+ * @return string
+ */
+function smarty_modifier_strip($text, $replace = ' ')
+{
+ return preg_replace('!\s+!', $replace, $text);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.strip_tags.php b/BSF/include/smarty/libs/plugins/modifier.strip_tags.php
new file mode 100644
index 000000000..93011a892
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.strip_tags.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty strip_tags modifier plugin
+ *
+ * Type: modifier<br>
+ * Name: strip_tags<br>
+ * Purpose: strip html tags from text
+ * @link http://smarty.php.net/manual/en/language.modifier.strip.tags.php
+ * strip_tags (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string
+ * @param boolean
+ * @return string
+ */
+function smarty_modifier_strip_tags($string, $replace_with_space = true)
+{
+ if ($replace_with_space)
+ return preg_replace('!<[^>]*?>!', ' ', $string);
+ else
+ return strip_tags($string);
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.truncate.php b/BSF/include/smarty/libs/plugins/modifier.truncate.php
new file mode 100644
index 000000000..35c89690a
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.truncate.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty truncate modifier plugin
+ *
+ * Type: modifier<br>
+ * Name: truncate<br>
+ * Purpose: Truncate a string to a certain length if necessary,
+ * optionally splitting in the middle of a word, and
+ * appending the $etc string or inserting $etc into the middle.
+ * @link http://smarty.php.net/manual/en/language.modifier.truncate.php
+ * truncate (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string
+ * @param integer
+ * @param string
+ * @param boolean
+ * @param boolean
+ * @return string
+ */
+function smarty_modifier_truncate($string, $length = 80, $etc = '...',
+ $break_words = false, $middle = false)
+{
+ if ($length == 0)
+ return '';
+
+ if (strlen($string) > $length) {
+ $length -= min($length, strlen($etc));
+ if (!$break_words && !$middle) {
+ $string = preg_replace('/\s+?(\S+)?$/', '', substr($string, 0, $length+1));
+ }
+ if(!$middle) {
+ return substr($string, 0, $length) . $etc;
+ } else {
+ return substr($string, 0, $length/2) . $etc . substr($string, -$length/2);
+ }
+ } else {
+ return $string;
+ }
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.upper.php b/BSF/include/smarty/libs/plugins/modifier.upper.php
new file mode 100644
index 000000000..c12480fbd
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.upper.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty upper modifier plugin
+ *
+ * Type: modifier<br>
+ * Name: upper<br>
+ * Purpose: convert string to uppercase
+ * @link http://smarty.php.net/manual/en/language.modifier.upper.php
+ * upper (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string
+ * @return string
+ */
+function smarty_modifier_upper($string)
+{
+ return strtoupper($string);
+}
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/modifier.wordwrap.php b/BSF/include/smarty/libs/plugins/modifier.wordwrap.php
new file mode 100644
index 000000000..ce0718156
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/modifier.wordwrap.php
@@ -0,0 +1,29 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Smarty wordwrap modifier plugin
+ *
+ * Type: modifier<br>
+ * Name: wordwrap<br>
+ * Purpose: wrap a string of text at a given length
+ * @link http://smarty.php.net/manual/en/language.modifier.wordwrap.php
+ * wordwrap (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string
+ * @param integer
+ * @param string
+ * @param boolean
+ * @return string
+ */
+function smarty_modifier_wordwrap($string,$length=80,$break="\n",$cut=false)
+{
+ return wordwrap($string,$length,$break,$cut);
+}
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/outputfilter.trimwhitespace.php b/BSF/include/smarty/libs/plugins/outputfilter.trimwhitespace.php
new file mode 100644
index 000000000..97b0d21e8
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/outputfilter.trimwhitespace.php
@@ -0,0 +1,75 @@
+<?php
+/**
+ * Smarty plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+/**
+ * Smarty trimwhitespace outputfilter plugin
+ *
+ * File: outputfilter.trimwhitespace.php<br>
+ * Type: outputfilter<br>
+ * Name: trimwhitespace<br>
+ * Date: Jan 25, 2003<br>
+ * Purpose: trim leading white space and blank lines from
+ * template source after it gets interpreted, cleaning
+ * up code and saving bandwidth. Does not affect
+ * <<PRE>></PRE> and <SCRIPT></SCRIPT> blocks.<br>
+ * Install: Drop into the plugin directory, call
+ * <code>$smarty->load_filter('output','trimwhitespace');</code>
+ * from application.
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @author Contributions from Lars Noschinski <lars@usenet.noschinski.de>
+ * @version 1.3
+ * @param string
+ * @param Smarty
+ */
+function smarty_outputfilter_trimwhitespace($source, &$smarty)
+{
+ // Pull out the script blocks
+ preg_match_all("!<script[^>]+>.*?</script>!is", $source, $match);
+ $_script_blocks = $match[0];
+ $source = preg_replace("!<script[^>]+>.*?</script>!is",
+ '@@@SMARTY:TRIM:SCRIPT@@@', $source);
+
+ // Pull out the pre blocks
+ preg_match_all("!<pre>.*?</pre>!is", $source, $match);
+ $_pre_blocks = $match[0];
+ $source = preg_replace("!<pre>.*?</pre>!is",
+ '@@@SMARTY:TRIM:PRE@@@', $source);
+
+ // Pull out the textarea blocks
+ preg_match_all("!<textarea[^>]+>.*?</textarea>!is", $source, $match);
+ $_textarea_blocks = $match[0];
+ $source = preg_replace("!<textarea[^>]+>.*?</textarea>!is",
+ '@@@SMARTY:TRIM:TEXTAREA@@@', $source);
+
+ // remove all leading spaces, tabs and carriage returns NOT
+ // preceeded by a php close tag.
+ $source = trim(preg_replace('/((?<!\?>)\n)[\s]+/m', '\1', $source));
+
+ // replace textarea blocks
+ smarty_outputfilter_trimwhitespace_replace("@@@SMARTY:TRIM:TEXTAREA@@@",$_textarea_blocks, $source);
+
+ // replace pre blocks
+ smarty_outputfilter_trimwhitespace_replace("@@@SMARTY:TRIM:PRE@@@",$_pre_blocks, $source);
+
+ // replace script blocks
+ smarty_outputfilter_trimwhitespace_replace("@@@SMARTY:TRIM:SCRIPT@@@",$_script_blocks, $source);
+
+ return $source;
+}
+
+function smarty_outputfilter_trimwhitespace_replace($search_str, $replace, &$subject) {
+ $_len = strlen($search_str);
+ $_pos = 0;
+ for ($_i=0, $_count=count($replace); $_i<$_count; $_i++)
+ if (($_pos=strpos($subject, $search_str, $_pos))!==false)
+ $subject = substr_replace($subject, $replace[$_i], $_pos, $_len);
+ else
+ break;
+
+}
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/shared.escape_special_chars.php b/BSF/include/smarty/libs/plugins/shared.escape_special_chars.php
new file mode 100644
index 000000000..c07ce31be
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/shared.escape_special_chars.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Smarty shared plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * escape_special_chars common function
+ *
+ * Function: smarty_function_escape_special_chars<br>
+ * Purpose: used by other smarty functions to escape
+ * special chars except for already escaped ones
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string
+ * @return string
+ */
+function smarty_function_escape_special_chars($string)
+{
+ if(!is_array($string)) {
+ $string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string);
+ $string = htmlspecialchars($string);
+ $string = str_replace(array('%%%SMARTY_START%%%','%%%SMARTY_END%%%'), array('&',';'), $string);
+ }
+ return $string;
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/smarty/libs/plugins/shared.make_timestamp.php b/BSF/include/smarty/libs/plugins/shared.make_timestamp.php
new file mode 100644
index 000000000..b42eb11d8
--- /dev/null
+++ b/BSF/include/smarty/libs/plugins/shared.make_timestamp.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * Smarty shared plugin
+ * @package Smarty
+ * @subpackage plugins
+ */
+
+
+/**
+ * Function: smarty_make_timestamp<br>
+ * Purpose: used by other smarty functions to make a timestamp
+ * from a string.
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string
+ * @return string
+ */
+function smarty_make_timestamp($string)
+{
+ if(empty($string)) {
+ // use "now":
+ $time = time();
+
+ } elseif (preg_match('/^\d{14}$/', $string)) {
+ // it is mysql timestamp format of YYYYMMDDHHMMSS?
+ $time = mktime(substr($string, 8, 2),substr($string, 10, 2),substr($string, 12, 2),
+ substr($string, 4, 2),substr($string, 6, 2),substr($string, 0, 4));
+
+ } elseif (is_numeric($string)) {
+ // it is a numeric string, we handle it as timestamp
+ $time = (int)$string;
+
+ } else {
+ // strtotime should handle it
+ $time = strtotime($string);
+ if ($time == -1 || $time === false) {
+ // strtotime() was not able to parse $string, use "now":
+ $time = time();
+ }
+ }
+ return $time;
+
+}
+
+/* vim: set expandtab: */
+
+?>
diff --git a/BSF/include/template.class.php b/BSF/include/template.class.php
new file mode 100644
index 000000000..1fd3c94ac
--- /dev/null
+++ b/BSF/include/template.class.php
@@ -0,0 +1,385 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+
+require_once 'smarty/libs/Smarty.class.php';
+
+// migrate lang:XXX
+// sed "s/{lang:\([^}]\+\)}/{\'\1\'|@translate}/g" my_template.tpl
+// migrate change root level vars {XXX}
+// sed "s/{pwg_root}/{ROOT_URL}/g" my_template.tpl
+// migrate change root level vars {XXX}
+// sed "s/{\([a-zA-Z_]\+\)}/{$\1}/g" my_template.tpl
+// migrate all
+// cat my_template.tpl | sed "s/{lang:\([^}]\+\)}/{\'\1\'|@translate}/g" | sed "s/{pwg_root}/{ROOT_URL}/g" | sed "s/{\([a-zA-Z_]\+\)}/{$\1}/g"
+
+
+class Template {
+
+ var $smarty;
+
+ var $output = '';
+
+ // Hash of filenames for each template handle.
+ var $files = array();
+
+ // used by html_head smarty block to add content before </head>
+ var $html_head_elements = array();
+
+ function Template($root = ".", $theme= "")
+ {
+ global $conf;
+
+ $this->smarty = new Smarty;
+ $this->smarty->debugging = $conf['debug_template'];
+
+ if ( isset($conf['compiled_template_dir'] ) )
+ {
+ $compile_dir = $conf['compiled_template_dir'];
+ }
+ else
+ {
+ $compile_dir = $conf['local_data_dir'];
+ if ( !is_dir($compile_dir) )
+ {
+ mkdir( $compile_dir, 0777);
+ file_put_contents($compile_dir.'/index.htm', '');
+ }
+ $compile_dir .= '/templates_c';
+ }
+ if ( !is_dir($compile_dir) )
+ {
+ mkdir( $compile_dir, 0777 );
+ file_put_contents($compile_dir.'/index.htm', '');
+ }
+
+ $this->smarty->compile_dir = $compile_dir;
+
+ $this->smarty->assign_by_ref( 'pwg', new PwgTemplateAdapter() );
+ $this->smarty->register_modifier( 'translate', array('Template', 'mod_translate') );
+ $this->smarty->register_modifier( 'explode', array('Template', 'mod_explode') );
+ $this->smarty->register_block('html_head', array(&$this, 'block_html_head') );
+ if ( $conf['compiled_template_cache_language'] )
+ {
+ $this->smarty->register_prefilter( array(&$this, 'prefilter_language') );
+ }
+
+ if ( !empty($theme) )
+ {
+ include($root.'/theme/'.$theme.'/themeconf.inc.php');
+ $this->smarty->assign('themeconf', $themeconf);
+ }
+
+ $this->set_template_dir($root);
+ }
+
+ /**
+ * Sets the template root directory for this Template object.
+ */
+ function set_template_dir($dir)
+ {
+ $this->smarty->template_dir = $dir;
+
+ $real_dir = realpath($dir);
+ $compile_id = crc32( $real_dir===false ? $dir : $real_dir);
+ $this->smarty->compile_id = base_convert($compile_id, 10, 36 );
+ }
+
+ /**
+ * Gets the template root directory for this Template object.
+ */
+ function get_template_dir()
+ {
+ return $this->smarty->template_dir;
+ }
+
+ /**
+ * Deletes all compiled templates.
+ */
+ function delete_compiled_templates()
+ {
+ $save_compile_id = $this->smarty->compile_id;
+ $this->smarty->compile_id = null;
+ $this->smarty->clear_compiled_tpl();
+ $this->smarty->compile_id = $save_compile_id;
+ file_put_contents($this->smarty->compile_dir.'/index.htm', '');
+ }
+
+ function get_themeconf($val)
+ {
+ $tc = $this->smarty->get_template_vars('themeconf');
+ return isset($tc[$val]) ? $tc[$val] : '';
+ }
+
+ /**
+ * Sets the template filename for handle.
+ */
+ function set_filename($handle, $filename)
+ {
+ return $this->set_filenames( array($handle=>$filename) );
+ }
+
+ /**
+ * Sets the template filenames for handles. $filename_array should be a
+ * hash of handle => filename pairs.
+ */
+ function set_filenames($filename_array)
+ {
+ if (!is_array($filename_array))
+ {
+ return false;
+ }
+
+ reset($filename_array);
+ while(list($handle, $filename) = each($filename_array))
+ {
+ if (is_null($filename))
+ unset( $this->files[$handle] );
+ else
+ $this->files[$handle] = $filename;
+ }
+ return true;
+ }
+
+ /** see smarty assign http://www.smarty.net/manual/en/api.assign.php */
+ function assign($tpl_var, $value = null)
+ {
+ $this->smarty->assign( $tpl_var, $value );
+ }
+
+ /**
+ * Inserts the uncompiled code for $handle as the value of $varname in the
+ * root-level. This can be used to effectively include a template in the
+ * middle of another template.
+ * This is equivalent to assign($varname, $this->parse($handle, true))
+ */
+ function assign_var_from_handle($varname, $handle)
+ {
+ $this->assign($varname, $this->parse($handle, true));
+ return true;
+ }
+
+ /** see smarty append http://www.smarty.net/manual/en/api.append.php */
+ function append($tpl_var, $value=null, $merge=false)
+ {
+ $this->smarty->append( $tpl_var, $value, $merge );
+ }
+
+ /**
+ * Root-level variable concatenation. Appends a string to an existing
+ * variable assignment with the same name.
+ */
+ function concat($tpl_var, $value)
+ {
+ $old_val = & $this->smarty->get_template_vars($tpl_var);
+ if ( isset($old_val) )
+ {
+ $old_val .= $value;
+ }
+ else
+ {
+ $this->assign($tpl_var, $value);
+ }
+ }
+
+ /** see smarty append http://www.smarty.net/manual/en/api.clear_assign.php */
+ function clear_assign($tpl_var)
+ {
+ $this->smarty->clear_assign( $tpl_var );
+ }
+
+ /** see smarty get_template_vars http://www.smarty.net/manual/en/api.get_template_vars.php */
+ function &get_template_vars($name=null)
+ {
+ return $this->smarty->get_template_vars( $name );
+ }
+
+
+ /**
+ * Load the file for the handle, eventually compile the file and run the compiled
+ * code. This will add the output to the results or return the result if $return
+ * is true.
+ */
+ function parse($handle, $return=false)
+ {
+ if ( !isset($this->files[$handle]) )
+ {
+ die("Template->parse(): Couldn't load template file for handle $handle");
+ }
+
+ $this->smarty->assign( 'ROOT_URL', get_root_url() );
+ $this->smarty->assign( 'TAG_INPUT_ENABLED',
+ ((is_adviser()) ? 'disabled="disabled" onclick="return false;"' : ''));
+
+ global $conf, $lang_info;
+ if ( $conf['compiled_template_cache_language'] and isset($lang_info['code']) )
+ {
+ $save_compile_id = $this->smarty->compile_id;
+ $this->smarty->compile_id .= '.'.$lang_info['code'];
+ }
+
+ $v = $this->smarty->fetch($this->files[$handle], null, null, false);
+
+ if (isset ($save_compile_id) )
+ {
+ $this->smarty->compile_id = $save_compile_id;
+ }
+
+ if ($return)
+ {
+ return $v;
+ }
+ $this->output .= $v;
+ }
+
+ /**
+ * Load the file for the handle, eventually compile the file and run the compiled
+ * code. This will print out the results of executing the template.
+ */
+ function pparse($handle)
+ {
+ $this->parse($handle, false);
+ $this->flush();
+ }
+
+ function flush()
+ {
+ if ( count($this->html_head_elements) )
+ {
+ $search = "\n</head>";
+ $pos = strpos( $this->output, $search );
+ if ($pos !== false)
+ {
+ $this->output = substr_replace( $this->output, "\n".implode( "\n", $this->html_head_elements ), $pos, 0 );
+ } //else maybe error or warning ?
+ $this->html_head_elements = array();
+ }
+ echo $this->output;
+ $this->output='';
+ }
+
+ /** flushes the output */
+ function p()
+ {
+ $start = get_moment();
+
+ $this->flush();
+
+ if ($this->smarty->debugging)
+ {
+ global $t2;
+ $this->smarty->assign(
+ array(
+ 'AAAA_DEBUG_OUTPUT_TIME__' => get_elapsed_time($start, get_moment()),
+ 'AAAA_DEBUG_TOTAL_TIME__' => get_elapsed_time($t2, get_moment())
+ )
+ );
+ require_once(SMARTY_CORE_DIR . 'core.display_debug_console.php');
+ echo smarty_core_display_debug_console(null, $this->smarty);
+ }
+ }
+
+ /**
+ * translate variable modifier - translates a text to the currently loaded
+ * language
+ */
+ /*static*/ function mod_translate($text)
+ {
+ return l10n($text);
+ }
+
+ /**
+ * explode variable modifier - similar to php explode
+ * 'Yes;No'|@explode:';' -> array('Yes', 'No')
+ */
+ /*static*/ function mod_explode($text, $delimiter=',')
+ {
+ return explode($delimiter, $text);
+ }
+
+ /**
+ * This smarty "html_head" block allows to add content just before
+ * </head> element in the output after the head has been parsed. This is
+ * handy in order to respect strict standards when <style> and <link>
+ * html elements must appear in the <head> element
+ */
+ function block_html_head($params, $content, &$smarty, &$repeat)
+ {
+ $content = trim($content);
+ if ( !empty($content) )
+ { // second call
+ if ( empty($this->output) )
+ {//page header not parsed yet
+ $this->append('head_elements', $content);
+ }
+ else
+ {
+ $this->html_head_elements[] = $content;
+ }
+ }
+ }
+
+ /**
+ * Smarty prefilter to allow caching (whenever possible) language strings
+ * from templates.
+ */
+ function prefilter_language($source, &$smarty)
+ {
+ global $lang;
+ $ldq = preg_quote($this->smarty->left_delimiter, '~');
+ $rdq = preg_quote($this->smarty->right_delimiter, '~');
+
+ $regex = "~$ldq *\'([^'$]+)\'\|@translate *$rdq~";
+ $source = preg_replace( $regex.'e', 'isset($lang[\'$1\']) ? $lang[\'$1\'] : \'$0\'', $source);
+
+ $regex = "~$ldq *\'([^'$]+)\'\|@translate\|~";
+ $source = preg_replace( $regex.'e', 'isset($lang[\'$1\']) ? \'{\'.var_export($lang[\'$1\'],true).\'|\' : \'$0\'', $source);
+
+ return $source;
+ }
+}
+
+/**
+ * This class contains basic functions that can be called directly from the
+ * templates in the form $pwg->l10n('edit')
+ */
+class PwgTemplateAdapter
+{
+ function l10n($text)
+ {
+ return l10n($text);
+ }
+
+ function l10n_dec($s, $p, $v)
+ {
+ return l10n_dec($s, $p, $v);
+ }
+
+ function sprintf()
+ {
+ $args = func_get_args();
+ return call_user_func_array('sprintf', $args );
+ }
+}
+
+?>
diff --git a/BSF/include/upload.class.php b/BSF/include/upload.class.php
new file mode 100644
index 000000000..f9587ddb8
--- /dev/null
+++ b/BSF/include/upload.class.php
@@ -0,0 +1,41 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+class Upload
+{
+ function Upload()
+ {
+ }
+
+ /**
+ * Return list of Uploadable categories
+ *
+ * @param void
+ * @return array
+ */
+ function get_cat()
+ {
+ }
+}
+
+?>
diff --git a/BSF/include/user.inc.php b/BSF/include/user.inc.php
new file mode 100644
index 000000000..b267790f8
--- /dev/null
+++ b/BSF/include/user.inc.php
@@ -0,0 +1,74 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+// by default we start with guest
+$user['id'] = $conf['guest_id'];
+
+if (isset($_COOKIE[session_name()]))
+{
+ session_start();
+ if (isset($_GET['act']) and $_GET['act'] == 'logout')
+ { // logout
+ $_SESSION = array();
+ session_unset();
+ session_destroy();
+ setcookie(session_name(),'',0,
+ ini_get('session.cookie_path'),
+ ini_get('session.cookie_domain')
+ );
+ setcookie($conf['remember_me_name'], '', 0, cookie_path());
+ redirect(make_index_url());
+ }
+ elseif (!empty($_SESSION['pwg_uid']))
+ {
+ $user['id'] = $_SESSION['pwg_uid'];
+ }
+}
+
+// Now check the auto-login
+if ( $user['id']==$conf['guest_id'] )
+{
+ auto_login();
+}
+
+if (session_id()=="")
+{
+ session_start();
+}
+
+// using Apache authentication override the above user search
+if ($conf['apache_authentication'] and isset($_SERVER['REMOTE_USER']))
+{
+ if (!($user['id'] = get_userid($_SERVER['REMOTE_USER'])))
+ {
+ register_user($_SERVER['REMOTE_USER'], '', '', false);
+ $user['id'] = get_userid($_SERVER['REMOTE_USER']);
+ }
+}
+
+$user = build_user( $user['id'],
+ ( defined('IN_ADMIN') and IN_ADMIN ) ? false : true // use cache ?
+ );
+
+trigger_action('user_init', $user);
+?>
diff --git a/BSF/include/ws_core.inc.php b/BSF/include/ws_core.inc.php
new file mode 100644
index 000000000..c78d4f402
--- /dev/null
+++ b/BSF/include/ws_core.inc.php
@@ -0,0 +1,619 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+/**** WEB SERVICE CORE CLASSES************************************************
+ * PwgServer - main object - the link between web service methods, request
+ * handler and response encoder
+ * PwgRequestHandler - base class for handlers
+ * PwgResponseEncoder - base class for response encoders
+ * PwgError, PwgNamedArray, PwgNamedStruct - can be used by web service functions
+ * as return values
+ */
+
+
+define( 'WS_PARAM_ACCEPT_ARRAY', 0x010000 );
+define( 'WS_PARAM_FORCE_ARRAY', 0x030000 );
+define( 'WS_PARAM_OPTIONAL', 0x040000 );
+
+define( 'WS_ERR_INVALID_METHOD', 1001 );
+define( 'WS_ERR_MISSING_PARAM', 1002 );
+define( 'WS_ERR_INVALID_PARAM', 1003 );
+
+define( 'WS_XML_ATTRIBUTES', 'attributes_xml_');
+define( 'WS_XML_CONTENT', 'content_xml_');
+
+/**
+ * PwgError object can be returned from any web service function implementation.
+ */
+class PwgError
+{
+ var $_code;
+ var $_codeText;
+
+ function PwgError($code, $codeText)
+ {
+ $this->_code = $code;
+ $this->_codeText = $codeText;
+ }
+
+ function code() { return $this->_code; }
+ function message() { return $this->_codeText; }
+}
+
+/**
+ * Simple wrapper around an array (keys are consecutive integers starting at 0).
+ * Provides naming clues for xml output (xml attributes vs. xml child elements?)
+ * Usually returned by web service function implementation.
+ */
+class PwgNamedArray
+{
+ /*private*/ var $_content;
+ /*private*/ var $_itemName;
+ /*private*/ var $_xmlAttributes;
+
+ /**
+ * Constructs a named array
+ * @param arr array (keys must be consecutive integers starting at 0)
+ * @param itemName string xml element name for values of arr (e.g. image)
+ * @param xmlAttributes array of sub-item attributes that will be encoded as
+ * xml attributes instead of xml child elements
+ */
+ function PwgNamedArray(&$arr, $itemName, $xmlAttributes=array() )
+ {
+ $this->_content = $arr;
+ $this->_itemName = $itemName;
+ $this->_xmlAttributes = array_flip($xmlAttributes);
+ }
+}
+/**
+ * Simple wrapper around a "struct" (php array whose keys are not consecutive
+ * integers starting at 0). Provides naming clues for xml output (what is xml
+ * attributes and what is element)
+ */
+class PwgNamedStruct
+{
+ /*private*/ var $_content;
+ /*private*/ var $_name;
+ /*private*/ var $_xmlAttributes;
+
+ /**
+ * Constructs a named struct (usually returned by web service function
+ * implementation)
+ * @param name string - containing xml element name
+ * @param content array - the actual content (php array)
+ * @param xmlAttributes array - name of the keys in $content that will be
+ * encoded as xml attributes (if null - automatically prefer xml attributes
+ * whenever possible)
+ */
+ function PwgNamedStruct($name, $content, $xmlAttributes=null, $xmlElements=null )
+ {
+ $this->_name = $name;
+ $this->_content = $content;
+ if ( isset($xmlAttributes) )
+ {
+ $this->_xmlAttributes = array_flip($xmlAttributes);
+ }
+ else
+ {
+ $this->_xmlAttributes = array();
+ foreach ($this->_content as $key=>$value)
+ {
+ if (!empty($key) and (is_scalar($value) or is_null($value)) )
+ {
+ if ( empty($xmlElements) or !in_array($key,$xmlElements) )
+ {
+ $this->_xmlAttributes[$key]=1;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ * Replace array_walk_recursive()
+ *
+ * @category PHP
+ * @package PHP_Compat
+ * @link http://php.net/function.array_walk_recursive
+ * @author Tom Buskens <ortega@php.net>
+ * @author Aidan Lister <aidan@php.net>
+ * @version $Revision$
+ * @since PHP 5
+ * @require PHP 4.0.6 (is_callable)
+ */
+if (!function_exists('array_walk_recursive')) {
+ function array_walk_recursive(&$input, $funcname)
+ {
+ if (!is_callable($funcname)) {
+ if (is_array($funcname)) {
+ $funcname = $funcname[0] . '::' . $funcname[1];
+ }
+ user_error('array_walk_recursive() Not a valid callback ' . $user_func,
+ E_USER_WARNING);
+ return;
+ }
+
+ if (!is_array($input)) {
+ user_error('array_walk_recursive() The argument should be an array',
+ E_USER_WARNING);
+ return;
+ }
+
+ $args = func_get_args();
+
+ foreach ($input as $key => $item) {
+ if (is_array($item)) {
+ array_walk_recursive($item, $funcname, $args);
+ $input[$key] = $item;
+ } else {
+ $args[0] = &$item;
+ $args[1] = &$key;
+ call_user_func_array($funcname, $args);
+ $input[$key] = $item;
+ }
+ }
+ }
+}
+
+/**
+ * Abstract base class for request handlers.
+ */
+class PwgRequestHandler
+{
+ /** Virtual abstract method. Decodes the request (GET or POST) handles the
+ * method invocation as well as response sending.
+ */
+ function handleRequest(&$server) { assert(false); }
+}
+
+/**
+ *
+ * Base class for web service response encoder.
+ */
+class PwgResponseEncoder
+{
+ /** encodes the web service response to the appropriate output format
+ * @param response mixed the unencoded result of a service method call
+ */
+ function encodeResponse($response) { assert(false); }
+
+ /** default "Content-Type" http header for this kind of response format
+ */
+ function getContentType() { assert(false); }
+
+ /**
+ * returns true if the parameter is a 'struct' (php array type whose keys are
+ * NOT consecutive integers starting with 0)
+ */
+ function is_struct(&$data)
+ {
+ if (is_array($data) )
+ {
+ if (range(0, count($data) - 1) !== array_keys($data) )
+ { # string keys, unordered, non-incremental keys, .. - whatever, make object
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * removes all XML formatting from $response (named array, named structs, etc)
+ * usually called by every response encoder, except rest xml.
+ */
+ function flattenResponse(&$response)
+ {
+ PwgResponseEncoder::_mergeAttributesAndContent($response);
+ PwgResponseEncoder::_removeNamedArray($response);
+ PwgResponseEncoder::_removeNamedStruct($response);
+ if (is_array($response))
+ { // need to call 2 times (first time might add new arrays)
+ array_walk_recursive($response, array('PwgResponseEncoder', '_remove_named_callback') );
+ array_walk_recursive($response, array('PwgResponseEncoder', '_remove_named_callback') );
+ }
+//print_r($response);
+ PwgResponseEncoder::_mergeAttributesAndContent($response);
+ }
+
+ /*private*/ function _remove_named_callback(&$value, $key)
+ {
+ do
+ {
+ $changed = 0;
+ $changed += PwgResponseEncoder::_removeNamedArray($value);
+ $changed += PwgResponseEncoder::_removeNamedStruct($value);
+ // print_r('walk '.$key."<br/>\n");
+ }
+ while ($changed);
+ }
+
+ /*private*/ function _mergeAttributesAndContent(&$value)
+ {
+ if ( !is_array($value) )
+ return;
+/* $first_key = '';
+ if (count($value)) { $ak = array_keys($value); $first_key = $ak[0]; }
+
+ print_r( '_mergeAttributesAndContent is_struct='.PwgResponseEncoder::is_struct($value)
+ .' count='.count($value)
+ .' first_key='.$first_key
+ ."<br/>\n"
+ );*/
+ $ret = 0;
+ if (PwgResponseEncoder::is_struct($value))
+ {
+ if ( isset($value[WS_XML_ATTRIBUTES]) )
+ {
+ $value = array_merge( $value, $value[WS_XML_ATTRIBUTES] );
+ unset( $value[WS_XML_ATTRIBUTES] );
+ $ret=1;
+ }
+ if ( isset($value[WS_XML_CONTENT]) )
+ {
+ $cont_processed = 0;
+ if ( count($value)==1 )
+ {
+ $value = $value[WS_XML_CONTENT];
+ $cont_processed=1;
+ }
+ else
+ {
+ if (PwgResponseEncoder::is_struct($value[WS_XML_CONTENT]))
+ {
+ $value = array_merge( $value, $value[WS_XML_CONTENT] );
+ unset( $value[WS_XML_CONTENT] );
+ $cont_processed=1;
+ }
+ }
+ $ret += $cont_processed;
+ if (!$cont_processed)
+ {
+ $value['_content'] = $value[WS_XML_CONTENT];
+ unset( $value[WS_XML_CONTENT] );
+ $ret++;
+ }
+ }
+ }
+
+ foreach ($value as $key=>$v)
+ {
+ if ( PwgResponseEncoder::_mergeAttributesAndContent($v) )
+ {
+ $value[$key]=$v;
+ $ret++;
+ }
+ }
+ return $ret;
+ }
+
+ /*private*/ function _removeNamedArray(&$value)
+ {
+ if ( strtolower( get_class($value) ) =='pwgnamedarray')
+ {
+ $value = $value->_content;
+ return 1;
+ }
+ return 0;
+ }
+
+ /*private*/ function _removeNamedStruct(&$value)
+ {
+ if ( strtolower( get_class($value) ) =='pwgnamedstruct')
+ {
+ if ( isset($value->_content['']) )
+ {
+ $unknown = $value->_content[''];
+ unset( $value->_content[''] );
+ $value->_content[$value->_name] = $unknown;
+ }
+ $value = $value->_content;
+ return 1;
+ }
+ return 0;
+ }
+}
+
+
+
+class PwgServer
+{
+ var $_requestHandler;
+ var $_requestFormat;
+ var $_responseEncoder;
+ var $_responseFormat;
+
+ var $_methods;
+ var $_methodSignatures;
+
+ function PwgServer()
+ {
+ $methods = array();
+ }
+
+ /**
+ * Initializes the request handler.
+ */
+ function setHandler($requestFormat, &$requestHandler)
+ {
+ $this->_requestHandler = &$requestHandler;
+ $this->_requestFormat = $requestFormat;
+ }
+
+ /**
+ * Initializes the request handler.
+ */
+ function setEncoder($responseFormat, &$encoder)
+ {
+ $this->_responseEncoder = &$encoder;
+ $this->_responseFormat = $responseFormat;
+ }
+
+ /**
+ * Runs the web service call (handler and response encoder should have been
+ * created)
+ */
+ function run()
+ {
+ if ( is_null($this->_responseEncoder) )
+ {
+ set_status_header(500);
+ @header("Content-Type: text/plain");
+ echo ("Cannot process your request. Unknown response format.
+Request format: ".@$this->_requestFormat." handler:".$this->_requestHandler."
+Response format: ".@$this->_responseFormat." encoder:".$this->_responseEncoder."
+ ");
+ var_export($this);
+ die(0);
+ }
+
+ if ( is_null($this->_requestHandler) )
+ {
+ $this->sendResponse(
+ new PwgError(500, 'Unknown request format')
+ );
+ return;
+ }
+
+ $this->addMethod('reflection.getMethodList',
+ array('PwgServer', 'ws_getMethodList'),
+ null, '' );
+ $this->addMethod('reflection.getMethodDetails',
+ array('PwgServer', 'ws_getMethodDetails'),
+ array('methodName'),'');
+
+ trigger_action('ws_add_methods', array(&$this) );
+ uksort( $this->_methods, 'strnatcmp' );
+ $this->_requestHandler->handleRequest($this);
+ }
+
+ /**
+ * Encodes a response and sends it back to the browser.
+ */
+ function sendResponse($response)
+ {
+ $encodedResponse = $this->_responseEncoder->encodeResponse($response);
+ $contentType = $this->_responseEncoder->getContentType();
+
+ @header('Content-Type: '.$contentType);
+ print_r($encodedResponse);
+ }
+
+ /**
+ * Registers a web service method.
+ * @param methodName string - the name of the method as seen externally
+ * @param callback mixed - php method to be invoked internally
+ * @param params array - map of allowed parameter names with optional default
+ * values and parameter flags. Example of $params:
+ * array( 'param1' => array('default'=>523, 'flags'=>WS_PARAM_FORCE_ARRAY) ) .
+ * Possible parameter flags are:
+ * WS_PARAM_ALLOW_ARRAY - this parameter can be an array
+ * WS_PARAM_FORCE_ARRAY - if this parameter is scalar, force it to an array
+ * before invoking the method
+ * @param description string - a description of the method.
+ */
+ function addMethod($methodName, $callback, $params=array(), $description, $include_file='')
+ {
+ $this->_methods[$methodName] = $callback;
+ $this->_methodDescriptions[$methodName] = $description;
+
+ if (!is_array($params))
+ {
+ $params = array();
+ }
+
+ if ( range(0, count($params) - 1) === array_keys($params) )
+ {
+ $params = array_flip($params);
+ }
+
+ foreach( $params as $param=>$options)
+ {
+ if ( !is_array($options) )
+ {
+ $params[$param] = array('flags'=>0);
+ }
+ else
+ {
+ $flags = isset($options['flags']) ? $options['flags'] : 0;
+ if ( array_key_exists('default', $options) )
+ {
+ $flags |= WS_PARAM_OPTIONAL;
+ }
+ if ( $flags & WS_PARAM_FORCE_ARRAY )
+ {
+ $flags |= WS_PARAM_ACCEPT_ARRAY;
+ }
+ $options['flags'] = $flags;
+ $params[$param] = $options;
+ }
+ }
+ $this->_methodSignatures[$methodName] = $params;
+ }
+
+ function hasMethod($methodName)
+ {
+ return isset($this->_methods[$methodName]);
+ }
+
+ function getMethodDescription($methodName)
+ {
+ $desc = @$this->_methodDescriptions[$methodName];
+ return isset($desc) ? $desc : '';
+ }
+
+ function getMethodSignature($methodName)
+ {
+ $signature = @$this->_methodSignatures[$methodName];
+ return isset($signature) ? $signature : array();
+ }
+
+ /*static*/ function isPost()
+ {
+ return isset($HTTP_RAW_POST_DATA) or !empty($_POST);
+ }
+
+ /*static*/ function makeArrayParam(&$param)
+ {
+ if ( $param==null )
+ {
+ $param = array();
+ }
+ else
+ {
+ if (! is_array($param) )
+ {
+ $param = array($param);
+ }
+ }
+ }
+
+ /**
+ * Invokes a registered method. Returns the return of the method (or
+ * a PwgError object if the method is not found)
+ * @param methodName string the name of the method to invoke
+ * @param params array array of parameters to pass to the invoked method
+ */
+ function invoke($methodName, $params)
+ {
+ $callback = @$this->_methods[$methodName];
+
+ if ( $callback==null )
+ {
+ return new PwgError(WS_ERR_INVALID_METHOD, 'Method name "'.$methodName.'" is not valid');
+ }
+
+ // parameter check and data coercion !
+ $signature = @$this->_methodSignatures[$methodName];
+ $missing_params = array();
+ foreach($signature as $name=>$options)
+ {
+ $flags = $options['flags'];
+ if ( !array_key_exists($name, $params) )
+ {// parameter not provided in the request
+ if ( !($flags&WS_PARAM_OPTIONAL) )
+ {
+ $missing_params[] = $name;
+ }
+ else if ( array_key_exists('default',$options) )
+ {
+ $params[$name] = $options['default'];
+ if ( ($flags&WS_PARAM_FORCE_ARRAY) )
+ {
+ $this->makeArrayParam( $params[$name] );
+ }
+ }
+ }
+ else
+ {// parameter provided - do some basic checks
+ $the_param = $params[$name];
+ if ( is_array($the_param) and ($flags&WS_PARAM_ACCEPT_ARRAY)==0 )
+ {
+ return new PwgError(WS_ERR_INVALID_PARAM, $name.' must be scalar' );
+ }
+ if ( ($flags&WS_PARAM_FORCE_ARRAY) )
+ {
+ $this->makeArrayParam( $the_param );
+ }
+ if ( isset($options['maxValue']) and $the_param>$options['maxValue'])
+ {
+ $the_param = $options['maxValue'];
+ }
+ $params[$name] = $the_param;
+ }
+ }
+ if (count($missing_params))
+ {
+ return new PwgError(WS_ERR_MISSING_PARAM, 'Missing parameters: '.implode(',',$missing_params));
+ }
+ $result = trigger_event('ws_invoke_allowed', true, $methodName, $params);
+ if ( strtolower( get_class($result) )!='pwgerror')
+ {
+ $result = call_user_func_array($callback, array($params, &$this) );
+ }
+ return $result;
+ }
+
+ /**
+ * WS reflection method implementation: lists all available methods
+ */
+ /*static*/ function ws_getMethodList($params, &$service)
+ {
+ return array('methods' => new PwgNamedArray( array_keys($service->_methods),'method' ) );
+ }
+
+ /**
+ * WS reflection method implementation: gets information about a given method
+ */
+ /*static*/ function ws_getMethodDetails($params, &$service)
+ {
+ $methodName = $params['methodName'];
+ if (!$service->hasMethod($methodName))
+ {
+ return new PwgError(WS_ERR_INVALID_PARAM,
+ 'Requested method does not exist');
+ }
+ $res = array(
+ 'name' => $methodName,
+ 'description' => $service->getMethodDescription($methodName),
+ 'params' => array(),
+ );
+ $signature = $service->getMethodSignature($methodName);
+ foreach ($signature as $name => $options)
+ {
+ $param_data = array(
+ 'name' => $name,
+ 'optional' => ($options['flags']&WS_PARAM_OPTIONAL)?true:false,
+ 'acceptArray' => ($options['flags']&WS_PARAM_ACCEPT_ARRAY)?true:false,
+ );
+ if (isset($options['default']))
+ {
+ $param_data['defaultValue'] = $options['default'];
+ }
+ $res['params'][] = $param_data;
+ }
+ return $res;
+ }
+}
+?>
diff --git a/BSF/include/ws_functions.inc.php b/BSF/include/ws_functions.inc.php
new file mode 100644
index 000000000..2488fd0bf
--- /dev/null
+++ b/BSF/include/ws_functions.inc.php
@@ -0,0 +1,1103 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+/**** IMPLEMENTATION OF WEB SERVICE METHODS ***********************************/
+
+/**
+ * Event handler for method invocation security check. Should return a PwgError
+ * if the preconditions are not satifsied for method invocation.
+ */
+function ws_isInvokeAllowed($res, $methodName, $params)
+{
+ global $conf, $calling_partner_id;
+
+ if ( strpos($methodName,'reflection.')===0 )
+ { // OK for reflection
+ return $res;
+ }
+
+ if ( !is_autorize_status(ACCESS_GUEST) and
+ strpos($methodName,'pwg.session.')!==0 )
+ {
+ return new PwgError(401, 'Access denied');
+ }
+
+ if ( !$conf['ws_access_control'] )
+ {
+ return $res; // No controls are requested
+ }
+ $query = '
+SELECT * FROM '.WEB_SERVICES_ACCESS_TABLE."
+ WHERE `name` = '$calling_partner_id'
+ AND NOW() <= end; ";
+ $result = pwg_query($query);
+ $row = mysql_fetch_assoc($result);
+ if ( empty($row) )
+ {
+ return new PwgError(403, 'Partner id does not exist or is expired');
+ }
+ if ( !empty($row['request'])
+ and strpos($methodName, $row['request'])==false
+ and strpos($methodName, 'session')==false
+ and strpos($methodName, 'getVersion')==false )
+ { // session and getVersion are allowed to diagnose any failure reason
+ return new PwgError(403, 'Method not allowed');
+ }
+
+ return $res;
+}
+
+/**
+ * ws_addControls
+ * returns additionnal controls if requested
+ * usable for 99% of Web Service methods
+ *
+ * - Args
+ * $methodName: is the requested method
+ * $partner: is the key
+ * $tbl_name: is the alias_name in the query (sometimes called correlation name)
+ * null if !getting picture informations
+ * - Logic
+ * Access_control is not active: Return
+ * Key is incorrect: Return 0 = 1 (False condition for MySQL)
+ * One of Params doesn't match with type of request: return 0 = 1 again
+ * Access list(id/cat/tag) is converted in expended image-id list
+ * image-id list: converted to an in-where-clause
+ *
+ * The additionnal in-where-clause is return
+ */
+function ws_addControls( $methodName, &$params, $tbl_name )
+{
+ global $conf, $calling_partner_id;
+ if ( !$conf['ws_access_control'] or !isset($calling_partner_id) )
+ {
+ return '1=1'; // No controls are requested
+ }
+
+// Is it an active Partner?
+ $query = '
+SELECT * FROM '.WEB_SERVICES_ACCESS_TABLE."
+ WHERE `name` = '$calling_partner_id'
+ AND NOW() <= end; ";
+$result = pwg_query($query);
+ if ( mysql_num_rows( $result ) == 0 )
+ {
+ return '0=1'; // Unknown partner or Obsolate agreement
+ }
+
+ $row = mysql_fetch_array($result);
+
+// Overide general object limit
+ $params['per_page'] = $row['limit'];
+
+// Target restrict
+// 3 cases: list, cat or tag
+// Behind / we could found img-ids, cat-ids or tag-ids
+ $target = $row['access'];
+ if ( $target == '')
+ {
+ return '1=1'; // No controls are requested
+ }
+ list($type, $str_ids) = explode('/',$target); // Find type list
+
+// (array) 1,2,21,3,22,4,5,9-12,6,11,12,13,2,4,6,
+ $arr_ids = expand_id_list( explode( ',',$str_ids ) );
+ $addings = implode(',', $arr_ids);
+// (string) 1,2,3,4,5,6,9,10,11,12,13,21,22,
+ if ( $type == 'list')
+ {
+ return $tbl_name . 'id IN ( ' . $addings . ' ) ';
+ }
+
+ if ( $type == 'cat' )
+ {
+ $addings = implode(',', get_image_ids_for_cats($arr_ids));
+ return $tbl_name . 'id IN ( ' . $addings . ' ) ';
+ }
+
+ if ( $type == 'tag' )
+ {
+ $addings = implode(',', get_image_ids_for_tags($arr_ids, 'OR'));
+ return $tbl_name . 'id IN ( ' . $addings . ' ) ';
+ }
+ // Unmanaged new type?
+ return ' 0 = 1 '; // ???
+}
+
+/**
+ * returns a "standard" (for our web service) array of sql where clauses that
+ * filters the images (images table only)
+ */
+function ws_std_image_sql_filter( $params, $tbl_name='' )
+{
+ $clauses = array();
+ if ( is_numeric($params['f_min_rate']) )
+ {
+ $clauses[] = $tbl_name.'average_rate>'.$params['f_min_rate'];
+ }
+ if ( is_numeric($params['f_max_rate']) )
+ {
+ $clauses[] = $tbl_name.'average_rate<='.$params['f_max_rate'];
+ }
+ if ( is_numeric($params['f_min_hit']) )
+ {
+ $clauses[] = $tbl_name.'hit>'.$params['f_min_hit'];
+ }
+ if ( is_numeric($params['f_max_hit']) )
+ {
+ $clauses[] = $tbl_name.'hit<='.$params['f_max_hit'];
+ }
+ if ( isset($params['f_min_date_posted']) )
+ {
+ $clauses[] = $tbl_name."date_available>='".$params['f_min_date_posted']."'";
+ }
+ if ( isset($params['f_max_date_posted']) )
+ {
+ $clauses[] = $tbl_name."date_available<'".$params['f_max_date_posted']."'";
+ }
+ if ( isset($params['f_min_date_created']) )
+ {
+ $clauses[] = $tbl_name."date_creation>='".$params['f_min_date_created']."'";
+ }
+ if ( isset($params['f_max_date_created']) )
+ {
+ $clauses[] = $tbl_name."date_creation<'".$params['f_max_date_created']."'";
+ }
+ if ( is_numeric($params['f_min_ratio']) )
+ {
+ $clauses[] = $tbl_name.'width/'.$tbl_name.'height>'.$params['f_min_ratio'];
+ }
+ if ( is_numeric($params['f_max_ratio']) )
+ {
+ $clauses[] = $tbl_name.'width/'.$tbl_name.'height<='.$params['f_max_ratio'];
+ }
+ if ( $params['f_with_thumbnail'] )
+ {
+ $clauses[] = $tbl_name.'tn_ext IS NOT NULL';
+ }
+ return $clauses;
+}
+
+/**
+ * returns a "standard" (for our web service) ORDER BY sql clause for images
+ */
+function ws_std_image_sql_order( $params, $tbl_name='' )
+{
+ $ret = '';
+ if ( empty($params['order']) )
+ {
+ return $ret;
+ }
+ $matches = array();
+ preg_match_all('/([a-z_]+) *(?:(asc|desc)(?:ending)?)? *(?:, *|$)/i',
+ $params['order'], $matches);
+ for ($i=0; $i<count($matches[1]); $i++)
+ {
+ switch ($matches[1][$i])
+ {
+ case 'date_created':
+ $matches[1][$i] = 'date_creation'; break;
+ case 'date_posted':
+ $matches[1][$i] = 'date_available'; break;
+ case 'rand': case 'random':
+ $matches[1][$i] = 'RAND()'; break;
+ }
+ $sortable_fields = array('id', 'file', 'name', 'hit', 'average_rate',
+ 'date_creation', 'date_available', 'RAND()' );
+ if ( in_array($matches[1][$i], $sortable_fields) )
+ {
+ if (!empty($ret))
+ $ret .= ', ';
+ if ($matches[1][$i] != 'RAND()' )
+ {
+ $ret .= $tbl_name;
+ }
+ $ret .= $matches[1][$i];
+ $ret .= ' '.$matches[2][$i];
+ }
+ }
+ return $ret;
+}
+
+/**
+ * returns an array map of urls (thumb/element) for image_row - to be returned
+ * in a standard way by different web service methods
+ */
+function ws_std_get_urls($image_row)
+{
+ $ret = array(
+ 'tn_url' => get_thumbnail_url($image_row),
+ 'element_url' => get_element_url($image_row)
+ );
+ global $user;
+ if ($user['enabled_high'] and $image_row['has_high'] )
+ {
+ $ret['high_url'] = get_high_url($image_row);
+ }
+ return $ret;
+}
+
+/**
+ * returns an array of image attributes that are to be encoded as xml attributes
+ * instead of xml elements
+ */
+function ws_std_get_image_xml_attributes()
+{
+ return array(
+ 'id','tn_url','element_url','high_url', 'file','width','height','hit'
+ );
+}
+
+/**
+ * returns PWG version (web service method)
+ */
+function ws_getVersion($params, &$service)
+{
+ global $conf;
+ if ($conf['show_version'])
+ return PHPWG_VERSION;
+ else
+ return new PwgError(403, 'Forbidden');
+}
+
+
+/**
+ * returns images per category (web service method)
+ */
+function ws_categories_getImages($params, &$service)
+{
+ @include_once(PHPWG_ROOT_PATH.'include/functions_picture.inc.php');
+ global $user, $conf;
+
+ $images = array();
+
+ //------------------------------------------------- get the related categories
+ $where_clauses = array();
+ foreach($params['cat_id'] as $cat_id)
+ {
+ $cat_id = (int)$cat_id;
+ if ($cat_id<=0)
+ continue;
+ if ($params['recursive'])
+ {
+ $where_clauses[] = 'uppercats REGEXP \'(^|,)'.$cat_id.'(,|$)\'';
+ }
+ else
+ {
+ $where_clauses[] = 'id='.$cat_id;
+ }
+ }
+ if (!empty($where_clauses))
+ {
+ $where_clauses = array( '('.
+ implode('
+ OR ', $where_clauses) . ')'
+ );
+ }
+ $where_clauses[] = get_sql_condition_FandF(
+ array('forbidden_categories' => 'id'),
+ NULL, true
+ );
+
+ $query = '
+SELECT id, name, permalink, image_order
+ FROM '.CATEGORIES_TABLE.'
+ WHERE '. implode('
+ AND ', $where_clauses);
+ $result = pwg_query($query);
+ $cats = array();
+ while ($row = mysql_fetch_assoc($result))
+ {
+ $row['id'] = (int)$row['id'];
+ $cats[ $row['id'] ] = $row;
+ }
+
+ //-------------------------------------------------------- get the images
+ if ( !empty($cats) )
+ {
+ $where_clauses = ws_std_image_sql_filter( $params, 'i.' );
+ $where_clauses[] = 'category_id IN ('
+ .implode(',', array_keys($cats) )
+ .')';
+ $where_clauses[] = get_sql_condition_FandF( array(
+ 'visible_images' => 'i.id'
+ ), null, true
+ );
+ $where_clauses[] = ws_addControls( 'categories.getImages', $params, 'i.' );
+
+ $order_by = ws_std_image_sql_order($params, 'i.');
+ if ( empty($order_by)
+ and count($params['cat_id'])==1
+ and isset($cats[ $params['cat_id'][0] ]['image_order'])
+ )
+ {
+ $order_by = $cats[ $params['cat_id'][0] ]['image_order'];
+ }
+ $order_by = empty($order_by) ? $conf['order_by'] : 'ORDER BY '.$order_by;
+
+ $query = '
+SELECT i.*, GROUP_CONCAT(category_id) cat_ids
+ FROM '.IMAGES_TABLE.' i
+ INNER JOIN '.IMAGE_CATEGORY_TABLE.' ON i.id=image_id
+ WHERE '. implode('
+ AND ', $where_clauses).'
+GROUP BY i.id
+'.$order_by.'
+LIMIT '.$params['per_page']*$params['page'].','.$params['per_page'];
+
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_assoc($result))
+ {
+ $image = array();
+ foreach ( array('id', 'width', 'height', 'hit') as $k )
+ {
+ if (isset($row[$k]))
+ {
+ $image[$k] = (int)$row[$k];
+ }
+ }
+ foreach ( array('file', 'name', 'comment') as $k )
+ {
+ $image[$k] = $row[$k];
+ }
+ $image = array_merge( $image, ws_std_get_urls($row) );
+
+ $image_cats = array();
+ foreach ( explode(',', $row['cat_ids']) as $cat_id )
+ {
+ $url = make_index_url(
+ array(
+ 'category' => $cats[$cat_id],
+ )
+ );
+ $page_url = make_picture_url(
+ array(
+ 'category' => $cats[$cat_id],
+ 'image_id' => $row['id'],
+ 'image_file' => $row['file'],
+ )
+ );
+ array_push( $image_cats, array(
+ WS_XML_ATTRIBUTES => array (
+ 'id' => (int)$cat_id,
+ 'url' => $url,
+ 'page_url' => $page_url,
+ )
+ )
+ );
+ }
+
+ $image['categories'] = new PwgNamedArray(
+ $image_cats,'category', array('id','url','page_url')
+ );
+ array_push($images, $image);
+ }
+ }
+
+ return array( 'images' =>
+ array (
+ WS_XML_ATTRIBUTES =>
+ array(
+ 'page' => $params['page'],
+ 'per_page' => $params['per_page'],
+ 'count' => count($images)
+ ),
+ WS_XML_CONTENT => new PwgNamedArray($images, 'image',
+ ws_std_get_image_xml_attributes() )
+ )
+ );
+}
+
+
+/**
+ * returns a list of categories (web service method)
+ */
+function ws_categories_getList($params, &$service)
+{
+ global $user,$conf;
+
+ $where = array();
+
+ if (!$params['recursive'])
+ {
+ if ($params['cat_id']>0)
+ $where[] = '(id_uppercat='.(int)($params['cat_id']).'
+ OR id='.(int)($params['cat_id']).')';
+ else
+ $where[] = 'id_uppercat IS NULL';
+ }
+ else if ($params['cat_id']>0)
+ {
+ $where[] = 'uppercats REGEXP \'(^|,)'.
+ (int)($params['cat_id'])
+ .'(,|$)\'';
+ }
+
+ if ($params['public'])
+ {
+ $where[] = 'status = "public"';
+ $where[] = 'visible = "true"';
+ $where[]= 'user_id='.$conf['guest_id'];
+ }
+ else
+ {
+ $where[]= 'user_id='.$user['id'];
+ }
+
+ $query = '
+SELECT id, name, permalink, uppercats, global_rank,
+ nb_images, count_images AS total_nb_images,
+ date_last, max_date_last, count_categories AS nb_categories
+ FROM '.CATEGORIES_TABLE.'
+ INNER JOIN '.USER_CACHE_CATEGORIES_TABLE.' ON id=cat_id
+ WHERE '. implode('
+ AND ', $where);
+
+ $result = pwg_query($query);
+
+ $cats = array();
+ while ($row = mysql_fetch_assoc($result))
+ {
+ $row['url'] = make_index_url(
+ array(
+ 'category' => $row
+ )
+ );
+ foreach( array('id','nb_images','total_nb_images','nb_categories') as $key)
+ {
+ $row[$key] = (int)$row[$key];
+ }
+ array_push($cats, $row);
+ }
+ usort($cats, 'global_rank_compare');
+ return array(
+ 'categories' =>
+ new PwgNamedArray($cats,'category',
+ array('id','url','nb_images','total_nb_images','nb_categories','date_last','max_date_last')
+ )
+ );
+}
+
+
+/**
+ * returns detailed information for an element (web service method)
+ */
+function ws_images_addComment($params, &$service)
+{
+ if (!$service->isPost())
+ {
+ return new PwgError(405, "This method requires HTTP POST");
+ }
+ $params['image_id'] = (int)$params['image_id'];
+ $query = '
+SELECT DISTINCT image_id
+ FROM '.IMAGE_CATEGORY_TABLE.' INNER JOIN '.CATEGORIES_TABLE.' ON category_id=id
+ WHERE commentable="true"
+ AND image_id='.$params['image_id'].
+ get_sql_condition_FandF(
+ array(
+ 'forbidden_categories' => 'id',
+ 'visible_categories' => 'id',
+ 'visible_images' => 'image_id'
+ ),
+ ' AND'
+ );
+ if ( !mysql_num_rows( pwg_query( $query ) ) )
+ {
+ return new PwgError(WS_ERR_INVALID_PARAM, "Invalid image_id");
+ }
+
+ include_once(PHPWG_ROOT_PATH.'include/functions_comment.inc.php');
+
+ $comm = array(
+ 'author' => trim( stripslashes($params['author']) ),
+ 'content' => trim( stripslashes($params['content']) ),
+ 'image_id' => $params['image_id'],
+ );
+
+ include_once(PHPWG_ROOT_PATH.'include/functions_comment.inc.php');
+
+ $comment_action = insert_user_comment(
+ $comm, $params['key'], $infos
+ );
+
+ switch ($comment_action)
+ {
+ case 'reject':
+ array_push($infos, l10n('comment_not_added') );
+ return new PwgError(403, implode("\n", $infos) );
+ case 'validate':
+ case 'moderate':
+ $ret = array(
+ 'id' => $comm['id'],
+ 'validation' => $comment_action=='validate',
+ );
+ return new PwgNamedStruct(
+ 'comment',
+ $ret,
+ null, array()
+ );
+ default:
+ return new PwgError(500, "Unknown comment action ".$comment_action );
+ }
+}
+
+/**
+ * returns detailed information for an element (web service method)
+ */
+function ws_images_getInfo($params, &$service)
+{
+ @include_once(PHPWG_ROOT_PATH.'include/functions_picture.inc.php');
+ global $user, $conf;
+ $params['image_id'] = (int)$params['image_id'];
+ if ( $params['image_id']<=0 )
+ {
+ return new PwgError(WS_ERR_INVALID_PARAM, "Invalid image_id");
+ }
+
+ $query='
+SELECT * FROM '.IMAGES_TABLE.'
+ WHERE id='.$params['image_id'].
+ get_sql_condition_FandF(
+ array('visible_images' => 'id'),
+ ' AND'
+ ).' AND '.
+ ws_addControls( 'images.getInfo', $params, '' ).'
+LIMIT 1;';
+
+ $image_row = mysql_fetch_assoc(pwg_query($query));
+ if ($image_row==null)
+ {
+ return new PwgError(404, "image_id not found");
+ }
+ $image_row = array_merge( $image_row, ws_std_get_urls($image_row) );
+
+ //-------------------------------------------------------- related categories
+ $query = '
+SELECT id, name, permalink, uppercats, global_rank, commentable
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ INNER JOIN '.CATEGORIES_TABLE.' ON category_id = id
+ WHERE image_id = '.$image_row['id'].
+ get_sql_condition_FandF(
+ array( 'forbidden_categories' => 'category_id' ),
+ ' AND'
+ ).'
+;';
+ $result = pwg_query($query);
+ $is_commentable = false;
+ $related_categories = array();
+ while ($row = mysql_fetch_assoc($result))
+ {
+ if ($row['commentable']=='true')
+ {
+ $is_commentable = true;
+ }
+ unset($row['commentable']);
+ $row['url'] = make_index_url(
+ array(
+ 'category' => $row
+ )
+ );
+
+ $row['page_url'] = make_picture_url(
+ array(
+ 'image_id' => $image_row['id'],
+ 'image_file' => $image_row['file'],
+ 'category' => $row
+ )
+ );
+ $row['id']=(int)$row['id'];
+ array_push($related_categories, $row);
+ }
+ usort($related_categories, 'global_rank_compare');
+ if ( empty($related_categories) )
+ {
+ return new PwgError(401, 'Access denied');
+ }
+
+ //-------------------------------------------------------------- related tags
+ $related_tags = get_common_tags( array($image_row['id']), -1 );
+ foreach( $related_tags as $i=>$tag)
+ {
+ $tag['url'] = make_index_url(
+ array(
+ 'tags' => array($tag)
+ )
+ );
+ $tag['page_url'] = make_picture_url(
+ array(
+ 'image_id' => $image_row['id'],
+ 'image_file' => $image_row['file'],
+ 'tags' => array($tag),
+ )
+ );
+ unset($tag['counter']);
+ $tag['id']=(int)$tag['id'];
+ $related_tags[$i]=$tag;
+ }
+ //------------------------------------------------------------- related rates
+ $query = '
+SELECT COUNT(rate) AS count
+ , ROUND(AVG(rate),2) AS average
+ , ROUND(STD(rate),2) AS stdev
+ FROM '.RATE_TABLE.'
+ WHERE element_id = '.$image_row['id'].'
+;';
+ $rating = mysql_fetch_assoc(pwg_query($query));
+ $rating['count'] = (int)$rating['count'];
+
+ //---------------------------------------------------------- related comments
+ $related_comments = array();
+
+ $where_comments = 'image_id = '.$image_row['id'];
+ if ( !is_admin() )
+ {
+ $where_comments .= '
+ AND validated="true"';
+ }
+
+ $query = '
+SELECT COUNT(id) nb_comments
+ FROM '.COMMENTS_TABLE.'
+ WHERE '.$where_comments;
+ list($nb_comments) = array_from_query($query, 'nb_comments');
+ $nb_comments = (int)$nb_comments;
+
+ if ( $nb_comments>0 and $params['comments_per_page']>0 )
+ {
+ $query = '
+SELECT id, date, author, content
+ FROM '.COMMENTS_TABLE.'
+ WHERE '.$where_comments.'
+ ORDER BY date
+ LIMIT '.$params['comments_per_page']*(int)$params['comments_page'].
+ ','.$params['comments_per_page'];
+
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_assoc($result))
+ {
+ $row['id']=(int)$row['id'];
+ array_push($related_comments, $row);
+ }
+ }
+
+ $comment_post_data = null;
+ if ($is_commentable and
+ (!is_a_guest()
+ or (is_a_guest() and $conf['comments_forall'] )
+ )
+ )
+ {
+ include_once(PHPWG_ROOT_PATH.'include/functions_comment.inc.php');
+ $comment_post_data['author'] = $user['username'];
+ $comment_post_data['key'] = get_comment_post_key($params['image_id']);
+ }
+
+ $ret = $image_row;
+ foreach ( array('id','width','height','hit','filesize') as $k )
+ {
+ if (isset($ret[$k]))
+ {
+ $ret[$k] = (int)$ret[$k];
+ }
+ }
+ foreach ( array('path', 'storage_category_id') as $k )
+ {
+ unset($ret[$k]);
+ }
+
+ $ret['rates'] = array( WS_XML_ATTRIBUTES => $rating );
+ $ret['categories'] = new PwgNamedArray($related_categories, 'category', array('id','url', 'page_url') );
+ $ret['tags'] = new PwgNamedArray($related_tags, 'tag', array('id','url_name','url','page_url') );
+ if ( isset($comment_post_data) )
+ {
+ $ret['comment_post'] = array( WS_XML_ATTRIBUTES => $comment_post_data );
+ }
+ $ret['comments'] = array(
+ WS_XML_ATTRIBUTES =>
+ array(
+ 'page' => $params['comments_page'],
+ 'per_page' => $params['comments_per_page'],
+ 'count' => count($related_comments),
+ 'nb_comments' => $nb_comments,
+ ),
+ WS_XML_CONTENT => new PwgNamedArray($related_comments, 'comment', array('id','date') )
+ );
+
+ return new PwgNamedStruct('image',$ret, null, array('name','comment') );
+}
+
+/**
+ * returns a list of elements corresponding to a query search
+ */
+function ws_images_search($params, &$service)
+{
+ global $page;
+ $images = array();
+ include_once( PHPWG_ROOT_PATH .'include/functions_search.inc.php' );
+ include_once(PHPWG_ROOT_PATH.'include/functions_picture.inc.php');
+
+ $where_clauses = ws_std_image_sql_filter( $params, 'i.' );
+ $order_by = ws_std_image_sql_order($params, 'i.');
+
+ if ( !empty($order_by) )
+ {
+ global $conf;
+ $conf['order_by'] = 'ORDER BY '.$order_by;
+ $page['super_order_by']=1; // quick_search_result might be faster
+ }
+
+ $search_result = get_quick_search_results($params['query'],
+ implode(',', $where_clauses) );
+ $image_ids = $search_result['items'];
+
+ $image_ids = array_slice($image_ids,
+ $params['page']*$params['per_page'],
+ $params['per_page'] );
+
+ if ( count($image_ids) )
+ {
+ $query = '
+SELECT * FROM '.IMAGES_TABLE.'
+ WHERE id IN ('
+ .wordwrap(implode(', ', $image_ids), 80, "\n")
+ .')';
+
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_assoc($result))
+ {
+ $image = array();
+ foreach ( array('id', 'width', 'height', 'hit') as $k )
+ {
+ if (isset($row[$k]))
+ {
+ $image[$k] = (int)$row[$k];
+ }
+ }
+ foreach ( array('file', 'name', 'comment') as $k )
+ {
+ $image[$k] = $row[$k];
+ }
+ $image = array_merge( $image, ws_std_get_urls($row) );
+ array_push($images, $image);
+ }
+
+ $image_ids = array_flip($image_ids);
+ usort(
+ $images,
+ create_function('$i1,$i2', 'global $image_ids; return $image_ids[$i1["id"]]-$image_ids[$i2["id"]];')
+ );
+ }
+
+
+ return array( 'images' =>
+ array (
+ WS_XML_ATTRIBUTES =>
+ array(
+ 'page' => $params['page'],
+ 'per_page' => $params['per_page'],
+ 'count' => count($images)
+ ),
+ WS_XML_CONTENT => new PwgNamedArray($images, 'image',
+ ws_std_get_image_xml_attributes() )
+ )
+ );
+}
+
+/**
+ * perform a login (web service method)
+ */
+function ws_session_login($params, &$service)
+{
+ global $conf;
+
+ if (!$service->isPost())
+ {
+ return new PwgError(405, "This method requires HTTP POST");
+ }
+ if (try_log_user($params['username'], $params['password'],false))
+ {
+ return true;
+ }
+ return new PwgError(999, 'Invalid username/password');
+}
+
+
+/**
+ * performs a logout (web service method)
+ */
+function ws_session_logout($params, &$service)
+{
+ global $user, $conf;
+ if (!is_a_guest())
+ {
+ $_SESSION = array();
+ session_unset();
+ session_destroy();
+ setcookie(session_name(),'',0,
+ ini_get('session.cookie_path'),
+ ini_get('session.cookie_domain')
+ );
+ setcookie($conf['remember_me_name'], '', 0, cookie_path());
+ }
+ return true;
+}
+
+function ws_session_getStatus($params, &$service)
+{
+ global $user;
+ $res = array();
+ $res['username'] = is_a_guest() ? 'guest' : $user['username'];
+ foreach ( array('status', 'template', 'theme', 'language') as $k )
+ {
+ $res[$k] = $user[$k];
+ }
+ $res['charset'] = get_pwg_charset();
+ return $res;
+}
+
+
+/**
+ * returns a list of tags (web service method)
+ */
+function ws_tags_getList($params, &$service)
+{
+ $tags = get_available_tags();
+ if ($params['sort_by_counter'])
+ {
+ usort($tags, create_function('$a,$b', 'return -$a["counter"]+$b["counter"];') );
+ }
+ else
+ {
+ usort($tags, 'name_compare');
+ }
+ for ($i=0; $i<count($tags); $i++)
+ {
+ $tags[$i]['id'] = (int)$tags[$i]['id'];
+ $tags[$i]['counter'] = (int)$tags[$i]['counter'];
+ $tags[$i]['url'] = make_index_url(
+ array(
+ 'section'=>'tags',
+ 'tags'=>array($tags[$i])
+ )
+ );
+ }
+ return array('tags' => new PwgNamedArray($tags, 'tag', array('id','url_name','url', 'counter' )) );
+}
+
+
+/**
+ * returns a list of images for tags (web service method)
+ */
+function ws_tags_getImages($params, &$service)
+{
+ @include_once(PHPWG_ROOT_PATH.'include/functions_picture.inc.php');
+ global $conf;
+
+ // first build all the tag_ids we are interested in
+ $params['tag_id'] = array_map( 'intval',$params['tag_id'] );
+ $tags = find_tags($params['tag_id'], $params['tag_url_name'], $params['tag_name']);
+ $tags_by_id = array();
+ foreach( $tags as $tag )
+ {
+ $tags['id'] = (int)$tag['id'];
+ $tags_by_id[ $tag['id'] ] = $tag;
+ }
+ unset($tags);
+ $tag_ids = array_keys($tags_by_id);
+
+
+ $image_ids = array();
+ $image_tag_map = array();
+
+ if ( !empty($tag_ids) )
+ { // build list of image ids with associated tags per image
+ if ($params['tag_mode_and'])
+ {
+ $image_ids = get_image_ids_for_tags( $tag_ids );
+ }
+ else
+ {
+ $query = '
+SELECT image_id, GROUP_CONCAT(tag_id) tag_ids
+ FROM '.IMAGE_TAG_TABLE.'
+ WHERE tag_id IN ('.implode(',',$tag_ids).')
+ GROUP BY image_id';
+ $result = pwg_query($query);
+ while ( $row=mysql_fetch_assoc($result) )
+ {
+ $row['image_id'] = (int)$row['image_id'];
+ array_push( $image_ids, $row['image_id'] );
+ $image_tag_map[ $row['image_id'] ] = explode(',', $row['tag_ids']);
+ }
+ }
+ }
+
+ $images = array();
+ if ( !empty($image_ids))
+ {
+ $where_clauses = ws_std_image_sql_filter($params);
+ $where_clauses[] = get_sql_condition_FandF(
+ array
+ (
+ 'forbidden_categories' => 'category_id',
+ 'visible_categories' => 'category_id',
+ 'visible_images' => 'i.id'
+ ),
+ '', true
+ );
+ $where_clauses[] = 'id IN ('.implode(',',$image_ids).')';
+ $where_clauses[] = ws_addControls( 'tags.getImages', $params, 'i.' );
+
+ $order_by = ws_std_image_sql_order($params);
+ if (empty($order_by))
+ {
+ $order_by = $conf['order_by'];
+ }
+ else
+ {
+ $order_by = 'ORDER BY '.$order_by;
+ }
+
+ $query = '
+SELECT DISTINCT i.* FROM '.IMAGES_TABLE.' i
+ INNER JOIN '.IMAGE_CATEGORY_TABLE.' ON i.id=image_id
+ WHERE '. implode('
+ AND ', $where_clauses).'
+'.$order_by.'
+LIMIT '.$params['per_page']*$params['page'].','.$params['per_page'];
+
+ $result = pwg_query($query);
+ while ($row = mysql_fetch_assoc($result))
+ {
+ $image = array();
+ foreach ( array('id', 'width', 'height', 'hit') as $k )
+ {
+ if (isset($row[$k]))
+ {
+ $image[$k] = (int)$row[$k];
+ }
+ }
+ foreach ( array('file', 'name', 'comment') as $k )
+ {
+ $image[$k] = $row[$k];
+ }
+ $image = array_merge( $image, ws_std_get_urls($row) );
+
+ $image_tag_ids = ($params['tag_mode_and']) ? $tag_ids : $image_tag_map[$image['id']];
+ $image_tags = array();
+ foreach ($image_tag_ids as $tag_id)
+ {
+ $url = make_index_url(
+ array(
+ 'section'=>'tags',
+ 'tags'=> array($tags_by_id[$tag_id])
+ )
+ );
+ $page_url = make_picture_url(
+ array(
+ 'section'=>'tags',
+ 'tags'=> array($tags_by_id[$tag_id]),
+ 'image_id' => $row['id'],
+ 'image_file' => $row['file'],
+ )
+ );
+ array_push($image_tags, array(
+ 'id' => (int)$tag_id,
+ 'url' => $url,
+ 'page_url' => $page_url,
+ )
+ );
+ }
+ $image['tags'] = new PwgNamedArray($image_tags, 'tag',
+ array('id','url_name','url','page_url')
+ );
+ array_push($images, $image);
+ }
+ }
+
+ return array( 'images' =>
+ array (
+ WS_XML_ATTRIBUTES =>
+ array(
+ 'page' => $params['page'],
+ 'per_page' => $params['per_page'],
+ 'count' => count($images)
+ ),
+ WS_XML_CONTENT => new PwgNamedArray($images, 'image',
+ ws_std_get_image_xml_attributes() )
+ )
+ );
+}
+
+
+/**
+ * expand_id_list($ids) convert a human list expression to a full ordered list
+ * example : expand_id_list( array(5,2-3,2) ) returns array( 2, 3, 5)
+ * */
+function expand_id_list($ids)
+{
+ $tid = array();
+ foreach ( $ids as $id )
+ {
+ if ( is_numeric($id) )
+ {
+ $tid[] = (int) $id;
+ }
+ else
+ {
+ $range = explode( '-', $id );
+ if ( is_numeric($range[0]) and is_numeric($range[1]) )
+ {
+ $from = min($range[0],$range[1]);
+ $to = max($range[0],$range[1]);
+ for ($i = $from; $i <= $to; $i++)
+ {
+ $tid[] = (int) $i;
+ }
+ }
+ }
+ }
+ $result = array_unique ($tid); // remove duplicates...
+ sort ($result);
+ return $result;
+}
+
+
+/**
+ * converts a cat-ids array in image-ids array
+ * FIXME Function which should already exist somewhere else
+ * */
+function get_image_ids_for_cats($cat_ids)
+{
+ $cat_list = implode(',', $cat_ids);
+ $ret_ids = array();
+ $query = '
+ SELECT DISTINCT image_id
+ FROM '.IMAGE_CATEGORY_TABLE.'
+ WHERE category_id in ('.$cat_list.')
+ ;';
+ return array_from_query($query, 'image_id');
+}
+
+?>
diff --git a/BSF/include/ws_protocols/index.php b/BSF/include/ws_protocols/index.php
new file mode 100644
index 000000000..c15b15795
--- /dev/null
+++ b/BSF/include/ws_protocols/index.php
@@ -0,0 +1,30 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+// Recursive call
+$url = '../';
+header( 'Request-URI: '.$url );
+header( 'Content-Location: '.$url );
+header( 'Location: '.$url );
+exit();
+?>
diff --git a/BSF/include/ws_protocols/json_encoder.php b/BSF/include/ws_protocols/json_encoder.php
new file mode 100644
index 000000000..8c2d7b0a0
--- /dev/null
+++ b/BSF/include/ws_protocols/json_encoder.php
@@ -0,0 +1,87 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+
+#_____________________ PHP 5.2
+if (! function_exists('json_encode')) {
+ function json_encode($data) {
+ switch (gettype($data)) {
+ case 'boolean':
+ return ($data ? 'true' : 'false');
+ case 'null':
+ case 'NULL':
+ return 'null';
+ case 'integer':
+ case 'double':
+ return $data;
+ case 'string':
+ return '"'. str_replace(array("\\",'"',"/","\n","\r","\t"), array("\\\\",'\"',"\\/","\\n","\\r","\\t"), $data) .'"';
+ case 'object':
+ case 'array':
+ if ($data === array()) return '[]'; # empty array
+ if (range(0, count($data) - 1) !== array_keys($data) ) { # string keys, unordered, non-incremental keys, .. - whatever, make object
+ $out = "\n".'{';
+ foreach($data as $key => $value) {
+ $out .= json_encode((string) $key) . ':' . json_encode($value) . ',';
+ }
+ $out = substr($out, 0, -1) . "\n". '}';
+ }else{
+ # regular array
+ $out = "\n".'[' . join("\n".',', array_map('json_encode', $data)) ."\n".']';
+ }
+ return $out;
+ }
+ }
+}
+
+class PwgJsonEncoder extends PwgResponseEncoder
+{
+ function encodeResponse($response)
+ {
+ $respClass = strtolower( get_class($response) );
+ if ($respClass=='pwgerror')
+ {
+ return json_encode(
+ array(
+ 'stat' => 'fail',
+ 'err' => $response->code(),
+ 'message' => $response->message(),
+ )
+ );
+ }
+ parent::flattenResponse($response);
+ return json_encode(
+ array(
+ 'stat' => 'ok',
+ 'result' => $response,
+ )
+ );
+ }
+
+ function getContentType()
+ {
+ return 'text/plain';
+ }
+}
+
+?>
diff --git a/BSF/include/ws_protocols/php_encoder.php b/BSF/include/ws_protocols/php_encoder.php
new file mode 100644
index 000000000..ce9afd3e6
--- /dev/null
+++ b/BSF/include/ws_protocols/php_encoder.php
@@ -0,0 +1,54 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+class PwgSerialPhpEncoder extends PwgResponseEncoder
+{
+ function encodeResponse($response)
+ {
+ $respClass = strtolower( get_class($response) );
+ if ($respClass=='pwgerror')
+ {
+ return serialize(
+ array(
+ 'stat' => 'fail',
+ 'err' => $response->code(),
+ 'message' => $response->message(),
+ )
+ );
+ }
+ parent::flattenResponse($response);
+ return serialize(
+ array(
+ 'stat' => 'ok',
+ 'result' => $response
+ )
+ );
+ }
+
+ function getContentType()
+ {
+ return 'text/plain';
+ }
+}
+
+?>
diff --git a/BSF/include/ws_protocols/rest_encoder.php b/BSF/include/ws_protocols/rest_encoder.php
new file mode 100644
index 000000000..e057c6427
--- /dev/null
+++ b/BSF/include/ws_protocols/rest_encoder.php
@@ -0,0 +1,281 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+
+class PwgXmlWriter
+{
+ var $_indent;
+ var $_indentStr;
+
+ var $_elementStack;
+ var $_lastTagOpen;
+ var $_indentLevel;
+
+ var $_encodedXml;
+
+ function PwgXmlWriter()
+ {
+ $this->_elementStack = array();
+ $this->_lastTagOpen = false;
+ $this->_indentLevel = 0;
+
+ $this->_encodedXml = '';
+ $this->_indent = true;
+ $this->_indentStr = "\t";
+ }
+
+ function &getOutput()
+ {
+ return $this->_encodedXml;
+ }
+
+
+ function start_element($name)
+ {
+ $this->_end_prev(false);
+ if (!empty($this->_elementStack))
+ {
+ $this->_eol_indent();
+ }
+ $this->_indentLevel++;
+ $this->_indent();
+ $this->_output( '<'.$name );
+ $this->_lastTagOpen = true;
+ array_push( $this->_elementStack, $name);
+ }
+
+ function end_element($x)
+ {
+ $close_tag = $this->_end_prev(true);
+ $name = array_pop( $this->_elementStack );
+ if ($close_tag)
+ {
+ $this->_indentLevel--;
+ $this->_indent();
+// $this->_eol_indent();
+ $this->_output('</'.$name.">");
+ }
+ }
+
+ function write_content($value)
+ {
+ $this->_end_prev(false);
+ $value = (string)$value;
+ $this->_output( htmlspecialchars( $value ) );
+ }
+
+ function write_cdata($value)
+ {
+ $this->_end_prev(false);
+ $value = (string)$value;
+ $this->_output(
+ '<![CDATA['
+ . str_replace(']]>', ']]&gt;', $value)
+ . ']]>' );
+ }
+
+ function write_attribute($name, $value)
+ {
+ $this->_output(' '.$name.'="'.$this->encode_attribute($value).'"');
+ }
+
+ function encode_attribute($value)
+ {
+ return htmlspecialchars( (string)$value);
+ }
+
+ function _end_prev($done)
+ {
+ $ret = true;
+ if ($this->_lastTagOpen)
+ {
+ if ($done)
+ {
+ $this->_indentLevel--;
+ $this->_output( ' />' );
+ //$this->_eol_indent();
+ $ret = false;
+ }
+ else
+ {
+ $this->_output( '>' );
+ }
+ $this->_lastTagOpen = false;
+ }
+ return $ret;
+ }
+
+ function _eol_indent()
+ {
+ if ($this->_indent)
+ $this->_output("\n");
+ }
+
+ function _indent()
+ {
+ if ($this->_indent and
+ $this->_indentLevel > count($this->_elementStack) )
+ {
+ $this->_output(
+ str_repeat( $this->_indentStr, count($this->_elementStack) )
+ );
+ }
+ }
+
+ function _output($raw_content)
+ {
+ $this->_encodedXml .= $raw_content;
+ }
+}
+
+class PwgRestEncoder extends PwgResponseEncoder
+{
+ function encodeResponse($response)
+ {
+ $respClass = strtolower( get_class($response) );
+ if ($respClass=='pwgerror')
+ {
+ $ret = '<?xml version="1.0"?>
+<rsp stat="fail">
+ <err code="'.$response->code().'" msg="'.htmlspecialchars($response->message()).'" />
+</rsp>';
+ return $ret;
+ }
+
+ $this->_writer = new PwgXmlWriter();
+ $this->encode($response);
+ $ret = $this->_writer->getOutput();
+ $ret = '<?xml version="1.0" encoding="'.get_pwg_charset().'" ?>
+<rsp stat="ok">
+'.$ret.'
+</rsp>';
+
+ return $ret;
+ }
+
+ function getContentType()
+ {
+ return 'text/xml';
+ }
+
+ function encode_array($data, $itemName, $xml_attributes=array())
+ {
+ foreach ($data as $item)
+ {
+ $this->_writer->start_element( $itemName );
+ $this->encode($item, $xml_attributes);
+ $this->_writer->end_element( $itemName );
+ }
+ }
+
+ function encode_struct($data, $skip_underscore, $xml_attributes=array())
+ {
+ foreach ($data as $name => $value)
+ {
+ if (is_numeric($name))
+ continue;
+ if ($skip_underscore and $name[0]=='_')
+ continue;
+ if ( is_null($value) )
+ continue; // null means we dont put it
+ if ( $name==WS_XML_ATTRIBUTES)
+ {
+ foreach ($value as $attr_name => $attr_value)
+ {
+ $this->_writer->write_attribute($attr_name, $attr_value);
+ }
+ unset($data[$name]);
+ }
+ else if ( isset($xml_attributes[$name]) )
+ {
+ $this->_writer->write_attribute($name, $value);
+ unset($data[$name]);
+ }
+ }
+
+ foreach ($data as $name => $value)
+ {
+ if (is_numeric($name))
+ continue;
+ if ($skip_underscore and $name[0]=='_')
+ continue;
+ if ( is_null($value) )
+ continue; // null means we dont put it
+ if ($name!=WS_XML_CONTENT)
+ $this->_writer->start_element($name);
+ $this->encode($value);
+ if ($name!=WS_XML_CONTENT)
+ $this->_writer->end_element($name);
+ }
+ }
+
+ function encode($data, $xml_attributes=array() )
+ {
+ switch (gettype($data))
+ {
+ case 'null':
+ case 'NULL':
+ $this->_writer->write_content('');
+ break;
+ case 'boolean':
+ $this->_writer->write_content($data ? '1' : '0');
+ break;
+ case 'integer':
+ case 'double':
+ $this->_writer->write_content($data);
+ break;
+ case 'string':
+ $this->_writer->write_content($data);
+ break;
+ case 'array':
+ $is_array = range(0, count($data) - 1) === array_keys($data);
+ if ($is_array)
+ {
+ $this->encode_array($data, 'item' );
+ }
+ else
+ {
+ $this->encode_struct($data, false, $xml_attributes);
+ }
+ break;
+ case 'object':
+ switch ( strtolower(get_class($data)) )
+ {
+ case 'pwgnamedarray':
+ $this->encode_array($data->_content, $data->_itemName, $data->_xmlAttributes);
+ break;
+ case 'pwgnamedstruct':
+ $this->encode_array( array($data->_content), $data->_name, $data->_xmlAttributes);
+ break;
+ default:
+ $this->encode_struct(get_object_vars($data), true);
+ break;
+ }
+ break;
+ default:
+ trigger_error("Invalid type ". gettype($data)." ".get_class($data), E_USER_WARNING );
+ }
+ }
+}
+
+?>
diff --git a/BSF/include/ws_protocols/rest_handler.php b/BSF/include/ws_protocols/rest_handler.php
new file mode 100644
index 000000000..c9c8ad9b0
--- /dev/null
+++ b/BSF/include/ws_protocols/rest_handler.php
@@ -0,0 +1,57 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+class PwgRestRequestHandler
+{
+ function handleRequest(&$service)
+ {
+ $params = array();
+
+ $param_array = $service->isPost() ? $_POST : $_GET;
+ foreach ($param_array as $name => $value)
+ {
+ if ($name=='format' or $name=='partner')
+ continue; // ignore - special keys
+ if ($name=='method')
+ {
+ $method = $value;
+ }
+ else
+ {
+ $params[$name]=$value;
+ }
+ }
+
+ if ( empty($method) )
+ {
+ $service->sendResponse(
+ new PwgError(400, 'Missing "method" name')
+ );
+ return;
+ }
+ $resp = $service->invoke($method, $params);
+ $service->sendResponse($resp);
+ }
+}
+
+?>
diff --git a/BSF/include/ws_protocols/xmlrpc_encoder.php b/BSF/include/ws_protocols/xmlrpc_encoder.php
new file mode 100644
index 000000000..26f0f510e
--- /dev/null
+++ b/BSF/include/ws_protocols/xmlrpc_encoder.php
@@ -0,0 +1,115 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+function xmlrpc_encode($data)
+{
+ switch (gettype($data))
+ {
+ case 'boolean':
+ return '<boolean>'.($data ? '1' : '0').'</boolean>';
+ case 'integer':
+ return '<int>'.$data.'</int>';
+ case 'double':
+ return '<double>'.$data.'</double>';
+ case 'string':
+ return '<string>'.htmlspecialchars($data).'</string>';
+ case 'object':
+ case 'array':
+ $is_array = range(0, count($data) - 1) === array_keys($data);
+ if ($is_array)
+ {
+ $return = '<array><data>'."\n";
+ foreach ($data as $item)
+ {
+ $return .= ' <value>'.xmlrpc_encode($item)."</value>\n";
+ }
+ $return .= '</data></array>';
+ }
+ else
+ {
+ $return = '<struct>'."\n";
+ foreach ($data as $name => $value)
+ {
+ $name = htmlspecialchars($name);
+ $return .= " <member><name>$name</name><value>";
+ $return .= xmlrpc_encode($value)."</value></member>\n";
+ }
+ $return .= '</struct>';
+ }
+ return $return;
+ }
+}
+
+class PwgXmlRpcEncoder extends PwgResponseEncoder
+{
+ function encodeResponse($response)
+ {
+ $respClass = strtolower( get_class($response) );
+ if ($respClass=='pwgerror')
+ {
+ $code = $response->code();
+ $msg = htmlspecialchars($response->message());
+ $ret = <<<EOD
+<methodResponse>
+ <fault>
+ <value>
+ <struct>
+ <member>
+ <name>faultCode</name>
+ <value><int>{$code}</int></value>
+ </member>
+ <member>
+ <name>faultString</name>
+ <value><string>{$msg}</string></value>
+ </member>
+ </struct>
+ </value>
+ </fault>
+</methodResponse>
+EOD;
+ return $ret;
+ }
+
+ parent::flattenResponse($response);
+ $ret = xmlrpc_encode($response);
+ $ret = <<<EOD
+<methodResponse>
+ <params>
+ <param>
+ <value>
+ $ret
+ </value>
+ </param>
+ </params>
+</methodResponse>
+EOD;
+ return $ret;
+ }
+
+ function getContentType()
+ {
+ return 'text/xml';
+ }
+}
+
+?>