Actualizado plugin Redmine Questions 1.0.0 light
This commit is contained in:
parent
27e60f8ec1
commit
5d7889f1c9
140 changed files with 5342 additions and 1430 deletions
290
plugins/redmine_questions/app/models/question.rb
Normal file
290
plugins/redmine_questions/app/models/question.rb
Normal file
|
@ -0,0 +1,290 @@
|
|||
# This file is a part of Redmine Q&A (redmine_questions) plugin,
|
||||
# Q&A plugin for Redmine
|
||||
#
|
||||
# Copyright (C) 2011-2018 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_questions is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# redmine_questions is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with redmine_questions. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
class Question < ActiveRecord::Base
|
||||
unloadable
|
||||
|
||||
include Redmine::SafeAttributes
|
||||
extend ApplicationHelper
|
||||
|
||||
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
|
||||
belongs_to :section, :class_name => 'QuestionsSection', :foreign_key => 'section_id'
|
||||
belongs_to :status, :class_name => 'QuestionsStatus', :foreign_key => 'status_id'
|
||||
|
||||
delegate :section_type, :to => :section, :allow_nil => true
|
||||
|
||||
has_many :answers, :class_name => 'QuestionsAnswer', :dependent => :destroy
|
||||
|
||||
if ActiveRecord::VERSION::MAJOR >= 4
|
||||
has_many :comments, lambda { order('created_on') }, :as => :commented, :dependent => :delete_all
|
||||
else
|
||||
has_many :comments, :as => :commented, :dependent => :delete_all, :order => "created_on"
|
||||
end
|
||||
rcrm_acts_as_viewed
|
||||
|
||||
acts_as_attachable_questions
|
||||
acts_as_watchable
|
||||
|
||||
acts_as_event :datetime => :created_on,
|
||||
:url => Proc.new {|o| {:controller => 'questions', :action => 'show', :id => o }},
|
||||
:type => Proc.new {|o| 'icon ' + (o.is_solution? ? 'icon-solution': 'icon-question')},
|
||||
:description => :content,
|
||||
:title => Proc.new {|o| o.subject }
|
||||
|
||||
|
||||
if ActiveRecord::VERSION::MAJOR >= 4
|
||||
acts_as_activity_provider :type => 'questions',
|
||||
:permission => :view_questions,
|
||||
:author_key => :author_id,
|
||||
:timestamp => "#{table_name}.created_on",
|
||||
:scope => joins({:section => :project}, :author)
|
||||
acts_as_searchable :columns => ["#{table_name}.subject",
|
||||
"#{table_name}.content",
|
||||
"#{QuestionsAnswer.table_name}.content"],
|
||||
:scope => joins({:section => :project}, :answers),
|
||||
:project_key => "#{QuestionsSection.table_name}.project_id"
|
||||
else
|
||||
acts_as_activity_provider :type => 'questions',
|
||||
:permission => :view_questions,
|
||||
:author_key => :author_id,
|
||||
:timestamp => "#{table_name}.created_on",
|
||||
:find_options => { :include => [{:section => :project}, :author] }
|
||||
acts_as_searchable :columns => ["#{table_name}.subject",
|
||||
"#{table_name}.content",
|
||||
"#{QuestionsAnswer.table_name}.content"],
|
||||
:include => [{:section => :project}, :answers],
|
||||
:project_key => "#{QuestionsSection.table_name}.project_id"
|
||||
end
|
||||
|
||||
scope :solutions, lambda { joins(:section).where(:questions_sections => {:section_type => QuestionsSection::SECTION_TYPE_SOLUTIONS}) }
|
||||
scope :questions, lambda { joins(:section).where(:questions_sections => {:section_type => QuestionsSection::SECTION_TYPE_QUESTIONS}) }
|
||||
scope :by_votes, lambda { order("#{Question.table_name}.cached_weighted_score DESC") }
|
||||
scope :by_date, lambda { order("#{Question.table_name}.created_on DESC") }
|
||||
scope :by_update, lambda { order("#{Question.table_name}.updated_on DESC") }
|
||||
scope :by_views, lambda { order("#{Question.table_name}.views DESC") }
|
||||
scope :positive, lambda { where("#{Question.table_name}.cached_weighted_score > 0") }
|
||||
scope :featured, lambda {|*args| where(:featured => true) }
|
||||
scope :in_section, lambda { |section|
|
||||
where(:section_id => section) if section.present?
|
||||
}
|
||||
scope :in_project, lambda { |project|
|
||||
joins(:section => :project).where("#{QuestionsSection.table_name}.project_id = ?", project) if project.present?
|
||||
}
|
||||
scope :visible, lambda { |*args|
|
||||
joins(:section => :project)
|
||||
.where(Project.allowed_to_condition(args.shift || User.current, :view_questions, *args))
|
||||
}
|
||||
|
||||
validates_presence_of :author, :content, :subject, :section
|
||||
|
||||
after_create :add_author_as_watcher
|
||||
after_create :send_notification
|
||||
|
||||
safe_attributes 'author',
|
||||
'subject',
|
||||
'content',
|
||||
'tag_list',
|
||||
'section_id',
|
||||
'status_id'
|
||||
|
||||
safe_attributes 'status_id',
|
||||
:if => lambda {|question, user| question.is_idea?}
|
||||
|
||||
def self.visible_condition(user)
|
||||
user.reload if user
|
||||
global_questions_allowed = user.allowed_to?(:view_questions, nil)
|
||||
projects_allowed_to_view_questions = Project.where(Project.allowed_to_condition(user, :view_questions)).pluck(:id)
|
||||
allowed_to_view_condition = global_questions_allowed ? "(#{table_name}.project_id IS NULL)" : '(0=1)'
|
||||
allowed_to_view_condition += projects_allowed_to_view_questions.empty? ? ' OR (0=1) ' : " OR (#{table_name}.project_id IN (#{projects_allowed_to_view_questions.join(',')}))"
|
||||
|
||||
user.admin? ? '(1=1)' : allowed_to_view_condition
|
||||
end
|
||||
|
||||
def self.related(question, limit)
|
||||
tokens = question.subject.strip.scan(%r{((\s|^)"[\s\w]+"(\s|$)|\S+)}).
|
||||
collect {|m| m.first.gsub(%r{(^\s*"\s*|\s*"\s*$)}, '').gsub(%r{('|"|`)}, '')}.select{|m| m.size > 3} || ""
|
||||
|
||||
related_questions = where(tokens.map{ |t| "LOWER(subject) LIKE LOWER('%#{t}%')" }.join(' OR '))
|
||||
related_questions = related_questions.in_project(question.project)
|
||||
related_questions = related_questions.where("#{Question.table_name}.id != ?", question.id)
|
||||
related_questions.limit(limit).to_a.compact
|
||||
end
|
||||
|
||||
def commentable?(user = User.current)
|
||||
return false if locked?
|
||||
user.allowed_to?(:comment_question, project)
|
||||
end
|
||||
|
||||
def visible?(user=User.current)
|
||||
user.allowed_to?(:view_questions, project)
|
||||
end
|
||||
|
||||
def to_param
|
||||
"#{id}-#{ActiveSupport::Inflector.transliterate(subject || " ").parameterize}"
|
||||
end
|
||||
|
||||
def section_name
|
||||
section.try(:name)
|
||||
end
|
||||
|
||||
def project
|
||||
section.project
|
||||
end
|
||||
|
||||
def allow_voting?
|
||||
false
|
||||
end
|
||||
|
||||
def allow_liking?
|
||||
section.allow_liking?
|
||||
end
|
||||
|
||||
def allow_answering?
|
||||
!locked? && section.allow_answering?
|
||||
end
|
||||
|
||||
def last_reply
|
||||
answers.order('created_on DESC').last
|
||||
end
|
||||
|
||||
def last_comment
|
||||
Comment.where(:commented_type => self.class.name, :commented_id => [id] + answer_ids).order('created_on DESC').last
|
||||
end
|
||||
|
||||
def replies_count
|
||||
answers.count
|
||||
end
|
||||
|
||||
def editable_by?(user)
|
||||
(author == user && user.allowed_to?(:edit_own_questions, project)) ||
|
||||
user.allowed_to?(:edit_questions, project)
|
||||
end
|
||||
|
||||
def destroyable_by?(user)
|
||||
user.allowed_to?(:delete_questions, project)
|
||||
end
|
||||
|
||||
def votable_by?(user)
|
||||
user.allowed_to?(:vote_questions, project)
|
||||
end
|
||||
|
||||
def convertable_by?(user)
|
||||
return false if project.blank?
|
||||
user.allowed_to?(:convert_questions, project)
|
||||
end
|
||||
|
||||
def answered?
|
||||
answers.where(:accepted => true).any?
|
||||
end
|
||||
|
||||
def is_question?
|
||||
section && section.is_questions?
|
||||
end
|
||||
|
||||
def is_solution?
|
||||
section && section.is_solutions?
|
||||
end
|
||||
|
||||
def is_idea?
|
||||
section && section.is_ideas?
|
||||
end
|
||||
|
||||
# def to_issue
|
||||
# issue = Issue.new
|
||||
# issue.author = self.author
|
||||
# issue.created_on = self.created_on
|
||||
# issue.subject = self.subject
|
||||
# issue.description = self.content
|
||||
# issue.watchers = self.watchers
|
||||
# issue.attachments = self.attachments
|
||||
# issue.project = self.project
|
||||
# issue.tracker = self.project.trackers.first
|
||||
# issue.status = IssueStatus.first
|
||||
# self.answers.each do |ans|
|
||||
# journal = Journal.new(:notes => ans.content, :user => ans.author)
|
||||
# issue.journals << journal
|
||||
# end
|
||||
# issue
|
||||
# end
|
||||
|
||||
# def self.from_issue(issue)
|
||||
# question = Question.new
|
||||
# question.author = issue.author
|
||||
# question.created_on = issue.created_on
|
||||
# question.subject = issue.subject
|
||||
# question.content = issue.description.blank? ? issue.subject : issue.description
|
||||
# question.watchers = issue.watchers
|
||||
# question.attachments = issue.attachments
|
||||
# question.project = issue.project
|
||||
# question.section = issue.project.questions_sections.first
|
||||
# issue.journals.select{|j| j.notes.present?}.each do |journal|
|
||||
# reply = Question.new
|
||||
# reply.author = journal.user
|
||||
# reply.created_on = journal.created_on
|
||||
# reply.content = journal.notes
|
||||
# reply.project = issue.project
|
||||
# reply.question = question
|
||||
# question.answers << reply
|
||||
# end
|
||||
# question
|
||||
# end
|
||||
|
||||
def notified_users
|
||||
project.notified_users.select { |user| visible?(user) }.collect(&:mail)
|
||||
end
|
||||
|
||||
def self.to_text(input)
|
||||
textile_glyphs = {
|
||||
'’' => "'",
|
||||
'‘' => "'",
|
||||
'<' => '<',
|
||||
'>' => '>',
|
||||
'”' => "'",
|
||||
'“' => '"',
|
||||
'…' => '...',
|
||||
'\1—' => '--',
|
||||
' → ' => '->',
|
||||
'¶' => ' ',
|
||||
' – ' => '-',
|
||||
'×' => '-',
|
||||
'™' => '(TM)',
|
||||
'®' => '(R)',
|
||||
'©' => '(C)',
|
||||
'&' => '&'
|
||||
}.freeze
|
||||
|
||||
html_regexp = /<(?:[^>"']+|"(?:\\.|[^\\"]+)*"|'(?:\\.|[^\\']+)*')*>/xm
|
||||
|
||||
input.dup.gsub(html_regexp, '').tap do |h|
|
||||
textile_glyphs.each do |entity, char|
|
||||
h.gsub!(entity, char)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_author_as_watcher
|
||||
Watcher.create(:watchable => self, :user => author)
|
||||
end
|
||||
|
||||
def send_notification
|
||||
Mailer.question_question_added(self).deliver if Setting.notified_events.include?('question_added')
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue