Actualizar plugin Questions a 1.0.2 light

This commit is contained in:
Manuel Cillero 2020-11-22 21:40:10 +01:00
parent b9e569d03f
commit b37d1305f1
64 changed files with 394 additions and 229 deletions

View file

@ -1,7 +1,7 @@
# This file is a part of Redmine Q&A (redmine_questions) plugin,
# Q&A plugin for Redmine
#
# Copyright (C) 2011-2018 RedmineUP
# Copyright (C) 2011-2020 RedmineUP
# http://www.redmineup.com/
#
# redmine_questions is free software: you can redistribute it and/or modify
@ -34,9 +34,11 @@ class QuestionsAnswersController < ApplicationController
end
def edit
(render_403; return false) unless @answer.editable_by?(User.current)
end
def update
(render_403; return false) unless @answer.editable_by?(User.current) || User.current.allowed_to?(:accept_answers, @project)
@answer.safe_attributes = params[:answer]
@answer.save_attachments(params[:attachments])
if @answer.save
@ -87,7 +89,7 @@ class QuestionsAnswersController < ApplicationController
private
def redirect_to_question
redirect_to question_path(@answer.question, :anchor => "question_item_#{@answer.id}")
redirect_to question_path(@answer.question, :anchor => "questions_answer_#{@answer.id}")
end
def find_answer

View file

@ -1,7 +1,7 @@
# This file is a part of Redmine Q&A (redmine_questions) plugin,
# Q&A plugin for Redmine
#
# Copyright (C) 2011-2018 RedmineUP
# Copyright (C) 2011-2020 RedmineUP
# http://www.redmineup.com/
#
# redmine_questions is free software: you can redistribute it and/or modify

View file

@ -1,7 +1,7 @@
# This file is a part of Redmine Q&A (redmine_questions) plugin,
# Q&A plugin for Redmine
#
# Copyright (C) 2011-2018 RedmineUP
# Copyright (C) 2011-2020 RedmineUP
# http://www.redmineup.com/
#
# redmine_questions is free software: you can redistribute it and/or modify
@ -54,6 +54,7 @@ class QuestionsController < ApplicationController
end
def update
(render_403; return false) unless @question_item.editable_by?(User.current)
@question_item.safe_attributes = params[:question]
@question_item.save_attachments(params[:attachments])
if @question_item.save
@ -133,6 +134,7 @@ class QuestionsController < ApplicationController
@text = (params[:question] ? params[:question][:content] : nil)
render :partial => 'common/preview'
end
private
def find_questions
@ -142,10 +144,10 @@ class QuestionsController < ApplicationController
scope = Question.visible
scope = scope.where(:section_id => @section) if @section
columns = ["subject", "content"]
tokens = seach.to_s.scan(%r{((\s|^)"[\s\w]+"(\s|$)|\S+)}).collect{|m| m.first.gsub(%r{(^\s*"\s*|\s*"\s*$)}, '')}.uniq.select {|w| w.length > 1 }
columns = ['subject', 'content']
tokens = seach.to_s.scan(%r{((\s|^)"[\s\w]+"(\s|$)|\S+)}).collect { |m| m.first.gsub(%r{(^\s*"\s*|\s*"\s*$)}, '') }.uniq.select { |w| w.length > 1 }
tokens = [] << tokens unless tokens.is_a?(Array)
token_clauses = columns.collect {|column| "(LOWER(#{column}) LIKE ?)"}
token_clauses = columns.collect { |column| "(LOWER(#{column}) LIKE ?)" }
sql = (['(' + token_clauses.join(' OR ') + ')'] * tokens.size).join(' AND ')
find_options = [sql, * (tokens.collect {|w| "%#{w.downcase}%"} * token_clauses.size).sort]
@ -166,9 +168,9 @@ class QuestionsController < ApplicationController
end
@limit = per_page_option
@offset = params[:page].to_i*@limit
@offset = params[:page].to_i * @limit
scope = scope.limit(@limit).offset(@offset)
scope = scope.tagged_with(params[:tag]) unless params[:tag].blank?
scope = scope.tagged_with(params[:tag]) if params[:tag].present?
@topic_count = scope.count
@topic_pages = Paginator.new @topic_count, @limit, params[:page]
@ -178,7 +180,7 @@ class QuestionsController < ApplicationController
def find_section
@section = QuestionsSection.find_by_id(params[:section_id] || (params[:question] && params[:question][:section_id]))
@section ||= @project.questions_sections.first
@section ||= @project.questions_sections.first if @project
rescue ActiveRecord::RecordNotFound
render_404
end

View file

@ -1,7 +1,7 @@
# This file is a part of Redmine Q&A (redmine_questions) plugin,
# Q&A plugin for Redmine
#
# Copyright (C) 2011-2018 RedmineUP
# Copyright (C) 2011-2020 RedmineUP
# http://www.redmineup.com/
#
# redmine_questions is free software: you can redistribute it and/or modify
@ -58,6 +58,7 @@ class QuestionsSectionsController < ApplicationController
def update
@section.safe_attributes = params[:questions_section]
@section.insert_at(@section.position) if @section.position_changed?
if @section.save
respond_to do |format|
format.html do

View file

@ -1,7 +1,7 @@
# This file is a part of Redmine Q&A (redmine_questions) plugin,
# Q&A plugin for Redmine
#
# Copyright (C) 2011-2018 RedmineUP
# Copyright (C) 2011-2020 RedmineUP
# http://www.redmineup.com/
#
# redmine_questions is free software: you can redistribute it and/or modify
@ -39,12 +39,13 @@ class QuestionsStatusesController < ApplicationController
end
def create
@questions_status = QuestionsStatus.new(params[:questions_status])
@questions_status = QuestionsStatus.new
@questions_status.safe_attributes = params[:questions_status]
if request.post? && @questions_status.save
flash[:notice] = l(:notice_successful_create)
redirect_to :action => "plugin", :id => "redmine_questions", :controller => "settings", :tab => 'questions_statuses'
redirect_to action: 'plugin', id: 'redmine_questions', controller: 'settings', tab: 'questions_statuses'
else
render :action => 'new'
render action: 'new'
end
end
@ -54,17 +55,18 @@ class QuestionsStatusesController < ApplicationController
def update
@questions_status = QuestionsStatus.find(params[:id])
if @questions_status.update_attributes(params[:questions_status])
@questions_status.safe_attributes = params[:questions_status]
if @questions_status.save
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'plugin', :id => 'redmine_questions', :controller => 'settings', :tab => 'questions_statuses'
redirect_to action: 'plugin', id: 'redmine_questions', controller: 'settings', tab: 'questions_statuses'
}
format.js { head 200 }
end
else
respond_to do |format|
format.html { render :action => 'edit' }
format.html { render action: 'edit' }
format.js { head 422 }
end
end
@ -72,11 +74,9 @@ class QuestionsStatusesController < ApplicationController
def destroy
QuestionsStatus.find(params[:id]).destroy
redirect_to :action =>"plugin", :id => "redmine_questions", :controller => "settings", :tab => 'questions_statuses'
redirect_to action: 'plugin', id: 'redmine_questions', controller: 'settings', tab: 'questions_statuses'
rescue
flash[:error] = l(:error_products_unable_delete_questions_status)
redirect_to :action =>"plugin", :id => "redmine_questions", :controller => "settings", :tab => 'questions_statuses'
redirect_to action: 'plugin', id: 'redmine_questions', controller: 'settings', tab: 'questions_statuses'
end
end

View file

@ -3,7 +3,7 @@
# This file is a part of Redmine Q&A (redmine_questions) plugin,
# Q&A plugin for Redmine
#
# Copyright (C) 2011-2018 RedmineUP
# Copyright (C) 2011-2020 RedmineUP
# http://www.redmineup.com/
#
# redmine_questions is free software: you can redistribute it and/or modify

View file

@ -1,7 +1,7 @@
# This file is a part of Redmine Q&A (redmine_questions) plugin,
# Q&A plugin for Redmine
#
# Copyright (C) 2011-2018 RedmineUP
# Copyright (C) 2011-2020 RedmineUP
# http://www.redmineup.com/
#
# redmine_questions is free software: you can redistribute it and/or modify
@ -54,22 +54,22 @@ class Question < ActiveRecord::Base
:author_key => :author_id,
:timestamp => "#{table_name}.created_on",
:scope => joins({:section => :project}, :author)
acts_as_searchable :columns => ["#{table_name}.subject",
"#{table_name}.content",
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"
: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",
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"
:project_key => "#{QuestionsSection.table_name}.project_id"
end
scope :solutions, lambda { joins(:section).where(:questions_sections => {:section_type => QuestionsSection::SECTION_TYPE_SOLUTIONS}) }
@ -114,7 +114,7 @@ class Question < ActiveRecord::Base
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
end
def self.related(question, limit)
tokens = question.subject.strip.scan(%r{((\s|^)"[\s\w]+"(\s|$)|\S+)}).
@ -127,7 +127,7 @@ class Question < ActiveRecord::Base
end
def commentable?(user = User.current)
return false if locked?
return false if locked?
user.allowed_to?(:comment_question, project)
end
@ -285,6 +285,6 @@ class Question < ActiveRecord::Base
end
def send_notification
Mailer.question_question_added(self).deliver if Setting.notified_events.include?('question_added')
Mailer.question_question_added(User.current, self).deliver if Setting.notified_events.include?('question_added')
end
end

View file

@ -1,7 +1,7 @@
# This file is a part of Redmine Q&A (redmine_questions) plugin,
# Q&A plugin for Redmine
#
# Copyright (C) 2011-2018 RedmineUP
# Copyright (C) 2011-2020 RedmineUP
# http://www.redmineup.com/
#
# redmine_questions is free software: you can redistribute it and/or modify
@ -97,6 +97,7 @@ class QuestionsAnswer < ActiveRecord::Base
end
def editable_by?(user)
(author == user && user.allowed_to?(:edit_own_answers, project)) ||
user.allowed_to?(:edit_questions, project)
end
@ -107,7 +108,7 @@ class QuestionsAnswer < ActiveRecord::Base
def votable_by?(user)
user.allowed_to?(:vote_questions, project)
end
private
def check_accepted
question.answers.update_all(:accepted => false) if question &&
@ -121,6 +122,6 @@ class QuestionsAnswer < ActiveRecord::Base
end
def send_notification
Mailer.question_answer_added(self).deliver if Setting.notified_events.include?('question_answer_added')
Mailer.question_answer_added(User.current, self).deliver if Setting.notified_events.include?('question_answer_added')
end
end

View file

@ -1,7 +1,7 @@
# This file is a part of Redmine Q&A (redmine_questions) plugin,
# Q&A plugin for Redmine
#
# Copyright (C) 2011-2018 RedmineUP
# Copyright (C) 2011-2020 RedmineUP
# http://www.redmineup.com/
#
# redmine_questions is free software: you can redistribute it and/or modify
@ -27,7 +27,12 @@ class QuestionsSection < ActiveRecord::Base
attr_protected :id if ActiveRecord::VERSION::MAJOR <= 4
safe_attributes 'name', 'project', 'position', 'description', 'section_type'
scope :with_questions_count, lambda { select("#{QuestionsSection.table_name}.*, count(#{QuestionsSection.table_name}.id) as questions_count").joins(:questions).order("project_id ASC").group("#{QuestionsSection.table_name}.id, #{QuestionsSection.table_name}.name, #{QuestionsSection.table_name}.project_id, #{QuestionsSection.table_name}.section_type") }
scope :with_questions_count, lambda {
select("#{QuestionsSection.table_name}.*, count(#{QuestionsSection.table_name}.id) as questions_count").
joins(:questions).
order("project_id ASC").
group(QuestionsSection.column_names.map { |column| "#{QuestionsSection.table_name}.#{column}" }.join(', '))
}
scope :for_project, lambda { |project| where(:project_id => project) unless project.blank? }
scope :visible, lambda {|*args|
joins(:project).

View file

@ -1,7 +1,7 @@
# This file is a part of Redmine Q&A (redmine_questions) plugin,
# Q&A plugin for Redmine
#
# Copyright (C) 2011-2018 RedmineUP
# Copyright (C) 2011-2020 RedmineUP
# http://www.redmineup.com/
#
# redmine_questions is free software: you can redistribute it and/or modify

View file

@ -1,7 +1,7 @@
# This file is a part of Redmine Q&A (redmine_questions) plugin,
# Q&A plugin for Redmine
#
# Copyright (C) 2011-2018 RedmineUP
# Copyright (C) 2011-2020 RedmineUP
# http://www.redmineup.com/
#
# redmine_questions is free software: you can redistribute it and/or modify
@ -26,7 +26,7 @@ class QuestionsStatus < ActiveRecord::Base
attr_protected :id if ActiveRecord::VERSION::MAJOR <= 4
safe_attributes 'name', 'is_closed', 'position', 'color'
validates :name, :presence => true, :uniqueness => true
validates :name, presence: true, uniqueness: true
scope :sorted, lambda { order(:position) }

View file

@ -1,36 +1,41 @@
<% @project_sections = QuestionsSection.for_project(@project).sorted %>
<h3><%= l(:label_questions_sections_plural) %></h3>
<table class="list questions_sections">
<thead>
<tr>
<th><%= l(:field_name) %></th>
<th><%=l(:field_type)%></th>
<th></th>
</tr>
</thead>
<tbody>
<% QuestionsSection.for_project(@project).sorted.each do |section| %>
<tr class="<%= cycle 'odd', 'even' %>">
<td class="name">
<%= h(section.name) %>
</td>
<td>
<%= section.l_type %>
</td>
<td class="buttons">
<% if User.current.allowed_to?(:manage_sections, @project) %>
<%= reorder_handle(section, :url => project_questions_section_path(@project, section), :param => 'questions_section') if respond_to?(:reorder_handle) %>
<%= link_to l(:button_edit), {:controller => 'questions_sections', :action => 'edit', :project_id => @project, :id => section}, :class => 'icon icon-edit' %>
<%= delete_link :controller => 'questions_sections', :action => 'destroy', :project_id => @project, :id => section %>
<% end %>
</td>
<% if @project_sections.any? %>
<table class="list questions_sections">
<thead>
<tr>
<th><%= l(:field_name) %></th>
<th><%=l(:field_type)%></th>
<th></th>
</tr>
<% end %>
</tbody>
</table>
</thead>
<tbody>
<% @project_sections.each do |section| %>
<tr class="<%= cycle 'odd', 'even' %>">
<td class="name">
<%= h(section.name) %>
</td>
<td>
<%= section.l_type %>
</td>
<td class="buttons">
<% if User.current.allowed_to?(:manage_sections, @project) %>
<%= reorder_handle(section, url: project_questions_section_path(@project, section), param: 'questions_section') if respond_to?(:reorder_handle) %>
<%= link_to l(:button_edit), edit_questions_section_path(section, project_id: @project), class: 'icon icon-edit' %>
<%= delete_link questions_section_path(section, project_id: @project) %>
<% end %>
</td>
</tr>
<% end %>
</tbody>
</table>
<% else %>
<p class="nodata"><%= l(:label_no_data) %></p>
<% end %>
<% if User.current.allowed_to?(:manage_sections, @project) %>
<%= link_to image_tag('add.png', :style => 'vertical-align: middle;')+l(:label_questions_section_new), :controller => 'questions_sections', :action => 'new', :project_id => @project %>
<%= link_to image_tag('add.png', style: 'vertical-align: middle;') + l(:label_questions_section_new), new_questions_section_path(project_id: @project) %>
<% end %>
<%= javascript_tag do %>

View file

@ -6,7 +6,7 @@
<%= f.text_field :subject, :id => "question_subject", :size => 120%>
</p>
<p><label><%= l(:label_questions_section) %></label><br />
<%= f.select :section_id, options_from_collection_for_select(QuestionsSection.where(:project_id => @project),:id, :name, f.object.section_id), :style => "width: 80%;", :required => true %>
<%= f.select :section_id, options_from_collection_for_select(QuestionsSection.where(:project_id => @project).sorted, :id, :name, f.object.section_id), :style => "width: 80%;", :required => true %>
<%= javascript_tag do %>
$('#question_section_id').change(function() {
$.ajax({

View file

@ -1,6 +1,6 @@
<div class="question<%= " votable" if question_item.allow_voting? %> div-table" id="question_<%= question_item.id %>">
<a href="#<%= question_item.id %>" class="wiki-anchor"></a>
<% if question_item.allow_voting? && User.current.allowed_to?(:vote_questions, @project) %>
<% if question_item.allow_voting? %>
<div class="vote">
<%= render :partial => 'questions_votes/question_item_vote', :locals => {:question_item => question_item} %>
</div>

View file

@ -8,7 +8,8 @@
<%= render :partial => 'form', :locals => {:f => f} %>
</div>
<%= submit_tag l(:button_save) %>
<%= preview_link({:controller => 'questions', :action => 'preview', :question_id => @question_item}, 'question_form') %>
<%= preview_link({:controller => 'questions', :action => 'preview', :question_id => @question_item}, 'question_form') if Redmine::VERSION.to_s <= '3.4.5' %>
| <%= link_to l(:button_cancel), question_path(@question_item) %>
<% end %>
<div id="preview" class="wiki"></div>

View file

@ -19,7 +19,7 @@
<div class="filters">
<%= form_tag({:controller => "questions", :action => "index"}, :method => :get, :id => "query_form") do %>
<%= text_field_tag(:topic_search, params[:topic_search], :autocomplete => "off", :class => "questions-search", :placeholder => l(:label_questions_search) ) %>
<%= javascript_tag "observeSearchfield('topic_search', 'topics_list', '#{ escape_javascript(autocomplete_for_subject_questions_path(:project_id => @project, :section_id => @section)) }')" %>
<%= javascript_tag "observeSearchfield('topic_search', 'topics_list', '#{ escape_javascript(autocomplete_for_subject_questions_path(:project_id => @project, :section_id => @section, :sort_order => @sort_order)) }')" %>
<% end %>
</div>
</div>

View file

@ -1,12 +1,12 @@
<h2><%= l(:label_message_new) %></h2>
<%= form_for @question_item, { :url => questions_path, :html => {:multipart => true,
<%= form_for @question_item, { :url => project_questions_path(@project, {}), :html => {:multipart => true,
:id => 'question_form'}} do |f| %>
<div id="all_attributes">
<%= render :partial => 'form', :locals => {:f => f} %>
<%= render partial: 'form', locals: { f: f } %>
</div>
<%= submit_tag l(:button_create) %>
<% preview_link({:controller => 'questions', :action => 'preview', :id => @question_item}, 'question_form') %>
<% preview_link({ controller: 'questions', action: 'preview', id: @question_item }, 'question_form') if Redmine::VERSION.to_s <= '3.4.5' %>
<% end %>
<div id="preview" class="wiki"></div>

View file

@ -1,5 +1,11 @@
<div class="contextual">
<%= link_to(l(:button_edit), edit_questions_answer_path(question_item), :class => 'icon icon-edit') if question_item.editable_by?(User.current) %>
<%= link_to(l(:button_delete), questions_answer_path(question_item), :method => :delete, :data => {:confirm => l(:text_are_you_sure)}, :class => 'icon icon-del') if question_item.destroyable_by?(User.current)
<% if question_item.editable_by?(User.current) %>
<%= link_to(l(:button_edit), edit_questions_answer_path(question_item), :class => 'icon icon-edit') %>
<% elsif User.current.allowed_to?(:accept_answers, @project) && !question_item.accepted? %>
<%= link_to(l(:label_questions_accept), questions_answer_path(question_item, answer: {accepted: true}), method: :put, :class => 'icon icon-accept') %>
<% elsif User.current.allowed_to?(:accept_answers, @project) && question_item.accepted? %>
<%= link_to(l(:label_questions_discard), questions_answer_path(question_item, answer: {accepted: false}), method: :put, :class => 'icon icon-discard') %>
<% end %>
<%= link_to(l(:button_delete), questions_answer_path(question_item), :method => :delete, :data => {:confirm => l(:text_are_you_sure)}, :class => 'icon icon-del') if question_item.destroyable_by?(User.current)
%>
</div>

View file

@ -1,6 +1,6 @@
<div class="question answer<%= ' votable' if question_item.allow_voting? %> div-table" id="questions_answer_<%= question_item.id %>">
<a href="#<%= question_item.id %>" class="wiki-anchor"></a>
<% if question_item.allow_voting? && User.current.allowed_to?(:vote_questions, @project) %>
<% if question_item.allow_voting? %>
<div class="vote" >
<%= render :partial => 'questions_votes/question_item_vote', :locals => {:question_item => question_item } %>
</div>

View file

@ -5,8 +5,8 @@
<%= back_url_hidden_field_tag %>
<%= render :partial => 'form', :locals => {:f => f} %>
<%= submit_tag l(:button_save) %>
<%= preview_link(preview_questions_answers_path(@answer), 'answer-form') %>
<%= preview_link(preview_questions_answers_path(@answer), 'answer-form') if Redmine::VERSION.to_s <= '3.4.5' %>
| <%= link_to l(:button_cancel), question_path(@answer.question, :anchor => "question_item_#{@answer.id}") %>
<% end %>
<div id="preview" class="wiki"></div>

View file

@ -1,29 +1,25 @@
<% previous_group = false %>
<div class="section-list">
<% @sections.each do |section| %>
<% if @project.blank? && (group = section.project) != previous_group %>
<% reset_cycle %>
<% @sections.group_by(&:project).each do |project, sections| %>
<% if @project.blank? %>
<div class="project-forums">
<h3>
<%= project.name %>
<%= link_to " \xc2\xbb", project_questions_sections_path(project_id: project.identifier) %>
</h3>
</div>
<% if group %>
<div class="project-forums">
<h3>
<%= group.name %>
<%= link_to " \xc2\xbb", project_questions_sections_path(:project_id => group.identifier) %>
</h3>
</div>
<% end %>
<div class="section-list">
<% previous_group = group %>
<% end %>
<a href="<%= url_for({:controller => "questions", :action => 'index', :section_id => section, :project_id => section.project}) %>" id="section_<%= section.id %>" class="section-tile">
<h4>
<%= section.name %>
<span class="topic-count"><%= "(#{section.questions_count})" %></span>
</h4>
<div class="description">
<%= section.description %>
</div>
</a>
<div class="section-list">
<% sections.each do |section| %>
<a id="section_<%= section.id %>" class="section-tile"
href="<%= url_for({ controller: "questions", action: 'index', section_id: section, project_id: section.project }) %>">
<h4>
<%= section.name %>
<span class="topic-count"><%= "(#{section.questions_count})" %></span>
</h4>
<div class="description">
<%= section.description %>
</div>
</a>
<% end %>
</div>
<% end %>
</ul>

View file

@ -26,4 +26,3 @@
<% content_for :header_tags do %>
<%= javascript_include_tag :questions, :plugin => 'redmine_questions' %>
<% end %>