/[drupal]/contributions/modules/lastfmsimple/lastfmsimple.module
ViewVC logotype

Contents of /contributions/modules/lastfmsimple/lastfmsimple.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.3 - (show annotations) (download) (as text)
Sun Apr 5 21:02:08 2009 UTC (7 months, 3 weeks ago) by mikereem
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-6--3
Changes since 1.2: +18 -4 lines
File MIME type: text/x-php
Theme function implemented.
1 <?php
2
3 /**
4 * @defgroup lastfmsimple Last.fm Simple Stats: simple Last.fm user statistics parser.
5 *
6 * This module can fetch user statistics from Last.fm's Audioscrobbler web
7 * services, cache them in the database and perform customisable and intelligent
8 * updates.
9 */
10
11 /**
12 * @file
13 * Last.fm Simple Stats module file.
14 *
15 * @ingroup lastfmsimple
16 */
17
18 global $_lastfmsimple_username_field;
19
20 define('LASTFMSIMPLE_UPDATE_CRON', 0);
21 define('LASTFMSIMPLE_UPDATE_REQUEST', 1);
22 define('LASTFMSIMPLE_UPDATE_DEFAULT', LASTFMSIMPLE_UPDATE_REQUEST);
23 define('LASTFMSIMPLE_STATLINE_LINK', 0);
24 define('LASTFMSIMPLE_STATLINE_TEXT', 1);
25 define('LASTFMSIMPLE_STATLINE_DEFAULT', LASTFMSIMPLE_STATLINE_LINK);
26 define('LASTFMSIMPLE_BLOCK_STAT_TYPE_WEEKLYARTISTCHART', "weeklyartistchart");
27 define('LASTFMSIMPLE_BLOCK_STAT_TYPE_RECENTTRACKS', "recenttracks");
28
29 /**
30 * Implementation of hook_init().
31 */
32 function lastfmsimple_init() {
33 global $_lastfmsimple_username_field;
34 $_lastfmsimple_username_field = _lastfmsimple_get_field(variable_get('lastfmsimple_username_field_id', NULL));
35 }
36
37 /**
38 * Implementation of hook_help().
39 */
40 function lastfmsimple_help($path, $arg) {
41 switch ($path) {
42 case 'admin/help#lastfmsimple':
43 return '<p>'. t('This module ables you to load in user statistics from Last.fm\'s Audioscrobbler web services. The statistics can be shown using <code>lastfmsimple_show_feed($user, $type, $count)</code>, which returns an array of item objects.') .'</p>';
44 }
45 }
46
47 /**
48 * Implementation of the hook_block().
49 * Generate HTML for the lastfmsimple block
50 * @param op the operation from the URL
51 * @param delta offset
52 * @returns block HTML
53 */
54 function lastfmsimple_block($op='list', $delta=0) {
55 global $user;
56 global $_lastfmsimple_username_field;
57 // If the _lastfmsimple_username_field hasn't been set yet, do not continue
58 if (!isset($_lastfmsimple_username_field)) {
59 return;
60 }
61 // listing of blocks, such as on the admin/block page
62 if ($op == "list") {
63 $block[0]["info"] = t("Last.fm Simple Stats");
64 return $block;
65 } else if ($op == 'view') {
66 // content variable that will be returned for display
67 $block_content = '';
68 $result = db_query("SELECT * FROM {profile_values} WHERE uid = %d AND fid = %d", $user->uid, variable_get('lastfmsimple_username_field_id', NULL));
69 $lastfmuser = db_fetch_object($result);
70 if ($lastfmuser !== FALSE) {
71 $content = lastfmsimple_show_feed($lastfmuser->value, variable_get('lastfmsimple_block_stattype', NULL), "10");
72
73 // setting up the block
74 $block_content = _lastfmsimple_create_content($content, variable_get('lastfmsimple_block_stattype', NULL));
75 }
76 // check to see if there was any content before setting up the block
77 if ($block_content == '') {
78 // no content from a week ago, return nothing.
79 return;
80 }
81 // set up the block
82 $block['subject'] = 'Last.fm Stats';
83 $block['content'] = $block_content;
84 return $block;
85 }
86 }
87
88 /**
89 * Implementation of hook_menu().
90 */
91 function lastfmsimple_menu() {
92 return array(
93 'admin/settings/lastfmsimple' => array(
94 'title' => 'Last.fm Simple Stats',
95 'description' => 'Configure Last.fm Simple Stats settings.',
96 'page callback' => 'drupal_get_form',
97 'page arguments' => array('lastfmsimple_admin_settings'),
98 'access arguments' => array('administer site configuration'),
99 'file' => 'lastfmsimple.admin.inc',
100 ),
101 );
102 }
103
104 /**
105 * Return an array of item objects for a feed.
106 *
107 * @param $user
108 * A string containing the Last.fm user name.
109 * @param $type
110 * A string containing the feed type. By default: "recenttracks".
111 * @param $count
112 * The number of items to be shown. By default: 10.
113 * @return
114 * An array of item objects.
115 */
116 function lastfmsimple_show_feed($user, $type = 'recenttracks', $count = 10) {
117 // Only these are tested/supported (for now).
118 if (!in_array($type, array('recenttracks', 'weeklyartistchart'))) {
119 return array();
120 }
121
122 // Get the feed.
123 $feed = db_fetch_object(db_query("SELECT * FROM {lastfmsimple_feed} WHERE usr = '%s' AND feed_type = '%s'", $user, $type));
124
125 if ($feed === FALSE) {
126 // New feed.
127 $id = _lastfmsimple_new_feed($user, $type, $count);
128 }
129 else {
130 // Existing feed.
131
132 if ($count > $feed->current_count && $count <= $feed->max_count) {
133 // Need more items while possible, update.
134 _lastfmsimple_update_feed($feed, $count);
135 }
136 elseif ((int) variable_get('lastfmsimple_update_method', LASTFMSIMPLE_UPDATE_DEFAULT) === LASTFMSIMPLE_UPDATE_REQUEST) {
137 // Updating is done on request.
138
139 if ($feed->expires > 0) {
140 // There's an Expires header set, update when it's in the past.
141 $update = ($feed->expires <= time());
142 }
143 else {
144 // When the interval is zero (update always), do so;
145 // otherwise, update when the previous update is too long ago.
146 $interval = (int) variable_get('lastfmsimple_interval', 5);
147 $update = $interval === 0 ? TRUE : ($feed->updated <= (time() - $interval * 60));
148 }
149
150 // Do the update.
151 if ($update) {
152 _lastfmsimple_update_feed($feed);
153 }
154 }
155
156 $id = $feed->fid;
157 }
158
159 // Get the items for this feed.
160 $result = db_query_range("SELECT name, url, item_data FROM {lastfmsimple_item} WHERE fid = %d ORDER BY iid ASC", $id, 0, $count);
161
162 // Fetch items as objects into an array.
163 $items = array();
164 while ($item = db_fetch_object($result)) {
165 // Unserialize item_data and assign values as properties, if present.
166 if (!empty($item->item_data)) {
167 foreach (unserialize($item->item_data) as $key => $val) {
168 $item->$key = $val;
169 }
170 }
171
172 // Unset item_data.
173 unset($item->item_data);
174
175 $items[] = $item;
176 }
177
178 return $items;
179 }
180
181 /**
182 * Implementation of hook_cron().
183 */
184 function lastfmsimple_cron() {
185 // Cron is unused.
186 if ((int) variable_get('lastfmsimple_update_method', LASTFMSIMPLE_UPDATE_DEFAULT) !== LASTFMSIMPLE_UPDATE_CRON) {
187 return;
188 }
189
190 $interval = (int) variable_get('lastfmsimple_interval', 5);
191
192 if ($interval === 0) {
193 // Update all the feeds without Expires and with an Expires in the past.
194 $result = db_query("SELECT * FROM {lastfmsimple_feed} WHERE expires <= %d", time());
195 }
196 else {
197 // Update all the feeds without Expires but updated too long ago or with an Expires in the past.
198 $result = db_query("SELECT * FROM {lastfmsimple_feed} WHERE (expires = 0 AND updated <= %d) OR (expires > 0 AND expires <= %d)", (time() - $interval * 60), time());
199 }
200
201 // Update all of them.
202 while ($feed = db_fetch_object($result)) {
203 _lastfmsimple_update_feed($feed);
204 }
205 }
206
207 /*
208 * Implementation of hook_theme().
209 */
210 function lastfmsimple_theme() {
211 return array(
212 'lastfmsimple_show_recenttracks' => array(
213 'arguments' => array('content' => NULL),
214 ),
215 'lastfmsimple_show_weeklyartistchart' => array(
216 'arguments' => array('content' => NULL),
217 ),
218 );
219 }
220
221 /**
222 * Create block content based on statistic type.
223 *
224 * @param $content
225 * Result of the feed query.
226 * @param $type
227 * String containing the selected feed type.
228 * @return unknown
229 * Returns the formatted block content.
230 */
231 function _lastfmsimple_create_content($content, $type) {
232 switch ($type) {
233 case 'recenttracks':
234 return theme('lastfmsimple_show_recenttracks',$content);
235 case 'weeklyartistchart':
236 return theme('lastfmsimple_show_weeklyartistchart',$content);
237 }
238 }
239
240 /**
241 * Create block content for recenttracks stat.
242 *
243 * @param $content
244 * Result of the feed query.
245 * @return unknown
246 * Returns the formatted block content.
247 */
248 function theme_lastfmsimple_show_recenttracks($content) {
249 // setting up the block
250 $statline = variable_get('lastfmsimple_statline_display', LASTFMSIMPLE_STATLINE_DEFAULT);
251 $block_content = '<ol>';
252 foreach ( $content as $item ):
253 if ($statline == LASTFMSIMPLE_STATLINE_LINK) {
254 $block_content .= '<li><a href="'. $item->url . '">' . $item->artist . ' - ' . $item->name . '</a></li>';
255 } else {
256 $block_content .= '<li>' . $item->artist . ' - ' . $item->name . '</li>';
257 }
258 endforeach;
259 $block_content .= '</ol>';
260
261 return $block_content;
262 }
263
264 /**
265 * Create block content for weeklyartistchart stat.
266 *
267 * @param $content
268 * Result of the feed query.
269 * @return unknown
270 * Returns the formatted block content.
271 */
272 function theme_lastfmsimple_show_weeklyartistchart($content) {
273 // setting up the block
274 $statline = variable_get('lastfmsimple_statline_display', LASTFMSIMPLE_STATLINE_DEFAULT);
275 $block_content = '<ol>';
276 foreach ( $content as $item ):
277 if ($statline == LASTFMSIMPLE_STATLINE_LINK) {
278 $block_content .= '<li><a href="'. $item->url . '">' . $item->name . '</a> (' . $item->playcount . ')</li>';
279 } else {
280 $block_content .= '<li>' . $item->name . ' (' . $item->playcount . ')</li>';
281 }
282 endforeach;
283 $block_content .= '</ol>';
284
285 return $block_content;
286 }
287
288 /**
289 * Create a new feed that is not yet in the database.
290 *
291 * @param $user
292 * A string containing the Last.fm user name.
293 * @param $type
294 * A string containing the feed type.
295 * @param $count
296 * The number of items to be shown.
297 * @return
298 * The ID for the feed just created.
299 */
300 function _lastfmsimple_new_feed($user, $type, $count) {
301 // Fetch the feed.
302 $request = _lastfmsimple_fetch_feed($user, $type);
303
304 if ($request === FALSE) {
305 return;
306 }
307
308 // Insert into the database.
309 db_query("INSERT INTO {lastfmsimple_feed} (usr, feed_type, updated, modified, expires, current_count, max_count, fetches, updates) VALUES ('%s', '%s', %d, %d, %d, %d, %d, 1, 1)", $user, $type, time(), $request['headers']['Last-Modified'], $request['headers']['Expires'], $count, $request['max_count']);
310
311 // Feed ID.
312 $id = db_last_insert_id('lastfmsimple_feed', 'fid');
313
314 // Parse the feed data.
315 _lastfmsimple_parse_feed($request['data'], $id, $request['element'], $count);
316
317 return $id;
318 }
319
320 /**
321 * Update an existing feed.
322 *
323 * Supplying a $count will disable the usage of If-Modified-Since, thus always
324 * parsing the feed.
325 *
326 * @param $feed
327 * The feed object.
328 * @param $count
329 * Optional number of items, when being changed.
330 */
331 function _lastfmsimple_update_feed(&$feed, $count = NULL) {
332 if ($count === NULL) {
333 // Count is not being changed, use the current count and fetch using the Last-Modified header.
334 $count = (int) $feed->current_count;
335 $request = _lastfmsimple_fetch_feed($feed->usr, $feed->feed_type, $feed->modified);
336 }
337 else {
338 // Count is being changed, fetch without the Last-Modified header.
339 $request = _lastfmsimple_fetch_feed($feed->usr, $feed->feed_type);
340 }
341
342 if ($request === FALSE) {
343 return;
344 }
345
346 // The feed was not modified, update the feed's row and return.
347 if ($request['not_modified']) {
348 db_query("UPDATE {lastfmsimple_feed} SET updated = %d, fetches = fetches + 1 WHERE fid = %d", time(), $feed->fid);
349
350 return;
351 }
352
353 // Remove existing items.
354 db_query("DELETE FROM {lastfmsimple_item} WHERE fid = %d", $feed->fid);
355
356 // Parse the feed data.
357 _lastfmsimple_parse_feed($request['data'], $feed->fid, $request['element'], $count);
358
359 // Update the feed's row.
360 db_query("UPDATE {lastfmsimple_feed} SET updated = %d, modified = %d, expires = %d, current_count = %d, max_count = %d, fetches = fetches + 1, updates = updates + 1 WHERE fid = %d", time(), $request['headers']['Last-Modified'], $request['headers']['Expires'], $count, $request['max_count'], $feed->fid);
361 }
362
363 /**
364 * Fetch a feed.
365 *
366 * @param $user
367 * A string containing the Last.fm user name.
368 * @param $type
369 * A string containing the feed type.
370 * @param $modified_since
371 * UNIX timestamp containing Modified-Since date (optional).
372 * @return
373 * Array containing data, array of headers, not_modified boolean, element name and max_count.
374 */
375 function _lastfmsimple_fetch_feed($user, $type, $modified_since = 0) {
376 // Use an If-Modified-Since header when possible.
377 $headers = $modified_since > 0 ? array('If-Modified-Since' => gmdate('D, d M Y H:i:s', $modified_since)) : array();
378
379 // Do a HTTP request.
380 $request = drupal_http_request('http://ws.audioscrobbler.com/1.0/user/'. urlencode($user) .'/'. $type .'.xml', $headers);
381
382 // Response code.
383 $code = (int) $request->code;
384
385 // Something went wrong.
386 if (!in_array($code, array(200, 302, 304, 307))) {
387 watchdog('lastfmsimple', 'Feed %feed for user %user could not be fetched: %error.', array(
388 '%user' => $user,
389 '%feed' => $type,
390 '%error' => $request->code .' '. $request->error
391 ), WATCHDOG_WARNING);
392
393 return FALSE;
394 }
395
396 // What's the item's element?
397 $element = array(
398 'recenttracks' => 'track',
399 'weeklyartistchart' => 'artist'
400 );
401 $element = $element[$type];
402
403 $expires = array_key_exists('Expires', $request->headers) ? strtotime($request->headers['Expires']) : 0;
404
405 return array(
406 'data' => $request->data,
407 'headers' => array(
408 'Last-Modified' => array_key_exists('Last-Modified', $request->headers) ? strtotime($request->headers['Last-Modified']) : 0,
409 // Do not use Expires when it's in the past.
410 'Expires' => $expires > time() ? $expires : 0,
411 ),
412 // Feed was not modified when we got response code 304.
413 'not_modified' => ($code === 304),
414 'element' => $element,
415 // Number of feed items present.
416 'max_count' => substr_count($request->data, '<'. $element),
417 );
418 }
419
420 /**
421 * Parse a feed's data.
422 *
423 * @param $data
424 * String of feed data.
425 * @param $id
426 * Feed ID.
427 * @param $element
428 * String containing item's element name.
429 * @param $count
430 * Number of items to parse.
431 */
432 function _lastfmsimple_parse_feed(&$data, $id, $element, $count) {
433 // Create the parser and get the array.
434 $parser = drupal_xml_parser_create($data);
435 xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
436 xml_parse_into_struct($parser, $data, $values);
437
438 // Get a nice array of items.
439 $items = array();
440 $i = 0;
441 foreach ($values as $value) {
442 // Current feed item has ended, start a new one.
443 if (strtolower($value['tag']) == $element && $value['type'] == 'close') {
444 $i++;
445
446 // We do not need any more items.
447 if ($i === $count) {
448 break;
449 }
450
451 continue;
452 }
453
454 // This is a property we want to save.
455 if ($value['type'] == 'complete') {
456 $items[$i][strtolower($value['tag'])] = $value['value'];
457 }
458 }
459
460 // Insert each item.
461 foreach ($items as $item) {
462 $name = $item['name'];
463 $url = $item['url'];
464 unset($item['name'], $item['url']);
465
466 // Serialize the remaining properties.
467 $item_data = count($item) > 0 ? serialize($item) : '';
468
469 // Insert new feed item.
470 db_query("INSERT INTO {lastfmsimple_item} (fid, name, url, item_data) VALUES (%d, '%s', '%s', '%s')", $id, $name, $url, $item_data);
471 }
472 }
473
474 /**
475 * Retrieve all fields of type 'textfield' from the profile.module's tables
476 * @return array with fieldnames
477 */
478 function _lastfmsimple_get_textfield_fields() {
479 $options = array();
480 $result = db_query("SELECT fid, name FROM {profile_fields} WHERE type = 'textfield'");
481
482 while ($field = db_fetch_object($result)) {
483 $options[$field->fid] = $field->name;
484 }
485
486 return $options;
487 }
488
489
490 /**
491 * Retrieve profile field object
492 *
493 * @var string $fid
494 * Id of field to retrieve
495 * @return object
496 * profile field object
497 */
498 function _lastfmsimple_get_field($fid) {
499 if (isset($fid)) {
500 $field = db_fetch_object(db_query("SELECT * FROM {profile_fields} WHERE fid = %d", $fid));
501 return empty($field) ? NULL : $field;
502 }
503 else {
504 return NULL;
505 }
506 }

  ViewVC Help
Powered by ViewVC 1.1.2