Merge lp://qastaging/~swe3tdave-deactivatedaccount/ubuntu-drupal-planet/6.x into lp://qastaging/~m-baert/ubuntu-drupal-planet/6.x
- 6.x
- Merge into 6.x
Proposed by
David Giard
Status: | Merged |
---|---|
Merged at revision: | 20 |
Proposed branch: | lp://qastaging/~swe3tdave-deactivatedaccount/ubuntu-drupal-planet/6.x |
Merge into: | lp://qastaging/~m-baert/ubuntu-drupal-planet/6.x |
Diff against target: | None lines |
To merge this branch: | bzr merge lp://qastaging/~swe3tdave-deactivatedaccount/ubuntu-drupal-planet/6.x |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Rocky Road | Pending | ||
Review via email: mp+4744@code.qastaging.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
David Giard (swe3tdave-deactivatedaccount) wrote : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'README.txt' |
2 | --- README.txt 2009-03-01 02:52:51 +0000 |
3 | +++ README.txt 2009-03-20 04:37:38 +0000 |
4 | @@ -1,19 +1,17 @@ |
5 | -This module allows site administrators to associate a feed with users who belong |
6 | -to a given role and to display it in a "planet" fashion. Feed items can be |
7 | -auto-converted to blogs if categories or the post itself contains a snippet |
8 | -specified in the planet settings, or posts can be converted manually. |
9 | - |
10 | -The idea is that it's nice in some cases to aggregate a group's personal blogs, |
11 | -promoting to prime time only those posts which are relevant to the site's purpose. |
12 | - |
13 | +Planet is an aggregator that allows you to aggregate the blogs for users in a given |
14 | +role (e.g. staff) and to display them in a "planet" fashion. Associating content |
15 | +with the users rather than as a detached feed. |
16 | |
17 | Installation |
18 | ------------ |
19 | |
20 | 1. Copy the planet directory into your Drupal modules directory. |
21 | |
22 | -2. Go to admin/modules and enable the planet module. |
23 | - |
24 | -3. To perform further configuration (and to add feeds), go to admin/settings/planet. |
25 | +2. Download the SimplePie 1.1 library: http://simplepie.org/ |
26 | + Place simplepie.inc in your Planet module directory. |
27 | + |
28 | +3. Go to admin/modules and enable the planet module. |
29 | + |
30 | +4. To perform further configuration (and to add feeds), go to admin/settings/planet. |
31 | |
32 | |
33 | |
34 | === added directory 'doc' |
35 | === added file 'doc/drupal-planet.dox' |
36 | --- doc/drupal-planet.dox 1970-01-01 00:00:00 +0000 |
37 | +++ doc/drupal-planet.dox 2009-03-21 16:38:04 +0000 |
38 | @@ -0,0 +1,120 @@ |
39 | +/** |
40 | +@mainpage Planet Module API |
41 | + |
42 | +Source repository: https://code.edge.launchpad.net/~m-baert . |
43 | + |
44 | +You may download it directly from command line: |
45 | +@verbatim |
46 | +bzr branch lp:~m-baert/drupal-planet/6.x |
47 | +@endverbatim |
48 | + |
49 | +@section planet_info Module Definition |
50 | +Here's how planet is defined in @c planet.info : |
51 | + |
52 | +@li @c name Planet |
53 | +@li @c description Aggregates RSS feeds and faciliates their association with site users who belong to a given role. |
54 | +@li @c package Community - optional |
55 | +@li @c version 6.x |
56 | +@li @c core 6.x |
57 | + |
58 | +@section menu_links Menus and Pages |
59 | + |
60 | +Menu items and page callbacks are defined in the hook_menu() |
61 | + implementation, planet_menu. |
62 | + |
63 | +This <a href="../../menu-links.svg">diagram</a> shows how requests are handled. |
64 | + |
65 | +@dotfile menu-links.dot "Menus and Page Requests" |
66 | + |
67 | +Each arrow is labeled with the permission required to access link. |
68 | + |
69 | +The menu item colors reflect the item type: |
70 | + |
71 | +@li <span class="menu_normal_item">MENU_NORMAL_ITEM</span> (default) Normal menu items show up in the menu tree and can be moved/hidden by the administrator. |
72 | +@li <span class="menu_local_task">MENU_LOCAL_TASK</span> Local tasks are rendered as tabs by default. For menu items that describe actions to be performed on their parent item. |
73 | +@li <span class="menu_callback">MENU_CALLBACK</span> Callbacks simply register a path so that the correct function is fired when the URL is accessed. They are not shown in the menu. |
74 | + |
75 | +@section funcalls Global calls graph |
76 | + |
77 | +Here is a map of internal function calls. |
78 | + |
79 | +It is quite large for now, and may not be easy to read as displayed here, |
80 | +but you may <a href="../../funcalls.svg">download the svg version here</a>. |
81 | + |
82 | +Dashed lines show indirect invocations ( |
83 | +<span class="hook">hooks</span>, |
84 | +<span class="page">callbacks</span>, |
85 | +<span class="form">forms</span>, |
86 | +other <span class="handler">handlers</span> |
87 | +). |
88 | + |
89 | +<span class="db_access">Database access</span> is also shown with access type indicators: |
90 | +@li @b C : Create table rows |
91 | +@li @b R : Read table rows |
92 | +@li @b U : Update table rows |
93 | +@li @b D : Delete table rows |
94 | + |
95 | + @dotfile funcalls.dot "Internal Function Calls" |
96 | + |
97 | + |
98 | +@section planet_groups Function Groups |
99 | + |
100 | +Each planet function may belong to one or more categories: |
101 | + |
102 | +@li @ref base |
103 | +@li @ref system |
104 | +@li @ref page |
105 | +@li @ref iforms |
106 | +@li @ref planet_node |
107 | +@li @ref ihooks |
108 | +@li @ref planet_feed |
109 | +@li @ref planet_item |
110 | +@li @ref rss |
111 | + |
112 | + |
113 | +*/ |
114 | +#@li @ref db_access |
115 | +#@li @ref internal |
116 | + |
117 | +// @DIFFINFO Group definitions for doxygen documentation |
118 | +/** |
119 | + @defgroup ihooks All Implemented Hooks |
120 | + @{ |
121 | + @defgroup base Basic Definitions for Planet Module |
122 | +Main user interface. |
123 | + |
124 | + @defgroup system System Interaction |
125 | +Various callbacks for reacting to drupal some system events. |
126 | + |
127 | + @defgroup planet_node Planet Node Type support |
128 | +This group may not be easy to identify because our node type |
129 | +has the same name as the module: "planet". |
130 | + |
131 | +@defgroup empty . |
132 | +@} |
133 | +*/ |
134 | +# above "empty" group is a dirty trick to prevent doxygen from ejecting |
135 | +# the last subgroup . |
136 | + |
137 | +# Note: |
138 | +Renaming "Modules" output section probably implies recompiling doxygen. |
139 | + http://www.nabble.com/Modules-book-title:-possible-to-rename---td15608879.html |
140 | + |
141 | +/** |
142 | + @defgroup page Page callbacks (Menu System) |
143 | +Functions registered in planet_menu() |
144 | + |
145 | + @defgroup iforms Forms |
146 | +User forms, invoked with drupal_get_form() . |
147 | + |
148 | + @defgroup isettings General Planet Settings |
149 | + |
150 | + @defgroup planet_feed Managing Planet feeds |
151 | + |
152 | + @defgroup planet_item Managing Planet feed items |
153 | + |
154 | + @defgroup rss RSS Feed Handling |
155 | + |
156 | + */ |
157 | + #* @defgroup internal Internal Functions |
158 | + #* @defgroup db_access Functions accessing database (directly through Drupal API) |
159 | |
160 | === modified file 'planet.info' |
161 | --- planet.info 2009-03-01 02:54:23 +0000 |
162 | +++ planet.info 2009-03-21 01:30:45 +0000 |
163 | @@ -1,6 +1,7 @@ |
164 | ; $Id: planet.info,v 1.2 2007/05/30 03:22:01 daryl Exp $ |
165 | -name = Planet |
166 | -description = Planet blog aggregator |
167 | +; Planet blog aggregator |
168 | +name = Planet |
169 | +description = Aggregates RSS feeds and faciliates their association with site users who belong to a given role. |
170 | package = Community - optional |
171 | version = 6.x |
172 | core = 6.x |
173 | |
174 | === modified file 'planet.install' |
175 | --- planet.install 2009-03-01 03:00:14 +0000 |
176 | +++ planet.install 2009-03-21 01:41:56 +0000 |
177 | @@ -10,49 +10,70 @@ |
178 | 'description' => t('The base table for planet.'), |
179 | 'fields' => array( |
180 | 'fid' => array( |
181 | - 'description' => t('The primary identifier for a planet_feeds table.'), |
182 | + 'description' => t('Primary Key: Unique identifier for a planet RSS feed.'), |
183 | 'type' => 'serial', |
184 | 'unsigned' => TRUE, |
185 | 'not null' => TRUE, |
186 | ), |
187 | - 'uid' => array( |
188 | + 'uid' => array( |
189 | + 'description' => t('Foreign key to {users}.uid . Identifies user who choose the feed.'), |
190 | 'type' => 'int', |
191 | 'unsigned' => 1, |
192 | 'not null' => FALSE, |
193 | ), |
194 | 'title' => array( |
195 | 'type' => 'varchar', |
196 | + 'description' => t('Title of the feed.'), |
197 | 'length' => 50, |
198 | 'not null' => TRUE, |
199 | ), |
200 | 'link' => array( |
201 | 'type' => 'varchar', |
202 | + 'description' => t('URL to the feed'), |
203 | 'length' => 80, |
204 | 'not null' => TRUE, |
205 | ), |
206 | 'image' => array( |
207 | + 'description' => t('An image representing the feed'), |
208 | 'type' => 'varchar', |
209 | 'length' => 120, |
210 | 'not null' => FALSE, |
211 | ), |
212 | - 'checked' => array( |
213 | + 'checked' => array( |
214 | + 'description' => t('Last time feed was checked for new items, as Unix timestamp'), |
215 | 'type' => 'int', |
216 | 'not null' => FALSE, |
217 | ), |
218 | 'frozen' => array( |
219 | - 'type' => 'int', |
220 | - 'not null' => TRUE, |
221 | - 'default' => 0, |
222 | - ), |
223 | + // TODO: add field description 'description' => t(''), |
224 | + 'type' => 'int', // TODO change to boolean when supported |
225 | + 'not null' => TRUE, |
226 | + 'default' => 0, |
227 | + ), |
228 | + 'hash' => array( |
229 | + 'type' => 'varchar', |
230 | + 'length' => 32, |
231 | + 'description' => t("A hash of the feed's headers."), |
232 | + ), |
233 | + 'error' => array( |
234 | + 'type' => 'int', |
235 | + 'not null' => TRUE, |
236 | + 'default' => 0, |
237 | + 'length' => 1, |
238 | + 'description' => t("Whether the feed is throwing errors or not."), |
239 | + ), |
240 | ), |
241 | 'primary key' => array('fid'), |
242 | + 'indexes' => array( |
243 | + 'error' => array('error'), |
244 | + ), |
245 | ); |
246 | |
247 | $schema['planet_items'] = array( |
248 | 'description' => t('contain feed id and its corresponding nid'), |
249 | 'fields' => array( |
250 | 'id' => array( |
251 | - 'description' => t('The primary identifier for a planet_items table'), |
252 | + 'description' => t('Primary key: Unique identifier for a planet feed item'), |
253 | 'type' => 'serial', |
254 | 'unsigned' => 1, |
255 | 'not null' => TRUE, |
256 | @@ -67,6 +88,12 @@ |
257 | 'unsigned' => 1, |
258 | 'not null' => FALSE, |
259 | ), |
260 | + 'iid' => array( |
261 | + 'type' => 'varchar', |
262 | + 'length' => 32, |
263 | + 'not null' => TRUE, |
264 | + 'description' => t("md5 of the feed item's title and body."), |
265 | + ), |
266 | 'guid' => array( |
267 | 'type' => 'varchar', |
268 | 'length' => 120, |
269 | @@ -91,11 +118,9 @@ |
270 | /** |
271 | * Implementation of hook_install() |
272 | * |
273 | - * This will automatically install the database tables for the planet module for MySQL. |
274 | - * |
275 | - * If you are using another database, you will have to install the tables by hand, using the queries below as a reference. |
276 | - * |
277 | - * |
278 | + * This will automatically install the database tables for the planet |
279 | + * module, using Drupal's data abstraction layer. |
280 | + * |
281 | */ |
282 | |
283 | function planet_install() { |
284 | @@ -106,9 +131,9 @@ |
285 | /** |
286 | * Implementation of hook_uninstall() |
287 | * |
288 | - * This will automatically uninstall the database tables for the planet module for MySQL. |
289 | + * Uninstalls planet module by removing its own database tables, nodes |
290 | + * and persistent variables. |
291 | * |
292 | - * |
293 | */ |
294 | |
295 | function planet_uninstall() { |
296 | @@ -120,3 +145,42 @@ |
297 | db_query("DELETE FROM {node} WHERE type = '%s'", planet); |
298 | |
299 | } |
300 | + |
301 | +function planet_update_1() { |
302 | + $ret = array(); |
303 | + db_add_field($ret, 'planet_feeds', 'hash', array( |
304 | + 'type' => 'varchar', |
305 | + 'length' => 32, |
306 | + 'description' => t("A hash of the feed's headers."), |
307 | + ) |
308 | + ); |
309 | + return $ret; |
310 | +} |
311 | + |
312 | +function planet_update_2() { |
313 | + $ret = array(); |
314 | + db_add_field($ret, 'planet_items', 'iid', array( |
315 | + 'type' => 'varchar', |
316 | + 'length' => 32, |
317 | + 'not null' => TRUE, |
318 | + 'default' => '', |
319 | + 'description' => t("md5 of the feed item's title and body."), |
320 | + ) |
321 | + ); |
322 | + return $ret; |
323 | +} |
324 | + |
325 | +function planet_update_3() { |
326 | + $ret = array(); |
327 | + db_add_field($ret, 'planet_feeds', 'error', array( |
328 | + 'type' => 'int', |
329 | + 'not null' => TRUE, |
330 | + 'default' => 0, |
331 | + 'size' => 'tiny', |
332 | + 'description' => t("Whether the feed is throwing errors or not."), |
333 | + ) |
334 | + ); |
335 | + db_add_index($ret, 'planet_feeds', 'error', array('error')); |
336 | + |
337 | + return $ret; |
338 | +} |
339 | |
340 | === modified file 'planet.module' |
341 | --- planet.module 2009-03-08 19:00:05 +0000 |
342 | +++ planet.module 2009-03-21 16:38:04 +0000 |
343 | @@ -1,48 +1,92 @@ |
344 | <?php |
345 | -// $Id: planet.install Exp $ |
346 | +// $Id: planet.module Exp $ |
347 | |
348 | -/** |
349 | + /** |
350 | * @file |
351 | - * The planet module |
352 | + * The planet module. |
353 | + * |
354 | + * Planet is an aggregator that allows you to aggregate the blogs for |
355 | + * users in a given role (e.g. staff) and associate content with the |
356 | + * users rather than as a detached feed. This provides the benefit of |
357 | + * showing avatars with content, providing per-user aggregation of |
358 | + * planet content in addition to blog content, etc. |
359 | * |
360 | */ |
361 | |
362 | +/** |
363 | + * Implementation of hook_node_info. |
364 | + * |
365 | + * @return An array of information on the module's node types. |
366 | + * |
367 | + * @ingroup planet_node |
368 | + */ |
369 | function planet_node_info() { |
370 | return array( |
371 | 'planet' => array( |
372 | - 'name' => t('Planet Entry'), |
373 | - 'module' => 'planet', |
374 | - 'description' => t('Node to contain posts aggregated from various blogs.'), |
375 | + 'name' => t('Planet Entry'), |
376 | + 'module' => 'planet', |
377 | + 'description' => t('Node to contain posts aggregated from various blogs.'), |
378 | ) |
379 | ); |
380 | } |
381 | |
382 | /** |
383 | - * Implementation of hook_perm. |
384 | + * Implementation of hook_perm - Defines user permissions. |
385 | + * The suggested naming convention for permissions is "action_verb modulename". |
386 | + * |
387 | + * @return An array of permissions strings. |
388 | + * |
389 | + * @ingroup base |
390 | */ |
391 | function planet_perm() { |
392 | - return array('administer planet', 'administer own planet feeds'); |
393 | + return array('administer planet', 'administer own planet feeds', 'view all planet nodes'); |
394 | } |
395 | |
396 | +/** |
397 | + * Implementation of hook_help - Provides online user help. |
398 | + * |
399 | + * @param $path A Drupal menu router path the help is being requested for |
400 | + * @param $arg |
401 | + * @return A localized string containing the help text. |
402 | + * |
403 | + * @ingroup base |
404 | + */ |
405 | function planet_help($path, $arg) { |
406 | switch ($path) { |
407 | - case 'admin/help/planet': |
408 | + case 'admin/help#planet': |
409 | + // The module's help text, displayed on the admin/help page and through the module's individual help link. |
410 | $output = '<p>Planet is an aggregator that allows you to aggregate the blogs for users in a given role (e.g. staff) and associate content with the users rather than as a detached feed. This provides the benefit of showing avatars with content, providing per-user aggregation of planet content in addition to blog content, etc.</p>'; |
411 | $output .= '<p>To use planet, go to admin/settings/planet and note the following sections:</p>'; |
412 | $output .= '<ul>'; |
413 | $output .= '<li><strong>General Settings</strong>. The role to select bloggers from lets you narrow the user list for when you\'re adding a feed and associating it with a user. A common setting will be to create a staff role and use this for planet.</li>'; |
414 | $output .= '<li><strong>Feeds</strong>. This section lets you add a new feed. Give it a title, select an author, provide the feed url, and you\'re off. You\'ll have to manually refresh it or wait for a cron run for items to be imported.</li>'; |
415 | $output .= '<li><strong>Feeds</strong>. This section lists current feeds, when they were last updated, how many items they have, and it allows you to edit, refresh, or freeze them. Freezing is a quick way to temporarily suspend updates from the given feed.</li>'; |
416 | + // @DIFFGROUP output syntax |
417 | + $output .= '</ul>'; |
418 | return $output; |
419 | - case 'admin/modules#description': |
420 | - return t('Aggregates RSS feeds and faciliates their association with site users who belong to a given role.'); |
421 | + // @DIFFINFO in D6, admin/modules#description is moved to .info file |
422 | } |
423 | } |
424 | |
425 | -function planet_view($node, $teaser = FALSE, $page = FALSE, $links = TRUE) { |
426 | +/** |
427 | + * Implementation of hook_view() - Displays a planet node. |
428 | + * |
429 | + * @param $node The node to be displayed. |
430 | + * @param $teaser Whether to generate only a summary ("teaser") of the node. |
431 | + * @param $page Whether the node is being displayed as a standalone page. |
432 | + * @return The modified $node parameter, properly presented for output. |
433 | + * |
434 | + * @ingroup planet_node |
435 | + * @CRUD{variables,R} |
436 | + * @CRUD{planet_items,R} |
437 | + */ |
438 | +function planet_view($node, $teaser = FALSE, $page = FALSE) { |
439 | + // @DIFFINFO removed $link param to match D6 hook definition |
440 | if ($page === true && variable_get('planet_redirect_page', 0) == 1) { |
441 | - $obj = db_fetch_object(db_query('SELECT * FROM {planet_items} WHERE nid = %d', $node->nid)); |
442 | - if ($obj->nid == $node->nid && $obj->link != '') { |
443 | + // @DIFFINFO only 'link' field is used, so I restricted the query |
444 | + $obj = db_fetch_object(db_query('SELECT link FROM {planet_items} WHERE nid = %d', $node->nid)); |
445 | + // @DIFFINFO if the query succeeds, $obj->nid == $node->nid , otherwise, $obj==FALSE, |
446 | + if ($obj && $obj->link != '') { |
447 | header('Location: '. $obj->link); |
448 | exit; |
449 | } |
450 | @@ -52,22 +96,51 @@ |
451 | } |
452 | } |
453 | |
454 | +/** |
455 | + * Implementation of hook_access() - Define access permissions for planet nodes (aka CRUD) . |
456 | + * @param $op The operation to be performed: 'create', 'view', 'update', 'delete' |
457 | + * @param $node The node on which the operation is to be performed, or, if it does not yet exist, the type of node to be created. |
458 | + * @param $account A user object representing the user for whom the operation is to be performed. |
459 | + * @return true if the user is allowed to perform operation, false otherwise. |
460 | + * |
461 | + * @ingroup planet_node |
462 | + */ |
463 | function planet_access($op, $node, $account) { |
464 | - |
465 | - |
466 | + // @DIFFINFO using planet-dedicated permissions |
467 | + // Planet administrator has all permissions related to planet module |
468 | + if (user_access('administer planet', $account)) |
469 | + return TRUE; |
470 | + // Users allowed to edit their own planet nodes, are also allowed to create them, others are not. |
471 | + $author = user_access('edit own planet feeds', $account); |
472 | if ($op == 'create') { |
473 | - return user_access('edit own blog', $account) && $account->uid; |
474 | + return $author; |
475 | } |
476 | - |
477 | + |
478 | + // Does current user own requested node ? |
479 | + $own = $account->uid == $node->uid; |
480 | + if ($op == 'view') |
481 | + return $own || user_access('view all planet nodes'); |
482 | if ($op == 'update' || $op == 'delete') { |
483 | - if (user_access('edit own blog', $account) && ($account->uid == $node->uid) || user_access('administer nodes', $account)) { |
484 | - return TRUE; |
485 | - } |
486 | + return $author && $own; // ok if $author owns the node |
487 | } |
488 | + trigger_error("Unknown operation requested: $op"); |
489 | + return FALSE; |
490 | } |
491 | |
492 | +/** |
493 | + * Implementation of hook_menu() - Defines menu items and page callbacks. |
494 | + * |
495 | + * @return An array of menu items. |
496 | + * Each menu item has a key corresponding to the Drupal path being registered. |
497 | + * |
498 | + * @ingroup base |
499 | + * |
500 | + * This diagram shows how requests are handled. |
501 | + * Each arrow is labeled with the permission required to access link. |
502 | + * The menu item colors reflect the item type. |
503 | + * @dotfile menu-links.dot "Menus and Pages" |
504 | + */ |
505 | function planet_menu() { |
506 | - |
507 | $items['admin/settings/planet'] = array( |
508 | 'title' => 'Planet Settings', |
509 | 'description' => 'Configure settings for the planet module.', |
510 | @@ -84,31 +157,28 @@ |
511 | ); |
512 | |
513 | // if (arg(0) == 'planet' && is_numeric(arg(1))) { |
514 | + // FIXME access denied http://localhost/drupal/?q=planet/1 |
515 | $items['planet/'. '%'] = array( |
516 | 'title' => 'planet', |
517 | 'page callback' => 'planet_page_user', |
518 | 'page arguments' => array(arg(1)) |
519 | ); |
520 | -// } |
521 | |
522 | -// if (arg(3) == 'refresh' && is_numeric(arg(4))) { |
523 | - |
524 | + // if (arg(3) == 'refresh' && is_numeric(arg(4))) { |
525 | $items['admin/settings/planet/refresh/%'] = array( |
526 | 'title' => 'planet refresh', |
527 | 'page callback' => 'planet_call_refresh', |
528 | 'access arguments' => array('administer nodes'), |
529 | 'type' => MENU_CALLBACK |
530 | ); |
531 | -// } |
532 | |
533 | -// if (is_numeric(arg(4)) && (arg(3) == 'freeze' || arg(3) == 'unfreeze')) { |
534 | + // if (is_numeric(arg(4)) && (arg(3) == 'freeze' || arg(3) == 'unfreeze')) { |
535 | $items['admin/settings/planet/'. arg(3) .'/%'] = array( |
536 | 'title' => 'planet freeze', |
537 | 'page callback' => 'planet_toggle_frozen', |
538 | 'access arguments' => array('administer nodes'), |
539 | 'type' => MENU_CALLBACK |
540 | ); |
541 | -// } |
542 | |
543 | $items['planet'] = array( |
544 | 'title' => 'Planet', |
545 | @@ -128,6 +198,12 @@ |
546 | return $items; |
547 | } |
548 | |
549 | + |
550 | +/** |
551 | + * Page callback for 'admin/settings/planet/refresh/%' ("planet refresh") |
552 | + * |
553 | + * @ingroup page |
554 | + */ |
555 | function planet_call_refresh() { |
556 | $title = planet_refresh(); |
557 | watchdog('planet', 'Feed "'. $title .'" refreshed.'); |
558 | @@ -135,6 +211,12 @@ |
559 | drupal_goto('admin/settings/planet'); |
560 | } |
561 | |
562 | +/** |
563 | + * Page callback for 'admin/settings/planet/(un|)freeze/%' ("planet freeze") |
564 | + * |
565 | + * @ingroup page |
566 | + * @CRUD{planet_feeds,U} |
567 | + */ |
568 | function planet_toggle_frozen() { |
569 | |
570 | $fid = intval(arg(4)); |
571 | @@ -144,53 +226,42 @@ |
572 | } |
573 | |
574 | |
575 | +/** |
576 | + * Deletes a feed and related items |
577 | + * @param $fid the feed identifier key (integer) |
578 | + * @param $path the drupal path for redirection after user confirmed. |
579 | + * |
580 | + * @internal |
581 | + * @ingroup planet_feed |
582 | + * @CRUD{planet_items,R} |
583 | + * @invoke{drupal_get_form,planet_multiple_delete_confirm} |
584 | + */ |
585 | +function planet__drop_feed($fid, $path) { |
586 | + // @DIFFINFO documented $path param, removed unused $edit param |
587 | + $result = db_query('SELECT nid FROM {planet_items} WHERE fid = %d', $fid); |
588 | + $nodes = array(); // @DIFFINFO needs to be defined even empty |
589 | + while ($node = db_fetch_object($result)) { |
590 | + $nodes[$node->nid] = TRUE; |
591 | + } |
592 | + return drupal_get_form('planet_multiple_delete_confirm', $nodes, |
593 | + $fid, $path); |
594 | +} |
595 | + |
596 | +/** |
597 | + * Page callback for 'user/%user/planet' ("Planet Feeds") |
598 | + * |
599 | + * @ingroup page |
600 | + * @CRUD{planet_feeds,R} |
601 | + * @invoke{drupal_get_form,planet_multiple_delete_confirm_submit} |
602 | + * @invoke{drupal_get_form,planet_feed_form} |
603 | + */ |
604 | function planet_user_feeds() { |
605 | -global $user; |
606 | -if ($_POST) { |
607 | - $edit = $_POST; |
608 | - if ($_POST['op'] == 'Delete' && intval($edit['fid']) > 0) { |
609 | - $result = db_query('SELECT nid FROM {planet_items} WHERE fid = %d', intval($edit['fid'])); |
610 | - while ($node = db_fetch_object($result)) { |
611 | - $nodes[$node->nid] = TRUE; |
612 | - } |
613 | - return drupal_get_form('planet_multiple_delete_confirm', $nodes, intval($edit['fid']), 'user/'. $user->uid .'/planet'); |
614 | - } |
615 | - else if ($_POST['op'] == 'Delete all' && $_POST['confirm'] == 1) { |
616 | - $edit['fid'] = intval(arg(3)); |
617 | - $edit['redirect'] = 'user/'. $user->uid .'/planet'; |
618 | - return drupal_get_form('planet_multiple_delete_confirm_submit', $edit); |
619 | - } |
620 | - else { |
621 | - if (isset($edit['fid']) && intval($edit['fid']) == 0) { |
622 | - db_query('INSERT INTO {planet_feeds} (uid, title, link, image, checked, frozen) VALUES(%d, "%s", "%s", "%s", 0, 0)', $user->uid, $edit['title'], $edit['link'], $edit['image']); |
623 | - $edit_r = db_fetch_array(db_query('SELECT fid FROM {planet_feeds} WHERE uid = %d AND title = "%s" AND link = "%s"', $user->uid, $edit['title'], $edit['link'])); |
624 | - $title = planet_refresh(intval($edit_r['fid'])); |
625 | - drupal_set_message('Added new feed: ' . $title); |
626 | - } |
627 | - else if ($edit['fid'] && intval($edit['fid']) > 0) { |
628 | - db_query('UPDATE {planet_feeds} SET uid = %d, title="%s", link = "%s", image="%s" WHERE fid=%d', $user->uid, $edit['title'], $edit['link'], $edit['image'], $edit['fid']); |
629 | - drupal_set_message('Edited "'. $edit['title'] .'" feed.'); |
630 | - } |
631 | - else { |
632 | - if ($edit['planet_author_roles']) { |
633 | - variable_set('planet_author_roles', $edit['planet_author_roles']); |
634 | - } |
635 | - if ($edit['planet_filter_formats']) { |
636 | - variable_set('planet_filter_formats', $edit['planet_filter_formats']); |
637 | - } |
638 | - if ($edit['planet_redirect_page'] == 1) { |
639 | - variable_set('planet_redirect_page', $edit['planet_redirect_page']); |
640 | - } |
641 | - else { |
642 | - variable_del('planet_redirect_page'); |
643 | - } |
644 | - drupal_set_message('Edited general planet settings.'); |
645 | - } |
646 | - } |
647 | - drupal_goto('user/'. $user->uid .'/planet'); |
648 | + global $user; |
649 | + if ($_POST) { |
650 | + planet__update($_POST, 'user/'. $user->uid .'/planet'); |
651 | } |
652 | - else { |
653 | - $fid = intval(arg(3)); |
654 | + else { // no $_POST |
655 | + $fid = intval(arg(3)); // @deprecated: arg() |
656 | if ($fid > 0) { |
657 | $edit = db_fetch_array(db_query('SELECT * FROM {planet_feeds} WHERE fid = %d', $fid)); |
658 | $output .= drupal_get_form('planet_feed_form', $edit, true, $user); |
659 | @@ -199,79 +270,183 @@ |
660 | |
661 | $output .= drupal_get_form('planet_feed_form', $edit, false, $user); |
662 | |
663 | - // $result = db_query('SELECT *, (UNIX_TIMESTAMP(NOW()) - checked) _checked FROM {planet_feeds}'); |
664 | - $result = db_query('SELECT COUNT(f.fid) cnt, f.*, (UNIX_TIMESTAMP(NOW()) - checked) _checked FROM {planet_feeds} f LEFT OUTER JOIN {planet_items} i ON i.fid = f.fid WHERE f.uid = %d GROUP BY f.fid;', $user->uid); |
665 | + $output .= '<h2>Feeds</h2>'; |
666 | + $output .= planet__build_user_feeds_table(); |
667 | + } |
668 | + print theme('page', $output); |
669 | + } |
670 | +} |
671 | + |
672 | +// Note: I use 'planet__' prefix to name internal functions |
673 | +// TODO: apply the same naming convention (this one or another) in whole module |
674 | +/** |
675 | + * @todo merge with planet__build_admin_feeds_table() |
676 | + * |
677 | + * @ingroup planet_feed |
678 | + * @internal |
679 | + * @CRUD{planet_feeds,R} |
680 | + * @CRUD{planet_items,R} |
681 | + */ |
682 | +function planet__build_user_feeds_table() { |
683 | + // @DIFFINFO renamed planet__build_feeds_table1 |
684 | + global $user; |
685 | + $feeds = db_query('SELECT fid, title, checked FROM {planet_feeds} ' |
686 | + . ' WHERE uid = %d;', $user->uid); |
687 | $rows = array(); |
688 | $headers = array('Feed', 'Items', 'Edit', 'Last checked'); |
689 | - while ($feed = db_fetch_object($result)) { |
690 | - $checked = intval($feed->_checked / 60) .' minutes'; |
691 | - if ($feed->_checked % 60 > 0) { |
692 | + $now = time(); |
693 | + $items_statement = 'SELECT count(*) FROM {planet_items}' |
694 | + . ' WHERE fid=%d'; |
695 | + // TODO: change this to prepared statement when supported by drupal DB abstraction layer. |
696 | + while ($feed = db_fetch_object($feeds)) { |
697 | + $_checked = $now - $feed->checked; |
698 | + $checked = intval($_checked / 60) .' minutes'; |
699 | + if ($_checked % 60 > 0) { |
700 | $checked .= ', '. $feed->_checked % 60 .' seconds'; |
701 | } |
702 | $checked .= ' ago'; |
703 | + $items = db_query($items_statement, $feed->fid); |
704 | + if (!$items) trigger_error('ERROR while counting feed items.'); |
705 | + $cnt = db_result($items); |
706 | array_push($rows, array( |
707 | $feed->title, |
708 | - $feed->cnt, |
709 | + $cnt, |
710 | l('edit', 'user/'. $user->uid .'/planet/'. intval($feed->fid)), |
711 | $checked, |
712 | ) |
713 | ); |
714 | } |
715 | - $output .= '<h2>Feeds</h2>'; |
716 | - $output .= theme('table', $headers, $rows); |
717 | - } |
718 | - print theme('page', $output); |
719 | - } |
720 | -} |
721 | - |
722 | - |
723 | + return theme('table', $headers, $rows); |
724 | +} |
725 | + |
726 | +/** |
727 | + * Updates a feed |
728 | + */ |
729 | +function planet__update($edit, $path) { |
730 | + $fid = intval($edit['fid']); |
731 | + if (($edit['op'] == 'Delete') && ($fid != 0)) { |
732 | + return planet__drop_feed($fid, $edit, $path); |
733 | + } |
734 | + else if ($edit['op'] == 'Delete all' && $edit['confirm'] == 1) { |
735 | + $edit['fid'] = intval(arg(3)); // @deprecated arg() |
736 | + $edit['redirect'] = $path; |
737 | + return drupal_get_form('planet_multiple_delete_confirm_submit', $edit); |
738 | + } |
739 | + else { |
740 | + planet__submit_feed_data($edit); |
741 | + } |
742 | + drupal_goto($path); |
743 | +} |
744 | + |
745 | +/** |
746 | + * Submit the feed's data to the appropriate update or add function |
747 | + */ |
748 | +function planet__submit_feed_data($data) { |
749 | + if (isset($data['fid'])) { |
750 | + if (intval($data['fid']) == 0) { |
751 | + planet__add_feed($data); |
752 | + } else { |
753 | + planet__update_feed($data); |
754 | + } |
755 | + } else { |
756 | + // TODO if user needs to click separately for each fieldset, |
757 | + // why not using different forms ? |
758 | + // or conversely, allow setting everything at once ? |
759 | + planet__update_vars($data); |
760 | + } |
761 | +} |
762 | + |
763 | +/** |
764 | + * Adds a new feed to planet_feeds table. |
765 | + * @param $data associative array with keys |
766 | + * 'uid' (optional), 'title', 'link', 'image' (optional) |
767 | + * @return success status |
768 | + * |
769 | + * @ingroup planet_feed |
770 | + * @internal |
771 | + * @CRUD{planet_feeds,C} |
772 | + */ |
773 | +function planet__add_feed($data) { |
774 | + $data['checked'] = 0; |
775 | + $data['frozen'] = 0; |
776 | + // @DIFFINFO Removed debugging block. Internal function is supposed to receive clean arguments. Checking will be left to planet_feed class |
777 | + $rslt = drupal_write_record('planet_feeds', $data); |
778 | + if ($rslt==SAVED_NEW) { |
779 | + $title = planet_refresh(intval($data['fid'])); |
780 | + drupal_set_message('Added new feed: ' . $title); |
781 | + return TRUE; |
782 | + } |
783 | + trigger_error('Failed to add new feed'); |
784 | + return FALSE; |
785 | +} |
786 | + |
787 | +/** |
788 | + * Updates an existing feed in planet_feeds table. |
789 | + * @param $data associative array with keys |
790 | + * 'fid' (mandatory, primary key), |
791 | + * other fields as needed: |
792 | + * 'uid', 'title', 'link', 'image', 'checked', 'frozen' |
793 | + * @return success status |
794 | + * |
795 | + * @ingroup planet_feed |
796 | + * @internal |
797 | + * @CRUD{planet_feeds,U} |
798 | +*/ |
799 | +function planet__update_feed($data) { |
800 | + $rslt = drupal_write_record('planet_feeds', $data, 'fid'); |
801 | + |
802 | + if ($rslt==SAVED_UPDATED) { |
803 | + drupal_set_message('Edited "'. $data['title'] .'" feed.'); |
804 | + return TRUE; |
805 | + } |
806 | + if ($rslt==SAVED_NEW) // @DEBUGGING |
807 | + trigger_error('Feed {$data->fid} didn\'t exist. Record added.', E_ERROR); |
808 | + else |
809 | + trigger_error('Failed to edit feed'); |
810 | + return FALSE; |
811 | +} |
812 | + |
813 | +/** |
814 | + * Updates an planet settings persistent variables. |
815 | + * @param $data associative array with all optional keys |
816 | + * 'planet_author_roles', 'planet_filter_formats', |
817 | + * 'planet_redirect_page'. |
818 | + * |
819 | + * @ingroup isettings |
820 | + * @internal |
821 | + * @CRUD{variables,CUD} |
822 | +*/ |
823 | +function planet__update_vars($data) { |
824 | + if ($data['planet_author_roles']) { |
825 | + variable_set('planet_author_roles', $data['planet_author_roles']); |
826 | + } |
827 | + if ($data['planet_filter_formats']) { |
828 | + variable_set('planet_filter_formats', $data['planet_filter_formats']); |
829 | + } |
830 | + if ($data['planet_redirect_page'] == 1) { |
831 | + variable_set('planet_redirect_page', $data['planet_redirect_page']); |
832 | + } else { |
833 | + variable_del('planet_redirect_page'); |
834 | + } |
835 | + drupal_set_message('Edited general planet settings.'); |
836 | +} |
837 | + |
838 | +/** |
839 | + * Page callback for 'admin/settings/planet' ("Planet Settings") |
840 | + * |
841 | + * @ingroup page |
842 | + * @CRUD{planet_items,R} |
843 | + * @CRUD{planet_feeds,R} |
844 | + * @invoke{drupal_get_form,planet_multiple_delete_confirm} |
845 | + * @invoke{drupal_get_form,planet_multiple_delete_confirm_submit} |
846 | + * @invoke{drupal_get_form,planet_feed_form} |
847 | + * @invoke{drupal_get_form,planet_settings_form} |
848 | + */ |
849 | function _planet_settings() { |
850 | if ($_POST) { |
851 | - $edit = $_POST; |
852 | - |
853 | - if ($_POST['op'] == 'Delete' && intval($edit['fid']) > 0) { |
854 | - $result = db_query('SELECT nid FROM {planet_items} WHERE fid = %d', intval($edit['fid'])); |
855 | - while ($node = db_fetch_object($result)) { |
856 | - $nodes[$node->nid] = TRUE; |
857 | - } |
858 | - return drupal_get_form('planet_multiple_delete_confirm', $nodes, intval($edit['fid']), 'admin/settings/planet'); |
859 | - } |
860 | - else if ($_POST['op'] == 'Delete all' && $_POST['confirm'] == 1) { |
861 | - $edit['fid'] = intval(arg(3)); |
862 | - $edit['redirect'] = 'admin/settings/planet'; |
863 | - return drupal_get_form('planet_multiple_delete_confirm_submit', $edit); |
864 | - } |
865 | - else { |
866 | - if (isset($edit['fid']) && intval($edit['fid']) == 0) { |
867 | - db_query('INSERT INTO {planet_feeds} (uid, title, link, image, checked, frozen) VALUES(%d, "%s", "%s", "%s", 0, 0)', $edit['uid'], $edit['title'], $edit['link'], $edit['image']); |
868 | - $edit_r = db_fetch_array(db_query('SELECT fid FROM {planet_feeds} WHERE uid = %d AND title = "%s" AND link = "%s"', $edit['uid'], $edit['title'], $edit['link'])); |
869 | - $title = planet_refresh(intval($edit_r['fid'])); |
870 | - drupal_set_message('Added new feed: ' . $title); |
871 | - } |
872 | - else if ($edit['fid'] && intval($edit['fid']) > 0) { |
873 | - db_query('UPDATE {planet_feeds} SET uid = %d, title="%s", link = "%s", image="%s" WHERE fid=%d', $edit['uid'], $edit['title'], $edit['link'], $edit['image'], $edit['fid']); |
874 | - drupal_set_message('Edited "'. $edit['title'] .'" feed.'); |
875 | - } |
876 | - else { |
877 | - if ($edit['planet_author_roles']) { |
878 | - variable_set('planet_author_roles', $edit['planet_author_roles']); |
879 | - } |
880 | - if ($edit['planet_filter_formats']) { |
881 | - variable_set('planet_filter_formats', $edit['planet_filter_formats']); |
882 | - } |
883 | - if ($edit['planet_redirect_page'] == 1) { |
884 | - variable_set('planet_redirect_page', $edit['planet_redirect_page']); |
885 | - } |
886 | - else { |
887 | - variable_del('planet_redirect_page'); |
888 | - } |
889 | - drupal_set_message('Edited general planet settings.'); |
890 | - } |
891 | - } |
892 | - drupal_goto('admin/settings/planet'); |
893 | - } |
894 | - else { |
895 | - $fid = intval(arg(3)); |
896 | + planet__update($_POST, 'admin/settings/planet'); |
897 | + } else { |
898 | + $fid = intval(arg(3)); // @deprecated arg() |
899 | if ($fid > 0) { |
900 | $edit = db_fetch_array(db_query('SELECT * FROM {planet_feeds} WHERE fid = %d', $fid)); |
901 | $output .= drupal_get_form('planet_feed_form', $edit, true); |
902 | @@ -279,38 +454,72 @@ |
903 | else { |
904 | |
905 | $output .= drupal_get_form('planet_settings_form'); |
906 | - //$output .= drupal_get_form('settings', $form); |
907 | - //$output .= $form; |
908 | |
909 | $output .= drupal_get_form('planet_feed_form', $edit); |
910 | |
911 | - // $result = db_query('SELECT *, (UNIX_TIMESTAMP(NOW()) - checked) _checked FROM {planet_feeds}'); |
912 | - $result = db_query('SELECT COUNT(f.fid) cnt, f.*, (UNIX_TIMESTAMP(NOW()) - checked) _checked FROM {planet_feeds} f LEFT OUTER JOIN {planet_items} i ON i.fid = f.fid GROUP BY f.fid;'); |
913 | - $rows = array(); |
914 | - $headers = array('Feed', 'Items', 'Edit', 'Last checked', 'Refresh', 'Freeze'); |
915 | - while ($feed = db_fetch_object($result)) { |
916 | - $checked = intval($feed->_checked / 60) .' minutes'; |
917 | - if ($feed->_checked % 60 > 0) { |
918 | - $checked .= ', '. $feed->_checked % 60 .' seconds'; |
919 | - } |
920 | - $checked .= ' ago'; |
921 | - array_push($rows, array( |
922 | - $feed->title, |
923 | - $feed->cnt, |
924 | - l('edit', 'admin/settings/planet/'. intval($feed->fid)), |
925 | - $checked, |
926 | - l('refresh', 'admin/settings/planet/refresh/'. intval($feed->fid)), |
927 | - l($feed->frozen ? 'unfreeze' : 'freeze', 'admin/settings/planet/'. ($feed->frozen ? 'unfreeze/' : 'freeze/') . intval($feed->fid)) |
928 | - ) |
929 | - ); |
930 | - } |
931 | $output .= '<h2>Feeds</h2>'; |
932 | - $output .= theme('table', $headers, $rows); |
933 | + $output .= planet__build_admin_feeds_table(); |
934 | } |
935 | print theme('page', $output); |
936 | } |
937 | } |
938 | |
939 | +/** |
940 | + * builds an HTML table of feeds for administrator interaction. |
941 | + * |
942 | + * @return |
943 | + * |
944 | + * @ingroup planet_feed |
945 | + * @internal |
946 | + * @CRUD{planet_feeds,R} |
947 | + * @CRUD{planet_items,R} |
948 | + */ |
949 | +function planet__build_admin_feeds_table() { |
950 | + // @DIFFINFO renamed planet__build_feeds_table() { |
951 | + global $user; |
952 | + $feeds = db_query('SELECT fid, title, checked, frozen FROM {planet_feeds} ' |
953 | + . ' WHERE uid = %d;', $user->uid); |
954 | + $rows = array(); |
955 | + $headers = array('Feed', 'Items', 'Edit', 'Last checked', 'Refresh', 'Freeze'); |
956 | + $now = time(); |
957 | + $items_statement = 'SELECT count(*) FROM {planet_items} WHERE fid=%d'; |
958 | + // TODO: change this to prepared statement when supported by drupal DB Abstraction Layer. |
959 | + while ($feed = db_fetch_object($feeds)) { |
960 | + $_checked = $now - $feed->checked; |
961 | + $checked = intval($_checked / 60) .' minutes'; |
962 | + if ($_checked % 60 > 0) { |
963 | + $checked .= ', '. $feed->_checked % 60 .' seconds'; |
964 | + } |
965 | + $checked .= ' ago'; |
966 | + $items = db_query($items_statement, $feed->fid); |
967 | + if (!$items) trigger_error('ERROR while counting feed items.'); |
968 | + $cnt = db_result($items); |
969 | + $fid = strval($feed->fid); |
970 | + array_push($rows, array( |
971 | + $feed->title, |
972 | + $cnt, |
973 | + l('edit', 'admin/settings/planet/'. $fid), |
974 | + $checked, |
975 | + l('refresh', 'admin/settings/planet/refresh/'. $fid), |
976 | + l($feed->frozen ? 'unfreeze' : 'freeze', 'admin/settings/planet/'. ($feed->frozen ? 'unfreeze/' : 'freeze/') . $fid) |
977 | + ) |
978 | + ); |
979 | + } |
980 | + return theme('table', $headers, $rows); |
981 | +} |
982 | + |
983 | +/** |
984 | + * TODO. |
985 | + * |
986 | + * @param &$form_state |
987 | + * @param $nodes |
988 | + * @param $fid |
989 | + * @param $redirect |
990 | + * @return |
991 | + * |
992 | + * @ingroup iforms |
993 | + * @CRUD{node,R} |
994 | + */ |
995 | function planet_multiple_delete_confirm(&$form_state, $nodes, $fid, $redirect) { |
996 | $form_state['values']['fid'] = $fid; |
997 | $form_state['values']['redirect'] = $redirect; |
998 | @@ -333,21 +542,42 @@ |
999 | t('Delete all'), t('Cancel')); |
1000 | } |
1001 | |
1002 | +/** |
1003 | + * TODO. |
1004 | + * |
1005 | + * @param &$form_state |
1006 | + * @param $edit |
1007 | + * @return |
1008 | + * |
1009 | + * @ingroup iforms |
1010 | + * @CRUD{node,D} |
1011 | + * @CRUD{planet_items,D} |
1012 | + * @CRUD{planet_feeds,D} |
1013 | + */ |
1014 | function planet_multiple_delete_confirm_submit(&$form_state, $edit) { |
1015 | $fid = $edit['fid']; |
1016 | if ($edit['confirm']) { |
1017 | foreach ($edit['nodes'] as $nid => $value) { |
1018 | node_delete($nid); |
1019 | } |
1020 | + // @DIFFINFO inverted drop order to respect foreign keys |
1021 | + db_query('DELETE FROM {planet_items} WHERE fid = %d', $fid); |
1022 | db_query('DELETE FROM {planet_feeds} WHERE fid = %d', $fid); |
1023 | - db_query('DELETE FROM {planet_items} WHERE fid = %d', $fid); |
1024 | drupal_set_message(t('The feed and items have been deleted.')); |
1025 | } |
1026 | drupal_goto($edit['redirect']); |
1027 | } |
1028 | |
1029 | |
1030 | - |
1031 | +/** |
1032 | + * TODO. |
1033 | + * |
1034 | + * @param &$form_state |
1035 | + * @return |
1036 | + * @ingroup iforms |
1037 | + * @CRUD{role,R} |
1038 | + * @CRUD{variables,R} |
1039 | + */ |
1040 | function planet_settings_form(&$form_state) { |
1041 | $roles = array(); |
1042 | |
1043 | @@ -401,7 +631,20 @@ |
1044 | return $form; |
1045 | } |
1046 | |
1047 | - |
1048 | +/** |
1049 | + * TODO. |
1050 | + * |
1051 | + * @param &$form_state |
1052 | + * @param $edit |
1053 | + * @param $individual |
1054 | + * @param $user |
1055 | + * @return |
1056 | + * @ingroup iforms |
1057 | + * @CRUD{users,R} |
1058 | + * @CRUD{role,R} |
1059 | + * @CRUD{users_roles,R} |
1060 | + * @CRUD{variables,R} |
1061 | + */ |
1062 | function planet_feed_form(&$form_state, $edit = array(), $individual = false, $user = NULL) { |
1063 | $uids = array(); |
1064 | $result = db_query('SELECT u.uid, u.name FROM {users} u, {role} r, {users_roles} ur WHERE u.uid = ur.uid AND ur.rid = r.rid AND r.rid = %d ORDER BY u.name ASC', variable_get('planet_author_roles', 2)); |
1065 | @@ -474,6 +717,13 @@ |
1066 | return $form; |
1067 | } |
1068 | |
1069 | +/** |
1070 | + * Implementation of hook_cron - Perform periodic actions. |
1071 | + * |
1072 | + * @return none |
1073 | + * @ingroup system |
1074 | + * @CRUD{planet_feeds,R} |
1075 | + */ |
1076 | function planet_cron() { |
1077 | $result = db_query('SELECT fid FROM {planet_feeds} WHERE frozen = 0'); |
1078 | while ($feed = db_fetch_object($result)) { |
1079 | @@ -482,515 +732,76 @@ |
1080 | } |
1081 | } |
1082 | |
1083 | + |
1084 | +/** |
1085 | + * Private function; Checks a news feed for new items. |
1086 | + * |
1087 | + * @param $fid feed identifier (defaults to arg(4)) |
1088 | + * @return |
1089 | + * |
1090 | + * @private |
1091 | + * @CRUD{planet_feeds,U} |
1092 | + * @invoke{module_invoke,taxonomy_node_get_terms} |
1093 | + */ |
1094 | function planet_refresh($fid = null) { |
1095 | if (!$fid) { |
1096 | $fid = intval(arg(4)); |
1097 | } |
1098 | - |
1099 | - $feed = db_fetch_object(db_query('SELECT * FROM {planet_feeds} WHERE fid = %d', $fid)); |
1100 | - |
1101 | - $headers = array(); |
1102 | - $result = planet_http_request($feed->link, $headers, 15); |
1103 | - |
1104 | - switch ($result->code) { |
1105 | - case 304: |
1106 | - drupal_set_message(t('No new content syndicated from %site.', array('%site' => '<em>'. $feed->title .'</em>'))); |
1107 | - break; |
1108 | - |
1109 | - case 301: |
1110 | - if ($result->redirect_url) { |
1111 | - $feed->link = $result->redirect_url; |
1112 | - watchdog('planet', 'Updated URL for feed %title to %url.', array('%title' => '<em>'. $feed->title .'</em>', '%url' => '<em>'. $feed->url .'</em>'), WATCHDOG_NOTICE, l(t('view'), 'planet/'.$feed->fid)); |
1113 | - db_query("UPDATE {planet_feeds} SET link = '%s' WHERE fid = %d", $feed->link, $feed->fid); |
1114 | - } |
1115 | - break; |
1116 | - |
1117 | - case 200: |
1118 | - case 302: |
1119 | - case 307: |
1120 | - $xml_tree = planet_parse_xml($result->data); |
1121 | - |
1122 | - if ($xml_tree['parser_error']) { |
1123 | - watchdog('planet', 'Failed to parse RSS feed %site: %error at line %line.', array('%site' => '<em>'. $feed->title .'</em>', '%error' => $xml_tree['parser_error'], '%line' => $xml_tree['parser_line']), WATCHDOG_ERROR); |
1124 | - drupal_set_message(t('Failed to parse RSS feed %site: %error at line %line.', array('%site' => '<em>'. $feed->title .'</em>', '%error' => $xml_tree['parser_error'], '%line' => $xml_tree['parser_line'])), 'error'); |
1125 | - break; |
1126 | - } |
1127 | - else { |
1128 | - drupal_set_message('Parsing feed '. $feed->title .' took '. $xml_tree['parser_time'] .' seconds.'); |
1129 | - } |
1130 | - |
1131 | - if (planet_parse_items($xml_tree, $feed) !== false) { |
1132 | - if ($result->headers['Last-Modified']) { |
1133 | - $modified = strtotime($result->headers['Last-Modified']); |
1134 | - } |
1135 | - |
1136 | - /* |
1137 | - ** Prepare data: |
1138 | - */ |
1139 | - if ($xml_tree['RSS']) { // RSS 0.91, 0.92, 2.0 |
1140 | - $root = &$xml_tree['RSS'][0]; |
1141 | - $channel = &$root['CHANNEL'][0]; |
1142 | - $image = &$channel['IMAGE'][0]; |
1143 | - $description = &$channel['DESCRIPTION'][0]['VALUE']; |
1144 | - $link = &$channel['LINK'][0]['VALUE']; |
1145 | - } |
1146 | - else if ($xml_tree['RDF:RDF']) { |
1147 | - $root = &$xml_tree['RDF:RDF'][0]; |
1148 | - $channel = &$root['CHANNEL'][0]; |
1149 | - $image = &$root['IMAGE'][0]; |
1150 | - $description = &$channel['DESCRIPTION'][0]['VALUE']; |
1151 | - $link = &$channel['LINK'][0]['VALUE']; |
1152 | - } |
1153 | - else if ($xml_tree['FEED']) { // Atom 0.3, 1.0 |
1154 | - $root = &$xml_tree['FEED'][0]; |
1155 | - $channel = &$root; |
1156 | - $image = &$channel['LOGO'][0]['VALUE']; |
1157 | - $description = ($channel['TAGLINE'][0]['VALUE'] ? $channel['TAGLINE'][0]['VALUE'] : ''); |
1158 | - // TODO: remove this Atom hack when we have field mapping or at least specialized parsers in place |
1159 | - if (count($channel['LINK']) > 1) { |
1160 | - $link = $feed->link; |
1161 | - foreach ($channel['LINK'] as $l) { |
1162 | - if ($l['REL'] == 'alternate') { |
1163 | - $link = $l['HREF']; |
1164 | - } |
1165 | - } |
1166 | - } |
1167 | - else { |
1168 | - $link = $channel['LINK'][0]['HREF']; |
1169 | - } |
1170 | - } |
1171 | - else if ($xml_tree['CHANNEL']) { // RSS 1.1 |
1172 | - $root = &$xml_tree['CHANNEL'][0]; |
1173 | - $channel = &$root; |
1174 | - $image = &$channel['IMAGE'][0]; |
1175 | - $description = &$channel['DESCRIPTION'][0]['VALUE']; |
1176 | - $link = &$channel['LINK'][0]['VALUE']; |
1177 | - } |
1178 | - else if ($xml_tree['OPML']) { |
1179 | - $root = &$xml_tree['OPML'][0]; |
1180 | - $channel = &$root; |
1181 | - $image = NULL; |
1182 | - $description = NULL; |
1183 | - $link = NULL; |
1184 | - } |
1185 | - else { |
1186 | - // unsupported format |
1187 | - break; |
1188 | - } |
1189 | - |
1190 | - if (!$feed->uid) { |
1191 | - if ($channel['AUTHOR'][0]['VALUE']) { |
1192 | - $feed->uid = $channel['AUTHOR'][0]['VALUE']; |
1193 | - } |
1194 | - if ($channel['AUTHOR'][0]['NAME'][0]['VALUE']) { |
1195 | - $feed->uid = $channel['AUTHOR'][0]['NAME'][0]['VALUE']; |
1196 | - } |
1197 | - else if ($channel['DC:CREATOR']) { |
1198 | - $feed->uid = $channel['DC:CREATOR'][0]['VALUE']; |
1199 | - } |
1200 | - else { |
1201 | - $feed->uid = ''; |
1202 | - } |
1203 | - } |
1204 | - |
1205 | - /* |
1206 | - ** Generate image link |
1207 | - */ |
1208 | - if (!$feed->image && $image['LINK'] && $image['URL'] && $image['TITLE']) { |
1209 | - if (strlen($image['TITLE'][0]['VALUE']) > 250) { |
1210 | - $image['TITLE'][0]['VALUE'] = trim(substr($image['TITLE'][0]['VALUE'], 0, 250)).'...'; |
1211 | - } |
1212 | - $feed->image = '<a href="'. $image['LINK'][0]['VALUE'] .'" class="planet_logo_link"><img src="'. $image['URL'][0]['VALUE'] .'" class="planet_logo" alt="'. $image['TITLE'][0]['VALUE'] .'" /></a>'; |
1213 | - } |
1214 | - |
1215 | - /* |
1216 | - ** Update the feed data: |
1217 | - */ |
1218 | - $feed->checked = time(); |
1219 | - $feed->link = $link; |
1220 | - $feed->etag = $result->headers['ETag']; |
1221 | - $feed->modified = $modified; |
1222 | - if ($feed->body == '' && $description/* && valid_input_data($description)*/) { |
1223 | - $feed->body = $feed->teaser = $description; |
1224 | - } |
1225 | - $feed->rss_data = &$xml_tree; |
1226 | - |
1227 | - /* |
1228 | - ** Taxonomy module doesn't add taxonomy terms at load time... so we have to do it by hand :(( |
1229 | - */ |
1230 | - $terms = module_invoke('taxonomy', 'node_get_terms', $feed->nid, 'tid'); |
1231 | - $feed->taxonomy = array(); |
1232 | - foreach ($terms as $tid => $term) { |
1233 | - if ($term->tid) { |
1234 | - $feed->taxonomy[] = $term->tid; |
1235 | - } |
1236 | - } |
1237 | - } |
1238 | - default: |
1239 | - } |
1240 | - |
1241 | - |
1242 | - db_query('UPDATE {planet_feeds} SET checked = %d WHERE fid = %d', time(), $fid); |
1243 | - return $feed->title; |
1244 | - //print theme('page', 'refreshing '. $fid .'.');// and got '. print_r($feed, 1)); |
1245 | -} |
1246 | - |
1247 | -/** |
1248 | - * Private function; Parse HTTP headers from data retreived with cURL |
1249 | - * from: http://pl2.php.net/manual/en/function.curl-setopt.php#42009 |
1250 | - */ |
1251 | -function planet_parse_response($response) { |
1252 | - /* |
1253 | - ***original code extracted from examples at |
1254 | - ***http://www.webreference.com/programming |
1255 | - /php/cookbook/chap11/1/3.html |
1256 | - |
1257 | - ***returns an array in the following format which varies depending on headers returned |
1258 | - |
1259 | - [0] => the HTTP error or response code such as 404 |
1260 | - [1] => Array |
1261 | - ( |
1262 | - [Server] => Microsoft-IIS/5.0 |
1263 | - [Date] => Wed, 28 Apr 2004 23:29:20 GMT |
1264 | - [X-Powered-By] => ASP.NET |
1265 | - [Connection] => close |
1266 | - [Set-Cookie] => COOKIESTUFF |
1267 | - [Expires] => Thu, 01 Dec 1994 16:00:00 GMT |
1268 | - [Content-Type] => text/html |
1269 | - [Content-Length] => 4040 |
1270 | - ) |
1271 | - [2] => Response body (string) |
1272 | - */ |
1273 | - |
1274 | - do { |
1275 | - list($response_headers, $response) = explode("\r\n\r\n", $response, 2); |
1276 | - $response_header_lines = explode("\r\n", $response_headers); |
1277 | - |
1278 | - // first line of headers is the HTTP response code |
1279 | - $http_response_line = array_shift($response_header_lines); |
1280 | - if (preg_match('@^HTTP/[0-9]\.[0-9] ([0-9]{3})@', $http_response_line, $matches)) { |
1281 | - $response_code = $matches[1]; |
1282 | - } |
1283 | - else { |
1284 | - $response_code = "Error"; |
1285 | - } |
1286 | - } |
1287 | - while (substr($response_code, 0, 1) == "1"); |
1288 | - |
1289 | - $response_body = $response; |
1290 | - |
1291 | - // put the rest of the headers in an array |
1292 | - $response_header_array = array(); |
1293 | - foreach ($response_header_lines as $header_line) { |
1294 | - list($header, $value) = explode(':', $header_line, 2); |
1295 | - $response_header_array[$header] = trim($value); |
1296 | - } |
1297 | - |
1298 | - return array($response_code, $response_header_array, $response_body, $http_response_line); |
1299 | -} |
1300 | - |
1301 | -/** |
1302 | - * Private function; Gets data from given URL :) |
1303 | - */ |
1304 | -function planet_http_request($url, $headers = array(), $timeout = 15, $method = 'GET', $data = NULL, $follow = 3) { |
1305 | - if (!function_exists('curl_init')) { |
1306 | - return drupal_http_request($url, $headers, $method, $data, $follow); |
1307 | - } |
1308 | - |
1309 | - // convert headers array to format used by cURL |
1310 | - $temp = array(); |
1311 | - foreach ($headers as $header => $value) { |
1312 | - $temp[] = $header .': '. $value; |
1313 | - } |
1314 | - $headers = $temp; |
1315 | - |
1316 | - $result = new StdClass(); |
1317 | - |
1318 | - $ch = curl_init(); |
1319 | - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); |
1320 | - curl_setopt($ch, CURLOPT_URL, $url); |
1321 | - curl_setopt($ch, CURLOPT_HEADER, 1); |
1322 | - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); |
1323 | - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0); |
1324 | - curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); |
1325 | - |
1326 | - $data = curl_exec($ch); |
1327 | - $info = curl_getinfo($ch); |
1328 | - |
1329 | - curl_close($ch); |
1330 | - unset($ch); |
1331 | - |
1332 | - $response = planet_parse_response($data); |
1333 | - $result->code = $response[0]; |
1334 | - $result->headers = $response[1]; |
1335 | - $result->data = $response[2]; |
1336 | - $error = $response[3]; |
1337 | - switch ($code) { |
1338 | - case 200: // OK |
1339 | - case 304: // Not modified |
1340 | - break; |
1341 | - case 301: // Moved permanently |
1342 | - case 302: // Moved temporarily |
1343 | - case 307: // Moved temporarily |
1344 | - $location = $result->headers['Location']; |
1345 | - |
1346 | - if ($follow) { |
1347 | - $result = planet_http_request($result->headers['Location'], $headers, $timeout, $method, $data, --$follow); |
1348 | - $result->redirect_code = $result->code; |
1349 | - } |
1350 | - $result->redirect_url = $location; |
1351 | - break; |
1352 | - default: |
1353 | - $result->error = $error; |
1354 | - break; |
1355 | - } |
1356 | - |
1357 | - $result->code = $response[0]; |
1358 | - return $result; |
1359 | -} |
1360 | - |
1361 | -/** |
1362 | - * Private function; Checks a news feed for new items. |
1363 | - */ |
1364 | - |
1365 | - |
1366 | -/** |
1367 | - * Private function; |
1368 | - * Parse the W3C date/time format, a subset of ISO 8601. PHP date parsing |
1369 | - * functions do not handle this format. |
1370 | - * See http://www.w3.org/TR/NOTE-datetime for more information. |
1371 | - * Origionally from MagpieRSS (http://magpierss.sourceforge.net/). |
1372 | - * |
1373 | - * @param $date_str A string with a potentially W3C DTF date. |
1374 | - * @return A timestamp if parsed successfully or -1 if not. |
1375 | - */ |
1376 | -function planet_parse_w3cdtf($date_str) { |
1377 | - if (preg_match('/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(:(\d{2}))?(?:([-+])(\d{2}):?(\d{2})|(Z))?/', $date_str, $match)) { |
1378 | - list($year, $month, $day, $hours, $minutes, $seconds) = array($match[1], $match[2], $match[3], $match[4], $match[5], $match[6]); |
1379 | - // calc epoch for current date assuming GMT |
1380 | - $epoch = gmmktime($hours, $minutes, $seconds, $month, $day, $year); |
1381 | - if ($match[10] != 'Z') { // Z is zulu time, aka GMT |
1382 | - list($tz_mod, $tz_hour, $tz_min) = array($match[8], $match[9], $match[10]); |
1383 | - // zero out the variables |
1384 | - if (!$tz_hour) { |
1385 | - $tz_hour = 0; |
1386 | - } |
1387 | - if (!$tz_min) { |
1388 | - $tz_min = 0; |
1389 | - } |
1390 | - $offset_secs = (($tz_hour * 60) + $tz_min) * 60; |
1391 | - // is timezone ahead of GMT? then subtract offset |
1392 | - if ($tz_mod == '+') { |
1393 | - $offset_secs *= -1; |
1394 | - } |
1395 | - $epoch += $offset_secs; |
1396 | - } |
1397 | - return $epoch; |
1398 | - } |
1399 | - else { |
1400 | - return -1; |
1401 | - } |
1402 | -} |
1403 | - |
1404 | -/** |
1405 | - * Private function; |
1406 | - * from: http://pl2.php.net/manual/en/function.html-entity-decode.php#51055 |
1407 | - * Used as callback function for preg_replace_all() to decode numeric entities to UTF-8 chars |
1408 | - * |
1409 | - * @param $ord Number |
1410 | - * @return UTF-8 string |
1411 | - */ |
1412 | -function planet_replace_num_entity($ord) { |
1413 | - $ord = $ord[1]; |
1414 | - if (preg_match('/^x([0-9a-f]+)$/i', $ord, $match)) { |
1415 | - $ord = hexdec($match[1]); |
1416 | - } |
1417 | - else { |
1418 | - $ord = intval($ord); |
1419 | - } |
1420 | - |
1421 | - $no_bytes = 0; |
1422 | - $byte = array(); |
1423 | - |
1424 | - if ($ord == 128) { |
1425 | - return chr(226) . chr(130) . chr(172); |
1426 | - } |
1427 | - else if ($ord == 129) { |
1428 | - return chr(239) . chr(191) . chr(189); |
1429 | - } |
1430 | - else if ($ord == 130) { |
1431 | - return chr(226) . chr(128) . chr(154); |
1432 | - } |
1433 | - else if ($ord == 131) { |
1434 | - return chr(198) . chr(146); |
1435 | - } |
1436 | - else if ($ord == 132) { |
1437 | - return chr(226) . chr(128) . chr(158); |
1438 | - } |
1439 | - else if ($ord == 133) { |
1440 | - return chr(226) . chr(128) . chr(166); |
1441 | - } |
1442 | - else if ($ord == 134) { |
1443 | - return chr(226) . chr(128) . chr(160); |
1444 | - } |
1445 | - else if ($ord == 135) { |
1446 | - return chr(226) . chr(128) . chr(161); |
1447 | - } |
1448 | - else if ($ord == 136) { |
1449 | - return chr(203) . chr(134); |
1450 | - } |
1451 | - else if ($ord == 137) { |
1452 | - return chr(226) . chr(128) . chr(176); |
1453 | - } |
1454 | - else if ($ord == 138) { |
1455 | - return chr(197) . chr(160); |
1456 | - } |
1457 | - else if ($ord == 139) { |
1458 | - return chr(226) . chr(128) . chr(185); |
1459 | - } |
1460 | - else if ($ord == 140) { |
1461 | - return chr(197) . chr(146); |
1462 | - } |
1463 | - else if ($ord == 141) { |
1464 | - return chr(239) . chr(191) . chr(189); |
1465 | - } |
1466 | - else if ($ord == 142) { |
1467 | - return chr(197) . chr(189); |
1468 | - } |
1469 | - else if ($ord == 143) { |
1470 | - return chr(239) . chr(191) . chr(189); |
1471 | - } |
1472 | - else if ($ord == 144) { |
1473 | - return chr(239) . chr(191) . chr(189); |
1474 | - } |
1475 | - else if ($ord == 145) { |
1476 | - return chr(226) . chr(128) . chr(152); |
1477 | - } |
1478 | - else if ($ord == 146) { |
1479 | - return chr(226) . chr(128) . chr(153); |
1480 | - } |
1481 | - else if ($ord == 147) { |
1482 | - return chr(226) . chr(128) . chr(156); |
1483 | - } |
1484 | - else if ($ord == 148) { |
1485 | - return chr(226) . chr(128) . chr(157); |
1486 | - } |
1487 | - else if ($ord == 149) { |
1488 | - return chr(226) . chr(128) . chr(162); |
1489 | - } |
1490 | - else if ($ord == 150) { |
1491 | - return chr(226) . chr(128) . chr(147); |
1492 | - } |
1493 | - else if ($ord == 151) { |
1494 | - return chr(226) . chr(128) . chr(148); |
1495 | - } |
1496 | - else if ($ord == 152) { |
1497 | - return chr(203) . chr(156); |
1498 | - } |
1499 | - else if ($ord == 153) { |
1500 | - return chr(226) . chr(132) . chr(162); |
1501 | - } |
1502 | - else if ($ord == 154) { |
1503 | - return chr(197) . chr(161); |
1504 | - } |
1505 | - else if ($ord == 155) { |
1506 | - return chr(226) . chr(128) . chr(186); |
1507 | - } |
1508 | - else if ($ord == 156) { |
1509 | - return chr(197) . chr(147); |
1510 | - } |
1511 | - else if ($ord == 157) { |
1512 | - return chr(239) . chr(191) . chr(189); |
1513 | - } |
1514 | - else if ($ord == 158) { |
1515 | - return chr(197) . chr(190); |
1516 | - } |
1517 | - else if ($ord == 159) { |
1518 | - return chr(197) . chr(184); |
1519 | - } |
1520 | - else if ($ord == 160) { |
1521 | - return chr(194) . chr(160); |
1522 | - } |
1523 | - |
1524 | - if ($ord < 128) { |
1525 | - return chr($ord); |
1526 | - } |
1527 | - else if ($ord < 2048) { |
1528 | - $no_bytes = 2; |
1529 | - } |
1530 | - else if ($ord < 65536) { |
1531 | - $no_bytes = 3; |
1532 | - } |
1533 | - else if ($ord < 1114112) { |
1534 | - $no_bytes = 4; |
1535 | - } |
1536 | - else { |
1537 | - return; |
1538 | - } |
1539 | - |
1540 | - switch ($no_bytes) { |
1541 | - case 2: |
1542 | - $prefix = array(31, 192); |
1543 | - break; |
1544 | - |
1545 | - case 3: |
1546 | - $prefix = array(15, 224); |
1547 | - break; |
1548 | - |
1549 | - case 4: |
1550 | - $prefix = array(7, 240); |
1551 | - break; |
1552 | - } |
1553 | - |
1554 | - for ($i = 0; $i < $no_bytes; $i++) { |
1555 | - $byte[$no_bytes - $i - 1] = (($ord & (63 * pow(2, 6 * $i))) / pow(2, 6 * $i)) & 63 | 128; |
1556 | - } |
1557 | - |
1558 | - $byte[0] = ($byte[0] & $prefix[0]) | $prefix[1]; |
1559 | - |
1560 | - $ret = ''; |
1561 | - for ($i = 0; $i < $no_bytes; $i++) { |
1562 | - $ret .= chr($byte[$i]); |
1563 | - } |
1564 | - |
1565 | - return $ret; |
1566 | -} |
1567 | - |
1568 | -/** |
1569 | - * Private function; Convert named entities to UTF-8 characters |
1570 | - * from: http://pl2.php.net/manual/en/function.html-entity-decode.php#51722 |
1571 | - */ |
1572 | -function planet_replace_name_entities(&$text) { |
1573 | - static $ttr; |
1574 | - if (!$ttr) { |
1575 | - $trans_tbl = get_html_translation_table(HTML_ENTITIES); |
1576 | - foreach ($trans_tbl as $k => $v) { |
1577 | - $ttr[$v] = utf8_encode($k); |
1578 | - } |
1579 | - $ttr['''] = "'"; |
1580 | - } |
1581 | - return strtr($text, $ttr); |
1582 | -} |
1583 | - |
1584 | -/** |
1585 | - * Private function; Convert all entities to UTF-8 characters |
1586 | - */ |
1587 | -function planet_replace_entities(&$text) { |
1588 | - $result = planet_replace_name_entities($text); |
1589 | - return preg_replace_callback('/&#([0-9a-fx]+);/mi', 'planet_replace_num_entity', $result); |
1590 | -} |
1591 | - |
1592 | -/** |
1593 | - * Private function; Clone object function to stay compatible with both php4 and php5 |
1594 | - * from: Drupal 4.7CVS |
1595 | - * TODO: remove after moving to Drupal 4.7 |
1596 | - */ |
1597 | -function planet_clone($object) { |
1598 | - return version_compare(phpversion(), '5.0') < 0 ? $object : clone($object); |
1599 | -} |
1600 | + // initialize simplepie |
1601 | + // we want to do this only once and not each time per feed, which would be slower |
1602 | + include_once './'. drupal_get_path('module', 'planet') .'/simplepie.inc'; |
1603 | + |
1604 | + $process_feed = db_fetch_object(db_query('SELECT * FROM {planet_feeds} WHERE fid = %d', $fid)); |
1605 | + |
1606 | + $feed = new SimplePie(); |
1607 | + $feed->enable_cache(FALSE); |
1608 | + $feed->set_timeout(15); |
1609 | + // prevent SimplePie from using all of it's data santization since we use Drupal's input formats to handle this |
1610 | + $feed->set_stupidly_fast(TRUE); |
1611 | + $feed->set_feed_url($process_feed->link); |
1612 | + // FeedBurner requires this check otherwise it won't work well with SimplePie |
1613 | + // also performance improvement |
1614 | + header('If-Modified-Since:'. $process_feed->checked); |
1615 | + $success = $feed->init(); |
1616 | + |
1617 | + if ($success && $feed->data) { |
1618 | + // get a unique hash of the headers in the feed, fast and easy way to compare if this feed is updated or not |
1619 | + $hash = md5(serialize($feed->data)); |
1620 | + |
1621 | + // hashes don't match so likely the feed is updated |
1622 | + if ($process_feed->hash != $hash) { |
1623 | + // above we define hook_view() which then performs check_url() on the $url in the feed node |
1624 | + // the problem is check_url() calls filter_xss_bad_protocol() which does it thing to prevent XSS |
1625 | + // but it returns the string through check_plain() which calls htmlspecialchars() |
1626 | + // this converts & in a url to & and then causes SimplePie not to be able to parse it |
1627 | + // because of this, we decode this URL since we are passing it directly to SimplePie |
1628 | + // it is still encoded everywhere else it is output to prevent XSS |
1629 | + $process_feed->link = htmlspecialchars_decode($process_feed->link, ENT_QUOTES); |
1630 | + |
1631 | + // turn each feed item into a node |
1632 | + planet_item_feed_parse($process_feed, $feed); |
1633 | + } |
1634 | + |
1635 | + // finished processing this feed so we can mark it checked |
1636 | + db_query("UPDATE {planet_feeds} SET checked = %d, hash = '%s', error = 0 WHERE fid = %d", time(), $hash, $process_feed->fid); |
1637 | + } |
1638 | + else if (isset($feed->error)) { |
1639 | + db_query("UPDATE {planet_feeds} SET error = 1 WHERE fid = %d", $process_feed->fid); |
1640 | + watchdog('planet', 'The feed %feed could not be processed due to the following error: %error', array('%feed' => $process_feed->title, '%error' => $feed->error), WATCHDOG_ERROR, l('view', $process_feed->link)); |
1641 | + } |
1642 | + else { |
1643 | + watchdog('planet', 'You shouldn\'t be here. Something has gone terribly wrong.'); |
1644 | + } |
1645 | + |
1646 | + return $process_feed->title; |
1647 | +} |
1648 | + |
1649 | +// @DIFFINFO removed orphaned doc-block |
1650 | |
1651 | /** |
1652 | * Private function; Convert relative URLs |
1653 | + * @ingroup rss |
1654 | + * @private |
1655 | */ |
1656 | function planet_convert_relative_urls(&$data, $base_url) { |
1657 | $src = '%( href| src)="(?!\w+://)/?([^"]*)"%'; |
1658 | @@ -999,340 +810,11 @@ |
1659 | } |
1660 | |
1661 | /** |
1662 | - * Private function; Creates nodes from data found in given xml_tree |
1663 | - */ |
1664 | -function planet_parse_items(&$xml_tree, &$feed) { |
1665 | - |
1666 | - if ($xml_tree['RSS']) { // RSS 0.91, 0.92, 2.0 |
1667 | - $items = &$xml_tree['RSS'][0]['CHANNEL'][0]['ITEM']; |
1668 | - $link_field = 'VALUE'; |
1669 | - } |
1670 | - else if ($xml_tree['RDF:RDF']) { |
1671 | - $items = &$xml_tree['RDF:RDF'][0]['ITEM']; |
1672 | - $link_field = 'VALUE'; |
1673 | - } |
1674 | - else if ($xml_tree['FEED']) { // Atom 0.3, 1.0 |
1675 | - $items = &$xml_tree['FEED'][0]['ENTRY']; |
1676 | - $link_field = 'HREF'; |
1677 | - } |
1678 | - else if ($xml_tree['CHANNEL']) { // RSS 1.1 |
1679 | - $items = &$xml_tree['CHANNEL'][0]['ITEMS'][0]['ITEM']; |
1680 | - $link_field = 'VALUE'; |
1681 | - } |
1682 | - else { |
1683 | - // unsupported format |
1684 | - $items = array(); |
1685 | - return false; |
1686 | - } |
1687 | - |
1688 | - /* |
1689 | - ** We reverse the array such that we store the first item last, |
1690 | - ** and the last item first. In the database, the newest item |
1691 | - ** should be at the top. |
1692 | - */ |
1693 | - $items_added = 0; |
1694 | - |
1695 | - |
1696 | - for ($index = count($items) - 1; $index >= 0; $index--) { |
1697 | - $item = &$items[$index]; |
1698 | - //print '<pre>'. print_r($item, 1) .'</pre>'; |
1699 | - $teaser = NULL; |
1700 | - $body = NULL; |
1701 | - |
1702 | - // Description field is needed early for case when no title is specified |
1703 | - if ($item['DESCRIPTION']) { // RSS 0.91, 0.92, 1.0, 1.1, 2.0 |
1704 | - $body = &$item['DESCRIPTION'][0]['VALUE']; |
1705 | - } |
1706 | - else if ($item['SUMMARY']) { // Atom 0.3, 1.0 |
1707 | - $body = &$item['SUMMARY'][0]['VALUE']; |
1708 | - } |
1709 | - |
1710 | - if ($item['CONTENT']) { // Atom 0.3, 1.0 |
1711 | - if (strlen($body) < strlen($item['CONTENT'][0]['VALUE'])) { |
1712 | - if ($body) { |
1713 | - $teaser = $body; |
1714 | - } |
1715 | - $body = &$item['CONTENT'][0]['VALUE']; |
1716 | - } |
1717 | - } |
1718 | - else if ($item['CONTENT:ENCODED']) { // Don't know where it came from but it can be found in RSS 2.0 feeds |
1719 | - if (strlen($body) < strlen($item['CONTENT:ENCODED'][0]['VALUE'])) { |
1720 | - if ($body) { |
1721 | - $teaser = $body; |
1722 | - } |
1723 | - $body = &$item['CONTENT:ENCODED'][0]['VALUE']; |
1724 | - } |
1725 | - } |
1726 | - |
1727 | - /* |
1728 | - ** Resolve the item's title. If no title is found, we use |
1729 | - ** up to 40 characters of the description ending at a word |
1730 | - ** boundary but not splitting potential entities. |
1731 | - */ |
1732 | - if (!($title = $item['TITLE'][0]['VALUE'])) { |
1733 | - $title = preg_replace('/^(.*)[^\w;&].*?$/', "\\1", truncate_utf8($body, 40)); |
1734 | - } |
1735 | - |
1736 | - // If title was "escaped" then it may still contain entities, becuase each & from entity was also escabet to & before |
1737 | - // TODO: the same for content? |
1738 | - if ($item['TITLE'][0]['MODE'] == 'escaped') { |
1739 | - $title = planet_replace_entities($title); |
1740 | - } |
1741 | - $title = strip_tags($title); |
1742 | - |
1743 | - /* |
1744 | - ** Resolve the items link. |
1745 | - */ |
1746 | - if ($item['LINK']) { |
1747 | - // TODO: remove this Atom hack when we have field mapping or at least specialized parsers in place |
1748 | - if (count($item['LINK']) > 1) { |
1749 | - $link = $feed->link; |
1750 | - foreach ($item['LINK'] as $temp) { |
1751 | - if ($temp['REL'] == 'alternate') { |
1752 | - $link = $temp[$link_field]; |
1753 | - } |
1754 | - } |
1755 | - } |
1756 | - else { |
1757 | - $link = $item['LINK'][0][$link_field]; |
1758 | - } |
1759 | - } |
1760 | - elseif ($item['GUID'] && (strncmp($item['GUID'][0][$link_field], 'http://', 7) == 0) && $item['GUID'][0]['ISPERMALINK'] != 'false') { |
1761 | - $link = $item['GUID'][0][$link_field]; |
1762 | - } |
1763 | - else { |
1764 | - $link = $feed->link; |
1765 | - } |
1766 | - |
1767 | - /* |
1768 | - ** Resolve the items source. |
1769 | - */ |
1770 | - if ($item['SOURCE'][0]['VALUE'] && $item['SOURCE'][0]['URL']) { // RSS 2.0 |
1771 | - $source_title = &$item['SOURCE'][0]['VALUE']; |
1772 | - $source_link = &$item['SOURCE'][0]['URL']; |
1773 | - } |
1774 | - else if ($item['SOURCE'] || $item['ATOM:SOURCE']) { // ATOM 1.0 |
1775 | - if ($item['SOURCE'][0]['TITLE']) $source_title = &$item['SOURCE'][0]['TITLE'][0]['VALUE']; |
1776 | - else if ($item['SOURCE'][0]['ATOM:TITLE']) $source_title = &$item['SOURCE'][0]['ATOM:TITLE'][0]['VALUE']; |
1777 | - if ($item['SOURCE'][0]['LINK']) $source_link = &$item['SOURCE'][0]['LINK'][0]['VALUE']; |
1778 | - else if ($item['SOURCE'][0]['ATOM:LINK']) $source_link = &$item['SOURCE'][0]['ATOM:LINK'][0]['VALUE']; |
1779 | - } |
1780 | - else { |
1781 | - $source_title = ''; |
1782 | - $source_link = ''; |
1783 | - } |
1784 | - |
1785 | - /* |
1786 | - ** Try to resolve and parse the item's publication date. If no |
1787 | - ** date is found, we use the current date instead. |
1788 | - */ |
1789 | - // TODO: find nicer way for handling namespaces ;) |
1790 | - if ($item['PUBDATE']) $date = $item['PUBDATE'][0]['VALUE']; // RSS 2.0 |
1791 | - else if ($item['DC:DATE']) $date = $item['DC:DATE'][0]['VALUE']; // Dublin core |
1792 | - else if ($item['DATE']) $date = $item['DATE'][0]['VALUE']; // Dublin core |
1793 | - else if ($item['DCTERMS:ISSUED']) $date = $item['DCTERMS:ISSUED'][0]['VALUE']; // Dublin core |
1794 | - else if ($item['ISSUED']) $date = $item['ISSUED'][0]['VALUE']; // Dublin core |
1795 | - else if ($item['DCTERMS:CREATED']) $date = $item['DCTERMS:CREATED'][0]['VALUE']; // Dublin core |
1796 | - else if ($item['CREATED']) $date = $item['CREATED'][0]['VALUE']; // Dublin core |
1797 | - else if ($item['DCTERMS:MODIFIED']) $date = $item['DCTERMS:MODIFIED'][0]['VALUE']; // Dublin core |
1798 | - else if ($item['MODIFIED']) $date = $item['MODIFIED'][0]['VALUE']; // Dublin core |
1799 | - else if ($item['ATOM:UPDATED']) $date = $item['ATOM:UPDATED'][0]['VALUE']; // Atom |
1800 | - else if ($item['UPDATED']) $date = $item['UPDATED'][0]['VALUE']; // Atom |
1801 | - else $date = 'now'; |
1802 | - |
1803 | - if ($feed->item_date_source == FEEDS_ITEM_DATE_SNIFFED && $date) { |
1804 | - $timestamp = strtotime($date); // strtotime() returns -1 on failure |
1805 | - if ($timestamp < 0) { |
1806 | - $timestamp = planet_parse_w3cdtf($date); // also returns -1 on failure |
1807 | - if ($timestamp < 0) { |
1808 | - $timestamp = time(); // better than nothing |
1809 | - } |
1810 | - } |
1811 | - } |
1812 | - else { |
1813 | - $timestamp = time(); |
1814 | - } |
1815 | - |
1816 | - // Ignore items older than allowed for feed |
1817 | - if ($timestamp < $time_horizont) { |
1818 | - continue; |
1819 | - } |
1820 | - |
1821 | - /* |
1822 | - ** Save this item. Try to avoid duplicate entries as much as |
1823 | - ** possible. If we find a duplicate entry, we resolve it and |
1824 | - ** pass along it's ID such that we can update it if needed. |
1825 | - */ |
1826 | - // Try to use RSS:GUID/ATOM:ID as unique identifier |
1827 | - $guid = ''; |
1828 | - if ($item['GUID'][0]['VALUE']) { // RSS 2.0 |
1829 | - $guid = $item['GUID'][0]['VALUE']; |
1830 | - } |
1831 | - else if ($item['ATOM:ID'][0]['VALUE']) { // ATOM 0.3, 1.0 |
1832 | - $guid = $item['ATOM:ID'][0]['VALUE']; |
1833 | - } |
1834 | - else if ($item['ID'][0]['VALUE']) { // ATOM 0.3, 1.0 |
1835 | - $guid = $item['ID'][0]['VALUE']; |
1836 | - } |
1837 | - else { |
1838 | - // feed may contain duplicated links for different items, so we try to generate unique ID for each item |
1839 | - $guid = md5("$title - . " . $feed->fid); |
1840 | - } |
1841 | - // TODO: is there anyway to check if DC:IDENTIFIER is unique? |
1842 | - // http://dublincore.org/documents/usageguide/elements.shtml says it can be non-unique so useles for us :( |
1843 | - |
1844 | - $entry = NULL; |
1845 | - if ($guid && strlen($guid) > 0) { |
1846 | - $entry = db_fetch_object(db_query("SELECT nid FROM {planet_items} WHERE guid = '%s' AND fid = %d", $guid, $feed->fid)); |
1847 | - } |
1848 | - else if ($link && $link != $feed->link && $link != $feed->url) { |
1849 | - $entry = db_fetch_object(db_query("SELECT nid FROM {planet_items} WHERE guid = '%s' AND fid = %d", $link, $feed->fid)); |
1850 | - } |
1851 | - else { |
1852 | - $entry = db_fetch_object(db_query("SELECT ai.nid AS nid FROM {node} n, {planet_items} ai WHERE ai.fid = %d AND ai.nid = n.nid AND n.title = '%s'", $feed->fid, $title)); |
1853 | - } |
1854 | - |
1855 | - //print $guid .'<br />'; |
1856 | - //print $entry->nid .'<br />'; |
1857 | - // Ignore items already existing in database and not allowed to be updated |
1858 | - |
1859 | - //Fields to update in either case |
1860 | - $entry->changed = strtotime($date); |
1861 | - $entry->title = $title; |
1862 | - $entry->body = $body; |
1863 | - $entry->body = planet_convert_relative_urls($body, $link); |
1864 | - $entry->teaser = node_teaser($entry->body); |
1865 | - $entry->revision = true; |
1866 | - |
1867 | - //Fields to set if it's a new item. |
1868 | - if (!isset($entry->nid)) { |
1869 | - //print "Planet item " . $entry->title . "<br />"; |
1870 | - $entry->type = 'planet'; |
1871 | - |
1872 | - $options = variable_get('node_options_planet', array()); |
1873 | - |
1874 | - $entry->uid = $feed->uid; |
1875 | - $entry->status = 1; |
1876 | - $entry->moderate = 0; |
1877 | - $entry->promote = in_array('promote', $options) ? 1 : 0; |
1878 | - $entry->sticky = in_array('sticky', $options) ? 1 : 0; |
1879 | - $entry->comment = in_array('comment', $options) ? 2 : 0; |
1880 | - $entry->format = variable_get('planet_filter_formats', 1); |
1881 | - $entry->created = strtotime($date); |
1882 | - $entry->revision = true; |
1883 | - |
1884 | - $terms = module_invoke('taxonomy', 'node_get_terms', $edit->nid, 'tid'); |
1885 | - foreach ($terms as $tid => $term) { |
1886 | - if ($term->tid) { |
1887 | - $edit->taxonomy[] = $term->tid; |
1888 | - } |
1889 | - } |
1890 | - //print '<pre>'. print_r($entry, 1) .'</pre>'; |
1891 | - node_save($entry); |
1892 | - db_query('INSERT INTO {planet_items} (fid, nid, guid, link, created) VALUES(%d, %d, "%s", "%s", UNIX_TIMESTAMP(NOW()))', $feed->fid, $entry->nid, $guid, $link); |
1893 | - watchdog('planet', 'Adding '. $title); |
1894 | - drupal_set_message('Adding '. $title); |
1895 | - } |
1896 | - } |
1897 | - |
1898 | - return $items_added; |
1899 | -} |
1900 | - |
1901 | - |
1902 | -/** |
1903 | - * Private function; parses given XML data and returns array |
1904 | - */ |
1905 | -function planet_parse_xml(&$data) { |
1906 | - global $xml_tree, $xml_paths, $xml_path_cur; |
1907 | - $xml_tree = array(); |
1908 | - $xml_paths[] = &$xml_tree; |
1909 | - $xml_path_cur = 0; |
1910 | - |
1911 | - $_start = microtime(); |
1912 | - |
1913 | - // Some feeds already use CDATA but in "wrong way": http://www.rocketboom.com/vlog/quicktime_daily_enclosures.xml (ie. <description> something <CDATA soemthing else></description> |
1914 | - $data = trim(str_replace(array('<![CDATA[', ']]>'), '', $data)); |
1915 | - |
1916 | - // Add CDATA around content which may contain (x)html data, and is not contained in CDATA yet |
1917 | - $src = array( |
1918 | - '%(<(link|content|content:encoded|description|title|summary|info|tagline|copyright|source|itunes:summary|media:text|text)(?>[^<]*(?<!/)>)(?!<!\[CDATA\[))(.*)(</\2>)%sUS', |
1919 | - '%24:(\d\d:\d\d)%' // workaround buggy hour format in feeds |
1920 | - /*'%(<(\w+)(?>[^<]*type=")(?:text/html|application/xhtml\+xml|html|xhtml")(?>[^<]*(?<!/)>)(?!<!\[CDATA\[))(.*)(</\2>)%sUS'*/ |
1921 | - ); |
1922 | - $dst = array( |
1923 | - '$1<![CDATA[$3]]>$4', |
1924 | - '00:$1' |
1925 | - ); |
1926 | - $data = preg_replace($src, $dst, $data); |
1927 | - |
1928 | - // parse the data: |
1929 | - $xml_parser = drupal_xml_parser_create($data); |
1930 | - if ($xml_parser == NULL) { |
1931 | - return $xml_tree; |
1932 | - } |
1933 | - |
1934 | - xml_set_element_handler($xml_parser, 'planet_element_start', 'planet_element_end'); |
1935 | - xml_set_character_data_handler($xml_parser, 'planet_element_data'); |
1936 | - xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 1); |
1937 | - xml_parser_set_option($xml_parser, XML_OPTION_SKIP_WHITE, 1); |
1938 | - if (!xml_parse($xml_parser, $data, 1)) { |
1939 | - $xml_tree['parser_error'] = xml_error_string(xml_get_error_code($xml_parser)); |
1940 | - $xml_tree['parser_line'] = xml_get_current_line_number($xml_parser); |
1941 | - } |
1942 | - else { |
1943 | - unset($xml_tree['parser_error']); |
1944 | - unset($xml_tree['parser_line']); |
1945 | - } |
1946 | - xml_parser_free($xml_parser); |
1947 | - |
1948 | - $_end = microtime(); |
1949 | - |
1950 | - list($sec, $usec) = explode(' ', $_start); |
1951 | - $_start = $sec + $usec; |
1952 | - list($sec, $usec) = explode(' ', $_end); |
1953 | - $xml_tree['parser_time'] = ($sec + $usec) - $_start; |
1954 | - |
1955 | - return $xml_tree; |
1956 | -} |
1957 | - |
1958 | -/** |
1959 | - * Private call-back function used by the XML parser. |
1960 | - */ |
1961 | -function planet_element_start($parser, $name, $attributes) { |
1962 | - global $xml_tree, $xml_paths, $xml_path_cur; |
1963 | - |
1964 | - $temp = &$xml_paths[$xml_path_cur++]; |
1965 | - $temp[$name][] = $attributes; |
1966 | - $xml_paths[$xml_path_cur] = &$temp[$name][count($temp[$name])-1]; |
1967 | -} |
1968 | - |
1969 | -/** |
1970 | - * Private call-back function used by the XML parser. |
1971 | - */ |
1972 | -function planet_element_end($parser, $name) { |
1973 | - global $xml_tree, $xml_paths, $xml_path_cur; |
1974 | - |
1975 | - $temp = &$xml_paths[$xml_path_cur]; |
1976 | - array_pop($xml_paths); |
1977 | - $xml_path_cur--; |
1978 | - if (isset($temp['VALUE'])) { |
1979 | - $temp['VALUE'] = trim(planet_replace_entities($temp['VALUE'])); |
1980 | - } |
1981 | -} |
1982 | - |
1983 | -/** |
1984 | - * Private call-back function used by the XML parser. |
1985 | - */ |
1986 | -function planet_element_data($parser, $data) { |
1987 | - global $xml_tree, $xml_paths, $xml_path_cur; |
1988 | - |
1989 | - $temp = trim($data); |
1990 | - if (strlen($temp) > 0) { |
1991 | - $temp = &$xml_paths[$xml_path_cur]; |
1992 | - $temp['VALUE'] .= $data; |
1993 | - } |
1994 | -} |
1995 | - |
1996 | + * Page callback for 'planet' ("Planet"). |
1997 | + * @ingroup page |
1998 | + * @CRUD{node,R} |
1999 | + * @CRUD{variables,R} |
2000 | + */ |
2001 | function planet_page_last() { |
2002 | global $user; |
2003 | |
2004 | @@ -1351,6 +833,12 @@ |
2005 | print theme('page', $output); |
2006 | } |
2007 | |
2008 | +/** |
2009 | + * Page callback for 'planet/feed' ("Planet"). |
2010 | + * @ingroup page |
2011 | + * @CRUD{node,R} |
2012 | + * @CRUD{menu_links,R} |
2013 | + */ |
2014 | function planet_feed() { |
2015 | $result = db_query_range(db_rewrite_sql("SELECT n.nid, n.created FROM {node} n WHERE n.type = 'planet' AND n.status = 1 ORDER BY n.created DESC"), 0, 15); |
2016 | $title = db_fetch_array(db_query("SELECT link_title FROM {menu_links} WHERE link_path = 'planet'")); |
2017 | @@ -1363,12 +851,14 @@ |
2018 | while ($row = db_fetch_object($result)) { |
2019 | $items[] = $row->nid; |
2020 | } |
2021 | - |
2022 | + // generate RSS feed from $items set of nodes. |
2023 | node_feed($items, $channel); |
2024 | } |
2025 | |
2026 | /** |
2027 | - * Implementation of hook_user(). |
2028 | + * Implementation of hook_user() - React when operations are performed on user accounts. |
2029 | + * @ingroup system |
2030 | + * @CRUD{planet_feeds,R} |
2031 | */ |
2032 | function planet_user($type, &$edit, &$user) { |
2033 | if ($type == 'view' && user_access('edit own blog', $user)) { |
2034 | @@ -1390,9 +880,12 @@ |
2035 | |
2036 | /** |
2037 | * Menu callback; displays a Drupal page containing recent planet entries. |
2038 | + * |
2039 | + * @todo remove orphan function, or reuse it |
2040 | + * @ingroup page |
2041 | */ |
2042 | function planet_page($a = NULL, $b = NULL) { |
2043 | - |
2044 | + // @DIFFINFO I keep this for now to keep showing, in API doc, historic relation between called functions |
2045 | if (is_numeric($a)) { // $a is a user ID |
2046 | if ($b == 'feed') { |
2047 | return planet_feed_user($a); |
2048 | @@ -1409,6 +902,13 @@ |
2049 | } |
2050 | } |
2051 | |
2052 | +/** |
2053 | + * Page callback for 'planet/%' ("planet"). |
2054 | + * @ingroup page |
2055 | + * |
2056 | + * @CRUD{node,R} |
2057 | + * @CRUD{variables,R} |
2058 | + */ |
2059 | function planet_page_user($uid) { |
2060 | global $user; |
2061 | |
2062 | @@ -1441,15 +941,148 @@ |
2063 | } |
2064 | } |
2065 | |
2066 | +/** |
2067 | + * Implementation of hook_load - Load node-type-specific information. |
2068 | + * |
2069 | + * @param $node The node being loaded. |
2070 | + * @return An object containing properties of the node being loaded. |
2071 | + * @ingroup system |
2072 | + * @CRUD{planet_items,R} |
2073 | + */ |
2074 | function planet_load($node) { |
2075 | $additions = db_fetch_object(db_query('SELECT link, guid FROM {planet_items} WHERE nid = %d', $node->nid)); |
2076 | return $additions; |
2077 | } |
2078 | |
2079 | -function planet_form(&$node, &$param) { |
2080 | +/** |
2081 | + * Implementation of hook_form - Display a node editing form. |
2082 | + * |
2083 | + * @param &$node The node being added or edited. |
2084 | + * @param $form_state The form state array (unused). |
2085 | + * @return An array containing the form elements to be displayed in the node edit form. |
2086 | + * @ingroup planet_node |
2087 | + */ |
2088 | +function planet_form(&$node, &$form_state) { |
2089 | $form = array(); |
2090 | $form['title'] = array('#type' => 'textfield', '#title' => 'Title', '#value' => $node->title, '#size' => 30, '#maxlength' => 80); |
2091 | $form['body'] = array('#type' => 'textarea', '#title' => 'Body', '#value' => $node->body); |
2092 | return $form; |
2093 | } |
2094 | |
2095 | +/** |
2096 | + * Turn each feed item into a node. |
2097 | + * |
2098 | + * @param $process_feed |
2099 | + * Feed node object |
2100 | + * @param $feed |
2101 | + * SimplePie feed object instaniated. |
2102 | + */ |
2103 | +function planet_item_feed_parse($process_feed, $feed) { |
2104 | + // loop through all of the items in the feed, faster than foreach |
2105 | + $max = $feed->get_item_quantity(); |
2106 | + $count = 0; |
2107 | + module_load_include('inc', 'node', 'node.pages'); |
2108 | + module_load_include('inc', 'node', 'content_types'); |
2109 | + $node = node_get_types('type', 'feed_item'); |
2110 | + |
2111 | + for ($i = 0; $i < $max; $i++) { |
2112 | + $item = $feed->get_item($i); |
2113 | + |
2114 | + // we don't use $item->get_id(true) from SimplePie because it is slightly buggy |
2115 | + // and requires a lot of overhead to compute each time (since it uses a gigantic array structure) |
2116 | + // instead we opt for a much lighter weight comparison of just the title and body, eliminating the |
2117 | + // possibility of any date changes or other tiny changes causing duplicate nodes that otherwise |
2118 | + // appear to be the same |
2119 | + // that is why the body and title processing appears out here, so we can check for duplicates |
2120 | + // it is fast enough to not make much of a difference otherwise |
2121 | + $body = $item->get_content(); |
2122 | + // this strips out any tags that may appear as <b> in the title, and makes sure " -> " for display |
2123 | + $title = strip_tags(decode_entities($item->get_title())); |
2124 | + |
2125 | + // some feeds don't provide titles so we construct one with the first 72 characters of the body |
2126 | + if (!$title) { |
2127 | + // remove any HTML or line breaks so these don't appear in the title |
2128 | + $title = trim(str_replace(array("\n", "\r"), ' ', strip_tags($body))); |
2129 | + $title = trim(substr($title, 0, 72)); |
2130 | + $lastchar = substr($title, -1, 1); |
2131 | + // check to see if the last character in the title is a non-alphanumeric character, except for ? or ! |
2132 | + // if it is strip it off so you don't get strange looking titles |
2133 | + if (preg_match('/[^0-9A-Za-z\!\?]/', $lastchar)) { |
2134 | + $title = substr($title, 0, -1); |
2135 | + } |
2136 | + // ? and ! are ok to end a title with since they make sense |
2137 | + if ($lastchar != '!' and $lastchar != '?') { |
2138 | + $title .= '...'; |
2139 | + } |
2140 | + } |
2141 | + |
2142 | + // unique id for each feed item, try and use item permalink, otherwise use feed permalink |
2143 | + if (!$link = $item->get_permalink()) { |
2144 | + $link = $feed->get_permalink(); |
2145 | + } |
2146 | + // we don't need serialize() since we already have strings |
2147 | + $iid = md5($title . $link); |
2148 | + $guid = md5("$title - . " . $process_feed->fid); |
2149 | + // make sure we don't already have this feed item |
2150 | + $duplicate = db_result(db_query("SELECT COUNT(iid) FROM {planet_items} WHERE iid = '%s'", $iid)); |
2151 | + |
2152 | + if (!$duplicate) { |
2153 | + |
2154 | + |
2155 | + $entry = NULL; |
2156 | + if ($guid && strlen($guid) > 0) { |
2157 | + $entry = db_fetch_object(db_query("SELECT nid FROM {planet_items} WHERE guid = '%s' AND fid = %d", $guid, $process_feed->fid)); |
2158 | + } |
2159 | + else if ($link && $link != $feed->link && $link != $feed->url) { |
2160 | + $entry = db_fetch_object(db_query("SELECT nid FROM {planet_items} WHERE guid = '%s' AND fid = %d", $link, $process_feed->fid)); |
2161 | + } |
2162 | + else { |
2163 | + $entry = db_fetch_object(db_query("SELECT ai.nid AS nid FROM {node} n, {planet_items} ai WHERE ai.fid = %d AND ai.nid = n.nid AND n.title = '%s'", $process_feed->fid, $title)); |
2164 | + } |
2165 | + |
2166 | + $link = $item->get_permalink(); |
2167 | + // this is node created date format for Drupal |
2168 | + $date = $item->get_date('Y-m-d H:i:s O'); |
2169 | + |
2170 | + $entry->changed = $date; |
2171 | + $entry->title = $title; |
2172 | + $entry->body = $body; |
2173 | + $entry->body = planet_convert_relative_urls($body, $link); |
2174 | + $entry->teaser = node_teaser($entry->body); |
2175 | + $entry->revision = true; |
2176 | + |
2177 | + if (!isset($entry->nid)) { |
2178 | + //print "Planet item " . $entry->title . "<br />"; |
2179 | + $entry->type = 'planet'; |
2180 | + |
2181 | + $options = variable_get('node_options_planet', array()); |
2182 | + |
2183 | + $entry->uid = $process_feed->uid; |
2184 | + $entry->status = 1; |
2185 | + $entry->moderate = 0; |
2186 | + $entry->promote = in_array('promote', $options) ? 1 : 0; |
2187 | + $entry->sticky = in_array('sticky', $options) ? 1 : 0; |
2188 | + $entry->comment = in_array('comment', $options) ? 2 : 0; |
2189 | + $entry->format = variable_get('planet_filter_formats', 1); |
2190 | + $entry->created = $date; |
2191 | + $entry->revision = true; |
2192 | + |
2193 | + } |
2194 | + |
2195 | + node_save($entry); |
2196 | + $item_record = array('fid' => $process_feed->fid, |
2197 | + 'nid' => $entry->nid, |
2198 | + 'iid' => $iid, |
2199 | + 'guid'=> $guid, |
2200 | + 'link' =>$link, |
2201 | + 'created' => time()); |
2202 | + drupal_write_record('planet_items', $item_record); |
2203 | + watchdog('planet', 'Adding '. $title); |
2204 | + drupal_set_message('Adding '. $title); |
2205 | + } |
2206 | + |
2207 | + // we unset $item each time to prevent any pass by reference memory leaks that PHP encounters with objects in foreach loops |
2208 | + unset($item); |
2209 | + } |
2210 | + |
2211 | +} |
there are some change i think you forgot to merge, in planet_user_feeds.