summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--batch.php29
-rw-r--r--browse.php6
-rw-r--r--lib/class/api.class.php8
-rw-r--r--lib/class/browse.class.php5
-rw-r--r--lib/class/playlist.class.php54
-rw-r--r--lib/class/playlist_object.abstract.php72
-rw-r--r--lib/class/query.class.php40
-rw-r--r--lib/class/random.class.php197
-rw-r--r--lib/class/search.class.php1033
-rw-r--r--lib/class/update.class.php22
-rw-r--r--lib/init.php2
-rw-r--r--lib/javascript/search-data.php49
-rw-r--r--lib/javascript/search.js168
-rw-r--r--lib/search.php294
-rw-r--r--random.php7
-rw-r--r--search.php20
-rw-r--r--server/browse.ajax.php6
-rw-r--r--smartplaylist.php103
-rw-r--r--templates/show_edit_smartplaylist_row.inc.php45
-rw-r--r--templates/show_edit_smartplaylist_title.inc.php43
-rw-r--r--templates/show_random.inc.php118
-rw-r--r--templates/show_random_rules.inc.php59
-rw-r--r--templates/show_rules.inc.php67
-rw-r--r--templates/show_search.inc.php145
-rw-r--r--templates/show_search_bar.inc.php17
-rw-r--r--templates/show_search_options.inc.php2
-rw-r--r--templates/show_smartplaylist.inc.php67
-rw-r--r--templates/show_smartplaylist_row.inc.php39
-rw-r--r--templates/show_smartplaylist_title.inc.php24
-rw-r--r--templates/show_smartplaylists.inc.php63
-rw-r--r--templates/sidebar_home.inc.php3
31 files changed, 2095 insertions, 712 deletions
diff --git a/batch.php b/batch.php
index 427a726e..c57961eb 100644
--- a/batch.php
+++ b/batch.php
@@ -53,6 +53,18 @@ switch ($_REQUEST['action']) {
$media_ids = $playlist->get_songs();
$name = $playlist->name;
break;
+ case 'smartplaylist':
+ $search = new Search('song', $_REQUEST['id']);
+ $sql = $search->to_sql();
+ $sql = $sql['base'] . ' ' . $sql['table_sql'] . ' WHERE ' .
+ $sql['where_sql'];
+ $db_results = Dba::read($sql);
+ $media_ids = array();
+ while ($row = Dba::fetch_assoc($db_results)) {
+ $media_ids[] = $row['id'];
+ }
+ $name = $search->name;
+ break;
case 'album':
$album = new Album($_REQUEST['id']);
$media_ids = $album->get_songs();
@@ -66,7 +78,22 @@ switch ($_REQUEST['action']) {
case 'browse':
$id = scrub_in($_REQUEST['browse_id']);
$browse = new Browse($id);
- $media_ids = $browse->get_saved();
+ $browse_media_ids = $browse->get_saved();
+ $media_ids = array();
+ foreach ($browse_media_ids as $media_id) {
+ switch ($_REQUEST['type']) {
+ case 'album':
+ $album = new Album($media_id);
+ $media_ids = array_merge($media_ids, $album->get_songs());
+ break;
+ case 'song':
+ $media_ids[] = $media_id;
+ break;
+ case 'video':
+ $media_ids[] = array('Video', $media_id);
+ break;
+ } // switch on type
+ } // foreach media_id
$name = 'Batch-' . date("dmY",time());
default:
// Rien a faire
diff --git a/browse.php b/browse.php
index 91f6e30a..62f4cdfd 100644
--- a/browse.php
+++ b/browse.php
@@ -55,6 +55,7 @@ switch ($_REQUEST['action']) {
case 'album':
case 'artist':
case 'playlist':
+ case 'smartplaylist':
case 'live_stream':
case 'video':
case 'song':
@@ -108,6 +109,11 @@ switch($_REQUEST['action']) {
$browse->set_filter('playlist_type','1');
$browse->show_objects();
break;
+ case 'smartplaylist':
+ $browse->set_sort('type', 'ASC');
+ $browse->set_filter('playlist_type','1');
+ $browse->show_objects();
+ break;
case 'video':
$browse->set_sort('title','ASC');
$browse->show_objects();
diff --git a/lib/class/api.class.php b/lib/class/api.class.php
index 508d38c2..9bd659a4 100644
--- a/lib/class/api.class.php
+++ b/lib/class/api.class.php
@@ -594,11 +594,13 @@ class Api {
/**
* search_songs
- * This returns the songs and returns... songs
+ * This searches the songs and returns... songs
*/
public static function search_songs($input) {
+ $array['rule_1'] = 'anywhere';
+ $array['rule_1_input'] = $input['filter'];
+ $array['rule_1_operator'] = 0;
- $array['s_all'] = $input['filter'];
ob_end_clean();
xmlData::set_offset($input['offset']);
@@ -608,7 +610,7 @@ class Api {
//Run search references these variables, ooh the huge manatee
unset($input['limit'],$input['offset']);
- $results = run_search($array);
+ $results = Search::run($array);
echo xmlData::songs($results);
diff --git a/lib/class/browse.class.php b/lib/class/browse.class.php
index 56eda8b7..c997c478 100644
--- a/lib/class/browse.class.php
+++ b/lib/class/browse.class.php
@@ -188,6 +188,11 @@ class Browse extends Query {
require_once Config::get('prefix') . '/templates/show_localplay_playlist.inc.php';
show_box_bottom();
break;
+ case 'smartplaylist':
+ show_box_top(_('Smart Playlists') . $match, $class);
+ require_once Config::get('prefix') . '/templates/show_smartplaylists.inc.php';
+ show_box_bottom();
+ break;
case 'catalog':
show_box_top(_('Catalogs'), $class);
require_once Config::get('prefix') . '/templates/show_catalogs.inc.php';
diff --git a/lib/class/playlist.class.php b/lib/class/playlist.class.php
index 6624540c..42638820 100644
--- a/lib/class/playlist.class.php
+++ b/lib/class/playlist.class.php
@@ -46,13 +46,9 @@
* @link http://www.ampache.org/
* @since Class available since Release 1.0
*/
-class Playlist extends database_object {
+class Playlist extends playlist_object {
/* Variables from the database */
- public $id;
- public $name;
- public $user;
- public $type;
public $genre;
public $date;
@@ -94,43 +90,33 @@ class Playlist extends database_object {
} // build_cache
/**
- * format
- * This takes the current playlist object and gussies it up a little
- * bit so it is presentable to the users
+ * get_playlists
+ * Returns a list of playlists accessible by the current user.
*/
- public function format() {
-
- $this->f_name = truncate_with_ellipsis($this->name,Config::get('ellipse_threshold_title'));
- $this->f_link = '<a href="' . Config::get('web_path') . '/playlist.php?action=show_playlist&amp;playlist_id=' . $this->id . '">' . $this->f_name . '</a>';
-
- $this->f_type = ($this->type == 'private') ? get_user_icon('lock',_('Private')) : '';
+ public static function get_playlists() {
+ $sql = "SELECT `id` from `playlist` WHERE `type`='public' OR " .
+ "`user`='" . $GLOBALS['user']->id . "' ORDER BY `name`";
+ $db_results = Dba::read($sql);
- $client = new User($this->user);
+ $results = array();
- $this->f_user = $client->fullname;
+ while ($row = Dba::fetch_assoc($db_results)) {
+ $results[] = $row['id'];
+ }
- } // format
+ return $results;
+ } // get_playlists
/**
- * has_access
- * This function returns true or false if the current user
- * has access to this playlist
+ * format
+ * This takes the current playlist object and gussies it up a little
+ * bit so it is presentable to the users
*/
- public function has_access() {
-
- if (!Access::check('interface','25')) {
- return false;
- }
- if ($this->user == $GLOBALS['user']->id) {
- return true;
- }
- else {
- return Access::check('interface','100');
- }
-
- return false;
+ public function format() {
+ parent::format();
+ $this->f_link = '<a href="' . Config::get('web_path') . '/playlist.php?action=show_playlist&amp;playlist_id=' . $this->id . '">' . $this->f_name . '</a>';
- } // has_access
+ } // format
/**
* get_track
diff --git a/lib/class/playlist_object.abstract.php b/lib/class/playlist_object.abstract.php
new file mode 100644
index 00000000..def7b3f2
--- /dev/null
+++ b/lib/class/playlist_object.abstract.php
@@ -0,0 +1,72 @@
+<?php
+/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
+/*
+
+ Copyright (c) Ampache.org
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License v2
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * playlist_object
+ * Abstracting out functionality needed by both normal and smart playlists
+ */
+abstract class playlist_object extends database_object {
+
+ // Database variables
+ public $id;
+ public $name;
+ public $user;
+ public $type;
+
+ /**
+ * format
+ * This takes the current playlist object and gussies it up a little
+ * bit so it is presentable to the users
+ */
+ public function format() {
+
+ $this->f_name = truncate_with_ellipsis($this->name,Config::get('ellipse_threshold_title'));
+ $this->f_type = ($this->type == 'private') ? get_user_icon('lock',_('Private')) : '';
+
+ $client = new User($this->user);
+
+ $this->f_user = $client->fullname;
+
+ } // format
+
+ /**
+ * has_access
+ * This function returns true or false if the current user
+ * has access to this playlist
+ */
+ public function has_access() {
+
+ if (!Access::check('interface','25')) {
+ return false;
+ }
+ if ($this->user == $GLOBALS['user']->id) {
+ return true;
+ }
+ else {
+ return Access::check('interface','100');
+ }
+
+ return false;
+
+ } // has_access
+
+
+} // end playlist_object
diff --git a/lib/class/query.class.php b/lib/class/query.class.php
index 898e8029..11095b80 100644
--- a/lib/class/query.class.php
+++ b/lib/class/query.class.php
@@ -144,6 +144,10 @@ class Query {
'alpha_match',
'starts_with'
),
+ 'smartplaylist' => array(
+ 'alpha_match',
+ 'starts_with'
+ ),
'tag' => array(
'tag',
'object_type',
@@ -194,6 +198,10 @@ class Query {
'name',
'user'
),
+ 'smartplaylist' => array(
+ 'name',
+ 'user'
+ ),
'shoutbox' => array(
'date',
'user',
@@ -465,6 +473,7 @@ class Query {
case 'video':
case 'playlist':
case 'playlist_song':
+ case 'smartplaylist':
case 'song':
case 'flagged':
case 'catalog':
@@ -735,6 +744,10 @@ class Query {
$this->set_select("`playlist`.`id`");
$sql = "SELECT %%SELECT%% FROM `playlist` ";
break;
+ case 'smartplaylist':
+ self::set_select('`search`.`id`');
+ $sql = "SELECT %%SELECT%% FROM `search` ";
+ break;
case 'flagged':
$this->set_select("`flagged`.`id`");
$sql = "SELECT %%SELECT%% FROM `flagged` ";
@@ -1127,6 +1140,20 @@ class Query {
break;
} // end filter
break;
+ case 'smartplaylist':
+ switch ($filter) {
+ case 'alpha_match':
+ $filter_sql = " `search`.`name` LIKE '%" . Dba::escape($value) . "%' AND ";
+ break;
+ case 'starts_with':
+ $filter_sql = " `search`.`name` LIKE '" . Dba::escape($value) . "%' AND ";
+ break;
+ case 'playlist_type':
+ $user_id = intval($GLOBALS['user']->id);
+ $filter_sql = " (`search`.`type` = 'public' OR `search`.`user`='$user_id') AND ";
+ break;
+ } // end switch on $filter
+ break;
case 'tag':
switch ($filter) {
case 'alpha_match':
@@ -1251,6 +1278,19 @@ class Query {
break;
} // end switch
break;
+ case 'smartplaylist':
+ switch ($field) {
+ case 'type':
+ $sql = "`search`.`type`";
+ break;
+ case 'name':
+ $sql = "`search`.`name`";
+ break;
+ case 'user':
+ $sql = "`search`.`user`";
+ break;
+ } // end switch on $field
+ break;
case 'live_stream':
switch ($field) {
case 'name':
diff --git a/lib/class/random.class.php b/lib/class/random.class.php
index 575cf8fb..5472bb80 100644
--- a/lib/class/random.class.php
+++ b/lib/class/random.class.php
@@ -254,120 +254,60 @@ class Random implements media {
* This processes the results of a post from a form and returns an
* array of song items that were returned from said randomness
*/
- public static function advanced($data) {
+ public static function advanced($type, $data) {
/* Figure out our object limit */
$limit = intval($data['random']);
// Generate our matchlist
- if ($data['catalog'] != '-1') {
- $matchlist['catalog'] = $data['catalog'];
- }
- if ($data['genre'][0] != '-1') {
- $matchlist['genre'] = $data['genre'];
- }
- /* If they've passed -1 as limit then don't get everything */
+ /* If they've passed -1 as limit then get everything */
if ($data['random'] == "-1") { unset($data['random']); }
else { $limit_sql = "LIMIT " . Dba::escape($limit); }
- $where = "1=1 ";
- if (is_array($matchlist)) {
- foreach ($matchlist as $type => $value) {
- if (is_array($value)) {
- foreach ($value as $v) {
- if (!strlen($v)) { continue; }
- $v = Dba::escape($v);
- if ($v != $value[0]) { $where .= " OR $type='$v' "; }
- else { $where .= " AND ( $type='$v'"; }
- }
- $where .= ")";
- }
- elseif (strlen($value)) {
- $value = Dba::escape($value);
- $where .= " AND $type='$value' ";
- }
- } // end foreach
- } // end if matchlist
-
- switch ($data['random_type']) {
- case 'full_album':
- $query = "SELECT `album`.`id` FROM `song` INNER JOIN `album` ON `song`.`album`=`album`.`id` " .
- "WHERE $where GROUP BY `song`.`album` ORDER BY RAND() $limit_sql";
- $db_results = Dba::read($query);
- while ($row = Dba::fetch_assoc($db_results)) {
- $albums_where .= " OR `song`.`album`=" . $row['id'];
- }
- $albums_where = ltrim($albums_where," OR");
- $sql = "SELECT `song`.`id`,`song`.`size`,`song`.`time` FROM `song` WHERE $albums_where ORDER BY `song`.`album`,`song`.`track` ASC";
- break;
- case 'full_artist':
- $query = "SELECT `artist`.`id` FROM `song` INNER JOIN `artist` ON `song`.`artist`=`artist`.`id` " .
- "WHERE $where GROUP BY `song`.`artist` ORDER BY RAND() $limit_sql";
- $db_results = Dba::read($query);
- while ($row = Dba::fetch_row($db_results)) {
- $artists_where .= " OR song.artist=" . $row[0];
- }
- $artists_where = ltrim($artists_where," OR");
- $sql = "SELECT song.id,song.size,song.time FROM song WHERE $artists_where ORDER BY RAND()";
- break;
- case 'unplayed':
- $uid = Dba::escape($GLOBALS['user']->id);
- $sql = "SELECT object_id,COUNT(`id`) AS `total` FROM `object_count` WHERE `user`='$uid' GROUP BY `object_id`";
- $db_results = Dba::read($sql);
-
- $in_sql = "`id` IN (";
+ $search_data = Search::clean_request($data);
- while ($row = Dba::fetch_assoc($db_results)) {
- $row['object_id'] = Dba::escape($row['object_id']);
- $in_sql .= "'" . $row['object_id'] . "',";
- }
+ $search_info = false;
- $in_sql = rtrim($in_sql,',') . ')';
+ if (count($search_data) > 1) {
+ $search = new Search($type);
+ $search->parse_rules($search_data);
+ $search_info = $search->to_sql();
+ }
- $sql = "SELECT song.id,song.size,song.time FROM song " .
- "WHERE ($where) AND $in_sql ORDER BY RAND() $limit_sql";
- break;
- case 'high_rating':
- $sql = "SELECT `rating`.`object_id`,`rating`.`rating` FROM `rating` " .
- "WHERE `rating`.`object_type`='song' ORDER BY `rating` DESC";
- $db_results = Dba::read($sql);
-
- // Get all of the ratings for songs
- while ($row = Dba::fetch_assoc($db_results)) {
- $results[$row['object_id']][] = $row['rating'];
+ switch ($type) {
+ case 'song':
+ $sql = "SELECT `song`.`id`, `size`, `time` " .
+ "FROM `song` ";
+ if ($search_info) {
+ $sql .= $search_info['table_sql'];
+ $sql .= ' WHERE ' . $search_info['where_sql'];
}
- // Calculate the averages
- foreach ($results as $key=>$rating_array) {
- $average = intval(array_sum($rating_array) / count($rating_array));
- // We have to do this because array_slice doesn't maintain indexes
- $new_key = $average . $key;
- $ratings[$new_key] = $key;
+ break;
+ case 'album':
+ $sql = "SELECT `album`.`id`, SUM(`song`.`size`) AS `size`, SUM(`song`.`time`) AS `time` FROM `album` ";
+ if (! $search_info || ! $search_info['join']['song']) {
+ $sql .= "LEFT JOIN `song` ON `song`.`album`=`album`.`id` ";
}
-
- // Sort it by the value and slice at $limit * 2 so we have a little bit of randomness
- krsort($ratings);
- $ratings = array_slice($ratings,0,$limit*2);
-
- $in_sql = "`song`.`id` IN (";
-
- // Build the IN query, cause if you're OUT it ain't cool
- foreach ($ratings as $song_id) {
- $key = Dba::escape($song_id);
- $in_sql .= "'$key',";
+ if ($search_info) {
+ $sql .= $search_info['table_sql'];
+ $sql .= ' WHERE ' . $search_info['where_sql'];
}
-
- $in_sql = rtrim($in_sql,',') . ')';
-
- // Apply true limit and order by rand
- $sql = "SELECT song.id,song.size,song.time FROM song " .
- "WHERE ($where) AND $in_sql ORDER BY RAND() $limit_sql";
+ $sql .= ' GROUP BY `album`.`id`';
break;
- default:
- $sql = "SELECT `id`,`size`,`time` FROM `song` WHERE $where ORDER BY RAND() $limit_sql";
-
+ case 'artist':
+ $sql = "SELECT `artist`.`id`, SUM(`song`.`size`) AS `size`, SUM(`song`.`time`) AS `time` FROM `artist` ";
+ if (! $search_info || ! $search_info['join']['song']) {
+ $sql .= "LEFT JOIN `song` ON `song`.`artist`=`artist`.`id` ";
+ }
+ if ($search_info) {
+ $sql .= $search_info['table_sql'];
+ $sql .= ' WHERE ' . $search_info['where_sql'];
+ }
+ $sql .= ' GROUP BY `artist`.`id`';
break;
- } // end switch on type of random play
+ }
+ $sql .= " ORDER BY RAND() $limit_sql";
// Run the query generated above so we can while it
$db_results = Dba::read($sql);
@@ -380,17 +320,23 @@ class Random implements media {
// Convert
$new_size = ($row['size'] / 1024) / 1024;
- // Only fuzzy 10 times
- if ($fuzzy_size > 10) { return $results; }
+ // Only fuzzy 100 times
+ if ($fuzzy_size > 100) {
+ break;
+ }
- // Add and check, skip if over don't return incase theres a smaller one commin round
- if (($size_total + $new_size) > $data['size_limit']) { $fuzzy_size++; continue; }
+ // Add and check, skip if over size
+ if (($size_total + $new_size) > $data['size_limit']) {
+ $fuzzy_size++;
+ continue;
+ }
$size_total = $size_total + $new_size;
$results[] = $row['id'];
// If we are within 4mb of target then jump ship
- if (($data['size_limit'] - floor($size_total)) < 4) { return $results; }
+ if (($data['size_limit'] - floor($size_total)) < 4) {
+ break; }
} // if size_limit
// If length really does matter
@@ -398,33 +344,60 @@ class Random implements media {
// base on min, seconds are for chumps and chumpettes
$new_time = floor($row['time'] / 60);
- if ($fuzzy_time > 10) { return $results; }
+ if ($fuzzy_time > 100) {
+ break;;
+ }
- // If the new one would go voer skip!
- if (($time_total + $new_time) > $data['length']) { $fuzzy_time++; continue; }
+ // If the new one would go over skip!
+ if (($time_total + $new_time) > $data['length']) {
+ $fuzzy_time++;
+ continue;
+ }
$time_total = $time_total + $new_time;
$results[] = $row['id'];
// If there are less then 2 min of free space return
- if (($data['length'] - $time_total) < 2) { return $results; }
-
+ if (($data['length'] - $time_total) < 2) {
+ return $results;
+ }
} // if length does matter
- if (!$data['size_limit'] AND !$data['length']) {
+ if (!$data['size_limit'] && !$data['length']) {
$results[] = $row['id'];
}
} // end while results
-
- return $results;
-
+ switch ($type) {
+ case 'song':
+ return $results;
+ break;
+ case 'album':
+ $songs = array();
+ foreach ($results as $result) {
+ $album = new Album($result);
+ $songs = array_merge($songs, $album->get_songs());
+ }
+ return $songs;
+ break;
+ case 'artist':
+ $songs = array();
+ foreach ($results as $result) {
+ $artist = new Artist($result);
+ $songs = array_merge($songs, $artist->get_songs());
+ }
+ return $songs;
+ break;
+ default:
+ return false;
+ break;
+ }
} // advanced
/**
* get_type_name
- * This returns a 'purrty' name for the differnt random types
+ * This returns a 'purrty' name for the different random types
*/
public static function get_type_name($type) {
diff --git a/lib/class/search.class.php b/lib/class/search.class.php
new file mode 100644
index 00000000..f2c3cee9
--- /dev/null
+++ b/lib/class/search.class.php
@@ -0,0 +1,1033 @@
+<?php
+/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
+/*
+
+ Copyright (c) Ampache.org
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License v2
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+/**
+ * Search Class
+ * Search-related voodoo. Beware tentacles.
+ */
+
+class Search extends playlist_object {
+
+ public $searchtype;
+ public $rules;
+ public $logic_operator = 'AND';
+ public $type = 'public';
+
+ public $basetypes;
+ public $types;
+
+ /**
+ * constructor
+ */
+ public function __construct($searchtype = 'song', $id = '') {
+ $this->searchtype = $searchtype;
+ if ($id) {
+ $info = $this->get_info($id);
+ foreach ($info as $key=>$value) {
+ $this->$key = $value;
+ }
+
+ $this->rules = unserialize($this->rules);
+ }
+
+ // Define our basetypes
+
+ $this->basetypes['numeric'][] = array(
+ 'name' => 'gte',
+ 'description' => _('is greater than or equal to'),
+ 'sql' => '>='
+ );
+
+ $this->basetypes['numeric'][] = array(
+ 'name' => 'lte',
+ 'description' => _('is less than or equal to'),
+ 'sql' => '<='
+ );
+
+ $this->basetypes['numeric'][] = array(
+ 'name' => 'equal',
+ 'description' => _('is'),
+ 'sql' => '<=>'
+ );
+
+ $this->basetypes['numeric'][] = array(
+ 'name' => 'ne',
+ 'description' => _('is not'),
+ 'sql' => '<>'
+ );
+
+ $this->basetypes['numeric'][] = array(
+ 'name' => 'gt',
+ 'description' => _('is greater than'),
+ 'sql' => '>'
+ );
+
+ $this->basetypes['numeric'][] = array(
+ 'name' => 'lt',
+ 'description' => _('is less than'),
+ 'sql' => '<'
+ );
+
+
+ $this->basetypes['boolean'][] = array(
+ 'name' => 'true',
+ 'description' => _('is true')
+ );
+
+ $this->basetypes['boolean'][] = array(
+ 'name' => 'false',
+ 'description' => _('is false')
+ );
+
+
+ $this->basetypes['text'][] = array(
+ 'name' => 'contain',
+ 'description' => _('contains'),
+ 'sql' => 'LIKE',
+ 'preg_match' => array('/^/','/$/'),
+ 'preg_replace' => array('%', '%')
+ );
+
+ $this->basetypes['text'][] = array(
+ 'name' => 'notcontain',
+ 'description' => _('does not contain'),
+ 'sql' => 'NOT LIKE',
+ 'preg_match' => array('/^/','/$/'),
+ 'preg_replace' => array('%', '%')
+ );
+
+ $this->basetypes['text'][] = array(
+ 'name' => 'start',
+ 'description' => _('starts with'),
+ 'sql' => 'LIKE',
+ 'preg_match' => '/$/',
+ 'preg_replace' => '%'
+ );
+
+ $this->basetypes['text'][] = array(
+ 'name' => 'end',
+ 'description' => _('ends with'),
+ 'sql' => 'LIKE',
+ 'preg_match' => '/^/',
+ 'preg_replace' => '%'
+ );
+
+ $this->basetypes['text'][] = array(
+ 'name' => 'equal',
+ 'description' => _('is'),
+ 'sql' => '='
+ );
+
+ $this->basetypes['text'][] = array(
+ 'name' => 'sounds',
+ 'description' => _('sounds like'),
+ 'sql' => 'SOUNDS LIKE'
+ );
+
+ $this->basetypes['text'][] = array(
+ 'name' => 'notsounds',
+ 'description' => _('does not sound like'),
+ 'sql' => 'NOT SOUNDS LIKE'
+ );
+
+
+ $this->basetypes['boolean_numeric'][] = array(
+ 'name' => 'equal',
+ 'description' => _('is'),
+ 'sql' => '<=>'
+ );
+
+ $this->basetypes['boolean_numeric'][] = array(
+ 'name' => 'ne',
+ 'description' => _('is not'),
+ 'sql' => '<>'
+ );
+
+
+ $this->basetypes['boolean_subsearch'][] = array(
+ 'name' => 'equal',
+ 'description' => _('is'),
+ 'sql' => ''
+ );
+
+ $this->basetypes['boolean_subsearch'][] = array(
+ 'name' => 'ne',
+ 'description' => _('is not'),
+ 'sql' => 'NOT'
+ );
+
+
+ $this->basetypes['date'][] = array(
+ 'name' => 'lt',
+ 'description' => _('before'),
+ 'sql' => '>'
+ );
+
+ $this->basetypes['date'][] = array(
+ 'name' => 'gt',
+ 'description' => _('after'),
+ 'sql' => '>'
+ );
+
+ switch ($searchtype) {
+ case 'song':
+ $this->types[] = array(
+ 'name' => 'anywhere',
+ 'label' => _('Any searchable text'),
+ 'type' => 'text',
+ 'widget' => array('input', 'text')
+ );
+
+ $this->types[] = array(
+ 'name' => 'title',
+ 'label' => _('Title'),
+ 'type' => 'text',
+ 'widget' => array('input', 'text')
+ );
+
+ $this->types[] = array(
+ 'name' => 'album',
+ 'label' => _('Album'),
+ 'type' => 'text',
+ 'widget' => array('input', 'text')
+ );
+
+ $this->types[] = array(
+ 'name' => 'artist',
+ 'label' => _('Artist'),
+ 'type' => 'text',
+ 'widget' => array('input', 'text')
+ );
+
+ $this->types[] = array(
+ 'name' => 'comment',
+ 'label' => _('Comment'),
+ 'type' => 'text',
+ 'widget' => array('input', 'text')
+ );
+
+
+ $this->types[] = array(
+ 'name' => 'tag',
+ 'label' => _('Tag'),
+ 'type' => 'text',
+ 'widget' => array('input', 'text')
+ );
+
+ $this->types[] = array(
+ 'name' => 'file',
+ 'label' => _('Filename'),
+ 'type' => 'text',
+ 'widget' => array('input', 'text')
+ );
+
+ $this->types[] = array(
+ 'name' => 'year',
+ 'label' => _('Year'),
+ 'type' => 'numeric',
+ 'widget' => array('input', 'text')
+ );
+
+ $this->types[] = array(
+ 'name' => 'time',
+ 'label' => _('Length (in minutes)'),
+ 'type' => 'numeric',
+ 'widget' => array('input', 'text')
+ );
+
+ if (Config::get('ratings')) {
+ $this->types[] = array(
+ 'name' => 'rating',
+ 'label' => _('Rating'),
+ 'type' => 'numeric',
+ 'widget' => array(
+ 'select',
+ array(
+ '1 Star',
+ '2 Stars',
+ '3 Stars',
+ '4 Stars',
+ '5 Stars'
+ )
+ )
+ );
+ }
+
+ $this->types[] = array(
+ 'name' => 'bitrate',
+ 'label' => _('Bitrate'),
+ 'type' => 'numeric',
+ 'widget' => array(
+ 'select',
+ array(
+ '32',
+ '40',
+ '48',
+ '56',
+ '64',
+ '80',
+ '96',
+ '112',
+ '128',
+ '160',
+ '192',
+ '224',
+ '256',
+ '320'
+ )
+ )
+ );
+
+ $this->types[] = array(
+ 'name' => 'played',
+ 'label' => _('Played'),
+ 'type' => 'boolean',
+ 'widget' => array('input', 'hidden')
+ );
+
+ $this->types[] = array(
+ 'name' => 'added',
+ 'label' => _('Added'),
+ 'type' => 'date',
+ 'widget' => array('input', 'text')
+ );
+
+ $this->types[] = array(
+ 'name' => 'updated',
+ 'label' => _('Updated'),
+ 'type' => 'date',
+ 'widget' => array('input', 'text')
+ );
+
+ $catalogs = array();
+ foreach (Catalog::get_catalogs() as $catid) {
+ $catalog = new Catalog($catid);
+ $catalog->format();
+ $catalogs[$catid] = $catalog->f_name;
+ }
+ $this->types[] = array(
+ 'name' => 'catalog',
+ 'label' => _('Catalog'),
+ 'type' => 'boolean_numeric',
+ 'widget' => array('select', $catalogs)
+ );
+
+ $playlists = array();
+ foreach (Playlist::get_playlists() as $playlistid) {
+ $playlist = new Playlist($playlistid);
+ $playlist->format();
+ $playlists[$playlistid] = $playlist->f_name;
+ }
+ $this->types[] = array(
+ 'name' => 'playlist',
+ 'label' => _('Playlist'),
+ 'type' => 'boolean_numeric',
+ 'widget' => array('select', $playlists)
+ );
+
+ $playlists = array();
+ foreach (Search::get_searches() as $playlistid) {
+ // Slightly different from the above so we don't
+ // instigate a vicious loop.
+ $playlists[$playlistid] = Search::get_name_byid($playlistid);
+ }
+ $this->types[] = array(
+ 'name' => 'smartplaylist',
+ 'label' => _('Smart Playlist'),
+ 'type' => 'boolean_subsearch',
+ 'widget' => array('select', $playlists)
+ );
+ break;
+ case 'album':
+ $this->types[] = array(
+ 'name' => 'title',
+ 'label' => _('Title'),
+ 'type' => 'text',
+ 'widget' => array('input', 'text')
+ );
+
+ $this->types[] = array(
+ 'name' => 'year',
+ 'label' => _('Year'),
+ 'type' => 'numeric',
+ 'widget' => array('input', 'text')
+ );
+
+ if (Config::get('ratings')) {
+ $this->types[] = array(
+ 'name' => 'rating',
+ 'label' => _('Rating'),
+ 'type' => 'numeric',
+ 'widget' => array(
+ 'select',
+ array(
+ '1 Star',
+ '2 Stars',
+ '3 Stars',
+ '4 Stars',
+ '5 Stars'
+ )
+ )
+ );
+ }
+
+ $catalogs = array();
+ foreach (Catalog::get_catalogs() as $catid) {
+ $catalog = new Catalog($catid);
+ $catalog->format();
+ $catalogs[$catid] = $catalog->f_name;
+ }
+ $this->types[] = array(
+ 'name' => 'catalog',
+ 'label' => _('Catalog'),
+ 'type' => 'boolean_numeric',
+ 'widget' => array('select', $catalogs)
+ );
+
+
+ $this->types[] = array(
+ 'name' => 'tag',
+ 'label' => _('Tag'),
+ 'type' => 'text',
+ 'widget' => array('input', 'text')
+ );
+ break;
+ case 'video':
+ $this->types[] = array(
+ 'name' => 'filename',
+ 'label' => _('Filename'),
+ 'type' => 'text',
+ 'widget' => array('input', 'text')
+ );
+ break;
+ case 'artist':
+ $this->types[] = array(
+ 'name' => 'name',
+ 'label' => _('Name'),
+ 'type' => 'text',
+ 'widget' => array('input', 'text')
+ );
+ $this->types[] = array(
+ 'name' => 'tag',
+ 'label' => _('Tag'),
+ 'type' => 'text',
+ 'widget' => array('input', 'text')
+ );
+ break;
+ } // end switch on searchtype
+
+ } // end constructor
+
+ /**
+ * clean_request
+ * Sanitizes raw search data
+ */
+ public static function clean_request($data) {
+ foreach ($data as $key => $value) {
+ $prefix = substr($key, 0, 4);
+ $value = trim($value);
+
+ if ($prefix == 'rule' && strlen($value)) {
+ $request[$key] = Dba::escape($value);
+ }
+ } // end foreach $data
+
+ // Figure out if they want an AND based search or an OR based
+ // search
+ switch($data['operator']) {
+ case 'or':
+ $request['operator'] = 'OR';
+ break;
+ default:
+ $request['operator'] = 'AND';
+ break;
+ } // end switcn on operator
+
+ return $request;
+ } // end clean_request
+
+ /**
+ * get_name_byid
+ * Returns the name of the saved search corresponding to the given ID
+ */
+ public static function get_name_byid($id) {
+ $sql = "SELECT `name` FROM `search` WHERE `id`='$id'";
+ $db_results = Dba::read($sql);
+ $r = Dba::fetch_assoc($db_results);
+ return $r['name'];
+ } // end get_name_byid
+
+ /**
+ * get_searches
+ * Return the IDs of all saved searches accessible by the current user.
+ */
+ public static function get_searches() {
+ $sql = "SELECT `id` from `search` WHERE `type`='public' OR " .
+ "`user`='" . $GLOBALS['user']->id . "' ORDER BY `name`";
+ $db_results = Dba::read($sql);
+
+ $results = array();
+
+ while ($row = Dba::fetch_assoc($db_results)) {
+ $results[] = $row['id'];
+ }
+
+ return $results;
+ } // end get_searches
+
+ /**
+ * run
+ * This function actually runs the search, and returns an array of the
+ * results.
+ */
+ public static function run($data) {
+ $limit = intval($data['limit']);
+ /* Create an array of the object we need to search on */
+ $data = Search::clean_request($data);
+
+ $search = new Search($_REQUEST['type']);
+ $search->parse_rules($data);
+
+ /* Generate BASE SQL */
+
+ if ($limit > 0) {
+ $limit_sql = " LIMIT " . $limit;
+ }
+
+ $search_info = $search->to_sql();
+ $sql = $search_info['base'] . ' ' . $search_info['table_sql'] .
+ ' WHERE ' . $search_info['where_sql'] . " $limit_sql";
+
+ $db_results = Dba::read($sql);
+
+ $results = array();
+
+ while ($row = Dba::fetch_assoc($db_results)) {
+ $results[] = $row['id'];
+ }
+
+ return $results;
+ } // run
+
+ /**
+ * delete
+ * Does what it says on the tin.
+ */
+ public function delete() {
+ $id = Dba::escape($this->id);
+ $sql = "DELETE FROM `search` WHERE `id`='$id'";
+ $db_results = Dba::write($sql);
+
+ return true;
+ } // end delete
+
+ /**
+ * format
+ * Gussy up the data
+ */
+ public function format() {
+ parent::format();
+ $this->f_link = '<a href="' . Config::get('web_path') . '/smartplaylist.php?action=show_playlist&amp;playlist_id=' . $this->id . '">' . $this->f_name . '</a>';
+ } // end format
+
+ /**
+ * get_items
+ * return an array of the items output by our search (part of the
+ * playlist interface).
+ */
+ public function get_items() {
+ $results = array();
+
+ $sql = $this->to_sql();
+ $sql = $sql['base'] . ' ' . $sql['table_sql'] . ' WHERE ' .
+ $sql['where_sql'];
+
+ $db_results = Dba::read($sql);
+
+ while ($row = Dba::fetch_assoc($db_results)) {
+ $results[] = array(
+ 'object_id' => $row['id'],
+ 'type' => $this->searchtype
+ );
+ }
+
+ return $results;
+ } // end get_items
+
+ /**
+ * name_to_basetype
+ * Iterates over our array of types to find out the basetype for
+ * the passed string.
+ */
+ public function name_to_basetype($name) {
+ foreach ($this->types as $type) {
+ if ($type['name'] == $name) {
+ return $type['type'];
+ }
+ }
+ return false;
+ } // end name_to_basetype
+
+ /**
+ * parse_rules
+ * Takes an array of sanitized search data from the form and generates
+ * our real array from it.
+ */
+ public function parse_rules($data) {
+ $this->rules = array();
+ foreach ($data as $rule => $value) {
+ if (preg_match('/^rule_(\d)$/', $rule, $ruleID)) {
+ $ruleID = $ruleID[1];
+ foreach (explode('|', $data['rule_' . $ruleID . '_input']) as $input) {
+ $this->rules[] = array(
+ $value,
+ $this->basetypes[$this->name_to_basetype($value)][$data['rule_' . $ruleID . '_operator']]['name'],
+ $input
+ );
+ }
+ }
+ }
+ $this->logic_operator = $data['operator'];
+ } // end parse_rules
+
+ /**
+ * save
+ * Save this search to the database for use as a smart playlist
+ */
+ public function save() {
+ // Make sure we have a unique name
+ if (! $this->name) {
+ $this->name = $GLOBALS['user']->username . ' - ' . date("Y-m-d H:i:s",time());
+ }
+ $sql = "SELECT `id` FROM `search` WHERE `name`='$this->name'";
+ $db_results = Dba::read($sql);
+ if (Dba::num_rows($db_results)) {
+ $this->name .= uniqid('', true);
+ }
+
+ // clean up variables for insert
+ $name = Dba::escape($this->name);
+ $user = Dba::escape($GLOBALS['user']->id);
+ $type = Dba::escape($this->type);
+ $rules = serialize($this->rules);
+ $logic_operator = $this->logic_operator;
+
+ $sql = "INSERT INTO `search` (`name`, `type`, `user`, `rules`, `logic_operator`) VALUES ('$name', '$type', '$user', '$rules', '$logic_operator')";
+ $db_results = Dba::write($sql);
+ $insert_id = Dba::insert_id();
+ $this->id = $insert_id;
+ return $insert_id;
+ } // end save
+
+
+ /**
+ * to_js
+ * Outputs the javascript necessary to re-show the current set of
+ * rules.
+ */
+ public function to_js() {
+ foreach ($this->rules as $rule) {
+ $js .= '<script type="text/javascript">' .
+ 'SearchRow.add("' . $rule[0] . '","' .
+ $rule[1] . '","' . $rule[2] . '"); </script>';
+ }
+ return $js;
+ } // end to_js
+
+ /**
+ * to_sql
+ * Call the appropriate real function
+ */
+ public function to_sql() {
+ return call_user_func(
+ array($this, $this->searchtype . "_to_sql"));
+ } // end to_sql
+
+ /**
+ * update
+ * This function updates the saved version with the current settings
+ */
+ public function update() {
+ if (!$this->id) {
+ return false;
+ }
+
+ $name = Dba::escape($this->name);
+ $user = Dba::escape($GLOBALS['user']->id);
+ $type = Dba::escape($this->type);
+ $rules = serialize($this->rules);
+ $logic_operator = $this->logic_operator;
+
+ $sql = "UPDATE `search` SET `name`='$name', `type`='$type', `rules`='$rules', `logic_operator`='$logic_operator' WHERE `id`='" . Dba::escape($this->id) . "'";
+ $db_results = Dba::write($sql);
+ return $db_results;
+ } // end update
+
+ /**
+ * mangle_data
+ * Private convenience function. Mangles the input according to a set
+ * of predefined rules so that we don't have to include this logic in
+ * foo_to_sql.
+ */
+ private function mangle_data($data, $type, $operator) {
+ if ($operator['preg_match']) {
+ $data = preg_replace(
+ $operator['preg_match'],
+ $operator['preg_replace'],
+ $data
+ );
+ }
+
+ if ($type == 'numeric') {
+ return intval($data);
+ }
+
+ if ($type == 'boolean') {
+ return make_bool($input);
+ }
+
+ return $data;
+ } // end mangle_data
+
+ /**
+ * album_to_sql
+ * Handles the generation of the SQL for album searches.
+ */
+ private function album_to_sql() {
+ $sql_logic_operator = $this->logic_operator;
+
+ $where = array();
+ $table = array();
+ $join = array();
+
+ foreach ($this->rules as $rule) {
+ $type = $this->name_to_basetype($rule[0]);
+ foreach ($this->basetypes[$type] as $operator) {
+ if ($operator['name'] == $rule[1]) {
+ break;
+ }
+ }
+ $input = $this->mangle_data($rule[2], $type, $operator);
+ $sql_match_operator = $operator['sql'];
+
+ switch ($rule[0]) {
+ case 'title':
+ $where[] = "`album`.`name` $sql_match_operator '$input'";
+ break;
+ case 'year':
+ $where[] = "`album`.`year` $sql_match_operator '$input'";
+ break;
+ case 'rating':
+ $where[] = " `realrating`.`rating` $sql_match_operator '$input'";
+ $join['rating'] = true;
+ break;
+ case 'catalog':
+ $where[] = "`song`.`catalog` $sql_match_operator '$input'";
+ $join['song'] = true;
+ break;
+ case 'tag':
+ $where[] = "`realtag`.`name` $sql_match_operator '$input'";
+ $join['tag'] = true;
+ break;
+ default:
+ // Nae laird!
+ break;
+ } // switch on ruletype
+ } // foreach rule
+
+ $where_sql = implode(" $sql_logic_operator ", $where);
+
+ if ($join['tag']) {
+ $table['tag'] = "LEFT JOIN (SELECT `object_id`, `name` FROM `tag` " .
+ "LEFT JOIN `tag_map` ON `tag`.`id`=`tag_map`.`tag_id` " .
+ "WHERE `tag_map`.`object_type`='album') AS realtag " .
+ "ON `album`.`id`=`realtag`.`object_id`";
+ }
+ if ($join['song']) {
+ $table['song'] = "LEFT JOIN `song` ON `song`.`album`=`album`.`id`";
+ }
+ if ($join['rating']) {
+ $userid = $GLOBALS['user']->id;
+ $table['rating'] = "LEFT JOIN " .
+ "(SELECT `object_id`, `rating` FROM `rating` " .
+ "WHERE `object_type`='album' AND `user`='$userid' " .
+ "UNION " .
+ "SELECT `object_id`, FLOOR(AVG(`rating`)) AS 'rating' FROM `rating` " .
+ "WHERE `object_type`='album' AND " .
+ "`object_id` NOT IN (SELECT `object_id` FROM `rating` " .
+ "WHERE `object_type`='album' AND `user`='$userid') " .
+ "GROUP BY `object_id` " .
+ ") AS realrating ON `album`.`id` = `realrating`.`object_id`";
+ }
+
+ $table_sql = implode(' ', $table);
+
+ return array(
+ 'base' => 'SELECT DISTINCT(`album`.`id`) FROM `album`',
+ 'join' => $join,
+ 'where' => $where,
+ 'where_sql' => $where_sql,
+ 'table' => $table,
+ 'table_sql' => $table_sql
+ );
+ } // album_to_sql
+
+ /**
+ * artist_to_sql
+ * Handles the generation of the SQL for artist searches.
+ */
+ private function artist_to_sql() {
+ $sql_logic_operator = $this->logic_operator;
+ $where = array();
+ $table = array();
+ $join = array();
+
+ foreach ($this->rules as $rule) {
+ $type = $this->name_to_basetype($rule[0]);
+ foreach ($this->basetypes[$type] as $operator) {
+ if ($operator['name'] == $rule[1]) {
+ break;
+ }
+ }
+ $input = $this->mangle_data($rule[2], $type, $operator);
+ $sql_match_operator = $operator['sql'];
+
+ switch ($rule[0]) {
+ case 'name':
+ $where[] = "`artist`.`name` $sql_match_operator '$input'";
+ break;
+ case 'tag':
+ $where[] = " realtag`.`name` $sql_match_operator '$input'";
+ $join['tag'] = true;
+ break;
+ default:
+ // Nihil
+ break;
+ } // switch on ruletype
+ } // foreach rule
+
+ $where_sql = implode(" $sql_logic_operator ", $where);
+
+ if ($join['tag']) {
+ $table['tag'] = "LEFT JOIN (SELECT `object_id`, `name` FROM `tag` " .
+ "LEFT JOIN `tag_map` ON `tag`.`id`=`tag_map`.`tag_id` " .
+ "WHERE `tag_map`.`object_type`='artist') AS realtag " .
+ "ON `artist`.`id`=`realtag`.`object_id`";
+ }
+
+ $table_sql = implode(' ', $table);
+
+ return array(
+ 'base' => 'SELECT DISTINCT(`artist`.`id`) FROM `artist`',
+ 'join' => $join,
+ 'where' => $where,
+ 'where_sql' => $where_sql,
+ 'table' => $table,
+ 'table_sql' => $table_sql
+ );
+ } // artist_to_sql
+
+ /**
+ * song_to_sql
+ * Handles the generation of the SQL for song searches.
+ */
+ private function song_to_sql() {
+ $sql_logic_operator = $this->logic_operator;
+
+ $where = array();
+ $table = array();
+ $join = array();
+
+ foreach ($this->rules as $rule) {
+ $type = $this->name_to_basetype($rule[0]);
+ foreach ($this->basetypes[$type] as $operator) {
+ if ($operator['name'] == $rule[1]) {
+ break;
+ }
+ }
+ $input = $this->mangle_data($rule[2], $type, $operator);
+ $sql_match_operator = $operator['sql'];
+
+ switch ($rule[0]) {
+ case 'anywhere':
+ $where[] = "(`artist`.`name` $sql_match_operator '$input' OR `album`.`name` $sql_match_operator '$input' OR `song_data`.`comment` $sql_match_operator '$input' OR `song`.`file` $sql_match_operator '$input' OR `song`.`title` $sql_match_operator '$input')";
+ $join['album'] = true;
+ $join['artist'] = true;
+ $join['song_data'] = true;
+ break;
+ case 'tag':
+ $where[] = "`realtag`.`name` $sql_match_operator '$input'";
+ $join['tag'] = true;
+ break;
+ case 'title':
+ $where[] = "`song`.`title` $sql_match_operator '$input'";
+ break;
+ case 'album':
+ $where[] = "`album`.`name` $sql_match_operator '$input'";
+ $join['album'] = true;
+ break;
+ case 'artist':
+ $where[] = "`artist`.`name` $sql_match_operator '$input'";
+ $join['artist'] = true;
+ break;
+ case 'time':
+ $input = $input * 60;
+ $where[] = "`song`.`time` $sql_match_operator '$input'";
+ break;
+ case 'file':
+ $where[] = "`song`.`file` $sql_match_operator '$input'";
+ break;
+ case 'year':
+ $where[] = "`song`.`year` $sql_match_operator '$input'";
+ break;
+ case 'comment':
+ $where[] = "`song_data`.`comment` $sql_match_operator '$input'";
+ $join['song_data'] = true;
+ break;
+ case 'played':
+ $where[] = " `song`.`played` = '$input'";
+ break;
+ case 'bitrate':
+ $input = $input * 1000;
+ $where[] = "`song`.`bitrate` $sql_match_operator '$input'";
+ break;
+ case 'rating':
+ $where[] = "`realrating`.`rating` $sql_match_operator '$input'";
+ $join['rating'] = true;
+ break;
+ case 'catalog':
+ $where[] = "`song`.`catalog` $sql_match_operator '$input'";
+ break;
+ case 'playlist':
+ $join['playlist_data'] = true;
+ $where[] = "`playlist_data`.`playlist` $sql_match_operator '$input'";
+ break;
+ case 'smartplaylist':
+ $subsearch = new Search('song', $input);
+ $subsql = $subsearch->to_sql();
+ $where[] = "$sql_match_operator (" . $subsql['where_sql'] . ")";
+ $join = array_merge($subsql['join'], $join);
+ break;
+ case 'added':
+ $input = strtotime($input);
+ $where[] = "`song`.`addition_time` $sql_match_operator $input";
+ break;
+ case 'updated':
+ $input = strtotime($input);
+ $where[] = "`song`.`update_time` $sql_match_operator $input";
+ default:
+ // NOSSINK!
+ break;
+ } // end switch on type
+ } // end foreach over rules
+
+ $where_sql = implode(" $sql_logic_operator ", $where);
+
+ // now that we know which things we want to JOIN...
+ if ($join['artist']) {
+ $table['artist'] = "LEFT JOIN `artist` ON `song`.`artist`=`artist`.`id`";
+ }
+ if ($join['album']) {
+ $table['album'] = "LEFT JOIN `album` ON `song`.`album`=`album`.`id`";
+ }
+ if ($join['song_data']) {
+ $table['song_data'] = "LEFT JOIN `song_data` ON `song`.`id`=`song_data`.`song_id`";
+ }
+ if ($join['tag']) {
+ $table['tag'] = "LEFT JOIN (SELECT `object_id`, `name` FROM `tag` " .
+ "LEFT JOIN `tag_map` ON `tag`.`id`=`tag_map`.`tag_id` " .
+ "WHERE `tag_map`.`object_type`='song') AS realtag " .
+ "ON `song`.`id`=`realtag`.`object_id`";
+ }
+ if ($join['rating']) {
+ // We do a join on ratings from the table with a
+ // preference for our own and fall back to the FLOORed
+ // average of everyone's rating if it's a song we
+ // haven't rated.
+ $userid = $GLOBALS['user']->id;
+ $table['rating'] = "LEFT JOIN " .
+ "(SELECT `object_id`, `rating` FROM `rating` " .
+ "WHERE `object_type`='song' AND `user`='$userid' " .
+ "UNION " .
+ "SELECT `object_id`, FLOOR(AVG(`rating`)) AS 'rating' FROM `rating` " .
+ "WHERE `object_type`='song' AND " .
+ "`object_id` NOT IN (SELECT `object_id` FROM `rating` " .
+ "WHERE `object_type`='song' AND `user`='$userid') " .
+ "GROUP BY `object_id` " .
+ ") AS realrating ON `song`.`id`=`realrating`.`object_id`";
+ }
+ if ($join['playlist_data']) {
+ $table['playlist_data'] = "LEFT JOIN `playlist_data` ON `song`.`id`=`playlist_data`.`object_id` AND `playlist_data`.`object_type`='song'";
+ }
+
+ $table_sql = implode(' ', $table);
+
+ return array(
+ 'base' => 'SELECT DISTINCT(`song`.`id`) FROM `song`',
+ 'join' => $join,
+ 'where' => $where,
+ 'where_sql' => $where_sql,
+ 'table' => $table,
+ 'table_sql' => $table_sql
+ );
+ } // end song_to_sql
+
+ /**
+ * video_to_sql
+ * Handles the generation of the SQL for video searches.
+ */
+ private function video_to_sql() {
+ $sql_logic_operator = $this->logic_operator;
+
+ $where = array();
+
+
+ foreach ($this->rules as $rule) {
+ $type = $this->name_to_basetype($rule[0]);
+ foreach ($this->basetypes[$type] as $operator) {
+ if ($operator['name'] == $rule[1]) {
+ break;
+ }
+ }
+ $input = $this->mangle_data($rule[2], $type, $operator);
+ $sql_match_operator = $operator['sql'];
+
+ switch ($rule[0]) {
+ case 'filename':
+ $where[] = "`video`.`file` $sql_match_operator '$input'";
+ break;
+ default:
+ // WE WILLNA BE FOOLED AGAIN!
+ } // switch on ruletype
+ } // foreach rule
+
+ $where_sql = explode(" $sql_logic_operator ", $where);
+
+ return array(
+ 'base' => 'SELECT DISTINCT(`video`.`id`) FROM `video`',
+ 'where' => $where,
+ 'where_sql' => $where_sql
+ );
+ } // end video_to_sql
+
+} // end of Search class
+?>
diff --git a/lib/class/update.class.php b/lib/class/update.class.php
index 0dc1f726..14478de0 100644
--- a/lib/class/update.class.php
+++ b/lib/class/update.class.php
@@ -371,6 +371,9 @@ class Update {
$update_string = '- Modify tmp_browse to allow caching of multiple browses per session.<br />';
$version[] = array('version' => '360005','description' => $update_string);
+ $update_string = '- Add table for dynamic playlists.<br />';
+ $version[] = array('version' => '360006','description' => $update_string);
+
return $version;
} // populate_version
@@ -1979,5 +1982,24 @@ class Update {
self::set_version('db_version','360005');
} // update_360005
+ /**
+ * update_360006
+ * This adds the table for newsearch/dynamic playlists
+ */
+ public static function update_360006() {
+ $sql = "CREATE TABLE `search` (
+ `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+ `user` int(11) NOT NULL,
+ `type` enum('private','public') CHARACTER SET utf8 DEFAULT NULL,
+ `rules` mediumtext COLLATE utf8_unicode_ci NOT NULL,
+ `name` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
+ `logic_operator` varchar(3) CHARACTER SET utf8 DEFAULT NULL,
+ PRIMARY KEY (`id`)
+ ) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci";
+ $db_results = Dba::write($sql);
+
+ self::set_version('db_version','360006');
+ }
+
} // end update class
?>
diff --git a/lib/init.php b/lib/init.php
index d492dc8b..d43c3fc2 100644
--- a/lib/init.php
+++ b/lib/init.php
@@ -134,7 +134,6 @@ $results['mysql_db'] = $results['database_name'];
define('INIT_LOADED','1');
// Library and module includes we can't do with the autoloader
-require_once $prefix . '/lib/search.php';
require_once $prefix . '/lib/preferences.php';
require_once $prefix . '/lib/log.lib.php';
require_once $prefix . '/lib/ui.lib.php';
@@ -143,6 +142,7 @@ require_once $prefix . '/lib/batch.lib.php';
require_once $prefix . '/lib/themes.php';
require_once $prefix . '/lib/class/localplay.abstract.php';
require_once $prefix . '/lib/class/database_object.abstract.php';
+require_once $prefix . '/lib/class/playlist_object.abstract.php';
require_once $prefix . '/lib/class/media.interface.php';
require_once $prefix . '/modules/getid3/getid3.php';
require_once $prefix . '/modules/nusoap/nusoap.php';
diff --git a/lib/javascript/search-data.php b/lib/javascript/search-data.php
new file mode 100644
index 00000000..11ef68d9
--- /dev/null
+++ b/lib/javascript/search-data.php
@@ -0,0 +1,49 @@
+<?php
+/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
+/**
+
+Copyright (c) Ampache.org
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License v2
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+require_once '../init.php';
+
+function arrayToJSON($array) {
+ $json = '{ ';
+ foreach ($array as $key => $value) {
+ $json .= '"' . $key . '" : ';
+ if (is_array($value)) {
+ $json .= arrayToJSON($value);
+ }
+ else {
+ $json .= '"' . $value . '"';
+ }
+ $json .= ' , ';
+ }
+ $json = rtrim($json, ', ');
+ return $json . ' }';
+}
+
+Header('content-type: application/x-javascript');
+
+$search = new Search($_REQUEST['type']);
+
+echo 'var types = $H(\'';
+echo arrayToJSON($search->types) . "'.evalJSON());\n";
+echo 'var basetypes = $H(\'';
+echo arrayToJSON($search->basetypes) . "'.evalJSON());\n";
+echo 'removeIcon = \'<a href="javascript: void(0)">' . get_user_icon('disable', _('Remove')) . '</a>\';';
+?>
diff --git a/lib/javascript/search.js b/lib/javascript/search.js
new file mode 100644
index 00000000..35995001
--- /dev/null
+++ b/lib/javascript/search.js
@@ -0,0 +1,168 @@
+// vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab:
+//
+// Copyright (c) Ampache.org
+// All rights reserved.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License v2
+// as published by the Free Software Foundation.
+//
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+var rowIter = 1;
+var rowCount = 0;
+
+var SearchRow = {
+ add: function(ruleType, operator, input) {
+ if (typeof(ruleType) != 'string') {
+ ruleType = 0;
+ }
+ else {
+ types.each(function(i) {
+ if (i.value.name == ruleType) {
+ ruleType = i.key;
+ throw $break;
+ }
+ });
+ }
+
+ if (typeof(operator) != 'string') {
+ operator = 0;
+ }
+ else {
+ $H(basetypes.get(types.get(ruleType).type)).each(function(i) {
+ if (i.value.name == operator) {
+ operator = i.key;
+ throw $break;
+ }
+ });
+ }
+
+ var row = document.createElement('tr');
+ var cells = new Array();
+ for (var i = 0 ; i < 5 ; i++) {
+ cells[i] = document.createElement('td');
+ }
+
+ cells[0].appendChild(SearchRow.constructOptions(ruleType, rowIter));
+ cells[1].appendChild(SearchRow.constructOperators(ruleType, rowIter, operator));
+ cells[2].appendChild(SearchRow.constructInput(ruleType, rowIter, input));
+ cells[3].innerHTML = removeIcon;
+
+ cells.each(function(i) {
+ row.appendChild(i);
+ });
+
+ $('searchtable').appendChild(row);
+ rowCount++;
+
+ Event.observe(cells[3], 'click', function(e){if(rowCount > 1) { Element.remove(this.parentNode); rowCount--; }});
+
+ rowIter++;
+ },
+ constructInput: function(ruleType, ruleNumber, input) {
+ if (input === null || input === undefined) {
+ input = '';
+ }
+
+ widget = $H(types.get(ruleType).widget);
+
+ var inputNode = document.createElement(widget.get('0'));
+ inputNode.id = 'rule_' + ruleNumber + '_input';
+ inputNode.name = 'rule_' + ruleNumber + '_input';
+
+ switch(widget.get('0')) {
+ case 'input':
+ inputNode.setAttribute('type', widget.get('1'));
+ inputNode.setAttribute('value', input);
+ break;
+ case 'select':
+ $H(widget.get('1')).each(function(i) {
+ var option = document.createElement('option');
+ if ( isNaN(parseInt(i.value)) ) {
+ realvalue = i.key;
+ }
+ else {
+ realvalue = parseInt(i.value);
+ }
+ if ( input == realvalue ) {
+ option.selected = true;
+ }
+ option.value = realvalue;
+ option.innerHTML = i.value;
+ inputNode.appendChild(option);
+ });
+ break;
+ }
+
+ return inputNode;
+ },
+ constructOptions: function(ruleType, ruleNumber) {
+ var optionsNode = document.createElement('select');
+ optionsNode.id = 'rule_' + ruleNumber;
+ optionsNode.name = 'rule_' + ruleNumber;
+
+ types.each(function(i) {
+ var option = document.createElement('option');
+ option.innerHTML = i.value.label;
+ option.value = i.value.name;
+ if ( i.key == ruleType ) {
+ option.selected = true;
+ }
+ optionsNode.appendChild(option);
+ });
+
+ Event.observe(optionsNode, 'change', SearchRow.update);
+
+ return optionsNode;
+ },
+ constructOperators: function(ruleType, ruleNumber, operator) {
+ var operatorNode = document.createElement('select');
+ operatorNode.id = 'rule_' + ruleNumber + '_operator';
+ operatorNode.name = 'rule_' + ruleNumber + '_operator';
+
+ basetype = types.get(ruleType).type;
+ operatorNode.className = 'operator' + basetype;
+
+ $H(basetypes.get(basetype)).each(function(i) {
+ var option = document.createElement('option');
+ option.innerHTML = i.value.description;
+ option.value = i.key;
+ if (i.key == operator) {
+ option.selected = true;
+ }
+ operatorNode.appendChild(option);
+ });
+
+ return operatorNode;
+ },
+ update: function() {
+ var r_findID = /rule_(\d+)/;
+ var targetID = r_findID.exec(this.id)[1];
+
+ var operator = $('rule_' + targetID + '_operator');
+ if (operator.className != 'operator' + types.get(this.selectedIndex).type) {
+ var operator_cell = operator.parentNode;
+ Element.remove(operator);
+ operator_cell.appendChild(SearchRow.constructOperators(this.selectedIndex, targetID));
+ }
+
+ var input = $('rule_' + targetID + '_input');
+
+ if (input.type == 'text') {
+ var oldinput = input.value;
+ }
+
+ var input_cell = input.parentNode;
+ Element.remove(input);
+ input_cell.appendChild(SearchRow.constructInput(this.selectedIndex, targetID, oldinput));
+ }
+};
diff --git a/lib/search.php b/lib/search.php
deleted file mode 100644
index 992b01d2..00000000
--- a/lib/search.php
+++ /dev/null
@@ -1,294 +0,0 @@
-<?php
-/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
-/**
- * Search Library
- *
- * This library handles all the searching!
- *
- * PHP version 5
- *
- * LICENSE: GNU General Public License, version 2 (GPLv2)
- * Copyright (c) 2001 - 2011 Ampache.org All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License v2
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @category Search
- * @package Library
- * @author Karl Vollmer <vollmer@ampache.org>
- * @copyright 2001 - 2011 Ampache.org
- * @license http://opensource.org/licenses/gpl-2.0 GPLv2
- * @version PHP 5.2
- * @link http://www.ampache.org/
- * @since File available since Release 1.0
- */
-
-/**
- * run_search
- * this function actually runs the search, and returns an array of the results. Unlike the previous
- * function it does not do the display work its self.
- */
-function run_search($data) {
-
- /* Create an array of the object we need to search on */
- foreach ($data as $key=>$value) {
- /* Get the first two chars to check
- * and see if it's s_
- */
- $prefix = substr($key,0,2);
- $value = trim($value);
-
- if ($prefix == 's_' AND strlen($value)) {
- $true_name = substr($key,2,strlen($key));
- $search[$true_name] = Dba::escape($value);
- }
-
- } // end foreach
-
- /* Figure out if they want a AND based search or a OR based search */
- switch($_REQUEST['operator']) {
- case 'or':
- $operator = 'OR';
- break;
- default:
- $operator = 'AND';
- break;
- } // end switch on operator
-
- /* Figure out what type of method they would like to use, exact or fuzzy */
- switch($_REQUEST['method']) {
- case 'fuzzy':
- $method = "LIKE '%__%'";
- break;
- default:
- $method = "= '__'";
- break;
- } // end switch on method
-
- $limit = intval($_REQUEST['limit']);
-
- /* Switch, and run the correct function */
- switch($_REQUEST['object_type']) {
- case 'artist':
- case 'album':
- case 'song':
- $function_name = 'search_' . $_REQUEST['object_type'];
- if (function_exists($function_name)) {
- $results = call_user_func($function_name,$search,$operator,$method,$limit);
- return $results;
- }
- break;
- default:
- $results = search_song($search,$operator,$method,$limit);
- return $results;
- break;
- } // end switch
-
- return array();
-
-} // run_search
-
-/**
- * search_song
- * This function deals specificly with returning song object for the run_search
- * function, it assumes that our root table is songs
- * @package Search
- * @catagory Search
- */
-function search_song($data,$operator,$method,$limit) {
-
- /* Generate BASE SQL */
-
- $where_sql = '';
- $table_sql = '';
- $group_sql = ' GROUP BY';
- $select_sql = ',';
- $field_sql = '';
- $order_sql = '';
-
- if ($limit > 0) {
- $limit_sql = " LIMIT $limit";
- }
-
- foreach ($data as $type=>$value) {
-
- /* Create correct Value statement based on method */
-
- $value_string = str_replace("__",$value,$method);
-
- switch ($type) {
- case 'all':
- $additional_soundex = false;
-
- if (!(strpos($value, '-'))) // if we want a fuzzier search
- $additional_soundex = true;
-
- $where_sql = "( MATCH (`artist2`.`name`, `album2`.`name`, `song`.`title`) AGAINST ('$value' IN BOOLEAN MODE)";
-
- if ($additional_soundex) {
- $where_sql.= " OR `artist2`.`name` SOUNDS LIKE '$value'";
- $where_sql.= " OR `album2`.`name` SOUNDS LIKE '$value'";
- $where_sql.= " OR `song`.`title` SOUNDS LIKE '$value' ";
- }
- $where_sql .= ") $operator";
-
- $table_sql = " LEFT JOIN `album` as `album2` ON `song`.`album`=`album2`.`id`";
- $table_sql.= " LEFT JOIN `artist` AS `artist2` ON `song`.`artist`=`artist2`.`id`";
-
- $order_sql = " ORDER BY";
-
- $order_sql.= " MATCH (`artist2`.`name`) AGAINST ('$value' IN BOOLEAN MODE)";
- if ($additional_soundex) $order_sql.= " + (SOUNDEX(`artist2`.`name`)=SOUNDEX('$value')) DESC,"; else $order_sql.= " DESC,";
-
- $order_sql.= " MATCH (`album2`.`name`) AGAINST ('$value' IN BOOLEAN MODE)";
- if ($additional_soundex) $order_sql.= " + (SOUNDEX(`album2`.`name`)=SOUNDEX('$value')) DESC,"; else $order_sql.= " DESC,";
-
- $order_sql.= " MATCH (`song`.`title`) AGAINST ('$value' IN BOOLEAN MODE)";
- if ($additional_soundex) $order_sql.= " + (SOUNDEX(`song`.`title`)=SOUNDEX('$value')) DESC,"; else $order_sql.= " DESC,";
-
- $order_sql.= " `artist2`.`name`,";
- $order_sql.= " `album2`.`name`,";
- $order_sql.= " `song`.`track`,";
- $order_sql.= " `song`.`title`";
- break;
- case 'title':
- $where_sql .= " `song`.`title` $value_string $operator";
- break;
- case 'album':
- $where_sql .= " `album`.`name` $value_string $operator";
- $table_sql .= " LEFT JOIN `album` ON `song`.`album`=`album`.`id`";
- break;
- case 'artist':
- $where_sql .= " `artist`.`name` $value_string $operator";
- $table_sql .= " LEFT JOIN `artist` ON `song`.`artist`=`artist`.`id` ";
- break;
- case 'year':
- if (empty($data["year2"]) && is_numeric($data["year"])) {
- $where_sql .= " `song`.`year` $value_string $operator";
- }
- elseif (!empty($data["year"]) && is_numeric($data["year"]) && !empty($data["year2"]) && is_numeric($data["year2"])) {
- $where_sql .= " (`song`.`year` BETWEEN ".$data["year"]." AND ".$data["year2"].") $operator";
- }
- break;
- case 'time':
- if (!empty($data['time2'])) {
- $where_sql .= " `song`.`time` <= " . Dba::escape(intval($data['time2'])*60) . " $operator";
- }
- if (!empty($data['time'])) {
- $where_sql .= " `song`.`time` >= " . Dba::escape(intval($data['time'])*60) . " $operator";
- }
- break;
- case 'filename':
- $where_sql .= " `song`.`file` $value_string $operator";
- break;
- case 'comment':
- $table_sql .= ' INNER JOIN `song_data` ON `song`.`id`=`song_data`.`song_id`';
- $where_sql .= " `song_data`.`comment` $value_string $operator";
- break;
- case 'played':
- /* This is a 0/1 value so bool it */
- $value = make_bool($value);
- $where_sql .= " `song`.`played` = '$value' $operator";
- break;
- case 'minbitrate':
- $value = intval($value);
- $where_sql .= " `song`.`bitrate` >= ('$value'*1000) $operator";
- break;
- case 'rating':
- $value = intval($value);
- $userid = $GLOBALS['user']->id;
- $rcomparison = '>=';
- if ($_REQUEST['s_rating_operator'] == '1') {
- $rcomparison = '<=';
- }
- elseif ($_REQUEST['s_rating_operator'] == '2') {
- $rcomparison = '<=>';
- }
- // Complex SQL follows
- // We do a join on ratings from the table with a
- // preference for our own and fall back to the
- // FLOORed average of everyone's rating if it's
- // a song we haven't rated.
- if ($operator == 'AND') {
- $table_sql .= ' INNER JOIN';
- }
- else {
- $table_sql .= ' LEFT JOIN';
- }
- $table_sql .= " (SELECT `object_id`, `rating` FROM `rating` WHERE `object_type`='song' AND `user`='$userid'
- UNION
- SELECT `object_id`, FLOOR(AVG(`rating`)) AS 'rating' FROM `rating`
- WHERE `object_type`='song' AND
- `object_id` NOT IN (SELECT `object_id` FROM `rating` WHERE `object_type`='song' AND `user`='$userid')
- GROUP BY `object_id`
- ) AS realrating ON `song`.`id` = `realrating`.`object_id`";
-
- $where_sql .= " `realrating`.`rating` $rcomparison '$value' $operator";
- break;
- case 'tag':
-
- // Fill it with one value to prevent sql error on no results
- $ids = array('0');
-
- $tag_sql = "SELECT `object_id` FROM `tag` LEFT JOIN `tag_map` ON `tag`.`id`=`tag_map`.`tag_id` " .
- "WHERE `tag_map`.`object_type`='song' AND `tag`.`name` $value_string ";
- $db_results = Dba::read($tag_sql);
-
- while ($row = Dba::fetch_assoc($db_results)) {
- $ids[] = $row['object_id'];
- }
-
- $where_sql = " `song`.`id` IN (" . implode(',',$ids) . ") $operator";
-
- break;
- default:
- // Notzing!
- break;
- } // end switch on type
-
-
- } // foreach data
-
- /* Trim off the extra $method's and ,'s then combine the sucka! */
- $where_sql = rtrim($where_sql,$operator);
- $group_sql = rtrim($group_sql,',');
- $select_sql = rtrim($select_sql,',');
-
- if ($group_sql == ' GROUP BY') { $group_sql = ''; }
-
- $base_sql = "SELECT DISTINCT(`song`.`id`) $field_sql $select_sql FROM `song`";
-
- $sql = $base_sql . $table_sql . " WHERE " . $where_sql . $group_sql . $order_sql . $limit_sql;
-
- /**
- * Because we might need this for Dynamic Playlist Action
- * but we don't trust users to provide this store it in the
- * session where they can't get to it!
- */
-
- $_SESSION['userdata']['stored_search'] = $sql;
-
- $db_results = Dba::read($sql);
-
- $results = array();
-
- while ($row = Dba::fetch_assoc($db_results)) {
- $results[] = $row['id'];
- }
-
- return $results;
-
-} // search_songs
-
-
-?>
diff --git a/random.php b/random.php
index 841b333b..e8dd8fb4 100644
--- a/random.php
+++ b/random.php
@@ -37,18 +37,15 @@ show_header();
switch ($_REQUEST['action']) {
case 'get_advanced':
- $object_ids = Random::advanced($_POST);
+ $object_ids = Random::advanced($_REQUEST['type'], $_POST);
// We need to add them to the active playlist
foreach ($object_ids as $object_id) {
- $GLOBALS['user']->playlist->add_object($object_id,'song');
+ $GLOBALS['user']->playlist->add_object($object_id, 'song');
}
-
case 'advanced':
default:
require_once Config::get('prefix') . '/templates/show_random.inc.php';
-/* require_once Config::get('prefix') . '/templates/show_random_rules.inc.php';*/
-
break;
} // end switch
diff --git a/search.php b/search.php
index 4ef60940..c8eff20d 100644
--- a/search.php
+++ b/search.php
@@ -39,24 +39,12 @@ show_header();
* action switch
*/
switch ($_REQUEST['action']) {
- case 'quick_search':
- /* This needs to be done because we don't know what thing
- * they used the quick search to search on until after they've
- * submited it
- */
- $_REQUEST['s_all'] = $_REQUEST['search_string'];
-
- if (strlen($_REQUEST['search_string']) < 1) {
- Error::add('keyword',_('Error: No Keyword Entered'));
- require_once Config::get('prefix') . '/templates/show_search.inc.php';
- break;
- }
case 'search':
$browse = new Browse();
require_once Config::get('prefix') . '/templates/show_search.inc.php';
require_once Config::get('prefix') . '/templates/show_search_options.inc.php';
- $results = run_search($_REQUEST);
- $browse->set_type('song');
+ $results = Search::run($_REQUEST);
+ $browse->set_type($_REQUEST['type']);
$browse->show_objects($results);
$browse->store();
break;
@@ -65,6 +53,10 @@ switch ($_REQUEST['action']) {
$playlist = new Playlist($playlist_id);
show_confirmation(_('Search Saved'),sprintf(_('Your Search has been saved as a track in %s'), $playlist->name),conf('web_path') . "/search.php");
break;
+ case 'save_as_smartplaylist':
+ $playlist = new Search();
+ $playlist->parse_rules(Search::clean_request($_REQUEST));
+ $playlist->save();
default:
require_once Config::get('prefix') . '/templates/show_search.inc.php';
break;
diff --git a/server/browse.ajax.php b/server/browse.ajax.php
index 60d3cb8d..f24511b0 100644
--- a/server/browse.ajax.php
+++ b/server/browse.ajax.php
@@ -99,6 +99,12 @@ switch ($_REQUEST['action']) {
$playlist->delete();
$key = 'playlist_row_' . $playlist->id;
break;
+ case 'smartplaylist':
+ $playlist = new Search('song', $_REQUEST['id']);
+ if (!$playlist->has_access()) { exit; }
+ $playlist->delete();
+ $key = 'playlist_row_' . $playlist->id;
+ break;
case 'live_stream':
if (!$GLOBALS['user']->has_access('75')) { exit; }
$radio = new Radio($_REQUEST['id']);
diff --git a/smartplaylist.php b/smartplaylist.php
new file mode 100644
index 00000000..fa243ea3
--- /dev/null
+++ b/smartplaylist.php
@@ -0,0 +1,103 @@
+<?php
+/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
+/*
+
+ Copyright (c) Ampache.org
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; version 2
+ of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+require_once 'lib/init.php';
+
+// We special-case this so we can send a 302 if the delete succeeded
+if ($_REQUEST['action'] == 'delete_playlist') {
+ // Check rights
+ $playlist = new Search('song', $_REQUEST['playlist_id']);
+ if ($playlist->has_access()) {
+ $playlist->delete();
+ // Go elsewhere
+ header('Location: ' . Config::get('web_path') . '/browse.php?action=smartplaylist');
+ }
+}
+
+show_header();
+
+/* Switch on the action passed in */
+switch ($_REQUEST['action']) {
+ case 'create_playlist':
+ /* Check rights */
+ if (!Access::check('interface','25')) {
+ access_denied();
+ break;
+ }
+
+ foreach ($_REQUEST as $key => $value) {
+ $prefix = substr($key, 0, 4);
+ $value = trim($value);
+
+ if ($prefix == 'rule' && strlen($value)) {
+ $rules[$key] = Dba::escape($value);
+ }
+ }
+
+ switch($_REQUEST['operator']) {
+ case 'or':
+ $operator = 'OR';
+ break;
+ default:
+ $operator = 'AND';
+ break;
+ } // end switch on operator
+
+ $playlist_name = scrub_in($_REQUEST['playlist_name']);
+
+ $playlist = new Search('song');
+ $playlist->parse_rules($data);
+ $playlist->logic_operator = $operator;
+ $playlist->name = $playlist_name;
+ $playlist->save();
+
+ break;
+ case 'delete_playlist':
+ // If we made it here, we didn't have sufficient rights.
+ access_denied();
+ break;
+ case 'show_playlist':
+ $playlist = new Search('song', $_REQUEST['playlist_id']);
+ $playlist->format();
+ require_once Config::get('prefix') . '/templates/show_smartplaylist.inc.php';
+ break;
+ case 'update_playlist':
+ $playlist = new Search('song', $_REQUEST['playlist_id']);
+ if ($playlist->has_access()) {
+ $playlist->parse_rules(Search::clean_request($_REQUEST));
+ $playlist->update();
+ $playlist->format();
+ }
+ else {
+ access_denied();
+ break;
+ }
+ require_once Config::get('prefix') . '/templates/show_smartplaylist.inc.php';
+ break;
+ default:
+ require_once Config::get('prefix') . '/templates/show_smartplaylist.inc.php';
+ break;
+} // switch on the action
+
+show_footer();
+?>
diff --git a/templates/show_edit_smartplaylist_row.inc.php b/templates/show_edit_smartplaylist_row.inc.php
new file mode 100644
index 00000000..fadd1bc7
--- /dev/null
+++ b/templates/show_edit_smartplaylist_row.inc.php
@@ -0,0 +1,45 @@
+<?php
+/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
+/*
+
+ Copyright (c) 2001 - 2007 Ampache.org
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License v2
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+?>
+<td colspan="6">
+<form method="post" id="edit_playlist_<?php echo $playlist->id; ?>" action="javascript:void(0);">
+<table cellpadding="0" cellspacing="0">
+<tr>
+ <td>
+ <input type="text" name="name" size="25" value="<?php echo scrub_out($playlist->name); ?>" />
+ </td>
+ <td>
+ <?php $name = 'select_' . $playlist->type; ${$name} = ' selected="selected"'; ?>
+ <select name="pl_type">
+ <option value="public"<?php echo $select_public; ?>><?php echo _('Public'); ?></option>
+ <option value="private"<?php echo $select_private; ?>><?php echo _('Private'); ?></option>
+ </select>
+ <td>
+ <input type="hidden" name="id" value="<?php echo $playlist->id; ?>" />
+ <input type="hidden" name="type" value="smartplaylist_row" />
+ <?php echo Ajax::button('?action=edit_object&id=' . $playlist->id . '&type=smartplaylist_row','download',_('Save Changes'),'save_playlist_' . $playlist->id,'edit_playlist_' . $playlist->id); ?>
+ </td>
+</tr>
+</table>
+</form>
+</td>
+
diff --git a/templates/show_edit_smartplaylist_title.inc.php b/templates/show_edit_smartplaylist_title.inc.php
new file mode 100644
index 00000000..e6451e66
--- /dev/null
+++ b/templates/show_edit_smartplaylist_title.inc.php
@@ -0,0 +1,43 @@
+<?php
+/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
+/*
+
+ Copyright (c) 2001 - 2007 Ampache.org
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License v2
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+?>
+<form method="post" id="edit_playlist_<?php echo $playlist->id; ?>" action="javascript:void(0);">
+<table cellpadding="0" cellspacing="0">
+<tr>
+ <td>
+ <input type="text" name="name" size="25" value="<?php echo scrub_out($playlist->name); ?>" />
+ </td>
+ <td>
+ <?php $name = 'select_' . $playlist->type; ${$name} = ' selected="selected"'; ?>
+ <select name="pl_type">
+ <option value="public"<?php echo $select_public; ?>><?php echo _('Public'); ?></option>
+ <option value="private"<?php echo $select_private; ?>><?php echo _('Private'); ?></option>
+ </select>
+ <td>
+ <input type="hidden" name="id" value="<?php echo $playlist->id; ?>" />
+ <input type="hidden" name="type" value="smartplaylist_title" />
+ <?php echo Ajax::button('?action=edit_object&id=' . $playlist->id . '&type=smartplaylist_title','download',_('Save Changes'),'save_playlist_' . $playlist->id,'edit_playlist_' . $playlist->id); ?>
+ </td>
+</tr>
+</table>
+</form>
+
diff --git a/templates/show_random.inc.php b/templates/show_random.inc.php
index 65815e5e..b1287ee8 100644
--- a/templates/show_random.inc.php
+++ b/templates/show_random.inc.php
@@ -33,80 +33,70 @@
?>
<?php show_box_top(_('Play Random Selection')); ?>
-<form id="random" method="post" enctype="multipart/form-data" action="<?php echo Config::get('web_path'); ?>/random.php?action=get_advanced">
-<table class="table-data" cellspacing="0" cellpadding="3">
+<form id="random" method="post" enctype="multipart/form-data" action="<?php echo Config::get('web_path'); ?>/random.php?action=get_advanced&type=<?php echo $_REQUEST['type'] ? scrub_out($_REQUEST['type']) : 'song'; ?>">
+<table class="tabledata" cellpadding="3" cellspacing="0">
<tr>
- <td><?php echo _('Item count'); ?></td>
- <td>
- <?php $name = 'random_' . scrub_in($_POST['random']); ${$name} = ' selected="selected"'; ?>
- <select name="random">
- <option value="1"<?php echo $random_1; ?>>1</option>
- <option value="5"<?php echo $random_5; ?>>5</option>
- <option value="10"<?php echo $random_10; ?>>10</option>
- <option value="20"<?php echo $random_20; ?>>20</option>
- <option value="30"<?php echo $random_30; ?>>30</option>
- <option value="50"<?php echo $random_50; ?>>50</option>
- <option value="100"<?php echo $random_100; ?>>100</option>
- <option value="500"<?php echo $random_500; ?>>500</option>
- <option value="1000"<?php echo $random_1000; ?>>1000</option>
- <option value="-1" ><?php echo _('All'); ?></option>
- </select>
- </td>
-</tr>
-<tr>
- <td><?php echo _('Length'); ?></td>
- <td>
- <?php $name = 'length_' . intval($_POST['length']); ${$name} = ' selected="selected"'; ?>
- <select name="length">
- <option value="0"<?php echo $length_0; ?>><?php echo _('Unlimited'); ?></option>
- <option value="15"<?php echo $length_15; ?>><?php printf(ngettext('%d minute','%d minutes',15), "15"); ?></option>
- <option value="30"<?php echo $length_30; ?>><?php printf(ngettext('%d minute','%d minutes',30), "30"); ?></option>
- <option value="60"<?php echo $length_60; ?>><?php printf(ngettext('%d hour','%d hours',1), "1"); ?></option>
- <option value="120"<?php echo $length_120; ?>><?php printf(ngettext('%d hour','%d hours',2), "2"); ?></option>
- <option value="240"<?php echo $length_240; ?>><?php printf(ngettext('%d hour','%d hours',4), "4"); ?></option>
- <option value="480"<?php echo $length_480; ?>><?php printf(ngettext('%d hour','%d hours',8), "8"); ?></option>
- <option value="960"<?php echo $length_960; ?>><?php printf(ngettext('%d hour','%d hours',16), "16"); ?></option>
- </select>
- </td>
+ <td><?php if ($_REQUEST['type'] != 'song') { ?><a href="<?php echo Config::get('web_path'); ?>/random.php?action=advanced&type=song"><?php echo _('Songs'); ?></a><?php } else { echo _('Songs'); } ?></td>
+ <td><?php if ($_REQUEST['type'] != 'album') { ?><a href="<?php echo Config::get('web_path'); ?>/random.php?action=advanced&type=album"><?php echo _('Albums'); ?></a><?php } else { echo _('Albums'); } ?></td>
+ <td><?php if ($_REQUEST['type'] != 'artist') { ?><a href="<?php echo Config::get('web_path'); ?>/random.php?action=advanced&type=artist"><?php echo _('Artists'); ?></a><?php } else { echo _('Artists'); } ?></td>
</tr>
+<tr><td>&nbsp;</td></tr>
+</table>
+<table class="tabledata" cellpadding="0" cellspacing="0">
<tr>
- <td><?php echo _('Type'); ?></td>
- <td>
- <?php $name = 'type_' . scrub_in($_POST['random_type']); ${$name} = ' selected="selected"'; ?>
- <select name="random_type">
- <option value="normal"<?php echo $type_normal; ?>><?php echo _('Standard'); ?></option>
- <option value="unplayed"<?php echo $type_unplayed; ?>><?php echo _('Less Played'); ?></option>
- <option value="full_album"<?php echo $type_full_album; ?>><?php echo _('Full Albums'); ?></option>
- <option value="full_artist"<?php echo $type_full_artist; ?>><?php echo _('Full Artist'); ?></option>
- <?php if (Config::get('ratings')) { ?>
- <option value="high_rating"<?php echo $type_high_rating; ?>><?php echo _('Highest Rated'); ?></option>
- <?php } ?>
- </select>
- </td>
+ <td><?php echo _('Item count'); ?></td>
+ <td>
+ <?php $name = 'random_' . scrub_in($_POST['random']); ${$name} = ' selected="selected"'; ?>
+ <select name="random">
+ <option value="1"<?php echo $random_1; ?>>1</option>
+ <option value="5"<?php echo $random_5; ?>>5</option>
+ <option value="10"<?php echo $random_10; ?>>10</option>
+ <option value="20"<?php echo $random_20; ?>>20</option>
+ <option value="30"<?php echo $random_30; ?>>30</option>
+ <option value="50"<?php echo $random_50; ?>>50</option>
+ <option value="100"<?php echo $random_100; ?>>100</option>
+ <option value="500"<?php echo $random_500; ?>>500</option>
+ <option value="1000"<?php echo $random_1000; ?>>1000</option>
+ <option value="-1" ><?php echo _('All'); ?></option>
+ </select>
+ </td>
</tr>
<tr>
- <td nowrap="nowrap"><?php echo _('From catalog'); ?></td>
- <td>
- <?php show_catalog_select('catalog','',$_POST['catalog']); ?>
- </td>
+ <td><?php echo _('Length'); ?></td>
+ <td>
+ <?php $name = 'length_' . intval($_POST['length']); ${$name} = ' selected="selected"'; ?>
+ <select name="length">
+ <option value="0"<?php echo $length_0; ?>><?php echo _('Unlimited'); ?></option>
+ <option value="15"<?php echo $length_15; ?>><?php printf(ngettext('%d minute','%d minutes',15), "15"); ?></option>
+ <option value="30"<?php echo $length_30; ?>><?php printf(ngettext('%d minute','%d minutes',30), "30"); ?></option>
+ <option value="60"<?php echo $length_60; ?>><?php printf(ngettext('%d hour','%d hours',1), "1"); ?></option>
+ <option value="120"<?php echo $length_120; ?>><?php printf(ngettext('%d hour','%d hours',2), "2"); ?></option>
+ <option value="240"<?php echo $length_240; ?>><?php printf(ngettext('%d hour','%d hours',4), "4"); ?></option>
+ <option value="480"<?php echo $length_480; ?>><?php printf(ngettext('%d hour','%d hours',8), "8"); ?></option>
+ <option value="960"<?php echo $length_960; ?>><?php printf(ngettext('%d hour','%d hours',16), "16"); ?></option>
+ </select>
+ </td>
</tr>
<tr>
- <td><?php echo _('Size Limit'); ?></td>
- <td>
- <?php $name = 'size_' . intval($_POST['size_limit']); ${$name} = ' selected="selected"'; ?>
- <select name="size_limit">
- <option value="0"<?php echo $size_0; ?>><?php echo _('Unlimited'); ?></option>
- <option value="64"<?php echo $size_64; ?>>64MB</option>
- <option value="128"<?php echo $size_128; ?>>128MB</option>
- <option value="256"<?php echo $size_256; ?>>256MB</option>
- <option value="512"<?php echo $size_512; ?>>512MB</option>
- <option value="1024"<?php echo $size_1024; ?>>1024MB</option>
- </select>
- </td>
+ <td><?php echo _('Size Limit'); ?></td>
+ <td>
+ <?php $name = 'size_' . intval($_POST['size_limit']); ${$name} = ' selected="selected"'; ?>
+ <select name="size_limit">
+ <option value="0"<?php echo $size_0; ?>><?php echo _('Unlimited'); ?></option>
+ <option value="64"<?php echo $size_64; ?>>64MB</option>
+ <option value="128"<?php echo $size_128; ?>>128MB</option>
+ <option value="256"<?php echo $size_256; ?>>256MB</option>
+ <option value="512"<?php echo $size_512; ?>>512MB</option>
+ <option value="1024"<?php echo $size_1024; ?>>1024MB</option>
+ </select>
+ </td>
</tr>
</table>
+
+<?php require Config::get('prefix') . '/templates/show_rules.inc.php'; ?>
+
<div class="formValidation">
- <input type="submit" value="<?php echo _('Enqueue'); ?>" />
+ <input type="submit" value="<?php echo _('Enqueue'); ?>" />
</div>
</form>
<?php show_box_bottom(); ?>
diff --git a/templates/show_random_rules.inc.php b/templates/show_random_rules.inc.php
deleted file mode 100644
index 45be357e..00000000
--- a/templates/show_random_rules.inc.php
+++ /dev/null
@@ -1,59 +0,0 @@
-<?php
-/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
-/**
- * Show Random Rules
- *
- * PHP version 5
- *
- * LICENSE: GNU General Public License, version 2 (GPLv2)
- * Copyright (c) 2001 - 2011 Ampache.org All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License v2
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * @category Template
- * @package Template
- * @author Karl Vollmer <vollmer@ampache.org>
- * @copyright 2001 - 2011 Ampache.org
- * @license http://opensource.org/licenses/gpl-2.0 GPLv2
- * @version PHP 5.2
- * @link http://www.ampache.org/
- * @since File available since Release 1.0
- */
-
-?>
-<?php show_box_top(_('Rules')); ?>
-<table class="tabledata" cellpadding="0" cellspacing="0">
-<colgroup>
- <col id="col_field" />
- <col id="col_operator" />
- <col id="col_value" />
- <col id="col_method" />
- <col id="col_action" />
-</colgroup>
-<tr class="th-top">
- <th class="col_field"><?php echo _('Field'); ?></th>
- <th class="col_operator"><?php echo _('Operator'); ?></th>
- <th class="col_value"><?php echo _('Value'); ?></th>
- <th class="col_method"><?php echo _('Method'); ?></th>
- <th class="col_action"><?php echo _('Action'); ?></th>
-</tr>
-<tr>
- <td></td>
- <td></td>
- <td></td>
- <td></td>
- <td></td>
-</tr>
-</table>
-<?php show_box_bottom(); ?>
diff --git a/templates/show_rules.inc.php b/templates/show_rules.inc.php
new file mode 100644
index 00000000..1f98e0ae
--- /dev/null
+++ b/templates/show_rules.inc.php
@@ -0,0 +1,67 @@
+<?php
+/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
+/*
+
+ Copyright (c) Ampache.org
+ All Rights Reserved
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License v2
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+?>
+
+<script type="text/javascript" src="<?php echo Config::get('web_path'); ?>/lib/javascript/search.js"></script>
+<script type="text/javascript" src="<?php echo Config::get('web_path'); ?>/lib/javascript/search-data.php?type=<?php echo $_REQUEST['type'] ? scrub_out($_REQUEST['type']) : 'song'; ?>"></script>
+
+<?php show_box_top(_('Rules') . "..."); ?>
+<table class="tabledata" cellpadding="3" cellspacing="0">
+<tbody id="searchtable">
+ <tr>
+ <td><?php echo _('Match'); ?></td>
+ <td>
+ <select name="operator">
+ <option value="and" <?php if($_REQUEST['operator']=="and") echo "selected=\"selected\""?>><?php echo _('all rules'); ?></option>
+ <option value="or" <?php if($_REQUEST['operator']=="or") echo "selected=\"selected\""?>><?php echo _('any rule'); ?></option>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <a id="addrowbutton" href="javascript:void(0)">
+ <?php echo get_user_icon('add'); ?>
+ <?php echo _('Add Another Rule'); ?>
+ </a>
+ <script type="text/javascript">Event.observe('addrowbutton', 'click', SearchRow.add);</script>
+ </td>
+ </tr>
+</tbody>
+</table>
+<?php show_box_bottom(); ?>
+
+<?php
+if ($playlist) {
+ $out = $playlist->to_js();
+}
+else {
+ $mysearch = new Search($_REQUEST['type']);
+ $mysearch->parse_rules(Search::clean_request($_REQUEST));
+ $out = $mysearch->to_js();
+}
+if ($out) {
+ echo $out;
+}
+else {
+ echo '<script type="text/javascript">SearchRow.add();</script>';
+}
+?>
diff --git a/templates/show_search.inc.php b/templates/show_search.inc.php
index 47c3085f..18992b16 100644
--- a/templates/show_search.inc.php
+++ b/templates/show_search.inc.php
@@ -37,130 +37,39 @@
*/
?>
<?php show_box_top(_('Search Ampache') . "..."); ?>
-<form name="search" method="post" action="<?php echo Config::get('web_path'); ?>/search.php" enctype="multipart/form-data" style="Display:inline">
+<form id="search" name="search" method="post" action="<?php echo Config::get('web_path'); ?>/search.php?type=<?php echo $_REQUEST['type'] ? scrub_out($_REQUEST['type']) : 'song'; ?>" enctype="multipart/form-data" style="Display:inline">
<table class="tabledata" cellpadding="3" cellspacing="0">
-<tr class="<?php echo flip_class(); ?>">
- <td><?php echo _('Keywords') ?></td>
+ <tr>
+ <td><?php if ($_REQUEST['type'] != 'song') { ?><a href="<?php echo Config::get('web_path'); ?>/search.php?type=song"><?php echo _('Songs'); ?></a><?php } else { echo _('Songs'); } ?></td>
+ <td><?php if ($_REQUEST['type'] != 'album') { ?><a href="<?php echo Config::get('web_path'); ?>/search.php?type=album"><?php echo _('Albums'); ?></a><?php } else { echo _('Albums'); } ?></td>
+ <td><?php if ($_REQUEST['type'] != 'artist') { ?><a href="<?php echo Config::get('web_path'); ?>/search.php?type=artist"><?php echo _('Artists'); ?></a><?php } else { echo _('Artists'); } ?></td>
+ <td><?php if ($_REQUEST['type'] != 'video') { ?><a href="<?php echo Config::get('web_path'); ?>/search.php?type=video"><?php echo _('Videos'); ?></a><?php } else { echo _('Videos'); } ?></td>
+ </tr>
+ <tr><td>&nbsp;</td></tr>
+</table>
+<table class="tabledata" cellpadding="3" cellspacing="0">
+ <tr>
+ <td><?php echo _('Maximum Results'); ?></td>
<td>
- <input type="text" id="s_all" name="s_all" value="<?php echo scrub_out($_REQUEST['s_all']); ?>"/>
+ <select name="limit">
+ <option value="0"><?php echo _('Unlimited'); ?></option>
+ <option value="25" <?php if($_REQUEST['limit']=="25") echo "selected=\"selected\""?>>25</option>
+ <option value="50" <?php if($_REQUEST['limit']=="50") echo "selected=\"selected\""?>>50</option>
+ <option value="100" <?php if($_REQUEST['limit']=="100") echo "selected=\"selected\""?>>100</option>
+ <option value="500" <?php if($_REQUEST['limit']=="500") echo "selected=\"selected\""?>>500</option>
+ </select>
</td>
- <td><?php echo _('Comment'); ?></td>
- <td>
- <input type="text" id="s_comment" name="s_comment" value="<?php echo scrub_out($_REQUEST['s_comment']); ?>" />
- </td>
-</tr>
-<tr class="<?php echo flip_class(); ?>">
- <td><?php echo _('Title'); ?></td>
- <td>
- <input type="text" id="s_title" name="s_title" value="<?php echo scrub_out($_REQUEST['s_title']); ?>" />
- </td>
- <td><?php echo _('Artist'); ?></td>
- <td>
- <input type="text" id="s_artist" name="s_artist" value="<?php echo scrub_out($_REQUEST['s_artist']); ?>" />
- </td>
-</tr>
-<tr class="<?php echo flip_class(); ?>">
- <td><?php echo _('Album'); ?></td>
- <td>
- <input type="text" id="s_album" name="s_album" value="<?php echo scrub_out($_REQUEST['s_album']); ?>" />
- </td>
- <td><?php echo _('Tag'); ?></td>
- <td>
- <input type="text" id="s_tag" name="s_tag" value="<?php echo scrub_out($_REQUEST['s_tag']); ?>" />
- </td>
-</tr>
-<tr class="<?php echo flip_class(); ?>">
- <td><?php echo _('Year'); ?></td>
- <td>
- <input type="text" id="s_year" name="s_year" size="5" value="<?php echo scrub_out($_REQUEST['s_year']); ?>" />
- -
- <input type="text" id="s_year2" name="s_year2" size="5" value="<?php echo scrub_out($_REQUEST['s_year2']); ?>" />
- </td>
- <td><?php echo _('Filename'); ?></td>
- <td>
- <input type="text" id="s_filename" name="s_filename" value="<?php echo scrub_out($_REQUEST['s_filename']); ?>" />
- </td>
-</tr>
-<tr class="<?php echo flip_class(); ?>">
- <td><?php echo _('Time'); ?></td>
- <td>
- <input type="text" id="s_time" name="s_time" size="3" value="<?php echo scrub_out($_REQUEST['s_time']); ?>" />
- -
- <input type="text" id="s_time2" name="s_time2" size="3" value="<?php echo scrub_out($_REQUEST['s_time2']); ?>" />
- <?php echo _('minutes'); ?>
- </td>
- <td><?php echo _('Codec'); ?></td>
- <td>
- <input type="text" id="s_codec" name="s_codec" size="5" value="<?php echo scrub_out($_REQUEST['s_codec']); ?>" />
- </td>
-</tr>
-<tr class="<?php echo flip_class(); ?>">
- <td><?php echo _('Played'); ?></td>
- <td>
- <select id="s_played" name="s_played" >
- <option value="">&nbsp;</option>
- <option value="1" <?php if($_REQUEST['s_played']=="1") echo "selected=\"selected\""?>><?php echo _('Yes'); ?></option>
- <option value="0" <?php if($_REQUEST['s_played']=="0") echo "selected=\"selected\""?>><?php echo _('No'); ?></option>
- </select>
- </td>
- <td><?php echo _('Min Bitrate'); ?></td>
- <td>
- <select id="s_minbitrate" name="s_minbitrate" >
- <option value="">&nbsp;</option>
- <?php foreach(array(32,40,48,56,64,80,96,112,128,160,192,224,256,320) as $val) { ?>
- <option value="<?php echo $val?>" <?php if($_REQUEST['s_minbitrate']==$val) echo "selected=\"selected\""?>><?php echo $val?></option>
- <?php } ?>
- </select>
- </td>
-</tr>
-<tr class="<?php echo flip_class(); ?>">
- <td><?php echo _('Rating'); ?></td>
- <td>
- <select id="s_rating_operator" name="s_rating_operator">
- <option value="0" <?php if($_REQUEST['s_rating_operator']=="0") echo "selected=\"selected\""?>><?php echo _('>='); ?></option>
- <option value="1" <?php if($_REQUEST['s_rating_operator']=="1") echo "selected=\"selected\""?>><?php echo _('<='); ?></option>
- <option value="2" <?php if($_REQUEST['s_rating_operator']=="2") echo "selected=\"selected\""?>><?php echo _('='); ?></option>
- </select>
- <select id="s_rating" name="s_rating">
- <option value="">&nbsp;</option>
- <option value="1" <?php if($_REQUEST['s_rating']=="1") echo "selected=\"selected\""?>><?php echo _('One Star'); ?></option>
- <option value="2" <?php if($_REQUEST['s_rating']=="2") echo "selected=\"selected\""?>><?php echo _('Two Stars'); ?></option>
- <option value="3" <?php if($_REQUEST['s_rating']=="3") echo "selected=\"selected\""?>><?php echo _('Three Stars'); ?></option>
- <option value="4" <?php if($_REQUEST['s_rating']=="4") echo "selected=\"selected\""?>><?php echo _('Four Stars'); ?></option>
- <option value="5" <?php if($_REQUEST['s_rating']=="5") echo "selected=\"selected\""?>><?php echo _('Five Stars'); ?></option>
- </select>
- </td>
- <td><?php echo _('Operator'); ?></td>
- <td>
- <select name="operator">
- <option value="and" <?php if($_REQUEST['operator']=="and") echo "selected=\"selected\""?>><?php echo _('AND'); ?></option>
- <option value="or" <?php if($_REQUEST['operator']=="or") echo "selected=\"selected\""?>><?php echo _('OR'); ?></option>
- </select>
- </td>
-</tr>
-<tr class="<?php echo flip_class(); ?>">
- <td><?php echo _('Method'); ?></td>
- <td>
- <select name="method">
- <option value="fuzzy" <?php if($_REQUEST['method']=="fuzzy") echo "selected=\"selected\""?>><?php echo _('Fuzzy'); ?></option>
- <option value="exact" <?php if($_REQUEST['method']=="exact") echo "selected=\"selected\""?>><?php echo _('Exact'); ?></option>
- </select>
- </td>
- <td><?php echo _('Maximum Results'); ?></td>
- <td>
- <select name="limit">
- <option value="0"><?php echo _('Unlimited'); ?></option>
- <option value="25" <?php if($_REQUEST['limit']=="25") echo "selected=\"selected\""?>>25</option>
- <option value="50" <?php if($_REQUEST['limit']=="50") echo "selected=\"selected\""?>>50</option>
- <option value="100" <?php if($_REQUEST['limit']=="100") echo "selected=\"selected\""?>>100</option>
- <option value="500" <?php if($_REQUEST['limit']=="500") echo "selected=\"selected\""?>>500</option>
- </select>
- </td>
-</tr>
+ </tr>
</table>
+
+<?php require Config::get('prefix') . '/templates/show_rules.inc.php'; ?>
+
<div class="formValidation">
<input class="button" type="submit" value="<?php echo _('Search'); ?>" />&nbsp;&nbsp;
- <input type="hidden" name="action" value="search" />
+<?php if ($_REQUEST['type'] == 'song' || ! $_REQUEST['type']) { ?>
+ <input id="savesearchbutton" class="button" type="submit" value="<?php echo _('Save as Smart Playlist'); ?>" onClick="$('hiddenaction').setValue('save_as_smartplaylist');" />&nbsp;&nbsp;
+<?php } ?>
+ <input type="hidden" id="hiddenaction" name="action" value="search" />
</div>
</form>
<?php show_box_bottom(); ?>
diff --git a/templates/show_search_bar.inc.php b/templates/show_search_bar.inc.php
index 486ad9fe..b5bb4f4f 100644
--- a/templates/show_search_bar.inc.php
+++ b/templates/show_search_bar.inc.php
@@ -33,13 +33,20 @@
?>
<div id="sb_Subsearch">
- <form name="search" method="post" action="<?php echo $web_path; ?>/search.php" enctype="multipart/form-data" style="Display:inline">
- <input type="text" name="search_string" id="searchString"/>
- <input type="hidden" name="action" value="quick_search" />
- <input type="hidden" name="method" value="fuzzy" />
+ <form name="search" method="post" action="<?php echo $web_path; ?>/search.php?type=song" enctype="multipart/form-data" style="Display:inline">
+ <input type="text" name="rule_1_input" id="searchString"/>
+ <input type="hidden" name="action" value="search" />
+ <input type="hidden" name="rule_1_operator" value="0" />
<input type="hidden" name="object_type" value="song" />
+ <select name="rule_1">
+ <option value="anywhere"><?php echo _('Anywhere')?></option>
+ <option value="title"><?php echo _('Title')?></option>
+ <option value="album"><?php echo _('Album')?></option>
+ <option value="artist"><?php echo _('Artist')?></option>
+ <option value="tag"><?php echo _('Tag')?></option>
+ </select>
<input class="button" type="submit" value="<?php echo _('Search'); ?>" id="searchBtn" />
- <a href="<?php echo $web_path; ?>/search.php" class="button" id="advSearchBtn"><?php echo _('Advanced Search'); ?></a>
+ <a href="<?php echo $web_path; ?>/search.php?type=song" class="button" id="advSearchBtn"><?php echo _('Advanced Search'); ?></a>
</form>
</div>
diff --git a/templates/show_search_options.inc.php b/templates/show_search_options.inc.php
index dad9d733..1d890767 100644
--- a/templates/show_search_options.inc.php
+++ b/templates/show_search_options.inc.php
@@ -40,7 +40,7 @@
</li>
<?php if (Access::check_function('batch_download')) { ?>
<li>
- <a href="<?php echo Config::get('web_path'); ?>/batch.php?action=browse&browse_id=<?php echo $browse->id; ?>"><?php echo get_user_icon('batch_download', _('Batch Download')); ?></a>
+ <a href="<?php echo Config::get('web_path'); ?>/batch.php?action=browse&amp;type=<?php echo scrub_out($_REQUEST['type']); ?>&amp;browse_id=<?php echo $browse->id; ?>"><?php echo get_user_icon('batch_download', _('Batch Download')); ?></a>
<?php echo _('Batch Download'); ?>
</li>
<?php } ?>
diff --git a/templates/show_smartplaylist.inc.php b/templates/show_smartplaylist.inc.php
new file mode 100644
index 00000000..4c276ffd
--- /dev/null
+++ b/templates/show_smartplaylist.inc.php
@@ -0,0 +1,67 @@
+<?php
+/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
+/*
+ Copyright (c) Ampache.org
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License v2
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+?>
+<?php
+ob_start();
+require Config::get('prefix') . '/templates/show_smartplaylist_title.inc.php';
+$title = ob_get_contents();
+ob_end_clean();
+show_box_top('<div id="playlist_row_' . $playlist->id . '">' . $title .
+ '</div>');
+?>
+<div id="information_actions">
+<ul>
+ <?php if (Access::check_function('batch_download')) { ?>
+ <li>
+ <a href="<?php echo Config::get('web_path'); ?>/batch.php?action=search&amp;id=<?php echo $playlist->id; ?>"><?php echo get_user_icon('batch_download', _('Batch Download')); ?></a>
+ <?php echo _('Batch Download'); ?>
+ </li>
+ <?php } ?>
+ <li>
+ <?php echo Ajax::button('?action=basket&type=smartplaylist&id=' . $playlist->id,'add',_('Add All'),'play_playlist'); ?>
+ <?php echo _('Add All'); ?>
+ </li>
+ <?php if ($playlist->has_access()) { ?>
+ <li>
+ <?php echo Ajax::button('?action=show_edit_object&type=smartplaylist_title&id=' . $playlist->id,'edit',_('Edit'),'edit_playlist_' . $playlist->id); ?>
+ <?php echo _('Edit'); ?>
+ </li>
+ <li>
+ <a href="<?php echo Config::get('web_path'); ?>/smartplaylist.php?action=delete_playlist&playlist_id=<?php echo $playlist->id; ?>">
+ <?php echo get_user_icon('delete'); ?>
+ </a>
+ <?php echo _('Delete'); ?>
+ </li>
+ <?php } ?>
+</ul>
+</div>
+
+<form id="editplaylist" name="editplaylist" method="post" action="<?php echo Config::get('web_path'); ?>/smartplaylist.php?action=update_playlist&playlist_id=<?php echo $playlist->id; ?>" enctype="multipart/form-data" style="Display:inline">
+
+<?php require Config::get('prefix') . '/templates/show_rules.inc.php'; ?>
+
+<div class="formValidation">
+ <input class="button" type="submit" value="<?php echo _('Save Changes'); ?>" />
+</div>
+
+</form>
+
+<?php show_box_bottom(); ?>
diff --git a/templates/show_smartplaylist_row.inc.php b/templates/show_smartplaylist_row.inc.php
new file mode 100644
index 00000000..bf1f43f0
--- /dev/null
+++ b/templates/show_smartplaylist_row.inc.php
@@ -0,0 +1,39 @@
+<?php
+/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
+/*
+
+ Copyright (c) 2001 - 2007 Ampache.org
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License v2
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+?>
+<td class="cel_add">
+ <?php echo Ajax::button('?action=basket&type=smartplaylist&id=' . $playlist->id,'add',_('Add'),'add_playlist_' . $playlist->id); ?>
+</td>
+<td class="cel_playlist"><?php echo $playlist->f_link; ?></td>
+<td class="cel_type"><?php echo $playlist->f_type; ?></td>
+<td class="cel_owner"><?php echo scrub_out($playlist->f_user); ?></td>
+<td class="cel_action">
+ <?php if (Access::check_function('batch_download')) { ?>
+ <a href="<?php echo Config::get('web_path'); ?>/batch.php?action=smartplaylist&amp;id=<?php echo $playlist->id; ?>">
+ <?php echo get_user_icon('batch_download',_('Batch Download')); ?>
+ </a>
+ <?php } ?>
+ <?php if ($playlist->has_access()) { ?>
+ <?php echo Ajax::button('?action=show_edit_object&type=smartplaylist_row&id=' . $playlist->id,'edit',_('Edit'),'edit_playlist_' . $playlist->id); ?>
+ <?php echo Ajax::button('?page=browse&action=delete_object&type=smartplaylist&id=' . $playlist->id,'delete',_('Delete'),'delete_playlist_' . $playlist->id); ?>
+ <?php } ?>
+</td>
diff --git a/templates/show_smartplaylist_title.inc.php b/templates/show_smartplaylist_title.inc.php
new file mode 100644
index 00000000..8691ac98
--- /dev/null
+++ b/templates/show_smartplaylist_title.inc.php
@@ -0,0 +1,24 @@
+<?php
+/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
+/*
+
+ Copyright (c) 2001 - 2007 Ampache.org
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License v2
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+?>
+
+<?php echo sprintf(_('%s %s (Smart Playlist)'), $playlist->f_type, $playlist->name); ?>
diff --git a/templates/show_smartplaylists.inc.php b/templates/show_smartplaylists.inc.php
new file mode 100644
index 00000000..2c891884
--- /dev/null
+++ b/templates/show_smartplaylists.inc.php
@@ -0,0 +1,63 @@
+<?php
+/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
+/*
+
+ Copyright (c) Ampache.org
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+?>
+<?php require Config::get('prefix') . '/templates/list_header.inc.php' ?>
+<table class="tabledata" cellpadding="0" cellspacing="0">
+<colgroup>
+ <col id="col_add" />
+ <col id="col_playlist" />
+ <col id="col_type" />
+ <col id="col_songs" />
+ <col id="col_owner" />
+ <col id="col_action" />
+</colgroup>
+<tr class="th-top">
+ <th class="cel_add"><?php echo _('Add'); ?></th>
+ <th class="cel_playlist"><?php echo Ajax::text('?page=browse&action=set_sort&type=smartplaylist&sort=name',_('Playlist Name'),'playlist_sort_name'); ?></th>
+ <th class="cel_type">&nbsp;</th>
+ <th class="cel_owner"><?php echo Ajax::text('?page=browse&action=set_sort&type=smartplaylist&sort=user',_('Owner'),'playlist_sort_owner'); ?></th>
+ <th class="cel_action"><?php echo _('Actions'); ?></th>
+</tr>
+<?php
+foreach ($object_ids as $playlist_id) {
+ $playlist = new Search('song', $playlist_id);
+ $playlist->format();
+?>
+<tr class="<?php echo flip_class(); ?>" id="playlist_row_<?php echo $playlist->id; ?>">
+ <?php require Config::get('prefix') . '/templates/show_smartplaylist_row.inc.php'; ?>
+</tr>
+<?php } // end foreach ($playlists as $playlist) ?>
+<?php if (!count($object_ids)) { ?>
+<tr class="<?php echo flip_class(); ?>">
+ <td colspan="6"><span class="fatalerror"><?php echo _('Not Enough Data'); ?></span></td>
+</tr>
+<?php } ?>
+<tr class="th-bottom">
+ <th class="cel_add"><?php echo _('Add'); ?></th>
+ <th class="cel_playlist"><?php echo Ajax::text('?page=browse&action=set_sort&type=playlist&sort=name',_('Playlist Name'),'playlist_sort_name_bottom'); ?></th>
+ <th class="cel_type">&nbsp;</th>
+ <th class="cel_owner"><?php echo Ajax::text('?page=browse&action=set_sort&type=playlist&sort=user',_('Owner'),'playlist_sort_owner_bottom'); ?></th>
+ <th class="cel_action"><?php echo _('Actions'); ?></th>
+</tr>
+</table>
+<?php require Config::get('prefix') . '/templates/list_header.inc.php' ?>
diff --git a/templates/sidebar_home.inc.php b/templates/sidebar_home.inc.php
index b0fb642a..4b97abd5 100644
--- a/templates/sidebar_home.inc.php
+++ b/templates/sidebar_home.inc.php
@@ -48,6 +48,7 @@ $ajax_info = Config::get('ajax_url'); $web_path = Config::get('web_path');
<li id="sb_browse_bb_Artist"><a href="<?php echo $web_path; ?>/browse.php?action=artist"><?php echo _('Artists'); ?></a></li>
<li id="sb_browse_bb_Tags"><a href="<?php echo $web_path; ?>/browse.php?action=tag"><?php echo _('Tag Cloud'); ?></a></li>
<li id="sb_browse_bb_Playlist"><a href="<?php echo $web_path; ?>/browse.php?action=playlist"><?php echo _('Playlists'); ?></a></li>
+ <li id="sb_browse_bb_SmartPlaylist"><a href="<?php echo $web_path; ?>/browse.php?action=smartplaylist"><?php echo _('Smart Playlists'); ?></a></li>
<li id="sb_browse_bb_RadioStation"><a href="<?php echo $web_path; ?>/browse.php?action=live_stream"><?php echo _('Radio Stations'); ?></a></li>
<li id="sb_browse_bb_Video"><a href="<?php echo $web_path; ?>/browse.php?action=video"><?php echo _('Videos'); ?></a></li>
</ul>
@@ -77,7 +78,7 @@ $ajax_info = Config::get('ajax_url'); $web_path = Config::get('web_path');
<li id="sb_home_random_album"><?php echo Ajax::text('?page=random&action=album',_('Album'),'home_random_album'); ?></li>
<li id="sb_home_random_artist"><?php echo Ajax::text('?page=random&action=artist',_('Artist'),'home_random_artist'); ?></li>
<li id="sb_home_random_playlist"><?php echo Ajax::text('?page=random&action=playlist',_('Playlist'),'home_random_playlist'); ?></li>
- <li id="sb_home_random_advanced"><a href="<?php echo $web_path; ?>/random.php?action=advanced"><?php echo _('Advanced'); ?></a></li>
+ <li id="sb_home_random_advanced"><a href="<?php echo $web_path; ?>/random.php?action=advanced&type=song"><?php echo _('Advanced'); ?></a></li>
</ul>
</li>
<li><h4><?php echo _('Information'); ?></h4>