View Issue Details

IDProjectCategoryView StatusLast Update
2049Composrcns_forumpublic2024-07-23 15:57
ReporterChris Graham Assigned ToGuest  
PrioritynormalSeverityfeature 
Status newResolutionopen 
Summary2049: Inline mode for forum groupings
DescriptionA forum grouping can be set as 'inline'.

If inline then...

The top of the forum grouping box would be a posting box (similar to posting your status on Facebook).

Topics would show together in a single combined feed with "show previous" links to go back through topic history (and a small "view whole topic" link to get to the topicview module). i.e. AJAX browsing back into a topic, no separate screens for normal browsing

No signatures

Very mobile friendly and simplified. Look for ideas from #1659

Each topic would show the forum it is in as a tag rather than making you dig down. You can select the tag when you make the topic and it just defaults to the first forum in the grouping. You can only have one tag per topic for this reason, but also I think that does keep it simple.

When viewing, tags would be clickable like filters. When applying a tag filter you would see the forum description for associated forum display, similar to as if you were in that forum.


Optionally inline-created topics would not need a title defining, it would be taken automatically:
E.g.
"Hello, how are you today, this is my first post. I love this forum" might have a title of "How are you today". We'd try and be quite smart to make the auto-detection as good as possible, and use ellipsis (...) if we can't effectively grab a full sentence.
A config option would determine whether titles are auto-detected for inline forum groupings, or have to be explicitly entered.



If a site has only one forum grouping then the forum grouping box should not be visible at all.
Additional InformationThis should create a forum experience similar to the ease of use of Facebook, without compromising on the overall capabilities of Composr. It would be ideally suited to small forums that need to really draw out content before they have grown.
TagsHas Patch, Roadmap: Over the horizon, Type: Tagging
Attach Tags
Attached Files
forum_subjects.diff (91,520 bytes)   
diff --git a/forum/pages/modules/forumview.php b/forum/pages/modules/forumview.php
index fc076f6..ed52f5b 100644
--- a/forum/pages/modules/forumview.php
+++ b/forum/pages/modules/forumview.php
@@ -124,9 +124,18 @@ class Module_forumview
             }
 
             $forum_name = $forum_info['f_name'];
-            $ltitle = do_lang_tempcode('NAMED_FORUM', make_fractionable_editable('forum', $id, $forum_name));
+            $ltitle = do_lang_tempcode(
+                cns_in_subject_view() ? 'NAMED_FORUM_SUBJECT_VIEW' : 'NAMED_FORUM_REGULAR_VIEW',
+                make_fractionable_editable('forum', $id, $forum_name),
+                escape_html(get_self_url(true, false, array('keep_subject_view' => cns_in_subject_view() ? '0' : '1')))
+            );
+            $subtitle = do_lang_tempcode(
+                cns_in_subject_view() ? 'NAMED_FORUM_SUBJECT_VIEW_SUBLINE' : 'NAMED_FORUM_REGULAR_VIEW_SUBLINE',
+                make_fractionable_editable('forum', $id, $forum_name),
+                escape_html(get_self_url(true, false, array('keep_subject_view' => cns_in_subject_view() ? '0' : '1')))
+            );
 
-            $this->title = get_screen_title($ltitle, false, null, null, $awards);
+            $this->title = get_screen_title($ltitle, false, null, null, $awards, true, $subtitle->is_empty() ? null : $subtitle);
 
             if (($forum_info['f_redirection'] != '') && (looks_like_url($forum_info['f_redirection']))) {
                 require_code('site2');
@@ -196,13 +205,15 @@ class Module_forumview
             $sort_default = $forum_info['f_order'];
         }
 
+        $subject_view = cns_in_subject_view();
+
         require_code('cns_general');
         cns_set_context_forum($id);
 
         require_code('templates_pagination');
         list($max, $start, $sort, $sql_sup, $sql_sup_order_by, $true_start, , $keyset_field_stripped) = get_keyset_pagination_settings('forum_max', intval(get_option('forum_topics_per_page')), 'forum_start', $compound_name, 'sort', $sort_default, 'get_forum_sort_order');
 
-        $test = cns_render_forumview($id, $forum_info, $current_filter_cat, $max, $start, $true_start, get_param_string('order', 'last_post'), $sql_sup, $sql_sup_order_by, $keyset_field_stripped, $root, $of_member_id, $this->breadcrumbs);
+        $test = cns_render_forumview($id, $forum_info, $current_filter_cat, $max, $start, $true_start, get_param_string('order', 'last_post'), $sql_sup, $sql_sup_order_by, $keyset_field_stripped, $root, $of_member_id, $this->breadcrumbs, $subject_view);
         if (is_array($test)) {
             list($content, $forum_name) = $test;
         } else {
@@ -225,6 +236,7 @@ class Module_forumview
             'NUM_GUESTS' => is_null($num_guests) ? '' : integer_format($num_guests),
             'NUM_MEMBERS' => is_null($num_members) ? '' : integer_format($num_members),
             'MEMBERS_VIEWING' => $members_viewing,
+            'SUBJECT_VIEW' => $subject_view,
         ));
 
         require_code('templates_internalise_screen');
diff --git a/forum/pages/modules/topics.php b/forum/pages/modules/topics.php
index 7603f8e..7920b91 100644
--- a/forum/pages/modules/topics.php
+++ b/forum/pages/modules/topics.php
@@ -570,7 +570,14 @@ class Module_topics
         // Certain aspects relating to the posting system
         $fields = new Tempcode();
         $hidden = $this->keep_markers();
-        $fields->attach(form_input_tree_list(do_lang_tempcode('DESTINATION_FORUM'), do_lang_tempcode('DESCRIPTION_POSTS_DESTINATION_FORUM'), 'to_forum_id', null, 'choose_forum', array(), true, strval($topic_info[0]['t_forum_id'])));
+        if (cns_in_subject_view()) {
+            $df_label = do_lang_tempcode('FORUM_SUBJECT');
+            $df_description = do_lang_tempcode('DESCRIPTION_POSTS_NEW_SUBJECT');
+        } else {
+            $df_label = do_lang_tempcode('DESTINATION_FORUM');
+            $df_description = do_lang_tempcode('DESCRIPTION_POSTS_DESTINATION_FORUM');
+        }
+        $fields->attach(form_input_tree_list($df_label, $df_description, 'to_forum_id', null, 'choose_forum', array(), true, strval($topic_info[0]['t_forum_id'])));
         $fields->attach(form_input_line(do_lang_tempcode('TITLE'), do_lang_tempcode('TOPIC_TITLE_WILL_BE'), 'title', $default_title, false, null, 120));
         $fields->attach(form_input_tick(do_lang_tempcode('DELETE_IF_EMPTY'), do_lang_tempcode('DESCRIPTION_DELETE_IF_EMPTY'), 'delete_if_empty', true));
         $fields->attach(form_input_line(do_lang_tempcode('REASON'), do_lang_tempcode('DESCRIPTION_REASON'), 'reason', '', false));
@@ -1192,16 +1199,30 @@ class Module_topics
 
         // Certain aspects relating to the posting system
         $fields = new Tempcode();
-        $fields->attach(form_input_tree_list(do_lang_tempcode('DESTINATION_FORUM'), do_lang_tempcode('DESCRIPTION_DESTINATION_FORUM'), 'to', null, 'choose_forum', array(), true, strval($forum_id)));
+        if (cns_in_subject_view()) {
+            $df_label = do_lang_tempcode('NEW_SUBJECT');
+            $df_description = do_lang_tempcode('DESCRIPTION_NEW_SUBJECT');
+        } else {
+            $df_label = do_lang_tempcode('DESTINATION_FORUM');
+            $df_description = do_lang_tempcode('DESCRIPTION_DESTINATION_FORUM');
+        }
+        $fields->attach(form_input_tree_list($df_label, $df_description, 'to', null, 'choose_forum', array(), true, strval($forum_id)));
         $fields->attach(form_input_line(do_lang_tempcode('REASON'), do_lang_tempcode('DESCRIPTION_REASON'), 'description', '', false));
         $hidden = $this->keep_markers();
 
         $breadcrumbs = cns_forum_breadcrumbs($forum_id, null, null, false);
-        breadcrumb_set_self(do_lang_tempcode('MOVE_TOPICS'));
         breadcrumb_set_parents($breadcrumbs);
 
-        $title = get_screen_title('MOVE_TOPICS');
-        $submit_name = do_lang_tempcode('MOVE_TOPICS');
+        if (cns_in_subject_view()) {
+            breadcrumb_set_self(do_lang_tempcode('CHANGE_SUBJECT'));
+            $title = get_screen_title('CHANGE_SUBJECT');
+            $submit_name = do_lang_tempcode('CHANGE_SUBJECT');
+        } else {
+            breadcrumb_set_self(do_lang_tempcode('MOVE_TOPICS'));
+            $title = get_screen_title('MOVE_TOPICS');
+            $submit_name = do_lang_tempcode('MOVE_TOPICS');
+        }
+
         return do_template('FORM_SCREEN', array(
             '_GUID' => '7532b5e7239e0f9ceb64d09c28fd7261',
             'SKIP_WEBSTANDARDS' => true,
@@ -1234,7 +1255,7 @@ class Module_topics
         require_code('cns_topics_action');
         require_code('cns_topics_action2');
         cns_move_topics($from, $to, $topics);
-        return $this->redirect_to_forum('MOVE_TOPIC', $from);
+        return $this->redirect_to_forum(cns_in_subject_view() ? 'CHANGE_SUBJECT' : 'MOVE_TOPICS', $from);
     }
 
     /**
@@ -1676,7 +1697,14 @@ class Module_topics
             }
             $threaded = false;
         } else {
-            $hidden_fields->attach(form_input_hidden('forum_id', strval($forum_id)));
+            if (cns_in_subject_view()) {
+                require_code('cns_forums2');
+                $forums_list = create_selection_list_forum_tree(null, null, array($forum_id));
+                $field = form_input_list(do_lang_tempcode('FORUM_SUBJECT'), '', 'forum_id', $forums_list, null, false, false);
+                $specialisation->attach($field);
+            } else {
+                $hidden_fields->attach(form_input_hidden('forum_id', strval($forum_id)));
+            }
             $_threaded = ($GLOBALS['FORUM_DB']->query_select_value_if_there('f_forums', 'f_is_threaded', array('id' => $forum_id)) == 1);
             if (is_null($_threaded)) {
                 warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'forum'));
@@ -1733,7 +1761,7 @@ class Module_topics
             if (get_option('enable_sunk') == '1') {
                 $moderation_options[] = array(do_lang_tempcode('SUNK'), 'sunk', false, do_lang_tempcode('DESCRIPTION_SUNK'));
             }
-            if (!$private_topic) {
+            if ((!$private_topic) && (!cns_in_subject_view())) {
                 $moderation_options[] = array(do_lang_tempcode('CASCADING'), 'cascading', false, do_lang_tempcode('DESCRIPTION_CASCADING'));
             }
             if (addon_installed('calendar')) {
@@ -1983,7 +2011,7 @@ class Module_topics
             if (addon_installed('unvalidated')) {
                 $moderation_options[] = array(do_lang_tempcode('VALIDATED'), 'validated', true, do_lang_tempcode($GLOBALS['FORUM_DRIVER']->is_super_admin(get_member()) ? 'DESCRIPTION_VALIDATED_SIMPLE' : 'DESCRIPTION_VALIDATED', 'post'));
             }
-            //if ($intended_solely_for == -1) $moderation_options[] = array(do_lang_tempcode('CASCADING'), 'cascading', false, do_lang_tempcode('DESCRIPTION_CASCADING'));     Too much to offer this too
+            //if (($intended_solely_for == -1) && (!cns_in_subject_view())) $moderation_options[] = array(do_lang_tempcode('CASCADING'), 'cascading', false, do_lang_tempcode('DESCRIPTION_CASCADING'));     Too much to offer this too
         } else {
             $moderation_options = array();
             $hidden_fields->attach(form_input_hidden('validated', '1'));
@@ -2073,7 +2101,14 @@ class Module_topics
 
             $specialisation2->attach(do_template('FORM_SCREEN_FIELD_SPACER', array('_GUID' => '061fdbebcc17e08e8d6ff2c329f3d483', 'SECTION_HIDDEN' => true, 'TITLE' => do_lang_tempcode('TOPIC_MODERATION'))));
             $specialisation2->attach(form_input_line(do_lang_tempcode('TITLE'), '', 'new_title', $topic_title, false));
-            $specialisation2->attach(form_input_tree_list(do_lang_tempcode('DESTINATION_FORUM'), do_lang_tempcode('DESCRIPTION_DESTINATION_FORUM'), 'to', null, 'choose_forum', array(), false, is_null($forum_id) ? '' : strval($forum_id)));
+            if (cns_in_subject_view()) {
+                $df_label = do_lang_tempcode('NEW_SUBJECT');
+                $df_description = do_lang_tempcode('DESCRIPTION_NEW_SUBJECT');
+            } else {
+                $df_label = do_lang_tempcode('DESTINATION_FORUM');
+                $df_description = do_lang_tempcode('DESCRIPTION_DESTINATION_FORUM');
+            }
+            $specialisation2->attach(form_input_tree_list($df_label, $df_description, 'to', null, 'choose_forum', array(), false, is_null($forum_id) ? '' : strval($forum_id)));
             $options = array(
                 array(do_lang_tempcode('OPEN'), 'open', $topic_info[0]['t_is_open'] == 1, do_lang_tempcode('DESCRIPTION_OPEN')),
                 array(do_lang_tempcode('PINNED'), 'pinned', $topic_info[0]['t_pinned'] == 1, do_lang_tempcode('DESCRIPTION_PINNED')),
@@ -2091,7 +2126,7 @@ class Module_topics
             if (get_option('enable_sunk') == '1') {
                 $moderation_options[] = array(do_lang_tempcode('SUNK'), 'sunk', $topic_info[0]['t_sunk'] == 1, do_lang_tempcode('DESCRIPTION_SUNK'));
             }
-            if (!is_null($forum_id)) {
+            if (((!$forum_id !== null) && (!cns_in_subject_view())) || ($topic_info[0]['t_cascading'] == 1)) {
                 $options[] = array(do_lang_tempcode('CASCADING'), 'cascading', $topic_info[0]['t_cascading'] == 1, do_lang_tempcode('DESCRIPTION_CASCADING'));
             }
             $specialisation2->attach(form_input_various_ticks($options, ''));
@@ -2909,7 +2944,7 @@ END;
 
         // Show it worked / Refresh
         $url = get_param_string('redirect', null);
-        if ($url !== null) {
+        if (($url !== null) && ((strpos($url, 'topicview') === false) || ($_topic_info[0]['t_cache_first_post_id'] != $post_id))) {
             return redirect_screen(get_screen_title('DELETE_POST'), $url, do_lang_tempcode('SUCCESS'));
         }
 
@@ -3516,7 +3551,7 @@ END;
             if (get_option('enable_sunk') == '1') {
                 $moderation_options[] = array(do_lang_tempcode('SUNK'), 'sunk', $topic_info[0]['t_sunk'] == 1, do_lang_tempcode('DESCRIPTION_SUNK'));
             }
-            if (!$private_topic) {
+            if ((!$private_topic) && (!cns_in_subject_view())) {
                 $options[] = array(do_lang_tempcode('CASCADING'), 'cascading', $topic_info[0]['t_cascading'] == 1, do_lang_tempcode('DESCRIPTION_CASCADING'));
             }
         } else {
@@ -3889,7 +3924,9 @@ END;
         }
         $forum_id = $topic_info[0]['t_forum_id'];
 
-        $this->handle_topic_breadcrumbs($topic_info[0]['t_forum_id'], $topic_id, $topic_info[0]['t_cache_first_title'], do_lang_tempcode('MOVE_TOPIC'));
+        $in_subject_view = cns_in_subject_view();
+
+        $this->handle_topic_breadcrumbs($topic_info[0]['t_forum_id'], $topic_id, $topic_info[0]['t_cache_first_title'], do_lang_tempcode($in_subject_view ? 'CHANGE_SUBJECT' : 'MOVE_TOPIC'));
 
         $this->check_has_mod_access($topic_id);
 
@@ -3900,15 +3937,27 @@ END;
         // Certain aspects relating to the posting system
         $fields = new Tempcode();
         $fields->attach(form_input_line(do_lang_tempcode('TITLE'), '', 'title', $topic_info[0]['t_cache_first_title'], false));
-        $fields->attach(form_input_tree_list(do_lang_tempcode('DESTINATION_FORUM'), do_lang_tempcode('DESCRIPTION_DESTINATION_FORUM'), 'to', null, 'choose_forum', array(), true, strval($forum_id)));
+        if (cns_in_subject_view()) {
+            $df_label = do_lang_tempcode('NEW_SUBJECT');
+            $df_description = do_lang_tempcode('DESCRIPTION_NEW_SUBJECT');
+        } else {
+            $df_label = do_lang_tempcode('DESTINATION_FORUM');
+            $df_description = do_lang_tempcode('DESCRIPTION_DESTINATION_FORUM');
+        }
+        $fields->attach(form_input_tree_list($df_label, $df_description, 'to', null, 'choose_forum', array(), true, strval($forum_id)));
         $fields->attach(form_input_line(do_lang_tempcode('REASON'), do_lang_tempcode('DESCRIPTION_REASON'), 'description', '', false));
 
         $fields->attach(do_template('FORM_SCREEN_FIELD_SPACER', array('_GUID' => '20a9918ec1abc48c55dd7ba9ce5545a8', 'TITLE' => do_lang_tempcode('ACTIONS'))));
         $fields->attach(form_input_tick(do_lang_tempcode('REDIRECT_TO_TOPIC'), do_lang_tempcode('DESCRIPTION_REDIRECT_TO_TOPIC'), 'redir_topic', false));
 
         $topic_title = $topic_info[0]['t_cache_first_title'];
-        $title = get_screen_title('_MOVE_TOPIC', true, array(escape_html($topic_title)));
-        $submit_name = do_lang_tempcode('MOVE_TOPIC');
+        if ($in_subject_view) {
+            $title = get_screen_title('_CHANGE_SUBJECT', true, array(escape_html($topic_title)));
+            $submit_name = do_lang_tempcode('CHANGE_SUBJECT');
+        } else {
+            $title = get_screen_title('_MOVE_TOPIC', true, array(escape_html($topic_title)));
+            $submit_name = do_lang_tempcode('MOVE_TOPIC');
+        }
 
         return do_template('FORM_SCREEN', array(
             '_GUID' => '313fd175ccd376caa32794fedad21ac6',
@@ -3938,7 +3987,8 @@ END;
         require_code('cns_topics_action2');
         cns_move_topics($from, $to, array($topic_id));
         cns_edit_topic($topic_id, null, null, null, null, null, null, null, '', post_param_string('title'));
-        return (post_param_integer('redir_topic', 0) == 0) ? $this->redirect_to_forum('MOVE_TOPIC', $from) : $this->redirect_to('MOVE_TOPIC', $topic_id);
+        $in_subject_view = cns_in_subject_view();
+        return (post_param_integer('redir_topic', 0) == 0) ? $this->redirect_to_forum($in_subject_view ? 'CHANGE_SUBJECT' : 'MOVE_TOPIC', $from) : $this->redirect_to($in_subject_view ? 'CHANGE_SUBJECT' : 'MOVE_TOPIC', $topic_id);
     }
 
     /**
diff --git a/forum/pages/modules/topicview.php b/forum/pages/modules/topicview.php
index a341bbb..56faa3e 100644
--- a/forum/pages/modules/topicview.php
+++ b/forum/pages/modules/topicview.php
@@ -864,7 +864,7 @@ class Module_topicview
                 }
             }
             if (array_key_exists('may_move_topic', $topic_info)) {
-                $moderator_actions .= '<option value="move_topic">' . do_lang('MOVE_TOPIC') . '</option>';
+                $moderator_actions .= '<option value="move_topic">' . do_lang(cns_in_subject_view() ? 'CHANGE_SUBJECT' : 'MOVE_TOPIC') . '</option>';
             }
             if (array_key_exists('may_edit_topic', $topic_info)) {
                 $moderator_actions .= '<option value="edit_topic">' . do_lang('EDIT_TOPIC') . '</option>';
diff --git a/forum/pages/modules/vforums.php b/forum/pages/modules/vforums.php
index ec3b1f1..0fed074 100644
--- a/forum/pages/modules/vforums.php
+++ b/forum/pages/modules/vforums.php
@@ -277,6 +277,8 @@ class Module_vforums
      */
     public function _vforum($title, $condition, $order, $no_pin = false, $extra_tpl_map = null, $initial_table = null, $extra_select = '')
     {
+        $subject_view = cns_in_subject_view();
+
         require_code('templates_pagination');
         list($max, $start, , $sql_sup, $sql_sup_order_by, $true_start, , $keyset_field_stripped) = get_keyset_pagination_settings('forum_max', intval(get_option('forum_topics_per_page')), 'forum_start', null, null, $order, 'get_forum_sort_order_simplified');
 
@@ -286,6 +288,8 @@ class Module_vforums
         $breadcrumbs = breadcrumb_segments_to_tempcode($_breadcrumbs);
 
         $type = get_param_string('type', 'browse');
+        $subject_view = cns_in_subject_view();
+
         $forum_name = do_lang_tempcode('VIRTUAL_FORUM');
 
         // Find topics
@@ -357,7 +361,7 @@ class Module_vforums
         }
         $topics_array = array();
         foreach ($topic_rows as $topic_row) {
-            $topics_array[] = cns_get_topic_array($topic_row, get_member(), $hot_topic_definition, in_array($topic_row['id'], $involved)) + $topic_row;
+            $topics_array[] = cns_get_topic_array($topic_row, get_member(), $hot_topic_definition, in_array($topic_row['id'], $involved), $subject_view) + $topic_row;
         }
 
         // Display topics
@@ -424,6 +428,7 @@ class Module_vforums
             'BUTTONS' => $_buttons,
             'TOPIC_WRAPPER' => $topic_wrapper,
             'FORUM_GROUPINGS' => '',
+            'SUBJECT_VIEW' => $subject_view,
         );
         if (!is_null($extra_tpl_map)) {
             $tpl_map += $extra_tpl_map;
diff --git a/themes/default/css_custom/cns.css b/themes/default/css_custom/cns.css
index 97379c8..52c6fba 100644
--- a/themes/default/css_custom/cns.css
+++ b/themes/default/css_custom/cns.css
@@ -196,6 +196,109 @@ div.cns_table_footer>div>div {
 .cns_off {
 }
 
+/*
+==========================
+=Subject-based Forum View=
+==========================
+*/
+
+.cns_topic_title_by_subject {
+       margin-right: 0.5em;
+}
+.forum_subject_wrap {
+       margin-bottom: 0.25em;
+}
+.forum_subject_wrap_active {
+       margin-bottom: 0.65em;
+}
+.forum_subject {
+       background-color: {$GET,dtgrad};
+       border-radius: 5px;
+       padding: 0.2em 0.3em;
+       font-size: 0.85em;
+       vertical-align: middle;
+       display: inline-block;
+}
+.forum_subject_wrap:nth-child(odd) a {
+       /*-{$BETA_CSS_PROPERTY,background-image: linear-gradient(to right\, {$GET,ltgrad}\, {$GET,dtgrad});}*/
+}
+.forum_subject_wrap:nth-child(even) a {
+       /*-{$BETA_CSS_PROPERTY,background-image: linear-gradient(to right\, {$GET,dtgrad}\, {$GET,ltgrad});}*/
+}
+.box___block_side_forum_subjects .forum_subject {
+       width: 100%;
+       box-sizing: border-box;
+}
+.cns_forum_topic_title_bits .forum_subject {
+       white-space: nowrap;
+}
+.forum_subject, .forum_subject:hover, .forum_subject:visited, .forum_subject:active {
+       color: white;
+       text-decoration: none;
+}
+/*
+.forum_subject.subject_class_index_1 {
+       background-color: #ff0000;
+}
+.forum_subject.subject_class_index_2 {
+       background-color: #ff9999;
+}
+.forum_subject.subject_class_index_3 {
+       background-color: #ff8800;
+}
+.forum_subject.subject_class_index_4 {
+       background-color: #663600;
+}
+.forum_subject.subject_class_index_5 {
+       background-color: #8b9505;
+}
+.forum_subject.subject_class_index_6 {
+       background-color: #5cb024;
+}
+.forum_subject.subject_class_index_7 {
+       background-color: #00660e;
+}
+.forum_subject.subject_class_index_8 {
+       background-color: #649d8a;
+}
+.forum_subject.subject_class_index_9 {
+       background-color: #0894b7;
+}
+.forum_subject.subject_class_index_10 {
+       background-color: #0044ff;
+}
+.forum_subject.subject_class_index_11 {
+       background-color: #1b3580;
+}
+.forum_subject.subject_class_index_12 {
+       background-color: #7b8dbd;
+}
+.forum_subject.subject_class_index_13 {
+       background-color: #b499ff;
+}
+.forum_subject.subject_class_index_14 {
+       background-color: #6c599f;
+}
+.forum_subject.subject_class_index_15 {
+       background-color: #660044;
+}
+.forum_subject.subject_class_index_16 {
+       background-color: #ff5cc9;
+}
+.forum_subject.subject_class_index_17 {
+       background-color: #d0ce36;
+}
+.forum_subject.subject_class_index_18 {
+       background-color: #b0500c;
+}
+.forum_subject.subject_class_index_19 {
+       background-color: #b59172;
+}
+.forum_subject.subject_class_index_20 {
+       background-color: #000000;
+}
+*/
+
 /*
 =========================
 =======Topic View========
diff --git a/themes/default/templates/CNS_FORUM.tpl b/themes/default/templates/CNS_FORUM.tpl
index 26d06b5..88fa9fc 100644
--- a/themes/default/templates/CNS_FORUM.tpl
+++ b/themes/default/templates/CNS_FORUM.tpl
@@ -38,13 +38,13 @@
 			<div class="non_accessibility_redundancy">
 				<div class="float_surrounder">
 					<div class="buttons_group cns_buttons_screen">
-						{+START,IF_PASSED,ID}
+						{+START,IF_PASSED,ID}{+START,IF,{$NOT,{SUBJECT_VIEW}}}
 							{+START,INCLUDE,NOTIFICATION_BUTTONS}
 								NOTIFICATIONS_TYPE=cns_topic
 								NOTIFICATIONS_ID=forum:{ID}
 								NOTIFICATIONS_PAGE_LINK=forum:topics:toggle_notifications_forum:forum%3A{ID}
 							{+END}
-						{+END}
+						{+END}{+END}
 						{BUTTONS}
 					</div>
 				</div>
@@ -58,13 +58,13 @@
 {+START,IF,{$NOT,{$WIDE_HIGH}}}
 	<div class="float_surrounder">
 		<div class="buttons_group cns_buttons_screen">
-			{+START,IF_PASSED,ID}
+			{+START,IF_PASSED,ID}{+START,IF,{$NOT,{SUBJECT_VIEW}}}
 				{+START,INCLUDE,NOTIFICATION_BUTTONS}
 					NOTIFICATIONS_TYPE=cns_topic
 					NOTIFICATIONS_ID=forum:{ID}
 					NOTIFICATIONS_PAGE_LINK=forum:topics:toggle_notifications_forum:forum%3A{ID}
 				{+END}
-			{+END}
+			{+END}{+END}
 			{BUTTONS}
 		</div>
 	</div>
diff --git a/themes/default/templates/CNS_FORUM_TOPIC_ROW.tpl b/themes/default/templates/CNS_FORUM_TOPIC_ROW.tpl
index 6004604..4aa3ef7 100644
--- a/themes/default/templates/CNS_FORUM_TOPIC_ROW.tpl
+++ b/themes/default/templates/CNS_FORUM_TOPIC_ROW.tpl
@@ -24,7 +24,11 @@
 					{+END}
 				</span>
 
-				<a class="vertical_alignment {+START,IF_NON_EMPTY,{TOPIC_ROW_MODIFIERS}{TOPIC_ROW_LINKS}} cns_forum_topic_indent{+END}{+START,IF,{UNREAD}} cns_unread_topic_title{+END}" href="{URL*}" title="{$ALTERNATOR_TRUNCATED,{TITLE},60,{!TOPIC_STARTED_DATE_TIME,{HOVER;~}},,1}">{$TRUNCATE_LEFT,{TITLE},46,1}</a>
+				<a class="{+START,IF_PASSED,SUBJECT_CLASS}{+START,IF_PASSED,SUBJECT_LABEL}{+START,IF_NON_EMPTY,{SUBJECT_LABEL}}cns_topic_title_by_subject {+END}{+END}{+END}vertical_alignment {+START,IF_NON_EMPTY,{TOPIC_ROW_MODIFIERS}{TOPIC_ROW_LINKS}} cns_forum_topic_indent{+END}{+START,IF,{UNREAD}} cns_unread_topic_title{+END}" href="{URL*}" title="{$ALTERNATOR_TRUNCATED,{TITLE},60,{!TOPIC_STARTED_DATE_TIME,{HOVER;~}},,1}">{TITLE}</a>
+
+				{+START,IF_PASSED,SUBJECT_CLASS}{+START,IF_PASSED,SUBJECT_LABEL}{+START,IF_NON_EMPTY,{SUBJECT_LABEL}}
+					<a href="{$PAGE_LINK*,_SEARCH:forumview:id={FORUM_ID}}" class="forum_subject {SUBJECT_CLASS*}">{SUBJECT_LABEL*}</a>
+				{+END}{+END}{+END}
 
 				{PAGES}
 
@@ -39,7 +43,10 @@
 
 		{+START,IF,{$MOBILE}}
 			<div class="cns_forum_topic_title_bits">
-				<a class="vertical_alignment {+START,IF_NON_EMPTY,{TOPIC_ROW_MODIFIERS}{TOPIC_ROW_LINKS}} cns_forum_topic_indent{+END}{+START,IF,{UNREAD}} cns_unread_topic_title{+END}" href="{URL*}" title="{$ALTERNATOR_TRUNCATED,{TITLE},60,{!TOPIC_STARTED_DATE_TIME,{HOVER;~}},,1}">{$TRUNCATE_LEFT,{TITLE},46,1}</a>
+				<a class="vertical_alignment {+START,IF_NON_EMPTY,{TOPIC_ROW_MODIFIERS}{TOPIC_ROW_LINKS}} cns_forum_topic_indent{+END}{+START,IF,{UNREAD}} cns_unread_topic_title{+END}" href="{URL*}" title="{$ALTERNATOR_TRUNCATED,{TITLE},60,{!TOPIC_STARTED_DATE_TIME,{HOVER;~}},,1}">{TITLE}</a>
+				{+START,IF_PASSED,SUBJECT_CLASS}{+START,IF_PASSED,SUBJECT_LABEL}{+START,IF_NON_EMPTY,{SUBJECT_LABEL}}
+					<a href="{$PAGE_LINK*,_SEARCH:forumview:id={FORUM_ID}}" class="forum_subject {SUBJECT_CLASS*}">{SUBJECT_LABEL*}</a>
+				{+END}{+END}{+END}
 
 				{PAGES}
 
diff --git a/themes/default/templates/BLOCK_SIDE_FORUM_SUBJECTS.tpl b/themes/default/templates/BLOCK_SIDE_FORUM_SUBJECTS.tpl
new file mode 100644
index 0000000..5b24b70
--- /dev/null
+++ b/themes/default/templates/BLOCK_SIDE_FORUM_SUBJECTS.tpl
@@ -0,0 +1,17 @@
+<section class="box box___block_side_forum_subjects"><div class="box_inner">
+	<h3>{TITLE*}</h3>
+
+	{+START,IF_PASSED,ACTIVE_SUBJECT_LABEL}{+START,IF_PASSED,ACTIVE_SUBJECT_CLASS}{+START,IF_PASSED,ACTIVE_SUBJECT_DESCRIPTION}
+		<div class="forum_subject_wrap_active">
+			<a href="{$PAGE_LINK*,_SEARCH:forumview}" title="{ACTIVE_SUBJECT_DESCRIPTION*}" class="forum_subject {ACTIVE_SUBJECT_CLASS*}">&#10799; {$REPLACE,:,:&#8203;,{ACTIVE_SUBJECT_LABEL}}</a>
+		</div>
+	{+END}{+END}{+END}
+
+	{+START,LOOP,SUBJECTS}
+		{+START,IF_NON_EMPTY,{SUBJECT_LABEL}}
+			<div class="forum_subject_wrap">
+				<a href="{$PAGE_LINK*,_SEARCH:forumview:id={_loop_key}}" title="{SUBJECT_DESCRIPTION*}" class="forum_subject {SUBJECT_CLASS*}">{$REPLACE,:,:&#8203;,{SUBJECT_LABEL}} ({_SUBJECT_NUM_TOPICS*})</a>
+			</div>
+		{+END}
+	{+END}
+</div></section>
diff --git a/sources/hooks/systems/profiles_tabs/pts.php b/sources/hooks/systems/profiles_tabs/pts.php
index a199e1a..d46f868 100644
--- a/sources/hooks/systems/profiles_tabs/pts.php
+++ b/sources/hooks/systems/profiles_tabs/pts.php
@@ -68,7 +68,7 @@ class Hook_profiles_tabs_pts
 
         $root = db_get_first_id();
 
-        list($content) = cns_render_forumview($id, null, $current_filter_cat, $max, $start, $true_start, get_param_string('order', 'last_post'), $sql_sup, $sql_sup_order_by, $keyset_field_stripped, $root, $member_id_of, new Tempcode());
+        list($content) = cns_render_forumview($id, null, $current_filter_cat, $max, $start, $true_start, get_param_string('order', 'last_post'), $sql_sup, $sql_sup_order_by, $keyset_field_stripped, $root, $member_id_of, new Tempcode(), false);
 
         $content = do_template('CNS_MEMBER_PROFILE_PTS', array('_GUID' => '5d0cae3320634a1e4eb345154c853c35', 'CONTENT' => $content));
 
diff --git a/sources/hooks/systems/profiles_tabs/about.php b/sources/hooks/systems/profiles_tabs/about.php
index 5bf5188..d5f2105 100644
--- a/sources/hooks/systems/profiles_tabs/about.php
+++ b/sources/hooks/systems/profiles_tabs/about.php
@@ -279,12 +279,14 @@ class Hook_profiles_tabs_about
         $best_yet_forum = 0; // Initialise to integer type
         $best_yet_forum = null;
         $most_active_forum = null;
-        $_best_yet_forum = $GLOBALS['FORUM_DB']->query_select('f_posts', array('COUNT(*) as cnt', 'p_cache_forum_id'), array('p_poster' => $member_id_of), 'GROUP BY p_cache_forum_id ORDER BY COUNT(*) DESC', 1); // order by and limit have been added since original code, makes it run a bit faster
-        $_best_yet_forum = collapse_2d_complexity('p_cache_forum_id', 'cnt', $_best_yet_forum);
-        foreach ($forums as $forum) {
-            if (((array_key_exists($forum['id'], $_best_yet_forum)) && ((is_null($best_yet_forum)) || ($_best_yet_forum[$forum['id']] > $best_yet_forum)))) {
-                $most_active_forum = has_category_access($member_id_viewing, 'forums', strval($forum['id'])) ? protect_from_escaping(escape_html($forum['f_name'])) : do_lang_tempcode('PROTECTED_FORUM');
-                $best_yet_forum = $_best_yet_forum[$forum['id']];
+        if (!cns_in_subject_view()) {
+            $_best_yet_forum = $GLOBALS['FORUM_DB']->query_select('f_posts', array('COUNT(*) as cnt', 'p_cache_forum_id'), array('p_poster' => $member_id_of), 'GROUP BY p_cache_forum_id ORDER BY COUNT(*) DESC', 1); // order by and limit have been added since original code, makes it run a bit faster
+            $_best_yet_forum = collapse_2d_complexity('p_cache_forum_id', 'cnt', $_best_yet_forum);
+            foreach ($forums as $forum) {
+                if (((array_key_exists($forum['id'], $_best_yet_forum)) && ((is_null($best_yet_forum)) || ($_best_yet_forum[$forum['id']] > $best_yet_forum)))) {
+                    $most_active_forum = has_category_access($member_id_viewing, 'forums', strval($forum['id'])) ? protect_from_escaping(escape_html($forum['f_name'])) : do_lang_tempcode('PROTECTED_FORUM');
+                    $best_yet_forum = $_best_yet_forum[$forum['id']];
+                }
             }
         }
         $post_count = $GLOBALS['FORUM_DRIVER']->get_member_row_field($member_id_of, 'm_cache_num_posts');
diff --git a/sources/hooks/systems/config/forum_subject_view.php b/sources/hooks/systems/config/forum_subject_view.php
new file mode 100644
index 0000000..4518e30
--- /dev/null
+++ b/sources/hooks/systems/config/forum_subject_view.php
@@ -0,0 +1,55 @@
+<?php /*
+
+ Composr
+ Copyright (c) ocProducts, 2004-2016
+
+ See text/EN/licence.txt for full licencing information.
+
+
+ NOTE TO PROGRAMMERS:
+   Do not edit this file. If you need to make changes, save your changed file to the appropriate *_custom folder
+   **** If you ignore this advice, then your website upgrades (e.g. for bug fixes) will likely kill your changes ****
+
+*/
+
+/**
+ * @license    http://opensource.org/licenses/cpal_1.0 Common Public Attribution License
+ * @copyright  ocProducts Ltd
+ * @package    cns_forum
+ */
+
+/**
+ * Hook class.
+ */
+class Hook_config_forum_subject_view
+{
+    /**
+     * Gets the details relating to the config option.
+     *
+     * @return ?array The details (null: disabled)
+     */
+    public function get_details()
+    {
+        return array(
+            'human_name' => 'FORUM_SUBJECT_VIEW',
+            'type' => 'tick',
+            'category' => 'FORUMS',
+            'group' => 'SECTION_FORUMS',
+            'explanation' => 'CONFIG_OPTION_forum_subject_view',
+            'shared_hosting_restricted' => '0',
+            'list_options' => '',
+
+            'addon' => 'cns_forum',
+        );
+    }
+
+    /**
+     * Gets the default value for the config option.
+     *
+     * @return ?string The default value (null: option is disabled)
+     */
+    public function get_default()
+    {
+        return '0';
+    }
+}
diff --git a/sources/hooks/systems/ajax_tree/choose_forum.php b/sources/hooks/systems/ajax_tree/choose_forum.php
index cf8d6b9..585a955 100644
--- a/sources/hooks/systems/ajax_tree/choose_forum.php
+++ b/sources/hooks/systems/ajax_tree/choose_forum.php
@@ -74,6 +74,9 @@ class Hook_choose_forum
                 continue; // Possible when we look under as a root
             }
             $title = $t['title'];
+            if (($t['second_cat'] != '') && (!cns_in_subject_view())) {
+                $title = strtoupper($t['second_cat']) . ': ' . $title;
+            }
             $description = array_key_exists($t['group'], $categories) ? $categories[$t['group']] : '';
             $has_children = ($t['child_count'] != 0);
             $selectable = ((!$addable_filter) || cns_may_post_topic($t['id']));
diff --git a/sources/hooks/systems/addon_registry/cns_forum.php b/sources/hooks/systems/addon_registry/cns_forum.php
index 89d9e73..53c9159 100644
--- a/sources/hooks/systems/addon_registry/cns_forum.php
+++ b/sources/hooks/systems/addon_registry/cns_forum.php
@@ -326,9 +326,12 @@ class Hook_addon_registry_cns_forum
             'sources/hooks/systems/config/forum_posts_per_page.php',
             'sources/hooks/systems/config/forum_topics_per_page.php',
             'sources/hooks/systems/config/delete_trashed_pts.php',
+            'sources/hooks/systems/config/forum_subject_view.php',
             'sources/hooks/systems/tasks/cns_recache.php',
             'sources/hooks/systems/tasks/cns_topics_recache.php',
             'sources/hooks/systems/tasks/notify_topics_moved.php',
+            'sources/blocks/side_forum_subjects.php',
+            'themes/default/templates/BLOCK_SIDE_FORUM_SUBJECTS.tpl',
         );
     }
 
@@ -397,6 +400,7 @@ class Hook_addon_registry_cns_forum
             'templates/CNS_MEMBER_PROFILE_POSTS.tpl' => 'cns_member_profile_posts',
             'templates/CNS_MEMBER_PROFILE_PTS.tpl' => 'cns_member_profile_pts',
             'templates/CNS_VFORUM_FILTERING.tpl' => 'cns_vforum_filtering',
+            'templates/BLOCK_SIDE_FORUM_SUBJECTS.tpl' => 'block_side_forum_subjects',
         );
     }
 
@@ -422,11 +426,13 @@ class Hook_addon_registry_cns_forum
             'FORUM_GROUPINGS' => '',
             'ID' => placeholder_id(),
             'DESCRIPTION' => lorem_phrase(),
+            'SUBJECT_VIEW' => false,
         ));
 
         $screen = do_lorem_template('CNS_FORUM_SCREEN', array(
             'TITLE' => lorem_title(),
             'CONTENT' => $content,
+            'SUBJECT_VIEW' => false,
         ));
 
         return array(
@@ -1157,6 +1163,7 @@ class Hook_addon_registry_cns_forum
             'FORUM_GROUPINGS' => $forum_groupings,
             'ID' => placeholder_id(),
             'DESCRIPTION' => lorem_phrase(),
+            'SUBJECT_VIEW' => false,
         ));
 
         $members_viewing = new Tempcode();
@@ -1177,6 +1184,7 @@ class Hook_addon_registry_cns_forum
             'NUM_GUESTS' => placeholder_number(),
             'NUM_MEMBERS' => placeholder_number(),
             'MEMBERS_VIEWING' => $members_viewing,
+            'SUBJECT_VIEW' => false,
         ));
 
         return array(
@@ -1748,4 +1756,39 @@ class Hook_addon_registry_cns_forum
             lorem_globalise($topic_tpl, null, '', true)
         );
     }
+
+    /**
+     * Get a preview(s) of a (group of) template(s), as a full standalone piece of HTML in Tempcode format.
+     * Uses sources/lorem.php functions to place appropriate stock-text. Should not hard-code things, as the code is intended to be declaritive.
+     * Assumptions: You can assume all Lang/CSS/JavaScript files in this addon have been pre-required.
+     *
+     * @return array Array of previews, each is Tempcode. Normally we have just one preview, but occasionally it is good to test templates are flexible (e.g. if they use IF_EMPTY, we can test with and without blank data).
+     */
+    public function tpl_preview__block_side_forum_subjects()
+    {
+        require_css('cns');
+
+        $subjects = array(
+            array(
+                'SUBJECT_CLASS' => '1',
+                'SUBJECT_LABEL' => lorem_phrase(),
+                'SUBJECT_DESCRIPTION' => lorem_phrase(),
+                'SUBJECT_NUM_TOPICS' => placeholder_number(),
+                '_SUBJECT_NUM_TOPICS' => placeholder_number(),
+            ),
+            array(
+                'SUBJECT_CLASS' => '2',
+                'SUBJECT_LABEL' => lorem_word(),
+                'SUBJECT_DESCRIPTION' => lorem_phrase(),
+                'SUBJECT_NUM_TOPICS' => placeholder_number(),
+                '_SUBJECT_NUM_TOPICS' => placeholder_number(),
+            ),
+        );
+
+        $block = do_template('BLOCK_SIDE_FORUM_SUBJECTS', array('SUBJECTS' => $subjects));
+
+        return array(
+            lorem_globalise($block, null, '', true)
+        );
+    }
 }
diff --git a/sources/cns_forums.php b/sources/cns_forums.php
index 1880188..de22907 100644
--- a/sources/cns_forums.php
+++ b/sources/cns_forums.php
@@ -387,3 +387,90 @@ function cns_forum_allows_anonymous_posts($forum_id)
     }
     return ($GLOBALS['FORUM_DB']->query_select_value_if_there('f_forums', 'f_allows_anonymous_posts', array('id' => $forum_id)) === 1);
 }
+
+/**
+ * Find details about subjects (for subject view).
+ *
+ * @param  boolean $simplify_down Whether to simplify subjects down if possible.
+ * @return array Mapping between forum ID and subject map.
+ */
+function cns_find_all_forum_subjects($simplify_down = true)
+{
+    static $subjects = array();
+    if (!empty($subjects)) {
+        return $subjects;
+    }
+
+    $db = $GLOBALS['FORUM_DB'];
+    $prefix = $db->get_table_prefix();
+    $_all_forums = $db->query_select('f_forums f JOIN ' . $prefix . 'f_forum_groupings g ON g.id=f.f_forum_grouping_id', array('f.id', 'f_name', 'f_parent_forum', 'c_title', 'f_forum_grouping_id', 'f_cache_num_topics', 'f_description'), array(), 'ORDER BY id');
+    $all_forums = list_to_map('id', $_all_forums);
+    $single_category_forums = array();
+    foreach ($all_forums as $forum) {
+        $all_categories = array();
+        foreach ($all_forums as $_forum) {
+            if ($_forum['f_parent_forum'] == $forum['f_parent_forum']) {
+                $all_categories[$_forum['f_forum_grouping_id']] = true;
+            }
+        }
+        $single_category_forums[$forum['id']] = ($simplify_down) && (count($all_categories) <= 1);
+    }
+
+    $i = 1;
+    $max_subjects_in_css = intval(get_value('max_subjects_in_css', '20'));
+    foreach ($all_forums as $forum) {
+        if (!has_category_access(get_member(), 'forums', strval($forum['id']))) {
+            continue;
+        }
+
+        if ($forum['f_parent_forum'] === null) {
+            $label = '';
+        } else {
+            $parents = '';
+            $pf = $forum['f_parent_forum'];
+            while (($pf !== null) && ($all_forums[$pf]['f_parent_forum'] !== null)) {
+                $parent_forum_scoper = trim($all_forums[$pf]['f_name']);
+                $parents = $parent_forum_scoper . ':' . $parents;
+                if ((!$single_category_forums[$all_forums[$pf]['id']]) && ($all_forums[$pf]['c_title'] != $parent_forum_scoper)) {
+                    $parent_category_scoper = trim(strtoupper($all_forums[$pf]['c_title']));
+                    $parents = $parent_category_scoper . ':' . $parents;
+                }
+                $pf = $all_forums[$pf]['f_parent_forum'];
+            }
+            $label = $parents;
+            if ((!$single_category_forums[$forum['id']]) && ($forum['c_title'] != $forum['f_name'])) {
+                $label .= trim(strtoupper($forum['c_title'])) . ':';
+            }
+            $label .= trim($forum['f_name']);
+        }
+
+        $subjects[$forum['id']] = array(
+            'id' => $forum['id'],
+            'label' => $label,
+            'class_index' => $i,
+            'label' => $label,
+            'num_topics' => $forum['f_cache_num_topics'],
+            'description' => $forum['f_description'],
+        );
+        $i++;
+        if ($i > $max_subjects_in_css) {
+            $i = 1;
+        }
+    }
+
+    return $subjects;
+}
+
+/**
+ * Find if we are in the special (simplified) subject view of the forum.
+ *
+ * @return boolean Whether we are.
+ */
+function cns_in_subject_view()
+{
+    static $result = null;
+    if ($result === null) {
+        $result = (get_param_integer('keep_subject_view', intval(get_option('forum_subject_view'))) == 1);
+    }
+    return $result;
+}
diff --git a/sources/cns_forums2.php b/sources/cns_forums2.php
index 4351a61..ba1bedf 100644
--- a/sources/cns_forums2.php
+++ b/sources/cns_forums2.php
@@ -207,6 +207,11 @@ function cns_get_forum_tree($member_id = null, $base_forum = null, $breadcrumbs
         return $use_compound_list ? array(array(), '') : array();
     }
 
+    $subject_view = cns_in_subject_view();
+    if ($subject_view) {
+        $subjects = cns_find_all_forum_subjects(false);
+    }
+
     global $FORUM_TREE_SECURE_CACHE;
 
     if (is_null($member_id)) {
@@ -248,14 +253,18 @@ function cns_get_forum_tree($member_id = null, $base_forum = null, $breadcrumbs
     }
     sort_maps_by($forums, $order);
     $compound_list = '';
-    $child_breadcrumbs = ($breadcrumbs == '') ? '' : ($breadcrumbs . ' > ');
+    if ($subject_view) {
+        $child_breadcrumbs = '';
+    } else {
+        $child_breadcrumbs = ($breadcrumbs == '') ? '' : ($breadcrumbs . ' > ');
+    }
     foreach ($forums as $forum) {
         $access = has_category_access($member_id, 'forums', strval($forum['id']));
         $cat_sort_key = '!' . (is_null($forum['f_forum_grouping_id']) ? '' : strval($forum['f_forum_grouping_id']));
 
         if (($access) && ($skip !== $forum['id']) && ($levels !== 0)) {
             $cat_bit = '';
-            if (!is_null($forum['f_forum_grouping_id'])) {
+            if ((!is_null($forum['f_forum_grouping_id'])) && (!$subject_view)) {
                 global $FORUM_GROUPINGS_TITLES_CACHE;
                 if (is_null($FORUM_GROUPINGS_TITLES_CACHE)) {
                     $FORUM_GROUPINGS_TITLES_CACHE = collapse_2d_complexity('id', 'c_title', $GLOBALS['FORUM_DB']->query_select('f_forum_groupings', array('id', 'c_title')));
@@ -271,13 +280,17 @@ function cns_get_forum_tree($member_id = null, $base_forum = null, $breadcrumbs
 
             $child = array(
                 'id' => $forum['id'],
-                'title' => $forum['f_name'],
                 'breadcrumbs' => $child_breadcrumbs,
                 'compound_list' => (!$use_compound_list ? strval($forum['id']) : (strval($forum['id']) . ',' . $_compound_list)),
                 'second_cat' => $cat_bit,
                 'group' => $forum['f_forum_grouping_id'],
                 'children' => $below,
             );
+            if ($subject_view) {
+                $child['title'] = $subjects[$forum['id']]['label'];
+            } else {
+                $child['title'] = $forum['f_name'];
+            }
             if ($do_stats) {
                 $child['child_count'] = $GLOBALS['FORUM_DB']->query_select_value('f_forums', 'COUNT(*)', array('f_parent_forum' => $forum['id']));
                 $child['updated_since'] = $forum['f_cache_last_time'];
diff --git a/sources/cns_forums_action.php b/sources/cns_forums_action.php
index 730777a..3902032 100644
--- a/sources/cns_forums_action.php
+++ b/sources/cns_forums_action.php
@@ -216,5 +216,7 @@ function cns_make_forum($name, $description, $forum_grouping_id, $access_mapping
     }
     notify_sitemap_node_add('_SEARCH:forumview:id=' . strval($forum_id), null, null, $sitemap_priority, 'monthly', has_category_access($GLOBALS['FORUM_DRIVER']->get_guest_id(), 'forums', strval($forum_id)));
 
+    decache('side_forum_subjects');
+
     return $forum_id;
 }
diff --git a/sources/cns_forums_action2.php b/sources/cns_forums_action2.php
index 8172c9f..4064017 100644
--- a/sources/cns_forums_action2.php
+++ b/sources/cns_forums_action2.php
@@ -50,6 +50,8 @@ function cns_edit_forum_grouping($forum_grouping_id, $title, $description, $expa
         require_code('resource_fs');
         generate_resource_fs_moniker('forum_grouping', strval($forum_grouping_id));
     }
+
+    decache('side_forum_subjects');
 }
 
 /**
@@ -78,6 +80,8 @@ function cns_delete_forum_grouping($forum_grouping_id, $target_forum_grouping_id
         require_code('resource_fs');
         expunge_resource_fs_moniker('forum_grouping', strval($forum_grouping_id));
     }
+
+    decache('side_forum_subjects');
 }
 
 /**
@@ -185,6 +189,8 @@ function cns_edit_forum($forum_id, $name, $description, $forum_grouping_id, $new
 
     require_code('sitemap_xml');
     notify_sitemap_node_edit('_SEARCH:forumview:id=' . strval($forum_id), has_category_access($GLOBALS['FORUM_DRIVER']->get_guest_id(), 'forums', strval($forum_id)));
+
+    decache('side_forum_subjects');
 }
 
 /**
@@ -247,6 +253,8 @@ function cns_delete_forum($forum_id, $target_forum_id = null, $delete_topics = 0
 
     require_code('sitemap_xml');
     notify_sitemap_node_delete('_SEARCH:forumview:id=' . strval($forum_id));
+
+    decache('side_forum_subjects');
 }
 
 /**
diff --git a/sources/cns_forumview.php b/sources/cns_forumview.php
index e92a8f0..54cc753 100644
--- a/sources/cns_forumview.php
+++ b/sources/cns_forumview.php
@@ -110,9 +110,10 @@ function get_forum_sort_order($_sort = 'first_post', $simplified = false)
  * @param  AUTO_LINK $root Virtual root
  * @param  ?MEMBER $of_member_id The member to show private topics of (null: not showing private topics)
  * @param  Tempcode $breadcrumbs The breadcrumbs
+ * @param  boolean $subject_view Whether we are displaying in subject view (i.e. flattened)
  * @return mixed Either Tempcode (an interface that must be shown) or a pair: The main Tempcode, the forum name (string). For a PT view, it is always a tuple, never raw Tempcode (as it can go inside a tabset).
  */
-function cns_render_forumview($id, $forum_info, $current_filter_cat, $max, $start, $true_start, $sort, $sql_sup, $sql_sup_order_by, $keyset_field_stripped, $root, $of_member_id, $breadcrumbs)
+function cns_render_forumview($id, $forum_info, $current_filter_cat, $max, $start, $true_start, $sort, $sql_sup, $sql_sup_order_by, $keyset_field_stripped, $root, $of_member_id, $breadcrumbs, $subject_view)
 {
     require_css('cns');
 
@@ -133,7 +134,7 @@ function cns_render_forumview($id, $forum_info, $current_filter_cat, $max, $star
         }
         $details['name'] = do_lang_tempcode('PRIVATE_TOPICS_OF', escape_html($pt_displayname), escape_html($pt_username));
     } else {
-        $details = cns_get_forum_view($id, $forum_info, $start, $true_start, $max, $sql_sup, $sql_sup_order_by);
+        $details = cns_get_forum_view($id, $forum_info, $start, $true_start, $max, $sql_sup, $sql_sup_order_by, $subject_view);
 
         if ((array_key_exists('question', $details)) && (is_null(get_bot_type()))) {
             // Was there a question answering attempt?
@@ -166,7 +167,7 @@ function cns_render_forumview($id, $forum_info, $current_filter_cat, $max, $star
 
     // Find forum groupings
     $forum_groupings = new Tempcode();
-    if ($type != 'pt') {
+    if (($type != 'pt') && (!$subject_view)) {
         foreach ($details['forum_groupings'] as $best => $forum_grouping) {
             if (array_key_exists('subforums', $forum_grouping)) { // We only show if there is something in it
                 // Subforums
@@ -319,7 +320,7 @@ function cns_render_forumview($id, $forum_info, $current_filter_cat, $max, $star
 
     // Mass moderation
     if (array_key_exists('may_move_topics', $details)) {
-        $moderator_actions .= '<option value="move_topics">' . do_lang('MOVE_TOPICS') . '</option>';
+        $moderator_actions .= '<option value="move_topics">' . do_lang($subject_view ? 'CHANGE_SUBJECT' : 'MOVE_TOPICS') . '</option>';
     }
     if (array_key_exists('may_delete_topics', $details)) {
         if (has_privilege(get_member(), 'delete_midrange_content', 'topics', array('forums', $id))) {
@@ -371,6 +372,13 @@ function cns_render_forumview($id, $forum_info, $current_filter_cat, $max, $star
 
     // Buttons
     $button_array = array();
+    if (get_value('include_forum_view_switch_buttons') === '1') {
+        if ($subject_view) {
+            $button_array[] = array('immediate' => true, 'title' => do_lang_tempcode('REGULAR_VIEW'), 'url' => get_self_url(false, false, array('keep_subject_view' => 0)), 'img' => 'buttons__advanced');
+        } else {
+            $button_array[] = array('immediate' => true, 'title' => do_lang_tempcode('SUBJECT_VIEW'), 'url' => get_self_url(false, false, array('keep_subject_view' => 1)), 'img' => 'buttons__simple');
+        }
+    }
     if ((!is_guest()) && ($type != 'pt')) {
         if (get_option('enable_mark_forum_read') == '1') {
             $read_url = build_url(array('page' => 'topics', 'type' => 'mark_read', 'id' => $id), get_module_zone('topics'));
@@ -468,13 +476,13 @@ function cns_render_forumview($id, $forum_info, $current_filter_cat, $max, $star
         '_GUID' => '1c14afd9265b1bf69375169dd6faf83c',
         'STARTER_TITLE' => $starter_title,
         'ID' => is_null($id) ? null : strval($id),
-        'DESCRIPTION' => array_key_exists('description',
-            $details) ? $details['description'] : '',
+        'DESCRIPTION' => array_key_exists('description', $details) ? $details['description'] : '',
         'FILTERS' => $filters,
         'BUTTONS' => $buttons,
         'TOPIC_WRAPPER' => $topic_wrapper,
         'BREADCRUMBS' => $breadcrumbs,
         'FORUM_GROUPINGS' => $forum_groupings,
+        'SUBJECT_VIEW' => $subject_view,
     );
     $content = do_template('CNS_FORUM', $map);
 
@@ -488,9 +496,10 @@ function cns_render_forumview($id, $forum_info, $current_filter_cat, $max, $star
  * @param  MEMBER $member_id The member the details are being prepared for.
  * @param  integer $hot_topic_definition The hot topic definition (taken from the config options).
  * @param  boolean $involved Whether the viewing member has a post in the topic.
+ * @param  boolean $subject_view Whether we are displaying in subject view (i.e. flattened)
  * @return array The details.
  */
-function cns_get_topic_array($topic_row, $member_id, $hot_topic_definition, $involved)
+function cns_get_topic_array($topic_row, $member_id, $hot_topic_definition, $involved, $subject_view = false)
 {
     $topic = array();
 
@@ -501,10 +510,12 @@ function cns_get_topic_array($topic_row, $member_id, $hot_topic_definition, $inv
         $topic['first_post'] = new Tempcode();
     }
 
+    $forum_id = $topic_row['t_forum_id'];
+
     $topic['id'] = $topic_row['id'];
     $topic['num_views'] = $topic_row['t_num_views'];
     $topic['num_posts'] = $topic_row['t_cache_num_posts'];
-    $topic['forum_id'] = $topic_row['t_forum_id'];
+    $topic['forum_id'] = $forum_id;
     $topic['description'] = $topic_row['t_description'];
     $topic['description_link'] = $topic_row['t_description_link'];
 
@@ -588,6 +599,13 @@ function cns_get_topic_array($topic_row, $member_id, $hot_topic_definition, $inv
         $topic['modifiers'][] = 'hot';
     }
 
+    if (($subject_view) && ($forum_id !== null)) {
+        $subjects = cns_find_all_forum_subjects();
+        $subject_index = $forum_id;
+        $topic['subject_class'] = 'subject_class_index_' . strval($subjects[$subject_index]['class_index']);
+        $topic['subject_label'] = $subjects[$subject_index]['label'];
+    }
+
     return $topic;
 }
 
@@ -695,7 +713,7 @@ function cns_render_topic($topic, $has_topic_marking, $pt = false, $show_forum =
 
     // Tpl
     $post = $topic['first_post'];
-    if (!is_null($show_forum)) {
+    if (($show_forum !== null) && (!isset($topic['subject_label']))) {
         $hover = do_lang_tempcode('FORUM_AND_TIME_HOVER', $show_forum, protect_from_escaping(escape_html(get_timezoned_date($topic['first_time']))));
         $breadcrumbs = breadcrumb_segments_to_tempcode(cns_forum_breadcrumbs($topic['forum_id'], null, null, false));
     } else {
@@ -726,6 +744,8 @@ function cns_render_topic($topic, $has_topic_marking, $pt = false, $show_forum =
         'NUM_POSTS' => integer_format($topic['num_posts']),
         'NUM_VIEWS' => integer_format($topic['num_views']),
         'LAST_POST' => $last_post,
+        'SUBJECT_LABEL' => isset($topic['subject_label']) ? strval($topic['subject_label']) : '',
+        'SUBJECT_CLASS' => isset($topic['subject_class']) ? $topic['subject_class'] : null,
     ));
 }
 
@@ -739,9 +759,10 @@ function cns_render_topic($topic, $has_topic_marking, $pt = false, $show_forum =
  * @param  ?integer $max The maximum number of topics to get detail of (null: default).
  * @param  string $sql_sup Extra SQL to append.
  * @param  string $sql_sup_order_by Extra SQL to append as order clause.
+ * @param  boolean $subject_view Whether we are displaying in subject view (i.e. flattened)
  * @return array The details.
  */
-function cns_get_forum_view($forum_id, $forum_info, $start = 0, $true_start = 0, $max = null, $sql_sup = '', $sql_sup_order_by = '')
+function cns_get_forum_view($forum_id, $forum_info, $start = 0, $true_start = 0, $max = null, $sql_sup = '', $sql_sup_order_by = '', $subject_view = false)
 {
     if (is_null($max)) {
         $max = intval(get_option('forum_topics_per_page'));
@@ -758,132 +779,134 @@ function cns_get_forum_view($forum_id, $forum_info, $start = 0, $true_start = 0,
     }
 
     // Find our subforums first
+    $forum_groupings = array();
     $sort = $forum_info['f_order_sub_alpha'] ? 'f_name' : 'f_position';
-    $max_forum_detail = intval(get_option('max_forum_detail'));
-    $huge_forums = $GLOBALS['FORUM_DB']->query_select_value('f_forums', 'COUNT(*)') > $max_forum_detail;
-    if ($huge_forums) {
-        $max_forum_inspect = intval(get_option('max_forum_inspect'));
-
-        $subforum_rows = $GLOBALS['FORUM_DB']->query('SELECT f.* FROM ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_forums f WHERE f.id=' . strval($forum_id) . ' OR f_parent_forum=' . strval($forum_id) . ' ORDER BY f_parent_forum,' . $sort, $max_forum_inspect, null, false, false, array('f_description' => 'LONG_TRANS__COMCODE', 'f_intro_question' => 'LONG_TRANS__COMCODE'));
-        if (count($subforum_rows) == $max_forum_inspect) {
-            $subforum_rows = array(); // Will cause performance breakage
-        }
-    } else {
-        $subforum_rows = $GLOBALS['FORUM_DB']->query_select('f_forums f', array('f.*'), null, 'ORDER BY f_parent_forum,' . $sort, null, null, false, array('f_description' => 'LONG_TRANS__COMCODE', 'f_intro_question' => 'LONG_TRANS__COMCODE'));
-    }
-    $unread_forums = array();
-    if ((!is_null($forum_id)) && (get_member() != $GLOBALS['CNS_DRIVER']->get_guest_id())) {
-        // Where are there unread topics in subforums?
-        $tree = array();
-        $subforum_rows_copy = $subforum_rows;
-        $tree = cns_organise_into_tree($subforum_rows_copy, $forum_id);
-        if ($forum_id != db_get_first_id()) {
-            $child_or_list = cns_get_all_subordinate_forums($forum_id, 't_forum_id', $tree);
+    if (!$subject_view) {
+        $max_forum_detail = intval(get_option('max_forum_detail'));
+        $huge_forums = $GLOBALS['FORUM_DB']->query_select_value('f_forums', 'COUNT(*)') > $max_forum_detail;
+        if ($huge_forums) {
+            $max_forum_inspect = intval(get_option('max_forum_inspect'));
+
+            $subforum_rows = $GLOBALS['FORUM_DB']->query('SELECT f.* FROM ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_forums f WHERE f.id=' . strval($forum_id) . ' OR f_parent_forum=' . strval($forum_id) . ' ORDER BY f_parent_forum,' . $sort, $max_forum_inspect, null, false, false, array('f_description' => 'LONG_TRANS__COMCODE', 'f_intro_question' => 'LONG_TRANS__COMCODE'));
+            if (count($subforum_rows) == $max_forum_inspect) {
+                $subforum_rows = array(); // Will cause performance breakage
+            }
         } else {
-            $child_or_list = '';
-        }
-        if ($child_or_list != '') {
-            $child_or_list .= ' AND ';
+            $subforum_rows = $GLOBALS['FORUM_DB']->query_select('f_forums f', array('f.*'), null, 'ORDER BY f_parent_forum,' . $sort, null, null, false, array('f_description' => 'LONG_TRANS__COMCODE', 'f_intro_question' => 'LONG_TRANS__COMCODE'));
         }
-        $query = 'SELECT DISTINCT t_forum_id FROM ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_topics t LEFT JOIN ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_read_logs l ON (t.id=l_topic_id AND l_member_id=' . strval(get_member()) . ') WHERE t_forum_id IS NOT NULL AND ' . $child_or_list . 't_cache_last_time>' . strval(time() - 60 * 60 * 24 * intval(get_option('post_read_history_days'))) . ' AND (l_time<t_cache_last_time OR l_time IS NULL)';
-        if ((!has_privilege(get_member(), 'see_unvalidated')) && (addon_installed('unvalidated'))) {
-            $query .= ' AND t_validated=1';
-        }
-        $unread_forums = array_flip(collapse_1d_complexity('t_forum_id', $GLOBALS['FORUM_DB']->query($query)));
-    }
-
-    // Find all the forum groupings that are used
-    $forum_groupings = array();
-    $or_list = '';
-    foreach ($subforum_rows as $tmp_key => $subforum_row) {
-        if ($subforum_row['f_parent_forum'] != $forum_id) {
-            continue;
-        }
-
-        if (!has_category_access($member_id, 'forums', strval($subforum_row['id']))) {
-            unset($subforum_rows[$tmp_key]);
-            continue;
+        $unread_forums = array();
+        if ((!is_null($forum_id)) && (get_member() != $GLOBALS['CNS_DRIVER']->get_guest_id())) {
+            // Where are there unread topics in subforums?
+            $tree = array();
+            $subforum_rows_copy = $subforum_rows;
+            $tree = cns_organise_into_tree($subforum_rows_copy, $forum_id);
+            if ($forum_id != db_get_first_id()) {
+                $child_or_list = cns_get_all_subordinate_forums($forum_id, 't_forum_id', $tree);
+            } else {
+                $child_or_list = '';
+            }
+            if ($child_or_list != '') {
+                $child_or_list .= ' AND ';
+            }
+            $query = 'SELECT DISTINCT t_forum_id FROM ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_topics t LEFT JOIN ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_read_logs l ON (t.id=l_topic_id AND l_member_id=' . strval(get_member()) . ') WHERE t_forum_id IS NOT NULL AND ' . $child_or_list . 't_cache_last_time>' . strval(time() - 60 * 60 * 24 * intval(get_option('post_read_history_days'))) . ' AND (l_time<t_cache_last_time OR l_time IS NULL)';
+            if ((!has_privilege(get_member(), 'see_unvalidated')) && (addon_installed('unvalidated'))) {
+                $query .= ' AND t_validated=1';
+            }
+            $unread_forums = array_flip(collapse_1d_complexity('t_forum_id', $GLOBALS['FORUM_DB']->query($query)));
         }
 
-        $forum_grouping_id = $subforum_row['f_forum_grouping_id'];
-        if (!array_key_exists($forum_grouping_id, $forum_groupings)) {
-            $forum_groupings[$forum_grouping_id] = array('subforums' => array());
-            if ($or_list != '') {
-                $or_list .= ' OR ';
-            }
-            $or_list .= 'id=' . strval($forum_grouping_id);
-        }
-    }
-    if ($or_list != '') {
-        $forum_grouping_rows = $GLOBALS['FORUM_DB']->query('SELECT * FROM ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_forum_groupings WHERE ' . $or_list, null, null, false, true);
-        foreach ($forum_grouping_rows as $forum_grouping_row) {
-            $forum_grouping_id = $forum_grouping_row['id'];
-            $title = $forum_grouping_row['c_title'];
-            $description = $forum_grouping_row['c_description'];
-            $expanded_by_default = $forum_grouping_row['c_expanded_by_default'];
-            $forum_groupings[$forum_grouping_id]['title'] = $title;
-            $forum_groupings[$forum_grouping_id]['description'] = $description;
-            $forum_groupings[$forum_grouping_id]['expanded_by_default'] = $expanded_by_default;
-        }
-        $forum_groupings[null]['title'] = '';
-        $forum_groupings[null]['description'] = '';
-        $forum_groupings[null]['expanded_by_default'] = true;
-        foreach ($subforum_rows as $subforum_row) {
+        // Find all the forum groupings that are used
+        $or_list = '';
+        foreach ($subforum_rows as $tmp_key => $subforum_row) {
             if ($subforum_row['f_parent_forum'] != $forum_id) {
                 continue;
             }
 
-            $forum_grouping_id = $subforum_row['f_forum_grouping_id'];
+            if (!has_category_access($member_id, 'forums', strval($subforum_row['id']))) {
+                unset($subforum_rows[$tmp_key]);
+                continue;
+            }
 
-            $subforum = array();
-            $subforum['id'] = $subforum_row['id'];
-            $subforum['name'] = $subforum_row['f_name'];
-            $subforum['description'] = get_translated_tempcode('f_forums', $subforum_row, 'f_description', $GLOBALS['FORUM_DB']);
-            $subforum['redirection'] = $subforum_row['f_redirection'];
-            $subforum['intro_question'] = get_translated_tempcode('f_forums', $subforum_row, 'f_intro_question', $GLOBALS['FORUM_DB']);
-            $subforum['intro_answer'] = $subforum_row['f_intro_answer'];
-
-            if (is_numeric($subforum_row['f_redirection'])) {
-                $subforum_row = $GLOBALS['FORUM_DB']->query_select('f_forums', array('*'), array('id' => intval($subforum_row['f_redirection'])), '', 1);
-                $subforum_row = $subforum_row[0];
+            $forum_grouping_id = $subforum_row['f_forum_grouping_id'];
+            if (!array_key_exists($forum_grouping_id, $forum_groupings)) {
+                $forum_groupings[$forum_grouping_id] = array('subforums' => array());
+                if ($or_list != '') {
+                    $or_list .= ' OR ';
+                }
+                $or_list .= 'id=' . strval($forum_grouping_id);
+            }
+        }
+        if ($or_list != '') {
+            $forum_grouping_rows = $GLOBALS['FORUM_DB']->query('SELECT * FROM ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_forum_groupings WHERE ' . $or_list, null, null, false, true);
+            foreach ($forum_grouping_rows as $forum_grouping_row) {
+                $forum_grouping_id = $forum_grouping_row['id'];
+                $title = $forum_grouping_row['c_title'];
+                $description = $forum_grouping_row['c_description'];
+                $expanded_by_default = $forum_grouping_row['c_expanded_by_default'];
+                $forum_groupings[$forum_grouping_id]['title'] = $title;
+                $forum_groupings[$forum_grouping_id]['description'] = $description;
+                $forum_groupings[$forum_grouping_id]['expanded_by_default'] = $expanded_by_default;
             }
+            $forum_groupings[null]['title'] = '';
+            $forum_groupings[null]['description'] = '';
+            $forum_groupings[null]['expanded_by_default'] = true;
+            foreach ($subforum_rows as $subforum_row) {
+                if ($subforum_row['f_parent_forum'] != $forum_id) {
+                    continue;
+                }
+
+                $forum_grouping_id = $subforum_row['f_forum_grouping_id'];
+
+                $subforum = array();
+                $subforum['id'] = $subforum_row['id'];
+                $subforum['name'] = $subforum_row['f_name'];
+                $subforum['description'] = get_translated_tempcode('f_forums', $subforum_row, 'f_description', $GLOBALS['FORUM_DB']);
+                $subforum['redirection'] = $subforum_row['f_redirection'];
+                $subforum['intro_question'] = get_translated_tempcode('f_forums', $subforum_row, 'f_intro_question', $GLOBALS['FORUM_DB']);
+                $subforum['intro_answer'] = $subforum_row['f_intro_answer'];
+
+                if (is_numeric($subforum_row['f_redirection'])) {
+                    $subforum_row = $GLOBALS['FORUM_DB']->query_select('f_forums', array('*'), array('id' => intval($subforum_row['f_redirection'])), '', 1);
+                    $subforum_row = $subforum_row[0];
+                }
 
-            if (($subforum_row['f_redirection'] == '') || (is_numeric($subforum_row['f_redirection']))) {
-                $subforum['num_topics'] = $subforum_row['f_cache_num_topics'];
-                $subforum['num_posts'] = $subforum_row['f_cache_num_posts'];
+                if (($subforum_row['f_redirection'] == '') || (is_numeric($subforum_row['f_redirection']))) {
+                    $subforum['num_topics'] = $subforum_row['f_cache_num_topics'];
+                    $subforum['num_posts'] = $subforum_row['f_cache_num_posts'];
 
-                $subforum['has_new'] = false;
-                if (get_member() != $GLOBALS['CNS_DRIVER']->get_guest_id()) {
-                    $subforums_recurse = cns_get_all_subordinate_forums($subforum['id'], null, $tree[$subforum['id']]['children']);
-                    foreach ($subforums_recurse as $subforum_potential) {
-                        if (array_key_exists($subforum_potential, $unread_forums)) {
-                            $subforum['has_new'] = true;
+                    $subforum['has_new'] = false;
+                    if (get_member() != $GLOBALS['CNS_DRIVER']->get_guest_id()) {
+                        $subforums_recurse = cns_get_all_subordinate_forums($subforum['id'], null, $tree[$subforum['id']]['children']);
+                        foreach ($subforums_recurse as $subforum_potential) {
+                            if (array_key_exists($subforum_potential, $unread_forums)) {
+                                $subforum['has_new'] = true;
+                            }
                         }
                     }
-                }
 
-                if ((is_null($subforum_row['f_cache_last_forum_id'])) || (has_category_access($member_id, 'forums', strval($subforum_row['f_cache_last_forum_id'])))) {
-                    $subforum['last_topic_id'] = $subforum_row['f_cache_last_topic_id'];
-                    $subforum['last_title'] = $subforum_row['f_cache_last_title'];
-                    $subforum['last_time'] = $subforum_row['f_cache_last_time'];
-                    $subforum['last_username'] = $subforum_row['f_cache_last_username'];
-                    $subforum['last_member_id'] = $subforum_row['f_cache_last_member_id'];
-                    $subforum['last_forum_id'] = $subforum_row['f_cache_last_forum_id'];
-                } else {
-                    $subforum['protected_last_post'] = true;
-                }
+                    if ((is_null($subforum_row['f_cache_last_forum_id'])) || (has_category_access($member_id, 'forums', strval($subforum_row['f_cache_last_forum_id'])))) {
+                        $subforum['last_topic_id'] = $subforum_row['f_cache_last_topic_id'];
+                        $subforum['last_title'] = $subforum_row['f_cache_last_title'];
+                        $subforum['last_time'] = $subforum_row['f_cache_last_time'];
+                        $subforum['last_username'] = $subforum_row['f_cache_last_username'];
+                        $subforum['last_member_id'] = $subforum_row['f_cache_last_member_id'];
+                        $subforum['last_forum_id'] = $subforum_row['f_cache_last_forum_id'];
+                    } else {
+                        $subforum['protected_last_post'] = true;
+                    }
 
-                // Subsubforums
-                $subforum['children'] = array();
-                foreach ($subforum_rows as $tmp_key_2 => $subforum_row2) {
-                    if (($subforum_row2['f_parent_forum'] == $subforum_row['id']) && (has_category_access($member_id, 'forums', strval($subforum_row2['id'])))) {
-                        $subforum['children'][$subforum_row2['f_name'] . '__' . strval($subforum_row2['id'])] = array('id' => $subforum_row2['id'], 'name' => $subforum_row2['f_name'], 'redirection' => $subforum_row2['f_redirection']);
+                    // Subsubforums
+                    $subforum['children'] = array();
+                    foreach ($subforum_rows as $tmp_key_2 => $subforum_row2) {
+                        if (($subforum_row2['f_parent_forum'] == $subforum_row['id']) && (has_category_access($member_id, 'forums', strval($subforum_row2['id'])))) {
+                            $subforum['children'][$subforum_row2['f_name'] . '__' . strval($subforum_row2['id'])] = array('id' => $subforum_row2['id'], 'name' => $subforum_row2['f_name'], 'redirection' => $subforum_row2['f_redirection']);
+                        }
                     }
+                    sort_maps_by($subforum['children'], 'name');
                 }
-                sort_maps_by($subforum['children'], 'name');
-            }
 
-            $forum_groupings[$forum_grouping_id]['subforums'][] = $subforum;
+                $forum_groupings[$forum_grouping_id]['subforums'][] = $subforum;
+            }
         }
     }
 
@@ -892,15 +915,20 @@ function cns_get_forum_view($forum_id, $forum_info, $start = 0, $true_start = 0,
     if ((!has_privilege(get_member(), 'see_unvalidated')) && (addon_installed('unvalidated')) && (!cns_may_moderate_forum($forum_id, $member_id))) {
         $extra = 't_validated=1 AND ';
     }
-    if ((is_null($forum_info['f_parent_forum'])) || ($GLOBALS['FORUM_DB']->query_select_value('f_topics', 'COUNT(*)', array('t_cascading' => 1)) == 0)) {
-        $where = $extra . ' (t_forum_id=' . strval($forum_id) . ')';
+    if ($subject_view) {
+        $parent_or_list = cns_get_all_subordinate_forums($forum_id, 't_forum_id');
+        $where = $extra . ' (t_forum_id=' . strval($forum_id) . ' OR ' . $parent_or_list . ')';
     } else {
-        $extra2 = '';
-        $parent_or_list = cns_get_forum_parent_or_list($forum_id, $forum_info['f_parent_forum']);
-        if ($parent_or_list != '') {
-            $extra2 = 'AND (' . $parent_or_list . ')';
+        if ((is_null($forum_info['f_parent_forum'])) || ($GLOBALS['FORUM_DB']->query_select_value('f_topics', 'COUNT(*)', array('t_cascading' => 1)) == 0)) {
+            $where = $extra . ' (t_forum_id=' . strval($forum_id) . ')';
+        } else {
+            $extra2 = '';
+            $parent_or_list = cns_get_forum_parent_or_list($forum_id, $forum_info['f_parent_forum']);
+            if ($parent_or_list != '') {
+                $extra2 = 'AND (' . $parent_or_list . ')';
+            }
+            $where = $extra . ' (t_forum_id=' . strval($forum_id) . ' OR (t_cascading=1 ' . $extra2 . '))';
         }
-        $where = $extra . ' (t_forum_id=' . strval($forum_id) . ' OR (t_cascading=1 ' . $extra2 . '))';
     }
     if (is_guest()) {
         $query = 'SELECT ttop.*,NULL AS l_time';
@@ -956,7 +984,7 @@ function cns_get_forum_view($forum_id, $forum_info, $start = 0, $true_start = 0,
         $involved = array();
     }
     foreach ($topic_rows as $topic_row) {
-        $rendered_topic = cns_get_topic_array($topic_row, $member_id, $hot_topic_definition, in_array($topic_row['id'], $involved));
+        $rendered_topic = cns_get_topic_array($topic_row, $member_id, $hot_topic_definition, in_array($topic_row['id'], $involved), $subject_view);
         if ($rendered_topic !== null) {
             $topics[] = $rendered_topic;
         }
diff --git a/sources/blocks/side_forum_subjects.php b/sources/blocks/side_forum_subjects.php
new file mode 100644
index 0000000..bad542b
--- /dev/null
+++ b/sources/blocks/side_forum_subjects.php
@@ -0,0 +1,131 @@
+<?php /*
+
+ Composr
+ Copyright (c) ocProducts, 2004-2021
+
+ See text/EN/licence.txt for full licencing information.
+
+
+ NOTE TO PROGRAMMERS:
+   Do not edit this file. If you need to make changes, save your changed file to the appropriate *_custom folder
+   **** If you ignore this advice, then your website upgrades (e.g. for bug fixes) will likely kill your changes ****
+
+*/
+
+/**
+ * @license    http://opensource.org/licenses/cpal_1.0 Common Public Attribution License
+ * @copyright  ocProducts Ltd
+ * @package    cns_forum
+ */
+
+/**
+ * Block class.
+ */
+class Block_side_forum_subjects
+{
+    /**
+     * Find details of the block.
+     *
+     * @return ?array Map of block info (null: block is disabled).
+     */
+    public function info()
+    {
+        $info = array();
+        $info['author'] = 'Chris Graham';
+        $info['organisation'] = 'ocProducts';
+        $info['hacked_by'] = null;
+        $info['hack_version'] = null;
+        $info['version'] = 1;
+        $info['locked'] = false;
+        $info['parameters'] = array('title', 'max');
+        return $info;
+    }
+
+    /**
+     * Find caching details for the block.
+     *
+     * @return ?array Map of cache details (cache_on and ttl) (null: block is disabled).
+     */
+    public function caching_environment()
+    {
+        $info = array();
+        $info['cache_on'] = 'array(get_page_name(), get_param_integer(\'id\', db_get_first_id()), array_key_exists(\'title\', $map) ? $map[\'title\'] : do_lang(\'cns:FORUM_SUBJECTS\'), array_key_exists(\'max\', $map) ? intval($map[\'max\']) : 30, get_param_integer(\'keep_subject_view\', get_option(\'forum_subject_view\')) == 1)';
+        $info['ttl'] = (get_value('no_block_timeout') === '1') ? 60 * 60 * 24 * 365 * 5/*5 year timeout*/ : 15;
+        return $info;
+    }
+
+    /**
+     * Execute the block.
+     *
+     * @param  array $map A map of parameters.
+     * @return Tempcode The result of execution.
+     */
+    public function run($map)
+    {
+        if (has_no_forum()) {
+            return new Tempcode();
+        }
+
+        require_code('cns_forums');
+
+        if (!cns_in_subject_view()) {
+            return new Tempcode();
+        }
+
+        $title = array_key_exists('title', $map) ? $map['title'] : do_lang('FORUM_SUBJECTS');
+        $max = array_key_exists('max', $map) ? intval($map['max']) : 30;
+
+        require_css('cns');
+        require_lang('cns');
+
+        $active_subject_class = null;
+        $active_subject_label = null;
+        $active_subject_description = null;
+        $filter_active = false;
+        if ((get_page_name() == 'forumview') && (get_param_string('type', 'browse') == 'browse')) {
+            $active_id = get_param_integer('id', db_get_first_id());
+            if ($active_id != db_get_first_id()) {
+                $filter_active = true;
+            }
+        }
+
+        $_subjects = cns_find_all_forum_subjects();
+        if (count($_subjects) > $max) {
+            sort_maps_by($_subjects, '!num_topics');
+        } else {
+            sort_maps_by($_subjects, 'label');
+        }
+        $subjects = array();
+        $i = 0;
+        foreach ($_subjects as $subject) {
+            if ($subject['num_topics'] > 0) {
+                if (($filter_active) && ($subject['id'] == $active_id)) {
+                    $active_subject_class = 'subject_class_index_' . strval($subject['class_index']);
+                    $active_subject_label = $subject['label'];
+                    $active_subject_description = $subject['description'];
+                } else {
+                    $subjects[$subject['id']] = array(
+                        'SUBJECT_CLASS' => 'subject_class_index_' . strval($subject['class_index']),
+                        'SUBJECT_LABEL' => $subject['label'],
+                        'SUBJECT_DESCRIPTION' => $subject['description'],
+                        'SUBJECT_NUM_TOPICS' => integer_format($subject['num_topics']),
+                        '_SUBJECT_NUM_TOPICS' => strval($subject['num_topics']),
+                    );
+                }
+
+                $i++;
+                if ($i == $max) {
+                    break;
+                }
+            }
+        }
+
+        return do_template('BLOCK_SIDE_FORUM_SUBJECTS', array(
+            'TITLE' => $title,
+            'SUBJECTS' => $subjects,
+            'ACTIVE_SUBJECT_CLASS' => $active_subject_class,
+            'ACTIVE_SUBJECT_LABEL' => $active_subject_label,
+            'ACTIVE_SUBJECT_DESCRIPTION' => $active_subject_description,
+        ));
+    }
+}
diff --git a/sources/blocks/main_cns_involved_topics.php b/sources/blocks/main_cns_involved_topics.php
index 5bef3e7..6b69ae0 100644
--- a/sources/blocks/main_cns_involved_topics.php
+++ b/sources/blocks/main_cns_involved_topics.php
@@ -59,14 +59,17 @@ class Block_main_cns_involved_topics
 
         $block_id = get_block_id($map);
 
-        $member_id_of = array_key_exists('member_id', $map) ? intval($map['member_id']) : get_member();
-        $max = get_param_integer($block_id . '_max', array_key_exists('max', $map) ? intval($map['max']) : 10);
-        $start = get_param_integer($block_id . '_start', array_key_exists('start', $map) ? intval($map['start']) : 0);
-
         require_code('cns_topics');
         require_code('cns_general');
         require_lang('cns');
         require_code('cns_forumview');
+        require_code('cns_forums');
+
+        $subject_view = cns_in_subject_view();
+
+        $member_id_of = array_key_exists('member_id', $map) ? intval($map['member_id']) : get_member();
+        $max = get_param_integer($block_id . '_max', array_key_exists('max', $map) ? intval($map['max']) : 10);
+        $start = get_param_integer($block_id . '_start', array_key_exists('start', $map) ? intval($map['start']) : 0);
 
         $topics = new Tempcode();
 
@@ -133,7 +136,7 @@ class Block_main_cns_involved_topics
             $hot_topic_definition = intval(get_option('hot_topic_definition'));
             foreach ($rows as $row) {
                 if (array_key_exists($row['p_topic_id'], $topic_rows_map)) {
-                    $topics->attach(cns_render_topic(cns_get_topic_array($topic_rows_map[$row['p_topic_id']], get_member(), $hot_topic_definition, true), $has_topic_marking));
+                    $topics->attach(cns_render_topic(cns_get_topic_array($topic_rows_map[$row['p_topic_id']], get_member(), $hot_topic_definition, true, $subject_view), $has_topic_marking));
                 }
             }
             if (!$topics->is_empty()) {
diff --git a/site/pages/modules/warnings.php b/site/pages/modules/warnings.php
index f56ccc7..b93c7c7 100644
--- a/site/pages/modules/warnings.php
+++ b/site/pages/modules/warnings.php
@@ -539,7 +539,9 @@ class Module_warnings extends Standard_crud_module
                         $silence_forum_time = $active_until;
                     }
                     $fields->attach(form_input_date(do_lang_tempcode('SILENCE_FROM_TOPIC'), do_lang_tempcode('DESCRIPTION_SILENCE_FROM_TOPIC'), 'silence_from_topic', false, true, true, $silence_topic_time, 2));
-                    $fields->attach(form_input_date(do_lang_tempcode('SILENCE_FROM_FORUM'), do_lang_tempcode('DESCRIPTION_SILENCE_FROM_FORUM'), 'silence_from_forum', false, true, true, $silence_forum_time, 2));
+                    if (($silence_forum_time !== null) || (cns_in_subject_view())) {
+                        $fields->attach(form_input_date(do_lang_tempcode('SILENCE_FROM_FORUM'), do_lang_tempcode('DESCRIPTION_SILENCE_FROM_FORUM'), 'silence_from_forum', false, true, true, $silence_forum_time, 2));
+                    }
                 }
             }
 
diff --git a/lang/EN/cns.ini b/lang/EN/cns.ini
index 77b9741..70ae286 100644
--- a/lang/EN/cns.ini
+++ b/lang/EN/cns.ini
@@ -22,7 +22,7 @@ DOC_LDAP=If you plan to integrate your website into a corporate network, perhaps
 DOC_GROUPS=Members exist in a primary usergroup, and may be placed in any number of secondary usergroups. Usergroups serve many purposes, including:\n - demonstrating rank (i.e. a status symbol)\n - grouping members for common permission allocation\n - highlighting involvement\n - incentivising involvement (member contributions to the site and community)
 DOC_CUSTOM_PROFILE_FIELDS=The 'Custom Profile Fields' allow you to create new data fields to attach to member profiles. By default, an 'About me' field is included, and a number of hidden/locked/non-editable fields that store details relating to point counts, and staff-membership and role.
 DOC_CUSTOM_PROFILE_FIELDS_STATS=View tallies of how popular different Custom Profile Field values are. 
-DOC_FORUMS=Discussion forums are a communication tool for site users, based around topics of conversation located within forums.\n\nDiscussion forums are organised into a hierarchical structure (also known as a tree structure). The forum you view when you visit the [concept]discussion forums[/concept] is the [concept]home forum[/concept] in the hierarchy, and contains child forums ([concept]sub-forum[/concept]s), which may themselves contain child forums, and so on. Within each forum there may also be [concept]topic[/concept]s (known as threads in some other software). And within each topic, there are one or more [concept]post[/concept]s.
+DOC_FORUMS=Discussion forums are a communication tool for site users, based around topics of conversation located within forums.\n\nDiscussion forums are organised into a hierarchical structure (also known as a tree structure). The forum you view when you visit the [concept]discussion forums[/concept] is the [concept]home forum[/concept] in the hierarchy, and contains child forums ([concept]sub-forum[/concept]s), which may themselves contain child forums, and so on. Within each forum there may also be [concept]topic[/concept]s. And within each topic, there are one or more [concept]post[/concept]s.
 DOC_FORUM_GROUPINGS=Apart from forums, topics, and posts, there is one additional organisational construct in the inbuilt forum system: forum groupings (known as 'categories' in some software). However, forum groupings have a very limited effect on organisation. The system stores a number of groupings, and any forum, at any position in the tree can be 'tagged' against one of these groupings: the groupings have no effect on, and are not a part of, the forum structure, and all they do is group up sub-forums of the same forum grouping visually. By default, every forum is of the forum grouping 'General' and hence all sub-forums are grouped under this when displayed.
 DOC_EMOTICONS=Emoticons (also known as smilies) are little images used to convey 'emotion' within text. These are an important part of informal communication on the Internet, as body language and tone are not available to (sub-consciously) transmit the same information.\n\nWithout use of emoticons, it can be impossible (unless the writer took great care to express themselves fully) to gauge unwritten details, such as the importance of what is being written, or the writer's emotional perspective on it.
 DESCRIPTION_USERGROUP_TITLE=The human-readable name of the usergroup.
@@ -63,7 +63,10 @@ INCORRECT_CONFIRM_CODE=The given confirmation code is not correct.
 NAMED_TOPIC={1}
 NAMED_PRIVATE_TOPIC={1}
 READING_PRIVATE_TOPIC=<em>Reading a Private Topic</em>
-NAMED_FORUM={1}
+NAMED_FORUM_SUBJECT_VIEW={1}
+NAMED_FORUM_REGULAR_VIEW={1}
+NAMED_FORUM_SUBJECT_VIEW_SUBLINE=
+NAMED_FORUM_REGULAR_VIEW_SUBLINE=
 RANK_IMAGE=Rank image
 DESCRIPTION_RANK_IMAGE=Rank images of all a member's usergroups are displayed next to their posts. You may opt not to have a rank image for this usergroup if you desire.
 DEFAULT_RANK_0=Newbie
@@ -99,6 +102,7 @@ _ADD_POST_TOP_LEVEL=Reply to topic '{1}' <span class="associated_details">(top-l
 _ADD_POST_UNDER=Reply to topic '{1}' <span class="associated_details">(under {2}'s post)</span>
 NO_PRIVATE_SELF=You cannot create a private topic/post to yourself.
 _MOVE_TOPIC=Move topic '{1}'
+_CHANGE_SUBJECT=Change topic subject of '{1}'
 FIRST_POST=First post
 HIGHLIGHTED_NAME=Highlighted name
 DESCRIPTION_HIGHLIGHTED_NAME=Whether the member's username will be highlighted in certain positions on the system. This is an ego booster.
@@ -287,7 +291,7 @@ _DELETE_MEMBER_SEARCH=To review content that may be owned by this member, <a hre
 DELETE_WITHOUT_MERGING=Delete (without merging)
 MEMBER_WARNING=Warning/punitive action for '{1}' by {2} on {3}
 REDIRECT_TO_TOPIC=Redirect to topic
-DESCRIPTION_REDIRECT_TO_TOPIC=Tick (check) this if you would like to go to the topic next &ndash; leave unticked (unchecked) if you would like to go to the original forum next.
+DESCRIPTION_REDIRECT_TO_TOPIC=Tick (check) this if you would like to go to the topic next &ndash; leave unticked (unchecked) if you would like to go to the forum.
 _EMAIL_MEMBER=E-mail member
 EMAIL_MEMBER=E-mail member: {1}
 EMAIL_CC_ADDRESS=CC to
@@ -422,15 +401,18 @@ DESCRIPTION_MINIMUM_SELECTIONS=The minimum number of choices on the poll a membe
 MAXIMUM_SELECTIONS=Maximum selections
 DESCRIPTION_MAXIMUM_SELECTIONS=The maximum number of choices on the poll a member can make when voting. For a poll where a user may only vote for 1 answer, leave it as 1.
 MOVE_POSTS_A_TEXT=<p class="lonely_label">Choose one of two ways to move the posts:</p><ul><li>Choose an existing topic (only the most recent are shown)</li><li>Enter a topic ID manually</li></ul><p>Only fill one of the next two fields, corresponding to the method you want.</p>
-MOVE_POSTS_B_TEXT=This form allows you to move posts into a new topic in the selected forum.
+MOVE_POSTS_B_TEXT=This form allows you to move posts into a new topic.
 MERGE_POSTS=Merge posts into
 SPLIT_POSTS=Split posts from
 MAKE_PERSONAL=Make topic personal
 MAKE_PERSONAL_DESCRIPTION=Use this screen to change a public topic into a Private Topic between the members below.
 DELETE_TOPIC_TEXT=You may choose to either:<ul><li>Delete the topic's existing posts (by leaving the &ldquo;Destination topic&rdquo; settings unset)</li><li>Move the posts to an existing topic (by using the &lsquo;Choose&rsquo; list)</li><li>Move the posts to an existing topic (by manually entering the &lsquo;Destination topic ID&rsquo;)</li></ul>
 DESTINATION_FORUM=Destination forum
-DESCRIPTION_POSTS_DESTINATION_FORUM=If an entry is chosen, the posts will be moved into a new topic in this forum; the topic title will be that of the first post, or &lsquo;N/A&rsquo;.
-DESCRIPTION_DESTINATION_FORUM=The forum where this will be moved to.
+NEW_SUBJECT=New subject
+DESCRIPTION_POSTS_DESTINATION_FORUM=If selected, the posts will be moved into a new topic in this forum; the topic title will be that of the first post, or &lsquo;N/A&rsquo;.
+DESCRIPTION_POSTS_NEW_SUBJECT=If selected, the posts will be moved into a new topic with this subject; the topic title will be that of the first post, or &lsquo;N/A&rsquo;.
+DESCRIPTION_DESTINATION_FORUM=The forum to move to.
+DESCRIPTION_NEW_SUBJECT=The new subject that will be assigned.
 DESTINATION_TOPIC=Destination topic.
 DESTINATION_TOPIC_ID=Destination topic ID.
 DESCRIPTION_DESTINATION_TOPIC=The ID of an existing topic into which these will be moved.
@@ -595,6 +577,7 @@ EDIT_FORUM_GROUPING=Edit forum grouping
 EDIT_THIS_FORUM_GROUPING=Edit this forum grouping
 DELETE_FORUM_GROUPING=Delete forum grouping
 MOVE_TOPICS=Move topics
+CHANGE_SUBJECT=Change topic subject
 ADD_EMOTICON=Add emoticon
 EDIT_EMOTICON=Edit emoticon
 EDIT_THIS_EMOTICON=Edit this emoticon
@@ -712,13 +694,12 @@ EDIT_THIS_CLUB=Edit this club
 CLUBS=Clubs
 CLUB_WITH_MEMBERS_APPROVAL={1} is a social club with {2} {2|member|members}. Membership requires approval by {3}.
 CLUB_WITH_MEMBERS_OPEN={1} is a social club with {2} {2|member|members}. Membership does not require approval.
-DOC_CLUBS=Clubs are special usergroups that come with their own forum. Members may add their own clubs, and manage them as the club leader.
+DOC_CLUBS=Clubs are special usergroups that come with their own forum (or subject, in simplified view). Members may add their own clubs, and manage them as the club leader.
 FORUM_FOR_CLUB=Forum for the [i]{1}[/i] club.
 INVITED_TO_PTS=Topics invited to
@@ -792,9 +773,9 @@ NOW_DISABLED_NOTIFICATIONS_FORUM=You've just disabled notifications for this for
 NOW_ENABLED_NOTIFICATIONS_TOPIC=You now have notifications enabled for this topic (already saved). Make any further changes to your settings you might required then click &ldquo;Save&rdquo; to return to where you were.
 NOW_DISABLED_NOTIFICATIONS_TOPIC=You no longer have notifications enabled for this topic (already saved). Make any further changes to your settings you might required then click &ldquo;Save&rdquo; to return to where you were.
 POST_NOTIFICATION_MAIL_SUBJECT=New reply to '{2}' on {1}
-POST_NOTIFICATION_MAIL=There has been a new post by {3}. This notification was sent due to your notification settings.\n\nYou may view the post from the following link:\n[url="{2}"]{2}[/url].\n\nThe post is as follows:\n\n[quote="{6}"]\n{4}\n[/quote]\n\n\nIf you do not want to receive these notifications then you will find an unmonitor button on the topic. You can change your default notification settings by editing your account.
+POST_NOTIFICATION_MAIL=There has been a new post by {3}.\n\nYou may view the post from the following link:\n[url="{2}"]{2}[/url].\n\nThe post is as follows:\n\n[quote="{6}"]\n{4}\n[/quote]\n\n\nIf you do not want to receive notifications on this topic then you will find an unmonitor button on the topic.
 TOPIC_NOTIFICATION_MAIL_SUBJECT=New topic on {1} forum ({2})
-TOPIC_NOTIFICATION_MAIL=A new topic has been posted by {3}. This notification was sent due to your notification settings.\n\nYou may view the topic from the following link:\n[url="{2}"]{2}[/url]\n\nThe first post in the topic is as follows:\n\n[quote="{6}"]\n{4}\n[/quote]
+TOPIC_NOTIFICATION_MAIL=A new topic has been posted by {3}.\n\nYou may view the topic from the following link:\n[url="{2}"]{2}[/url]\n\nThe first post in the topic is as follows:\n\n[quote="{6}"]\n{4}\n[/quote]
 TOPIC_MOVE_MAIL_SUBJECT=Topic on {1} forum moved ({2})
 TOPIC_MOVE_MAIL=A topic for which you have notifications enabled has been moved. The topic '{2}' has been moved to the forum '{3}'.
 NOTIFICATION_TYPE_cns_member_needs_validation=New member needs validating
@@ -884,3 +868,8 @@ DESCRIPTION_ENABLE_AUTO_MARK_READ=Whether to mark topics as read automatically o
 AVATARS_CARTOONS=Cartoons
 AVATARS_THEMATIC=Hobbies
 AVATARS_MISC=Miscellaneous
+FORUM_SUBJECTS=Subjects
+FORUM_SUBJECT=Subject
+SUBJECT_VIEW=Simplified view
+REGULAR_VIEW=Detailed view
diff --git a/lang/EN/cns_components.ini b/lang/EN/cns_components.ini
index c9a2b69..a41a09f 100644
--- a/lang/EN/cns_components.ini
+++ b/lang/EN/cns_components.ini
@@ -42,3 +42,10 @@ BLOCK_main_member_bar_USE=Used on the forum to show login details and some navig
 BLOCK_TRANS_NAME_main_bottom_bar=Bottom bar
 BLOCK_main_bottom_bar_DESCRIPTION=Shows details relating to the forum, and online members.
 BLOCK_main_bottom_bar_USE=Used on the forum to show what is (and has been) going on.
+BLOCK_TRANS_NAME_side_forum_subjects=Forum subjects
+BLOCK_side_forum_subjects_DESCRIPTION=Shows all the subjects on the forum. Only for the simplified view of the forum, otherwise it is blank.
+BLOCK_side_forum_subjects_USE=Used on the forum panel to allow easy filtering by subject.
+BLOCK_side_forum_subjects_PARAM_title_TITLE=Title
+BLOCK_side_forum_subjects_PARAM_title=The title for the block. Default: 'Subjects'.
+BLOCK_side_forum_subjects_PARAM_max_TITLE=Maximum to show
+BLOCK_side_forum_subjects_PARAM_max=The maximum number to return. Default: '30'.
diff --git a/lang/EN/cns_config.ini b/lang/EN/cns_config.ini
index 0188330..3b08ce4 100644
--- a/lang/EN/cns_config.ini
+++ b/lang/EN/cns_config.ini
@@ -287,3 +281,6 @@ NEW_MEMBER_DEFAULT_EMAIL_MESSAGE=Default e-mail message
 CONFIG_OPTION_new_member_default_email_message=The default message pre-populated onto the &ldquo;Add member account&rdquo;, so that manually added new members get an e-mail.
 NEW_MEMBER_DEFAULT_TEMPORARY_PASSWORD=Temporary password by default
 CONFIG_OPTION_new_member_default_temporary_password=Whether new members are given a temporary password, by default, on the &ldquo;Add member account&rdquo; form.
+FORUM_SUBJECT_VIEW=Simplified view
+CONFIG_OPTION_forum_subject_view=Whether to make the forum use the simplified view by default. The simplified view removes the visible forum structure, showing all topics together. Forums/forum-groupings are then identified to the user as subjects, and they can click on those subjects to filter by them (equivalent of entering a forum). Simplified view is great for forums that don't have a large number of topics. Users still have the choice to switch to the regular view if they wish.
forum_subjects.diff (91,520 bytes)   
Time estimation (hours)40
Sponsorship open

Sponsor

Date Added Member Amount Sponsored

Relationships

related to 1730 ClosedChris Graham Composr non-bundled addons Facebook-sync forums with a Facebook page (for example - other services too) 
related to 5436 Not AssignedGuest Composr Conversr topic tags 

Activities

Chris Graham

2015-10-18 13:32

administrator   ~3132

Last edited: 2015-10-18 13:32

I was thinking, and we can allow multiple tags per topic by virtue of forum trees.

E.g. a topic in A > B > C, would have A, B, C as the tags.

We could set Composr to automatically self-organise the tree based on what tags people are using. The optimal smallest tree for the particular tags people have set.

The available tags would be all forum names added in a particular group, but the actual structure would grow out automatically.

For this we should change the admin-UI, as it would be totally confusing. We should have a simple add/edit/delete tag system, rather than full forum structure management.

I am really excited about this concept as it solves some long-running problems. People want rich categorisation of forums, but it so often leads to these messy situations where topics are really spread out (especially for quiet forums). Also managing structure is a real hassle. So we can have everything together by default, while still providing full classification, full filtering capability, and maintaining a rigorous underlying forum structure that can be toggled back on later should the forum become very busy.

Chris Graham

2015-11-17 01:01

administrator   ~3183

New topics would take on active tag filter as default tags.

Chris Graham

2021-02-05 23:06

administrator   ~6938

Last edited: 2023-02-21 02:02

I have implemented some of this for a client. It's more of just removing forum structure and replacing with a single list of topics and tags, so there's lots more that can be done. It's a great start though. Hopefully I can merge in a patch before too long.
EDIT: Patch may now be outdated.

Add Note

View Status
Note
Upload Files
Maximum size: 32,768 KiB

Attach files by dragging & dropping, selecting or pasting them.
You are not logged in You are not logged in. This means you will not get any e-mail notifications. And if you reply, we will not know for sure you are the original poster of the issue.

Issue History

Date Modified Username Field Change
2018-11-10 22:04 Chris Graham Tag Attached: Type: Tagging
2021-02-05 23:06 Chris Graham Note Added: 0006938
2021-02-05 23:07 Chris Graham Tag Attached: Roadmap: v12
2021-11-01 20:11 Chris Graham Tag Attached: Has Patch
2021-11-02 03:36 Chris Graham File Added: forum_subjects.diff
2021-11-02 03:36 Chris Graham File Deleted: forum_subjects.diff
2021-11-02 03:38 Chris Graham File Added: forum_subjects.diff
2022-08-15 16:59 Chris Graham Tag Detached: Roadmap: v12
2022-08-15 16:59 Chris Graham Tag Attached: Roadmap: v11
2022-08-15 20:14 Chris Graham Assigned To => Chris Graham
2022-08-15 20:14 Chris Graham Status Not Assigned => Assigned
2022-11-20 02:57 Chris Graham Tag Detached: Roadmap: v11
2022-11-20 02:57 Chris Graham Tag Attached: Roadmap: v12
2022-11-20 03:05 Chris Graham Assigned To Chris Graham =>
2022-11-20 03:05 Chris Graham Status Assigned => Not Assigned
2023-02-21 02:02 Chris Graham Note Edited: 0006938
2024-03-26 00:58 PDStig Tag Renamed Roadmap: v12 => Roadmap: Over the horizon
2024-07-23 15:57 Chris Graham Relationship added related to 5436