From a4cc433ccbf2b71e274dc2cc37c0e441dcb6ae8d Mon Sep 17 00:00:00 2001
From: Kieran Hayes
Date: Wed, 16 Nov 2016 15:44:04 +0100
Subject: [PATCH 1/2] enable multi-value facets
---
app/controllers/redsun_search_controller.rb | 27 +++++---
app/helpers/redsun_search_helper.rb | 2 +-
app/views/redsun_search/_facet.html.erb | 77 +++++++++++----------
app/views/redsun_search/index.html.erb | 15 ++--
init.rb | 4 +-
5 files changed, 70 insertions(+), 55 deletions(-)
diff --git a/app/controllers/redsun_search_controller.rb b/app/controllers/redsun_search_controller.rb
index a7db017..6d0a446 100644
--- a/app/controllers/redsun_search_controller.rb
+++ b/app/controllers/redsun_search_controller.rb
@@ -90,20 +90,27 @@ def index
priority_id
tracker_id
status_id
- filetype
- category_id).each do |easy_facet|
- facet easy_facet, minimum_count: 1
- if params.key?(:search_form) && params[:search_form][easy_facet].present?
- with(easy_facet, params[:search_form][easy_facet])
- end
+ category_id
+ filetype).each do |easy_facet|
+ facet_filter = if params[:search_form].key?(easy_facet)
+ Rails.logger.info "#{easy_facet} is in facet_filter"
+ with(easy_facet).any_of(params[:search_form][easy_facet])
+ else
+ all_of {} # not necessary for searching, but object is needed to set exclude option
+ end
+ facet easy_facet, minimum_count: 1, exclude: facet_filter
+ #if params.key?(:search_form) && params[:search_form][easy_facet].present?
+ # with(easy_facet).any_of(params[:search_form][easy_facet])
+ #end
+
end
# class filter
class_filter = if params[:search_form].key?(:class_name)
- with(:class_name).any_of(params[:search_form][:class_name])
- else
- all_of {} # not necessary for searching, but object is needed to set exclude option
- end
+ with(:class_name).any_of(params[:search_form][:class_name])
+ else
+ all_of {} # not necessary for searching, but object is needed to set exclude option
+ end
facet :class_name, minimum_count: 1, exclude: class_filter # exclude option gives access to full list of items
%w(created_on updated_on).each do |date_facet|
diff --git a/app/helpers/redsun_search_helper.rb b/app/helpers/redsun_search_helper.rb
index e7d001c..60b3da5 100644
--- a/app/helpers/redsun_search_helper.rb
+++ b/app/helpers/redsun_search_helper.rb
@@ -6,7 +6,7 @@ def facet(name, results, attribute = nil, partial = 'facet')
def selected_attr_for(facet, val)
if params[:search_form] && params[:search_form][facet.to_sym]
- "selected" if params[:search_form][facet.to_sym] == val.to_s
+ "selected" if val.to_s.in?(params[:search_form][facet.to_sym])
end
end
diff --git a/app/views/redsun_search/_facet.html.erb b/app/views/redsun_search/_facet.html.erb
index f76ee8e..90baa65 100644
--- a/app/views/redsun_search/_facet.html.erb
+++ b/app/views/redsun_search/_facet.html.erb
@@ -1,42 +1,45 @@
<% if results.facet(facet).present? && results.facet(facet).rows && results.facet(facet).rows.count > 1 || params[:search_form][facet].present? %>
<%= t("redsun.facet.#{facet}")%>
-<% if search2_enabled? %>
-
- <% else %>
-
- <% results.facet(facet).rows.each do |result| %>
- -
- <% if (attribute.present? && result.instance.present?) %>
- <% label = result.instance.send(attribute.to_sym) %>
- <%= link_to((label.present? ? label : "unknown" ) + " (#{result.count})",
- url_for(:search_form => {facet => result.value}.reverse_merge(params[:search_form])))
- %>
-
+ <% params[:search_form][facet] ||= [] %>
+
+ <% if search2_enabled? %>
+
- <% end -%>
-
- <% end %>
+ <% end %>
+
+ <% else %>
+
+ <% results.facet(facet).rows.each do |result| %>
+ -
+ <% if (attribute.present? && result.instance.present?) %>
+ <% label = result.instance.send(attribute.to_sym) %>
+ <%= link_to((label.present? ? label : "unknown" ) + " (#{result.count})",
+ url_for(:search_form => { facet => (params[:search_form][facet] + [result.value]) }.reverse_merge(params[:search_form]))) %>
+ <% else %>
+ <%= link_to link_to "#{result.value} (#{result.count})",
+ url_for(:search_form => { facet => (params[:search_form][facet] + [result.value]) }.reverse_merge(params[:search_form]))%>
+ <% end %>
+ <% if params[:search_form][facet].present? && result.value.to_s.in?(params[:search_form][facet]) %>
+ <%= link_to t("redsun.facet.remove"),
+ url_for(search_form: { facet.to_sym => (params[:search_form][facet] - [result.value.to_s]) }.reverse_merge(params[:search_form]) ),
+ class: "remove" %>
+ <% end %>
+
+ <% end -%>
+
+ <% end %>
<% end -%>
\ No newline at end of file
diff --git a/app/views/redsun_search/index.html.erb b/app/views/redsun_search/index.html.erb
index 1759146..5dcc416 100644
--- a/app/views/redsun_search/index.html.erb
+++ b/app/views/redsun_search/index.html.erb
@@ -138,6 +138,11 @@
float: right;
padding-top: 2em;
}
+ #sidebar .redsun-select2 .select2-choices .select2-search-choice{
+ padding: 3px 5px 3px 18px;
+ margin: 3px 0 3px 5px;
+ }
+
<%= form_for('search_form', url: @redsun_path, html: { id: 'redsun-search-form', method: 'get' }) do |f| %>
@@ -165,7 +170,6 @@
<%=t('redsun.hints')%>
<% end -%>
-
<% if @search.results.present? %>
<%= render 'results' %>
<% else %>
@@ -192,11 +196,12 @@
$('#redsun-search-form .select2').select2();
$('.redsun-select2').select2({
width: '95%',
- allowClear: true
+ allowClear: true,
+ maximumSelectionSize: 100
}).on('select2-selecting', function(e){
- window.location.href = e.val;
- }).on('select2-clearing', function(e){
- window.location.href = $(e.target).data('cleared-url');
+ window.location.href = e.choice.element[0].dataset["facetUrl"];
+ }).on('select2-clearing select2-removed', function(e){
+ window.location.href = e.choice.element[0].dataset["facetUrl"];
})
}
})
diff --git a/init.rb b/init.rb
index 83a90ff..7454f3d 100644
--- a/init.rb
+++ b/init.rb
@@ -4,9 +4,9 @@
name 'Redmine Redsun Plugin'
author 'Kieran Hayes'
description 'This plugin utilizes the sunspot gem for search'
- version '2.0.0'
+ version '2.1.0'
url 'http://www.dkd.de'
- author_url 'http://www.dkd.de'
+ author_url 'https://www.dkd.de'
settings default: {
enable_solr_search_field: false,
From 672a99cf83af292844e8158e053fb6658d885217 Mon Sep 17 00:00:00 2001
From: Kieran Hayes
Date: Wed, 8 Feb 2017 16:23:04 +0100
Subject: [PATCH 2/2] Change facet behavior, try to maintain facets if search
term changes track news increase thumbnail size display solr url in settings
CSS tweaks
---
Gemfile | 4 +-
app/controllers/redsun_search_controller.rb | 74 +++++-----
.../redsun_search/_class_name_facet.html.erb | 26 ++--
app/views/redsun_search/_date_facet.html.erb | 2 +-
app/views/redsun_search/_facet.html.erb | 62 +++++----
app/views/redsun_search/_results.html.erb | 7 +-
app/views/redsun_search/index.html.erb | 129 ++++++++++++++----
.../results/_attachment_result.html.erb | 2 +-
.../results/_news_result.html.erb | 23 ++++
app/views/settings/_redsun_settings.html.erb | 9 +-
config/locales/de.yml | 5 +-
config/locales/en.yml | 5 +-
init.rb | 2 +
lib/redmine_redsun/attachment_patch.rb | 5 +-
lib/redmine_redsun/news_patch.rb | 80 +++++++++++
lib/tasks/redsun.rake | 59 +++++++-
16 files changed, 378 insertions(+), 116 deletions(-)
create mode 100644 app/views/redsun_search/results/_news_result.html.erb
create mode 100644 lib/redmine_redsun/news_patch.rb
diff --git a/Gemfile b/Gemfile
index fd64a31..f2f4735 100644
--- a/Gemfile
+++ b/Gemfile
@@ -5,6 +5,4 @@ group :development do
gem 'sunspot_solr', '2.2.0'
end
-group :development do
- gem 'progress_bar', '~> 1.0.3'
-end
+gem 'progress_bar', '~> 1.0.3'
diff --git a/app/controllers/redsun_search_controller.rb b/app/controllers/redsun_search_controller.rb
index 6d0a446..9544995 100644
--- a/app/controllers/redsun_search_controller.rb
+++ b/app/controllers/redsun_search_controller.rb
@@ -22,7 +22,7 @@ def index
params[:search_form][:project_id] = @project.id
projects = @project.self_and_descendants.all
allowed_projects << projects.collect { |p| p.id if User.current.allowed_to?(:view_project, p) }.compact
- allowed_issues << projects.collect { |project| project .id if User.current.allowed_to?(:view_issues, project) }.compact
+ allowed_issues << projects.collect { |project| project.id if User.current.allowed_to?(:view_issues, project) }.compact
allowed_wikis << projects.collect { |project| project.id if User.current.allowed_to?(:view_wiki_pages, @project) }.compact
elsif search_scope == 'my_projects'
projects = User.current.memberships.collect(&:project).compact.uniq
@@ -41,9 +41,10 @@ def index
allowed_issues = allowed_issues.push(0)
allowed_wikis = allowed_wikis.push(0)
- @search = Sunspot.search([Project, Issue, WikiPage, Journal, Attachment]) do
+ @search = Sunspot.search([Project, Issue, WikiPage, Journal, Attachment, News]) do
fulltext searchstring do
highlight :description
+ highlight :summary
highlight :comments
highlight :subject
highlight :wiki_content
@@ -60,6 +61,7 @@ def index
with(:project_id).any_of allowed_issues.flatten
with(:is_private, false)
end
+
all_of do
with :class, WikiPage
with(:project_id).any_of allowed_wikis.flatten
@@ -81,6 +83,11 @@ def index
with :class, Attachment
with(:project_id).any_of allowed_projects.flatten
end
+
+ all_of do
+ with :class, News
+ with(:project_id).any_of allowed_projects.flatten
+ end
end
@@ -91,28 +98,24 @@ def index
tracker_id
status_id
category_id
- filetype).each do |easy_facet|
+ filetype
+ class_name).each do |easy_facet|
facet_filter = if params[:search_form].key?(easy_facet)
- Rails.logger.info "#{easy_facet} is in facet_filter"
with(easy_facet).any_of(params[:search_form][easy_facet])
else
all_of {} # not necessary for searching, but object is needed to set exclude option
end
- facet easy_facet, minimum_count: 1, exclude: facet_filter
- #if params.key?(:search_form) && params[:search_form][easy_facet].present?
- # with(easy_facet).any_of(params[:search_form][easy_facet])
- #end
-
+ facet easy_facet, minimum_count: 0, exclude: facet_filter
end
-
- # class filter
- class_filter = if params[:search_form].key?(:class_name)
- with(:class_name).any_of(params[:search_form][:class_name])
- else
- all_of {} # not necessary for searching, but object is needed to set exclude option
- end
- facet :class_name, minimum_count: 1, exclude: class_filter # exclude option gives access to full list of items
-
+
+ # Class
+ class_facet_filter = if params[:search_form].key?(:class_name)
+ with(:class_name).any_of(params[:search_form][:class_name])
+ else
+ all_of {} # not necessary for searching, but object is needed to set exclude option
+ end
+ facet :class_name, minimum_count: 0, exclude: class_facet_filter
+
%w(created_on updated_on).each do |date_facet|
if params[:search_form].present? && params[:search_form][date_facet].present?
date_range = date_conditions.collect { |c| c[:date] if (c[:name].to_s == params[:search_form][date_facet.to_sym]) }.compact.first
@@ -120,10 +123,6 @@ def index
end
end
- if params.key?(:search_form) && params[:search_form][:active].present?
- with(:active, (params[:search_form][:active] == 'true' ? true : false))
- end
-
# Pagination
paginate(page: params[:page], per_page: 15)
@@ -147,9 +146,10 @@ def index
order_by(sort_field.to_sym, sort_order.downcase.to_sym)
order_by(:score, :desc)
- spellcheck :count => 3
+ spellcheck(count: 3)
end
@searchstring = searchstring
+ redirect_if_neccessary
rescue Errno::ECONNREFUSED
render 'connection_refused'
@@ -161,22 +161,9 @@ def index
def set_search_form
params[:search_form] = {} unless params[:search_form].present?
- # Reset facets if search button is pressed
- if params[:commit].present?
- [:author_id,
- :status_id,
- :tracker_id,
- :priority_id,
- :created_on,
- :updated_on,
- :class_name,
- :active,
- :project_name].each do |facet|
- params[:search_form].delete(facet) if params[:search_form][facet].present?
- end
- end
@scope = params[:search_form][:scope] || 'all_projects'
params[:search_form][:class_name] ||= []
+ params[:search_form][:class_name] = [] if params[:search_form][:class_name].blank?
@sort_field = sort_field
@sort_order = sort_order
@scope_selector = [[l(:label_my_projects), 'my_projects'], [l(:label_project_all), 'all_projects']]
@@ -195,6 +182,19 @@ def date_conditions
end
private
+
+ def redirect_if_neccessary
+ # No need to redirect
+ return true if @search.total > 0
+ # Class name was select but no result was found
+ if params[:search_form].key?(:class_name) && params[:search_form][:class_name].any? && @search.facet(:class_name).rows.map(&:count).count > 0
+ flash_message = I18n.translate("redsun.redirect_for_missing_results",
+ class_name: I18n.translate("redsun.#{params[:search_form][:class_name].try(:first).try(:downcase)}") )
+ redirect_to redsun_search_url(search_form: params[:search_form].except(:class_name)), notice: flash_message
+ else
+ true
+ end
+ end
def search_scope
params[:search_form][:scope] || 'all_projects'
diff --git a/app/views/redsun_search/_class_name_facet.html.erb b/app/views/redsun_search/_class_name_facet.html.erb
index 8012e86..28fc02f 100644
--- a/app/views/redsun_search/_class_name_facet.html.erb
+++ b/app/views/redsun_search/_class_name_facet.html.erb
@@ -1,12 +1,15 @@
-<% if results.facet(facet).present? && results.facet(facet).rows && results.facet(facet).rows.count >= 1 %>
-
-
- -
- <%= link_to t("redsun.facet.reset_object_facet"),
+
+
+ -
+ <% count_all = @search.facet(:class_name).rows.map(&:count).sum %>
+ <% if count_all > 0 %>
+ <%= link_to t("redsun.facet.reset_object_facet", count: number_with_delimiter(count_all)),
url_for(search_form: {facet.to_sym => ""}.reverse_merge(params[:search_form]) ),
class: params[:search_form][facet.to_sym].blank? ? "selected" : "" %>
-
- <% results.facet(facet.to_sym).rows.each do |result| %>
+ <% end %>
+
+ <% results.facet(facet.to_sym).rows.each do |result| %>
+ <% if result.count > 0 || facet.in?(params[:search_form][:class_name]) %>
-
<% if @project.present?
url = redsun_project_search_path(@project, search_form: { class_name: [result.value] }.reverse_merge(params[:search_form]))
@@ -17,8 +20,7 @@
url, class: result.value.in?(params[:search_form][:class_name]) ? 'selected' : ''
%>
- <% end -%>
-
-
-
-<% end -%>
+ <% end %>
+ <% end %>
+
+
diff --git a/app/views/redsun_search/_date_facet.html.erb b/app/views/redsun_search/_date_facet.html.erb
index 20c2d25..00ed156 100644
--- a/app/views/redsun_search/_date_facet.html.erb
+++ b/app/views/redsun_search/_date_facet.html.erb
@@ -1,7 +1,7 @@
<% if results.facet(facet).present? && results.facet(facet).rows && results.facet(facet).rows.count > 1 || params[:search_form][facet].present? %>
<%=t "redsun.facet.#{facet.to_sym}"%>
<% if search2_enabled? %>
-
\ No newline at end of file
+
+
+
+ <%= Sunspot.try(:config).try(:solr).try(:url) %>
+
+
diff --git a/config/locales/de.yml b/config/locales/de.yml
index 5ce6dd4..c875412 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -16,6 +16,7 @@ de:
page_gap: '…'
previous_label: "← Vorige"
redsun:
+ redirect_for_missing_results: "Kein Resultat gefunden. Die Facette %{class_name} wurde deshalb entfernt."
placeholder:
project_name: "Wähle ein Projekt"
author_id: "Wähle einen Autor"
@@ -31,11 +32,13 @@ de:
settings:
enable_solr_search_field: "Solr Suche aktivieren"
enable_select2_plugin: "Select2 Plugin aktivieren"
+ solr_url: "Solr URL"
spellcheck_question: "Meintest du vielleicht?"
journal: "Kommentar"
issue: "Ticket"
wiki_page: "Wikiseite"
project: "Projekt"
+ news: "Nachrichten"
search_button: "Suchen!"
header: "Solr-Suche"
searchstring: "Suchbegriffe"
@@ -53,7 +56,7 @@ de:
wikipage: "Wikiseiten"
project: "Projekte"
facet:
- reset_object_facet: "Alle Objekte anzeigen"
+ reset_object_facet: "Alle Objekte (%{count})"
remove: "- entfernen"
created_on: "Erstellungszeitraum"
updated_on: "Aktualisierungdatum"
diff --git a/config/locales/en.yml b/config/locales/en.yml
index bc8102f..00c2e1d 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -21,6 +21,8 @@ en:
settings:
enable_solr_search_field: "Enable Solr search"
enable_select2_plugin: "Use Select2 Plugin"
+ solr_url: "Solr URL"
+ redirect_for_missing_results: "No results found, so the facet %{class_name} has been removed."
spellcheck_question: "Did you mean?"
inactive: "Inactive"
records: "Records"
@@ -34,6 +36,7 @@ en:
attachment: "Files"
wiki_page: "Wiki Pages"
project: "Projects"
+ news: "News"
search_button: "Search!"
hints: "Use * as wildcard in order to find more results."
placeholder:
@@ -56,7 +59,7 @@ en:
"false": "No"
"true": "Yes"
facet:
- reset_object_facet: "Show all objects"
+ reset_object_facet: "All objects (%{count})"
remove: "- remove"
created_on: "Created On"
author_id: "Author"
diff --git a/init.rb b/init.rb
index 7454f3d..65e3ee7 100644
--- a/init.rb
+++ b/init.rb
@@ -27,6 +27,8 @@
WikiPage.send(:include, RedmineRedsun::WikiPagePatch) unless WikiPage.included_modules.include? RedmineRedsun::WikiPagePatch
Journal.send(:include, RedmineRedsun::JournalPatch) unless Journal.included_modules.include? RedmineRedsun::JournalPatch
Attachment.send(:include, RedmineRedsun::AttachmentPatch) unless Attachment.included_modules.include? RedmineRedsun::AttachmentPatch
+ News.send(:include, RedmineRedsun::NewsPatch) unless News.included_modules.include? RedmineRedsun::NewsPatch
+
unless ActiveSupport::TestCase.included_modules.include? RedmineRedsun::ActiveSupport::TestCasePatch
ActiveSupport::TestCase.send(:include, RedmineRedsun::ActiveSupport::TestCasePatch)
end
diff --git a/lib/redmine_redsun/attachment_patch.rb b/lib/redmine_redsun/attachment_patch.rb
index e1f3f8d..584eb9c 100644
--- a/lib/redmine_redsun/attachment_patch.rb
+++ b/lib/redmine_redsun/attachment_patch.rb
@@ -2,7 +2,7 @@
# :nodoc:
module RedmineRedsun
- # Patches Redmine's Issue dynamically.
+ # Patches Redmine's attachments dynamically.
module AttachmentPatch
def self.included(base) # :nodoc:
base.extend ClassMethods
@@ -49,6 +49,9 @@ def self.included(base) # :nodoc:
# Created at
time :created_on, trie: true
+ # Name of Project
+ string :project_name, stored: true
+
end
end
end
diff --git a/lib/redmine_redsun/news_patch.rb b/lib/redmine_redsun/news_patch.rb
new file mode 100644
index 0000000..b565212
--- /dev/null
+++ b/lib/redmine_redsun/news_patch.rb
@@ -0,0 +1,80 @@
+require_dependency 'news'
+
+# :nodoc:
+module RedmineRedsun
+ # Patches Redmine's News dynamically.
+ module NewsPatch
+ def self.included(base) # :nodoc:
+ base.extend ClassMethods
+ base.send(:include, InstanceMethods)
+
+ # Same as typing in the class
+ base.class_eval do
+ unloadable # Send unloadable so it will not be unloaded in development
+
+ searchable do
+ # Class Name
+ string :class_name, stored: true
+
+ # Title
+ text :title, stored: true
+
+ # News ID
+ integer :id
+
+ # Creator
+ integer :author_id, references: User
+
+ # Description
+ text :description, stored: true
+
+ # Summary
+ text :summary, stored: true
+
+ # Project ID
+ integer :project_id
+
+ # Created at
+ time :created_on, trie: true
+
+ # Name of Project
+ string :project_name, stored: true
+
+ end
+ end
+ end
+ # :nodoc:
+ module ClassMethods
+ end
+ # :nodoc:
+ module InstanceMethods
+ SORT_FIELDS = %w(updated_on created_on score)
+ SORT_ORDER = [%w(ASC label_ascending), %w(DESC label_descending)]
+
+ def class_name
+ self.class.name
+ end
+
+ def project_name
+ project.name if project
+ end
+
+ def project_id
+ project.id if project
+ end
+
+ def active?
+ return false if project.nil?
+ project.active?
+ end
+
+ def description_for_search
+ fields = []
+ fields << summary if summary.present?
+ fields << description if description.present?
+ fields.compact.join(' ')
+ end
+
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/tasks/redsun.rake b/lib/tasks/redsun.rake
index 257df79..fe1d292 100644
--- a/lib/tasks/redsun.rake
+++ b/lib/tasks/redsun.rake
@@ -1,20 +1,71 @@
+#require 'progress_bar'
+
namespace :redsun do
desc 'Reindex specific Projects, set PROJECTS=123,234'
task reindex: :environment do
fail 'Use PROJECTS=123,234 to index specific projects' if ENV['PROJECTS'].nil?
projects = ENV['PROJECTS'].split(',').map(&:strip)
projects.each do |p|
- reindex(Project.find(p))
+ begin
+ project = Project.find(p)
+ puts "\n\nIndexing #{project.name}"
+ reindex(project)
+ rescue ActiveRecord::RecordNotFound
+ puts "#{p} could not be found. Did you mispell it?\n\n"
+ end
end
end
+
+ desc 'Opimize Solr index for suggestions'
+ task optimize_index: :environment do
+ Sunspot.optimize
+ end
+
end
def reindex(project)
project.index!
- project.issues.map do |issue|
+
+ # Issues
+ puts "Indexing issues ..."
+ issues = project.issues
+ issues_bar = ProgressBar.new(issues.count)
+ issues.map do |issue|
issue.index!
issue.journals.map &:index!
+ issues_bar.increment!
+ end
+
+ # Wiki
+ puts "\nIndexing wiki ..."
+ wikipages = project.try(:wiki).try(:pages)
+ if wikipages && wikipages.any?
+ wikipages_bar = ProgressBar.new(wikipages.count)
+ wikipages.map do |wikipage|
+ wikipage.index!
+ wikipages_bar.increment!
+ end
+ end
+
+ # News
+ puts "\nIndexing news ..."
+ news_entries = project.news
+ if news_entries && news_entries.any?
+ news_entries_bar = ProgressBar.new(news_entries.count)
+ news_entries.map do |news_entry|
+ news_entry.index!
+ news_entries_bar.increment!
+ end
+ end
+
+ # Attachments
+ puts "\nIndexing attachments"
+ attachments = project.try(:attachments)
+ if attachments && attachments.any?
+ attachments_bar = ProgressBar.new(attachments.count)
+ project.attachments.map do |attachment|
+ attachment.index!
+ attachments_bar.increment!
+ end
end
- project.wiki.pages.map &:index!
- project.attachments.map &:index!
end