diff --git a/core/modules/aggregator/aggregator.admin.inc b/core/modules/aggregator/aggregator.admin.inc index 3715a5f..a5478a4 100644 --- a/core/modules/aggregator/aggregator.admin.inc +++ b/core/modules/aggregator/aggregator.admin.inc @@ -42,7 +42,8 @@ function aggregator_view() { $links = array(); $links['edit'] = array( 'title' => t('Edit'), - 'href' => "admin/config/services/aggregator/edit/feed/$feed->fid", + 'href' => "aggregator/sources/$feed->fid/edit", + 'query' => drupal_get_destination(), ); $links['remove'] = array( 'title' => t('Remove items'), @@ -63,31 +64,41 @@ function aggregator_view() { } $output .= theme('table', array('header' => $header, 'rows' => $rows, 'empty' => t('No feeds available. Add feed.', array('@link' => url('admin/config/services/aggregator/add/feed'))))); - $result = db_query('SELECT c.cid, c.title, COUNT(ci.iid) as items FROM {aggregator_category} c LEFT JOIN {aggregator_category_item} ci ON c.cid = ci.cid GROUP BY c.cid, c.title ORDER BY title'); - - $output .= '

' . t('Category overview') . '

'; - - $header = array(t('Title'), t('Items'), t('Operations')); - $rows = array(); - foreach ($result as $category) { - $row = array(); - $row[] = l($category->title, "aggregator/categories/$category->cid"); - $row[] = format_plural($category->items, '1 item', '@count items'); - $links = array(); - $links['edit'] = array( - 'title' => t('Edit'), - 'href' => "admin/config/services/aggregator/edit/category/$category->cid", - ); - $row[] = array( - 'data' => array( - '#type' => 'operations', - '#links' => $links, - ), - ); - $rows[] = $row; + // Only show the categories table if we actually have an aggregator_categories + // vocabulary. If user deletes it, no point giving a broken link. + if (taxonomy_vocabulary_load('aggregator_categories')) { + $result = entity_query('taxonomy_term') + ->condition('vid', 'aggregator_categories') + ->execute(); + + $rows = array(); + if ($result) { + foreach (entity_load_multiple('taxonomy_term', $result) as $category) { + $row = array(); + $items = entity_query('aggregator_item') + ->condition('field_aggregator_categories.target_id', $category->id()) + ->execute(); + $row[] = l($category->label(), 'aggregator/categories/' . $category->id()); + $row[] = format_plural(count($items), '1 item', '@count items'); + $links = array(); + $links['edit'] = array( + 'title' => t('Edit'), + 'href' => 'taxonomy/term/' . $category->id() . '/edit', + 'query' => drupal_get_destination(), + ); + $row[] = array( + 'data' => array( + '#type' => 'operations', + '#links' => $links, + ), + ); + $rows[] = $row; + } + } + $output .= '

' . t('Category overview') . '

'; + $header = array(t('Title'), t('Items'), t('Operations')); + $output .= theme('table', array('header' => $header, 'rows' => $rows, 'empty' => t('No categories available. Add category.', array('@link' => url('admin/structure/taxonomy/aggregator_categories/add'))))); } - $output .= theme('table', array('header' => $header, 'rows' => $rows, 'empty' => t('No categories available. Add category.', array('@link' => url('admin/config/services/aggregator/add/category'))))); - return $output; } @@ -144,7 +155,11 @@ function aggregator_form_opml($form, &$form_state) { ); // Handling of categories. - $options = array_map('check_plain', db_query("SELECT cid, title FROM {aggregator_category} ORDER BY title")->fetchAllKeyed()); + $options = array(); + $categories = entity_load_multiple_by_properties('taxonomy_term', array('vid' => 'aggregator_categories')); + foreach($categories as $category) { + $options[$category->id()] = check_plain($category->label()); + } if ($options) { $form['category'] = array( '#type' => 'checkboxes', @@ -227,7 +242,7 @@ function aggregator_form_opml_submit($form, &$form_state) { 'refresh' => $form_state['values']['refresh'], 'block' => $form_state['values']['block'], )); - $new_feed->categories = $form_state['values']['category']; + $new_feed->field_aggregator_categories->setValue(array_filter($form_state['values']['category'])); $new_feed->save(); } @@ -397,108 +412,3 @@ function aggregator_admin_form_submit($form, &$form_state) { } $config->save(); } - -/** - * Form constructor to add/edit/delete aggregator categories. - * - * @param $edit - * An object containing: - * - title: A string to use for the category title. - * - description: A string to use for the category description. - * - cid: The category ID. - * - * @ingroup forms - * @see aggregator_menu() - * @see aggregator_form_category_validate() - * @see aggregator_form_category_submit() - */ -function aggregator_form_category($form, &$form_state, $edit = NULL) { - $form['title'] = array('#type' => 'textfield', - '#title' => t('Title'), - '#default_value' => isset($edit->title) ? $edit->title : '', - '#maxlength' => 64, - '#required' => TRUE, - ); - $form['description'] = array('#type' => 'textarea', - '#title' => t('Description'), - '#default_value' => isset($edit->description) ? $edit->description : '', - ); - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save')); - if (!empty($edit->cid)) { - $form['actions']['delete'] = array('#type' => 'submit', '#value' => t('Delete')); - $form['cid'] = array('#type' => 'hidden', '#value' => $edit->cid); - } - - return $form; -} - -/** - * Form validation handler for aggregator_form_category(). - * - * @see aggregator_form_category_submit() - */ -function aggregator_form_category_validate($form, &$form_state) { - if ($form_state['values']['op'] == t('Save')) { - // Check for duplicate titles - if (isset($form_state['values']['cid'])) { - $category = db_query("SELECT cid FROM {aggregator_category} WHERE title = :title AND cid <> :cid", array(':title' => $form_state['values']['title'], ':cid' => $form_state['values']['cid']))->fetchObject(); - } - else { - $category = db_query("SELECT cid FROM {aggregator_category} WHERE title = :title", array(':title' => $form_state['values']['title']))->fetchObject(); - } - if ($category) { - form_set_error('title', t('A category named %category already exists. Enter a unique title.', array('%category' => $form_state['values']['title']))); - } - } -} - -/** - * Form submission handler for aggregator_form_category(). - * - * @see aggregator_form_category_validate() - * - * @todo Add delete confirmation dialog. - */ -function aggregator_form_category_submit($form, &$form_state) { - // @todo Replicate this cache invalidation when these ops are separated. - // Invalidate the block cache to update aggregator category-based derivatives. - if (module_exists('block')) { - drupal_container()->get('plugin.manager.block')->clearCachedDefinitions(); - } - if ($form_state['values']['op'] == t('Delete')) { - $title = $form_state['values']['title']; - // Unset the title. - unset($form_state['values']['title']); - } - aggregator_save_category($form_state['values']); - if (isset($form_state['values']['cid'])) { - if (isset($form_state['values']['title'])) { - drupal_set_message(t('The category %category has been updated.', array('%category' => $form_state['values']['title']))); - if (arg(0) == 'admin') { - $form_state['redirect'] = 'admin/config/services/aggregator/'; - return; - } - else { - $form_state['redirect'] = 'aggregator/categories/' . $form_state['values']['cid']; - return; - } - } - else { - watchdog('aggregator', 'Category %category deleted.', array('%category' => $title)); - drupal_set_message(t('The category %category has been deleted.', array('%category' => $title))); - if (arg(0) == 'admin') { - $form_state['redirect'] = 'admin/config/services/aggregator/'; - return; - } - else { - $form_state['redirect'] = 'aggregator/categories/'; - return; - } - } - } - else { - watchdog('aggregator', 'Category %category added.', array('%category' => $form_state['values']['title']), WATCHDOG_NOTICE, l(t('view'), 'admin/config/services/aggregator')); - drupal_set_message(t('The category %category has been added.', array('%category' => $form_state['values']['title']))); - } -} diff --git a/core/modules/aggregator/aggregator.info.yml b/core/modules/aggregator/aggregator.info.yml index 4cade24..16f80c3 100644 --- a/core/modules/aggregator/aggregator.info.yml +++ b/core/modules/aggregator/aggregator.info.yml @@ -6,3 +6,5 @@ core: 8.x configure: admin/config/services/aggregator/settings dependencies: - file + - entity_reference + - taxonomy diff --git a/core/modules/aggregator/aggregator.install b/core/modules/aggregator/aggregator.install index 54f9053..26ec606 100644 --- a/core/modules/aggregator/aggregator.install +++ b/core/modules/aggregator/aggregator.install @@ -5,6 +5,8 @@ * Install, update and uninstall functions for the aggregator module. */ +use Drupal\Component\Uuid\Uuid; + /** * Implements hook_requirements(). */ @@ -27,97 +29,6 @@ function aggregator_requirements($phase) { * Implements hook_schema(). */ function aggregator_schema() { - $schema['aggregator_category'] = array( - 'description' => 'Stores categories for aggregator feeds and feed items.', - 'fields' => array( - 'cid' => array( - 'type' => 'serial', - 'not null' => TRUE, - 'description' => 'Primary Key: Unique aggregator category ID.', - ), - 'title' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => 'Title of the category.', - ), - 'description' => array( - 'type' => 'text', - 'not null' => TRUE, - 'size' => 'big', - 'description' => 'Description of the category', - ), - 'block' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'size' => 'tiny', - 'description' => 'The number of recent items to show within the category block.', - ) - ), - 'primary key' => array('cid'), - 'unique keys' => array( - 'title' => array('title'), - ), - ); - - $schema['aggregator_category_feed'] = array( - 'description' => 'Bridge table; maps feeds to categories.', - 'fields' => array( - 'fid' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => "The feed's {aggregator_feed}.fid.", - ), - 'cid' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The {aggregator_category}.cid to which the feed is being assigned.', - ) - ), - 'primary key' => array('cid', 'fid'), - 'indexes' => array( - 'fid' => array('fid'), - ), - 'foreign keys' => array( - 'aggregator_category' => array( - 'table' => 'aggregator_category', - 'columns' => array('cid' => 'cid'), - ), - ), - ); - - $schema['aggregator_category_item'] = array( - 'description' => 'Bridge table; maps feed items to categories.', - 'fields' => array( - 'iid' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => "The feed item's {aggregator_item}.iid.", - ), - 'cid' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The {aggregator_category}.cid to which the feed item is being assigned.', - ) - ), - 'primary key' => array('cid', 'iid'), - 'indexes' => array( - 'iid' => array('iid'), - ), - 'foreign keys' => array( - 'aggregator_category' => array( - 'table' => 'aggregator_category', - 'columns' => array('cid' => 'cid'), - ), - ), - ); - $schema['aggregator_feed'] = array( 'description' => 'Stores feeds to be parsed by the aggregator.', 'fields' => array( @@ -291,6 +202,37 @@ function aggregator_schema() { } /** + * Implements hook_install(). + */ +function aggregator_install() { + // Attach category field to feeds and items. + aggregator_add_category_field('aggregator_feed', 'aggregator_feed'); + aggregator_add_category_field('aggregator_item', 'aggregator_item'); +} + +/** + * Implements hook_uninstall(). + */ +function aggregator_uninstall() { + // Delete category reference field. + field_delete_field('aggregator_categories'); + // Remove feed and items field instances. + field_attach_delete_bundle('aggregator_feed', 'aggregator_feed'); + field_attach_delete_bundle('aggregator_item', 'aggregator_item'); +} + +/** + * Implements hook_update_dependencies(). + */ +function aggregator_update_dependencies() { + // Migrate aggregator categories after vid has changed to string. + $dependencies['aggregator'][8003] = array( + 'taxonomy' => 8006, + ); + return $dependencies; +} + +/** * Moves aggregator settings from variables to config. * * @ingroup config_upgrade @@ -304,7 +246,6 @@ function aggregrator_update_8000() { 'aggregator_teaser_length' => 'items.teaser_length', 'aggregator_clear' => 'items.expire', 'aggregator_summary_items' => 'source.list_max', - 'aggregator_category_selector' => 'source.category_selector', )); } @@ -330,3 +271,171 @@ function aggregator_update_8001() { 'initial' => LANGUAGE_DEFAULT, )); } + +/** + * Enables entity reference module and attaches aggregator category field. + */ +function aggregator_update_8002() { + // Enable taxonomy and entity_reference modules to migrate categories to. + $modules = array('entity_reference'); + if (!module_exists('taxonomy')) { + $modules[] = 'taxonomy'; + } + update_module_enable($modules); + + // Import aggregator_categories vocabulary. + config_install_default_config('module', 'aggregator'); + + // Create the entity reference field. + $field = array( + 'field_name' => 'field_aggregator_categories', + 'type' => 'entity_reference', + 'cardinality' => -1, + 'entity_types' => array('aggregator_feed', 'aggregator_item'), + 'module' => 'entity_reference', + 'settings' => array( + 'target_type' => 'taxonomy_term', + ), + ); + _update_7000_field_create_field($field); + + $selector = update_variable_get('aggregator_category_selector', 'checkboxes'); + $widget = array( + 'module' => 'options', + 'type' => 'options_buttons', + 'settings' => array(), + 'weight' => 0, + ); + if ($selector == 'select') { + $widget['type'] = 'options_select'; + } + + // Attach the field to aggregator_feeds. + $instance = array( + 'field_name' => 'field_aggregator_categories', + 'entity_type' => 'aggregator_feed', + 'bundle' => 'aggregator_feed', + 'label' => 'Category', + 'settings' => array( + 'handler' => 'default', + 'handler_settings' => array( + 'target_bundles' => array( + 'aggregator_categories' => 'aggregator_categories', + ), + 'sort' => array( + 'field' => 'weight', + 'direction' => 'ASC', + ), + ), + ), + 'widget' => $widget, + ); + _update_7000_field_create_instance($field, $instance); + + // Attach the field to aggregator_items. + $instance['entity_type'] = 'aggregator_item'; + $instance['bundle'] = 'aggregator_item'; + _update_7000_field_create_instance($field, $instance); + + // Delete old variable. + update_variable_del('aggregator_category_selector'); +} + +/** + * Migrates old categories to entity reference fields. + */ +function aggregator_update_8003(&$sandbox) { + if (!isset($sandbox['progress'])) { + $sandbox['progress'] = 0; + $sandbox['current_cid'] = 0; + $sandbox['max'] = db_query('SELECT COUNT(cid) FROM {aggregator_category}')->fetchField(); + } + $categories = db_select('aggregator_category', 'ac') + ->fields('ac') + ->range(0, 5) + ->condition('cid', $sandbox['current_cid'], '>') + ->orderBy('cid', 'ASC') + ->execute(); + + $uuid = new Uuid(); + foreach ($categories as $category) { + $term = array( + 'vid' => 'aggregator_categories', + 'uuid' => $uuid->generate(), + 'name' => $category->title, + 'description' => $category->description, + 'langcode' => LANGUAGE_NOT_SPECIFIED, + 'format' => config('filter.settings')->get('fallback_format'), + ); + $term['tid'] = db_insert('taxonomy_term_data') + ->fields($term) + ->execute(); + + // Add {taxonomy_term_hierarchy} entry. + db_insert('taxonomy_term_hierarchy') + ->fields(array('tid' => $term['tid'])) + ->execute(); + + // Make sure delta is always different, since we might have more than one + // categories per feed / feed item. + $delta = $category->cid; + + // Find feeds assigned to this category. + $fids = db_query('SELECT fid FROM {aggregator_category_feed} WHERE cid = :cid', array(':cid' => $category->cid)); + foreach ($fids->fetchCol() as $fid) { + // Add a row to the field data and revision tables. + db_insert('field_data_field_aggregator_categories')->fields(array( + 'entity_type' => 'aggregator_feed', + 'bundle' => 'aggregator_feed', + 'entity_id' => $fid, + 'revision_id' => $fid, + 'langcode' => LANGUAGE_NOT_SPECIFIED, + 'delta' => $delta, + 'field_aggregator_categories_target_id' => $term['tid'], + 'field_aggregator_categories_revision_id' => $term['tid'], + ))->execute(); + db_insert('field_revision_field_aggregator_categories')->fields(array( + 'entity_type' => 'aggregator_feed', + 'bundle' => 'aggregator_feed', + 'entity_id' => $fid, + 'revision_id' => $fid, + 'langcode' => LANGUAGE_NOT_SPECIFIED, + 'delta' => $delta, + 'field_aggregator_categories_target_id' => $term['tid'], + 'field_aggregator_categories_revision_id' => $term['tid'], + ))->execute(); + } + // Find items assigned to this category. + $iids = db_query('SELECT iid FROM {aggregator_category_item} WHERE cid = :cid', array(':cid' => $category->cid)); + foreach ($iids->fetchCol() as $iid) { + // Add a row to the field data and revision tables. + db_insert('field_data_field_aggregator_categories') + ->fields(array( + 'entity_type' => 'aggregator_item', + 'bundle' => 'aggregator_item', + 'entity_id' => $iid, + 'revision_id' => $iid, + 'langcode' => LANGUAGE_NOT_SPECIFIED, + 'delta' => $delta, + 'field_aggregator_categories_target_id' => $term['tid'], + 'field_aggregator_categories_revision_id' => $term['tid'], + )) + ->execute(); + db_insert('field_revision_field_aggregator_categories') + ->fields(array( + 'entity_type' => 'aggregator_item', + 'bundle' => 'aggregator_item', + 'entity_id' => $iid, + 'revision_id' => $iid, + 'langcode' => LANGUAGE_NOT_SPECIFIED, + 'delta' => $delta, + 'field_aggregator_categories_target_id' => $term['tid'], + 'field_aggregator_categories_revision_id' => $term['tid'], + )) + ->execute(); + } + $sandbox['progress']++; + $sandbox['current_cid'] = $category->cid; + } + $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']); +} diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module index deed583..3aa544c 100644 --- a/core/modules/aggregator/aggregator.module +++ b/core/modules/aggregator/aggregator.module @@ -38,9 +38,9 @@ function aggregator_help($path, $arg) { $output = '

' . t('Thousands of sites (particularly news sites and blogs) publish their latest headlines and posts in feeds, using a number of standardized XML-based formats. Formats supported by the aggregator include RSS, RDF, and Atom.', array('@rss' => 'http://cyber.law.harvard.edu/rss/', '@rdf' => 'http://www.w3.org/RDF/', '@atom' => 'http://www.atomenabled.org')) . '

'; $output .= '

' . t('Current feeds are listed below, and new feeds may be added. For each feed or feed category, the latest items block may be enabled at the blocks administration page.', array('@addfeed' => url('admin/config/services/aggregator/add/feed'), '@block' => url('admin/structure/block'))) . '

'; return $output; - case 'admin/config/services/aggregator/add/feed': + case 'aggregator/sources/add': return '

' . t('Add a feed in RSS, RDF or Atom format. A feed may only have one entry.') . '

'; - case 'admin/config/services/aggregator/add/category': + case 'admin/structure/taxonomy/aggregator_categories/add': return '

' . t('Categories allow feed items from different feeds to be grouped together. For example, several sport-related feeds may belong to a category named Sports. Feed items may be grouped automatically (by selecting a category when creating or editing a feed) or manually (via the Categorize page available from feed item listings). Each category provides its own feed page and block.') . '

'; case 'admin/config/services/aggregator/add/opml': return '

' . t('OPML is an XML format used to exchange multiple feeds between aggregators. A single OPML document may contain a collection of many feeds. Drupal can parse such a file and import all feeds at once, saving you the effort of adding them manually. You may either upload a local file from your computer or enter a URL where Drupal can download it.') . '

'; @@ -53,7 +53,7 @@ function aggregator_help($path, $arg) { function aggregator_theme() { return array( 'aggregator_categorize_items' => array( - 'render element' => 'form', + 'variables' => array('items' => NULL, 'categories' => NULL), 'file' => 'aggregator.pages.inc', ), 'aggregator_feed_source' => array( @@ -83,7 +83,7 @@ function aggregator_theme() { 'file' => 'aggregator.pages.inc', ), 'aggregator_page_rss' => array( - 'variables' => array('feeds' => NULL, 'category' => NULL), + 'variables' => array('items' => NULL, 'category' => NULL), 'file' => 'aggregator.pages.inc', ), ); @@ -101,21 +101,6 @@ function aggregator_menu() { 'weight' => 10, 'file' => 'aggregator.admin.inc', ); - $items['admin/config/services/aggregator/add/feed'] = array( - 'title' => 'Add feed', - 'page callback' => 'aggregator_feed_add', - 'access arguments' => array('administer news feeds'), - 'type' => MENU_LOCAL_ACTION, - 'file' => 'aggregator.admin.inc', - ); - $items['admin/config/services/aggregator/add/category'] = array( - 'title' => 'Add category', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('aggregator_form_category'), - 'access arguments' => array('administer news feeds'), - 'type' => MENU_LOCAL_ACTION, - 'file' => 'aggregator.admin.inc', - ); $items['admin/config/services/aggregator/add/opml'] = array( 'title' => 'Import OPML', 'page callback' => 'drupal_get_form', @@ -156,18 +141,6 @@ function aggregator_menu() { 'weight' => 5, 'file' => 'aggregator.pages.inc', ); - $items['aggregator/sources'] = array( - 'title' => 'Sources', - 'page callback' => 'aggregator_page_sources', - 'access arguments' => array('access news feeds'), - 'file' => 'aggregator.pages.inc', - ); - $items['aggregator/categories'] = array( - 'title' => 'Categories', - 'page callback' => 'aggregator_page_categories', - 'access callback' => '_aggregator_has_categories', - 'file' => 'aggregator.pages.inc', - ); $items['aggregator/rss'] = array( 'title' => 'RSS feed', 'page callback' => 'aggregator_page_rss', @@ -182,19 +155,25 @@ function aggregator_menu() { 'type' => MENU_CALLBACK, 'file' => 'aggregator.pages.inc', ); - $items['aggregator/categories/%aggregator_category'] = array( - 'title callback' => '_aggregator_category_title', + $items['aggregator/categories'] = array( + 'title' => 'Categories', + 'page callback' => 'aggregator_page_categories', + 'access callback' => '_aggregator_has_categories', + 'file' => 'aggregator.pages.inc', + ); + $items['aggregator/categories/%taxonomy_term'] = array( + 'title callback' => 'taxonomy_term_title', 'title arguments' => array(2), 'page callback' => 'aggregator_page_category', 'page arguments' => array(2), 'access arguments' => array('access news feeds'), 'file' => 'aggregator.pages.inc', ); - $items['aggregator/categories/%aggregator_category/view'] = array( + $items['aggregator/categories/%taxonomy_term/view'] = array( 'title' => 'View', 'type' => MENU_DEFAULT_LOCAL_TASK, ); - $items['aggregator/categories/%aggregator_category/categorize'] = array( + $items['aggregator/categories/%taxonomy_term/categorize'] = array( 'title' => 'Categorize', 'page callback' => 'drupal_get_form', 'page arguments' => array('aggregator_page_category_form', 2), @@ -202,13 +181,16 @@ function aggregator_menu() { 'type' => MENU_LOCAL_TASK, 'file' => 'aggregator.pages.inc', ); - $items['aggregator/categories/%aggregator_category/configure'] = array( - 'title' => 'Configure', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('aggregator_form_category', 2), + $items['aggregator/sources'] = array( + 'title' => 'Sources', + 'page callback' => 'aggregator_page_sources', + 'access arguments' => array('access news feeds'), + 'file' => 'aggregator.pages.inc', + ); + $items['aggregator/sources/add'] = array( + 'title' => 'Add feed', + 'page callback' => 'aggregator_feed_add', 'access arguments' => array('administer news feeds'), - 'type' => MENU_LOCAL_TASK, - 'weight' => 10, 'file' => 'aggregator.admin.inc', ); $items['aggregator/sources/%aggregator_feed'] = array( @@ -223,55 +205,26 @@ function aggregator_menu() { 'title' => 'View', 'type' => MENU_DEFAULT_LOCAL_TASK, ); - $items['aggregator/sources/%aggregator_feed/categorize'] = array( - 'title' => 'Categorize', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('aggregator_page_source_form', 2), - 'access arguments' => array('administer news feeds'), - 'type' => MENU_LOCAL_TASK, - 'file' => 'aggregator.pages.inc', - ); - $items['aggregator/sources/%aggregator_feed/configure'] = array( - 'title' => 'Configure', + $items['aggregator/sources/%aggregator_feed/edit'] = array( + 'title' => 'Edit', 'page callback' => 'entity_get_form', 'page arguments' => array(2), 'access arguments' => array('administer news feeds'), 'type' => MENU_LOCAL_TASK, - 'weight' => 10, - 'file' => 'aggregator.admin.inc', - ); - $items['admin/config/services/aggregator/edit/feed/%aggregator_feed'] = array( - 'title' => 'Edit feed', - 'page callback' => 'entity_get_form', - 'page arguments' => array(6), - 'access arguments' => array('administer news feeds'), - 'file' => 'aggregator.admin.inc', ); - $items['admin/config/services/aggregator/edit/category/%aggregator_category'] = array( - 'title' => 'Edit category', + $items['aggregator/sources/%aggregator_feed/categorize'] = array( + 'title' => 'Categorize', 'page callback' => 'drupal_get_form', - 'page arguments' => array('aggregator_form_category', 6), + 'page arguments' => array('aggregator_page_source_form', 2), 'access arguments' => array('administer news feeds'), - 'file' => 'aggregator.admin.inc', + 'type' => MENU_LOCAL_TASK, + 'file' => 'aggregator.pages.inc', ); return $items; } /** - * Title callback: Returns a title for aggregator category pages. - * - * @param $category - * An aggregator category. - * - * @return - * A string with the aggregator category title. - */ -function _aggregator_category_title($category) { - return $category->title; -} - -/** * Access callback: Determines whether there are any aggregator categories. * * @return @@ -279,7 +232,13 @@ function _aggregator_category_title($category) { * FALSE otherwise. */ function _aggregator_has_categories() { - return user_access('access news feeds') && (bool) db_query_range('SELECT 1 FROM {aggregator_category}', 0, 1)->fetchField(); + $categories = array(); + if ($vocabulary = taxonomy_vocabulary_load('aggregator_categories')) { + $categories = entity_query('taxonomy_term') + ->condition('vid', $vocabulary->id()) + ->execute(); + } + return user_access('access news feeds') && !empty($categories); } /** @@ -338,53 +297,134 @@ function aggregator_queue_info() { } /** - * Adds/edits/deletes aggregator categories. - * - * @param $edit - * An associative array describing the category to be added/edited/deleted. + * Implements hook_entity_bundle_info(). */ -function aggregator_save_category($edit) { - $link_path = 'aggregator/categories/'; - if (!empty($edit['cid'])) { - $link_path .= $edit['cid']; - if (!empty($edit['title'])) { - db_merge('aggregator_category') - ->key(array('cid' => $edit['cid'])) - ->fields(array( - 'title' => $edit['title'], - 'description' => $edit['description'], - )) - ->execute(); - $op = 'update'; +function aggregator_entity_bundle_info() { + $bundles['aggregator_feed']['aggregator_feed'] = array( + 'label' => t('Aggregator feed'), + 'admin' => array( + 'path' => 'admin/config/services/aggregator', + ), + ); + $bundles['aggregator_item']['aggregator_item'] = array( + 'label' => t('Aggregator item'), + ); + return $bundles; +} + +/** + * Implements hook_admin_paths(). + */ +function aggregator_admin_paths() { + $paths = array( + 'aggregator/sources/add' => TRUE, + 'aggregator/sources/add/*' => TRUE, + 'aggregator/sources/*/edit' => TRUE, + 'aggregator/sources/*/categorize' => TRUE, + 'aggregator/sources/*/delete' => TRUE, + 'aggregator/sources/*/translations' => TRUE, + 'aggregator/sources/*/translations/*' => TRUE, + ); + return $paths; +} + +/** + * Implements hook_menu_local_tasks(). + */ +function aggregator_menu_local_tasks(&$data, $router_item, $root_path) { + // Add action link to 'aggregator/sources/add' and + // 'admin/structure/taxonomy/aggregator_categories/add' + // on 'admin/config/services/aggregator' page. + if ($root_path == 'admin/config/services/aggregator') { + $item = menu_get_item('admin/structure/taxonomy/aggregator_categories/add'); + if ($item && $item['access']) { + $item['title'] = t('Add category'); + $data['actions'][] = array( + '#theme' => 'menu_local_action', + '#link' => $item, + ); } - else { - db_delete('aggregator_category') - ->condition('cid', $edit['cid']) - ->execute(); - // Make sure there is no active block for this category. - if (module_exists('block')) { - foreach (entity_load_multiple_by_properties('block', array('plugin' => 'aggregator_category_block:' . $edit['cid'])) as $block) { - $block->delete(); - } - } - $edit['title'] = ''; - $op = 'delete'; + $item = menu_get_item('aggregator/sources/add'); + if ($item['access']) { + $data['actions'][] = array( + '#theme' => 'menu_local_action', + '#link' => $item, + ); } } - elseif (!empty($edit['title'])) { - // A single unique id for bundles and feeds, to use in blocks. - $link_path .= db_insert('aggregator_category') - ->fields(array( - 'title' => $edit['title'], - 'description' => $edit['description'], - 'block' => 5, - )) - ->execute(); - $op = 'insert'; + // Add edit link to 'taxonomy/term/*/edit' + // on pages that start with 'aggregator/categories/%'. + if (strpos($root_path, 'aggregator/categories/%') === 0 && $term = reset($router_item['page_arguments'])) { + $item = menu_get_item('taxonomy/term/' . $term->id() . '/edit'); + if ($item['access']) { + $data['tabs'][0][] = array( + '#theme' => 'menu_local_task', + '#link' => $item, + ); + } + } +} + +/** + * Adds the default category reference field to an entity. + * + * @todo Use entity_reference_create_instance() when it allows to pass widget + * info, more than one (or none) allowed entity types and cardinality. + * + * @param string $bundle + * The bundle to attach this field too. + * @param string $label + * (optional) The label for the body instance. Defaults to 'Category' + * + * @return array + * Category field instance. + */ +function aggregator_add_category_field($entity_type, $bundle, $label = 'Category') { + + // Look for or add the specified field to the requested entity bundle. + $field = field_info_field('field_aggregator_categories'); + $instance = field_info_instance($entity_type, 'field_aggregator_categories', $bundle); + + if (empty($field)) { + $field = array( + 'field_name' => 'field_aggregator_categories', + 'type' => 'entity_reference', + 'cardinality' => FIELD_CARDINALITY_UNLIMITED, + 'entity_types' => array('aggregator_feed', 'aggregator_item'), + 'settings' => array( + 'target_type' => 'taxonomy_term', + ), + ); + field_create_field($field); } - if (isset($op) && module_exists('menu_link')) { - menu_link_maintain('aggregator', $op, $link_path, $edit['title']); + + if (empty($instance)) { + $instance = array( + 'field_name' => 'field_aggregator_categories', + 'entity_type' => $entity_type, + 'bundle' => $bundle, + 'label' => $label, + 'settings' => array( + 'handler' => 'default', + 'handler_settings' => array( + 'target_bundles' => array( + 'aggregator_categories' => 'aggregator_categories', + ), + 'sort' => array( + 'field' => 'weight', + 'direction' => 'ASC', + ), + ), + ), + 'widget' => array( + 'module' => 'options', + 'type' => 'options_buttons', + ), + ); + field_create_instance($instance); } + + return $instance; } /** @@ -508,24 +548,6 @@ function aggregator_feed_load($fid) { } /** - * Loads an aggregator category. - * - * @param $cid - * The category id. - * - * @return - * An associative array describing the category. - */ -function aggregator_category_load($cid) { - $categories = &drupal_static(__FUNCTION__); - if (!isset($categories[$cid])) { - $categories[$cid] = db_query('SELECT * FROM {aggregator_category} WHERE cid = :cid', array(':cid' => $cid))->fetchObject(); - } - - return $categories[$cid]; -} - -/** * Returns HTML for an individual feed item for display in the block. * * @param $variables diff --git a/core/modules/aggregator/aggregator.pages.inc b/core/modules/aggregator/aggregator.pages.inc index 4310d79..6063552 100644 --- a/core/modules/aggregator/aggregator.pages.inc +++ b/core/modules/aggregator/aggregator.pages.inc @@ -38,8 +38,6 @@ function aggregator_page_last() { function aggregator_page_source(Feed $feed) { $feed_source = entity_view($feed, 'default'); - // It is safe to include the fid in the query because it's loaded from the - // database by aggregator_feed_load(). $items = aggregator_load_feed_items('source', $feed); return _aggregator_page_list($items, arg(3), $feed_source); @@ -75,10 +73,8 @@ function aggregator_page_source_form($form, $form_state, $feed) { * @ingroup forms */ function aggregator_page_category($category) { - drupal_add_feed('aggregator/rss/' . $category->cid, config('system.site')->get('name') . ' ' . t('aggregator - @title', array('@title' => $category->title))); + drupal_add_feed('aggregator/rss/' . $category->id(), config('system.site')->get('name') . ' ' . t('aggregator - @title', array('@title' => $category->label()))); - // It is safe to include the cid in the query because it's loaded from the - // database by aggregator_category_load(). $items = aggregator_load_feed_items('category', $category); return _aggregator_page_list($items, arg(3)); @@ -113,10 +109,8 @@ function aggregator_page_category_form($form, $form_state, $category) { * @param $data * Feed or category data used for filtering. The type and value of $data * depends on $type: - * - source: $data is an object with $data->fid identifying the feed used to - * as filter. - * - category: $data is an array with $data['cid'] being the category id to - * filter on. + * - source: $data is an \Drupal\aggregator\Plugin\Core\Entity\Feed object. + * - category: $data is an \Drupal\taxonomy\Plugin\Core\Entity\Term object. * The $data parameter is not used when $type is 'sum'. * * @return @@ -124,36 +118,23 @@ function aggregator_page_category_form($form, $form_state, $category) { */ function aggregator_load_feed_items($type, $data = NULL, $limit = 20) { $items = array(); + $query = entity_query('aggregator_item'); switch ($type) { - case 'sum': - $query = db_select('aggregator_item', 'i'); - $query->join('aggregator_feed', 'f', 'i.fid = f.fid'); - $query->fields('i', array('iid')); - break; case 'source': - $query = db_select('aggregator_item', 'i'); - $query - ->fields('i', array('iid')) - ->condition('i.fid', $data->id()); + $query->condition('fid', $data->id()); break; case 'category': - $query = db_select('aggregator_category_item', 'c'); - $query->leftJoin('aggregator_item', 'i', 'c.iid = i.iid'); - $query->leftJoin('aggregator_feed', 'f', 'i.fid = f.fid'); - $query - ->fields('i', array('iid')) - ->condition('cid', $data->cid); + $query->condition('field_aggregator_categories.target_id', $data->id()); break; } $result = $query - ->extend('Drupal\Core\Database\Query\PagerSelectExtender') - ->limit($limit) - ->orderBy('i.timestamp', 'DESC') - ->orderBy('i.iid', 'DESC') + ->pager($limit) + ->sort('timestamp', 'DESC') + ->sort('iid', 'DESC') ->execute(); - $items = entity_load_multiple('aggregator_item', $result->fetchCol()); + $items = entity_load_multiple('aggregator_item', $result); return $items; } @@ -205,78 +186,25 @@ function _aggregator_page_list($items, $op, $feed_source = '') { * @return array * An array of FAPI elements. * - * @see aggregator_categorize_items_submit() * @see theme_aggregator_categorize_items() * @ingroup forms */ function aggregator_categorize_items($items, $feed_source = '') { - $form['#submit'][] = 'aggregator_categorize_items_submit'; - $form['#theme'] = 'aggregator_categorize_items'; - $form['feed_source'] = array( - '#value' => $feed_source, - ); + $build['#theme'] = 'aggregator_categorize_items'; + $build['#feed_source'] = $feed_source; $categories = array(); - $done = FALSE; - $form['items'] = array(); + + $build['#items'] = array(); if ($items) { - $form['items'] = entity_view_multiple($items, 'default'); + $build['#items'] = entity_view_multiple($items, 'default'); } - $form['categories'] = array( + $build['#categories'] = array( '#tree' => TRUE, ); foreach ($items as $item) { - $form['categories'][$item->id()] = array(); - $categories_result = db_query('SELECT c.cid, c.title, ci.iid FROM {aggregator_category} c LEFT JOIN {aggregator_category_item} ci ON c.cid = ci.cid AND ci.iid = :iid', array(':iid' => $item->id())); - $selected = array(); - foreach ($categories_result as $category) { - if (!$done) { - $categories[$category->cid] = check_plain($category->title); - } - if ($category->iid) { - $selected[] = $category->cid; - } - } - $done = TRUE; - $form['categories'][$item->id()] = array( - '#type' => config('aggregator.settings')->get('source.category_selector'), - '#default_value' => $selected, - '#options' => $categories, - '#size' => 10, - '#multiple' => TRUE - ); - } - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save categories')); - - return $form; -} - -/** - * Form submission handler for aggregator_categorize_items(). - */ -function aggregator_categorize_items_submit($form, &$form_state) { - if (!empty($form_state['values']['categories'])) { - foreach ($form_state['values']['categories'] as $iid => $selection) { - db_delete('aggregator_category_item') - ->condition('iid', $iid) - ->execute(); - $insert = db_insert('aggregator_category_item')->fields(array('iid', 'cid')); - $has_values = FALSE; - foreach ($selection as $cid) { - if ($cid && $iid) { - $has_values = TRUE; - $insert->values(array( - 'iid' => $iid, - 'cid' => $cid, - )); - } - } - if ($has_values) { - $insert->execute(); - } - } + $build['#categories'][$item->id()] = entity_get_form($item); } - drupal_set_message(t('The categories have been saved.')); + return $build; } /** @@ -289,21 +217,16 @@ function aggregator_categorize_items_submit($form, &$form_state) { * @ingroup themeable */ function theme_aggregator_categorize_items($variables) { - $form = $variables['form']; - - $output = drupal_render($form['feed_source']); $rows = array(); - if (!empty($form['items'])) { - foreach (element_children($form['items']) as $key) { + if (!empty($variables['items'])) { + foreach (element_children($variables['items']) as $key) { $rows[] = array( - drupal_render($form['items'][$key]), - array('data' => drupal_render($form['categories'][$key]), 'class' => array('categorize-item')), + drupal_render($variables['items'][$key]), + array('data' => drupal_render($variables['categories'][$key]), 'class' => array('categorize-item')), ); } } - $output .= theme('table', array('header' => array('', t('Categorize')), 'rows' => $rows)); - $output .= drupal_render($form['submit']); - $output .= drupal_render_children($form); + $output = theme('table', array('header' => array('', t('Categorize')), 'rows' => $rows)); $output .= theme('pager'); return $output; @@ -338,9 +261,9 @@ function template_preprocess_aggregator_item(&$variables) { $variables['source_url'] = ''; $variables['source_title'] = ''; - if (isset($item->ftitle) && isset($item->fid->value)) { - $variables['source_url'] = url("aggregator/sources/$item->fid->value"); - $variables['source_title'] = check_plain($item->ftitle); + if (isset($item->fid->value)) { + $variables['source_url'] = url('aggregator/sources/' . $item->fid->value); + $variables['source_title'] = aggregator_feed_load($item->fid->value)->label(); } if (date('Ymd', $item->timestamp->value) == date('Ymd')) { $variables['source_date'] = t('%ago ago', array('%ago' => format_interval(REQUEST_TIME - $item->timestamp->value))); @@ -350,8 +273,10 @@ function template_preprocess_aggregator_item(&$variables) { } $variables['categories'] = array(); - foreach ($item->categories as $category) { - $variables['categories'][$category->cid] = l($category->title, 'aggregator/categories/' . $category->cid); + foreach ($item->field_aggregator_categories as $field) { + if ($field->entity) { + $variables['categories'][$field->entity->id()] = l($field->entity->label(), 'aggregator/categories/' . $field->entity->id()); + } } } @@ -404,7 +329,9 @@ function aggregator_page_sources() { * @see aggregator_menu() */ function aggregator_page_categories() { - $result = db_query('SELECT c.cid, c.title, c.description FROM {aggregator_category} c LEFT JOIN {aggregator_category_item} ci ON c.cid = ci.cid LEFT JOIN {aggregator_item} i ON ci.iid = i.iid GROUP BY c.cid, c.title, c.description'); + $result = entity_query('taxonomy_term') + ->condition('vid', 'aggregator_categories') + ->execute(); $build = array( '#type' => 'container', @@ -412,15 +339,17 @@ function aggregator_page_categories() { '#sorted' => TRUE, ); $aggregator_summary_items = config('aggregator.settings')->get('source.list_max'); - foreach ($result as $category) { + foreach ($result as $tid) { + $category = taxonomy_term_load($tid); $summary_items = array(); if ($aggregator_summary_items) { if ($items = aggregator_load_feed_items('category', $category, $aggregator_summary_items)) { $summary_items = entity_view_multiple($items, 'summary'); } } - $category->url = url('aggregator/categories/' . $category->cid); - $build[$category->cid] = array( + // Emulate ->url->value field api pattern. + $category->url = (object) array('value' => url('aggregator/categories/' . $category->id())); + $build[$category->id()] = array( '#theme' => 'aggregator_summary_items', '#summary_items' => $summary_items, '#source' => $category, @@ -439,21 +368,23 @@ function aggregator_page_categories() { * @see aggregator_menu() */ function aggregator_page_rss() { - $result = NULL; + $category = NULL; $rss_config = config('system.rss'); // arg(2) is the passed cid, only select for that category. if (arg(2)) { - $category = db_query('SELECT cid, title FROM {aggregator_category} WHERE cid = :cid', array(':cid' => arg(2)))->fetchObject(); - $result = db_query_range('SELECT i.*, f.title AS ftitle, f.link AS flink FROM {aggregator_category_item} c LEFT JOIN {aggregator_item} i ON c.iid = i.iid LEFT JOIN {aggregator_feed} f ON i.fid = f.fid WHERE cid = :cid ORDER BY timestamp DESC, i.iid DESC', 0, $rss_config->get('items.limit'), array(':cid' => $category->cid)); + $category = taxonomy_term_load(arg(2)); + $items = aggregator_load_feed_items('category', $category, $rss_config->get('items.limit')); } // Or, get the default aggregator items. else { - $category = NULL; - $result = db_query_range('SELECT i.*, f.title AS ftitle, f.link AS flink FROM {aggregator_item} i INNER JOIN {aggregator_feed} f ON i.fid = f.fid ORDER BY i.timestamp DESC, i.iid DESC', 0, $rss_config->get('items.limit')); + $items = aggregator_load_feed_items('sum', NULL, $rss_config->get('items.limit')); + } + // Add feed info to each item. + foreach ($items as $item) { + $item->feed = aggregator_feed_load($item->fid->value); } - $feeds = $result->fetchAll(); - return theme('aggregator_page_rss', array('feeds' => $feeds, 'category' => $category)); + return theme('aggregator_page_rss', array('items' => $items, 'category' => $category)); } /** @@ -461,7 +392,7 @@ function aggregator_page_rss() { * * @param $variables * An associative array containing: - * - feeds: An array of the feeds to theme. + * - items: An array of the aggregtor items to theme. * - category: A common category, if any, for all the feeds. * * @return void @@ -469,32 +400,33 @@ function aggregator_page_rss() { * @ingroup themeable */ function theme_aggregator_page_rss($variables) { - $feeds = $variables['feeds']; + $aggregator_items = $variables['items']; $category = $variables['category']; drupal_add_http_header('Content-Type', 'application/rss+xml; charset=utf-8'); $items = ''; $feed_length = config('system.rss')->get('items.view_mode'); - foreach ($feeds as $feed) { + foreach ($aggregator_items as $item) { switch ($feed_length) { case 'teaser': - $summary = text_summary($feed->description, NULL, config('aggregator.settings')->get('items.teaser_length')); - if ($summary != $feed->description) { - $summary .= '

' . t('read more') . "

\n"; + $summary = text_summary($item->description->value, NULL, config('aggregator.settings')->get('items.teaser_length')); + if ($summary != $item->description->value) { + $summary .= '

' . t('read more') . "

\n"; } - $feed->description = $summary; break; case 'title': - $feed->description = ''; + $summary = ''; break; + default: + $summary = $item->description->value; } - $items .= format_rss_item($feed->ftitle . ': ' . $feed->title, $feed->link, $feed->description, array('pubDate' => date('r', $feed->timestamp))); + $items .= format_rss_item($item->feed->label() . ': ' . $item->title->value, $item->link->value, $summary, array('pubDate' => date('r', $item->timestamp->value))); } $site_name = config('system.site')->get('name'); - $url = url((isset($category) ? 'aggregator/categories/' . $category->cid : 'aggregator'), array('absolute' => TRUE)); - $description = isset($category) ? t('@site_name - aggregated feeds in category @title', array('@site_name' => $site_name, '@title' => $category->title)) : t('@site_name - aggregated feeds', array('@site_name' => $site_name)); + $url = url((isset($category) ? 'aggregator/categories/' . $category->id() : 'aggregator'), array('absolute' => TRUE)); + $description = isset($category) ? t('@site_name - aggregated feeds in category @title', array('@site_name' => $site_name, '@title' => $category->label())) : t('@site_name - aggregated feeds', array('@site_name' => $site_name)); $output = "\n"; $output .= "\n"; @@ -507,7 +439,7 @@ function theme_aggregator_page_rss($variables) { /** * Page callback: Generates an OPML representation of all feeds. * - * @param $cid + * @param $tid * (optional) If set, feeds are exported only from a category with this ID. * Otherwise, all feeds are exported. Defaults to NULL. * @@ -516,15 +448,14 @@ function theme_aggregator_page_rss($variables) { * * @see aggregator_menu() */ -function aggregator_page_opml($cid = NULL) { - if ($cid) { - $result = db_query('SELECT f.title, f.url FROM {aggregator_feed} f LEFT JOIN {aggregator_category_feed} c on f.fid = c.fid WHERE c.cid = :cid ORDER BY title', array(':cid' => $cid)); +function aggregator_page_opml($tid = NULL) { + $query = entity_query('aggregator_feed'); + if ($tid) { + $query->condition('field_aggregator_categories.target_id', $tid); } - else { - $result = db_query('SELECT * FROM {aggregator_feed} ORDER BY title'); - } - - $feeds = $result->fetchAll(); + $result = $query->sort('title') + ->execute(); + $feeds = $result ? entity_load_multiple('aggregator_feed', $result) : array(); return theme('aggregator_page_opml', array('feeds' => $feeds)); } @@ -550,7 +481,7 @@ function theme_aggregator_page_opml($variables) { $output .= "\n"; $output .= "\n"; foreach ($feeds as $feed) { - $output .= '\n"; + $output .= '\n"; } $output .= "\n"; $output .= "\n"; @@ -564,7 +495,7 @@ function theme_aggregator_page_opml($variables) { * @see aggregator-summary-items.tpl.php */ function template_preprocess_aggregator_summary_items(&$variables) { - $variables['title'] = check_plain($variables['source'] instanceof EntityInterface ? $variables['source']->label() : $variables['source']->title); + $variables['title'] = check_plain($variables['source']->label()); $summary_items = array(); foreach (element_children($variables['summary_items']) as $key) { $summary_items[] = $variables['summary_items'][$key]; @@ -573,7 +504,7 @@ function template_preprocess_aggregator_summary_items(&$variables) { '#theme' => 'item_list', '#items' => $summary_items, ); - $variables['source_url'] = $variables['source'] instanceof EntityInterface ? $variables['source']->url->value : $variables['source']->url; + $variables['source_url'] = $variables['source']->url->value; } /** diff --git a/core/modules/aggregator/config/aggregator.settings.yml b/core/modules/aggregator/config/aggregator.settings.yml index dfc0aa9..2a33d52 100644 --- a/core/modules/aggregator/config/aggregator.settings.yml +++ b/core/modules/aggregator/config/aggregator.settings.yml @@ -8,4 +8,3 @@ items: expire: '9676800' source: list_max: '3' - category_selector: checkboxes diff --git a/core/modules/aggregator/config/schema/aggregator.schema.yml b/core/modules/aggregator/config/schema/aggregator.schema.yml index f4a0394..d4a93c5 100644 --- a/core/modules/aggregator/config/schema/aggregator.schema.yml +++ b/core/modules/aggregator/config/schema/aggregator.schema.yml @@ -36,6 +36,3 @@ aggregator.settings: list_max: type: integer label: 'Number of items shown in listing pages' - category_selector: - type: string - label: 'Select categories using' diff --git a/core/modules/aggregator/config/taxonomy.vocabulary.aggregator_categories.yml b/core/modules/aggregator/config/taxonomy.vocabulary.aggregator_categories.yml new file mode 100644 index 0000000..e4a6db5 --- /dev/null +++ b/core/modules/aggregator/config/taxonomy.vocabulary.aggregator_categories.yml @@ -0,0 +1,7 @@ +vid: aggregator_categories +name: 'Aggregator categories' +description: 'Categories allow feed items from different feeds to be grouped together.' +hierarchy: '0' +weight: '0' +status: '1' +langcode: en diff --git a/core/modules/aggregator/lib/Drupal/aggregator/FeedFormController.php b/core/modules/aggregator/lib/Drupal/aggregator/FeedFormController.php index 97fb698..e1444af 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/FeedFormController.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/FeedFormController.php @@ -59,27 +59,6 @@ public function form(array $form, array &$form_state, EntityInterface $feed) { '#description' => t("Drupal can make a block with the most recent news items of this feed. You can configure blocks to be displayed in the sidebar of your page. This setting lets you configure the number of news items to show in this feed's block. If you choose '0' this feed's block will be disabled.", array('@block-admin' => url('admin/structure/block'))), ); - // Handling of categories. - $options = array(); - $values = array(); - $categories = db_query('SELECT c.cid, c.title FROM {aggregator_category} c ORDER BY title'); - foreach ($categories as $category) { - $options[$category->cid] = check_plain($category->title); - if (!empty($feed->categories) && in_array($category->cid, array_keys($feed->categories))) { - $values[] = $category->cid; - } - } - - if ($options) { - $form['category'] = array( - '#type' => 'checkboxes', - '#title' => t('Categorize news items'), - '#default_value' => $values, - '#options' => $options, - '#description' => t('New feed items are automatically filed in the checked categories.'), - ); - } - return parent::form($form, $form_state, $feed); } @@ -113,11 +92,6 @@ public function validate(array $form, array &$form_state) { public function save(array $form, array &$form_state) { $feed = $this->getEntity($form_state); $insert = (bool) $feed->id(); - if (!empty($form_state['values']['category'])) { - // Store category values for post save operations. - // @see Drupal\Core\Entity\FeedStorageController::postSave() - $feed->categories = $form_state['values']['category']; - } $feed->save(); if ($insert) { drupal_set_message(t('The feed %feed has been updated.', array('%feed' => $feed->label()))); diff --git a/core/modules/aggregator/lib/Drupal/aggregator/FeedStorageController.php b/core/modules/aggregator/lib/Drupal/aggregator/FeedStorageController.php index 82a5643..c1a3f49 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/FeedStorageController.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/FeedStorageController.php @@ -31,16 +31,6 @@ public function create(array $values) { } /** - * Overrides Drupal\Core\Entity\DataBaseStorageController::attachLoad(). - */ - protected function attachLoad(&$queried_entities, $load_revision = FALSE) { - parent::attachLoad($queried_entities, $load_revision); - foreach ($queried_entities as $item) { - $item->categories = db_query('SELECT c.cid, c.title FROM {aggregator_category} c JOIN {aggregator_category_feed} f ON c.cid = f.cid AND f.fid = :fid ORDER BY title', array(':fid' => $item->id()))->fetchAllKeyed(); - } - } - - /** * Overrides Drupal\Core\Entity\DataBaseStorageController::preDelete(). */ protected function preDelete($entities) { @@ -87,30 +77,6 @@ protected function preSave(EntityInterface $entity) { if (module_exists('block')) { drupal_container()->get('plugin.manager.block')->clearCachedDefinitions(); } - // An existing feed is being modified, delete the category listings. - db_delete('aggregator_category_feed') - ->condition('fid', $entity->id()) - ->execute(); - } - - /** - * Overrides Drupal\Core\Entity\DataBaseStorageController::postSave(). - */ - protected function postSave(EntityInterface $entity, $update) { - parent::postSave($entity, $update); - - if (!empty($entity->categories)) { - foreach ($entity->categories as $cid => $value) { - if ($value) { - db_insert('aggregator_category_feed') - ->fields(array( - 'fid' => $entity->id(), - 'cid' => $cid, - )) - ->execute(); - } - } - } } /** diff --git a/core/modules/aggregator/lib/Drupal/aggregator/ItemFormController.php b/core/modules/aggregator/lib/Drupal/aggregator/ItemFormController.php new file mode 100644 index 0000000..4216979 --- /dev/null +++ b/core/modules/aggregator/lib/Drupal/aggregator/ItemFormController.php @@ -0,0 +1,36 @@ +getEntity($form_state)->save(); + } + + /** + * Overrides Drupal\Core\Entity\EntityFormController::actions(). + */ + public function actions(array $form, array &$form_state) { + $actions = parent::actions($form, $form_state); + // Do not provide a delete action. + unset($actions['delete']); + return $actions; + } + +} diff --git a/core/modules/aggregator/lib/Drupal/aggregator/ItemStorageController.php b/core/modules/aggregator/lib/Drupal/aggregator/ItemStorageController.php index 69876ab..edaef8a 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/ItemStorageController.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/ItemStorageController.php @@ -30,44 +30,6 @@ public function create(array $values) { } /** - * Overrides Drupal\Core\Entity\DataBaseStorageController::attachLoad(). - */ - protected function attachLoad(&$queried_entities, $load_revision = FALSE) { - parent::attachLoad($queried_entities, $load_revision); - foreach ($queried_entities as $item) { - $item->categories = db_query('SELECT c.title, c.cid FROM {aggregator_category_item} ci LEFT JOIN {aggregator_category} c ON ci.cid = c.cid WHERE ci.iid = :iid ORDER BY c.title', array(':iid' => $item->id()))->fetchAll(); - } - } - - /** - * Overrides Drupal\Core\Entity\DataBaseStorageController::preDelete(). - */ - protected function preDelete($entities) { - parent::preDelete($entities); - - db_delete('aggregator_category_item') - ->condition('iid', array_keys($entities), 'IN') - ->execute(); - } - - /** - * Overrides Drupal\Core\Entity\DataBaseStorageController::postSave(). - */ - protected function postSave(EntityInterface $entity, $update) { - parent::postSave($entity, $update); - - $result = db_query('SELECT cid FROM {aggregator_category_feed} WHERE fid = :fid', array(':fid' => $entity->fid->value)); - foreach ($result as $category) { - db_merge('aggregator_category_item') - ->key(array( - 'iid' => $entity->id(), - 'cid' => $category->cid, - )) - ->execute(); - } - } - - /** * Implements Drupal\Core\Entity\DataBaseStorageControllerNG::baseFieldDefinitions(). */ public function baseFieldDefinitions() { diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Feed.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Feed.php index 2c3cc20..7cc799d 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Feed.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Feed.php @@ -136,7 +136,7 @@ class Feed extends EntityNG implements ContentEntityInterface { public $block; /** - * Overrides Drupal\Core\Entity\EntityNG::init(). + * Overrides \Drupal\Core\Entity\EntityNG::init(). */ public function init() { parent::init(); @@ -159,16 +159,17 @@ public function init() { } /** - * Implements Drupal\Core\Entity\EntityInterface::id(). + * Implements \Drupal\Core\Entity\EntityInterface::id(). */ public function id() { return $this->get('fid')->value; } /** - * Implements Drupal\Core\Entity\EntityInterface::label(). + * Implements \Drupal\Core\Entity\EntityInterface::label(). */ public function label($langcode = NULL) { return $this->get('title')->value; } + } diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Item.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Item.php index 4d19a75..3b53b2d 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Item.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Item.php @@ -21,6 +21,9 @@ * module = "aggregator", * controller_class = "Drupal\aggregator\ItemStorageController", * render_controller_class = "Drupal\aggregator\ItemRenderController", + * form_controller_class = { + * "default" = "Drupal\aggregator\ItemFormController" + * }, * base_table = "aggregator_item", * fieldable = TRUE, * entity_keys = { diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Derivative/AggregatorCategoryBlock.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Derivative/AggregatorCategoryBlock.php index 8b3c616..1e7a10d 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Derivative/AggregatorCategoryBlock.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Derivative/AggregatorCategoryBlock.php @@ -30,7 +30,7 @@ public function getDerivativeDefinition($derivative_id, array $base_plugin_defin if (!empty($this->derivatives) && !empty($this->derivatives[$derivative_id])) { return $this->derivatives[$derivative_id]; } - $result = db_query('SELECT cid, title FROM {aggregator_category} ORDER BY title WHERE cid = :cid', array(':cid' => $derivative_id))->fetchObject(); + $result = taxonomy_term_load($derivative_id); $this->derivatives[$derivative_id] = $base_plugin_definition; $this->derivatives[$derivative_id]['delta'] = $result->cid; $this->derivatives[$derivative_id]['admin_label'] = t('@title category latest items', array('@title' => $result->title)); @@ -42,11 +42,15 @@ public function getDerivativeDefinition($derivative_id, array $base_plugin_defin */ public function getDerivativeDefinitions(array $base_plugin_definition) { // Provide a block plugin definition for each aggregator category. - $result = db_query('SELECT cid, title FROM {aggregator_category} ORDER BY title'); - foreach ($result as $category) { - $this->derivatives[$category->cid] = $base_plugin_definition; - $this->derivatives[$category->cid]['delta'] = $category->cid; - $this->derivatives[$category->cid]['admin_label'] = t('@title category latest items', array('@title' => $category->title)); + $categories = entity_query('taxonomy_term') + ->condition('vid', 'aggregator_categories') + ->execute(); + if ($categories) { + foreach (entity_load_multiple('taxonomy_term', $categories) as $category) { + $this->derivatives[$category->id()] = $base_plugin_definition; + $this->derivatives[$category->id()]['delta'] = $category->id(); + $this->derivatives[$category->id()]['admin_label'] = t('@title category latest items', array('@title' => $category->label())); + } } return $this->derivatives; } diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/aggregator/processor/DefaultProcessor.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/aggregator/processor/DefaultProcessor.php index 08777d1..a4e96f4 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/aggregator/processor/DefaultProcessor.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/aggregator/processor/DefaultProcessor.php @@ -65,14 +65,6 @@ public function settingsForm(array $form, array &$form_state) { '#description' => t('Requires a correctly configured cron maintenance task.', array('@cron' => url('admin/reports/status'))), ); - $form['processors'][$info['id']]['aggregator_category_selector'] = array( - '#type' => 'radios', - '#title' => t('Select categories using'), - '#default_value' => $config->get('source.category_selector'), - '#options' => array('checkboxes' => t('checkboxes'), - 'select' => t('multiple selector')), - '#description' => t('For a small number of categories, checkboxes are easier to use, while a multiple selector works well with large numbers of categories.'), - ); $form['processors'][$info['id']]['aggregator_teaser_length'] = array( '#type' => 'select', '#title' => t('Length of trimmed description'), @@ -91,7 +83,6 @@ public function settingsSubmit(array $form, array &$form_state) { $config->set('items.expire', $form_state['values']['aggregator_clear']) ->set('items.teaser_length', $form_state['values']['aggregator_teaser_length']) ->set('source.list_max', $form_state['values']['aggregator_summary_items']) - ->set('source.category_selector', $form_state['values']['aggregator_category_selector']) ->save(); } @@ -133,7 +124,6 @@ public function process(Feed $feed) { if ($item['timestamp']) { $entry->timestamp->value = $item['timestamp']; } - // Make sure the item title and author fit in the 255 varchar column. $entry->title->value = truncate_utf8($item['title'], 255, TRUE, TRUE); $entry->author->value = truncate_utf8($item['author'], 255, TRUE, TRUE); @@ -142,6 +132,10 @@ public function process(Feed $feed) { $entry->link->value = $item['link']; $entry->description->value = $item['description']; $entry->guid->value = $item['guid']; + if (isset($feed->field_aggregator_categories) && !$feed->field_aggregator_categories->isEmpty()) { + $entry->field_aggregator_categories->setValue($feed->field_aggregator_categories->getValue()); + } + $entry->save(); } } diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/block/block/AggregatorCategoryBlock.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/block/block/AggregatorCategoryBlock.php index 10bc52e..fdf20bc 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/block/block/AggregatorCategoryBlock.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/block/block/AggregatorCategoryBlock.php @@ -66,11 +66,14 @@ public function blockSubmit($form, &$form_state) { */ public function build() { $id = $this->getPluginId(); - if ($category = db_query('SELECT cid, title, block FROM {aggregator_category} WHERE cid = :cid', array(':cid' => $id))->fetchObject()) { - $result = db_query_range('SELECT i.* FROM {aggregator_category_item} ci LEFT JOIN {aggregator_item} i ON ci.iid = i.iid WHERE ci.cid = :cid ORDER BY i.timestamp DESC, i.iid DESC', 0, $this->configuration['block_count'], array(':cid' => $category->cid)); - $read_more = theme('more_link', array('url' => 'aggregator/categories/' . $category->cid, 'title' => t("View this category's recent news."))); + if ($category = taxonomy_term_load($id)) { + $result = entity_query('aggregator_item') + ->condition('field_aggregator_categories.target_id', $category->id()) + ->range(0, $this->configuration['block_count']) + ->execute(); + $read_more = theme('more_link', array('url' => 'aggregator/categories/' . $category->id(), 'title' => t("View this category's recent news."))); - $items = array(); + $items = $result ? entity_load_multiple('aggregator_item', $result) : array(); foreach ($result as $item) { $items[] = theme('aggregator_block_item', array('item' => $item)); } diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/AddFeedTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/AddFeedTest.php index 6d27c91..05fa0df 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/AddFeedTest.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/AddFeedTest.php @@ -26,7 +26,7 @@ function testAddFeed() { $feed = $this->createFeed(); // Check feed data. - $this->assertEqual($this->getUrl(), url('admin/config/services/aggregator/add/feed', array('absolute' => TRUE)), 'Directed to correct url.'); + $this->assertEqual($this->getUrl(), url('aggregator/sources/add', array('absolute' => TRUE)), 'Directed to correct url.'); $this->assertTrue($this->uniqueFeed($feed->label(), $feed->url->value), 'The feed is unique.'); // Check feed source. diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorConfigurationTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorConfigurationTest.php index 59d5339..e0e1108 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorConfigurationTest.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorConfigurationTest.php @@ -34,7 +34,6 @@ function testSettingsPage() { 'aggregator_allowed_html_tags' => '', 'aggregator_summary_items' => 10, 'aggregator_clear' => 3600, - 'aggregator_category_selector' => 'select', 'aggregator_teaser_length' => 200, 'aggregator_fetcher' => 'aggregator_test_fetcher', 'aggregator_parser' => 'aggregator_test_parser', diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorCronTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorCronTest.php index e9aac8c..e898de7 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorCronTest.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorCronTest.php @@ -24,8 +24,6 @@ public static function getInfo() { */ public function testCron() { // Create feed and test basic updating on cron. - global $base_url; - $key = state()->get('system.cron_key'); $this->createSampleNodes(); $feed = $this->createFeed(); $this->cronRun(); diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorTestBase.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorTestBase.php index 71e36fc..db5ca2e 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorTestBase.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorTestBase.php @@ -30,7 +30,7 @@ function setUp() { $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article')); } - $web_user = $this->drupalCreateUser(array('administer news feeds', 'access news feeds', 'create article content')); + $web_user = $this->drupalCreateUser(array('administer news feeds', 'access news feeds', 'create article content', 'administer taxonomy')); $this->drupalLogin($web_user); } @@ -53,7 +53,7 @@ function setUp() { */ function createFeed($feed_url = NULL, array $edit = array()) { $edit = $this->getFeedEditArray($feed_url, $edit); - $this->drupalPost('admin/config/services/aggregator/add/feed', $edit, t('Save')); + $this->drupalPost('aggregator/sources/add', $edit, t('Save')); $this->assertRaw(t('The feed %name has been added.', array('%name' => $edit['title'])), format_string('The feed !name has been added.', array('!name' => $edit['title']))); $fid = db_query("SELECT fid FROM {aggregator_feed} WHERE title = :title AND url = :url", array(':title' => $edit['title'], ':url' => $edit['url']))->fetchField(); @@ -68,7 +68,7 @@ function createFeed($feed_url = NULL, array $edit = array()) { * Feed object representing the feed. */ function deleteFeed(Feed $feed) { - $this->drupalPost('admin/config/services/aggregator/edit/feed/' . $feed->id(), array(), t('Delete')); + $this->drupalPost('aggregator/sources/' . $feed->id() . '/edit', array(), t('Delete')); $this->assertRaw(t('The feed %title has been deleted.', array('%title' => $feed->label())), 'Feed deleted successfully.'); } @@ -164,14 +164,8 @@ function updateFeedItems(Feed $feed, $expected_count = NULL) { $this->drupalGet('admin/config/services/aggregator'); $this->clickLink('Update items'); + $feed->items = entity_load_multiple_by_properties('aggregator_item', array('fid' => $feed->id())); // Ensure we have the right number of items. - $result = db_query('SELECT iid FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->id())); - $items = array(); - $feed->items = array(); - foreach ($result as $item) { - $feed->items[] = $item->iid; - } - if ($expected_count !== NULL) { $feed->item_count = count($feed->items); $this->assertEqual($expected_count, $feed->item_count, format_string('Total items in feed equal to the total items in database (!val1 != !val2)', array('!val1' => $expected_count, '!val2' => $feed->item_count))); @@ -207,37 +201,6 @@ function updateAndRemove(Feed $feed, $expected_count) { } /** - * Pulls feed categories from {aggregator_category_feed} table. - * - * @param \Drupal\aggregator\Plugin\Core\Entity\Feed $feed - * Feed object representing the feed. - */ - function getFeedCategories(Feed $feed) { - // add the categories to the feed so we can use them - $result = db_query('SELECT cid FROM {aggregator_category_feed} WHERE fid = :fid', array(':fid' => $feed->id())); - - foreach ($result as $category) { - $feed->categories[] = $category->cid; - } - } - - /** - * Pulls categories from {aggregator_category} table. - * - * @return array - * An associative array keyed by category ID and values are set to the - * category names. - */ - function getCategories() { - $categories = array(); - $result = db_query('SELECT * FROM {aggregator_category}'); - foreach ($result as $category) { - $categories[$category->cid] = $category; - } - return $categories; - } - - /** * Checks whether the feed name and URL are unique. * * @param $feed_name diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/CategorizeFeedItemTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/CategorizeFeedItemTest.php index 2485f17..396b6ee 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/CategorizeFeedItemTest.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/CategorizeFeedItemTest.php @@ -29,39 +29,29 @@ function testCategorizeFeedItem() { $this->createSampleNodes(); // Simulate form submission on "admin/config/services/aggregator/add/category". - $edit = array('title' => $this->randomName(10), 'description' => ''); - $this->drupalPost('admin/config/services/aggregator/add/category', $edit, t('Save')); - $this->assertRaw(t('The category %title has been added.', array('%title' => $edit['title'])), format_string('The category %title has been added.', array('%title' => $edit['title']))); + $edit = array('name' => $this->randomName(10), 'description[value]' => ''); + $this->drupalPost('admin/structure/taxonomy/aggregator_categories/add', $edit, t('Save')); + $this->assertRaw(t('Created new term %name.', array('%name' => $edit['name'])), format_string('The category %name has been added.', array('%name' => $edit['name']))); - $category = db_query("SELECT * FROM {aggregator_category} WHERE title = :title", array(':title' => $edit['title']))->fetch(); + $categories = taxonomy_term_load_multiple_by_name($edit['name'], 'aggregator_categories'); + $category = reset($categories); $this->assertTrue(!empty($category), 'The category found in database.'); - $link_path = 'aggregator/categories/' . $category->cid; - $menu_links = entity_load_multiple_by_properties('menu_link', array('link_path' => $link_path)); - $this->assertTrue(!empty($menu_links), 'The menu link associated with the category found in database.'); + $this->drupalGet('aggregator/categories/' . $category->id()); + $this->assertResponse(200, 'The menu link associated with the category was found.'); - $feed = $this->createFeed(); - db_insert('aggregator_category_feed') - ->fields(array( - 'cid' => $category->cid, - 'fid' => $feed->id(), - )) - ->execute(); + $feed = $this->createFeed(NULL, array( + 'field_aggregator_categories[und][' . $category->id() . ']' => $category->id(), + )); $this->updateFeedItems($feed, $this->getDefaultFeedItemCount()); - $this->getFeedCategories($feed); - $this->assertTrue(!empty($feed->categories), 'The category found in the feed.'); - // For each category of a feed, ensure feed items have that category, too. - if (!empty($feed->categories) && !empty($feed->items)) { - foreach ($feed->categories as $category) { - $categorized_count = db_select('aggregator_category_item') - ->condition('iid', $feed->items, 'IN') - ->countQuery() - ->execute() - ->fetchField(); + $this->assertTrue(!$feed->field_aggregator_categories->isEmpty(), 'The category found in the feed.'); - $this->assertEqual($feed->item_count, $categorized_count, 'Total items in feed equal to the total categorized feed items in database'); + // For each category of a feed, ensure feed items have that category, too. + if (!$feed->field_aggregator_categories->isEmpty() && !empty($feed->items)) { + foreach ($feed->items as $item) { + $this->assertEqual($feed->field_aggregator_categories->getValue(), $item->field_aggregator_categories->getValue(), 'Total items in feed equal to the total categorized feed items in database'); } } diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/CategorizeFeedTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/CategorizeFeedTest.php index 569f26a..a9372e7 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/CategorizeFeedTest.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/CategorizeFeedTest.php @@ -25,33 +25,31 @@ public static function getInfo() { function testCategorizeFeed() { // Create 2 categories. - $category_1 = array('title' => $this->randomName(10), 'description' => ''); - $this->drupalPost('admin/config/services/aggregator/add/category', $category_1, t('Save')); - $this->assertRaw(t('The category %title has been added.', array('%title' => $category_1['title'])), format_string('The category %title has been added.', array('%title' => $category_1['title']))); + $category_1 = array('name' => $this->randomName(10), 'description[value]' => ''); + $this->drupalPost('admin/structure/taxonomy/aggregator_categories/add', $category_1, t('Save')); + $this->assertRaw(t('Created new term %name.', array('%name' => $category_1['name'])), format_string('Created new term %name.', array('%name' => $category_1['name']))); - $category_2 = array('title' => $this->randomName(10), 'description' => ''); - $this->drupalPost('admin/config/services/aggregator/add/category', $category_2, t('Save')); - $this->assertRaw(t('The category %title has been added.', array('%title' => $category_2['title'])), format_string('The category %title has been added.', array('%title' => $category_2['title']))); + $category_2 = array('name' => $this->randomName(10), 'description[value]' => ''); + $this->drupalPost('admin/structure/taxonomy/aggregator_categories/add', $category_2, t('Save')); + $this->assertRaw(t('Created new term %name.', array('%name' => $category_2['name'])), format_string('Created new term %name.', array('%name' => $category_2['name']))); // Get categories from database. - $categories = $this->getCategories(); + $categories = entity_load_multiple_by_properties('taxonomy_term', array('vid' => 'aggregator_categories')); // Create a feed and assign 2 categories to it. $feed = $this->getFeedEditObject(NULL, array('block' => 5)); - foreach ($categories as $cid => $category) { - $feed->categories[$cid] = $cid; - } + $feed->field_aggregator_categories->setValue(array_keys($categories)); $feed->save(); $db_fid = db_query("SELECT fid FROM {aggregator_feed} WHERE title = :title AND url = :url", array(':title' => $feed->label(), ':url' => $feed->url->value))->fetchField(); $db_feed = aggregator_feed_load($db_fid); // Assert the feed has two categories. - $this->assertEqual(count($db_feed->categories), 2, 'Feed has 2 categories'); + $this->assertEqual(count($db_feed->field_aggregator_categories), 2, 'Feed has 2 categories'); // Verify the categories overview page is correctly displayed. $this->drupalGet('aggregator/categories'); - $this->assertText($category_1['title']); - $this->assertText($category_2['title']); + $this->assertText($category_1['name']); + $this->assertText($category_2['name']); } } diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/FeedParserTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/FeedParserTest.php index caf2e7d..21f20b4 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/FeedParserTest.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/FeedParserTest.php @@ -94,7 +94,10 @@ function testRedirectFeed() { function testInvalidFeed() { // Simulate a typo in the URL to force a curl exception. $invalid_url = 'http:/www.drupal.org'; - $feed = entity_create('aggregator_feed', array('url' => $invalid_url, 'title' => $this->randomName())); + $feed = entity_create('aggregator_feed', array( + 'url' => $invalid_url, + 'title' => $this->randomName(), + )); $feed->save(); // Update the feed. Use the UI to be able to check the message easily. diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/FeedProcessorPluginTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/FeedProcessorPluginTest.php index 6087b6f..09e3f71 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/FeedProcessorPluginTest.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/FeedProcessorPluginTest.php @@ -39,8 +39,7 @@ public function setUp() { public function testProcess() { $feed = $this->createFeed(); $this->updateFeedItems($feed); - foreach ($feed->items as $iid) { - $item = entity_load('aggregator_item', $iid); + foreach ($feed->items as $item) { $this->assertTrue(strpos($item->title->value, 'testProcessor') === 0); } } diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/ImportOpmlTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/ImportOpmlTest.php index 7a9f463..1ddbd61 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/ImportOpmlTest.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/ImportOpmlTest.php @@ -30,7 +30,7 @@ public static function getInfo() { function setUp() { parent::setUp(); - $admin_user = $this->drupalCreateUser(array('administer news feeds', 'access news feeds', 'create article content', 'administer blocks')); + $admin_user = $this->drupalCreateUser(array('administer news feeds', 'access news feeds', 'create article content', 'administer blocks', 'administer taxonomy')); $this->drupalLogin($admin_user); } @@ -38,15 +38,12 @@ function setUp() { * Opens OPML import form. */ function openImportForm() { - db_delete('aggregator_category')->execute(); - - $category = $this->randomName(10); - $cid = db_insert('aggregator_category') - ->fields(array( - 'title' => $category, - 'description' => '', - )) - ->execute(); + $category = entity_create('taxonomy_term', array( + 'name' => $this->randomName(10), + 'vid' => 'aggregator_categories', + 'description' => '', + )); + $category->save(); // Enable the help block. $this->drupalPlaceBlock('system_help_block', array('region' => 'help')); @@ -56,7 +53,7 @@ function openImportForm() { $this->assertField('files[upload]', 'Found file upload field.'); $this->assertField('remote', 'Found Remote URL field.'); $this->assertField('refresh', '', 'Found Refresh field.'); - $this->assertFieldByName("category[$cid]", $cid, 'Found category field.'); + $this->assertFieldByName("category[{$category->id()}]", $category->id(), 'Found category field.'); } /** @@ -102,18 +99,12 @@ function submitImportForm() { $after = db_query('SELECT COUNT(*) FROM {aggregator_feed}')->fetchField(); $this->assertEqual($before, $after, 'No feeds were added during the two last form submissions.'); - db_delete('aggregator_feed')->execute(); - db_delete('aggregator_category')->execute(); - db_delete('aggregator_category_feed')->execute(); - - $category = $this->randomName(10); - db_insert('aggregator_category') - ->fields(array( - 'cid' => 1, - 'title' => $category, + $category = entity_create('taxonomy_term', array( + 'name' => $this->randomName(10), + 'vid' => 'aggregator_categories', 'description' => '', - )) - ->execute(); + )); + $category->save(); $feeds[0] = $this->getFeedEditArray(); $feeds[1] = $this->getFeedEditArray(); @@ -121,7 +112,7 @@ function submitImportForm() { $edit = array( 'files[upload]' => $this->getValidOpml($feeds), 'refresh' => '900', - 'category[1]' => $category, + 'category[' . $category->id() . ']' => $category->id(), ); $this->drupalPost('admin/config/services/aggregator/add/opml', $edit, t('Import')); $this->assertRaw(t('A feed with the URL %url already exists.', array('%url' => $feeds[0]['url'])), 'Verifying that a duplicate URL was identified'); @@ -130,19 +121,19 @@ function submitImportForm() { $after = db_query('SELECT COUNT(*) FROM {aggregator_feed}')->fetchField(); $this->assertEqual($after, 2, 'Verifying that two distinct feeds were added.'); - $feeds_from_db = db_query("SELECT f.title, f.url, f.refresh, cf.cid FROM {aggregator_feed} f LEFT JOIN {aggregator_category_feed} cf ON f.fid = cf.fid"); - $refresh = $category = TRUE; + $feeds_from_db = entity_load_multiple('aggregator_feed'); + $refresh = $category_field = TRUE; foreach ($feeds_from_db as $feed) { - $title[$feed->url] = $feed->title; - $url[$feed->title] = $feed->url; - $category = $category && $feed->cid == 1; - $refresh = $refresh && $feed->refresh == 900; + $title[$feed->url->value] = $feed->label(); + $url[$feed->label()] = $feed->url->value; + $category_field = $category_field && $feed->field_aggregator_categories->value == $category->id(); + $refresh = $refresh && $feed->refresh->value == 900; } $this->assertEqual($title[$feeds[0]['url']], $feeds[0]['title'], 'First feed was added correctly.'); $this->assertEqual($url[$feeds[1]['title']], $feeds[1]['url'], 'Second feed was added correctly.'); $this->assertTrue($refresh, 'Refresh times are correct.'); - $this->assertTrue($category, 'Categories are correct.'); + $this->assertTrue($category_field, 'Categories are correct.'); } /** diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/UpdateFeedItemTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/UpdateFeedItemTest.php index 2cc1c8c..e37e7b1 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/UpdateFeedItemTest.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/UpdateFeedItemTest.php @@ -44,7 +44,7 @@ function testUpdateFeedItem() { $this->drupalGet($edit['url']); $this->assertResponse(array(200), format_string('URL !url is accessible', array('!url' => $edit['url']))); - $this->drupalPost('admin/config/services/aggregator/add/feed', $edit, t('Save')); + $this->drupalPost('aggregator/sources/add', $edit, t('Save')); $this->assertRaw(t('The feed %name has been added.', array('%name' => $edit['title'])), format_string('The feed !name has been added.', array('!name' => $edit['title']))); $fid = db_query("SELECT fid FROM {aggregator_feed} WHERE url = :url", array(':url' => $edit['url']))->fetchField(); diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/UpdateFeedTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/UpdateFeedTest.php index 8b25bc7..91518b7 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/UpdateFeedTest.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/UpdateFeedTest.php @@ -33,11 +33,10 @@ function testUpdateFeed() { if (isset($feed->{$same_field}->value)) { $edit[$same_field] = $feed->{$same_field}->value; } - $this->drupalPost('admin/config/services/aggregator/edit/feed/' . $feed->id(), $edit, t('Save')); + $this->drupalPost('aggregator/sources/' . $feed->id() . '/edit', $edit, t('Save')); $this->assertRaw(t('The feed %name has been updated.', array('%name' => $edit['title'])), format_string('The feed %name has been updated.', array('%name' => $edit['title']))); // Check feed data. - $this->assertEqual($this->getUrl(), url('admin/config/services/aggregator', array('absolute' => TRUE))); $this->assertTrue($this->uniqueFeed($edit['title'], $edit['url']), 'The feed is unique.'); // Check feed source. diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/Upgrade/AggregatorCategoriesUpgradePathTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/Upgrade/AggregatorCategoriesUpgradePathTest.php new file mode 100644 index 0000000..868aa8b --- /dev/null +++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/Upgrade/AggregatorCategoriesUpgradePathTest.php @@ -0,0 +1,86 @@ + 'Aggregator categories upgrade test', + 'description' => 'Upgrade tests for taxonomy term fields.', + 'group' => 'Upgrade path', + ); + } + + public function setUp() { + // Path to the database dump files. + $path = drupal_get_path('module', 'system') . '/tests/upgrade'; + $this->databaseDumpFiles = array( + $path . '/drupal-7.bare.standard_all.database.php.gz', + $path . '/drupal-7.aggregator.database.php', + ); + parent::setUp(); + } + + /** + * Tests a successful point release update. + */ + public function testAggregatorCategoriesUpgrade() { + $this->assertTrue($this->performUpgrade(), 'The upgrade was completed successfully.'); + + // Check category pages. + $this->drupalGet('aggregator/categories'); + $this->assertResponse(200, 'Categories page displayed successfully.'); + $this->drupalGet('aggregator/categories/1/categorize'); + $this->assertResponse(200, 'Category categorize page displayed successfully.'); + + $categories = entity_load_multiple_by_properties('taxonomy_term', array('vid' => 'aggregator_categories')); + $this->assertTrue(count($categories) === 2, 'Categories successfully migrated to taxonomy terms.'); + + // First category. + $category1 = entity_load_multiple_by_properties('taxonomy_term', array( + 'vid' => 'aggregator_categories', + 'name' => 'Test category1', + 'description' => 'Test description', + )); + $this->assertTrue(count($category1) === 1, 'First category successfully migrated to taxonomy term.'); + $category1 = reset($category1); + + // Second category. + $category2 = entity_load_multiple_by_properties('taxonomy_term', array( + 'vid' => 'aggregator_categories', + 'name' => 'Test category2', + 'description' => '', + )); + $this->assertTrue(count($category2) === 1, 'Second category successfully migrated to taxonomy term.'); + $category2 = reset($category2); + + // Check that the default feeds page is displayed successfully. + $this->drupalGet('aggregator/sources'); + $this->assertResponse(200, 'Feed sources page displayed successfully.'); + + // Check edit/categorize feed pages. + $this->drupalGet('aggregator/sources/1/edit'); + $this->assertResponse(200, 'Feed edit page displayed successfully.'); + $this->assertFieldByName('field_aggregator_categories[und][' . $category1->id() . ']', $category1->id(), 'First category field found checked for feed.'); + $this->assertFieldByName('field_aggregator_categories[und][' . $category2->id() . ']', $category2->id(), 'Second category field found checked for feed.'); + + $this->drupalGet('aggregator/sources/1/categorize'); + $this->assertResponse(200, 'Feed categorize page displayed successfully.'); + $this->assertFieldByName('field_aggregator_categories[und][' . $category1->id() . ']', $category1->id(), 'First category field found checked for feed items.'); + $this->assertFieldByName('field_aggregator_categories[und][' . $category2->id() . ']', $category2->id(), 'Second category field found checked for feed items.'); + + } +} diff --git a/core/modules/field_sql_storage/field_sql_storage.module b/core/modules/field_sql_storage/field_sql_storage.module index e604ac6..edbb8bf 100644 --- a/core/modules/field_sql_storage/field_sql_storage.module +++ b/core/modules/field_sql_storage/field_sql_storage.module @@ -457,7 +457,8 @@ function field_sql_storage_field_storage_write(EntityInterface $entity, $op, $fi foreach ($field_langcodes as $langcode) { $items = (array) $entity->{$field_name}[$langcode]; $delta_count = 0; - foreach ($items as $delta => $item) { + // @todo temp hack till http://drupal.org/node/1957888 is fixed. + foreach (array_filter($items) as $delta => $item) { // We now know we have someting to insert. $do_insert = TRUE; $record = array( diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php index e561ab7..416d111 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php @@ -139,7 +139,7 @@ function testModuleEnableOrder() { // - options depends on number // - ban depends on php (via module_test) // The correct enable order is: - $expected_order = array('php', 'ban', 'comment', 'history', 'number', 'options', 'taxonomy', 'forum'); + $expected_order = array('number', 'options', 'taxonomy', 'php', 'ban', 'comment', 'history', 'forum'); // Enable the modules through the UI, verifying that the dependency chain // is correct. diff --git a/core/modules/system/tests/upgrade/drupal-7.aggregator.database.php b/core/modules/system/tests/upgrade/drupal-7.aggregator.database.php new file mode 100644 index 0000000..18d0ffb --- /dev/null +++ b/core/modules/system/tests/upgrade/drupal-7.aggregator.database.php @@ -0,0 +1,104 @@ +fields(array( + 'fid' => 1, + 'title' => 'Test feed', + 'url' => 'http://example.com/rss', + 'link' => 'http://example.com', + 'description' => '', + 'image' => '', + 'hash' => '', + 'etag' => '', + )) + ->execute(); + +// Add a few categories. +db_insert('aggregator_category') + ->fields(array( + 'cid' => 1, + 'title' => 'Test category1', + 'description' => 'Test description', + )) + ->execute(); +db_insert('aggregator_category') + ->fields(array( + 'cid' => 2, + 'title' => 'Test category2', + 'description' => '', + )) + ->execute(); + +// Add two items. +db_insert('aggregator_item') + ->fields(array( + 'iid' => 1, + 'fid' => 1, + 'title' => 'Test item1', + 'link' => 'http://example.com/1', + 'author' => 'example', + 'description' => 'Test description1', + 'guid' => '1', + )) + ->execute(); +db_insert('aggregator_item') + ->fields(array( + 'iid' => 2, + 'fid' => 1, + 'title' => 'Test item2', + 'link' => 'http://example.com/2', + 'author' => 'example', + 'description' => 'Test description2', + 'guid' => '2', + )) + ->execute(); + +// Categories - feeds mapping. +db_insert('aggregator_category_feed')->fields(array( + 'fid', + 'cid', +)) +->values(array( + 'fid' => 1, + 'cid' => 1, +)) +->values(array( + 'fid' => 1, + 'cid' => 2, +)) +->execute(); + +// Categories - items mapping. +db_insert('aggregator_category_item')->fields(array( + 'iid', + 'cid', +)) +->values(array( + 'iid' => 1, + 'cid' => 1, +)) +->values(array( + 'iid' => 2, + 'cid' => 1, +)) +->values(array( + 'iid' => 1, + 'cid' => 2, +)) +->values(array( + 'iid' => 2, + 'cid' => 2, +)) +->execute(); diff --git a/core/modules/user/lib/Drupal/user/Tests/UserPermissionsTest.php b/core/modules/user/lib/Drupal/user/Tests/UserPermissionsTest.php index 2455b10..c220b8c 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserPermissionsTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserPermissionsTest.php @@ -77,8 +77,12 @@ function testAdministratorRole() { // permission is assigned by default. $edit = array(); $edit['modules[Core][aggregator][enable]'] = TRUE; - // Aggregator depends on file module, enable that as well. + // Aggregator depends on a few modules, enable them as well. $edit['modules[Core][file][enable]'] = TRUE; + $edit['modules[Core][entity_reference][enable]'] = TRUE; + $edit['modules[Core][taxonomy][enable]'] = TRUE; + $edit['modules[Core][options][enable]'] = TRUE; + $edit['modules[Core][number][enable]'] = TRUE; $this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->assertTrue(user_access('administer news feeds', $this->admin_user), 'The permission was automatically assigned to the administrator role'); }