diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index e3b0e20a74ac39fb33de9ea3bb340fe9c763477d..4f55356b8b3cf2094f31d6c9367dc14d2e2cf55c 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -31,13 +31,28 @@ class ApplicationController < ActionController::Base
   end
 
   def not_found
-    render 'errors/not_found', layout: 'without_sidebar', status: :not_found
+    respond_to do |format|
+      format.html do
+        render 'errors/not_found', layout: 'without_sidebar', status: :not_found
+      end
+      format.json do
+        render json: { status: 'failed', errors: ['not_found'] }, status: :not_found
+      end
+    end
     false
   end
 
   def verify_moderator
     if !user_signed_in? || !(current_user.is_moderator || current_user.is_admin)
-      render 'errors/not_found', layout: 'without_sidebar', status: :not_found
+      respond_to do |format|
+        format.html do
+          render 'errors/not_found', layout: 'without_sidebar', status: :not_found
+        end
+        format.json do
+          render json: { status: 'failed', errors: ['not_found'] }, status: :not_found
+        end
+      end
+
       return false
     end
     true
diff --git a/app/controllers/articles_controller.rb b/app/controllers/articles_controller.rb
deleted file mode 100644
index 7f9d4ba3537b138736060c424a47128486047b76..0000000000000000000000000000000000000000
--- a/app/controllers/articles_controller.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-class ArticlesController < ApplicationController
-  before_action :set_article
-  before_action :check_article
-
-  def share
-    redirect_to article_path(params[:id])
-  end
-
-  private
-
-  def set_article
-    @article = Article.find params[:id]
-    if @article.deleted && !current_user&.has_post_privilege?('flag_curate', @article)
-      not_found
-    end
-  end
-
-  def check_article
-    unless @article.post_type_id == Article.post_type_id
-      not_found
-    end
-  end
-end
diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb
index 1480024223a52613a77bc1770b59e7d206844f47..f8843272a27a706e2f203ae048bd95608a1ffd3e 100644
--- a/app/controllers/posts_controller.rb
+++ b/app/controllers/posts_controller.rb
@@ -322,14 +322,6 @@ class PostsController < ApplicationController
     render json: { link: uploaded_url(@blob.key) }
   end
 
-  def share_q
-    redirect_to question_path(id: params[:id])
-  end
-
-  def share_a
-    redirect_to question_path(id: params[:qid], anchor: "answer-#{params[:id]}")
-  end
-
   def help_center
     @posts = Post.where(post_type_id: [PolicyDoc.post_type_id, HelpDoc.post_type_id])
                  .or(Post.unscoped.where(post_type_id: [PolicyDoc.post_type_id, HelpDoc.post_type_id],
@@ -367,15 +359,11 @@ class PostsController < ApplicationController
   end
 
   def toggle_comments
-    @post.comments_disabled = !@post.comments_disabled
-    @post.save
+    @post.update(comments_disabled: !@post.comments_disabled)
     if @post.comments_disabled && params[:delete_all_comments]
-      @post.comments.undeleted.map do |c|
-        c.deleted = true
-        c.save
-      end
+      @post.comments.update_all(deleted: true)
     end
-    render json: { success: true }
+    render json: { status: 'success', success: true }
   end
 
   def lock
diff --git a/app/views/articles/_form.html.erb b/app/views/articles/_form.html.erb
deleted file mode 100644
index 594316ee5b7a93ac6810684bf5a2f845178e27eb..0000000000000000000000000000000000000000
--- a/app/views/articles/_form.html.erb
+++ /dev/null
@@ -1,48 +0,0 @@
-<%= render 'posts/markdown_script' %>
-
-<% if @article.errors.any? %>
-  <div class="notice is-danger is-filled">
-    The following errors prevented this post from being saved:
-    <ul>
-      <% @article.errors.full_messages.each do |msg| %>
-        <li><%= msg %></li>
-      <% end %>
-    </ul>
-  </div>
-<% end %>
-
-<%= render 'posts/image_upload' %>
-
-<%= form_for @article, url: edit_article_path(@article) do |f| %>
-  <div class="form-group">
-    <%= f.label :title, "Title your post:", class: "form-element" %>
-    <%= f.text_field :title, class: "form-element" %>
-  </div>
-
-  <%= render 'shared/body_field', f: f, field_name: :body_markdown, field_label: 'Body', post: @article %>
-
-  <div class="post-preview"></div>
-
-  <div class="form-group">
-    <%= f.label :tags_cache, "Tags", class: "form-element" %>
-    <div class="form-caption">
-      Tags help to categorize posts. Separate them by space. Use hyphens for multiple-word tags.
-    </div>
-    <%= f.select :tags_cache, options_for_select(@article.tags_cache.map { |t| [t, t] }, selected: @article.tags_cache),
-                 { include_blank: true }, multiple: true, class: "form-element js-tag-select",
-                 data: { tag_set: @article.category.tag_set.id } %>
-  </div>
-
-  <div class="form-group">
-    <%= label_tag :edit_comment, 'Edit comment', class: "form-element" %>
-    <div class="form-caption">
-      Describe&mdash;if necessary&mdash;what you are changing and why you are making this edit.
-    </div>
-    <%= text_field_tag :edit_comment, params[:edit_comment], class: 'form-element' %>
-  </div>
-
-  <div class="form-group">
-    <%= f.submit check_your_post_privilege(@article, 'edit_posts') ? "Save changes" : "Suggest changes", class: "button is-filled" %>
-    <%= link_to 'Cancel', article_path(@article), class: 'button is-outlined is-muted' %>
-  </div>
-<% end %>
diff --git a/app/views/articles/edit.html.erb b/app/views/articles/edit.html.erb
deleted file mode 100644
index 6727a85a707a9f1bfb920d6498c26629bb9bfc8f..0000000000000000000000000000000000000000
--- a/app/views/articles/edit.html.erb
+++ /dev/null
@@ -1 +0,0 @@
-<%= render 'form', is_edit: true %>
\ No newline at end of file
diff --git a/app/views/articles/show.html.erb b/app/views/articles/show.html.erb
deleted file mode 100644
index ffbd774949cb4b6869468649a2c380dcff20517b..0000000000000000000000000000000000000000
--- a/app/views/articles/show.html.erb
+++ /dev/null
@@ -1,19 +0,0 @@
-<% content_for :title, @article.title.truncate(50) %>
-<% content_for :description do %>
-  <% Rails.cache.fetch "posts/#{@article.id}/description" do %>
-    <%= @article.body_plain[0..74].strip %>...
-  <% end %>
-<% end %>
-
-<% content_for :twitter_card_meta do %>
-  <meta name="twitter:card" content="summary" />
-  <% if @article.user.twitter.present? %>
-    <meta name="twitter:creator" content="@<%= @article.user.twitter %>" />
-  <% end %>
-  <meta property="og:url" content="<%= question_url(@article) %>" />
-  <meta property="og:title" content="<%= @article.title %>" />
-  <meta property="og:description" content="<%= @article.body_plain[0..150].strip %>..." />
-  <meta property="og:image" content="<%= "https://#{RequestContext.community.host}#{SiteSetting['SiteLogoPath']}" %>" />
-<% end %>
-
-<%= render 'posts/expanded', post: @article %>
\ No newline at end of file
diff --git a/app/views/posts/_form_old.html.erb b/app/views/posts/_form_old.html.erb
deleted file mode 100644
index 91758e9ce08e8a349f07ca4df125c84ed6b919a1..0000000000000000000000000000000000000000
--- a/app/views/posts/_form_old.html.erb
+++ /dev/null
@@ -1,102 +0,0 @@
-<% with_post_type ||= false %>
-
-<% content_for :head do %>
-  <%= render 'posts/markdown_script' %>
-<% end %>
-
-<div class="notice is-info">
-  <p><strong>Posting Tips</strong></p>
-  <div class="has-font-size-caption">
-  <% guidance = @category.asking_guidance_override %>
-  <%= raw(sanitize(render_markdown(guidance.present? ? guidance : SiteSetting['AskingGuidance']), scrubber: scrubber)) %>
-  </div>
-</div>
-
-<% if @post.errors.any? %>
-  <div class="notice is-danger is-filled">
-    <p>The following errors prevented your post being saved:</p>
-    <ul>
-      <% @post.errors.full_messages.each do |msg| %>
-        <li><%= msg %></li>
-      <% end %>
-    </ul>
-  </div>
-<% end %>
-
-<%= render 'posts/image_upload' %>
-
-<%= form_for @post, url: submit_path, html: { class: 'has-margin-top-4' } do |f| %>
-  <%= f.hidden_field :category_id %>
-
-  <% if with_post_type %>
-    <div class="form-group">
-      <%= f.label :post_type_id, 'Post type', class: 'form-element' %>
-      <span class="form-caption">What kind of post is this? Questions can have answers; articles only have comments.</span>
-      <% ids = @category.display_post_types.reject { |e| e.to_s.empty? } %>
-      <% post_types = PostType.where(id: ids) %>
-      <% opts = post_types.map { |pt| [pt.name, pt.id] } %>
-      <%= f.select :post_type_id, options_for_select(opts, selected: @post.post_type_id),
-                   { include_blank: true }, class: 'form-element' %>
-    </div>
-  <% else %>
-    <%= f.hidden_field :post_type_id %>
-  <% end %>
-
-  <%= render 'shared/body_field', f: f, field_name: :body_markdown, field_label: 'Body', post: @post %>
-
-  <div class="post-preview"></div>
-
-  <div class="form-group">
-    <%= f.label :title, 'Summarize your post with a title:', class: 'form-element' %>
-    <%= f.text_field :title, class: 'form-element' %>
-  </div>
-
-  <div class="form-group">
-    <%= f.label :tags_cache, 'Tags (at least one):', class: 'form-element' %>
-    <% required_tags = @category.required_tags.to_a %>
-    <% unless required_tags.empty? %>
-      <span class="form-caption">
-        Requires at least one of
-        <% required_tags.each do |tag| %>
-          <a class="badge is-tag is-filled js-add-required-tag" href="javascript:void(0)" data-tag-id="<%= tag.id %>"
-             data-tag-name="<%= tag.name %>">
-            <%= tag.name %>
-          </a>
-        <% end %>
-      </span>
-    <% end %>
-    <%= f.select :tags_cache, options_for_select(@post.tags_cache.map { |t| [t, t] }, selected: @post.tags_cache),
-                 { include_blank: true }, multiple: true, class: "form-element js-tag-select",
-                 data: { tag_set: @category.tag_set_id } %>
-  </div>
-
-  <% unless @post.id.present? %>
-    <div class="form-group">
-      <%= f.label :license_id, 'License', class: 'form-element' %>
-      <span class="form-caption">
-        <% site_default = License.site_default %>
-        <% category_default = @category.license %>
-        <% if site_default.present? %>
-          site default: <a href="javascript:void(0)" class="js-license-autofill" data-license-id="<%= site_default.id %>">
-            <%= site_default.name %>
-          </a>
-        <% end %>
-        <% if site_default.present? && category_default.present? %>
-          &middot;
-        <% end %>
-        <% if category_default.present? %>
-          category default: <a href="javascript:void(0)" class="js-license-autofill" data-license-id="<%= category_default.id %>">
-            <%= category_default.name %>
-          </a>
-        <% end %>
-      </span>
-      <%= f.select :license_id, options_for_select(License.enabled.default_order(@category).map { |l| [l.name, l.id] },
-                                                   selected: @post.license_id), {}, class: 'form-element' %>
-    </div>
-  <% end %>
-
-  <div class="actions">
-    <%= f.submit "Save Post in #{@category.name}", class: 'button is-filled' %>
-    <%= link_to 'Cancel', category_path(@category), class: 'button is-muted is-outlined' %>
-  </div>
-<% end %>
\ No newline at end of file
diff --git a/app/views/posts/document.html.erb b/app/views/posts/document.html.erb
index 00e5560a2ee51cad47a051b13a3bd162c0063532..a5cb84a167ba3c82b7ae75a9110be610f0b9a887 100644
--- a/app/views/posts/document.html.erb
+++ b/app/views/posts/document.html.erb
@@ -3,7 +3,7 @@
 <% end %>
 <% unless @post.nil? %>
   <% if (moderator? && @post.post_type_id == HelpDoc.post_type_id) || (admin? && @post.post_type_id == PolicyDoc.post_type_id) %>
-    <%= link_to 'edit', edit_help_post_path(@post), class: "button is-outlined is-muted" %>
+    <%= link_to 'edit', edit_post_path(@post), class: "button is-outlined is-muted" %>
   <% end %>
 <% end %>
 
diff --git a/app/views/posts/edit_help.html.erb b/app/views/posts/edit_help.html.erb
deleted file mode 100644
index 1122a1799bcacc0a265c803b9a1483fee671438f..0000000000000000000000000000000000000000
--- a/app/views/posts/edit_help.html.erb
+++ /dev/null
@@ -1,39 +0,0 @@
-<% content_for :title, "Editing '#{@post.title.truncate(50)}'" %>
-
-<% if @post.errors.any? %>
-  <div class="notice is-danger">
-    These errors prevented this post being saved:
-    <ul>
-      <% @post.errors.full_messages.each do |msg| %>
-        <li><%= msg %></li>
-      <% end %>
-    </ul>
-  </div>
-<% end %>
-
-<h1>Edit <%= @post.policy_doc? ? 'Policy' : (@post.help_doc? ? 'Help Article' : 'Post') %></h1>
-<%= form_for @post, url: update_help_post_path(@post) do |f| %>
-  <div class="form-group">
-    <%= f.label :title, "Title your post:", class: "form-element" %>
-    <%= f.text_field :title, class: "form-element" %>
-  </div>
-  <div class="form-group">
-    <%= f.label :body_markdown, 'Body', class: "form-element" %>
-    <%= f.text_area :body_markdown, { class: "form-element is-large post-field", rows: 15 } %>
-  </div>
-  <div class="form-group">
-    <%= f.label :help_category, 'Category', class: 'form-element' %>
-    <span class="form-caption">
-      Name a category under which to display this post in the help center.
-    </span>
-    <%= f.text_field :help_category, class: 'form-element' %>
-  </div>
-  <div class="form-group">
-    <%= f.label :help_ordering, 'Order', class: 'form-element' %>
-    <span class="form-caption">
-      Control where this post appears in the list of help articles. Higher values appear later in the list.
-    </span>
-    <%= f.number_field :help_ordering, class: 'form-element' %>
-  </div>
-  <%= f.submit "Update", class: "button is-filled is-very-large" %>
-<% end %>
diff --git a/app/views/posts/new_help.html.erb b/app/views/posts/new_help.html.erb
deleted file mode 100644
index a65f5646e2e9fd1f34e36a58b936b6324a62d387..0000000000000000000000000000000000000000
--- a/app/views/posts/new_help.html.erb
+++ /dev/null
@@ -1,55 +0,0 @@
-<% content_for :title, "New Policy Document" %>
-
-<% if @post.errors.any? %>
-  <div class="notice is-danger">
-    These errors prevented this post being saved:
-    <ul>
-      <% @post.errors.full_messages.each do |msg| %>
-        <li><%= msg %></li>
-      <% end %>
-    </ul>
-  </div>
-<% end %>
-
-<h1>New Policy Document</h1>
-<p>
-  If you're a moderator, you may use this page to create help documents that you can link to from /help/&lt;document&gt;.
-  If you're an administrator, you can also create policy documents, including legal documents.
-</p>
-<%= form_for @post, url: create_help_post_path do |f| %>
-  <div class="form-group">
-    <%= f.label :post_type_id, 'Post type', class: "form-element" %>
-    <%= f.select :post_type_id, options_for_select(current_user.is_admin ?
-                                                     [['Policy', PolicyDoc.post_type_id], ['Help', HelpDoc.post_type_id]] :
-                                                     [['Help', HelpDoc.post_type_id]]),
-                 { include_blank: true }, class: 'form-element' %>
-  </div>
-  <div class="form-group">
-    <%= f.label :doc_slug, 'URL slug', class: "form-element" %>
-    <span class="form-caption">In a URL of "https://yoursite.codidact.com/help/topic", the "topic" is the slug.</span>
-    <%= f.text_field :doc_slug, class: 'form-element' %>
-  </div>
-  <div class="form-group">
-    <%= f.label :title, "Title your post:", class: "form-element" %>
-    <%= f.text_field :title, class: "form-element" %>
-  </div>
-  <div class="form-group">
-    <%= f.label :body_markdown, 'Body', class: "form-element" %>
-    <%= f.text_area :body_markdown, { class: "form-element post-field", rows: 15 } %>
-  </div>
-  <div class="form-group">
-    <%= f.label :help_category, 'Category', class: 'form-element' %>
-    <span class="form-caption">
-      Name a category under which to display this post in the help center.
-    </span>
-    <%= f.text_field :help_category, class: 'form-element' %>
-  </div>
-  <div class="form-group">
-    <%= f.label :help_ordering, 'Order', class: 'form-element' %>
-    <span class="form-caption">
-      Control where this post appears in the list of help articles. Higher values appear later in the list.
-    </span>
-    <%= f.number_field :help_ordering, class: 'form-element' %>
-  </div>
-  <%= f.submit "Create", class: "button is-filled is-very-large" %>
-<% end %>
diff --git a/app/views/posts/new_old.html.erb b/app/views/posts/new_old.html.erb
deleted file mode 100644
index 1db9ccf3fdc9292ecaef382020eb94e25d15532c..0000000000000000000000000000000000000000
--- a/app/views/posts/new_old.html.erb
+++ /dev/null
@@ -1,5 +0,0 @@
-<h1 class="has-margin-bottom-2">New Post in <%= @category.name %></h1>
-<p class="has-color-tertiary-500">Not where you meant to post? See <%= link_to 'Categories', categories_path %></p>
-
-<%= render 'form', with_post_type: @category.display_post_types.reject { |e| e.to_s.empty? }.size > 1,
-           submit_path: create_post_path(@category.id) %>
\ No newline at end of file
diff --git a/test/controllers/posts_controller_test.rb b/test/controllers/posts_controller_test.rb
index ce99f5d5ae7eca98323ed9380c8ad32847368167..f89ed09f261ed4ba3d0425e607826a0fafa5d488 100644
--- a/test/controllers/posts_controller_test.rb
+++ b/test/controllers/posts_controller_test.rb
@@ -3,24 +3,49 @@ require 'test_helper'
 class PostsControllerTest < ActionController::TestCase
   include Devise::Test::ControllerHelpers
 
-  test 'should successfully get help center' do
+  # Help
+
+  test 'can get help center' do
     get :help_center
     assert_response 200
     assert_not_nil assigns(:posts)
   end
 
-  test 'question permalink should correctly redirect' do
-    get :share_q, params: { id: posts(:question_one).id }
-    assert_response 302
-    assert_redirected_to question_path(posts(:question_one))
+  test 'can get help article' do
+    get :document, params: { slug: posts(:help_article).doc_slug }
+    assert_response 200
+    assert_not_nil assigns(:post)
   end
 
-  test 'answer permalink should correctly redirect' do
-    get :share_a, params: { qid: posts(:question_one).id, id: posts(:answer_one).id }
-    assert_response 302
-    assert_redirected_to question_path(id: posts(:question_one).id, anchor: "answer-#{posts(:answer_one).id}")
+  test 'moderator can get mod help article' do
+    sign_in users(:moderator)
+    get :document, params: { slug: posts(:mod_help_article).doc_slug }
+    assert_response 200
+    assert_not_nil assigns(:post)
+  end
+
+  test 'moderator help requires authentication' do
+    get :document, params: { slug: posts(:mod_help_article).doc_slug }
+    assert_response 404
+    assert_not_nil assigns(:post)
+  end
+
+  test 'regular user cannot get mod help' do
+    sign_in users(:standard_user)
+    get :document, params: { slug: posts(:mod_help_article).doc_slug }
+    assert_response 404
+    assert_not_nil assigns(:post)
   end
 
+  test 'cannot get disabled help article' do
+    sign_in users(:moderator)
+    get :document, params: { slug: posts(:disabled_help_article).doc_slug }
+    assert_response 404
+    assert_not_nil assigns(:post)
+  end
+
+  # Change category
+
   test 'should change category' do
     sign_in users(:deleter)
     post :change_category, params: { id: posts(:article_one).id, target_id: categories(:articles_only).id }
@@ -604,4 +629,46 @@ class PostsControllerTest < ActionController::TestCase
     assert_response 401
     assert_equal before_history, after_history, 'PostHistory event incorrectly created on deletion'
   end
+
+  # Toggle comments
+
+  test 'can toggle comments' do
+    sign_in users(:moderator)
+    post :toggle_comments, params: { id: posts(:question_one).id }
+    assert_response 200
+    assert_not_nil assigns(:post)
+    assert_nothing_raised do
+      JSON.parse(response.body)
+    end
+    assert_equal 'success', JSON.parse(response.body)['status']
+    assert assigns(:post).comments_disabled
+  end
+
+  test 'toggle comments requires authentication' do
+    post :toggle_comments, params: { id: posts(:question_one).id }
+    assert_response 302
+    assert_redirected_to new_user_session_path
+    assert_not assigns(:post).comments_disabled
+  end
+
+  test 'regular users cannot toggle comments' do
+    sign_in users(:standard_user)
+    post :toggle_comments, params: { id: posts(:question_one).id }
+    assert_response 404
+    assert_not_nil assigns(:post)
+    assert_not assigns(:post).comments_disabled
+  end
+
+  test 'specifying delete all results in comments being deleted' do
+    sign_in users(:moderator)
+    post :toggle_comments, params: { id: posts(:question_one).id, delete_all_comments: true }
+    assert_response 200
+    assert_not_nil assigns(:post)
+    assert_nothing_raised do
+      JSON.parse(response.body)
+    end
+    assert_equal 'success', JSON.parse(response.body)['status']
+    assert assigns(:post).comments_disabled
+    assert assigns(:post).comments.all?(&:deleted?)
+  end
 end
diff --git a/test/fixtures/posts.yml b/test/fixtures/posts.yml
index d58e8ca3c6fc6999f381139fc270647727c83ddc..25ac1dd327909d03773cbe268cf4e1d3a5eacd28 100644
--- a/test/fixtures/posts.yml
+++ b/test/fixtures/posts.yml
@@ -256,3 +256,33 @@ deleted_article:
   deleted_by: deleter
   upvote_count: 0
   downvote_count: 0
+
+help_article:
+  post_type: help_doc
+  body: ABCDEF GHIJKL MNOPQR STUVWX YZ ABCDEF GHIJKL MNOPQR STUVWX YZ
+  body_markdown: ABCDEF GHIJKL MNOPQR STUVWX YZ ABCDEF GHIJKL MNOPQR STUVWX YZ
+  user: system
+  community: sample
+  help_category: Site Information
+  help_ordering: 99
+  doc_slug: sample
+
+mod_help_article:
+  post_type: help_doc
+  body: ABCDEF GHIJKL MNOPQR STUVWX YZ ABCDEF GHIJKL MNOPQR STUVWX YZ
+  body_markdown: ABCDEF GHIJKL MNOPQR STUVWX YZ ABCDEF GHIJKL MNOPQR STUVWX YZ
+  user: system
+  community: sample
+  help_category: $Moderator
+  help_ordering: 99
+  doc_slug: sample-mod
+
+disabled_help_article:
+  post_type: help_doc
+  body: ABCDEF GHIJKL MNOPQR STUVWX YZ ABCDEF GHIJKL MNOPQR STUVWX YZ
+  body_markdown: ABCDEF GHIJKL MNOPQR STUVWX YZ ABCDEF GHIJKL MNOPQR STUVWX YZ
+  user: system
+  community: sample
+  help_category: $Disabled
+  help_ordering: 99
+  doc_slug: sample-disable
diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml
index 460c4740e67c90eafc37c89bccfc14355045669a..b3b65427bca634104cb3728508140c486d7cfec1 100644
--- a/test/fixtures/users.yml
+++ b/test/fixtures/users.yml
@@ -84,3 +84,13 @@ no_community_user:
   is_global_admin: false
   is_global_moderator: false
   confirmed_at: 2020-01-01T00:00:00.000000Z
+
+system:
+  id: -99
+  email: system@qpixel-test.net
+  encrypted_password: abcdefghijklmnopqrstuvwxyz
+  sign_in_count: 1337
+  username: system
+  is_global_admin: true
+  is_global_moderator: true
+  confirmed_at: 2020-01-01T00:00:00.000000Z