mirror of
https://github.com/WhatCD/Gazelle.git
synced 2024-12-12 18:36:29 +00:00
Empty commit
This commit is contained in:
parent
4b19c58200
commit
96f89020d0
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -1,24 +1,67 @@
|
||||
<?
|
||||
class Misc {
|
||||
/**
|
||||
* Send an email.
|
||||
*
|
||||
* @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') {
|
||||
$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";
|
||||
$Headers .= 'Reply-To: '.$From.'@'.NONSSL_SITE_URL."\r\n";
|
||||
$Headers .= 'X-Mailer: Project Gazelle'."\r\n";
|
||||
$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);
|
||||
}
|
||||
/**
|
||||
* 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, $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";
|
||||
$Headers .= 'Reply-To: '.$From.'@'.NONSSL_SITE_URL."\r\n";
|
||||
$Headers .= 'X-Mailer: Project Gazelle'."\r\n";
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -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(
|
||||
'('=>'\\\\(',
|
||||
')'=>'\\\\)',
|
||||
'|'=>'\\\\|',
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -197,30 +197,34 @@ public static function get_aliases() {
|
||||
public static function remove_aliases($Tags) {
|
||||
$TagAliases = self::get_aliases();
|
||||
|
||||
$End = count($Tags['include']);
|
||||
for ($i = 0; $i < $End; $i++) {
|
||||
foreach ($TagAliases as $TagAlias) {
|
||||
if ($Tags['include'][$i] === $TagAlias['BadTag']) {
|
||||
$Tags['include'][$i] = $TagAlias['AliasTag'];
|
||||
break;
|
||||
if (isset($Tags['include'])) {
|
||||
$End = count($Tags['include']);
|
||||
for ($i = 0; $i < $End; $i++) {
|
||||
foreach ($TagAliases as $TagAlias) {
|
||||
if ($Tags['include'][$i] === $TagAlias['BadTag']) {
|
||||
$Tags['include'][$i] = $TagAlias['AliasTag'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Only keep unique entries after unifying tag standard
|
||||
$Tags['include'] = array_unique($Tags['include']);
|
||||
}
|
||||
|
||||
$End = count($Tags['exclude']);
|
||||
for ($i = 0; $i < $End; $i++) {
|
||||
foreach ($TagAliases as $TagAlias) {
|
||||
if (substr($Tags['exclude'][$i], 1) === $TagAlias['BadTag']) {
|
||||
$Tags['exclude'][$i] = '!'.$TagAlias['AliasTag'];
|
||||
break;
|
||||
if (isset($Tags['exclude'])) {
|
||||
$End = count($Tags['exclude']);
|
||||
for ($i = 0; $i < $End; $i++) {
|
||||
foreach ($TagAliases as $TagAlias) {
|
||||
if (substr($Tags['exclude'][$i], 1) === $TagAlias['BadTag']) {
|
||||
$Tags['exclude'][$i] = '!'.$TagAlias['AliasTag'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Only keep unique entries after unifying tag standard
|
||||
$Tags['exclude'] = array_unique($Tags['exclude']);
|
||||
}
|
||||
|
||||
// Only keep unique entries after unifying tag standard
|
||||
$Tags['include'] = array_unique($Tags['include']);
|
||||
$Tags['exclude'] = array_unique($Tags['exclude']);
|
||||
|
||||
return $Tags;
|
||||
}
|
||||
|
||||
|
@ -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,12 +157,23 @@ 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.
|
||||
*
|
||||
|
@ -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>
|
||||
<?
|
||||
}
|
||||
|
769
classes/torrentsearch.class.php
Normal file
769
classes/torrentsearch.class.php
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
BIN
ocelot-1.0.tar.gz
Normal file
Binary file not shown.
@ -77,7 +77,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
json_die("success", array(
|
||||
json_print("success", array(
|
||||
'announcements' => $JsonAnnouncements,
|
||||
'blogPosts' => $JsonBlog
|
||||
));
|
||||
|
@ -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,
|
||||
|
@ -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']
|
||||
);
|
||||
'id' => (int)$Artist['id'],
|
||||
'name' => $Artist['name'],
|
||||
'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(
|
||||
'currentPage' => intval($Page),
|
||||
'pages' => ceil($NumResults / TORRENTS_PER_PAGE),
|
||||
'results' => $JsonGroups)));
|
||||
json_print('success', array(
|
||||
'currentPage' => intval($Page),
|
||||
'pages' => ceil($NumResults / TORRENTS_PER_PAGE),
|
||||
'results' => $JsonGroups));
|
||||
|
@ -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'],
|
||||
|
@ -37,4 +37,4 @@
|
||||
);
|
||||
}
|
||||
|
||||
json_die('success', json_encode($NewsResponse));
|
||||
json_print('success', json_encode($NewsResponse));
|
||||
|
@ -99,7 +99,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
json_die("success", array(
|
||||
json_print("success", array(
|
||||
'currentPages' => intval($Page),
|
||||
'pages' => ceil($TorrentCount / NOTIFICATIONS_PER_PAGE),
|
||||
'numNew' => $NumNew,
|
||||
|
@ -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'],
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
));
|
||||
?>
|
||||
|
@ -86,7 +86,7 @@
|
||||
$JsonPosts[] = $JsonPost;
|
||||
}
|
||||
|
||||
json_die('success', array(
|
||||
json_print('success', array(
|
||||
'threads' => $JsonPosts
|
||||
));
|
||||
?>
|
||||
|
@ -31,7 +31,7 @@
|
||||
);
|
||||
}
|
||||
|
||||
json_die("success", array(
|
||||
json_print("success", array(
|
||||
'page' => (int)$Page,
|
||||
'pages' => ceil($NumComments / TORRENT_COMMENTS_PER_PAGE),
|
||||
'comments' => $JsonComments
|
||||
|
@ -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)));
|
||||
|
@ -118,4 +118,4 @@
|
||||
);
|
||||
}
|
||||
|
||||
json_die("success", array('group' => $JsonTorrentDetails, 'torrents' => $JsonTorrentList));
|
||||
json_print("success", array('group' => $JsonTorrentDetails, 'torrents' => $JsonTorrentList));
|
||||
|
@ -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)
|
||||
)
|
||||
));
|
||||
?>
|
||||
|
@ -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;
|
||||
|
@ -51,7 +51,7 @@
|
||||
);
|
||||
}
|
||||
|
||||
json_die("success", array(
|
||||
json_print("success", array(
|
||||
'currentPage' => (int)$Page,
|
||||
'pages' => ceil($NumResults / USERS_PER_PAGE),
|
||||
'results' => $JsonUsers
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
$ReleaseTypeName = $ReleaseTypes[$Download['ReleaseType']];
|
||||
} elseif ($Releases[$GroupID]['Importance'] == 2) {
|
||||
$ReleaseTypeName = 'Guest Appearance';
|
||||
} elseif ($Releases[$GroupID]['Importance'] == 3) {
|
||||
$ReleaseTypeName = 'Remixed By';
|
||||
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']];
|
||||
break;
|
||||
case '2':
|
||||
$ReleaseTypeName = 'Guest Appearance';
|
||||
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);
|
||||
|
@ -30,21 +30,22 @@
|
||||
// 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
|
||||
|
||||
// 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'));
|
||||
|
||||
|
@ -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");
|
||||
?>
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -35,6 +35,7 @@
|
||||
case 'lastfm':
|
||||
include(SERVER_ROOT.'/sections/top10/lastfm.php');
|
||||
break;
|
||||
|
||||
default:
|
||||
error(404);
|
||||
break;
|
||||
|
@ -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&order_by=$SortKey&".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'); ?> />
|
||||
<input type="search" size="40" id="tags" name="taglist" class="inputtext smaller" value="<?=display_str($Search->get_terms('taglist'))?>"<? Users::has_autocomplete_enabled('other'); ?> />
|
||||
<input type="radio" name="tags_type" id="tags_type0" value="0"<?Format::selected('tags_type', 0, 'checked')?> /><label for="tags_type0"> Any</label>
|
||||
<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<? } ?>'" />
|
||||
|
||||
<? 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'];
|
||||
}
|
||||
$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);
|
||||
|
@ -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
|
||||
|
@ -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')) {
|
||||
|
@ -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("
|
||||
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
|
||||
LIMIT $Limit");
|
||||
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
|
||||
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&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&userid=<?=$UserID?>&usersonly=1" class="brackets">View IP addresses with users</a>
|
||||
<a href="userhistory.php?<?=Format::get_url()?>&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> » <?=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> » <?=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"> » <?=$MatchCount?> matches skipped for <?=Users::format_username($OtherUserID, false, false, false)?></td>
|
||||
</tr>
|
||||
<?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user