diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml index 60f34c34f0fbe7a9f3663e093dea4aa42176fb7a..63a35eb3775d8d3587ad70fa07ba9add42e060cc 100644 --- a/core/config/schema/core.entity.schema.yml +++ b/core/config/schema/core.entity.schema.yml @@ -244,3 +244,23 @@ entity_view_display.field.number_unformatted: label: 'Settings' sequence: - type: string + +entity_view_display.field.uri_link: + type: entity_field_view_display_base + label: 'URI as link display format settings' + mapping: + settings: + type: sequence + label: 'Settings' + sequence: + - type: string + +entity_view_display.field.timestamp_ago: + type: entity_field_view_display_base + label: 'Timestamp ago display format settings' + mapping: + settings: + type: sequence + label: 'Settings' + sequence: + - type: string diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampAgoFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampAgoFormatter.php new file mode 100644 index 0000000000000000000000000000000000000000..648a17141c70d28aa601bff8871aa598205d00d3 --- /dev/null +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampAgoFormatter.php @@ -0,0 +1,102 @@ +<?php + +/** + * @file + * Contains \Drupal\Core\Field\Plugin\Field\FieldFormatter\TimestampAgoFormatter. + */ + +namespace Drupal\Core\Field\Plugin\Field\FieldFormatter; + +use Drupal\Core\Datetime\DateFormatter; +use Drupal\Core\Field\FieldDefinitionInterface; +use Drupal\Core\Field\FieldItemListInterface; +use Drupal\Core\Field\FormatterBase; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Plugin implementation of the 'timestamp' formatter as time ago. + * + * @FieldFormatter( + * id = "timestamp_ago", + * label = @Translation("Time ago"), + * field_types = { + * "timestamp", + * "created", + * "updated", + * } + * ) + */ +class TimestampAgoFormatter extends FormatterBase implements ContainerFactoryPluginInterface { + + /** + * The date formatter service. + * + * @var \Drupal\Core\Datetime\DateFormatter + */ + protected $dateFormatter; + + /** + * Constructs a TimestampAgoFormatter object. + * + * @param string $plugin_id + * The plugin_id for the formatter. + * @param mixed $plugin_definition + * The plugin implementation definition. + * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition + * The definition of the field to which the formatter is associated. + * @param array $settings + * The formatter settings. + * @param string $label + * The formatter label display setting. + * @param string $view_mode + * The view mode. + * @param array $third_party_settings + * Any third party settings settings. + * @param \Drupal\Core\Datetime\DateFormatter $date_formatter + * The date formatter service. + */ + public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, DateFormatter $date_formatter) { + parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings); + + $this->dateFormatter = $date_formatter; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + // @see \Drupal\Core\Field\FormatterPluginManager::createInstance(). + return new static( + $plugin_id, + $plugin_definition, + $configuration['field_definition'], + $configuration['settings'], + $configuration['label'], + $configuration['view_mode'], + $configuration['third_party_settings'], + $container->get('date.formatter') + ); + } + + /** + * {@inheritdoc} + */ + public function viewElements(FieldItemListInterface $items) { + $elements = array(); + + foreach ($items as $delta => $item) { + if ($item->value) { + $updated = $this->t('@time ago', array('@time' => $this->dateFormatter->formatInterval(REQUEST_TIME - $item->value))); + } + else { + $updated = $this->t('never'); + } + + $elements[$delta] = array('#markup' => $updated); + } + + return $elements; + } + +} diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/UriLinkFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/UriLinkFormatter.php new file mode 100644 index 0000000000000000000000000000000000000000..762d5f38531b8d75799aed424117b713ae9ef80c --- /dev/null +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/UriLinkFormatter.php @@ -0,0 +1,43 @@ +<?php + +/** + * @file + * Contains \Drupal\Core\Field\Plugin\Field\FieldFormatter\UriLinkFormatter. + */ + +namespace Drupal\Core\Field\Plugin\Field\FieldFormatter; + +use Drupal\Core\Field\FormatterBase; +use Drupal\Core\Field\FieldItemListInterface; + +/** + * Plugin implementation of the 'uri_link' formatter. + * + * @FieldFormatter( + * id = "uri_link", + * label = @Translation("Link to URI"), + * field_types = { + * "uri", + * } + * ) + */ +class UriLinkFormatter extends FormatterBase { + + /** + * {@inheritdoc} + */ + public function viewElements(FieldItemListInterface $items) { + $elements = array(); + + foreach ($items as $delta => $item) { + $elements[$delta] = array( + '#type' => 'link', + '#href' => $item->value, + '#title' => $item->value, + ); + } + + return $elements; + } + +} diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php index 2e4703e70c5e1b21e05b1d399fb0a20a479d8141..a823cac3ae054c0b2fa755f2dbcd0a115957d72b 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php @@ -21,7 +21,8 @@ * id = "uri", * label = @Translation("URI"), * description = @Translation("An entity field containing a URI."), - * no_ui = TRUE + * no_ui = TRUE, + * default_formatter = "uri_link", * ) */ class UriItem extends StringItem { diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module index 7361345b88fef2777803c8c29110c3f351f62caa..1cd23c0ead94a6b15847d497164e8af2536a8180 100644 --- a/core/modules/aggregator/aggregator.module +++ b/core/modules/aggregator/aggregator.module @@ -56,28 +56,18 @@ function aggregator_help($route_name, RouteMatchInterface $route_match) { */ function aggregator_theme() { return array( - 'aggregator_feed_source' => array( - 'variables' => array('aggregator_feed' => NULL, 'view_mode' => NULL), + 'aggregator_feed' => array( + 'render element' => 'elements', 'file' => 'aggregator.theme.inc', - 'template' => 'aggregator-feed-source', + 'template' => 'aggregator-feed', ), 'aggregator_block_item' => array( 'variables' => array('item' => NULL, 'feed' => 0), 'file' => 'aggregator.theme.inc', 'template' => 'aggregator-block-item', ), - 'aggregator_summary_items' => array( - 'variables' => array('summary_items' => NULL, 'source' => NULL), - 'file' => 'aggregator.theme.inc', - 'template' => 'aggregator-summary-items', - ), - 'aggregator_summary_item' => array( - 'variables' => array('aggregator_item' => NULL, 'view_mode' => NULL), - 'file' => 'aggregator.theme.inc', - 'template' => 'aggregator-summary-item', - ), 'aggregator_item' => array( - 'variables' => array('aggregator_item' => NULL, 'view_mode' => NULL), + 'render element' => 'elements', 'file' => 'aggregator.theme.inc', 'template' => 'aggregator-item', ), @@ -85,11 +75,59 @@ function aggregator_theme() { 'variables' => array('feeds' => NULL), 'file' => 'aggregator.theme.inc', ), - 'aggregator_page_rss' => array( - 'variables' => array('feeds' => NULL), - 'file' => 'aggregator.theme.inc', + ); +} + +/** + * Implements hook_entity_extra_field_info(). + */ +function aggregator_entity_extra_field_info() { + $extra = array(); + + $extra['aggregator_feed']['aggregator_feed'] = array( + 'display' => array( + 'items' => array( + 'label' => t('Items'), + 'description' => t('Items associated with this feed'), + 'weight' => 0, + ), + // @todo Move to a formatter at https://www.drupal.org/node/2339917. + 'image' => array( + 'label' => t('Image'), + 'description' => t('The feed image'), + 'weight' => 2, + ), + // @todo Move to a formatter at https://drupal.org/node/2149845. + 'description' => array( + 'label' => t('Description'), + 'description' => t('The description of this feed'), + 'weight' => 3, + ), + 'more_link' => array( + 'label' => t('More link'), + 'description' => t('A more link to the feed detail page'), + 'weight' => 5, + ), + 'feed_icon' => array( + 'label' => t('Feed icon'), + 'description' => t('An icon that links to the feed url'), + 'weight' => 6, + ), ), ); + + $extra['aggregator_item']['aggregator_item'] = array( + 'display' => array( + // @todo Move to a formatter at https://drupal.org/node/2149845. + 'description' => array( + 'label' => t('Description'), + 'description' => t('The description of this feed item'), + 'weight' => 2, + ), + ), + ); + + return $extra; } /** diff --git a/core/modules/aggregator/aggregator.routing.yml b/core/modules/aggregator/aggregator.routing.yml index 00766ebdbc6cd94a8fdd89717c10a0c9fe683c56..40f1e943daf5d42b107d8c513947dbbacb1dd088 100644 --- a/core/modules/aggregator/aggregator.routing.yml +++ b/core/modules/aggregator/aggregator.routing.yml @@ -52,7 +52,7 @@ aggregator.feed_add: entity.aggregator_feed.canonical: path: '/aggregator/sources/{aggregator_feed}' defaults: - _content: '\Drupal\aggregator\Controller\AggregatorController::viewFeed' + _entity_view: 'aggregator_feed' _title_callback: '\Drupal\aggregator\Controller\AggregatorController::feedTitle' requirements: _permission: 'access news feeds' diff --git a/core/modules/aggregator/aggregator.theme.inc b/core/modules/aggregator/aggregator.theme.inc index 605025c10b75f78bd24d8788eae530a6915f423a..e90a79890cb0e0f2dcbc5493f517925b92bca452 100644 --- a/core/modules/aggregator/aggregator.theme.inc +++ b/core/modules/aggregator/aggregator.theme.inc @@ -15,31 +15,18 @@ * * @param array $variables * An associative array containing: - * - aggregator_item: An individual feed item for display on the aggregator - * page. + * - elements: An array of elements to display in view mode. */ function template_preprocess_aggregator_item(&$variables) { - $item = $variables['aggregator_item']; + $item = $variables['elements']['#aggregator_item']; - $variables['feed_url'] = check_url($item->getLink()); - $variables['feed_title'] = String::checkPlain($item->getTitle()); - $variables['content'] = aggregator_filter_xss($item->getDescription()); - - $variables['source_url'] = ''; - $variables['source_title'] = ''; - $fid = $item->getFeedId(); - if (isset($item->ftitle) && $fid !== NULL) { - $variables['source_url'] = url('aggregator/sources/' . $fid); - $variables['source_title'] = String::checkPlain($item->ftitle); - } - if (date('Ymd', $item->getPostedTime()) == date('Ymd')) { - $variables['source_date'] = t('%ago ago', array('%ago' => \Drupal::service('date.formatter')->formatInterval(REQUEST_TIME - $item->getPostedTime()))); - } - else { - $variables['source_date'] = format_date($item->getPostedTime(), 'medium'); + // Helpful $content variable for templates. + foreach (Element::children($variables['elements']) as $key) { + $variables['content'][$key] = $variables['elements'][$key]; } - $variables['attributes']['class'][] = 'feed-item'; + $variables['url'] = check_url($item->getLink()); + $variables['title'] = String::checkPlain($item->label()); } /** @@ -74,104 +61,23 @@ function theme_aggregator_page_opml($variables) { } /** - * Prepares variables for aggregator summary templates. - * - * Default template: aggregator-summary-items.html.twig. - * - * @param array $variables - * An associative array containing: - * - source: A Drupal\aggregator\FeedInterface object representing the feed - * source. - * - summary_items: An array of feed items. - */ -function template_preprocess_aggregator_summary_items(&$variables) { - $variables['title'] = String::checkPlain($variables['source']->label()); - $summary_items = array(); - foreach (Element::children($variables['summary_items']) as $key) { - $summary_items[] = $variables['summary_items'][$key]; - } - $variables['summary_list'] = array( - '#theme' => 'item_list', - '#items' => $summary_items, - ); - $variables['source_url'] = $variables['source']->getUrl(); -} - -/** - * Processes variables for aggregator summary item templates. + * Prepares variables for aggregator feed templates. * - * Default template: aggregator-summary-item.html.twig. + * Default template: aggregator-feed.html.twig. * * @param array $variables * An associative array containing: - * - aggregator_item: The feed item. - * - view_mode: How the item is being displayed. + * - elements: An array of elements to display in view mode. */ -function template_preprocess_aggregator_summary_item(&$variables) { - $item = $variables['aggregator_item']; - - $variables['url'] = l(String::checkPlain($item->label()), check_url(url($item->getLink(), array('absolute' => TRUE))), array( - 'attributes' => array( - 'class' => array('feed-item-url'), - ), - )); - $variables['age'] = array( - '#theme' => 'time', - '#attributes' => array( - 'datetime' => format_date($item->getPostedTime(), 'html_datetime', '', 'UTC'), - 'class' => array('feed-item-age',), - ), - '#text' => t('%age old', array('%age' => \Drupal::service('date.formatter')->formatInterval(REQUEST_TIME - $item->getPostedTime()))), - '#html' => TRUE, - ); -} +function template_preprocess_aggregator_feed(&$variables) { + $feed = $variables['elements']['#aggregator_feed']; -/** - * Prepares variables for aggregator feed source templates. - * - * Default template: aggregator-feed-source.html.twig. - * - * @param array $variables - * An associative array containing: - * - aggregator_feed: A Drupal\aggregator\FeedInterface object representing - * the feed source. - */ -function template_preprocess_aggregator_feed_source(&$variables) { - $feed = $variables['aggregator_feed']; - - $feed_icon = array( - '#theme' => 'feed_icon', - '#url' => $feed->getUrl(), - '#title' => t('!title feed', array('!title' => $feed->label())), - ); - $variables['source_icon'] = drupal_render($feed_icon); - - if ($feed->getImage() && $feed->label() && $feed->getWebsiteUrl()) { - $image = array( - '#theme' => 'image', - '#path' => $feed->getImage(), - '#alt' => $feed->label(), - ); - $variables['source_image'] = l($image, $feed->getWebsiteUrl(), array('html' => TRUE, 'attributes' => array('class' => 'feed-image'))); + // Helpful $content variable for templates. + foreach (Element::children($variables['elements']) as $key) { + $variables['content'][$key] = $variables['elements'][$key]; } - else { - $variables['source_image'] = ''; - } - $variables['source_description'] = aggregator_filter_xss($feed->getDescription()); - $variables['source_url'] = check_url(url($feed->getWebsiteUrl(), array('absolute' => TRUE))); - - if ($feed->checked) { - $variables['last_checked'] = t('@time ago', array('@time' => \Drupal::service('date.formatter')->formatInterval(REQUEST_TIME - $feed->getLastCheckedTime()))); - } - else { - $variables['last_checked'] = t('never'); - } - - if (\Drupal::currentUser()->hasPermission('administer news feeds')) { - $variables['last_checked'] = l($variables['last_checked'], 'admin/config/services/aggregator'); - } - - $variables['attributes']['class'][] = 'feed-source'; + $variables['full'] = $variables['elements']['#view_mode'] == 'full'; + $variables['title'] = String::checkPlain($feed->label()); } /** diff --git a/core/modules/aggregator/config/install/core.entity_view_display.aggregator_feed.aggregator_feed.default.yml b/core/modules/aggregator/config/install/core.entity_view_display.aggregator_feed.aggregator_feed.default.yml new file mode 100644 index 0000000000000000000000000000000000000000..58e360f4557cc914b2f9b10db929629aac110ccb --- /dev/null +++ b/core/modules/aggregator/config/install/core.entity_view_display.aggregator_feed.aggregator_feed.default.yml @@ -0,0 +1,31 @@ +id: aggregator_feed.aggregator_feed.default +targetEntityType: aggregator_feed +bundle: aggregator_feed +mode: default +status: true +content: + items: + weight: 0 + checked: + type: timestamp_ago + weight: 1 + settings: { } + third_party_settings: { } + label: inline + image: + weight: 2 + description: + weight: 3 + link: + type: uri_link + weight: 4 + settings: { } + third_party_settings: { } + label: inline + feed_icon: + weight: 5 +hidden: + more_link: true +dependencies: + module: + - aggregator diff --git a/core/modules/aggregator/config/install/core.entity_view_display.aggregator_feed.aggregator_feed.summary.yml b/core/modules/aggregator/config/install/core.entity_view_display.aggregator_feed.aggregator_feed.summary.yml new file mode 100644 index 0000000000000000000000000000000000000000..d775c94dbe40cbdc890940ea974746ba77e6430a --- /dev/null +++ b/core/modules/aggregator/config/install/core.entity_view_display.aggregator_feed.aggregator_feed.summary.yml @@ -0,0 +1,22 @@ +id: aggregator_feed.aggregator_feed.summary +targetEntityType: aggregator_feed +bundle: aggregator_feed +mode: summary +status: true +content: + items: + weight: 0 + more_link: + weight: 1 +hidden: + link: true + checked: true + description: true + image: true + feed_icon: true +status: true +dependencies: + entity: + - core.entity_view_mode.aggregator_feed.summary + module: + - aggregator diff --git a/core/modules/aggregator/config/install/core.entity_view_display.aggregator_item.aggregator_item.summary.yml b/core/modules/aggregator/config/install/core.entity_view_display.aggregator_item.aggregator_item.summary.yml new file mode 100644 index 0000000000000000000000000000000000000000..bc468928e7299c17949faeca4ee3e168e4a242a1 --- /dev/null +++ b/core/modules/aggregator/config/install/core.entity_view_display.aggregator_item.aggregator_item.summary.yml @@ -0,0 +1,20 @@ +id: aggregator_item.aggregator_item.summary +targetEntityType: aggregator_item +bundle: aggregator_item +mode: summary +status: true +content: + timestamp: + weight: 0 +hidden: + link: true + author: true + feed: true + description: true +status: true +dependencies: + entity: + - core.entity_view_mode.aggregator_item.summary + module: + - aggregator + - entity_reference diff --git a/core/modules/aggregator/config/install/core.entity_view_mode.aggregator_feed.summary.yml b/core/modules/aggregator/config/install/core.entity_view_mode.aggregator_feed.summary.yml new file mode 100644 index 0000000000000000000000000000000000000000..c1b4bb8643b21ab1d4b6ca75d0f9e1a2811b6638 --- /dev/null +++ b/core/modules/aggregator/config/install/core.entity_view_mode.aggregator_feed.summary.yml @@ -0,0 +1,8 @@ +id: aggregator_feed.summary +label: Summary +status: true +cache: true +targetEntityType: aggregator_feed +dependencies: + module: + - aggregator diff --git a/core/modules/aggregator/src/Controller/AggregatorController.php b/core/modules/aggregator/src/Controller/AggregatorController.php index 1cedb04b39d598ea4c9d7c5f935654587d8dd9b7..6c46b2a9fda5303e101aeaa2b0925a7efb973da3 100644 --- a/core/modules/aggregator/src/Controller/AggregatorController.php +++ b/core/modules/aggregator/src/Controller/AggregatorController.php @@ -60,26 +60,6 @@ public function feedAdd() { return $this->entityFormBuilder()->getForm($feed); } - /** - * Displays all the items captured from the particular feed. - * - * @param \Drupal\aggregator\FeedInterface $aggregator_feed - * The feed for which to display all items. - * - * @return array - * The rendered list of items for the feed. - */ - public function viewFeed(FeedInterface $aggregator_feed) { - $entity_manager = $this->entityManager(); - $feed_source = $entity_manager->getViewBuilder('aggregator_feed') - ->view($aggregator_feed, 'default'); - // Load aggregator feed item for the particular feed id. - $items = $entity_manager->getStorage('aggregator_item')->loadByFeed($aggregator_feed->id(), 20); - // Print the feed items. - $build = $this->buildPageList($items, $feed_source); - return $build; - } - /** * Builds a listing of aggregator feed items. * @@ -210,35 +190,8 @@ public function sources() { $feeds = $entity_manager->getStorage('aggregator_feed')->loadMultiple(); - $build = array( - '#type' => 'container', - '#attributes' => array('class' => array('aggregator-wrapper')), - '#sorted' => TRUE, - ); - - foreach ($feeds as $feed) { - // Most recent items: - $summary_items = array(); - $aggregator_summary_items = $this->config('aggregator.settings') - ->get('source.list_max'); - if ($aggregator_summary_items) { - $items = $entity_manager->getStorage('aggregator_item') - ->loadByFeed($feed->id(), 20); - if ($items) { - $summary_items = $entity_manager->getViewBuilder('aggregator_item') - ->viewMultiple($items, 'summary'); - } - } - $feed->url = $this->url('entity.aggregator_feed.canonical', array('aggregator_feed' => $feed->id())); - $build[$feed->id()] = array( - '#theme' => 'aggregator_summary_items', - '#summary_items' => $summary_items, - '#source' => $feed, - '#cache' => array( - 'tags' => $feed->getCacheTag(), - ), - ); - } + $build = $entity_manager->getViewBuilder('aggregator_feed') + ->viewMultiple($feeds, 'summary'); $build['feed_icon'] = array( '#theme' => 'feed_icon', '#url' => 'aggregator/opml', diff --git a/core/modules/aggregator/src/Entity/Feed.php b/core/modules/aggregator/src/Entity/Feed.php index 5a9317a56148e178d0e30eae70e2acc0fbaa7a40..0f2cc781bc4d6373015e88f4cfe4d18664ec8b17 100644 --- a/core/modules/aggregator/src/Entity/Feed.php +++ b/core/modules/aggregator/src/Entity/Feed.php @@ -37,6 +37,7 @@ * "edit-form" = "entity.aggregator_feed.edit_form", * "delete-form" = "entity.aggregator_feed.delete_form", * }, + * field_ui_base_route = "aggregator.admin_overview", * base_table = "aggregator_feed", * fieldable = TRUE, * render_cache = FALSE, @@ -148,7 +149,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setDisplayOptions('form', array( 'type' => 'string_textfield', 'weight' => -5, - )); + )) + ->setDisplayConfigurable('form', TRUE); $fields['langcode'] = BaseFieldDefinition::create('language') ->setLabel(t('Language code')) @@ -161,7 +163,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setDisplayOptions('form', array( 'type' => 'uri', 'weight' => -3, - )); + )) + ->setDisplayConfigurable('form', TRUE); $intervals = array(900, 1800, 3600, 7200, 10800, 21600, 32400, 43200, 64800, 86400, 172800, 259200, 604800, 1209600, 2419200); $period = array_map(array(\Drupal::service('date.formatter'), 'formatInterval'), array_combine($intervals, $intervals)); @@ -176,12 +179,19 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setDisplayOptions('form', array( 'type' => 'options_select', 'weight' => -2, - )); + )) + ->setDisplayConfigurable('form', TRUE); $fields['checked'] = BaseFieldDefinition::create('timestamp') ->setLabel(t('Checked')) ->setDescription(t('Last time feed was checked for new items, as Unix timestamp.')) - ->setDefaultValue(0); + ->setDefaultValue(0) + ->setDisplayOptions('view', array( + 'label' => 'inline', + 'type' => 'timestamp_ago', + 'weight' => 1, + )) + ->setDisplayConfigurable('view', TRUE); $fields['queued'] = BaseFieldDefinition::create('timestamp') ->setLabel(t('Queued')) @@ -189,8 +199,13 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setDefaultValue(0); $fields['link'] = BaseFieldDefinition::create('uri') - ->setLabel(t('Link')) - ->setDescription(t('The link of the feed.')); + ->setLabel(t('URL')) + ->setDescription(t('The link of the feed.')) + ->setDisplayOptions('view', array( + 'label' => 'inline', + 'weight' => 4, + )) + ->setDisplayConfigurable('view', TRUE); $fields['description'] = BaseFieldDefinition::create('string_long') ->setLabel(t('Description')) diff --git a/core/modules/aggregator/src/Entity/Item.php b/core/modules/aggregator/src/Entity/Item.php index 3f6cba0aa618b752878ed80899c9e81bcf07a0e6..5df701358d62e7f0741f529222b8a03418c9e358 100644 --- a/core/modules/aggregator/src/Entity/Item.php +++ b/core/modules/aggregator/src/Entity/Item.php @@ -58,9 +58,15 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setSetting('unsigned', TRUE); $fields['fid'] = BaseFieldDefinition::create('entity_reference') - ->setLabel(t('Aggregator feed ID')) - ->setDescription(t('The ID of the aggregator feed.')) - ->setSetting('target_type', 'aggregator_feed'); + ->setLabel(t('Source feed')) + ->setDescription(t('The aggregator feed entity associated with this item.')) + ->setSetting('target_type', 'aggregator_feed') + ->setDisplayOptions('view', array( + 'label' => 'hidden', + 'type' => 'entity_reference_label', + 'weight' => 0, + )) + ->setDisplayConfigurable('form', TRUE); $fields['title'] = BaseFieldDefinition::create('string') ->setLabel(t('Title')) @@ -72,19 +78,34 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields['link'] = BaseFieldDefinition::create('uri') ->setLabel(t('Link')) - ->setDescription(t('The link of the feed item.')); + ->setDescription(t('The link of the feed item.')) + ->setDisplayOptions('view', array( + 'type' => 'hidden', + )) + ->setDisplayConfigurable('view', TRUE); $fields['author'] = BaseFieldDefinition::create('string') ->setLabel(t('Author')) - ->setDescription(t('The author of the feed item.')); + ->setDescription(t('The author of the feed item.')) + ->setDisplayOptions('view', array( + 'label' => 'hidden', + 'weight' => 3, + )) + ->setDisplayConfigurable('view', TRUE); $fields['description'] = BaseFieldDefinition::create('string_long') ->setLabel(t('Description')) ->setDescription(t('The body of the feed item.')); $fields['timestamp'] = BaseFieldDefinition::create('created') - ->setLabel(t('Posted timestamp')) - ->setDescription(t('Posted date of the feed item, as a Unix timestamp.')); + ->setLabel(t('Posted on')) + ->setDescription(t('Posted date of the feed item, as a Unix timestamp.')) + ->setDisplayOptions('view', array( + 'label' => 'hidden', + 'type' => 'timestamp_ago', + 'weight' => 1, + )) + ->setDisplayConfigurable('view', TRUE); // @todo Convert to a real UUID field in https://drupal.org/node/2149851. $fields['guid'] = BaseFieldDefinition::create('string_long') diff --git a/core/modules/aggregator/src/FeedViewBuilder.php b/core/modules/aggregator/src/FeedViewBuilder.php index 853122fb81de96f790c8d994b981a4f1fb4640a1..26523ab6c44e415cab125b859e35bac9ecf753b3 100644 --- a/core/modules/aggregator/src/FeedViewBuilder.php +++ b/core/modules/aggregator/src/FeedViewBuilder.php @@ -7,21 +7,135 @@ namespace Drupal\aggregator; -use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityViewBuilder; +use Drupal\Core\Config\Config; +use Drupal\Core\Language\LanguageManagerInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Render controller for aggregator feed items. */ class FeedViewBuilder extends EntityViewBuilder { + /** + * Constructs a new FeedViewBuilder. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type definition. + * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager + * The entity manager service. + * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager + * The language manager. + * @param \Drupal\Core\Config\Config $config + * The 'aggregator.settings' config. + */ + public function __construct(EntityTypeInterface $entity_type, EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, Config $config) { + parent::__construct($entity_type, $entity_manager, $language_manager); + $this->config = $config; + } + + /** + * {@inheritdoc} + */ + public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { + return new static( + $entity_type, + $container->get('entity.manager'), + $container->get('language_manager'), + $container->get('config.factory')->get('aggregator.settings') + ); + } + /** * {@inheritdoc} */ - protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langcode) { - $defaults = parent::getBuildDefaults($entity, $view_mode, $langcode); - $defaults['#theme'] = 'aggregator_feed_source'; - return $defaults; + public function buildComponents(array &$build, array $entities, array $displays, $view_mode, $langcode = NULL) { + parent::buildComponents($build, $entities, $displays, $view_mode, $langcode); + + foreach ($entities as $id => $entity) { + $bundle = $entity->bundle(); + $display = $displays[$bundle]; + + if ($display->getComponent('items')) { + // When in summary view mode, respect the list_max setting. + $limit = $view_mode == 'summary' ? $this->config->get('source.list_max') : 20; + // Retrieve the items attached to this feed. + $items = $this->entityManager + ->getStorage('aggregator_item') + ->loadByFeed($entity->id(), $limit); + + $build[$id]['items'] = $this->entityManager + ->getViewBuilder('aggregator_item') + ->viewMultiple($items, $view_mode, $langcode); + + if ($view_mode == 'full') { + // Also add the pager. + $build[$id]['pager'] = array('#theme' => 'pager'); + } + } + + if ($display->getComponent('description')) { + $build[$id]['description'] = array( + '#markup' => aggregator_filter_xss($entity->getDescription()), + '#prefix' => '<div class="feed-description">', + '#suffix' => '</div>', + ); + } + + if ($display->getComponent('image')) { + $image_link = array(); + // Render the image as link if it is available. + $image = $entity->getImage(); + $label = $entity->label(); + $link_href = $entity->getWebsiteUrl(); + if ($image && $label && $link_href) { + $link_title = array( + '#theme' => 'image', + '#path' => $image, + '#alt' => $label, + ); + $image_link = array( + '#type' => 'link', + '#title' => $link_title, + '#href' => $link_href, + '#options' => array( + 'attributes' => array('class' => array('feed-image')), + 'html' => TRUE, + ), + ); + } + $build[$id]['image'] = $image_link; + } + + if ($display->getComponent('feed_icon')) { + $build[$id]['feed_icon'] = array( + '#theme' => 'feed_icon', + '#url' => $entity->getUrl(), + '#title' => t('!title feed', array('!title' => $entity->label())), + ); + } + + if ($display->getComponent('more_link')) { + $title_stripped = strip_tags($entity->label()); + $build[$id]['more_link'] = array( + '#type' => 'link', + '#title' => t('More<span class="visually-hidden"> posts about @title</span>', array( + '@title' => $title_stripped, + )), + '#route_name' => 'entity.aggregator_feed.canonical', + '#route_parameters' => array('aggregator_feed' => $entity->id()), + '#options' => array( + 'html' => TRUE, + 'attributes' => array( + 'title' => $title_stripped, + ), + ), + ); + } + + } } } diff --git a/core/modules/aggregator/src/ItemViewBuilder.php b/core/modules/aggregator/src/ItemViewBuilder.php index 7d3f919eff3b882e2897329c02ca6178af20bc7f..9207b71161d9f2c7eaae38350b4d0321c3a51f15 100644 --- a/core/modules/aggregator/src/ItemViewBuilder.php +++ b/core/modules/aggregator/src/ItemViewBuilder.php @@ -7,7 +7,6 @@ namespace Drupal\aggregator; -use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityViewBuilder; /** @@ -18,14 +17,21 @@ class ItemViewBuilder extends EntityViewBuilder { /** * {@inheritdoc} */ - protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langcode) { - $defaults = parent::getBuildDefaults($entity, $view_mode, $langcode); + public function buildComponents(array &$build, array $entities, array $displays, $view_mode, $langcode = NULL) { + parent::buildComponents($build, $entities, $displays, $view_mode, $langcode); - // Use a different template for the summary view mode. - if ($view_mode == 'summary') { - $defaults['#theme'] = 'aggregator_summary_item'; + foreach ($entities as $id => $entity) { + $bundle = $entity->bundle(); + $display = $displays[$bundle]; + + if ($display->getComponent('description')) { + $build[$id]['description'] = array( + '#markup' => aggregator_filter_xss($entity->getDescription()), + '#prefix' => '<div class="item-description">', + '#suffix' => '</div>', + ); + } } - return $defaults; } } diff --git a/core/modules/aggregator/templates/aggregator-feed-source.html.twig b/core/modules/aggregator/templates/aggregator-feed-source.html.twig deleted file mode 100644 index d492904a0f9546c39bb54f42d81f2755286a1ce9..0000000000000000000000000000000000000000 --- a/core/modules/aggregator/templates/aggregator-feed-source.html.twig +++ /dev/null @@ -1,32 +0,0 @@ -{# -/** - * @file - * Default theme implementation to present the source of the feed. - * - * The contents are rendered below feed listings when browsing source feeds. - * For example, "example.com/aggregator/sources/1". - * - * Available variables: - * - source_icon: Feed icon linked to the source. Rendered through - * feed-icon.html.twig. - * - source_image: Image set by the feed source. - * - source_description: Description set by the feed source. - * - source_url: URL to the feed source. - * - last_checked: How long ago the feed was checked locally. - * - * @see template_preprocess_aggregator_feed_source() - * - * @ingroup themeable - */ -#} -<div{{ attributes }}> - {{ source_icon }} - {{ source_image }} - <p class="feed-description">{{ source_description }}</p> - <dl class="feed-details"> - <dt class="feed-url">{{ 'URL'|t }}</dt> - <dd>{{ source_url }}</dd> - <dt class="feed-updated">{{ 'Updated'|t }}</dt> - <dd>{{ last_checked }}</dd> - </dl> -</div> diff --git a/core/modules/aggregator/templates/aggregator-feed.html.twig b/core/modules/aggregator/templates/aggregator-feed.html.twig new file mode 100644 index 0000000000000000000000000000000000000000..1876c453f41cc882a1b4fce46419763558dceb2f --- /dev/null +++ b/core/modules/aggregator/templates/aggregator-feed.html.twig @@ -0,0 +1,31 @@ +{# +/** + * @file + * Default theme implementation to present an aggregator feed. + * + * The contents are rendered above feed listings when browsing source feeds. + * For example, "example.com/aggregator/sources/1". + * + * Available variables: + * - title: Title of the feed item. + * - content: All field items. Use {{ content }} to print them all, + * or print a subset such as {{ content.field_example }}. Use + * {{ content|without('field_example') }} to temporarily suppress the printing + * of a given element. + * + * @see template_preprocess_aggregator_feed() + * + * @ingroup themeable + */ +#} +<div{{ attributes.addClass('aggregator-feed') }}> + + {{ title_prefix }} + {% if not full %} + <h2{{ title_attributes }}>{{ title }}</h2> + {% endif %} + {{ title_suffix }} + + {{ content }} + +</div> diff --git a/core/modules/aggregator/templates/aggregator-item.html.twig b/core/modules/aggregator/templates/aggregator-item.html.twig index 8c23106fbd6c12df75f07e780ac284ebaed12df2..8f31937e28e033f16478d0f7feacc74d792ec38d 100644 --- a/core/modules/aggregator/templates/aggregator-item.html.twig +++ b/core/modules/aggregator/templates/aggregator-item.html.twig @@ -4,33 +4,25 @@ * Default theme implementation to present a feed item in an aggregator page. * * Available variables: - * - feed_url: URL to the originating feed item. - * - feed_title: Title of the feed item. - * - source_url: Link to the local source section. - * - source_title: Title of the remote source. - * - source_date: Date the feed was posted on the remote source. - * - content: Feed item content. + * - url: URL to the originating feed item. + * - title: Title of the feed item. + * - content: All field items. Use {{ content }} to print them all, + * or print a subset such as {{ content.field_example }}. Use + * {{ content|without('field_example') }} to temporarily suppress the printing + * of a given element. * * @see template_preprocess_aggregator_item() * * @ingroup themeable */ #} -<div{{ attributes }}> +<div{{ attributes.addClass('aggregator-item') }}> + {{ title_prefix }} <h3 class="feed-item-title"> - <a href="{{ feed_url }}">{{ feed_title }}</a> + <a href="{{ url }}">{{ title }}</a> </h3> + {{ title_suffix }} - <div class="feed-item-meta"> - {% if source_url %} - <a href="{{ source_url }}" class="feed-item-source">{{ source_title }}</a> - - {% endif %} - <span class="feed-item-date">{{ source_date }}</span> - </div> + {{ content }} - {% if content %} - <div class="feed-item-body"> - {{ content }} - </div> - {% endif %} </div> diff --git a/core/modules/aggregator/templates/aggregator-summary-item.html.twig b/core/modules/aggregator/templates/aggregator-summary-item.html.twig deleted file mode 100644 index 3cb227908245181376ececc0a9202f2e6267cfaf..0000000000000000000000000000000000000000 --- a/core/modules/aggregator/templates/aggregator-summary-item.html.twig +++ /dev/null @@ -1,15 +0,0 @@ -{# -/** - * @file - * Default theme implementation for a single feed in a list of feed items. - * - * Available variables: - * - url: URL of item. - * - age: Age of the item. - * - * @see template_preprocess_aggregator_summary_item() - * - * @ingroup themeable - */ -#} -{{ url }} {{ age }} diff --git a/core/modules/aggregator/templates/aggregator-summary-items.html.twig b/core/modules/aggregator/templates/aggregator-summary-items.html.twig deleted file mode 100644 index a00b8dc3d2b21feb778b114e3e458e536737402e..0000000000000000000000000000000000000000 --- a/core/modules/aggregator/templates/aggregator-summary-items.html.twig +++ /dev/null @@ -1,23 +0,0 @@ -{# -/** - * @file - * Default theme implementation to present feeds as list items. - * - * Each iteration generates a single feed source. - * - * Available variables: - * - title: Title of the feed. - * - summary_list: Unordered list of linked feed items generated through - * theme_item_list(). - * - source_url: URL to the local source. - * - * @see template_preprocess_aggregator_summary_items() - * - * @ingroup themeable - */ -#} -<h3>{{ title }}</h3> -{{ summary_list }} -<div class="links"> - <a href="{{ source_url }}">{% trans %}More<span class="visually-hidden"> posts about {{ title|placeholder }}</span>{% endtrans %}</a> -</div>