From 1316d7ca0335dd4ff5f8b4c7bb46e961b37503f0 Mon Sep 17 00:00:00 2001 From: MrYummy Date: Sun, 28 May 2017 18:08:57 -0400 Subject: Added Searching Features * Added Thread Search Feature * Added User Search Feature * Re-organized searching, added @mention support to author search --- app/controllers/application_controller.rb | 2 +- app/controllers/blogposts_controller.rb | 2 +- app/controllers/forums_controller.rb | 2 +- app/controllers/forumthreads_controller.rb | 23 ++++++- app/controllers/users_controller.rb | 13 +++- app/helpers/mailer_helper.rb | 2 +- app/helpers/users_helper.rb | 2 +- app/models/forum.rb | 2 +- app/models/forumthread.rb | 43 +++++++++++++ app/models/role.rb | 2 +- app/models/user.rb | 4 ++ app/views/application/_md_editor_user.html.erb | 8 +++ app/views/forums/index.html.erb | 4 +- app/views/forums/show.html.erb | 13 +++- app/views/forumthreads/index.html.erb | 84 +++++++++++++++++++++++++ app/views/forumthreads/search.html.erb | 56 +++++++++++++++++ app/views/users/index.html.erb | 31 ++++++--- config/routes.rb | 7 ++- db/migrate/20170522210610_add_search_indexes.rb | 8 +++ db/schema.rb | 12 +++- 20 files changed, 293 insertions(+), 27 deletions(-) create mode 100644 app/views/application/_md_editor_user.html.erb create mode 100644 app/views/forumthreads/index.html.erb create mode 100644 app/views/forumthreads/search.html.erb create mode 100644 db/migrate/20170522210610_add_search_indexes.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index a0e166e..d489611 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -75,4 +75,4 @@ class ApplicationController < ActionController::Base !!(current_user && current_user.confirmed?) end -end \ No newline at end of file +end diff --git a/app/controllers/blogposts_controller.rb b/app/controllers/blogposts_controller.rb index 79c9e5d..7a9851d 100644 --- a/app/controllers/blogposts_controller.rb +++ b/app/controllers/blogposts_controller.rb @@ -75,4 +75,4 @@ class BlogpostsController < ApplicationController end end -end \ No newline at end of file +end diff --git a/app/controllers/forums_controller.rb b/app/controllers/forums_controller.rb index 486d21c..761a86b 100644 --- a/app/controllers/forums_controller.rb +++ b/app/controllers/forums_controller.rb @@ -1,4 +1,5 @@ class ForumsController < ApplicationController + before_filter :check_permission, only: [:show, :edit, :update, :destroy] def index @@ -77,7 +78,6 @@ class ForumsController < ApplicationController redirect_to forums_path end - private def check_permission diff --git a/app/controllers/forumthreads_controller.rb b/app/controllers/forumthreads_controller.rb index b1dffd9..e21c6d4 100644 --- a/app/controllers/forumthreads_controller.rb +++ b/app/controllers/forumthreads_controller.rb @@ -3,9 +3,14 @@ class ForumthreadsController < ApplicationController before_filter :check_permission, only: [:show, :edit, :update, :destroy] def index - redirect_to forum_path(@thread.forum.forumgroup, f) + if params[:label] && !Label.where("lower(name) = ?", params[:label].downcase).try(:first) && params[:label].downcase != "no label" + flash[:alert] = "'#{params[:label]}' is not a valid label." + redirect_to forumthreads_path(params.except(:label, :controller, :action)) + return + end + @threads = Forumthread.filter(current_user, params[:title], params[:content], params[:reply], params[:label], User.where("lower(ign) = ?", params[:author].to_s.downcase).try(:first), params[:query], Forum.where(id: params[:id]).try(:first)) + .page(params[:page]).per(30) end - def show if params[:reverse] @replies = @thread.replies.reverse_order.page(params[:page]) @@ -80,6 +85,20 @@ class ForumthreadsController < ApplicationController redirect_to @thread.forum end + def search + end + + def search_redirect + params.each do |key, value| + params[key] = nil if params[key] == "" + end + params[:id] = nil if params[:id] == "Search All Threads" + params[:label] = nil if params[:label] && params[:label].downcase == "label" + params[:author] = params[:author].tr("@ ", "") if params[:author] + params_list = Hash[params.except(:commit, :utf8, :authenticity_token)] + redirect_to forumthreads_path(params_list) + end + private def check_permission diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 16f42d3..6b31d22 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -4,7 +4,7 @@ class UsersController < ApplicationController include MailerHelper include ERB::Util - before_filter :set_user, except: [:index, :new, :create, :lost_password, :reset_password, :suggestions] + before_filter :set_user, except: [:index, :new, :create, :lost_password, :reset_password, :suggestions, :search_redirect] def index if params[:role] @@ -13,7 +13,7 @@ class UsersController < ApplicationController else if role = Role.get(params[:role]) @users = User.joins(:role).where(role: role) - else + elsif params[:search] == nil flash[:alert] = "role '#{params[:role]}' does not exist!" redirect_to users_path return @@ -30,6 +30,7 @@ class UsersController < ApplicationController else @users = User.joins(:role).where.not(id: User.first.id) #Remove first user end + @users = User.search(@users, params[:search]) if params[:search] @users = @users.order("roles.value desc", "confirmed desc", :name) unless params[:badge] @count = @users.size @users = @users.page(params[:page]).per(100) @@ -339,6 +340,14 @@ class UsersController < ApplicationController end end + def search_redirect + params.each do |key, value| + params[key] = nil if params[key] == "" + end + params_list = Hash[params.except(:commit, :utf8, :authenticity_token)] + redirect_to users_path(params_list) + end + private def validate_token(uuid, email, token) diff --git a/app/helpers/mailer_helper.rb b/app/helpers/mailer_helper.rb index dbacf81..5e5649c 100644 --- a/app/helpers/mailer_helper.rb +++ b/app/helpers/mailer_helper.rb @@ -24,4 +24,4 @@ module MailerHelper end end end -end \ No newline at end of file +end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index 2ce1765..7ad99d8 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -52,4 +52,4 @@ module UsersHelper end end -end \ No newline at end of file +end diff --git a/app/models/forum.rb b/app/models/forum.rb index 39e8f2a..a239dbc 100644 --- a/app/models/forum.rb +++ b/app/models/forum.rb @@ -32,4 +32,4 @@ class Forum < ActiveRecord::Base def to_param [id, to_s.parameterize].join("-") end -end \ No newline at end of file +end diff --git a/app/models/forumthread.rb b/app/models/forumthread.rb index 905e4d3..86823ac 100644 --- a/app/models/forumthread.rb +++ b/app/models/forumthread.rb @@ -65,4 +65,47 @@ class Forumthread < ActiveRecord::Base def to_param [id, to_s.parameterize].join("-") end + + def self.filter (user, title, content, reply, label, author, query, forum) + userid = user.try(:id).to_i + role = user.try(:role).to_i + + can_read = "COALESCE(forum_role_read.value, 0) <= ? AND COALESCE(forumgroup_role_read.value, 0) <= ?" + sticky_can_write = "sticky = true AND (COALESCE(forum_role_write.value, 0) <= ? OR COALESCE(forumgroup_role_write.value, 0) <= ?)" + + threads = forum.try(:forumthreads) || Forumthread + threads = threads.where("forumthreads.user_author_id = ? OR (#{can_read}) OR (#{sticky_can_write})", userid, role, role, role, role) + .joins("LEFT JOIN threadreplies ON forumthreads.id = threadreplies.forumthread_id") + .joins(forum: :forumgroup) + .joins("LEFT JOIN roles as forum_role_read ON forums.role_read_id = forum_role_read.id") + .joins("LEFT JOIN roles as forum_role_write ON forums.role_write_id = forum_role_write.id") + .joins("LEFT JOIN roles as forumgroup_role_read ON forumgroups.role_read_id = forumgroup_role_read.id") + .joins("LEFT JOIN roles as forumgroup_role_write ON forumgroups.role_write_id = forumgroup_role_write.id") + + if [content, title, reply, label, author, query].any? + label_o = Label.find_by(name: label) + if label_o + threads = threads.where(label: label_o) + elsif label.try(:downcase) == "no label" + threads = threads.where(label: nil) + end + + threads = threads.where(user_author: author) if author + + if query + threads = threads.where("MATCH (title, forumthreads.content) AGAINST (?) OR MATCH (threadreplies.content) AGAINST (?)", query, query) + elsif [title, content, reply].any? + query = [title, content, reply].select(&:present?).join(" ") + threads = threads.where("MATCH (title) AGAINST (?)", title) if title + threads = threads.where("MATCH (forumthreads.content) AGAINST (?)", content) if content + threads = threads.where("MATCH (threadreplies.content) AGAINST (?)", reply) if reply + threads = threads.group("threadreplies.id", "forumthreads.id") + threads = threads.order("(MATCH (title, forumthreads.content) AGAINST ('#{query}')) DESC") + end + end + + threads = threads.order("sticky desc", "threadreplies.created_at desc", "forumthreads.created_at desc") if threads.order_values.empty? + + threads + end end diff --git a/app/models/role.rb b/app/models/role.rb index 708fb40..e780b8c 100644 --- a/app/models/role.rb +++ b/app/models/role.rb @@ -53,4 +53,4 @@ class Role < ActiveRecord::Base Role.order(:value).select {|r| r >= from}.select {|r| r <= to} end -end \ No newline at end of file +end diff --git a/app/models/user.rb b/app/models/user.rb index ab7471e..a96410a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -174,4 +174,8 @@ class User < ActiveRecord::Base def set_email_token self.email_token ||= SecureRandom.hex(16) end + + def self.search (users, search) + return users.where("users.name like ? OR ign like ?", "%#{User.send(:sanitize_sql_like, search)}%", "%#{User.send(:sanitize_sql_like, search)}%") + end end diff --git a/app/views/application/_md_editor_user.html.erb b/app/views/application/_md_editor_user.html.erb new file mode 100644 index 0000000..25f63a4 --- /dev/null +++ b/app/views/application/_md_editor_user.html.erb @@ -0,0 +1,8 @@ +
+
+ <% options = (defined?(options) && options || {}) %> + <% options[:class] = "#{options[:class]} editor_field" %> + <% options[:placeholder] ||= "Enter user's name. Prefix with \"@\" to get suggestions." %> + <%= text_field_tag name, content, options %> +
+
diff --git a/app/views/forums/index.html.erb b/app/views/forums/index.html.erb index f09ea20..0a2fbaf 100644 --- a/app/views/forums/index.html.erb +++ b/app/views/forums/index.html.erb @@ -1,5 +1,7 @@ <% title "Forums" %> +<%= link_to "All threads", forumthreads_path(params.except("controller", "action")), class: "btn blue right" %> +
<% @groups.each do |group| %>
@@ -56,4 +58,4 @@ <%= link_to "New group", new_forumgroup_path, class: "btn blue" %> <% elsif mod? %> <%= link_to "New group", "#", class: "btn blue", disabled: true %> -<% end %> \ No newline at end of file +<% end %> diff --git a/app/views/forums/show.html.erb b/app/views/forums/show.html.erb index 60f3185..9dcdad8 100644 --- a/app/views/forums/show.html.erb +++ b/app/views/forums/show.html.erb @@ -1,8 +1,15 @@ <%= link_to @forum.group, forumgroup_path(@forum.group) %> → <%= @forum %> -

<%= title @forum %>

+

+ <%= title @forum %> + <% params[:id] = params[:id].split("-")[0] %> + <%= link_to "Search Threads", forumthreads_path(params.except("action", "controller")), class: "btn blue right" %> +

<% if @forum.can_write?(current_user) %> -

<%= link_to "New thread", new_forumthread_path(forum: @forum), class: "btn blue" %>

+

+ <%= link_to "New thread", new_forumthread_path(forum: @forum), class: "btn blue" %> + <% params[:id] = params[:id].split("-")[0] %> +

<% end %> <% if @forum.role_read && @forum.role_write && @forum.role_write < @forum.role_read %> @@ -51,4 +58,4 @@
<% end %> <%= paginate @threads %> -
\ No newline at end of file + diff --git a/app/views/forumthreads/index.html.erb b/app/views/forumthreads/index.html.erb new file mode 100644 index 0000000..d765cda --- /dev/null +++ b/app/views/forumthreads/index.html.erb @@ -0,0 +1,84 @@ +<%= link_to "Forums", forums_path %> → +<% if params.to_hash.slice("label", "title", "content", "author", "reply").size > 0 %> + <%= link_to "All Threads", forumthreads_path %> → Search Results +<% else %> + <%= "All Threads" %> +<% end %> +<% params_list = params.to_hash.slice("id", "query", "label", "title", "content", "author", "reply") %> +

+ <% if params[:id] %> + <% text = "forum '#{Forum.find(params[:id]).name}'" %> + <% else %> + <% text = "all threads" %> + <% end %> + <% if params_list.size > 0 %> + <%= title "Search results in #{text} (#{@threads.length})" %> + <% else %> + <% if params[:id] %> + <%= title "All threads in #{text}" %> + <% else %> + <%= title "All Threads" %> + <% end %> + <% end %> +
+ <%= link_to "Advanced Search", search_forumthreads_path(params_list), class: "btn right blue" %> + <% if params_list.size > 0 && params[:id] %> + <%= link_to "Show All Threads", forumthreads_path(params_list.except("id")), class: "btn right blue" %> + <% elsif params_list.size > 0 && !params[:id] %> + <%= link_to "Show All Threads", forumthreads_path, class: "btn right blue" %> + <% end %> + <% if params[:id] %> + <%= link_to "Go to Forum", forum_path(params[:id]), class: "btn right blue" %> + <% end %> +

+
+<%= form_tag({controller: "forumthreads", action: "search_redirect"}, method: :post, style: "margin:0px;height:40px") do %> + <%= text_field_tag "query", nil, placeholder: "Search...", style: "margin:0px;height:40px;width:300px" %> + <% params.each do |key, value| %> + <%= hidden_field_tag key, params[key] if params[key] && params[key] != params[:query] %> + <% end %> + <%= submit_tag "Go", class: "btn blue", style: "margin:0px;height:40px;width:40px" %> +<% end %> + +
+ <% counter = 0 %> + <% @threads.each do |thread| %> + <% counter += 1 %> +
+
+ <%= link_to(thread.author.avatar(64), thread.author, title: thread.author.ign) %> + <%= render partial: "users/username", locals: { user: thread.author } %> + <%= link_to thread do %> + <%= ago thread.created_at %> + <% end %> + + <%= link_to pluralize(thread.replies.count, "Reply"), thread %> + +
+
+
+
"> + <%= render partial: "labels/label", locals: {label: thread.label} %><%= link_to truncate(thread.title, length: 60, omission: " …"), forumthread_path(thread), title: thread.title %> +
+ <% if rpl = thread.replies.last %> + <%= rpl.author.name %> + <% + position = thread.replies.count - 1 + page = position / Kaminari.config.default_per_page + 1 + %> + <%= link_to "replied", forumthread_path(thread, page: page) + "#reply-#{rpl.id}" %> + <%= ago rpl.created_at %>. + <% else %> + No replies yet. + <% end %> +
+
+
+
+
+ <% end %> + <% if counter == 0 %> +

No results found

+ <% end %> + <%= paginate @threads %> +
diff --git a/app/views/forumthreads/search.html.erb b/app/views/forumthreads/search.html.erb new file mode 100644 index 0000000..a3c631f --- /dev/null +++ b/app/views/forumthreads/search.html.erb @@ -0,0 +1,56 @@ +<% title "Thread Search" %> +

Thread Search

+

Leave a field blank to ignore that search aspect.

+<% label = Label.where(name: params[:label]).first %> + + +<%= form_tag({controller: "forumthreads", action: "search_redirect"}, method: :post) do %> + <% + forums = [] + Forum.all.sort_by{ |f| f.forumgroup && f.forumgroup.position || 0 }.each do |f| + if current_user != nil && current_user.role_id > f.role_read_id.to_i || current_user == nil && f.role_read_id == nil + forums << ["#{f.forumgroup.name} → #{f.name}", f.id] if f.forumgroup + end + end + %> + <% label_list = Label.pluck(:name).insert(0, "Label").insert(1, "No Label") %> + + + + + + + + + + + + + + + + + + + + + + + + + +<% end %> + +
Forum<%= select_tag "id", options_for_select(["Search All Threads"] + forums, params[:id]) %>
Label + <%= select_tag "label", options_for_select(label_list, params[:label]), class: "auto-width" %> +
Title + <%= text_field_tag "title", params[:title], placeholder: "Search Titles" %> +
Content + <%= text_field_tag "content", params[:content], placeholder: "Search Contents" %> +
Author + <%= render partial: "md_editor_user", locals: {name: "author", content: params[:author]} %> +
Replies + <%= text_field_tag "reply", params[:reply], placeholder: "Search Replies" %> +
+ <%= submit_tag "Go", class: "btn blue", style: "width:50px" %> +
diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb index 95ab480..8df0fd0 100644 --- a/app/views/users/index.html.erb +++ b/app/views/users/index.html.erb @@ -1,14 +1,29 @@ +<%= form_tag({controller: "users", action: "search_redirect"}, method: :post, style: "margin:0px;height:40px") do %> + <%= text_field_tag "search", nil, placeholder: "Search for a user", style: "margin:0px;height:40px;width:300px" %> + <%= submit_tag "Go", class: "btn blue", style: "margin:0px;height:40px;width:40px" %> + <%= hidden_field_tag "role", params[:role] %> +<% end %> +

- <% if params[:role] && !params[:badge]%> - <%= title "All '#{params[:role]}' users" %> - <% elsif params[:badge] && !params[:role] %> - <%= title "All '#{params[:badge]}' users" %> - <% elsif params[:role] && params[:badge] %> - <%= title "All '#{params[:role]}' and '#{params[:badge]}' users" %> + <% + if params[:role] && !params[:badge] + text = "All '#{params[:role]}' users" + elsif params[:badge] && !params[:role] + text = "All '#{params[:badge]}' users" + elsif params[:role] && params[:badge] + text = "All '#{params[:role]}' and '#{params[:badge]}' users" + else + text = "All users" + end + text += " that contain '#{params[:search]}'" if params[:search] + %> + <%= title text %> + <% if params[:search] %> + (<%= @users.select {|u| u.name.downcase.include?(params[:search].downcase) || u.ign.downcase.include?(params[:search].downcase) }.size %>) <% else %> - <%= title "All Users" %> + (<%= @count %>) <% end %> - (<%= @count %>) +

<%= link_to "show all", users_path if params[:role] || params[:badge] %> diff --git a/config/routes.rb b/config/routes.rb index f79ab5b..4db3f30 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -27,14 +27,19 @@ Redstoner::Application.routes.draw do get 'lost_password' post 'reset_password' post 'suggestions' + post 'search_redirect' end end resources :forumgroups, path: '/forums/groups' - resources :forums, path: '/forums' resources :forumthreads, path: '/forums/threads' do resources :threadreplies, path: 'replies' + collection do + get 'search' + post 'search_redirect' + end end + resources :forums, path: '/forums' resources :tools do collection do diff --git a/db/migrate/20170522210610_add_search_indexes.rb b/db/migrate/20170522210610_add_search_indexes.rb new file mode 100644 index 0000000..2225d7b --- /dev/null +++ b/db/migrate/20170522210610_add_search_indexes.rb @@ -0,0 +1,8 @@ +class AddSearchIndexes < ActiveRecord::Migration + def change + add_index :forumthreads, [:title, :content], type: :fulltext + add_index :forumthreads, :title, type: :fulltext + add_index :forumthreads, :content, type: :fulltext + add_index :threadreplies, :content, type: :fulltext + end +end diff --git a/db/schema.rb b/db/schema.rb index 0a29b6b..aa35812 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170515200733) do +ActiveRecord::Schema.define(version: 20170522210610) do create_table "blogposts", force: :cascade do |t| t.string "title", limit: 191 @@ -65,6 +65,10 @@ ActiveRecord::Schema.define(version: 20170515200733) do t.integer "label_id", limit: 4 end + add_index "forumthreads", ["content"], name: "index_forumthreads_on_content", type: :fulltext + add_index "forumthreads", ["title", "content"], name: "index_forumthreads_on_title_and_content", type: :fulltext + add_index "forumthreads", ["title"], name: "index_forumthreads_on_title", type: :fulltext + create_table "info", force: :cascade do |t| t.string "title", limit: 191 t.text "content", limit: 65535 @@ -78,8 +82,8 @@ ActiveRecord::Schema.define(version: 20170515200733) do end create_table "register_tokens", force: :cascade do |t| - t.string "uuid", limit: 191, null: false - t.string "token", limit: 191, null: false + t.string "uuid", limit: 32, null: false + t.string "token", limit: 6, null: false t.string "email", limit: 191, null: false end @@ -116,6 +120,8 @@ ActiveRecord::Schema.define(version: 20170515200733) do t.datetime "updated_at" end + add_index "threadreplies", ["content"], name: "index_threadreplies_on_content", type: :fulltext + create_table "users", force: :cascade do |t| t.string "uuid", limit: 191, null: false t.string "name", limit: 191, null: false -- cgit v1.2.3