From d80abf91ed47fee77c79af54d5b8bdd97d1673ca Mon Sep 17 00:00:00 2001 From: akiko_pusu Date: Mon, 12 Mar 2012 20:36:16 +0900 Subject: [PATCH] Initial commit. --- .hgignore | 1 + README.rdoc | 27 +++++ app/controllers/issue_templates_controller.rb | 113 ++++++++++++++++++ .../issue_templates_settings_controller.rb | 35 ++++++ app/helpers/issue_template_settings_helper.rb | 2 + app/helpers/issue_templates_helper.rb | 14 +++ app/models/issue_template.rb | 12 ++ app/models/issue_template_setting.rb | 24 ++++ app/views/issue_templates/_form.html.erb | 21 ++++ .../issue_templates/_issue_select_form.rhtml | 48 ++++++++ .../issue_templates/_template_pulldown.rhtml | 3 + app/views/issue_templates/index.html.erb | 36 ++++++ app/views/issue_templates/new.html.erb | 22 ++++ app/views/issue_templates/show.html.erb | 59 +++++++++ .../issue_templates_settings/_show.html.erb | 34 ++++++ assets/images/issue_templates.png | Bin 0 -> 425 bytes assets/javascripts/issue_templates.js | 44 +++++++ assets/stylesheets/issue_templates.css | 73 +++++++++++ config/locales/en.yml | 14 +++ config/locales/ja.yml | 14 +++ config/routes.rb | 7 ++ db/migrate/0001_create_issue_templates.rb | 25 ++++ .../0002_create_issue_template_settings.rb | 17 +++ init.rb | 35 ++++++ lang/en.yml | 2 + lib/issue_templates_issues_hook.rb | 17 +++ lib/issue_templates_projects_helper_patch.rb | 28 +++++ test/fixtures/issue_template_settings.yml | 19 +++ test/fixtures/issue_templates.yml | 53 ++++++++ .../issue_templates_controller_test.rb | 104 ++++++++++++++++ ...issue_templates_setting_controller_test.rb | 60 ++++++++++ test/test_helper.rb | 5 + test/unit/issue_template_setting_test.rb | 29 +++++ test/unit/issue_template_test.rb | 23 ++++ 34 files changed, 1020 insertions(+) create mode 100644 .hgignore create mode 100644 README.rdoc create mode 100644 app/controllers/issue_templates_controller.rb create mode 100644 app/controllers/issue_templates_settings_controller.rb create mode 100644 app/helpers/issue_template_settings_helper.rb create mode 100644 app/helpers/issue_templates_helper.rb create mode 100644 app/models/issue_template.rb create mode 100644 app/models/issue_template_setting.rb create mode 100644 app/views/issue_templates/_form.html.erb create mode 100644 app/views/issue_templates/_issue_select_form.rhtml create mode 100644 app/views/issue_templates/_template_pulldown.rhtml create mode 100644 app/views/issue_templates/index.html.erb create mode 100644 app/views/issue_templates/new.html.erb create mode 100644 app/views/issue_templates/show.html.erb create mode 100644 app/views/issue_templates_settings/_show.html.erb create mode 100644 assets/images/issue_templates.png create mode 100644 assets/javascripts/issue_templates.js create mode 100644 assets/stylesheets/issue_templates.css create mode 100644 config/locales/en.yml create mode 100644 config/locales/ja.yml create mode 100644 config/routes.rb create mode 100644 db/migrate/0001_create_issue_templates.rb create mode 100644 db/migrate/0002_create_issue_template_settings.rb create mode 100644 init.rb create mode 100644 lang/en.yml create mode 100644 lib/issue_templates_issues_hook.rb create mode 100644 lib/issue_templates_projects_helper_patch.rb create mode 100644 test/fixtures/issue_template_settings.yml create mode 100644 test/fixtures/issue_templates.yml create mode 100644 test/functional/issue_templates_controller_test.rb create mode 100644 test/functional/issue_templates_setting_controller_test.rb create mode 100644 test/test_helper.rb create mode 100644 test/unit/issue_template_setting_test.rb create mode 100644 test/unit/issue_template_test.rb diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..72380a9 --- /dev/null +++ b/.hgignore @@ -0,0 +1 @@ +^coverage$ diff --git a/README.rdoc b/README.rdoc new file mode 100644 index 0000000..b23dd11 --- /dev/null +++ b/README.rdoc @@ -0,0 +1,27 @@ += Redmine Issue Templates Plugin + +Plugin to generate and use issue templates to assist issue creation. + +=== Plugin installation + +1. Copy the plugin directory into the vendor/plugins directory. +2. Do migration task. (e.g. rake db:migrate_plugins RAILS_ENV=production) +2. (Re)Start Redmine. + +=== Note + +This plugin aims to assist contributor's feedback by using template if the +project has some format for issues. + +=== Repository + +* https://bitbucket.org/akiko_pusu/redmine_issue_templates + +=== WebPage + +* http://www.r-labs.org/projects/issue-template (Project Page) + + +=== License + +This software is licensed under the GNU GPL v2. See COPYRIGHT and COPYING for details. diff --git a/app/controllers/issue_templates_controller.rb b/app/controllers/issue_templates_controller.rb new file mode 100644 index 0000000..d68de2c --- /dev/null +++ b/app/controllers/issue_templates_controller.rb @@ -0,0 +1,113 @@ +class IssueTemplatesController < ApplicationController + unloadable + layout 'base' + helper :sort + include SortHelper + include IssueTemplatesHelper + helper :issues + include IssuesHelper + + before_filter :find_object, :only => [:show, :edit, :destroy] + before_filter :find_user, :find_project, :authorize, :except => [ :preview ] + before_filter :find_tracker, :only => [:set_pulldown] + + def index + sort_init "id", 'desc' + sort_update ["id", "name", "tracker_id", "author_id", "updated_on", "enabled"] + @issue_templates = IssueTemplate.find(:all, + :conditions => ['project_id = ?', @project.id], :order => sort_clause) + + render :template => 'issue_templates/index.html.erb', :layout => !request.xhr? + end + + def show + end + + def new + # create empty instance + @issue_template = IssueTemplate.new(:author => @user, :project => @project, + :tracker => @tracker) + if request.post? + # Case post, set attributes passed as parameters. + @issue_template.attributes = params[:issue_template] + if @issue_template.save + flash[:notice] = l(:notice_successful_create) + redirect_to :action => "show", :id => @issue_template.id, :project_id => @project + end + # In case failed to save, redirect to show. + end + end + + def edit + if request.post? + @issue_template.attributes = params[:issue_template] + if @issue_template.save + flash[:notice] = l(:notice_successful_update) + redirect_to :action => "show", :id => @issue_template.id, :project_id => @project + end + end + end + + def destroy + if request.post? + if @issue_template.destroy + flash[:notice] = l(:notice_successful_delete) + redirect_to :action => "index", :project_id => @project + end + end + end + + # load template description + def load + @issue_template = IssueTemplate.find(params[:issue_template]) + render :text => @issue_template.to_json + end + + # update pulldown + def set_pulldown + issue_templates = IssueTemplate.find(:all, + :conditions => ['project_id = ? AND tracker_id = ? AND enabled = ?', + @project.id, params[:issue_tracker_id], true]) + @grouped_options = [] + group = [] + tmpls = issue_templates + if tmpls.size > 0 + tmpls.each { |x| group.push([x.title, x.id]) } + @grouped_options.push([@tracker.name, group]) + end + render :action => "issue_templates/_template_pulldown", :layout => false + end + + # preview + def preview + @text = (params[:issue_template] ? params[:issue_template][:description] : nil) + @issue_template = IssueTemplate.find(params[:id]) if params[:id] + render :partial => 'common/preview' + end + + private + def find_user + @user = User.current + end + + def find_tracker + @tracker = Tracker.find(params[:issue_tracker_id]) + end + + def find_object + @issue_template = IssueTemplate.find(params[:id]) + @project = @issue_template.project + rescue ActiveRecord::RecordNotFound + render_404 + end + + def find_project + begin + @project = Project.find(params[:project_id]) + rescue ActiveRecord::RecordNotFound + render_404 + end + end + +end + diff --git a/app/controllers/issue_templates_settings_controller.rb b/app/controllers/issue_templates_settings_controller.rb new file mode 100644 index 0000000..2e1f1f9 --- /dev/null +++ b/app/controllers/issue_templates_settings_controller.rb @@ -0,0 +1,35 @@ +class IssueTemplatesSettingsController < ApplicationController + unloadable + before_filter :find_project, :find_user + before_filter :authorize, :except => [ :show_help, :preview ] + + def edit + if (params[:settings] != nil) + @issue_templates_setting = IssueTemplateSetting.find_or_create(@project.id) + @issue_templates_setting.attributes = params[:settings] + @issue_templates_setting.save! + flash[:notice] = l(:notice_successful_update) + redirect_to :controller => 'projects', + :action => "settings", :id => @project, :tab => 'issue_templates' + end + end + + def preview + @text = params[:settings][:help_message] + render :partial => 'common/preview' + end + + private + def find_user + @user = User.current + end + + def find_project + begin + @project = Project.find(params[:project_id]) + rescue ActiveRecord::RecordNotFound + render_404 + end + end + +end diff --git a/app/helpers/issue_template_settings_helper.rb b/app/helpers/issue_template_settings_helper.rb new file mode 100644 index 0000000..fc8ff8e --- /dev/null +++ b/app/helpers/issue_template_settings_helper.rb @@ -0,0 +1,2 @@ +module IssueTemplateSettingsHelper +end diff --git a/app/helpers/issue_templates_helper.rb b/app/helpers/issue_templates_helper.rb new file mode 100644 index 0000000..05587f9 --- /dev/null +++ b/app/helpers/issue_templates_helper.rb @@ -0,0 +1,14 @@ +module IssueTemplatesHelper + def grouped_array(trackers, project_id) + array = [] + trackers.each {|tracker| + group = [] + tmpls = IssueTemplate.find(:all, + :conditions => ['tracker_id = ? AND project_id = ?', + item.id, project_id]) + tmpls.each { |x| group.push([x.id, x.title]) } + array.push([tracker.name, group]) + } + return array + end +end diff --git a/app/models/issue_template.rb b/app/models/issue_template.rb new file mode 100644 index 0000000..89eefd0 --- /dev/null +++ b/app/models/issue_template.rb @@ -0,0 +1,12 @@ +class IssueTemplate < ActiveRecord::Base + unloadable + belongs_to :project + belongs_to :author, :class_name => 'User', :foreign_key => 'author_id' + belongs_to :tracker + validates_presence_of :project, :title, :description, :tracker + validates_uniqueness_of :title, :scope => :project_id + + def enabled? + self.enabled == true + end +end diff --git a/app/models/issue_template_setting.rb b/app/models/issue_template_setting.rb new file mode 100644 index 0000000..fa8e70a --- /dev/null +++ b/app/models/issue_template_setting.rb @@ -0,0 +1,24 @@ +class IssueTemplateSetting < ActiveRecord::Base + unloadable + belongs_to :project + + validates_uniqueness_of :project_id + validates_presence_of :project_id + + def self.find_or_create(project_id) + setting = IssueTemplateSetting.find(:first, :conditions => ['project_id = ?', project_id]) + unless setting + setting = IssueTemplateSetting.new + setting.project_id = project_id + setting.save! + end + return setting + end + + def enable_help? + if self.enabled == true && !self.help_message.blank? + return true + end + return false + end +end diff --git a/app/views/issue_templates/_form.html.erb b/app/views/issue_templates/_form.html.erb new file mode 100644 index 0000000..1f2c45a --- /dev/null +++ b/app/views/issue_templates/_form.html.erb @@ -0,0 +1,21 @@ +<%= error_messages_for 'issue_template' %> +
+

<%= f.text_field :title, :required => true, :size => 100, :label => l(:field_title) %>

+

<%= f.select :tracker_id, @project.trackers.collect {|t| [t.name, t.id]}, :required => true,:label => l(:label_tracker) %>

+

+<%= f.text_area :description,:cols => 78, :rows => 12, + :required => true, + :label => l(:template_description), :style => "overflow:auto;" %> +

+

+<%= f.text_area :note,:cols => 70, :rows => 3, + :required => false, + :label => l(:issue_template_note), :style => "overflow:auto;" %> +

+

<%= f.check_box :enabled, :label => l(:label_enabled) %> <%= l(:label_enabled_help_message) %>

+
+ +<%= wikitoolbar_for 'issue_template_description' %> + + + diff --git a/app/views/issue_templates/_issue_select_form.rhtml b/app/views/issue_templates/_issue_select_form.rhtml new file mode 100644 index 0000000..5e404de --- /dev/null +++ b/app/views/issue_templates/_issue_select_form.rhtml @@ -0,0 +1,48 @@ +<% return '' unless @project.module_enabled? :issue_templates %> + <% + # TODO: This code should be moved from vire template. + @setting = IssueTemplateSetting.find_or_create(@project.id) + grouprd_options = [] + + # default set templates of first tracker + tracker = @project.trackers[0] + group = [] + tmpls = IssueTemplate.find(:all, + :conditions => ['tracker_id = ? AND project_id = ? AND enabled = ?', + tracker.id, @project.id, true]) + if tmpls.size > 0 + tmpls.each { |x| group.push([x.title, x.id]) } + grouprd_options.push([tracker.name, group]) + end + %> + +
+ + + + <% if @setting.enable_help? == true %> + +<%= l(:label_help_message) %> +
+ <% end %> + +
+ diff --git a/app/views/issue_templates/_template_pulldown.rhtml b/app/views/issue_templates/_template_pulldown.rhtml new file mode 100644 index 0000000..cba9c8d --- /dev/null +++ b/app/views/issue_templates/_template_pulldown.rhtml @@ -0,0 +1,3 @@ + + <%= grouped_options_for_select(@grouped_options) %> + diff --git a/app/views/issue_templates/index.html.erb b/app/views/issue_templates/index.html.erb new file mode 100644 index 0000000..075d3ac --- /dev/null +++ b/app/views/issue_templates/index.html.erb @@ -0,0 +1,36 @@ +
+ <%= link_to_if_authorized(l(:label_new), + {:controller => 'issue_templates', :action => 'new', :project_id => @project}, + :class => 'icon icon-add',:onclick => 'Element.show("add-template"); return false;') %> +
+ +<% if @notice -%> +
<%= @notice -%>
+<% end -%> + + + + + + + + + + + + + + <% @issue_templates.each do |issue_template| %> + issue_template issue"> + + + + + + + + <% end %> + +
<%= sort_header_tag 'id', :caption => '#' %><%= sort_header_tag 'title', :caption => l(:field_title) %><%= sort_header_tag 'tracker_id', :caption => l(:field_tracker) %><%= sort_header_tag 'author_id', :caption => l(:field_author) %><%= sort_header_tag 'updated_on', :caption => l(:field_updated_on) %><%= sort_header_tag 'enabled', :caption => l(:label_enabled) %>
<%= issue_template.id %><%= link_to h(issue_template.title), :controller => 'issue_templates', + :action => 'show', :id => issue_template.id, :project_id => @project %><%=h issue_template.tracker.name %><%=h issue_template.author %><%= format_time(issue_template.updated_on)%> <%= image_tag('true.png') if issue_template.enabled? %>
+ diff --git a/app/views/issue_templates/new.html.erb b/app/views/issue_templates/new.html.erb new file mode 100644 index 0000000..9cb29fa --- /dev/null +++ b/app/views/issue_templates/new.html.erb @@ -0,0 +1,22 @@ +

<%=h "#{l(:issue_templates)} / #{l(:button_add)}" %>

+
+ <%= error_messages_for 'issue_template' %> + <% labelled_tabular_form_for :issue_template, @issue_template, + :url => { :controller => 'issue_templates', :action => 'new', :project_id => @project }, + :html => { :id => 'issue_template-form', + :class => nil, :multipart => false } do |f| %> + <%= render :partial => 'form', :locals => { :f => f } %> + <%= submit_tag l(:button_apply) %> + <% end %> + + <%= link_to_remote l(:label_preview), + { :url => { :controller => 'issue_templates', :action => 'preview', :id => @issue_template }, + :method => 'post', + :update => 'preview', + :with => "Form.serialize('issue_template-form')" + }, :accesskey => accesskey(:preview) %> | + <%= link_to l(:button_cancel), {:action => 'index'}, :confirm => l(:text_are_you_sure) %> +
+ +
+ diff --git a/app/views/issue_templates/show.html.erb b/app/views/issue_templates/show.html.erb new file mode 100644 index 0000000..64c2aea --- /dev/null +++ b/app/views/issue_templates/show.html.erb @@ -0,0 +1,59 @@ +
+<%= link_to_if_authorized l(:button_edit), + {:controller => 'issue_templates', :action => 'edit', :id => @issue_template, + :project_id => @project}, + :class => 'icon icon-edit', + :accesskey => accesskey(:edit), + :onclick => 'Element.show("edit-issue_template"); return false;' %> +<%= link_to_if_authorized l(:button_delete), + {:controller => 'issue_templates', :action => 'destroy', + :id => @issue_template, :project_id => @project}, + :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %> +
+ +

<%= l(:issue_templates) %>: #<%= @issue_template.id %> <%= avatar(@issue_template.author, :size => "24") %>

+ +<% if authorize_for('issue_templates', 'edit') %> + +<% end %> + + +
+

+ +<%= h @issue_template.title %> +

+

+<%= h @issue_template.tracker.name %> +

+

+<%= @issue_template.author %> (<%= @issue_template.updated_on %>)

+ +

+ +<%= textilizable(@issue_template.description) %> +

+

+<%= @issue_template.note.blank? ? l(:label_none) : @issue_template.note %> +

+

+<%= @issue_template.enabled? ? l(:label_enabled) : l(:label_disabled) %> +

+
diff --git a/app/views/issue_templates_settings/_show.html.erb b/app/views/issue_templates_settings/_show.html.erb new file mode 100644 index 0000000..578b409 --- /dev/null +++ b/app/views/issue_templates_settings/_show.html.erb @@ -0,0 +1,34 @@ +
+<% +@issue_templates_setting = IssueTemplateSetting.find_or_create(@project.id) + %> +

<%= l(:about_help_message) %>

+<% labelled_tabular_form_for :settings, @issue_templates_setting, + :url => {:controller => 'issue_templates_settings', + :action => 'edit', :project_id => @project, :tab => 'issue_templates', + :setting_id => @issue_templates_setting.id}, + :html => {:id => 'issue_templates_settings' } do |f| %> + <%= error_messages_for 'issue_templates_setting' %> +
+

<%= f.check_box :enabled, :label => l(:field_enabled) %>

+

+ <%=content_tag(:label, l(:label_help_message)) %> + <%=text_area_tag 'settings[help_message]', @issue_templates_setting['help_message'], :size =>"50x5", + :class => 'wiki-edit' ,:required => true %>
+ <%= wikitoolbar_for "settings_help_message" %> + <%= link_to_remote l(:label_preview), + { :url => { :controller => 'issue_templates_settings', + :action => 'preview', :project_id => @project}, + :method => 'post', + :id => 'help_preview', + :update => 'template_help_preview', + :with => "Form.serialize('issue_templates_settings')", + :complete => "Element.scrollTo('template_help_preview')" + }, :accesskey => accesskey(:preview) %> +

+ +
+ <%= submit_tag l(:button_update) %> +<% end %> +
+ diff --git a/assets/images/issue_templates.png b/assets/images/issue_templates.png new file mode 100644 index 0000000000000000000000000000000000000000..e4c2ef072c53b8846a04a2cc75647849638ce6d4 GIT binary patch literal 425 zcmV;a0apHrP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02*{fSaefwW^{L9 za%BKeVQFr3E>1;MAa*k@H7+qQF!XYv0003PNklpa!^i@818tdynD7(^pRT zUpzhTe_Ega|Hu}HPo5QeH7NFiG~)uBk1hY-xH|FwmMc^LUwF3bfB){R|Ka_*|IM={ zd*Ft^3;?+V9Vd2r{NH$a!vBreCV}nf*^~Oee@{BdfbSRvfE1v6$EVux|F$c0P#n1F z(o~QE|FOCN-8=RLO8>ieCH-&Tl>j!ReP;r07r<tZ4KpL?H=#&jD{~e1|z|IEIr4us|&IkDeIS|nU^!fAW|B*4w5}eV9FG^t{2~vaA z0Bm7^9<5}%06nFmxBxxwLAId#9b^DV466%}O~VxiFmVk3qi04!8H*a37sU(!+HzI= Ts9|Iy00000NkvXXu0mjfhk~*# literal 0 HcmV?d00001 diff --git a/assets/javascripts/issue_templates.js b/assets/javascripts/issue_templates.js new file mode 100644 index 0000000..d1f3990 --- /dev/null +++ b/assets/javascripts/issue_templates.js @@ -0,0 +1,44 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +function checkExpand(ch) { + var obj=document.all && document.all(ch) || document.getElementById && document.getElementById(ch); + if(obj && obj.style) obj.style.display= + "none" == obj.style.display ?"" : "none" +} + +// Change Location of pulldown. +document.observe("dom:loaded", function() { + new Insertion.After($('issue_tracker_id'), $('template_area')); + //ConnectedSelect(['issue_tracker_id','issue_template']); +}); + + +function load_template(evt, target_url, token) { + if (evt.target.value != "") { + new Ajax.Request(target_url, + {asynchronous:true, evalScripts:true, + onComplete:function(request){ + eval("var template = " + request.responseText); + $('issue_description').value = template.description + $('issue_subject').value = template.title + }, + parameters:'issue_template=' + encodeURIComponent(evt.target.value) + + '&authenticity_token=' + encodeURIComponent(token) + } + ); + } +} + +function set_pulldown(evt, target_url, token) { + new Ajax.Request(target_url, + { asynchronous:true, evalScripts:true, + onComplete:function(request){ + Element.update('issue_template', request.responseText); + }, + parameters:'issue_tracker_id=' + encodeURIComponent(evt.target.value) + + '&authenticity_token=' + encodeURIComponent(token) + } + ); +} diff --git a/assets/stylesheets/issue_templates.css b/assets/stylesheets/issue_templates.css new file mode 100644 index 0000000..4eaa32a --- /dev/null +++ b/assets/stylesheets/issue_templates.css @@ -0,0 +1,73 @@ +#main-menu a.issue-templates.selected { + background-image: url(../images/issue_templates.png); + background-repeat: no-repeat; + background-position: 3px center; + padding-left: 18px; +} + +#main-menu a.issue-templates.selected:hover { + background-image: url(../images/issue_templates.png); + background-repeat: no-repeat; + background-position: 3px center; + padding-left: 18px; +} + +#main-menu a.issue-templates { + background-image: url(../images/issue_templates.png); + background-repeat: no-repeat; + background-position: 3px center; + color: transparent; + padding-left: 18px; +} + +#main-menu a.issue-templates:hover { + background-image: url(../images/issue_templates.png); + background-repeat: no-repeat; + background-position: 3px center; + padding-left: 18px; +} + +#template_help_message { +} + +#template_area { +} + +.template_help { + display: block; + border: solid #BBB 1px; + background-color: #F0F0F0; + padding: 1em; + margin-top: 0.5em; + margin-bottom: 1em; +} + +#template_help_content { + position:relative; + text-align: left; + margin-left: 0.5em; +} + +#template_help_content p{ + padding-left: 5px; +} + + +div.template_help div#template_help_hide { + display:block; + position:absolute; + bottom:5px; + right:10px; + font-size:smaller; + background:none; + color:#000; + filter: alpha(opacity=30); + -moz-opacity: 0.3; + opacity: 0.3; +} + +div.template_help div#template_help_hide:hover { + filter: alpha(opacity=80); + -moz-opacity: 0.8; + opacity: 0.8; +} \ No newline at end of file diff --git a/config/locales/en.yml b/config/locales/en.yml new file mode 100644 index 0000000..2c1dc3e --- /dev/null +++ b/config/locales/en.yml @@ -0,0 +1,14 @@ +# English strings go here for Rails i18n +en: + issue_templates: Templates + issue_template: Issue template + issue_template_note: note + label_enabled: Enable + label_disabled: Disable + template_description: Template description + label_help_meessage: About templates + label_show_help_message: Show help message when create/update issues. + about_help_message: Each project can customize help message for templates. + close_help: Close help message. + about_template_help_message: You can see the instruction of issue templates on this project. + label_enabled_help_message: Checkbox to activate (enabled) to this template. If you hope to save this template as draft, turn this checkbox off. \ No newline at end of file diff --git a/config/locales/ja.yml b/config/locales/ja.yml new file mode 100644 index 0000000..b453853 --- /dev/null +++ b/config/locales/ja.yml @@ -0,0 +1,14 @@ +ja: + issue_templates: テンプレート + issue_template: チケットテンプレート + issue_template_note: メモ + label_enabled: "有効" + label_disabled: "無効" + template_description: "テンプレートの本文" + label_help_message: "テンプレートについて" + label_show_help_message: "ヘルプメッセージを有効にする" + field_enabled: "ヘルプメッセージを有効にする" + about_help_message: "プロジェクト毎にテンプレートに関するヘルプを設定できます。" + close_help: "ヘルプメッセージを閉じます。" + about_template_help_message: "プロジェクト毎のテンプレートに関するヘルプを表示します。" + label_enabled_help_message: "有効にチェックを入れると、選択できるようになります。ドラフト状態の場合は、チェックを外して下さい。" \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb new file mode 100644 index 0000000..efa3150 --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,7 @@ +# Routes +ActionController::Routing::Routes.draw do |map| + map.connect 'projects/:project_id/issue_templates/:action', :controller => 'issue_templates' + map.connect 'projects/:project_id/issue_templates/:action/:id', :controller => 'issue_templates', :action => 'edit' + map.connect 'projects/:project_id/issue_templates_settings/:action', + :controller => 'issue_templates_settings' +end \ No newline at end of file diff --git a/db/migrate/0001_create_issue_templates.rb b/db/migrate/0001_create_issue_templates.rb new file mode 100644 index 0000000..24e7fa5 --- /dev/null +++ b/db/migrate/0001_create_issue_templates.rb @@ -0,0 +1,25 @@ +class CreateIssueTemplates < ActiveRecord::Migration + def self.up + create_table :issue_templates do |t| + t.column :title, :string, :null => false + t.column :project_id, :integer + t.column :tracker_id, :integer, :null => false + t.column :author_id, :integer, :null => false + t.column :note, :string + t.column :description, :text + t.column :enabled, :boolean + t.column :created_on, :timestamp + t.column :updated_on, :timestamp + end + add_index :issue_templates, :author_id + add_index :issue_templates, :project_id + add_index :issue_templates, :tracker_id + end + + def self.down + drop_table :issue_templates + remove_index :issue_templates, :author_id + remove_index :issue_templates, :project_id + remove_index :issue_templates, :tracker_id + end +end diff --git a/db/migrate/0002_create_issue_template_settings.rb b/db/migrate/0002_create_issue_template_settings.rb new file mode 100644 index 0000000..35ed3a1 --- /dev/null +++ b/db/migrate/0002_create_issue_template_settings.rb @@ -0,0 +1,17 @@ +class CreateIssueTemplateSettings < ActiveRecord::Migration + def self.up + create_table :issue_template_settings do |t| + + t.column :project_id, :integer + + t.column :help_message, :text + + t.column :enabled, :boolean + + end + end + + def self.down + drop_table :issue_template_settings + end +end diff --git a/init.rb b/init.rb new file mode 100644 index 0000000..ac9b2ac --- /dev/null +++ b/init.rb @@ -0,0 +1,35 @@ +require 'redmine' +require 'issue_templates_issues_hook' +require 'issue_templates_projects_helper_patch' + +Redmine::Plugin.register :redmine_issue_templates do + name 'Redmine Issue Templates plugin' + author 'Akiko Takano' + description 'This is a plugin for Redmine' + version '0.0.1' + author_url 'http://twitter.com/akiko_pusu' + requires_redmine :version_or_higher => '1.2.0' + + project_module :issue_templates do + permission :edit_issue_templates, {:issue_templates => [:new, :edit, :destroy]} + permission :show_issue_templates, {:issue_templates => [:index, :show, :load, :set_pulldown]} + permission :manage_issue_templates, + {:issue_templates_settings => [:show, :edit]}, :require => :member + end + + menu :project_menu, :issue_templates, { :controller => 'issue_templates', + :action => 'index' }, :caption => :issue_templates, + :param => :project_id, + :last => true + +end + +require 'dispatcher' +Dispatcher.to_prepare :redmine_issue_templates do + require_dependency 'projects_helper' + unless ProjectsHelper.included_modules.include? IssueTemplatesProjectsHelperPatch + ProjectsHelper.send(:include, IssueTemplatesProjectsHelperPatch) + end +end + + diff --git a/lang/en.yml b/lang/en.yml new file mode 100644 index 0000000..3a7e543 --- /dev/null +++ b/lang/en.yml @@ -0,0 +1,2 @@ +# English strings go here +my_label: "My label" diff --git a/lib/issue_templates_issues_hook.rb b/lib/issue_templates_issues_hook.rb new file mode 100644 index 0000000..ec679cf --- /dev/null +++ b/lib/issue_templates_issues_hook.rb @@ -0,0 +1,17 @@ +# To change this template, choose Tools | Templates +# and open the template in the editor. + +class IssueTemplatesIssuesHook < Redmine::Hook::ViewListener + include IssuesHelper + + def view_layouts_base_html_head(context = {}) + o = stylesheet_link_tag('issue_templates', :plugin => 'redmine_issue_templates') + if context[:controller].class.name == 'IssuesController' and + context[:controller].action_name != 'index' + o << javascript_include_tag('issue_templates', :plugin => 'redmine_issue_templates') + end + return o + end + + render_on :view_issues_form_details_top, :partial => 'issue_templates/issue_select_form' +end diff --git a/lib/issue_templates_projects_helper_patch.rb b/lib/issue_templates_projects_helper_patch.rb new file mode 100644 index 0000000..bcd0ebf --- /dev/null +++ b/lib/issue_templates_projects_helper_patch.rb @@ -0,0 +1,28 @@ +# To change this template, choose Tools | Templates +# and open the template in the editor. +require_dependency 'projects_helper' + +module IssueTemplatesProjectsHelperPatch + def self.included base # :nodoc: + base.send :include, ProjectsHelperMethodsIssueTemplates + base.class_eval do + alias_method_chain :project_settings_tabs, :issue_templates + end + end +end + +module ProjectsHelperMethodsIssueTemplates + def project_settings_tabs_with_issue_templates + tabs = project_settings_tabs_without_issue_templates + action = {:name => 'issue_templates', + :controller => 'issue_templates_settings', + :action => :show, + :partial => 'issue_templates_settings/show', :label => :issue_templates} + tabs << action if User.current.allowed_to?(action, @project) + tabs + end +end + +ProjectsHelper.send(:include, IssueTemplatesProjectsHelperPatch) + + diff --git a/test/fixtures/issue_template_settings.yml b/test/fixtures/issue_template_settings.yml new file mode 100644 index 0000000..cd2fae0 --- /dev/null +++ b/test/fixtures/issue_template_settings.yml @@ -0,0 +1,19 @@ +# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html +one: + id: 1 + + project_id: 1 + + help_message: MyText + + enabled: true + +two: + id: 2 + + project_id: 1 + + help_message: MyText + + enabled: false + diff --git a/test/fixtures/issue_templates.yml b/test/fixtures/issue_templates.yml new file mode 100644 index 0000000..972d020 --- /dev/null +++ b/test/fixtures/issue_templates.yml @@ -0,0 +1,53 @@ +# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html +one: + id: 1 + + project_id: 1 + + tracker_id: 1 + + author_id: 1 + + title: title1 + + description: description1 + + note: note1 + + enabled: 1 + + +two: + id: 2 + + project_id: 1 + + tracker_id: 3 + + author_id: 1 + + title: titel2 + + description: description2 + + note: note2 + + enabled: 0 + +three: + id: 3 + + project_id: 1 + + tracker_id: 2 + + author_id: 1 + + title: titel3 + + description: description3 + + note: note3 + + enabled: 1 + diff --git a/test/functional/issue_templates_controller_test.rb b/test/functional/issue_templates_controller_test.rb new file mode 100644 index 0000000..256b794 --- /dev/null +++ b/test/functional/issue_templates_controller_test.rb @@ -0,0 +1,104 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class IssueTemplatesControllerTest < ActionController::TestCase + fixtures :projects, :users, :roles, :members, :enabled_modules, :issue_templates + def setup + @controller = IssueTemplatesController.new + @request = ActionController::TestRequest.new + @request.session[:user_id] = 1 + @response = ActionController::TestResponse.new + @request.env["HTTP_REFERER"] = '/' + # Enabled Template module + @project = Project.find(1) + @project.enabled_modules << EnabledModule.new(:name => 'issue_templates') + @project.save! + end + + context "new" do + setup do + + end + + should "should get index" do + get :index, :project_id => 1 + assert_response :success + assert_template 'index' + assert_not_nil assigns(:issue_templates) + end + + should "return new template instance when request is get" do + get :new, :project_id => 1, :author_id => User.current.id + assert_response :success + + template = assigns(:issue_template) + assert_not_nil template + assert template.title.blank? + assert template.description.blank? + assert template.note.blank? + assert template.tracker.blank? + assert_equal(1, template.author.id) + assert_equal(1, template.project.id) + end + + # do post + should "insert new template record when request is post" do + count = IssueTemplate.find(:all).length + post :new, :issue_template => {:title => "newtitle", :note => "note", + :description => "description", :tracker_id => 1, :enabled => 1, + :author_id => 1 }, :project_id => 1 + + template = IssueTemplate.first(:order => 'id DESC') + assert_response :redirect # show + + assert_equal(count + 1, IssueTemplate.find(:all).length) + + assert_not_nil template + assert_equal("newtitle", template.title) + assert_equal("note", template.note) + assert_equal("description", template.description) + assert_equal(1, template.project.id) + assert_equal(1, template.tracker.id) + assert_equal(1, template.author.id) + end + + # fail check + should "not be able to save if title is empty" do + count = IssueTemplate.find(:all).length + + # when title blank, validation bloks to save. + post :new, :issue_template => {:title => "", :note => "note", + :description => "description", :tracker_id => 1, :enabled => 1, + :author_id => 1 }, :project_id => 1 + + assert_response :success + assert_equal(count, IssueTemplate.find(:all).length) + end + + should "should preview template" do + get :preview, {:issue_template => {:description=> "h1. Test data."}} + assert_template "common/_preview.html.erb" + assert_select 'h1', /Test data\./, "#{@response.body}" + end + + should "should edit template when request is post" do + post :edit, :id => 2, + :issue_template => { :description => 'Update Test template2' }, + :project_id => 1 + project = Project.find 1 + assert_response :redirect # show + issue_template = IssueTemplate.find(2) + assert_redirected_to :controller => 'issue_templates', + :action => "show", :id => issue_template.id, :project_id => project + assert_equal 'Update Test template2', issue_template.description + end + + should "should destroy template when request is delete" do + post :destroy, :id => 1, :project_id => 1 + project = Project.find 1 + assert_redirected_to :controller => 'issue_templates', + :action => "index", :project_id => project + assert_raise(ActiveRecord::RecordNotFound) {IssueTemplate.find(1)} + end + + end +end diff --git a/test/functional/issue_templates_setting_controller_test.rb b/test/functional/issue_templates_setting_controller_test.rb new file mode 100644 index 0000000..c111531 --- /dev/null +++ b/test/functional/issue_templates_setting_controller_test.rb @@ -0,0 +1,60 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class IssuteTemplatesSettingControllerTest < ActionController::TestCase + fixtures :projects, + :users, + :roles, + :members, + :member_roles, + :enabled_modules, + :issue_templates + + def setup + @controller = IssueTemplatesSettingsController.new + @response = ActionController::TestResponse.new + # Enabled Template module + #EnabledModule.generate! :project_id => 1, :name => 'issue_templates' + enabled_module = EnabledModule.new + enabled_module.project_id = 1 + enabled_module.name = 'issue_templates' + enabled_module.save + end + + context "#update" do + + context "by member" do + setup do + @request.session[:user_id] = 2 + end + + context "without permission" do + should "403 post" do + project = Project.find 1 + post :edit, :project_id => project, + :settings => { :enabled => "1", :help_message => "Hoo"}, + :setting_id => 1, :tab => "issue_templates" + assert_response 403 + end + end + + context "with permission" do + setup do + Role.find(1).add_permission! :manage_issue_templates + end + + should "should redirect post" do + project = Project.find 1 + post :edit, :project_id => project, + :settings => { :enabled => "1", :help_message => "Hoo"}, + :setting_id => 1, :tab => "issue_templates" + assert_response :redirect + + assert_redirected_to :controller => 'projects', + :action => "settings", :id => project, :tab => 'issue_templates' + end + end + end + end +end + + diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 0000000..7cc962f --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +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 diff --git a/test/unit/issue_template_setting_test.rb b/test/unit/issue_template_setting_test.rb new file mode 100644 index 0000000..74f849e --- /dev/null +++ b/test/unit/issue_template_setting_test.rb @@ -0,0 +1,29 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class IssueTemplateSettingTest < ActiveSupport::TestCase + fixtures :issue_template_settings + + def setup + @issue_template_setting = IssueTemplateSetting.find(1) + end + + def test_truth + assert_kind_of IssueTemplateSetting, @issue_template_setting + end + + def test_help_message_enabled + assert_equal(true, @issue_template_setting.enabled) + assert_equal(false, !@issue_template_setting.enabled) + end + + def test_duplicate_project_setting + templ = IssueTemplateSetting.find_or_create(3) + templ.attributes = {:enabled => true, :help_message => 'Help!'} + assert templ.save!, 'Failed to save.' + + # test which has the same proect id + templ2 = IssueTemplateSetting.new + templ2.attributes = {:project_id => 1, :enabled => true, :help_message => 'Help!'} + assert !templ2.save, 'Dupricate project should be denied.' + end +end diff --git a/test/unit/issue_template_test.rb b/test/unit/issue_template_test.rb new file mode 100644 index 0000000..979f646 --- /dev/null +++ b/test/unit/issue_template_test.rb @@ -0,0 +1,23 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class IssueTemplateTest < ActiveSupport::TestCase + fixtures :issue_templates + + def setup + @issue_template = IssueTemplate.find(1) + end + + def test_truth + assert_kind_of IssueTemplate, @issue_template + end + + def test_template_enabled + @issue_template.enabled = true + @issue_template.save! + assert_equal true, @issue_template.enabled?, @issue_template.enabled? + + @issue_template.enabled = false + @issue_template.save! + assert_equal false, @issue_template.enabled?, @issue_template.enabled? + end +end