diff --git a/app/controllers/tag_controller.rb b/app/controllers/tag_controller.rb
index 4855c384f7..d508d22faa 100644
--- a/app/controllers/tag_controller.rb
+++ b/app/controllers/tag_controller.rb
@@ -242,6 +242,35 @@ def related
render partial: 'tag/related', layout: false, locals: { tags: @tags }
end
+ def subtopics
+ @topic = (params[:subtopic] || params[:topic] || params[:id]).to_s.strip
+ if @topic.blank?
+ @subtopics = []
+ else
+ parent_tag = Tag.find_by(name: "parent:#{@topic}")
+ node_subtopics = parent_tag ? parent_tag.nodes.where(status: 1) : []
+ node_slugs = node_subtopics.collect(&:slug).compact.uniq
+
+ if node_slugs.present?
+ @subtopics = Tag.where(name: node_slugs).or(Tag.where(parent: @topic)).order(:name)
+ else
+ @subtopics = Tag.where(parent: @topic).order(:name)
+ end
+
+ existing_names = @subtopics.collect(&:name)
+ missing_slugs = node_slugs - existing_names
+ if missing_slugs.present?
+ missing_tags = missing_slugs.map { |slug| Tag.new(name: slug) }
+ @subtopics = @subtopics.to_a + missing_tags
+ @subtopics.sort_by!(&:name)
+ end
+ end
+
+ render partial: 'tag/subtopics', layout: false, locals: { subtopics: @subtopics, topic: @topic }
+ end
+
+ def topic_tree; end
+
def widget
num = params[:n] || 4
nids = Tag.find_nodes_by_type(params[:id], 'note', num).collect(&:nid)
@@ -566,6 +595,4 @@ def fetch_counts
@nodes.size
end
end
-
- def topic_tree; end
end
diff --git a/app/views/tag/_subtopic.html.erb b/app/views/tag/_subtopic.html.erb
new file mode 100644
index 0000000000..336b1b62ac
--- /dev/null
+++ b/app/views/tag/_subtopic.html.erb
@@ -0,0 +1 @@
+<%= render partial: 'tag/subtopics', locals: { subtopics: local_assigns[:subtopics] || @subtopics, topic: local_assigns[:topic] || @topic } %>
diff --git a/app/views/tag/_subtopics.html.erb b/app/views/tag/_subtopics.html.erb
new file mode 100644
index 0000000000..09c5a09bed
--- /dev/null
+++ b/app/views/tag/_subtopics.html.erb
@@ -0,0 +1,19 @@
+<% list = local_assigns[:subtopics] || @subtopics %>
+<% if list.present? %>
+ <% list.each do |subtopic| %>
+ <% name = subtopic.respond_to?(:name) ? subtopic.name : (subtopic.respond_to?(:slug) ? subtopic.slug : subtopic.to_s) %>
+
+ <% end %>
+<% else %>
+
+ No subtopics found
+
+<% end %>
diff --git a/app/views/tag/topic_tree.html.erb b/app/views/tag/topic_tree.html.erb
index c6adf8c814..14664a2cab 100644
--- a/app/views/tag/topic_tree.html.erb
+++ b/app/views/tag/topic_tree.html.erb
@@ -1,107 +1,553 @@
-Topic Tree
-
-
-
-
-
-
- Water
-
-
-
-
-
- Soil
-
-
-
-
-
- Air
-
-
-
-
- Industry
-
-
-
-
- Climate
-
-
-
-
- Organizing
-
-
-
-
- Monitoring
-
-
-
-
-
-
-
\ No newline at end of file
+Topic Tree
+
+
+
+
+
+ Water
+
+
+
+
+ Soil
+
+
+
+
+ Air
+
+
+
+
+ Industry
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Climate
+
+
+
+
+ Organizing
+
+
+
+
+ Monitoring
+
+
+
+
+
+
+
+
+
diff --git a/config/routes.rb b/config/routes.rb
index b7aafe7315..0089f41acf 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -208,6 +208,8 @@
get 'blog2/:id' => 'tag#blog2'
get 'tags' => 'tag#index'
get 'tags/related(/:id)' => 'tag#related'
+ get 'tags/subtopics(/:subtopic)' => 'tag#subtopics'
+ get 'tags/subtopic(/:subtopic)' => 'tag#subtopics'
get 'tags/:id' => redirect('/tag/:id')
post 'tag/suggested/:id' => 'tag#suggested'
get 'tag/author/:id.json' => 'tag#author'
diff --git a/test/fixtures/node_tags.yml b/test/fixtures/node_tags.yml
index 4f2be77509..511f1811cc 100644
--- a/test/fixtures/node_tags.yml
+++ b/test/fixtures/node_tags.yml
@@ -284,3 +284,10 @@ sub_tag:
tid: 37
nid: 1
uid: 2
+
+subtopic_node_tag:
+ tid: 38
+ uid: 1
+ nid: 2
+ date: <%= DateTime.now.to_i %>
+
diff --git a/test/fixtures/tags.yml b/test/fixtures/tags.yml
index 0fc023fc87..d95aa6317f 100644
--- a/test/fixtures/tags.yml
+++ b/test/fixtures/tags.yml
@@ -150,3 +150,12 @@ hidden_response_tag:
subscribed_tag:
tid: 37
name: sub:tag
+
+subtopic_parent_tag:
+ tid: 38
+ name: parent:water
+
+subtopic_child_tag:
+ tid: 39
+ name: about
+
diff --git a/test/functional/tag_controller_test.rb b/test/functional/tag_controller_test.rb
index 0c60755709..89489155fd 100644
--- a/test/functional/tag_controller_test.rb
+++ b/test/functional/tag_controller_test.rb
@@ -694,4 +694,52 @@ def setup
assert_not_nil comments
assert_template 'comments/_comments'
end
+
+ test 'subtopics partial with parent column tag' do
+ get :subtopics, params: { subtopic: 'spectrometry' }
+ assert_response :success
+ assert_not_nil assigns(:subtopics)
+ assert_includes assigns(:subtopics).map(&:name), 'spectrometer'
+ assert_template partial: 'tag/_subtopics'
+ assert_select '.topic-item[data-topic="spectrometer"]'
+ end
+
+ test 'subtopics partial with parent:tagname on node' do
+ get :subtopics, params: { subtopic: 'water' }
+ assert_response :success
+ assert_not_nil assigns(:subtopics)
+ assert_includes assigns(:subtopics).map(&:name), 'about'
+ assert_template partial: 'tag/_subtopics'
+ assert_select '.topic-item[data-topic="about"]'
+ end
+
+ test 'subtopics accepts topic parameter alias' do
+ get :subtopics, params: { topic: 'water' }
+ assert_response :success
+ assert_not_nil assigns(:subtopics)
+ assert_includes assigns(:subtopics).map(&:name), 'about'
+ end
+
+ test 'subtopics with missing or empty parameter' do
+ get :subtopics, params: { subtopic: '' }
+ assert_response :success
+ assert_equal [], assigns(:subtopics)
+ assert_select '.text-muted'
+ end
+
+ test 'subtopics with nonexistent topic returns empty list message' do
+ get :subtopics, params: { subtopic: 'nonexistent-topic-xyz' }
+ assert_response :success
+ assert_equal [], assigns(:subtopics)
+ assert_select '.text-muted'
+ end
+
+ test 'topic_tree page renders correctly with expandable topics' do
+ get :topic_tree
+ assert_response :success
+ assert_select '#accordion'
+ assert_select '.topic-item'
+ assert_select '.expand-btn'
+ assert_select '.subtopics-container'
+ end
end