diff --git a/core/modules/forum/config/entity.display.taxonomy_term.forums.default.yml b/core/modules/forum/config/entity.display.taxonomy_term.forums.default.yml
new file mode 100644
index 0000000..ef18047
--- /dev/null
+++ b/core/modules/forum/config/entity.display.taxonomy_term.forums.default.yml
@@ -0,0 +1,9 @@
+id: taxonomy_term.forums.default
+uuid: adaef6a9-8dc0-4f2e-9858-88daac440aa9
+targetEntityType: taxonomy_term
+bundle: forums
+mode: default
+content:
+ description:
+ weight: '0'
+ visible: '1'
diff --git a/core/modules/forum/config/entity.form_display.taxonomy_term.forums.default.yml b/core/modules/forum/config/entity.form_display.taxonomy_term.forums.default.yml
new file mode 100644
index 0000000..203b69a
--- /dev/null
+++ b/core/modules/forum/config/entity.form_display.taxonomy_term.forums.default.yml
@@ -0,0 +1,12 @@
+id: taxonomy_term.forums.default
+uuid: c8eab085-8fd3-4545-8600-e13b7d8bb9c4
+targetEntityType: taxonomy_term
+bundle: forums
+mode: default
+content:
+ name:
+ weight: '-5'
+ visible: '1'
+ description:
+ weight: '0'
+ visible: '1'
diff --git a/core/modules/forum/config/field.field.forum_container.yml b/core/modules/forum/config/field.field.forum_container.yml
new file mode 100644
index 0000000..96eb824
--- /dev/null
+++ b/core/modules/forum/config/field.field.forum_container.yml
@@ -0,0 +1,18 @@
+id: taxonomy_term.forum_container
+uuid: babf2ba1-505f-4c71-8a07-7be19f4fb9f3
+status: '1'
+langcode: en
+name: forum_container
+type: list_boolean
+settings:
+ allowed_values:
+ - ''
+ - ''
+ allowed_values_function: ''
+module: options
+active: '1'
+entity_type: taxonomy_term
+locked: '1'
+cardinality: '1'
+translatable: '0'
+indexes: { }
diff --git a/core/modules/forum/config/field.instance.taxonomy_term.forums.forum_container.yml b/core/modules/forum/config/field.instance.taxonomy_term.forums.forum_container.yml
new file mode 100644
index 0000000..c8c36af
--- /dev/null
+++ b/core/modules/forum/config/field.instance.taxonomy_term.forums.forum_container.yml
@@ -0,0 +1,16 @@
+id: taxonomy_term.forums.forum_container
+uuid: 8421d585-f6ef-4209-ad00-cfb30a1ab075
+status: '1'
+langcode: en
+field_uuid: babf2ba1-505f-4c71-8a07-7be19f4fb9f3
+entity_type: taxonomy_term
+bundle: forums
+label: Container
+description: ''
+required: '1'
+default_value:
+ -
+ value: '0'
+default_value_function: ''
+settings: { }
+field_type: list_boolean
diff --git a/core/modules/forum/config/forum.settings.yml b/core/modules/forum/config/forum.settings.yml
index 3f0b30f..4f2516e 100644
--- a/core/modules/forum/config/forum.settings.yml
+++ b/core/modules/forum/config/forum.settings.yml
@@ -3,7 +3,6 @@ block:
limit: '5'
new:
limit: '5'
-containers: []
topics:
hot_threshold: '15'
order: '1'
diff --git a/core/modules/forum/config/schema/forum.schema.yml b/core/modules/forum/config/schema/forum.schema.yml
index 60bd0f7..9ca1ed8 100644
--- a/core/modules/forum/config/schema/forum.schema.yml
+++ b/core/modules/forum/config/schema/forum.schema.yml
@@ -22,12 +22,6 @@ forum.settings:
limit:
type: integer
label: 'New forum Count'
- containers:
- type: sequence
- label: 'Containers to group related forums'
- sequence:
- - type: integer
- label: 'Taxonomy Term ID'
topics:
type: mapping
label: 'Forum topics block'
diff --git a/core/modules/forum/forum.admin.inc b/core/modules/forum/forum.admin.inc
index 332f11c..962e84d 100644
--- a/core/modules/forum/forum.admin.inc
+++ b/core/modules/forum/forum.admin.inc
@@ -32,7 +32,7 @@ function forum_overview($form, &$form_state) {
$term = $form['terms'][$key]['#term'];
$form['terms'][$key]['term']['#href'] = 'forum/' . $term->id();
unset($form['terms'][$key]['operations']['#links']['delete']);
- if (in_array($form['terms'][$key]['#term']->id(), $config->get('containers'))) {
+ if (!empty($term->forum_container->value)) {
$form['terms'][$key]['operations']['#links']['edit']['title'] = t('edit container');
$form['terms'][$key]['operations']['#links']['edit']['href'] = 'admin/structure/forum/edit/container/' . $term->id();
// We don't want the redirect from the link so we can redirect the
diff --git a/core/modules/forum/forum.info.yml b/core/modules/forum/forum.info.yml
index 86e5627..c136ae4 100644
--- a/core/modules/forum/forum.info.yml
+++ b/core/modules/forum/forum.info.yml
@@ -6,6 +6,7 @@ dependencies:
- history
- taxonomy
- comment
+ - options
package: Core
version: VERSION
core: 8.x
diff --git a/core/modules/forum/forum.install b/core/modules/forum/forum.install
index adc8ddb..e4b1e26 100644
--- a/core/modules/forum/forum.install
+++ b/core/modules/forum/forum.install
@@ -5,6 +5,8 @@
* Install, update, and uninstall functions for the Forum module.
*/
+use Drupal\Core\Language\Language;
+
/**
* Implements hook_install().
*/
@@ -73,6 +75,7 @@ function forum_enable() {
'description' => '',
'parent' => array(0),
'vid' => $vocabulary->id(),
+ 'forum_container' => 0,
));
$term->save();
@@ -109,6 +112,18 @@ function forum_enable() {
}
/**
+ * Implements hook_modules_preinstall().
+ */
+function forum_modules_preinstall($modules) {
+ $list_boolean = Drupal::service('plugin.manager.entity.field.field_type')->getDefinition('list_boolean');
+ if (empty($list_boolean) && in_array('forum', $modules)) {
+ // Make sure that the list_boolean field type is available before our
+ // default config is installed.
+ field_info_cache_clear();
+ }
+}
+
+/**
* Implements hook_uninstall().
*/
function forum_uninstall() {
@@ -119,8 +134,15 @@ function forum_uninstall() {
$field->delete();
}
- // Purge field data now to allow taxonomy module to be uninstalled
- // if this is the only field remaining.
+ if ($field = field_info_field('taxonomy_term', 'forum_container')) {
+ $field->delete();
+ }
+
+ // Purge field data now to allow taxonomy and options module to be uninstalled
+ // if this is the only field remaining. We need to run it twice because
+ // field_purge_batch() will not remove the instance and the field in the same
+ // pass.
+ field_purge_batch(10);
field_purge_batch(10);
// Allow to delete a forum's node type.
$locked = Drupal::state()->get('node.type.locked');
@@ -261,13 +283,131 @@ function forum_update_last_removed() {
* @ingroup config_upgrade
*/
function forum_update_8000() {
+ $map = db_query('SELECT vid, machine_name FROM {taxonomy_vocabulary}')->fetchAllKeyed();
+ $forum_vid = update_variable_get('forum_nav_vocabulary', FALSE);
+ if (!empty($map[$forum_vid])) {
+ // Update the variable to reference the machine name instead of the vid.
+ update_variable_set('forum_nav_vocabulary', $map[$forum_vid]);
+ }
update_variables_to_config('forum.settings', array(
'forum_hot_topic' => 'topics.hot_threshold',
'forum_per_page' => 'topics.page_limit',
'forum_order' => 'topics.order',
'forum_nav_vocabulary' => 'vocabulary',
- 'forum_containers' => 'containers',
'forum_block_num_active' => 'block.active.limit',
'forum_block_num_new' => 'block.new.limit',
));
}
+
+/**
+ * Implements hook_update_dependencies().
+ */
+function forum_update_dependencies() {
+ // Convert containers to field after the fields and instances are converted to
+ // ConfigEntities.
+ $dependencies['forum'][8001] = array(
+ 'field' => 8003,
+ 'taxonomy' => 8007,
+ );
+ return $dependencies;
+}
+
+/**
+ * Adds the forum_container field and copies the values over.
+ */
+function forum_update_8001() {
+ $vocabulary = config('forum.settings')->get('vocabulary');
+ // Create the field and instance.
+ $field = array(
+ 'id' => 'taxonomy_term.forum_container',
+ 'name' => 'forum_container',
+ 'entity_type' => 'taxonomy_term',
+ 'module' => 'options',
+ 'type' => 'list_boolean',
+ 'cardinality' => 1,
+ 'locked' => TRUE,
+ 'indexes' => array(),
+ 'settings' => array(
+ 'allowed_values' => array('', ''),
+ 'allowed_values_function' => '',
+ ),
+ 'schema' => array(
+ 'columns' => array(
+ 'value' => array(
+ 'type' => 'int',
+ 'not null' => FALSE,
+ ),
+ ),
+ 'indexes' => array(),
+ 'foreign keys' => array(),
+ ),
+ );
+ _update_8003_field_create_field($field);
+
+ $instance = array(
+ 'id' => 'taxonomy_term.' . $vocabulary . '.forum_container',
+ 'entity_type' => 'taxonomy_term',
+ 'label' => 'Container',
+ 'bundle' => $vocabulary,
+ 'description' => '',
+ 'required' => TRUE,
+ 'settings' => array(),
+ 'default_value' => array('value' => 0),
+ );
+ _update_8003_field_create_instance($field, $instance);
+}
+
+/**
+ * Migrate forum containers from variable to field values.
+ */
+function forum_update_8002(&$sandbox) {
+ // Initialize total values to process.
+ if (!isset($sandbox['total'])) {
+ $containers = update_variable_get('forum_containers', array());
+ $vocabulary = config('forum.settings')->get('vocabulary');
+ $sandbox['containers'] = $containers;
+ $sandbox['vocabulary'] = $vocabulary;
+ $sandbox['total'] = count($containers);
+ $sandbox['processed'] = 0;
+ }
+
+ if ($sandbox['total']) {
+ // Retrieve next 20 containers to migrate.
+ $containers = array_splice($containers, $sandbox['processed'], 20);
+ foreach ($containers as $tid) {
+ // Add a row to the field data and revision tables.
+ db_insert('taxonomy_term__forum_container')
+ ->fields(array(
+ 'bundle' => $sandbox['vocabulary'],
+ 'entity_id' => $tid,
+ 'revision_id' => $tid,
+ 'langcode' => Language::LANGCODE_NOT_SPECIFIED,
+ 'delta' => 0,
+ 'forum_container_value' => 1,
+ ))
+ ->execute();
+ db_insert('taxonomy_term_revision__forum_container')
+ ->fields(array(
+ 'bundle' => $vocabulary,
+ 'entity_id' => $tid,
+ 'revision_id' => $tid,
+ 'langcode' => Language::LANGCODE_NOT_SPECIFIED,
+ 'delta' => 0,
+ 'forum_container_value' => 1,
+ ))
+ ->execute();
+ }
+
+ // Report status.
+ $sandbox['processed'] += count($containers);
+ }
+ $sandbox['#finished'] = $sandbox['total'] ? $sandbox['processed'] / $sandbox['total'] : 1;
+
+}
+
+/**
+ * Remove the forum_containers variable.
+ */
+function forum_update_8003() {
+ update_variable_del('forum_containers');
+}
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 1ed6ac3..0b5df66 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -6,6 +6,7 @@
*/
use Drupal\Core\Entity\EntityInterface;
+use Drupal\field\Field;
use Drupal\node\NodeInterface;
use Drupal\taxonomy\Entity\Term;
@@ -74,7 +75,7 @@ function forum_theme() {
return array(
'forums' => array(
'template' => 'forums',
- 'variables' => array('forums' => NULL, 'topics' => NULL, 'parents' => NULL, 'tid' => NULL, 'sortby' => NULL, 'forum_per_page' => NULL),
+ 'variables' => array('forums' => NULL, 'topics' => NULL, 'parents' => NULL, 'term' => NULL, 'sortby' => NULL, 'forum_per_page' => NULL),
),
'forum_list' => array(
'template' => 'forum-list',
@@ -105,18 +106,13 @@ function forum_theme() {
function forum_menu() {
$items['forum'] = array(
'title' => 'Forums',
- 'page callback' => 'forum_page',
- 'access arguments' => array('access content'),
- 'file' => 'forum.pages.inc',
+ 'route_name' => 'forum_index',
);
- $items['forum/%forum_forum'] = array(
+ $items['forum/%forum'] = array(
'title' => 'Forums',
'title callback' => 'entity_page_label',
'title arguments' => array(1),
- 'page callback' => 'forum_page',
- 'page arguments' => array(1),
- 'access arguments' => array('access content'),
- 'file' => 'forum.pages.inc',
+ 'route_name' => 'forum_page',
);
$items['admin/structure/forum'] = array(
'title' => 'Forums',
@@ -168,48 +164,53 @@ function forum_menu_local_tasks(&$data, $router_item, $root_path) {
// Add action link to 'node/add/forum' on 'forum' sub-pages.
if ($root_path == 'forum' || $root_path == 'forum/%') {
- $tid = (isset($router_item['page_arguments'][0]) ? $router_item['page_arguments'][0]->id() : 0);
- $forum_term = forum_forum_load($tid);
- if ($forum_term) {
- $links = array();
- // Loop through all bundles for forum taxonomy vocabulary field.
- $field = field_info_field('node', 'taxonomy_forums');
- foreach ($field['bundles'] as $type_name) {
- if (($type = entity_load('node_type', $type_name)) && node_access('create', $type_name)) {
- $links[$type_name] = array(
- '#theme' => 'menu_local_action',
- '#link' => array(
- 'title' => t('Add new @node_type', array('@node_type' => $type->label())),
- 'href' => 'node/add/' . $type_name . '/' . $forum_term->id(),
- ),
- );
+ $request = Drupal::request();
+ $forum_term = $request->attributes->get('taxonomy_term');
+ $vid = Drupal::config('forum.settings')->get('vocabulary');
+ $links = array();
+ // Loop through all bundles for forum taxonomy vocabulary field.
+ $field = Field::fieldInfo()->getField('node', 'taxonomy_forums');
+ foreach ($field['bundles'] as $type) {
+ if (node_access('create', $type)) {
+ $links[$type] = array(
+ '#theme' => 'menu_local_action',
+ '#link' => array(
+ 'title' => t('Add new @node_type', array('@node_type' => entity_load('node_type', $type)->label())),
+ 'href' => 'node/add/' . $type,
+ ),
+ );
+ if ($forum_term && $forum_term->bundle() == $vid) {
+ // We are viewing a forum term (specific forum), append the tid to the
+ // url.
+ $links[$type]['#link']['href'] .= '/' . $forum_term->id();
+
}
}
- if (empty($links)) {
- // Authenticated user does not have access to create new topics.
- if ($user->isAuthenticated()) {
- $links['disallowed'] = array(
- '#theme' => 'menu_local_action',
- '#link' => array(
- 'title' => t('You are not allowed to post new content in the forum.'),
- ),
- );
- }
- // Anonymous user does not have access to create new topics.
- else {
- $links['login'] = array(
- '#theme' => 'menu_local_action',
- '#link' => array(
- 'title' => t('Log in to post new content in the forum.', array(
- '@login' => url('user/login', array('query' => drupal_get_destination())),
- )),
- 'localized_options' => array('html' => TRUE),
- ),
- );
- }
+ }
+ if (empty($links)) {
+ // Authenticated user does not have access to create new topics.
+ if ($user->isAuthenticated()) {
+ $links['disallowed'] = array(
+ '#theme' => 'menu_local_action',
+ '#link' => array(
+ 'title' => t('You are not allowed to post new content in the forum.'),
+ ),
+ );
+ }
+ // Anonymous user does not have access to create new topics.
+ else {
+ $links['login'] = array(
+ '#theme' => 'menu_local_action',
+ '#link' => array(
+ 'title' => t('Log in to post new content in the forum.', array(
+ '@login' => url('user/login', array('query' => drupal_get_destination())),
+ )),
+ 'localized_options' => array('html' => TRUE),
+ ),
+ );
}
- $data['actions'] += $links;
}
+ $data['actions'] += $links;
}
}
@@ -256,33 +257,17 @@ function forum_uri($forum) {
}
/**
- * Checks whether a node can be used in a forum, based on its content type.
- *
- * @param \Drupal\Core\Entity\EntityInterface $node
- * A node entity.
- *
- * @return
- * Boolean indicating if the node can be assigned to a forum.
- */
-function _forum_node_check_node_type(EntityInterface $node) {
- // Fetch information about the forum field.
- $instance = field_info_instance('node', 'taxonomy_forums', $node->getType());
- return !empty($instance);
-}
-
-/**
* Implements hook_node_validate().
*
* Checks in particular that the node is assigned only a "leaf" term in the
* forum taxonomy.
*/
function forum_node_validate(EntityInterface $node, $form) {
- if (_forum_node_check_node_type($node)) {
+ if (Drupal::service('forum_manager')->checkNodeType($node)) {
$langcode = $form['taxonomy_forums']['#language'];
// vocabulary is selected, not a "container" term.
if (!$node->taxonomy_forums->isEmpty()) {
// Extract the node's proper topic ID.
- $containers = Drupal::config('forum.settings')->get('containers');
foreach ($node->taxonomy_forums as $delta => $item) {
// If no term was selected (e.g. when no terms exist yet), remove the
// item.
@@ -299,7 +284,7 @@ function forum_node_validate(EntityInterface $node, $form) {
':tid' => $term->id(),
':vid' => $term->bundle(),
))->fetchField();
- if ($used && in_array($term->id(), $containers)) {
+ if ($used && !empty($term->forum_container->value)) {
form_set_error('taxonomy_forums', t('The item %forum is a forum container, not a forum. Select one of the forums below instead.', array('%forum' => $term->label())));
}
}
@@ -313,8 +298,7 @@ function forum_node_validate(EntityInterface $node, $form) {
* Assigns the forum taxonomy when adding a topic from within a forum.
*/
function forum_node_presave(EntityInterface $node) {
-
- if (_forum_node_check_node_type($node)) {
+ if (Drupal::service('forum_manager')->checkNodeType($node)) {
// Make sure all fields are set properly:
$node->icon = !empty($node->icon) ? $node->icon : '';
if (!$node->taxonomy_forums->isEmpty()) {
@@ -335,7 +319,7 @@ function forum_node_presave(EntityInterface $node) {
* Implements hook_node_update().
*/
function forum_node_update(EntityInterface $node) {
- if (_forum_node_check_node_type($node)) {
+ if (Drupal::service('forum_manager')->checkNodeType($node)) {
// If this is not a new revision and does exist, update the forum record,
// otherwise insert a new one.
if ($node->getRevisionId() == $node->original->getRevisionId() && db_query('SELECT tid FROM {forum} WHERE nid=:nid', array(':nid' => $node->id()))->fetchField()) {
@@ -399,8 +383,8 @@ function forum_node_update(EntityInterface $node) {
}
$query->execute();
// The logic for determining last_comment_count is fairly complex, so
- // call _forum_update_forum_index() too.
- _forum_update_forum_index($node->id());
+ // call update the index too.
+ Drupal::service('forum_manager')->updateIndex($node->id());
}
// When a forum node is unpublished, remove it from the forum_index table.
else {
@@ -413,7 +397,7 @@ function forum_node_update(EntityInterface $node) {
* Implements hook_node_insert().
*/
function forum_node_insert(EntityInterface $node) {
- if (_forum_node_check_node_type($node)) {
+ if (Drupal::service('forum_manager')->checkNodeType($node)) {
if (!empty($node->forum_tid)) {
$nid = db_insert('forum')
->fields(array(
@@ -448,7 +432,7 @@ function forum_node_insert(EntityInterface $node) {
* Implements hook_node_predelete().
*/
function forum_node_predelete(EntityInterface $node) {
- if (_forum_node_check_node_type($node)) {
+ if (Drupal::service('forum_manager')->checkNodeType($node)) {
db_delete('forum')
->condition('nid', $node->id())
->execute();
@@ -464,7 +448,7 @@ function forum_node_predelete(EntityInterface $node) {
function forum_node_load($nodes) {
$node_vids = array();
foreach ($nodes as $node) {
- if (_forum_node_check_node_type($node)) {
+ if (Drupal::service('forum_manager')->checkNodeType($node)) {
$node_vids[] = $node->getRevisionId();
}
}
@@ -493,27 +477,13 @@ function forum_permission() {
}
/**
- * Implements hook_taxonomy_term_delete().
- */
-function forum_taxonomy_term_delete(Term $term) {
- // For containers, remove the tid from the forum_containers variable.
- $config = Drupal::config('forum.settings');
- $containers = $config->get('containers');
- $key = array_search($term->id(), $containers);
- if ($key !== FALSE) {
- unset($containers[$key]);
- }
- $config->set('containers', $containers)->save();
-}
-
-/**
* Implements hook_comment_publish().
*
* This actually handles the insertion and update of published nodes since
* $comment->save() calls hook_comment_publish() for all published comments.
*/
function forum_comment_publish($comment) {
- _forum_update_forum_index($comment->nid->target_id);
+ Drupal::service('forum_manager')->updateIndex($comment->nid->target_id);
}
/**
@@ -526,7 +496,7 @@ function forum_comment_update($comment) {
// $comment->save() calls hook_comment_publish() for all published comments,
// so we need to handle all other values here.
if (!$comment->status->value) {
- _forum_update_forum_index($comment->nid->target_id);
+ Drupal::service('forum_manager')->updateIndex($comment->nid->target_id);
}
}
@@ -534,14 +504,14 @@ function forum_comment_update($comment) {
* Implements hook_comment_unpublish().
*/
function forum_comment_unpublish($comment) {
- _forum_update_forum_index($comment->nid->target_id);
+ Drupal::service('forum_manager')->updateIndex($comment->nid->target_id);
}
/**
* Implements hook_comment_delete().
*/
function forum_comment_delete($comment) {
- _forum_update_forum_index($comment->nid->target_id);
+ Drupal::service('forum_manager')->updateIndex($comment->nid->target_id);
}
/**
@@ -628,303 +598,6 @@ function forum_block_view_pre_render($elements) {
}
/**
- * Returns a tree of all forums for a given taxonomy term ID.
- *
- * @param $tid
- * (optional) Taxonomy term ID of the forum. If not given all forums will be
- * returned.
- *
- * @return
- * A tree of taxonomy objects, with the following additional properties:
- * - num_topics: Number of topics in the forum.
- * - num_posts: Total number of posts in all topics.
- * - last_post: Most recent post for the forum.
- * - forums: An array of child forums.
- */
-function forum_forum_load($tid = NULL) {
- $cache = &drupal_static(__FUNCTION__, array());
-
- // Return a cached forum tree if available.
- if (!isset($tid)) {
- $tid = 0;
- }
- if (isset($cache[$tid])) {
- return $cache[$tid];
- }
-
- $config = Drupal::config('forum.settings');
- $vid = $config->get('vocabulary');
-
- // Load and validate the parent term.
- if ($tid) {
- $forum_term = entity_load('taxonomy_term', $tid);
- if (!$forum_term || ($forum_term->bundle() != $vid)) {
- return $cache[$tid] = FALSE;
- }
- }
- // If $tid is 0, create an empty entity to hold the child terms.
- elseif ($tid === 0) {
- $forum_term = entity_create('taxonomy_term', array(
- 'tid' => 0,
- 'vid' => $vid,
- ));
- }
-
- // Determine if the requested term is a container.
- if (!$forum_term->id() || in_array($forum_term->id(), $config->get('containers'))) {
- $forum_term->container = 1;
- }
-
- // Load parent terms.
- $forum_term->parents = taxonomy_term_load_parents_all($forum_term->id());
-
- // Load the tree below.
- $forums = array();
- $_forums = taxonomy_get_tree($vid, $tid, NULL, TRUE);
-
- if (count($_forums)) {
- $query = db_select('node_field_data', 'n');
- $query->join('node_comment_statistics', 'ncs', 'n.nid = ncs.nid');
- $query->join('forum', 'f', 'n.vid = f.vid');
- $query->addExpression('COUNT(n.nid)', 'topic_count');
- $query->addExpression('SUM(ncs.comment_count)', 'comment_count');
- $counts = $query
- ->fields('f', array('tid'))
- ->condition('n.status', 1)
- // @todo This should be actually filtering on the desired node status
- // field language and just fall back to the default language.
- ->condition('n.default_langcode', 1)
- ->groupBy('tid')
- ->addTag('node_access')
- ->execute()
- ->fetchAllAssoc('tid');
- }
-
- foreach ($_forums as $forum) {
- // Determine if the child term is a container.
- if (in_array($forum->id(), $config->get('containers'))) {
- $forum->container = 1;
- }
-
- // Merge in the topic and post counters.
- if (!empty($counts[$forum->id()])) {
- $forum->num_topics = $counts[$forum->id()]->topic_count;
- $forum->num_posts = $counts[$forum->id()]->topic_count + $counts[$forum->id()]->comment_count;
- }
- else {
- $forum->num_topics = 0;
- $forum->num_posts = 0;
- }
-
- // Query "Last Post" information for this forum.
- $query = db_select('node_field_data', 'n');
- $query->join('forum', 'f', 'n.vid = f.vid AND f.tid = :tid', array(':tid' => $forum->id()));
- $query->join('node_comment_statistics', 'ncs', 'n.nid = ncs.nid');
- $query->join('users', 'u', 'ncs.last_comment_uid = u.uid');
- $query->addExpression('CASE ncs.last_comment_uid WHEN 0 THEN ncs.last_comment_name ELSE u.name END', 'last_comment_name');
-
- $topic = $query
- ->fields('ncs', array('last_comment_timestamp', 'last_comment_uid'))
- ->condition('n.status', 1)
- // @todo This should be actually filtering on the desired node status
- // field language and just fall back to the default language.
- ->condition('n.default_langcode', 1)
- ->orderBy('last_comment_timestamp', 'DESC')
- ->range(0, 1)
- ->addTag('node_access')
- ->execute()
- ->fetchObject();
-
- // Merge in the "Last Post" information.
- $last_post = new stdClass();
- if (!empty($topic->last_comment_timestamp)) {
- $last_post->created = $topic->last_comment_timestamp;
- $last_post->name = $topic->last_comment_name;
- $last_post->uid = $topic->last_comment_uid;
- }
- $forum->last_post = $last_post;
-
- $forums[$forum->id()] = $forum;
- }
-
- // Cache the result, and return the tree.
- $forum_term->forums = $forums;
- $cache[$tid] = $forum_term;
- return $forum_term;
-}
-
-/**
- * Calculates the number of new posts in a forum that the user has not yet read.
- *
- * Nodes are new if they are newer than HISTORY_READ_LIMIT.
- *
- * @param $term
- * The term ID of the forum.
- * @param $uid
- * The user ID.
- *
- * @return
- * The number of new posts in the forum that have not been read by the user.
- */
-function _forum_topics_unread($term, $uid) {
- $query = db_select('node_field_data', 'n');
- $query->join('forum', 'f', 'n.vid = f.vid AND f.tid = :tid', array(':tid' => $term));
- $query->leftJoin('history', 'h', 'n.nid = h.nid AND h.uid = :uid', array(':uid' => $uid));
- $query->addExpression('COUNT(n.nid)', 'count');
- return $query
- ->condition('status', 1)
- // @todo This should be actually filtering on the desired node status field
- // language and just fall back to the default language.
- ->condition('n.default_langcode', 1)
- ->condition('n.created', HISTORY_READ_LIMIT, '>')
- ->isNull('h.nid')
- ->addTag('node_access')
- ->execute()
- ->fetchField();
-}
-
-/**
- * Gets all the topics in a forum.
- *
- * @param $tid
- * The term ID of the forum.
- * @param $sortby
- * One of the following integers indicating the sort criteria:
- * - 1: Date - newest first.
- * - 2: Date - oldest first.
- * - 3: Posts with the most comments first.
- * - 4: Posts with the least comments first.
- * @param $forum_per_page
- * The maximum number of topics to display per page.
- *
- * @return
- * A list of all the topics in a forum.
- */
-function forum_get_topics($tid, $sortby, $forum_per_page) {
- global $user, $forum_topic_list_header;
-
- $forum_topic_list_header = array(
- array('data' => t('Topic'), 'field' => 'f.title'),
- array('data' => t('Replies'), 'field' => 'f.comment_count'),
- array('data' => t('Last reply'), 'field' => 'f.last_comment_timestamp'),
- );
-
- $order = _forum_get_topic_order($sortby);
- for ($i = 0; $i < count($forum_topic_list_header); $i++) {
- if ($forum_topic_list_header[$i]['field'] == $order['field']) {
- $forum_topic_list_header[$i]['sort'] = $order['sort'];
- }
- }
-
- $query = db_select('forum_index', 'f')
- ->extend('Drupal\Core\Database\Query\PagerSelectExtender')
- ->extend('Drupal\Core\Database\Query\TableSortExtender');
- $query->fields('f');
- $query
- ->condition('f.tid', $tid)
- ->addTag('node_access')
- ->addMetaData('base_table', 'forum_index')
- ->orderBy('f.sticky', 'DESC')
- ->orderByHeader($forum_topic_list_header)
- ->limit($forum_per_page);
-
- $count_query = db_select('forum_index', 'f');
- $count_query->condition('f.tid', $tid);
- $count_query->addExpression('COUNT(*)');
- $count_query->addTag('node_access');
- $count_query->addMetaData('base_table', 'forum_index');
-
- $query->setCountQuery($count_query);
- $result = $query->execute();
- $nids = array();
- foreach ($result as $record) {
- $nids[] = $record->nid;
- }
- if ($nids) {
- $nodes = node_load_multiple($nids);
-
- $query = db_select('node_field_data', 'n')
- ->extend('Drupal\Core\Database\Query\TableSortExtender');
- $query->fields('n', array('nid'));
-
- $query->join('node_comment_statistics', 'ncs', 'n.nid = ncs.nid');
- $query->fields('ncs', array('cid', 'last_comment_uid', 'last_comment_timestamp', 'comment_count'));
-
- $query->join('forum_index', 'f', 'f.nid = ncs.nid');
- $query->addField('f', 'tid', 'forum_tid');
-
- $query->join('users', 'u', 'n.uid = u.uid');
- $query->addField('u', 'name');
-
- $query->join('users', 'u2', 'ncs.last_comment_uid = u2.uid');
-
- $query->addExpression('CASE ncs.last_comment_uid WHEN 0 THEN ncs.last_comment_name ELSE u2.name END', 'last_comment_name');
-
- $query
- ->orderBy('f.sticky', 'DESC')
- ->orderByHeader($forum_topic_list_header)
- ->condition('n.nid', $nids)
- // @todo This should be actually filtering on the desired node language
- // and just fall back to the default language.
- ->condition('n.default_langcode', 1);
-
- $result = array();
- foreach ($query->execute() as $row) {
- $topic = $nodes[$row->nid];
- $topic->comment_mode = $topic->comment;
-
- foreach ($row as $key => $value) {
- $topic->{$key} = $value;
- }
- $result[] = $topic;
- }
- }
- else {
- $result = array();
- }
-
- $topics = array();
- $first_new_found = FALSE;
- foreach ($result as $topic) {
- if ($user->isAuthenticated()) {
- // A forum is new if the topic is new, or if there are new comments since
- // the user's last visit.
- if ($topic->forum_tid != $tid) {
- $topic->new = 0;
- }
- else {
- $history = _forum_user_last_visit($topic->id());
- $topic->new_replies = comment_num_new($topic->id(), $history);
- $topic->new = $topic->new_replies || ($topic->last_comment_timestamp > $history);
- }
- }
- else {
- // Do not track "new replies" status for topics if the user is anonymous.
- $topic->new_replies = 0;
- $topic->new = 0;
- }
-
- // Make sure only one topic is indicated as the first new topic.
- $topic->first_new = FALSE;
- if ($topic->new != 0 && !$first_new_found) {
- $topic->first_new = TRUE;
- $first_new_found = TRUE;
- }
-
- if ($topic->comment_count > 0) {
- $last_reply = new stdClass();
- $last_reply->created = $topic->last_comment_timestamp;
- $last_reply->name = $topic->last_comment_name;
- $last_reply->uid = $topic->last_comment_uid;
- $topic->last_reply = $last_reply;
- }
- $topics[$topic->id()] = $topic;
- }
-
- return $topics;
-}
-
-/**
* Implements hook_preprocess_HOOK() for block.html.twig.
*/
function forum_preprocess_block(&$variables) {
@@ -945,7 +618,7 @@ function forum_preprocess_block(&$variables) {
* - topics: An array of all the topics in the current forum.
* - parents: An array of taxonomy term objects that are ancestors of the
* current term ID.
- * - tid: Taxonomy term ID of the current forum.
+ * - term: Taxonomy term of the current forum.
* - sortby: One of the following integers indicating the sort criteria:
* - 1: Date - newest first.
* - 2: Date - oldest first.
@@ -954,6 +627,7 @@ function forum_preprocess_block(&$variables) {
* - forum_per_page: The maximum number of topics to display per page.
*/
function template_preprocess_forums(&$variables) {
+ $variables['tid'] = $variables['term']->id();
if ($variables['forums_defined'] = count($variables['forums']) || count($variables['parents'])) {
if (!empty($variables['forums'])) {
$variables['forums'] = array(
@@ -967,7 +641,7 @@ function template_preprocess_forums(&$variables) {
$variables['forums'] = array();
}
- if ($variables['tid'] && array_search($variables['tid'], Drupal::config('forum.settings')->get('containers')) === FALSE) {
+ if ($variables['term'] && empty($variables['term']->forum_container->value)) {
$variables['topics'] = array(
'#theme' => 'forum_topic_list',
'#tid' => $variables['tid'],
@@ -1024,7 +698,7 @@ function template_preprocess_forum_list(&$variables) {
$variables['forums'][$id]->description = filter_xss_admin($forum->description->value);
$variables['forums'][$id]->link = url("forum/" . $forum->id());
$variables['forums'][$id]->name = check_plain($forum->label());
- $variables['forums'][$id]->is_container = !empty($forum->container);
+ $variables['forums'][$id]->is_container = !empty($forum->forum_container->value);
$variables['forums'][$id]->zebra = $row % 2 == 0 ? 'odd' : 'even';
$row++;
@@ -1035,7 +709,7 @@ function template_preprocess_forum_list(&$variables) {
$variables['forums'][$id]->icon_class = 'default';
$variables['forums'][$id]->icon_title = t('No new posts');
if ($user->isAuthenticated()) {
- $variables['forums'][$id]->new_topics = _forum_topics_unread($forum->id(), $user->id());
+ $variables['forums'][$id]->new_topics = Drupal::service('forum_manager')->unreadTopics($forum->id(), $user->id());
if ($variables['forums'][$id]->new_topics) {
$variables['forums'][$id]->new_text = format_plural($variables['forums'][$id]->new_topics, '1 new post in forum %title', '@count new posts in forum %title', array('%title' => $variables['forums'][$id]->label()));
$variables['forums'][$id]->new_url = url('forum/' . $forum->id(), array('fragment' => 'new'));
@@ -1071,12 +745,14 @@ function template_preprocess_forum_list(&$variables) {
function template_preprocess_forum_topic_list(&$variables) {
global $forum_topic_list_header;
- // Create the tablesorting header.
- $ts = tablesort_init($forum_topic_list_header);
$header = '';
- foreach ($forum_topic_list_header as $cell) {
- $cell = tablesort_header($cell, $forum_topic_list_header, $ts);
- $header .= _theme_table_cell($cell, TRUE);
+ if (!empty($forum_topic_list_header)) {
+ // Create the tablesorting header.
+ $ts = tablesort_init($forum_topic_list_header);
+ foreach ($forum_topic_list_header as $cell) {
+ $cell = tablesort_header($cell, $forum_topic_list_header, $ts);
+ $header .= _theme_table_cell($cell, TRUE);
+ }
}
$variables['header'] = $header;
@@ -1204,102 +880,6 @@ function template_preprocess_forum_submitted(&$variables) {
}
/**
- * Gets the last time the user viewed a node.
- *
- * @param $nid
- * The node ID.
- *
- * @return
- * The timestamp when the user last viewed this node, if the user has
- * previously viewed the node; otherwise HISTORY_READ_LIMIT.
- */
-function _forum_user_last_visit($nid) {
- global $user;
- $history = &drupal_static(__FUNCTION__, array());
-
- if (empty($history)) {
- $result = db_query('SELECT nid, timestamp FROM {history} WHERE uid = :uid', array(':uid' => $user->id()));
- foreach ($result as $t) {
- $history[$t->nid] = $t->timestamp > HISTORY_READ_LIMIT ? $t->timestamp : HISTORY_READ_LIMIT;
- }
- }
- return isset($history[$nid]) ? $history[$nid] : HISTORY_READ_LIMIT;
-}
-
-/**
- * Gets topic sorting information based on an integer code.
- *
- * @param $sortby
- * One of the following integers indicating the sort criteria:
- * - 1: Date - newest first.
- * - 2: Date - oldest first.
- * - 3: Posts with the most comments first.
- * - 4: Posts with the least comments first.
- *
- * @return
- * An array with the following values:
- * - field: A field for an SQL query.
- * - sort: 'asc' or 'desc'.
- */
-function _forum_get_topic_order($sortby) {
- switch ($sortby) {
- case 1:
- return array('field' => 'f.last_comment_timestamp', 'sort' => 'desc');
- break;
- case 2:
- return array('field' => 'f.last_comment_timestamp', 'sort' => 'asc');
- break;
- case 3:
- return array('field' => 'f.comment_count', 'sort' => 'desc');
- break;
- case 4:
- return array('field' => 'f.comment_count', 'sort' => 'asc');
- break;
- }
-}
-
-/**
- * Updates the taxonomy index for a given node.
- *
- * @param $nid
- * The ID of the node to update.
- */
-function _forum_update_forum_index($nid) {
- $count = db_query('SELECT COUNT(cid) FROM {comment} c INNER JOIN {forum_index} i ON c.nid = i.nid WHERE c.nid = :nid AND c.status = :status', array(
- ':nid' => $nid,
- ':status' => COMMENT_PUBLISHED,
- ))->fetchField();
-
- if ($count > 0) {
- // Comments exist.
- $last_reply = db_query_range('SELECT cid, name, created, uid FROM {comment} WHERE nid = :nid AND status = :status ORDER BY cid DESC', 0, 1, array(
- ':nid' => $nid,
- ':status' => COMMENT_PUBLISHED,
- ))->fetchObject();
- db_update('forum_index')
- ->fields( array(
- 'comment_count' => $count,
- 'last_comment_timestamp' => $last_reply->created,
- ))
- ->condition('nid', $nid)
- ->execute();
- }
- else {
- // Comments do not exist.
- // @todo This should be actually filtering on the desired node language and
- // just fall back to the default language.
- $node = db_query('SELECT uid, created FROM {node_field_data} WHERE nid = :nid AND default_langcode = 1', array(':nid' => $nid))->fetchObject();
- db_update('forum_index')
- ->fields( array(
- 'comment_count' => 0,
- 'last_comment_timestamp' => $node->created,
- ))
- ->condition('nid', $nid)
- ->execute();
- }
-}
-
-/**
* Returns HTML for a forum form.
*
* By default this does not alter the appearance of a form at all, but is
diff --git a/core/modules/forum/forum.pages.inc b/core/modules/forum/forum.pages.inc
deleted file mode 100644
index 3908f31..0000000
--- a/core/modules/forum/forum.pages.inc
+++ /dev/null
@@ -1,62 +0,0 @@
-get('vocabulary'));
-
- if (!isset($forum_term)) {
- // On the main page, display all the top-level forums.
- $forum_term = forum_forum_load(0);
- // Set the page title to forum's vocabulary name.
- drupal_set_title($vocabulary->label());
- }
-
- if ($forum_term->id() && array_search($forum_term->id(), $config->get('containers')) === FALSE) {
- // Add RSS feed for forums.
- drupal_add_feed('taxonomy/term/' . $forum_term->id() . '/feed', 'RSS - ' . $forum_term->label());
- }
-
- if (empty($forum_term->forums) && empty($forum_term->parents)) {
- // Root of empty forum.
- drupal_set_title(t('No forums defined'));
- }
-
- $forum_per_page = $config->get('topics.page_limit');
- $sort_by = $config->get('topics.order');
-
- if (empty($forum_term->container)) {
- $topics = forum_get_topics($forum_term->id(), $sort_by, $forum_per_page);
- }
- else {
- $topics = '';
- }
-
- $build = array(
- '#theme' => 'forums',
- '#forums' => $forum_term->forums,
- '#topics' => $topics,
- '#parents' => $forum_term->parents,
- '#tid' => $forum_term->id(),
- '#sortby' => $sort_by,
- '#forums_per_page' => $forum_per_page,
- );
- $build['#attached']['css'][] = drupal_get_path('module', 'forum') . '/css/forum.module.css';
- return $build;
-}
diff --git a/core/modules/forum/forum.routing.yml b/core/modules/forum/forum.routing.yml
index fe816f0..954e04a 100644
--- a/core/modules/forum/forum.routing.yml
+++ b/core/modules/forum/forum.routing.yml
@@ -4,12 +4,28 @@ forum_delete:
_form: 'Drupal\forum\Form\DeleteForm'
requirements:
_permission: 'administer forums'
+
forum_settings:
pattern: '/admin/structure/forum/settings'
defaults:
_form: '\Drupal\forum\ForumSettingsForm'
requirements:
_permission: 'administer forums'
+
+forum_index:
+ pattern: '/forum'
+ defaults:
+ _content: 'Drupal\forum\Controller\ForumController::forumIndex'
+ requirements:
+ _permission: 'access content'
+
+forum_page:
+ pattern: '/forum/{taxonomy_term}'
+ defaults:
+ _content: 'Drupal\forum\Controller\ForumController::forumPage'
+ requirements:
+ _permission: 'access content'
+
forum_add_container:
pattern: 'admin/structure/forum/add/container'
defaults:
diff --git a/core/modules/forum/forum.services.yml b/core/modules/forum/forum.services.yml
index e246711..8ea4dac 100644
--- a/core/modules/forum/forum.services.yml
+++ b/core/modules/forum/forum.services.yml
@@ -1,6 +1,9 @@
services:
+ forum_manager:
+ class: Drupal\forum\ForumManager
+ arguments: ['@config.factory', '@plugin.manager.entity', '@database', '@field.info', '@string_translation']
forum.breadcrumb:
class: Drupal\forum\ForumBreadcrumbBuilder
- arguments: ['@entity.manager', '@config.factory']
+ arguments: ['@entity.manager', '@config.factory', '@forum_manager']
tags:
- { name: breadcrumb_builder, priority: 1001 }
diff --git a/core/modules/forum/lib/Drupal/forum/Controller/ForumController.php b/core/modules/forum/lib/Drupal/forum/Controller/ForumController.php
index e9fec4b..a0db5d2 100644
--- a/core/modules/forum/lib/Drupal/forum/Controller/ForumController.php
+++ b/core/modules/forum/lib/Drupal/forum/Controller/ForumController.php
@@ -7,10 +7,15 @@
namespace Drupal\forum\Controller;
-use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Config\Config;
+use Drupal\Core\Controller\ControllerInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityManager;
+use Drupal\Core\StringTranslation\TranslationManager;
+use Drupal\forum\ForumManagerInterface;
+use Drupal\taxonomy\TermInterface;
use Drupal\taxonomy\TermStorageControllerInterface;
+use Drupal\taxonomy\VocabularyStorageControllerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -19,6 +24,13 @@
class ForumController implements ContainerInjectionInterface {
/**
+ * Forum manager service.
+ *
+ * @var \Drupal\forum\ForumManagerInterface
+ */
+ protected $forumManager;
+
+ /**
* Entity Manager Service.
*
* @var \Drupal\Core\Entity\EntityManager
@@ -33,37 +45,141 @@ class ForumController implements ContainerInjectionInterface {
protected $config;
/**
+ * Vocabulary storage controller.
+ *
+ * @var \Drupal\taxonomy\VocabularyStorageControllerInterface
+ */
+ protected $vocabularyStorageController;
+
+ /**
* Term storage controller.
*
* @var \Drupal\taxonomy\TermStorageControllerInterface
*/
- protected $storageController;
+ protected $termStorageController;
+
+ /**
+ * Translation manager service.
+ *
+ * @var \Drupal\Core\StringTranslation\TranslationManager
+ */
+ protected $translationManager;
+
+ /**
+ * Constructs a ForumController object.
+ *
+ * @param \Drupal\Core\Config\Config $config
+ * Config object for forum.settings.
+ * @param \Drupal\forum\ForumManagerInterface $forum_manager
+ * The forum manager service.
+ * @param \Drupal\taxonomy\VocabularyStorageControllerInterface $vocabulary_storage_controller
+ * Vocabulary storage controller.
+ * @param \Drupal\taxonomy\TermStorageControllerInterface $term_storage_controller
+ * Term storage controller.
+ * @param \Drupal\Core\Entity\EntityManager $entity_manager
+ * The entity manager service.
+ * @param \Drupal\Core\StringTranslation\TranslationManager $translation_manager
+ * The translation manager service.
+ */
+ public function __construct(Config $config, ForumManagerInterface $forum_manager, VocabularyStorageControllerInterface $vocabulary_storage_controller, TermStorageControllerInterface $term_storage_controller, EntityManager $entity_manager, TranslationManager $translation_manager) {
+ $this->config = $config;
+ $this->forumManager = $forum_manager;
+ $this->vocabularyStorageController = $vocabulary_storage_controller;
+ $this->termStorageController = $term_storage_controller;
+ $this->entityManager = $entity_manager;
+ $this->translationManager = $translation_manager;
+ }
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
+ $container->get('config.factory')->get('forum.settings'),
+ $container->get('forum_manager'),
+ $container->get('entity.manager')->getStorageController('taxonomy_vocabulary'),
+ $container->get('entity.manager')->getStorageController('taxonomy_term'),
$container->get('entity.manager'),
- $container->get('config.factory'),
- $container->get('entity.manager')->getStorageController('taxonomy_term')
+ $container->get('string_translation')
);
}
/**
- * Constructs a ForumController object.
+ * Returns forum page for a given forum.
*
- * @param \Drupal\Core\Entity\EntityManager $entity_manager
- * The entity manager service.
- * @param \Drupal\Core\Config\ConfigFactory $config_factory
- * The factory for configuration objects.
- * @param \Drupal\taxonomy\TermStorageControllerInterface $storage_controller
- * The term storage controller.
+ * @param \Drupal\taxonomy\TermInterface $taxonomy_term
+ * The forum to render the page for.
+ *
+ * @return array
+ * A render array.
*/
- public function __construct(EntityManager $entity_manager, ConfigFactory $config_factory, TermStorageControllerInterface $storage_controller) {
- $this->entityManager = $entity_manager;
- $this->config = $config_factory->get('forum.settings');
- $this->storageController = $storage_controller;
+ public function forumPage(TermInterface $taxonomy_term) {
+ // Get forum details.
+ $taxonomy_term->forums = $this->forumManager->getChildren($this->config->get('vocabulary'), $taxonomy_term->id());
+ $taxonomy_term->parents = $this->forumManager->getParents($taxonomy_term->id());
+ if (empty($taxonomy_term->forum_container->value)) {
+ // Add RSS feed for forums.
+ drupal_add_feed('taxonomy/term/' . $taxonomy_term->id() . '/feed', 'RSS - ' . $taxonomy_term->label());
+ }
+
+ if (empty($taxonomy_term->forum_container->value)) {
+ $topics = $this->forumManager->getTopics($taxonomy_term->id());
+ }
+ else {
+ $topics = '';
+ }
+ return $this->build($taxonomy_term->forums, $taxonomy_term, $topics, $taxonomy_term->parents);
+ }
+
+ /**
+ * Returns forum index page.
+ *
+ * @return array
+ * A render array.
+ */
+ public function forumIndex() {
+ $vocabulary = $this->vocabularyStorageController->load($this->config->get('vocabulary'));
+ $index = $this->forumManager->getIndex();
+ $build = $this->build($index->forums, $index);
+ if (empty($index->forums)) {
+ // Root of empty forum.
+ $build['#title'] = $this->translationManager->translate('No forums defined');
+ }
+ else {
+ // Set the page title to forum's vocabulary name.
+ $build['#title'] = $vocabulary->label();
+ }
+ return $build;
+ }
+
+ /**
+ * Returns a renderable forum index page array.
+ *
+ * @param array $forums
+ * A list of forums.
+ * @param \Drupal\taxonomy\TermInterface $term
+ * The taxonomy term of the forum.
+ * @param array $topics
+ * The topics of this forum.
+ * @param array $parents
+ * The parent forums in relation this forum.
+ *
+ * @return array
+ * A render array.
+ */
+ protected function build($forums, TermInterface $term, $topics = array(), $parents = array()) {
+ $build = array(
+ '#theme' => 'forums',
+ '#forums' => $forums,
+ '#topics' => $topics,
+ '#parents' => $parents,
+ '#term' => $term,
+ '#sortby' => $this->config->get('topics.order'),
+ '#forums_per_page' => $this->config->get('topics.page_limit'),
+ );
+ // @todo Make this a library - see https://drupal.org/node/2028113.
+ $build['#attached']['css'][] = drupal_get_path('module', 'forum') . '/css/forum.module.css';
+ return $build;
}
/**
@@ -74,8 +190,9 @@ public function __construct(EntityManager $entity_manager, ConfigFactory $config
*/
public function addForum() {
$vid = $this->config->get('vocabulary');
- $taxonomy_term = $this->storageController->create(array(
+ $taxonomy_term = $this->termStorageController->create(array(
'vid' => $vid,
+ 'forum_controller' => 0,
));
return $this->entityManager->getForm($taxonomy_term, 'forum');
}
@@ -88,8 +205,9 @@ public function addForum() {
*/
public function addContainer() {
$vid = $this->config->get('vocabulary');
- $taxonomy_term = $this->storageController->create(array(
+ $taxonomy_term = $this->termStorageController->create(array(
'vid' => $vid,
+ 'forum_container' => 1,
));
return $this->entityManager->getForm($taxonomy_term, 'container');
}
diff --git a/core/modules/forum/lib/Drupal/forum/Form/ContainerFormController.php b/core/modules/forum/lib/Drupal/forum/Form/ContainerFormController.php
index 208d39c..40f8199 100644
--- a/core/modules/forum/lib/Drupal/forum/Form/ContainerFormController.php
+++ b/core/modules/forum/lib/Drupal/forum/Form/ContainerFormController.php
@@ -42,14 +42,8 @@ public function form(array $form, array &$form_state) {
*/
public function save(array $form, array &$form_state) {
$is_new = $this->entity->isNew();
+ $this->entity->forum_container = TRUE;
$term = parent::save($form, $form_state);
- if ($is_new) {
- // Update config item to track the container terms.
- $config = $this->configFactory->get('forum.settings');
- $containers = $config->get('containers');
- $containers[] = $term->id();
- $config->set('containers', $containers)->save();
- }
}
}
diff --git a/core/modules/forum/lib/Drupal/forum/ForumBreadcrumbBuilder.php b/core/modules/forum/lib/Drupal/forum/ForumBreadcrumbBuilder.php
index c8d523c..724baaf 100644
--- a/core/modules/forum/lib/Drupal/forum/ForumBreadcrumbBuilder.php
+++ b/core/modules/forum/lib/Drupal/forum/ForumBreadcrumbBuilder.php
@@ -10,6 +10,8 @@
use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
use Drupal\Core\Config\ConfigFactory;
use Drupal\Core\Entity\EntityManager;
+use Drupal\forum\ForumManagerInterface;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
/**
* Class to define the forum breadcrumb builder.
@@ -31,47 +33,47 @@ class ForumBreadcrumbBuilder implements BreadcrumbBuilderInterface {
protected $entityManager;
/**
+ * The forum manager service.
+ *
+ * @var \Drupal\forum\ForumManagerInterface
+ */
+ protected $forumManager;
+
+ /**
* Constructs a new ForumBreadcrumbBuilder.
*
* @param \Drupal\Core\Entity\EntityManager
* The entity manager.
* @param \Drupal\Core\Config\ConfigFactory $configFactory
* The configuration factory.
+ * @param \Drupal\forum\ForumManagerInterface $forum_manager
+ * The forum manager service.
*/
- public function __construct(EntityManager $entity_manager, ConfigFactory $configFactory) {
+ public function __construct(EntityManager $entity_manager, ConfigFactory $configFactory, ForumManagerInterface $forum_manager) {
$this->entityManager = $entity_manager;
$this->config = $configFactory->get('forum.settings');
+ $this->forumManager = $forum_manager;
}
/**
* {@inheritdoc}
*/
public function build(array $attributes) {
-
// @todo This only works for legacy routes. Once node/% and forum/% are
// converted to the new router this code will need to be updated.
- if (isset($attributes['_drupal_menu_item'])) {
- $item = $attributes['_drupal_menu_item'];
- switch ($item['path']) {
-
- case 'node/%':
- $node = $item['map'][1];
- // Load the object in case of missing wildcard loaders.
- $node = is_object($node) ? $node : node_load($node);
- if (_forum_node_check_node_type($node)) {
- $breadcrumb = $this->forumPostBreadcrumb($node);
- }
- break;
-
- case 'forum/%':
- $term = $item['map'][1];
- // Load the object in case of missing wildcard loaders.
- $term = is_object($term) ? $term : forum_forum_load($term);
- $breadcrumb = $this->forumTermBreadcrumb($term);
- break;
+ if (isset($attributes['_drupal_menu_item']) && ($item = $attributes['_drupal_menu_item']) && $item['path'] == 'node/%') {
+ $node = $item['map'][1];
+ // Load the object in case of missing wildcard loaders.
+ $node = is_object($node) ? $node : node_load($node);
+ if ($this->forumManager->checkNodeType($node)) {
+ $breadcrumb = $this->forumPostBreadcrumb($node);
}
}
+ if (!empty($attributes[RouteObjectInterface::ROUTE_NAME]) && $attributes[RouteObjectInterface::ROUTE_NAME] == 'forum_page' && isset($attributes['taxonomy_term'])) {
+ $breadcrumb = $this->forumTermBreadcrumb($attributes['taxonomy_term']);
+ }
+
if (!empty($breadcrumb)) {
return $breadcrumb;
}
diff --git a/core/modules/forum/lib/Drupal/forum/ForumManager.php b/core/modules/forum/lib/Drupal/forum/ForumManager.php
new file mode 100644
index 0000000..0f03e63
--- /dev/null
+++ b/core/modules/forum/lib/Drupal/forum/ForumManager.php
@@ -0,0 +1,551 @@
+configFactory = $config_factory;
+ $this->entityManager = $entity_manager;
+ $this->connection = $connection;
+ $this->fieldInfo = $field_info;
+ $this->translationManager = $translation_manager;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getTopics($tid) {
+ $config = $this->configFactory->get('forum.settings');
+ $forum_per_page = $config->get('topics.page_limit');
+ $sortby = $config->get('topics.order');
+
+ global $user, $forum_topic_list_header;
+
+ $forum_topic_list_header = array(
+ array('data' => $this->translationManager->translate('Topic'), 'field' => 'f.title'),
+ array('data' => $this->translationManager->translate('Replies'), 'field' => 'f.comment_count'),
+ array('data' => $this->translationManager->translate('Last reply'), 'field' => 'f.last_comment_timestamp'),
+ );
+
+ $order = $this->getTopicOrder($sortby);
+ for ($i = 0; $i < count($forum_topic_list_header); $i++) {
+ if ($forum_topic_list_header[$i]['field'] == $order['field']) {
+ $forum_topic_list_header[$i]['sort'] = $order['sort'];
+ }
+ }
+
+ $query = $this->connection->select('forum_index', 'f')
+ ->extend('Drupal\Core\Database\Query\PagerSelectExtender')
+ ->extend('Drupal\Core\Database\Query\TableSortExtender');
+ $query->fields('f');
+ $query
+ ->condition('f.tid', $tid)
+ ->addTag('node_access')
+ ->addMetaData('base_table', 'forum_index')
+ ->orderBy('f.sticky', 'DESC')
+ ->orderByHeader($forum_topic_list_header)
+ ->limit($forum_per_page);
+
+ $count_query = $this->connection->select('forum_index', 'f');
+ $count_query->condition('f.tid', $tid);
+ $count_query->addExpression('COUNT(*)');
+ $count_query->addTag('node_access');
+ $count_query->addMetaData('base_table', 'forum_index');
+
+ $query->setCountQuery($count_query);
+ $result = $query->execute();
+ $nids = array();
+ foreach ($result as $record) {
+ $nids[] = $record->nid;
+ }
+ if ($nids) {
+ $nodes = $this->entityManager->getStorageController('node')->loadMultiple($nids);
+
+ $query = $this->connection->select('node_field_data', 'n')
+ ->extend('Drupal\Core\Database\Query\TableSortExtender');
+ $query->fields('n', array('nid'));
+
+ $query->join('node_comment_statistics', 'ncs', 'n.nid = ncs.nid');
+ $query->fields('ncs', array(
+ 'cid',
+ 'last_comment_uid',
+ 'last_comment_timestamp',
+ 'comment_count'
+ ));
+
+ $query->join('forum_index', 'f', 'f.nid = ncs.nid');
+ $query->addField('f', 'tid', 'forum_tid');
+
+ $query->join('users', 'u', 'n.uid = u.uid');
+ $query->addField('u', 'name');
+
+ $query->join('users', 'u2', 'ncs.last_comment_uid = u2.uid');
+
+ $query->addExpression('CASE ncs.last_comment_uid WHEN 0 THEN ncs.last_comment_name ELSE u2.name END', 'last_comment_name');
+
+ $query
+ ->orderBy('f.sticky', 'DESC')
+ ->orderByHeader($forum_topic_list_header)
+ ->condition('n.nid', $nids)
+ // @todo This should be actually filtering on the desired node language
+ // and just fall back to the default language.
+ ->condition('n.default_langcode', 1);
+
+ $result = array();
+ foreach ($query->execute() as $row) {
+ $topic = $nodes[$row->nid];
+ $topic->comment_mode = $topic->comment;
+
+ foreach ($row as $key => $value) {
+ $topic->{$key} = $value;
+ }
+ $result[] = $topic;
+ }
+ }
+ else {
+ $result = array();
+ }
+
+ $topics = array();
+ $first_new_found = FALSE;
+ foreach ($result as $topic) {
+ if ($user->isAuthenticated()) {
+ // A forum is new if the topic is new, or if there are new comments since
+ // the user's last visit.
+ if ($topic->forum_tid != $tid) {
+ $topic->new = 0;
+ }
+ else {
+ $history = $this->lastVisit($topic->id());
+ $topic->new_replies = $this->numberNew($topic->id(), $history);
+ $topic->new = $topic->new_replies || ($topic->last_comment_timestamp > $history);
+ }
+ }
+ else {
+ // Do not track "new replies" status for topics if the user is anonymous.
+ $topic->new_replies = 0;
+ $topic->new = 0;
+ }
+
+ // Make sure only one topic is indicated as the first new topic.
+ $topic->first_new = FALSE;
+ if ($topic->new != 0 && !$first_new_found) {
+ $topic->first_new = TRUE;
+ $first_new_found = TRUE;
+ }
+
+ if ($topic->comment_count > 0) {
+ $last_reply = new \stdClass();
+ $last_reply->created = $topic->last_comment_timestamp;
+ $last_reply->name = $topic->last_comment_name;
+ $last_reply->uid = $topic->last_comment_uid;
+ $topic->last_reply = $last_reply;
+ }
+ $topics[$topic->id()] = $topic;
+ }
+
+ return $topics;
+
+ }
+
+ /**
+ * Gets topic sorting information based on an integer code.
+ *
+ * @param int $sortby
+ * One of the following integers indicating the sort criteria:
+ * - ForumManager::NEWEST_FIRST: Date - newest first.
+ * - ForumManager::OLDEST_FIRST: Date - oldest first.
+ * - ForumManager::MOST_POPULAR_FIRST: Posts with the most comments first.
+ * - ForumManager::LEAST_POPULAR_FIRST: Posts with the least comments first.
+ *
+ * @return array
+ * An array with the following values:
+ * - field: A field for an SQL query.
+ * - sort: 'asc' or 'desc'.
+ */
+ protected function getTopicOrder($sortby) {
+ switch ($sortby) {
+ case static::NEWEST_FIRST:
+ return array('field' => 'f.last_comment_timestamp', 'sort' => 'desc');
+
+ case static::OLDEST_FIRST:
+ return array('field' => 'f.last_comment_timestamp', 'sort' => 'asc');
+
+ case static::MOST_POPULAR_FIRST:
+ return array('field' => 'f.comment_count', 'sort' => 'desc');
+
+ case static::LEAST_POPULAR_FIRST:
+ return array('field' => 'f.comment_count', 'sort' => 'asc');
+
+ }
+ }
+
+ /**
+ * Wraps comment_num_new() in a method.
+ *
+ * @param int $nid
+ * Node ID.
+ * @param int $timestamp
+ * Timestamp of last read.
+ *
+ * @return int
+ * Number of new comments.
+ */
+ protected function numberNew($nid, $timestamp) {
+ return comment_num_new($nid, $timestamp);
+ }
+
+ /**
+ * Gets the last time the user viewed a node.
+ *
+ * @param int $nid
+ * The node ID.
+ *
+ * @return int
+ * The timestamp when the user last viewed this node, if the user has
+ * previously viewed the node; otherwise HISTORY_READ_LIMIT.
+ */
+ protected function lastVisit($nid) {
+ global $user;
+
+ if (empty($this->history[$nid])) {
+ $result = $this->connection->select('history', 'h')
+ ->fields('h', array('nid', 'timestamp'))
+ ->condition('uid', $user->id())
+ ->execute();
+ foreach ($result as $t) {
+ $this->history[$t->nid] = $t->timestamp > HISTORY_READ_LIMIT ? $t->timestamp : HISTORY_READ_LIMIT;
+ }
+ }
+ return isset($this->history[$nid]) ? $this->history[$nid] : HISTORY_READ_LIMIT;
+ }
+
+ /**
+ * Provides the last post information for the given forum tid.
+ *
+ * @param int $tid
+ * The forum tid.
+ *
+ * @return \stdClass
+ * The last post for the given forum.
+ */
+ protected function getLastPost($tid) {
+ if (!empty($this->lastPostData[$tid])) {
+ return $this->lastPostData[$tid];
+ }
+ // Query "Last Post" information for this forum.
+ $query = $this->connection->select('node_field_data', 'n');
+ $query->join('forum', 'f', 'n.vid = f.vid AND f.tid = :tid', array(':tid' => $tid));
+ $query->join('node_comment_statistics', 'ncs', 'n.nid = ncs.nid');
+ $query->join('users', 'u', 'ncs.last_comment_uid = u.uid');
+ $query->addExpression('CASE ncs.last_comment_uid WHEN 0 THEN ncs.last_comment_name ELSE u.name END', 'last_comment_name');
+
+ $topic = $query
+ ->fields('ncs', array('last_comment_timestamp', 'last_comment_uid'))
+ ->condition('n.status', 1)
+ ->orderBy('last_comment_timestamp', 'DESC')
+ ->range(0, 1)
+ ->addTag('node_access')
+ ->execute()
+ ->fetchObject();
+
+ // Build the last post information.
+ $last_post = new \stdClass();
+ if (!empty($topic->last_comment_timestamp)) {
+ $last_post->created = $topic->last_comment_timestamp;
+ $last_post->name = $topic->last_comment_name;
+ $last_post->uid = $topic->last_comment_uid;
+ }
+
+ $this->lastPostData[$tid] = $last_post;
+ return $last_post;
+ }
+
+ /**
+ * Provides statistics for a forum.
+ *
+ * @param int $tid
+ * The forum tid.
+ *
+ * @return \stdClass|null
+ * Statistics for the given forum if statistics exist, else NULL.
+ */
+ protected function getForumStatistics($tid) {
+ if (empty($this->forumStatistics)) {
+ // Prime the statistics.
+ $query = $this->connection->select('node_field_data', 'n');
+ $query->join('node_comment_statistics', 'ncs', 'n.nid = ncs.nid');
+ $query->join('forum', 'f', 'n.vid = f.vid');
+ $query->addExpression('COUNT(n.nid)', 'topic_count');
+ $query->addExpression('SUM(ncs.comment_count)', 'comment_count');
+ $this->forumStatistics = $query
+ ->fields('f', array('tid'))
+ ->condition('n.status', 1)
+ ->condition('n.default_langcode', 1)
+ ->groupBy('tid')
+ ->addTag('node_access')
+ ->execute()
+ ->fetchAllAssoc('tid');
+ }
+
+ if (!empty($this->forumStatistics[$tid])) {
+ return $this->forumStatistics[$tid];
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getChildren($vid, $tid) {
+ if (!empty($this->forumChildren[$tid])) {
+ return $this->forumChildren[$tid];
+ }
+ $forums = array();
+ $_forums = taxonomy_get_tree($vid, $tid, NULL, TRUE);
+ foreach ($_forums as $forum) {
+ // Merge in the topic and post counters.
+ if (($count = $this->getForumStatistics($forum->id()))) {
+ $forum->num_topics = $count->topic_count;
+ $forum->num_posts = $count->topic_count + $count->comment_count;
+ }
+ else {
+ $forum->num_topics = 0;
+ $forum->num_posts = 0;
+ }
+
+ // Merge in last post details.
+ $forum->last_post = $this->getLastPost($forum->id());
+ $forums[$forum->id()] = $forum;
+ }
+
+ $this->forumChildren[$tid] = $forums;
+ return $forums;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getIndex() {
+ if ($this->index) {
+ return $this->index;
+ }
+
+ $vid = $this->configFactory->get('forum.settings')->get('vocabulary');
+ $index = $this->entityManager->getStorageController('taxonomy_term')->create(array(
+ 'tid' => 0,
+ 'container' => 1,
+ 'parents' => array(),
+ 'isIndex' => TRUE,
+ 'vid' => $vid
+ ));
+
+ // Load the tree below.
+ $index->forums = $this->getChildren($vid, 0);
+ $this->index = $index;
+ return $index;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function resetCache() {
+ // Reset the index.
+ $this->index = NULL;
+ // Reset history.
+ $this->history = NULL;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getParents($tid) {
+ return taxonomy_term_load_parents_all($tid);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function checkNodeType(NodeInterface $node) {
+ // Fetch information about the forum field.
+ $instances = $this->fieldInfo->getBundleInstances('node', $node->bundle());
+ return !empty($instances['taxonomy_forums']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function unreadTopics($term, $uid) {
+ $query = $this->connection->select('node_field_data', 'n');
+ $query->join('forum', 'f', 'n.vid = f.vid AND f.tid = :tid', array(':tid' => $term));
+ $query->leftJoin('history', 'h', 'n.nid = h.nid AND h.uid = :uid', array(':uid' => $uid));
+ $query->addExpression('COUNT(n.nid)', 'count');
+ return $query
+ ->condition('status', 1)
+ // @todo This should be actually filtering on the desired node status
+ // field language and just fall back to the default language.
+ ->condition('n.default_langcode', 1)
+ ->condition('n.created', HISTORY_READ_LIMIT, '>')
+ ->isNull('h.nid')
+ ->addTag('node_access')
+ ->execute()
+ ->fetchField();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function updateIndex($nid) {
+ $count = $this->connection->query('SELECT COUNT(cid) FROM {comment} c INNER JOIN {forum_index} i ON c.nid = i.nid WHERE c.nid = :nid AND c.status = :status', array(
+ ':nid' => $nid,
+ ':status' => COMMENT_PUBLISHED,
+ ))->fetchField();
+
+ if ($count > 0) {
+ // Comments exist.
+ $last_reply = $this->connection->queryRange('SELECT cid, name, created, uid FROM {comment} WHERE nid = :nid AND status = :status ORDER BY cid DESC', 0, 1, array(
+ ':nid' => $nid,
+ ':status' => COMMENT_PUBLISHED,
+ ))->fetchObject();
+ $this->connection->update('forum_index')
+ ->fields( array(
+ 'comment_count' => $count,
+ 'last_comment_timestamp' => $last_reply->created,
+ ))
+ ->condition('nid', $nid)
+ ->execute();
+ }
+ else {
+ // Comments do not exist.
+ // @todo This should be actually filtering on the desired node language and
+ // just fall back to the default language.
+ $node = $this->connection->query('SELECT uid, created FROM {node_field_data} WHERE nid = :nid AND default_langcode = 1', array(':nid' => $nid))->fetchObject();
+ $this->connection->update('forum_index')
+ ->fields( array(
+ 'comment_count' => 0,
+ 'last_comment_timestamp' => $node->created,
+ ))
+ ->condition('nid', $nid)
+ ->execute();
+ }
+ }
+}
diff --git a/core/modules/forum/lib/Drupal/forum/ForumManagerInterface.php b/core/modules/forum/lib/Drupal/forum/ForumManagerInterface.php
new file mode 100644
index 0000000..6072d87
--- /dev/null
+++ b/core/modules/forum/lib/Drupal/forum/ForumManagerInterface.php
@@ -0,0 +1,104 @@
+assertEqual($topics, '6', 'Number of topics found.');
// Verify the number of unread topics.
- $unread_topics = _forum_topics_unread($this->forum['tid'], $this->edit_any_topics_user->id());
+ $unread_topics = $this->container->get('forum_manager')->unreadTopics($this->forum['tid'], $this->edit_any_topics_user->id());
$unread_topics = format_plural($unread_topics, '1 new post', '@count new posts');
$xpath = $this->buildXPathQuery('//tr[@id=:forum]//td[@class="topics"]//a', $forum_arg);
$this->assertFieldByXPath($xpath, $unread_topics, 'Number of unread topics found.');
@@ -417,6 +417,8 @@ function createForum($type, $parent = 0) {
$parent_tid = db_query("SELECT t.parent FROM {taxonomy_term_hierarchy} t WHERE t.tid = :tid", array(':tid' => $tid))->fetchField();
$this->assertTrue($parent == $parent_tid, 'The ' . $type . ' is linked to its container');
+ $forum = $this->container->get('plugin.manager.entity')->getStorageController('taxonomy_term')->load($tid);
+ $this->assertEqual(($type == 'forum container'), (bool) $forum->forum_container->value);
return $term;
}
@@ -437,11 +439,6 @@ function deleteForum($tid) {
// Assert that the forum no longer exists.
$this->drupalGet('forum/' . $tid);
$this->assertResponse(404, 'The forum was not found');
-
- // Assert that the associated term has been removed from the
- // forum_containers variable.
- $containers = \Drupal::config('forum.settings')->get('containers');
- $this->assertFalse(in_array($tid, $containers), 'The forum_containers variable has been updated.');
}
/**
diff --git a/core/modules/forum/tests/Drupal/forum/Tests/ForumManagerTest.php b/core/modules/forum/tests/Drupal/forum/Tests/ForumManagerTest.php
new file mode 100644
index 0000000..96675fb
--- /dev/null
+++ b/core/modules/forum/tests/Drupal/forum/Tests/ForumManagerTest.php
@@ -0,0 +1,101 @@
+ 'Forum Manager',
+ 'description' => 'Tests the forum manager functionality.',
+ 'group' => 'Forum',
+ );
+ }
+
+ /**
+ * Tests ForumManager::getIndex().
+ */
+ public function testGetIndex() {
+ $entity_manager = $this->getMockBuilder('\Drupal\Core\Entity\EntityManager')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $storage_controller = $this->getMockBuilder('\Drupal\taxonomy\VocabularyStorageController')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $config_factory = $this->getMockBuilder('\Drupal\Core\Config\ConfigFactory')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $config = $this->getMockBuilder('\Drupal\Core\Config\Config')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $config_factory->expects($this->once())
+ ->method('get')
+ ->will($this->returnValue($config));
+
+ $config->expects($this->once())
+ ->method('get')
+ ->will($this->returnValue('forums'));
+
+ $entity_manager->expects($this->once())
+ ->method('getStorageController')
+ ->will($this->returnValue($storage_controller));
+
+ // This is sufficient for testing purposes.
+ $term = new \stdClass();
+
+ $storage_controller->expects($this->once())
+ ->method('create')
+ ->will($this->returnValue($term));
+
+ $connection = $this->getMockBuilder('\Drupal\Core\Database\Connection')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $translation_manager = $this->getMockBuilder('\Drupal\Core\StringTranslation\TranslationManager')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $field_info = $this->getMockBuilder('\Drupal\field\FieldInfo')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $manager = $this->getMock('\Drupal\forum\ForumManager', array('getChildren'), array(
+ $config_factory,
+ $entity_manager,
+ $connection,
+ $field_info,
+ $translation_manager,
+ ));
+
+ $manager->expects($this->once())
+ ->method('getChildren')
+ ->will($this->returnValue(array()));
+
+ // Get the index once.
+ $index1 = $manager->getIndex();
+
+ // Get it again. This should not return the previously generated index. If
+ // it does not, then the test will fail as the mocked methods will be called
+ // more than once.
+ $index2 = $manager->getIndex();
+
+ $this->assertEquals($index1, $index2);
+ }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/ForumUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/ForumUpgradePathTest.php
new file mode 100644
index 0000000..e1a50e4
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/ForumUpgradePathTest.php
@@ -0,0 +1,58 @@
+ 'Forum upgrade test',
+ 'description' => 'Upgrade tests with forum data.',
+ 'group' => 'Upgrade path',
+ );
+ }
+
+ public function setUp() {
+ $path = drupal_get_path('module', 'system') . '/tests/upgrade';
+ $this->databaseDumpFiles = array(
+ $path . '/drupal-7.bare.standard_all.database.php.gz',
+ $path . '/drupal-7.forum.database.php',
+ );
+ parent::setUp();
+ }
+
+ /**
+ * Tests expected forum and container conversions after a successful upgrade.
+ */
+ public function testForumUpgrade() {
+ $this->assertTrue($this->performUpgrade(), 'The upgrade was completed successfully.');
+
+ // Make sure the field is created.
+ $vocabulary = $this->container->get('config.factory')->get('forum.settings')->get('vocabulary');
+ $field = field_info_instance('taxonomy_term', 'forum_container', $vocabulary);
+ $this->assertTrue((bool) $field, 'Field was found');
+
+ // Check that the values of forum_container are correct.
+ $containers = entity_load_multiple_by_properties('taxonomy_term', array('name' => 'Container'));
+ $container = reset($containers);
+ $this->assertTrue((bool) $container->forum_container->value);
+
+ $forums = entity_load_multiple_by_properties('taxonomy_term', array('name' => 'Forum'));
+ $forum = reset($forums);
+ $this->assertFalse((bool) $forum->forum_container->value);
+ }
+
+}
diff --git a/core/modules/system/tests/upgrade/drupal-7.forum.database.php b/core/modules/system/tests/upgrade/drupal-7.forum.database.php
new file mode 100644
index 0000000..b6e6cf8
--- /dev/null
+++ b/core/modules/system/tests/upgrade/drupal-7.forum.database.php
@@ -0,0 +1,48 @@
+fields('tv', array('vid'))
+ ->condition('name', 'forums')
+ ->execute()
+ ->fetchField();
+
+$container = db_insert('taxonomy_term_data')
+ ->fields(array(
+ 'vid' => $vocabulary,
+ 'name' => 'Container',
+ 'description' => 'Container',
+ 'format' => 'full_html',
+ 'weight' => 0,
+ ))
+ ->execute();
+
+$forum = db_insert('taxonomy_term_data')
+ ->fields(array(
+ 'vid' => $vocabulary,
+ 'name' => 'Forum',
+ 'description' => 'Forum',
+ 'format' => 'full_html',
+ 'weight' => 0,
+ ))
+ ->execute();
+
+db_delete('variable')
+ ->condition('name', 'forum_containers')
+ ->execute();
+
+db_insert('variable')->fields(array(
+ 'name' => 'forum_containers',
+ 'value' => serialize(array($container)),
+))
+->execute();