<?php
/**
* Quicksilver Forums
* Copyright (c) 2005-2008 The Quicksilver Forums Development Team
* http://www.quicksilverforums.com/
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
**/
if (!defined('QUICKSILVERFORUMS')) {
header('HTTP/1.0 403 Forbidden');
die;
}
require_once $set['include_path'] . '/lib/htmltools.php';
/**
* Contains all the functions for generating small pieces of
* HTML that can not be easily done in a template
*
* @author Geoffrey Dunn <hide@address.com>
* @since 1.2
**/
class htmlwidgets extends htmltools
{
var $tree = null; // The navigational tree @var string
/**
* Constructor
*
* @param $qsf - Quicksilver Forums module
**/
function htmlwidgets(&$qsf)
{
parent::htmltools($qsf);
// Need the time for timezone stuff
$this->time = &$qsf->time;
}
/**
* Creates HTML-formatted page numbers
*
* @param mixed $rows Can be either a resource, query, or number; number of total entries for pagination
* @param string $link Query string to attach to link
* @param int $min First entry to display
* @param int $num Number of entries per page
* @author Mark Elliot <hide@address.com>
* @since Beta 1.0
* @return string HTML-formatted page numbers
**/
function get_pages($rows, $link, $min = 0, $num = 10)
{
if (!$num) {
$num = 10;
}
// preliminary row handling
if (!is_resource($rows)) {
if (!is_numeric($rows)) {
$rows = $this->db->num_rows($this->db->query($rows));
}
} else {
$rows = $this->db->num_rows($rows);
}
// some base variables
$current = ceil($min / $num);
$string = null;
$pages = ceil($rows / $num);
$end = ($pages - 1) * $num;
// check if there's previous articles
if ($min == 0) {
$startlink = '<<';
$previouslink = $this->lang->main_prev;
} else {
$startlink = "<a href=\"{$this->self}?$link&min=0&num=$num\" class=\"pagelinks\"><<</a>";
$prev = $min - $num;
$previouslink = "<a href=\"{$this->self}?$link&min=$prev&num=$num\" class=\"pagelinks\">{$this->lang->main_prev}</a> ";
}
// check for next/end
if (($min + $num) < $rows) {
$next = $min + $num;
$nextlink = "<a href=\"{$this->self}?$link&min=$next&num=$num\" class=\"pagelinks\">{$this->lang->main_next}</a>";
$endlink = "<a href=\"{$this->self}?$link&min=$end&num=$num\" class=\"pagelinks\">>></a>";
} else {
$nextlink = $this->lang->main_next;
$endlink = '>>';
}
// setup references
$b = $current - 2;
$e = $current + 2;
// set end and beginning of loop
if ($b < 0) {
$e = $e - $b;
$b = 0;
}
// check that end coheres to the issues
if ($e > $pages - 1) {
$b = $b - ($e - $pages + 1);
$e = ($pages - 1 < $current) ? $pages : $pages - 1;
// b may need adjusting again
if ($b < 0) {
$b = 0;
}
}
// ellipses
if ($b != 0) {
$badd = '...';
} else {
$badd = '';
}
if (($e != $pages - 1) && $rows) {
$eadd = '...';
} else {
$eadd = '';
}
// run loop for numbers to the page
for ($i = $b; $i < $current; $i++)
{
$where = $num * $i;
$string .= ", <a href=\"{$this->self}?$link&min=$where&num=$num\" class=\"bodylinktype\">" . ($i + 1) . '</a>';
}
// add in page
$string .= ', <strong>' . ($current + 1) . '</strong>';
// run to the end
for ($i = $current + 1; $i <= $e; $i++)
{
$where = $num * $i;
$string .= ", <a href=\"{$this->self}?$link&min=$where&num=$num\" class=\"bodylinktype\">" . ($i + 1) . '</a>';
}
// get rid of preliminary comma. (optimized by jason: mark uses preg_replace() like candy)
if (substr($string, 0, 1) == ',') {
$string = substr($string, 1);
}
return "<span class=\"pagelinks\">$startlink $previouslink $badd $string $eadd $nextlink $endlink</span>";
}
/**
* Creates HTML-formatted page numbers for topics - see get_pages()
*
* @param int $records Number of replies in the topic
* @param int $link Query string to attach to link
* @param string $sep Separator for pages
* @param int $min First entry to display
* @param int $n Number of entries to display
* @author Mark Elliot <hide@address.com>
* @since Beta 2.0
* @return
**/
function get_pages_topic($records, $link, $sep, $min = 0, $n = 10)
{
$records++;
$pages = ceil($records / $n);
$max_page = ($pages - 1) * $n;
if ($pages == 1) {
return null;
}
$pagelinks = null;
if ($pages > 3) {
$countfor = 3;
} else {
$countfor = $pages;
}
for ($i = 0; $i < $countfor; $i++)
{
$minpag = $i * $n;
$page = $i + 1;
$pagelinks .= "<a href=\"{$this->self}?$link&min=$minpag&num=$n\" class=\"pages\">$page</a>{$sep}";
}
if (substr($pagelinks, -(strlen($sep))) == $sep) {
$pagelinks = substr($pagelinks, 0, -(strlen($sep)));
}
if ($pages > 3) {
$ellipsis = ($pages == 4) ? '' : '..';
$pagelinks .= "$sep<a href=\"{$this->self}?$link&min=$max_page&num=$n\" class=\"pages\">{$ellipsis}{$pages}</a>";
}
$pagelinks = "( $pagelinks )";
return $pagelinks;
}
/**
* Look at all the avatars avaialble and make them selectable options
*
* @param string $current filename of the current avatar (if any)
* @param string $relative Path to look for avatars in (optional)
* @return string HTML
**/
function select_avatars($current, $relative = '.', $subfolder = '/')
{
if (substr($subfolder, -1) != '/') {
$subfolder .= '/';
}
$out = null;
$dir = opendir($relative . '/avatars' . $subfolder);
$subDirs = array();
while (($file = readdir($dir)) !== false)
{
if (is_dir('./avatars' . $subfolder . $file)) {
if ($file == 'uploaded' || $file[0] == '.') continue;
$subDirs[] = $file;
}
$split = explode('.', $file);
$ext = array_pop($split);
if (($ext != 'jpg')
&& ($ext != 'jpeg')
&& ($ext != 'gif')
&& ($ext != 'png')) {
continue;
}
$out .= "<option value=\"./avatars$subfolder$file\"" . (("./avatars$subfolder$file" == $current) ? ' selected="selected"' : null) . '>' . implode('.', $split) . "</option>\n";
}
foreach ($subDirs as $file) {
$extra = $this->select_avatars($current, $relative, $subfolder . $file);
if ($extra) {
if ($subfolder == '/') {
$out .= '<optgroup label="' . htmlspecialchars($file) . "\">\n";
$out .= $extra;
$out .= "</optgroup>\n";
} else {
$out .= "</optgroup>\n";
$out .= '<optgroup label="' . htmlspecialchars(substr($subfolder . $file, 1)) . "\">\n";
$out .= $extra;
}
}
}
return $out;
}
/**
* Create options of group names
*
* @param int $var group_id of the selected group
* @param bool $custom_only Show only groups that are not part of the built in groups
* @return string HTML
**/
function select_groups($val, $custom_only = false)
{
if ($custom_only) {
$groups = $this->db->query('SELECT group_name, group_id FROM %pgroups WHERE group_type="" ORDER BY group_name');
} else {
$groups = $this->db->query('SELECT group_name, group_id FROM %pgroups ORDER BY group_name');
}
$out = null;
while ($group = $this->db->nqfetch($groups))
{
$out .= "<option value=\"{$group['group_id']}\"" . (($val == $group['group_id']) ? ' selected="selected"' : '') . ">{$group['group_name']}</option>";
}
return $out;
}
/**
* Create options of "null" to 31 selectable as a day of the month
*
* @param int $dat current day of the month (if any)
* @return string HTML
**/
function select_days($day)
{
$i = 1;
$out = "<option value=\"00\"></option>\n";
while ($i <= 31)
{
$out .= "<option value=\"$i\"" . (($i == $day) ? ' selected="selected"' : null) . ">$i</option>\n";
$i++;
}
return $out;
}
/**
* Create options of "null" to 12 selectable months
*
* @param int $mon current month (if any)
* @return string HTML
**/
function select_months($mon)
{
$i = 1;
$out = null;
$month = array(
'00' => '',
'1' => $this->lang->cp_jan,
'2' => $this->lang->cp_feb,
'3' => $this->lang->cp_mar,
'4' => $this->lang->cp_apr,
'5' => $this->lang->cp_may,
'6' => $this->lang->cp_june,
'7' => $this->lang->cp_july,
'8' => $this->lang->cp_aug,
'9' => $this->lang->cp_sept,
'10' => $this->lang->cp_oct,
'11' => $this->lang->cp_nov,
'12' => $this->lang->cp_dec
);
foreach ($month as $digit => $name)
{
$out .= "<option value=\"$digit\"" . (($digit == $mon) ? ' selected="selected"' : null) . ">$name</option>\n";
}
return $out;
}
/**
* Create options of "null" to 100 years ago as selectable years
*
* @param int $year the selected year (if any)
* @return string HTML
**/
function select_years($year)
{
$thisyear = gmdate("Y", time()); // Could end up missing the 'current year' if on new years day/eve
$i = $thisyear;
$out = "<option value='0000'></option>\n";
while ($i >= $thisyear-100)
{
$out .= "<option value=\"$i\"" . (($i == $year) ? ' selected="selected"' : null) . ">$i</option>\n";
$i--;
}
return $out;
}
/**
* Generates a select box of skins
*
* @param string $skin user_skin to select
* @author Jason Warner <hide@address.com>
* @since Beta 4.0
* @return string HTML
**/
function select_skins($skin)
{
$out = null;
$query = $this->db->query("SELECT * FROM %pskins");
while ($s = $this->db->nqfetch($query))
{
if ($s['skin_dir'] == 'default') {
$s['skin_name'] .= ' (default)';
}
$out .= "<option value=\"{$s['skin_dir']}\"" . (($s['skin_dir'] == $skin) ? ' selected="selected"' : null) . ">{$s['skin_name']}</option>\n";
}
return $out;
}
/**
* Create options of different timezones
*
* @param int $zone current timezone id
* @return string HTML
**/
function select_timezones($zone)
{
$out = null;
$query = $this->db->query("SELECT zone_id, zone_name, zone_offset, zone_updated, zone_abbrev FROM %ptimezones ORDER BY zone_name ASC");
while($row = $this->db->nqfetch($query))
{
if ($row['zone_updated'] < $this->time)
{
$tz = new $this->modules['timezone']( $this->sets['include_path'] . '/ptzf/' . $row['zone_name'] . '.php' );
$tz->magic2();
if (strlen($tz->abba)<1) $tz->abba='N/A';
$this->db->query("UPDATE %ptimezones SET zone_offset=%d, zone_updated=%d, zone_abbrev='%s' WHERE zone_id=%d",
$tz->gmt_offset, $tz->next_update, $tz->abba, $row['zone_id']);
$row['zone_abbrev'] = $tz->abba;
$row['zone_offset'] = $tz->gmt_offset;
}
$padding = str_repeat(' ', 30 - strlen($row['zone_name']));
$out .= '<option value="' . $row['zone_id'] . '"' . (($zone == $row['zone_id']) ? ' selected="selected"' : null) . '>' .
$row['zone_name'] . $padding . ' ' . $row['zone_abbrev']. ' (GMT'.(($row['zone_offset'] >= 0) ? '+' : '') . ($row['zone_offset']/3600). ')</option>'."\n";
}
return $out;
}
/**
* Options for an HTML select box (all forums in correct order)
*
* @param int $select Option to set as selected (-1 for all)
* @param int $parent Used to degredate down through the recursive loop
* @param string $space Used to increment the spacing before the text in the box
* @param bool $identify_category Set to true to place a period before the value of a category
* @return string Options for an HTML select box (all forums in correct order)
**/
function select_forums($select = 0, $parent = 0, $space = '', $identify_category = false)
{
$array = $this->forum_grab();
return $this->_select_forums_recurse($array, $select, $parent, $space, $identify_category);
}
/**
* Options for an HTML select box (all forums in correct order)
*
* PRIVATE
*
* @param array $array Array of forums to look through
* @param int $select Option to set as selected (-1 for all)
* @param int $parent Used to degredate down through the recursive loop
* @param string $space Used to increment the spacing before the text in the box
* @param bool $identify_category Set to true to place a period before the value of a category
* @author Mark Elliot <hide@address.com>
* @since Beta 4.0
* @return string Options for an HTML select box (all forums in correct order)
**/
function _select_forums_recurse($array, $select, $parent, $space, $identify_category = false)
{
$arr = $this->forum_array($array, $parent);
$return = null;
foreach ($arr as $val)
{
if (!$this->perms->auth('forum_view', $val['forum_id'])) {
continue;
}
if ($identify_category && !$val['forum_parent']) {
$dot = '.';
} else {
$dot = null;
}
if (($val['forum_id'] != $select) && ($select != -1)) {
$selected = null;
} else {
$selected = ' selected="selected"';
}
$return .= '<option value="' . $dot . $val['forum_id'] . '"' . $selected . '>' . $space . $val['forum_name'] . "</option>\n" .
$this->_select_forums_recurse($array, $select, $val['forum_id'], $space . ' ');
}
return $return;
}
/**
* Create options of selectable languages
*
* @param string $current The current language being used
* @param string $relative Path to look for avatars in (optional)
* @return string HTML
**/
function select_langs($current, $relative = '.')
{
$out = null;
$langs = array();
$dir = opendir($relative . '/languages');
while (($file = readdir($dir)) !== false)
{
if (is_dir($relative . '/languages/' . $file)) {
continue;
}
$code = substr($file, 0, -4);
$ext = substr($file, -4);
if ($ext != '.php') {
continue;
}
$langs[$code] = $this->get_lang_name($code);
}
asort($langs);
foreach ($langs as $code => $name)
{
$out .= "<option value='$code'" . (($code == $current) ? ' selected=\'selected\'' : null) . ">$name</option>\n";
}
return $out;
}
/**
* Fetch the language name for the language code
*
* @param string $code Two character country code
* @return string Language name (in English)
**/
function get_lang_name($code)
{
$code = strtolower($code);
switch($code)
{
case 'bg': return 'Bulgarian'; break;
case 'zh': return 'Chinese'; break;
case 'cs': return 'Czech'; break;
case 'nl': return 'Dutch'; break;
case 'en': return 'English'; break;
case 'fi': return 'Finnish'; break;
case 'fr': return 'French'; break;
case 'de': return 'German'; break;
case 'he': return 'Hebrew'; break;
case 'hu': return 'Hungarian'; break;
case 'id': return 'Indonesian'; break;
case 'it': return 'Italian'; break;
case 'no': return 'Norwegian'; break;
case 'pt': return 'Portuguese'; break;
case 'ru': return 'Russian'; break;
case 'sk': return 'Slovak'; break;
case 'es': return 'Spanish'; break;
case 'sv': return 'Swedish'; break;
default: return $code; break;
}
}
/**
* Adds an entry to the navigation tree
*
* @param string $label Label for the tree entry
* @param string $link URL to link to
* @author Jason Warner <hide@address.com>
* @since Beta 2.1
* @return void
**/
function tree($label, $link = null)
{
$this->tree .= ' <b>»</b> ' . ($link ? "<a href='$link'>$label</a>" : $label);
}
/**
* Traces a forum back to the parent category and adds entries to the tree - see tree()
*
* @param int $f Forum to generate tree for
* @param bool $linklast True to make the last entry a link (default is false)
* @author Jason Warner <hide@address.com>
* @since Beta 2.1
* @return void
**/
function tree_forums($f, $linklast = false)
{
$forumData = $this->forum_grab_sorted();
if (!isset($forumData[$f])) { //error? lets get out while we can
return;
}
$cat = 1; //first forum is always a category
$ft = explode(',', $forumData[$f]['forum_tree']);
foreach ($ft as $i)
{
if ($i) {
if (!$cat) {
$this->tree($forumData[$i]['forum_name'], "$this->self?a=forum&f={$i}");
} else {
$this->tree($forumData[$i]['forum_name'], "$this->self?a=board&c={$i}");
$cat = 0;
}
}
}
if (!$linklast) {
$this->tree($forumData[$f]['forum_name']);
} else {
$this->tree($forumData[$f]['forum_name'], "$this->self?a=forum&f={$f}");
}
}
/**
* Retrieves message icons and puts them into a unordered list
*
* @param string $select Icon to select
* @author Jason Warner <hide@address.com>
* @since Beta 2.0
* @return string HTML-formatted message icons
**/
function get_icons($select = -1)
{
$i = 0;
$icons = array();
$dir = opendir("./skins/$this->skin/mbicons");
while (($file = readdir($dir)) !== false)
{
$ext = substr($file, -4);
if ((($ext == '.gif') || ($ext == '.jpg') || ($ext == '.png')) && !is_dir("./skins/$this->skin/mbicons/$file")) {
$icons[$i] = $file;
$i++;
}
}
closedir($dir);
natsort($icons);
$msgicons = null;
$i = 0;
foreach ($icons as $icon)
{
$msgicons .= "<li><input type=\"radio\" name=\"icon\" id=\"icon$i\" value=\"$icon\"" . (($select == $icon) ? '
checked=\'checked\'' : null) . " /> <label for=\"icon$i\"><img src=\"./skins/$this->skin/mbicons/$icon\" alt=\"{$this->lang->post_icon}\" /></label></li>\n";
$i++;
}
return $msgicons;
}
}
?>