Gazelle/classes/class_format.php

531 lines
16 KiB
PHP
Raw Normal View History

2012-10-11 08:00:15 +00:00
<?
2013-01-03 08:00:30 +00:00
class Format
{
/**
* Torrent Labels
* Map a common display string to a CSS class
* Indexes are lower case
* Note the "tl_" prefix for "torrent label"
*
* There are five basic types:
* * tl_free (leech status)
* * tl_snatched
* * tl_reported
* * tl_approved
* * tl_notice (default)
*
* @var array Strings
*/
private static $TorrentLabels = array(
'default' => 'tl_notice',
'snatched' => 'tl_snatched',
'freeleech' => 'tl_free',
'neutral leech' => 'tl_free tl_neutral',
'personal freeleech' => 'tl_free tl_personal',
'reported' => 'tl_reported',
'bad tags' => 'tl_reported tl_bad_tags',
'bad folders' => 'tl_reported tl_bad_folders',
'bad file names' => 'tl_reported tl_bad_file_names',
'cassette approved' => 'tl_approved tl_cassete',
'lossy master approved' => 'tl_approved tl_lossy_master',
'lossy web approved' => 'tl_approved tl_lossy_web'
);
2013-02-22 08:00:24 +00:00
2012-10-11 08:00:15 +00:00
/**
* Shorten a string
*
* @param $Str string to cut
* @param $Length cut at length
* @param $Hard force cut at length instead of at closest word
* @param $ShowDots Show dots at the end
* @return string formatted string
*/
public static function cut_string($Str, $Length, $Hard = false, $ShowDots = true) {
if (mb_strlen($Str, 'UTF-8') > $Length) {
if ($Hard == 0) {
// Not hard, cut at closest word
$CutDesc = mb_substr($Str, 0, $Length, 'UTF-8');
$DescArr = explode(' ', $CutDesc);
if (count($DescArr) > 1) {
array_pop($DescArr);
$CutDesc = implode(' ', $DescArr);
}
if ($ShowDots) { $CutDesc .= '...'; }
} else {
$CutDesc = mb_substr($Str, 0, $Length, 'UTF-8');
if ($ShowDots) { $CutDesc .= '...'; }
}
return $CutDesc;
} else {
return $Str;
}
}
/**
* Gets the CSS class corresponding to a ratio
*
* @param $Ratio ratio to get the css class for
* @return string the CSS class corresponding to the ratio range
*/
public static function get_ratio_color($Ratio) {
if ($Ratio < 0.1) { return 'r00'; }
if ($Ratio < 0.2) { return 'r01'; }
if ($Ratio < 0.3) { return 'r02'; }
if ($Ratio < 0.4) { return 'r03'; }
if ($Ratio < 0.5) { return 'r04'; }
if ($Ratio < 0.6) { return 'r05'; }
if ($Ratio < 0.7) { return 'r06'; }
if ($Ratio < 0.8) { return 'r07'; }
if ($Ratio < 0.9) { return 'r08'; }
if ($Ratio < 1) { return 'r09'; }
if ($Ratio < 2) { return 'r10'; }
if ($Ratio < 5) { return 'r20'; }
return 'r50';
}
/**
* Calculates and formats a ratio.
*
* @param int $Dividend AKA numerator
* @param int $Divisor
* @param boolean $Color if true, ratio will be coloured.
2012-10-27 08:00:09 +00:00
* @return string formatted ratio HTML
2012-10-11 08:00:15 +00:00
*/
2012-10-27 08:00:09 +00:00
public static function get_ratio_html($Dividend, $Divisor, $Color = true)
{
$Ratio = self::get_ratio($Dividend, $Divisor);
if ($Ratio === false) return '--';
if ($Ratio === '∞') return '<span class="r99" title="Infinite">∞</span>';
if ($Color)
$Ratio = sprintf('<span class="%s" title="%s">%s</span>',
self::get_ratio_color($Ratio),
self::get_ratio($Dividend, $Divisor, 5),
$Ratio
);
2012-10-11 08:00:15 +00:00
return $Ratio;
}
2012-10-27 08:00:09 +00:00
/**
* Returns ratio
* @param int $Dividend
* @param int $Divisor
2012-11-09 08:00:18 +00:00
* @param int $Decimal floor to n decimals (eg Subtract .005 to floor to 2 decimals)
* @return boolean|string
2012-10-27 08:00:09 +00:00
*/
public function get_ratio ($Dividend, $Divisor, $Decimal = 2)
{
if ($Divisor == 0 && $Dividend == 0) return false;
if ($Divisor == 0) return '∞';
2012-11-09 08:00:18 +00:00
return number_format(max($Dividend/$Divisor - (0.5/pow(10, $Decimal)), 0), $Decimal);
2012-10-27 08:00:09 +00:00
}
2012-10-11 08:00:15 +00:00
/**
* Gets the query string of the current page, minus the parameters in $Exclude
*
* @param array $Exclude Query string parameters to leave out, or blank to include all parameters.
* @return An HTML sanatized query string
*/
public static function get_url($Exclude = false) {
if ($Exclude !== false) {
$QueryItems = array();
parse_str($_SERVER['QUERY_STRING'], $QueryItems);
foreach ($QueryItems AS $Key => $Val) {
if (!in_array(strtolower($Key),$Exclude)) {
$Query[$Key] = $Val;
}
}
if (empty($Query)) {
return;
}
return display_str(http_build_query($Query));
} else {
return display_str($_SERVER['QUERY_STRING']);
}
}
/**
* Finds what page we're on and gives it to us, as well as the LIMIT clause for SQL
* Takes in $_GET['page'] as an additional input
*
* @param $PerPage Results to show per page
* @param $DefaultResult Optional, which result's page we want if no page is specified
* If this parameter is not specified, we will default to page 1
*
* @return array(int,string) What page we are on, and what to use in the LIMIT section of a query
* i.e. "SELECT [...] LIMIT $Limit;"
*/
public static function page_limit($PerPage, $DefaultResult = 1) {
if (!isset($_GET['page'])) {
$Page = ceil($DefaultResult/$PerPage);
if ($Page == 0) $Page = 1;
$Limit=$PerPage;
} else {
if (!is_number($_GET['page'])) {
error(0);
}
$Page = $_GET['page'];
if ($Page <= 0) { $Page = 1; }
$Limit=$PerPage*$Page-$PerPage . ', ' . $PerPage;
}
return array($Page,$Limit);
}
// A9 magic. Some other poor soul can write the phpdoc.
// For data stored in memcached catalogues (giant arrays), eg. forum threads
public static function catalogue_limit($Page, $PerPage, $CatalogueSize=500) {
$CatalogueID = floor(($PerPage*$Page-$PerPage)/$CatalogueSize);;
$CatalogueLimit = ($CatalogueID*$CatalogueSize).', '.$CatalogueSize;
return array($CatalogueID, $CatalogueLimit);
}
public static function catalogue_select($Catalogue, $Page, $PerPage, $CatalogueSize=500) {
return array_slice($Catalogue,(($PerPage*$Page-$PerPage)%$CatalogueSize),$PerPage,true);
}
/* Get pages
* Returns a page list, given certain information about the pages.
*
* @param int $StartPage: The current record the page you're on starts with.
* eg. if you're on page 2 of a forum thread with 25 posts per page, $StartPage is 25.
* If you're on page 1, $StartPage is 0.
* @param int $TotalRecords: The total number of records in the result set.
* eg. if you're on a forum thread with 152 posts, $TotalRecords is 152.
* @param int $ItemsPerPage: Self-explanatory. The number of records shown on each page
* eg. if there are 25 posts per forum page, $ItemsPerPage is 25.
* @param int $ShowPages: The number of page links that are shown.
* eg. If there are 20 pages that exist, but $ShowPages is only 11, only 11 links will be shown.
* @param string $Anchor A URL fragment to attach to the links.
* eg. '#comment12'
* @return A sanitized HTML page listing.
*/
public static function get_pages($StartPage,$TotalRecords,$ItemsPerPage,$ShowPages=11,$Anchor='') {
global $Document, $Method, $Mobile;
$Location = $Document.'.php';
$StartPage = ceil($StartPage);
$TotalPages = 0;
if ($TotalRecords > 0) {
$StartPage = min($StartPage, ceil($TotalRecords/$ItemsPerPage));
$ShowPages--;
$TotalPages = ceil($TotalRecords/$ItemsPerPage);
if ($TotalPages > $ShowPages) {
$StartPosition = $StartPage-round($ShowPages/2);
if ($StartPosition <= 0) {
$StartPosition = 1;
} else {
if ($StartPosition >= ($TotalPages-$ShowPages)) {
$StartPosition = $TotalPages-$ShowPages;
}
}
$StopPage = $ShowPages+$StartPosition;
} else {
$StopPage = $TotalPages;
$StartPosition = 1;
}
$StartPosition = max($StartPosition, 1);
2012-10-27 08:00:09 +00:00
$QueryString = self::get_url(array('page','post'));
2012-10-11 08:00:15 +00:00
if ($QueryString != '') {
$QueryString = '&amp;'.$QueryString;
}
$Pages = '';
if ($StartPage > 1) {
$Pages .= '<a href="'.$Location.'?page=1'.$QueryString.$Anchor.'"><strong>&lt;&lt; First</strong></a> ';
$Pages .= '<a href="'.$Location.'?page='.($StartPage-1).$QueryString.$Anchor.'" class="pager_prev"><strong>&lt; Prev</strong></a> | ';
}
//End change
if (!$Mobile) {
for ($i = $StartPosition; $i <= $StopPage; $i++) {
if ($i != $StartPage) {
$Pages .= '<a href="'.$Location.'?page='.$i.$QueryString.$Anchor.'">';
}
$Pages .= "<strong>";
if ($i*$ItemsPerPage > $TotalRecords) {
$Pages .= ((($i-1)*$ItemsPerPage)+1).'-'.($TotalRecords);
} else {
$Pages .= ((($i-1)*$ItemsPerPage)+1).'-'.($i*$ItemsPerPage);
}
$Pages .= "</strong>";
if ($i != $StartPage) {
$Pages.='</a>';
}
if ($i < $StopPage) {
$Pages.=" | ";
}
}
} else {
$Pages .= $StartPage;
}
if ($StartPage && $StartPage < $TotalPages) {
$Pages .= ' | <a href="'.$Location.'?page='.($StartPage+1).$QueryString.$Anchor.'" class="pager_next"><strong>Next &gt;</strong></a> ';
$Pages .= '<a href="'.$Location.'?page='.$TotalPages.$QueryString.$Anchor.'"><strong> Last &gt;&gt;</strong></a>';
}
}
if ($TotalPages > 1) {
return $Pages;
}
}
/**
* Format a size in bytes as a human readable string in KiB/MiB/...
*
* @param int $Size
* @param int $Levels Number of decimal places. Defaults to 2, unless the size >= 1TB, in which case it defaults to 4.
* @return string formatted number.
*/
public static function get_size($Size, $Levels = 2) {
$Units = array(' B',' KB',' MB',' GB',' TB',' PB',' EB',' ZB',' YB');
$Size = (double) $Size;
for($Steps = 0; abs($Size) >= 1024; $Size /= 1024, $Steps++) {}
if (func_num_args() == 1 && $Steps >= 4) {
$Levels++;
}
2012-10-27 08:00:09 +00:00
return number_format($Size, $Levels) . $Units[$Steps];
2012-10-11 08:00:15 +00:00
}
/**
* Format a number as a multiple of its highest power of 1000 (eg. 10035 -> '10.04k')
*
* @param int $Number
* @return string formatted number.
*/
public static function human_format($Number) {
$Steps = 0;
while($Number>=1000) {
$Steps++;
$Number=$Number/1000;
}
switch ($Steps) {
case 0: return round($Number); break;
case 1: return round($Number,2).'k'; break;
case 2: return round($Number,2).'M'; break;
case 3: return round($Number,2).'G'; break;
case 4: return round($Number,2).'T'; break;
case 5: return round($Number,2).'P'; break;
default:
return round($Number,2).'E + '.$Steps*3;
}
}
/**
* Given a formatted string of a size, get the number of bytes it represents.
*
* @param string $Size formatted size string, eg. 123.45k
* @return Number of bytes it represents, eg. (123.45 * 1024)
*/
public static function get_bytes($Size) {
list($Value,$Unit) = sscanf($Size, "%f%s");
$Unit = ltrim($Unit);
if (empty($Unit)) {
return $Value ? round($Value) : 0;
}
switch(strtolower($Unit[0])) {
case 'k': return round($Value * 1024);
case 'm': return round($Value * 1048576);
case 'g': return round($Value * 1073741824);
case 't': return round($Value * 1099511627776);
default: return 0;
}
}
/**
* Reverse the effects of display_str - un-sanitize HTML.
* Use sparingly.
*
* @param string $Str the string to unsanitize
* @return unsanitized string
*/
// Use sparingly
public static function undisplay_str($Str) {
return mb_convert_encoding($Str, 'UTF-8', 'HTML-ENTITIES');
}
/**
* Echo data sent in a GET form field, useful for text areas.
*
* @param string $Index the name of the form field
* @param boolean $Return if set to true, value is returned instead of echoed.
* @return Sanitized value of field index if $Return == true
*/
public static function form($Index, $Return = false) {
if (!empty($_GET[$Index])) {
if ($Return) {
return display_str($_GET[$Index]);
} else {
echo display_str($_GET[$Index]);
}
}
}
/**
* Convenience function to echo out selected='selected' and checked='checked' so you don't have to.
*
* @param $Name the name of the option in the select (or field in $Array)
* @param $Value the value that the option must be for the option to be marked as selected or checked
* @param $Attribute The value returned/echoed is $Attribute="$Attribute"
* @param $Array The array the option is in, defaults to GET.
* @return
*/
public static function selected($Name, $Value, $Attribute='selected', $Array = array()) {
if (empty($Array)) {
2012-10-12 08:00:19 +00:00
$Array = $_GET;
2012-10-11 08:00:15 +00:00
}
if (isset($Array[$Name]) && $Array[$Name]!=='') {
if ($Array[$Name] == $Value) {
echo ' '.$Attribute.'="'.$Attribute.'"';
}
}
}
2012-11-18 08:00:19 +00:00
/**
* Return a css class name if certain conditions are met. Mainly useful to mark links as 'active'
*
* @param mixed $Target The variable to compare all values against
* @param mixed $Tests The condition values. Type and dimension determines test type
* Scalar: $Tests must be equal to $Target for a match
* Vector: All elements in $Tests must correspond to equal values in $Target
* 2-dimensional array: At least one array must be identical to $Target
* @param string $ClassName CSS class name to return
* @param bool $AddAttribute Whether to include the "class" attribute in the output
* @param string $UserIDKey Key in _REQUEST for a user id parameter, which if given will be compared to $LoggedUser[ID]
*
* @return class name on match, otherwise an empty string
*/
public static function add_class($Target, $Tests, $ClassName, $AddAttribute, $UserIDKey = false) {
global $LoggedUser;
if ($UserIDKey && isset($_REQUEST[$UserIDKey]) && $LoggedUser['ID'] != $_REQUEST[$UserIDKey]) {
return '';
}
$Pass = true;
if (!is_array($Tests)) {
// Scalars are nice and easy
$Pass = $Tests === $Target;
} elseif (!is_array($Tests[0])) {
// Test all values in vectors
foreach ($Tests as $Type => $Part) {
if (!isset($Target[$Type]) || $Target[$Type] !== $Part) {
$Pass = false;
break;
}
}
} else {
// Loop to the end of the array or until we find a matching test
foreach ($Tests as $Test) {
$Pass = true;
// If $Pass remains true after this test, it's a match
foreach ($Test as $Type => $Part) {
if (!isset($Target[$Type]) || $Target[$Type] !== $Part) {
$Pass = false;
break;
}
}
if ($Pass) {
break;
}
}
}
if (!$Pass) {
return '';
}
if ($AddAttribute) {
return ' class="'.$ClassName.'"';
}
return " $ClassName";
}
2012-10-11 08:00:15 +00:00
/**
* Detect the encoding of a string and transform it to UTF-8.
*
* @param string $Str
* @return UTF-8 encoded version of $Str
*/
public static function make_utf8($Str) {
if ($Str!="") {
2012-10-27 08:00:09 +00:00
if (self::is_utf8($Str)) { $Encoding="UTF-8"; }
2012-10-11 08:00:15 +00:00
if (empty($Encoding)) { $Encoding=mb_detect_encoding($Str,'UTF-8, ISO-8859-1'); }
if (empty($Encoding)) { $Encoding="ISO-8859-1"; }
if ($Encoding=="UTF-8") { return $Str; }
else { return @mb_convert_encoding($Str,"UTF-8",$Encoding); }
}
}
/**
* Magical function.
*
* @param string $Str function to detect encoding on.
* @return true if the string is in UTF-8.
*/
public static function is_utf8($Str) {
return preg_match('%^(?:
[\x09\x0A\x0D\x20-\x7E] // ASCII
| [\xC2-\xDF][\x80-\xBF] // non-overlong 2-byte
| \xE0[\xA0-\xBF][\x80-\xBF] // excluding overlongs
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} // straight 3-byte
| \xED[\x80-\x9F][\x80-\xBF] // excluding surrogates
| \xF0[\x90-\xBF][\x80-\xBF]{2} // planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} // planes 4-15
| \xF4[\x80-\x8F][\x80-\xBF]{2} // plane 16
)*$%xs', $Str
);
}
2013-01-03 08:00:30 +00:00
/**
* Modified accessor for the $TorrentLabels array
2013-02-22 08:00:24 +00:00
*
2013-01-03 08:00:30 +00:00
* Converts $text to lowercase and strips non-word characters
2013-02-22 08:00:24 +00:00
*
2013-01-03 08:00:30 +00:00
* @param string $text Search string
* @return string CSS class(es)
*/
public static function find_torrent_label_class ($text)
{
$index = mb_eregi_replace('(?:[^\w\d\s]+)', '', strtolower($text));
if (isset(self::$TorrentLabels[$index])) return self::$TorrentLabels[$index];
return self::$TorrentLabels['default'];
}
/**
* Creates a strong element that notes the torrent's state.
* Eg: snatched/freeleech/neutral leech/reported
*
* The CSS class is infered using find_torrent_label_class($text)
*
* @param string $text Display text
* @param string $class Custom CSS class
* @return string Strong element
*/
public static function torrent_label ($text, $class='')
{
if (empty($class)) $class = self::find_torrent_label_class($text);
return sprintf('<strong class="torrent_label %1$s" title="%2$s">%2$s</strong>',
display_str($class), display_str($text));
}
2012-10-27 08:00:09 +00:00
}