FEATURE: Added unlisted topics option to advanced search (#7447)

FEATURE: Added unlisted topics option to advanced search (#7447)

diff --git a/app/assets/javascripts/discourse/components/search-advanced-options.js.es6 b/app/assets/javascripts/discourse/components/search-advanced-options.js.es6
index cbe6564..939d5fc 100644
--- a/app/assets/javascripts/discourse/components/search-advanced-options.js.es6
+++ b/app/assets/javascripts/discourse/components/search-advanced-options.js.es6
@@ -28,6 +28,17 @@ const REGEXP_POST_TIME_WHEN = /^(before|after)/gi;
 
 const IN_OPTIONS_MAPPING = { images: "with" };
 
+const DEFAULT_STATUS_OPTIONS = [
+  { name: I18n.t("search.advanced.statuses.open"), value: "open" },
+  { name: I18n.t("search.advanced.statuses.closed"), value: "closed" },
+  { name: I18n.t("search.advanced.statuses.archived"), value: "archived" },
+  { name: I18n.t("search.advanced.statuses.noreplies"), value: "noreplies" },
+  {
+    name: I18n.t("search.advanced.statuses.single_user"),
+    value: "single_user"
+  }
+];
+
 export default Ember.Component.extend({
   classNames: ["search-advanced-options"],
 
@@ -47,17 +58,6 @@ export default Ember.Component.extend({
     { name: I18n.t("search.advanced.filters.images"), value: "images" }
   ],
 
-  statusOptions: [
-    { name: I18n.t("search.advanced.statuses.open"), value: "open" },
-    { name: I18n.t("search.advanced.statuses.closed"), value: "closed" },
-    { name: I18n.t("search.advanced.statuses.archived"), value: "archived" },
-    { name: I18n.t("search.advanced.statuses.noreplies"), value: "noreplies" },
-    {
-      name: I18n.t("search.advanced.statuses.single_user"),
-      value: "single_user"
-    }
-  ],
-
   postTimeOptions: [
     { name: I18n.t("search.advanced.post.time.before"), value: "before" },
     { name: I18n.t("search.advanced.post.time.after"), value: "after" }
@@ -102,10 +102,20 @@ export default Ember.Component.extend({
           days: ""
         }
       },
+      statusOptions: DEFAULT_STATUS_OPTIONS,
       inOptions: this.currentUser
         ? this.inOptionsForUsers.concat(this.inOptionsForAll)
         : this.inOptionsForAll
     });
+
+    if (this.currentUser.get("staff")) {
+      this.setProperties({
+        statusOptions: DEFAULT_STATUS_OPTIONS.concat({
+          name: I18n.t("search.advanced.statuses.unlisted"),
+          value: "unlisted"
+        })
+      });
+    }
   },
 
   _update() {
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 63f6b11..329a858 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -1772,6 +1772,7 @@ en:
           archived: are archived
           noreplies: have zero replies
           single_user: contain a single user
+          unlisted: are unlisted
         post:
           count:
             label: Minimum Post Count
diff --git a/lib/search.rb b/lib/search.rb
index fbc1e8c..c7e691d 100644
--- a/lib/search.rb
+++ b/lib/search.rb
@@ -280,6 +280,14 @@ class Search
     posts.where("topics.participant_count = 1")
   end
 
+  advanced_filter(/^status:unlisted$/) do |posts|
+    if @guardian.is_staff?
+      posts.where("NOT topics.visible")
+    else
+      posts.where("1=0")
+    end
+  end
+
   advanced_filter(/^posts_count:(\d+)$/) do |posts, match|
     posts.where("topics.posts_count = ?", match.to_i)
   end
@@ -737,7 +745,7 @@ class Search
 
     is_topic_search = @search_context.present? && @search_context.is_a?(Topic)
 
-    posts = posts.where("topics.visible") unless is_topic_search
+    posts = posts.where("topics.visible") if !is_topic_search && !@guardian.is_staff?
 
     if opts[:private_messages] || (is_topic_search && @search_context.private_message?)
       posts = posts.where("topics.archetype =  ?", Archetype.private_message)
diff --git a/spec/components/search_spec.rb b/spec/components/search_spec.rb
index 7b23bb8..24f2b49 100644
--- a/spec/components/search_spec.rb
+++ b/spec/components/search_spec.rb
@@ -1130,6 +1130,25 @@ describe Search do
           .to eq([post7.id, post8.id])
       end
 
+      it "can search for unlisted topics as staff" do
+        topic1 = Fabricate(:topic, visible: false)
+        post = Fabricate(:post, raw: 'Testing post', topic: topic1)
+        topic2 = Fabricate(:topic)
+        Fabricate(:post, raw: 'Testing post', topic: topic2)
+
+        results = Search.execute('Testing post status:unlisted', guardian: Guardian.new(Fabricate(:moderator)))
+        expect(results.posts.length).to eq(1)
+        expect(results.posts.first.id).to eq(post.id)
+      end
+
+      it "unlisted topics can't be found using search for non-staff" do
+        topic = Fabricate(:topic, visible: false)
+        Fabricate(:post, raw: 'Testing post', topic: topic)
+
+        results = Search.execute('Testing post', guardian: Guardian.new(Fabricate(:user)))
+        expect(results.posts.length).to eq(0)
+      end
+
     end
 
     it "can find posts which contains filetypes" do

GitHub sha: 539723f8

Revert "FEATURE: Added unlisted topics option to advanced search (#7447)"