Parent Directory
|
Revision Log
|
Revision Graph
Removed error output print statement.
| 1 | <?php |
| 2 | // $Id: feedmanager.module,v 1.46 2007/01/01 14:32:23 budda Exp $ |
| 3 | |
| 4 | /** |
| 5 | * Feed Manager |
| 6 | * @description: Provides a standard interface to manage RSS feeds |
| 7 | * |
| 8 | * @author: Mike Carter <mike @ www.ixis.co.uk/contact> |
| 9 | * |
| 10 | * @todo: link feedmanager_opml() sources page |
| 11 | */ |
| 12 | |
| 13 | /** |
| 14 | * Implementation of hook_help(). |
| 15 | */ |
| 16 | function feedmanager_help($section) { |
| 17 | switch ($section) { |
| 18 | case 'admin/help#aggregator': |
| 19 | $output = '<p>'. t('The news aggregator is a powerful on-site RSS syndicator/news reader that can gather fresh content from news sites and weblogs around the web.') .'</p>'; |
| 20 | $output .= '<p>'. t('Users can view the latest news chronologically in the <a href="%aggregator">main news aggregator display</a> or by <a href="%aggregator-sources">source</a>. Administrators can add, edit and delete feeds and choose how often to check for newly updated news for each individual feed. Administrators can also tag individual feeds with categories, offering selective grouping of some feeds into separate displays. Listings of the latest news for individual sources or categorized sources can be enabled as blocks for display in the sidebar through the <a href="%admin-block">block administration page</a>. The news aggregator requires cron to check for the latest news from the sites to which you have subscribed. Drupal also provides a <a href="%aggregator-opml">machine-readable OPML file</a> of all of your subscribed feeds.', array('%aggregator' => url('aggregator'), '%aggregator-sources' => url('aggregator/sources'), '%admin-block' => url('admin/block'), '%aggregator-opml' => url('aggregator/opml'))) .'</p>'; |
| 21 | $output .= t('<p>You can</p> |
| 22 | <ul> |
| 23 | <li>administer your list of news feeds <a href="%admin-aggregator">administer >> aggregator</a>.</li> |
| 24 | <li>add a new feed <a href="%admin-aggregator-add-feed">administer >> aggregator >> add feed</a>.</li> |
| 25 | <li>add a new category <a href="%admin-aggregator-add-category">administer >> aggregator >> add category</a>.</li> |
| 26 | <li>configure global settings for the news aggregator <a href="%admin-settings-aggregator">administer >> settings >> aggregator</a>.</li> |
| 27 | <li>control access to the aggregator module through access permissions <a href="%admin-access">administer >> access control >> permissions</a>.</li> |
| 28 | <li>set permissions to access new feeds for user roles such as anonymous users at <a href="%admin-access">administer >> access control</a>.</li> |
| 29 | <li>view the <a href="%aggregator">aggregator page</a>.</li> |
| 30 | </ul> |
| 31 | ', array('%admin-aggregator' => url('admin/aggregator'), '%admin-aggregator-add-feed' => url('admin/aggregator/add/feed'), '%admin-aggregator-add-category' => url('admin/aggregator/add/category'), '%admin-settings-aggregator' => url('admin/settings/feedmanager'), '%admin-access' => url('admin/access'), '%aggregator' => url('aggregator'))); |
| 32 | $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%aggregator">Aggregator page</a>.', array('%aggregator' => 'http://drupal.org/handbook/modules/aggregator/')) .'</p>'; |
| 33 | return $output; |
| 34 | case 'admin/modules#description': |
| 35 | return t('Provides a management interface for RSS/RDF/Atom feeds.'); |
| 36 | case 'admin/aggregator': |
| 37 | return t('<p>Thousands of sites (particularly news sites and weblogs) publish their latest headlines and/or stories in a machine-readable format so that other sites can easily link to them. This content is usually in the form of an <a href="http://blogs.law.harvard.edu/tech/rss">RSS</a> feed (which is an XML-based syndication standard). To display the feed or category in a block you must decide how many items to show by editing the feed or block and turning on the <a href="%block">feed\'s block</a>.</p>', array('%block' => url('admin/block'))); |
| 38 | case 'admin/aggregator/add/feed': |
| 39 | return t('<p>Add a site that has an RSS/RDF/Atom feed. The URL is the full path to the feed file. For the feed to update automatically you must run "cron.php" on a regular basis. If you already have a feed with the URL you are planning to use, the system will not accept another feed with the same URL.</p>'); |
| 40 | case 'admin/aggregator/add/category': |
| 41 | return t('<p>Categories provide a way to group items from different news feeds together. Each news category has its own feed page and block. For example, you could tag various sport-related feeds as belonging to a category called <em>Sports</em>. News items can be added to a category automatically by setting a feed to automatically place its item into that category, or by using the categorize items link in any listing of news items.</p>'); |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | |
| 46 | /** |
| 47 | * Implementation of hook_menu(). |
| 48 | */ |
| 49 | function feedmanager_menu($may_cache) { |
| 50 | $items = array(); |
| 51 | $edit = user_access('administer news feeds'); |
| 52 | $view = user_access('access news feeds'); |
| 53 | |
| 54 | if ($may_cache) { |
| 55 | $items[] = array('path' => 'aggregator/sources', |
| 56 | 'title' => t('sources'), |
| 57 | 'callback' => 'feedmanager_page_sources', |
| 58 | 'access' => $view); |
| 59 | $items[] = array('path' => 'admin/aggregator', |
| 60 | 'title' => t('aggregator'), |
| 61 | 'callback' => 'feedmanager_admin_overview', |
| 62 | 'access' => $edit); |
| 63 | $items[] = array('path' => 'admin/aggregator/add/feed', |
| 64 | 'title' => t('add feed'), |
| 65 | 'callback' => 'feedmanager_form_feed', |
| 66 | 'access' => $edit, |
| 67 | 'type' => MENU_LOCAL_TASK); |
| 68 | $items[] = array('path' => 'admin/aggregator/remove', |
| 69 | 'title' => t('remove items'), |
| 70 | 'callback' => 'feedmanager_remove_confirm', |
| 71 | 'access' => $edit, |
| 72 | 'type' => MENU_CALLBACK); |
| 73 | $items[] = array('path' => 'admin/aggregator/update', |
| 74 | 'title' => t('update items'), |
| 75 | 'callback' => 'feedmanager_admin_refresh_feed', |
| 76 | 'access' => $edit, |
| 77 | 'type' => MENU_CALLBACK); |
| 78 | $items[] = array('path' => 'admin/aggregator/list', |
| 79 | 'title' => t('list'), |
| 80 | 'type' => MENU_DEFAULT_LOCAL_TASK, |
| 81 | 'weight' => -10); |
| 82 | $items[] = array('path' => 'admin/settings/feedmanager', |
| 83 | 'title' => t('FeedManager'), |
| 84 | 'callback' => 'feedmanager_admin_settings_dispatcher', |
| 85 | 'access' => user_access('administer site configuration'), |
| 86 | 'type' => MENU_NORMAL_ITEM, |
| 87 | ); |
| 88 | } |
| 89 | else if (arg(1) == 'aggregator' && is_numeric(arg(4))) { |
| 90 | if (arg(3) == 'feed') { |
| 91 | $feed = feedmanager_get_feed(arg(4)); |
| 92 | if ($feed) { |
| 93 | $items[] = array('path' => 'admin/aggregator/edit/feed/'. $feed['fid'], |
| 94 | 'title' => t('edit feed'), |
| 95 | 'callback' => 'feedmanager_form_feed', |
| 96 | 'callback arguments' => array($feed), |
| 97 | 'access' => $edit, |
| 98 | 'type' => MENU_CALLBACK); |
| 99 | } |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | return $items; |
| 104 | } |
| 105 | |
| 106 | |
| 107 | /** |
| 108 | * Admin settings |
| 109 | */ |
| 110 | function feedmanager_admin_settings_dispatcher() { |
| 111 | if (strncmp(VERSION, '4', 1) == 0) { |
| 112 | $form_html = feedmanager_admin_settings(); |
| 113 | } else { |
| 114 | $form_html = drupal_get_form('feedmanager_admin_settings'); |
| 115 | } |
| 116 | |
| 117 | return $form_html; |
| 118 | } |
| 119 | |
| 120 | function feedmanager_admin_settings() { |
| 121 | $form['feedprocessing'] = array( |
| 122 | '#type' => 'fieldset', |
| 123 | '#title' => t('Feed Parsing') |
| 124 | ); |
| 125 | |
| 126 | $feedmanager_count['0'] = t('None'); |
| 127 | $feedmanager_count += drupal_map_assoc(array(1, 2, 3, 4, 5, 10, 15, 20, 25, 50, 100)); |
| 128 | $feedmanager_count['9999999'] = t('All'); |
| 129 | $form['feedprocessing']['feedmanager_cron_count'] = array ( |
| 130 | '#type' => 'select', |
| 131 | '#title' => t('Update count'), |
| 132 | '#default_value' => variable_get('feedmanager_cron_count', 5), |
| 133 | '#options' => $feedmanager_count, |
| 134 | '#description' => t('Select how many feeds can be updated in one cron run.') |
| 135 | ); |
| 136 | |
| 137 | $form['feedprocessing']['feedmanager_timeout'] = array( |
| 138 | '#type' => 'select', |
| 139 | '#options' => drupal_map_assoc(array(10,20,30,40,50,60)), |
| 140 | '#title' => t('Feed Timeout'), |
| 141 | '#description' => t('Define how many seconds to wait before giving up when attempting to load a feed.'), |
| 142 | '#default_value' => variable_get('feedmanager_timeout', 20) |
| 143 | ); |
| 144 | |
| 145 | $vocabularies = taxonomy_get_vocabularies(); |
| 146 | $vocabularies_list = array(); |
| 147 | foreach($vocabularies as $vocabulary) { |
| 148 | $vocabularies_list[$vocabulary->vid] = check_plain($vocabulary->name); |
| 149 | } |
| 150 | |
| 151 | $form['feedprocessing']['feedmanager_vocabulary'] = array( |
| 152 | '#type' => 'select', |
| 153 | '#multiple' => FALSE, |
| 154 | '#title' => t('Vocabulary'), |
| 155 | '#default_value' => variable_get('feedmanager_vocabulary', FALSE), |
| 156 | '#description' => t('Used to store taxonomy terms in for feeds.'), |
| 157 | '#options' => $vocabularies_list, |
| 158 | ); |
| 159 | |
| 160 | $form['feedsources'] = array( |
| 161 | '#type' => 'fieldset', |
| 162 | '#title' => t('Feed Sources'), |
| 163 | '#description' => t('Control the layout of feed items on the %url pages.', array('%url' => l('aggregator sources', 'aggregator/sources'))) |
| 164 | ); |
| 165 | |
| 166 | $items = array(0 => t('none')) + drupal_map_assoc(array(3, 5, 10, 15, 20, 25), '_feedmanager_items'); |
| 167 | $form['feedsources']['aggregator_summary_items'] = array( |
| 168 | '#type' => 'select', |
| 169 | '#title' => t('Items shown in sources and categories pages') , |
| 170 | '#default_value' => variable_get('aggregator_summary_items', 3), |
| 171 | '#options' => $items, |
| 172 | '#description' => t('The number of items which will be shown with each feed or category in the feed and category summary pages.') |
| 173 | ); |
| 174 | |
| 175 | $form['fullstory'] = array( |
| 176 | '#type' => 'fieldset', |
| 177 | '#title' => t('Full Story links') |
| 178 | ); |
| 179 | |
| 180 | $form['fullstory']['feedmanager_showfullstory'] = array( |
| 181 | '#type' => 'checkbox', |
| 182 | '#title' => t('Show a full story link'), |
| 183 | '#description' => t('When available in the feed item, a link back to the original post will be displayed at the end of a node.'), |
| 184 | '#default_value' => variable_get('feedmanager_showfullstory', true), |
| 185 | ); |
| 186 | |
| 187 | $options = array( |
| 188 | 'default' => t('Default (no target attribute)'), |
| 189 | '_top' => t('Open link in window root'), |
| 190 | '_blank' => t('Open link in new window'), |
| 191 | ); |
| 192 | $form['fullstory']['feedmanager_target'] = array( |
| 193 | '#type' => 'radios', |
| 194 | '#title' => t('Link Target'), |
| 195 | '#default_value' => variable_get('feedmanager_target', 'default'), |
| 196 | '#options' => $options, |
| 197 | ); |
| 198 | $form['fullstory']['feedmanager_rel'] = array( |
| 199 | '#type' => 'checkbox', |
| 200 | '#return_value' => 'nofollow', |
| 201 | '#prefix' => '<div class="form-item"><label>Nofollow Value: </label>', |
| 202 | '#suffix' => '</div>', |
| 203 | '#title' => t('Add rel="nofollow" Attribute'), |
| 204 | '#description' => t('The <a href="http://en.wikipedia.org/wiki/Nofollow#rel.3D.22nofollow.22">rel="nofollow" attribute</a> prevents some search engines from spidering entered links.'), |
| 205 | '#default_value' => variable_get('feedmanager_rel', false), |
| 206 | ); |
| 207 | |
| 208 | if (strncmp(VERSION, '4', 1) == 0) { |
| 209 | return system_settings_form('feedmanager_admin_settings', $form); |
| 210 | } else { |
| 211 | return system_settings_form($form); |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | |
| 216 | /** |
| 217 | * Implementation of hook_perm(). |
| 218 | */ |
| 219 | function feedmanager_perm() { |
| 220 | return array('administer news feeds', 'access news feeds'); |
| 221 | } |
| 222 | |
| 223 | |
| 224 | /** |
| 225 | * Generate a form to add/edit feed sources. |
| 226 | */ |
| 227 | function feedmanager_form_feed($edit = array()) { |
| 228 | $op = arg(2); |
| 229 | |
| 230 | switch($op) { |
| 231 | case 'edit': |
| 232 | $form['title'] = array('#type' => 'textfield', |
| 233 | '#title' => t('Title'), |
| 234 | '#default_value' => $edit['title'], |
| 235 | '#maxlength' => 64, |
| 236 | '#description' => t('The name of the feed; typically the name of the web site you syndicate content from.'), |
| 237 | '#required' => TRUE, |
| 238 | ); |
| 239 | |
| 240 | $form['url'] = array('#type' => 'textfield', |
| 241 | '#title' => t('URL'), |
| 242 | '#default_value' => $edit['url'], |
| 243 | '#size' => 100, |
| 244 | '#description' => t('The fully-qualified URL of the feed.'), |
| 245 | '#attributes' => array('readonly' => 'readonly') |
| 246 | ); |
| 247 | |
| 248 | $form['description'] = array('#type' => 'textarea', |
| 249 | '#title' => t('Description'), |
| 250 | '#default_value' => $edit['description'], |
| 251 | '#rows' => 5, |
| 252 | '#description' => t('As supplied by the RSS feed.'), |
| 253 | ); |
| 254 | |
| 255 | $period = array(0 => t('never')) + drupal_map_assoc(array(300, 900, 1800, 3600, 7200, 10800, 21600, 32400, 43200, 64800, 86400, 172800, 259200, 604800, 1209600, 2419200), 'format_interval'); |
| 256 | if ($edit['refresh'] == -1) { |
| 257 | $edit['refresh'] = 3600; |
| 258 | } |
| 259 | $form['refresh'] = array('#type' => 'select', |
| 260 | '#title' => t('Update interval'), |
| 261 | '#default_value' => $edit['refresh'], |
| 262 | '#options' => $period, |
| 263 | '#description' => t('The refresh interval indicating how often you want to update this feed. Requires %cron.', array('%cron' => l('crontab', 'admin/settings') )), |
| 264 | ); |
| 265 | |
| 266 | $period = array(0 => t('never')) + drupal_map_assoc(array(3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800), 'format_interval'); |
| 267 | $form['expires'] = array( |
| 268 | '#type' => 'select', |
| 269 | '#title' => t('Discard news items older than'), |
| 270 | '#default_value' => $edit['expires'], |
| 271 | '#options' => $period, |
| 272 | '#description' => t('Older news items will be automatically discarded. Requires %cron.', array('%cron' => l('crontab', 'admin/settings') )) |
| 273 | ); |
| 274 | |
| 275 | // Handling of categories: |
| 276 | $form += feedmanager_freetag($edit); |
| 277 | |
| 278 | $form['advanced'] = array( |
| 279 | '#type' => 'fieldset', |
| 280 | '#title' => t('Advanced'), |
| 281 | '#description' => t('These settings are specific to the type of feed processor you have selected.'), |
| 282 | '#collapsible' => TRUE, |
| 283 | '#collapsed' => FALSE, |
| 284 | '#tree' => TRUE |
| 285 | ); |
| 286 | |
| 287 | $form['advanced']['update_items'] = array( |
| 288 | '#type' => 'checkbox', |
| 289 | '#title' => t('Update news items'), |
| 290 | '#description' => t('If the same news item is found in the feed on the next update the contents will overwrite the previous version stored here.'), |
| 291 | '#default_value' => $edit['update_items'] ? $edit['update_items'] : FALSE, |
| 292 | ); |
| 293 | |
| 294 | $form['advanced']['processor'] = array('#type' => 'value', '#value' => $edit['processor']); |
| 295 | |
| 296 | $form['advanced']['stripads'] = array( |
| 297 | '#type' => 'checkbox', |
| 298 | '#title' => t('Strip Adverts'), |
| 299 | '#description' => t('Strip out ads from certain advertisers, namely Pheedo, Google AdSense, and certain types of Doubleclick ads.'), |
| 300 | '#default_value' => $edit['stripads'] ? $edit['stripads'] : TRUE, |
| 301 | ); |
| 302 | break; |
| 303 | |
| 304 | case 'add': |
| 305 | $form['url'] = array('#type' => 'textfield', |
| 306 | '#title' => t('URL'), |
| 307 | '#default_value' => $edit['url'], |
| 308 | '#size' => 100, |
| 309 | '#description' => t('The fully-qualified URL of the feed.'), |
| 310 | '#required' => TRUE, |
| 311 | ); |
| 312 | |
| 313 | // We set feeds to not update until they have been edited |
| 314 | $form['refresh'] = array('#type' => 'value', '#value' => -1); |
| 315 | |
| 316 | $processors = module_invoke_all('feedapi', $feed, 'processor_name'); |
| 317 | $form['processor'] = array( |
| 318 | '#type' => 'select', |
| 319 | '#title' => t('Processor'), |
| 320 | '#description' => t("Each feed can have it's items generated by a different processor. This allows you to generate different types of content per feed - such as nodes or traditional aggregator items. Re-edit the feed to see processor specific settings."), |
| 321 | '#options' => $processors, |
| 322 | '#default_value' => $edit['processor'], |
| 323 | '#size' => 1, |
| 324 | '#required' => TRUE, |
| 325 | ); |
| 326 | break; |
| 327 | } |
| 328 | |
| 329 | $form['submit'] = array( |
| 330 | '#type' => 'submit', |
| 331 | '#value' => t('Submit'), |
| 332 | '#weight' => 35, |
| 333 | ); |
| 334 | |
| 335 | if ($edit['fid']) { |
| 336 | $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'), '#weight' => 19); |
| 337 | $form['fid'] = array('#type' => 'value', '#value' => $edit['fid']); |
| 338 | } |
| 339 | |
| 340 | return drupal_get_form('feedmanager_form_feed_'.$op, $form); |
| 341 | } |
| 342 | |
| 343 | |
| 344 | /** |
| 345 | * Provides a freetagging form element. |
| 346 | * |
| 347 | * Code lifted from the taxonomy.module hook_form_alter() |
| 348 | */ |
| 349 | function feedmanager_freetag(&$feed) { |
| 350 | $form = array(); |
| 351 | $terms = $feed['taxonomy']; |
| 352 | |
| 353 | // Find the vocabulary (if any) to use |
| 354 | $vid = variable_get('feedmanager_vocabulary', FALSE); |
| 355 | |
| 356 | if($vid && $vocabulary = taxonomy_get_vocabulary($vid)) { |
| 357 | if($terms) { |
| 358 | $typed_terms = array(); |
| 359 | foreach ($terms as $term) { |
| 360 | // Extract terms belonging to the vocabulary in question. |
| 361 | if ($term->vid == $vocabulary->vid) { |
| 362 | |
| 363 | // Commas and quotes in terms are special cases, so encode 'em. |
| 364 | if (preg_match('/,/', $term->name) || preg_match('/"/', $term->name)) { |
| 365 | $term->name = '"'.preg_replace('/"/', '""', $term->name).'"'; |
| 366 | } |
| 367 | |
| 368 | $typed_terms[] = $term->name; |
| 369 | } |
| 370 | } |
| 371 | |
| 372 | $typed_string = implode(', ', $typed_terms) . (array_key_exists('tags', $terms) ? $terms['tags'][$vocabulary->vid] : NULL); |
| 373 | } |
| 374 | |
| 375 | if ($vocabulary->help) { |
| 376 | $help = $vocabulary->help; |
| 377 | } |
| 378 | else { |
| 379 | $help = t('A comma-separated list of terms describing this content. Example: funny, bungee jumping, "Company, Inc.".'); |
| 380 | } |
| 381 | |
| 382 | $form['taxonomy']['tags'] = array('#type' => 'textfield', |
| 383 | '#title' => $vocabulary->name, |
| 384 | '#description' => $help, |
| 385 | '#required' => $vocabulary->required, |
| 386 | '#default_value' => $typed_string, |
| 387 | '#autocomplete_path' => 'taxonomy/autocomplete/'. $vocabulary->vid, |
| 388 | '#weight' => $vocabulary->weight, |
| 389 | '#maxlength' => 255, |
| 390 | ); |
| 391 | } else { |
| 392 | $form['taxonomy_help'] = array( |
| 393 | '#value' => t('You can automatically use tags defined in this feed to categories your nodes as they are created. To do this you need to %url to the %type content-type.', array('%url' => l(t('create a vocabulary and assign it'), 'admin/taxonomy'), '%type' => theme('placeholder', 'aggregator-item'))) |
| 394 | ); |
| 395 | } |
| 396 | |
| 397 | return $form; |
| 398 | } |
| 399 | |
| 400 | /** |
| 401 | * Validate feedmanager_form_feed form submissions. |
| 402 | */ |
| 403 | function feedmanager_form_feed_add_validate($form_id, $form_values) { |
| 404 | // Check for duplicate feed urls |
| 405 | if (isset($form_values['fid'])) { |
| 406 | $result = db_query("SELECT title, url FROM {aggregator_feed} WHERE (title = '%s' OR url='%s') AND fid != %d", $form_values['title'], $form_values['url'], $form_values['fid']); |
| 407 | } |
| 408 | else { |
| 409 | $result = db_query("SELECT title, url FROM {aggregator_feed} WHERE title = '%s' OR url='%s'", $form_values['title'], $form_values['url']); |
| 410 | } |
| 411 | while ($feed = db_fetch_array($result)) { |
| 412 | if (strcasecmp($feed['url'], $form_values['url']) == 0) { |
| 413 | form_set_error('url', t('A feed with this URL %url already exists. Please enter a unique URL.', array('%url' => $form_values['url']))); |
| 414 | } |
| 415 | } |
| 416 | } |
| 417 | |
| 418 | |
| 419 | function feedmanager_form_feed_add_submit($form_id, $form_values) { |
| 420 | $feed = feedmanager_query_feed($form_values['url']); |
| 421 | |
| 422 | // Create a Feed title if none is available |
| 423 | if(!$feed_title = $feed->get_feed_title()) { |
| 424 | $urlinfo = parse_url($form_values['url']); |
| 425 | $feed_title = $urlinfo['host']; |
| 426 | } |
| 427 | |
| 428 | $form_values['title'] = $feed_title; |
| 429 | $form_values['description'] = $feed->get_feed_description(); |
| 430 | $form_values['advanced']['processor'] = $form_values['processor']; |
| 431 | feedmanager_save_feed($form_values); |
| 432 | |
| 433 | // Allow editing of further details |
| 434 | drupal_goto('admin/aggregator/edit/feed/'.$form_values['fid']); |
| 435 | } |
| 436 | |
| 437 | |
| 438 | /** |
| 439 | * Process feedmanager_form_feed form submissions. |
| 440 | * @todo Add delete confirmation dialog. |
| 441 | */ |
| 442 | function feedmanager_form_feed_edit_submit($form_id, $form_values) { |
| 443 | if ($_POST['op'] == t('Delete')) { |
| 444 | $title = $form_values['title']; |
| 445 | // Unset the title: |
| 446 | unset($form_values['title']); |
| 447 | } |
| 448 | |
| 449 | feedmanager_save_feed($form_values); |
| 450 | |
| 451 | menu_rebuild(); |
| 452 | if (isset($form_values['fid'])) { |
| 453 | if (isset($form_values['title'])) { |
| 454 | drupal_set_message(t('The feed %feed has been updated.', array('%feed' => theme('placeholder', $form_values['title'])))); |
| 455 | if (arg(0) == 'admin') { |
| 456 | return 'admin/aggregator/'; |
| 457 | } |
| 458 | else { |
| 459 | return 'aggregator/sources/'. $form_values['fid']; |
| 460 | } |
| 461 | } |
| 462 | else { |
| 463 | watchdog('aggregator', t('Feed %feed deleted.', array('%feed' => theme('placeholder', $title)))); |
| 464 | drupal_set_message(t('The feed %feed has been deleted.', array('%feed' => theme('placeholder', $title)))); |
| 465 | |
| 466 | module_invoke($form_values['processor'], 'delete_feed', $form_values); |
| 467 | |
| 468 | if (arg(0) == 'admin') { |
| 469 | return 'admin/aggregator/'; |
| 470 | } |
| 471 | else { |
| 472 | return 'aggregator/sources/'; |
| 473 | } |
| 474 | } |
| 475 | } |
| 476 | else { |
| 477 | watchdog('aggregator', t('Feed %feed added.', array('%feed' => theme('placeholder', $form_values['title']))), WATCHDOG_NOTICE, l(t('view'), 'admin/aggregator')); |
| 478 | drupal_set_message(t('The feed %feed has been added.', array('%feed' => theme('placeholder', $form_values['title'])))); |
| 479 | } |
| 480 | } |
| 481 | |
| 482 | |
| 483 | |
| 484 | /** |
| 485 | * Add/edit/delete an aggregator feed. |
| 486 | */ |
| 487 | function feedmanager_save_feed(&$edit) { |
| 488 | if ($edit['fid']) { |
| 489 | // An existing feed is being modified, delete the existing category listings. |
| 490 | db_query('DELETE FROM {aggregator_category_feed} WHERE fid = %d', $edit['fid']); |
| 491 | } |
| 492 | $data = serialize($edit['advanced']); |
| 493 | |
| 494 | if ($edit['fid'] && $edit['title']) { |
| 495 | db_query("UPDATE {aggregator_feed} SET title = '%s', url = '%s', refresh = %d, description = '%s', expires = %d, data = '%s' WHERE fid = %d", $edit['title'], $edit['url'], $edit['refresh'], $edit['description'], $edit['expires'], $data, $edit['fid']); |
| 496 | } |
| 497 | else if ($edit['fid']) { |
| 498 | |
| 499 | // Delete all feed category items |
| 500 | $result = db_query('SELECT iid FROM {aggregator_item} WHERE fid = %d', $edit['fid']); |
| 501 | while ($item = db_fetch_object($result)) { |
| 502 | $items[] = "iid = $item->iid"; |
| 503 | } |
| 504 | if ($items) { |
| 505 | db_query('DELETE FROM {aggregator_category_item} WHERE '. implode(' OR ', $items)); |
| 506 | } |
| 507 | |
| 508 | db_query('DELETE FROM {aggregator_feed} WHERE fid = %d', $edit['fid']); |
| 509 | db_query('DELETE FROM {aggregator_item} WHERE fid = %d', $edit['fid']); |
| 510 | } |
| 511 | else if ($edit['title']) { |
| 512 | // A single unique id for bundles and feeds, to use in blocks. |
| 513 | $edit['fid'] = db_next_id('{aggregator_feed}_fid'); |
| 514 | db_query("INSERT INTO {aggregator_feed} (fid, title, url, refresh, description, expires, data) VALUES (%d, '%s', '%s', %d, '%s', %d, '%s')", $edit['fid'], $edit['title'], $edit['url'], $edit['refresh'], $edit['description'], $edit['expires'], $data); |
| 515 | } |
| 516 | |
| 517 | if ($edit['title']) { |
| 518 | // The feed is being saved, save the categories as well. |
| 519 | |
| 520 | $vid = variable_get('feedmanager_vocabulary', FALSE); |
| 521 | if ($edit['tags'] && $vid) { |
| 522 | |
| 523 | $vid_value = $edit['tags']; |
| 524 | |
| 525 | // This regexp allows the following types of user input: |
| 526 | // this, "somecmpany, llc", "and ""this"" w,o.rks", foo bar |
| 527 | $regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x'; |
| 528 | preg_match_all($regexp, $vid_value, $matches); |
| 529 | $typed_terms = array_unique($matches[1]); |
| 530 | |
| 531 | $inserted = array(); |
| 532 | foreach ($typed_terms as $typed_term) { |
| 533 | // If a user has escaped a term (to demonstrate that it is a group, |
| 534 | // or includes a comma or quote character), we remove the escape |
| 535 | // formatting so to save the term into the DB as the user intends. |
| 536 | $typed_term = str_replace('""', '"', preg_replace('/^"(.*)"$/', '\1', $typed_term)); |
| 537 | $typed_term = trim($typed_term); |
| 538 | if ($typed_term == "") { continue; } |
| 539 | |
| 540 | // See if the term exists in the chosen vocabulary |
| 541 | // and return the tid, otherwise, add a new record. |
| 542 | $possibilities = taxonomy_get_term_by_name($typed_term); |
| 543 | $typed_term_tid = NULL; // tid match if any. |
| 544 | foreach ($possibilities as $possibility) { |
| 545 | if ($possibility->vid == $vid) { |
| 546 | $typed_term_tid = $possibility->tid; |
| 547 | } |
| 548 | } |
| 549 | |
| 550 | // if the term does not already exist, add it to the feedmanager vocabulary |
| 551 | if (!$typed_term_tid) { |
| 552 | $term = array('vid' => $vid, 'name' => $typed_term); |
| 553 | $status = taxonomy_save_term($term); |
| 554 | $typed_term_tid = $term['tid']; |
| 555 | } |
| 556 | |
| 557 | // defend against duplicate, different cased tags |
| 558 | if (!isset($inserted[$typed_term_tid])) { |
| 559 | db_query('INSERT INTO {aggregator_category_feed} (fid, cid) VALUES (%d, %d)', $edit['fid'], $typed_term_tid); |
| 560 | $inserted[$typed_term_tid] = TRUE; |
| 561 | } |
| 562 | } |
| 563 | } |
| 564 | } else { |
| 565 | drupal_set_message(t('Unable to add the feed due to incomplete details being provided.')); |
| 566 | } |
| 567 | } |
| 568 | |
| 569 | function feedmanager_remove_confirm() { |
| 570 | $feed_id = arg(3); |
| 571 | $edit = $_POST['edit']; |
| 572 | |
| 573 | $feed = feedmanager_get_feed($feed_id); |
| 574 | |
| 575 | $form['feed_id'] = array('#type' => 'hidden', '#value' => $feed_id); |
| 576 | $form['operation'] = array('#type' => 'hidden', '#value' => 'remove'); |
| 577 | |
| 578 | $counter = module_invoke($feed['processor'], 'feedapi', $feed, 'item_count'); |
| 579 | return confirm_form('feedmanager_remove_confirm', $form, |
| 580 | t('Are you sure you want to delete all %count?', array('%count' => format_plural($counter['item_count'], '1 item', '%count items'))), |
| 581 | 'admin/node', t('This action cannot be undone.'), |
| 582 | t('Delete all'), t('Cancel')); |
| 583 | } |
| 584 | |
| 585 | |
| 586 | /** |
| 587 | * Removes all items (and associated data) from a feed |
| 588 | */ |
| 589 | function feedmanager_remove_confirm_submit($form_id, $form_values) { |
| 590 | if ($form_values['confirm']) { |
| 591 | $feed = feedmanager_get_feed($form_values['feed_id']); |
| 592 | module_invoke_all('feedapi', $feed, 'remove'); |
| 593 | |
| 594 | db_query("UPDATE {aggregator_feed} SET checked = 0, etag = '', modified = 0 WHERE fid = %d", $feed['fid']); |
| 595 | drupal_set_message(t('The news items from %site have been removed.', array('%site' => theme('placeholder', $feed['title'])))); |
| 596 | } |
| 597 | return 'admin/aggregator'; |
| 598 | } |
| 599 | |
| 600 | |
| 601 | function feedmanager_query_feed($url) { |
| 602 | include_once 'simplepie.inc'; |
| 603 | |
| 604 | $pie_feed = new SimplePie(); |
| 605 | |
| 606 | $pie_feed->enable_caching(false); |
| 607 | $pie_feed->timeout = variable_get('feedmanager_timeout', 20); |
| 608 | $pie_feed->cache_max_minutes(240); // configuration option |
| 609 | $pie_feed->feed_url($url); // process the feed URL |
| 610 | $pie_feed->init(); // process the feed |
| 611 | $pie_feed->handle_content_type(); // set content type and character encoding |
| 612 | |
| 613 | // Check if there's any problems loading the feed |
| 614 | $feed_error = $pie_feed->error; |
| 615 | if (isset($feed_error)) { |
| 616 | watchdog('aggregator', t('The RSS-feed from %site seems to be broken, due to "%error".', array('%site' => theme('placeholder', $pie_feed->get_feed_title()), '%error' => $feed_error)), WATCHDOG_WARNING); |
| 617 | drupal_set_message(t('The RSS-feed from %site seems to be broken, because of error "%error".', array('%site' => theme('placeholder', $pie_feed->get_feed_title()), '%error' => $feed_error))); |
| 618 | return FALSE; |
| 619 | } |
| 620 | |
| 621 | return $pie_feed; |
| 622 | } |
| 623 | |
| 624 | |
| 625 | /** |
| 626 | * Checks a news feed for new items. |
| 627 | */ |
| 628 | function feedmanager_refresh($feed) { |
| 629 | // Load The Feed |
| 630 | $pie_feed = feedmanager_query_feed($feed['url']); |
| 631 | if(!$pie_feed) return FALSE; |
| 632 | |
| 633 | if (!$feed['data'] = $pie_feed->data) return FALSE; |
| 634 | |
| 635 | $pie_feed->strip_ads($feed['stripads']); // configuration option |
| 636 | |
| 637 | $feed['items'] = $pie_feed->get_items(); |
| 638 | |
| 639 | $feed_links = $pie_feed->get_feed_links(); |
| 640 | $feed['link'] = array(); |
| 641 | $feed['link']['self'] = $feed_links['self'] ? $feed_links['self'][0] : $feed['url']; |
| 642 | $feed['link']['alternate'] = $feed_links['alternate'] ? $feed_links['alternate'][0] : $feed['link']['self']; |
| 643 | |
| 644 | $feed['description'] = $pie_feed->get_feed_description(); |
| 645 | |
| 646 | $feed['expires'] = 0; |
| 647 | $feed['modified'] = 0; |
| 648 | $feed['etag'] = 0; |
| 649 | // end simplepie specific |
| 650 | |
| 651 | if ($pie_feed->get_image_link() && $pie_feed->get_image_url()) { // FIXME: use theme_image |
| 652 | $feed['image_html'] = '<a href="'. $pie_feed->get_image_link() .'" class="feed-image"><img src="'. check_url($pie_feed->get_image_url()) .'" alt="'. check_plain($pie_feed->get_image_title()) .'" /></a>'; |
| 653 | } |
| 654 | else { |
| 655 | $feed['image_html'] = NULL; |
| 656 | } |
| 657 | |
| 658 | // update feed information in database |
| 659 | db_query("UPDATE {aggregator_feed} SET url = '%s', checked = %d, link = '%s', description = '%s', image = '%s', etag = '%s', modified = %d WHERE fid = %d", $feed['link']['self'], time(), $feed['link']['alternate'], $feed['description'], $feed['image_html'], $feed['etag'], $feed['modified'], $feed['fid']); |
| 660 | |
| 661 | // process all feed items |
| 662 | $processed_items = 0; |
| 663 | foreach($feed['items'] as $item) { |
| 664 | $item->data['iid'] = db_next_id('{feedparser}_iid'); |
| 665 | $r = module_invoke_all('feedapi', $feed, 'item_save', $item); |
| 666 | |
| 667 | // if an item is saved, increase counter |
| 668 | if($r['item_save']) { |
| 669 | $processed_items++; |
| 670 | } |
| 671 | } |
| 672 | |
| 673 | // Log how many items were added and/or updated from a feed |
| 674 | if($processed_items > 0) { |
| 675 | watchdog('aggregator', t('There is %items new syndicated items from %site.', array('%items' => $processed_items, '%site' => theme('placeholder', $feed['title'])))); |
| 676 | } |
| 677 | |
| 678 | // Clear out any old feed items |
| 679 | if($feed['expires'] != 0) { |
| 680 | module_invoke($feed['processor'], 'feedapi', $feed, 'expire_items'); |
| 681 | } |
| 682 | } |
| 683 | |
| 684 | |
| 685 | /** |
| 686 | * Gets information about a feed, either from the dbase or a temp cache |
| 687 | */ |
| 688 | function feedmanager_get_feed($fid) { |
| 689 | static $feeds; |
| 690 | |
| 691 | if(!$feeds[$fid] && $fid) { |
| 692 | $feed = db_fetch_array(db_query('SELECT * FROM {aggregator_feed} WHERE fid = %d', $fid)); |
| 693 | $data = unserialize($feed['data']); |
| 694 | $feed += (array)$data; |
| 695 | unset($feed['data']); |
| 696 | |
| 697 | $feed['taxonomy'] = array(); |
| 698 | |
| 699 | $terms = db_query('SELECT cid, fid FROM {aggregator_category_feed} WHERE fid = %d', $feed['fid']); |
| 700 | while ($term = db_fetch_object($terms)) { |
| 701 | if ($term->fid) $feed['taxonomy'][] = taxonomy_get_term($term->cid); |
| 702 | } |
| 703 | |
| 704 | $feeds[$fid] = $feed; |
| 705 | } |
| 706 | |
| 707 | return $feeds[$fid]; |
| 708 | } |
| 709 | |
| 710 | |
| 711 | function feedmanager_view() { |
| 712 | $output .= '<h3>'. t('Feed overview') .'</h3>'; |
| 713 | |
| 714 | $header = array(t('Title'), t('Items'), t('Last update'), t('Next update'), array('data' => t('Operations'), 'colspan' => '3')); |
| 715 | $rows = array(); |
| 716 | |
| 717 | $result = db_query('SELECT fid FROM {aggregator_feed} ORDER BY title'); |
| 718 | while ($feed = db_fetch_array($result)) { |
| 719 | $feed = feedmanager_get_feed($feed['fid']); |
| 720 | |
| 721 | $item_count = module_invoke($feed['processor'], 'feedapi', $feed, 'item_count'); |
| 722 | |
| 723 | // If A Feed Processor Implements Listing of Items Provide A Link |
| 724 | if(module_hook($feed['processor'], 'list_items')) { |
| 725 | $item_list_link = l($feed['title'], "aggregator/sources/{$feed['fid']}"); |
| 726 | } else { |
| 727 | $item_list_link = check_plain($feed['title']); |
| 728 | } |
| 729 | |
| 730 | $rows[] = array( |
| 731 | $item_list_link, |
| 732 | format_plural($item_count['item_count'], '1 item', '%count items'), |
| 733 | ($feed['checked'] ? t('%time ago', array('%time' => format_interval(time() - $feed['checked']))) : t('never')), |
| 734 | ($feed['checked'] ? t('%time left', array('%time' => format_interval($feed['checked'] + $feed['refresh'] - time()))) : t('never')), |
| 735 | l(t('edit'), "admin/aggregator/edit/feed/{$feed['fid']}"), |
| 736 | l(t('remove items'), "admin/aggregator/remove/{$feed['fid']}"), |
| 737 | l(t('update items'), "admin/aggregator/update/{$feed['fid']}")); |
| 738 | } |
| 739 | $output .= theme('table', $header, $rows); |
| 740 | |
| 741 | return $output; |
| 742 | } |
| 743 | |
| 744 | |
| 745 | /** |
| 746 | * Menu callback; removes all items from a feed, then redirects to the overview page. |
| 747 | */ |
| 748 | function feedmanager_admin_remove_feed($feed) { |
| 749 | feedmanager_remove(feedmanager_get_feed($feed)); |
| 750 | drupal_goto('admin/aggregator'); |
| 751 | } |
| 752 | |
| 753 | |
| 754 | /** |
| 755 | * Menu callback; refreshes a feed, then redirects to the overview page. |
| 756 | */ |
| 757 | function feedmanager_admin_refresh_feed($feed) { |
| 758 | feedmanager_refresh(feedmanager_get_feed($feed)); |
| 759 | drupal_goto('admin/aggregator'); |
| 760 | } |
| 761 | |
| 762 | |
| 763 | /** |
| 764 | * Menu callback; displays the aggregator administration page. |
| 765 | */ |
| 766 | function feedmanager_admin_overview() { |
| 767 | return feedmanager_view(); |
| 768 | } |
| 769 | |
| 770 | |
| 771 | /** |
| 772 | * Display items from a feed source |
| 773 | */ |
| 774 | function feedmanager_page_sources() { |
| 775 | $fid = arg(2); |
| 776 | |
| 777 | $output = '<div id="aggregator">'; |
| 778 | |
| 779 | // If A feed id is provided only show items from that feed |
| 780 | if(is_numeric($fid)) { |
| 781 | if($feed = feedmanager_get_feed($fid)) { |
| 782 | drupal_set_breadcrumb(array( l(t('Home'), NULL), l('Aggregator','aggregator/sources'), l($feed['title'], 'aggregator/sources/'.$feed['fid'] ) )); |
| 783 | $output .= theme('feedmanager_feed', $feed); |
| 784 | $output .= module_invoke($feed['processor'], 'list_items', $feed); |
| 785 | } |
| 786 | } else { |
| 787 | $result = db_query('SELECT fid FROM {aggregator_feed} ORDER BY title'); |
| 788 | while ($fid = db_fetch_array($result)) { |
| 789 | $feed = feedmanager_get_feed($fid['fid']); |
| 790 | if(module_hook($feed['processor'], 'list_items')) { |
| 791 | $output .= '<h2>'. check_plain($feed['title']) ."</h2>\n"; |
| 792 | $output .= module_invoke($feed['processor'], 'list_items', $feed, variable_get('aggregator_summary_items', 3)); |
| 793 | //add more link to full page |
| 794 | } |
| 795 | } |
| 796 | |
| 797 | // Add OPML XML link |
| 798 | } |
| 799 | |
| 800 | $output .= '</div>'; |
| 801 | |
| 802 | return $output; |
| 803 | } |
| 804 | |
| 805 | |
| 806 | /** |
| 807 | * Generates an OPML representation of all feeds. |
| 808 | */ |
| 809 | function feedmanager_opml() { |
| 810 | $result = db_query(db_rewrite_sql('SELECT title, url FROM {aggregator_feed} ORDER BY title ASC')); |
| 811 | |
| 812 | $output = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; |
| 813 | $output .= "<opml version=\"1.1\">\n"; |
| 814 | $output .= "<head>\n"; |
| 815 | $output .= '<title>'. variable_get('site_name', 'drupal') .' - '. variable_get('site_slogan', '') ."</title>\n"; |
| 816 | $output .= '<dateModified>'. gmdate('r') ."</dateModified>\n"; |
| 817 | $output .= "</head>\n"; |
| 818 | $output .= "<body>\n"; |
| 819 | |
| 820 | while ($feed = db_fetch_object($result)) { |
| 821 | $output .= '<outline text="'. check_plain($feed->title) .'" xmlUrl="'. check_plain($feed->url) .'" />'."\n"; |
| 822 | } |
| 823 | |
| 824 | $output .= "</body>\n"; |
| 825 | $output .= "</opml>\n"; |
| 826 | |
| 827 | drupal_set_header('Content-Type: text/xml; charset=utf-8'); |
| 828 | print $output; |
| 829 | } |
| 830 | |
| 831 | |
| 832 | /** |
| 833 | * Implementation of hook_cron(). |
| 834 | * |
| 835 | * Checks news feeds for updates once their refresh interval has elapsed. |
| 836 | */ |
| 837 | function feedmanager_cron() { |
| 838 | // check how many feeds we can update at a time |
| 839 | $limit = variable_get('feedmanager_cron_count', 5); |
| 840 | if (is_numeric($limit) && $limit > -1) { |
| 841 | $limit = 'LIMIT '. $limit; |
| 842 | } |
| 843 | else { |
| 844 | $limit = ''; |
| 845 | } |
| 846 | |
| 847 | $result = db_query('SELECT fid FROM {aggregator_feed} WHERE refresh != 0 AND checked + refresh < %d ORDER BY checked ASC '. $limit, time()); |
| 848 | while ($fid = db_fetch_array($result)) { |
| 849 | $feed = feedmanager_get_feed($fid['fid']); |
| 850 | feedmanager_refresh($feed); |
| 851 | } |
| 852 | } |
| 853 | |
| 854 | |
| 855 | /** |
| 856 | * Helper function for drupal_map_assoc. |
| 857 | */ |
| 858 | function _feedmanager_items($count) { |
| 859 | return format_plural($count, '1 item', '%count items'); |
| 860 | } |
| 861 | |
| 862 | |
| 863 | /** |
| 864 | * Safely render HTML content, as allowed. |
| 865 | */ |
| 866 | function feedmanager_filter_xss($value) { |
| 867 | return filter_xss($value, preg_split('/\s+|<|>/', variable_get("feedmanager_allowed_html_tags", '<a> <b> <br> <dd> <dl> <dt> <em> <i> <li> <ol> <p> <strong> <u> <ul>'), -1, PREG_SPLIT_NO_EMPTY)); |
| 868 | } |
| 869 | |
| 870 | |
| 871 | /** |
| 872 | * Private function; |
| 873 | * from: http://uk2.php.net/manual/en/function.html-entity-decode.php#51055 |
| 874 | * Used as callback function for preg_replace_all() to decode numeric entities to UTF-8 chars |
| 875 | * |
| 876 | * @param $ord Number |
| 877 | * @return UTF-8 string |
| 878 | */ |
| 879 | function _parse_num_entity($ord) { |
| 880 | $ord = $ord[1]; |
| 881 | if (preg_match('/^x([0-9a-f]+)$/i', $ord, $match)) { |
| 882 | $ord = hexdec($match[1]); |
| 883 | } |
| 884 | else { |
| 885 | $ord = intval($ord); |
| 886 | } |
| 887 | |
| 888 | $no_bytes = 0; |
| 889 | $byte = array(); |
| 890 | |
| 891 | if ($ord == 128) { |
| 892 | return chr(226).chr(130).chr(172); |
| 893 | } |
| 894 | else if($ord == 129) { |
| 895 | return chr(239).chr(191).chr(189); |
| 896 | } |
| 897 | else if($ord == 130) { |
| 898 | return chr(226).chr(128).chr(154); |
| 899 | } |
| 900 | else if($ord == 131) { |
| 901 | return chr(198).chr(146); |
| 902 | } |
| 903 | else if($ord == 132) { |
| 904 | return chr(226).chr(128).chr(158); |
| 905 | } |
| 906 | else if($ord == 133) { |
| 907 | return chr(226).chr(128).chr(166); |
| 908 | } |
| 909 | else if($ord == 134) { |
| 910 | return chr(226).chr(128).chr(160); |
| 911 | } |
| 912 | else if($ord == 135) { |
| 913 | return chr(226).chr(128).chr(161); |
| 914 | } |
| 915 | else if($ord == 136) { |
| 916 | return chr(203).chr(134); |
| 917 | } |
| 918 | else if($ord == 137) { |
| 919 | return chr(226).chr(128).chr(176); |
| 920 | } |
| 921 | else if($ord == 138) { |
| 922 | return chr(197).chr(160); |
| 923 | } |
| 924 | else if($ord == 139) { |
| 925 | return chr(226).chr(128).chr(185); |
| 926 | } |
| 927 | else if($ord == 140) { |
| 928 | return chr(197).chr(146); |
| 929 | } |
| 930 | else if($ord == 141) { |
| 931 | return chr(239).chr(191).chr(189); |
| 932 | } |
| 933 | else if($ord == 142) { |
| 934 | return chr(197).chr(189); |
| 935 | } |
| 936 | else if($ord == 143) { |
| 937 | return chr(239).chr(191).chr(189); |
| 938 | } |
| 939 | else if($ord == 144) { |
| 940 | return chr(239).chr(191).chr(189); |
| 941 | } |
| 942 | else if($ord == 145) { |
| 943 | return chr(226).chr(128).chr(152); |
| 944 | } |
| 945 | else if($ord == 146) { |
| 946 | return chr(226).chr(128).chr(153); |
| 947 | } |
| 948 | else if($ord == 147) { |
| 949 | return chr(226).chr(128).chr(156); |
| 950 | } |
| 951 | else if($ord == 148) { |
| 952 | return chr(226).chr(128).chr(157); |
| 953 | } |
| 954 | else if($ord == 149) { |
| 955 | return chr(226).chr(128).chr(162); |
| 956 | } |
| 957 | else if($ord == 150) { |
| 958 | return chr(226).chr(128).chr(147); |
| 959 | } |
| 960 | else if($ord == 151) { |
| 961 | return chr(226).chr(128).chr(148); |
| 962 | } |
| 963 | else if($ord == 152) { |
| 964 | return chr(203).chr(156); |
| 965 | } |
| 966 | else if($ord == 153) { |
| 967 | return chr(226).chr(132).chr(162); |
| 968 | } |
| 969 | else if($ord == 154) { |
| 970 | return chr(197).chr(161); |
| 971 | } |
| 972 | else if($ord == 155) { |
| 973 | return chr(226).chr(128).chr(186); |
| 974 | } |
| 975 | else if($ord == 156) { |
| 976 | return chr(197).chr(147); |
| 977 | } |
| 978 | else if($ord == 157) { |
| 979 | return chr(239).chr(191).chr(189); |
| 980 | } |
| 981 | else if($ord == 158) { |
| 982 | return chr(197).chr(190); |
| 983 | } |
| 984 | else if($ord == 159) { |
| 985 | return chr(197).chr(184); |
| 986 | } |
| 987 | else if($ord == 160) { |
| 988 | return chr(194).chr(160); |
| 989 | } |
| 990 | |
| 991 | if ($ord < 128) { |
| 992 | return chr($ord); |
| 993 | } |
| 994 | else if ($ord < 2048) { |
| 995 | $no_bytes = 2; |
| 996 | } |
| 997 | else if ($ord < 65536) { |
| 998 | $no_bytes = 3; |
| 999 | } |
| 1000 | else if ($ord < 1114112) { |
| 1001 | $no_bytes = 4; |
| 1002 | } |
| 1003 | else { |
| 1004 | return; |
| 1005 | } |
| 1006 | |
| 1007 | switch ($no_bytes) { |
| 1008 | case 2: |
| 1009 | $prefix = array(31, 192); |
| 1010 | break; |
| 1011 | |
| 1012 | case 3: |
| 1013 | $prefix = array(15, 224); |
| 1014 | break; |
| 1015 | |
| 1016 | case 4: |
| 1017 | $prefix = array(7, 240); |
| 1018 | break; |
| 1019 | } |
| 1020 | |
| 1021 | for ($i = 0; $i < $no_bytes; $i++) { |
| 1022 | $byte[$no_bytes - $i - 1] = (($ord & (63 * pow(2, 6 * $i))) / pow(2, 6 * $i)) & 63 | 128; |
| 1023 | } |
| 1024 | |
| 1025 | $byte[0] = ($byte[0] & $prefix[0]) | $prefix[1]; |
| 1026 | |
| 1027 | $ret = ''; |
| 1028 | for ($i = 0; $i < $no_bytes; $i++) { |
| 1029 | $ret .= chr($byte[$i]); |
| 1030 | } |
| 1031 | |
| 1032 | return $ret; |
| 1033 | } |
| 1034 | |
| 1035 | /** |
| 1036 | * Private function; Convert named entities to UTF-8 characters |
| 1037 | * from: http://pl2.php.net/manual/en/function.html-entity-decode.php#51722 |
| 1038 | */ |
| 1039 | function _parse_name_entities(&$data) { |
| 1040 | static $ttr; |
| 1041 | if (!$ttr) { |
| 1042 | $trans_tbl = get_html_translation_table(HTML_ENTITIES); |
| 1043 | foreach ($trans_tbl as $k => $v) { |
| 1044 | $ttr[$v] = utf8_encode($k); |
| 1045 | } |
| 1046 | $ttr['''] = "'"; |
| 1047 | } |
| 1048 | return strtr($data, $ttr); |
| 1049 | } |
| 1050 | |