View source
<?php
namespace Drupal\feeds\Entity;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\feeds\Event\DeleteFeedsEvent;
use Drupal\feeds\Event\EntityEvent;
use Drupal\feeds\Event\FeedsEvents;
use Drupal\feeds\Event\ImportFinishedEvent;
use Drupal\feeds\Exception\LockException;
use Drupal\feeds\FeedInterface;
use Drupal\feeds\Feeds\Item\ItemInterface;
use Drupal\feeds\Feeds\State\CleanState;
use Drupal\feeds\FeedTypeInterface;
use Drupal\feeds\Plugin\Type\FeedsPluginInterface;
use Drupal\feeds\State;
use Drupal\feeds\StateInterface;
use Drupal\user\UserInterface;
class Feed extends ContentEntityBase implements FeedInterface {
use EntityChangedTrait;
protected $states;
public function __wakeup() {
$this->states = [];
}
protected function eventDispatcher() {
return \Drupal::service('event_dispatcher');
}
public function id() {
return $this
->get('fid')->value;
}
public function label() {
return $this
->get('title')->value;
}
public function getSource() {
return $this
->get('source')->value;
}
public function setSource($source) {
return $this
->set('source', $source);
}
public function getCreatedTime() {
return (int) $this
->get('created')->value;
}
public function setCreatedTime($timestamp) {
$this
->set('created', (int) $timestamp);
}
public function getChangedTime() {
return (int) $this
->get('changed')->value;
}
public function getImportedTime() {
return (int) $this
->get('imported')->value;
}
public function getNextImportTime() {
return (int) $this
->get('next')->value;
}
public function getQueuedTime() {
return (int) $this
->get('queued')->value;
}
public function setQueuedTime($queued) {
$this
->set('queued', (int) $queued);
}
public function getType() {
$type = $this
->get('type')->entity;
if (empty($type)) {
if ($this
->id()) {
throw new EntityStorageException(strtr('The feed type "@type" for feed @id no longer exists.', [
'@type' => $this
->bundle(),
'@id' => $this
->id(),
]));
}
else {
throw new EntityStorageException(strtr('The feed type "@type" no longer exists.', [
'@type' => $this
->bundle(),
]));
}
}
return $type;
}
public function getOwner() {
return $this
->get('uid')->entity;
}
public function getOwnerId() {
return $this
->get('uid')->target_id;
}
public function setOwnerId($uid) {
$this
->set('uid', $uid);
return $this;
}
public function setOwner(UserInterface $account) {
$this
->set('uid', $account
->id());
return $this;
}
public function isActive() {
return (bool) $this
->get('status')->value;
}
public function setActive($active) {
$this
->set('status', $active ? static::ACTIVE : static::INACTIVE);
}
public function import() {
$this
->entityTypeManager()
->getHandler('feeds_feed', 'feed_import')
->import($this);
}
public function startBatchImport() {
$this
->entityTypeManager()
->getHandler('feeds_feed', 'feed_import')
->startBatchImport($this);
}
public function startCronImport() {
$this
->entityTypeManager()
->getHandler('feeds_feed', 'feed_import')
->startCronImport($this);
}
public function pushImport($raw) {
return $this
->entityTypeManager()
->getHandler('feeds_feed', 'feed_import')
->pushImport($this, $raw);
}
public function startBatchClear() {
$this
->entityTypeManager()
->getHandler('feeds_feed', 'feed_clear')
->startBatchClear($this);
}
public function startBatchExpire() {
return $this
->entityTypeManager()
->getHandler('feeds_feed', 'feed_expire')
->startBatchExpire($this);
}
public function dispatchEntityEvent($event, EntityInterface $entity, ItemInterface $item) {
return $this
->eventDispatcher()
->dispatch($event, new EntityEvent($this, $entity, $item));
}
public function finishImport() {
$time = time();
$this
->getType()
->getProcessor()
->postProcess($this, $this
->getState(StateInterface::PROCESS));
foreach ($this->states as $state) {
if (is_object($state)) {
$state
->displayMessages();
$state
->logMessages($this);
}
}
$this
->eventDispatcher()
->dispatch(FeedsEvents::IMPORT_FINISHED, new ImportFinishedEvent($this));
$this
->clearStates();
$this
->setQueuedTime(0);
$this
->set('imported', $time);
$interval = $this
->getType()
->getImportPeriod();
if ($interval !== FeedTypeInterface::SCHEDULE_NEVER) {
$this
->set('next', $interval + $time);
}
$this
->save();
$this
->unlock();
}
public function finishClear() {
$this
->getType()
->getProcessor()
->postClear($this, $this
->getState(StateInterface::CLEAR));
foreach ($this->states as $state) {
is_object($state) ? $state
->displayMessages() : NULL;
}
$this
->clearStates();
}
public function getState($stage) {
if (!isset($this->states[$stage])) {
$state = \Drupal::keyValue('feeds_feed.' . $this
->id())
->get($stage);
if (empty($state)) {
switch ($stage) {
case StateInterface::CLEAN:
$state = new CleanState($this
->id());
break;
default:
$state = new State();
break;
}
}
$this->states[$stage] = $state;
}
return $this->states[$stage];
}
public function setState($stage, StateInterface $state = NULL) {
$this->states[$stage] = $state;
}
public function clearStates() {
$this->states = [];
\Drupal::keyValue('feeds_feed.' . $this
->id())
->deleteAll();
\Drupal::database()
->delete(CleanState::TABLE_NAME)
->condition('feed_id', $this
->id())
->execute();
}
public function saveStates() {
\Drupal::keyValue('feeds_feed.' . $this
->id())
->setMultiple($this->states);
}
public function progressFetching() {
return $this
->getState(StateInterface::FETCH)->progress;
}
public function progressParsing() {
return $this
->getState(StateInterface::PARSE)->progress;
}
public function progressImporting() {
$fetcher = $this
->getState(StateInterface::FETCH);
$parser = $this
->getState(StateInterface::PARSE);
if ($fetcher->progress === StateInterface::BATCH_COMPLETE && $parser->progress === StateInterface::BATCH_COMPLETE) {
return StateInterface::BATCH_COMPLETE;
}
$fetcher_fraction = $fetcher->total ? 1.0 / $fetcher->total : 1.0;
$parser_progress = $parser->progress * $fetcher_fraction;
$result = $fetcher->progress - $fetcher_fraction + $parser_progress;
if ($result >= StateInterface::BATCH_COMPLETE) {
return 0.99;
}
return $result;
}
public function progressCleaning() {
return $this
->getState(StateInterface::CLEAN)->progress;
}
public function progressClearing() {
return $this
->getState(StateInterface::CLEAR)->progress;
}
public function progressExpiring() {
return $this
->getState(StateInterface::EXPIRE)->progress;
}
public function getItemCount() {
return (int) $this
->get('item_count')->value;
}
public function lock() {
if (!\Drupal::service('lock.persistent')
->acquire("feeds_feed_{$this->id()}", 3600 * 12)) {
$args = [
'@id' => $this
->bundle(),
'@fid' => $this
->id(),
];
throw new LockException(new FormattableMarkup('Cannot acquire lock for feed @id / @fid.', $args));
}
Cache::invalidateTags([
'feeds_feed_locked',
]);
}
public function unlock() {
\Drupal::service('lock.persistent')
->release("feeds_feed_{$this->id()}");
Cache::invalidateTags([
'feeds_feed_locked',
]);
}
public function isLocked() {
return !\Drupal::service('lock.persistent')
->lockMayBeAvailable("feeds_feed_{$this->id()}");
}
public function getConfigurationFor(FeedsPluginInterface $client) {
$type = $client
->pluginType();
$data = $this
->get('config')->{$type};
$data = !empty($data) ? $data : [];
return $data + $client
->defaultFeedConfiguration();
}
public function setConfigurationFor(FeedsPluginInterface $client, array $configuration) {
$type = $client
->pluginType();
$this
->get('config')->{$type} = array_intersect_key($configuration, $client
->defaultFeedConfiguration()) + $client
->defaultFeedConfiguration();
}
public function preSave(EntityStorageInterface $storage_controller, $update = TRUE) {
$feed_type = $this
->getType();
foreach ($feed_type
->getPlugins() as $plugin) {
$plugin
->onFeedSave($this, $update);
}
if ($feed_type
->getImportPeriod() === FeedTypeInterface::SCHEDULE_NEVER) {
$this
->set('next', FeedTypeInterface::SCHEDULE_NEVER);
}
$this
->set('item_count', $feed_type
->getProcessor()
->getItemCount($this));
}
public static function postDelete(EntityStorageInterface $storage_controller, array $feeds) {
$ids = array_keys($feeds);
$grouped = [];
foreach ($feeds as $fid => $feed) {
$grouped[$feed
->bundle()][$fid] = $feed;
}
foreach ($grouped as $group) {
$feed = reset($group);
try {
foreach ($feed
->getType()
->getPlugins() as $plugin) {
$plugin
->onFeedDeleteMultiple($group);
}
} catch (EntityStorageException $e) {
$args = [
'%title' => $feed
->label(),
'@error' => $e
->getMessage(),
];
\Drupal::logger('feeds')
->warning('Could not perform some post cleanups for feed %title because of the following error: @error', $args);
}
}
\Drupal::database()
->delete(CleanState::TABLE_NAME)
->condition('feed_id', $ids, 'IN')
->execute();
\Drupal::service('event_dispatcher')
->dispatch(FeedsEvents::FEEDS_DELETE, new DeleteFeedsEvent($feeds));
}
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields = [];
$fields['fid'] = BaseFieldDefinition::create('integer')
->setLabel(t('Feed ID'))
->setDescription(t('The feed ID.'))
->setReadOnly(TRUE)
->setSetting('unsigned', TRUE);
$fields['uuid'] = BaseFieldDefinition::create('uuid')
->setLabel(t('UUID'))
->setDescription(t('The feed UUID.'))
->setReadOnly(TRUE);
$fields['type'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Feed type'))
->setDescription(t('The feed type.'))
->setSetting('target_type', 'feeds_feed_type')
->setReadOnly(TRUE);
$fields['title'] = BaseFieldDefinition::create('string')
->setLabel(t('Title'))
->setDescription(t('The title of this feed, always treated as non-markup plain text.'))
->setRequired(TRUE)
->setDefaultValue('')
->setSetting('max_length', 255)
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'string',
'weight' => -5,
])
->setDisplayOptions('form', [
'type' => 'string_textfield',
'weight' => -5,
])
->setDisplayConfigurable('form', TRUE);
$fields['uid'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Authored by'))
->setDescription(t('The user ID of the feed author.'))
->setRevisionable(TRUE)
->setSetting('target_type', 'user')
->setSetting('handler', 'default')
->setDefaultValueCallback('Drupal\\feeds\\Entity\\Feed::getCurrentUserId')
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'author',
'weight' => 0,
])
->setDisplayOptions('form', [
'type' => 'entity_reference_autocomplete',
'weight' => 5,
'settings' => [
'match_operator' => 'CONTAINS',
'size' => '60',
'autocomplete_type' => 'tags',
'placeholder' => '',
],
])
->setDisplayConfigurable('form', TRUE);
$fields['status'] = BaseFieldDefinition::create('boolean')
->setLabel(t('Importing status'))
->setDescription(t('A boolean indicating whether the feed is active.'))
->setDefaultValue(TRUE);
$fields['created'] = BaseFieldDefinition::create('created')
->setLabel(t('Authored on'))
->setDescription(t('The time that the feed was created.'))
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'timestamp',
'weight' => 0,
])
->setDisplayOptions('form', [
'type' => 'datetime_timestamp',
'weight' => 10,
])
->setDisplayConfigurable('form', TRUE);
$fields['changed'] = BaseFieldDefinition::create('changed')
->setLabel(t('Changed'))
->setDescription(t('The time that the feed was last edited.'));
$fields['imported'] = BaseFieldDefinition::create('timestamp')
->setLabel(t('Last import'))
->setDescription(t('The time that the feed was imported.'))
->setDefaultValue(0)
->setDisplayOptions('view', [
'label' => 'inline',
'type' => 'timestamp_ago',
'weight' => 1,
])
->setDisplayConfigurable('view', TRUE);
$fields['next'] = BaseFieldDefinition::create('timestamp')
->setLabel(t('Next import'))
->setDescription(t('The time that the feed will import next.'))
->setDefaultValue(0)
->setDisplayOptions('view', [
'label' => 'inline',
'type' => 'timestamp',
'weight' => 1,
])
->setDisplayConfigurable('view', TRUE);
$fields['queued'] = BaseFieldDefinition::create('timestamp')
->setLabel(t('Queued'))
->setDescription(t('Time when this feed was queued for refresh, 0 if not queued.'))
->setDefaultValue(0);
$fields['source'] = BaseFieldDefinition::create('uri')
->setLabel(t('Source'))
->setDescription(t('The source of the feed.'))
->setRequired(TRUE)
->setDisplayOptions('view', [
'label' => 'inline',
'type' => 'feeds_uri_link',
'weight' => -3,
])
->setDisplayConfigurable('view', TRUE);
$fields['config'] = BaseFieldDefinition::create('map')
->setLabel(t('Config'))
->setDescription(t('The config of the feed.'));
$fields['item_count'] = BaseFieldDefinition::create('integer')
->setLabel(t('Items imported'))
->setDescription(t('The number of items imported.'))
->setDefaultValue(0)
->setDisplayOptions('view', [
'label' => 'inline',
'type' => 'number_integer',
'weight' => 0,
]);
return $fields;
}
public static function getCurrentUserId() {
return [
\Drupal::currentUser()
->id(),
];
}
}