. * */ /** * XML_Data Class * * This class takes care of all of the xml document stuff in Ampache these * are all static calls * */ class XML_Data { // This is added so that we don't pop any webservers private static $limit = 5000; private static $offset = 0; private static $type = ''; /** * constructor * * We don't use this, as its really a static class */ private function __construct() { // Rien a faire } // constructor /** * set_offset * * This takes an int and changes the offset * * @param integer $offset (description here...) * @return void */ public static function set_offset($offset) { $offset = intval($offset); self::$offset = $offset; } // set_offset /** * set_limit * * This sets the limit for any ampache transactions * * @param integer $limit (description here...) * @return void */ public static function set_limit($limit) { if (!$limit) { return false; } $limit = intval($limit); self::$limit = $limit; } // set_limit /** * set_type * * This sets the type of XML_Data we are working on * * @param string $type XML_Data type * @return void */ public static function set_type($type) { if (!in_array($type,array('rss','xspf','itunes'))) { return false; } self::$type = $type; } // set_type /** * error * * This generates a standard XML Error message * nothing fancy here... * * @param integer $code Error code * @param string $string Error message * @return string return error message xml */ public static function error($code,$string) { $string = "\t"; return self::output_xml($string); } // error /** * single_string * * This takes two values, first the key second the string * * @param string $key (description here...) * @param string $string xml data * @return string return xml */ public static function single_string($key, $string='') { $final = self::_header(); if (!empty($string)) { $final .= "\t<$key>"; } else { $final .= "\t<$key />"; } $final .= self::_footer(); return $final; } // single_string /** * header * * This returns the header * * @see _header() * @return string return xml */ public static function header($title = null) { return self::_header($title); } // header /** * footer * * This returns the footer * * @see _footer() * @return string return xml */ public static function footer() { return self::_footer(); } // footer /** * tags_string * * This returns the formatted 'tags' string for an xml document * */ private static function tags_string($tags) { $string = ''; if (is_array($tags)) { $atags = array(); foreach ($tags as $tag_id => $data) { if (array_key_exists($data['id'], $atags)) { $atags[$data['id']]['count']++; } else { $atags[$data['id']] = array('name' => $data['name'], 'count' => 1); } } foreach ($atags as $id => $data) { $string .= "\t\n"; } } return $string; } // tags_string /** * playlist_song_tracks_string * * This returns the formatted 'playlistTrack' string for an xml document * */ private static function playlist_song_tracks_string($song, $playlist_data) { if (empty($playlist_data)) { return ""; } $playlist_track = ""; foreach ($playlist_data as $playlist) { if ($playlist["object_id"] == $song->id) { return "\t" . $playlist["track"] . "\n"; } } return ""; } // playlist_song_tracks_string /** * keyed_array * * This will build an xml document from a key'd array, * * @param array $array (description here...) * @param boolean $callback (description here...) * @return string return xml */ public static function keyed_array($array,$callback='') { $string = ''; // Foreach it foreach ($array as $key=>$value) { $attribute = ''; // See if the key has attributes if (is_array($value) and isset($value[''])) { $attribute = ' ' . $value['']; $key = $value['value']; } // If it's an array, run again if (is_array($value)) { $value = self::keyed_array($value,1); $string .= "<$key$attribute>\n$value\n\n"; } else { $string .= "\t<$key$attribute>\n"; } } // end foreach if (!$callback) { $string = self::output_xml($string); } return $string; } // keyed_array /** * tags * * This returns tags to the user, in a pretty xml document with the information * * @param array $tags (description here...) * @return string return xml */ public static function tags($tags) { if (count($tags) > self::$limit or self::$offset > 0) { $tags = array_splice($tags,self::$offset,self::$limit); } $string = ''; foreach ($tags as $tag_id) { $tag = new Tag($tag_id); $counts = $tag->count(); $string .= "\n" . "\tname]]>\n" . "\t" . intval($counts['album']) . "\n" . "\t" . intval($counts['artist']) . "\n" . "\t" . intval($counts['song']) . "\n" . "\t" . intval($counts['video']) . "\n" . "\t" . intval($counts['playlist']) . "\n" . "\t" . intval($counts['live_stream']) . "\n" . "\n"; } // end foreach return self::output_xml($string); } // tags /** * artists * * This takes an array of artists and then returns a pretty xml document with the information * we want * * @param array $artists (description here...) * @return string return xml */ public static function artists($artists) { if (count($artists) > self::$limit or self::$offset > 0) { $artists = array_splice($artists,self::$offset,self::$limit); } $string = ''; Rating::build_cache('artist',$artists); foreach ($artists as $artist_id) { $artist = new Artist($artist_id); $artist->format(); $rating = new Rating($artist_id,'artist'); $tag_string = self::tags_string($artist->tags); $string .= "id . "\">\n" . "\tf_full_name . "]]>\n" . $tag_string . "\t" . ($artist->albums ?: 0) . "\n" . "\t" . ($artist->songs ?: 0) . "\n" . "\t" . ($rating->get_user_rating() ?: 0) . "\n" . "\t" . ($rating->get_user_rating() ?: 0) . "\n" . "\t" . ($rating->get_average_rating() ?: 0) . "\n" . "\t" . $artist->mbid . "\n" . "\tsummary . "]]>\n" . "\t" . $artist->yearformed . "\n" . "\tplaceformed . "]]>\n" . "\n"; } // end foreach artists return self::output_xml($string); } // artists /** * albums * * This echos out a standard albums XML document, it pays attention to the limit * * @param array $albums (description here...) * @return string return xml */ public static function albums($albums) { if (count($albums) > self::$limit or self::$offset > 0) { $albums = array_splice($albums,self::$offset,self::$limit); } Rating::build_cache('album',$albums); $string = ""; foreach ($albums as $album_id) { $album = new Album($album_id); $album->format(); $rating = new Rating($album_id,'album'); // Build the Art URL, include session $art_url = AmpConfig::get('web_path') . '/image.php?object_id=' . $album->id . '&object_type=album&auth=' . scrub_out($_REQUEST['auth']); $string .= "id . "\">\n" . "\tname . "]]>\n"; // Do a little check for artist stuff if ($album->artist_count != 1) { $string .= "\t\n"; } else { $string .= "\tartist_id\">artist_name]]>\n"; } $string .= "\t" . $album->year . "\n" . "\t" . $album->song_count . "\n" . "\t" . $album->disk . "\n" . self::tags_string($album->tags) . "\t\n" . "\t" . $rating->get_user_rating() . "\n" . "\t" . $rating->get_user_rating() . "\n" . "\t" . $rating->get_average_rating() . "\n" . "\t" . $album->mbid . "\n" . "\n"; } // end foreach return self::output_xml($string); } // albums /** * playlists * * This takes an array of playlist ids and then returns a nice pretty XML document * * @param array $playlists (description here...) * @return string return xml */ public static function playlists($playlists) { if (count($playlists) > self::$limit or self::$offset > 0) { $playlists = array_slice($playlists,self::$offset,self::$limit); } $string = ''; // Foreach the playlist ids foreach ($playlists as $playlist_id) { $playlist = new Playlist($playlist_id); $playlist->format(); $item_total = $playlist->get_media_count('song'); // Build this element $string .= "id\">\n" . "\tname]]>\n" . "\tf_user]]>\n" . "\t$item_total\n" . "\t$playlist->type\n" . "\n"; } // end foreach return self::output_xml($string); } // playlists /** * songs * * This returns an xml document from an array of song ids. * (Spiffy isn't it!) */ public static function songs($songs, $playlist_data='') { if (count($songs) > self::$limit or self::$offset > 0) { $songs = array_slice($songs, self::$offset, self::$limit); } Song::build_cache($songs); Stream::set_session($_REQUEST['auth']); $string = ""; // Foreach the ids! foreach ($songs as $song_id) { $song = new Song($song_id); // If the song id is invalid/null if (!$song->id) { continue; } $song->format(); $playlist_track_string = self::playlist_song_tracks_string($song, $playlist_data); $tag_string = self::tags_string(Tag::get_top_tags('song', $song_id)); $rating = new Rating($song_id, 'song'); $art_url = Art::url($song->album, 'album', $_REQUEST['auth']); $string .= "id . "\">\n" . "\t<![CDATA[" . $song->title . "]]>\n" . "\tartist . '">get_artist_name() . "]]>\n" . "\talbum . '">get_album_name() . "]]>\n"; if ($song->albumartist) { $string .= "\talbumartist . "\">get_album_artist_name() . "]]>\n"; } $string .= $tag_string . "\tfile . "]]>\n" . "\t" . $song->track . "\n" . $playlist_track_string . "\t\n" . "\t" . $song->year . "\n" . "\t" . $song->bitrate . "\n" . "\t" . $song->rate . "\n" . "\t" . $song->mode . "\n" . "\t" . $song->mime . "\n" . "\tid, '', 'api') . "]]>\n" . "\t" . $song->size . "\n" . "\t" . $song->mbid . "\n" . "\t" . $song->album_mbid . "\n" . "\t" . $song->artist_mbid . "\n" . "\t" . $song->albumartist_mbid . "\n" . "\t\n" . "\t" . ($rating->get_user_rating() ?: 0) . "\n" . "\t" . ($rating->get_user_rating() ?: 0) . "\n" . "\t" . ($rating->get_average_rating() ?: 0) . "\n" . "\tcomposer . "]]>\n" . "\t" . $song->channels . "\n" . "\tcomment . "]]>\n"; $string .= "\tlabel . "]]>\n" . "\t" . $song->language . "\n" . "\t" . $song->replaygain_album_gain . "\n" . "\t" . $song->replaygain_album_peak . "\n" . "\t" . $song->replaygain_track_gain . "\n" . "\t" . $song->replaygain_track_peak . "\n"; foreach ($song->tags as $tag) { $string .= "\t\n"; } $string .= "\n"; } // end foreach return self::output_xml($string); } // songs /** * videos * * This builds the xml document for displaying video objects * * @param array $videos (description here...) * @return string return xml */ public static function videos($videos) { if (count($videos) > self::$limit or self::$offset > 0) { $videos = array_slice($videos,self::$offset,self::$limit); } $string = ''; foreach ($videos as $video_id) { $video = new Video($video_id); $video->format(); $string .= "\n"; } // end foreach return self::output_xml($string); } // videos /** * democratic * * This handles creating an xml document for democratic items, this can be a little complicated * due to the votes and all of that * * @param array $object_ids Object IDs * @return string return xml */ public static function democratic($object_ids=array()) { if (!is_array($object_ids)) { $object_ids = array(); } $democratic = Democratic::get_current_playlist(); $string = ''; foreach ($object_ids as $row_id=>$data) { $song = new $data['object_type']($data['object_id']); $song->format(); //FIXME: This is duplicate code and so wrong, functions need to be improved $tag = new Tag($song->tags['0']); $song->genre = $tag->id; $song->f_genre = $tag->name; $tag_string = self::tags_string($song->tags); $rating = new Rating($song->id,'song'); $art_url = Art::url($song->album, 'album', $_REQUEST['auth']); $string .= "id . "\">\n" . "\t<![CDATA[" . $song->title . "]]>\n" . "\tartist . "\">f_artist_full . "]]>\n" . "\talbum . "\">f_album_full . "]]>\n" . "\tgenre . "\">f_genre . "]]>\n" . $tag_string . "\t" . $song->track . "\n" . "\t\n" . "\t" . $song->mime . "\n" . "\tid, '', 'api') . "]]>\n" . "\t" . $song->size . "\n" . "\t\n" . "\t" . $rating->get_user_rating() . "\n" . "\t" . $rating->get_user_rating() . "\n" . "\t" . $rating->get_average_rating() . "\n" . "\t" . $democratic->get_vote($row_id) . "\n" . "\n"; } // end foreach return self::output_xml($string); } // democratic /** * user * * This handles creating an xml document for an user * * @param User $user User * @return string return xml */ public static function user(User $user) { $user->format(); $string = "id . "\">\n" . "\tusername . "]]>\n" . "\t" . $user->create_date . "\n" . "\t" . $user->last_seen . "\n" . "\twebsite . "]]>\n" . "\tstate . "]]>\n" . "\tcity . "]]>\n"; if ($user->fullname_public) { $string .= "\tfullname . "]]>\n"; } $string .= "\n"; return self::output_xml($string); } // user /** * users * * This handles creating an xml document for an user list * * @param int[] $users User identifier list * @return string return xml */ public static function users($users) { $string = "\n"; foreach ($users as $user_id) { $user = new User($user_id); $string .= "\tusername . "]]>\n"; } $string .= "\n"; return self::output_xml($string); } // users /** * shouts * * This handles creating an xml document for a shout list * * @param int[] $shouts Shout identifier list * @return string return xml */ public static function shouts($shouts) { $string = "\n"; foreach ($shouts as $shout_id) { $shout = new Shoutbox($shout_id); $shout->format(); $user = new User($shout->user); $string .= "\t\n" . "\t\t" . $shout->date . "\n" . "\t\ttext . "]]>\n"; if ($user->id) { $string .= "\t\tusername . "]]>"; } $string .= "\tn"; } $string .= "\n"; return self::output_xml($string); } // shouts public static function output_xml($string) { return self::_header() . UI::clean_utf8($string) . self::_footer(); } /** * timeline * * This handles creating an xml document for an activity list * * @param int[] $activities Activity identifier list * @return string return xml */ public static function timeline($activities) { $string = "\n"; foreach ($activities as $aid) { $activity = new Useractivity($aid); $shout->format(); $user = new User($activity->user); $string .= "\t\n" . "\t\t" . $activity->activity_date . "\n" . "\t\tobject_type . "]]>\n" . "\t\t" . $activity->object_id . "\n" . "\t\ttext . "]]>\n"; if ($user->id) { $string .= "\t\tusername . "]]>"; } $string .= "\tn"; } $string .= "\n"; $final = self::_header() . $string . self::_footer(); return $final; } // timeline /** * rss_feed * * (description here...) * * @param array $data (descriptiong here...) * @param string $title RSS feed title * @param string $description (not use yet?) * @param string $date publish date * @return string RSS feed xml * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public static function rss_feed($data, $title, $description, $date = null) { $string = "\t$title\n\t" . AmpConfig::get('web_path') . "\n\t"; if ($date != null) { $string .= "" . date("r", $date) . "\n"; } // Pass it to the keyed array xml function foreach ($data as $item) { // We need to enclose it in an item tag $string .= self::keyed_array(array('item'=>$item), 1); } $final = self::_header() . $string . self::_footer(); return $final; } // rss_feed /** * _header * * this returns a standard header, there are a few types * so we allow them to pass a type if they want to * * @return string Header xml tag. */ private static function _header($title = null) { switch (self::$type) { case 'xspf': $header = "\n" . "\n" . "" . ($title ?: "Ampache XSPF Playlist") . "\n" . "" . scrub_out(AmpConfig::get('site_title')) . "\n" . "" . scrub_out(AmpConfig::get('site_title')) . "\n" . "" . AmpConfig::get('web_path') . "\n" . "\n"; break; case 'itunes': $header = "\n" . "\n"; "\n" . "\n" . "\n" . " Major Version1\n" . " Minor Version1\n" . " Application Version7.0.2\n" . " Features1\n" . " Show Content Ratings\n" . " Tracks\n" . " \n"; break; case 'rss': $header = "\n " . "\n" . "\n\n"; break; default: $header = "\n\n"; break; } // end switch return $header; } // _header /** * _footer * * this returns the footer for this document, these are pretty boring * * @return string Footer xml tag. */ private static function _footer() { switch (self::$type) { case 'itunes': $footer = "\t\t\t\n\n\n"; break; case 'xspf': $footer = "\n\n"; break; case 'rss': $footer = "\n\n\n"; break; default: $footer = "\n\n"; break; } // end switch on type return $footer; } // _footer public static function podcast(library_item $libitem) { $xml = new SimpleXMLElement(''); $xml->addAttribute("xmlns:xmlns:atom", "http://www.w3.org/2005/Atom"); $xml->addAttribute("xmlns:xmlns:itunes", "http://www.itunes.com/dtds/podcast-1.0.dtd"); $xml->addAttribute("version", "2.0"); $xchannel = $xml->addChild("channel"); $xchannel->addChild("title", $libitem->get_fullname() . " Podcast"); $xlink = $xchannel->addChild("atom:link", htmlentities($libitem->link)); if (Art::has_db($libitem->id, get_class($libitem))) { $ximg = $xchannel->addChild("xmlns:itunes:image"); $ximg->addAttribute("href", Art::url($libitem->id, get_class($libitem))); } $summary = $libitem->get_description(); if (!empty($summary)) { $summary = htmlentities($summary); $xchannel->addChild("description", $summary); $xchannel->addChild("xmlns:itunes:summary", $summary); } $xchannel->addChild("generator", "Ampache"); $xchannel->addChild("xmlns:itunes:category", "Music"); $owner = $libitem->get_user_owner(); if ($owner) { $user_owner = new User($owner); $user_owner->format(); $xowner = $xchannel->addChild("xmlns:itunes:owner"); $xowner->addChild("xmlns:itunes:name", $user_owner->f_name); } $medias = $libitem->get_medias(); foreach ($medias as $media_info) { $media = new $media_info['object_type']($media_info['object_id']); $media->format(); $xitem = $xchannel->addChild("item"); $xitem->addChild("title", htmlentities($media->get_fullname())); if ($media->f_artist) { $xitem->addChild("xmlns:itunes:author", $media->f_artist); } $xmlink = $xitem->addChild("link", htmlentities($media->link)); $xitem->addChild("guid", htmlentities($media->link)); if ($media->addition_time) { $xitem->addChild("pubDate", date("r", $media->addition_time)); } $description = $media->get_description(); if (!empty($description)) { $xitem->addChild("description", htmlentities($description)); } $xitem->addChild("xmlns:itunes:duration", $media->f_time); if ($media->mime) { $surl = $media_info['object_type']::play_url($media_info['object_id']); $xencl = $xitem->addChild("enclosure"); $xencl->addAttribute("type", $media->mime); $xencl->addAttribute("length", $media->size); $xencl->addAttribute("url", $surl); } } $xmlstr = $xml->asXml(); // Format xml output $dom = new DOMDocument(); if ($dom->loadXML($xmlstr) !== false) { $dom->formatOutput = true; return $dom->saveXML(); } else { return $xmlstr; } } } // XML_Data