diff --git a/classes/config.template b/classes/config.template
index 8c837351..e46e3c66 100644
--- a/classes/config.template
+++ b/classes/config.template
@@ -25,7 +25,7 @@ define('SQLLOGIN', '');//The MySQL login
define('SQLPASS', ''); //The MySQL password
define('SQLDB', 'gazelle'); //The MySQL database to use
define('SQLPORT', 3306); //The MySQL port to connect on
-define('SQLSOCK', '/var/run/mysqld/mysql.sock');
+define('SQLSOCK', '/var/run/mysqld/mysqld.sock');
// Memcached details
$MemcachedServers = array(
@@ -58,12 +58,13 @@ if (!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 80) {
// Site settings
if (PHP_VERSION_ID < 50307) {
die("PHP version 5.3.7 or later is highly recommended for gazelle's password hashing methods.".
- " Uncomment line ".__LINE__." in file ".__FILE__." if you want to continue to use this version.");
+ " Comment out line ".__LINE__." in file ".__FILE__." if you want to continue to use this version.");
define('CRYPT_HASH_PREFIX', '$2a$07$'); // Crypt salt prefix for hash settings. See http://php.net/crypt for details
} else {
define('CRYPT_HASH_PREFIX', '$2y$07$');
}
define('DEBUG_MODE', false); //Set to false if you dont want everyone to see debug information, can be overriden with 'site_debug'
+define('DEBUG_WARNINGS', true); //Set to true if you want to see PHP warnings in the footer
define('OPEN_REGISTRATION', true); //Set to false to disable open regirstration, true to allow anyone to register
define('USER_LIMIT', 5000); //The maximum number of users the site can have, 0 for no limit
define('STARTING_INVITES', 0); //# of invites to give to newly registered users
@@ -149,7 +150,7 @@ $Formats = array('MP3', 'FLAC', 'Ogg Vorbis', 'AAC', 'AC3', 'DTS');
$Bitrates = array('192', 'APS (VBR)', 'V2 (VBR)', 'V1 (VBR)', '256', 'APX (VBR)', 'V0 (VBR)', 'q8.x (VBR)', '320', 'Lossless', '24bit Lossless', 'Other');
$Media = array('CD', 'DVD', 'Vinyl', 'Soundboard', 'SACD', 'DAT', 'Cassette', 'WEB');
-$CollageCats = array(0=>'Personal', 1=>'Theme', 2=>'Genre introduction', 3=>'Discography', 4=>'Label', 5=>'Staff picks', 6=>'Charts');
+$CollageCats = array(0=>'Personal', 1=>'Theme', 2=>'Genre introduction', 3=>'Discography', 4=>'Label', 5=>'Staff picks', 6=>'Charts', 7=>'Artists');
$ReleaseTypes = array(1=>'Album', 3=>'Soundtrack', 5=>'EP', 6=>'Anthology', 7=>'Compilation', 9=>'Single', 11=>'Live album', 13=>'Remix', 14=>'Bootleg', 15=>'Interview', 16=>'Mixtape', 21=>'Unknown');
//$ForumCats = array(1=>'Site', 5=>'Community', 10=>'Help', 8=>'Music', 20=>'Trash'); //No longer needed
diff --git a/classes/debug.class.php b/classes/debug.class.php
index e998a30e..e2a461a7 100644
--- a/classes/debug.class.php
+++ b/classes/debug.class.php
@@ -203,12 +203,9 @@ public function php_error_handler($Level, $Error, $File, $Line) {
$File = str_replace(SERVER_ROOT, '', $File);
$Error = str_replace(SERVER_ROOT, '', $Error);
- /*
- //Hiding "session_start(): Server 10.10.0.1 (tcp 11211) failed with: No route to host (113)" errors
- if ($Call != "session_start") {
+ if (defined('DEBUG_WARNINGS')) {
$this->Errors[] = array($Error, $File.':'.$Line, $Call, $Args);
}
- */
return true;
}
diff --git a/classes/donations.class.php b/classes/donations.class.php
index 22251017..a1e78867 100644
--- a/classes/donations.class.php
+++ b/classes/donations.class.php
@@ -289,7 +289,7 @@ public static function has_donor_forum($UserID) {
public static function is_mod($UserID) {
$Permissions = Permissions::get_permissions_for_user($UserID);
- return $Permissions['users_mod'];
+ return isset($Permissions['users_mod']) && $Permissions['users_mod'];
}
@@ -376,9 +376,21 @@ public static function get_enabled_rewards($UserID) {
$SpecialRank = self::get_special_rank($UserID);
$HasAll = $SpecialRank == 3;
- if ($Rank >= 1 || $HasAll) {
+ $Rewards = array(
+ 'HasAvatarMouseOverText' => false,
+ 'HasCustomDonorIcon' => false,
+ 'HasDonorForum' => false,
+ 'HasDonorIconLink' => false,
+ 'HasDonorIconMouseOverText' => false,
+ 'HasProfileInfo1' => false,
+ 'HasProfileInfo2' => false,
+ 'HasProfileInfo3' => false,
+ 'HasProfileInfo4' => false,
+ 'HasSecondAvatar' => false);
- }
+// if ($Rank >= 1 || $HasAll) {
+//
+// }
if ($Rank >= 2 || $HasAll) {
$Rewards["HasDonorIconMouseOverText"] = true;
$Rewards["HasProfileInfo1"] = true;
diff --git a/classes/format.class.php b/classes/format.class.php
index 93d80376..9c852207 100644
--- a/classes/format.class.php
+++ b/classes/format.class.php
@@ -127,7 +127,7 @@ public static function get_ratio_html($Dividend, $Divisor, $Color = true) {
* @param int $Decimal floor to n decimals (e.g. subtract .005 to floor to 2 decimals)
* @return boolean|string
*/
- public function get_ratio ($Dividend, $Divisor, $Decimal = 2) {
+ public static function get_ratio($Dividend, $Divisor, $Decimal = 2) {
if ($Divisor == 0 && $Dividend == 0) {
return false;
}
diff --git a/classes/forums.class.php b/classes/forums.class.php
index effb8636..54b74a5a 100644
--- a/classes/forums.class.php
+++ b/classes/forums.class.php
@@ -12,9 +12,10 @@ class Forums {
* @return array holding thread information.
*/
public static function get_thread_info($ThreadID, $Return = true, $SelectiveCache = false) {
- if ((!$ThreadInfo = G::$Cache->get_value('thread_' . $ThreadID . '_info')) || !isset($ThreadInfo['OP'])) {
+ if ((!$ThreadInfo = G::$Cache->get_value('thread_' . $ThreadID . '_info')) || !isset($ThreadInfo['Ranking'])) {
$QueryID = G::$DB->get_query_id();
- G::$DB->query("SELECT
+ G::$DB->query(
+ "SELECT
t.Title,
t.ForumID,
t.IsLocked,
@@ -23,10 +24,11 @@ public static function get_thread_info($ThreadID, $Return = true, $SelectiveCach
t.LastPostAuthorID,
ISNULL(p.TopicID) AS NoPoll,
t.StickyPostID,
- t.AuthorID as OP
+ t.AuthorID as OP,
+ t.Ranking
FROM forums_topics AS t
- JOIN forums_posts AS fp ON fp.TopicID = t.ID
- LEFT JOIN forums_polls AS p ON p.TopicID=t.ID
+ JOIN forums_posts AS fp ON fp.TopicID = t.ID
+ LEFT JOIN forums_polls AS p ON p.TopicID=t.ID
WHERE t.ID = '$ThreadID'
GROUP BY fp.TopicID");
if (G::$DB->record_count() == 0) {
@@ -35,17 +37,19 @@ public static function get_thread_info($ThreadID, $Return = true, $SelectiveCach
$ThreadInfo = G::$DB->next_record(MYSQLI_ASSOC, false);
if ($ThreadInfo['StickyPostID']) {
$ThreadInfo['Posts']--;
- G::$DB->query("SELECT
- p.ID,
- p.AuthorID,
- p.AddedTime,
- p.Body,
- p.EditedUserID,
- p.EditedTime,
- ed.Username
- FROM forums_posts as p
- LEFT JOIN users_main AS ed ON ed.ID = p.EditedUserID
- WHERE p.TopicID = '$ThreadID' AND p.ID = '" . $ThreadInfo['StickyPostID'] . "'");
+ G::$DB->query(
+ "SELECT
+ p.ID,
+ p.AuthorID,
+ p.AddedTime,
+ p.Body,
+ p.EditedUserID,
+ p.EditedTime,
+ ed.Username
+ FROM forums_posts as p
+ LEFT JOIN users_main AS ed ON ed.ID = p.EditedUserID
+ WHERE p.TopicID = '$ThreadID'
+ AND p.ID = '" . $ThreadInfo['StickyPostID'] . "'");
list ($ThreadInfo['StickyPost']) = G::$DB->to_array(false, MYSQLI_ASSOC);
}
G::$DB->set_query_id($QueryID);
@@ -69,7 +73,7 @@ public static function get_thread_info($ThreadID, $Return = true, $SelectiveCach
*/
public static function check_forumperm($ForumID, $Perm = 'Read') {
$Forums = self::get_forums();
- if (G::$LoggedUser['CustomForums'][$ForumID] == 1) {
+ if (isset(G::$LoggedUser['CustomForums'][$ForumID]) && G::$LoggedUser['CustomForums'][$ForumID] == 1) {
return true;
}
if ($ForumID == DONOR_FORUM && Donations::has_donor_forum(G::$LoggedUser['ID'])) {
diff --git a/classes/lastfm.class.php b/classes/lastfm.class.php
index 424ba052..e76c229f 100644
--- a/classes/lastfm.class.php
+++ b/classes/lastfm.class.php
@@ -93,6 +93,11 @@ public static function get_top_tracks($Username, $Limit = 15) {
return $Response;
}
+ public static function get_artist_chart($Username, $From = '', $To = '') {
+ $Response = self::lastfm_request("user.getWeeklyArtistChart", array("user" => $Username));
+ $Response = json_encode($Response);
+ }
+
public static function clear_cache($Username, $Uid) {
$Response = G::$Cache->get_value('lastfm_clear_cache_' . G::$LoggedUser['ID'] . '_' . $_GET['id']);
if (empty($Response)) {
diff --git a/classes/permissions.class.php b/classes/permissions.class.php
index d1b00893..294dda27 100644
--- a/classes/permissions.class.php
+++ b/classes/permissions.class.php
@@ -77,10 +77,8 @@ public static function get_permissions_for_user($UserID, $CustomPermissions = fa
$BonusPerms = array_merge($BonusPerms, $ClassPerms['Permissions']);
}
- if (!empty($CustomPermissions)) {
- $CustomPerms = $CustomPermissions;
- } else {
- $CustomPerms = array();
+ if (empty($CustomPermissions)) {
+ $CustomPermissions = array();
}
// This is legacy donor cruft
@@ -90,18 +88,20 @@ public static function get_permissions_for_user($UserID, $CustomPermissions = fa
$DonorPerms = array('Permissions' => array());
}
- $DonorCollages = self::get_personal_collages($UserID, $Permissions['Permissions']['users_mod']);
+ $IsMod = isset($Permissions['Permissions']['users_mod']) && $Permissions['Permissions']['users_mod'];
+ $DonorCollages = self::get_personal_collages($UserID, $IsMod);
- $MaxCollages = $Permissions['Permissions']['MaxCollages']
- + $BonusCollages
- + $CustomPerms['MaxCollages']
- + $DonorCollages;
+ $MaxCollages = $Permissions['Permissions']['MaxCollages'] + $BonusCollages + $DonorCollages;
+
+ if (isset($CustomPermissions['MaxCollages'])) {
+ $MaxCollages += $CustomPermissions['MaxCollages'];
+ }
//Combine the permissions
return array_merge(
$Permissions['Permissions'],
$BonusPerms,
- $CustomPerms,
+ $CustomPermissions,
$DonorPerms['Permissions'],
array('MaxCollages' => $MaxCollages));
}
diff --git a/classes/sphinxql.class.php b/classes/sphinxql.class.php
index b652e2c9..51f9b888 100644
--- a/classes/sphinxql.class.php
+++ b/classes/sphinxql.class.php
@@ -26,7 +26,7 @@ public function __construct($Server, $Port, $Socket) {
$this->Server = $Server;
$this->Port = $Port;
$this->Socket = $Socket;
- $this->Ident = $this->get_ident($Server, $Port, $Socket);
+ $this->Ident = self::get_ident($Server, $Port, $Socket);
}
/**
@@ -37,7 +37,7 @@ public function __construct($Server, $Port, $Socket) {
* @param string $Socket Unix socket address, overrides $Server:$Port
* @return identification string
*/
- private function get_ident($Server, $Port, $Socket) {
+ private static function get_ident($Server, $Port, $Socket) {
if ($Socket) {
return $Socket;
} else {
@@ -64,7 +64,7 @@ public static function init_connection($Server, $Port, $Socket) {
/**
* Connect the Sphinxql object to the Sphinx server
*/
- public function sphconnect() {
+ public function sph_connect() {
if ($this->Connected || $this->connect_errno) {
return;
}
@@ -114,7 +114,7 @@ public function error($Msg, $Halt = false) {
* @param string $String string to escape
* @return escaped string
*/
- public function escape_string($String) {
+ public static function sph_escape_string($String) {
return strtr($String, array(
'('=>'\\\\(',
')'=>'\\\\)',
@@ -141,8 +141,8 @@ public function escape_string($String) {
* @param string $QueryString query text
* @param param $QueryProcessTime time building and processing the query
*/
- public function register_query($QueryString, $QueryProcessTime) {
- Sphinxql::$Queries[] = array($QueryString, $QueryProcessTime);
- Sphinxql::$Time += $QueryProcessTime;
+ public static function register_query($QueryString, $QueryProcessTime) {
+ self::$Queries[] = array($QueryString, $QueryProcessTime);
+ self::$Time += $QueryProcessTime;
}
}
diff --git a/classes/sphinxqlquery.class.php b/classes/sphinxqlquery.class.php
index bb666c16..d948ca45 100644
--- a/classes/sphinxqlquery.class.php
+++ b/classes/sphinxqlquery.class.php
@@ -116,7 +116,7 @@ public function where_match($Expr, $Field = '*', $Escape = true) {
$Field = "@$Field ";
}
if ($Escape === true) {
- $this->Expressions[] = "$Field".Sphinxql::escape_string($Expr);
+ $this->Expressions[] = "$Field".Sphinxql::sph_escape_string($Expr);
} else {
$this->Expressions[] = $Field.$Expr;
}
@@ -282,13 +282,15 @@ private function send_query($GetMeta) {
if (!$this->QueryString) {
return false;
}
- $this->Sphinxql->sphconnect();
+ $this->Sphinxql->sph_connect();
$Result = $this->Sphinxql->query($this->QueryString);
if ($Result === false) {
$Errno = $this->Sphinxql->errno;
$Error = $this->Sphinxql->error;
$this->error("Query returned error $Errno ($Error).\n$this->QueryString");
} else {
+ $Errno = 0;
+ $Error = '';
$Meta = $GetMeta ? $this->get_meta() : null;
}
return new SphinxqlResult($Result, $Meta, $Errno, $Error);
diff --git a/classes/tags.class.php b/classes/tags.class.php
index 0fe8fdcf..b25ba072 100644
--- a/classes/tags.class.php
+++ b/classes/tags.class.php
@@ -56,13 +56,17 @@ class Tags {
* E.g., compilations and soundtracks are skipped, so false
*/
public function __construct($TagList, $Merge = true) {
- $this->Tags = array_filter(explode(' ', str_replace('_', '.', $TagList)));
+ if ($TagList) {
+ $this->Tags = array_filter(explode(' ', str_replace('_', '.', $TagList)));
- if ($Merge) {
- self::$All = array_merge(self::$All, $this->Tags);
+ if ($Merge) {
+ self::$All = array_merge(self::$All, $this->Tags);
+ }
+
+ $this->Primary = $this->Tags[0];
+ } else {
+ $this->Tags = array();
}
-
- $this->Primary = $this->Tags[0];
}
/**
diff --git a/classes/users.class.php b/classes/users.class.php
index 1cc612b7..df6b6af6 100644
--- a/classes/users.class.php
+++ b/classes/users.class.php
@@ -631,8 +631,7 @@ public static function show_avatar($Avatar, $UserID, $Username, $Setting, $Size
$ToReturn = ($ReturnHTML ? "" : $Avatar);
} else {
$URL = STATIC_SERVER.'common/avatars/default.png';
- //TODO: what is the $JS variable for? why is it unassigned?
- $ToReturn = ($ReturnHTML ? "" : $URL);
+ $ToReturn = ($ReturnHTML ? "" : $URL);
}
break;
case 2:
diff --git a/classes/view.class.php b/classes/view.class.php
index 5f8ec9d9..047e3a5c 100644
--- a/classes/view.class.php
+++ b/classes/view.class.php
@@ -41,7 +41,7 @@ public static function show_header($PageTitle = '', $JSIncludes = '', $CSSInclud
* Here is a list of parameters that work in the $Options array:
* ['disclaimer'] = [boolean] (False) Displays the disclaimer in the footer
*/
- public static function show_footer ($Options = array()) {
+ public static function show_footer($Options = array()) {
global $ScriptStartTime, $SessionID, $UserSessions, $Debug, $Time;
if (!is_array(G::$LoggedUser)) {
require(SERVER_ROOT.'/design/publicfooter.php');
@@ -65,7 +65,7 @@ public static function show_footer ($Options = array()) {
* @param string $TemplateName The name of the template, in underscore_format
* @param array $Args the arguments passed to the template.
*/
- public static function render_template ($TemplateName, $Args) {
+ public static function render_template($TemplateName, $Args) {
static $LoadedTemplates; // Keep track of templates we've already loaded.
$ClassName = '';
if (isset($LoadedTemplates[$TemplateName])) {
@@ -113,7 +113,7 @@ public static function render_template ($TemplateName, $Args) {
* echo $SavedTemplate; // Output the buffer
*
*/
- public static function parse ($TemplateFile, array $Variables = null, $Buffer = false) {
+ public static function parse($TemplateFile, array $Variables = array(), $Buffer = false) {
$Template = self::IncludePath . $TemplateFile;
if (file_exists($Template)) {
extract($Variables);
diff --git a/docs/INSTALL.txt b/docs/INSTALL.txt
index 495c3447..1d974ca5 100644
--- a/docs/INSTALL.txt
+++ b/docs/INSTALL.txt
@@ -1,12 +1,12 @@
INSTALLATION NOTES
-1. Set up MySQL and memcached. We run memcached with the command:
- memcached -d -m 5120 -s /var/run/memcached.sock -a 0777 -t16 -C -u root
- This gives it 5 gigs of RAM; you probably want to set that a bit lower!
+1. Set up MySQL and memcached. memcached can be started with the command:
+ memcached -d -m 5120 -s /var/run/memcached.sock -a 0777 -t4 -C -u nobody
+ This starts 4 threads and gives it 5 gigs of RAM
2. Run gazelle.sql (preferably as root) to create the database, the table, and the default data.
3. Install Sphinx - we recommend you use the included sphinx.conf. You can copy this to
/etc/sphinx/sphinx.conf. You need to fill in the details of the SQL server though!
- You might also need to create the /var/data/sphinx folder.
+ You might also need to create the /var/lib/sphinx folder.
For documentation, read http://www.sphinxsearch.com/docs/current.html
@@ -21,10 +21,10 @@ the peerupdate (all groups are cached, but the peer counts change often,
so peerupdate is a script to update them), and the two Sphinx indices.
These are our cron jobs. SCHEDULE_KEY is the same as in classes/config.php:
-0,15,30,45 * * * * /usr/local/bin/php /var/www/vhosts/what/schedule.php SCHEDULE_KEY >> /root/schedule.log
-10,25,40,55 * * * * /usr/local/bin/php /var/www/vhosts/what/peerupdate.php SCHEDULE_KEY >> /root/peerupdate.log
-* * * * * /usr/local/bin/indexer -c /etc/sphinx/sphinx.conf --rotate delta
-5 0,12 * * * /usr/local/bin/indexer -c /etc/sphinx/sphinx.conf --rotate --all
+0,15,30,45 * * * * /usr/bin/php /var/www/vhosts/what/schedule.php SCHEDULE_KEY >> /root/schedule.log
+10,25,40,55 * * * * /usr/bin/php /var/www/vhosts/what/peerupdate.php SCHEDULE_KEY >> /root/peerupdate.log
+* * * * * /usr/bin/indexer -c /etc/sphinx/sphinx.conf --rotate delta requests_delta log_delta >/dev/null
+5 0,12 * * * /usr/bin/indexer -c /etc/sphinx/sphinx.conf --rotate --all >>/root/sphinx-indexer.log
7. You're probably going to want IP geolocation information, so first you need to fill in the geoip_country tables by visiting /tools.php?action=update_geoip
After that finishes parsing information from MaxMind, you may want to map users to countries by running:
diff --git a/sections/ajax/browse.php b/sections/ajax/browse.php
index 00e98f1c..20e93ce0 100644
--- a/sections/ajax/browse.php
+++ b/sections/ajax/browse.php
@@ -164,7 +164,7 @@ function header_link($SortKey, $DefaultWay = 'desc') {
if (!empty($_GET['filelist'])) {
$SearchString = trim($_GET['filelist']);
if ($SearchString !== '') {
- $SearchString = '"'.Sphinxql::escape_string($_GET['filelist']).'"~20';
+ $SearchString = '"'.Sphinxql::sph_escape_string($_GET['filelist']).'"~20';
$SphQL->where_match($SearchString, 'filelist', false);
$SphQLTor->where_match($SearchString, 'filelist', false);
$EnableNegation = true;
@@ -244,11 +244,11 @@ function header_link($SortKey, $DefaultWay = 'desc') {
}
$QueryParts = array();
foreach ($BasicSearch['include'] as $Word) {
- $QueryParts[] = Sphinxql::escape_string($Word);
+ $QueryParts[] = Sphinxql::sph_escape_string($Word);
}
if (!empty($BasicSearch['exclude'])) {
foreach ($BasicSearch['exclude'] as $Word) {
- $QueryParts[] = '!'.Sphinxql::escape_string(substr($Word, 1));
+ $QueryParts[] = '!'.Sphinxql::sph_escape_string(substr($Word, 1));
}
}
if (!empty($FilterBitrates)) {
@@ -280,11 +280,11 @@ function header_link($SortKey, $DefaultWay = 'desc') {
unset($Tags['exclude']);
}
foreach ($Tags['include'] as &$Tag) {
- $Tag = Sphinxql::escape_string($Tag);
+ $Tag = Sphinxql::sph_escape_string($Tag);
}
if (!empty($Tags['exclude'])) {
foreach ($Tags['exclude'] as &$Tag) {
- $Tag = '!'.Sphinxql::escape_string(substr($Tag, 1));
+ $Tag = '!'.Sphinxql::sph_escape_string(substr($Tag, 1));
}
}
@@ -307,6 +307,7 @@ function header_link($SortKey, $DefaultWay = 'desc') {
}
if (!empty($QueryParts)) {
$SphQL->where_match(implode(' ', $QueryParts), 'taglist', false);
+ $SphQLTor->where_match(implode(' ', $QueryParts), 'taglist', false);
$Filtered = true;
}
unset($SearchWords['taglist']);
@@ -321,11 +322,11 @@ function header_link($SortKey, $DefaultWay = 'desc') {
unset($Words['exclude']);
}
foreach ($Words['include'] as $Word) {
- $QueryParts[] = Sphinxql::escape_string($Word);
+ $QueryParts[] = Sphinxql::sph_escape_string($Word);
}
if (!empty($Words['exclude'])) {
foreach ($Words['exclude'] as $Word) {
- $QueryParts[] = '!'.Sphinxql::escape_string(substr($Word, 1));
+ $QueryParts[] = '!'.Sphinxql::sph_escape_string(substr($Word, 1));
}
}
if (!empty($QueryParts)) {
@@ -390,7 +391,7 @@ function header_link($SortKey, $DefaultWay = 'desc') {
}
}
-if (!empty($_GET['freetorrent'])) {
+if (isset($_GET['freetorrent'])) {
switch ($_GET['freetorrent']) {
case 0: // Only normal freeleech
$SphQL->where('freetorrent', 0);
@@ -677,7 +678,7 @@ function header_link($SortKey, $DefaultWay = 'desc') {
'logScore' => (int) $Data['LogScore'],
'hasCue' => $Data['HasCue'] == '1',
'scene' => $Data['Scene'] == '1',
- 'vanityHouse' => $Data['VanityHouse'] == '1',
+ 'vanityHouse' => $GroupInfo['VanityHouse'] == '1',
'fileCount' => (int) $Data['FileCount'],
'time' => $Data['Time'],
'size' => (int) $Data['Size'],
@@ -699,7 +700,7 @@ function header_link($SortKey, $DefaultWay = 'desc') {
'cover' => $GroupInfo['WikiImage'],
'tags' => $TagList,
'bookmarked' => in_array($GroupID, $Bookmarks),
- 'vanityHouse' => $GroupVanityHouse == '1',
+ 'vanityHouse' => $GroupInfo['VanityHouse'] == '1',
'groupYear' => (int) $GroupYear,
'releaseType' => $ReleaseTypes[$ReleaseType],
'groupTime' => (string) $GroupTime,
diff --git a/sections/artist/artist.php b/sections/artist/artist.php
index f55a9490..ec2ecb4d 100644
--- a/sections/artist/artist.php
+++ b/sections/artist/artist.php
@@ -236,7 +236,7 @@ function compare($X, $Y) {
break;
}
- if (!empty($LoggedUser['DiscogView']) || (isset($LoggedUser['SortHide']) && array_key_exists($ReleaseType, $LoggedUser['SortHide']) && $LoggedUser['SortHide'][$ReleaseType] == 1)) {
+ if (!empty($LoggedUser['DiscogView']) || (isset($LoggedUser['SortHide'][$ReleaseID]) && $LoggedUser['SortHide'][$ReleaseID] == 1)) {
$ToggleStr = " onclick=\"$('.releases_$ReleaseID').gshow(); return true;\"";
} else {
$ToggleStr = '';
@@ -402,12 +402,14 @@ function compare($X, $Y) {
} ?>
=$DisplayName?>
- if (Bookmarks::has_bookmarked('torrent', $GroupID)) {
- echo "
Remove bookmark";
- } else {
- echo "
Bookmark";
- } ?>
-
+ if (Bookmarks::has_bookmarked('torrent', $GroupID)) { ?>
+
Remove bookmark
+ } else { ?>
+
Bookmark
+ }
+ $VoteType = isset($UserVotes[$GroupID]['Type']) ? $UserVotes[$GroupID]['Type'] : '';
+ Votes::vote_link($GroupID, $VoteType);
+?>
=$TorrentTags->format('torrents.php?taglist=', $Name)?>
@@ -432,7 +434,7 @@ function compare($X, $Y) {
|| $Torrent['RemasterYear'] != $LastRemasterYear
|| $Torrent['RemasterRecordLabel'] != $LastRemasterRecordLabel
|| $Torrent['RemasterCatalogueNumber'] != $LastRemasterCatalogueNumber
- || $FirstUnknown
+ || isset($FirstUnknown) && $FirstUnknown
|| $Torrent['Media'] != $LastMedia
) {
diff --git a/sections/collages/browse.php b/sections/collages/browse.php
index 7cd7f9ff..7166d5aa 100644
--- a/sections/collages/browse.php
+++ b/sections/collages/browse.php
@@ -148,7 +148,7 @@
$SQL .= " AND CategoryID IN(".db_string(implode(',', $Categories)).')';
}
-if ($_GET['action'] === 'mine') {
+if (isset($_GET['action']) && $_GET['action'] === 'mine') {
$SQL = $BaseSQL;
$SQL .= "
AND c.UserID = '".$LoggedUser['ID']."'
diff --git a/sections/collages/collage.php b/sections/collages/collage.php
index 13e72217..09dc608a 100644
--- a/sections/collages/collage.php
+++ b/sections/collages/collage.php
@@ -70,7 +70,7 @@ function compare($X, $Y) {
WHERE UserID = ".$LoggedUser['ID']."
AND CollageID = $CollageID");
-if ($CollageCategoryID == array_search(ARTIST_COLLAGE, $CollageCats)) {
+if ((int)$CollageCategoryID === array_search(ARTIST_COLLAGE, $CollageCats)) {
include(SERVER_ROOT.'/sections/collages/artist_collage.php');
} else {
include(SERVER_ROOT.'/sections/collages/torrent_collage.php');
diff --git a/sections/index/private.php b/sections/index/private.php
index e8aa2b60..919b45e7 100644
--- a/sections/index/private.php
+++ b/sections/index/private.php
@@ -73,19 +73,15 @@
?>
-if (count($Blog) < 5) {
- $Limit = count($Blog);
-} else {
- $Limit = 5;
-}
-for ($i = 0; $i < $Limit; $i++) {
- list($BlogID, $Author, $Title, $Body, $BlogTime, $ThreadID) = $Blog[$i];
+$End = min(count($Blog), 5);
+for ($i = 0; $i < $End; $i++) {
+ list($BlogID, $Author, $Title, $Body, $BlogTime) = $Blog[$i];
$BlogTime = strtotime($BlogTime);
?>
-
- =($SBlogReadTime < $BlogTime) ? '' : ''?>=($i + 1)?>.
+ =$SBlogReadTime < $BlogTime ? '' : ''?>=($i + 1)?>.
=$Title?>
- =($SBlogReadTime < $BlogTime)?'':''?>
+ =$SBlogReadTime < $BlogTime ? '' : ''?>
}
diff --git a/sections/tools/data/bitcoin_balance.php b/sections/tools/data/bitcoin_balance.php
index 1e9f2e89..f6ccacff 100644
--- a/sections/tools/data/bitcoin_balance.php
+++ b/sections/tools/data/bitcoin_balance.php
@@ -28,7 +28,7 @@
while (list($UserID, $BitcoinAddress) = $DB->next_record(MYSQLI_NUM, false)) {
- if (!$BitcoinAddresses[$BitcoinAddress]) {
+ if (!isset($BitcoinAddresses[$BitcoinAddress])) {
continue;
}
?>
diff --git a/sections/torrents/browse2.php b/sections/torrents/browse2.php
index 4062bb73..4a562c19 100644
--- a/sections/torrents/browse2.php
+++ b/sections/torrents/browse2.php
@@ -139,6 +139,7 @@ function header_link($SortKey, $DefaultWay = 'desc') {
'sumleechers' => 'SUM(leechers) AS sumleechers',
'sumsnatched' => 'SUM(snatched) AS sumsnatched');
} else {
+ $GroupResults = false;
$SortOrders = array(
'year' => 'year',
'time' => 'id',
@@ -208,7 +209,7 @@ function header_link($SortKey, $DefaultWay = 'desc') {
if (!empty($_GET['filelist'])) {
$SearchString = trim($_GET['filelist']);
if ($SearchString != '') {
- $SearchString = '"'.Sphinxql::escape_string($_GET['filelist']).'"~20';
+ $SearchString = '"'.Sphinxql::sph_escape_string($_GET['filelist']).'"~20';
$SphQL->where_match($SearchString, 'filelist', false);
$SphQLTor->where_match($SearchString, 'filelist', false);
$EnableNegation = true;
@@ -296,11 +297,11 @@ function header_link($SortKey, $DefaultWay = 'desc') {
}
$QueryParts = array();
foreach ($BasicSearch['include'] as $Word) {
- $QueryParts[] = Sphinxql::escape_string($Word);
+ $QueryParts[] = Sphinxql::sph_escape_string($Word);
}
if (!empty($BasicSearch['exclude'])) {
foreach ($BasicSearch['exclude'] as $Word) {
- $QueryParts[] = '!'.Sphinxql::escape_string(substr($Word, 1));
+ $QueryParts[] = '!'.Sphinxql::sph_escape_string(substr($Word, 1));
}
}
if (!empty($FilterBitrates)) {
@@ -376,11 +377,11 @@ function header_link($SortKey, $DefaultWay = 'desc') {
unset($Tags['exclude']);
}
foreach ($Tags['include'] as &$Tag) {
- $Tag = Sphinxql::escape_string($Tag);
+ $Tag = Sphinxql::sph_escape_string($Tag);
}
if (!empty($Tags['exclude'])) {
foreach ($Tags['exclude'] as &$Tag) {
- $Tag = '!'.Sphinxql::escape_string(substr($Tag, 1));
+ $Tag = '!'.Sphinxql::sph_escape_string(substr($Tag, 1));
}
}
@@ -405,6 +406,7 @@ function header_link($SortKey, $DefaultWay = 'desc') {
}
if (!empty($QueryParts)) {
$SphQL->where_match(implode(' ', $QueryParts), 'taglist', false);
+ $SphQLTor->where_match(implode(' ', $QueryParts), 'taglist', false);
$Filtered = true;
}
unset($SearchWords['taglist']);
@@ -412,6 +414,9 @@ function header_link($SortKey, $DefaultWay = 'desc') {
elseif (!isset($_GET['tags_type'])) {
$_GET['tags_type'] = '1';
}
+if (!isset($TagListString)) {
+ $TagListString = "";
+}
foreach ($SearchWords as $Search => $Words) {
$QueryParts = array();
@@ -420,11 +425,11 @@ function header_link($SortKey, $DefaultWay = 'desc') {
unset($Words['exclude']);
}
foreach ($Words['include'] as $Word) {
- $QueryParts[] = Sphinxql::escape_string($Word);
+ $QueryParts[] = Sphinxql::sph_escape_string($Word);
}
if (!empty($Words['exclude'])) {
foreach ($Words['exclude'] as $Word) {
- $QueryParts[] = '!'.Sphinxql::escape_string(substr($Word, 1));
+ $QueryParts[] = '!'.Sphinxql::sph_escape_string(substr($Word, 1));
}
}
if (!empty($QueryParts)) {
@@ -490,7 +495,7 @@ function header_link($SortKey, $DefaultWay = 'desc') {
}
}
-if (!empty($_GET['freetorrent']) || $_GET['freetorrent'] === '0') {
+if (isset($_GET['freetorrent'])) {
switch ($_GET['freetorrent']) {
case 0: // Only normal freeleech
$SphQL->where('freetorrent', 0);
@@ -1013,7 +1018,7 @@ function header_link($SortKey, $DefaultWay = 'desc') {
if ($GroupYear > 0) {
$DisplayName .= " [$GroupYear]";
}
- if ($GroupVanityHouse) {
+ if ($GroupInfo['VanityHouse']) {
$DisplayName .= ' [VH]';
}
$DisplayName .= ' ['.$ReleaseTypes[$ReleaseType].']';
diff --git a/sections/torrents/details.php b/sections/torrents/details.php
index 6a957c7d..e2e03c32 100644
--- a/sections/torrents/details.php
+++ b/sections/torrents/details.php
@@ -284,9 +284,9 @@ function compare($X, $Y) {
}
}
- if ((count($Artists[6]) > 0) && (count($Artists[1]) > 0)) {
+ if (!empty($Artists[6]) && !empty($Artists[1])) {
print ' - Artists:
';
- } elseif ((count($Artists[4]) > 0) && (count($Artists[1]) > 0)) {
+ } elseif (!empty($Artists[6]) && !empty($Artists[1])) {
print ' - Performers:
';
}
foreach ($Artists[1] as $Artist) {
@@ -630,8 +630,6 @@ function filelist($Str) {
$ExtraInfo = ''; // String that contains information on the torrent (e.g. format and encoding)
$AddExtra = ''; // Separator between torrent properties
- $TorrentUploader = $Username; // Save this for "Uploaded by:" below
-
// similar to Torrents::torrent_info()
if ($Format) { $ExtraInfo.=display_str($Format); $AddExtra=' / '; }
if ($Encoding) { $ExtraInfo.=$AddExtra.display_str($Encoding); $AddExtra=' / '; }
diff --git a/sections/torrents/functions.php b/sections/torrents/functions.php
index 5aab5515..5fb75c17 100644
--- a/sections/torrents/functions.php
+++ b/sections/torrents/functions.php
@@ -3,16 +3,6 @@ function get_group_info($GroupID, $Return = true, $RevisionID = 0, $PersonalProp
global $Cache, $DB;
if (!$RevisionID) {
$TorrentCache = $Cache->get_value("torrents_details_$GroupID");
-
- // This block can be used to test if the cached data predates structure changes
- if (isset($TorrentCache[0][0])) {
- $OutdatedCache = true;
- } else {
- $Torrent = current($TorrentCache[1]);
- if (!isset($Torrent['InfoHash'])) {
- $OutdatedCache = true;
- }
- }
}
if ($RevisionID || !is_array($TorrentCache) || isset($OutdatedCache)) {
// Fetch the group details
diff --git a/sections/torrents/ranking_funcs.php b/sections/torrents/ranking_funcs.php
index 8f1e2ee3..1226aa68 100644
--- a/sections/torrents/ranking_funcs.php
+++ b/sections/torrents/ranking_funcs.php
@@ -80,7 +80,7 @@ function inverse_ncdf($p) {
}
// Confidence level for binomial scoring. Just compute this once.
-define(Z_VAL, inverse_ncdf(1-(1-.95)/2));
+define('Z_VAL', inverse_ncdf(1-(1-.95)/2));
// Implementation of the algorithm described at http://www.evanmiller.org/how-not-to-sort-by-average-rating.html
function binomial_score($Ups, $Total) {
diff --git a/sphinx.conf b/sphinx.conf
index 39f9e283..497c3e44 100644
--- a/sphinx.conf
+++ b/sphinx.conf
@@ -1,11 +1,8 @@
-# We use /var/data, because sphinx created it on FreeBSD
-# In other distros/operating systems, you may want to use /var/lib/sphinx or /var/lib/sphinxsearch
-
source connect {
type = mysql
sql_host = localhost
sql_port = 3306
- sql_sock = /var/run/mysql/mysql.sock
+ sql_sock = /var/run/mysqld/mysqld.sock
sql_user =
sql_pass =
sql_db = gazelle
@@ -90,12 +87,12 @@ source torrents : torrents_base {
sql_joined_field = artistname from query; \
select t.id, aname from sphinx_a join sphinx_t t using(gid) order by t.id asc;
- sql_query_post = delete from sphinx_delta where time<=unix_timestamp(@starttime)
+ sql_query_post_index = delete from sphinx_delta where time<=unix_timestamp(@starttime)
}
index torrents {
source = torrents
- path = /var/data/sphinx/torrents
+ path = /var/lib/sphinx/torrents
# stopwords = /etc/sphinx/stopwords.txt # Path to file containing a space separated list of words not to index
preopen = 1
morphology = none
@@ -306,7 +303,7 @@ source delta : torrents_base {
index delta : torrents {
source = delta
- path = /var/data/sphinx/delta
+ path = /var/lib/sphinx/delta
}
source requests : connect {
@@ -403,11 +400,11 @@ source requests_delta : requests {
}
index requests : torrents {
source = requests
- path = /var/data/sphinx/requests
+ path = /var/lib/sphinx/requests
}
index requests_delta : requests {
source = requests_delta
- path = /var/data/sphinx/requests_delta
+ path = /var/lib/sphinx/requests_delta
}
source log : connect {
@@ -424,11 +421,11 @@ source log_delta : log {
index log : torrents {
source = log
min_word_len = 1
- path = /var/data/sphinx/log
+ path = /var/lib/sphinx/log
}
index log_delta : log {
source = log_delta
- path = /var/data/sphinx/log_delta
+ path = /var/lib/sphinx/log_delta
}
source better_transcode : connect {
@@ -458,7 +455,7 @@ source better_transcode : connect {
}
index better_transcode : torrents {
source = better_transcode
- path = /var/data/sphinx/better_transcode
+ path = /var/lib/sphinx/better_transcode
phrase_boundary =
}
diff --git a/static/functions/collages.js b/static/functions/collages.js
new file mode 100644
index 00000000..c602b88e
--- /dev/null
+++ b/static/functions/collages.js
@@ -0,0 +1,22 @@
+$(document).ready(function() {
+ $('#tiles').imagesLoaded(function() {
+ $("#tiles img").each(function() {
+ $(this).height(this.height);
+ });
+
+ // Prepare layout options.
+ var options = {
+ container: $('#tiles_container'), // Optional, used for some extra CSS styling
+ offset: 5, // Optional, the distance between grid items
+ outerOffset: 10, // Optional, the distance to the containers border
+ align: 'center',
+ };
+
+ // Get a reference to your grid items.
+ var handler = $('#tiles li');
+
+ // Call the layout function.
+ handler.wookmark(options);
+
+ });
+});
\ No newline at end of file
diff --git a/static/functions/jquery.imagesloaded.js b/static/functions/jquery.imagesloaded.js
new file mode 100644
index 00000000..3726d158
--- /dev/null
+++ b/static/functions/jquery.imagesloaded.js
@@ -0,0 +1,7 @@
+/*!
+ * imagesLoaded PACKAGED v3.0.4
+ * https://github.com/desandro/imagesloaded
+ * JavaScript is all like "You images are done yet or what?"
+ */
+
+(function(){"use strict";function e(){}function t(e,t){for(var n=e.length;n--;)if(e[n].listener===t)return n;return-1}var n=e.prototype;n.getListeners=function(e){var t,n,i=this._getEvents();if("object"==typeof e){t={};for(n in i)i.hasOwnProperty(n)&&e.test(n)&&(t[n]=i[n])}else t=i[e]||(i[e]=[]);return t},n.flattenListeners=function(e){var t,n=[];for(t=0;e.length>t;t+=1)n.push(e[t].listener);return n},n.getListenersAsObject=function(e){var t,n=this.getListeners(e);return n instanceof Array&&(t={},t[e]=n),t||n},n.addListener=function(e,n){var i,r=this.getListenersAsObject(e),o="object"==typeof n;for(i in r)r.hasOwnProperty(i)&&-1===t(r[i],n)&&r[i].push(o?n:{listener:n,once:!1});return this},n.on=n.addListener,n.addOnceListener=function(e,t){return this.addListener(e,{listener:t,once:!0})},n.once=n.addOnceListener,n.defineEvent=function(e){return this.getListeners(e),this},n.defineEvents=function(e){for(var t=0;e.length>t;t+=1)this.defineEvent(e[t]);return this},n.removeListener=function(e,n){var i,r,o=this.getListenersAsObject(e);for(r in o)o.hasOwnProperty(r)&&(i=t(o[r],n),-1!==i&&o[r].splice(i,1));return this},n.off=n.removeListener,n.addListeners=function(e,t){return this.manipulateListeners(!1,e,t)},n.removeListeners=function(e,t){return this.manipulateListeners(!0,e,t)},n.manipulateListeners=function(e,t,n){var i,r,o=e?this.removeListener:this.addListener,s=e?this.removeListeners:this.addListeners;if("object"!=typeof t||t instanceof RegExp)for(i=n.length;i--;)o.call(this,t,n[i]);else for(i in t)t.hasOwnProperty(i)&&(r=t[i])&&("function"==typeof r?o.call(this,i,r):s.call(this,i,r));return this},n.removeEvent=function(e){var t,n=typeof e,i=this._getEvents();if("string"===n)delete i[e];else if("object"===n)for(t in i)i.hasOwnProperty(t)&&e.test(t)&&delete i[t];else delete this._events;return this},n.emitEvent=function(e,t){var n,i,r,o,s=this.getListenersAsObject(e);for(r in s)if(s.hasOwnProperty(r))for(i=s[r].length;i--;)n=s[r][i],o=n.listener.apply(this,t||[]),(o===this._getOnceReturnValue()||n.once===!0)&&this.removeListener(e,s[r][i].listener);return this},n.trigger=n.emitEvent,n.emit=function(e){var t=Array.prototype.slice.call(arguments,1);return this.emitEvent(e,t)},n.setOnceReturnValue=function(e){return this._onceReturnValue=e,this},n._getOnceReturnValue=function(){return this.hasOwnProperty("_onceReturnValue")?this._onceReturnValue:!0},n._getEvents=function(){return this._events||(this._events={})},"function"==typeof define&&define.amd?define(function(){return e}):"undefined"!=typeof module&&module.exports?module.exports=e:this.EventEmitter=e}).call(this),function(e){"use strict";var t=document.documentElement,n=function(){};t.addEventListener?n=function(e,t,n){e.addEventListener(t,n,!1)}:t.attachEvent&&(n=function(t,n,i){t[n+i]=i.handleEvent?function(){var t=e.event;t.target=t.target||t.srcElement,i.handleEvent.call(i,t)}:function(){var n=e.event;n.target=n.target||n.srcElement,i.call(t,n)},t.attachEvent("on"+n,t[n+i])});var i=function(){};t.removeEventListener?i=function(e,t,n){e.removeEventListener(t,n,!1)}:t.detachEvent&&(i=function(e,t,n){e.detachEvent("on"+t,e[t+n]);try{delete e[t+n]}catch(i){e[t+n]=void 0}});var r={bind:n,unbind:i};"function"==typeof define&&define.amd?define(r):e.eventie=r}(this),function(e){"use strict";function t(e,t){for(var n in t)e[n]=t[n];return e}function n(e){return"[object Array]"===c.call(e)}function i(e){var t=[];if(n(e))t=e;else if("number"==typeof e.length)for(var i=0,r=e.length;r>i;i++)t.push(e[i]);else t.push(e);return t}function r(e,n){function r(e,n,s){if(!(this instanceof r))return new r(e,n);"string"==typeof e&&(e=document.querySelectorAll(e)),this.elements=i(e),this.options=t({},this.options),"function"==typeof n?s=n:t(this.options,n),s&&this.on("always",s),this.getImages(),o&&(this.jqDeferred=new o.Deferred);var a=this;setTimeout(function(){a.check()})}function c(e){this.img=e}r.prototype=new e,r.prototype.options={},r.prototype.getImages=function(){this.images=[];for(var e=0,t=this.elements.length;t>e;e++){var n=this.elements[e];"IMG"===n.nodeName&&this.addImage(n);for(var i=n.querySelectorAll("img"),r=0,o=i.length;o>r;r++){var s=i[r];this.addImage(s)}}},r.prototype.addImage=function(e){var t=new c(e);this.images.push(t)},r.prototype.check=function(){function e(e,r){return t.options.debug&&a&&s.log("confirm",e,r),t.progress(e),n++,n===i&&t.complete(),!0}var t=this,n=0,i=this.images.length;if(this.hasAnyBroken=!1,!i)return this.complete(),void 0;for(var r=0;i>r;r++){var o=this.images[r];o.on("confirm",e),o.check()}},r.prototype.progress=function(e){this.hasAnyBroken=this.hasAnyBroken||!e.isLoaded;var t=this;setTimeout(function(){t.emit("progress",t,e),t.jqDeferred&&t.jqDeferred.notify(t,e)})},r.prototype.complete=function(){var e=this.hasAnyBroken?"fail":"done";this.isComplete=!0;var t=this;setTimeout(function(){if(t.emit(e,t),t.emit("always",t),t.jqDeferred){var n=t.hasAnyBroken?"reject":"resolve";t.jqDeferred[n](t)}})},o&&(o.fn.imagesLoaded=function(e,t){var n=new r(this,e,t);return n.jqDeferred.promise(o(this))});var f={};return c.prototype=new e,c.prototype.check=function(){var e=f[this.img.src];if(e)return this.useCached(e),void 0;if(f[this.img.src]=this,this.img.complete&&void 0!==this.img.naturalWidth)return this.confirm(0!==this.img.naturalWidth,"naturalWidth"),void 0;var t=this.proxyImage=new Image;n.bind(t,"load",this),n.bind(t,"error",this),t.src=this.img.src},c.prototype.useCached=function(e){if(e.isConfirmed)this.confirm(e.isLoaded,"cached was confirmed");else{var t=this;e.on("confirm",function(e){return t.confirm(e.isLoaded,"cache emitted confirmed"),!0})}},c.prototype.confirm=function(e,t){this.isConfirmed=!0,this.isLoaded=e,this.emit("confirm",this,t)},c.prototype.handleEvent=function(e){var t="on"+e.type;this[t]&&this[t](e)},c.prototype.onload=function(){this.confirm(!0,"onload"),this.unbindProxyEvents()},c.prototype.onerror=function(){this.confirm(!1,"onerror"),this.unbindProxyEvents()},c.prototype.unbindProxyEvents=function(){n.unbind(this.proxyImage,"load",this),n.unbind(this.proxyImage,"error",this)},r}var o=e.jQuery,s=e.console,a=s!==void 0,c=Object.prototype.toString;"function"==typeof define&&define.amd?define(["eventEmitter/EventEmitter","eventie/eventie"],r):e.imagesLoaded=r(e.EventEmitter,e.eventie)}(window);
diff --git a/static/functions/jquery.wookmark.js b/static/functions/jquery.wookmark.js
new file mode 100644
index 00000000..656cec56
--- /dev/null
+++ b/static/functions/jquery.wookmark.js
@@ -0,0 +1,450 @@
+/*!
+ jQuery Wookmark plugin
+ @name jquery.wookmark.js
+ @author Christoph Ono (chri@sto.ph or @gbks)
+ @author Sebastian Helzle (sebastian@helzle.net or @sebobo)
+ @version 1.4.3
+ @date 8/25/2013
+ @category jQuery plugin
+ @copyright (c) 2009-2013 Christoph Ono (www.wookmark.com)
+ @license Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
+*/
+(function (factory) {
+ if (typeof define === 'function' && define.amd)
+ define(['jquery'], factory);
+ else
+ factory(jQuery);
+}(function ($) {
+
+ var Wookmark, defaultOptions, __bind;
+
+ __bind = function(fn, me) {
+ return function() {
+ return fn.apply(me, arguments);
+ };
+ };
+
+ // Wookmark default options
+ defaultOptions = {
+ align: 'center',
+ autoResize: false,
+ comparator: null,
+ container: $('body'),
+ ignoreInactiveItems: true,
+ itemWidth: 0,
+ fillEmptySpace: false,
+ flexibleWidth: 0,
+ direction: undefined,
+ offset: 2,
+ onLayoutChanged: undefined,
+ outerOffset: 0,
+ resizeDelay: 50
+ };
+
+ Wookmark = (function() {
+
+ function Wookmark(handler, options) {
+ // Instance variables.
+ this.handler = handler;
+ this.columns = this.containerWidth = this.resizeTimer = null;
+ this.activeItemCount = 0;
+ this.itemHeightsDirty = true;
+ this.placeholders = [];
+
+ $.extend(true, this, defaultOptions, options);
+
+ // Bind instance methods
+ this.update = __bind(this.update, this);
+ this.onResize = __bind(this.onResize, this);
+ this.onRefresh = __bind(this.onRefresh, this);
+ this.getItemWidth = __bind(this.getItemWidth, this);
+ this.layout = __bind(this.layout, this);
+ this.layoutFull = __bind(this.layoutFull, this);
+ this.layoutColumns = __bind(this.layoutColumns, this);
+ this.filter = __bind(this.filter, this);
+ this.clear = __bind(this.clear, this);
+ this.getActiveItems = __bind(this.getActiveItems, this);
+ this.refreshPlaceholders = __bind(this.refreshPlaceholders, this);
+ this.sortElements = __bind(this.sortElements, this);
+
+ // Collect filter data
+ var i = 0, j = 0, filterClasses = {}, itemFilterClasses, $item, filterClass;
+
+ for (; i < handler.length; i++) {
+ $item = handler.eq(i);
+
+ // Read filter classes
+ itemFilterClasses = $item.data('filterClass');
+
+ // Globally store each filter class as object and the fitting items in the array
+ if (typeof itemFilterClasses == 'object' && itemFilterClasses.length > 0) {
+ for (j = 0; j < itemFilterClasses.length; j++) {
+ filterClass = $.trim(itemFilterClasses[j]).toLowerCase();
+
+ if (!(filterClass in filterClasses)) {
+ filterClasses[filterClass] = [];
+ }
+ filterClasses[filterClass].push($item[0]);
+ }
+ }
+ }
+ this.filterClasses = filterClasses;
+
+ // Listen to resize event if requested.
+ if (this.autoResize)
+ $(window).bind('resize.wookmark', this.onResize);
+
+ this.container.bind('refreshWookmark', this.onRefresh);
+ }
+
+ // Method for updating the plugins options
+ Wookmark.prototype.update = function(options) {
+ this.itemHeightsDirty = true;
+ $.extend(true, this, options);
+ };
+
+ // This timer ensures that layout is not continuously called as window is being dragged.
+ Wookmark.prototype.onResize = function() {
+ clearTimeout(this.resizeTimer);
+ this.itemHeightsDirty = this.flexibleWidth !== 0;
+ this.resizeTimer = setTimeout(this.layout, this.resizeDelay);
+ };
+
+ // Marks the items heights as dirty and does a relayout
+ Wookmark.prototype.onRefresh = function() {
+ this.itemHeightsDirty = true;
+ this.layout();
+ };
+
+ /**
+ * Filters the active items with the given string filters.
+ * @param filters array of string
+ * @param mode 'or' or 'and'
+ */
+ Wookmark.prototype.filter = function(filters, mode) {
+ var activeFilters = [], activeFiltersLength, activeItems = $(),
+ i, j, k, filter;
+
+ filters = filters || [];
+ mode = mode || 'or';
+
+ if (filters.length) {
+ // Collect active filters
+ for (i = 0; i < filters.length; i++) {
+ filter = $.trim(filters[i].toLowerCase());
+ if (filter in this.filterClasses) {
+ activeFilters.push(this.filterClasses[filter]);
+ }
+ }
+
+ // Get items for active filters with the selected mode
+ activeFiltersLength = activeFilters.length;
+ if (mode == 'or' || activeFiltersLength == 1) {
+ // Set all items in all active filters active
+ for (i = 0; i < activeFiltersLength; i++) {
+ activeItems = activeItems.add(activeFilters[i]);
+ }
+ } else if (mode == 'and') {
+ var shortestFilter = activeFilters[0],
+ itemValid = true, foundInFilter,
+ currentItem, currentFilter;
+
+ // Find shortest filter class
+ for (i = 1; i < activeFiltersLength; i++) {
+ if (activeFilters[i].length < shortestFilter.length) {
+ shortestFilter = activeFilters[i];
+ }
+ }
+
+ // Iterate over shortest filter and find elements in other filter classes
+ for (i = 0; i < shortestFilter.length; i++) {
+ currentItem = shortestFilter[i];
+ itemValid = true;
+
+ for (j = 0; j < activeFilters.length && itemValid; j++) {
+ currentFilter = activeFilters[j];
+ if (shortestFilter == currentFilter) continue;
+
+ // Search for current item in each active filter class
+ for (k = 0, foundInFilter = false; k < currentFilter.length && !foundInFilter; k++) {
+ foundInFilter = currentFilter[k] == currentItem;
+ }
+ itemValid &= foundInFilter;
+ }
+ if (itemValid)
+ activeItems.push(shortestFilter[i]);
+ }
+ }
+ // Hide inactive items
+ this.handler.not(activeItems).addClass('inactive');
+ } else {
+ // Show all items if no filter is selected
+ activeItems = this.handler;
+ }
+
+ // Show active items
+ activeItems.removeClass('inactive');
+
+ // Unset columns and refresh grid for a full layout
+ this.columns = null;
+ this.layout();
+ };
+
+ /**
+ * Creates or updates existing placeholders to create columns of even height
+ */
+ Wookmark.prototype.refreshPlaceholders = function(columnWidth, sideOffset) {
+ var i = this.placeholders.length,
+ $placeholder, $lastColumnItem,
+ columnsLength = this.columns.length, column,
+ height, top, innerOffset,
+ containerHeight = this.container.innerHeight();
+
+ for (; i < columnsLength; i++) {
+ $placeholder = $('').appendTo(this.container);
+ this.placeholders.push($placeholder);
+ }
+
+ innerOffset = this.offset + parseInt(this.placeholders[0].css('borderWidth'), 10) * 2;
+
+ for (i = 0; i < this.placeholders.length; i++) {
+ $placeholder = this.placeholders[i];
+ column = this.columns[i];
+
+ if (i >= columnsLength || !column[column.length - 1]) {
+ $placeholder.css('display', 'none');
+ } else {
+ $lastColumnItem = column[column.length - 1];
+ if (!$lastColumnItem) continue;
+ top = $lastColumnItem.data('wookmark-top') + $lastColumnItem.data('wookmark-height') + this.offset;
+ height = containerHeight - top - innerOffset;
+
+ $placeholder.css({
+ position: 'absolute',
+ display: height > 0 ? 'block' : 'none',
+ left: i * columnWidth + sideOffset,
+ top: top,
+ width: columnWidth - innerOffset,
+ height: height
+ });
+ }
+ }
+ };
+
+ // Method the get active items which are not disabled and visible
+ Wookmark.prototype.getActiveItems = function() {
+ return this.ignoreInactiveItems ? this.handler.not('.inactive') : this.handler;
+ };
+
+ // Method to get the standard item width
+ Wookmark.prototype.getItemWidth = function() {
+ var itemWidth = this.itemWidth,
+ innerWidth = this.container.width() - 2 * this.outerOffset,
+ firstElement = this.handler.eq(0),
+ flexibleWidth = this.flexibleWidth;
+
+ if (this.itemWidth === undefined || this.itemWidth === 0 && !this.flexibleWidth) {
+ itemWidth = firstElement.outerWidth();
+ }
+ else if (typeof this.itemWidth == 'string' && this.itemWidth.indexOf('%') >= 0) {
+ itemWidth = parseFloat(this.itemWidth) / 100 * innerWidth;
+ }
+
+ // Calculate flexible item width if option is set
+ if (flexibleWidth) {
+ if (typeof flexibleWidth == 'string' && flexibleWidth.indexOf('%') >= 0) {
+ flexibleWidth = parseFloat(flexibleWidth) / 100 * innerWidth;
+ }
+
+ var columns = ~~(0.5 + (innerWidth + this.offset) / (flexibleWidth + this.offset)),
+ columnWidth = Math.min(flexibleWidth, ~~((innerWidth - (columns - 1) * this.offset) / columns));
+
+ itemWidth = Math.max(itemWidth, columnWidth);
+
+ // Stretch items to fill calculated width
+ this.handler.css('width', itemWidth);
+ }
+
+ return itemWidth;
+ };
+
+ // Main layout method.
+ Wookmark.prototype.layout = function(force) {
+ // Do nothing if container isn't visible
+ if (!this.container.is(':visible')) return;
+
+ // Calculate basic layout parameters.
+ var columnWidth = this.getItemWidth() + this.offset,
+ containerWidth = this.container.width(),
+ innerWidth = containerWidth - 2 * this.outerOffset,
+ columns = ~~((innerWidth + this.offset) / columnWidth),
+ offset = 0, maxHeight = 0, i = 0,
+ activeItems = this.getActiveItems(),
+ activeItemsLength = activeItems.length,
+ $item;
+
+ // Cache item height
+ if (this.itemHeightsDirty) {
+ for (; i < activeItemsLength; i++) {
+ $item = activeItems.eq(i);
+ $item.data('wookmark-height', $item.outerHeight());
+ }
+ this.itemHeightsDirty = false;
+ }
+
+ // Use less columns if there are to few items
+ columns = Math.max(1, Math.min(columns, activeItemsLength));
+
+ // Calculate the offset based on the alignment of columns to the parent container
+ offset = this.outerOffset;
+ if (this.align == 'center') {
+ offset += ~~(0.5 + (innerWidth - (columns * columnWidth - this.offset)) >> 1);
+ }
+
+ // Get direction for positioning
+ this.direction = this.direction || (this.align == 'right' ? 'right' : 'left');
+
+ // If container and column count hasn't changed, we can only update the columns.
+ if (!force && this.columns !== null && this.columns.length == columns && this.activeItemCount == activeItemsLength) {
+ maxHeight = this.layoutColumns(columnWidth, offset);
+ } else {
+ maxHeight = this.layoutFull(columnWidth, columns, offset);
+ }
+ this.activeItemCount = activeItemsLength;
+
+ // Set container height to height of the grid.
+ this.container.css('height', maxHeight);
+
+ // Update placeholders
+ if (this.fillEmptySpace) {
+ this.refreshPlaceholders(columnWidth, offset);
+ }
+
+ if (this.onLayoutChanged !== undefined && typeof this.onLayoutChanged === 'function') {
+ this.onLayoutChanged();
+ }
+ };
+
+ /**
+ * Sort elements with configurable comparator
+ */
+ Wookmark.prototype.sortElements = function(elements) {
+ return typeof(this.comparator) === 'function' ? elements.sort(this.comparator) : elements;
+ };
+
+ /**
+ * Perform a full layout update.
+ */
+ Wookmark.prototype.layoutFull = function(columnWidth, columns, offset) {
+ var $item, i = 0, k = 0,
+ activeItems = $.makeArray(this.getActiveItems()),
+ length = activeItems.length,
+ shortest = null, shortestIndex = null,
+ itemCSS = {position: 'absolute'},
+ sideOffset, heights = [],
+ leftAligned = this.align == 'left' ? true : false;
+
+ this.columns = [];
+
+ // Sort elements before layouting
+ activeItems = this.sortElements(activeItems);
+
+ // Prepare arrays to store height of columns and items.
+ while (heights.length < columns) {
+ heights.push(this.outerOffset);
+ this.columns.push([]);
+ }
+
+ // Loop over items.
+ for (; i < length; i++ ) {
+ $item = $(activeItems[i]);
+
+ // Find the shortest column.
+ shortest = heights[0];
+ shortestIndex = 0;
+ for (k = 0; k < columns; k++) {
+ if (heights[k] < shortest) {
+ shortest = heights[k];
+ shortestIndex = k;
+ }
+ }
+
+ // stick to left side if alignment is left and this is the first column
+ sideOffset = offset;
+ if (shortestIndex > 0 || !leftAligned)
+ sideOffset += shortestIndex * columnWidth;
+
+ // Position the item.
+ itemCSS[this.direction] = sideOffset;
+ itemCSS.top = shortest;
+ $item.css(itemCSS).data('wookmark-top', shortest);
+
+ // Update column height and store item in shortest column
+ heights[shortestIndex] += $item.data('wookmark-height') + this.offset;
+ this.columns[shortestIndex].push($item);
+ }
+
+ // Return longest column
+ return Math.max.apply(Math, heights);
+ };
+
+ /**
+ * This layout method only updates the vertical position of the
+ * existing column assignments.
+ */
+ Wookmark.prototype.layoutColumns = function(columnWidth, offset) {
+ var heights = [],
+ i = 0, k = 0, currentHeight,
+ column, $item, itemCSS, sideOffset;
+
+ for (; i < this.columns.length; i++) {
+ heights.push(this.outerOffset);
+ column = this.columns[i];
+ sideOffset = i * columnWidth + offset;
+ currentHeight = heights[i];
+
+ for (k = 0; k < column.length; k++) {
+ $item = column[k];
+ itemCSS = {
+ top: currentHeight
+ };
+ itemCSS[this.direction] = sideOffset;
+
+ $item.css(itemCSS).data('wookmark-top', currentHeight);
+
+ currentHeight += $item.data('wookmark-height') + this.offset;
+ }
+ heights[i] = currentHeight;
+ }
+
+ // Return longest column
+ return Math.max.apply(Math, heights);
+ };
+
+ /**
+ * Clear event listeners and time outs.
+ */
+ Wookmark.prototype.clear = function() {
+ clearTimeout(this.resizeTimer);
+ $(window).unbind('resize.wookmark', this.onResize);
+ this.container.unbind('refreshWookmark', this.onRefresh);
+ };
+
+ return Wookmark;
+ })();
+
+ $.fn.wookmark = function(options) {
+ // Create a wookmark instance if not available
+ if (!this.wookmarkInstance) {
+ this.wookmarkInstance = new Wookmark(this, options || {});
+ } else {
+ this.wookmarkInstance.update(options || {});
+ }
+
+ // Apply layout
+ this.wookmarkInstance.layout(true);
+
+ // Display items (if hidden) and return jQuery object to maintain chainability
+ return this.show();
+ };
+}));
diff --git a/static/styles/collages/style.css b/static/styles/collages/style.css
new file mode 100644
index 00000000..f739ece8
--- /dev/null
+++ b/static/styles/collages/style.css
@@ -0,0 +1,58 @@
+
+#tiles_container {
+ position: relative;
+}
+
+/**
+ * Grid container
+ */
+ #tiles {
+ list-style-type: none;
+ position: relative; /** Needed to ensure items are laid out relative to this container **/
+ margin: 0;
+ padding: 0;
+}
+
+/**
+ * Grid items
+ */
+ #tiles li {
+ background-color: #ffffff;
+ border: 1px solid #dedede;
+ border-radius: 2px;
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ display: none; /** Hide items initially to avoid a flicker effect **/
+ cursor: pointer;
+ padding: 4px;
+}
+
+#tiles li.inactive {
+ visibility: hidden;
+ opacity: 0;
+}
+
+#tiles li img {
+ display: block;
+}
+
+/**
+ * Grid item text
+ */
+ #tiles li p {
+ color: #666;
+ font-size: 12px;
+ margin: 7px 0 0 7px;
+}
+
+/**
+ * Placerholder css
+ */
+ .wookmark-placeholder {
+ border-radius: 2px;
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ background-color: #eee;
+ border: 1px solid #dedede;
+ z-index: -1;
+}
\ No newline at end of file