2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
20 * @package block_tag_youtube
21 * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com)
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 define('DEFAULT_NUMBER_OF_VIDEOS', 5);
27 class block_tag_youtube extends block_base {
30 * @var Google_Service_Youtube
32 protected $service = null;
35 $this->title = get_string('pluginname','block_tag_youtube');
36 $this->config = new stdClass();
39 function applicable_formats() {
40 return array('tag' => true);
44 * It can be configured.
48 public function has_config() {
52 function specialization() {
53 $this->title = !empty($this->config->title) ? $this->config->title : get_string('pluginname', 'block_tag_youtube');
54 // Convert numeric categories (old YouTube API) to
55 // textual ones (new Google Data API)
56 $this->config->category = !empty($this->config->category) ? $this->category_map_old2new($this->config->category) : '0';
59 function instance_allow_multiple() {
63 function get_content() {
66 //note: do NOT include files at the top of this file
67 require_once($CFG->dirroot.'/tag/lib.php');
68 require_once($CFG->libdir . '/filelib.php');
70 if ($this->content !== NULL) {
71 return $this->content;
74 $this->content = new stdClass();
75 $this->content->footer = '';
77 if (!$this->get_service()) {
78 $this->content->text = $this->get_error_message();
79 return $this->content;
83 if(!empty($this->config->playlist)){
84 //videos from a playlist
85 $text = $this->get_videos_by_playlist();
88 if(!empty($this->config->category)){
89 //videos from category with tag
90 $text = $this->get_videos_by_tag_and_category();
94 $text = $this->get_videos_by_tag();
98 $this->content->text = $text;
100 return $this->content;
103 function get_videos_by_playlist(){
105 if (!$service = $this->get_service()) {
106 return $this->get_error_message();
109 $numberofvideos = DEFAULT_NUMBER_OF_VIDEOS;
110 if( !empty($this->config->numberofvideos)) {
111 $numberofvideos = $this->config->numberofvideos;
115 $response = $service->playlistItems->listPlaylistItems('id,snippet', array(
116 'playlistId' => $this->config->playlist,
117 'maxResults' => $numberofvideos
119 } catch (Google_Service_Exception $e) {
120 debugging('Google service exception: ' . $e->getMessage(), DEBUG_DEVELOPER);
121 return $this->get_error_message(get_string('requesterror', 'block_tag_youtube'));
124 return $this->render_items($response);
127 function get_videos_by_tag(){
129 if (!$service = $this->get_service()) {
130 return $this->get_error_message();
133 $tagid = optional_param('id', 0, PARAM_INT); // tag id - for backware compatibility
134 $tag = optional_param('tag', '', PARAM_TAG); // tag
137 $tagobject = tag_get('name', $tag);
139 $tagobject = tag_get('id', $tagid);
142 if (empty($tagobject)) {
146 $querytag = urlencode($tagobject->name);
148 $numberofvideos = DEFAULT_NUMBER_OF_VIDEOS;
149 if ( !empty($this->config->numberofvideos) ) {
150 $numberofvideos = $this->config->numberofvideos;
154 $response = $service->search->listSearch('id,snippet', array(
157 'maxResults' => $numberofvideos
159 } catch (Google_Service_Exception $e) {
160 debugging('Google service exception: ' . $e->getMessage(), DEBUG_DEVELOPER);
161 return $this->get_error_message(get_string('requesterror', 'block_tag_youtube'));
164 return $this->render_items($response);
167 function get_videos_by_tag_and_category(){
169 if (!$service = $this->get_service()) {
170 return $this->get_error_message();
173 $tagid = optional_param('id', 0, PARAM_INT); // tag id - for backware compatibility
174 $tag = optional_param('tag', '', PARAM_TAG); // tag
177 $tagobject = tag_get('name', $tag);
179 $tagobject = tag_get('id', $tagid);
182 if (empty($tagobject)) {
186 $querytag = urlencode($tagobject->name);
188 $numberofvideos = DEFAULT_NUMBER_OF_VIDEOS;
189 if( !empty($this->config->numberofvideos)) {
190 $numberofvideos = $this->config->numberofvideos;
194 $response = $service->search->listSearch('id,snippet', array(
197 'maxResults' => $numberofvideos,
198 'videoCategoryId' => $this->config->category
200 } catch (Google_Service_Exception $e) {
201 debugging('Google service exception: ' . $e->getMessage(), DEBUG_DEVELOPER);
202 return $this->get_error_message(get_string('requesterror', 'block_tag_youtube'));
205 return $this->render_items($response);
209 * Sends a request to fetch data.
211 * @see block_tag_youtube::service
212 * @deprecated since Moodle 2.8.8, 2.9.2 and 3.0 MDL-49085 - please do not use this function any more.
213 * @param string $request
214 * @throws coding_exception
216 public function fetch_request($request) {
217 throw new coding_exception('Sorry, this function has been deprecated in Moodle 2.8.8, 2.9.2 and 3.0. Use block_tag_youtube::get_service instead.');
219 $c = new curl(array('cache' => true, 'module_cache'=>'tag_youtube'));
220 $c->setopt(array('CURLOPT_TIMEOUT' => 3, 'CURLOPT_CONNECTTIMEOUT' => 3));
222 $response = $c->get($request);
224 $xml = new SimpleXMLElement($response);
225 return $this->render_video_list($xml);
229 * Renders the video list.
231 * @see block_tag_youtube::render_items
232 * @deprecated since Moodle 2.8.8, 2.9.2 and 3.0 MDL-49085 - please do not use this function any more.
233 * @param SimpleXMLElement $xml
234 * @throws coding_exception
236 function render_video_list(SimpleXMLElement $xml){
237 throw new coding_exception('Sorry, this function has been deprecated in Moodle 2.8.8, 2.9.2 and 3.0. Use block_tag_youtube::render_items instead.');
241 * Returns an error message.
243 * Useful when the block is not properly set or something goes wrong.
245 * @param string $message The message to display.
246 * @return string HTML
248 protected function get_error_message($message = null) {
251 if (empty($message)) {
252 $message = get_string('apierror', 'block_tag_youtube');
254 return $OUTPUT->notification($message);
258 * Gets the youtube service object.
260 * @return Google_Service_YouTube
262 protected function get_service() {
265 if (!$apikey = get_config('block_tag_youtube', 'apikey')) {
269 // Wrapped in an if in case we call different get_videos_* multiple times.
270 if (!isset($this->service)) {
271 require_once($CFG->libdir . '/google/lib.php');
272 $client = get_google_client();
273 $client->setDeveloperKey($apikey);
274 $client->setScopes(array(Google_Service_YouTube::YOUTUBE_READONLY));
275 $this->service = new Google_Service_YouTube($client);
278 return $this->service;
282 * Renders the list of items.
284 * @param array $videosdata
285 * @return string HTML
287 protected function render_items($videosdata) {
289 if (!$videosdata || empty($videosdata->items)) {
290 if (!empty($videosdata->error)) {
291 debugging('Error fetching data from youtube: ' . $videosdata->error->message, DEBUG_DEVELOPER);
296 // If we reach that point we already know that the API key is set.
297 $service = $this->get_service();
299 $text = html_writer::start_tag('ul', array('class' => 'yt-video-entry unlist img-text'));
300 foreach ($videosdata->items as $video) {
302 // Link to the video included in the playlist if listing a playlist.
303 if (!empty($video->snippet->resourceId)) {
304 $id = $video->snippet->resourceId->videoId;
305 $playlist = '&list=' . $video->snippet->playlistId;
307 $id = $video->id->videoId;
311 $thumbnail = $video->snippet->getThumbnails()->getDefault();
312 $url = 'http://www.youtube.com/watch?v=' . $id . $playlist;
314 $videodetails = $service->videos->listVideos('id,contentDetails', array('id' => $id));
315 if ($videodetails && !empty($videodetails->items)) {
317 // We fetch by id so we just use the first one.
318 $details = $videodetails->items[0];
319 $start = new DateTime('@0');
320 $start->add(new DateInterval($details->contentDetails->duration));
321 $seconds = $start->format('U');
324 $text .= html_writer::start_tag('li');
326 $imgattrs = array('class' => 'youtube-thumb', 'src' => $thumbnail->url, 'alt' => $video->snippet->title);
327 $thumbhtml = html_writer::empty_tag('img', $imgattrs);
328 $link = html_writer::tag('a', $thumbhtml, array('href' => $url));
329 $text .= html_writer::tag('div', $link, array('class' => 'clearfix'));
331 $text .= html_writer::tag('span', html_writer::tag('a', $video->snippet->title, array('href' => $url)));
333 if (!empty($seconds)) {
334 $text .= html_writer::tag('div', format_time($seconds));
336 $text .= html_writer::end_tag('li');
338 $text .= html_writer::end_tag('ul');
343 function get_categories() {
344 // TODO: Right now using sticky categories from
345 // http://gdata.youtube.com/schemas/2007/categories.cat
346 // This should be performed from time to time by the block insead
347 // and cached somewhere, avoiding deprecated ones and observing regions
349 '0' => get_string('anycategory', 'block_tag_youtube'),
350 'Film' => get_string('filmsanimation', 'block_tag_youtube'),
351 'Autos' => get_string('autosvehicles', 'block_tag_youtube'),
352 'Music' => get_string('music', 'block_tag_youtube'),
353 'Animals'=> get_string('petsanimals', 'block_tag_youtube'),
354 'Sports' => get_string('sports', 'block_tag_youtube'),
355 'Travel' => get_string('travel', 'block_tag_youtube'),
356 'Games' => get_string('gadgetsgames', 'block_tag_youtube'),
357 'Comedy' => get_string('comedy', 'block_tag_youtube'),
358 'People' => get_string('peopleblogs', 'block_tag_youtube'),
359 'News' => get_string('newspolitics', 'block_tag_youtube'),
360 'Entertainment' => get_string('entertainment', 'block_tag_youtube'),
361 'Education' => get_string('education', 'block_tag_youtube'),
362 'Howto' => get_string('howtodiy', 'block_tag_youtube'),
363 'Tech' => get_string('scienceandtech', 'block_tag_youtube')
368 * Provide conversion from old numeric categories available in youtube API
369 * to the new ones available in the Google API
371 * @param int $oldcat old category code
372 * @return mixed new category code or 0 (if no match found)
374 * TODO: Someday this should be applied on upgrade for all the existing
375 * block instances so we won't need the mapping any more. That would imply
376 * to implement restore handling to perform the conversion of old blocks.
378 function category_map_old2new($oldcat) {
379 $oldoptions = array (
384 24 => 'Entertainment',
394 if (array_key_exists($oldcat, $oldoptions)) {
395 return $oldoptions[$oldcat];