diff --git a/Gemfile b/Gemfile
index 9a69086..e69de29 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,3 +0,0 @@
-group :test do
- gem 'webrat'
-end
diff --git a/Rakefile b/Rakefile
old mode 100755
new mode 100644
diff --git a/app/controllers/questions_controller.rb b/app/controllers/issuequestions_controller.rb
similarity index 55%
rename from app/controllers/questions_controller.rb
rename to app/controllers/issuequestions_controller.rb
index 16e9c4e..0ec70ce 100644
--- a/app/controllers/questions_controller.rb
+++ b/app/controllers/issuequestions_controller.rb
@@ -1,4 +1,4 @@
-class QuestionsController < ApplicationController
+class IssuequestionsController < ApplicationController
unloadable
layout 'base'
@@ -12,18 +12,31 @@ def user_issue_filter
new_filter_for_questions_assigned_to(params[:user_id])
redirect_to :controller => 'issues', :action => 'index', :project_id => params[:project]
end
+
+ def hide
+ @question = Question.find(params[:id])
+ @question.hidden = true
+ if @question.save
+ redirect_to :back
+ end
+
+ end
def autocomplete_for_user_login
- if params[:user]
- @users = User.active.all(:conditions => ["LOWER(login) LIKE :user OR LOWER(firstname) LIKE :user OR LOWER(lastname) LIKE :user", {:user => params[:user]+"%" }],
- :limit => 10,
- :order => 'login ASC')
+ if params[:issue_id] && Setting.plugin_question_plugin[:only_members] == 1
+ @issue = Issue.find(params[:issue_id])
+ base = @issue.project.users
+ else
+ base = User
+ end
+ q = (params[:q] || params[:term] || params[:user]).to_s.strip.downcase
+ if q.present?
+ @users = base.active.where(["LOWER(login) LIKE :user OR LOWER(firstname) LIKE :user OR LOWER(lastname) LIKE :user", {:user => q + "%" }]).
+ limit(10).
+ order('login ASC')
end
@users ||=[]
- if params[:issue_id]
- @issue = Issue.find_by_id(params[:issue_id])
- end
render :layout => false
end
@@ -32,7 +45,7 @@ def autocomplete_for_user_login
def new_filter_for_questions_assigned_to(user_id)
@project = Project.find(params[:project]) unless params[:project].nil?
- @query = Query.new(:name => "_",
+ @query = (ActiveSupport::Dependencies::search_for_file('issue_query') ? IssueQuery : Query).new(:name => "_",
:filters => {'status_id' => {:operator => '*', :values => [""]}}
)
@query.project = @project unless params[:project].nil?
diff --git a/app/models/question.rb b/app/models/question.rb
index 1834499..54ddf23 100644
--- a/app/models/question.rb
+++ b/app/models/question.rb
@@ -5,18 +5,22 @@ class Question < ActiveRecord::Base
belongs_to :assigned_to, :class_name => "User", :foreign_key => "assigned_to_id"
belongs_to :author, :class_name => "User", :foreign_key => "author_id"
belongs_to :issue
- belongs_to :journal
+ belongs_to :journal, :class_name => "Journal", :foreign_key => "journal_id"
validates_presence_of :author
validates_presence_of :issue
validates_presence_of :journal
+
+ attr_protected :journal
+
+ scope :opened, lambda { where(:opened => true) }
+ scope :not_hidden, lambda { where(:hidden => false) }
- named_scope :opened, :conditions => {:opened => true}
- named_scope :for_user, lambda {|user|
- { :conditions => {:assigned_to_id => user.id }}
+ scope :for_user, lambda {|user|
+ where(:assigned_to_id => user.id)
}
- named_scope :by_user, lambda {|user|
- { :conditions => {:author_id => user.id }}
+ scope :by_user, lambda {|user|
+ where(:author_id => user.id)
}
delegate :notes, :to => :journal, :allow_nil => true
@@ -29,22 +33,20 @@ def close!(closing_journal=nil)
if self.opened
self.opened = false
if self.save && closing_journal
- QuestionMailer.deliver_answered_question(self, closing_journal)
+ QuestionMailer.answered_question(self, closing_journal).deliver
end
end
end
-
+
# TODO: refactor to named_scope
def self.count_of_open_for_user(user)
- Question.count(:conditions => {:assigned_to_id => user.id, :opened => true})
+ Question.where(:assigned_to_id => user.id, :opened => true).count
end
# TODO: refactor to named_scope
def self.count_of_open_for_user_on_project(user, project)
- Question.count(:conditions => ["#{Question.table_name}.assigned_to_id = ? AND #{Project.table_name}.id = ? AND #{Question.table_name}.opened = ?",
- user.id,
- project.id,
- true],
- :include => [:issue => [:project]])
+ Question.where(["(#{Question.table_name}.assigned_to_id = ?) AND #{project.project_condition(Setting.display_subprojects_issues?)} AND (#{Question.table_name}.opened = ?)",
+ user.id, true]).
+ joins(:issue => :project).preload(:project).count
end
end
diff --git a/app/models/question_mailer.rb b/app/models/question_mailer.rb
index 3ce6175..b5496ff 100644
--- a/app/models/question_mailer.rb
+++ b/app/models/question_mailer.rb
@@ -1,69 +1,90 @@
+# encoding: utf-8
class QuestionMailer < Mailer
unloadable
def asked_question(journal)
question = journal.question
- subject "[Question #{question.issue.project.name} ##{question.issue.id}] #{question.issue.subject}"
- recipients question.assigned_to.mail unless question.assigned_to.nil?
- @from = "#{question.author.name} (Redmine) <#{Setting.mail_from}>" unless question.author.nil?
+
+ if Setting.plugin_question_plugin[:obfuscate_author] == "1"
+ # Obfuscate author infos
+ from = "#{Setting.app_title} <#{Setting.mail_from}>"
+ else
+ # Clear author infos
+ from = question.author ? "#{question.author.name} (#{l(:field_system_name)} - #{I18n.t(:text_question)}) <#{Setting.mail_from}>" : nil
+ end
+ to = question.assigned_to ? question.assigned_to.mail : nil
+ subject = "[#{question.issue.project.name} ##{question.issue.id}] #{question.issue.subject}"
+
+ @from = from
+ @question = question
+ @issue = question.issue
+ @journal = journal
+ @users = [question.assigned_to]
+ @issue_url = url_for(:controller => 'issues', :action => 'show', :id => question.issue)
+ @users = [question.assigned_to]
+
redmine_headers 'Issue-Id' => question.issue.id
redmine_headers 'Question-Asked' => question.author.login if question.author.present?
redmine_headers 'Question-Assigned-To' => question.assigned_to.login if question.assigned_to.present?
- body({
- :question => question,
- :issue => question.issue,
- :journal => journal,
- :issue_url => url_for(:controller => 'issues', :action => 'show', :id => question.issue)
- })
-
- RAILS_DEFAULT_LOGGER.debug 'Sending QuestionMailer#asked_question'
- render_multipart('asked_question', body)
+ Rails.logger.debug 'Sending QuestionMailer#asked_question'
+ mail(:from => from, :to => to, :subject => subject)
end
def answered_question(question, closing_journal)
- subject "[Answered #{question.issue.project.name} ##{question.issue.id}] #{question.issue.subject}"
+ if Setting.plugin_question_plugin[:obfuscate_author] == "1"
+ # Obfuscate author infos
+ from = "#{Setting.app_title} <#{Setting.mail_from}>"
+ @from = from
+ else
+ # Clear author infos
+ from = question.assigned_to ? "#{question.assigned_to.name} (#{l(:field_system_name)} - #{I18n.t(:text_answer)}) <#{Setting.mail_from}>" : nil
+ @from = "#{question.assigned_to.name} (#{l(:field_system_name)} - #{I18n.t(:text_answer)}) <#{Setting.mail_from}>" unless question.assigned_to.nil?
+ end
+ to = question.author ? question.author.mail : nil
+ subject = "[#{question.issue.project.name} ##{question.issue.id}] #{question.issue.subject}"
+
+ @question = question
+ @issue = question.issue
+ @journal = closing_journal
+ @users = [question.author]
+ @issue_url = url_for(:controller => 'issues', :action => 'show', :id => question.issue)
+ @users = [question.author]
- recipients question.author.mail unless question.author.nil?
- @from = "#{question.assigned_to.name} (Redmine) <#{Setting.mail_from}>" unless question.assigned_to.nil?
redmine_headers 'Issue-Id' => question.issue.id
redmine_headers 'Question-Answer' => "#{question.issue.id}-#{closing_journal.id}"
- body({
- :question => question,
- :issue => question.issue,
- :journal => closing_journal,
- :issue_url => url_for(:controller => 'issues', :action => 'show', :id => question.issue)
- })
-
- RAILS_DEFAULT_LOGGER.debug 'Sending QuestionMailer#answered_question'
- render_multipart('answered_question', body)
+ Rails.logger.debug 'Sending QuestionMailer#answered_question'
+ mail(:from => from, :to => to, :subject => subject)
end
# Creates an email with a list of issues that have open questions
# assigned to the user
def question_reminder(user, issues)
- redmine_headers 'Type' => "Question"
set_language_if_valid user.language
- recipients user.mail
- subject l(:question_reminder_subject, :count => issues.size)
- body :issues => issues,
- :issues_url => url_for(:controller => 'questions', :action => 'my_issue_filter')
- render_multipart('question_reminder', body)
+
+ to = user.mail
+ subject = l(:question_reminder_subject, :count => issues.size)
+
+ @issues = issues
+ @issues_url = url_for(:controller => 'issuequestions', :action => 'my_issue_filter')
+
+ redmine_headers 'Type' => 'Question'
+
+ mail(:to => to, :subject => subject)
end
# Send email reminders to users who have open questions.
def self.question_reminders
-
- open_questions_by_assignee = Question.opened.all(:order => 'id desc').group_by(&:assigned_to)
+ open_questions_by_assignee = Question.opened.order('id desc').all.group_by(&:assigned_to)
open_questions_by_assignee.each do |assignee, questions|
- next unless assignee.present?
+ next unless assignee.present? and not assignee.locked?
- issues = questions.collect {|q| q.issue.visible?(assignee) ? q.issue : nil }.compact.uniq
+ issues = questions.reject {|q| not q.issue.visible?(assignee) }.collect {|q| q.issue }.uniq
next if issues.count == 0
- deliver_question_reminder(assignee, issues)
+ question_reminder(assignee, issues).deliver
end
end
end
diff --git a/app/views/issuequestions/autocomplete_for_user_login.html.erb b/app/views/issuequestions/autocomplete_for_user_login.html.erb
new file mode 100644
index 0000000..14c0ccd
--- /dev/null
+++ b/app/views/issuequestions/autocomplete_for_user_login.html.erb
@@ -0,0 +1,7 @@
+<%= raw @users.map {|user| {
+ 'id' => user.login,
+ 'label' => "#{user.login}: #{user.name(:lastname_coma_firstname)}",
+ 'value' => user.login
+ }
+ }.to_json
+%>
\ No newline at end of file
diff --git a/app/views/issuequestions/autocomplete_for_user_login.json.erb b/app/views/issuequestions/autocomplete_for_user_login.json.erb
new file mode 100644
index 0000000..8c1bf6d
--- /dev/null
+++ b/app/views/issuequestions/autocomplete_for_user_login.json.erb
@@ -0,0 +1,7 @@
+<%= raw @users.map {|user| {
+ 'id' => user.login,
+ 'label' => "#{user.login}: #{user.name(:lastname_coma_firstname)}",
+ 'value' => user.login
+ }
+ }.to_json
+%>
\ No newline at end of file
diff --git a/app/views/layouts/_questions.html.erb b/app/views/layouts/_questions.html.erb
new file mode 100644
index 0000000..4da7ee5
--- /dev/null
+++ b/app/views/layouts/_questions.html.erb
@@ -0,0 +1,18 @@
+<% questions = Question.opened.not_hidden.for_user(User.current) %>
+<% if questions && questions.any? %>
+
+
<%= l(:text_questions_for_me)%> (<%= h(questions.length) %>)
+
+ <% questions.each do |question| %>
+ -
+ <%= link_to_issue(question.issue, :project => true, :subject => false) %>:
+ <%= truncate(question.notes, :length => 120) %>
+ <%= link_to l(:label_question_plugin_hide), hide_path(question) %>
+
+ <% end %>
+
+
+<% end %>
+
+
+
diff --git a/app/views/question_mailer/answered_question.html.erb b/app/views/question_mailer/answered_question.html.erb
new file mode 100644
index 0000000..b9a7b01
--- /dev/null
+++ b/app/views/question_mailer/answered_question.html.erb
@@ -0,0 +1,20 @@
+<% if Setting.plugin_question_plugin[:obfuscate_content] == "1" %>
+<%= textilizable(l(:mail_body_answer_link)) %>
+<% else %>
+<%= l(:text_answer) %>
+<%= textilizable(@journal, :notes, :only_path => false) %>
+<% end %>
+
+<%= l(:text_question_for) %> <%= @question.assigned_to %>
+<%= textilizable(@question.journal, :notes, :only_path => false) %>
+
+<% if Setting.plugin_question_plugin[:obfuscate_content] == "0" %>
+
+<% for detail in @journal.details %>
+ - <%= show_detail(detail, true) %>
+<% end %>
+
+
+
+<% end %>
+<%= render :partial => "mailer/issue", :locals => { :issue => @issue, :issue_url => @issue_url, :users => @users } %>
diff --git a/app/views/question_mailer/answered_question.text.erb b/app/views/question_mailer/answered_question.text.erb
new file mode 100644
index 0000000..ae1118c
--- /dev/null
+++ b/app/views/question_mailer/answered_question.text.erb
@@ -0,0 +1,20 @@
+<% if Setting.plugin_question_plugin[:obfuscate_content] == "1" %>
+<%= l(:mail_body_answer_link) %>
+<% else %>
+<%= l(:text_answer) %>
+<%= textilizable(@journal, :notes, :only_path => false) %>
+<% end %>
+
+<%= l(:text_question_for) %> <%= @question.assigned_to %>
+<%= textilizable(@question.journal, :notes, :only_path => false) %>
+
+<% if Setting.plugin_question_plugin[:obfuscate_content] == "0" %>
+
+<% for detail in @journal.details %>
+ - <%= show_detail(detail, true) %>
+<% end %>
+
+
+
+<% end %>
+<%= render :partial => "mailer/issue", :locals => { :issue => @issue, :issue_url => @issue_url, :users => @users } %>
diff --git a/app/views/question_mailer/answered_question.text.html.rhtml b/app/views/question_mailer/answered_question.text.html.rhtml
deleted file mode 100644
index ceeafe4..0000000
--- a/app/views/question_mailer/answered_question.text.html.rhtml
+++ /dev/null
@@ -1,14 +0,0 @@
-<%= l(:text_answer) %>
-<%= textilizable(@journal, :notes, :only_path => false) %>
-
-<%= l(:text_question) %>
-<%= textilizable(@question.journal, :notes, :only_path => false) %>
-
-
-<% for detail in @journal.details %>
- - <%= @journal.render_detail(detail, true) %>
-<% end %>
-
-
-
-<%= render :partial => "mailer/issue_text_html", :locals => { :issue => @issue, :issue_url => @issue_url } %>
diff --git a/app/views/question_mailer/answered_question.text.plain.rhtml b/app/views/question_mailer/answered_question.text.plain.rhtml
deleted file mode 100644
index d01eb36..0000000
--- a/app/views/question_mailer/answered_question.text.plain.rhtml
+++ /dev/null
@@ -1,14 +0,0 @@
-<%= l(:text_answer) %>
-
-<%= @journal.notes if @journal.notes? %>
-
-<%= l(:text_question) %>
-
-<%= @question.journal.notes if @question.journal && @question.journal.notes? %>
-
-<% for detail in @journal.details -%>
-<%= @journal.render_detail(detail, true) %>
-<% end -%>
-
-----------------------------------------
-<%= render :partial => "mailer/issue_text_plain", :locals => { :issue => @issue, :issue_url => @issue_url } %>
diff --git a/app/views/question_mailer/asked_question.html.erb b/app/views/question_mailer/asked_question.html.erb
new file mode 100644
index 0000000..6749a00
--- /dev/null
+++ b/app/views/question_mailer/asked_question.html.erb
@@ -0,0 +1,16 @@
+<%= l(:mail_body_title, name: @question.assigned_to, issue: "##{@issue.id}", author: @question.author) %>
+
+<% if Setting.plugin_question_plugin[:obfuscate_content] == "1" %>
+<%= textilizable(l(:mail_body_question_link)) %>
+<% else %>
+<%= textilizable(@journal, :notes, :only_path => false) %>
+
+
+<% for detail in @journal.details %>
+ - <%= show_detail(detail, true) %>
+<% end %>
+
+
+
+<% end %>
+<%= render :partial => "mailer/issue", :locals => { :issue => @issue, :issue_url => @issue_url, :users => @users } %>
diff --git a/app/views/question_mailer/asked_question.text.erb b/app/views/question_mailer/asked_question.text.erb
new file mode 100644
index 0000000..8dc8bf3
--- /dev/null
+++ b/app/views/question_mailer/asked_question.text.erb
@@ -0,0 +1,14 @@
+<%= l(:mail_body_title, name: @question.assigned_to, issue: "##{@issue.id}", author: @question.author) %>
+
+<% if Setting.plugin_question_plugin[:obfuscate_content] == "1" %>
+<%= l(:mail_body_question_link) %>
+<% else %>
+<%= @journal.notes if @journal.notes? %>
+
+<% for detail in @journal.details -%>
+<%= show_detail(detail, true) %>
+<% end -%>
+
+----------------------------------------
+<% end %>
+<%= render :partial => "mailer/issue", :locals => { :issue => @issue, :issue_url => @issue_url, :users => @users } %>
diff --git a/app/views/question_mailer/asked_question.text.html.rhtml b/app/views/question_mailer/asked_question.text.html.rhtml
deleted file mode 100644
index 36ca1ff..0000000
--- a/app/views/question_mailer/asked_question.text.html.rhtml
+++ /dev/null
@@ -1,12 +0,0 @@
-<%= l(:text_question_asked) %> <%= h "##{@issue.id}" %> <%= @question.author %>
-
-<%= textilizable(@journal, :notes, :only_path => false) %>
-
-
-<% for detail in @journal.details %>
- - <%= @journal.render_detail(detail, true) %>
-<% end %>
-
-
-
-<%= render :partial => "mailer/issue_text_html", :locals => { :issue => @issue, :issue_url => @issue_url } %>
diff --git a/app/views/question_mailer/asked_question.text.plain.rhtml b/app/views/question_mailer/asked_question.text.plain.rhtml
deleted file mode 100644
index 658618b..0000000
--- a/app/views/question_mailer/asked_question.text.plain.rhtml
+++ /dev/null
@@ -1,10 +0,0 @@
-<%= l(:text_question_asked) %> <%= h "##{@issue.id}" %> <%= @question.author %>
-
-<%= @journal.notes if @journal.notes? %>
-
-<% for detail in @journal.details -%>
-<%= @journal.render_detail(detail, true) %>
-<% end -%>
-
-----------------------------------------
-<%= render :partial => "mailer/issue_text_plain", :locals => { :issue => @issue, :issue_url => @issue_url } %>
diff --git a/app/views/question_mailer/question_reminder.text.html.rhtml b/app/views/question_mailer/question_reminder.html.erb
similarity index 100%
rename from app/views/question_mailer/question_reminder.text.html.rhtml
rename to app/views/question_mailer/question_reminder.html.erb
diff --git a/app/views/question_mailer/question_reminder.text.plain.rhtml b/app/views/question_mailer/question_reminder.text.erb
similarity index 100%
rename from app/views/question_mailer/question_reminder.text.plain.rhtml
rename to app/views/question_mailer/question_reminder.text.erb
diff --git a/app/views/questions/autocomplete_for_user_login.html.erb b/app/views/questions/autocomplete_for_user_login.html.erb
deleted file mode 100644
index c6057b4..0000000
--- a/app/views/questions/autocomplete_for_user_login.html.erb
+++ /dev/null
@@ -1,22 +0,0 @@
-
- - <%= l(:text_anyone) %>
-
- <% if @issue %>
- -
- <%= l(:field_issue) %> <%= l(:field_author) %> -
- <%= h @issue.author.login %>
- (<%= h(@issue.author.name(:lastname_coma_firstname)) %>)
-
- <% if @issue.assigned_to %>
- -
- <%= l(:field_issue) %> <%= l(:field_assigned_to) %> -
- <%= h @issue.assigned_to.login %>
- (<%= h(@issue.assigned_to.name(:lastname_coma_firstname)) %>)
-
- <% end %>
- <% end %>
-
- <% @users.each do |user| -%>
- - <%= h user.login %> (<%= h(user.name(:lastname_coma_firstname)) %>)
- <% end -%>
-
diff --git a/app/views/settings/_question_plugin.html.erb b/app/views/settings/_question_plugin.html.erb
new file mode 100644
index 0000000..28117c6
--- /dev/null
+++ b/app/views/settings/_question_plugin.html.erb
@@ -0,0 +1,28 @@
+
+ <%= label_tag "settings[only_members]", l(:label_question_plugin_only_members) %>
+ <%= select_tag 'settings[only_members]', options_for_select([
+ [l(:text_question_all), 0],
+ [l(:text_question_project_member), 1],
+ ], Setting.plugin_question_plugin[:only_members]) %>
+
+
+ <%= label_tag "settings[close_all_questions]", l(:label_question_plugin_close_settings) %>
+ <%= select_tag 'settings[close_all_questions]', options_for_select([
+ [l(:text_question_close_all), 1],
+ [l(:text_question_close_selected), 0],
+ ], Setting.plugin_question_plugin[:close_all_questions]) %>
+
+
+
+ <%= label_tag "settings[obfuscate_author]", l(:label_question_plugin_notifications_obfuscate_author_settings) %>
+ <%= check_box_tag 'settings[obfuscate_author]', 1, (Setting.plugin_question_plugin[:obfuscate_author] ? true : false) %>
+
+
+
+ <%= label_tag "settings[obfuscate_content]", l(:label_question_plugin_notifications_obfuscate_content_settings) %>
+ <%= check_box_tag 'settings[obfuscate_content]', 1, (Setting.plugin_question_plugin[:obfuscate_content] ? true : false) %>
+
+
+ <%= label_tag "settings[show_banner]", l(:label_question_plugin_show_banner_settings) %>
+ <%= check_box_tag 'settings[show_banner]', 1, (Setting.plugin_question_plugin[:show_banner] ? true : false) %>
+
diff --git a/config/locales/cs.yml b/config/locales/cs.yml
new file mode 100644
index 0000000..71d150b
--- /dev/null
+++ b/config/locales/cs.yml
@@ -0,0 +1,36 @@
+cs:
+ field_question_assign_to: Položit otázku na
+ field_question_assigned_to: Otázka pro
+ field_question_asked_by: Otázka od
+ field_formatted_questions: Otázky
+ text_question: Otázka
+ text_question_answered: Otázka zodpovězena
+ text_answer: Odpověď
+ text_anyone: Kdokoliv
+ text_question_for: Otázka pro
+ text_question_for_anyone: Otázka pro kohokoliv
+ text_questions_for_me: Otázky pro mě
+ text_question_asked: Položené otázky
+ mail_body_title: "Máte novou otázku od %{author}"
+ mail_body_question_link: Otázku zobrazíte kliknutím na následující odkaz
+ mail_body_answer_link: "Vaše otázka byla zodpovězena: klikněte na odkaz níže k jejímu zobrazení"
+ text_question_remove: "<< Odstranit Otázku >>"
+ question_text_asked_by: "Otázka od"
+ question_text_assigned_to: "Otázka pro"
+ question_text_created_on: "Vytvořeno"
+ question_text_ratio_questions_answered: "{{ratio}} zodpovězených otázek"
+ field_journal: "Žurnál"
+ text_questions_asked_by_me: "Otázky ode mne"
+ question_reminder_subject: "%{count} nezodpovězených otázek"
+ question_reminder_body: "%{count} nezodpovězených otázek, které jsou položeny na vás:"
+ label_question_plugin_only_members: Otázku lze položit na
+ text_question_all: Jakéhokoliv uživatele
+ text_question_project_member: Pouze členy projektu
+ label_question_plugin_close_settings: Zodpovědět při přidání komentáře
+ text_question_close_all: všechny nezodpovězené otázky v úkolu
+ text_question_close_selected: pouze vybranou otázku
+ label_question_plugin_notifications_obfuscate_author_settings: Skrýt jméno a e-mail dotazujícího/dotazovaného v notifikacích
+ label_question_plugin_notifications_obfuscate_content_settings: Skrýt otázku/odpověď v notifikacích
+ field_question_to_answer: Otázka k zodpovězení
+ label_question_plugin_show_banner_settings: Zobrazit nezodpovězenou otázku na každé stránce
+ label_question_plugin_hide: Skrýt
diff --git a/config/locales/de.yml b/config/locales/de.yml
index 190785a..31b1577 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -1,17 +1,33 @@
de:
- field_question_assign_to: Frage zuweisen an
- field_question_assigned_to: Frage ist gestellt an
- field_question_asked_by: Frage wurde gestellt durch
- field_formatted_questions: Fragen
- text_question: Frage
- text_question_answered: Frage beantwortet
- text_answer: Antwort
- text_anyone: Irgendwer
- text_question_for: Frage für
- text_question_for_anyone: Frage für irgendjemanden
- text_questions_for_me: Fragen für mich
- text_question_asked: Frage gestellt
+ field_question_assign_to: "Frage zuweisen an"
+ field_question_assigned_to: "Frage ist gestellt an"
+ field_question_asked_by: "Frage wurde gestellt durch"
+ field_formatted_questions: "Fragen"
+ text_question: "Frage"
+ text_question_answered: "Frage beantwortet"
+ text_answer: "Antwort"
+ text_anyone: "Irgendwer"
+ text_question_for: "Frage für"
+ text_question_for_anyone: "Frage für irgendjemanden"
+ text_questions_for_me: "Fragen für mich"
+ text_question_asked: "Frage gestellt"
text_question_remove: "<>"
question_text_asked_by: "gefragt von"
question_text_assigned_to: "Zugewiesen an"
- question_text_created_on: "Erstellt am"
+ question_text_created_on: "erstellt am"
+ question_text_ratio_questions_answered: "{{ratio}} beantwortete Fragen"
+ field_journal: "Journal"
+ text_questions_asked_by_me: "meine gestellten Fragen"
+ question_reminder_subject: "%{count} offene Frage(n)"
+ question_reminder_body: "%{count} offene dir zugewiesene Frage(n)"
+ label_question_plugin_only_members: Die Frage kann diesem Benutzer zugewiesen werden
+ text_question_all: jedem Benutzer
+ text_question_project_member: Projektmitarbeitern
+ label_question_plugin_close_settings: Frage des Tickets abschließend beantworten
+ text_question_close_all: alle offene Fragen
+ text_question_close_selected: ausschließlich aktuell ausgewählte Frage
+ label_question_plugin_notifications_obfuscate_author_settings: Name und Email des Fragestellers in Email Benachrichtigungen verbergen
+ label_question_plugin_notifications_obfuscate_content_settings: Frage- und Anworttext in Email Benachrichtigungen verbergen
+ field_question_to_answer: Frage beantworten
+ mail_body_question_link: Zeige Sie die Frage für sich an, indem sie auf den nachfolgenden Link für das Ticket klicken
+ mail_body_answer_link: "Ihre Frage wurde beantwortet: Klicken Sie auf den nachfolgenden Link für das Ticket um die Antwort zu sehen"
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 8098bf2..a1afce1 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -11,13 +11,26 @@ en:
text_question_for_anyone: Question for anyone
text_questions_for_me: Questions for me
text_question_asked: Question asked
+ mail_body_title: "You have new question from %{author}"
+ mail_body_question_link: Display question by clicking on the issue link below
+ mail_body_answer_link: "Your question has received an answer: click on the issue link below to display it"
text_question_remove: "<< Remove Question>>"
question_text_asked_by: "Asked By"
- question_text_assigned_to: "Assigned To"
+ question_text_assigned_to: "Question assigned To"
question_text_created_on: "Created On"
question_text_ratio_questions_answered: "{{ratio}} questions answered"
field_journal: "Journal"
text_questions_asked_by_me: "Questions asked by me"
question_reminder_subject: "%{count} open question(s)"
question_reminder_body: "%{count} open questions(s) that are assigned to you:"
-
+ label_question_plugin_only_members: Question can be assigned to
+ text_question_all: Any user
+ text_question_project_member: Project members only
+ label_question_plugin_close_settings: When add comment close
+ text_question_close_all: all opened questions in issue
+ text_question_close_selected: only selected question
+ label_question_plugin_notifications_obfuscate_author_settings: Obfuscate asker/answerer name and e-mail address in notifications
+ label_question_plugin_notifications_obfuscate_content_settings: Obfuscate question/answer text in notifications
+ field_question_to_answer: Answer question
+ label_question_plugin_show_banner_settings: Show unanswered questions on every page
+ label_question_plugin_hide: Hide
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 7d6384d..b30e402 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -1,19 +1,19 @@
es:
- field_question_assign_to: Asigne la pregunta a
- field_question_assigned_to: Pregunata se asigna a
- field_question_asked_by: Pregunta fue hecha cerca
- field_formatted_questions: Preguntas
- text_question: Pregunta
- text_question_answered: Pregunta contest
- text_answer: Respuesta
- text_anyone: Cualquier persona
- text_question_for: Pregunta para
- text_question_for_anyone: Pregunta para cualquier persona
- text_questions_for_me: Preguntas para m
- text_question_asked: Pregunta pidi
+ field_question_assign_to: "Asigne la pregunta a"
+ field_question_assigned_to: "Pregunata se asigna a"
+ field_question_asked_by: "Pregunta fue hecha cerca"
+ field_formatted_questions: "Preguntas"
+ text_question: "Pregunta"
+ text_question_answered: "Pregunta contestó"
+ text_answer: "Respuesta"
+ text_anyone: "Cualquier persona"
+ text_question_for: "Pregunta para"
+ text_question_for_anyone: "Pregunta para cualquier persona"
+ text_questions_for_me: "Preguntas para mí"
+ text_question_asked: "Pregunta pidió"
text_question_remove: "<< Quite la pregunta >>"
question_text_asked_by: "Pedido Cerca"
question_text_assigned_to: "Asignado A"
question_text_created_on: "Creada hace"
question_text_ratio_questions_answered: "{{ratio}} Preguntas contestaron"
- field_journal: "Diario"
\ No newline at end of file
+ field_journal: "Diario"
diff --git a/config/locales/hu.yml b/config/locales/hu.yml
index ffe7d21..4ecc704 100644
--- a/config/locales/hu.yml
+++ b/config/locales/hu.yml
@@ -1,16 +1,16 @@
hu:
- field_question_assign_to: Kérdés hozzá
- field_question_assigned_to: Kérdés hozzá
- field_question_asked_by: Kérdést feltette
- field_formatted_questions: Kérdések
- text_question: Kérdés
- text_question_answered: Kérdés megválaszolva
- text_answer: Kérdés
- text_anyone: - bárkihez -
- text_question_for: Kérdés hozzá
- text_question_for_anyone: Kérdés bárkinek
- text_questions_for_me: Kérdések hozzám
- text_question_asked: Feltett kérdések
+ field_question_assign_to: "Kérdés hozzá"
+ field_question_assigned_to: "Kérdés hozzá"
+ field_question_asked_by: "Kérdést feltette"
+ field_formatted_questions: "Kérdések"
+ text_question: "Kérdés"
+ text_question_answered: "Kérdés megválaszolva"
+ text_answer: "Kérdés"
+ text_anyone: "- bárkihez -"
+ text_question_for: "Kérdés hozzá"
+ text_question_for_anyone: "Kérdés bárkinek"
+ text_questions_for_me: "Kérdések hozzám"
+ text_question_asked: "Feltett kérdések"
text_question_remove: "kérdés törlése"
question_text_asked_by: "Kérdezte"
question_text_assigned_to: "Hozzárendelve"
diff --git a/config/locales/ru.yml b/config/locales/ru.yml
new file mode 100644
index 0000000..dd3adb5
--- /dev/null
+++ b/config/locales/ru.yml
@@ -0,0 +1,35 @@
+ru:
+ field_question_assign_to: Спросить у
+ field_question_assigned_to: Вопрос к
+ field_question_asked_by: Вопрос задал
+ field_formatted_questions: Вопросы
+ text_question: Вопрос
+ text_question_answered: Вопрос отвечен
+ text_answer: Ответ
+ text_anyone: Все
+ text_question_for: Вопрос к
+ text_question_for_anyone: Вопрос ко всем
+ text_questions_for_me: Вопросы ко мне
+ mail_body_question_link: Для просмотра вопроса нажмите на нижеследующую ссылку на задачу
+ mail_body_answer_link: "Ваш вопрос получил ответ: Для его просмотра нажмите на нижеследующую ссылку на задачу"
+ text_question_asked: Вопрос задан
+ text_question_remove: "<< Удалить вопрос >>"
+ question_text_asked_by: "Автор вопроса"
+ question_text_assigned_to: "Вопрос назначен"
+ question_text_created_on: "Создан"
+ question_text_ratio_questions_answered: "{{ratio}} вопросов отвечено"
+ field_journal: "Journal"
+ text_questions_asked_by_me: "Вопросы заданные мной"
+ question_reminder_subject: "%{count} не отвеченных вопросов"
+ question_reminder_body: "%{count} не отвеченных вопросов заданных Вам:"
+ label_question_plugin_only_members: Вопрос может быть задан
+ text_question_all: Любому пользователю
+ text_question_project_member: Только участникам проекта
+ label_question_plugin_close_settings: При добавлении комментария закрывать
+ text_question_close_all: все неотвеченные вопросы в задаче
+ text_question_close_selected: только выбранный вопрос
+ label_question_plugin_notifications_obfuscate_author_settings: Прятать имя и e-mail спрашивающего/отвечающего в оповещениях
+ label_question_plugin_notifications_obfuscate_content_settings: Прятать текст вопроса/ответа в оповещениях
+ field_question_to_answer: Ответ на вопрос
+ label_question_plugin_show_banner_settings: Показывать неотвеченные вопросы на каждой странице
+ label_question_plugin_hide: Скрыть
diff --git a/config/locales/tr.yml b/config/locales/tr.yml
new file mode 100644
index 0000000..a8f157c
--- /dev/null
+++ b/config/locales/tr.yml
@@ -0,0 +1,29 @@
+tr:
+ field_question_assign_to: Soruyu şu kişiye ata
+ field_question_assigned_to: Soru şu kişiye atanmış
+ field_question_asked_by: Soruyu soran
+ field_formatted_questions: Sorular
+ text_question: Soru
+ text_question_answered: Soru Cevaplandı
+ text_answer: Cevap
+ text_anyone: Herhangi biri
+ text_question_for: Şu kişiye sor
+ text_question_for_anyone: Herhangi birine sor
+ text_questions_for_me: Bana sorulmuş sorular
+ text_question_asked: Sorulmuş sorular
+ text_question_remove: "<< Soruyu Sil >>"
+ question_text_asked_by: "Şu kişi sormuş"
+ question_text_assigned_to: "Soru şu kişiye atanmış"
+ question_text_created_on: "Oluşturulma"
+ question_text_ratio_questions_answered: "{{ratio}} soru cevaplandı"
+ field_journal: "Günlük"
+ text_questions_asked_by_me: "Benim sorduğum sorular"
+ question_reminder_subject: "%{count} açık soru"
+ question_reminder_body: "Bana atanmış %{count} açık soru:"
+ label_question_plugin_only_members: Soru şu kişilere atanabilir
+ text_question_all: Herhangi bir kullanıcı
+ text_question_project_member: Sadece birim üyeleri
+ label_question_plugin_close_settings: Not ekleyince kapat
+ text_question_close_all: İş içindeki tüm sorular
+ text_question_close_selected: sadece seçilmiş soru
+ field_question_to_answer: Soruyu cevapla
diff --git a/config/routes.rb b/config/routes.rb
new file mode 100644
index 0000000..811bff0
--- /dev/null
+++ b/config/routes.rb
@@ -0,0 +1,6 @@
+RedmineApp::Application.routes.draw do
+ match 'issuequestions/autocomplete_for_user_login/project/:id/issue/:issue_id' => 'issuequestions#autocomplete_for_user_login', :format => false, :as => 'issuequestions_autocomplete_for_user_login', :via => [:get, :post]
+ match 'issuequestions/my_issue_filter(/project/:project)' => 'issuequestions#my_issue_filter', :format => false, :as => 'questions_my_issue_filter', :via => [:get, :post]
+ match 'issuequestions/user_issue_filter/user/:user_id' => 'issuequestions#user_issue_filter', :format => false, :as => 'questions_user_issue_filter', :via => [:get, :post]
+ match 'issuequestions/hide/:id' => 'issuequestions#hide', :as => 'hide', :via => [:get, :post]
+end
diff --git a/db/migrate/006_add_hidden_to_questions.rb b/db/migrate/006_add_hidden_to_questions.rb
new file mode 100644
index 0000000..55d7cdd
--- /dev/null
+++ b/db/migrate/006_add_hidden_to_questions.rb
@@ -0,0 +1,9 @@
+class AddHiddenToQuestions < ActiveRecord::Migration
+ def self.up
+ add_column :questions, :hidden, :boolean, :default => false
+ end
+
+ def self.down
+ remove_column :questions, :hidden
+ end
+end
diff --git a/init.rb b/init.rb
index d1e0648..5a476df 100644
--- a/init.rb
+++ b/init.rb
@@ -1,31 +1,38 @@
require 'redmine'
-require 'question_issue_hooks'
-require 'question_kanban_hooks'
-require 'question_layout_hooks'
-require 'question_journal_hooks'
+require 'question_plugin/hooks/issue_hooks'
+require 'question_plugin/hooks/kanban_hooks'
+require 'question_plugin/hooks/layout_hooks'
+require 'question_plugin/hooks/journal_hooks'
-# Patches to the Redmine core.
-require 'dispatcher'
-
-Dispatcher.to_prepare :question_plugin do
-
- require_dependency 'journal_observer'
- JournalObserver.send(:include, QuestionPlugin::Patches::JournalObserverPatch)
+Rails.configuration.to_prepare do
require_dependency 'issue'
- Issue.send(:include, QuestionIssuePatch) unless Issue.included_modules.include? QuestionIssuePatch
+ Issue.send(:include, QuestionPlugin::Patches::IssuePatch) unless Issue.included_modules.include? QuestionPlugin::Patches::IssuePatch
require_dependency 'journal'
- Journal.send(:include, QuestionJournalPatch) unless Journal.included_modules.include? QuestionJournalPatch
-
- require_dependency 'queries_helper'
- QueriesHelper.send(:include, QuestionQueriesHelperPatch) unless QueriesHelper.included_modules.include? QuestionQueriesHelperPatch
-
- require_dependency "query"
- Query.send(:include, QuestionQueryPatch) unless Query.included_modules.include? QuestionQueryPatch
+ Journal.send(:include, QuestionPlugin::Patches::JournalPatch) unless Journal.included_modules.include? QuestionPlugin::Patches::JournalPatch
+
+ require_dependency 'application_helper'
+ ApplicationHelper.send(:include, QuestionPlugin::Patches::ApplicationHelperPatch) unless ApplicationHelper.included_modules.include? QuestionPlugin::Patches::ApplicationHelperPatch
+
+ if ActiveSupport::Dependencies::search_for_file('issue_queries_helper')
+ require_dependency 'issue_queries_helper'
+ IssueQueriesHelper.send(:include, QuestionPlugin::Patches::QueriesHelperPatch) unless IssueQueriesHelper.included_modules.include? QuestionPlugin::Patches::QueriesHelperPatch
+ else
+ require_dependency 'queries_helper'
+ QueriesHelper.send(:include, QuestionPlugin::Patches::QueriesHelperPatch) unless QueriesHelper.included_modules.include? QuestionPlugin::Patches::QueriesHelperPatch
+ end
+
+ if ActiveSupport::Dependencies::search_for_file('issue_query')
+ require_dependency 'issue_query'
+ IssueQuery.send(:include, QuestionPlugin::Patches::QueryPatch) unless Query.included_modules.include? QuestionPlugin::Patches::QueryPatch
+ else
+ require_dependency 'query'
+ Query.send(:include, QuestionPlugin::Patches::QueryPatch) unless Query.included_modules.include? QuestionPlugin::Patches::QueryPatch
+ end
end
-Redmine::Plugin.register :question_plugin do
+p = Redmine::Plugin.register :question_plugin do
name 'Redmine Question plugin'
author 'Eric Davis'
url "https://projects.littlestreamsoftware.com/projects/redmine-questions" if respond_to?(:url)
@@ -33,8 +40,21 @@
description 'This is a plugin for Redmine that will allow users to ask questions to each other in issue notes'
version '0.3.0'
- requires_redmine :version_or_higher => '0.8.0'
+ requires_redmine :version_or_higher => '3.0.0'
+
+ settings :default => {
+ :only_members => 1,
+ :close_all_questions => 1,
+ :obfuscate_author => 0,
+ :obfuscate_content => 0,
+ }, :partial => 'settings/question_plugin'
+
+end
+# Ensure ActionMailer knows where to find the views for the question plugin
+view_path = File.join(p.directory, 'app', 'views')
+if File.directory?(view_path)
+ ActionMailer::Base.prepend_view_path(view_path)
end
require 'question_plugin/hooks/view_user_kanbans_show_contextual_top_hook'
diff --git a/lang/de.yml b/lang/de.yml
deleted file mode 100644
index f390902..0000000
--- a/lang/de.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-field_question_assign_to: Frage zuweisen an
-field_question_assigned_to: Frage ist gestellt an
-field_question_asked_by: Frage wurde gestellt durch
-field_formatted_questions: Fragen
-text_question: Frage
-text_question_answered: Frage beantwortet
-text_answer: Antwort
-text_anyone: Irgendwer
-text_question_for: Frage für
-text_question_for_anyone: Frage für irgendjemanden
-text_questions_for_me: Fragen für mich
-text_question_asked: Frage gestellt
-text_question_remove: "<>"
-question_text_asked_by: "gefragt von"
-question_text_assigned_to: "Zugewiesen an"
-question_text_created_on: "Erstellt am"
diff --git a/lang/en.yml b/lang/en.yml
deleted file mode 100644
index 360c3f0..0000000
--- a/lang/en.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-field_question_assign_to: Assign question to
-field_question_assigned_to: Question is assigned to
-field_question_asked_by: Question was asked by
-field_formatted_questions: Questions
-text_question: Question
-text_question_answered: Question Answered
-text_answer: Answer
-text_anyone: Anyone
-text_question_for: Question for
-text_question_for_anyone: Question for anyone
-text_questions_for_me: Questions for me
-text_question_asked: Question asked
-text_question_remove: "<< Remove Question>>"
-question_text_asked_by: "Asked By"
-question_text_assigned_to: "Assigned To"
-question_text_created_on: "Created On"
diff --git a/lib/question_hooks_base.rb b/lib/question_hooks_base.rb
deleted file mode 100644
index 5aedfcf..0000000
--- a/lib/question_hooks_base.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-class QuestionHooksBase < Redmine::Hook::ViewListener
- # Have to inclue Gravatars because ApplicationHelper will not get it
- include Gravatarify::Helper
-
- protected
-
- def assigned_question_html(question)
- html = ""
- html << " "
- html << "#{l(:text_question_for)} #{question.assigned_to.to_s}"
- html << " "
- html << "#{avatar(question.assigned_to, { :size => 16, :class => '' })} " if question.assigned_to && question.assigned_to.mail
- html
- end
-
- def unassigned_question_html(question)
- html = ""
- html << " "
- html << l(:text_question_for_anyone)
- html << " "
- html << ""
- html
- end
-end
diff --git a/lib/question_issue_hooks.rb b/lib/question_issue_hooks.rb
deleted file mode 100644
index 175a0b1..0000000
--- a/lib/question_issue_hooks.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-class QuestionIssueHooks < QuestionHooksBase
- # Applies the question class to each journal div if they are questions
- def view_issues_history_journal_bottom(context = { })
- o = ''
- if context[:journal] && context[:journal].question && context[:journal].question.opened?
- question = context[:journal].question
-
- if question.assigned_to
- html = assigned_question_html(question)
- else
- html = unassigned_question_html(question)
- end
-
- o += <
- $('change-#{context[:journal].id}').addClassName('question');
- $$('#change-#{context[:journal].id} h4 div').each(function(ele) { ele.insert({ top: ' #{html} ' }) });
-
-JS
-
- end
- return o
- end
-
- def view_issues_edit_notes_bottom(context = { })
- f = context[:form]
- @issue = context[:issue]
- o = ''
- o << content_tag(:p,
- " " +
- text_field_tag('note[question_assigned_to]', nil, :size => "40"))
-
- o << content_tag(:div,'', :id => "note_question_assigned_to_choices", :class => "autocomplete")
- o << javascript_tag("new Ajax.Autocompleter('note_question_assigned_to', 'note_question_assigned_to_choices', '#{ url_for(:controller => 'questions', :action => 'autocomplete_for_user_login', :id => @issue.project, :issue_id => @issue) }', { minChars: 1, frequency: 0.5, paramName: 'user', select: 'field' });")
-
- return o
- end
-
- def controller_issues_edit_before_save(context = { })
- params = context[:params]
- journal = context[:journal]
- issue = context[:issue]
- if params[:note] && !params[:note][:question_assigned_to].blank?
- unless journal.question # Update handled by Journal hooks
- # New
- issue.extra_journal_attributes = {
- :question => Question.new(
- :author => User.current,
- :issue => journal.issue,
- :assigned_to => User.find_by_login(params[:note][:question_assigned_to])
- )
- }
- end
- end
-
- return ''
- end
-
- def view_issues_sidebar_issues_bottom(context = { })
- project = context[:project]
- if project
- question_count = Question.count_of_open_for_user_on_project(User.current, project)
- else
- question_count = Question.count_of_open_for_user(User.current)
- end
-
- if question_count > 0
- return link_to(l(:text_questions_for_me) + " (#{question_count})",
- {
- :controller => 'questions',
- :action => 'my_issue_filter',
- :project => project,
- :only_path => true
- },
- { :class => 'question-link' }
- ) + '
'
- else
- return ''
- end
-
- end
-
- private
-
- def assign_question_to_user(journal, user)
- journal.question.assigned_to = user
- end
-end
diff --git a/lib/question_issue_patch.rb b/lib/question_issue_patch.rb
deleted file mode 100644
index 88ed551..0000000
--- a/lib/question_issue_patch.rb
+++ /dev/null
@@ -1,101 +0,0 @@
-module QuestionIssuePatch
- def self.included(base) # :nodoc:
- base.extend(ClassMethods)
-
- base.send(:include, InstanceMethods)
-
- base.class_eval do
- unloadable # Send unloadable so it will not be unloaded in development
- has_many :questions
- has_many :open_questions, :class_name => 'Question', :conditions => { :opened => true }
-
- include ActionView::Helpers::TextHelper # for truncate
-
- class << self
- # I dislike alias method chain, it's not the most readable backtraces
- alias_method :default_find, :find
- alias_method :find, :find_with_questions_added_to_the_includes
-
- alias_method :default_count, :count
- alias_method :count, :count_with_questions_added_to_the_includes
-
- alias_method :default_sum, :sum
- alias_method :sum, :sum_with_questions_added_to_the_includes
- end
- end
-
- end
-
- module ClassMethods
- def find_with_questions_added_to_the_includes(*args)
- scan_for_options_hash_and_add_includes_if_needed(args)
- default_find(*args)
- end
-
- def count_with_questions_added_to_the_includes(*args)
- scan_for_options_hash_and_add_includes_if_needed(args)
- default_count(*args)
- end
-
- def sum_with_questions_added_to_the_includes(*args)
- scan_for_options_hash_and_add_includes_if_needed(args)
- default_sum(*args)
- end
-
- private
-
- # Finds the options hash. If question is part of the conditions then
- # add questions to the includes
- def scan_for_options_hash_and_add_includes_if_needed(args)
- args.each do |arg|
- if arg.is_a?(Hash) && arg[:conditions]
- if arg[:conditions].is_a?(String) && arg[:conditions].include?('question')
- # String conditions
- add_questions_to_the_includes(arg)
- elsif arg[:conditions].is_a?(Array) && arg[:conditions][0].include?('question')
- # Array conditions
- add_questions_to_the_includes(arg)
- end
- end
- end
- end
-
- def add_questions_to_the_includes(arg)
- if arg[:include]
- # Has includes
- if arg[:include].is_a?(Hash)
- # Hash includes
- arg[:include] << :questions
- else
- # single includes
- arg[:include] = [ arg[:include] , :questions ]
- end
- else
- # No includes
- arg[:include] = :questions
- end
- end
- end
-
- module InstanceMethods
- def pending_question?(user)
- self.open_questions.find(:all).each do |question|
- return true if question.assigned_to == user || question.for_anyone?
- end
- return false
- end
-
- def close_pending_questions(user, closing_journal)
- self.open_questions.find(:all).each do |question|
- question.close!(closing_journal) if question.assigned_to == user || question.for_anyone?
- end
- end
-
- def formatted_questions
- open_questions.collect do |question|
- truncate(question.journal.notes, Question::TruncateTo)
- end.join(", ")
- end
- end
-end
-
diff --git a/lib/question_journal_hooks.rb b/lib/question_journal_hooks.rb
deleted file mode 100644
index 66ff203..0000000
--- a/lib/question_journal_hooks.rb
+++ /dev/null
@@ -1,96 +0,0 @@
-class QuestionJournalHooks < QuestionHooksBase
- def view_journals_notes_form_after_notes(context = { })
- @journal = context[:journal]
- if @journal.question && @journal.question.opened && @journal.question.assigned_to
- assigned_to = @journal.question.assigned_to.login
- else
- assigned_to = ''
- end
-
- o = ''
- o << content_tag(:p,
- " " +
- text_field_tag('question[assigned_to]', assigned_to, :size => "40"))
-
- o << content_tag(:div,'', :id => "question_assigned_to_choices", :class => "autocomplete")
- o << javascript_tag("new Ajax.Autocompleter('question_assigned_to', 'question_assigned_to_choices', '#{ url_for(:controller => 'questions', :action => 'autocomplete_for_user_login', :id => @journal.project, :issue_id => @journal.issue) }', { minChars: 1, frequency: 0.5, paramName: 'user', select: 'field' });")
-
- return o
- end
-
- def controller_journals_edit_post(context = { })
- journal = context[:journal]
- params = context[:params]
-
- # Handle destroying journals through the 'edit' action (done by clearing notes)
- return '' if journal.destroyed?
-
- if params[:question] && params[:question][:assigned_to]
- if journal.question && params[:question][:assigned_to].blank?
- # Wants to remove the question
- journal.question.destroy
- elsif journal.question && journal.question.opened
- # Reassignment
- if params[:question][:assigned_to].downcase == 'anyone'
- journal.question.update_attributes(:assigned_to => nil)
- else
- journal.question.update_attributes(:assigned_to => User.find_by_login(params[:question][:assigned_to]))
- end
- elsif journal.question && !journal.question.opened
- # Existing question, destry it first and then add a new question
- journal.question.destroy
- add_new_question(journal, User.find_by_login(params[:question][:assigned_to]))
- else
- if params[:question][:assigned_to].downcase == 'anyone'
- add_new_question(journal)
- elsif !params[:question][:assigned_to].blank?
- add_new_question(journal, User.find_by_login(params[:question][:assigned_to]))
- else
- # No question
- end
- end
-
- end
-
- return ''
- end
-
- def view_journals_update_rjs_bottom(context = { })
- @journal = context[:journal]
- page = context[:page]
- unless @journal.frozen?
- @journal.reload
- if @journal && @journal.question && @journal.question.opened?
- question = @journal.question
-
- if question.assigned_to
- html = assigned_question_html(question)
- else
- html = unassigned_question_html(question)
- end
-
- page << "$('change-#{@journal.id}').addClassName('question');"
- page << "$$('#change-#{@journal.id} h4 div span.question-line').each(function(ele) {ele.remove()});"
- page << "$$('#change-#{@journal.id} h4 div').each(function(ele) { ele.insert({ top: ' #{html} ' }) });"
-
- elsif @journal && @journal.question.nil?
- # No question found, make sure the UI reflects this
- page << "$('change-#{@journal.id}').removeClassName('question');"
- page << "$$('#change-#{@journal.id} h4 div span.question-line').each(function(ele) {ele.remove()});"
- end
- end
- return ''
- end
-
- private
-
- def add_new_question(journal, assigned_to=nil)
- journal.question = Question.new(
- :author => User.current,
- :issue => journal.issue,
- :assigned_to => assigned_to
- )
- journal.question.save!
- journal.save
- end
-end
diff --git a/lib/question_journal_patch.rb b/lib/question_journal_patch.rb
deleted file mode 100644
index 5070005..0000000
--- a/lib/question_journal_patch.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-module QuestionJournalPatch
- def self.included(base) # :nodoc:
- base.extend(ClassMethods)
-
- base.send(:include, InstanceMethods)
-
- base.class_eval do
- unloadable # Send unloadable so it will not be unloaded in development
- has_one :question, :dependent => :destroy
- end
-
- end
-
- module ClassMethods
- end
-
- module InstanceMethods
- def question_assigned_to
- # TODO: pull out the assigned user on edits
- end
- end
-end
diff --git a/lib/question_kanban_hooks.rb b/lib/question_kanban_hooks.rb
deleted file mode 100644
index f308378..0000000
--- a/lib/question_kanban_hooks.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-class QuestionKanbanHooks < QuestionHooksBase
- def view_kanbans_issue_details(context = {})
- # GREY when there are no questions
- # RED when there are open questions
- # BLACK if all questions are answered
- issue = context[:issue]
-
- return '' unless issue
-
- if issue.questions.count == 0
- return question_icon(:gray, issue)
- end
-
- if issue.open_questions.count > 0
- return question_icon(:red, issue)
- end
-
- if issue.questions.count > 0 && issue.open_questions.count == 0
- return question_icon(:black, issue)
- end
-
- return ''
- end
-
- # * :user
- def view_kanbans_user_name(context = {})
- user = context[:user]
- if user
- count = Question.count_of_open_for_user(user)
-
- if count > 0
- return content_tag(:p, link_to(l(:field_formatted_questions) + " (#{count})",
- {
- :controller => 'questions',
- :action => 'user_issue_filter',
- :user_id => user.id,
- :only_path => true
- },
- { :class => 'question-link' }))
- end
- end
-
- return ''
-
- end
-
- protected
-
- def question_icon(color, issue)
- total_questions = issue.questions.count
- open_questions = issue.open_questions.count
- answered_questions = total_questions - open_questions
-
- title = l(:question_text_ratio_questions_answered, :ratio => "#{answered_questions}/#{total_questions}")
- link_to(image_tag("question-#{color}.png", :plugin => 'question_plugin', :title => title, :class => "kanban-question #{color}"),
- { :controller => 'issues', :action => 'show', :id => issue },
- :class => "issue-show-popup issue-id-#{h(issue.id)}")
- end
-end
diff --git a/lib/question_layout_hooks.rb b/lib/question_layout_hooks.rb
deleted file mode 100644
index 2870471..0000000
--- a/lib/question_layout_hooks.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-class QuestionLayoutHooks < Redmine::Hook::ViewListener
-
- # Add a question CSS class
- def view_layouts_base_html_head(context = { })
- o = <
-.question { background-color:#FFEBC1; border:2px solid #FDBD3B; margin-bottom:12px; padding:0px 4px 8px 4px; }
-td.formatted_questions { text-align: left; white-space: normal}
-td.formatted_questions ol { margin-top: 0px; margin-bottom: 0px; }
-
-.kanban-question { background:#FFFFFF none repeat scroll 0 0; border:1px solid #D5D5D5; padding:2px; font-size: 0.8em }
-.question-link {font-weight: bold; } /* Kanban Menu item */
-
-
-CSS
- return o
- end
-end
diff --git a/lib/question_plugin/hooks/hooks_base.rb b/lib/question_plugin/hooks/hooks_base.rb
new file mode 100644
index 0000000..aace9dc
--- /dev/null
+++ b/lib/question_plugin/hooks/hooks_base.rb
@@ -0,0 +1,25 @@
+module QuestionPlugin
+ module Hooks
+ class HooksBase < Redmine::Hook::ViewListener
+ # Have to inclue Gravatars because ApplicationHelper will not get it
+ include GravatarHelper::PublicMethods
+
+ protected
+
+ def assigned_question_html(question)
+ html = ""
+ html << " #{l(:text_question_for)} "
+ html << link_to_user(question.assigned_to)
+ html << " " if question.assigned_to && question.assigned_to.mail
+ html
+ end
+
+ def unassigned_question_html(question)
+ html = ""
+ html << l(:text_question_for_anyone)
+ html << " "
+ html
+ end
+ end
+ end
+end
diff --git a/lib/question_plugin/hooks/issue_hooks.rb b/lib/question_plugin/hooks/issue_hooks.rb
new file mode 100644
index 0000000..6817cb1
--- /dev/null
+++ b/lib/question_plugin/hooks/issue_hooks.rb
@@ -0,0 +1,112 @@
+module QuestionPlugin
+ module Hooks
+ class IssueHooks < HooksBase
+ # Applies the question class to each journal div if they are questions
+ def view_issues_history_journal_bottom(context = { })
+ o = ''
+ if context[:journal] && context[:journal].question
+ question = context[:journal].question
+
+ if question.assigned_to
+ html = assigned_question_html(question)
+ else
+ html = unassigned_question_html(question)
+ end
+
+ className = question.opened ? 'question' : 'question-closed'
+ o += <
+ $('#change-#{context[:journal].id}').addClass('#{className}');
+ $('#change-#{context[:journal].id} h4 .journal-link').after(' #{html} ');
+
+JS
+ end
+ return o
+ end
+
+ def view_issues_edit_notes_bottom(context = { })
+ f = context[:form]
+ @issue = context[:issue]
+ o = ''
+
+ if @issue.pending_question?(User.current) && Setting.plugin_question_plugin[:close_all_questions] != "1"
+ questions = @issue.pending_questions(User.current)
+ o << content_tag(:p,
+ " ".html_safe +
+ select_tag('question_to_answer', options_for_select([[]] + questions.collect {|q| [truncate(q.journal.notes, :length => Question::TruncateTo), q.id]}))
+ )
+ end
+ o << content_tag(:p,
+ " ".html_safe +
+ text_field_tag('note[question_assigned_to]', nil, :size => "40"))
+ o << javascript_tag("observeAutocompleteField('note_question_assigned_to', '#{escape_javascript issuequestions_autocomplete_for_user_login_path(@issue.project, @issue)}')")
+
+ return o
+ end
+
+ def controller_issues_edit_before_save(context = { })
+ params = context[:params]
+ journal = context[:journal]
+ issue = context[:issue]
+ if params[:note] && !params[:note][:question_assigned_to].blank?
+ unless journal.question # Update handled by Journal hooks
+ # New
+ journal.question = Question.new(
+ :author => User.current,
+ :issue => journal.issue
+ )
+ if params[:note][:question_assigned_to].downcase != 'anyone'
+ # Assigned to a specific user
+ assign_question_to_user(journal, User.find_by(:login => params[:note][:question_assigned_to]))
+ end
+ end
+ end
+
+ if Setting.plugin_question_plugin[:close_all_questions] == "1"
+ # Close any open questions
+ if journal.issue.present? && journal.issue.pending_question?(journal.user)
+ journal.issue.close_pending_questions(journal.user, journal)
+ end
+ else
+ # Close specific question
+ if params[:question_to_answer] and !params[:question_to_answer].empty?
+ question = Question.find(params[:question_to_answer])
+ question.close!(journal)
+ end
+ end
+
+ return ''
+ end
+
+ def view_issues_sidebar_issues_bottom(context = { })
+ project = context[:project]
+ if project
+ question_count = Question.count_of_open_for_user_on_project(User.current, project)
+ else
+ question_count = Question.count_of_open_for_user(User.current)
+ end
+
+ if question_count > 0
+ return link_to(l(:text_questions_for_me) + " (#{question_count})",
+ {
+ :controller => 'issuequestions',
+ :action => 'my_issue_filter',
+ :project => project,
+ :only_path => true
+ },
+ { :class => 'question-link' }
+ ) + '
'.html_safe
+ else
+ return ''
+ end
+
+ end
+
+ private
+
+ def assign_question_to_user(journal, user)
+ journal.question.assigned_to = user
+ end
+ end
+ end
+end
diff --git a/lib/question_plugin/hooks/journal_hooks.rb b/lib/question_plugin/hooks/journal_hooks.rb
new file mode 100644
index 0000000..dca1e9e
--- /dev/null
+++ b/lib/question_plugin/hooks/journal_hooks.rb
@@ -0,0 +1,100 @@
+module QuestionPlugin
+ module Hooks
+ class JournalHooks < HooksBase
+ def view_journals_notes_form_after_notes(context = { })
+ @journal = context[:journal]
+ if @journal.question && @journal.question.opened && @journal.question.assigned_to
+ assigned_to = @journal.question.assigned_to.login
+ else
+ assigned_to = ''
+ end
+
+ o = ''
+ o << content_tag(:p,
+ " ".html_safe +
+ text_field_tag('question[assigned_to]', assigned_to, :size => "40"))
+
+ o << javascript_tag("observeAutocompleteField('question_assigned_to', '#{escape_javascript issuequestions_autocomplete_for_user_login_path(@journal.issue.project, @journal.issue)}')")
+
+ return o
+ end
+
+ def controller_journals_edit_post(context = { })
+ journal = context[:journal]
+ params = context[:params]
+
+ # Handle destroying journals through the 'edit' action (done by clearing notes)
+ return '' if journal.destroyed?
+
+ if params[:question] && params[:question][:assigned_to]
+ if journal.question && params[:question][:assigned_to].blank?
+ # Wants to remove the question
+ journal.question.destroy
+ elsif journal.question && journal.question.opened
+ # Reassignment
+ if params[:question][:assigned_to].downcase == 'anyone'
+ journal.question.update_attributes(:assigned_to => nil)
+ else
+ journal.question.update_attributes(:assigned_to => User.find_by(:login => params[:question][:assigned_to]))
+ end
+ elsif journal.question && !journal.question.opened
+ # Existing question, destry it first and then add a new question
+ journal.question.destroy
+ add_new_question(journal, User.find_by(:login => params[:question][:assigned_to]))
+ else
+ if params[:question][:assigned_to].downcase == 'anyone'
+ add_new_question(journal)
+ elsif !params[:question][:assigned_to].blank?
+ add_new_question(journal, User.find_by(:login => params[:question][:assigned_to]))
+ else
+ # No question
+ end
+ end
+
+ end
+
+ return ''
+ end
+
+ def view_journals_update_js_bottom(context = { })
+ @journal = context[:journal]
+ page = context[:page]
+ page = ""
+ unless @journal.frozen?
+ @journal.reload
+ if @journal && @journal.question && @journal.question.opened?
+ question = @journal.question
+
+ if question.assigned_to
+ html = assigned_question_html(question)
+ else
+ html = unassigned_question_html(question)
+ end
+
+ page << "$('#change-#{@journal.id}').addClass('question');"
+ page << "$('#change-#{@journal.id} h4 span.question-line').remove();"
+ page << "$('#change-#{@journal.id} h4 .journal-link').after(' #{html} ') ;"
+
+ elsif @journal && @journal.question.nil?
+ # No question found, make sure the UI reflects this
+ page << "$('#change-#{@journal.id}').removeClass('question');"
+ page << "$('#change-#{@journal.id} h4 span.question-line').remove();"
+ end
+ end
+ return page
+ end
+
+ private
+
+ def add_new_question(journal, assigned_to=nil)
+ journal.question = Question.new(
+ :author => User.current,
+ :issue => journal.issue,
+ :assigned_to => assigned_to
+ )
+ journal.question.save!
+ journal.save
+ end
+ end
+ end
+end
diff --git a/lib/question_plugin/hooks/kanban_hooks.rb b/lib/question_plugin/hooks/kanban_hooks.rb
new file mode 100644
index 0000000..e775f22
--- /dev/null
+++ b/lib/question_plugin/hooks/kanban_hooks.rb
@@ -0,0 +1,63 @@
+module QuestionPlugin
+ module Hooks
+ class KanbanHooks < HooksBase
+ def view_kanbans_issue_details(context = {})
+ # GREY when there are no questions
+ # RED when there are open questions
+ # BLACK if all questions are answered
+ issue = context[:issue]
+
+ return '' unless issue
+
+ if issue.questions.count == 0
+ return question_icon(:gray, issue)
+ end
+
+ if issue.open_questions.count > 0
+ return question_icon(:red, issue)
+ end
+
+ if issue.questions.count > 0 && issue.open_questions.count == 0
+ return question_icon(:black, issue)
+ end
+
+ return ''
+ end
+
+ # * :user
+ def view_kanbans_user_name(context = {})
+ user = context[:user]
+ if user
+ count = Question.count_of_open_for_user(user)
+
+ if count > 0
+ return content_tag(:p, link_to(l(:field_formatted_questions) + " (#{count})",
+ {
+ :controller => 'issuequestions',
+ :action => 'user_issue_filter',
+ :user_id => user.id,
+ :only_path => true
+ },
+ { :class => 'question-link' }))
+ end
+ end
+
+ return ''
+
+ end
+
+ protected
+
+ def question_icon(color, issue)
+ total_questions = issue.questions.count
+ open_questions = issue.open_questions.count
+ answered_questions = total_questions - open_questions
+
+ title = l(:question_text_ratio_questions_answered, :ratio => "#{answered_questions}/#{total_questions}")
+ link_to(image_tag("question-#{color}.png", :plugin => 'question_plugin', :title => title, :class => "kanban-question #{color}"),
+ { :controller => 'issues', :action => 'show', :id => issue },
+ :class => "issue-show-popup issue-id-#{h(issue.id)}")
+ end
+ end
+ end
+end
diff --git a/lib/question_plugin/hooks/layout_hooks.rb b/lib/question_plugin/hooks/layout_hooks.rb
new file mode 100644
index 0000000..47f0055
--- /dev/null
+++ b/lib/question_plugin/hooks/layout_hooks.rb
@@ -0,0 +1,24 @@
+module QuestionPlugin
+ module Hooks
+ class LayoutHooks < Redmine::Hook::ViewListener
+
+ # Add a question CSS class
+ def view_layouts_base_html_head(context = { })
+ o = <
+
+.question, div.flash.question { background-color:#FFEBC1; border:2px solid; margin-bottom:12px; padding:0px 4px 8px 4px; border-color: #fdbd2b}
+.question-line { float: right; }
+td.formatted_questions { text-align: left; white-space: normal}
+td.formatted_questions ol { margin-top: 0px; margin-bottom: 0px; }
+
+.kanban-question { background:#FFFFFF none repeat scroll 0 0; border:1px solid #D5D5D5; padding:2px; font-size: 0.8em }
+.question-link {font-weight: bold; } /* Kanban Menu item */
+
+
+CSS
+ return o
+ end
+ end
+ end
+end
diff --git a/lib/question_plugin/hooks/view_user_kanbans_show_contextual_top_hook.rb b/lib/question_plugin/hooks/view_user_kanbans_show_contextual_top_hook.rb
index db4e995..1c23230 100644
--- a/lib/question_plugin/hooks/view_user_kanbans_show_contextual_top_hook.rb
+++ b/lib/question_plugin/hooks/view_user_kanbans_show_contextual_top_hook.rb
@@ -10,7 +10,7 @@ def view_user_kanbans_show_contextual_top(context={})
if count > 0
return link_to(l(:field_formatted_questions) + " (#{count})",
{
- :controller => 'questions',
+ :controller => 'issuequestions',
:action => 'user_issue_filter',
:user_id => user.id,
:only_path => true
diff --git a/lib/question_plugin/patches/application_helper_patch.rb b/lib/question_plugin/patches/application_helper_patch.rb
new file mode 100644
index 0000000..920c8c0
--- /dev/null
+++ b/lib/question_plugin/patches/application_helper_patch.rb
@@ -0,0 +1,28 @@
+module QuestionPlugin
+ module Patches
+ module ApplicationHelperPatch
+ def self.included(base) # :nodoc:
+ base.extend(ClassMethods)
+
+ base.send(:include, InstanceMethods)
+
+ base.class_eval do
+ unloadable # Send unloadable so it will not be unloaded in development
+ alias_method_chain :render_flash_messages, :question
+ end
+ end
+
+ module ClassMethods
+ end
+
+ module InstanceMethods
+ def render_flash_messages_with_question
+ s = ""
+ s = render :partial => 'layouts/questions', :layout => false unless Setting.plugin_question_plugin[:show_banner] != "1"
+ s << render_flash_messages_without_question
+ s.html_safe
+ end
+ end
+ end
+ end
+end
diff --git a/lib/question_plugin/patches/issue_patch.rb b/lib/question_plugin/patches/issue_patch.rb
new file mode 100644
index 0000000..47e84dd
--- /dev/null
+++ b/lib/question_plugin/patches/issue_patch.rb
@@ -0,0 +1,51 @@
+module QuestionPlugin
+ module Patches
+ module IssuePatch
+ def self.included(base) # :nodoc:
+ base.extend(ClassMethods)
+
+ base.send(:include, InstanceMethods)
+
+ base.class_eval do
+ unloadable # Send unloadable so it will not be unloaded in development
+ has_many :questions
+ has_many :open_questions, lambda { Question.opened }, :class_name => 'Question'
+
+ include ActionView::Helpers::TextHelper # for truncate
+ end
+ end
+
+ module ClassMethods
+ end
+
+ module InstanceMethods
+ def pending_question?(user)
+ self.open_questions.all.each do |question|
+ return true if question.assigned_to == user || question.for_anyone?
+ end
+ return false
+ end
+
+ def pending_questions(user)
+ q = []
+ self.open_questions.all.each do |question|
+ q << question if question.assigned_to == user || question.for_anyone?
+ end
+ return q
+ end
+
+ def close_pending_questions(user, closing_journal)
+ self.pending_questions(user).each do |question|
+ question.close!(closing_journal)
+ end
+ end
+
+ def formatted_questions
+ open_questions.collect do |question|
+ truncate(question.journal.notes, :length => Question::TruncateTo)
+ end.join(", ")
+ end
+ end
+ end
+ end
+end
diff --git a/lib/question_plugin/patches/journal_observer_patch.rb b/lib/question_plugin/patches/journal_observer_patch.rb
deleted file mode 100644
index 9a64d52..0000000
--- a/lib/question_plugin/patches/journal_observer_patch.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-module QuestionPlugin
- module Patches
- module JournalObserverPatch
- def self.included(base)
- base.extend(ClassMethods)
-
- base.send(:include, InstanceMethods)
- base.class_eval do
- unloadable
-
- alias_method_chain :after_create, :question
- end
- end
-
- module ClassMethods
- end
-
- module InstanceMethods
- def after_create_with_question(journal)
- after_create_without_question(journal)
-
- if journal.is_a?(IssueJournal)
- if journal.question
- journal.question.save
- QuestionMailer.deliver_asked_question(journal)
- end
-
- # Close any open questions
- if journal.journaled.present? && journal.journaled.pending_question?(journal.user)
- journal.journaled.close_pending_questions(journal.user, journal)
- end
- end
-
-
- end
-
- end
- end
- end
-end
diff --git a/lib/question_plugin/patches/journal_patch.rb b/lib/question_plugin/patches/journal_patch.rb
new file mode 100644
index 0000000..774201a
--- /dev/null
+++ b/lib/question_plugin/patches/journal_patch.rb
@@ -0,0 +1,45 @@
+module QuestionPlugin
+ module Patches
+ module JournalPatch
+ def self.included(base)
+
+ base.extend(ClassMethods)
+
+ base.send(:include, InstanceMethods)
+ base.class_eval do
+ unloadable
+ has_one :question, :dependent => :destroy
+
+
+ class << self
+ alias_method_chain :preload_journals_details_custom_fields, :question
+ end
+
+ #
+ # used for redmine >= 2.3.2
+ #
+ alias_method_chain :send_notification, :question unless ActiveSupport::Dependencies::search_for_file('journal_observer')
+ end
+ end
+
+ module ClassMethods
+ def preload_journals_details_custom_fields_with_question(journals)
+
+ # preload questions for all journal entries for faster display
+ ActiveRecord::Associations::Preloader.new.preload(journals, :question)
+
+ preload_journals_details_custom_fields_without_question(journals)
+ end
+ end
+
+ module InstanceMethods
+ def send_notification_with_question
+ send_notification_without_question
+ if question
+ QuestionMailer.asked_question(self).deliver
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/question_plugin/patches/queries_helper_patch.rb b/lib/question_plugin/patches/queries_helper_patch.rb
new file mode 100644
index 0000000..4dd776e
--- /dev/null
+++ b/lib/question_plugin/patches/queries_helper_patch.rb
@@ -0,0 +1,58 @@
+module QuestionPlugin
+ module Patches
+ module QueriesHelperPatch
+ def self.included(base) # :nodoc:
+ base.extend(ClassMethods)
+
+ base.send(:include, InstanceMethods)
+
+ base.class_eval do
+ unloadable
+ alias_method_chain :column_content, :question
+ end
+ end
+
+ module ClassMethods
+ end
+
+ module InstanceMethods
+
+
+ # removed format_questions for compatibility with redmine >= 2.3
+ # see http://www.redmine.org/boards/3/topics/37345,
+ # http://www.redmine.org/issues/13753
+ def column_content_with_question(column, issue)
+ if column.name == :formatted_questions
+ return '' if issue.open_questions.empty?
+ html = ''
+ issue.open_questions.each do |question|
+ html << "- "
+ html << "
"
+ html << " "
+ html << link_to(h(truncate(question.journal.notes, :length => Question::TruncateTo)),
+ :controller => 'issues',
+ :action => 'show',
+ :id => question.issue,
+ :anchor => "question-#{question.id}")
+ html << " "
+ html << " "
+ html << link_to_issue(question.issue)
+ html << ": #{h(question.journal.notes)}
"
+ html << "#{l(:question_text_asked_by)}: #{question.author.to_s}
"
+ html << "#{l(:question_text_assigned_to)}: #{question.assigned_to.to_s}
"
+ html << "#{l(:question_text_created_on)}: #{format_date(question.journal.created_on)}"
+ html << " "
+ html << "
"
+ html << " "
+ end
+ html << '
'
+ return html
+ else
+ column_content_without_question(column, issue)
+ end
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/question_plugin/patches/query_patch.rb b/lib/question_plugin/patches/query_patch.rb
new file mode 100644
index 0000000..287f592
--- /dev/null
+++ b/lib/question_plugin/patches/query_patch.rb
@@ -0,0 +1,94 @@
+module QuestionPlugin
+ module Patches
+ module QueryPatch
+ 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
+
+ base.add_available_column(QueryColumn.new(:formatted_questions))
+
+ alias_method_chain :available_filters, :question
+ alias_method_chain :sql_for_field, :question
+
+ end
+ end
+
+ module ClassMethods
+ unless Query.respond_to?(:available_columns=)
+ # Setter for +available_columns+ that isn't provided by the core.
+ def available_columns=(v)
+ self.available_columns = (v)
+ end
+ end
+
+ unless Query.respond_to?(:add_available_column)
+ # Method to add a column to the +available_columns+ that isn't provided by the core.
+ def add_available_column(column)
+ self.available_columns << (column)
+ end
+ end
+ end
+
+ module InstanceMethods
+ # Wrapper around the +available_filters+ to add a new Question filter
+ def available_filters_with_question
+ return @available_filters if @available_filters
+ available_filters_without_question
+
+ if @available_filters["assigned_to_id"]
+ user_values = @available_filters["assigned_to_id"][:values]
+
+ @available_filters["question_assigned_to_id"] = { :name => l("question_text_assigned_to"), :type => :list_optional, :order => 16, :values => user_values }
+ @available_filters["question_asked_by_id"] = { :name => l("question_text_asked_by"), :type => :list_optional, :order => 16, :values => user_values }
+ end
+
+ @available_filters
+ end
+
+ # Wrapper for +sql_for_field+ so Questions can use a different table than Issues
+ def sql_for_field_with_question(field, operator, v, db_table, db_field, is_custom_filter=false)
+ if field == "question_assigned_to_id" || field == "question_asked_by_id"
+ v = values_for(field).clone
+
+ db_table = Question.table_name
+ if field == "question_assigned_to_id"
+ db_field = 'assigned_to_id'
+ else
+ db_field = 'author_id'
+ end
+
+ # "me" value subsitution
+ v.push(User.current.logged? ? User.current.id.to_s : "0") if v.delete("me")
+ where_sql = []
+ case operator
+ when "="
+ where_sql << "#{db_table}.#{db_field} in (?)"
+ when "!"
+ where_sql << "#{db_table}.#{db_field} not in (?)"
+ when "!*"
+ where_sql << "#{db_table}.#{db_field} is null"
+# when "*"
+ end
+
+ where_sql << "#{db_table}.opened = true"
+
+ subselect_sql = Question.select("#{Journal.table_name}.journalized_id")
+ .joins(:journal)
+ .where(where_sql.join(' and '),[v.join(",")]).to_sql;
+
+ sql = "#{Issue.table_name}.id in (#{subselect_sql})"
+
+ return sql
+ else
+ return sql_for_field_without_question(field, operator, v, db_table, db_field, is_custom_filter)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/question_queries_helper_patch.rb b/lib/question_queries_helper_patch.rb
deleted file mode 100644
index 35a3bac..0000000
--- a/lib/question_queries_helper_patch.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-module QuestionQueriesHelperPatch
- def self.included(base) # :nodoc:
- base.extend(ClassMethods)
-
- base.send(:include, InstanceMethods)
-
- base.class_eval do
- alias_method :default_column_content, :column_content
- alias_method :column_content, :question_column_content
- end
- end
-
- module ClassMethods
- end
-
- module InstanceMethods
- def question_column_content(column, issue)
- if column.name == :formatted_questions
- return format_questions(issue.open_questions)
- else
- default_column_content(column, issue)
- end
- end
-
- def format_questions(questions)
- return '' if questions.empty?
- html = ''
- questions.each do |question|
- html << "- "
- html << "
"
- html << " "
- html << link_to(h(truncate(question.journal.notes, Question::TruncateTo)),
- :controller => 'issues',
- :action => 'show',
- :id => question.issue,
- :anchor => "question-#{question.id}")
- html << " "
- html << " "
- html << link_to_issue(question.issue)
- html << ": #{h(question.journal.notes)}
"
- html << "#{l(:question_text_asked_by)}: #{question.author.to_s}
"
- html << "#{l(:question_text_assigned_to)}: #{question.assigned_to.to_s}
"
- html << "#{l(:question_text_created_on)}: #{format_date(question.journal.created_on)}"
- html << " "
- html << "
"
- html << " "
- end
- html << '
'
- return html
- end
- end
-end
-
-
diff --git a/lib/question_query_patch.rb b/lib/question_query_patch.rb
deleted file mode 100644
index 20ff601..0000000
--- a/lib/question_query_patch.rb
+++ /dev/null
@@ -1,93 +0,0 @@
-module QuestionQueryPatch
- 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
- base.add_available_column(QueryColumn.new(:formatted_questions))
-
-
- alias_method :available_filters_before_question, :available_filters
- alias_method :available_filters, :question_available_filters
-
- alias_method :sql_for_field_before_question, :sql_for_field
- alias_method :sql_for_field, :question_sql_for_field
- end
-
- end
-
- module ClassMethods
- unless Query.respond_to?(:available_columns=)
- # Setter for +available_columns+ that isn't provided by the core.
- def available_columns=(v)
- self.available_columns = (v)
- end
- end
-
- unless Query.respond_to?(:add_available_column)
- # Method to add a column to the +available_columns+ that isn't provided by the core.
- def add_available_column(column)
- self.available_columns << (column)
- end
- end
- end
-
- module InstanceMethods
-
- # Wrapper around the +available_filters+ to add a new Question filter
- def question_available_filters
- @available_filters = available_filters_before_question
-
- user_values = []
- user_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
- if project
- user_values += project.users.sort.collect{|s| [s.name, s.id.to_s] }
- else
- user_values += User.current.projects.collect(&:users).flatten.uniq.sort.collect{|s| [s.name, s.id.to_s] }
- end
-
- question_filters = {
- "question_assigned_to_id" => { :type => :list, :order => 14, :values => user_values },
- "question_asked_by_id" => { :type => :list, :order => 14, :values => user_values }
- }
-
- return @available_filters.merge(question_filters)
- end
-
- # Wrapper for +sql_for_field+ so Questions can use a different table than Issues
- def question_sql_for_field(field, operator, v, db_table, db_field, is_custom_filter=false)
- if field == "question_assigned_to_id" || field == "question_asked_by_id"
- v = values_for(field).clone
-
- db_table = Question.table_name
- if field == "question_assigned_to_id"
- db_field = 'assigned_to_id'
- else
- db_field = 'author_id'
- end
-
-
- # "me" value subsitution
- v.push(User.current.logged? ? User.current.id.to_s : "0") if v.delete("me")
-
- case operator
- when "="
- sql = "#{db_table}.#{db_field} IN (" + v.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ") AND #{db_table}.opened = true"
- when "!"
- sql = "(#{db_table}.#{db_field} IS NULL OR #{db_table}.#{db_field} NOT IN (" + v.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")) AND #{db_table}.opened = true"
- end
-
- return sql
-
- else
- return sql_for_field_before_question(field, operator, v, db_table, db_field, is_custom_filter)
- end
-
- end
-
- end
-end
-
diff --git a/question_plugin.gemspec b/question_plugin.gemspec
index 7c034b5..bde82ae 100644
--- a/question_plugin.gemspec
+++ b/question_plugin.gemspec
@@ -22,7 +22,7 @@ Gem::Specification.new do |s|
"README.rdoc",
"Rakefile",
"VERSION",
- "app/controllers/questions_controller.rb",
+ "app/controllers/issuequestions_controller.rb",
"app/models/journal_questions_observer.rb",
"app/models/question.rb",
"app/models/question_mailer.rb",
diff --git a/rails/init.rb b/rails/init.rb
deleted file mode 100644
index 48087f0..0000000
--- a/rails/init.rb
+++ /dev/null
@@ -1 +0,0 @@
-require File.dirname(__FILE__) + "/../init"
diff --git a/test/integration/answering_question_test.rb b/test/integration/answering_question_test.rb
index 38feded..f8b81ef 100644
--- a/test/integration/answering_question_test.rb
+++ b/test/integration/answering_question_test.rb
@@ -10,9 +10,7 @@ class AnsweringQuestionTest < ActionController::IntegrationTest
@question = Question.new(:assigned_to => @author, :author => @author, :issue => @issue)
@issue.journal_notes = "Question"
- @issue.extra_journal_attributes = {
- :question => @question
- }
+ @issue.question = @question
assert @issue.save
ActionMailer::Base.deliveries.clear
end
diff --git a/test/integration/asking_question_test.rb b/test/integration/asking_question_test.rb
index 8f7ab2e..d56b42e 100644
--- a/test/integration/asking_question_test.rb
+++ b/test/integration/asking_question_test.rb
@@ -10,9 +10,7 @@ class AskingQuestionTest < ActionController::IntegrationTest
@issue = Issue.generate_for_project!(@project)
@issue.journal_notes = "Question"
- @issue.extra_journal_attributes = {
- :question => Question.new(:assigned_to => @author, :author => @author, :issue => @issue)
- }
+ @issue.question = Question.new(:assigned_to => @author, :author => @author, :issue => @issue)
assert_difference("Question.count") do
assert @issue.save
diff --git a/test/integration/mail_handler_test.rb b/test/integration/mail_handler_test.rb
index 18cf653..9a03940 100644
--- a/test/integration/mail_handler_test.rb
+++ b/test/integration/mail_handler_test.rb
@@ -12,7 +12,7 @@ class MailHandlerTest < ActionController::IntegrationTest
@issue = Issue.generate_for_project!(@project)
@question = Question.new(:issue => @issue, :author => @asker, :assigned_to => @responder)
@issue.journal_notes = "Test"
- @issue.extra_journal_attributes = { :question => @question }
+ @issue.question = @question
assert @issue.save
@question_journal = @issue.journals.last
diff --git a/test/integration/my_page_blocks_test.rb b/test/integration/my_page_blocks_test.rb
index 3631c5a..47e2877 100644
--- a/test/integration/my_page_blocks_test.rb
+++ b/test/integration/my_page_blocks_test.rb
@@ -9,7 +9,7 @@ def setup
@question = Question.new(:issue => @issue, :author => @me, :assigned_to => @me)
@issue.journal_notes = "Test"
@issue.journal_user = @me
- @issue.extra_journal_attributes = { :question => @question }
+ @issue.question = @question
assert @issue.save
@question_journal = @issue.journals.last
end
diff --git a/test/integration/question_plugin/hooks/question_issue_hooks_test.rb b/test/integration/question_plugin/hooks/question_issue_hooks_test.rb
index b070571..5980624 100644
--- a/test/integration/question_plugin/hooks/question_issue_hooks_test.rb
+++ b/test/integration/question_plugin/hooks/question_issue_hooks_test.rb
@@ -103,7 +103,7 @@ def call_hook(context)
@issue = Issue.generate_for_project!(@project)
@question = Question.new(:author => @user1, :assigned_to => @user1, :opened => true, :issue => @issue)
@issue.journal_notes = "A note"
- @issue.extra_journal_attributes = { :question => @question }
+ @issue.question = @question
assert @issue.save
@journal = @issue.journals.last
User.add_to_project(@user1, @project, Role.generate!(:permissions => [:view_issues, :add_issues, :edit_issues]))
diff --git a/test/test_helper.rb b/test/test_helper.rb
index e4c3ddb..087988f 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -1,9 +1,5 @@
# Load the normal Rails helper
-require File.expand_path(File.dirname(__FILE__) + '/../../../../test/test_helper')
-
-# Ensure that we are using the temporary fixture path
-Engines::Testing.set_fixture_path
-
+require File.expand_path(File.dirname(__FILE__) + '/../../../test/test_helper')
require "webrat"
diff --git a/test/unit/lib/question_plugin/patches/queries_helper_patch_test.rb b/test/unit/lib/question_plugin/patches/queries_helper_patch_test.rb
index f8bb367..665e252 100644
--- a/test/unit/lib/question_plugin/patches/queries_helper_patch_test.rb
+++ b/test/unit/lib/question_plugin/patches/queries_helper_patch_test.rb
@@ -34,7 +34,7 @@ def for_assert_select(response_text)
@assignee = User.generate!
@question = Question.new(:issue => @issue, :author => @author, :assigned_to => @assignee)
@issue.journal_notes = @content
- @issue.extra_journal_attributes = { :question => @question }
+ @issue.question = @question
assert @issue.save
@questions = [@question]
end
@@ -82,12 +82,12 @@ def for_assert_select(response_text)
@question_two = Question.new(:issue => @issue, :author => @author, :assigned_to => @assignee)
@issue.journal_notes = @content_one
- @issue.extra_journal_attributes = { :question => @question }
+ @issue.question = @question
assert @issue.save && @issue.reload
@journal_one = @issue.journals.last
@issue.journal_notes = @content_two
- @issue.extra_journal_attributes = { :question => @question_two }
+ @issue.question = @question_two
assert @issue.save && @issue.reload
@journal_two = @issue.journals.last
@@ -116,7 +116,7 @@ def for_assert_select(response_text)
@assignee = User.generate!
@question = Question.new(:issue => @issue, :author => @author, :assigned_to => @assignee)
@issue.journal_notes = "A question"
- @issue.extra_journal_attributes = { :question => @question }
+ @issue.question = @question
assert @issue.save
end
diff --git a/test/unit/question_mailer_test.rb b/test/unit/question_mailer_test.rb
index 03252b3..f0f7a12 100644
--- a/test/unit/question_mailer_test.rb
+++ b/test/unit/question_mailer_test.rb
@@ -13,7 +13,7 @@ class QuestionMailerTest < ActiveSupport::TestCase
@issue = Issue.generate_for_project!(@project, :subject => "Add new stuff")
@question = Question.new(:assigned_to => @user, :author => @author, :issue => @issue)
@issue.journal_notes = "This is the question for the user"
- @issue.extra_journal_attributes = { :question => @question }
+ @issue.question = @question
assert @issue.save
@journal = @issue.journals.last
@@ -81,7 +81,7 @@ class QuestionMailerTest < ActiveSupport::TestCase
@question = Question.new(:assigned_to => nil, :author => @author, :issue => @issue)
@issue.journal_notes = "This is the question for the user"
- @issue.extra_journal_attributes = { :question => @question }
+ @issue.question = @question
assert @issue.save
@journal = @issue.journals.last
@@ -101,7 +101,7 @@ class QuestionMailerTest < ActiveSupport::TestCase
@question = Question.new(:assigned_to => @user, :author => @author, :issue => @issue)
@issue.journal_notes = "This is the question for the user"
- @issue.extra_journal_attributes = { :question => @question }
+ @issue.question = @question
assert @issue.save
@journal_with_question = @issue.journals.last