diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module index 574d536..2620420 100644 --- a/core/modules/forum/forum.module +++ b/core/modules/forum/forum.module @@ -93,18 +93,11 @@ 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_page', ); - $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_forum_page', ); $items['admin/structure/forum'] = array( 'title' => 'Forums', @@ -118,6 +111,7 @@ function forum_menu() { 'title' => 'List', 'type' => MENU_DEFAULT_LOCAL_TASK, ); + // @todo route and form controllerify this. $items['admin/structure/forum/add/container'] = array( 'title' => 'Add container', 'page callback' => 'forum_form_main', @@ -127,6 +121,7 @@ function forum_menu() { 'parent' => 'admin/structure/forum', 'file' => 'forum.admin.inc', ); + // @todo route and form controllerify this. $items['admin/structure/forum/add/forum'] = array( 'title' => 'Add forum', 'page callback' => 'forum_form_main', @@ -136,6 +131,7 @@ function forum_menu() { 'parent' => 'admin/structure/forum', 'file' => 'forum.admin.inc', ); + // @todo route and form controllerify this. $items['admin/structure/forum/settings'] = array( 'title' => 'Settings', 'page callback' => 'drupal_get_form', @@ -146,6 +142,7 @@ function forum_menu() { 'parent' => 'admin/structure/forum', 'file' => 'forum.admin.inc', ); + // @todo route and form controllerify this. $items['admin/structure/forum/edit/container/%taxonomy_term'] = array( 'title' => 'Edit container', 'page callback' => 'forum_form_main', @@ -153,6 +150,7 @@ function forum_menu() { 'access arguments' => array('administer forums'), 'file' => 'forum.admin.inc', ); + // @todo route and form controllerify this. $items['admin/structure/forum/edit/forum/%taxonomy_term'] = array( 'title' => 'Edit forum', 'page callback' => 'forum_form_main', @@ -171,48 +169,45 @@ 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]->tid : 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('taxonomy_forums'); - foreach ($field['bundles']['node'] 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' => node_type_get_label($type))), - 'href' => 'node/add/' . $type . '/' . $forum_term->tid, - ), - ); - } + $tid = (isset($router_item['page_arguments'][0]) ? $router_item['page_arguments'][0] : 0); + $links = array(); + // Loop through all bundles for forum taxonomy vocabulary field. + $field = field_info_field('taxonomy_forums'); + foreach ($field['bundles']['node'] 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' => node_type_get_label($type))), + 'href' => 'node/add/' . $type . '/' . $tid, + ), + ); } - if (empty($links)) { - // Authenticated user does not have access to create new topics. - if ($user->uid) { - $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->uid) { + $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; } } @@ -675,125 +670,6 @@ function forum_form(EntityInterface $node, &$form_state) { } /** - * 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 = config('forum.settings'); - $vid = $config->get('vocabulary'); - - // Load and validate the parent term. - if ($tid) { - $forum_term = taxonomy_term_load($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, - )); - } - - // Determine if the requested term is a container. - if (!$forum_term->tid || in_array($forum_term->tid, $config->get('containers'))) { - $forum_term->container = 1; - } - - // Load parent terms. - $forum_term->parents = taxonomy_term_load_parents_all($forum_term->tid); - - // Load the tree below. - $forums = array(); - $_forums = taxonomy_get_tree($vid, $tid, NULL, TRUE); - - if (count($_forums)) { - $query = db_select('node', '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) - ->groupBy('tid') - ->addTag('node_access') - ->execute() - ->fetchAllAssoc('tid'); - } - - foreach ($_forums as $forum) { - // Determine if the child term is a container. - if (in_array($forum->tid, $config->get('containers'))) { - $forum->container = 1; - } - - // Merge in the topic and post counters. - if (!empty($counts[$forum->tid])) { - $forum->num_topics = $counts[$forum->tid]->topic_count; - $forum->num_posts = $counts[$forum->tid]->topic_count + $counts[$forum->tid]->comment_count; - } - else { - $forum->num_topics = 0; - $forum->num_posts = 0; - } - - // Query "Last Post" information for this forum. - $query = db_select('node', 'n'); - $query->join('forum', 'f', 'n.vid = f.vid AND f.tid = :tid', array(':tid' => $forum->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(); - - // 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->tid] = $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. diff --git a/core/modules/forum/forum.routing.yml b/core/modules/forum/forum.routing.yml new file mode 100644 index 0000000..155bc57 --- /dev/null +++ b/core/modules/forum/forum.routing.yml @@ -0,0 +1,12 @@ +forum_page: + pattern: 'forum' + defaults: + _content: 'Drupal\forum\Controller\ForumController::forumPage' + requirements: + _permission: 'access content' +forum_forum_page: + pattern: 'forum/{forum}' + defaults: + _content: 'Drupal\forum\Controller\ForumController::forumPage' + requirements: + _permission: 'access content' diff --git a/core/modules/forum/lib/Drupal/forum/Controller/ForumController.php b/core/modules/forum/lib/Drupal/forum/Controller/ForumController.php new file mode 100644 index 0000000..1e33923 --- /dev/null +++ b/core/modules/forum/lib/Drupal/forum/Controller/ForumController.php @@ -0,0 +1,144 @@ +get('config.factory'), $container->get('plugin.manager.entity')); + } + + /** + * Constructor. + * + * @param \Drupal\Core\Config\ConfigFactory $config_factory + * The config factory. + */ + public function __construct(ConfigFactory $config_factory, EntityManager $entity_manager) { + $this->config = $config_factory->get('forum.settings'); + $this->storageController = $entity_manager->getStorageController('forum'); + } + + /** + * Returns a forum page. + * + * @param \Drupal\forum\Plugin\Core\Entity\Forum $forum + * (optional) The forum to render the page for. If empty, the forum index is + * rendered instead. Defaults to NULL. + */ + public function forumPage(Forum $forum = NULL) { + if ($forum) { + return $this->forumForumPage($forum); + } + // Index page. + return $this->forumIndex(); + } + + /** + * Returns forum page for a given forum. + * + * @param \Drupal\forum\Plugin\Core\Entity\Forum $forum + * The forum to render the page for. + * + * @return array + * A render array. + */ + protected function forumForumPage(Forum $forum) { + // Page title. + drupal_set_title($forum->label()); + // Breadcrumb navigation. + $vocabulary = entity_load('taxonomy_vocabulary', $this->config->get('vocabulary')); + $breadcrumb[] = l(t('Home'), NULL); + $breadcrumb[] = l($vocabulary->label(), 'forum'); + foreach (array_reverse($forum->parents) as $parent) { + if ($parent->id() != $forum->id()) { + $breadcrumb[] = l($parent->label(), 'forum/' . $parent->id()); + } + } + drupal_set_breadcrumb($breadcrumb); + if ($forum->container) { + // Add RSS feed for forums. + drupal_add_feed('taxonomy/term/' . $forum->id() . '/feed', 'RSS - ' . $forum->label()); + } + $forum_per_page = $this->config->get('topics.page_limit'); + $sort_by = $this->config->get('topics.order'); + + if (!$forum->container) { + $topics = forum_get_topics($forum->id(), $sort_by, $forum_per_page); + } + else { + $topics = ''; + } + + $build = array( + '#theme' => 'forums', + '#forums' => $forum->forums, + '#topics' => $topics, + '#parents' => $forum->parents, + '#tid' => $forum->id(), + '#sortby' => $sort_by, + '#forums_per_page' => $forum_per_page, + ); + $build['#attached']['css'][] = drupal_get_path('module', 'forum') . '/forum.css'; + return $build; + } + + /** + * Returns forum index page. + * + * @return array + * A render array. + */ + protected function forumIndex() { + $vocabulary = entity_load('taxonomy_vocabulary', $this->config->get('vocabulary')); + $index = $this->storageController->forumIndex(); + // Set the page title to forum's vocabulary name. + drupal_set_title($vocabulary->label()); + if (empty($index->forums)) { + // Root of empty forum. + drupal_set_title(t('No forums defined')); + } + + $build = array( + '#theme' => 'forums', + '#forums' => $index->forums, + '#topics' => '', + '#parents' => array(), + '#tid' => $index->id(), + '#sortby' => $this->config->get('topics.order'), + '#forums_per_page' => $this->config->get('topics.page_limit'), + ); + $build['#attached']['css'][] = drupal_get_path('module', 'forum') . '/forum.css'; + return $build; + } + +} diff --git a/core/modules/forum/lib/Drupal/forum/ForumFormController.php b/core/modules/forum/lib/Drupal/forum/ForumFormController.php new file mode 100644 index 0000000..17419c7 --- /dev/null +++ b/core/modules/forum/lib/Drupal/forum/ForumFormController.php @@ -0,0 +1,27 @@ +config = config('forum.settings'); + } + + /** + * Overrides \Drupal\Core\Entity\DatabaseStorageController::delete(). + */ + public function delete(array $entities) { + parent::delete($entities); + // For containers, remove the tid from the forum_containers variable. + $containers = $this->config->get('containers'); + foreach (array_keys($entities) as $tid) { + if (!empty($containser[$tid])) { + unset($containers[$tid]); + } + } + $this->config->set('containers', $containers)->save(); + } + + /** + * Overrides \Drupal\Core\Entity\DatabaseStorageController::load(). + */ + public function load(array $ids = NULL) { + $entities = parent::load($ids); + $vid = $this->config->get('vocabulary'); + foreach ($entities as $tid => &$forum_term) { + if ($forum_term->bundle() != $vid) { + // Not a forum term. + unset($entities[$tid]); + } + + // Determine if the requested term is a container. + if (in_array($forum_term->id(), $this->config->get('containers'))) { + $forum_term->container = TRUE; + } + + // Load parent terms. + // @todo - is this still needed? + $forum_term->parents = taxonomy_term_load_parents_all($forum_term->id()); + + // Load the tree below. + $forum_term->forums = $this->getChildren($vid, $tid); + } + return $entities; + } + + /** + * Utility method to get 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 = db_select('node', '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; + } + + /** + * Utility method to fetch 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 = db_select('node', '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) + ->groupBy('tid') + ->addTag('node_access') + ->execute() + ->fetchAllAssoc('tid'); + } + + if (!empty($this->forumStatistics[$tid])) { + return $this->forumStatistics[$tid]; + } + } + + /** + * Utility method to fetch the child forums for a given forum. + * + * @param int $vid + * The forum vocabulary id. + * @param int $tid + * The forum id to fetch the children for. + * + * @return array + * Array of children. + */ + protected 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) { + // Determine if the child term is a container. + if (in_array($forum->tid, $this->config->get('containers'))) { + $forum->container = TRUE; + } + + // Merge in the topic and post counters. + if (($count = $this->getForumStatistics($forum->tid))) { + $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->tid); + $forums[$forum->tid] = $forum; + } + + $this->forumChildren[$tid] = $forums; + return $forums; + } + + /** + * Generates and returns the forum index. + * + * The forum index is a psuedo term that provides an overview of all forums. + * + * @return \Drupal\taxonomy\Plugin\Core\Entity\Term + * A psuedo term representing the overview of all forums. + */ + public function forumIndex() { + if ($this->index) { + return $index; + } + + $index = $this->create(array( + 'tid' => 0, + 'container' => TRUE, + 'parents' => array(), + 'isIndex' => TRUE, + )); + + // Load the tree below. + $index->forums = $this->getChildren($this->config->get('vocabulary'), 0); + $this->index = $index; + return $index; + } + + /** + * Overrides \Drupal\Core\Entity\DatabaseStorageController::resetCache(). + */ + public function resetCache(array $ids = NULL) { + parent::resetCache($ids); + // Reset the index. + $this->index = NULL; + } + + /** + * Overrides \Drupal\Core\Entity\DatabaseStorageController::save(). + */ + public function save(EntityInterface $entity) { + if (!empty($entity->isIndex)) { + throw new Exception(t('The forum index cannot be saved')); + } + return parent::save($entity); + } +} diff --git a/core/modules/forum/lib/Drupal/forum/Plugin/Core/Entity/Forum.php b/core/modules/forum/lib/Drupal/forum/Plugin/Core/Entity/Forum.php new file mode 100644 index 0000000..759a94d --- /dev/null +++ b/core/modules/forum/lib/Drupal/forum/Plugin/Core/Entity/Forum.php @@ -0,0 +1,84 @@ + 'forum/' . $this->id(), + ); + } +}