Added Searching Features

* Added Thread Search Feature

* Added User Search Feature

* Re-organized searching, added @mention support to author search
This commit is contained in:
MrYummy
2017-05-28 18:08:57 -04:00
parent 252dc5bdfa
commit 44392c1df3
20 changed files with 293 additions and 27 deletions

View File

@@ -75,4 +75,4 @@ class ApplicationController < ActionController::Base
!!(current_user && current_user.confirmed?)
end
end
end

View File

@@ -75,4 +75,4 @@ class BlogpostsController < ApplicationController
end
end
end
end

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -24,4 +24,4 @@ module MailerHelper
end
end
end
end
end

View File

@@ -52,4 +52,4 @@ module UsersHelper
end
end
end
end

View File

@@ -32,4 +32,4 @@ class Forum < ActiveRecord::Base
def to_param
[id, to_s.parameterize].join("-")
end
end
end

View File

@@ -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

View File

@@ -53,4 +53,4 @@ class Role < ActiveRecord::Base
Role.order(:value).select {|r| r >= from}.select {|r| r <= to}
end
end
end

View File

@@ -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

View File

@@ -0,0 +1,8 @@
<div class="md_editor">
<div class="field_container_user">
<% 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 %>
</div>
</div>

View File

@@ -1,5 +1,7 @@
<% title "Forums" %>
<%= link_to "All threads", forumthreads_path(params.except("controller", "action")), class: "btn blue right" %>
<div id="forum_groups">
<% @groups.each do |group| %>
<div class="item-group" id="group-<%= group.id %>">
@@ -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 %>
<% end %>

View File

@@ -1,8 +1,15 @@
<%= link_to @forum.group, forumgroup_path(@forum.group) %> → <%= @forum %>
<h1><%= title @forum %></h1>
<h1>
<%= title @forum %>
<% params[:id] = params[:id].split("-")[0] %>
<%= link_to "Search Threads", forumthreads_path(params.except("action", "controller")), class: "btn blue right" %>
</h1>
<% if @forum.can_write?(current_user) %>
<p><%= link_to "New thread", new_forumthread_path(forum: @forum), class: "btn blue" %></p>
<p>
<%= link_to "New thread", new_forumthread_path(forum: @forum), class: "btn blue" %>
<% params[:id] = params[:id].split("-")[0] %>
</p>
<% end %>
<% if @forum.role_read && @forum.role_write && @forum.role_write < @forum.role_read %>
@@ -51,4 +58,4 @@
</div>
<% end %>
<%= paginate @threads %>
</div>
</div>

View File

@@ -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") %>
<h1>
<% 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 %>
<br>
<%= 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 %>
</h1>
<br>
<%= 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 %>
</h1>
<div id="forum_groups">
<% counter = 0 %>
<% @threads.each do |thread| %>
<% counter += 1 %>
<div class="item-group with-avatar" id="thread-<%= thread.id %>">
<div class="header">
<%= 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 %>
<span class="comment-counter">
<%= link_to pluralize(thread.replies.count, "Reply"), thread %>
</span>
<div class="clear-right"></div>
</div>
<div class="items bold">
<div class="item <%= "#{"locked" if thread.locked}#{"sticky" if thread.sticky}" %>">
<%= render partial: "labels/label", locals: {label: thread.label} %><%= link_to truncate(thread.title, length: 60, omission: " …"), forumthread_path(thread), title: thread.title %>
<div class="item-info">
<% 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 %>
</div>
<div class="clear"></div>
</div>
</div>
</div>
<% end %>
<% if counter == 0 %>
<h3>No results found</h3>
<% end %>
<%= paginate @threads %>
</div>

View File

@@ -0,0 +1,56 @@
<% title "Thread Search" %>
<h1>Thread Search</h1>
<h3>Leave a field blank to ignore that search aspect.</h3>
<% label = Label.where(name: params[:label]).first %>
<table>
<tbody>
<%= 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") %>
<tr>
<td>Forum</td>
<td><%= select_tag "id", options_for_select(["Search All Threads"] + forums, params[:id]) %></td>
</tr>
<tr>
<td>Label</td>
<td>
<%= select_tag "label", options_for_select(label_list, params[:label]), class: "auto-width" %>
</td>
</tr>
<tr>
<td>Title</td>
<td>
<%= text_field_tag "title", params[:title], placeholder: "Search Titles" %>
</td>
</tr>
<tr>
<td>Content</td>
<td>
<%= text_field_tag "content", params[:content], placeholder: "Search Contents" %>
</td>
<tr>
<td>Author</td>
<td>
<%= render partial: "md_editor_user", locals: {name: "author", content: params[:author]} %>
</td>
</tr>
<td>Replies</td>
<td>
<%= text_field_tag "reply", params[:reply], placeholder: "Search Replies" %>
</td>
</tr>
<tr>
<td>
<%= submit_tag "Go", class: "btn blue", style: "width:50px" %>
</td>
</tr>
<% end %>
</tbody>
</table>

View File

@@ -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 %>
<h1>
<% 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 %>)
</h1>
<%= link_to "show all", users_path if params[:role] || params[:badge] %>

View File

@@ -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

View File

@@ -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

View File

@@ -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