├── .circleci
└── config.yml
├── .eslintrc.yml
├── .github
├── FUNDING.yml
└── workflows
│ ├── check-assets-js.yml
│ └── greetings.yml
├── .gitignore
├── .rubocop.yml
├── .rubocop_todo.yml
├── .tool-versions
├── CHANGELOG.md
├── Dockerfile
├── Gemfile.local
├── ISSUE_TEMPLATE.md
├── LICENSE.txt
├── README.md
├── RELEASE-NOTES.md
├── app
├── controllers
│ ├── concerns
│ │ ├── issue_templates_common.rb
│ │ └── project_templates_common.rb
│ ├── global_issue_templates_controller.rb
│ ├── global_note_templates_controller.rb
│ ├── issue_templates_controller.rb
│ ├── issue_templates_settings_controller.rb
│ └── note_templates_controller.rb
├── helpers
│ └── issue_templates_helper.rb
├── models
│ ├── concerns
│ │ ├── attribute_name_mapper.rb
│ │ └── issue_template_common.rb
│ ├── global_issue_template.rb
│ ├── global_note_template.rb
│ ├── global_note_template_project.rb
│ ├── global_note_visible_role.rb
│ ├── issue_template.rb
│ ├── issue_template_setting.rb
│ ├── note_template.rb
│ └── note_visible_role.rb
└── views
│ ├── common
│ ├── _nodata.html.erb
│ ├── _orphaned.html.erb
│ └── _template_links.html.erb
│ ├── global_issue_templates
│ ├── _form.html.erb
│ ├── index.html.erb
│ ├── new.html.erb
│ └── show.html.erb
│ ├── global_note_templates
│ ├── _form.html.erb
│ ├── index.html.erb
│ ├── new.html.erb
│ └── show.html.erb
│ ├── issue_templates
│ ├── _form.html.erb
│ ├── _issue_select_form.html.erb
│ ├── _issue_template_link.html.erb
│ ├── _list_templates.api.rsb
│ ├── _list_templates.html.erb
│ ├── _note_form.html.erb
│ ├── _template_pulldown.html.erb
│ ├── index.api.rsb
│ ├── index.html.erb
│ ├── new.html.erb
│ └── show.html.erb
│ ├── issue_templates_settings
│ └── index.html.erb
│ ├── note_templates
│ ├── _form.html.erb
│ ├── _list_note_templates.html.erb
│ ├── index.api.rsb
│ ├── index.html.erb
│ ├── new.html.erb
│ └── show.html.erb
│ └── settings
│ └── _redmine_issue_templates.html.erb
├── assets
├── images
│ ├── eraser.png
│ ├── issue_templates.png
│ ├── lamp.png
│ ├── preview.png
│ └── ticket.png
├── javascripts
│ └── issue_templates.js
└── stylesheets
│ └── issue_templates.css
├── config
├── locales
│ ├── bg.yml
│ ├── da.yml
│ ├── de.yml
│ ├── en.yml
│ ├── es.yml
│ ├── fr.yml
│ ├── it.yml
│ ├── ja.yml
│ ├── ko.yml
│ ├── pl.yml
│ ├── pt-BR.yml
│ ├── pt.yml
│ ├── ru.yml
│ ├── sr-YU.yml
│ ├── zh-TW.yml
│ └── zh.yml
└── routes.rb
├── db
└── migrate
│ ├── 0001_create_issue_templates.rb
│ ├── 0002_create_issue_template_settings.rb
│ ├── 0003_add_issue_title_to_issue_templates.rb
│ ├── 0004_add_position_to_issue_templates.rb
│ ├── 20121208150810_add_is_default_to_issue_templates.rb
│ ├── 20130630141710_add_enabled_sharing_to_issue_templates.rb
│ ├── 20130701024625_add_inherit_templates_to_issue_template_settings.rb
│ ├── 2014020191500_add_should_replaced_to_issue_template_settings.rb
│ ├── 20140307024626_create_global_issue_templates.rb
│ ├── 20140312054531_create_global_issue_templates_projects.rb
│ ├── 20140330155030_remove_is_default_from_global_issue_templates.rb
│ ├── 20160727222420_add_checklist_json_to_issue_templates.rb
│ ├── 20160828190000_add_checklist_json_to_global_issue_templates.rb
│ ├── 20160829001500_change_issue_template_enabled_column.rb
│ ├── 20160829001530_change_global_issue_template_enabled_column.rb
│ ├── 20170317082100_add_is_default_to_global_issue_templates.rb
│ ├── 20181104065200_add_unique_key_to_global_issue_templates_projects.rb
│ ├── 20190303082102_create_note_templates.rb
│ ├── 20190714171020_create_note_visible_roles.rb
│ ├── 20190714211530_add_visibility_to_note_templates.rb
│ ├── 20200101204020_add_related_link_to_issue_templates.rb
│ ├── 20200101204220_add_related_link_to_global_issue_templates.rb
│ ├── 20200102204815_add_link_title_to_issue_templates.rb
│ ├── 20200102205044_add_link_title_to_global_issue_templates.rb
│ ├── 20200103213630_add_builtin_fields_json_to_issue_templates.rb
│ ├── 20200115073600_add_builtin_fields_json_to_global_issue_templates.rb
│ ├── 20200314132500_change_column_note_template_description.rb
│ ├── 20200405115700_create_global_note_templates.rb
│ ├── 20200405120700_create_global_note_visible_roles.rb
│ ├── 20200418114157_create_join_table_global_note_template_project.rb
│ └── 20230330055341_change_global_note_template_projects_table_name.rb
├── docker-compose.yml
├── init.rb
├── lib
├── issue_templates
│ ├── issues_hook.rb
│ └── journals_hook.rb
└── tasks
│ ├── test.rake
│ └── util.rake
├── package-lock.json
├── package.json
├── scripts
├── components
│ ├── DisplayArea.vue
│ ├── FieldValue.vue
│ └── JsonGenerator.vue
├── issue_templates.js
├── plugins
│ ├── customFields.js
│ └── locales.js
└── template_fields.js
├── spec
├── controllers
│ ├── concerns
│ │ └── issue_templates_common_spec.rb
│ ├── global_issue_templates_controller_spec.rb
│ ├── issue_templates_controller_spec.rb
│ └── settings_controller_spec.rb
├── factories
│ ├── enabled_modules.rb
│ ├── global_issue_templates.rb
│ ├── global_note_templates.rb
│ ├── issue_statuses.rb
│ ├── issue_template_settings.rb
│ ├── issue_templates.rb
│ ├── note_templates.rb
│ ├── projects.rb
│ ├── role.rb
│ ├── trackers.rb
│ └── users.rb
├── features
│ ├── admin_spec.rb
│ ├── create_issue_spec.rb
│ ├── drag_and_drop_spec.rb
│ ├── issue_template_popup_spec.rb
│ ├── issue_template_spec.rb
│ ├── update_issue_spec.rb
│ └── update_template_spec.rb
├── helpers
│ └── issue_templates_helper_spec.rb
├── models
│ ├── global_issue_template_spec.rb
│ ├── global_note_template_spec.rb
│ ├── issue_template_setting_spec.rb
│ ├── issue_template_spec.rb
│ └── note_visible_role_spec.rb
├── rails_helper.rb
├── requests
│ ├── global_note_templates_spec.rb
│ └── note_templates_spec.rb
├── spec_helper.rb
└── support
│ ├── controller_helper.rb
│ └── login_helper.rb
├── test
├── fixtures
│ ├── global_issue_templates.yml
│ ├── global_issue_templates_projects.yml
│ ├── global_note_templates.yml
│ ├── issue_template_settings.yml
│ ├── issue_templates.yml
│ ├── note_templates.yml
│ └── note_visible_roles.yml
├── functional
│ ├── global_issue_templates_controller_test.rb
│ ├── issue_templates_controller_test.rb
│ ├── issue_templates_settings_controller_test.rb
│ ├── issues_controller_test.rb
│ ├── note_templates_controller_test.rb
│ └── projects_controller_test.rb
├── integration
│ └── layout_test.rb
├── test_helper.rb
└── unit
│ ├── global_issue_templates_test.rb
│ ├── global_note_template_test.rb
│ ├── issue_template_setting_test.rb
│ ├── issue_template_test.rb
│ └── note_template_test.rb
└── vite.config.js
/.eslintrc.yml:
--------------------------------------------------------------------------------
1 | env:
2 | browser: true
3 | es2021: true
4 | extends:
5 | - eslint:recommended
6 | - plugin:vue/vue3-essential
7 | overrides: []
8 | parserOptions:
9 | ecmaVersion: latest
10 | sourceType: module
11 | plugins:
12 | - vue
13 | rules:
14 | semi:
15 | - error
16 | - always
17 | indent:
18 | - error
19 | - 2
20 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 | ko_fi: akikopusu
3 |
4 |
--------------------------------------------------------------------------------
/.github/workflows/check-assets-js.yml:
--------------------------------------------------------------------------------
1 | name: Check assets/javascripts/issue_templates.js is up-to-date
2 |
3 | on:
4 | pull_request:
5 |
6 | permissions:
7 | contents: read
8 |
9 | jobs:
10 | check-asset-js:
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - uses: actions/checkout@v4
15 |
16 | - name: Set Node.js
17 | uses: actions/setup-node@v4
18 | with:
19 | node-version: 18.16.0
20 |
21 | - name: Install dependencies
22 | run: npm ci
23 |
24 | - name: Rebuild the assets/javascripts/issue_templates.js
25 | run: npm run build
26 |
27 | - name: Compare the expected and actual assets/javascripts/issue_templates.js
28 | run: |
29 | if [ "$(git diff --ignore-space-at-eol assets/javascripts/ | wc -l)" -gt "0" ]; then
30 | message="assets/javascripts/issue_templates.js is out of date. Please build and push again according to the \"Build scripts\" section in README.md."
31 | echo $message
32 |
33 | # Also add the message to the GitHub step summary
34 | echo "## :x: assets/javascripts/issue_templates.js is out of date" >> $GITHUB_STEP_SUMMARY
35 | echo $message >> $GITHUB_STEP_SUMMARY
36 | exit 1
37 | fi
38 |
--------------------------------------------------------------------------------
/.github/workflows/greetings.yml:
--------------------------------------------------------------------------------
1 | name: Greetings
2 |
3 | on: [issues]
4 |
5 | jobs:
6 | greeting:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/first-interaction@v1
10 | with:
11 | repo-token: ${{ secrets.GITHUB_TOKEN }}
12 | issue-message: 'Thank you for contributing to Redmine Issue Templates plugin!'' first issue'
13 | pr-message: 'Thanks you for contributing to Redmine Issue Templates plugin!'' first pr'
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | coverage/
2 | node_modules
3 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | inherit_from: .rubocop_todo.yml
2 | require:
3 | - rubocop-rails
4 | AllCops:
5 | TargetRubyVersion: 2.4
6 | Exclude:
7 | - 'db/**/*'
8 |
9 |
--------------------------------------------------------------------------------
/.rubocop_todo.yml:
--------------------------------------------------------------------------------
1 | # This configuration was generated by
2 | # `rubocop --auto-gen-config`
3 | # on 2016-06-01 07:37:41 +0900 using RuboCop version 0.40.0.
4 | # The point is for the user to remove these configuration records
5 | # one by one as the offenses are removed from the code base.
6 | # Note that changes in the inspected code, or installation of new
7 | # versions of RuboCop, may require this file to be generated again.
8 |
9 | # Offense count: 3
10 | Metrics/AbcSize:
11 | Max: 55
12 |
13 | # Offense count: 1
14 | # Configuration parameters: CountComments.
15 | Metrics/ClassLength:
16 | Max: 200
17 | Exclude:
18 | - 'spec/**/*'
19 | - 'test/**/*'
20 |
21 | # "Line is too long"を無効
22 | Layout/LineLength:
23 | Enabled: false
24 |
25 | # Offense count: 1
26 | Metrics/CyclomaticComplexity:
27 | Max: 10
28 |
29 | # Offense count: 1
30 | Metrics/PerceivedComplexity:
31 | Max: 10
32 |
33 | Metrics/BlockLength:
34 | Max: 30
35 | Exclude:
36 | - 'spec/**/*'
37 | - 'test/**/*'
38 |
39 | # Avoid methods longer than 10 lines of code
40 | MethodLength:
41 | CountComments: true # count full line comments?
42 | Max: 45
43 |
44 | # Aboid Missing top-level module documentation comment.
45 | Documentation:
46 | Enabled: false
47 |
48 | EndOfLine:
49 | Enabled: false
50 |
51 | Metrics/ModuleLength:
52 | Max: 120
53 |
54 | Rails/ApplicationRecord:
55 | Enabled: false
56 |
57 | Rails/UniqueValidationWithoutIndex:
58 | Enabled: false
59 |
60 | Rails/InverseOf:
61 | Enabled: false
62 |
63 | Rails/LexicallyScopedActionFilter:
64 | Enabled: false
65 |
66 | Rails/SkipsModelValidations:
67 | Enabled: false
68 |
--------------------------------------------------------------------------------
/.tool-versions:
--------------------------------------------------------------------------------
1 | nodejs 18.16.0
2 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ruby:2.6
2 | LABEL maintainer="AKIKO TAKANO / (Twitter: @akiko_pusu)" \
3 | description="Image to run Redmine simply with sqlite to try/review plugin."
4 |
5 | ### get Redmine source
6 | ### Replace shell with bash so we can source files ###
7 | RUN rm /bin/sh && ln -s /bin/bash /bin/sh
8 |
9 | ### install default sys packeges ###
10 |
11 | RUN apt-get update
12 | RUN apt-get install -qq -y \
13 | git vim \
14 | sqlite3 default-libmysqlclient-dev
15 | RUN apt-get install -qq -y build-essential libc6-dev
16 |
17 | # for e2e test env
18 | RUN sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
19 | RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
20 | RUN apt-get update && apt-get install -y google-chrome-stable
21 | RUN google-chrome --version | perl -pe 's/([^0-9]+)([0-9]+)(\.[0-9]+).+/$2/g' > chrome-version-major
22 | RUN curl https://chromedriver.storage.googleapis.com/LATEST_RELEASE_`cat chrome-version-major` > chrome-version
23 | RUN curl -O -L http://chromedriver.storage.googleapis.com/`cat chrome-version`/chromedriver_linux64.zip && rm chrome-version*
24 | RUN unzip chromedriver_linux64.zip && mv chromedriver /usr/local/bin
25 |
26 | RUN cd /tmp && svn co http://svn.redmine.org/redmine/trunk redmine
27 | WORKDIR /tmp/redmine
28 |
29 | COPY . /tmp/redmine/plugins/redmine_issue_templates/
30 |
31 |
32 | # add database.yml (for development, development with mysql, test)
33 | RUN echo $'test:\n\
34 | adapter: sqlite3\n\
35 | database: /tmp/data/redmine_test.sqlite3\n\
36 | encoding: utf8mb4\n\
37 | development:\n\
38 | adapter: sqlite3\n\
39 | database: /tmp/data/redmine_development.sqlite3\n\
40 | encoding: utf8mb4\n\
41 | development_mysql:\n\
42 | adapter: mysql2\n\
43 | host: mysql\n\
44 | password: pasword\n\
45 | database: redemine_development\n\
46 | username: root\n'\
47 | >> config/database.yml
48 |
49 | RUN gem update bundler
50 | RUN bundle install
51 | RUN bundle exec rake db:migrate
52 | EXPOSE 3000
53 | CMD ["rails", "server", "-b", "0.0.0.0"]
54 |
--------------------------------------------------------------------------------
/Gemfile.local:
--------------------------------------------------------------------------------
1 | group :test do
2 | gem 'simplecov-rcov', require: false
3 | gem 'rspec-rails'
4 | if RUBY_VERSION < '3.0'
5 | # factory_bot 6.4.5以上はRuby3.0が必要なため、それ以下のバージョンでは6.4.5未満にする
6 | # https://github.com/thoughtbot/factory_bot/releases/tag/v6.4.5
7 | gem 'factory_bot', '<6.4.5'
8 | end
9 | gem 'factory_bot_rails'
10 | gem 'launchy'
11 | gem 'database_cleaner'
12 | dependencies.reject! { |i| i.name == 'nokogiri' } # Ensure Nokogiri have new version
13 | end
14 |
15 | # for Debug
16 | group :development, :test do
17 | gem 'pry-rails'
18 | gem 'pry-byebug'
19 | end
20 |
--------------------------------------------------------------------------------
/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | This is a template to report bug related to redmine issue templates plugin.
2 | You can fill in any format, free style in case report features or questions.
3 |
4 | ## Summary
5 |
6 |
7 | ## Description
8 |
9 |
10 | ## Environment
11 |
12 | These informations are greatly helpful to support quickly.
13 | You can get these informations from Redmine's information page.
14 | (E.g. http://example.com/admin/info)
15 |
16 | - Redmine version
17 | - Installed plugins
18 | - Ruby version
19 | - OS Platform
20 | - Database (MariaDB, MySQL, PostgreSQL) and its version
21 | - Rails Env
22 |
23 |
24 | ## Visual Proof / Screenshot
25 |
26 | When reporting an application error, post the error stack trace that
27 | you should find in the **log file** (eg. log/production.log).
28 |
29 | Screen shot would be also helpful.
30 |
31 |
32 | ## Expected Results
33 |
34 | ## Actual Results
35 |
36 | ## Workaround
37 |
38 | Please let me know if you have any workaround.
39 |
--------------------------------------------------------------------------------
/app/controllers/concerns/project_templates_common.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ProjectTemplatesCommon
4 | extend ActiveSupport::Concern
5 | included do
6 | before_action :find_user, :find_project, :authorize, except: %i[preview load load_selectable_fields]
7 | before_action :find_object, only: %i[show edit update destroy]
8 | accept_api_auth :index, :list_templates, :load
9 | end
10 |
11 | def show
12 | render render_form_params
13 | end
14 |
15 | def destroy
16 | unless template.destroy
17 | flash[:error] = l(:enabled_template_cannot_destroy)
18 | redirect_to action: :show, project_id: @project, id: template
19 | return
20 | end
21 |
22 | flash[:notice] = l(:notice_successful_delete)
23 | redirect_to action: 'index', project_id: @project
24 | end
25 |
26 | def save_and_flash(message, action_on_failure)
27 | unless template.save
28 | render render_form_params.merge(action: action_on_failure)
29 | return
30 | end
31 |
32 | respond_to do |format|
33 | format.html do
34 | flash[:notice] = l(message)
35 | redirect_to action: 'show', id: template.id, project_id: @project
36 | end
37 | format.js { head 200 }
38 | end
39 | rescue NoteTemplate::NoteTemplateError => e
40 | flash[:error] = e.message
41 | render render_form_params.merge(action: action_on_failure)
42 | nil
43 | end
44 |
45 | def plugin_setting
46 | Setting.plugin_redmine_issue_templates
47 | end
48 |
49 | def apply_all_projects?
50 | plugin_setting['apply_global_template_to_all_projects'].to_s == 'true'
51 | end
52 |
53 | private
54 |
55 | def template
56 | raise NotImplementedError, "You must implement #{self.class}##{__method__}"
57 | end
58 |
59 | def find_user
60 | @user = User.current
61 | end
62 |
63 | def find_tracker
64 | @tracker = Tracker.find(params[:issue_tracker_id])
65 | end
66 |
67 | def find_project
68 | @project = Project.find(params[:project_id])
69 | rescue ActiveRecord::RecordNotFound
70 | render_404
71 | end
72 | end
73 |
--------------------------------------------------------------------------------
/app/controllers/global_note_templates_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # noinspection RubocopInspection
4 | class GlobalNoteTemplatesController < ApplicationController
5 | layout 'base'
6 | helper :issues
7 | helper :issue_templates
8 | menu_item :issues
9 |
10 | before_action :find_object, only: %i[show update destroy]
11 | before_action :find_project, only: %i[update]
12 | before_action :require_admin, only: %i[index new show], excep: [:preview]
13 |
14 | #
15 | # Action for global template : Admin right is required.
16 | #
17 | def index
18 | trackers = Tracker.sorted
19 | template_map = {}
20 | trackers.each do |tracker|
21 | tracker_id = tracker.id
22 | templates = GlobalNoteTemplate.search_by_tracker(tracker_id).sorted
23 | template_map[Tracker.find(tracker_id)] = templates if templates.any?
24 | end
25 |
26 | render layout: !request.xhr?, locals: { template_map: template_map, trackers: trackers }
27 | end
28 |
29 | def new
30 | # create empty instance
31 | @global_note_template = GlobalNoteTemplate.new
32 | render render_form_params
33 | end
34 |
35 | def create
36 | @global_note_template = GlobalNoteTemplate.new(template_params)
37 | @global_note_template.author = User.current
38 |
39 | save_and_flash(:notice_successful_create, :new) && return
40 | end
41 |
42 | def show
43 | render render_form_params
44 | end
45 |
46 | def update
47 | # Workaround in case author id is null
48 | @global_note_template.author = User.current if @global_note_template.author.blank?
49 | @global_note_template.safe_attributes = template_params
50 |
51 | save_and_flash(:notice_successful_update, :show)
52 | end
53 |
54 | def destroy
55 | unless @global_note_template.destroy
56 | flash[:error] = l(:enabled_template_cannot_destroy)
57 | redirect_to action: :show, id: @global_note_template
58 | return
59 | end
60 |
61 | flash[:notice] = l(:notice_successful_delete)
62 | redirect_to action: 'index'
63 | end
64 |
65 | def find_project
66 | @projects = Project.all
67 | end
68 |
69 | def find_object
70 | @global_note_template = GlobalNoteTemplate.find(params[:id])
71 | rescue ActiveRecord::RecordNotFound
72 | render_404
73 | end
74 |
75 | def save_and_flash(message, action_on_failure)
76 | unless @global_note_template.save
77 | render render_form_params.merge(action: action_on_failure)
78 | return
79 | end
80 |
81 | respond_to do |format|
82 | format.html do
83 | flash[:notice] = l(message)
84 | redirect_to action: 'show', id: @global_note_template.id
85 | end
86 | format.js { head 200 }
87 | end
88 | end
89 |
90 | def template_params
91 | params.require(:global_note_template)
92 | .permit(:global_note_template_id, :tracker_id, :name, :memo, :description,
93 | :enabled, :author_id, :position, :visibility, role_ids: [], project_ids: [])
94 | end
95 |
96 | def render_form_params
97 | trackers = Tracker.sorted
98 | projects = Project.all
99 |
100 | { layout: !request.xhr?,
101 | locals: { trackers: trackers, apply_all_projects: apply_all_projects?,
102 | note_template: @global_note_template, projects: projects } }
103 | end
104 |
105 | def apply_all_projects?
106 | plugin_setting['apply_global_template_to_all_projects'].to_s == 'true'
107 | end
108 |
109 | def plugin_setting
110 | Setting.plugin_redmine_issue_templates
111 | end
112 | end
113 |
--------------------------------------------------------------------------------
/app/controllers/issue_templates_settings_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # noinspection RubocopInspection
4 | class IssueTemplatesSettingsController < ApplicationController
5 | before_action :find_project, :find_user
6 | before_action :authorize, :find_issue_templates_setting, except: %i[preview]
7 |
8 | def index; end
9 |
10 | def edit
11 | return if params[:settings].blank?
12 |
13 | update_template_setting
14 | flash[:notice] = l(:notice_successful_update)
15 | redirect_to action: 'index', project_id: @project
16 | end
17 |
18 | def preview
19 | @text = params[:settings][:help_message]
20 | render partial: 'common/preview'
21 | end
22 |
23 | def menu_items
24 | { issue_templates_settings: { default: :issue_templates, actions: {} } }
25 | end
26 |
27 | private
28 |
29 | def find_user
30 | @user = User.current
31 | end
32 |
33 | def find_project
34 | @project = Project.find(params[:project_id])
35 | rescue ActiveRecord::RecordNotFound
36 | render_404
37 | end
38 |
39 | def find_issue_templates_setting
40 | @issue_templates_setting = IssueTemplateSetting.find_or_create(@project.id)
41 | end
42 |
43 | def update_template_setting
44 | issue_templates_setting = IssueTemplateSetting.find_or_create(@project.id)
45 | attribute = params[:settings]
46 | issue_templates_setting.update(enabled: attribute[:enabled],
47 | help_message: attribute[:help_message],
48 | inherit_templates: attribute[:inherit_templates],
49 | should_replaced: attribute[:should_replaced])
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/app/helpers/issue_templates_helper.rb:
--------------------------------------------------------------------------------
1 | module IssueTemplatesHelper
2 | def project_tracker?(tracker, project)
3 | return false unless tracker.present?
4 |
5 | project.trackers.exists?(tracker.id)
6 | end
7 |
8 | def non_project_tracker_msg(flag)
9 | return '' if flag
10 |
11 | "#{l(:unused_tracker_at_this_project)}".html_safe
12 | end
13 |
14 | def template_target_trackers(project, issue_template)
15 | trackers = project.trackers
16 | trackers |= [issue_template.tracker] unless issue_template.tracker.blank?
17 | trackers.collect { |obj| [obj.name, obj.id] }
18 | end
19 |
20 | def options_for_template_pulldown(options)
21 | options.map do |option|
22 | text = option.try(:name).to_s
23 | tag_builder.content_tag_string(:option, text, option, true)
24 | end.join("\n").html_safe
25 | end
26 |
27 | def localize_to_script
28 | return {
29 | button_add: l(:button_add),
30 | button_apply: l(:button_apply),
31 | button_reset: l(:button_reset),
32 | enter_value: l(:enter_value, default: "Please enter a value"),
33 | field_value: l(:field_value),
34 | help_for_this_field: l(:help_for_this_field),
35 | label_field_information: l(:label_field_information, default: "Field information"),
36 | label_builtin_fields_json: l(:label_builtin_fields_json, default: "JSON for fields"),
37 | label_select_field: l(:label_select_field, default: "Select a field"),
38 | unavailable_fields_for_this_tracker: l(:unavailable_fields_for_this_tracker, default: "Unavailable field for this tracker"),
39 | }
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/app/models/concerns/attribute_name_mapper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # A mixin to display the appropriate field name when displaying a validation error message.
4 | module AttributeNameMapper
5 | extend ActiveSupport::Concern
6 |
7 | module ClassMethods
8 | def attribute_map
9 | {}
10 | end
11 |
12 | def human_attribute_name(attr, options = {})
13 | map = ActiveSupport::HashWithIndifferentAccess.new(attribute_map)
14 | if map.has_key?(attr)
15 | l(map[attr])
16 | else
17 | super(attr, options)
18 | end
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/app/models/concerns/issue_template_common.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module IssueTemplateCommon
4 | extend ActiveSupport::Concern
5 |
6 | #
7 | # Common scope both global and project scope template.
8 | #
9 | included do
10 | belongs_to :author, class_name: 'User', foreign_key: 'author_id'
11 | belongs_to :tracker
12 | before_save :check_default
13 |
14 | before_destroy :confirm_disabled
15 |
16 | validates :title, presence: true
17 | validates :tracker, presence: true
18 | validates :description, presence: true
19 | validates :related_link, format: { with: URI::DEFAULT_PARSER.make_regexp }, allow_blank: true
20 |
21 | scope :enabled, -> { where(enabled: true) }
22 | scope :sorted, -> { order(:position) }
23 | scope :search_by_tracker, lambda { |tracker_id|
24 | where(tracker_id: tracker_id) if tracker_id.present?
25 | }
26 |
27 | scope :is_default, -> { where(is_default: true) }
28 | scope :not_default, -> { where(is_default: false) }
29 |
30 | scope :orphaned, lambda { |project_id = nil|
31 | condition = all
32 | if project_id.present? && try(:name) == 'IssueTemplate'
33 | condition = condition.where(project_id: project_id)
34 | ids = Tracker.joins(:projects).where(projects: { id: project_id }).pluck(:id)
35 | else
36 | ids = Tracker.pluck(:id)
37 | end
38 | condition.where.not(tracker_id: ids)
39 | }
40 |
41 | after_destroy do |template|
42 | logger.info("[Destroy] #{self.class}: #{template.inspect}")
43 | end
44 |
45 | # ActiveRecord::SerializationTypeMismatch may be thrown if non hash object is assigned.
46 | #
47 | # Passing the class as positional argument has removed in Rails 7.2.
48 | # https://edgeguides.rubyonrails.org/7_2_release_notes.html#active-record-removals
49 | if Rails.gem_version >= Gem::Version.new('7.2')
50 | serialize :builtin_fields_json, type: Hash, coder: YAML
51 | else
52 | serialize :builtin_fields_json, Hash
53 | end
54 | end
55 |
56 | #
57 | # Common methods both global and project scope template.
58 | #
59 | def enabled?
60 | enabled
61 | end
62 |
63 | def <=>(other)
64 | position <=> other.position
65 | end
66 |
67 | # Keep this method for a while, but this will be deprecated.
68 | # Please see: https://github.com/akiko-pusu/redmine_issue_templates/issues/363
69 | def checklist
70 | return [] if checklist_json.blank?
71 |
72 | begin
73 | JSON.parse(checklist_json)
74 | rescue StandardError
75 | []
76 | end
77 | end
78 |
79 | def template_json(except: nil)
80 | template = {}
81 | template[self.class::Config::JSON_OBJECT_NAME] = generate_json
82 | return template.to_json(root: true) if except.blank?
83 |
84 | template.to_json(root: true, except: [except])
85 | end
86 |
87 | def builtin_fields
88 | builtin_fields_json.to_json
89 | end
90 |
91 | def generate_json
92 | result = attributes
93 | result[:link_title] = link_title.presence || I18n.t(:issue_template_related_link, default: 'Related Link')
94 | result[:checklist] = checklist
95 | result.except('checklist_json')
96 | end
97 |
98 | def template_struct(option = {})
99 | Struct.new(:value, :name, :class, :selected).new(id, title, option[:class])
100 | end
101 |
102 | def log_destroy_action(template)
103 | logger.info "[Destroy] #{self.class}: #{template.inspect}" if logger&.info
104 | end
105 |
106 | def confirm_disabled
107 | return unless enabled?
108 |
109 | errors.add :base, 'enabled_template_cannot_destroy'
110 | throw :abort
111 | end
112 |
113 | def copy_title
114 | "copy_of_#{title}"
115 | end
116 | end
117 |
--------------------------------------------------------------------------------
/app/models/global_issue_template.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class GlobalIssueTemplate < (defined?(ApplicationRecord) == 'constant' ? ApplicationRecord : ActiveRecord::Base)
4 | include Redmine::SafeAttributes
5 | include IssueTemplateCommon
6 | include AttributeNameMapper
7 | validates :title, uniqueness: { scope: :tracker_id }
8 | has_and_belongs_to_many :projects
9 |
10 | acts_as_positioned scope: [:tracker_id]
11 |
12 | safe_attributes 'title',
13 | 'description',
14 | 'tracker_id',
15 | 'note',
16 | 'enabled',
17 | 'is_default',
18 | 'issue_title',
19 | 'project_ids',
20 | 'position',
21 | 'author_id',
22 | 'related_link',
23 | 'link_title',
24 | 'builtin_fields_json'
25 |
26 | # for intermediate table assosciations
27 | scope :search_by_project, lambda { |project_id|
28 | joins(:projects).where(projects: { id: project_id }) if project_id.present?
29 | }
30 |
31 | module Config
32 | JSON_OBJECT_NAME = 'global_issue_template'
33 | end
34 | Config.freeze
35 |
36 | #
37 | # In case set is_default and updated, others are also updated.
38 | #
39 | def check_default
40 | return unless is_default? && is_default_changed?
41 |
42 | self.class.search_by_tracker(tracker_id).update_all(is_default: false)
43 | end
44 |
45 | #
46 | # Class method
47 | #
48 | class << self
49 | def get_templates_for_project_tracker(project_id, tracker_id = nil)
50 | GlobalIssueTemplate.search_by_tracker(tracker_id)
51 | .search_by_project(project_id)
52 | .enabled
53 | .sorted
54 | end
55 |
56 | def attribute_map
57 | {
58 | description: :issue_description,
59 | title: :issue_template_name,
60 | }
61 | end
62 | end
63 | end
64 |
--------------------------------------------------------------------------------
/app/models/global_note_template_project.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class GlobalNoteTemplateProject < (defined?(ApplicationRecord) == 'constant' ? ApplicationRecord : ActiveRecord::Base)
4 | belongs_to :project
5 | belongs_to :global_note_template, optional: true
6 | end
7 |
--------------------------------------------------------------------------------
/app/models/global_note_visible_role.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class GlobalNoteVisibleRole < (defined?(ApplicationRecord) == 'constant' ? ApplicationRecord : ActiveRecord::Base)
4 | include Redmine::SafeAttributes
5 |
6 | safe_attributes 'global_note_template_id', 'role_id'
7 | belongs_to :role
8 | belongs_to :global_note_template, optional: true
9 |
10 | validates :role_id, presence: true
11 | validates :global_note_template_id, presence: true
12 |
13 | scope :search_by_note_template, lambda { |note_template_id|
14 | where(global_note_template_id: note_template_id)
15 | }
16 | end
17 |
--------------------------------------------------------------------------------
/app/models/issue_template.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class IssueTemplate < (defined?(ApplicationRecord) == 'constant' ? ApplicationRecord : ActiveRecord::Base)
4 | include Redmine::SafeAttributes
5 | include IssueTemplateCommon
6 | include AttributeNameMapper
7 | belongs_to :project
8 | validates :project_id, presence: true
9 | validates :title, uniqueness: { scope: :project_id }
10 | acts_as_positioned scope: %i[project_id tracker_id]
11 |
12 | # author and project should be stable.
13 | safe_attributes 'title',
14 | 'description',
15 | 'tracker_id',
16 | 'note',
17 | 'enabled',
18 | 'issue_title',
19 | 'is_default',
20 | 'enabled_sharing',
21 | 'visible_children',
22 | 'position',
23 | 'related_link',
24 | 'link_title',
25 | 'builtin_fields_json'
26 |
27 | scope :enabled_sharing, -> { where(enabled_sharing: true) }
28 | scope :search_by_project, lambda { |prolect_id|
29 | where(project_id: prolect_id)
30 | }
31 |
32 | module Config
33 | JSON_OBJECT_NAME = 'issue_template'
34 | end
35 | Config.freeze
36 |
37 | #
38 | # In case set is_default and updated, others are also updated.
39 | #
40 | def check_default
41 | return unless is_default? && is_default_changed?
42 |
43 | self.class.search_by_project(project_id).search_by_tracker(tracker_id).update_all(is_default: false)
44 | end
45 |
46 | # return projects that use this template
47 | def used_projects
48 | return [] unless enabled_sharing
49 |
50 | projects = project.descendants
51 | .joins(:trackers, :enabled_modules).merge(Tracker.where(id: tracker_id)).merge(EnabledModule.where(name: 'issue_templates'))
52 | IssueTemplateSetting.where(project_id: projects).inherit_templates.select(:project_id)
53 | end
54 |
55 | #
56 | # Class method
57 | #
58 | class << self
59 | def get_inherit_templates(project_ids, tracker_id)
60 | # keep ordering of project tree
61 | IssueTemplate.search_by_project(project_ids)
62 | .search_by_tracker(tracker_id)
63 | .enabled
64 | .enabled_sharing
65 | .sorted
66 | end
67 |
68 | def get_templates_for_project_tracker(project_id, tracker_id = nil)
69 | IssueTemplate.search_by_project(project_id)
70 | .search_by_tracker(tracker_id)
71 | .enabled
72 | .sorted
73 | end
74 |
75 | def attribute_map
76 | {
77 | description: :issue_description,
78 | title: :issue_template_name,
79 | }
80 | end
81 | end
82 | end
83 |
--------------------------------------------------------------------------------
/app/models/issue_template_setting.rb:
--------------------------------------------------------------------------------
1 | class IssueTemplateSetting < (defined?(ApplicationRecord) == 'constant' ? ApplicationRecord : ActiveRecord::Base)
2 | include Redmine::SafeAttributes
3 | belongs_to :project
4 |
5 | validates_uniqueness_of :project_id
6 | validates_presence_of :project_id
7 |
8 | safe_attributes 'help_message', 'enabled', 'inherit_templates', 'should_replaced'
9 |
10 | scope :inherit_templates, -> { where(inherit_templates: true) }
11 |
12 | def self.find_or_create(project_id)
13 | setting = IssueTemplateSetting.where(project_id: project_id).first
14 | unless setting.present?
15 | setting = IssueTemplateSetting.new
16 | setting.project_id = project_id
17 | setting.save!
18 | end
19 | setting
20 | end
21 |
22 | #
23 | # Class method
24 | #
25 | class << self
26 | def apply_template_to_child_projects(project_id)
27 | setting = find_setting(project_id)
28 | setting.apply_template_to_child_projects
29 | end
30 |
31 | def unapply_template_from_child_projects(project_id)
32 | setting = find_setting(project_id)
33 | setting.unapply_template_from_child_projects
34 | end
35 |
36 | private
37 |
38 | def find_setting(project_id)
39 | raise ArgumentError, 'Please specify valid project_id.' if project_id.blank?
40 |
41 | setting = IssueTemplateSetting.where(project_id: project_id).first
42 | raise ActiveRecord::RecordNotFound if setting.blank?
43 |
44 | setting
45 | end
46 | end
47 |
48 | def enable_help?
49 | enabled == true && !help_message.blank?
50 | end
51 |
52 | def enabled_inherit_templates?
53 | inherit_templates
54 | end
55 |
56 | def child_projects
57 | project.descendants
58 | end
59 |
60 | def apply_template_to_child_projects
61 | update_inherit_template_of_child_projects(true)
62 | end
63 |
64 | def unapply_template_from_child_projects
65 | update_inherit_template_of_child_projects(false)
66 | end
67 |
68 | def get_inherit_templates(tracker = nil)
69 | return [] unless enabled_inherit_templates?
70 |
71 | project_ids = project.ancestors.collect(&:id)
72 | tracker = project.trackers.pluck(:tracker_id) if tracker.blank?
73 |
74 | # first: get inherit_templates
75 | IssueTemplate.get_inherit_templates(project_ids, tracker)
76 | end
77 |
78 | private
79 |
80 | def update_inherit_template_of_child_projects(value)
81 | IssueTemplateSetting.where(project_id: child_projects).update_all(inherit_templates: value)
82 | end
83 | end
84 |
--------------------------------------------------------------------------------
/app/models/note_template.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class NoteTemplate < (defined?(ApplicationRecord) == 'constant' ? ApplicationRecord : ActiveRecord::Base)
4 | include Redmine::SafeAttributes
5 | include AttributeNameMapper
6 |
7 | class NoteTemplateError < StandardError; end
8 |
9 | # author and project should be stable.
10 | safe_attributes 'name', 'description', 'enabled', 'memo', 'tracker_id',
11 | 'project_id', 'position', 'visibility'
12 |
13 | attr_accessor :role_ids
14 | validates :role_ids, presence: true, if: :roles?
15 |
16 | belongs_to :project
17 | belongs_to :author, class_name: 'User', foreign_key: 'author_id'
18 | belongs_to :tracker
19 |
20 | has_many :note_visible_roles, dependent: :nullify
21 | has_many :roles, through: :note_visible_roles
22 |
23 | validates :project_id, presence: true
24 | validates :name, uniqueness: { scope: :project_id }
25 | validates :name, presence: true
26 | validates :tracker, presence: true
27 | validates :description, presence: true
28 | acts_as_positioned scope: %i[project_id tracker_id]
29 |
30 | enum visibility: { mine: 0, roles: 1, open: 2 }
31 |
32 | scope :mine_condition, lambda { |user_id|
33 | where(author_id: user_id).mine if user_id.present?
34 | }
35 | scope :roles_condition, lambda { |role_ids|
36 | joins(:note_visible_roles).where(note_visible_roles: { role_id: role_ids })
37 | }
38 |
39 | scope :enabled, -> { where(enabled: true) }
40 | scope :sorted, -> { order(:position) }
41 | scope :search_by_tracker, lambda { |tracker_id|
42 | where(tracker_id: tracker_id) if tracker_id.present?
43 | }
44 | scope :search_by_project, lambda { |prolect_id|
45 | where(project_id: prolect_id) if prolect_id.present?
46 | }
47 |
48 | before_save :check_visible_roles
49 | after_save :note_visible_roles!
50 | before_destroy :confirm_disabled
51 |
52 | def <=>(other)
53 | position <=> other.position
54 | end
55 |
56 | def template_json
57 | template = {}
58 | template['note_template'] = generate_json
59 | template.to_json(root: true)
60 | end
61 |
62 | def generate_json
63 | attributes
64 | end
65 |
66 | def note_visible_roles!
67 | return unless roles?
68 |
69 | if role_ids.blank?
70 | raise NoteTemplateError, l(:please_select_at_least_one_role,
71 | default: 'Please select at least one role.')
72 | end
73 |
74 | ActiveRecord::Base.transaction do
75 | NoteVisibleRole.where(note_template_id: id).delete_all if note_visible_roles.present?
76 | role_ids.each do |role_id|
77 | NoteVisibleRole.create!(note_template_id: id, role_id: role_id)
78 | end
79 | end
80 | end
81 |
82 | def loadable?(user_id:)
83 | user = User.find(user_id)
84 | return true if user.admin? || open?
85 |
86 | if mine?
87 | user_id == author_id
88 | elsif roles?
89 | user_project_roles = user.roles_for_project(project).pluck(:id)
90 | match_roles = user_project_roles & roles.ids
91 | !match_roles.empty?
92 | else
93 | false
94 | end
95 | end
96 |
97 | private
98 |
99 | def check_visible_roles
100 | return if roles? || note_visible_roles.empty?
101 |
102 | # Remove roles in case template visible scope is not "roles".
103 | # This remove action is included the same transaction scope.
104 | NoteVisibleRole.where(note_template_id: id).delete_all
105 | end
106 |
107 | def confirm_disabled
108 | return unless enabled?
109 |
110 | errors.add :base, 'enabled_template_cannot_destroy'
111 | throw :abort
112 | end
113 |
114 | #
115 | # Class method
116 | #
117 | class << self
118 | def visible_note_templates_condition(user_id:, project_id:, tracker_id:)
119 | user = User.find(user_id)
120 | project = Project.find(project_id)
121 | user_project_roles = user.roles_for_project(project).pluck(:id)
122 |
123 | base_condition = NoteTemplate.search_by_project(project_id).search_by_tracker(tracker_id)
124 |
125 | open_ids = base_condition.open.pluck(:id)
126 | mine_ids = base_condition.mine_condition(user_id).pluck(:id)
127 | role_ids = base_condition.roles_condition(user_project_roles).pluck(:id)
128 |
129 | # return uniq ids
130 | ids = open_ids | mine_ids | role_ids
131 | NoteTemplate.where(id: ids).enabled.includes(:note_visible_roles)
132 | end
133 |
134 | def attribute_map
135 | {
136 | description: :label_comment,
137 | name: :issue_template_name,
138 | }
139 | end
140 | end
141 | end
142 |
--------------------------------------------------------------------------------
/app/models/note_visible_role.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class NoteVisibleRole < (defined?(ApplicationRecord) == 'constant' ? ApplicationRecord : ActiveRecord::Base)
4 | include Redmine::SafeAttributes
5 |
6 | safe_attributes 'note_template_id', 'role_id'
7 | belongs_to :role
8 | belongs_to :note_template, optional: true
9 |
10 | validates :role_id, presence: true
11 | validates :note_template_id, presence: true
12 |
13 | scope :search_by_note_template, lambda { |note_template_id|
14 | where(note_template_id: note_template_id)
15 | }
16 | end
17 |
--------------------------------------------------------------------------------
/app/views/common/_nodata.html.erb:
--------------------------------------------------------------------------------
1 | <% if trackers.blank? %>
2 |
3 | <%= simple_format(l(:text_no_tracker_enabled)) %>
4 |
5 | <% end %>
--------------------------------------------------------------------------------
/app/views/common/_orphaned.html.erb:
--------------------------------------------------------------------------------
1 | <%= l(:orphaned_template) %>
2 |
3 |
4 |
5 | # |
6 | <%= l(:issue_template_name) %> |
7 | <%= l(:label_preview) %> |
8 | <%= l(:field_tracker) %> |
9 | <%= l(:field_author) %> |
10 | <%= l(:field_updated_on) %> |
11 |
12 |
13 |
14 | <% orphaned_templates.each do |issue_template| %>
15 | issue_template issue'>
16 |
17 | <%= link_to h(issue_template.id),
18 | { controller: controller.controller_name, action: 'show',
19 | id: issue_template.id,
20 | }.merge(issue_template.try(:project_id) ? { project_id: issue_template.project } : {}),
21 | { title: issue_template.note } %>
22 | |
23 |
24 | <%= link_to h(issue_template.title),
25 | { controller: controller.controller_name,
26 | id: issue_template.id, action: 'show' },
27 | { title: "#{html_escape(issue_template.note) }"} %>
28 | |
29 |
30 |
37 | |
38 | <%= "ID: #{issue_template.tracker_id}" %> |
39 | <%=h issue_template.author %> |
40 | <%= format_time(issue_template.updated_on) %> |
41 |
42 |
43 |
44 | <%= textilizable(issue_template.description) %> |
45 |
46 | <% end %>
47 |
48 |
49 |
--------------------------------------------------------------------------------
/app/views/common/_template_links.html.erb:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 | <%= link_to(l(:label_list_templates),
6 | { controller: 'issue_templates',
7 | action: 'index',
8 | project_id: @project}, class: 'icon icon-template') %> |
9 | <%= link_to_if_authorized(l(:label_new_templates),
10 | { controller: 'issue_templates', action: 'new', project_id: @project },
11 | class: 'icon icon-add') %>
12 |
13 |
14 |
15 | <%= link_to(l(:label_list_templates),
16 | { controller: 'note_templates',
17 | action: 'index',
18 | project_id: @project}, class: 'icon icon-template') %> |
19 | <%= link_to_if_authorized(l(:label_new_templates),
20 | { controller: 'note_templates', action: 'new', project_id: @project },
21 | class: 'icon icon-add') %>
22 |
23 |
24 |
25 |
26 | <%= link_to_if_authorized(l(:label_settings, default: 'Settings'),
27 | { controller: 'issue_templates_settings', action: 'index', project_id: @project },
28 | class: 'icon icon-settings') %>
29 |
30 |
31 |
--------------------------------------------------------------------------------
/app/views/global_issue_templates/index.html.erb:
--------------------------------------------------------------------------------
1 | <%=h "#{l(:global_issue_templates)}" %>
2 | <%= render partial: 'common/nodata', locals: { trackers: trackers } %>
3 |
4 | <%= link_to(l(:label_new_templates),
5 | { controller: 'global_issue_templates', action: 'new' }, class: 'icon icon-add') %>
6 | <%= link_to(l(:global_note_templates, default: 'Global Note Templates'),
7 | { controller: 'global_note_templates', action: 'index' },
8 | class: 'icon icon-template') %>
9 | <%= link_to(l(:label_settings),
10 | { controller: 'settings', action: 'plugin', id: 'redmine_issue_templates' },
11 | class: 'issue_template icon plugins') %>
12 |
13 |
14 |
15 | <% if template_map.blank? %>
16 |
17 | <%= l(:no_issue_templates_for_this_redmine) %>
18 |
19 | <% end %>
20 | <% template_map.each_key do |tracker| %>
21 |
22 |
<%= tracker.name %>
23 |
24 |
25 |
26 |
27 | # |
28 | <%= l(:issue_template_name) %> |
29 | <%= l(:label_preview) %> |
30 | <%= l(:field_tracker) %> |
31 | <%= l(:field_author) %> |
32 | <%= l(:field_updated_on) %> |
33 | <%= l(:field_is_default) %> |
34 | <%= l(:label_enabled) %> |
35 | <%=l(:button_sort)%> |
36 |
37 |
38 |
39 | <% template_map[tracker].sorted.each do |issue_template| %>
40 | issue_template issue'>
41 | <%= link_to h(issue_template.id), { controller: 'global_issue_templates',
42 | id: issue_template.id, action: 'show' },
43 | { title: issue_template.title } %>
44 | |
45 |
46 | <%= link_to h(issue_template.title), { controller: 'global_issue_templates',
47 | id: issue_template.id, action: 'show' },
48 | { title: "#{html_escape(issue_template.note)}" } %>
49 | |
50 |
51 |
63 |
64 | |
65 | <%=h issue_template.tracker.name %> |
66 | <%=h issue_template.author %> |
67 | <%= format_time(issue_template.updated_on)%> |
68 | <%= checked_image issue_template.is_default? %> |
69 | <%= checked_image issue_template.enabled? %> |
70 |
71 |
72 | <%= reorder_handle(issue_template, :url => url_for({ controller: 'global_issue_templates',
73 | id: issue_template.id, action: 'update' })) %>
74 | |
75 |
76 | <% end %>
77 |
78 |
79 |
80 |
81 | <%= javascript_tag do %>
82 | // NOTE: Sortable feature depends on Redmine's sorting jQuery plugin.
83 | $(function() { $('table.table-sortable tbody').positionedItems() })
84 | <% end %>
85 | <% end %>
86 |
87 |
91 | <%= l(:orphaned_templates, default: 'Orphaned Templates') %>
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/app/views/global_issue_templates/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to(l(:label_list_templates),
3 | { controller: 'global_issue_templates', action: 'index' }, class: 'icon icon-template') %>
4 |
5 | <%=h "#{l(:issue_templates)} / #{l(:button_add)}" %>
6 |
7 | <%= labelled_form_for :global_issue_template, issue_template,
8 | url: { controller: 'global_issue_templates', action: 'create' },
9 | html: { id: 'global_issue_template-form',
10 | class: nil, multipart: false } do |f| %>
11 |
12 | <%= render 'form', { f: f, trackers: trackers, projects: projects,
13 | issue_template: issue_template, apply_all_projects: apply_all_projects,
14 | custom_fields: custom_fields, builtin_fields_enable: builtin_fields_enable } %>
15 | <% end %>
16 |
--------------------------------------------------------------------------------
/app/views/global_issue_templates/show.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to l(:button_delete),
3 | { controller: 'global_issue_templates', action: 'destroy', id: issue_template },
4 | data: { confirm: l(:text_are_you_sure) },
5 | title: l(:enabled_template_cannot_destroy, default: 'Only disabled template can be destroyed.'),
6 | disabled: issue_template.enabled?, method: 'delete', class: 'icon icon-del template-disabled-link' %>
7 | <%= link_to(l(:label_list_templates),
8 | { controller: 'global_issue_templates', action: 'index' }, class: 'icon icon-template') %>
9 |
10 |
11 |
12 | <%= l(:global_issue_templates, default: 'Global Template for note') %>: #<%= issue_template.id %> <%= issue_template.title %>
13 | <%= avatar(issue_template.author, size: '24') %>
14 |
15 |
16 | <%= labelled_form_for :global_issue_template,
17 | issue_template,
18 | url: { controller: 'global_issue_templates', action: 'update', id: issue_template },
19 | html: { id: 'global_issue_template-form', class: nil,
20 | multipart: false } do |f| %>
21 | <%= render 'form', { f: f, trackers: trackers,
22 | issue_template: issue_template, projects: projects, apply_all_projects: apply_all_projects,
23 | custom_fields: custom_fields, builtin_fields_enable: builtin_fields_enable } %>
24 | <% end %>
25 |
--------------------------------------------------------------------------------
/app/views/global_note_templates/index.html.erb:
--------------------------------------------------------------------------------
1 | <%=h "#{l(:global_note_templates, default: 'Global Note Templates')}" %>
2 | <%= render partial: 'common/nodata', locals: { trackers: trackers } %>
3 |
4 | <%= link_to(l(:label_new_templates),
5 | { controller: 'global_note_templates', action: 'new' }, class: 'icon icon-add') %>
6 | <%= link_to(l(:global_issue_templates),
7 | { controller: 'global_issue_templates', action: 'index' },
8 | class: 'icon icon-template') %>
9 | <%= link_to(l(:label_settings),
10 | { controller: 'settings', action: 'plugin', id: 'redmine_issue_templates' },
11 | class: 'issue_template icon plugins') %>
12 |
13 |
14 |
15 | <% if template_map.blank? %>
16 |
17 | <%= l(:no_note_templates_for_this_redmine, default: 'No global note templates are defined for this Redmine site.') %>
18 |
19 | <% end %>
20 |
21 | <% template_map.each_key do |tracker| %>
22 |
23 |
<%= tracker.name %>
24 |
25 |
26 |
27 |
28 | # |
29 | <%= l(:issue_template_name) %> |
30 | <%= l(:label_preview) %> |
31 | <%= l(:field_tracker) %> |
32 | <%= l(:field_author) %> |
33 | <%= l(:field_updated_on) %> |
34 | <%= l(:label_enabled) %> |
35 | <%=l(:button_sort)%> |
36 |
37 |
38 |
39 | <% template_map[tracker].sorted.each do |note_template| %>
40 | issue_template issue'>
41 | <%= link_to h(note_template.id), { controller: 'global_note_templates',
42 | id: note_template.id, action: 'show' },
43 | { title: note_template.name } %>
44 | |
45 |
46 | <%= link_to h(note_template.name), { controller: 'global_note_templates',
47 | id: note_template.id, action: 'show' },
48 | { title: "#{html_escape(note_template.memo)}" } %>
49 | |
50 |
51 |
58 |
59 | |
60 | <%=h note_template.tracker.name %> |
61 | <%=h note_template.author %> |
62 | <%= format_time(note_template.updated_at)%> |
63 | <%= checked_image note_template.enabled? %> |
64 |
65 |
66 | <%= reorder_handle(note_template, :url => url_for({ controller: 'global_note_templates',
67 | id: note_template.id, action: 'update' })) %>
68 | |
69 |
70 | <% end %>
71 |
72 |
73 |
74 |
75 | <%= javascript_tag do %>
76 | // NOTE: Sortable feature depends on Redmine's sorting jQuery plugin.
77 | $(function() { $('table.table-sortable tbody').positionedItems() })
78 | <% end %>
79 | <% end %>
80 |
--------------------------------------------------------------------------------
/app/views/global_note_templates/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to(l(:label_list_templates),
3 | { controller: 'global_note_templates', action: 'index' }, class: 'icon icon-template') %>
4 |
5 | <%=h "#{l(:global_note_templates, default: 'Global Template for note') } / #{l(:button_add)}" %>
6 |
7 | <%= labelled_form_for :global_note_template, note_template,
8 | url: { controller: 'global_note_templates', action: 'create' },
9 | html: { id: 'global_note_template-form', class: nil, multipart: false } do |f| %>
10 |
11 | <%= render 'form', { f: f, trackers: trackers,
12 | note_template: note_template, projects: projects, apply_all_projects: apply_all_projects } %>
13 |
14 | <%= submit_tag l(:button_create) %>
15 | <%= link_to l(:button_cancel), { action: 'index'}, data: { confirm: l(:text_are_you_sure) } %>
16 | <% end %>
17 |
--------------------------------------------------------------------------------
/app/views/global_note_templates/show.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to l(:button_delete),
3 | { controller: 'global_note_templates', action: 'destroy', id: note_template },
4 | data: { confirm: l(:text_are_you_sure) },
5 | name: l(:enabled_template_cannot_destroy, default: 'Only disabled template can be destroyed.'),
6 | disabled: note_template.enabled?, method: 'delete', class: 'icon icon-del template-disabled-link' %>
7 | <%= link_to(l(:label_list_templates),
8 | { controller: 'global_note_templates', action: 'index' }, class: 'icon icon-template') %>
9 |
10 |
11 |
12 | <%= l(:global_note_templates) %>: #<%= note_template.id %> <%= note_template.name %>
13 | <%= avatar(note_template.author, size: '24') %>
14 |
15 |
16 | <%= labelled_form_for :global_note_template,
17 | note_template,
18 | url: { controller: 'global_note_templates', action: 'update', id: note_template },
19 | html: { id: 'global_note_template-form', class: nil,
20 | multipart: false } do |f| %>
21 | <%= render 'form', { f: f, trackers: trackers,
22 | note_template: note_template, projects: projects, apply_all_projects: apply_all_projects } %>
23 |
24 | <%= submit_tag l(:button_save) %>
25 | <%= link_to l(:button_cancel), { action: 'index' },
26 | onclick: 'Element.hide("edit-note_template"); return false' %>
27 | <% end %>
28 |
--------------------------------------------------------------------------------
/app/views/issue_templates/_issue_template_link.html.erb:
--------------------------------------------------------------------------------
1 | <% if authorize_for('issue_templates', 'show') %>
2 | <%= l(:issue_template) %>
3 |
4 | -
5 | <%= link_to(l(:label_list_templates),
6 | controller: 'issue_templates',
7 | action: 'index',
8 | project_id: @project) %>
9 |
10 | -
11 | <%= link_to_if_authorized(l(:label_new_templates),
12 | { controller: 'issue_templates', action: 'new', project_id: @project }) %>
13 |
14 |
15 | <%- end -%>
16 | <% if authorize_for('note_templates', 'show') %>
17 | <%= l(:note_template) %>
18 |
19 | -
20 | <%= link_to(l(:label_list_templates),
21 | controller: 'note_templates',
22 | action: 'index',
23 | project_id: @project) %>
24 |
25 | -
26 | <%= link_to_if_authorized(l(:label_new_templates),
27 | { controller: 'note_templates', action: 'new', project_id: @project }) %>
28 |
29 |
30 | <%- end -%>
--------------------------------------------------------------------------------
/app/views/issue_templates/_list_templates.api.rsb:
--------------------------------------------------------------------------------
1 | api.array :global_issue_templates do
2 | global_issue_templates.each do |template|
3 | api.template do
4 | api.id template.id
5 | api.tracker_id template.tracker_id
6 | api.tracker_name template.tracker.id
7 | api.title template.title
8 | api.issue_title template.issue_title
9 | api.description template.description
10 | api.note template.note
11 | api.enabled template.enabled
12 | api.updated_on template.updated_on
13 | api.created_on template.created_on
14 | api.updated_on template.updated_on
15 | end
16 | end
17 | end
18 | api.array :inherit_templates do
19 | inherit_templates.each do |template|
20 | api.template do
21 | api.id template.id
22 | api.tracker_id template.tracker_id
23 | api.tracker_name template.tracker.name
24 | api.title template.title
25 | api.issue_title template.issue_title
26 | api.description template.description
27 | api.note template.note
28 | api.enabled template.enabled
29 | api.is_default template.id == default_template
30 | api.enabled_sharing template.enabled_sharing
31 | api.position template.position
32 | api.updated_on template.updated_on
33 | api.created_on template.created_on
34 | api.updated_on template.updated_on
35 | end
36 | end
37 | end
38 | api.array :issue_templates do
39 | issue_templates.each do |template|
40 | api.template do
41 | api.id template.id
42 | api.tracker_id template.tracker_id
43 | api.tracker_name template.tracker.name
44 | api.title template.title
45 | api.issue_title template.issue_title
46 | api.description template.description
47 | api.note template.note
48 | api.enabled template.enabled
49 | api.is_default template.is_default
50 | api.enabled_sharing template.enabled_sharing
51 | api.position template.position
52 | api.updated_on template.updated_on
53 | api.created_on template.created_on
54 | api.updated_on template.updated_on
55 | end
56 | end
57 | end
58 |
--------------------------------------------------------------------------------
/app/views/issue_templates/_list_templates.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%=h l(:issue_template_name) %> |
5 | <%=h l(:issue_title) %> |
6 | <%=h l(:issue_description) %> |
7 | <%= l(:field_is_default) %> |
8 | <%=h l(:button_apply, default: 'Apply') %> |
9 |
10 |
11 | <% issue_templates.each do |template| %>
12 | template_data'>
13 |
14 | <%= template.title %>
15 | |
16 |
17 | <%= template.issue_title %>
18 | |
19 |
20 |
32 | |
33 | <%= checked_image template.is_default? %> |
34 |
35 |
37 | |
38 |
39 | <% end %>
40 | <% inherit_templates.each do |template| %>
41 |
42 |
43 | <%= template.title %>
44 | |
45 |
46 | <%= template.issue_title %>
47 | |
48 |
49 |
61 | |
62 | <%= checked_image template == default_template %> |
63 |
64 |
66 | |
67 |
68 | <% end %>
69 | <% global_issue_templates.each do |template| %>
70 | template_data'>
71 |
72 | <%= template.title %>
73 | |
74 |
75 | <%= template.issue_title %>
76 | |
77 |
78 |
90 | |
91 | <%= checked_image template == default_template %> |
92 |
93 |
95 | |
96 |
97 | <% end %>
98 |
99 |
100 |
109 |
--------------------------------------------------------------------------------
/app/views/issue_templates/_note_form.html.erb:
--------------------------------------------------------------------------------
1 | <%
2 | element_id = type
3 | if type == 'template_edit_journal'
4 | element_id = "template_journal_#{@journal.id}_notes"
5 | end
6 | project_id = issue&.project_id
7 | tracker_id = issue&.tracker_id
8 | %>
9 |
30 |
31 |
53 |
--------------------------------------------------------------------------------
/app/views/issue_templates/_template_pulldown.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/app/views/issue_templates/index.api.rsb:
--------------------------------------------------------------------------------
1 | api.array :global_issue_templates do
2 | @global_issue_templates.each do |template|
3 | api.template do
4 | api.id template.id
5 | api.tracker_id template.tracker_id
6 | api.tracker_name template.tracker.try(:name) || nil
7 | api.title template.title
8 | api.issue_title template.issue_title
9 | api.description template.description
10 | api.note template.note
11 | api.enabled template.enabled
12 | api.updated_on template.updated_on
13 | api.created_on template.created_on
14 | api.updated_on template.updated_on
15 | end
16 | end
17 | end
18 | api.array :inherit_templates do
19 | @inherit_templates.each do |template|
20 | api.template do
21 | api.id template.id
22 | api.tracker_id template.tracker_id
23 | api.tracker_name template.tracker.tracker.try(:name) || nil
24 | api.title template.title
25 | api.issue_title template.issue_title
26 | api.description template.description
27 | api.note template.note
28 | api.enabled template.enabled
29 | api.is_default template.is_default
30 | api.enabled_sharing template.enabled_sharing
31 | api.position template.position
32 | api.updated_on template.updated_on
33 | api.created_on template.created_on
34 | api.updated_on template.updated_on
35 | end
36 | end
37 | end
38 | api.array :issue_templates do
39 | project_templates.each do |template|
40 | api.template do
41 | api.id template.id
42 | api.tracker_id template.tracker_id
43 | api.tracker_name template.tracker.try(:name) || nil
44 | api.title template.title
45 | api.issue_title template.issue_title
46 | api.description template.description
47 | api.note template.note
48 | api.enabled template.enabled
49 | api.is_default template.is_default
50 | api.enabled_sharing template.enabled_sharing
51 | api.position template.position
52 | api.updated_on template.updated_on
53 | api.created_on template.created_on
54 | api.updated_on template.updated_on
55 | end
56 | end
57 | end
58 |
--------------------------------------------------------------------------------
/app/views/issue_templates/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to(l(:label_list_templates),
3 | { controller: 'issue_templates',
4 | action: 'index', project_id: project }, class: 'icon icon-template') %>
5 |
6 | <%=h "#{l(:issue_templates)} / #{l(:button_add)}" %>
7 | <%= render partial: 'common/nodata', locals: { trackers: project.trackers } %>
8 | <% if project.trackers.any? %>
9 | <%= labelled_form_for :issue_template, @issue_template,
10 | url: { controller: 'issue_templates', action: 'create', project_id: project },
11 | html: { id: 'issue_template-form', class: nil, multipart: false } do |f| %>
12 |
13 | <%= render 'form', { f: f, issue_template: issue_template, project: project, custom_fields: custom_fields,
14 | builtin_fields_enable: builtin_fields_enable } %>
15 |
16 | <%= submit_tag l(:button_create) %>
17 | <%= link_to l(:button_cancel), { action: 'index'}, data: { confirm: l(:text_are_you_sure) } %>
18 | <% end %>
19 | <% end %>
20 |
21 | <%= render partial: 'common/template_links' %>
--------------------------------------------------------------------------------
/app/views/issue_templates_settings/index.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= render partial: 'common/nodata', locals: { trackers: @project.trackers } %>
3 |
4 |
<%= l(:issue_templates_settings, default: 'Issue Templates Setting') %>
5 |
6 |
7 |
<%= l(:issue_templates_optional_settings, default: 'Templates Optional Settings') %>
8 |
<%= l(:about_help_message) %>
9 | <%= labelled_form_for :settings, @issue_templates_setting,
10 | url: { controller: 'issue_templates_settings',
11 | action: 'edit', project_id: @project,
12 | setting_id: @issue_templates_setting.id },
13 | html: { id: 'issue_templates_settings' } do |f| %>
14 | <%= error_messages_for 'issue_templates_setting' %>
15 |
16 |
17 |
<%= f.check_box :inherit_templates, label: l(:label_inherit_templates) %>
18 |
21 | <%= l(:help_for_this_field) %>
22 |
23 |
24 |
25 |
26 |
27 | <%= f.check_box :should_replaced, label: l(:label_should_replaced) %>
28 |
32 | <%= l(:help_for_this_field) %>
33 |
34 |
35 |
36 |
37 |
<%= f.check_box :enabled, label: l(:label_show_help_message) %>
38 |
39 |
40 | <%=content_tag(:label, l(:label_help_message)) %>
41 | <%=text_area_tag 'settings[help_message]', @issue_templates_setting['help_message'], size: '50x5',
42 | class: 'wiki-edit' %>
43 |
44 |
45 | <%= wikitoolbar_for 'settings_help_message' %>
46 |
47 |
48 |
49 | <%= submit_tag l(:button_save) %>
50 | <% end %>
51 |
52 |
53 |
54 |
55 | <%= l(:label_inherit_templates_help_message) %>
56 |
57 |
58 | <%= l(:label_should_replaced_help_message) %>
59 |
60 |
61 |
62 |
63 | <%= render partial: 'common/template_links' %>
64 |
--------------------------------------------------------------------------------
/app/views/note_templates/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= error_messages_for 'note_template' %>
2 |
3 |
<%= f.text_field :name, required: true, size: 80, label: l(:issue_template_name) %>
4 |
5 |
44 |
45 | <%= f.text_area :memo, cols: 70, rows: 3,
46 | required: false,
47 | label: l(:issue_template_note) %>
48 |
51 | <%= l(:help_for_this_field) %>
52 |
53 |
54 |
55 |
56 |
57 | <%= f.check_box :enabled, label: l(:label_enabled) %>
58 |
61 | <%= l(:help_for_this_field) %>
62 |
63 |
64 |
65 |
66 |
67 | <%= wikitoolbar_for 'note_template_description' %>
68 |
69 |
70 |
71 | <%= l(:help_for_issue_title) %>
72 |
73 |
74 |
75 | <%= l(:label_enabled_help_message) %>
76 |
77 |
78 |
79 | <%= l(:label_memo_help_message, default: 'Please set up a note explaining when applying this template.') %>
80 |
81 |
82 |
83 |
95 |
--------------------------------------------------------------------------------
/app/views/note_templates/_list_note_templates.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%=h l(:note_template_name, default: "Name") %> |
5 | <%=h l(:note_description, default: "Comment Body") %> |
6 | <%=h l(:button_apply, default: 'Apply') %> |
7 |
8 |
9 |
10 | <% note_templates.each do |template| %>
11 |
12 |
13 | <%= template.name %>
14 | |
15 |
16 |
26 | |
27 |
28 |
31 | |
32 |
33 | <% end %>
34 | <% global_note_templates.each do |template| %>
35 |
36 |
37 | <%= template.name %>
38 | |
39 |
40 |
50 | |
51 |
52 |
55 | |
56 |
57 | <% end %>
58 |
59 |
60 |
--------------------------------------------------------------------------------
/app/views/note_templates/index.api.rsb:
--------------------------------------------------------------------------------
1 | api.array :note_templates do
2 | note_templates.each do |template|
3 | api.template do
4 | api.id template.id
5 | api.tracker_id template.tracker_id
6 | api.tracker_name template.tracker.name
7 | api.title template.title
8 | api.description template.description
9 | api.enabled template.enabled
10 | api.created_on template.created_on
11 | api.updated_on template.updated_on
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/views/note_templates/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to(l(:label_list_templates),
3 | { controller: 'note_templates',
4 | action: 'index', project_id: project }, class: 'icon icon-template') %>
5 |
6 | <%=h "#{l(:note_template)} / #{l(:button_add)}" %>
7 | <% if project.trackers.any? %>
8 | <%= labelled_form_for :note_template, @note_template,
9 | url: { controller: 'note_templates', action: 'create', project_id: project },
10 | html: { id: 'issue_template-form', class: nil, multipart: false } do |f| %>
11 |
12 | <%= render 'form', { f: f, note_template: note_template, project: project } %>
13 |
14 | <%= submit_tag l(:button_create) %>
15 | <%= link_to l(:button_cancel), { action: 'index'}, data: { confirm: l(:text_are_you_sure) } %>
16 | <% end %>
17 | <% end %>
18 |
19 | <%= render partial: 'common/template_links' %>
--------------------------------------------------------------------------------
/app/views/note_templates/show.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to_if_authorized l(:button_edit),
3 | { controller: 'note_templates', action: 'update', id: note_template,
4 | project_id: project },
5 | class: 'icon icon-edit', accesskey: accesskey(:edit),
6 | onclick: "document.getElementById('edit-note_template').style.display = 'inline'; return false;" %>
7 | <%= link_to_if_authorized l(:button_delete),
8 | { controller: 'note_templates', action: 'destroy',
9 | id: note_template, project_id: project },
10 | data: { confirm: l(:template_remove_confirm,
11 | default: "Are you sure to remove this template? %{count} subprojects use this template.") },
12 | title: l(:enabled_template_cannot_destroy, default: 'Only disabled template can be destroyed.'),
13 | method: 'delete', class: 'icon icon-del template-disabled-link', disabled: note_template.enabled? %>
14 | <%= link_to(l(:label_list_templates),
15 | { controller: 'note_templates',
16 | action: 'index',
17 | project_id: project }, class: 'icon icon-template') %>
18 | <%= link_to_if_authorized(l(:label_new_templates),
19 | { controller: 'note_templates', action: 'new', project_id: @project },
20 | class: 'icon icon-add') %>
21 |
22 |
23 |
24 | <%= l(:note_template) %>: #<%= note_template.id %> <%= note_template.name %>
25 | <%= avatar(note_template.author, size: '24') %>
26 |
27 |
28 | <%= render partial: 'common/nodata', locals: { trackers: project.trackers } %>
29 | <% if authorize_for('note_templates', 'update') %>
30 |
31 | <%= labelled_form_for :note_template, note_template,
32 | url: { controller: 'note_templates', action: 'update',
33 | project_id: project, id: note_template },
34 | html: { id: 'note_template-form', class: nil, multipart: false } do |f| %>
35 |
36 | <%= render 'form', { f: f, note_template: note_template, project: project } %>
37 |
38 | <%= submit_tag l(:button_save) %>
39 | <%= link_to l(:button_cancel), { action: 'index' },
40 | onclick: 'Element.hide("edit-note_template"); return false' %>
41 | <% end %>
42 |
43 |
44 | <% else %>
45 |
46 |
47 |
48 |
49 |
50 | <%= h note_template.name %>
51 |
52 |
71 |
72 |
73 | <%= note_template.memo.blank? ? l(:label_none) : note_template.memo %>
74 |
75 |
76 |
77 | <%= checked_image note_template.enabled? %>
78 |
79 |
80 |
81 | <%= authoring note_template.created_at, note_template.author %>
82 | <% if note_template.created_at != note_template.updated_at %>
83 | (<%= l(:label_updated_time, time_tag(note_template.updated_at)).html_safe %>)
84 | <% end %>
85 |
86 |
87 |
88 | <% end %>
89 |
90 | <%= render partial: 'common/template_links' %>
91 |
--------------------------------------------------------------------------------
/app/views/settings/_redmine_issue_templates.html.erb:
--------------------------------------------------------------------------------
1 |
43 |
44 |
45 | <%= l(:help_project_local_template_override_global_template) %>
46 |
47 |
48 | <%= l(:warning_project_local_template_override_global_template) %>
49 |
50 |
51 |
52 | <%= l(:help_enable_builtin_fields) %>
53 |
54 |
55 | <%= l(:warning_enable_builtin_fields) %>
56 |
57 |
--------------------------------------------------------------------------------
/assets/images/eraser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/agileware-jp/redmine_issue_templates/24756d64c22a2ba771315ee11afdda843287b5c9/assets/images/eraser.png
--------------------------------------------------------------------------------
/assets/images/issue_templates.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/agileware-jp/redmine_issue_templates/24756d64c22a2ba771315ee11afdda843287b5c9/assets/images/issue_templates.png
--------------------------------------------------------------------------------
/assets/images/lamp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/agileware-jp/redmine_issue_templates/24756d64c22a2ba771315ee11afdda843287b5c9/assets/images/lamp.png
--------------------------------------------------------------------------------
/assets/images/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/agileware-jp/redmine_issue_templates/24756d64c22a2ba771315ee11afdda843287b5c9/assets/images/preview.png
--------------------------------------------------------------------------------
/assets/images/ticket.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/agileware-jp/redmine_issue_templates/24756d64c22a2ba771315ee11afdda843287b5c9/assets/images/ticket.png
--------------------------------------------------------------------------------
/config/locales/da.yml:
--------------------------------------------------------------------------------
1 | # Danish strings go here for Rails i18n
2 | da:
3 | issue_templates: Sagsskabeloner
4 | issue_template: Sagsskabelon
5 | issue_template_note: Note
6 | label_enabled: Aktiveret
7 | label_help_message: Skabelon-hjælpetekst
8 | label_show_help_message: Vis hjælpetekst ved oprettelse eller redigering af sag.
9 | about_help_message: Hvert projekt kan have sin egen hjælpetekst.
10 | close_help: Luk hjælpetekst.
11 | about_template_help_message: Du kan se instruktioner om sagsskabeloner på dette projekt.
12 | label_enabled_help_message: Tjekboks til aktivering af denne skabelon. Fjern afkrydsning for at gemme som kladde.
13 | label_list_templates: "Skabeloner"
14 | label_new_templates: "Tilføj skabelon"
15 | issue_template_name: "Skabelonnavn"
16 | issue_description: "Sagsbeskrivelse"
17 | issue_title: "Sagsemne"
18 | label_applied_for_issue: "Felter: "
19 | help_for_issue_title: "Hvis sat, vil sagens emnefelt blive udfyldt når skabelonen vælges."
20 | help_for_this_field: "Forklaring til dette felt."
21 | permission_manage_issue_templates: "Administrér skabeloner"
22 | permission_edit_issue_templates: "Ret skabeloner"
23 | permission_show_issue_templates: "Vis skabeloner"
24 | project_module_issue_templates: "Sagsskabeloner"
25 | label_isdefault_help_message: "Anvend som den forudvalgte skabelon på sagstypen."
26 | defaulf_template_loaded: "Indlæste forudvalgt skabelon. (Tracker: %{tracker})"
27 | text_no_tracker_enabled: "Der er ikke defineret sagstyper for dette projekt.\nSæt disse inden der defineres sagsskabeloner.. "
28 | label_enabled_sharing: "Del med projekttræet."
29 | label_inherit_templates: "Arv sagsskabeloner"
30 | label_inherit_templates_help_message: "Arv sagsskabeloner fra hovedprojekt. Hovedprojektets sagsskabeloner skal være delt med projekttræet."
31 | label_inherited_templates: "Nedarvede sagsskabeloner"
32 | no_issue_templates_for_this_project: "Ingen sagsskabeloner defineret for dette projekt."
33 | link_to_index_edit_template: "Sagsskabeloner / ret skabeloner"
34 | erase_issue_subject_and_description: "Tøm emne og beskrivelse."
35 | unused_tracker_at_this_project: "NB: Sagstypen anvendes ikke med projektet."
36 | label_enabledshaing_help_message: "Hvis valgt deles skabelonen med underliggende projekter. (Arv sagsskabeloner skal dog være valgt til på underprojekter.)"
37 | label_should_replaced: "Erstat emne og beskrivelse"
38 | label_should_replaced_help_message: "Hvis valgt erstattes emne og beskrivelse med skabelonen. Ellers tilføjes skabelonens indhold den eksisterende tekst."
39 | global_issue_templates: "Fælles sagsskabeloner"
40 | no_issue_templates_for_this_redmine: "Ingen fælles sagsskabeloner defineret."
41 | only_admin_can_associate_global_template: "Kun administratorer kan sammenkoble fælles sagsskabeloner med projekter."
42 | text_no_tracker_enabled_for_global: "Der er ikke defineret nogen sagstyper.\nSæt disse inden der defineres sagsskabeloner."
43 | display_and_filter_issue_templates_in_dialog: "Filtrér Skabeloner"
44 | label_filter_template: "Filtrér Skabeloner"
45 |
--------------------------------------------------------------------------------
/config/locales/es.yml:
--------------------------------------------------------------------------------
1 | # Spanish strings go here for Rails i18n
2 | # Translation by Andres Arias https://github.com/mrlocke
3 | es:
4 | issue_templates: "Plantillas de peticiones"
5 | issue_template: "Plantilla de peticiones"
6 | issue_template_note: "Nota"
7 | label_enabled: "Habilitado"
8 | label_help_message: "Acerca de plantillas"
9 | label_show_help_message: "Mostrar el mensaje de ayuda cunado se crean/modifican las peticiones."
10 | about_help_message: "Cada proyecto puede personalizar el mensaje de ayuda para las plantillas."
11 | close_help: "Cerrar mensaje de ayuda."
12 | about_template_help_message: "Puede ver las instrucciones para las plantillas de peticiones en este proyecto."
13 | label_enabled_help_message: "Marque para activar (habilitar) esta plantilla. Si desea guardarla como borrador, desmarque esta casilla."
14 | label_list_templates: "Lista plantillas"
15 | label_new_templates: "Añadir plantilla"
16 | issue_template_name: "Nombre de la plantilla"
17 | issue_description: "Descripción"
18 | issue_title: "Asunto"
19 | label_applied_for_issue: "Campos que rellenará la plantilla: "
20 | help_for_issue_title: "Si el Asunto ya existe, será reemplazado por el de la plantilla."
21 | help_for_this_field: "Ayuda para este campo."
22 | permission_manage_issue_templates: "Administrar Plantillas"
23 | permission_edit_issue_templates: "Editar Plantillas"
24 | permission_show_issue_templates: "Ver Plantillas"
25 | project_module_issue_templates: "plantillas de Peticiones"
26 | label_isdefault_help_message: "Marque la casilla para seleccionar como plantilla por defecto para este tipo de peticiones."
27 | defaulf_template_loaded: "Cargada la plantilla por defecto. (Tipo de Petición: %{tracker})"
28 | text_no_tracker_enabled: "Todavía no se han configurado Tipos de Peticiones para este proyecto.\nPor favor, configure los primero, ya que las plantillas son asignadas a Tipos de Peticiones."
29 | label_enabled_sharing: "Compartir con el árbol de proyectos."
30 | label_inherit_templates: "Heredar plantillas"
31 | label_inherit_templates_help_message: "Heredar plantillas desde el proyecto padre. (Sólo se comparten las plantillas marcadas como habilitadas.)"
32 | label_inherited_templates: "Plantillas heredadas"
33 | no_issue_templates_for_this_project: "No se han definido plantillas de peticiones para este proyecto."
34 | link_to_index_edit_template: "Ver/Editar plantillas de peticiones"
35 | erase_issue_subject_and_description: "Borrar el Asunto y la Descripción."
36 | unused_tracker_at_this_project: "NOTA: Este Tipo de Petición no está definido para este proyecto. Por favor, cambie la configuración de la plantilla, si es necesario."
37 | label_enabledshaing_help_message: "Si está marcada, la plantilla podrá ser compartida con los sub-proyectos descendientes. (También debe ser activada la opción de heredar plantillas en los proyectos hijos.)"
38 | label_should_replaced: "Reemplazar Asunto y Descripción"
39 | label_should_replaced_help_message: "Si está habilitado, el Asunto y Descripción serán borrados y reemplazados con el texto de la plantilla. De lo contrario, serán añadidos. (Opción por defecto.)"
40 | global_issue_templates: "Plantillas de Peticiones Globales"
41 | no_issue_templates_for_this_redmine: "No hay plantillas globales definidas en este redmine."
42 | only_admin_can_associate_global_template: "Solamente el Administrador de Redmine puede asociar plantillas globales con esté proyecto."
43 | text_no_tracker_enabled_for_global: "No se han definido todavía Tipos de Peticiones. Por favor, configurelos primero, ya que las plantillas son asignadas a Tipos de Peticiones."
44 |
--------------------------------------------------------------------------------
/config/locales/fr.yml:
--------------------------------------------------------------------------------
1 | # Les chaines en français sont définies ici pour Rails i18n
2 | fr:
3 | issue_templates: "Gabarits"
4 | issue_template: "Gabarit des demandes"
5 | issue_template_note: "Note"
6 | label_enabled: "Activer"
7 | label_help_message: "À propos des gabarits"
8 | label_show_help_message: "Afficher le message d'aide lors de la création ou mise à jour d'une demande."
9 | about_help_message: "Le message d'aide peut-être personnalisé pour chaque projet."
10 | close_help: "Fermer le messsage d'aide"
11 | about_template_help_message: "Aide au sujet des gabarits de ce projet."
12 | label_enabled_help_message: "Case à cocher pour activer ce gabarit. Si vous prévoyez sauvegarder ce gabarit en brouillon, décochez cette case."
13 | label_list_templates: "Liste des gabarits"
14 | label_new_templates: "Ajouter un gabarit"
15 | issue_template_name: "Nom du gabarit"
16 | issue_description: "Description de la demande"
17 | issue_title: "Titre de la demande"
18 | label_applied_for_issue: "Champs spécifiques de la demande: "
19 | help_for_issue_title: "Si le titre est défini, il sera appliqué aux demandes en choisissant un gabarit."
20 | help_for_this_field: "Aide pour ce champ."
21 | permission_manage_issue_templates: "Gérer les gabarits"
22 | permission_edit_issue_templates: "Modifier le gabarit"
23 | permission_show_issue_templates: "Afficher les gabarits"
24 | project_module_issue_templates: "Gabarit de demande"
25 | label_isdefault_help_message: "Cochez cette case pour établir ce gabarit par défaut pour ce type de demande."
26 | defaulf_template_loaded: " Le gabarit par défaut est chargé dans la zone de description (Type de demande: %{tracker})"
27 | text_no_tracker_enabled: " Aucun type de demande n'a été configuré encore.\nDéfinissez au moins un type car les gabarits sont définis en fonction des types de demandes."
28 | label_enabled_sharing: "Activer le partage avec l'arborescence du projet."
29 | label_inherit_templates: "Hériter gabarit"
30 | label_inherit_templates_help_message: "Hériter des gabarits du projet parent. (Seuls les gabarits parents dont l'option partage avec l'arborescence du projet est cochée sont listés.)"
31 | label_inherited_templates: "Gabarit hérité"
32 | no_issue_templates_for_this_project: "Aucun gabarit de demande n'est défini pour ce projet."
33 | link_to_index_edit_template: "Liste de gabarits de demandes / éditer gabarit"
34 | erase_issue_subject_and_description: "Enlever le sujet et le texte descriptif"
35 | unused_tracker_at_this_project: "NOTE: Ce tracker n'est pas défini pour ce projet. Veuillez redéfinir les réglages du gabarit si nécessaire."
36 | label_enabledshaing_help_message: "Si vrai, ce gabarit peut être partagé avec les sous-projets. (Vous devez également activer l'option Gabarit hérité dans le projet enfant.)"
37 | label_should_replaced: "Remplacer le sujet et la description"
38 | label_should_replaced_help_message: "Si vrai, les champs sujet et description sont effacés et remplacés par le texte du gabarit. (Par défaut à Faux et le texte en annexe.)"
39 | global_issue_templates: "Modèle de gabarit de demande"
40 | no_issue_templates_for_this_redmine: "Aucun modèle de gabarit de demande n'est défini pour ce redmine."
41 | only_admin_can_associate_global_template: "Seul l'administrateur peut associer un modèle de gabarit avec ce projet."
42 | text_no_tracker_enabled_for_global: "Aucun tracker n'est encore défini.\nVeuillez les définir en premier car les gabarits sont assignés à des trackers."
43 |
--------------------------------------------------------------------------------
/config/locales/it.yml:
--------------------------------------------------------------------------------
1 | # Italian strings go here for Rails i18n
2 | it:
3 | issue_templates: Modelli segnalazioni
4 | issue_template: Modello segnalazione
5 | issue_template_note: Note
6 | label_enabled: Attivato
7 | label_help_message: Info sui modelli
8 | label_show_help_message: Mostra messaggio d'aiuto durante la creazione o modifica delle segnalazioni.
9 | about_help_message: Ogni progetto può avere messaggi d'aiuto personalizzati per i modelli.
10 | close_help: Chiudi messaggio d'aiuto.
11 | about_template_help_message: Puoi vedere le istruzioni per i modelli di segnalazione di questo progetto.
12 | label_enabled_help_message: Abilita questo modello. Se vuoi salvarlo come bozza, disattiva questa opzione.
13 | label_list_templates: "Elenco modelli"
14 | label_new_templates: "Nuovo modello"
15 | issue_template_name: "Nome modello"
16 | issue_description: "Testo della segnalazione"
17 | issue_title: "Titolo segnalazione"
18 | label_applied_for_issue: "Campi applicati per la segnalazione: "
19 | help_for_issue_title: "Se il titolo della Segnalazione è definito, sarà applicato all'Oggetto della segnalazione quando si applica questo modello."
20 | help_for_this_field: "Aiuto per questo campo."
21 | permission_manage_issue_templates: "Gestisci Modelli"
22 | permission_edit_issue_templates: "Modifica Modelli"
23 | permission_show_issue_templates: "Mostra Modelli"
24 | project_module_issue_templates: "Modelli Segnalazioni"
25 | label_isdefault_help_message: "Usa questo come modello predefinito per le segnalazioni del tracker."
26 | defaulf_template_loaded: "Caricato modello predefinito nella descrizione. (Tracker: %{tracker})"
27 | text_no_tracker_enabled: "Nessun tracker è stato configurato per questo progetto.\nPer favore configurali prima perché i modelli segnalazione sono assegnati ai tracker. "
28 | label_enabled_sharing: "Abilita condivisione con gerarchia progetto."
29 | label_inherit_templates: "Eredita modelli segnalazioni"
30 | label_inherit_templates_help_message: "Eredita modelli da progetto padre. (Only parent's templates are listed which marked as enabled sharing with project tree.)"
31 | label_inherited_templates: "Modelli ereditati"
32 | no_issue_templates_for_this_project: "Nessun modello di segnalazione è definito per questo progetto."
33 | link_to_index_edit_template: "Elenco modelli segnalazione / modifica modelli"
34 | erase_issue_subject_and_description: "Pulisci oggetto e descrizione."
35 | unused_tracker_at_this_project: "NOTA: Questo tracker non è abilitato per questo progetto. Per favore modifica le impostazioni del modello se necessario."
36 | label_enabledshaing_help_message: "Se abilitato, questo modello può essere condiviso con i sottoprogetti. (Devi anche abilitare nel sottoprogetto l'opzione Eredita modelli segnalazioni.)"
37 | label_should_replaced: "Sostituisci oggetto e descrizione"
38 | label_should_replaced_help_message: "Se abilitato, l'oggetto e la descrizione esistenti saranno sostituiti con il testo del modello.(Normalmente disattivato, i testi saranno accodati.)"
39 | global_issue_templates: "Modelli Segnalazioni Globali"
40 | no_issue_templates_for_this_redmine: "Nessun modello globale di segnalazione è definito per questo sito Redmine."
41 | only_admin_can_associate_global_template: "Solo un amministratore di Redmine può associare modelli globali a questo progetto."
42 | text_no_tracker_enabled_for_global: "Nessun tracker definito\nÈ necessario configurarli prima perché i Modelli segnalazione sono assegnati ai tracker."
43 | display_and_filter_issue_templates_in_dialog: "Filtra modelli"
44 | label_filter_template: "Filtra modelli"
45 |
--------------------------------------------------------------------------------
/config/locales/pl.yml:
--------------------------------------------------------------------------------
1 | # Polish strings go here for Rails i18n
2 | pl:
3 | issue_templates: Szablony zagadnień
4 | issue_template: Szablon zagadnienia
5 | issue_template_note: info
6 | label_enabled: Włącz
7 | label_help_message: O szablonach
8 | label_show_help_message: Pokazuj pomoc przy tworzeniu / edycji zagadnień.
9 | about_help_message: Każdy projekt może mieć swoje wiadomości pomocy dla szablonów.
10 | close_help: Zamknij pomoc.
11 | about_template_help_message: Możesz zobaczyć instrukcję dla szablonów zagadnień w tym projekcie.
12 | label_enabled_help_message: Powoduje możliwość korzystania z tego szablonu. Jeśli chcesz, aby szablon był zapisany jako szkic, odznacz to pole.
13 | label_list_templates: "Lista szablonów"
14 | label_new_templates: "Dodaj szablon"
15 | issue_template_name: "Nazwa szablonu"
16 | issue_description: "Treść zagadnienia"
17 | issue_title: "Tytuł zagadnienia"
18 | label_applied_for_issue: "Pola wpisywane do zagadnienia: "
19 | help_for_issue_title: "Jeśli wpisany jest tytuł zagadnienia, to także będzie wpisywane przy wyborze szablonu."
20 | help_for_this_field: "Pomoc dla tego pola."
21 | permission_manage_issue_templates: "Zarządzaj Szablonami"
22 | permission_edit_issue_templates: "Edycja Szablonów"
23 | permission_show_issue_templates: "Pokaż Szablony"
24 | project_module_issue_templates: "Szablony Zagadnień"
25 | label_isdefault_help_message: "Zaznacz aby móc używać tego szablonu jako domyślnego dla danego typu zagadnień."
26 | defaulf_template_loaded: "Wczytano domyślny szablon do opisu zagadnienia. (Typ: %{tracker})"
27 | text_no_tracker_enabled: "Nie skonfigurowano typów zagadnień dla tego projektu.\nProszę je najpierw ustawić, ponieważ szablony są przypisane do typów zagadnień. "
28 | label_enabled_sharing: "Włączone współdzielenie z drzewem podprojektów."
29 | label_inherit_templates: "Dziedzicz szablony"
30 | label_inherit_templates_help_message: "Dziedzicz szablony z projektu nadrzędnego. (Tylko szablony projektu nadrzędnego, które mają włączoną funkcję współdzielenia z drzewem podprojektów są pokazane.)"
31 | label_inherited_templates: "Dziedziczone szablony"
32 | no_issue_templates_for_this_project: "Brak zdefiniowanych szablonów zagadnień dla tego projektu."
33 | link_to_index_edit_template: "Lista szablonów zagadnień / edycja szablonów"
34 | erase_issue_subject_and_description: "Czyść tytuł i tekst w opisie zagadnienia."
35 | unused_tracker_at_this_project: "INFO: Ten typ zagadnienia nie jest włączony dla tego projektu. Jeśli to konieczne proszę ponownie zdefiniować ustawienia projektu."
36 | label_enabledshaing_help_message: "Jeśli włączone, możliwe jest współdzielenie szablonu z podprojektami. (Musisz także włączyć szablony dziedziczone w podprojekcie.)"
37 | label_should_replaced: "Nadpisuj tytuł i opis zagadnienia"
38 | label_should_replaced_help_message: "Jeśli włączone, wpisany wcześniej tytuł i opis zagadnienia będą czyszczone i zastępowane tekstem z szablonu. (Domyślnie wyłączone, tekst jest dopisywany.)"
39 | global_issue_templates: "Globalne Szablony Zagadnień"
40 | no_issue_templates_for_this_redmine: "Brak zdefiniowanych globalnych szablonów zagadnień."
41 | only_admin_can_associate_global_template: "Tylko administrator może przypisywać globalne szablony do projektów."
42 | text_no_tracker_enabled_for_global: "Brak zdefiniowanych typów zagadnień.\nProszę je najpierw ustawić, ponieważ szablony są przypisane do typów zagadnień."
43 |
--------------------------------------------------------------------------------
/config/locales/pt.yml:
--------------------------------------------------------------------------------
1 | # Brazilian strings go here for Rails i18n
2 | pt:
3 | issue_templates: Modelos de tarefas
4 | issue_template: Modelo de tarefas
5 | issue_template_note: notas
6 | label_enabled: Habilitado
7 | label_help_message: Sobre modelos
8 | label_show_help_message: Visualizar mensagem de ajuda ao Criar/Atualizar tarefa.
9 | about_help_message: Cada projeto pode customizar mensagem de ajuda para os modelos.
10 | close_help: Fechar mensagem de ajuda.
11 | about_template_help_message: Poder visualizar instruções sobre modelos de tarefas neste projeto.
12 | label_enabled_help_message: Opção para ativar este modelo. Se deseja salvar este modelo como rascunho, desative esta opção.
13 | label_list_templates: "Lista de modelos"
14 | label_new_templates: "Incluir modelo"
15 | issue_template_name: "Nome do modelo"
16 | issue_description: "Corpo da tarefa"
17 | issue_title: "Título da tarefa"
18 | label_applied_for_issue: "Campos aplicados para tarefa: "
19 | help_for_issue_title: "Se definido o título da tarefa, este será aplicado à tarefa ao selecionar o modelo."
20 | help_for_this_field: "Ajuda sobre este campo."
21 | permission_manage_issue_templates: "Gerenciar Modelos"
22 | permission_edit_issue_templates: "Editar Modelos"
23 | permission_show_issue_templates: "Visualizar Modelos"
24 | project_module_issue_templates: "Modelos de Tarefas"
25 | label_isdefault_help_message: "Se esta opção estiver ativa este será o modelo padrão para este tipo de tarefa."
26 | defaulf_template_loaded: "Modelo padrão carregado na área da descrição. (Tipo: %{tracker})"
27 | text_no_tracker_enabled: "Não há tipos configurados para este projeto.\nPor favor configure os tipos pois os modelos são associados a eles. "
28 | label_enabled_sharing: "Habilitar compartilhamento com a árvore de projetos."
29 | label_inherit_templates: "Herdar modelos"
30 | label_inherit_templates_help_message: "Herda modelos do projeto pai. (Apenas os modelos do projeto pai listados são os marcados com compartilhamento habilitado com a árvore de projetos.)"
31 | label_inherited_templates: "Modelos herdados"
32 | no_issue_templates_for_this_project: "Não há modelos de tarefa definidos para este projeto."
33 | link_to_index_edit_template: "Lista de modelos de tarefa / editar modelos"
34 | erase_issue_subject_and_description: "Limpar assunto e descrição."
35 | unused_tracker_at_this_project: "NOTA: Este tipo não está habilitado para uso neste projeto. Por favor redefina as configurações do modelo se necessário."
36 | label_enabledshaing_help_message: "Se habilitado, este modelo pode ser compartilhado com projetos descendentes. (Você também deve ativar a opção de herdar modelos no projeto filho.)"
37 | label_should_replaced: "Substituir assunto e descrição"
38 | label_should_replaced_help_message: "Se habilitado assunto e descrição são excluídos e substituídos pelos textos do modelo. (Por padrão os textos são adicionados.)"
39 | global_issue_templates: "Modelos de Tarefa Globais"
40 | no_issue_templates_for_this_redmine: "Não há modelos de tarefa globais definidos neste redmine."
41 | only_admin_can_associate_global_template: "Somente administradores podem associar modelos globais a este projeto."
42 | text_no_tracker_enabled_for_global: "Nenhum tipo foi definido ainda.\nPor favor defina os tipos, pois os modelos são atribuídos a estes."
43 |
--------------------------------------------------------------------------------
/config/locales/sr-YU.yml:
--------------------------------------------------------------------------------
1 | # English strings go here for Rails i18n
2 | sr-YU:
3 | issue_templates: Šabloni problema
4 | issue_template: Šablon problema
5 | issue_template_note: nota
6 | label_enabled: Aktivno
7 | label_help_message: O šablonu
8 | label_show_help_message: Prikaži poruku za pomoć prilikom kreiranja ili ažuriranja problema.
9 | about_help_message: Za svaki projekat se može prilagoditi poruka za pomoć za upotrebu šablona.
10 | close_help: Zatvori poruku za pomoć.
11 | about_template_help_message: Možete videti instrukcije o korišćenju šablona na ovom projektu.
12 | label_enabled_help_message: Aktivira šablon. Ukoliko i dalje razvijate ovaj šablon, ostavite ga deaktiviranim dok nije završen.
13 | label_list_templates: "Lista šablona"
14 | label_new_templates: "Dodaj šablon"
15 | issue_template_name: "Ime šablona"
16 | issue_description: "Opis"
17 | issue_title: "Predmet"
18 | label_applied_for_issue: "Polja problema za koja se šablon odnosi"
19 | help_for_issue_title: "Ukoliko je definisan predmet problema on će takođe biti primenjen prilikom selekcije šablona."
20 | help_for_this_field: "Pomoć za ovo polje"
21 | permission_manage_issue_templates: "Upravljanje šablonima"
22 | permission_edit_issue_templates: "Ažuriraj šablon"
23 | permission_show_issue_templates: "Prikaži šablon"
24 | project_module_issue_templates: "Šabloni problema"
25 | label_isdefault_help_message: "Ukoliko je aktivirano, ovaj šablon će biti podrazumevani (default) za dato praćenje (tracker) problema."
26 | defaulf_template_loaded: "Učitan je podrazumevani šablon za ovo praćenje problema u polje za opis. (Praćenje: %{tracker})"
27 | text_no_tracker_enabled: "Još uvek nisu konfigurisani pratioci (tracker) za ovaj projekat."
28 | label_enabled_sharing: "Omogući deljenje sa stablom projekta"
29 | label_enabledshaing_help_message: "Aktiviraj da bi subprojekti nasledili šablone."
30 | label_inherit_templates: "Nasledi šablon"
31 | label_inherit_templates_help_message: "Nasledi šablon od majke projekta"
32 | label_inherited_templates: "Nasleđeni šabloni"
33 | no_issue_templates_for_this_project: "Nema definisanih šablona za ovaj projekat."
34 | link_to_index_edit_template: "Lista šablona problema / ažuriraj šablone."
35 | erase_issue_subject_and_description: "Obriši naslov i opis"
36 | unused_tracker_at_this_project: "NOTA: Ovo praćenje problema (tracker) se ne koristi u ovom projektu."
--------------------------------------------------------------------------------
/config/locales/zh-TW.yml:
--------------------------------------------------------------------------------
1 | # Tranditional Chinese strings go here for Rails i18n
2 | zh-TW:
3 | issue_templates: "問題樣板"
4 | issue_template: "問題樣板"
5 | issue_template_note: "註釋"
6 | label_enabled: "啟用"
7 | label_help_message: "關於樣板"
8 | label_show_help_message: "建立/修改問題時顯示提示訊息。"
9 | about_help_message: "每個專案都可以為這個樣板定義提示訊息。"
10 | close_help: "關閉提示訊息。"
11 | about_template_help_message: "你可以看到當前問題的樣板幫助訊息。"
12 | label_enabled_help_message: "該確認框用以啟用當前的樣板。若你只想把目前的樣板先當成草稿使用,請不要勾選。"
13 | label_list_templates: "樣板列表"
14 | label_new_templates: "新建樣板"
15 | issue_template_name: "樣板名稱"
16 | issue_description: "樣板内容"
17 | issue_title: "問題標題"
18 | label_applied_for_issue: "該問題所使用的欄位內容"
19 | help_for_issue_title: "如果在這邊有設定值,則使用該樣板時回自動帶入。"
20 | help_for_this_field: "本欄位說明。"
21 | permission_manage_issue_templates: "管理樣板"
22 | permission_edit_issue_templates: "編輯樣板"
23 | permission_show_issue_templates: "顯示樣板"
24 | project_module_issue_templates: "問題樣板"
25 | label_isdefault_help_message: "勾選此選項則該問題類型會使用此樣板當作預設值"
26 | defaulf_template_loaded: "載入樣板資訊至概述欄位中(追蹤標籤: %{tracker})"
27 | text_no_tracker_enabled: "本專案的追蹤標簽中沒有任何一個設定了問題樣板。\n 請在使用前先設定他們"
28 | label_enabled_sharing: "在專案樹狀結構中分享"
29 | label_inherit_templates: "繼承樣板"
30 | label_inherit_templates_help_message: " 從父專案繼承問題樣板(父專案中只有那些有勾選分享的才會出現在此處)。"
31 | label_inherited_templates: "繼承樣板"
32 | no_issue_templates_for_this_project: "此專案沒有定義任何的問題樣板"
33 | link_to_index_edit_template: "問題樣板列表/ 編輯樣板"
34 | erase_issue_subject_and_description: "清除主旨以及概述。"
35 | unused_tracker_at_this_project: "備註:這個追蹤標簽並沒有被指派給這個專案使用。必要的話請重新設定樣板"
36 | label_enabledshaing_help_message: "如果設定值為 True,這個樣板就可以分享給子專案使用(你還是得在子專案中把繼承而來的問題樣板啟用)。"
37 | label_should_replaced: "取代標題以及描述"
38 | label_should_replaced_help_message: "如果設定值為 True,當前的標題以及描述會被清除,且被樣板的文字所取代(預設是 False, 把文字附加在後面)。"
39 | global_issue_templates: "全域問題樣板"
40 | no_issue_templates_for_this_redmine: "網站目前沒有全域的問題樣板!"
41 | only_admin_can_associate_global_template: "只有 Redmine 管理員可以在這個專案中關聯全域樣板。"
42 | text_no_tracker_enabled_for_global: "尚未定義追蹤標籤。\n請先分配模板適用的追蹤標籤。"
43 | display_and_filter_issue_templates_in_dialog: "預覽樣板內容"
44 | label_filter_template: "篩選樣板"
45 | label_msg_confirm_to_replace: "確定要取代主旨和說明嗎?"
46 | label_apply_global_template_to_all_projects: "套用全域問題樣板到所有專案。"
47 | note_apply_global_template_to_all_projects_setting_enabled: "基於外掛設定,這個全域問題樣板被套用到所有專案。"
48 | project_list_associated_this_template: "專案列表: (已套用 %{all} 個中的 %{applied} 個)"
49 | note_project_local_template_override_global_template: "如果專案有自己的樣板,覆蓋全域樣板且新增問題時不顯示全域樣板選項。"
50 | help_project_local_template_override_global_template: "當啟用這個選項時,所有的全域問題樣板會被套用到所有專案。如果特定專案有針對每個追蹤標籤設定樣板,它將會覆蓋全域樣板,且新增問題時不會顯示全域樣板。"
51 | warning_project_local_template_override_global_template: "註冊包含機密內容的全域問題樣板時,請特別注意:一旦啟用了這個選項,所有的全域樣板會被套用到所有專案。"
52 | revert_before_applying_template: "還原"
53 | template_remove_confirm: "確定要刪除這個樣板嗎?目前有 %{count} 個子問題使用這個樣板。"
54 | label_number_of_subprojects_use_this_template: "%{count} 個子專案使用這個樣板。"
55 | enabled_template_cannot_destroy: "只有已停用的樣板可以被刪除。請先確認其他子問題是否有使用這個樣板,如果沒有則可以先停用它再來刪除。"
56 | orphaned_templates: "追蹤標籤中被孤立的樣板"
57 | orphaned_template: "追蹤標籤中被孤立的樣板"
58 | label_template_applied: "問題樣板已套用。你可以按「還原」連結來還原。"
59 | label_hide_confirm_dialog_in_the_future: "未來直接覆寫,不再詢問。"
60 | label_template_for_note: "筆記樣板"
61 | label_use_template_when_edit: "編輯問題時使用樣板"
62 | note_template: "筆記樣板"
63 | no_note_templates_for_this_project: "這個專案沒有任何回應樣板。"
64 | label_memo_help_message: "請建立一個筆記來說明為何套用這個樣板。"
65 | note_template_name: "筆記樣板名稱"
66 | note_description: "回應內容"
67 | field_template_visibility: 樣板可見度
68 | note_templates:
69 | visibility:
70 | mine: 只有我
71 | roles: 只有這些角色
72 | open: 任何人
73 | please_select_at_least_one_role: "請至少選擇一個角色。"
74 |
--------------------------------------------------------------------------------
/config/locales/zh.yml:
--------------------------------------------------------------------------------
1 | # Simplified Chinese strings go here for Rails i18n, based on file ja.yml
2 | zh:
3 | issue_templates: ISSUE模板管理
4 | issue_template: ISSUE模板管理
5 | issue_template_note: 批注
6 | label_enabled: 启用
7 | label_help_message: 关于ISSUE模板
8 | label_show_help_message: 当创建/修改问题时显示对应的帮助信息。
9 | about_help_message: 每个项目都可以自定义ISSUE模板帮助信息。
10 | close_help: 关闭帮助信息。
11 | about_template_help_message: 您可以查看当前项目ISSUE模板的帮助指南。
12 | label_enabled_help_message: 该复选框用于激活当前ISSUE模板。若您只想将ISSUE模板保存为草稿,请取消该复选框的勾选。
13 | label_list_templates: "ISSUE模板列表"
14 | label_new_templates: "新建ISSUE模板"
15 | issue_template_name: "ISSUE模板名称"
16 | issue_description: "ISSUE模板内容"
17 | issue_title: "ISSUE主题"
18 | label_applied_for_issue: "应用于该ISSUE的字段:"
19 | help_for_issue_title: "若在ISSUE模板中已定义了主题,则在使用该ISSUE模板时,将自动替换为对应的主题信息。"
20 | help_for_this_field: "当前字段帮助信息"
21 | permission_manage_issue_templates: "管理ISSUE模板"
22 | permission_edit_issue_templates: "编辑ISSUE模板"
23 | permission_show_issue_templates: "显示ISSUE模板"
24 | project_module_issue_templates: "ISSUE模板管理"
25 | label_isdefault_help_message: "勾选后,设置当前ISSUE模板为指定跟踪类型对应的默认模板。"
26 | defaulf_template_loaded: "在描述区域内加载模板默认信息。 (跟踪类型:%{tracker})"
27 | text_no_tracker_enabled: "当前项目中尚未配置跟踪类型。\n请在使用ISSUE模板前,先配置项目跟踪类型。"
28 | label_enabled_sharing: "启用向下继承(在当前项目的子项目中使用该ISSUE模板)"
29 | label_inherit_templates: "继承ISSUE模板"
30 | label_inherit_templates_help_message: "从父项目中继承ISSUE模板。(仅限在父项目中使用,且标记为向下继承的ISSUE模板。)"
31 | label_inherited_templates: "继承ISSUE模板"
32 | no_issue_templates_for_this_project: "当前项目中未定义ISSUE模板信息。"
33 | link_to_index_edit_template: "ISSUE模板列表 / 编辑ISSUE模板"
34 | erase_issue_subject_and_description: "点击清除主题及描述信息。"
35 | unused_tracker_at_this_project: "注意:当前项目中未定义可用的跟踪类型。请根据实际需要,重新配置ISSUE模板。"
36 | label_enabledshaing_help_message: "若选中,则当前ISSUE模板可以向下继承,与子项目共享使用。(您需在子项目中激活ISSUE模板继承选项便可使用。)"
37 | label_should_replaced: "替换主题及描述信息"
38 | label_should_replaced_help_message: "若选中,现有主题及描述信息将被自动清除,并替换为ISSUE模板中定义的内容。(默认不选中,则在现有内容后扩展模板内定义的信息。)"
39 | global_issue_templates: "全局ISSUE模板管理"
40 | no_issue_templates_for_this_redmine: "当前未定义全局ISSUE模板。请联系Redmine系统管理员。"
41 | only_admin_can_associate_global_template: "只有Redmine系统管理员才能在当前项目中关联全局ISSUE模板。"
42 | text_no_tracker_enabled_for_global: "未指定跟踪类型。\n在使用ISSUE模板前,请先指定模板适用的跟踪类型。"
43 | display_and_filter_issue_templates_in_dialog: "模板过滤器"
44 | label_filter_template: "模板过滤器"
45 | label_msg_confirm_to_replace: "确认替换主题及描述信息?"
46 | label_apply_global_template_to_all_projects: "将全局问题模板应用于全部项目。"
47 | note_apply_global_template_to_all_projects_setting_enabled: "该全局问题模板通过插件设置配置后,应用于所有项目。"
48 | project_list_associated_this_template: "项目列表: (已应用于全部 %{all} 个项目中的 %{applied} 个)"
49 | note_project_local_template_override_global_template: "如果存在项目本地模板,则覆盖全局模板,且在该项目内创建问题时,不显示任何全局模板。"
50 | help_project_local_template_override_global_template: "如果启用此选项,则问题的全局模板将应用于所有项目。 如果设置了每个跟踪器的项目特定模板,则会覆盖对应的全局模板,并在创建新问题时,隐藏全局模板。"
51 | warning_project_local_template_override_global_template: "注册全局问题模板时,务必注意模板信息中的特定项目的机密信息。 一旦启用此选项,则所有全局模板将应用于所有项目。"
52 | revert_before_applying_template: "回退"
53 | template_remove_confirm: "您确定要删除此模板吗? 当前有 %{count} 个项目正在使用此模板。"
54 | label_number_of_subprojects_use_this_template: "%{count} 个项目正在使用此模板 "
55 | enabled_template_cannot_destroy: "已启用的模板不能删除。检查您正在使用的模板是否在其他项目中存在,并在删除之前将其禁用。"
56 | orphaned_templates: "基于跟踪的个性化模板"
57 | orphaned_template: "基于跟踪的个性化模板"
58 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | #
2 | # TODO: Clean up routing.
3 | #
4 | Rails.application.routes.draw do
5 | concern :tamplate_common do
6 | get 'orphaned_templates', on: :collection
7 | end
8 |
9 | concern :previewable do
10 | post 'preview', on: :collection
11 | end
12 |
13 | resources :global_issue_templates, except: [:edit], concerns: %i[tamplate_common previewable]
14 |
15 | # for project issue template
16 | resources :projects, only: [] do
17 | resources :issue_templates, except: [:edit], concerns: [:tamplate_common] do
18 | post 'set_pulldown', on: :collection
19 | get 'list_templates', on: :collection
20 | end
21 |
22 | resources :issue_templates_settings, only: [:edit], concerns: [:previewable] do
23 | patch 'edit', on: :collection
24 | end
25 |
26 | get 'issue_templates_settings', to: 'issue_templates_settings#index'
27 |
28 | resources :note_templates, except: [:edit]
29 | end
30 |
31 | resources :issue_templates, only: %i[load preview load_selectable_fields], concerns: [:previewable] do
32 | post 'load', on: :collection
33 | get 'load_selectable_fields', on: :collection
34 | end
35 |
36 | # for note temlate
37 | resources :note_templates, only: %i[load preview list_templates] do
38 | post 'load', on: :collection
39 | get 'list_templates', on: :collection
40 | end
41 |
42 | # for global note temlate
43 | resources :global_note_templates, except: [:edit], concerns: %i[previewable]
44 | end
45 |
--------------------------------------------------------------------------------
/db/migrate/0001_create_issue_templates.rb:
--------------------------------------------------------------------------------
1 | class CreateIssueTemplates < ActiveRecord::Migration[4.2]
2 | def self.up
3 | create_table :issue_templates do |t|
4 | t.column :title, :string, null: false
5 | t.column :project_id, :integer
6 | t.column :tracker_id, :integer, null: false
7 | t.column :author_id, :integer, null: false
8 | t.column :note, :string
9 | t.column :description, :text
10 | t.column :enabled, :boolean
11 | t.column :created_on, :timestamp
12 | t.column :updated_on, :timestamp
13 | end
14 | add_index :issue_templates, :author_id
15 | add_index :issue_templates, :project_id
16 | add_index :issue_templates, :tracker_id
17 | end
18 |
19 | def self.down
20 | remove_index :issue_templates, :author_id
21 | remove_index :issue_templates, :project_id
22 | remove_index :issue_templates, :tracker_id
23 | drop_table :issue_templates
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/db/migrate/0002_create_issue_template_settings.rb:
--------------------------------------------------------------------------------
1 | class CreateIssueTemplateSettings < ActiveRecord::Migration[4.2]
2 | def self.up
3 | create_table :issue_template_settings do |t|
4 | t.column :project_id, :integer
5 |
6 | t.column :help_message, :text
7 |
8 | t.column :enabled, :boolean
9 | end
10 | end
11 |
12 | def self.down
13 | drop_table :issue_template_settings
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/db/migrate/0003_add_issue_title_to_issue_templates.rb:
--------------------------------------------------------------------------------
1 | class AddIssueTitleToIssueTemplates < ActiveRecord::Migration[4.2]
2 | def self.up
3 | add_column :issue_templates, :issue_title, :string
4 |
5 | IssueTemplate.reset_column_information
6 | issue_templates = IssueTemplate.all
7 | issue_templates.each do |t|
8 | t.issue_title = t.title
9 | t.save
10 | end
11 | end
12 |
13 | def self.down
14 | remove_column :issue_templates, :issue_title
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/db/migrate/0004_add_position_to_issue_templates.rb:
--------------------------------------------------------------------------------
1 | class AddPositionToIssueTemplates < ActiveRecord::Migration[4.2]
2 | def self.up
3 | add_column :issue_templates, :position, :integer, default: 1
4 |
5 | IssueTemplate.reset_column_information
6 |
7 | issue_templates = IssueTemplate.all
8 | say_with_time('Update each template to set default position.') do
9 | issue_templates.each_with_index { |t, i| t.update_attribute(:position, i + 1) }
10 | end
11 | end
12 |
13 | def self.down
14 | remove_column :issue_templates, :position
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/db/migrate/20121208150810_add_is_default_to_issue_templates.rb:
--------------------------------------------------------------------------------
1 | class AddIsDefaultToIssueTemplates < ActiveRecord::Migration[4.2]
2 | def self.up
3 | add_column :issue_templates, :is_default, :boolean, default: false
4 | end
5 |
6 | def self.down
7 | remove_column :issue_templates, :is_default
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20130630141710_add_enabled_sharing_to_issue_templates.rb:
--------------------------------------------------------------------------------
1 | class AddEnabledSharingToIssueTemplates < ActiveRecord::Migration[4.2]
2 | def self.up
3 | add_column :issue_templates, :enabled_sharing, :boolean, default: false
4 | end
5 |
6 | def self.down
7 | remove_column :issue_templates, :enabled_sharing
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20130701024625_add_inherit_templates_to_issue_template_settings.rb:
--------------------------------------------------------------------------------
1 | class AddInheritTemplatesToIssueTemplateSettings < ActiveRecord::Migration[4.2]
2 | def self.up
3 | add_column :issue_template_settings, :inherit_templates, :boolean, default: false, null: false
4 | end
5 |
6 | def self.down
7 | remove_column :issue_template_settings, :inherit_templates
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/2014020191500_add_should_replaced_to_issue_template_settings.rb:
--------------------------------------------------------------------------------
1 | class AddShouldReplacedToIssueTemplateSettings < ActiveRecord::Migration[4.2]
2 | def self.up
3 | add_column :issue_template_settings, :should_replaced, :boolean, default: false
4 | end
5 |
6 | def self.down
7 | remove_column :issue_template_settings, :should_replaced
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20140307024626_create_global_issue_templates.rb:
--------------------------------------------------------------------------------
1 | class CreateGlobalIssueTemplates < ActiveRecord::Migration[4.2]
2 | def change
3 | create_table :global_issue_templates do |t|
4 | t.string :title
5 | t.string :issue_title
6 | t.integer :tracker_id
7 | t.integer :author_id
8 | t.string :note
9 | t.text :description
10 | t.boolean :enabled
11 | t.integer :position
12 | t.boolean :is_default
13 | t.timestamp :created_on
14 | t.timestamp :updated_on
15 | end
16 | add_index :global_issue_templates, :author_id
17 | add_index :global_issue_templates, :tracker_id
18 | end
19 |
20 | def self.down
21 | remove_index :global_issue_templates, :author_id
22 | remove_index :global_issue_templates, :tracker_id
23 | drop_table :global_issue_templates
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/db/migrate/20140312054531_create_global_issue_templates_projects.rb:
--------------------------------------------------------------------------------
1 | class CreateGlobalIssueTemplatesProjects < ActiveRecord::Migration[4.2]
2 | def self.up
3 | create_table :global_issue_templates_projects, id: false do |t|
4 | t.integer :project_id
5 | t.integer :global_issue_template_id
6 | end
7 | end
8 |
9 | def self.down
10 | drop_table :global_issue_templates_projects
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/db/migrate/20140330155030_remove_is_default_from_global_issue_templates.rb:
--------------------------------------------------------------------------------
1 | class RemoveIsDefaultFromGlobalIssueTemplates < ActiveRecord::Migration[4.2]
2 | def self.up
3 | remove_column :global_issue_templates, :is_default
4 | end
5 |
6 | def self.down
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/db/migrate/20160727222420_add_checklist_json_to_issue_templates.rb:
--------------------------------------------------------------------------------
1 | class AddChecklistJsonToIssueTemplates < ActiveRecord::Migration[4.2]
2 | def self.up
3 | add_column :issue_templates, :checklist_json, :text
4 | end
5 |
6 | def self.down
7 | remove_column :issue_templates, :checklist_json
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20160828190000_add_checklist_json_to_global_issue_templates.rb:
--------------------------------------------------------------------------------
1 | class AddChecklistJsonToGlobalIssueTemplates < ActiveRecord::Migration[4.2]
2 | def self.up
3 | add_column :global_issue_templates, :checklist_json, :text
4 | end
5 |
6 | def self.down
7 | remove_column :global_issue_templates, :checklist_json
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20160829001500_change_issue_template_enabled_column.rb:
--------------------------------------------------------------------------------
1 | class ChangeIssueTemplateEnabledColumn < ActiveRecord::Migration[4.2]
2 | def self.up
3 | change_column :issue_templates, :enabled, :boolean, default: false, null: false
4 | end
5 |
6 | def self.down
7 | change_column :issue_templates, :enabled, :boolean
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20160829001530_change_global_issue_template_enabled_column.rb:
--------------------------------------------------------------------------------
1 | class ChangeGlobalIssueTemplateEnabledColumn < ActiveRecord::Migration[4.2]
2 | def self.up
3 | change_column :global_issue_templates, :enabled, :boolean, default: false, null: false
4 | end
5 |
6 | def self.down
7 | change_column :global_issue_templates, :enabled, :boolean
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20170317082100_add_is_default_to_global_issue_templates.rb:
--------------------------------------------------------------------------------
1 | class AddIsDefaultToGlobalIssueTemplates < ActiveRecord::Migration[4.2]
2 | def self.up
3 | add_column :global_issue_templates, :is_default, :boolean, default: false, null: false
4 | end
5 |
6 | def self.down
7 | remove_column :global_issue_templates, :is_default
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20181104065200_add_unique_key_to_global_issue_templates_projects.rb:
--------------------------------------------------------------------------------
1 | class AddUniqueKeyToGlobalIssueTemplatesProjects < ActiveRecord::Migration[4.2]
2 | def self.up
3 | add_index :global_issue_templates_projects,
4 | [:project_id, :global_issue_template_id], unique: true,
5 | name: 'projects_global_issue_templates'
6 | end
7 |
8 | def self.down
9 | remove_index :global_issue_templates_projects, name: 'projects_global_issue_templates'
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/db/migrate/20190303082102_create_note_templates.rb:
--------------------------------------------------------------------------------
1 | class CreateNoteTemplates < ActiveRecord::Migration[5.1]
2 | def up
3 | create_table :note_templates do |t|
4 | t.string :name
5 | t.string :description
6 | t.string :memo
7 | t.integer :project_id
8 | t.integer :tracker_id
9 | t.integer :author_id
10 | t.boolean :enabled
11 | t.integer :position
12 | t.timestamps
13 | end
14 | add_index :note_templates, :author_id
15 | add_index :note_templates, :project_id
16 | add_index :note_templates, :tracker_id
17 | add_index :note_templates, :enabled
18 | end
19 |
20 | def down
21 | remove_index :note_templates, :author_id
22 | remove_index :note_templates, :project_id
23 | remove_index :note_templates, :tracker_id
24 | remove_index :note_templates, :enabled
25 | drop_table :note_templates
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/db/migrate/20190714171020_create_note_visible_roles.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateNoteVisibleRoles < ActiveRecord::Migration[5.1]
4 | def up
5 | create_table :note_visible_roles do |t|
6 | t.integer :note_template_id
7 | t.integer :role_id
8 | t.timestamps
9 | end
10 | add_index :note_visible_roles, :note_template_id
11 | add_index :note_visible_roles, :role_id
12 | end
13 |
14 | def down
15 | remove_index :note_visible_roles, :role_id
16 | remove_index :note_visible_roles, :note_template_id
17 | drop_table :note_visible_roles
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/db/migrate/20190714211530_add_visibility_to_note_templates.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddVisibilityToNoteTemplates < ActiveRecord::Migration[5.1]
4 | def self.up
5 | add_column :note_templates, :visibility, :integer, default: 2
6 | end
7 |
8 | def self.down
9 | remove_column :note_templates, :visibility
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/db/migrate/20200101204020_add_related_link_to_issue_templates.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddRelatedLinkToIssueTemplates < ActiveRecord::Migration[5.2]
4 | def self.up
5 | add_column :issue_templates, :related_link, :text
6 | end
7 |
8 | def self.down
9 | remove_column :issue_templates, :related_link
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/db/migrate/20200101204220_add_related_link_to_global_issue_templates.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddRelatedLinkToGlobalIssueTemplates < ActiveRecord::Migration[5.2]
4 | def self.up
5 | add_column :global_issue_templates, :related_link, :text
6 | end
7 |
8 | def self.down
9 | remove_column :global_issue_templates, :related_link
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/db/migrate/20200102204815_add_link_title_to_issue_templates.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddLinkTitleToIssueTemplates < ActiveRecord::Migration[5.2]
4 | def self.up
5 | add_column :issue_templates, :link_title, :text
6 | end
7 |
8 | def self.down
9 | remove_column :issue_templates, :link_title
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/db/migrate/20200102205044_add_link_title_to_global_issue_templates.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddLinkTitleToGlobalIssueTemplates < ActiveRecord::Migration[5.2]
4 | def self.up
5 | add_column :global_issue_templates, :link_title, :text
6 | end
7 |
8 | def self.down
9 | remove_column :global_issue_templates, :link_title
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/db/migrate/20200103213630_add_builtin_fields_json_to_issue_templates.rb:
--------------------------------------------------------------------------------
1 | class AddBuiltinFieldsJsonToIssueTemplates < ActiveRecord::Migration[5.2]
2 | def self.up
3 | add_column :issue_templates, :builtin_fields_json, :text
4 | end
5 |
6 | def self.down
7 | remove_column :issue_templates, :builtin_fields_json
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20200115073600_add_builtin_fields_json_to_global_issue_templates.rb:
--------------------------------------------------------------------------------
1 | class AddBuiltinFieldsJsonToGlobalIssueTemplates < ActiveRecord::Migration[5.2]
2 | def self.up
3 | add_column :global_issue_templates, :builtin_fields_json, :text
4 | end
5 |
6 | def self.down
7 | remove_column :global_issue_templates, :builtin_fields_json
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20200314132500_change_column_note_template_description.rb:
--------------------------------------------------------------------------------
1 | class ChangeColumnNoteTemplateDescription < ActiveRecord::Migration[5.2]
2 | def self.up
3 | change_column :note_templates, :description, :text
4 | end
5 |
6 | def down
7 | change_column :note_templates, :description, :string
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20200405115700_create_global_note_templates.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateGlobalNoteTemplates < ActiveRecord::Migration[5.1]
4 | def up
5 | create_table :global_note_templates do |t|
6 | t.string :name
7 | t.text :description
8 | t.string :memo
9 | t.integer :tracker_id
10 | t.integer :author_id
11 | t.boolean :enabled
12 | t.integer :position
13 | t.integer :visibility, default: 2
14 | t.timestamps
15 | end
16 | add_index :global_note_templates, :author_id
17 | add_index :global_note_templates, :tracker_id
18 | add_index :global_note_templates, :enabled
19 | end
20 |
21 | def down
22 | remove_index :global_note_templates, :author_id
23 | remove_index :global_note_templates, :tracker_id
24 | remove_index :global_note_templates, :enabled
25 | drop_table :global_note_templates
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/db/migrate/20200405120700_create_global_note_visible_roles.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateGlobalNoteVisibleRoles < ActiveRecord::Migration[5.1]
4 | def up
5 | create_table :global_note_visible_roles do |t|
6 | t.integer :global_note_template_id
7 | t.integer :role_id
8 | t.timestamps
9 | end
10 | add_index :global_note_visible_roles, :global_note_template_id
11 | add_index :global_note_visible_roles, :role_id
12 | end
13 |
14 | def down
15 | remove_index :global_note_visible_roles, :role_id
16 | remove_index :global_note_visible_roles, :global_note_template_id
17 | drop_table :global_note_visible_roles
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/db/migrate/20200418114157_create_join_table_global_note_template_project.rb:
--------------------------------------------------------------------------------
1 | class CreateJoinTableGlobalNoteTemplateProject < ActiveRecord::Migration[5.2]
2 | def change
3 | create_join_table :global_note_templates, :projects, table_name: :global_note_template_projects do |t|
4 | # t.index [:global_note_template_id, :project_id]
5 | # t.index [:project_id, :global_note_template_id]
6 | end
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/db/migrate/20230330055341_change_global_note_template_projects_table_name.rb:
--------------------------------------------------------------------------------
1 | class ChangeGlobalNoteTemplateProjectsTableName < ActiveRecord::Migration[5.2]
2 | def up
3 | rename_table :global_note_template_projects, :global_note_templates_projects
4 | end
5 |
6 | def down
7 | rename_table :global_note_templates_projects, :global_note_template_projects
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.2'
2 | services:
3 | # start service for redmine with plugin
4 | # 1. $ docker-compose build --force-rm --no-cache
5 | # 2. $ docker-compose up -d web
6 | #
7 | #
8 | web:
9 | build:
10 | context: .
11 | image: redmine_sqlite3
12 | container_name: redmine_sqlite3
13 | command: >
14 | bash -c "bundle &&
15 | bundle exec rake db:migrate &&
16 | bundle exec rake redmine:plugins:migrate &&
17 | bundle exec rake generate_secret_token &&
18 | bundle exec rails s -p 3000 -b '0.0.0.0'"
19 | environment:
20 | RAILS_ENV: development
21 | volumes:
22 | - .:/tmp/redmine/plugins/redmine_issue_templates
23 | - ./.data:/tmp/data
24 | ports:
25 | - "3000:3000"
26 | mysql:
27 | image: mysql
28 | environment:
29 | MYSQL_ROOT_PASSWORD: pasword
30 | ports:
31 | - "3306:3306"
32 |
--------------------------------------------------------------------------------
/init.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Redmine Issue Template Plugin
4 | #
5 | # This is a plugin for Redmine to generate and use issue templates
6 | # for each project to assist issue creation.
7 | # Created by Akiko Takano.
8 | #
9 | # This program is free software; you can redistribute it and/or
10 | # modify it under the terms of the GNU General Public License
11 | # as published by the Free Software Foundation; either version 2
12 | # of the License, or (at your option) any later version.
13 | #
14 | # This program is distributed in the hope that it will be useful,
15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | # GNU General Public License for more details.
18 | #
19 | # You should have received a copy of the GNU General Public License
20 | # along with this program; if not, write to the Free Software
21 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 | require File.expand_path('lib/redmine')
23 |
24 | # NOTE: Keep error message for a while to support Redmine3.x users.
25 | def issue_template_version_message(original_message = nil)
26 | <<-"USAGE"
27 |
28 | ==========================
29 | #{original_message}
30 |
31 | If you use Redmine3.x, please use Redmine Issue Templates version 0.2.x or clone via
32 | 'v0.2.x-support-Redmine3' branch.
33 | You can download older version from here: https://github.com/akiko-pusu/redmine_issue_templates/releases
34 | ==========================
35 | USAGE
36 | end
37 |
38 | def template_menu_allowed?
39 | proc { |p| User.current.allowed_to?({ controller: 'issue_templates', action: 'show' }, p) }
40 | end
41 |
42 | Redmine::Plugin.register :redmine_issue_templates do
43 | begin
44 | name 'Redmine Issue Templates plugin'
45 | author 'Agileware Inc.'
46 | description 'Plugin to generate and use issue templates for each project to assist issue creation.'
47 | version '1.2.1'
48 | author_url 'https://agileware.jp/'
49 | requires_redmine version_or_higher: '4.0'
50 | url 'https://github.com/agileware-jp/redmine_issue_templates'
51 |
52 | settings partial: 'settings/redmine_issue_templates',
53 | default: {
54 | apply_global_template_to_all_projects: 'false',
55 | apply_template_when_edit_issue: 'false',
56 | enable_builtin_fields: 'false'
57 | }
58 |
59 | menu :admin_menu, :redmine_issue_templates, { controller: 'global_issue_templates', action: 'index' },
60 | caption: :global_issue_templates, html: { class: 'icon icon-global_issue_templates' }
61 |
62 | menu :project_menu, :issue_templates, { controller: 'issue_templates', action: 'index' },
63 | caption: :issue_templates, param: :project_id,
64 | after: :settings, if: template_menu_allowed?
65 |
66 | project_module :issue_templates do
67 | permission :edit_issue_templates, issue_templates: %i[new create edit update destroy move], note_templates: %i[new create edit update destroy move]
68 | permission :show_issue_templates, issue_templates: %i[index show load set_pulldown list_templates orphaned_templates],
69 | note_templates: %i[index show load list_templates]
70 | permission :manage_issue_templates, { issue_templates_settings: %i[index edit] }, require: :member
71 | end
72 | rescue ::Redmine::PluginRequirementError => e
73 | raise ::Redmine::PluginRequirementError.new(issue_template_version_message(e.message)) # rubocop:disable Style/RaiseArgs
74 | end
75 | end
76 |
77 | Rails.application.config.after_initialize do
78 | require File.expand_path('../lib/issue_templates/issues_hook', __FILE__)
79 | require File.expand_path('../lib/issue_templates/journals_hook', __FILE__)
80 | end
81 |
--------------------------------------------------------------------------------
/lib/issue_templates/issues_hook.rb:
--------------------------------------------------------------------------------
1 | # To change this template, choose Tools | Templates
2 | # and open the template in the editor.
3 | module IssueTemplates
4 | class IssuesHook < Redmine::Hook::ViewListener
5 | include IssuesHelper
6 |
7 | CONTROLLERS = %(
8 | 'IssuesController' 'IssueTemplatesController' 'ProjectsController' 'IssueTemplatesSettingsController'
9 | 'GlobalIssueTemplatesController' 'SettingsController' 'NoteTemplatesController'
10 | 'GlobalNoteTemplatesController'
11 | ).freeze
12 |
13 | ACTIONS = %('new' 'update_form' 'create', 'show').freeze
14 |
15 | def view_layouts_base_html_head(context = {})
16 | o = stylesheet_link_tag('issue_templates', plugin: 'redmine_issue_templates')
17 | o << redmine_issue_template_javascript_include_tag if need_template_js?(context[:controller])
18 | o
19 | end
20 |
21 | def view_issues_form_details_top(context = {})
22 | issue = context[:issue]
23 | parameters = context[:request].parameters
24 | return if existing_issue?(issue)
25 | return if copied_issue?(parameters)
26 |
27 | project = context[:project]
28 | project_id = issue.project_id.present? ? issue.project_id : project.id
29 | return unless create_action?(parameters[:action]) && project_id.present?
30 |
31 | context[:controller].send(
32 | :render_to_string,
33 | partial: 'issue_templates/issue_select_form',
34 | locals: locals_params(issue, project_id, parameters[:form_update_triggered_by])
35 | )
36 | end
37 |
38 | render_on :view_issues_sidebar_planning_bottom, partial: 'issue_templates/issue_template_link'
39 |
40 | private
41 |
42 | def existing_issue?(issue)
43 | return false if apply_template_when_edit_issue?
44 |
45 | issue.id.present? || issue.tracker_id.blank?
46 | end
47 |
48 | def copied_issue?(parameters)
49 | return false if apply_template_when_edit_issue?
50 |
51 | copy_from = parameters[:copy_from]
52 | copy_from.present?
53 | end
54 |
55 | def create_action?(action)
56 | return true if apply_template_when_edit_issue?
57 |
58 | ACTIONS.include?(action)
59 | end
60 |
61 | def setting(project_id)
62 | IssueTemplateSetting.find_or_create(project_id)
63 | end
64 |
65 | def need_template_js?(controller)
66 | CONTROLLERS.include?(controller.class.name)
67 | end
68 |
69 | def plugin_setting
70 | Setting.plugin_redmine_issue_templates
71 | end
72 |
73 | def apply_all_projects?
74 | plugin_setting['apply_global_template_to_all_projects'].to_s == 'true'
75 | end
76 |
77 | def apply_template_when_edit_issue?
78 | plugin_setting['apply_template_when_edit_issue'].to_s == 'true'
79 | end
80 |
81 | def locals_params(issue, project_id, is_triggered_by)
82 | { setting: setting(project_id),
83 | issue: issue,
84 | is_triggered_by: is_triggered_by,
85 | project_id: project_id,
86 | pulldown_url: pulldown_url(issue, project_id, is_triggered_by) }
87 | end
88 |
89 | def pulldown_url(issue, project_id, is_triggered_by)
90 | pulldown_url = if issue.try(:id).present?
91 | url_for(controller: 'issue_templates',
92 | action: 'set_pulldown', project_id: project_id, is_triggered_by: is_triggered_by,
93 | is_update_issue: issue.try(:id).present?)
94 | else
95 | url_for(controller: 'issue_templates',
96 | action: 'set_pulldown', project_id: project_id, is_triggered_by: is_triggered_by)
97 | end
98 | pulldown_url
99 | end
100 |
101 | def redmine_issue_template_javascript_include_tag
102 | if ENV['REDMINE_ISSUE_TEMPLATE_VITE_SERVE_URL'].present?
103 | source = "#{ENV['REDMINE_ISSUE_TEMPLATE_VITE_SERVE_URL']}/scripts/issue_templates.js"
104 | javascript_include_tag(source, type: :module)
105 | else
106 | javascript_include_tag(:issue_templates, plugin: :redmine_issue_templates, type: :module)
107 | end
108 | end
109 | end
110 | end
111 |
--------------------------------------------------------------------------------
/lib/issue_templates/journals_hook.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # To change this template, choose Tools | Templates
4 | # and open the template in the editor.
5 | module IssueTemplates
6 | class JournalsHook < Redmine::Hook::ViewListener
7 | def view_journals_notes_form_after_notes(context = {})
8 | journal = context[:journal]
9 | issue = journal.issue
10 | tracker_id = issue.try(:tracker_id)
11 | templates = target_templates(context, tracker_id)
12 | global_templates = global_note_templates(context, tracker_id)
13 | return if templates.empty? && global_templates.empty?
14 |
15 | context[:controller].send(
16 | :render_to_string,
17 | partial: 'issue_templates/note_form', locals: { type: 'template_edit_journal', templates: templates, issue: issue }
18 | )
19 | end
20 |
21 | # Add journal with edit issue
22 | def view_issues_edit_notes_bottom(context = {})
23 | issue = context[:issue]
24 | tracker_id = issue.try(:tracker_id)
25 | templates = target_templates(context, tracker_id)
26 | global_templates = global_note_templates(context, tracker_id)
27 | return if templates.empty? && global_templates.empty?
28 |
29 | context[:controller].send(
30 | :render_to_string,
31 | partial: 'issue_templates/note_form', locals: { type: 'template_issue_notes', templates: templates, issue: issue }
32 | )
33 | end
34 |
35 | def target_templates(context, tracker_id)
36 | (tracker_id, project_id) = tracker_project_ids(context, tracker_id)
37 | NoteTemplate.visible_note_templates_condition(
38 | user_id: User.current.id, project_id: project_id, tracker_id: tracker_id
39 | ).sorted
40 | end
41 |
42 | def global_note_templates(context, tracker_id)
43 | (tracker_id, project_id) = tracker_project_ids(context, tracker_id)
44 | GlobalNoteTemplate.visible_note_templates_condition(
45 | user_id: User.current.id, project_id: project_id, tracker_id: tracker_id
46 | ).sorted
47 | end
48 |
49 | def tracker_project_ids(context, tracker_id)
50 | project = context[:project]
51 | project_id = project.present? ? project.id : issue.try(:project_id)
52 | [tracker_id, project_id]
53 | end
54 | end
55 | end
56 |
--------------------------------------------------------------------------------
/lib/tasks/test.rake:
--------------------------------------------------------------------------------
1 | require 'rake/testtask'
2 |
3 | namespace :redmine_issue_templates do
4 | desc 'Run test for redmine_issue_template plugin.'
5 | task :test do |task_name|
6 | next unless ENV['RAILS_ENV'] == 'test' && task_name.name == 'redmine_issue_templates:test'
7 | end
8 |
9 | Rake::TestTask.new(:test) do |t|
10 | t.libs << 'lib'
11 | t.pattern = 'plugins/redmine_issue_templates/test/**/*_test.rb'
12 | t.verbose = false
13 | t.warning = false
14 | end
15 |
16 | desc 'Run spec for redmine_issue_template plugin'
17 | task :spec do |task_name|
18 | next unless ENV['RAILS_ENV'] == 'test' && task_name.name == 'redmine_issue_templates:spec'
19 |
20 | begin
21 | require 'rspec/core'
22 | path = 'plugins/redmine_issue_templates/spec/'
23 | options = ['-I plugins/redmine_issue_templates/spec']
24 | options << '--format'
25 | options << 'documentation'
26 | options << path
27 | RSpec::Core::Runner.run(options)
28 | rescue LoadError => ex
29 | puts "This task should be called only for redmine issue template spec. #{ex.message}"
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/lib/tasks/util.rake:
--------------------------------------------------------------------------------
1 | namespace :redmine_issue_templates do
2 | desc 'Apply inhelit template setting to child projects.'
3 | task :apply_inhelit_template_to_child_projects, 'project_id'
4 | task apply_inhelit_template_to_child_projects: :environment do |_t, args|
5 | project_id = args.project_id
6 | begin
7 | IssueTemplateSetting.apply_template_to_child_projects(project_id)
8 | rescue ActiveRecord::RecordNotFound
9 | puts "IssueTemplateSetting to project specified by #{project_id} does not exist."
10 | end
11 | end
12 |
13 | desc 'Unapply inhelit template setting from child projects.'
14 | task :unapply_inhelit_template_from_child_projects, 'project_id'
15 | task unapply_inhelit_template_from_child_projects: :environment do |_t, args|
16 | project_id = args.project_id
17 | begin
18 | IssueTemplateSetting.unapply_template_from_child_projects(project_id)
19 | rescue ActiveRecord::RecordNotFound
20 | puts "IssueTemplateSetting to project specified by #{project_id} does not exist."
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "redmine_issue_templates",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "dev": "vite",
6 | "build": "vite build",
7 | "preview": "vite preview --port 4173",
8 | "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
9 | },
10 | "dependencies": {
11 | "axios": "^1.3.6",
12 | "vue": "^2.7.7"
13 | },
14 | "devDependencies": {
15 | "@rushstack/eslint-patch": "^1.1.0",
16 | "@vitejs/plugin-legacy": "^2.0.0",
17 | "@vitejs/plugin-vue2": "^1.1.2",
18 | "@vue/eslint-config-prettier": "^7.0.0",
19 | "eslint": "^8.5.0",
20 | "eslint-plugin-vue": "^9.11.0",
21 | "prettier": "^2.5.1",
22 | "terser": "^5.14.2",
23 | "vite": "^3.0.2"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/scripts/components/DisplayArea.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 | {{ item.field.name }}: {{ item.value }} / {{ item.title }}
7 |
8 |
9 |
10 | {{ l('unavailable_fields_for_this_tracker') }}
11 | : {{ item.value }} / {{ item.title }}
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
33 |
--------------------------------------------------------------------------------
/scripts/components/FieldValue.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
18 |
27 |
33 |
42 |
50 |
51 |
52 |
53 |
79 |
--------------------------------------------------------------------------------
/scripts/plugins/customFields.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | export const CustomFieldPlugin = {
4 | install(Vue, options = {}) {
5 | const {
6 | baseUrl,
7 | templateId,
8 | projectId,
9 | } = options;
10 | Vue.prototype.getCustomFields = async (trackerId) => {
11 | const params = {
12 | tracker_id: trackerId,
13 | template_id: templateId,
14 | project_id: projectId,
15 | };
16 | const { data } = await axios.get(baseUrl, { params });
17 | const { custom_fields } = data;
18 | return custom_fields;
19 | };
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/scripts/plugins/locales.js:
--------------------------------------------------------------------------------
1 | export const LocalePlugin = {
2 | install(Vue, locale) {
3 | Vue.prototype.l = (key) => {
4 | return locale[key];
5 | };
6 | }
7 | };
8 |
--------------------------------------------------------------------------------
/scripts/template_fields.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import JsonGenerator from './components/JsonGenerator.vue';
3 | import { CustomFieldPlugin } from './plugins/customFields';
4 | import { LocalePlugin } from './plugins/locales';
5 |
6 | const TEMPLATE_FIELDS = function (props) {
7 | const {
8 | loadSelectableFieldsPath,
9 | templateId,
10 | projectId,
11 | locales,
12 | } = props;
13 | Vue.use(LocalePlugin, locales);
14 | Vue.use(CustomFieldPlugin, {
15 | baseUrl: loadSelectableFieldsPath,
16 | templateId,
17 | projectId,
18 | });
19 |
20 | new Vue({
21 | render: (h) => h(JsonGenerator, { props })
22 | }).$mount('#json_generator');
23 | };
24 |
25 | window.TEMPLATE_FIELDS = TEMPLATE_FIELDS;
26 |
--------------------------------------------------------------------------------
/spec/controllers/concerns/issue_templates_common_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../../spec_helper'
4 |
5 | describe 'IssueTemplatesCommon' do
6 | before do
7 | class FakesController < ApplicationController
8 | include IssueTemplatesCommon
9 | end
10 | allow_any_instance_of(FakesController).to receive(:action_name).and_return('fake_action')
11 | User.current = FactoryBot.build(:user)
12 | end
13 | let(:mock_controller) { FakesController.new }
14 |
15 | describe '#log_action' do
16 | subject { mock_controller.log_action }
17 |
18 | it do
19 | expect(Rails.logger).to receive(:info).with("[FakesController] fake_action called by #{User.current.name}").once
20 | subject
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/controllers/settings_controller_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../spec_helper'
4 | require File.expand_path(File.dirname(__FILE__) + '/../support/controller_helper')
5 |
6 | describe SettingsController, type: :controller do
7 | include_context 'As admin'
8 | before do
9 | Redmine::SudoMode.disable!
10 | Redmine::Plugin.register(:redmine_issue_templates) do
11 | settings partial: 'settings/redmine_issue_templates',
12 | default: { 'apply_global_template_to_all_projects' => 'false' }
13 | end
14 | @request.session[:user_id] = user.id
15 | end
16 |
17 | after(:all) do
18 | Redmine::Plugin.unregister(:redmine_issue_templates)
19 | end
20 |
21 | describe '#GET plugin' do
22 | render_views
23 | before do
24 | Setting.send 'plugin_redmine_issue_templates=', 'apply_global_template_to_all_projects' => 'false'
25 | get :plugin, params: { id: 'redmine_issue_templates' }
26 | end
27 | include_examples 'Right response', 200
28 | it 'Contains right plugin setting content' do
29 | expect(response.body).to match(/id="settings_apply_global_template_to_all_projects"/im)
30 | end
31 | end
32 |
33 | describe '#POST plugin' do
34 | render_views
35 | before do
36 | post :plugin, params: { id: 'redmine_issue_templates',
37 | settings: { apply_global_template_to_all_projects: true } }
38 | end
39 | include_examples 'Right response', 302
40 | it 'Setting value is changed true' do
41 | settings = Setting.plugin_redmine_issue_templates
42 | expect(settings['apply_global_template_to_all_projects']).to be_truthy
43 | end
44 | end
45 | end
46 |
--------------------------------------------------------------------------------
/spec/factories/enabled_modules.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :enabled_module do
3 | project_id { 1 }
4 | name { 'issue_templates' }
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/spec/factories/global_issue_templates.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :global_issue_template do |t|
3 | association :tracker
4 | t.sequence(:title) { |n| "global_template-title: #{n}" }
5 | t.sequence(:description) { |n| "global_template-description: #{n}" }
6 | t.sequence(:note) { |n| "global_template-note: #{n}" }
7 | t.sequence(:position) { |n| n }
8 | t.enabled { true }
9 | t.is_default { false }
10 | t.author_id { 1 }
11 |
12 | factory :global_issue_template_with_projects do
13 | transient do
14 | projects_count { 5 }
15 | end
16 |
17 | after(:create) do |global_issue_template, evaluator|
18 | global_issue_template.projects = create_list(:project, evaluator.projects_count)
19 | end
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/spec/factories/global_note_templates.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :global_note_template do |t|
3 | association :tracker
4 | t.sequence(:name) { |n| "global_template-title: #{n}" }
5 | t.sequence(:description) { |n| "global_template-description: #{n}" }
6 | t.sequence(:memo) { |n| "global_template-note: #{n}" }
7 | t.sequence(:position) { |n| n }
8 | t.enabled { true }
9 | t.author_id { 1 }
10 | t.visibility { 2 } # open
11 |
12 | factory :global_note_template_with_projects do
13 | transient do
14 | projects_count { 5 }
15 | end
16 |
17 | after(:create) do |global_note_template, evaluator|
18 | global_note_template.projects = create_list(:project, evaluator.projects_count)
19 | end
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/spec/factories/issue_statuses.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :issue_status do
3 | sequence(:name) { |n| "status-name: #{n}" }
4 | sequence(:position) { |n| n }
5 | is_closed { false }
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/spec/factories/issue_template_settings.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :issue_template_setting do |t|
3 | association :project
4 | t.sequence(:help_message) { |n| "Project-#{n}: temlpate help" }
5 | t.enabled { true }
6 | t.inherit_templates { false }
7 | t.should_replaced { false }
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/spec/factories/issue_templates.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :issue_template do |t|
3 | association :project
4 | association :tracker
5 | t.sequence(:title) { |n| "template-title: #{n}" }
6 | t.sequence(:issue_title) { |n| "template-issue_title: #{n}" }
7 | t.sequence(:description) { |n| "template-description: #{n}" }
8 | t.sequence(:note) { |n| "template-note: #{n}" }
9 | t.sequence(:position) { |n| n }
10 | t.enabled { true }
11 | t.enabled_sharing { true }
12 | t.author_id { 1 }
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/spec/factories/note_templates.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :note_template do
3 | association :project
4 | association :tracker
5 | author_id { 1 }
6 | sequence(:name) { |n| "note-template-name: #{n}" }
7 | sequence(:description) { |n| "note-template-description: #{n}" }
8 | sequence(:memo) { |n| "note-template-memo: #{n}" }
9 | enabled { true }
10 | sequence(:position) { |n| n }
11 | visibility { NoteTemplate.visibilities[:open] }
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/factories/projects.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :project do
3 | sequence(:name) { |n| "project-name: #{n}" }
4 | sequence(:description) { |n| "project-description: #{n}" }
5 | sequence(:identifier) { |n| "project-#{n}" }
6 | homepage { 'http://ecookbook.somenet.foo/' }
7 | is_public { true }
8 |
9 | trait :with_enabled_modules do
10 | after(:build) do |tracker|
11 | status = FactoryBot.create(:issue_status)
12 | tracker.default_status_id = status.id
13 | end
14 | end
15 |
16 | factory :project_with_enabled_modules do
17 | after(:create) do |project, _evaluator|
18 | FactoryBot.create(:enabled_module, project_id: project.id)
19 | end
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/spec/factories/role.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | FactoryBot.define do
4 | factory :role do
5 | sequence(:name) { |n| "Developer: #{n}" }
6 | builtin { 0 }
7 | issues_visibility { 'default' }
8 | users_visibility { 'all' }
9 | position { 1 }
10 | permissions { %i[
11 | edit_project
12 | manage_members
13 | manage_versions
14 | manage_categories
15 | view_issues
16 | add_issues
17 | edit_issues
18 | copy_issues
19 | manage_issue_relations
20 | manage_subtasks
21 | add_issue_notes
22 | delete_issues
23 | view_issue_watchers
24 | save_queries
25 | view_gantt
26 | view_calendar
27 | log_time
28 | view_time_entries
29 | edit_own_time_entries
30 | manage_news
31 | comment_news
32 | view_documents
33 | add_documents
34 | edit_documents
35 | delete_documents
36 | view_wiki_pages
37 | view_wiki_edits
38 | edit_wiki_pages
39 | protect_wiki_pages
40 | delete_wiki_pages
41 | add_messages
42 | edit_own_messages
43 | delete_own_messages
44 | manage_boards
45 | view_files
46 | manage_files
47 | browse_repository
48 | view_changesets
49 | ] }
50 |
51 | trait :manager_role do
52 | name { 'Manager' }
53 | issues_visibility { 'all' }
54 | users_visibility { 'all' }
55 | permissions { %i[
56 | add_project
57 | edit_project
58 | close_project
59 | select_project_modules
60 | manage_members
61 | manage_versions
62 | manage_categories
63 | view_issues
64 | add_issues
65 | edit_issues
66 | manage_issue_relations
67 | manage_subtasks
68 | add_issue_notes
69 | delete_issues
70 | view_issue_watchers
71 | set_issues_private
72 | set_notes_private
73 | view_private_notes
74 | delete_issue_watchers
75 | manage_public_queries
76 | save_queries
77 | view_gantt
78 | view_calendar
79 | log_time
80 | view_time_entries
81 | edit_own_time_entries
82 | delete_time_entries
83 | manage_news
84 | comment_news
85 | view_documents
86 | add_documents
87 | edit_documents
88 | delete_documents
89 | view_wiki_pages
90 | view_wiki_edits
91 | edit_wiki_pages
92 | delete_wiki_pages_attachments
93 | protect_wiki_pages
94 | delete_wiki_pages
95 | rename_wiki_pages
96 | add_messages
97 | edit_own_messages
98 | delete_own_messages
99 | manage_boards
100 | view_files
101 | manage_files
102 | browse_repository
103 | manage_repository
104 | view_changesets
105 | manage_related_issues
106 | manage_project_activities
107 | ] }
108 | end
109 | end
110 | end
111 |
--------------------------------------------------------------------------------
/spec/factories/trackers.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :tracker do
3 | sequence(:name) { |n| "tracker-name: #{n}" }
4 | sequence(:position) { |n| n }
5 | default_status_id { 1 }
6 | trait :with_default_status do
7 | after(:build) do |tracker|
8 | status = FactoryBot.create(:issue_status)
9 | tracker.default_status_id = status.id
10 | end
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/factories/users.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | FactoryBot.define do
4 | factory :user do |u|
5 | # sequence -> exp. :login -> user1, user2.....
6 | u.sequence(:login) { |n| "user#{n}" }
7 | u.sequence(:firstname) { |n| "User#{n}" }
8 | u.sequence(:lastname) { |n| "Test#{n}" }
9 | u.sequence(:mail) { |n| "user#{n}@badge.example.com" }
10 | u.language { 'en' }
11 | # password = foo
12 | u.hashed_password { '8f659c8d7c072f189374edacfa90d6abbc26d8ed' }
13 | u.salt { '7599f9963ec07b5a3b55b354407120c0' }
14 |
15 | # login and password is the same. (Note: login length should be longer than 7.)
16 | trait :password_same_login do
17 | after(:create) do |user|
18 | user.password = user.login
19 | user.auth_source_id = nil
20 | user.save
21 | end
22 | end
23 |
24 | trait :as_group do
25 | type { 'Group' }
26 | lastname { "Group#{n}" }
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/spec/features/admin_spec.rb:
--------------------------------------------------------------------------------
1 | require_relative '../spec_helper'
2 | require_relative '../rails_helper'
3 | require_relative '../support/login_helper'
4 |
5 | RSpec.configure do |c|
6 | c.include LoginHelper
7 | end
8 |
9 | feature 'PluginSetting to apply Global issue templates to all the projects', js: true do
10 | given(:user) { FactoryBot.create(:user, :password_same_login, login: 'admin', language: 'en') }
11 |
12 | background(:all) do
13 | Redmine::Plugin.register(:redmine_issue_templates) do
14 | settings partial: 'settings/redmine_issue_templates',
15 | default: { 'apply_global_template_to_all_projects' => 'false' }
16 | end
17 | end
18 |
19 | background do
20 | # Prevent to call User.deliver_security_notification when user is created.
21 | allow_any_instance_of(User).to receive(:deliver_security_notification).and_return(true)
22 |
23 | Setting.send 'plugin_redmine_issue_templates=', 'apply_global_template_to_all_projects' => 'false'
24 | user.update_attribute(:admin, true)
25 | log_user(user.login, user.login)
26 | visit '/settings/plugin/redmine_issue_templates'
27 | end
28 |
29 | scenario 'Settings "apply_global_template_to_all_projects" is displayed.' do
30 | expect(page).to have_content('Apply Global issue templates to all the projects.')
31 | expect(page).to have_selector('#settings_apply_global_template_to_all_projects')
32 | end
33 |
34 | scenario 'Activate "apply_global_template_to_all_projects".' do
35 | expect(page).to have_unchecked_field('settings_apply_global_template_to_all_projects')
36 | check 'settings_apply_global_template_to_all_projects'
37 | click_on 'Apply'
38 | expect(page).to have_selector('#settings_apply_global_template_to_all_projects')
39 | expect(page).to have_checked_field('settings_apply_global_template_to_all_projects')
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/spec/features/create_issue_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../spec_helper'
4 | require_relative '../rails_helper'
5 | require_relative '../support/login_helper'
6 |
7 | RSpec.configure do |c|
8 | c.include LoginHelper
9 | end
10 |
11 | feature 'Create issue', js: true do
12 | given(:user) { FactoryBot.create(:user, :password_same_login, login: 'test-manager', language: 'en', admin: false) }
13 | given(:project) { FactoryBot.create(:project_with_enabled_modules) }
14 | given(:tracker) { FactoryBot.create(:tracker, :with_default_status) }
15 | given(:role) { FactoryBot.create(:role, :manager_role) }
16 | given(:status) { IssueStatus.create(name: 'open', is_closed: false) }
17 | given(:issue_note) { page.find('textarea#issue_notes') }
18 |
19 | background(:all) do
20 | Redmine::Plugin.register(:redmine_issue_templates) do
21 | settings partial: 'settings/redmine_issue_templates',
22 | default: { 'apply_global_template_to_all_projects' => 'false', 'apply_template_when_edit_issue' => 'true' }
23 | end
24 | end
25 |
26 | background do
27 | project.trackers << tracker
28 | assign_template_priv(role, add_permission: :show_issue_templates)
29 | member = Member.new(project: project, user_id: user.id)
30 | member.member_roles << MemberRole.new(role: role)
31 | member.save
32 | end
33 |
34 | describe 'apply default issue template' do
35 | background do
36 | FactoryBot.create(
37 | :issue_template,
38 | project_id: project.id,
39 | tracker_id: tracker.id,
40 | issue_title: 'default issue title',
41 | description: 'default issue description',
42 | is_default: is_default,
43 | )
44 | end
45 |
46 | context 'is_default: true' do
47 | let(:is_default) { true }
48 | scenario 'Select tracker and apply default template' do
49 | log_user(user.login, user.login)
50 | visit "/projects/#{project.identifier}/issues/new"
51 | select tracker.name, from: 'issue[tracker_id]'
52 | expect(find('#issue_subject').value).to eq('default issue title')
53 | expect(find('#issue_description').value).to eq('default issue description')
54 | end
55 | end
56 |
57 | context 'is_default: false' do
58 | let(:is_default) { false }
59 | scenario 'Select tracker and apply default template' do
60 | log_user(user.login, user.login)
61 | visit "/projects/#{project.identifier}/issues/new"
62 | select tracker.name, from: 'issue[tracker_id]'
63 | expect(find('#issue_subject').value).to eq('')
64 | expect(find('#issue_description').value).to eq('')
65 | end
66 | end
67 | end
68 | end
69 |
--------------------------------------------------------------------------------
/spec/features/update_template_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../spec_helper'
4 | require_relative '../rails_helper'
5 | require_relative '../support/login_helper'
6 |
7 | RSpec.configure do |c|
8 | c.include LoginHelper
9 | end
10 |
11 | feature 'Update template', js: true do
12 | given(:user) { FactoryBot.create(:user, :password_same_login, login: 'test-manager', language: 'en', admin: false) }
13 | given(:project) { FactoryBot.create(:project_with_enabled_modules) }
14 | given(:tracker) { FactoryBot.create(:tracker, :with_default_status) }
15 | given(:role) { FactoryBot.create(:role, :manager_role) }
16 | given(:status) { IssueStatus.create(name: 'open', is_closed: false) }
17 | given(:expected_note_description) { 'Note Template desctiption' }
18 | given!(:template) {
19 | NoteTemplate.create(project_id: project.id, tracker_id: tracker.id,
20 | name: 'Note Template name', description: expected_note_description, enabled: true)
21 | }
22 |
23 | background(:all) do
24 | Redmine::Plugin.register(:redmine_issue_templates) do
25 | settings partial: 'settings/redmine_issue_templates',
26 | default: { 'apply_global_template_to_all_projects' => 'false', 'apply_template_when_edit_issue' => 'true' }
27 | end
28 | end
29 |
30 | background do
31 | project.trackers << tracker
32 |
33 | priority = IssuePriority.create(
34 | name: 'Low',
35 | position: 1, is_default: false, type: 'IssuePriority', active: true, project_id: nil, parent_id: nil,
36 | position_name: 'lowest'
37 | )
38 |
39 | member = Member.new(project: project, user_id: user.id)
40 | member.member_roles << MemberRole.new(role: role)
41 | member.save
42 |
43 | Issue.create(project_id: project.id, tracker_id: tracker.id,
44 | author_id: user.id,
45 | priority: priority,
46 | subject: 'test_create',
47 | status_id: status.id,
48 | description: 'IssueTest#test_create')
49 | end
50 |
51 | context 'Have show_issue_template permission' do
52 |
53 | background do
54 | assign_template_priv(role, add_permission: :show_issue_templates)
55 | end
56 |
57 | scenario 'Cannot edit the template, only view it' do
58 | visit_log_user(user)
59 | visit "/projects/#{project.identifier}/note_templates/#{template.id}"
60 | sleep(0.2)
61 | expect(page).to have_no_selector('div#edit-note_template')
62 | expect(page).to have_selector('div#view-note_template')
63 | end
64 | end
65 |
66 | context 'Have edit_issue_template permission' do
67 |
68 | background do
69 | assign_template_priv(role, add_permission: :edit_issue_templates)
70 | assign_template_priv(role, add_permission: :show_issue_templates)
71 | end
72 |
73 | scenario 'Can edit the template, and view it' do
74 | visit_log_user(user)
75 | visit "/projects/#{project.identifier}/note_templates/#{template.id}"
76 | sleep(0.2)
77 | expect(page).to have_selector('div#edit-note_template')
78 | expect(page).to have_no_selector('div#view-note_template')
79 | end
80 | end
81 |
82 | private
83 |
84 | def visit_log_user(user)
85 | user.update_attribute(:admin, false)
86 | log_user(user.login, user.login)
87 | end
88 | end
89 |
--------------------------------------------------------------------------------
/spec/helpers/issue_templates_helper_spec.rb:
--------------------------------------------------------------------------------
1 | require_relative '../spec_helper'
2 |
3 | describe IssueTemplatesHelper do
4 | describe '#project_tracker?' do
5 | let(:trackers) { FactoryBot.create_list(:tracker, 2, :with_default_status) }
6 | let(:project) { FactoryBot.create(:project) }
7 | let(:tracker) { trackers.first }
8 | subject { helper.project_tracker?(tracker, project) }
9 |
10 | context 'Tracker is associated' do
11 | before do
12 | project.trackers << tracker
13 | end
14 | it { is_expected.to be_truthy }
15 | end
16 | context 'Tracker is not associated' do
17 | before do
18 | project.trackers << trackers.last
19 | end
20 | it { is_expected.to be_falsey }
21 | end
22 | end
23 |
24 | describe '#non_project_tracker_msg' do
25 | it { expect(helper.non_project_tracker_msg(true)).to eq '' }
26 | it { expect(helper.non_project_tracker_msg(false)).to match('') }
27 | end
28 |
29 | describe '#template_target_trackers' do
30 | let(:trackers) { FactoryBot.create_list(:tracker, 2, :with_default_status) }
31 | let(:project) { FactoryBot.create(:project) }
32 | let(:tracker) { trackers.last }
33 | let(:template) do
34 | FactoryBot.create(:issue_template, tracker_id: tracker.id, project_id: project.id)
35 | end
36 | subject { helper.template_target_trackers(project, template) }
37 | before do
38 | project.trackers << trackers.first
39 | end
40 | it { expect(subject.include?([tracker.name, tracker.id])).to be_truthy }
41 | it { expect(subject.length).to eq 2 }
42 | end
43 |
44 | describe '#options_for_template_pulldown' do
45 | let(:options) do
46 | option = Struct.new(:id, :name)
47 | [].tap do |options|
48 | (0..2).each do |id|
49 | options << option.new(id, "name-#{id}")
50 | end
51 | end
52 | end
53 | subject { helper.options_for_template_pulldown(options) }
54 | it { expect(subject).to match('') }
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/spec/models/global_issue_template_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../spec_helper'
4 |
5 | describe GlobalIssueTemplate do
6 | describe '#valid?' do
7 | let(:instance) { GlobalIssueTemplate.new(tracker_id: tracker.id, title: 'sample', description: 'description1') }
8 | let(:tracker) { FactoryBot.create(:tracker, :with_default_status) }
9 | subject { instance.valid? }
10 |
11 | it 'related_link in invalid format' do
12 | instance.related_link = 'non url format string'
13 | is_expected.to be_falsey
14 | expect(instance.errors.messages.key?(:related_link)).to be_truthy
15 | end
16 |
17 | it 'related_link in valid format' do
18 | instance.related_link = 'https://valid.example.com/links.html'
19 | is_expected.to be_truthy
20 | end
21 | end
22 |
23 | describe '#builtin_fields_json' do
24 | let(:tracker) { FactoryBot.create(:tracker, :with_default_status) }
25 | let(:global_issue_template) do
26 | create(:global_issue_template, tracker_id: tracker.id)
27 | end
28 | subject { global_issue_template.update(builtin_fields_json: object) }
29 |
30 | context 'Data is a valid hash' do
31 | let(:object) { { 'key': 'value', 'foo': 'bar' } }
32 | it { is_expected.to be_truthy }
33 | end
34 |
35 | context 'Data is not a valid hash' do
36 | let(:object) { [1, 2, 3] }
37 | it { expect { subject }.to raise_error(ActiveRecord::SerializationTypeMismatch) }
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/spec/models/global_note_template_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../spec_helper'
4 |
5 | describe GlobalNoteTemplate do
6 | let(:tracker) { FactoryBot.create(:tracker, :with_default_status) }
7 | let!(:note_template) { FactoryBot.create(:global_note_template, tracker_id: tracker.id, position: 1) }
8 | let!(:note_template2) { FactoryBot.create(:global_note_template, tracker_id: tracker.id, position: 2) }
9 | let!(:note_template3) { FactoryBot.create(:global_note_template, tracker_id: tracker.id, position: 3) }
10 |
11 | it 'Instance of GlobalNoteTemplate' do
12 | expect(note_template).to be_an_instance_of(GlobalNoteTemplate)
13 | end
14 |
15 | describe 'scope: .sorted' do
16 | it 'do sort by position correctly' do
17 | expect([note_template, note_template2, note_template3]).to eq GlobalNoteTemplate.sorted
18 | expect(GlobalNoteTemplate.sorted.first).to eq note_template
19 | end
20 |
21 | it 'do sort by position correctly after update' do
22 | note_template.update(position: GlobalNoteTemplate.count)
23 | expect(GlobalNoteTemplate.sorted).to eq [note_template2, note_template3, note_template]
24 | end
25 | end
26 |
27 | it 'can be deleted even though some projects bound' do
28 | note_template_with_project = create(:global_note_template, tracker_id: tracker.id, position: 4, enabled: false)
29 | note_template_with_project.projects << create(:project) << create(:project) << create(:project)
30 |
31 | expect(GlobalNoteTemplate.where(id: note_template_with_project.id)).not_to be_empty
32 | note_template_with_project.destroy
33 | expect(GlobalNoteTemplate.where(id: note_template_with_project.id)).to be_empty
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/spec/models/issue_template_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../spec_helper'
4 |
5 | describe IssueTemplate do
6 | let(:tracker) { FactoryBot.create(:tracker, :with_default_status) }
7 | let(:project) { FactoryBot.create(:project) }
8 | let(:issue_template) { FactoryBot.create(:issue_template, tracker_id: tracker.id, project_id: project.id) }
9 | let(:issue_template2) { FactoryBot.create(:issue_template, tracker_id: tracker.id, project_id: project.id) }
10 | it 'Instance of IssueTemplate' do
11 | expect(issue_template).to be_an_instance_of(IssueTemplate)
12 | end
13 |
14 | describe 'scope .orphaned' do
15 | subject { IssueTemplate.orphaned.count }
16 | before do
17 | # Remove related tracker
18 | issue_template.tracker.delete
19 | end
20 | it { is_expected.to eq 1 }
21 | end
22 |
23 | describe 'scope: .sorted' do
24 | it 'do sort by position correctly' do
25 | expect([issue_template, issue_template2]).to eq [issue_template2, issue_template].sort
26 | expect(IssueTemplate.sorted.first).to eq issue_template
27 | end
28 |
29 | it 'do sort by position correctly after update' do
30 | issue_template.update(position: issue_template2.position + 100)
31 | expect(IssueTemplate.sorted.first).to eq issue_template2
32 | end
33 | end
34 |
35 | describe '#destroy' do
36 | subject { issue_template.destroy }
37 | context 'Template is enabled' do
38 | before do
39 | issue_template.enabled = true
40 | issue_template.save
41 | end
42 | it 'Failed to remove with invalid message' do
43 | expect(Rails.logger).to receive(:info).with(/\[Destroy\] IssueTemplate: /).never
44 | subject
45 | expect(issue_template.errors.present?).to be_truthy
46 | end
47 | end
48 | context 'Template is disabled' do
49 | before { issue_template.enabled = false }
50 | it 'Removed and log message is generated' do
51 | expect(Rails.logger).to receive(:info).with(/\[Destroy\] IssueTemplate: /).once
52 | subject
53 | expect(issue_template.errors.present?).to be_falsey
54 | end
55 | end
56 | end
57 |
58 | describe '#valid?' do
59 | let(:instance) { described_class.new(tracker_id: tracker.id, project_id: project.id, title: 'sample', description: 'description1') }
60 | subject { instance.valid? }
61 |
62 | it 'related_link in invalid format' do
63 | instance.related_link = 'non url format string'
64 | is_expected.to be_falsey
65 | expect(instance.errors.messages.key?(:related_link)).to be_truthy
66 | end
67 |
68 | it 'related_link in valid format' do
69 | instance.related_link = 'https://valid.example.com/links.html'
70 | is_expected.to be_truthy
71 | end
72 | end
73 |
74 | describe '#builtin_fields_json' do
75 | subject { issue_template.update(builtin_fields_json: object) }
76 |
77 | context 'Data is a valid hash' do
78 | let(:object) { { 'key': 'value', 'foo': 'bar' } }
79 | it { is_expected.to be_truthy }
80 | end
81 |
82 | context 'Data is not a valid hash' do
83 | let(:object) { [1, 2, 3] }
84 | it { expect { subject }.to raise_error(ActiveRecord::SerializationTypeMismatch) }
85 | end
86 | end
87 | end
88 |
--------------------------------------------------------------------------------
/spec/models/note_visible_role_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../spec_helper'
4 |
5 | describe NoteVisibleRole do
6 | let(:instance) { described_class.new }
7 | it 'Instance of NoteVisibleRole' do
8 | expect(instance).to be_an_instance_of(NoteVisibleRole)
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/spec/rails_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is copied to spec/ when you run 'rails generate rspec:install'
4 | ENV['RAILS_ENV'] ||= 'test'
5 | require File.expand_path('../../../config/environment', __dir__)
6 |
7 | abort('The Rails environment is running in production mode!') if Rails.env.production?
8 | require File.expand_path('./spec_helper', __dir__)
9 | require 'selenium-webdriver'
10 |
11 | ActiveRecord::Migration.maintain_test_schema!
12 |
13 | RSpec.configure do |config|
14 | config.fixture_path = "#{::Rails.root}/test/fixtures"
15 | config.include FactoryBot::Syntax::Methods
16 |
17 | config.before :suite do
18 | Capybara.register_driver :headless_chrome do |app|
19 | options = Selenium::WebDriver::Chrome::Options.new
20 | #
21 | # NOTE: When using Chrome headless, default window size is 800x600.
22 | # In case window size is not specified, Redmine renderes its contents with responsive mode.
23 | #
24 | options.add_argument('window-size=1280,800')
25 | unless ENV['HEADLESS'] == '0'
26 | options.add_argument('headless')
27 | options.add_argument('disable-gpu')
28 | end
29 | Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
30 | end
31 | end
32 |
33 | config.before :each, type: :feature do
34 | Capybara.javascript_driver = :headless_chrome
35 | Capybara.current_driver = :headless_chrome
36 | Capybara.default_max_wait_time = 30
37 | end
38 |
39 | config.include Capybara::DSL
40 | config.use_transactional_fixtures = false
41 | config.infer_spec_type_from_file_location!
42 |
43 | config.filter_rails_from_backtrace!
44 |
45 | config.before(:suite) do
46 | DatabaseCleaner.strategy = :truncation
47 | DatabaseCleaner.clean_with(:truncation)
48 | end
49 |
50 | config.before(:each) do
51 | DatabaseCleaner.start
52 | end
53 |
54 | config.after(:each) do
55 | DatabaseCleaner.clean
56 | end
57 | end
58 |
--------------------------------------------------------------------------------
/spec/requests/global_note_templates_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../spec_helper'
4 | require File.expand_path(File.dirname(__FILE__) + '/../support/controller_helper')
5 |
6 | RSpec.configure do |c|
7 | c.include ControllerHelper
8 | end
9 |
10 | RSpec.describe 'Global Note Template', type: :request do
11 | let(:user) { FactoryBot.create(:user, :password_same_login, login: 'test-manager', language: 'en', admin: true) }
12 | let(:tracker) { FactoryBot.create(:tracker, :with_default_status) }
13 | let(:target_template_name) { 'Global Note template name' }
14 | let(:target_template) { GlobalNoteTemplate.last }
15 |
16 | before do
17 | # do nothing
18 | end
19 |
20 | it 'show global note template list' do
21 | login_request(user.login, user.login)
22 | get '/global_note_templates'
23 | expect(response.status).to eq 200
24 |
25 | get '/global_note_templates/new'
26 | expect(response.status).to eq 200
27 | end
28 |
29 | it 'create global note template and load' do
30 | login_request(user.login, user.login)
31 | post '/global_note_templates',
32 | params: { global_note_template:
33 | { tracker_id: tracker.id, name: target_template_name,
34 | description: 'Global Note template description', memo: 'Test memo', enabled: 1 } }
35 | expect(response).to have_http_status(302)
36 |
37 | post '/note_templates/load', params: { note_template: { note_template_id: target_template.id, template_type: 'global' } }
38 | json = JSON.parse(response.body)
39 | expect(target_template.name).to eq(json['note_template']['name'])
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/spec/requests/note_templates_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative '../spec_helper'
4 | require File.expand_path(File.dirname(__FILE__) + '/../support/controller_helper')
5 |
6 | RSpec.configure do |c|
7 | c.include ControllerHelper
8 | end
9 |
10 | RSpec.describe 'Note Template', type: :request do
11 | let(:user) { FactoryBot.create(:user, :password_same_login, login: 'test-manager', language: 'en', admin: false) }
12 | let(:project) { FactoryBot.create(:project_with_enabled_modules) }
13 | let(:tracker) { FactoryBot.create(:tracker, :with_default_status) }
14 | let(:role) { FactoryBot.create(:role, :manager_role) }
15 | let(:target_template) { NoteTemplate.last }
16 |
17 | before do
18 | project.trackers << tracker
19 | assign_template_priv(role, add_permission: :show_issue_templates)
20 | assign_template_priv(role, add_permission: :edit_issue_templates)
21 | member = Member.new(project: project, user_id: user.id)
22 | member.member_roles << MemberRole.new(role: role)
23 | member.save
24 | end
25 |
26 | it 'show note template list' do
27 | login_request(user.login, user.login)
28 | get "/projects/#{project.identifier}/note_templates"
29 | expect(response.status).to eq 200
30 |
31 | get "/projects/#{project.identifier}/note_templates/new"
32 | expect(response.status).to eq 200
33 | end
34 |
35 | it 'create note template and load' do
36 | login_request(user.login, user.login)
37 | post "/projects/#{project.identifier}/note_templates",
38 | params: { note_template:
39 | { tracker_id: tracker.id, name: 'Note template name',
40 | description: 'Note template description', memo: 'Test memo', enabled: 1 } }
41 | expect(response).to have_http_status(302)
42 |
43 | post '/note_templates/load', params: { note_template: { note_template_id: target_template.id } }
44 | json = JSON.parse(response.body)
45 | expect(target_template.name).to eq(json['note_template']['name'])
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../../../../config/environment', __FILE__)
2 | require 'rspec/rails'
3 | require 'simplecov'
4 | require 'factory_bot_rails'
5 | require 'database_cleaner'
6 |
7 | SimpleCov.coverage_dir('coverage/redmine_issue_templates_spec')
8 | SimpleCov.start 'rails'
9 |
10 | RSpec.configure do |config|
11 | config.fixture_path = "#{::Rails.root}/test/fixtures"
12 | config.use_transactional_fixtures = false
13 | config.infer_spec_type_from_file_location!
14 | config.include FactoryBot::Syntax::Methods
15 | FactoryBot.definition_file_paths = [File.expand_path('../factories', __FILE__)]
16 | FactoryBot.find_definitions
17 | config.before(:all) do
18 | FactoryBot.reload
19 | end
20 |
21 | config.before(:suite) do
22 | DatabaseCleaner.strategy = :truncation
23 | DatabaseCleaner.clean_with(:truncation)
24 | end
25 |
26 | config.before(:each) do
27 | DatabaseCleaner.start
28 | end
29 |
30 | config.after(:each) do
31 | DatabaseCleaner.clean
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/spec/support/controller_helper.rb:
--------------------------------------------------------------------------------
1 | module ControllerHelper
2 | # AuthHeader with api key (Ref.http://www.redmine.org/projects/redmine/wiki/Rest_api)
3 | def auth_with_user(user)
4 | request.headers['X-Redmine-API-Key'] = user.api_key.to_s
5 | end
6 |
7 | def clear_token
8 | request.headers['X-Redmine-API-Key'] = nil
9 | end
10 |
11 | def login_request(login, password)
12 | post '/login', params: { username: login, password: password }
13 | end
14 |
15 | def assign_template_priv(role, add_permission: nil, remove_permission: nil)
16 | return if add_permission.blank? && remove_permission.blank?
17 |
18 | role.add_permission! add_permission if add_permission.present?
19 | role.remove_permission! remove_permission if remove_permission.present?
20 | end
21 |
22 | shared_context 'As admin' do
23 | let(:user) { FactoryBot.create(:user, status: 1, admin: is_admin) }
24 | let(:is_admin) { true }
25 | end
26 |
27 | shared_context 'Project and Tracler exists' do
28 | let(:count) { 4 }
29 | let(:trackers) { FactoryBot.create_list(:tracker, 2, :with_default_status) }
30 | let(:projects) { FactoryBot.create_list(:project, count) }
31 | end
32 |
33 | shared_examples 'Right response' do |status_code|
34 | it { expect(response.status).to eq status_code }
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/spec/support/login_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module LoginHelper
4 | def log_user(login, password)
5 | visit '/login'
6 |
7 | within('#login-form form') do
8 | fill_in 'username', with: login
9 | fill_in 'password', with: password
10 | find('input[name=login]').click
11 | end
12 | end
13 |
14 | def assign_template_priv(role, add_permission: nil, remove_permission: nil)
15 | return if add_permission.blank? && remove_permission.blank?
16 |
17 | role.add_permission! add_permission if add_permission.present?
18 | role.remove_permission! remove_permission if remove_permission.present?
19 | end
20 |
21 | def wait_for_ajax
22 | Timeout.timeout(Capybara.default_max_wait_time) do
23 | loop until finished_all_ajax_requests?
24 | end
25 | end
26 |
27 | def finished_all_ajax_requests?
28 | page.evaluate_script('jQuery.active').zero?
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/test/fixtures/global_issue_templates.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2 | one:
3 | id: 1
4 |
5 | tracker_id: 1
6 |
7 | author_id: 1
8 |
9 | title: global_title1
10 |
11 | description: "global description1"
12 |
13 | note: note1
14 |
15 | enabled: true
16 |
17 | position: 1
18 |
19 | two:
20 | id: 2
21 |
22 | tracker_id: 3
23 |
24 | author_id: 1
25 |
26 | title: titel2
27 |
28 | description: "global description2"
29 |
30 | note: note2
31 |
32 | enabled: false
33 |
34 | position: 1
35 |
36 | three:
37 | id: 3
38 |
39 | tracker_id: 2
40 |
41 | author_id: 1
42 |
43 | title: titel3
44 |
45 | description: "global description3"
46 |
47 | note: note3
48 |
49 | enabled: true
50 |
51 | position: 1
52 |
53 | four:
54 | id: 4
55 |
56 | tracker_id: 1
57 |
58 | author_id: 1
59 |
60 | title: titel4
61 |
62 | description: "global description4"
63 |
64 | note: note4
65 |
66 | enabled: true
67 |
68 | position: 2
69 |
70 | five:
71 | id: 5
72 |
73 | tracker_id: 1
74 |
75 | author_id: 1
76 |
77 | title: titel5
78 |
79 | description: "global description5"
80 |
81 | note: note5
82 |
83 | enabled: true
84 |
85 | position: 3
86 |
--------------------------------------------------------------------------------
/test/fixtures/global_issue_templates_projects.yml:
--------------------------------------------------------------------------------
1 | global_issue_templates_projects_001:
2 | project_id: 1
3 | global_issue_template_id: 1
4 | global_issue_templates_projects_002:
5 | project_id: 3
6 | global_issue_template_id: 1
--------------------------------------------------------------------------------
/test/fixtures/global_note_templates.yml:
--------------------------------------------------------------------------------
1 | ---
2 | global_note_templates_001:
3 | id: 1
4 | name: global note template 1
5 | description: |-
6 | global description 1-1
7 | global description 1-2
8 | tracker_id: 1
9 | author_id: 2
10 | enabled: true
11 | position: 1
12 | visibility: open
13 |
--------------------------------------------------------------------------------
/test/fixtures/issue_template_settings.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2 | one:
3 | id: 1
4 |
5 | project_id: 1
6 |
7 | help_message: MyText
8 |
9 | enabled: true
10 |
11 | inherit_templates: false
12 |
13 | two:
14 | id: 2
15 |
16 | project_id: 2
17 |
18 | help_message: MyText
19 |
20 | enabled: false
21 |
22 | inherit_templates: false
23 |
24 | three:
25 | id: 3
26 |
27 | project_id: 3
28 |
29 | help_message: Project3 help
30 |
31 | enabled: true
32 |
33 | inherit_templates: false
34 |
35 |
--------------------------------------------------------------------------------
/test/fixtures/issue_templates.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2 | one:
3 | id: 1
4 |
5 | project_id: 1
6 |
7 | tracker_id: 1
8 |
9 | author_id: 1
10 |
11 | title: title1
12 |
13 | description: description1
14 |
15 | note: note1
16 |
17 | enabled: true
18 |
19 | position: 1
20 |
21 | enabled_sharing: true
22 |
23 | two:
24 | id: 2
25 |
26 | project_id: 1
27 |
28 | tracker_id: 3
29 |
30 | author_id: 1
31 |
32 | title: titel2
33 |
34 | description: description2
35 |
36 | note: note2
37 |
38 | enabled: false
39 |
40 | position: 1
41 |
42 | enabled_sharing: false
43 |
44 | three:
45 | id: 3
46 |
47 | project_id: 1
48 |
49 | tracker_id: 2
50 |
51 | author_id: 1
52 |
53 | title: titel3
54 |
55 | description: description3
56 |
57 | note: note3
58 |
59 | enabled: true
60 |
61 | position: 1
62 |
63 | four:
64 | id: 4
65 |
66 | project_id: 1
67 |
68 | tracker_id: 1
69 |
70 | author_id: 2
71 |
72 | title: titel4
73 |
74 | description: description4
75 |
76 | note: note4
77 |
78 | enabled: true
79 |
80 | position: 2
81 |
82 | five:
83 | id: 5
84 |
85 | project_id: 1
86 |
87 | tracker_id: 1
88 |
89 | author_id: 1
90 |
91 | title: title5
92 |
93 | description: description5
94 |
95 | note: note5
96 |
97 | enabled: false
98 |
99 | position: 3
100 |
101 | six:
102 | id: 6
103 |
104 | project_id: 3
105 |
106 | tracker_id: 1
107 |
108 | author_id: 1
109 |
110 | title: title6
111 |
112 | description: description6
113 |
114 | note: note5
115 |
116 | enabled: true
117 |
118 | position: 3
119 |
--------------------------------------------------------------------------------
/test/fixtures/note_templates.yml:
--------------------------------------------------------------------------------
1 | ---
2 | note_templates_001:
3 | id: 1
4 | name: note template 1
5 | description: |-
6 | comment 1-1
7 | comment 1-2
8 | project_id: 1
9 | tracker_id: 1
10 | author_id: 2
11 | enabled: true
12 | position: 1
13 | visibility: mine
14 | note_templates_002:
15 | id: 2
16 | name: note template 2
17 | description: |-
18 | comment 2-1
19 | comment 2-2
20 | project_id: 1
21 | tracker_id: 1
22 | author_id: 2
23 | enabled: true
24 | position: 2
25 | visibility: roles
26 | note_templates_003:
27 | id: 3
28 | name: note template 3
29 | description: |-
30 | comment 3-1
31 | comment 3-2
32 | project_id: 1
33 | tracker_id: 1
34 | author_id: 2
35 | enabled: true
36 | position: 3
37 | visibility: roles
38 | note_templates_004:
39 | id: 4
40 | name: note template 4
41 | description: |-
42 | comment 4-1
43 | comment 4-2
44 | project_id: 1
45 | tracker_id: 1
46 | author_id: 2
47 | enabled: true
48 | position: 4
49 | visibility: open
50 | note_templates_005:
51 | id: 5
52 | name: note template 5
53 | description: |-
54 | comment 5-1
55 | comment 5-2
56 | project_id: 1
57 | tracker_id: 1
58 | author_id: 3
59 | enabled: true
60 | position: 5
61 | visibility: mine
62 |
--------------------------------------------------------------------------------
/test/fixtures/note_visible_roles.yml:
--------------------------------------------------------------------------------
1 | ---
2 | note_visible_roles_001:
3 | id: 1
4 | note_template_id: 2
5 | role_id: 1
6 | note_visible_roles_002:
7 | id: 2
8 | note_template_id: 2
9 | role_id: 2
10 | note_visible_roles_003:
11 | id: 3
12 | note_template_id: 3
13 | role_id: 2
14 |
--------------------------------------------------------------------------------
/test/functional/issue_templates_settings_controller_test.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require File.expand_path('../test_helper', __dir__)
4 |
5 | class IssueTemplatesSettingsControllerTest < Redmine::ControllerTest
6 | fixtures :projects,
7 | :users,
8 | :roles,
9 | :members,
10 | :member_roles,
11 | :enabled_modules,
12 | :issue_templates
13 |
14 | def setup
15 | # Enabled Template module
16 | enabled_module = EnabledModule.new
17 | enabled_module.project_id = 1
18 | enabled_module.name = 'issue_templates'
19 | enabled_module.save
20 |
21 | # set default user to 2 (as member)
22 | @request.session[:user_id] = 2
23 | Role.find(1).add_permission! :manage_issue_templates
24 |
25 | @project = Project.find(1)
26 | end
27 |
28 | def test_update_without_permission
29 | Role.find(1).remove_permission! :manage_issue_templates
30 | post :edit, params: { project_id: @project,
31 | settings: { enabled: '1', help_message: 'Hoo', inherit_templates: true },
32 | setting_id: 1, tab: 'issue_templates' }
33 | assert_response 403
34 | end
35 |
36 | def test_update_with_permission_and_non_project
37 | post :edit, params: { project_id: 'dummy',
38 | settings: { enabled: '1', help_message: 'Hoo', inherit_templates: true },
39 | setting_id: 1 }
40 | assert_response 404
41 | end
42 |
43 | def test_update_with_permission_and_redirect
44 | post :edit, params: { project_id: @project,
45 | settings: { enabled: '1', help_message: 'Hoo', inherit_templates: true },
46 | setting_id: 1 }
47 | assert_response :redirect
48 | assert_redirected_to controller: 'issue_templates_settings',
49 | action: 'index', project_id: @project
50 | end
51 |
52 | def test_preview_template_setting
53 | post :preview, params: { settings: { help_message: 'h1. Preview test.',
54 | enabled: '1' },
55 | project_id: @project }
56 | assert_select 'h1', /Preview test\./, @response.body.to_s
57 | end
58 |
59 | def test_create_template_setting
60 | IssueTemplateSetting.delete_all
61 |
62 | post :edit, params: { project_id: @project,
63 | settings: { enabled: '1', help_message: 'Hoo', inherit_templates: true },
64 | setting_id: 1 }
65 | assert_response :redirect
66 | assert_redirected_to controller: 'issue_templates_settings',
67 | action: 'index', project_id: @project
68 | end
69 | end
70 |
--------------------------------------------------------------------------------
/test/functional/issues_controller_test.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2 | require 'issues_controller'
3 |
4 | # Test for view hooks.
5 | class IssuesControllerTest < Redmine::ControllerTest
6 | fixtures :projects,
7 | :users,
8 | :roles,
9 | :members,
10 | :member_roles,
11 | :issues,
12 | :issue_statuses,
13 | :versions,
14 | :trackers,
15 | :projects_trackers,
16 | :issue_categories,
17 | :enabled_modules,
18 | :enumerations,
19 | :attachments,
20 | :workflows,
21 | :custom_fields,
22 | :custom_values,
23 | :custom_fields_trackers,
24 | :time_entries,
25 | :journals,
26 | :journal_details,
27 | :issue_templates
28 |
29 | def setup
30 | User.current = nil
31 | enabled_module = EnabledModule.new
32 | enabled_module.project_id = 1
33 | enabled_module.name = 'issue_templates'
34 | enabled_module.save
35 | roles = Role.all
36 | roles.each do |role|
37 | role.permissions << :show_issue_templates
38 | role.remove_permission! :edit_issue_templates
39 | role.save
40 | end
41 | @request.session[:user_id] = 2
42 | @project = Project.find(1)
43 | end
44 |
45 | def test_index_without_project
46 | get :index
47 | assert_response :success
48 | assert_select 'h3', count: 0, text: I18n.t('issue_template')
49 | end
50 |
51 | def test_index
52 | get :index, params: { project_id: @project.id }
53 | assert_response :success
54 | assert_select 'div#template_area select#issue_template', false,
55 | 'Action index should not contain template select pulldown.'
56 | assert_select 'h3', text: I18n.t('issue_template')
57 | assert_select 'a', { href: "/projects/#{@project}/issue_templates/new" }, false
58 | end
59 |
60 | def test_index_with_edit_permission
61 | Role.find(1).add_permission! :edit_issue_templates
62 | get :index, params: { project_id: @project.id }
63 | assert_select 'h3', text: I18n.t('issue_template')
64 | assert_select 'a', href: "/projects/#{@project}/issue_templates/new"
65 | end
66 |
67 | def test_new
68 | get :new, params: { project_id: 1 }
69 | assert_response :success
70 | assert_select 'div#template_area select#issue_template'
71 | end
72 |
73 | # NOTE: When copy, template area should not be displayed.
74 | def test_copy
75 | get :new, params: { project_id: 1, copy_from: 1 }
76 | assert_response :success
77 | assert_select 'div#template_area', false
78 | end
79 |
80 | def test_new_without_project
81 | get :new
82 | assert_response :success
83 | assert_select 'div#template_area select#issue_template', true
84 | end
85 | end
86 |
--------------------------------------------------------------------------------
/test/functional/note_templates_controller_test.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../test_helper', __dir__)
2 | require 'minitest/autorun'
3 |
4 | class NoteTemplatesControllerTest < Redmine::ControllerTest
5 | fixtures :projects, :enabled_modules,
6 | :users, :roles,
7 | :members, :member_roles,
8 | :trackers, :projects_trackers,
9 | :note_templates, :note_visible_roles
10 |
11 | def setup
12 | @request.session[:user_id] = 2 # jsmith
13 | @request.env['HTTP_REFERER'] = '/'
14 | # Enabled Template module
15 | @project = Project.find(1)
16 | @project.enabled_modules << EnabledModule.new(name: 'issue_templates')
17 | @project.save!
18 |
19 | # Set default permission: show template
20 | Role.find(1).add_permission! :show_issue_templates
21 | end
22 |
23 | def test_index_with_non_existing_project_should_be_not_found
24 | # set non existing project
25 | get :index, params: { project_id: 100 }
26 | assert_response :not_found
27 | end
28 |
29 | def test_index_without_show_permission_should_be_forbidden
30 | Role.find(1).remove_permission! :show_issue_templates
31 | get :index, params: { project_id: 1 }
32 | assert_response :forbidden
33 | end
34 |
35 | def test_index_with_normal_should_be_success
36 | get :index, params: { project_id: 1 }
37 | assert_response :success
38 | end
39 |
40 | def test_index_with_admin_logged_in_should_appear_all_note_templates
41 | @request.session[:user_id] = 1 # admin
42 |
43 | ids = NoteTemplate.reorder(id: :asc).where(project_id: 1).pluck(:id)
44 | assert_equal [1, 2, 3, 4, 5], ids
45 |
46 | get :index, params: { project_id: 1 }
47 | assert_response :success
48 |
49 | assert_select 'table.template_list tbody tr.note_template' do
50 | ids.each do |id|
51 | assert_select 'td a[href=?]', "/projects/ecookbook/note_templates/#{id}", count: 1
52 | end
53 | end
54 | end
55 |
56 | def test_index_should_appear_note_templates_with_open_visibility
57 | ids = NoteTemplate.reorder(id: :asc).where(project_id: 1).open.pluck(:id)
58 | assert_equal [4], ids
59 |
60 | get :index, params: { project_id: 1 }
61 | assert_response :success
62 |
63 | assert_select 'table.template_list tbody tr.note_template' do
64 | ids.each do |id|
65 | assert_select 'td a[href=?]', "/projects/ecookbook/note_templates/#{id}", count: 1
66 | end
67 | end
68 | end
69 |
70 | def test_index_with_author_logged_in_should_appear_note_templates_with_mine_visibility
71 | user_id = 3 # dlopper
72 | @request.session[:user_id] = user_id
73 | Role.find(2).add_permission! :show_issue_templates
74 |
75 | ids = NoteTemplate.reorder(id: :asc).where(project_id: 1).mine_condition(user_id).pluck(:id)
76 | assert_equal [5], ids
77 |
78 | get :index, params: { project_id: 1 }
79 | assert_response :success
80 |
81 | assert_select 'table.template_list tbody tr.note_template' do
82 | ids.each do |id|
83 | assert_select 'td a[href=?]', "/projects/ecookbook/note_templates/#{id}", count: 1
84 | end
85 | end
86 | end
87 |
88 | def test_index_should_appear_note_templates_with_roles_visibility
89 | ids = NoteTemplate.reorder(id: :asc).where(project_id: 1).where(visibility: :roles).pluck(:id)
90 | assert_equal [2, 3], ids
91 |
92 | @request.session[:user_id] = 2 # jsmith
93 | Role.find(1).add_permission! :show_issue_templates
94 |
95 | get :index, params: { project_id: 1 }
96 | assert_response :success
97 |
98 | assert_select 'table.template_list tbody tr.note_template' do
99 | assert_select 'td a[href=?]', "/projects/ecookbook/note_templates/2", count: 1
100 | assert_select 'td a[href=?]', "/projects/ecookbook/note_templates/3", count: 0
101 | end
102 |
103 | @request.session[:user_id] = 3 # dlopper
104 | Role.find(2).add_permission! :show_issue_templates
105 |
106 | get :index, params: { project_id: 1 }
107 | assert_response :success
108 |
109 | assert_select 'table.template_list tbody tr.note_template' do
110 | assert_select 'td a[href=?]', "/projects/ecookbook/note_templates/2", count: 1
111 | assert_select 'td a[href=?]', "/projects/ecookbook/note_templates/3", count: 1
112 | end
113 | end
114 | end
115 |
--------------------------------------------------------------------------------
/test/functional/projects_controller_test.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2 | require 'projects_controller'
3 |
4 | class ProjectsControllerTest < Redmine::ControllerTest
5 | fixtures :projects, :users, :roles, :members, :member_roles,
6 | :trackers, :projects_trackers, :enabled_modules
7 | def setup
8 | # as project admin
9 | @request.session[:user_id] = 2
10 | Role.find(1).add_permission! :show_issue_templates
11 | # Enabled Template module
12 | @project = Project.find(1)
13 | @project.enabled_modules << EnabledModule.new(name: 'issue_templates')
14 | @project.save!
15 | end
16 |
17 | def test_settings
18 | get :show, params: { id: 1 }
19 | assert_response :success
20 | assert_select '#main-menu > ul > li > a.issue-templates'
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/test/integration/layout_test.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2 |
3 | class LayoutTest < Redmine::IntegrationTest
4 | fixtures :projects, :trackers, :issue_statuses, :issues,
5 | :enumerations, :users,
6 | :projects_trackers,
7 | :roles,
8 | :member_roles,
9 | :members,
10 | :enabled_modules,
11 | :workflows,
12 | :issue_templates
13 |
14 | def test_issue_template_not_visible_when_module_off
15 | # module -> disabled
16 | log_user('admin', 'admin')
17 | post '/projects/ecookbook/modules',
18 | params: { enabled_module_names: ['issue_tracking'], commit: 'Save', id: 'ecookbook' }
19 |
20 | get '/projects/ecookbook/issues'
21 | assert_response :success
22 | assert_select 'h3', count: 0, text: I18n.t('issue_template')
23 |
24 | get '/projects/ecookbook/issues/new'
25 | assert_select 'div#template_area select#issue_template', 0
26 | end
27 |
28 | def test_issue_template_visible_when_module_on
29 | # module -> enabled
30 | log_user('admin', 'admin')
31 | post '/projects/ecookbook/modules',
32 | params: { enabled_module_names: %w[issue_tracking issue_templates],
33 | commit: 'Save', id: 'ecookbook' }
34 |
35 | get '/projects/ecookbook/issues'
36 | assert_response :success
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | begin
2 | require 'simplecov'
3 | require 'simplecov-rcov'
4 | rescue LoadError => ex
5 | puts <<-"EOS"
6 | This test should be called only for redmine issue template test.
7 | Test exit with LoadError -- #{ex.message}
8 | Please move redmine_issue_templates/Gemfile.local to redmine_issue_templates/Gemfile
9 | and run bundle install if you want to to run tests.
10 | EOS
11 | exit
12 | end
13 |
14 | if ENV['JENKINS'] == 'true'
15 | SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter
16 | true
17 | else
18 | SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([SimpleCov::Formatter::HTMLFormatter])
19 | end
20 |
21 | SimpleCov.coverage_dir('coverage/redmine_issue_templates_test')
22 | SimpleCov.start do
23 | add_filter do |source_file|
24 | # report this plugin only.
25 | !source_file.filename.include?('plugins/redmine_issue_templates') || !source_file.filename.end_with?('.rb')
26 | end
27 | end
28 |
29 | require File.expand_path(File.dirname(__FILE__) + '/../../../test/test_helper')
30 | ActiveRecord::FixtureSet.create_fixtures(File.dirname(__FILE__) + '/fixtures/',
31 | %i[issue_templates issue_template_settings
32 | global_issue_templates global_issue_templates_projects
33 | note_templates note_visible_roles
34 | global_note_templates])
35 |
36 | module Redmine
37 | class ControllerTest
38 | setup do
39 | Setting.text_formatting = 'textile'
40 | end
41 |
42 | teardown do
43 | Setting.delete_all
44 | Setting.clear_cache
45 | end
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/test/unit/global_issue_templates_test.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../test_helper', __dir__)
2 |
3 | class GlobalIssueTemplatesTest < ActiveSupport::TestCase
4 | fixtures :global_issue_templates, :users, :trackers
5 |
6 | def setup
7 | @global_issue_template = GlobalIssueTemplate.find(1)
8 | end
9 |
10 | def test_truth
11 | assert_kind_of GlobalIssueTemplate, @global_issue_template
12 | end
13 |
14 | def test_template_enabled
15 | enabled = @global_issue_template.enabled?
16 | assert_equal true, enabled, @global_issue_template.enabled?
17 |
18 | @global_issue_template.enabled = false
19 | @global_issue_template.save!
20 | enabled = @global_issue_template.enabled?
21 | assert_equal false, enabled, @global_issue_template.enabled?
22 | end
23 |
24 | def test_sort_by_position
25 | a = GlobalIssueTemplate.new(title: 'Template4', position: 2, tracker_id: 1)
26 | b = GlobalIssueTemplate.new(title: 'Template5', position: 1, tracker_id: 1)
27 | assert_equal [b, a], [a, b].sort
28 | end
29 |
30 | def test_required_attributes_should_be_validated
31 | {
32 | title: ' ',
33 | tracker: nil,
34 | description: " \n\n ",
35 | }.each do |attr, val|
36 | @global_issue_template.reload
37 | @global_issue_template.__send__("#{attr}=", val)
38 |
39 | assert_raises ActiveRecord::RecordInvalid do
40 | @global_issue_template.save!
41 | end
42 |
43 | assert_includes @global_issue_template.errors[attr], 'cannot be blank'
44 | end
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/test/unit/global_note_template_test.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
4 |
5 | class GlobalNoteTemplateTest < ActiveSupport::TestCase
6 | fixtures :projects, :users, :trackers, :roles, :global_note_templates
7 |
8 | def setup; end
9 | def teardown; end
10 |
11 | def test_create_should_require_tracker
12 | template = GlobalNoteTemplate.new(name: 'GlobalNoteTemplate1', visibility: 'open', description: 'description1')
13 | assert_no_difference 'GlobalNoteTemplate.count' do
14 | assert_raises ActiveRecord::RecordInvalid do
15 | template.save!
16 | end
17 | end
18 | assert_equal ['Tracker cannot be blank'], template.errors.full_messages
19 | end
20 |
21 | def test_required_attributes_should_be_validated
22 | template = GlobalNoteTemplate.find(1)
23 | {
24 | name: ' ',
25 | tracker: nil,
26 | description: " \n\n ",
27 | }.each do |attr, val|
28 | template.reload
29 | template.__send__("#{attr}=", val)
30 |
31 | assert_raises ActiveRecord::RecordInvalid do
32 | template.save!
33 | end
34 |
35 | assert_includes template.errors[attr], 'cannot be blank'
36 | end
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/test/unit/issue_template_setting_test.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2 |
3 | class IssueTemplateSettingTest < ActiveSupport::TestCase
4 | fixtures :issue_template_settings, :projects
5 |
6 | def setup
7 | @issue_template_setting = IssueTemplateSetting.find(1)
8 | end
9 |
10 | def test_truth
11 | assert_kind_of IssueTemplateSetting, @issue_template_setting
12 | end
13 |
14 | def test_help_message_enabled
15 | enable_help = @issue_template_setting.enable_help?
16 | assert_equal(true, enable_help)
17 | assert_equal(false, !enable_help)
18 | end
19 |
20 | def test_duplicate_project_setting
21 | templ = IssueTemplateSetting.find_or_create(3)
22 | templ.attributes = { enabled: true, help_message: 'Help!' }
23 | assert templ.save!, 'Failed to save.'
24 |
25 | # test which has the same proect id
26 | templ2 = IssueTemplateSetting.new
27 | templ2.attributes = { project_id: 1, enabled: true, help_message: 'Help!' }
28 | assert !templ2.save, 'Dupricate project should be denied.'
29 | end
30 |
31 | def test_help_message_disabled
32 | # load disabled template setting
33 | issue_template_setting = IssueTemplateSetting.find(2)
34 | enable_help = issue_template_setting.enable_help?
35 | assert_equal(false, enable_help)
36 | end
37 |
38 | def test_find_template_setting
39 | # for Project 6
40 | issue_template_setting = IssueTemplateSetting.find_or_create(6)
41 | assert_kind_of IssueTemplateSetting, issue_template_setting
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/test/unit/issue_template_test.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2 |
3 | class IssueTemplateTest < ActiveSupport::TestCase
4 | fixtures :issue_templates, :projects, :users, :trackers
5 |
6 | def setup
7 | @issue_template = IssueTemplate.find(1)
8 | end
9 |
10 | def test_truth
11 | assert_kind_of IssueTemplate, @issue_template
12 | end
13 |
14 | def test_template_enabled
15 | enabled = @issue_template.enabled?
16 | assert_equal true, enabled, @issue_template.enabled?
17 |
18 | @issue_template.enabled = false
19 | @issue_template.save!
20 | enabled = @issue_template.enabled?
21 | assert_equal false, enabled, @issue_template.enabled?
22 | end
23 |
24 | def test_sort_by_position
25 | a = IssueTemplate.new(title: 'Template1', position: 2, project_id: 1, tracker_id: 1)
26 | b = IssueTemplate.new(title: 'Template2', position: 1, project_id: 1, tracker_id: 1)
27 | assert_equal [b, a], [a, b].sort
28 | end
29 |
30 | def test_is_default
31 | # Reset default data
32 | IssueTemplate.update_all(is_default: false)
33 | assert !@issue_template.is_default?
34 |
35 | @issue_template.is_default = true
36 | @issue_template.save!
37 | assert @issue_template.is_default?
38 |
39 | templates = IssueTemplate.search_by_project(1).search_by_tracker(1).not_default
40 | templates.each do |template|
41 | assert !template.is_default?
42 | end
43 | end
44 |
45 | def test_required_attributes_should_be_validated
46 | {
47 | project_id: nil,
48 | title: ' ',
49 | tracker: nil,
50 | description: " \n\n ",
51 | }.each do |attr, val|
52 | @issue_template.reload
53 | @issue_template.__send__("#{attr}=", val)
54 |
55 | assert_raises ActiveRecord::RecordInvalid do
56 | @issue_template.save!
57 | end
58 |
59 | assert_includes @issue_template.errors[attr], 'cannot be blank'
60 | end
61 | end
62 | end
63 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { fileURLToPath, URL } from 'node:url';
2 |
3 | import { defineConfig } from 'vite';
4 | import vue2 from '@vitejs/plugin-vue2';
5 |
6 | export default defineConfig({
7 | plugins: [
8 | vue2(),
9 | ],
10 | build: {
11 | rollupOptions: {
12 | input: {
13 | issue_templates: '/scripts/issue_templates.js',
14 | },
15 | output: {
16 | entryFileNames: '[name].js',
17 | },
18 | },
19 | outDir: 'assets/javascripts',
20 | },
21 | server: {
22 | port: 5244,
23 | },
24 | resolve: {
25 | alias: {
26 | '^': fileURLToPath(new URL('./scripts', import.meta.url))
27 | }
28 | }
29 | });
30 |
--------------------------------------------------------------------------------