Empty commit

This commit is contained in:
Git 2015-01-28 08:00:26 +00:00
parent 4b19c58200
commit 96f89020d0
42 changed files with 1489 additions and 1227 deletions

View File

@ -104,6 +104,11 @@ define('LOG_ENTRIES_PER_PAGE', 50);
// Cache catalogues
define('THREAD_CATALOGUE', 500); // Limit to THREAD_CATALOGUE posts per cache key.
// Email delivery method and information
define('EMAIL_DELIVERY_TYPE', ''); // should be either 'mailgun' to use mailgun services or 'local' to use a local SMTP server or relay
define('MAILGUN_API_KEY', '');
define('MAILGUN_API_URL', '');
// IRC settings
define('BOT_NICK', '');
define('BOT_SERVER', ''); // IRC server address. Used for onsite chat tool.

View File

@ -180,46 +180,6 @@ protected function listen() {
}
}
if (preg_match("/:([^!]+)![^\s]* QUIT.* /", $this->Data, $Nick)) {
if (isset($this->Identified[$Nick[1]])) {
unset($this->Identified[$Nick[1]]);
}
if (isset($this->DisabledUsers[$Nick[1]])) {
if ($this->DisabledUsers[$Nick[1]]['UserID'] != 0) {
G::$DB->query("
DELETE FROM disable_list
WHERE Nick = '$Nick[1]'");
G::$Cache->decrement_value('num_disablees');
}
unset($this->DisabledUsers[$Nick[1]]);
}
}
if (preg_match("/:([^!]+)![^\s]* PART ".BOT_DISABLED_CHAN.'/', $this->Data, $Nick)) {
if (isset($this->DisabledUsers[$Nick[1]])) {
if ($this->DisabledUsers[$Nick[1]]['UserID'] != 0) {
G::$DB->query("
DELETE FROM disable_list
WHERE Nick = '$Nick[1]'");
G::$Cache->decrement_value('num_disablees');
}
unset($this->DisabledUsers[$Nick[1]]);
}
}
if (preg_match("/:([^!]+)![^\s]* KICK ".BOT_DISABLED_CHAN.'.* /', $this->Data, $Nick)) {
$Nick = explode(' ', $Nick[0]);
if (isset($this->DisabledUsers[$Nick[3]])) {
if ($this->DisabledUsers[$Nick[3]]['UserID'] != 0) {
G::$DB->query("
DELETE FROM disable_list
WHERE Nick = '$Nick[3]'");
G::$Cache->decrement_value('num_disablees');
}
unset($this->DisabledUsers[$Nick[3]]);
}
}
if (preg_match('/End of message of the day./', $this->Data)) {
$this->connect_events();
}

View File

@ -3,13 +3,22 @@ class Misc {
/**
* Send an email.
*
* We can do this one of two ways - either using MailGun or with PHP's mail function.
* Checks for EMAIL_DELIVERY_TYPE and then proceeds as directed to send e-mail.
*
* @param string $To the email address to send it to.
* @param string $Subject
* @param string $Body
* @param string $From The user part of the user@NONSSL_SITE_URL email address.
* @param string $ContentType text/plain or text/html
*/
public static function send_email($To, $Subject, $Body, $From = 'noreply', $ContentType = 'text/plain') {
public static function send_email($To, $Subject, $Body, $From, $ContentType) {
switch (EMAIL_DELIVERY_TYPE) {
case 'local':
// remove the next line if you want to send HTML email from some places...
$ContentType='text/plain';
$Headers = 'MIME-Version: 1.0'."\r\n";
$Headers .= 'Content-type: '.$ContentType.'; charset=iso-8859-1'."\r\n";
$Headers .= 'From: '.SITE_NAME.' <'.$From.'@'.NONSSL_SITE_URL.'>'."\r\n";
@ -18,6 +27,40 @@ public static function send_email($To, $Subject, $Body, $From = 'noreply', $Cont
$Headers .= 'Message-Id: <'.Users::make_secret().'@'.NONSSL_SITE_URL.">\r\n";
$Headers .= 'X-Priority: 3'."\r\n";
mail($To, $Subject, $Body, $Headers, "-f $From@".NONSSL_SITE_URL);
break;
case 'mailgun':
// set up our message first
$From .= '@'.NONSSL_SITE_URL;
$OutgoingEmail = array(
'from' => $From,
'to' => $To,
'h:Reply-To' => $From,
'subject' => $Subject,
'text' => $Body);
// now let's POST it to mailgun
$Curl = curl_init();
curl_setopt($Curl, CURLOPT_URL, MAILGUN_API_URL);
curl_setopt($Curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($Curl, CURLOPT_USERPWD, 'api:'.MAILGUN_API_KEY);
curl_setopt($Curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($Curl, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($Curl, CURLOPT_POST, true);
curl_setopt($Curl, CURLOPT_POSTFIELDS, $OutgoingEmail);
$RequestResult = curl_exec($Curl);
$RequestStatusCode = curl_getinfo($Curl, CURLINFO_HTTP_CODE);
curl_close($Curl);
// alert on failed emails
if ($RequestStatusCode != 200) {
send_irc('PRIVMSG '.STATUS_CHAN." !dev email failed to $To with error message $RequestResult");
}
break;
default:
die('You have either not configured an email delivery method in config.php or your value is incorrect.');
break;
}
}

View File

@ -110,12 +110,13 @@ public function error($Msg, $Halt = false) {
/**
* Escape special characters before sending them to the Sphinx server.
* Two escapes needed because the first one is eaten up by the mysql driver.
* Lowercase ASCII characters because some Sphinx operators are all caps words.
*
* @param string $String string to escape
* @return escaped string
*/
public static function sph_escape_string($String) {
return strtr($String, array(
return strtr(strtolower($String), array(
'('=>'\\\\(',
')'=>'\\\\)',
'|'=>'\\\\|',

View File

@ -30,7 +30,7 @@ public function __construct($Server = SPHINXQL_HOST, $Port = SPHINXQL_PORT, $Soc
* Specify what data the Sphinx query is supposed to return
*
* @param string $Fields Attributes and expressions
* @return current Sphinxql query object
* @return current SphinxqlQuery object
*/
public function select($Fields) {
$this->Select = $Fields;
@ -41,7 +41,7 @@ public function select($Fields) {
* Specify the indexes to use in the search
*
* @param string $Indexes comma separated list of indexes
* @return current Sphinxql query object
* @return current SphinxqlQuery object
*/
public function from($Indexes) {
$this->Indexes = $Indexes;
@ -54,7 +54,7 @@ public function from($Indexes) {
* @param string $Attribute attribute which the filter will apply to
* @param mixed $Values scalar or array of numerical values. Array uses boolean OR in query condition
* @param bool $Exclude whether to exclude or include matching documents. Default mode is to include matches
* @return current Sphinxql query object
* @return current SphinxqlQuery object
*/
public function where($Attribute, $Values, $Exclude = false) {
if (empty($Attribute) || !isset($Values)) {
@ -95,7 +95,7 @@ public function where($Attribute, $Values, $Exclude = false) {
* @param string $Attribute attribute which the filter will apply to
* @param array $Value upper limit for matches
* @param bool $Inclusive whether to use <= or <
* @return current Sphinxql query object
* @return current SphinxqlQuery object
*/
public function where_lt($Attribute, $Value, $Inclusive = false) {
if (empty($Attribute) || !isset($Value) || !is_number($Value)) {
@ -112,7 +112,7 @@ public function where_lt($Attribute, $Value, $Inclusive = false) {
* @param string $Attribute attribute which the filter will apply to
* @param array $Value lower limit for matches
* @param bool $Inclusive whether to use >= or >
* @return current Sphinxql query object
* @return current SphinxqlQuery object
*/
public function where_gt($Attribute, $Value, $Inclusive = false) {
if (empty($Attribute) || !isset($Value) || !is_number($Value)) {
@ -128,7 +128,7 @@ public function where_gt($Attribute, $Value, $Inclusive = false) {
*
* @param string $Attribute attribute which the filter will apply to
* @param array $Values pair of numerical values that defines the filter range
* @return current Sphinxql query object
* @return current SphinxqlQuery object
*/
public function where_between($Attribute, $Values) {
if (empty($Attribute) || empty($Values) || count($Values) != 2 || !is_number($Values[0]) || !is_number($Values[1])) {
@ -145,7 +145,7 @@ public function where_between($Attribute, $Values) {
*
* @param string $Expr query expression
* @param string $Field field to match $Expr against. Default is *, which means all available fields
* @return current Sphinxql query object
* @return current SphinxqlQuery object
*/
public function where_match($Expr, $Field = '*', $Escape = true) {
if (empty($Expr)) {
@ -168,7 +168,7 @@ public function where_match($Expr, $Field = '*', $Escape = true) {
* @param string $Attribute attribute to use for sorting.
* Passing an empty attribute value will clear the current sort settings
* @param string $Mode sort method to apply to the selected attribute
* @return current Sphinxql query object
* @return current SphinxqlQuery object
*/
public function order_by($Attribute = false, $Mode = false) {
if (empty($Attribute)) {
@ -184,7 +184,7 @@ public function order_by($Attribute = false, $Mode = false) {
*
* @param string $Attribute group matches with the same $Attribute value.
* Passing an empty attribute value will clear the current group settings
* @return current Sphinxql query object
* @return current SphinxqlQuery object
*/
public function group_by($Attribute = false) {
if (empty($Attribute)) {
@ -201,7 +201,7 @@ public function group_by($Attribute = false) {
* @param string $Attribute attribute to use for sorting.
* Passing an empty attribute will clear the current group sort settings
* @param string $Mode sort method to apply to the selected attribute
* @return current Sphinxql query object
* @return current SphinxqlQuery object
*/
public function order_group_by($Attribute = false, $Mode = false) {
if (empty($Attribute)) {
@ -218,7 +218,7 @@ public function order_group_by($Attribute = false, $Mode = false) {
* @param int $Offset number of matches to discard
* @param int $Limit number of matches to return
* @param int $MaxMatches number of results to store in the Sphinx server's memory. Must be >= ($Offset+$Limit)
* @return current Sphinxql query object
* @return current SphinxqlQuery object
*/
public function limit($Offset, $Limit, $MaxMatches = SPHINX_MAX_MATCHES) {
$this->Limits = "$Offset, $Limit";
@ -231,7 +231,7 @@ public function limit($Offset, $Limit, $MaxMatches = SPHINX_MAX_MATCHES) {
*
* @param string $Name setting name
* @param mixed $Value value
* @return current Sphinxql query object
* @return current SphinxqlQuery object
*/
public function set($Name, $Value) {
$this->Options[$Name] = $Value;
@ -265,6 +265,7 @@ private function build_query() {
}
if (!empty($this->Filters)) {
$this->QueryString .= "\nWHERE ".implode("\n\tAND ", $this->Filters);
unset($this->Filters['expr']);
}
if (!empty($this->GroupBy)) {
$this->QueryString .= "\nGROUP BY $this->GroupBy";
@ -288,7 +289,7 @@ private function build_query() {
* Construct and send the query. Register the query in the global Sphinxql object
*
* @param bool GetMeta whether to fetch meta data for the executed query. Default is yes
* @return Sphinxql result object
* @return SphinxqlResult object
*/
public function query($GetMeta = true) {
$QueryStartTime = microtime(true);
@ -310,7 +311,7 @@ public function query($GetMeta = true) {
*
* @param string Query query expression
* @param bool GetMeta whether to fetch meta data for the executed query. Default is yes
* @return Sphinxql result object
* @return SphinxqlResult object
*/
public function raw_query($Query, $GetMeta = true) {
$this->QueryString = $Query;
@ -321,7 +322,7 @@ public function raw_query($Query, $GetMeta = true) {
* Run a pre-processed query. Only used internally
*
* @param bool GetMeta whether to fetch meta data for the executed query
* @return Sphinxql result object
* @return SphinxqlResult object
*/
private function send_query($GetMeta) {
if (!$this->QueryString) {
@ -368,6 +369,16 @@ private function get_meta() {
return $this->raw_query("SHOW META", false)->to_pair(0, 1);
}
/**
* Copy attribute filters from another SphinxqlQuery object
*
* @param SphinxqlQuery $SphQLSource object to copy the filters from
* @return current SphinxqlQuery object
*/
public function copy_attributes_from($SphQLSource) {
$this->Filters = $SphQLSource->Filters;
}
/**
* Store error messages
*/

View File

@ -197,6 +197,7 @@ public static function get_aliases() {
public static function remove_aliases($Tags) {
$TagAliases = self::get_aliases();
if (isset($Tags['include'])) {
$End = count($Tags['include']);
for ($i = 0; $i < $End; $i++) {
foreach ($TagAliases as $TagAlias) {
@ -206,7 +207,11 @@ public static function remove_aliases($Tags) {
}
}
}
// Only keep unique entries after unifying tag standard
$Tags['include'] = array_unique($Tags['include']);
}
if (isset($Tags['exclude'])) {
$End = count($Tags['exclude']);
for ($i = 0; $i < $End; $i++) {
foreach ($TagAliases as $TagAlias) {
@ -216,10 +221,9 @@ public static function remove_aliases($Tags) {
}
}
}
// Only keep unique entries after unifying tag standard
$Tags['include'] = array_unique($Tags['include']);
$Tags['exclude'] = array_unique($Tags['exclude']);
}
return $Tags;
}

View File

@ -21,7 +21,6 @@ public static function site_ban_ip($IP) {
G::$DB->set_query_id($QueryID);
G::$Cache->cache_value('ip_bans_'.$A, $IPBans, 0);
}
$Debug->log_var($IPBans, 'IP bans for class '.$A);
foreach ($IPBans as $Index => $IPBan) {
list ($ID, $FromIP, $ToIP) = $IPBan;
if ($IPNum >= $FromIP && $IPNum <= $ToIP) {
@ -104,9 +103,21 @@ public static function get_host_by_ip($IP) {
* @return a span with JavaScript code
*/
public static function get_host_by_ajax($IP) {
static $ID = 0;
++$ID;
return '<span id="host_'.$ID.'">Resolving host...<script type="text/javascript">ajax.get(\'tools.php?action=get_host&ip='.$IP.'\',function(host) {$(\'#host_'.$ID.'\').raw().innerHTML=host;});</script></span>';
static $IPs = array();
$Class = strtr($IP, '.', '-');
$HTML = '<span class="host_'.$Class.'">Resolving host...';
if (!isset($IPs[$IP])) {
$HTML .= '<script type="text/javascript">' .
'$(document).ready(function() {' .
'$.get(\'tools.php?action=get_host&ip='.$IP.'\', function(host) {' .
'$(\'.host_'.$Class.'\').html(host);' .
'});' .
'});' .
'</script>';
}
$HTML .= '</span>';
$IPs[$IP] = 1;
return $HTML;
}
@ -146,11 +157,22 @@ public static function display_ip($IP) {
}
public static function get_country_code_by_ajax($IP) {
static $ID = 0;
++$ID;
return '<span id="cc_'.$ID.'">Resolving CC...<script type="text/javascript">ajax.get(\'tools.php?action=get_cc&ip='.$IP.'\', function(cc) {$(\'#cc_'.$ID.'\').raw().innerHTML = cc;});</script></span>';
static $IPs = array();
$Class = strtr($IP, '.', '-');
$HTML = '<span class="cc_'.$Class.'">Resolving CC...';
if (!isset($IPs[$IP])) {
$HTML .= '<script type="text/javascript">' .
'$(document).ready(function() {' .
'$.get(\'tools.php?action=get_cc&ip='.$IP.'\', function(cc) {' .
'$(\'.cc_'.$Class.'\').html(cc);' .
'});' .
'});' .
'</script>';
}
$HTML .= '</span>';
$IPs[$IP] = 1;
return $HTML;
}
/**
* Disable an array of users.

View File

@ -11,6 +11,7 @@ public static function render_linkbox($Selected) {
<a href="top10.php?type=tags" class="brackets"><?=self::get_selected_link("Tags", $Selected == "tags")?></a>
<a href="top10.php?type=votes" class="brackets"><?=self::get_selected_link("Favorites", $Selected == "votes")?></a>
<a href="top10.php?type=donors" class="brackets"><?=self::get_selected_link("Donors", $Selected == "donors")?></a>
</div>
<?
}

View File

@ -0,0 +1,769 @@
<?
class TorrentSearch {
const TAGS_ANY = 0;
const TAGS_ALL = 1;
const SPH_BOOL_AND = ' ';
const SPH_BOOL_OR = ' | ';
/**
* Map of sort mode => attribute name for ungrouped torrent page
*/
public static $SortOrders = array(
'year' => 'year',
'time' => 'id',
'size' => 'size',
'seeders' => 'seeders',
'leechers' => 'leechers',
'snatched' => 'snatched',
'random' => 1);
/**
* Map of sort mode => attribute name for grouped torrent page
*/
private static $SortOrdersGrouped = array(
'year' => 'year',
'time' => 'id',
'size' => 'maxsize',
'seeders' => 'sumseeders',
'leechers' => 'sumleechers',
'snatched' => 'sumsnatched',
'random' => 1);
/**
* Map of sort mode => aggregate expression required for some grouped sort orders
*/
private static $AggregateExp = array(
'size' => 'MAX(size) AS maxsize',
'seeders' => 'SUM(seeders) AS sumseeders',
'leechers' => 'SUM(leechers) AS sumleechers',
'snatched' => 'SUM(snatched) AS sumsnatched');
/**
* Map of attribute name => global variable name with list of values that can be used for filtering
*/
private static $Attributes = array(
'filter_cat' => false,
'releasetype' => 'ReleaseTypes',
'freetorrent' => false,
'hascue' => false,
'haslog' => false,
'scene' => false,
'vanityhouse' => false,
'year' => false);
/**
* List of fields that can be used for fulltext searches
*/
private static $Fields = array(
'artistname' => 1,
'cataloguenumber' => 1,
'description' => 1,
'encoding' => 1,
'filelist' => 1,
'format' => 1,
'groupname' => 1,
'media' => 1,
'recordlabel' => 1,
'remastercataloguenumber' => 1,
'remasterrecordlabel' => 1,
'remastertitle' => 1,
'remasteryear' => 1,
'searchstr' => 1,
'taglist' => 1);
/**
* List of torrent-specific fields that can be used for filtering
*/
private static $TorrentFields = array(
'description' => 1,
'encoding' => 1,
'filelist' => 1,
'format' => 1,
'media' => 1,
'remastercataloguenumber' => 1,
'remasterrecordlabel' => 1,
'remastertitle' => 1,
'remasteryear' => 1);
/**
* Some form field names don't match the ones in the index
*/
private static $FormsToFields = array(
'searchstr' => '(groupname,artistname,yearfulltext)');
/**
* Specify the operator type to use for fields. Empty key sets the default
*/
private static $FieldOperators = array(
'' => self::SPH_BOOL_AND,
'encoding' => self::SPH_BOOL_OR,
'format' => self::SPH_BOOL_OR,
'media' => self::SPH_BOOL_OR);
/**
* Specify the separator character to use for fields. Empty key sets the default
*/
private static $FieldSeparators = array(
'' => ' ',
'encoding' => '|',
'format' => '|',
'media' => '|',
'taglist' => ',');
/**
* Primary SphinxqlQuery object used to get group IDs or torrent IDs for ungrouped searches
*/
private $SphQL;
/**
* Second SphinxqlQuery object used to get torrent IDs if torrent-specific fulltext filters are used
*/
private $SphQLTor;
/**
* Ordered result array or false if query resulted in an error
*/
private $SphResults;
/**
* Requested page
*/
private $Page;
/**
* Number of results per page
*/
private $PageSize;
/**
* Number of results
*/
private $NumResults = 0;
/**
* Array with info from all matching torrent groups
*/
private $Groups = array();
/**
* True if the NOT operator can be used. Sphinx needs at least one positive search condition
*/
private $EnableNegation = false;
/**
* Whether any filters were used
*/
private $Filtered = false;
/**
* Whether the random sort order is selected
*/
private $Random = false;
/**
* Storage for fulltext search terms
* ['Field name' => [
* 'include' => [],
* 'exclude' => [],
* 'operator' => self::SPH_BOOL_AND | self::SPH_BOOL_OR
* ]], ...
*/
private $Terms = array();
/**
* Unprocessed search terms for retrieval
*/
private $RawTerms = array();
/**
* Storage for used torrent-specific attribute filters
* ['Field name' => 'Search expression', ...]
*/
private $UsedTorrentAttrs = array();
/**
* Storage for used torrent-specific fulltext fields
* ['Field name' => 'Search expression', ...]
*/
private $UsedTorrentFields = array();
/**
* Initialize and configure a TorrentSearch object
*
* @param bool $GroupResults whether results should be grouped by group id
* @param string $OrderBy attribute to use for sorting the results
* @param string $OrderWay Whether to use ascending or descending order
* @param int $Page Page number to display
* @param int $PageSize Number of results per page
*/
public function __construct($GroupResults, $OrderBy, $OrderWay, $Page, $PageSize) {
if ($GroupResults && !isset(self::$SortOrdersGrouped[$OrderBy])
|| !$GroupResults && !isset(self::$SortOrders[$OrderBy])
|| !in_array($OrderWay, array('asc', 'desc'))
) {
global $Debug;
$ErrMsg = "TorrentSearch constructor arguments:\n" . print_r(func_get_args(), true);
$Debug->analysis('Bad arguments in TorrentSearch constructor', $ErrMsg, 3600*24);
error('-1');
}
if (!is_number($Page) || $Page < 1) {
$Page = 1;
}
if (check_perms('site_search_many')) {
$this->Page = $Page;
} else {
$this->Page = min($Page, SPHINX_MAX_MATCHES / $PageSize);
}
$ResultLimit = $PageSize;
$this->PageSize = $PageSize;
$this->GroupResults = $GroupResults;
$this->SphQL = new SphinxqlQuery();
if ($OrderBy === 'random') {
$this->SphQL->select('id, groupid')
->order_by('RAND()', '');
$this->Random = true;
$this->Page = 1;
if ($GroupResults) {
// Get more results because ORDER BY RAND() can't be used in GROUP BY queries
$ResultLimit *= 5;
}
} elseif ($GroupResults) {
$Select = 'groupid';
if (isset(self::$AggregateExp[$OrderBy])) {
$Select .= ', ' . self::$AggregateExp[$OrderBy];
}
$this->SphQL->select($Select)
->group_by('groupid')
->order_group_by(self::$SortOrdersGrouped[$OrderBy], $OrderWay)
->order_by(self::$SortOrdersGrouped[$OrderBy], $OrderWay);
} else {
$this->SphQL->select('id, groupid')
->order_by(self::$SortOrders[$OrderBy], $OrderWay);
}
$Offset = ($this->Page - 1) * $ResultLimit;
$MaxMatches = $Offset + $ResultLimit;
$this->SphQL->from('torrents, delta')
->limit($Offset, $ResultLimit, $MaxMatches);
}
/**
* Process search terms and run the main query
*
* @param array $Terms Array containing all search terms (e.g. $_GET)
* @return array List of matching group IDs with torrent ID as key for ungrouped results
*/
public function query($Terms = array()) {
$this->process_search_terms($Terms);
$this->build_query();
$this->run_query();
$this->process_results();
return $this->SphResults;
}
/**
* Internal function that runs the queries needed to get the desired results
*/
private function run_query() {
$SphQLResult = $this->SphQL->query();
if ($SphQLResult->Errno > 0) {
$this->SphResults = false;
return;
}
if ($this->Random && $this->GroupResults) {
$TotalCount = $SphQLResult->get_meta('total_found');
$this->SphResults = $SphQLResult->collect('groupid');
$GroupIDs = array_keys($this->SphResults);
$GroupCount = count($GroupIDs);
while ($SphQLResult->get_meta('total') < $TotalCount && $GroupCount < $this->PageSize) {
// Make sure we get $PageSize results, or all of them if there are less than $PageSize hits
$this->SphQL->where('groupid', $GroupIDs, true);
$SphQLResult = $this->SphQL->query();
if (!$SphQLResult->has_results()) {
break;
}
$this->SphResults += $SphQLResult->collect('groupid');
$GroupIDs = array_keys($this->SphResults);
$GroupCount = count($GroupIDs);
}
if ($GroupCount > $this->PageSize) {
$this->SphResults = array_slice($this->SphResults, 0, $this->PageSize, true);
}
$this->NumResults = count($this->SphResults);
} else {
$this->NumResults = (int)$SphQLResult->get_meta('total_found');
if ($this->GroupResults) {
$this->SphResults = $SphQLResult->collect('groupid');
} else {
$this->SphResults = $SphQLResult->to_pair('id', 'groupid');
}
}
}
/**
* Process search terms and store the parts in appropriate arrays until we know if
* the NOT operator can be used
*/
private function build_query() {
foreach ($this->Terms as $Field => $Words) {
$SearchString = '';
if (isset(self::$FormsToFields[$Field])) {
$Field = self::$FormsToFields[$Field];
}
$QueryParts = array('include' => array(), 'exclude' => array());
if (!$this->EnableNegation && !empty($Words['exclude'])) {
$Words['include'] = $Words['exclude'];
unset($Words['exclude']);
}
if (!empty($Words['include'])) {
foreach ($Words['include'] as $Word) {
$QueryParts['include'][] = Sphinxql::sph_escape_string($Word);
}
}
if (!empty($Words['exclude'])) {
foreach ($Words['exclude'] as $Word) {
$QueryParts['exclude'][] = '!' . Sphinxql::sph_escape_string(substr($Word, 1));
}
}
if (!empty($QueryParts)) {
if (isset($Words['operator'])) {
// Is the operator already specified?
$Operator = $Words['operator'];
} elseif(isset(self::$FieldOperators[$Field])) {
// Does this field have a non-standard operator?
$Operator = self::$FieldOperators[$Field];
} else {
// Go for the default operator
$Operator = self::$FieldOperators[''];
}
if (!empty($QueryParts['include'])) {
$SearchString .= '( ' . implode($Operator, $QueryParts['include']) . ' ) ';
}
if (!empty($QueryParts['exclude'])) {
$SearchString .= implode(' ', $QueryParts['exclude']);
}
$this->SphQL->where_match($SearchString, $Field, false);
if (isset(self::$TorrentFields[$Field])) {
$this->UsedTorrentFields[$Field] = $SearchString;
}
$this->Filtered = true;
}
}
}
/**
* Look at each search term and figure out what to do with it
*
* @param array $Terms Array with search terms from query()
*/
private function process_search_terms($Terms) {
foreach ($Terms as $Key => $Term) {
if (isset(self::$Fields[$Key])) {
$this->process_field($Key, $Term);
} elseif (isset(self::$Attributes[$Key])) {
$this->process_attribute($Key, $Term);
}
$this->RawTerms[$Key] = $Term;
}
$this->post_process_fields();
}
/**
* Process attribute filters and store them in case we need to post-process grouped results
*
* @param string $Attribute Name of the attribute to filter against
* @param mixed $Value The filter's condition for a match
*/
private function process_attribute($Attribute, $Value) {
if ($Value === '') {
return;
}
switch ($Attribute) {
case 'year':
if (!$this->search_year($Value)) {
return;
}
break;
case 'haslog':
if ($Value == 0) {
$this->SphQL->where('haslog', 0);
} elseif ($Value == 100) {
$this->SphQL->where('logscore', 100);
} elseif ($Value < 0) {
$this->SphQL->where_lt('logscore', 100);
$this->SphQL->where('haslog', 1);
} else {
$this->SphQL->where('haslog', 1);
}
$this->UsedTorrentAttrs['haslog'] = $Value;
break;
case 'freetorrent':
if ($Value == 3) {
$this->SphQL->where('freetorrent', 0, true);
$this->UsedTorrentAttrs['freetorrent'] = 3;
} elseif ($Value >= 0 && $Value < 3) {
$this->SphQL->where('freetorrent', $Value);
$this->UsedTorrentAttrs[$Attribute] = $Value;
} else {
return;
}
break;
case 'filter_cat':
if (!is_array($Value)) {
$Value = array_fill_keys(explode('|', $Value), 1);
}
$CategoryFilter = array();
foreach (array_keys($Value) as $Category) {
if (is_number($Category)) {
$CategoryFilter[] = $Category;
} else {
global $Categories;
$ValidValues = array_map('strtolower', $Categories);
if (($CategoryID = array_search(strtolower($Category), $ValidValues)) !== false) {
$CategoryFilter[] = $CategoryID + 1;
}
}
}
if (empty($CategoryFilter)) {
$CategoryFilter = 0;
}
$this->SphQL->where('categoryid', $CategoryFilter);
break;
default:
if (!is_number($Value) && self::$Attributes[$Attribute] !== false) {
// Check if the submitted value can be converted to a valid one
$ValidValuesVarname = self::$Attributes[$Attribute];
global $$ValidValuesVarname;
$ValidValues = array_map('strtolower', $$ValidValuesVarname);
if (($Value = array_search(strtolower($Value), $ValidValues)) === false) {
// Force the query to return 0 results if value is still invalid
$Value = max(array_keys($ValidValues)) + 1;
}
}
$this->SphQL->where($Attribute, $Value);
$this->UsedTorrentAttrs[$Attribute] = $Value;
break;
}
$this->Filtered = true;
}
/**
* Look at a fulltext search term and figure out if it needs special treatment
*
* @param string $Field Name of the search field
* @param string $Term Search expression for the field
*/
private function process_field($Field, $Term) {
$Term = trim($Term);
if ($Term === '') {
return;
}
if ($Field === 'searchstr') {
$this->search_basic($Term);
} elseif ($Field === 'filelist') {
$this->search_filelist($Term);
} elseif ($Field === 'taglist') {
$this->search_taglist($Term);
} else {
$this->add_field($Field, $Term);
}
}
/**
* Some fields may require post-processing
*/
private function post_process_fields() {
if (isset($this->Terms['taglist'])) {
// Replace bad tags with tag aliases
$this->Terms['taglist'] = Tags::remove_aliases($this->Terms['taglist']);
if (isset($this->RawTerms['tags_type']) && (int)$this->RawTerms['tags_type'] === self::TAGS_ANY) {
$this->Terms['taglist']['operator'] = self::SPH_BOOL_OR;
}
// Update the RawTerms array so get_terms() can return the corrected search terms
if (isset($this->Terms['taglist']['include'])) {
$AllTags = $this->Terms['taglist']['include'];
} else {
$AllTags = array();
}
if (isset($this->Terms['taglist']['exclude'])) {
$AllTags = array_merge($AllTags, $this->Terms['taglist']['exclude']);
}
$this->RawTerms['taglist'] = str_replace('_', '.', implode(', ', $AllTags));
}
}
/**
* Handle magic keywords in the basic torrent search
*
* @param string $Term Given search expression
*/
private function search_basic($Term) {
global $Bitrates, $Formats, $Media;
$SearchBitrates = array_map('strtolower', $Bitrates);
array_push($SearchBitrates, 'v0', 'v1', 'v2', '24bit');
$SearchFormats = array_map('strtolower', $Formats);
$SearchMedia = array_map('strtolower', $Media);
foreach (explode(' ', $Term) as $Word) {
if (in_array($Word, $SearchBitrates)) {
$this->add_word('encoding', $Word);
} elseif (in_array($Word, $SearchFormats)) {
$this->add_word('format', $Word);
} elseif (in_array($Word, $SearchMedia)) {
$this->add_word('media', $Word);
} elseif ($Word === '100%') {
$this->process_attribute('haslog', 100);
} elseif ($Word === '!100%') {
$this->process_attribute('haslog', -1);
} else {
$this->add_word('searchstr', $Word);
}
}
}
/**
* Use phrase boundary for file searches to make sure we don't count
* partial hits from multiple files
*
* @param string $Term Given search expression
*/
private function search_filelist($Term) {
$SearchString = '"' . Sphinxql::sph_escape_string($Term) . '"~20';
$this->SphQL->where_match($SearchString, 'filelist', false);
$this->UsedTorrentFields['filelist'] = $SearchString;
$this->EnableNegation = true;
$this->Filtered = true;
}
/**
* Prepare tag searches before sending them to the normal treatment
*
* @param string $Term Given search expression
*/
private function search_taglist($Term) {
$Term = strtr($Term, '.', '_');
$this->add_field('taglist', $Term);
}
/**
* The year filter accepts a range. Figure out how to handle the filter value
*
* @param string $Term Filter condition. Can be an integer or a range with the format X-Y
* @return bool True if parameters are valid
*/
private function search_year($Term) {
$Years = explode('-', $Term);
if (count($Years) === 1 && is_number($Years[0])) {
// Exact year
$this->SphQL->where('year', $Years[0]);
} elseif (count($Years) === 2) {
if (empty($Years[0]) && is_number($Years[1])) {
// Range: 0 - 2005
$this->SphQL->where_lt('year', $Years[1], true);
} elseif (empty($Years[1]) && is_number($Years[0])) {
// Range: 2005 - 2^32-1
$this->SphQL->where_gt('year', $Years[0], true);
} elseif (is_number($Years[0]) && is_number($Years[1])) {
// Range: 2005 - 2009
$this->SphQL->where_between('year', array(min($Years), max($Years)));
} else {
// Invalid input
return false;
}
} else {
// Invalid input
return false;
}
return true;
}
/**
* Add a field filter that doesn't need special treatment
*
* @param string $Field Name of the search field
* @param string $Term Search expression for the field
*/
private function add_field($Field, $Term) {
if (isset(self::$FieldSeparators[$Field])) {
$Separator = self::$FieldSeparators[$Field];
} else {
$Separator = self::$FieldSeparators[''];
}
$Words = explode($Separator, $Term);
foreach ($Words as $Word) {
$this->add_word($Field, $Word);
}
}
/**
* Add a keyword to the array of search terms
*
* @param string $Field Name of the search field
* @param string $Word Keyword
*/
private function add_word($Field, $Word) {
$Word = trim($Word);
// Skip isolated hyphens to enable "Artist - Title" searches
if ($Word === '' || $Word === '-') {
return;
}
if ($Word[0] === '!' && strlen($Word) >= 2 && strpos($Word, '!', 1) === false) {
$this->Terms[$Field]['exclude'][] = $Word;
} else {
$this->Terms[$Field]['include'][] = $Word;
$this->EnableNegation = true;
}
}
/**
* @return array Torrent group information for the matches from Torrents::get_groups
*/
public function get_groups() {
return $this->Groups;
}
/**
* @param string $Type Field or attribute name
* @return string Unprocessed search terms
*/
public function get_terms($Type) {
return isset($this->RawTerms[$Type]) ? $this->RawTerms[$Type] : '';
}
/**
* @return int Result count
*/
public function record_count() {
return $this->NumResults;
}
/**
* @return bool Whether any filters were used
*/
public function has_filters() {
return $this->Filtered;
}
/**
* @return bool Whether any torrent-specific fulltext filters were used
*/
public function need_torrent_ft() {
return $this->GroupResults && $this->NumResults > 0 && !empty($this->UsedTorrentFields);
}
/**
* Get torrent group info and remove any torrents that don't match
*/
private function process_results() {
if (count($this->SphResults) == 0) {
return;
}
$this->Groups = Torrents::get_groups($this->SphResults);
if ($this->need_torrent_ft()) {
// Query Sphinx for torrent IDs if torrent-specific fulltext filters were used
$this->filter_torrents_sph();
} elseif ($this->GroupResults) {
// Otherwise, let PHP discard unmatching torrents
$this->filter_torrents_internal();
}
// Ungrouped searches don't need any additional filtering
}
/**
* Build and run a query that gets torrent IDs from Sphinx when fulltext filters
* were used to get primary results and they are grouped
*/
private function filter_torrents_sph() {
$AllTorrents = array();
foreach ($this->Groups as $GroupID => $Group) {
if (!empty($Group['Torrents'])) {
$AllTorrents += array_fill_keys(array_keys($Group['Torrents']), $GroupID);
}
}
$TorrentCount = count($AllTorrents);
$this->SphQLTor = new SphinxqlQuery();
$this->SphQLTor->select('id')->from('torrents, delta');
foreach ($this->UsedTorrentFields as $Field => $Term) {
$this->SphQLTor->where_match($Term, $Field, false);
}
$this->SphQLTor->copy_attributes_from($this->SphQL);
$this->SphQLTor->where('id', array_keys($AllTorrents))->limit(0, $TorrentCount, $TorrentCount);
$SphQLResultTor = $this->SphQLTor->query();
$MatchingTorrentIDs = $SphQLResultTor->to_pair('id', 'id');
foreach ($AllTorrents as $TorrentID => $GroupID) {
if (!isset($MatchingTorrentIDs[$TorrentID])) {
unset($this->Groups[$GroupID]['Torrents'][$TorrentID]);
}
}
}
/**
* Non-Sphinx method of collecting IDs of torrents that match any
* torrent-specific attribute filters that were used in the search query
*/
private function filter_torrents_internal() {
foreach ($this->Groups as $GroupID => $Group) {
if (empty($Group['Torrents'])) {
continue;
}
foreach ($Group['Torrents'] as $TorrentID => $Torrent) {
if (!$this->filter_torrent_internal($Torrent)) {
unset($this->Groups[$GroupID]['Torrents'][$TorrentID]);
}
}
}
}
/**
* Post-processing to determine if a torrent is a real hit or if it was
* returned because another torrent in the group matched. Only used if
* there are no torrent-specific fulltext conditions
*
* @param array $Torrent Torrent array, probably from Torrents::get_groups()
* @return bool True if it's a real hit
*/
private function filter_torrent_internal($Torrent) {
if (isset($this->UsedTorrentAttrs['freetorrent'])) {
$FilterValue = $this->UsedTorrentAttrs['freetorrent'];
if ($FilterValue == '3' && $Torrent['FreeTorrent'] == '0') {
// Either FL or NL is ok
return false;
} elseif ($FilterValue != '3' && $FilterValue != (int)$Torrent['FreeTorrent']) {
return false;
}
}
if (isset($this->UsedTorrentAttrs['hascue'])) {
if ($this->UsedTorrentAttrs['hascue'] != (int)$Torrent['HasCue']) {
return false;
}
}
if (isset($this->UsedTorrentAttrs['haslog'])) {
$FilterValue = $this->UsedTorrentAttrs['haslog'];
if ($FilterValue == '0') {
// No logs
$Pass = empty($Torrent['HasLog']);
} elseif ($FilterValue == '100') {
// 100% logs
$Pass = $Torrent['LogScore'] == '100';
} elseif ($FilterValue < 0) {
// Unscored or <100% logs
$Pass = !empty($Torrent['HasLog']) && $Torrent['LogScore'] != '100';
} else {
// Any log score
$Pass = $Torrent['HasLog'] == '1';
}
if (!$Pass) {
return false;
}
}
if (isset($this->UsedTorrentAttrs['scene'])) {
if ($this->UsedTorrentAttrs['scene'] != (int)$Torrent['Scene']) {
return false;
}
}
return true;
}
}

View File

@ -130,6 +130,8 @@ private static function send_request($Get, $MaxAttempts = 1, &$Err = false) {
if ($Sleep) {
sleep($Sleep);
}
$Sleep = 6;
// Send request
$File = fsockopen(TRACKER_HOST, TRACKER_PORT, $ErrorNum, $ErrorString);
if ($File) {
@ -140,7 +142,6 @@ private static function send_request($Get, $MaxAttempts = 1, &$Err = false) {
}
} else {
$Err = "Failed to fsockopen() - $ErrorNum - $ErrorString";
$Sleep = 6;
continue;
}

View File

@ -1,5 +1,76 @@
CHANGE LOG
2014-11-06 by porkpie
Create a TorrentSearch class that does most of the torrent search job.
Add PHP post-processing of matches if a search query doesn't need
any torrent-specific fulltext conditions.
Comments
2014-11-03 by downinthings
Fix comments on User API
2014-10-30 by tobbez
Add stats API method
2014-10-27 by porkpie
Clear news cache immediately after updating the database
2014-10-20 by Narcolepsy
Consolidated commit list since 2014-04-20:
Fix some LTR issues (bug thread 189685)
Move loading comments before View::show_header. This should fix subscriptions and quote notifications appearing when a user is already on the page of that last subscription
Replace some hardcoded IRC channels with configuration parameters
Fix HTML in <title> for non-{Music, Audiobooks, Comedy} requests (bug thread 189855)
Fix: link directly to the torrent on bad files/folders better.php pages (thread 190009)
Update internal cache when a cache key is changed
This should make quote notifications disappear when viewing threads (#183300)
Cast user id to int on user profile pages to get rid of trailing period
Remove WhatMan's spellcheck tool (seriously it was crap)
Fix a few cache key typos:
Blog => blog
requests_$RequestID => request_$RequestID
users_stats_$UserID => user_stats_$UserID
Fix some sorting issues related to Torrents::get_groups not returning the groups in the same order they were passed in; DO NOT iterate over Torrents::get_groups results...
Fix image in bookmarks JSON API
Fix comments appearing on the wrong page (bug thread 190203)
Passwords aren't limited to 40 characters
Fix better.php/single and rename some variables so they make some sense
Bug thread #190289
Don't continue to process forum post deletes if the post or thread doesn't exist
Trusting rdns is bad
Fix two cache issues in the schedule
- Permissions of users that are demoted to User/Member for low ratio or upload were not updated/cleared, which meant that e.g. demoted PU users could still see the PU forum
- The cache was cleared before the promotion/demotion queries. This could cause a race condition if the cache key were filled again (through a Users::user_info call)
Fix some cache issues (bug thread 190802)
Speed up BBCode
BBCode profiling
Fix a bunch of bookmarks stuff. HTML/CSS consistency, jQuerified functions
Bye sizzle
show rank 4 instead of 5 when 5 points in pms
More accurate donor rank expiration times
Apparently users_donor_ranks can contain NULL values.
Change these to 0 after fetching to fix cache
Fix a grammar error and use SITE_NAME constant in the post-invite confirmation PM that gets sent to the inviter
Remove trailing whitespace in public Gazelle, improve grammar, add some missing spaces in 80char's CSS file
Remove extraneous whitespace from various files
Coding standards cleanup in script_start.php
Remove some WCD-specific smileys from public Gazelle
Remove these:
* ackbar-what.png
* jesusfish-what.png
* louisdefunes-what.png
Add Tools::check_cidr_range to check if an IP is inside a given range in CIDR format
Torrent size is a 64-bit Sphinx attribute, so let's make use of it
Should partially fix bug #185554
Fix SQLI in PushServer::push_pushbullet
Minor coding standards fixes in classes/pushserver.class.php
Fix User Upload Count API
fix lack of artist name in concert titles
add mailgun as alternative to self-hosting email, config changes in template to match
user api additional stats, patch from user downinthings
2014-04-20 by SevenNationArmy
Replace old IRC applet with Mibbit

View File

@ -1128,7 +1128,7 @@ CREATE TABLE `torrents` (
`Seeders` int(6) NOT NULL DEFAULT '0',
`last_action` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`FreeTorrent` enum('0','1','2') NOT NULL DEFAULT '0',
`FreeLeechType` enum('0','1','2','3') NOT NULL DEFAULT '0',
`FreeLeechType` enum('0','1','2','3','4','5','6','7') NOT NULL DEFAULT '0',
`Time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`Description` text,
`Snatched` int(10) unsigned NOT NULL DEFAULT '0',

BIN
ocelot-1.0.tar.gz Normal file

Binary file not shown.

View File

@ -77,7 +77,7 @@
}
}
json_die("success", array(
json_print("success", array(
'announcements' => $JsonAnnouncements,
'blogPosts' => $JsonBlog
));

View File

@ -342,7 +342,7 @@ function compare($X, $Y) {
$Cache->cache_value($Key, $Data, 3600);
json_die("success", array(
json_print("success", array(
'id' => (int)$ArtistID,
'name' => $Name,
'notificationsEnabled' => $notificationsEnabled,

View File

@ -1,494 +1,44 @@
<?
include(SERVER_ROOT.'/sections/torrents/functions.php');
/** Start default parameters and validation **/
// Setting default search options
if (!empty($_GET['setdefault'])) {
$UnsetList = array('page', 'setdefault');
$UnsetRegexp = '/(&|^)('.implode('|', $UnsetList).')=.*?(&|$)/i';
$DB->query("
SELECT SiteOptions
FROM users_info
WHERE UserID = '".db_string($LoggedUser['ID'])."'");
list($SiteOptions) = $DB->next_record(MYSQLI_NUM, false);
if (!empty($SiteOptions)) {
$SiteOptions = unserialize($SiteOptions);
} else {
$SiteOptions = array();
}
$SiteOptions['DefaultSearch'] = preg_replace($UnsetRegexp, '', $_SERVER['QUERY_STRING']);
$DB->query("
UPDATE users_info
SET SiteOptions = '".db_string(serialize($SiteOptions))."'
WHERE UserID = '".db_string($LoggedUser['ID'])."'");
$Cache->begin_transaction("user_info_heavy_$UserID");
$Cache->update_row(false, array('DefaultSearch' => $SiteOptions['DefaultSearch']));
$Cache->commit_transaction(0);
// Clearing default search options
} elseif (!empty($_GET['cleardefault'])) {
$DB->query("
SELECT SiteOptions
FROM users_info
WHERE UserID = '".db_string($LoggedUser['ID'])."'");
list($SiteOptions) = $DB->next_record(MYSQLI_NUM, false);
$SiteOptions = unserialize($SiteOptions);
$SiteOptions['DefaultSearch'] = '';
$DB->query("
UPDATE users_info
SET SiteOptions = '".db_string(serialize($SiteOptions))."'
WHERE UserID = '".db_string($LoggedUser['ID'])."'");
$Cache->begin_transaction("user_info_heavy_$UserID");
$Cache->update_row(false, array('DefaultSearch' => ''));
$Cache->commit_transaction(0);
// Use default search options
} elseif (empty($_SERVER['QUERY_STRING']) || (count($_GET) === 1 && isset($_GET['page']))) {
if (!empty($LoggedUser['DefaultSearch'])) {
if (!empty($_GET['page'])) {
$Page = $_GET['page'];
parse_str($LoggedUser['DefaultSearch'], $_GET);
$_GET['page'] = $Page;
} else {
parse_str($LoggedUser['DefaultSearch'], $_GET);
}
}
}
// Terms were not submitted via the search form
if (!isset($_GET['searchsubmit'])) {
$_GET['group_results'] = !$LoggedUser['DisableGrouping2'];
}
if (isset($_GET['group_results']) && $_GET['group_results']) {
$_GET['group_results'] = 1;
$GroupResults = true;
$SortOrders = array(
// 'url attr' => [global order, order within group]
'year' => array('year', 'year'),
'time' => array('id', 'id'),
'size' => array('maxsize', 'size'),
'seeders' => array('sumseeders', 'seeders'),
'leechers' => array('sumleechers', 'leechers'),
'snatched' => array('sumsnatched', 'snatched'),
'random' => false);
$AggregateExp = array(
'maxsize' => 'MAX(size) AS maxsize',
'sumseeders' => 'SUM(seeders) AS sumseeders',
'sumleechers' => 'SUM(leechers) AS sumleechers',
'sumsnatched' => 'SUM(snatched) AS sumsnatched');
} else {
$GroupResults = false;
$SortOrders = array(
'year' => 'year',
'time' => 'id',
'size' => 'size',
'seeders' => 'seeders',
'leechers' => 'leechers',
'snatched' => 'snatched',
'random' => false);
}
if (empty($_GET['order_by']) || !isset($SortOrders[$_GET['order_by']])) {
$_GET['order_by'] = 'time';
$OrderBy = 'time'; // For header links
} else {
$OrderBy = $_GET['order_by'];
}
if (!empty($_GET['order_way']) && $_GET['order_way'] == 'asc') {
$OrderWay = 'asc';
} else {
$_GET['order_way'] = 'desc';
$OrderWay = 'desc';
}
/** End default parameters and validation **/
/** Start preparation of property arrays **/
array_pop($Bitrates); // remove 'other'
$SearchBitrates = array_merge($Bitrates, array('v0', 'v1', 'v2', '24bit'));
foreach ($SearchBitrates as $ID => $Val) {
$SearchBitrates[$ID] = strtolower($Val);
}
foreach ($Formats as $ID => $Val) {
$SearchFormats[$ID] = strtolower($Val);
}
/** End preparation of property arrays **/
/** Start query preparation **/
$SphQL = new SphinxqlQuery();
$SphQLTor = new SphinxqlQuery();
if ($OrderBy == 'random') {
$SphQL->select('id, groupid, categoryid')
->order_by('RAND()', '');
$Random = true;
} elseif ($GroupResults) {
$OrderProperties = $SortOrders[$OrderBy];
$SphQL->select('groupid, categoryid' . (isset($AggregateExp[$OrderProperties[0]]) ? ', '.$AggregateExp[$OrderProperties[0]] : ''))
->group_by('groupid')
->order_by($OrderProperties[0], $OrderWay)
->order_group_by($OrderProperties[1], $OrderWay);
if (empty($_GET['order_by']) || !isset(TorrentSearch::$SortOrders[$_GET['order_by']])) {
$OrderBy = 'time';
} else {
$SphQL->select('id, groupid, categoryid')
->order_by($SortOrders[$OrderBy], $OrderWay);
}
$SphQL->from('torrents, delta');
$SphQLTor->select('id, groupid')->from('torrents, delta');
/** End query preparation **/
/** Start building search query **/
$Filtered = false;
$EnableNegation = false; // Sphinx needs at least one positive search condition to support the NOT operator
// File list searches make use of the proximity operator to ensure that all keywords match the same file
if (!empty($_GET['filelist'])) {
$SearchString = trim($_GET['filelist']);
if ($SearchString !== '') {
$SearchString = '"'.Sphinxql::sph_escape_string($_GET['filelist']).'"~20';
$SphQL->where_match($SearchString, 'filelist', false);
$SphQLTor->where_match($SearchString, 'filelist', false);
$EnableNegation = true;
}
$OrderBy = $_GET['order_by'];
}
// Collect all entered search terms to find out whether to enable the NOT operator
$SearchWords = array();
foreach (array('artistname', 'groupname', 'recordlabel', 'cataloguenumber',
'taglist', 'remastertitle', 'remasteryear', 'remasterrecordlabel',
'remastercataloguenumber', 'encoding', 'format', 'media', 'description') as $Search) {
if (!empty($_GET[$Search])) {
$SearchString = trim($_GET[$Search]);
if ($SearchString !== '') {
$SearchWords[$Search] = array('include' => array(), 'exclude' => array());
if ($Search == 'taglist') {
$SearchString = strtr($SearchString, '.', '_');
$Words = explode(',', $SearchString);
} else {
$Words = explode(' ', $SearchString);
}
foreach ($Words as $Word) {
$Word = trim($Word);
// Skip isolated hyphens to enable "Artist - Title" searches
if ($Word === '-') {
continue;
}
if ($Word[0] === '!' && strlen($Word) >= 2) {
if (strpos($Word, '!', 1) === false) {
$SearchWords[$Search]['exclude'][] = $Word;
} else {
$SearchWords[$Search]['include'][] = $Word;
$EnableNegation = true;
}
} elseif ($Word !== '') {
$SearchWords[$Search]['include'][] = $Word;
$EnableNegation = true;
}
}
}
}
$GroupResults = !isset($_GET['group_results']) || $_GET['group_results'] != '0';
$Page = !empty($_GET['page']) ? (int)$_GET['page'] : 1;
$Search = new TorrentSearch($GroupResults, $OrderBy, $OrderWay, $Page, TORRENTS_PER_PAGE);
$Results = $Search->query($_GET);
$Groups = $Search->get_groups();
$NumResults = $Search->record_count();
if ($Results === false) {
json_die('error', 'Search returned an error. Make sure all parameters are valid and of the expected types.');
}
//Simple search
if (!empty($_GET['searchstr'])) {
$SearchString = trim($_GET['searchstr']);
$Words = explode(' ', strtolower($SearchString));
if (!empty($Words)) {
$FilterBitrates = $FilterFormats = array();
$BasicSearch = array('include' => array(), 'exclude' => array());
foreach ($Words as $Word) {
$Word = trim($Word);
// Skip isolated hyphens to enable "Artist - Title" searches
if ($Word === '-') {
continue;
}
if ($Word[0] === '!' && strlen($Word) >= 2) {
if ($Word === '!100%') {
$_GET['haslog'] = '-1';
} elseif (strpos($Word, '!', 1) === false) {
$BasicSearch['exclude'][] = $Word;
} else {
$BasicSearch['include'][] = $Word;
$EnableNegation = true;
}
} elseif (in_array($Word, $SearchBitrates)) {
$FilterBitrates[] = $Word;
$EnableNegation = true;
} elseif (in_array($Word, $SearchFormats)) {
$FilterFormats[] = $Word;
$EnableNegation = true;
} elseif ($Word === '100%') {
$_GET['haslog'] = '100';
} elseif ($Word !== '') {
$BasicSearch['include'][] = $Word;
$EnableNegation = true;
}
}
if (!$EnableNegation && !empty($BasicSearch['exclude'])) {
$BasicSearch['include'] = array_merge($BasicSearch['include'], $BasicSearch['exclude']);
unset($BasicSearch['exclude']);
}
$QueryParts = array();
foreach ($BasicSearch['include'] as $Word) {
$QueryParts[] = Sphinxql::sph_escape_string($Word);
}
if (!empty($BasicSearch['exclude'])) {
foreach ($BasicSearch['exclude'] as $Word) {
$QueryParts[] = '!'.Sphinxql::sph_escape_string(substr($Word, 1));
}
}
if (!empty($FilterBitrates)) {
$SearchString = implode(' ', $FilterBitrates);
$SphQL->where_match($SearchString, 'encoding', false);
$SphQLTor->where_match($SearchString, 'encoding', false);
}
if (!empty($FilterFormats)) {
$SearchString = implode(' ', $FilterFormats);
$SphQL->where_match($SearchString, 'format', false);
$SphQLTor->where_match($SearchString, 'format', false);
}
if (!empty($QueryParts)) {
$SearchString = implode(' ', $QueryParts);
$SphQL->where_match($SearchString, '(groupname,artistname,yearfulltext)', false);
$SphQLTor->where_match($SearchString, '(groupname,artistname,yearfulltext)', false);
}
}
}
// Tag list
if (!empty($SearchWords['taglist'])) {
$_GET['tags_type'] = (!isset($_GET['tags_type']) || $_GET['tags_type'] == 1) ? '1' : '0';
$TagType = (int)$_GET['tags_type'];
//Get tags
$Tags = $SearchWords['taglist'];
$TagFilter = Tags::tag_filter_sph($Tags, $EnableNegation, $TagType);
if (!empty($TagFilter['predicate'])) {
$SphQL->where_match($TagFilter['predicate'], 'taglist', false);
$SphQLTor->where_match($TagFilter['predicate'], 'taglist', false);
}
unset($SearchWords['taglist']);
}
elseif (!isset($_GET['tags_type'])) {
$_GET['tags_type'] = '1';
}
foreach ($SearchWords as $Search => $Words) {
$QueryParts = array();
if (!$EnableNegation && !empty($Words['exclude'])) {
$Words['include'] = array_merge($Words['include'], $Words['exclude']);
unset($Words['exclude']);
}
foreach ($Words['include'] as $Word) {
$QueryParts[] = Sphinxql::sph_escape_string($Word);
}
if (!empty($Words['exclude'])) {
foreach ($Words['exclude'] as $Word) {
$QueryParts[] = '!'.Sphinxql::sph_escape_string(substr($Word, 1));
}
}
if (!empty($QueryParts)) {
$SearchString = implode(' ', $QueryParts);
$SphQL->where_match($SearchString, $Search, false);
$SphQLTor->where_match($SearchString, $Search, false);
}
}
if (!empty($_GET['year'])) {
$Years = explode('-', $_GET['year']);
if (is_number($Years[0]) || (empty($Years[0]) && !empty($Years[1]) && is_number($Years[1]))) {
if (count($Years) === 1) {
$SphQL->where('year', (int)$Years[0]);
$SphQLTor->where('year', (int)$Years[0]);
} else {
if (empty($Years[1]) || !is_number($Years[1])) {
$Years[1] = PHP_INT_MAX;
} elseif ($Years[0] > $Years[1]) {
$Years = array_reverse($Years);
}
$SphQL->where_between('year', array((int)$Years[0], (int)$Years[1]));
$SphQLTor->where_between('year', array((int)$Years[0], (int)$Years[1]));
}
}
}
if (isset($_GET['haslog']) && $_GET['haslog'] !== '') {
if ($_GET['haslog'] === '100') {
$SphQL->where('logscore', 100);
$SphQLTor->where('logscore', 100);
} elseif ($_GET['haslog'] < 0) {
// Exclude torrents with log score equal to 100
$SphQL->where('logscore', 100, true);
$SphQL->where('haslog', 1);
$SphQLTor->where('logscore', 100, true);
$SphQLTor->where('haslog', 1);
} elseif ($_GET['haslog'] == 0) {
$SphQL->where('haslog', 0);
$SphQLTor->where('haslog', 0);
} else {
$SphQL->where('haslog', 1);
$SphQLTor->where('haslog', 1);
}
}
foreach (array('hascue', 'scene', 'vanityhouse', 'releasetype') as $Search) {
if (isset($_GET[$Search]) && $_GET[$Search] !== '') {
$SphQL->where($Search, $_GET[$Search]);
// Release type is group specific
if ($Search != 'releasetype') {
$SphQLTor->where($Search, $_GET[$Search]);
}
}
}
if (isset($_GET['freetorrent']) && $_GET['freetorrent'] !== '') {
switch ($_GET['freetorrent']) {
case 0: // Only normal freeleech
$SphQL->where('freetorrent', 0);
$SphQLTor->where('freetorrent', 0);
break;
case 1: // Only free leech
$SphQL->where('freetorrent', 1);
$SphQLTor->where('freetorrent', 1);
break;
case 2: // Only neutral leech
$SphQL->where('freetorrent', 2);
$SphQLTor->where('freetorrent', 2);
break;
case 3: // Free or neutral leech
$SphQL->where('freetorrent', 0, true);
$SphQLTor->where('freetorrent', 0, true);
break;
}
}
if (!empty($_GET['filter_cat'])) {
$SphQL->where('categoryid', array_keys($_GET['filter_cat']));
}
/** End building search query **/
/** Run search query and collect results **/
if (isset($Random) && $GroupResults) {
// ORDER BY RAND() can't be used together with GROUP BY, so we need some special tactics
$Page = 1;
$SphQL->limit(0, 5 * TORRENTS_PER_PAGE, 5 * TORRENTS_PER_PAGE);
$SphQLResult = $SphQL->query();
$TotalCount = $SphQLResult->get_meta('total_found');
$Results = $SphQLResult->to_array('groupid');
$GroupIDs = array_keys($Results);
$GroupCount = count($GroupIDs);
while ($SphQLResult->get_meta('total') < $TotalCount && $GroupCount < TORRENTS_PER_PAGE) {
// Make sure we get TORRENTS_PER_PAGE results, or all of them if there are less than TORRENTS_PER_PAGE hits
$SphQL->where('groupid', $GroupIDs, true);
$SphQLResult = $SphQL->query();
if (!$SphQLResult->has_results()) {
break;
}
$Results += $SphQLResult->to_array('groupid');
$GroupIDs = array_keys($Results);
$GroupCount = count($GroupIDs);
}
if ($GroupCount > TORRENTS_PER_PAGE) {
$Results = array_slice($Results, 0, TORRENTS_PER_PAGE, true);
}
$GroupIDs = array_keys($Results);
$NumResults = count($Results);
} else {
if (!empty($_GET['page']) && is_number($_GET['page']) && $_GET['page'] > 0) {
if (check_perms('site_search_many')) {
$Page = $_GET['page'];
} else {
$Page = min(SPHINX_MAX_MATCHES / TORRENTS_PER_PAGE, $_GET['page']);
}
$Offset = ($Page - 1) * TORRENTS_PER_PAGE;
$SphQL->limit($Offset, TORRENTS_PER_PAGE, $Offset + TORRENTS_PER_PAGE);
} else {
$Page = 1;
$SphQL->limit(0, TORRENTS_PER_PAGE, TORRENTS_PER_PAGE);
}
$SphQLResult = $SphQL->query();
$NumResults = $SphQLResult->get_meta('total_found');
if ($GroupResults) {
$Results = $SphQLResult->to_array('groupid');
$GroupIDs = array_keys($Results);
} else {
$Results = $SphQLResult->to_array('id');
$GroupIDs = $SphQLResult->collect('groupid');
}
}
if (!check_perms('site_search_many') && $NumResults > SPHINX_MAX_MATCHES) {
$NumResults = SPHINX_MAX_MATCHES;
}
if ($NumResults) {
$Groups = Torrents::get_groups($GroupIDs);
if (!empty($Groups) && $GroupResults) {
$TorrentIDs = array();
foreach ($Groups as $Group) {
if (!empty($Group['Torrents'])) {
$TorrentIDs = array_merge($TorrentIDs, array_keys($Group['Torrents']));
}
}
$TorrentCount = count($TorrentIDs);
if ($TorrentCount > 0) {
// Get a list of all torrent ids that match the search query
$SphQLTor->where('id', $TorrentIDs)->limit(0, $TorrentCount, $TorrentCount);
$SphQLResultTor = $SphQLTor->query();
$TorrentIDs = $SphQLResultTor->to_pair('id', 'id'); // Because isset() is faster than in_array()
}
}
}
/** End run search query and collect results **/
if ($NumResults == 0) {
$DB->query("
SELECT
tags.Name,
((COUNT(tags.Name) - 2) * (SUM(tt.PositiveVotes) - SUM(tt.NegativeVotes))) / (tags.Uses * 0.8) AS Score
FROM xbt_snatched AS s
INNER JOIN torrents AS t ON t.ID = s.fid
INNER JOIN torrents_group AS g ON t.GroupID = g.ID
INNER JOIN torrents_tags AS tt ON tt.GroupID = g.ID
INNER JOIN tags ON tags.ID = tt.TagID
WHERE s.uid = '$LoggedUser[ID]'
AND tt.TagID != '13679'
AND tt.TagID != '4820'
AND tt.TagID != '2838'
AND g.CategoryID = '1'
AND tags.Uses > '10'
GROUP BY tt.TagID
ORDER BY Score DESC
LIMIT 8");
$JsonYouMightLike = array();
while (list($Tag) = $DB->next_record()) {
$JsonYouMightLike[] = $Tag;
}
json_die("success", array(
json_die('success', array(
'results' => array(),
'youMightLike' => $JsonYouMightLike
'youMightLike' => array() // This slow and broken feature has been removed
));
}
$Bookmarks = Bookmarks::all_bookmarks('torrent');
$JsonGroups = array();
foreach ($Results as $Result) {
$GroupID = $Result['groupid'];
foreach ($Results as $Key => $GroupID) {
$GroupInfo = $Groups[$GroupID];
if (empty($GroupInfo['Torrents'])) {
continue;
}
$CategoryID = $Result['categoryid'];
$CategoryID = $GroupInfo['CategoryID'];
$GroupYear = $GroupInfo['Year'];
$ExtendedArtists = $GroupInfo['ExtendedArtists'];
$GroupCatalogueNumber = $GroupInfo['CatalogueNumber'];
@ -500,15 +50,14 @@
$GroupTime = $MaxSize = $TotalLeechers = $TotalSeeders = $TotalSnatched = 0;
foreach ($Torrents as $T) {
$GroupTime = max($GroupTime, strtotime($T['Time']));
if ($T['Size'] > $MaxSize) {
$MaxSize = $T['Size'];
}
$MaxSize = max($MaxSize, $T['Size']);
$TotalLeechers += $T['Leechers'];
$TotalSeeders += $T['Seeders'];
$TotalSnatched += $T['Snatched'];
}
} else {
$Torrents = array($Result['id'] => $GroupInfo['Torrents'][$Result['id']]);
$TorrentID = $Key;
$Torrents = array($TorrentID => $GroupInfo['Torrents'][$TorrentID]);
}
$TagList = explode(' ', str_replace('_', '.', $GroupInfo['TagList']));
@ -516,22 +65,12 @@
if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) || !empty($ExtendedArtists[5]) || !empty($ExtendedArtists[6])) {
unset($ExtendedArtists[2]);
unset($ExtendedArtists[3]);
$DisplayName = Artists::display_artists($ExtendedArtists, false, false, true);
$DisplayName = Artists::display_artists($ExtendedArtists, false, false, false);
foreach ($ExtendedArtists[1] as $Artist) {
$JsonArtists[] = array(
'id' => (int)$Artist['id'],
'name' => $Artist['name'],
'aliasid' => (int)$Artist['id']
);
}
} elseif (!empty($Artists)) {
$DisplayName = Artists::display_artists(array(1 => $Artists), false, false, true);
foreach ($Artists as $Artist) {
$JsonArtists[] = array(
'id' => (int)$Artist['id'],
'name' => $Artist['name'],
'aliasid' => (int)$Artist['id']
);
'aliasid' => (int)$Artist['aliasid']);
}
} else {
$DisplayName = '';
@ -551,11 +90,6 @@
foreach ($Torrents as $TorrentID => $Data) {
// All of the individual torrents in the group
// If they're using the advanced search and have chosen enabled grouping, we just skip the torrents that don't check out
if (!isset($TorrentIDs[$TorrentID])) {
continue;
}
if ($Data['Remastered'] && !$Data['RemasterYear']) {
$FirstUnknown = !isset($FirstUnknown);
}
@ -682,11 +216,7 @@
);
}
}
echo json_encode(
array(
'status' => 'success',
'response' => array(
json_print('success', array(
'currentPage' => intval($Page),
'pages' => ceil($NumResults / TORRENTS_PER_PAGE),
'results' => $JsonGroups)));
'results' => $JsonGroups));

View File

@ -93,7 +93,7 @@
// Subscriptions
$NewSubscriptions = Subscriptions::has_new_subscriptions();
json_die("success", array(
json_print("success", array(
'username' => $LoggedUser['Username'],
'id' => (int)$LoggedUser['ID'],
'authkey' => $LoggedUser['AuthKey'],

View File

@ -37,4 +37,4 @@
);
}
json_die('success', json_encode($NewsResponse));
json_print('success', json_encode($NewsResponse));

View File

@ -99,7 +99,7 @@
}
}
json_die("success", array(
json_print("success", array(
'currentPages' => intval($Page),
'pages' => ceil($TorrentCount / NOTIFICATIONS_PER_PAGE),
'numNew' => $NumNew,

View File

@ -112,7 +112,7 @@
foreach ($Request['Tags'] as $Tag) {
$JsonTags[] = $Tag;
}
json_die('success', array(
json_print('success', array(
'requestId' => (int)$RequestID,
'requestorId' => (int)$Request['UserID'],
'requestorName' => $Requestor['Username'],

View File

@ -321,7 +321,7 @@
}
if ($NumResults == 0) {
json_die("success", array(
json_print("success", array(
'currentPage' => 1,
'pages' => 1,
'results' => array()
@ -381,7 +381,7 @@
);
}
json_die("success", array(
json_print("success", array(
'currentPage' => intval($Page),
'pages' => ceil($NumResults / REQUESTS_PER_PAGE),
'results' => $JsonResults

View File

@ -1,7 +1,133 @@
<?
if (in_array($_GET['stat'], array('inbox', 'uploads', 'bookmarks', 'notifications', 'subscriptions', 'comments', 'friends'))) {
$Cache->begin_transaction('stats_links');
$Cache->update_row(false, array($_GET['stat'] => '+1'));
$Cache->commit_transaction(0);
// Begin user stats
if (($UserCount = $Cache->get_value('stats_user_count')) === false) {
$DB->query("
SELECT COUNT(ID)
FROM users_main
WHERE Enabled = '1'");
list($UserCount) = $DB->next_record();
$Cache->cache_value('stats_user_count', $UserCount, 0); //inf cache
}
if (($UserStats = $Cache->get_value('stats_users')) === false) {
$DB->query("
SELECT COUNT(ID)
FROM users_main
WHERE Enabled = '1'
AND LastAccess > '".time_minus(3600 * 24)."'");
list($UserStats['Day']) = $DB->next_record();
$DB->query("
SELECT COUNT(ID)
FROM users_main
WHERE Enabled = '1'
AND LastAccess > '".time_minus(3600 * 24 * 7)."'");
list($UserStats['Week']) = $DB->next_record();
$DB->query("
SELECT COUNT(ID)
FROM users_main
WHERE Enabled = '1'
AND LastAccess > '".time_minus(3600 * 24 * 30)."'");
list($UserStats['Month']) = $DB->next_record();
$Cache->cache_value('stats_users', $UserStats, 0);
}
// Begin torrent stats
if (($TorrentCount = $Cache->get_value('stats_torrent_count')) === false) {
$DB->query("
SELECT COUNT(ID)
FROM torrents");
list($TorrentCount) = $DB->next_record();
$Cache->cache_value('stats_torrent_count', $TorrentCount, 604800); // staggered 1 week cache
}
if (($AlbumCount = $Cache->get_value('stats_album_count')) === false) {
$DB->query("
SELECT COUNT(ID)
FROM torrents_group
WHERE CategoryID = '1'");
list($AlbumCount) = $DB->next_record();
$Cache->cache_value('stats_album_count', $AlbumCount, 604830); // staggered 1 week cache
}
if (($ArtistCount = $Cache->get_value('stats_artist_count')) === false) {
$DB->query("
SELECT COUNT(ArtistID)
FROM artists_group");
list($ArtistCount) = $DB->next_record();
$Cache->cache_value('stats_artist_count', $ArtistCount, 604860); // staggered 1 week cache
}
if (($PerfectCount = $Cache->get_value('stats_perfect_count')) === false) {
$DB->query("
SELECT COUNT(ID)
FROM torrents
WHERE ((LogScore = 100 AND Format = 'FLAC')
OR (Media = 'Vinyl' AND Format = 'FLAC')
OR (Media = 'WEB' AND Format = 'FLAC')
OR (Media = 'DVD' AND Format = 'FLAC')
OR (Media = 'Soundboard' AND Format = 'FLAC')
)");
list($PerfectCount) = $DB->next_record();
$Cache->cache_value('stats_perfect_count', $PerfectCount, 604890); // staggered 1 week cache
}
// Begin request stats
if (($RequestStats = $Cache->get_value('stats_requests')) === false) {
$DB->query("
SELECT COUNT(ID)
FROM requests");
list($RequestCount) = $DB->next_record();
$DB->query("
SELECT COUNT(ID)
FROM requests
WHERE FillerID > 0");
list($FilledCount) = $DB->next_record();
$Cache->cache_value('stats_requests', array($RequestCount, $FilledCount), 11280);
} else {
list($RequestCount, $FilledCount) = $RequestStats;
}
// Begin swarm stats
if (($PeerStats = $Cache->get_value('stats_peers')) === false) {
//Cache lock!
if ($Cache->get_query_lock('peer_stats')) {
$DB->query("
SELECT IF(remaining=0,'Seeding','Leeching') AS Type, COUNT(uid)
FROM xbt_files_users
WHERE active = 1
GROUP BY Type");
$PeerCount = $DB->to_array(0, MYSQLI_NUM, false);
$LeecherCount = isset($PeerCount['Leeching']) ? $PeerCount['Leeching'][1] : 0;
$SeederCount = isset($PeerCount['Seeding']) ? $PeerCount['Seeding'][1] : 0;
$Cache->cache_value('stats_peers', array($LeecherCount, $SeederCount), 1209600); // 2 week cache
$Cache->clear_query_lock('peer_stats');
} else {
$LeecherCount = $SeederCount = 0;
}
} else {
list($LeecherCount, $SeederCount) = $PeerStats;
}
json_print("success", array(
'maxUsers' => USER_LIMIT,
'enabledUsers' => (int) $UserCount,
'usersActiveThisDay' => (int) $UserStats['Day'],
'usersActiveThisWeek' => (int) $UserStats['Week'],
'usersActiveThisMonth' => (int) $UserStats['Month'],
'torrentCount' => (int) $TorrentCount,
'releaseCount' => (int) $AlbumCount,
'artistCount' => (int) $ArtistCount,
'perfectFlacCount' => (int) $PerfectCount,
'requestCount' => (int) $RequestCount,
'filledRequestCount' => (int) $FilledCount,
'seederCount' => (int) $SeederCount,
'leecherCount' => (int) $LeecherCount
));
?>

View File

@ -86,7 +86,7 @@
$JsonPosts[] = $JsonPost;
}
json_die('success', array(
json_print('success', array(
'threads' => $JsonPosts
));
?>

View File

@ -31,7 +31,7 @@
);
}
json_die("success", array(
json_print("success", array(
'page' => (int)$Page,
'pages' => ceil($NumComments / TORRENT_COMMENTS_PER_PAGE),
'comments' => $JsonComments

View File

@ -128,4 +128,4 @@
'username' => $Userinfo['Username']
);
json_die("success", array('group' => $JsonTorrentDetails, 'torrent' => array_pop($JsonTorrentList)));
json_print("success", array('group' => $JsonTorrentDetails, 'torrent' => array_pop($JsonTorrentList)));

View File

@ -118,4 +118,4 @@
);
}
json_die("success", array('group' => $JsonTorrentDetails, 'torrents' => $JsonTorrentList));
json_print("success", array('group' => $JsonTorrentDetails, 'torrents' => $JsonTorrentList));

View File

@ -101,8 +101,10 @@ function check_paranoia_here($Setting) {
WHERE UserID = '$UserID'");
list($Uploads) = $DB->next_record();
} else {
$RequestsVoted = 0;
$TotalSpent = 0;
$RequestsFilled = null;
$TotalBounty = null;
$RequestsVoted = null;
$TotalSpent = null;
}
if (check_paranoia_here('uploads+')) {
$DB->query("
@ -121,7 +123,7 @@ function check_paranoia_here($Setting) {
WHERE UserID = $UserID");
list($ArtistsAdded) = $DB->next_record();
} else {
$ArtistsAdded = 0;
$ArtistsAdded = null;
}
// Do the ranks.
@ -189,6 +191,33 @@ function check_paranoia_here($Setting) {
list($NumComments) = $DB->next_record();
}
if (check_paranoia_here('torrentcomments+')) {
$DB->query("
SELECT COUNT(ID)
FROM comments
WHERE Page = 'artist'
AND AuthorID = '$UserID'");
list($NumArtistComments) = $DB->next_record();
}
if (check_paranoia_here('torrentcomments+')) {
$DB->query("
SELECT COUNT(ID)
FROM comments
WHERE Page = 'collages'
AND AuthorID = '$UserID'");
list($NumCollageComments) = $DB->next_record();
}
if (check_paranoia_here('torrentcomments+')) {
$DB->query("
SELECT COUNT(ID)
FROM comments
WHERE Page = 'requests'
AND AuthorID = '$UserID'");
list($NumRequestComments) = $DB->next_record();
}
if (check_paranoia_here('collages+')) {
$DB->query("
SELECT COUNT(ID)
@ -304,7 +333,7 @@ function check_paranoia_here($Setting) {
header('Content-Type: text/plain; charset=utf-8');
json_die("success", array(
json_print("success", array(
'username' => $Username,
'avatar' => $Avatar,
'isFriend' => $Friend,
@ -338,18 +367,24 @@ function check_paranoia_here($Setting) {
),
'community' => array(
'posts' => (int)$ForumPosts,
'torrentComments' => (int)$NumComments,
'torrentComments' => (($NumComments == null) ? null : (int)$NumComments),
'artistComments' => (($NumArtistComments == null) ? null : (int)$NumArtistComments),
'collageComments' => (($NumCollageComments == null) ? null : (int)$NumCollageComments),
'requestComments' => (($NumRequestComments == null) ? null : (int)$NumRequestComments),
'collagesStarted' => (($NumCollages == null) ? null : (int)$NumCollages),
'collagesContrib' => (($NumCollageContribs == null) ? null : (int)$NumCollageContribs),
'requestsFilled' => (($RequestsFilled == null) ? null : (int)$RequestsFilled),
'bountyEarned' => (($TotalBounty == null) ? null : (int)$TotalBounty),
'requestsVoted' => (($RequestsVoted == null) ? null : (int)$RequestsVoted),
'bountySpent' => (($TotalSpent == null) ? null : (int)$TotalSpent),
'perfectFlacs' => (($PerfectFLACs == null) ? null : (int)$PerfectFLACs),
'uploaded' => (($Uploads == null) ? null : (int)$Uploads),
'groups' => (($UniqueGroups == null) ? null : (int)$UniqueGroups),
'seeding' => (($Seeding == null) ? null : (int)$Seeding),
'leeching' => (($Leeching == null) ? null : (int)$Leeching),
'snatched' => (($Snatched == null) ? null : (int)$Snatched),
'invited' => (($Invited == null) ? null : (int)$Invited)
'invited' => (($Invited == null) ? null : (int)$Invited),
'artistsAdded' => (($ArtistsAdded == null) ? null : (int)$ArtistsAdded)
)
));
?>

View File

@ -62,7 +62,7 @@
$Results['uploads'] = "hidden";
}
json_die("success", $Results);
json_print("success", $Results);
function check_paranoia_here($Setting) {
global $Paranoia, $Class, $UserID, $Preview;

View File

@ -51,7 +51,7 @@
);
}
json_die("success", array(
json_print("success", array(
'currentPage' => (int)$Page,
'pages' => ceil($NumResults / USERS_PER_PAGE),
'results' => $JsonUsers

View File

@ -23,7 +23,7 @@
Text::$TOC = true;
$TextBody = Text::full_format($Body, false);
json_die("success", array(
json_print("success", array(
'title' => $Title,
'bbBody' => $Body,
'body' => $TextBody,

View File

@ -16,7 +16,7 @@
make_concert_link($Event);
}
} else { // Single event
make_concert_link($ArtistEvents['events']['event']);
make_concert_link($ArtistEvents['events']['event'], $Name);
}
echo '</ul>';
}
@ -34,7 +34,7 @@
</div>
<?
function make_concert_link($Event) {
function make_concert_link($Event, $Name) {
// The event doesn't have a start date (this should never happen)
if ($Event['startDate'] == '') {
return;
@ -46,7 +46,7 @@ function make_concert_link($Event) {
?>
<form class="hidden" action="" id="concert<?=$Event['id']?>" method="post">
<input type="hidden" name="action" value="concert_thread" />
<input type="hidden" name="concert_title" value="<?='[Concert] ' . display_str($Name) . " - $ConcertTitle"?>" />
<input type="hidden" name="concert_title" value="<?='[Concert] ' . $Event['artists']['artist'] . " - $ConcertTitle"?>" />
<input type="hidden" name="concert_id" value="<?=$Event['id']?>" />
<input type="hidden" name="concert_template" value="<?=get_concert_post_template($Name, $Event)?>" />
</form>
@ -80,7 +80,7 @@ function get_concert_post_template($Artist, $Event) {
if (strpos ($Url, '://') === false) {
$Url = 'http://' . $Url;
}
$Website = '[b]Web site:[/b] ' . $Url;
$Website = '[b]Website:[/b] ' . $Url;
}
if (isset($Event['artists']['artist']) && (count($Event['artists']['artist']) === 1 && strtolower($Event['artists']['artist'][1]) == strtolower($Artist))) {
$i = 0;

View File

@ -81,8 +81,14 @@
if (!$DB->has_results()) {
error(404);
}
$Releases = $DB->to_array('GroupID', MYSQLI_ASSOC, false);
$GroupIDs = array_keys($Releases);
$ArtistRoles = array();
while (list($GroupID, $Importance) = $DB->next_record(MYSQLI_NUM, false)) {
// Get the highest importances to place the .torrents in the most relevant folders
if (!isset($ArtistRoles[$GroupID]) || $Importance < $ArtistRoles[$GroupID]) {
$ArtistRoles[$GroupID] = $Importance;
}
}
$GroupIDs = array_keys($ArtistRoles);
$SQL = 'SELECT CASE ';
@ -163,12 +169,40 @@
$Collector->skip_file($Download);
continue;
}
if ($Releases[$GroupID]['Importance'] == 1) {
switch ($ArtistRoles[$GroupID]) {
/**
* 1 => Main artist
* 2 => Guest artist
* 3 => Remixer
* 4 => Composer
* 5 => Conductor
* 6 => DJ / Compiler
* 7 => Producer
*/
case '1':
$ReleaseTypeName = $ReleaseTypes[$Download['ReleaseType']];
} elseif ($Releases[$GroupID]['Importance'] == 2) {
break;
case '2':
$ReleaseTypeName = 'Guest Appearance';
} elseif ($Releases[$GroupID]['Importance'] == 3) {
break;
case '3':
$ReleaseTypeName = 'Remixed By';
break;
case '4':
$ReleaseTypeName = 'Composition';
break;
case '5':
$ReleaseTypeName = 'Conducted By';
break;
case '6':
$ReleaseTypeName = 'DJ Mix';
break;
case '7':
$ReleaseTypeName = 'Produced By';
break;
default:
$ReleaseTypeName = 'Other';
break;
}
$Collector->add_file($TorrentFile, $Download, $ReleaseTypeName);
unset($Download);

View File

@ -30,20 +30,21 @@
// Recover password
if (!empty($_REQUEST['key'])) {
// User has entered a new password, use step 2
$DB->query("
SELECT
m.ID,
m.Email,
m.ipcc,
i.ResetExpires
FROM users_main AS m
FROM users_main as m
INNER JOIN users_info AS i ON i.UserID = m.ID
WHERE i.ResetKey = '".db_string($_REQUEST['key'])."'
AND i.ResetKey != ''
AND m.Enabled = '1'");
list($UserID, $Email, $Country, $Expires) = $DB->next_record();
if ($UserID && strtotime($Expires) > time()) {
// If the user has requested a password change, and his key has not expired
$Validate->SetFields('password', '1', 'regex', 'You entered an invalid password. A strong password is 8 characters or longer, contains at least 1 lowercase and uppercase letter, contains at least a number or symbol', array('regex' => '/(?=^.{8,}$)(?=.*[^a-zA-Z])(?=.*[A-Z])(?=.*[a-z]).*$/'));
$Validate->SetFields('verifypassword', '1', 'compare', 'Your passwords did not match.', array('comparefield' => 'password'));

View File

@ -219,5 +219,8 @@
$SphQL = new SphinxqlQuery();
$SphQL->raw_query("UPDATE requests, requests_delta SET torrentid = $TorrentID, fillerid = $FillerID WHERE id = $RequestID", false);
header("Location: requests.php?action=view&id=$RequestID");
?>

View File

@ -825,7 +825,7 @@ function next_hour() {
GROUP BY um.ID");
while (list($Username, $Email) = $DB->next_record()) {
$Body = "Hi $Username,\n\nIt has been almost 4 months since you used your account at ".site_url().". This is an automated email to inform you that your account will be disabled in 10 days if you do not sign in.";
Misc::send_email($Email, 'Your '.SITE_NAME.' account is about to be disabled', $Body);
Misc::send_email($Email, 'Your '.SITE_NAME.' account is about to be disabled', $Body, 'noreply');
}
$DB->query("
@ -878,22 +878,29 @@ function next_hour() {
//------------- Demote users --------------------------------------------//
sleep(10);
// Demote to Member when the ratio falls below 0.95 or they have less than 25 GB upload
$DemoteClasses = [POWER, ELITE, TORRENT_MASTER, POWER_TM, ELITE_TM];
$Query = $DB->query('
SELECT ID
FROM users_main
WHERE PermissionID IN('.POWER.', '.ELITE.', '.TORRENT_MASTER.')
AND Uploaded / Downloaded < 0.95
OR PermissionID IN('.POWER.', '.ELITE.', '.TORRENT_MASTER.')
AND Uploaded < 25 * 1024 * 1024 * 1024');
WHERE PermissionID IN(' . implode(', ', $DemoteClasses) . ')
AND (
Uploaded / Downloaded < 0.95
OR Uploaded < 25 * 1024 * 1024 * 1024
)');
echo "demoted 1\n";
$DB->query('
UPDATE users_main
SET PermissionID = '.MEMBER.'
WHERE PermissionID IN('.POWER.', '.ELITE.', '.TORRENT_MASTER.')
AND Uploaded / Downloaded < 0.95
OR PermissionID IN('.POWER.', '.ELITE.', '.TORRENT_MASTER.')
AND Uploaded < 25 * 1024 * 1024 * 1024');
UPDATE users_info AS ui
JOIN users_main AS um ON um.ID = ui.UserID
SET
um.PermissionID = ' . MEMBER . ",
ui.AdminComment = CONCAT('" . sqltime() . ' - Class changed to ' . Users::make_class_string(MEMBER) . " by System\n\n', ui.AdminComment)
WHERE um.PermissionID IN (" . implode(', ', $DemoteClasses) . ')
AND (
um.Uploaded / um.Downloaded < 0.95
OR um.Uploaded < 25 * 1024 * 1024 * 1024
)');
$DB->set_query_id($Query);
while (list($UserID) = $DB->next_record()) {
/*$Cache->begin_transaction("user_info_$UserID");
@ -905,17 +912,22 @@ function next_hour() {
}
echo "demoted 2\n";
// Demote to User when the ratio drops below 0.65
$DemoteClasses = [MEMBER, POWER, ELITE, TORRENT_MASTER, POWER_TM, ELITE_TM];
$Query = $DB->query('
SELECT ID
FROM users_main
WHERE PermissionID IN('.MEMBER.', '.POWER.', '.ELITE.', '.TORRENT_MASTER.')
WHERE PermissionID IN(' . implode(', ', $DemoteClasses) . ')
AND Uploaded / Downloaded < 0.65');
echo "demoted 3\n";
$DB->query('
UPDATE users_main
SET PermissionID = '.USER.'
WHERE PermissionID IN('.MEMBER.', '.POWER.', '.ELITE.', '.TORRENT_MASTER.')
AND Uploaded / Downloaded < 0.65');
UPDATE users_info AS ui
JOIN users_main AS um ON um.ID = ui.UserID
SET
um.PermissionID = ' . USER . ",
ui.AdminComment = CONCAT('" . sqltime() . ' - Class changed to ' . Users::make_class_string(USER) . " by System\n\n', ui.AdminComment)
WHERE um.PermissionID IN (" . implode(', ', $DemoteClasses) . ')
AND um.Uploaded / um.Downloaded < 0.65');
$DB->set_query_id($Query);
while (list($UserID) = $DB->next_record()) {
/*$Cache->begin_transaction("user_info_$UserID");

View File

@ -13,7 +13,7 @@
shell_exec('unzip GeoLiteCity-latest.zip');
shell_exec('rm GeoLiteCity-latest.zip');
if (($Locations = file("GeoLiteCity_".date('Ym')."05/GeoLiteCity-Location.csv", FILE_IGNORE_NEW_LINES)) === false) {
if (($Locations = file("GeoLiteCity_".date('Ym')."02/GeoLiteCity-Location.csv", FILE_IGNORE_NEW_LINES)) === false) {
error('Download or extraction of maxmind database failed');
}
array_shift($Locations);
@ -32,7 +32,7 @@
echo 'There are '.count($CountryIDs).' CountryIDs';
echo '<br />';
if (($Blocks = file("GeoLiteCity_".date('Ym')."05/GeoLiteCity-Blocks.csv", FILE_IGNORE_NEW_LINES)) === false) {
if (($Blocks = file("GeoLiteCity_".date('Ym')."02/GeoLiteCity-Blocks.csv", FILE_IGNORE_NEW_LINES)) === false) {
echo 'Error';
}
array_shift($Blocks);

View File

@ -168,15 +168,14 @@
$DB->query("
INSERT INTO news (UserID, Title, Body, Time)
VALUES ('$LoggedUser[ID]', '".db_string($_POST['title'])."', '".db_string($_POST['body'])."', '".sqltime()."')");
$Cache->delete_value('news_latest_id');
$Cache->delete_value('news_latest_title');
$Cache->delete_value('news');
NotificationsManager::send_push(NotificationsManager::get_push_enabled_users(), $_POST['title'], $_POST['body'], site_url() . 'index.php', NotificationsManager::NEWS);
$Cache->delete_value('news_latest_id');
$Cache->delete_value('news_latest_title');
$Cache->delete_value('news');
header('Location: index.php');
break;

View File

@ -35,6 +35,7 @@
case 'lastfm':
include(SERVER_ROOT.'/sections/top10/lastfm.php');
break;
default:
error(404);
break;

View File

@ -1,23 +1,4 @@
<?
/************************************************************************
*-------------------- Browse page ---------------------------------------
* Welcome to one of the most complicated pages in all of gazelle - the
* browse page.
*
* This is the page that is displayed when someone visits torrents.php
*
* It offers normal and advanced search, as well as enabled/disabled
* grouping.
*
* Don't blink.
* Blink and you're dead.
* Don't turn your back.
* Don't look away.
* And don't blink.
* Good Luck.
*
*************************************************************************/
include(SERVER_ROOT.'/sections/torrents/functions.php');
// The "order by x" links on columns headers
@ -35,7 +16,6 @@ function header_link($SortKey, $DefaultWay = 'desc') {
return "torrents.php?order_way=$NewWay&amp;order_by=$SortKey&amp;".Format::get_url(array('order_way', 'order_by'));
}
/** Start default parameters and validation **/
if (!empty($_GET['searchstr']) || !empty($_GET['groupname'])) {
if (!empty($_GET['searchstr'])) {
$InfoHash = $_GET['searchstr'];
@ -113,422 +93,29 @@ function header_link($SortKey, $DefaultWay = 'desc') {
}
}
// Terms were not submitted via the search form
if (!isset($_GET['searchsubmit'])) {
$_GET['group_results'] = !$LoggedUser['DisableGrouping2'];
}
if (isset($_GET['group_results']) && $_GET['group_results']) {
$_GET['group_results'] = 1;
$GroupResults = true;
$SortOrders = array(
// 'url attr' => [global order, order within group]
'year' => array('year', 'year'),
'time' => array('id', 'id'),
'size' => array('maxsize', 'size'),
'seeders' => array('sumseeders', 'seeders'),
'leechers' => array('sumleechers', 'leechers'),
'snatched' => array('sumsnatched', 'snatched'),
'random' => false);
$AggregateExp = array(
'maxsize' => 'MAX(size) AS maxsize',
'sumseeders' => 'SUM(seeders) AS sumseeders',
'sumleechers' => 'SUM(leechers) AS sumleechers',
'sumsnatched' => 'SUM(snatched) AS sumsnatched');
if (isset($_GET['searchsubmit'])) {
$GroupResults = !empty($_GET['group_results']);
} else {
$GroupResults = false;
$SortOrders = array(
'year' => 'year',
'time' => 'id',
'size' => 'size',
'seeders' => 'seeders',
'leechers' => 'leechers',
'snatched' => 'snatched',
'random' => false);
}
if (empty($_GET['order_by']) || !isset($SortOrders[$_GET['order_by']])) {
$_GET['order_by'] = 'time';
$OrderBy = 'time'; // For header links
} else {
$OrderBy = $_GET['order_by'];
$GroupResults = !$LoggedUser['DisableGrouping2'];
}
if (!empty($_GET['order_way']) && $_GET['order_way'] == 'asc') {
$OrderWay = 'asc';
} else {
$_GET['order_way'] = 'desc';
$OrderWay = 'desc';
}
/** End default parameters and validation **/
/** Start preparation of property arrays **/
array_pop($Bitrates); // remove 'other'
$SearchBitrates = array_merge($Bitrates, array('v0', 'v1', 'v2', '24bit'));
foreach ($SearchBitrates as $ID => $Val) {
$SearchBitrates[$ID] = strtolower($Val);
}
foreach ($Formats as $ID => $Val) {
$SearchFormats[$ID] = strtolower($Val);
}
/** End preparation of property arrays **/
/** Start query preparation **/
$SphQL = new SphinxqlQuery();
$SphQLTor = new SphinxqlQuery();
if ($OrderBy == 'random') {
$SphQL->select('id, groupid, categoryid')
->order_by('RAND()', '');
$Random = true;
} elseif ($GroupResults) {
$OrderProperties = $SortOrders[$OrderBy];
$SphQL->select('groupid, categoryid' . (isset($AggregateExp[$OrderProperties[0]]) ? ', '.$AggregateExp[$OrderProperties[0]] : ''))
->group_by('groupid')
->order_by($OrderProperties[0], $OrderWay)
->order_group_by($OrderProperties[1], $OrderWay);
if (empty($_GET['order_by']) || !isset(TorrentSearch::$SortOrders[$_GET['order_by']])) {
$OrderBy = 'time'; // For header links
} else {
$SphQL->select('id, groupid, categoryid')
->order_by($SortOrders[$OrderBy], $OrderWay);
}
$SphQL->from('torrents, delta');
$SphQLTor->select('id')->from('torrents, delta');
/** End query preparation **/
/** Start building search query **/
$Filtered = false;
$EnableNegation = false; // Sphinx needs at least one positive search condition to support the NOT operator
// File list searches make use of the proximity operator to ensure that all keywords match the same file
if (!empty($_GET['filelist'])) {
$SearchString = trim($_GET['filelist']);
if ($SearchString !== '') {
$SearchString = '"'.Sphinxql::sph_escape_string($_GET['filelist']).'"~20';
$SphQL->where_match($SearchString, 'filelist', false);
$SphQLTor->where_match($SearchString, 'filelist', false);
$EnableNegation = true;
$Filtered = true;
}
$OrderBy = $_GET['order_by'];
}
// Collect all entered search terms to find out whether to enable the NOT operator
$SearchWords = array();
foreach (array('artistname', 'groupname', 'recordlabel', 'cataloguenumber',
'taglist', 'remastertitle', 'remasteryear', 'remasterrecordlabel',
'remastercataloguenumber', 'encoding', 'format', 'media', 'description') as $Search) {
if (!empty($_GET[$Search])) {
$SearchString = trim($_GET[$Search]);
if ($SearchString !== '') {
$SearchWords[$Search] = array('include' => array(), 'exclude' => array());
if ($Search == 'taglist') {
$SearchString = strtr($SearchString, '.', '_');
$Words = explode(',', $SearchString);
} else {
$Words = explode(' ', $SearchString);
}
foreach ($Words as $Word) {
$Word = trim($Word);
// Skip isolated hyphens to enable "Artist - Title" searches
if ($Word === '-') {
continue;
}
if ($Word[0] === '!' && strlen($Word) >= 2) {
if (strpos($Word, '!', 1) === false) {
$SearchWords[$Search]['exclude'][] = $Word;
} else {
$SearchWords[$Search]['include'][] = $Word;
$EnableNegation = true;
}
} elseif ($Word !== '') {
$SearchWords[$Search]['include'][] = $Word;
$EnableNegation = true;
}
}
}
}
}
//Simple search
if (!empty($_GET['searchstr'])) {
$SearchString = trim($_GET['searchstr']);
$Words = explode(' ', strtolower($SearchString));
if (!empty($Words)) {
$FilterBitrates = $FilterFormats = array();
$BasicSearch = array('include' => array(), 'exclude' => array());
foreach ($Words as $Word) {
$Word = trim($Word);
// Skip isolated hyphens to enable "Artist - Title" searches
if ($Word === '-') {
continue;
}
if ($Word[0] === '!' && strlen($Word) >= 2) {
if ($Word === '!100%') {
$_GET['haslog'] = '-1';
} elseif (strpos($Word, '!', 1) === false) {
$BasicSearch['exclude'][] = $Word;
} else {
$BasicSearch['include'][] = $Word;
$EnableNegation = true;
}
} elseif (in_array($Word, $SearchBitrates)) {
$FilterBitrates[] = $Word;
$EnableNegation = true;
} elseif (in_array($Word, $SearchFormats)) {
$FilterFormats[] = $Word;
$EnableNegation = true;
} elseif ($Word === '100%') {
$_GET['haslog'] = '100';
} elseif ($Word !== '') {
$BasicSearch['include'][] = $Word;
$EnableNegation = true;
}
}
if (!$EnableNegation && !empty($BasicSearch['exclude'])) {
$BasicSearch['include'] = array_merge($BasicSearch['include'], $BasicSearch['exclude']);
unset($BasicSearch['exclude']);
}
$QueryParts = array();
foreach ($BasicSearch['include'] as $Word) {
$QueryParts[] = Sphinxql::sph_escape_string($Word);
}
if (!empty($BasicSearch['exclude'])) {
foreach ($BasicSearch['exclude'] as $Word) {
$QueryParts[] = '!'.Sphinxql::sph_escape_string(substr($Word, 1));
}
}
if (!empty($FilterBitrates)) {
$SearchString = implode(' ', $FilterBitrates);
$SphQL->where_match($SearchString, 'encoding', false);
$SphQLTor->where_match($SearchString, 'encoding', false);
$Filtered = true;
}
if (!empty($FilterFormats)) {
$SearchString = implode(' ', $FilterFormats);
$SphQL->where_match($SearchString, 'format', false);
$SphQLTor->where_match($SearchString, 'format', false);
$Filtered = true;
}
if (!empty($QueryParts)) {
$SearchString = implode(' ', $QueryParts);
$SphQL->where_match($SearchString, '(groupname,artistname,yearfulltext)', false);
$SphQLTor->where_match($SearchString, '(groupname,artistname,yearfulltext)', false);
$Filtered = true;
}
}
}
// Tag list
if (!empty($SearchWords['taglist'])) {
$_GET['tags_type'] = (!isset($_GET['tags_type']) || $_GET['tags_type'] == 1) ? '1' : '0';
$TagType = (int)$_GET['tags_type'];
//Get tags
$Tags = $SearchWords['taglist'];
$TagFilter = Tags::tag_filter_sph($Tags, $EnableNegation, $TagType);
$TagListString = $TagFilter['input'];
if (!empty($TagFilter['predicate'])) {
$SphQL->where_match($TagFilter['predicate'], 'taglist', false);
$SphQLTor->where_match($TagFilter['predicate'], 'taglist', false);
$Filtered = true;
}
unset($SearchWords['taglist']);
}
elseif (!isset($_GET['tags_type'])) {
$_GET['tags_type'] = '1';
}
foreach ($SearchWords as $Search => $Words) {
$QueryParts = array();
if (!$EnableNegation && !empty($Words['exclude'])) {
$Words['include'] = array_merge($Words['include'], $Words['exclude']);
unset($Words['exclude']);
}
foreach ($Words['include'] as $Word) {
$QueryParts[] = Sphinxql::sph_escape_string($Word);
}
if (!empty($Words['exclude'])) {
foreach ($Words['exclude'] as $Word) {
$QueryParts[] = '!'.Sphinxql::sph_escape_string(substr($Word, 1));
}
}
if (!empty($QueryParts)) {
$SearchString = implode(' ', $QueryParts);
$SphQL->where_match($SearchString, $Search, false);
$SphQLTor->where_match($SearchString, $Search, false);
$Filtered = true;
}
}
if (!empty($_GET['year'])) {
$Years = explode('-', $_GET['year']);
if (is_number($Years[0]) || (empty($Years[0]) && !empty($Years[1]) && is_number($Years[1]))) {
if (count($Years) === 1) {
$SphQL->where('year', $Years[0]);
$SphQLTor->where('year', $Years[0]);
} else {
if (empty($Years[0])) {
$SphQL->where_lt('year', $Years[1], true);
$SphQLTor->where_lt('year', $Years[1], true);
} elseif (empty($Years[1]) || !is_number($Years[1])) {
$SphQL->where_gt('year', $Years[0], true);
$SphQLTor->where_gt('year', $Years[0], true);
} else {
if ($Years[0] > $Years[1]) {
$Years = array_reverse($Years);
}
$SphQL->where_between('year', array($Years[0], $Years[1]));
$SphQLTor->where_between('year', array($Years[0], $Years[1]));
}
}
$Filtered = true;
}
}
if (isset($_GET['haslog']) && $_GET['haslog'] !== '') {
if ($_GET['haslog'] === '100') {
$SphQL->where('logscore', 100);
$SphQLTor->where('logscore', 100);
} elseif ($_GET['haslog'] < 0) {
// Look for torrents with log score < 100
$SphQL->where_lt('logscore', 100);
$SphQL->where('haslog', 1);
$SphQLTor->where_lt('logscore', 100);
$SphQLTor->where('haslog', 1);
} elseif ($_GET['haslog'] == 0) {
$SphQL->where('haslog', 0);
$SphQLTor->where('haslog', 0);
} else {
$SphQL->where('haslog', 1);
$SphQLTor->where('haslog', 1);
}
$Filtered = true;
}
foreach (array('hascue', 'scene', 'vanityhouse', 'releasetype') as $Search) {
if (isset($_GET[$Search]) && $_GET[$Search] !== '') {
$SphQL->where($Search, $_GET[$Search]);
// Release type is group specific
if ($Search != 'releasetype') {
$SphQLTor->where($Search, $_GET[$Search]);
}
$Filtered = true;
}
}
if (isset($_GET['freetorrent']) && $_GET['freetorrent'] !== '') {
switch ($_GET['freetorrent']) {
case 0: // Only normal freeleech
$SphQL->where('freetorrent', 0);
$SphQLTor->where('freetorrent', 0);
$Filtered = true;
break;
case 1: // Only free leech
$SphQL->where('freetorrent', 1);
$SphQLTor->where('freetorrent', 1);
$Filtered = true;
break;
case 2: // Only neutral leech
$SphQL->where('freetorrent', 2);
$SphQLTor->where('freetorrent', 2);
$Filtered = true;
break;
case 3: // Free or neutral leech
$SphQL->where('freetorrent', 0, true);
$SphQLTor->where('freetorrent', 0, true);
$Filtered = true;
break;
default:
unset($_GET['freetorrent']);
break;
}
}
if (!empty($_GET['filter_cat'])) {
$SphQL->where('categoryid', array_keys($_GET['filter_cat']));
$Filtered = true;
}
/** End building search query **/
/** Run search query and collect results **/
if (isset($Random) && $GroupResults) {
// ORDER BY RAND() can't be used together with GROUP BY, so we need some special tactics
$Page = 1;
$SphQL->limit(0, 5 * TORRENTS_PER_PAGE, 5 * TORRENTS_PER_PAGE);
$SphQLResult = $SphQL->query();
$TotalCount = $SphQLResult->get_meta('total_found');
$Results = $SphQLResult->to_array('groupid');
$GroupIDs = array_keys($Results);
$GroupCount = count($GroupIDs);
while ($SphQLResult->get_meta('total') < $TotalCount && $GroupCount < TORRENTS_PER_PAGE) {
// Make sure we get TORRENTS_PER_PAGE results, or all of them if there are less than TORRENTS_PER_PAGE hits
$SphQL->where('groupid', $GroupIDs, true);
$SphQLResult = $SphQL->query();
if (!$SphQLResult->has_results()) {
break;
}
$Results += $SphQLResult->to_array('groupid');
$GroupIDs = array_keys($Results);
$GroupCount = count($GroupIDs);
}
if ($GroupCount > TORRENTS_PER_PAGE) {
$Results = array_slice($Results, 0, TORRENTS_PER_PAGE, true);
}
$GroupIDs = array_keys($Results);
$NumResults = count($Results);
} else {
if (!empty($_GET['page']) && is_number($_GET['page']) && $_GET['page'] > 0) {
if (check_perms('site_search_many')) {
$Page = $_GET['page'];
} else {
$Page = min(SPHINX_MAX_MATCHES / TORRENTS_PER_PAGE, $_GET['page']);
}
$Offset = ($Page - 1) * TORRENTS_PER_PAGE;
$SphQL->limit($Offset, TORRENTS_PER_PAGE, $Offset + TORRENTS_PER_PAGE);
} else {
$Page = 1;
$SphQL->limit(0, TORRENTS_PER_PAGE, TORRENTS_PER_PAGE);
}
$SphQLResult = $SphQL->query();
$NumResults = $SphQLResult->get_meta('total_found');
if ($GroupResults) {
$Results = $SphQLResult->to_array('groupid');
$GroupIDs = array_keys($Results);
} else {
$Results = $SphQLResult->to_array('id');
$GroupIDs = $SphQLResult->collect('groupid');
}
}
if (!check_perms('site_search_many') && $NumResults > SPHINX_MAX_MATCHES) {
$NumResults = SPHINX_MAX_MATCHES;
}
if ($NumResults) {
$Groups = Torrents::get_groups($GroupIDs);
if (!empty($Groups) && $GroupResults) {
$TorrentIDs = array();
foreach ($Groups as $Group) {
if (!empty($Group['Torrents'])) {
$TorrentIDs = array_merge($TorrentIDs, array_keys($Group['Torrents']));
}
}
$TorrentCount = count($TorrentIDs);
if ($TorrentCount > 0) {
// Get a list of all torrent ids that match the search query
$SphQLTor->where('id', $TorrentIDs)->limit(0, $TorrentCount, $TorrentCount);
$SphQLResultTor = $SphQLTor->query();
$TorrentIDs = $SphQLResultTor->to_pair('id', 'id'); // Because isset() is faster than in_array()
}
}
}
/** End run search query and collect results **/
$Page = !empty($_GET['page']) ? (int) $_GET['page'] : 1;
$Search = new TorrentSearch($GroupResults, $OrderBy, $OrderWay, $Page, TORRENTS_PER_PAGE);
$Results = $Search->query($_GET);
$Groups = $Search->get_groups();
$NumResults = $Search->record_count();
$HideFilter = isset($LoggedUser['ShowTorFilter']) && $LoggedUser['ShowTorFilter'] == 0;
// This is kinda ugly, but the enormous if paragraph was really hard to read
@ -715,7 +302,7 @@ function header_link($SortKey, $DefaultWay = 'desc') {
<tr id="tagfilter">
<td class="label"><span title="Use !tag to exclude tag" class="tooltip">Tags (comma-separated):</span></td>
<td colspan="3" class="ft_taglist">
<input type="search" size="40" id="tags" name="taglist" class="inputtext smaller" value="<?=!empty($TagListString) ? display_str($TagListString) : ''?>"<? Users::has_autocomplete_enabled('other'); ?> />&nbsp;
<input type="search" size="40" id="tags" name="taglist" class="inputtext smaller" value="<?=display_str($Search->get_terms('taglist'))?>"<? Users::has_autocomplete_enabled('other'); ?> />&nbsp;
<input type="radio" name="tags_type" id="tags_type0" value="0"<?Format::selected('tags_type', 0, 'checked')?> /><label for="tags_type0"> Any</label>&nbsp;&nbsp;
<input type="radio" name="tags_type" id="tags_type1" value="1"<?Format::selected('tags_type', 1, 'checked')?> /><label for="tags_type1"> All</label>
</td>
@ -743,7 +330,7 @@ function header_link($SortKey, $DefaultWay = 'desc') {
<label for="group_results">Group by release:</label>
</td>
<td colspan="3" class="ft_group_results">
<input type="checkbox" value="1" name="group_results" id="group_results"<?Format::selected('group_results', 1, 'checked')?> />
<input type="checkbox" value="1" name="group_results" id="group_results"<?=$GroupResults ? ' checked="checked"' : ''?> />
</td>
</tr>
</table>
@ -818,7 +405,7 @@ function header_link($SortKey, $DefaultWay = 'desc') {
<input type="hidden" name="searchsubmit" value="1" />
<input type="button" value="Reset" onclick="location.href = 'torrents.php<? if (isset($_GET['action']) && $_GET['action'] === 'advanced') { ?>?action=advanced<? } ?>'" />
&nbsp;&nbsp;
<? if ($Filtered) { ?>
<? if ($Search->has_filters()) { ?>
<input type="submit" name="setdefault" value="Make default" />
<?
}
@ -913,13 +500,12 @@ function header_link($SortKey, $DefaultWay = 'desc') {
<?
// Start printing torrent list
foreach ($Results as $Result) {
$GroupID = $Result['groupid'];
foreach ($Results as $Key => $GroupID) {
$GroupInfo = $Groups[$GroupID];
if (empty($GroupInfo['Torrents'])) {
continue;
}
$CategoryID = $Result['categoryid'];
$CategoryID = $GroupInfo['CategoryID'];
$GroupYear = $GroupInfo['Year'];
$ExtendedArtists = $GroupInfo['ExtendedArtists'];
$GroupCatalogueNumber = $GroupInfo['CatalogueNumber'];
@ -930,16 +516,15 @@ function header_link($SortKey, $DefaultWay = 'desc') {
$Torrents = $GroupInfo['Torrents'];
$GroupTime = $MaxSize = $TotalLeechers = $TotalSeeders = $TotalSnatched = 0;
foreach ($Torrents as $T) {
if (isset($TorrentIDs[$T['ID']])) {
$GroupTime = max($GroupTime, strtotime($T['Time']));
$MaxSize = max($MaxSize, $T['Size']);
$TotalLeechers += $T['Leechers'];
$TotalSeeders += $T['Seeders'];
$TotalSnatched += $T['Snatched'];
}
}
} else {
$Torrents = array($Result['id'] => $GroupInfo['Torrents'][$Result['id']]);
$TorrentID = $Key;
$Torrents = array($TorrentID => $GroupInfo['Torrents'][$TorrentID]);
}
$TorrentTags = new Tags($GroupInfo['TagList']);
@ -1021,11 +606,6 @@ function header_link($SortKey, $DefaultWay = 'desc') {
foreach ($Torrents as $TorrentID => $Data) {
// All of the individual torrents in the group
// If they're using the advanced search and have chosen enabled grouping, we just skip the torrents that don't check out
if (!isset($TorrentIDs[$TorrentID])) {
continue;
}
//Get report info for each torrent, use the cache if available, if not, add to it.
$Reported = false;
$Reports = Torrents::get_reports($TorrentID);

View File

@ -37,7 +37,7 @@
/* uTorrent Remote and various scripts redownload .torrent files periodically.
To prevent this retardation from blowing bandwidth etc., let's block it
if the .torrent file has been downloaded four times before */
$ScriptUAs = array('BTWebClient*', 'Python-urllib*', 'python-requests*');
$ScriptUAs = array('BTWebClient*', 'Python-urllib*', 'python-requests*', 'uTorrent*');
if (Misc::in_array_partial($_SERVER['HTTP_USER_AGENT'], $ScriptUAs)) {
$DB->query("
SELECT 1

View File

@ -693,7 +693,7 @@
Once you are connected to our server you will need to join our disabled users channel.
Type: /join '.BOT_DISABLED_CHAN.'
Please visit us soon so we can help you resolve this matter.');
Please visit us soon so we can help you resolve this matter.', 'noreply');
}
if ($MergeStatsFrom && check_perms('users_edit_ratio')) {

View File

@ -16,35 +16,24 @@
if (!is_number($UserID)) {
error(404);
}
$DB->query("
SELECT
um.Username,
p.Level AS Class
FROM users_main AS um
LEFT JOIN permissions AS p ON p.ID = um.PermissionID
WHERE um.ID = $UserID");
list($Username, $Class) = $DB->next_record();
if (!check_perms('users_view_ips', $Class)) {
$UserInfo = Users::user_info($UserID);
if (!check_perms('users_view_ips', $UserInfo['Class'])) {
error(403);
}
$UsersOnly = $_GET['usersonly'];
$UsersOnly = !empty($_GET['usersonly']);
if (isset($_POST['ip'])) {
$SearchIP = db_string(str_replace("*", "%", trim($_POST['ip'])));
$SearchIPQuery = " AND h1.IP LIKE '$SearchIP' ";
if (!empty($_GET['ip']) && trim($_GET['ip']) != '') {
$SearchIP = db_string(str_replace("*", "%", trim($_GET['ip'])));
$SearchIPQuery = "AND IP LIKE '$SearchIP'";
} else {
$SearchIPQuery = "";
}
View::show_header("IP address history for $Username");
View::show_header("IP address history for $UserInfo[Username]");
?>
<script type="text/javascript">//<![CDATA[
function ShowIPs(rowname) {
$('tr[name="' + rowname + '"]').gtoggle();
}
function Ban(ip, id, elemID) {
function Ban(ip, elemID) {
var notes = prompt("Enter notes for this ban");
if (notes != null && notes.length > 0) {
var xmlhttp;
@ -74,7 +63,7 @@ function UnBan(ip, id, elemID) {
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
document.getElementById(elemID).innerHTML = "Ban";
document.getElementById(elemID).onclick = function() { Ban(ip, id, elemID); return false; };
document.getElementById(elemID).onclick = function() { Ban(ip, elemID); return false; };
}
}
xmlhttp.open("GET","tools.php?action=quick_ban&perform=delete&id=" + id + "&ip=" + ip, true);
@ -86,67 +75,107 @@ function UnBan(ip, id, elemID) {
<?
list($Page, $Limit) = Format::page_limit(IPS_PER_PAGE);
if ($UsersOnly == 1) {
$RS = $DB->query("
if ($UsersOnly) {
$DB->query("
SELECT DISTINCT IP
FROM users_history_ips
WHERE UserID = '$UserID'
$SearchIPQuery");
if ($DB->has_results()) {
$UserIPs = db_array($DB->collect('IP'), array(), true);
$DB->query("
SELECT DISTINCT IP
FROM users_history_ips
WHERE UserID != '$UserID'
AND IP IN (" . implode(',', $UserIPs) . ")");
unset($UserIPs);
if ($DB->has_results()) {
$OtherIPs = db_array($DB->collect('IP'), array(), true);
$QueryID = $DB->query("
SELECT
SQL_CALC_FOUND_ROWS
h1.IP,
h1.StartTime,
h1.EndTime,
GROUP_CONCAT(h2.UserID SEPARATOR '|'),
GROUP_CONCAT(h2.StartTime SEPARATOR '|'),
GROUP_CONCAT(IFNULL(h2.EndTime,0) SEPARATOR '|'),
GROUP_CONCAT(um2.Username SEPARATOR '|'),
GROUP_CONCAT(um2.Enabled SEPARATOR '|'),
GROUP_CONCAT(ui2.Donor SEPARATOR '|'),
GROUP_CONCAT(ui2.Warned SEPARATOR '|')
FROM users_history_ips AS h1
LEFT JOIN users_history_ips AS h2 ON h2.IP = h1.IP AND h2.UserID != $UserID
LEFT JOIN users_main AS um2 ON um2.ID = h2.UserID
LEFT JOIN users_info AS ui2 ON ui2.UserID = h2.UserID
WHERE h1.UserID = '$UserID'
AND h2.UserID > 0 $SearchIPQuery
GROUP BY h1.IP, h1.StartTime
ORDER BY h1.StartTime DESC
IP,
StartTime,
EndTime
FROM users_history_ips
WHERE UserID = '$UserID'
AND IP IN (" . implode(',', $OtherIPs) . ")
ORDER BY StartTime DESC
LIMIT $Limit");
unset($OtherIPs);
}
}
} else {
$RS = $DB->query("
$QueryID = $DB->query("
SELECT
SQL_CALC_FOUND_ROWS
h1.IP,
h1.StartTime,
h1.EndTime,
GROUP_CONCAT(h2.UserID SEPARATOR '|'),
GROUP_CONCAT(h2.StartTime SEPARATOR '|'),
GROUP_CONCAT(IFNULL(h2.EndTime,0) SEPARATOR '|'),
GROUP_CONCAT(um2.Username SEPARATOR '|'),
GROUP_CONCAT(um2.Enabled SEPARATOR '|'),
GROUP_CONCAT(ui2.Donor SEPARATOR '|'),
GROUP_CONCAT(ui2.Warned SEPARATOR '|')
FROM users_history_ips AS h1
LEFT JOIN users_history_ips AS h2 ON h2.IP = h1.IP AND h2.UserID != $UserID
LEFT JOIN users_main AS um2 ON um2.ID = h2.UserID
LEFT JOIN users_info AS ui2 ON ui2.UserID = h2.UserID
WHERE h1.UserID = '$UserID' $SearchIPQuery
GROUP BY h1.IP, h1.StartTime
ORDER BY h1.StartTime DESC
IP,
StartTime,
EndTime
FROM users_history_ips
WHERE UserID = '$UserID'
$SearchIPQuery
ORDER BY StartTime DESC
LIMIT $Limit");
}
$DB->query('SELECT FOUND_ROWS()');
list($NumResults) = $DB->next_record();
$DB->set_query_id($RS);
if (isset($QueryID)) {
$DB->query('SELECT FOUND_ROWS()');
list($NumResults) = $DB->next_record();
$DB->set_query_id($QueryID);
$Results = $DB->to_array(false, MYSQLI_ASSOC);
$IPMatches = $IPMatchesUser = $IPMatchesIgnored = array();
} else {
$NumResults = 0;
$Results = array();
}
if (!empty($Results)) {
$IPs = db_array($DB->collect('IP'), array(), true);
$DB->query("
SELECT
UserID,
IP,
StartTime,
EndTime
FROM users_history_ips
WHERE IP IN (" . implode(',', $IPs) . ")
AND UserID != '$UserID'
AND UserID != 0
ORDER BY StartTime DESC");
unset($IPs);
while ($Match = $DB->next_record(MYSQLI_ASSOC)) {
$OtherIP = $Match['IP'];
$OtherUserID = $Match['UserID'];
if (!isset($IPMatchesUser[$OtherIP][$OtherUserID])) {
$IPMatchesUser[$OtherIP][$OtherUserID] = 0;
}
if ($IPMatchesUser[$OtherIP][$OtherUserID] < 500) {
$IPMatches[$OtherIP][] = $Match;
} else {
if (!isset($IPMatchesIgnored[$OtherIP][$OtherUserID])) {
$IPMatchesIgnored[$OtherIP][$OtherUserID] = 0;
}
$IPMatchesIgnored[$OtherIP][$OtherUserID]++;
}
$IPMatchesUser[$OtherIP][$OtherUserID]++;
}
}
$Pages = Format::get_pages($Page, $NumResults, IPS_PER_PAGE, 9);
?>
<div class="thin">
<div class="header">
<h2>IP address history for <a href="user.php?id=<?=$UserID?>"><?=$Username?></a></h2>
<h2>IP address history for <a href="user.php?id=<?=$UserID?>"><?=$UserInfo['Username']?></a></h2>
<div class="linkbox">
<? if ($UsersOnly) { ?>
<a href="userhistory.php?action=ips&amp;userid=<?=$UserID?>" class="brackets">View all IP addresses</a>
<a href="userhistory.php?<?=Format::get_url(array('usersonly'))?>" class="brackets">View all IP addresses</a>
<? } else { ?>
<a href="userhistory.php?action=ips&amp;userid=<?=$UserID?>&amp;usersonly=1" class="brackets">View IP addresses with users</a>
<a href="userhistory.php?<?=Format::get_url()?>&amp;usersonly=1" class="brackets">View IP addresses with users</a>
<? } ?>
</div>
<? if ($Pages) { ?>
@ -159,8 +188,13 @@ function UnBan(ip, id, elemID) {
</tr>
<tr><td>
<form class="search_form" name="ip_log" method="post" action="">
<input type="text" name="ip" />
<form class="search_form" name="ip_log" method="get" action="">
<input type="hidden" name="action" value="<?=$_GET['action']?>" />
<input type="hidden" name="userid" value="<?=$UserID?>" />
<? if ($UsersOnly) { ?>
<input type="hidden" name="usersonly" value="1" />
<? } ?>
<input type="text" name="ip" value="<?=Format::form('ip')?>" />
<input type="submit" value="Search" />
Wildcard (*) search examples: 127.0.* or 1*2.0.*.1 or *.*.*.*
</form>
@ -170,91 +204,110 @@ function UnBan(ip, id, elemID) {
<table id="iphistory">
<tr class="colhead">
<td>IP address</td>
<td>Started <a href="#" onclick="$('#iphistory td:nth-child(2), #iphistory td:nth-child(4)').ghide(); $('#iphistory td:nth-child(3), #iphistory td:nth-child(5)').gshow(); return false;" class="brackets">Toggle</a></td>
<td class="hidden">Started <a href="#" onclick="$('#iphistory td:nth-child(2), #iphistory td:nth-child(4)').gshow(); $('#iphistory td:nth-child(3), #iphistory td:nth-child(5)').ghide(); return false;" class="brackets">Toggle</a></td>
<td>Started <a href="#" onclick="$('#iphistory .reltime').gtoggle(); $('#iphistory .abstime').gtoggle(); return false;" class="brackets">Toggle</a></td>
<td>Ended</td>
<td class="hidden">Ended</td>
<td>Elapsed</td>
</tr>
<?
$counter = 0;
$IPs = array();
$Results = $DB->to_array();
$Counter = 0;
$IPBanChecks = array();
$PrintedIPs = array();
$CanManageIPBans = check_perms('admin_manage_ipbans');
foreach ($Results as $Index => $Result) {
list($IP, $StartTime, $EndTime, $UserIDs, $UserStartTimes, $UserEndTimes, $Usernames, $UsersEnabled, $UsersDonor, $UsersWarned) = $Result;
$HasDupe = false;
$UserIDs = explode('|', $UserIDs);
if (!$EndTime) {
$IP = $Result['IP'];
$StartTime = $Result['StartTime'];
$EndTime = $Result['EndTime'];
if (!$Result['EndTime']) {
$EndTime = sqltime();
}
if ($UserIDs[0] != 0) {
$HasDupe = true;
$UserStartTimes = explode('|', $UserStartTimes);
$UserEndTimes = explode('|', $UserEndTimes);
$Usernames = explode('|', $Usernames);
$UsersEnabled = explode('|', $UsersEnabled);
$UsersDonor = explode('|', $UsersDonor);
$UsersWarned = explode('|', $UsersWarned);
$OtherUsers = isset($IPMatches[$IP]) ? $IPMatches[$IP] : array();
$ElementID = 'ip_' . strtr($IP, '.', '-');
$FirstOccurrence = !isset($IPIndexes[$IP]);
if ($FirstOccurrence) {
$IPIndexes[$IP] = $Index;
}
?>
<tr class="rowa">
<tr class="rowa" <?=$FirstOccurrence ? "id=\"$ElementID\"" : ''?>>
<td>
<?=$IP?> (<?=Tools::get_country_code_by_ajax($IP)?>)<?
if ($CanManageIPBans) {
if (!isset($IPs[$IP])) {
$sql = "
SELECT ID, FromIP, ToIP
FROM ip_bans
WHERE '".Tools::ip_to_unsigned($IP)."' BETWEEN FromIP AND ToIP
LIMIT 1";
$DB->query($sql);
if ($DB->has_results()) {
$IPs[$IP] = true;
if (!isset($IPBanChecks[$IP])) {
if (Tools::site_ban_ip($IP)) {
$IPBanChecks[$IP] = true;
?>
<strong>[Banned]</strong>
<?
} else {
$IPs[$IP] = false;
$IPBanChecks[$IP] = false;
?>
<a id="<?=$counter?>" href="#" onclick="Ban('<?=$IP?>', '<?=$ID?>', '<?=$counter?>'); this.onclick = null; return false;" class="brackets">Ban</a>
<a id="<?=$Counter?>" href="#" onclick="Ban('<?=$IP?>', '<?=$Counter?>'); this.onclick = null; return false;" class="brackets">Ban</a>
<?
}
$counter++;
$Counter++;
}
}
?>
<br />
<?=Tools::get_host_by_ajax($IP)?>
<?=($HasDupe ? '<a href="#" onclick="ShowIPs('.$Index.'); return false;">('.count($UserIDs).')</a>' : '(0)')?>
<?
if (!empty($OtherUsers)) {
if ($FirstOccurrence || count($OtherUsers) <= 100) {
?>
<a href="#" onclick="$('.otherusers' + <?=$Index?>).gtoggle(); return false;">(<?=count($OtherUsers)?>)</a>
<?
} else {
?>
<a href="#<?=$ElementID?>" onclick="$('.otherusers' + <?=$IPIndexes[$IP]?>).gshow();">(<?=count($OtherUsers)?>)</a>
<?
}
} else {
?>
(0)
<?
}
?>
</td>
<td>
<span class="reltime"><?=time_diff($StartTime)?></span>
<span class="abstime hidden"><?=$StartTime?></span>
</td>
<td>
<span class="reltime"><?=time_diff($EndTime)?></span>
<span class="abstime hidden"><?=$EndTime?></span>
</td>
<td><?=time_diff($StartTime)?></td>
<td class="hidden"><?=$StartTime?></td>
<td><?=time_diff($EndTime)?></td>
<td class="hidden"><?=$EndTime?></td>
<td><?//time_diff(strtotime($StartTime), strtotime($EndTime)); ?></td>
</tr>
<?
if ($HasDupe) {
$HideMe = (count($UserIDs) > 10);
foreach ($UserIDs as $Key => $Val) {
if (!$UserEndTimes[$Key]) {
$UserEndTimes[$Key] = sqltime();
if (!empty($OtherUsers) && ($FirstOccurrence || count($OtherUsers) < 100)) {
$HideMe = (count($OtherUsers) > 10);
foreach ($OtherUsers as $OtherUser) {
if (!$OtherUser['EndTime']) {
$OtherUser['EndTime'] = sqltime();
}
?>
<tr class="rowb<?=($HideMe ? ' hidden' : '')?>" name="<?=$Index?>">
<td>&nbsp;&nbsp;&#187;&nbsp;<?=Users::format_username($Val, true, true, true)?></td>
<td><?=time_diff($UserStartTimes[$Key])?></td>
<td class="hidden"><?=$UserStartTimes[$Key]?></td>
<td><?=time_diff($UserEndTimes[$Key])?></td>
<td class="hidden"><?=$UserEndTimes[$Key]?></td>
<td><?//time_diff(strtotime($UserStartTimes[$Key]), strtotime($UserEndTimes[$Key])); ?></td>
<tr class="rowb otherusers<?=$Index?><?=($HideMe ? ' hidden' : '')?>">
<td>&nbsp;&nbsp;&#187;&nbsp;<?=Users::format_username($OtherUser['UserID'], true, true, true)?></td>
<td>
<span class="reltime"><?=time_diff($OtherUser['StartTime'])?></span>
<span class="hidden abstime"><?=$OtherUser['StartTime']?></span>
</td>
<td>
<span class="reltime"><?=time_diff($OtherUser['EndTime'])?></span>
<span class="hidden abstime"><?=$OtherUser['EndTime']?></span>
</td>
<td><?//time_diff(strtotime($OtherUser['StartTime']), strtotime($OtherUser['EndTime'])); ?></td>
</tr>
<?
}
if (isset($IPMatchesIgnored[$IP])) {
foreach ($IPMatchesIgnored[$IP] as $OtherUserID => $MatchCount) {
?>
<tr class="rowb otherusers<?=$Index?><?=($HideMe ? ' hidden' : '')?>">
<td colspan="4">&nbsp;&nbsp;&#187;&nbsp;<?=$MatchCount?> matches skipped for <?=Users::format_username($OtherUserID, false, false, false)?></td>
</tr>
<?
}
}
}
}