/** * Handles an import exception. */ protected function handleException(FeedInterface $feed, \Exception $exception) { $feed->finishImport(); if (!$exception instanceof EmptyFeedException) { throw $exception; } }
/** * Lists the feed items belonging to a feed. */ public function listItems(FeedInterface $feeds_feed, Request $request) { $processor = $feeds_feed->getType()->getProcessor(); $header = ['title' => $this->t('Label'), 'imported' => $this->t('Imported'), 'guid' => ['data' => $this->t('GUID'), 'class' => [RESPONSIVE_PRIORITY_LOW]], 'url' => ['data' => $this->t('URL'), 'class' => [RESPONSIVE_PRIORITY_LOW]]]; $build = []; $build['table'] = ['#type' => 'table', '#header' => $header, '#rows' => [], '#empty' => $this->t('There are no items yet.')]; // @todo Allow processors to create their own entity listings. if (!$processor instanceof EntityProcessorInterface) { return $build; } $entity_ids = \Drupal::entityQuery($processor->entityType())->condition('feeds_item.target_id', $feeds_feed->id())->pager(50)->sort('feeds_item.imported', 'DESC')->execute(); $storage = $this->entityManager()->getStorage($processor->entityType()); foreach ($storage->loadMultiple($entity_ids) as $entity) { $ago = \Drupal::service('date.formatter')->formatInterval(REQUEST_TIME - $entity->get('feeds_item')->imported); $row = []; // Entity link. $row[] = ['data' => $entity->link(Unicode::truncate($entity->label(), 75, TRUE, TRUE)), 'title' => $entity->label()]; // Imported ago. $row[] = $this->t('@time ago', ['@time' => $ago]); // Item GUID. $row[] = ['data' => SafeMarkup::checkPlain(Unicode::truncate($entity->get('feeds_item')->guid, 30, FALSE, TRUE)), 'title' => $entity->get('feeds_item')->guid]; // Item URL. $row[] = ['data' => SafeMarkup::checkPlain(Unicode::truncate($entity->get('feeds_item')->url, 30, FALSE, TRUE)), 'title' => $entity->get('feeds_item')->url]; $build['table']['#rows'][] = $row; } $build['pager'] = ['#type' => 'pager']; $build['#title'] = $this->t('%title items', ['%title' => $feeds_feed->label()]); return $build; }
/** * {@inheritdoc} */ public function buildFeedForm(array $form, FormStateInterface $form_state, FeedInterface $feed) { $feed_config = $feed->getConfigurationFor($this); $form['parser']['#tree'] = TRUE; $form['parser']['#weight'] = -10; $form['parser']['delimiter'] = ['#type' => 'select', '#title' => $this->t('Delimiter'), '#description' => $this->t('The character that delimits fields in the CSV file.'), '#options' => [',' => ',', ';' => ';', 'TAB' => 'TAB', '|' => '|', '+' => '+'], '#default_value' => $feed_config['delimiter']]; $form['parser']['no_headers'] = ['#type' => 'checkbox', '#title' => $this->t('No Headers'), '#description' => $this->t("Check if the imported CSV file does not start with a header row. If checked, mapping sources must be named '0', '1', '2' etc."), '#default_value' => $feed_config['no_headers']]; return $form; }
/** * {@inheritdoc} */ public function postClear(FeedInterface $feed, StateInterface $state) { $tokens = ['@item' => $this->getItemLabel(), '@items' => $this->getItemLabelPlural(), '%title' => $feed->label()]; if ($state->deleted) { $state->setMessage($this->formatPlural($state->deleted, 'Deleted @count @item from %title.', 'Deleted @count @items from %title.', $tokens)); } else { $state->setMessage($this->t('There are no @items to delete.', $tokens)); } }
/** * Finalizes the import. */ protected function finish(FeedInterface $feed, FetcherResultInterface $fetcher_result) { if ($feed->progressParsing() !== StateInterface::BATCH_COMPLETE) { $this->queueFactory->get('feeds_feed_import:' . $feed->bundle())->createItem($feed); } elseif ($feed->progressFetching() !== StateInterface::BATCH_COMPLETE) { $this->queueFactory->get('feeds_feed_parse:' . $feed->bundle())->createItem($feed, $fetcher_result); } else { $feed->finishImport(); } }
public function postExpire(FeedInterface $feed) { $state = $feed->getState(StateInterface::EXPIRE); if ($state->total) { drupal_set_message(t('Expired @count items.', ['@count' => $state->total])); } $feed->clearStates(); $feed->save(); $feed->unlock(); }
/** * {@inheritdoc} * * @todo I guess we could cache this since the value will be the same for * $element_key/$feed id combo. */ public function getSourceElement(FeedInterface $feed, array $item, $element_key) { list(, $field) = explode(':', $element_key); $return = array(); if ($field_list = $feed->get($field)) { foreach ($field_list as $field) { $return[] = $field->value; } } return $return; }
/** * Executes a callback. * * @param \Drupal\feeds\FeedInterface $feeds_feed * The Feed we are executing a job for. * @param \Symfony\Component\HttpFoundation\Request $request * The request object to grab POST params from. * * @todo Configure a time limit. * @todo Really awesome error handling. */ public function execute(FeedInterface $feeds_feed, Request $request) { $cid = 'feeds_feed:' . $feeds_feed->id(); if ($token = $request->request->get('token') && ($job = $this->state->get($cid))) { if ($job['token'] == $token && ($lock = $this->lockBackend->acquire($cid))) { $method = $job['method']; $this->state->delete($cid); ignore_user_abort(TRUE); set_time_limit(0); while ($feeds_feed->{$method}() != StateInterface::BATCH_COMPLETE) { // Reset static caches in between runs to avoid memory leaks. drupal_reset_static(); } $this->lockBackend->release($cid); } } }
/** * {@inheritdoc} */ public function parse(FeedInterface $feed, FetcherResultInterface $fetcher_result, StateInterface $state) { $result = new ParserResult(); Reader::setExtensionManager(\Drupal::service('feed.bridge.reader')); Reader::registerExtension('GeoRSS'); $raw = $fetcher_result->getRaw(); if (!strlen(trim($raw))) { throw new EmptyFeedException(); } try { $channel = Reader::importString($raw); } catch (ExceptionInterface $e) { $args = ['%site' => $feed->label(), '%error' => trim($e->getMessage())]; throw new \RuntimeException($this->t('The feed from %site seems to be broken because of error "%error".', $args)); } foreach ($channel as $delta => $entry) { $item = new SyndicationItem(); // Move the values to an array as expected by processors. $item->set('title', $entry->getTitle())->set('guid', $entry->getId())->set('url', $entry->getLink())->set('guid', $entry->getId())->set('url', $entry->getLink())->set('description', $entry->getDescription())->set('tags', $entry->getCategories()->getValues())->set('feed_title', $channel->getTitle())->set('feed_description', $channel->getDescription())->set('feed_url', $channel->getLink()); if ($image = $channel->getImage()) { $item->set('feed_image_uri', $image['uri']); } if ($enclosure = $entry->getEnclosure()) { $item->set('enclosures', [rawurldecode($enclosure->url)]); } if ($author = $entry->getAuthor()) { $author += ['name' => '', 'email' => '']; $item->set('author_name', $author['name'])->set('author_email', $author['email']); } if ($date = $entry->getDateModified()) { $item->set('timestamp', $date->getTimestamp()); } if ($point = $entry->getGeoPoint()) { $item->set('georss_lat', $point['lat'])->set('georss_lon', $point['lon']); } $result->addItem($item); } return $result; }
/** * {@inheritdoc} */ public function parse(FeedInterface $feed, FetcherResultInterface $fetcher_result, StateInterface $state) { // Set time zone to GMT for parsing dates with strtotime(). $tz = date_default_timezone_get(); date_default_timezone_set('GMT'); // Reset item counter. $this->items_count = 0; // Get raw data. $raw = trim($fetcher_result->getRaw()); if (!strlen($raw)) { throw new EmptyFeedException(); } $data = Json::decode($raw); $result = new ParserResult(); if ($data && count($data['items']) > 0) { $this->processItems($data['items'], $result); } if ($data['pageInfo']['totalResults'] && $data['pageInfo']['resultsPerPage'] && $data['pageInfo']['totalResults'] > $data['pageInfo']['resultsPerPage']) { $number_of_pages = $data['pageInfo']['totalResults'] / $data['pageInfo']['resultsPerPage']; if ($number_of_pages > 1) { $feed_type = $feed->getType(); $fetcher_configuration = $feed_type->getFetcher()->getConfiguration(); $yt_state = ['channel_id' => $feed->getSource(), 'api_key' => $fetcher_configuration['api_key'], 'import_limit' => $fetcher_configuration['import_limit'], 'page_limit' => $fetcher_configuration['page_limit'], 'pageToken' => '']; for ($i = 0; $i <= $number_of_pages; $i++) { if (!$data) { throw new EmptyFeedException(); } if ($data['nextPageToken']) { $yt_state['pageToken'] = $data['nextPageToken']; $data = Json::decode($this->fetchInternal($feed, $yt_state)); $this->processItems($data['items'], $result); } } } } date_default_timezone_set($tz); return $result; }
/** * {@inheritdoc} */ public function parse(FeedInterface $feed, FetcherResultInterface $fetcher_result) { $result = new ParserResult(); Reader::setExtensionManager(\Drupal::service('feed.bridge.reader')); try { $channel = Reader::importString($fetcher_result->getRaw()); } catch (ExceptionInterface $e) { watchdog_exception('feeds', $e); drupal_set_message($this->t('The feed from %site seems to be broken because of error "%error".', array('%site' => $feed->label(), '%error' => $e->getMessage())), 'error'); return $result; } $result->title = $channel->getTitle(); $result->description = $channel->getDescription(); $result->link = $channel->getLink(); foreach ($channel as $item) { // Reset the parsed item. $parsed_item = array(); // Move the values to an array as expected by processors. $parsed_item['title'] = $item->getTitle(); $parsed_item['guid'] = $item->getId(); $parsed_item['url'] = $item->getLink(); $parsed_item['description'] = $item->getDescription(); if ($enclosure = $item->getEnclosure()) { $parsed_item['enclosures'][] = urldecode($enclosure->url); } if ($author = $item->getAuthor()) { $parsed_item['author_name'] = $author['name']; } if ($date = $item->getDateModified()) { $parsed_item['timestamp'] = $date->getTimestamp(); } $parsed_item['tags'] = $item->getCategories()->getValues(); $result->items[] = $parsed_item; } return $result; }
/** * {@inheritodc} */ public function clear(FeedInterface $feed, array &$context) { try { $this->dispatchEvent(FeedsEvents::INIT_CLEAR, new InitEvent($feed)); $this->dispatchEvent(FeedsEvents::CLEAR, new ClearEvent($feed)); } catch (\Exception $exception) { // Do nothing yet. } // Clean up. $context['finished'] = $feed->progressClearing(); if (isset($exception)) { $context['finished'] = StateInterface::BATCH_COMPLETE; } if ($context['finished'] === StateInterface::BATCH_COMPLETE) { $feed->finishClear(); $feed->save(); $feed->unlock(); } else { $feed->saveStates(); } if (isset($exception)) { throw $exception; } }
protected function unsubscribe(FeedInterface $feed, SubscriptionInterface $subscription = NULL) { if (!$subscription) { return; } $subscription->unsubscribe(); $batch = ['title' => t('Unsubscribing from: %title', ['%title' => $feed->label()]), 'init_message' => t('Unsubscribing from: %title', ['%title' => $feed->label()]), 'operations' => [['Drupal\\feeds\\EventSubscriber\\PubSubHubbub::runSubscribeBatch', [$subscription]]], 'progress_message' => t('Unsubscribing: %title', ['%title' => $feed->label()]), 'error_message' => t('An error occored while unsubscribing from %title.', ['%title' => $feed->label()])]; batch_set($batch); }
protected function getUniqueQuery(FeedInterface $feed) { if (!isset(static::$uniqueQueries[$feed->id()])) { $entity_type = $feed->getImporter()->getProcessor()->entityType(); static::$uniqueQueries[$feed->id()] = \Drupal::entityQuery($entity_type)->condition('feeds_item.target_id', $feed->id())->range(0, 1); } return clone static::$uniqueQueries[$feed->id()]; }
/** * Starts a Batch API job. * * @param \Drupal\feeds\FeedInterface $feed * The feed to start the job for. * @param string $title * Title to show to user when executing batch. * @param string $method * Method to execute on importer; one of 'import' or 'clear'. */ protected function startBatchAPIJob(FeedInterface $feed, $title, $method) { $batch = array('title' => $title, 'operations' => array(array('feeds_batch', array($method, $feed->id()))), 'progress_message' => ''); batch_set($batch); }
/** * {@inheritdoc} */ public function getItemCount(FeedInterface $feed) { return $this->queryFactory->get($this->entityType())->condition('feeds_item.target_id', $feed->id())->count()->execute(); }
/** * {@inheritdoc} */ public function submitFeedForm(array &$form, FormStateInterface $form_state, FeedInterface $feed) { // We need to store this for later so that we have the feed id. $new_fid = reset($form_state->getValue('source')); $feed_config = $feed->getConfigurationFor($this); // Generate a UUID that maps to this feed for file usage. We can't depend // on the feed id since this could be called before an id is assigned. $feed_config['usage_id'] = $feed_config['usage_id'] ?: $this->uuid->generate(); if ($new_fid == $feed_config['fid']) { return; } $this->deleteFile($feed_config['fid'], $feed_config['usage_id']); if ($new_fid) { $file = $this->fileStorage->load($new_fid); $this->fileUsage->add($file, 'feeds', $this->pluginType(), $feed_config['usage_id']); $file->setPermanent(); $file->save(); $feed_config['fid'] = $new_fid; $feed->setSource($file->getFileUri()); } $feed->setConfigurationFor($this, $feed_config); }
/** * Stub for plugins implementing FeedPluginFormInterface. * * Most all plugins should get automatic submit handlers from this. * * @see \Drupal\feeds\Plugin\Type\FeedPluginFormInterface */ public function submitFeedForm(array &$form, array &$form_state, FeedInterface $feed) { if (isset($form_state['values'][$this->pluginType()])) { $feed->setConfigurationFor($this, $form_state['values'][$this->pluginType()]); } }
/** * Schedule background expire tasks. * * This is also used as a callback for job_scheduler integration. * * @param \Drupal\feeds\FeedInterface $feed * The feed to schedule. */ public function scheduleExpire(FeedInterface $feed) { if (!$this->jobController) { return; } // Schedule as soon as possible if a batch is active. $period = $feed->progressExpiring() === StateInterface::BATCH_COMPLETE ? 3600 : 0; $job = array('name' => 'feeds_feed_expire', 'type' => $feed->bundle(), 'id' => $feed->id(), 'period' => $period, 'periodic' => TRUE); if ($feed->getImporter()->getProcessor()->expiryTime() == SchedulerInterface::EXPIRE_NEVER) { $this->jobController->remove($job); } else { $this->jobController->set($job); } }
/** * {@inheritdoc} */ public function onFeedSave(FeedInterface $feed, $update) { // We are only interested in continuing if we came from a form submit. if (!$this->feedConfig) { return; } // New file found. if ($this->feedConfig['new_fid'] != $this->feedConfig['fid']) { $this->deleteFile($this->feedConfig['fid'], $feed->id()); if ($this->feedConfig['new_fid']) { $file = $this->fileStorage->load($this->feedConfig['new_fid']); $this->fileUsage->add($file, 'feeds', $this->pluginType(), $feed->id()); $file->setPermanent(); $file->save(); $this->feedConfig['fid'] = $this->feedConfig['new_fid']; $this->feedConfig['source'] = $file->getFileUri(); $feed->setConfigurationFor($this, $this->feedConfig); } } }
/** * Handles an exception during importing. * * @param \Drupal\feeds\FeedInterface $feed * The feed. * @param \Exception $exception * The exception that was thrown. * * @throws \Exception * Thrown if $exception is not an instance of EmptyFeedException. */ protected function handleException(FeedInterface $feed, \Exception $exception) { $feed->finishImport(); if ($exception instanceof EmptyFeedException) { return; } if ($exception instanceof \RuntimeException) { drupal_set_message($exception->getMessage(), 'error'); return; } throw $exception; }
/** * {@inheritdoc} */ public function buildFeedForm(array $form, array &$form_state, FeedInterface $feed) { $feed_config = $feed->getConfigurationFor($this); $form['parser']['#tree'] = TRUE; $form['parser']['#weight'] = -10; $mappings = $this->importer->getMappings(); // $feeds = $uniques = array(); // foreach ($mappings as $mapping) { // $feeds[] = check_plain($mapping['source']); // if (!empty($mapping['unique'])) { // $uniques[] = check_plain($mapping['source']); // } // } // $output = $this->t('Import !csv_files with one or more of these columns: !columns.', array( // '!csv_files' => l($this->t('CSV files'), 'http://en.wikipedia.org/wiki/Comma-separated_values'), // '!columns' => implode(', ', $feeds), // )); // $items = array(); // $items[] = format_plural(count($uniques), $this->t('Column <strong>!column</strong> is mandatory and considered unique: only one item per !column value will be created.', array('!column' => implode(', ', $uniques))), $this->t('Columns <strong>!columns</strong> are mandatory and values in these columns are considered unique: only one entry per value in one of these column will be created.', array('!columns' => implode(', ', $uniques)))); // $items[] = l($this->t('Download a template'), 'import/' . $this->importer->id() . '/template'); // $form['parser']['help'] = array( // '#prefix' => '<div class="help">', // '#suffix' => '</div>', // 'description' => array( // '#prefix' => '<p>', // '#markup' => $output, // '#suffix' => '</p>', // ), // 'list' => array( // '#theme' => 'item_list', // '#items' => $items, // ), // ); $form['parser']['delimiter'] = array('#type' => 'select', '#title' => $this->t('Delimiter'), '#description' => $this->t('The character that delimits fields in the CSV file.'), '#options' => array(',' => ',', ';' => ';', 'TAB' => 'TAB', '|' => '|', '+' => '+'), '#default_value' => isset($feed_config['delimiter']) ? $feed_config['delimiter'] : ','); $form['parser']['no_headers'] = array('#type' => 'checkbox', '#title' => $this->t('No Headers'), '#description' => $this->t('Check if the imported CSV file does not start with a header row. If checked, mapping sources must be named \'0\', \'1\', \'2\' etc.'), '#default_value' => isset($feed_config['no_headers']) ? $feed_config['no_headers'] : 0); return $form; }
/** * Receives a notification. * * @param \Drupal\feeds\FeedInterface $feeds_feed * The feed to perform the request on. * @param \Symfony\Component\HttpFoundation\Request $request * The request object. * * @return Symfony\Component\HttpFoundation\Response * The response object. * * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException * Thrown if the subscription was not found or the request parameters were * invalid. */ public function receive(FeedInterface $feeds_feed, Request $request) { if (!($sig = $request->headers->get('X-Hub-Signature'))) { throw new NotFoundHttpException(); } $result = array(); parse_str($sig, $result); if (empty($result['sha1'])) { throw new NotFoundHttpException(); } if (!($sub = $this->subscriptionCrud->getSubscription($feeds_feed->id()))) { throw new NotFoundHttpException(); } $raw = file_get_contents('php://input'); if ($result['sha1'] !== hash_hmac('sha1', $raw, $sub['secret'])) { throw new NotFoundHttpException(); } try { $feeds_feed->importRaw($raw); } catch (InterfaceNotImplementedException $e) { // The fetcher does not support PuSH updates. throw new NotFoundHttpException(); } return new Response('', 200); }
/** * {@inheritdoc} */ public function importPeriod(FeedInterface $feed) { $sub = $this->subscription->getSubscription($feed->id()); if ($sub && $sub['state'] == 'subscribed') { // Delay for three days if there is a successful subscription. return 259200; } }
/** * {@inheritdoc} */ public function submitFeedForm(array &$form, FormStateInterface $form_state, FeedInterface $feed) { $feed->setSource($form_state->getValue('source')); }
/** * Stub for plugins implementing FeedPluginFormInterface. * * Most all plugins should get automatic submit handlers from this. * * @see \Drupal\feeds\Plugin\Type\FeedPluginFormInterface */ public function submitFeedForm(array &$form, FormStateInterface $form_state, FeedInterface $feed) { $feed->setConfigurationFor($this, $form_state->getValues()); }