# HG changeset patch # User luisf # Date 1320429035 0 # Node ID b7ac21913927627e527e3f6f1c6098a6b270c9ce # Parent 3f87a3b61d9ca58294f15165004fd97b2b3271a6 new files for project filtering and search (addresses also Feature #68); projects listing interface changes. diff -r 3f87a3b61d9c -r b7ac21913927 vendor/plugins/redmine_tags/app/views/projects/_filtered_projects.rhtml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/redmine_tags/app/views/projects/_filtered_projects.rhtml Fri Nov 04 17:50:35 2011 +0000 @@ -0,0 +1,19 @@ +<% if @featured_projects && @featured_projects.any? %> + +
+ <%= render_project_hierarchy_with_filtering(@projects, @custom_fields, @question) %> +
+ +
+ + +
+ +<% else %> + + <%= render_project_hierarchy_with_filtering(@projects, @custom_fields, @question) %> + +<% end %> + diff -r 3f87a3b61d9c -r b7ac21913927 vendor/plugins/redmine_tags/app/views/projects/index.rhtml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/redmine_tags/app/views/projects/index.rhtml Fri Nov 04 17:50:35 2011 +0000 @@ -0,0 +1,67 @@ +<% content_for :header_tags do %> + <%= auto_discovery_link_tag(:atom, {:action => 'index', :format => 'atom', :key => User.current.rss_key}) %> +<% end %> + +
+ <%= link_to l(:label_overall_activity), { :controller => 'activities', :action => 'index' }%> + <%= '| ' + link_to(l(:label_project_new), {:controller => 'projects', :action => 'new'}, :class => 'icon icon-add') if User.current.allowed_to?(:add_project, nil, :global => true) %> +
+ +<% form_tag('/projects', :method => :get, :id => :project_filtering) do %> +
+ <%= l(:label_filter_plural) %> +
+

+ <%= label_tag 'q', l('project_filtering_q_label') %> + <%= text_field_tag 'q', @question, :size => 30, :id => 'search-input' %> +

+ <%= render :partial => 'custom_field', :collection => @custom_fields_used_for_project_filtering %> +

<%= submit_tag( l(:button_send), :id => 'filter_button') -%>

+
+
+<% end %> + + +<%= javascript_tag "Field.focus('search-input');" %> +<%= javascript_tag "$('filter_button').hide();" %> +<%= observe_form( :project_filtering, + :frequency => 0.5, + :url => { :controller => :projects, :action => :index, :format => :js }, + :method => :get + ) +%> + +
+ <%= render :partial => 'filtered_projects' %> +
+ + +
+ +<% if User.current.logged? %> +

+<%= l(:label_my_projects) %> +

+<% end %> + + +END + +<% if @user_projects %> + <%= render_my_project_hierarchy(@user_projects)%> +<% end %> + +

+ <%= l("label_project_all") %> +

+ +<%= render_project_table(@projects) %> + +

<%= pagination_links_full @project_pages, @project_count %>

+ + +<% other_formats_links do |f| %> + <%= f.link_to 'Atom', :url => {:key => User.current.rss_key} %> +<% end %> + +<% html_title(l(:label_project_plural)) -%> diff -r 3f87a3b61d9c -r b7ac21913927 vendor/plugins/redmine_tags/init.rb --- a/vendor/plugins/redmine_tags/init.rb Tue Nov 01 15:20:40 2011 +0000 +++ b/vendor/plugins/redmine_tags/init.rb Fri Nov 04 17:50:35 2011 +0000 @@ -41,9 +41,16 @@ require 'dispatcher' Dispatcher.to_prepare :redmine_tags do + + require_dependency 'redmine_project_filtering' + unless Project.included_modules.include?(RedmineTags::Patches::ProjectPatch) Project.send(:include, RedmineTags::Patches::ProjectPatch) end + + unless ProjectsHelper.included_modules.include?(RedmineTags::Patches::ProjectsHelperPatch) + ProjectsHelper.send(:include, RedmineTags::Patches::ProjectsHelperPatch) + end unless Issue.included_modules.include?(RedmineTags::Patches::IssuePatch) Issue.send(:include, RedmineTags::Patches::IssuePatch) diff -r 3f87a3b61d9c -r b7ac21913927 vendor/plugins/redmine_tags/lib/redmine_project_filtering.rb --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/redmine_tags/lib/redmine_project_filtering.rb Fri Nov 04 17:50:35 2011 +0000 @@ -0,0 +1,16 @@ +module RedmineProjectFiltering + + # transforms a question and a list of custom fields into something that Project.search can process + def self.calculate_tokens(question, custom_fields=nil) + list = [] + list << custom_fields.values if custom_fields.present? + list << question if question.present? + + tokens = list.join(' ').scan(%r{((\s|^)"[\s\w]+"(\s|$)|\S+)}) + tokens = tokens.collect{ |m| m.first.gsub(%r{(^\s*"\s*|\s*"\s*$)}, '') } + + # tokens must be at least 2 characters long + tokens.select {|w| w.length > 1 } + end + +end diff -r 3f87a3b61d9c -r b7ac21913927 vendor/plugins/redmine_tags/lib/redmine_tags/patches/project_patch.rb --- a/vendor/plugins/redmine_tags/lib/redmine_tags/patches/project_patch.rb Tue Nov 01 15:20:40 2011 +0000 +++ b/vendor/plugins/redmine_tags/lib/redmine_tags/patches/project_patch.rb Fri Nov 04 17:50:35 2011 +0000 @@ -48,6 +48,17 @@ end module ClassMethods + + + def search_by_question(question) + if question.length > 1 + search(RedmineProjectFiltering.calculate_tokens(question), nil, :all_words => true).first.sort_by(&:lft) + else + all(:order => 'lft') + end + end + + # Returns available issue tags # === Parameters # * options = (optional) Options hash of diff -r 3f87a3b61d9c -r b7ac21913927 vendor/plugins/redmine_tags/lib/redmine_tags/patches/projects_controller_patch.rb --- a/vendor/plugins/redmine_tags/lib/redmine_tags/patches/projects_controller_patch.rb Tue Nov 01 15:20:40 2011 +0000 +++ b/vendor/plugins/redmine_tags/lib/redmine_tags/patches/projects_controller_patch.rb Fri Nov 04 17:50:35 2011 +0000 @@ -8,6 +8,7 @@ base.class_eval do unloadable before_filter :add_tags_to_project, :only => [:save, :update] + before_filter :filter_projects, :only => :index end end @@ -20,8 +21,60 @@ unless (old_tags == new_tags) @project.tag_list = new_tags end - end + end end + + + def calculate_project_filtering_settings + @project_filtering_settings = Setting[:plugin_redmine_project_filtering] + end + + def filter_projects + + logger.error { "FILTRA PA!" } + + respond_to do |format| + format.any(:html, :xml) { + calculate_filtered_projects + } + format.js { + calculate_filtered_projects + render :update do |page| + page.replace_html 'projects', :partial => 'filtered_projects' + end + } + format.atom { + projects = Project.visible.find(:all, :order => 'created_on DESC', + :limit => Setting.feeds_limit.to_i) + render_feed(projects, :title => "#{Setting.app_title}: #{l(:label_project_latest)}") + } + end + end + + private + + def calculate_filtered_projects + + @question = (params[:q] || "").strip + @custom_fields = params[:custom_fields] || {} + + @projects = Project.visible + + unless @custom_fields.empty? + @projects = @projects.with_custom_values(params[:custom_fields]) + end + + @featured_projects = @projects.featured if Project.respond_to? :featured + + @projects = @projects.search_by_question(@question) + debugger + @featured_projects = @featured_projects.search_by_question(@question) if @featured_projects + + end + + + + end end end diff -r 3f87a3b61d9c -r b7ac21913927 vendor/plugins/redmine_tags/lib/redmine_tags/patches/projects_helper_patch.rb --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/redmine_tags/lib/redmine_tags/patches/projects_helper_patch.rb Fri Nov 04 17:50:35 2011 +0000 @@ -0,0 +1,93 @@ +module RedmineTags + module Patches + module ProjectsHelperPatch + + def self.included(base) # :nodoc: + base.send(:include, InstanceMethods) + base.class_eval do + unloadable + end + end + + module InstanceMethods + # Renders a tree of projects as a nested set of unordered lists + # The given collection may be a subset of the whole project tree + # (eg. some intermediate nodes are private and can not be seen) + def render_project_hierarchy_with_filtering(projects,custom_fields,question) + s = [] + if projects.any? + tokens = RedmineProjectFiltering.calculate_tokens(question, custom_fields) + debugger + + + ancestors = [] + original_project = @project + projects.each do |project| + # set the project environment to please macros. + @project = project + if (ancestors.empty? || project.is_descendant_of?(ancestors.last)) + s << "" + end + end + classes = (ancestors.empty? ? 'root' : 'child') + s << "
  • " + + link_to( highlight_tokens(project.name, tokens), + {:controller => 'projects', :action => 'show', :id => project}, + :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}" + ) + s << "
      " + + # CustomField.usable_for_project_filtering.each do |field| + # value_model = project.custom_value_for(field.id) + # value = value_model.present? ? value_model.value : nil + # s << "
    • #{field.name.humanize}: #{highlight_tokens(value, tokens)}
    • " if value.present? + # end + + s << "
    " + s << "
    " + unless project.description.blank? + s << "
    " + s << "#{ t(:field_description) }:" + s << highlight_tokens(textilizable(project.short_description, :project => project), tokens) + s << "\n
    " + end + s << "
    " + ancestors << project + end + ancestors.size.times{ s << "
  • " } + @project = original_project + end + s.join "\n" + end + + private + + # copied from search_helper. This one doesn't escape html or limit the text length + def highlight_tokens(text, tokens) + return text unless text && tokens && !tokens.empty? + re_tokens = tokens.collect {|t| Regexp.escape(t)} + regexp = Regexp.new "(#{re_tokens.join('|')})", Regexp::IGNORECASE + result = '' + text.split(regexp).each_with_index do |words, i| + words = words.mb_chars + if i.even? + result << words + else + t = (tokens.index(words.downcase) || 0) % 4 + result << content_tag('span', words, :class => "highlight token-#{t}") + end + end + result + end + + end + end + end +end +