[177a560] | 1 | <?php |
---|
| 2 | |
---|
| 3 | /** |
---|
| 4 | * @file |
---|
| 5 | * Class definition of FeedsNodeProcessor. |
---|
| 6 | */ |
---|
| 7 | |
---|
| 8 | // Create or delete FEEDS_NODE_BATCH_SIZE at a time. |
---|
| 9 | define('FEEDS_NODE_BATCH_SIZE', 50); |
---|
| 10 | |
---|
| 11 | // Deprecated. Use FEEDS_SKIPE_EXISTING, FEEDS_REPLACE_EXISTNG, |
---|
| 12 | // FEEDS_UPDATE_EXISTING instead. |
---|
| 13 | define('FEEDS_NODE_SKIP_EXISTING', 0); |
---|
| 14 | define('FEEDS_NODE_REPLACE_EXISTING', 1); |
---|
| 15 | define('FEEDS_NODE_UPDATE_EXISTING', 2); |
---|
| 16 | |
---|
| 17 | /** |
---|
| 18 | * Creates nodes from feed items. |
---|
| 19 | */ |
---|
| 20 | class FeedsNodeProcessor extends FeedsProcessor { |
---|
| 21 | |
---|
| 22 | /** |
---|
| 23 | * Implementation of FeedsProcessor::process(). |
---|
| 24 | */ |
---|
| 25 | public function process(FeedsImportBatch $batch, FeedsSource $source) { |
---|
| 26 | |
---|
| 27 | // Keep track of processed items in this pass, set total number of items. |
---|
| 28 | $processed = 0; |
---|
| 29 | $batch_size = variable_get('feeds_node_batch_size', FEEDS_NODE_BATCH_SIZE); |
---|
| 30 | if (!$batch->getTotal(FEEDS_PROCESSING)) { |
---|
| 31 | $batch->setTotal(FEEDS_PROCESSING, count($batch->items)); |
---|
| 32 | } |
---|
| 33 | |
---|
| 34 | while ($item = $batch->shiftItem()) { |
---|
| 35 | |
---|
| 36 | // Create/update if item does not exist or update existing is enabled. |
---|
| 37 | if (!($nid = $this->existingItemId($batch, $source)) || ($this->config['update_existing'] != FEEDS_SKIP_EXISTING)) { |
---|
| 38 | // Only proceed if item has actually changed. |
---|
| 39 | $hash = $this->hash($item); |
---|
| 40 | if (!empty($nid) && $hash == $this->getHash($nid)) { |
---|
| 41 | continue; |
---|
| 42 | } |
---|
| 43 | |
---|
| 44 | $node = $this->buildNode($nid, $source->feed_nid); |
---|
| 45 | $node->feeds_node_item->hash = $hash; |
---|
| 46 | |
---|
| 47 | // Map and save node. If errors occur don't stop but report them. |
---|
| 48 | try { |
---|
| 49 | $this->map($batch, $node); |
---|
| 50 | if ($this->config['authorize']) { |
---|
| 51 | if (empty($node->nid)) { |
---|
| 52 | $op = 'create'; |
---|
| 53 | } |
---|
| 54 | else { |
---|
| 55 | $op = 'update'; |
---|
| 56 | } |
---|
| 57 | $account = user_load($node->uid); |
---|
| 58 | if (!node_access($op, $node, $account)) { |
---|
| 59 | throw new Exception('User ' . $account->uid . ' not authorized to ' . $op . ' content type ' . $node->type); |
---|
| 60 | } |
---|
| 61 | } |
---|
| 62 | node_save($node); |
---|
| 63 | if (!empty($nid)) { |
---|
| 64 | $batch->updated++; |
---|
| 65 | } |
---|
| 66 | else { |
---|
| 67 | $batch->created++; |
---|
| 68 | } |
---|
| 69 | } |
---|
| 70 | catch (Exception $e) { |
---|
| 71 | drupal_set_message($e->getMessage(), 'warning'); |
---|
| 72 | watchdog('feeds', $e->getMessage(), array(), WATCHDOG_WARNING); |
---|
| 73 | } |
---|
| 74 | } |
---|
| 75 | |
---|
| 76 | $processed++; |
---|
| 77 | if ($processed >= $batch_size) { |
---|
| 78 | $total = $batch->getTotal(FEEDS_PROCESSING); |
---|
| 79 | $batch->setProgress(FEEDS_PROCESSING, $total - count($batch->items)); |
---|
| 80 | return; |
---|
| 81 | } |
---|
| 82 | } |
---|
| 83 | |
---|
| 84 | // Set messages. |
---|
| 85 | if ($batch->created) { |
---|
| 86 | drupal_set_message(format_plural($batch->created, 'Created @number @type node.', 'Created @number @type nodes.', array('@number' => $batch->created, '@type' => node_get_types('name', $this->config['content_type'])))); |
---|
| 87 | } |
---|
| 88 | if ($batch->updated) { |
---|
| 89 | drupal_set_message(format_plural($batch->updated, 'Updated @number @type node.', 'Updated @number @type nodes.', array('@number' => $batch->updated, '@type' => node_get_types('name', $this->config['content_type'])))); |
---|
| 90 | } |
---|
| 91 | if (!$batch->created && !$batch->updated) { |
---|
| 92 | drupal_set_message(t('There is no new content.')); |
---|
| 93 | } |
---|
| 94 | $batch->setProgress(FEEDS_PROCESSING, FEEDS_BATCH_COMPLETE); |
---|
| 95 | } |
---|
| 96 | |
---|
| 97 | /** |
---|
| 98 | * Implementation of FeedsProcessor::clear(). |
---|
| 99 | */ |
---|
| 100 | public function clear(FeedsBatch $batch, FeedsSource $source) { |
---|
| 101 | if (!$batch->getTotal(FEEDS_CLEARING)) { |
---|
| 102 | $total = db_result(db_query("SELECT COUNT(nid) FROM {feeds_node_item} WHERE id = '%s' AND feed_nid = %d", $source->id, $source->feed_nid)); |
---|
| 103 | $batch->setTotal(FEEDS_CLEARING, $total); |
---|
| 104 | } |
---|
| 105 | $result = db_query_range("SELECT nid FROM {feeds_node_item} WHERE id = '%s' AND feed_nid = %d", $source->id, $source->feed_nid, 0, variable_get('feeds_node_batch_size', FEEDS_NODE_BATCH_SIZE)); |
---|
| 106 | while ($node = db_fetch_object($result)) { |
---|
| 107 | _feeds_node_delete($node->nid); |
---|
| 108 | $batch->deleted++; |
---|
| 109 | } |
---|
| 110 | if (db_result(db_query_range("SELECT nid FROM {feeds_node_item} WHERE id = '%s' AND feed_nid = %d", $source->id, $source->feed_nid, 0, 1))) { |
---|
| 111 | $batch->setProgress(FEEDS_CLEARING, $batch->deleted); |
---|
| 112 | return; |
---|
| 113 | } |
---|
| 114 | |
---|
| 115 | // Set message. |
---|
| 116 | drupal_get_messages('status'); |
---|
| 117 | if ($batch->deleted) { |
---|
| 118 | drupal_set_message(format_plural($batch->deleted, 'Deleted @number node.', 'Deleted @number nodes.', array('@number' => $batch->deleted))); |
---|
| 119 | } |
---|
| 120 | else { |
---|
| 121 | drupal_set_message(t('There is no content to be deleted.')); |
---|
| 122 | } |
---|
| 123 | $batch->setProgress(FEEDS_CLEARING, FEEDS_BATCH_COMPLETE); |
---|
| 124 | } |
---|
| 125 | |
---|
| 126 | /** |
---|
| 127 | * Implement expire(). |
---|
| 128 | */ |
---|
| 129 | public function expire($time = NULL) { |
---|
| 130 | if ($time === NULL) { |
---|
| 131 | $time = $this->expiryTime(); |
---|
| 132 | } |
---|
| 133 | if ($time == FEEDS_EXPIRE_NEVER) { |
---|
| 134 | return; |
---|
| 135 | } |
---|
| 136 | $result = db_query_range("SELECT n.nid FROM {node} n JOIN {feeds_node_item} fni ON n.nid = fni.nid WHERE fni.id = '%s' AND n.created < %d", $this->id, FEEDS_REQUEST_TIME - $time, 0, variable_get('feeds_node_batch_size', FEEDS_NODE_BATCH_SIZE)); |
---|
| 137 | while ($node = db_fetch_object($result)) { |
---|
| 138 | _feeds_node_delete($node->nid); |
---|
| 139 | } |
---|
| 140 | if (db_result(db_query_range("SELECT n.nid FROM {node} n JOIN {feeds_node_item} fni ON n.nid = fni.nid WHERE fni.id = '%s' AND n.created < %d", $this->id, FEEDS_REQUEST_TIME - $time, 0, 1))) { |
---|
| 141 | return FEEDS_BATCH_ACTIVE; |
---|
| 142 | } |
---|
| 143 | return FEEDS_BATCH_COMPLETE; |
---|
| 144 | } |
---|
| 145 | |
---|
| 146 | /** |
---|
| 147 | * Return expiry time. |
---|
| 148 | */ |
---|
| 149 | public function expiryTime() { |
---|
| 150 | return $this->config['expire']; |
---|
| 151 | } |
---|
| 152 | |
---|
| 153 | /** |
---|
| 154 | * Override parent::configDefaults(). |
---|
| 155 | */ |
---|
| 156 | public function configDefaults() { |
---|
| 157 | $types = node_get_types('names'); |
---|
| 158 | $type = isset($types['story']) ? 'story' : key($types); |
---|
| 159 | return array( |
---|
| 160 | 'content_type' => $type, |
---|
| 161 | 'input_format' => FILTER_FORMAT_DEFAULT, |
---|
| 162 | 'update_existing' => FEEDS_SKIP_EXISTING, |
---|
| 163 | 'expire' => FEEDS_EXPIRE_NEVER, |
---|
| 164 | 'mappings' => array(), |
---|
| 165 | 'author' => 0, |
---|
| 166 | 'authorize' => 0, |
---|
| 167 | ); |
---|
| 168 | } |
---|
| 169 | |
---|
| 170 | /** |
---|
| 171 | * Override parent::configForm(). |
---|
| 172 | */ |
---|
| 173 | public function configForm(&$form_state) { |
---|
| 174 | $types = node_get_types('names'); |
---|
| 175 | $form = array(); |
---|
| 176 | $form['content_type'] = array( |
---|
| 177 | '#type' => 'select', |
---|
| 178 | '#title' => t('Content type'), |
---|
| 179 | '#description' => t('Select the content type for the nodes to be created. <strong>Note:</strong> Users with "import !feed_id feeds" permissions will be able to <strong>import</strong> nodes of the content type selected here regardless of the node level permissions. Further, users with "clear !feed_id permissions" will be able to <strong>delete</strong> imported nodes regardless of their node level permissions.', array('!feed_id' => $this->id)), |
---|
| 180 | '#options' => $types, |
---|
| 181 | '#default_value' => $this->config['content_type'], |
---|
| 182 | ); |
---|
| 183 | $format_options = array(FILTER_FORMAT_DEFAULT => t('Default format')); |
---|
| 184 | $formats = filter_formats(); |
---|
| 185 | foreach ($formats as $format) { |
---|
| 186 | $format_options[$format->format] = $format->name; |
---|
| 187 | } |
---|
| 188 | $form['input_format'] = array( |
---|
| 189 | '#type' => 'select', |
---|
| 190 | '#title' => t('Input format'), |
---|
| 191 | '#description' => t('Select the input format for the body field of the nodes to be created.'), |
---|
| 192 | '#options' => $format_options, |
---|
| 193 | '#default_value' => $this->config['input_format'], |
---|
| 194 | ); |
---|
| 195 | $author = user_load(array('uid' => $this->config['author'])); |
---|
| 196 | $form['author'] = array( |
---|
| 197 | '#type' => 'textfield', |
---|
| 198 | '#title' => t('Author'), |
---|
| 199 | '#description' => t('Select the author of the nodes to be created - leave empty to assign "anonymous".'), |
---|
| 200 | '#autocomplete_path' => 'user/autocomplete', |
---|
| 201 | '#default_value' => empty($author->name) ? 'anonymous' : check_plain($author->name), |
---|
| 202 | ); |
---|
| 203 | $form['authorize'] = array( |
---|
| 204 | '#type' => 'checkbox', |
---|
| 205 | '#title' => t('Authorize'), |
---|
| 206 | '#description' => t('Check that the author has permission to create the node.'), |
---|
| 207 | '#default_value' => $this->config['authorize'], |
---|
| 208 | ); |
---|
| 209 | $period = drupal_map_assoc(array(FEEDS_EXPIRE_NEVER, 3600, 10800, 21600, 43200, 86400, 259200, 604800, 604800 * 4, 604800 * 12, 604800 * 24, 31536000), 'feeds_format_expire'); |
---|
| 210 | $form['expire'] = array( |
---|
| 211 | '#type' => 'select', |
---|
| 212 | '#title' => t('Expire nodes'), |
---|
| 213 | '#options' => $period, |
---|
| 214 | '#description' => t('Select after how much time nodes should be deleted. The node\'s published date will be used for determining the node\'s age, see Mapping settings.'), |
---|
| 215 | '#default_value' => $this->config['expire'], |
---|
| 216 | ); |
---|
| 217 | $form['update_existing'] = array( |
---|
| 218 | '#type' => 'radios', |
---|
| 219 | '#title' => t('Update existing nodes'), |
---|
| 220 | '#description' => t('Select how existing nodes should be updated. Existing nodes will be determined using mappings that are a "unique target".'), |
---|
| 221 | '#options' => array( |
---|
| 222 | FEEDS_SKIP_EXISTING => 'Do not update existing nodes', |
---|
| 223 | FEEDS_REPLACE_EXISTING => 'Replace existing nodes', |
---|
| 224 | FEEDS_UPDATE_EXISTING => 'Update existing nodes (slower than replacing them)', |
---|
| 225 | ), |
---|
| 226 | '#default_value' => $this->config['update_existing'], |
---|
| 227 | ); |
---|
| 228 | return $form; |
---|
| 229 | } |
---|
| 230 | |
---|
| 231 | /** |
---|
| 232 | * Override parent::configFormValidate(). |
---|
| 233 | */ |
---|
| 234 | public function configFormValidate(&$values) { |
---|
| 235 | if ($author = user_load(array('name' => $values['author']))) { |
---|
| 236 | $values['author'] = $author->uid; |
---|
| 237 | } |
---|
| 238 | else { |
---|
| 239 | $values['author'] = 0; |
---|
| 240 | } |
---|
| 241 | } |
---|
| 242 | |
---|
| 243 | /** |
---|
| 244 | * Reschedule if expiry time changes. |
---|
| 245 | */ |
---|
| 246 | public function configFormSubmit(&$values) { |
---|
| 247 | if ($this->config['expire'] != $values['expire']) { |
---|
| 248 | feeds_reschedule($this->id); |
---|
| 249 | } |
---|
| 250 | parent::configFormSubmit($values); |
---|
| 251 | } |
---|
| 252 | |
---|
| 253 | /** |
---|
| 254 | * Override setTargetElement to operate on a target item that is a node. |
---|
| 255 | */ |
---|
| 256 | public function setTargetElement(&$target_node, $target_element, $value) { |
---|
| 257 | switch ($target_element) { |
---|
| 258 | case 'url': |
---|
| 259 | case 'guid': |
---|
| 260 | $target_node->feeds_node_item->$target_element = $value; |
---|
| 261 | break; |
---|
| 262 | case 'body': |
---|
| 263 | $target_node->teaser = node_teaser($value); |
---|
| 264 | $target_node->body = $value; |
---|
| 265 | break; |
---|
| 266 | case 'title': |
---|
| 267 | case 'status': |
---|
| 268 | case 'created': |
---|
| 269 | case 'nid': |
---|
| 270 | case 'uid': |
---|
| 271 | $target_node->$target_element = $value; |
---|
| 272 | break; |
---|
| 273 | case 'user_name': |
---|
| 274 | if ($user = user_load(array('name' => $value))) { |
---|
| 275 | $target_node->uid = $user->uid; |
---|
| 276 | } |
---|
| 277 | break; |
---|
| 278 | case 'user_mail': |
---|
| 279 | if ($user = user_load(array('mail' => $value))) { |
---|
| 280 | $target_node->uid = $user->uid; |
---|
| 281 | } |
---|
| 282 | break; |
---|
| 283 | } |
---|
| 284 | } |
---|
| 285 | |
---|
| 286 | /** |
---|
| 287 | * Return available mapping targets. |
---|
| 288 | */ |
---|
| 289 | public function getMappingTargets() { |
---|
| 290 | $targets = array( |
---|
| 291 | 'title' => array( |
---|
| 292 | 'name' => t('Title'), |
---|
| 293 | 'description' => t('The title of the node.'), |
---|
| 294 | ), |
---|
| 295 | ); |
---|
| 296 | // Include body field only if available. |
---|
| 297 | $type = node_get_types('type', $this->config['content_type']); |
---|
| 298 | if ($type->has_body) { |
---|
| 299 | // Using 'teaser' instead of 'body' forces entire content above the break. |
---|
| 300 | $targets['body'] = array( |
---|
| 301 | 'name' => t('Body'), |
---|
| 302 | 'description' => t('The body of the node. The teaser will be the same as the entire body.'), |
---|
| 303 | ); |
---|
| 304 | } |
---|
| 305 | $targets += array( |
---|
| 306 | 'nid' => array( |
---|
| 307 | 'name' => t('Node ID'), |
---|
| 308 | 'description' => t('The nid of the node. NOTE: use this feature with care, node ids are usually assigned by Drupal.'), |
---|
| 309 | 'optional_unique' => TRUE, |
---|
| 310 | ), |
---|
| 311 | 'uid' => array( |
---|
| 312 | 'name' => t('User ID'), |
---|
| 313 | 'description' => t('The Drupal user ID of the node author.'), |
---|
| 314 | ), |
---|
| 315 | 'user_name' => array( |
---|
| 316 | 'name' => t('Username'), |
---|
| 317 | 'description' => t('The Drupal username of the node author.'), |
---|
| 318 | ), |
---|
| 319 | 'user_mail' => array( |
---|
| 320 | 'name' => t('User email'), |
---|
| 321 | 'description' => t('The email address of the node author.'), |
---|
| 322 | ), |
---|
| 323 | 'status' => array( |
---|
| 324 | 'name' => t('Published status'), |
---|
| 325 | 'description' => t('Whether a node is published or not. 1 stands for published, 0 for not published.'), |
---|
| 326 | ), |
---|
| 327 | 'created' => array( |
---|
| 328 | 'name' => t('Published date'), |
---|
| 329 | 'description' => t('The UNIX time when a node has been published.'), |
---|
| 330 | ), |
---|
| 331 | 'url' => array( |
---|
| 332 | 'name' => t('URL'), |
---|
| 333 | 'description' => t('The external URL of the node. E. g. the feed item URL in the case of a syndication feed. May be unique.'), |
---|
| 334 | 'optional_unique' => TRUE, |
---|
| 335 | ), |
---|
| 336 | 'guid' => array( |
---|
| 337 | 'name' => t('GUID'), |
---|
| 338 | 'description' => t('The external GUID of the node. E. g. the feed item GUID in the case of a syndication feed. May be unique.'), |
---|
| 339 | 'optional_unique' => TRUE, |
---|
| 340 | ), |
---|
| 341 | ); |
---|
| 342 | |
---|
| 343 | // Let other modules expose mapping targets. |
---|
| 344 | self::loadMappers(); |
---|
| 345 | drupal_alter('feeds_node_processor_targets', $targets, $this->config['content_type']); |
---|
| 346 | |
---|
| 347 | return $targets; |
---|
| 348 | } |
---|
| 349 | |
---|
| 350 | /** |
---|
| 351 | * Get nid of an existing feed item node if available. |
---|
| 352 | */ |
---|
| 353 | protected function existingItemId(FeedsImportBatch $batch, FeedsSource $source) { |
---|
| 354 | |
---|
| 355 | // Iterate through all unique targets and test whether they do already |
---|
| 356 | // exist in the database. |
---|
| 357 | foreach ($this->uniqueTargets($batch) as $target => $value) { |
---|
| 358 | switch ($target) { |
---|
| 359 | case 'nid': |
---|
| 360 | $nid = db_result(db_query("SELECT nid FROM {node} WHERE nid = %d", $value)); |
---|
| 361 | break; |
---|
| 362 | case 'url': |
---|
| 363 | $nid = db_result(db_query("SELECT nid FROM {feeds_node_item} WHERE feed_nid = %d AND id = '%s' AND url = '%s'", $source->feed_nid, $source->id, $value)); |
---|
| 364 | break; |
---|
| 365 | case 'guid': |
---|
| 366 | $nid = db_result(db_query("SELECT nid FROM {feeds_node_item} WHERE feed_nid = %d AND id = '%s' AND guid = '%s'", $source->feed_nid, $source->id, $value)); |
---|
| 367 | break; |
---|
| 368 | } |
---|
| 369 | if ($nid) { |
---|
| 370 | // Return with the first nid found. |
---|
| 371 | return $nid; |
---|
| 372 | } |
---|
| 373 | } |
---|
| 374 | return 0; |
---|
| 375 | } |
---|
| 376 | |
---|
| 377 | /** |
---|
| 378 | * Creates a new node object in memory and returns it. |
---|
| 379 | */ |
---|
| 380 | protected function buildNode($nid, $feed_nid) { |
---|
| 381 | $populate = FALSE; |
---|
| 382 | |
---|
| 383 | if (empty($nid)) { |
---|
| 384 | $node = new stdClass(); |
---|
| 385 | $node->created = FEEDS_REQUEST_TIME; |
---|
| 386 | $populate = TRUE; |
---|
| 387 | } |
---|
| 388 | else { |
---|
| 389 | if ($this->config['update_existing'] == FEEDS_UPDATE_EXISTING) { |
---|
| 390 | $node = node_load($nid, NULL, TRUE); |
---|
| 391 | } |
---|
| 392 | else { |
---|
| 393 | $node = db_fetch_object(db_query("SELECT created, nid, vid, status FROM {node} WHERE nid = %d", $nid)); |
---|
| 394 | $populate = TRUE; |
---|
| 395 | } |
---|
| 396 | } |
---|
| 397 | if ($populate) { |
---|
| 398 | $node->type = $this->config['content_type']; |
---|
| 399 | $node->changed = FEEDS_REQUEST_TIME; |
---|
| 400 | $node->format = $this->config['input_format']; |
---|
| 401 | $node->feeds_node_item = new stdClass(); |
---|
| 402 | $node->feeds_node_item->id = $this->id; |
---|
| 403 | $node->feeds_node_item->imported = FEEDS_REQUEST_TIME; |
---|
| 404 | $node->feeds_node_item->feed_nid = $feed_nid; |
---|
| 405 | $node->feeds_node_item->url = ''; |
---|
| 406 | $node->feeds_node_item->guid = ''; |
---|
| 407 | } |
---|
| 408 | |
---|
| 409 | static $included; |
---|
| 410 | if (!$included) { |
---|
| 411 | module_load_include('inc', 'node', 'node.pages'); |
---|
| 412 | $included = TRUE; |
---|
| 413 | } |
---|
| 414 | node_object_prepare($node); |
---|
| 415 | |
---|
| 416 | // Populate properties that are set by node_object_prepare(). |
---|
| 417 | $node->log = 'Created/updated by FeedsNodeProcessor'; |
---|
| 418 | if ($populate) { |
---|
| 419 | $node->uid = $this->config['author']; |
---|
| 420 | } |
---|
| 421 | return $node; |
---|
| 422 | } |
---|
| 423 | |
---|
| 424 | /** |
---|
| 425 | * Create MD5 hash of item and mappings array. |
---|
| 426 | * |
---|
| 427 | * Include mappings as a change in mappings may have an affect on the item |
---|
| 428 | * produced. |
---|
| 429 | * |
---|
| 430 | * @return Always returns a hash, even with empty, NULL, FALSE: |
---|
| 431 | * Empty arrays return 40cd750bba9870f18aada2478b24840a |
---|
| 432 | * Empty/NULL/FALSE strings return d41d8cd98f00b204e9800998ecf8427e |
---|
| 433 | */ |
---|
| 434 | protected function hash($item) { |
---|
| 435 | static $serialized_mappings; |
---|
| 436 | if (!$serialized_mappings) { |
---|
| 437 | $serialized_mappings = serialize($this->config['mappings']); |
---|
| 438 | } |
---|
| 439 | return hash('md5', serialize($item) . $serialized_mappings); |
---|
| 440 | } |
---|
| 441 | |
---|
| 442 | /** |
---|
| 443 | * Retrieve MD5 hash of $nid from DB. |
---|
| 444 | * @return Empty string if no item is found, hash otherwise. |
---|
| 445 | */ |
---|
| 446 | protected function getHash($nid) { |
---|
| 447 | $hash = db_result(db_query("SELECT hash FROM {feeds_node_item} WHERE nid = %d", $nid)); |
---|
| 448 | if ($hash) { |
---|
| 449 | // Return with the hash. |
---|
| 450 | return $hash; |
---|
| 451 | } |
---|
| 452 | return ''; |
---|
| 453 | } |
---|
| 454 | } |
---|
| 455 | |
---|
| 456 | /** |
---|
| 457 | * Copy of node_delete() that circumvents node_access(). |
---|
| 458 | * |
---|
| 459 | * Used for batch deletion. |
---|
| 460 | */ |
---|
| 461 | function _feeds_node_delete($nid) { |
---|
| 462 | if ($node = node_load($nid, NULL, TRUE)) { |
---|
| 463 | db_query('DELETE FROM {node} WHERE nid = %d', $node->nid); |
---|
| 464 | db_query('DELETE FROM {node_revisions} WHERE nid = %d', $node->nid); |
---|
| 465 | db_query('DELETE FROM {node_access} WHERE nid = %d', $node->nid); |
---|
| 466 | |
---|
| 467 | // Call the node-specific callback (if any): |
---|
| 468 | node_invoke($node, 'delete'); |
---|
| 469 | node_invoke_nodeapi($node, 'delete'); |
---|
| 470 | |
---|
| 471 | // Clear the page and block caches. |
---|
| 472 | cache_clear_all(); |
---|
| 473 | |
---|
| 474 | // Remove this node from the search index if needed. |
---|
| 475 | if (function_exists('search_wipe')) { |
---|
| 476 | search_wipe($node->nid, 'node'); |
---|
| 477 | } |
---|
| 478 | watchdog('content', '@type: deleted %title.', array('@type' => $node->type, '%title' => $node->title)); |
---|
| 479 | drupal_set_message(t('@type %title has been deleted.', array('@type' => node_get_types('name', $node), '%title' => $node->title))); |
---|
| 480 | } |
---|
| 481 | } |
---|