Skip to content
This repository has been archived by the owner on Jan 23, 2021. It is now read-only.

Commit

Permalink
Merge pull request #305 from akiko-pusu/develop
Browse files Browse the repository at this point in the history
Release v1.0.0
  • Loading branch information
akiko-pusu authored Jan 24, 2020
2 parents 489e4ae + 743806f commit d0fbbe7
Show file tree
Hide file tree
Showing 54 changed files with 2,549 additions and 1,174 deletions.
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
inherit_from: .rubocop_todo.yml
AllCops:
TargetRubyVersion: 2.3
Exclude:
- 'db/**/*'

11 changes: 2 additions & 9 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ Metrics/AbcSize:
# Offense count: 1
# Configuration parameters: CountComments.
Metrics/ClassLength:
Max: 200
Max: 200
Exclude:
- 'spec/**/*'
- 'test/**/*'

# "Line is too long"を無効
Metrics/LineLength:
Layout/LineLength:
Enabled: false

# Offense count: 1
Expand Down Expand Up @@ -50,10 +50,3 @@ EndOfLine:

Metrics/ModuleLength:
Max: 120

# NOTE: Follow Redmine's model definition
Rails/ApplicationRecord:
Enabled: false

Rails/InverseOf:
Enabled: false
9 changes: 9 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ RUN apt-get install -qq -y \
sqlite3 default-libmysqlclient-dev
RUN apt-get install -qq -y build-essential libc6-dev

# for e2e test env
RUN sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
RUN apt-get update && apt-get install -y google-chrome-stable
RUN google-chrome --version | perl -pe 's/([^0-9]+)([0-9]+)(\.[0-9]+).+/$2/g' > chrome-version-major
RUN curl https://chromedriver.storage.googleapis.com/LATEST_RELEASE_`cat chrome-version-major` > chrome-version
RUN curl -O -L http://chromedriver.storage.googleapis.com/`cat chrome-version`/chromedriver_linux64.zip && rm chrome-version*
RUN unzip chromedriver_linux64.zip && mv chromedriver /usr/local/bin

RUN cd /tmp && svn co http://svn.redmine.org/redmine/trunk redmine
WORKDIR /tmp/redmine

Expand Down
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,18 @@ If you have any requests, bug reports, please use GitHub issues. <https://github

## Changelog

### 1.0.0

NOTE: **Migration is required**.
Since ``Support Built-In / Custom Fields`` is an experimental feature, please **be careful** if you hope to try it.

* Feature: Add feature to show template usage / example (#303)
* Feature: Support Built-In / Custom Fields (#304)
* Rewrite JavaSctipt code from jQuery into plain JavaScript.

And some browsers may not work fine because Support Built-In / Custom Fields feature uses Vue.js for frontend.
So feedback, issue report, suggestion highly appreciate!

### 0.3.7

This is bugfix release to prevent the conflict with other plugins.
Expand Down Expand Up @@ -331,7 +343,7 @@ By default, use chrome as a webdriver. If you set environment variable
'DRIVER' to 'headless', headless_chrome is used.

```bash
% bundle exec rspec -I plugins/redmine_issue_templates/spec --format documentation plugins/redmine_issue_templates/spec/ DRIVER='headless'
% DRIVER='headless' bundle exec rspec -I plugins/redmine_issue_templates/spec --format documentation plugins/redmine_issue_templates/spec/
```

### License
Expand Down
114 changes: 107 additions & 7 deletions app/controllers/concerns/issue_templates_common.rb
Original file line number Diff line number Diff line change
@@ -1,29 +1,62 @@
# frozen_string_literal: true

module Concerns
module IssueTemplatesCommon
extend ActiveSupport::Concern

class InvalidTemplateFormatError < StandardError; end

included do
before_action :log_action, only: [:destroy]

# logging action
def log_action
logger.info "[#{self.class}] #{action_name} called by #{User.current.name}" if logger
logger&.info "[#{self.class}] #{action_name} called by #{User.current.name}"
end

def plugin_setting
Setting.plugin_redmine_issue_templates
end

def apply_all_projects?
plugin_setting['apply_global_template_to_all_projects'].to_s == 'true'
end

def apply_template_when_edit_issue?
plugin_setting['apply_template_when_edit_issue'].to_s == 'true'
end

def builtin_fields_enabled?
plugin_setting['enable_builtin_fields'].to_s == 'true'
end
end

def orphaned_templates
render partial: 'common/orphaned', locals: { orphaned_templates: orphaned }
def load_selectable_fields
tracker_id = params[:tracker_id]
project_id = params[:project_id]
render plain: {} && return if tracker_id.blank?

custom_fields = core_fields_map_by_tracker_id(tracker_id: tracker_id, project_id: project_id).merge(custom_fields_map_by_tracker_id(tracker_id))
render plain: { custom_fields: custom_fields }.to_json
end

def plugin_setting
@plugin_setting ||= Setting.plugin_redmine_issue_templates
def orphaned_templates
render partial: 'common/orphaned', locals: { orphaned_templates: orphaned }
end

def apply_all_projects?
plugin_setting['apply_global_template_to_all_projects'].to_s == 'true'
end

def checklists
template_params[:checklists].blank? ? [] : template_params[:checklists]
template_params[:checklists].presence || []
end

def builtin_fields_json
value = template_params[:builtin_fields].blank? ? {} : JSON.parse(template_params[:builtin_fields])
return value if value.is_a?(Hash)

raise InvalidTemplateFormatError
end

def checklist_enabled?
Expand All @@ -34,9 +67,76 @@ def checklist_enabled?

def valid_params
# convert attribute name and data for checklist plugin supporting
attributes = template_params.except(:checklists)
attributes = template_params.except(:checklists, :builtin_fields)
attributes[:builtin_fields_json] = builtin_fields_json if builtin_fields_enabled?
attributes[:checklist_json] = checklists.to_json if checklist_enabled?
attributes
end

def destroy
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
end

#
# TODO: Code should be refactored
#
def core_fields_map_by_tracker_id(tracker_id: nil, project_id: nil)
return {} unless builtin_fields_enabled?

fields = %w[status_id priority_id]

# exclude "description"
tracker = Tracker.find_by(id: tracker_id)
fields += tracker.core_fields.reject { |field| field == 'description' } if tracker.present?
fields.reject! { |field| %w[category_id fixed_version_id].include?(field) } if project_id.blank?

map = {}

fields.each do |field|
id = "issue_#{field}"
name = I18n.t('field_' + field.gsub(/_id$/, ''))
value = { name: name, core_field_id: id }
if field == 'priority_id'
value[:possible_values] = IssuePriority.active.pluck(:name)
value[:field_format] = 'list'
end

if field == 'status_id' && tracker.present?
value[:possible_values] = tracker.issue_statuses.pluck(:name)
value[:field_format] = 'list'
end

value[:field_format] = 'date' if %(start_date due_date).include?(field)

value[:field_format] = 'ratio' if field == 'done_ratio'

map[id] = value
end
map
rescue StandardError => e
logger&.info "core_fields_map_by_tracker_id failed due to this error: #{e.message}"
{}
end

def custom_fields_map_by_tracker_id(tracker_id = nil)
return {} unless builtin_fields_enabled?
return {} if tracker_id.blank?

tracker = Tracker.find_by(id: tracker_id)
ids = tracker&.custom_field_ids || []
fields = IssueCustomField.where(id: ids)
map = {}
fields.each do |field|
id = "issue_custom_field_values_#{field.id}"
attributes = field.attributes

attributes = attributes.merge(possible_values: field.possible_values_options.map { |value| value[0] }) if field.format.name == 'bool'
map[id] = attributes
end
map
rescue StandardError => e
logger&.info "core_fields_map_by_tracker_id failed due to this error: #{e.message}"
{}
end
end
end
2 changes: 1 addition & 1 deletion app/controllers/concerns/project_templates_common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module Concerns
module ProjectTemplatesCommon
extend ActiveSupport::Concern
included do
before_action :find_user, :find_project, :authorize, except: %i[preview load]
before_action :find_user, :find_project, :authorize, except: %i[preview load load_selectable_fields]
before_action :find_object, only: %i[show edit update destroy]
accept_api_auth :index, :list_templates, :load
end
Expand Down
40 changes: 35 additions & 5 deletions app/controllers/global_issue_templates_controller.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

# noinspection RubocopInspection
class GlobalIssueTemplatesController < ApplicationController
layout 'base'
Expand Down Expand Up @@ -30,8 +32,16 @@ def new
end

def create
@global_issue_template = GlobalIssueTemplate.new(valid_params)
@global_issue_template = GlobalIssueTemplate.new
@global_issue_template.author = User.current

begin
@global_issue_template.safe_attributes = valid_params
rescue ActiveRecord::SerializationTypeMismatch, Concerns::IssueTemplatesCommon::InvalidTemplateFormatError
flash[:error] = I18n.t(:builtin_fields_should_be_valid_json, default: 'Please enter a valid JSON fotmat string.')
render render_form_params.merge(action: :new)
return
end
save_and_flash(:notice_successful_create, :new) && return
end

Expand All @@ -40,15 +50,29 @@ def show
end

def update
@global_issue_template.safe_attributes = valid_params
begin
@global_issue_template.safe_attributes = valid_params
rescue ActiveRecord::SerializationTypeMismatch, Concerns::IssueTemplatesCommon::InvalidTemplateFormatError
flash[:error] = I18n.t(:builtin_fields_should_be_valid_json, default: 'Please enter a valid JSON fotmat string.')
render render_form_params.merge(action: :show)
return
end

save_and_flash(:notice_successful_update, :show)
end

def edit
# Change from request.post to request.patch for Rails4.
return unless request.patch? || request.put?

@global_issue_template.safe_attributes = valid_params
begin
@global_issue_template.safe_attributes = valid_params
rescue ActiveRecord::SerializationTypeMismatch
flash[:error] = I18n.t(:builtin_fields_should_be_valid_json, default: 'Please enter a valid JSON fotmat string.')
render render_form_params.merge(action: :show)
return
end

save_and_flash(:notice_successful_update, :show)
end

Expand Down Expand Up @@ -106,14 +130,20 @@ def save_and_flash(message, action_on_failure)
def template_params
params.require(:global_issue_template)
.permit(:title, :tracker_id, :issue_title, :description, :note, :is_default, :enabled,
:author_id, :position, project_ids: [], checklists: [])
:author_id, :position, :related_link, :link_title, :builtin_fields,
project_ids: [], checklists: [])
end

def render_form_params
trackers = Tracker.all
projects = Project.all
tracker_id = @global_issue_template.tracker_id
custom_fields = core_fields_map_by_tracker_id(tracker_id: tracker_id)
.merge(custom_fields_map_by_tracker_id(tracker_id)).to_json

{ layout: !request.xhr?,
locals: { checklist_enabled: checklist_enabled?, trackers: trackers, apply_all_projects: apply_all_projects?,
issue_template: @global_issue_template, projects: projects } }
issue_template: @global_issue_template, projects: projects, custom_fields: custom_fields.to_s,
builtin_fields_enable: builtin_fields_enabled? } }
end
end
36 changes: 30 additions & 6 deletions app/controllers/issue_templates_controller.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# frozen_string_literal: true

# noinspection ALL
class IssueTemplatesController < ApplicationController
layout 'base'
Expand Down Expand Up @@ -49,15 +50,30 @@ def new
end

def create
@issue_template = IssueTemplate.new(valid_params)
@issue_template = IssueTemplate.new
@issue_template.author = User.current
@issue_template.project = @project

begin
@issue_template.safe_attributes = valid_params
rescue ActiveRecord::SerializationTypeMismatch, Concerns::IssueTemplatesCommon::InvalidTemplateFormatError
flash[:error] = I18n.t(:builtin_fields_should_be_valid_json, default: 'Please enter a valid JSON fotmat string.')
render render_form_params.merge(action: :new)
return
end

# TODO: Should return validation error in case mandatory fields are blank.
save_and_flash(:notice_successful_create, :new) && return
end

def update
@issue_template.safe_attributes = valid_params
begin
@issue_template.safe_attributes = valid_params
rescue ActiveRecord::SerializationTypeMismatch, Concerns::IssueTemplatesCommon::InvalidTemplateFormatError
flash[:error] = I18n.t(:builtin_fields_should_be_valid_json, default: 'Please enter a valid JSON fotmat string.')
render render_form_params.merge(action: :show)
return
end
save_and_flash(:notice_successful_update, :show)
end

Expand All @@ -70,7 +86,9 @@ def load
else
IssueTemplate.find(issue_template_id)
end
render plain: issue_template.template_json
rendered_json = builtin_fields_enabled? ? issue_template.template_json : issue_template.template_json(except: 'builtin_fields_json')

render plain: rendered_json
end

# update pulldown
Expand Down Expand Up @@ -194,16 +212,22 @@ def inherit_templates

def template_params
params.require(:issue_template).permit(:tracker_id, :title, :note, :issue_title, :description, :is_default,
:enabled, :author_id, :position, :enabled_sharing, checklists: [])
:enabled, :author_id, :position, :enabled_sharing,
:related_link, :link_title, :builtin_fields,
checklists: [])
end

def templates_exist?
@inherit_templates.present? || @issue_templates.present?
end

def render_form_params
child_project_used_count = template&.used_projects&.count
custom_fields = core_fields_map_by_tracker_id(tracker_id: template&.tracker_id, project_id: @project.id)
.merge(custom_fields_map_by_tracker_id(template&.tracker_id)).to_json

{ layout: !request.xhr?,
locals: { issue_template: template, project: @project,
checklist_enabled: checklist_enabled? } }
locals: { issue_template: template, project: @project, child_project_used_count: child_project_used_count,
checklist_enabled: checklist_enabled?, custom_fields: custom_fields.to_s, builtin_fields_enable: builtin_fields_enabled? } }
end
end
Loading

0 comments on commit d0fbbe7

Please sign in to comment.