Skip to content

Commit

Permalink
Merge pull request #65 from onozaty/develop/2.7.0
Browse files Browse the repository at this point in the history
Develop 2.7.0
  • Loading branch information
onozaty authored Jul 5, 2020
2 parents 18872a9 + 9298d08 commit 8ea7cce
Show file tree
Hide file tree
Showing 24 changed files with 354 additions and 53 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.vagrant/
15 changes: 11 additions & 4 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,34 @@ bundle exec rake redmine:plugins:migrate RAILS_ENV=production

![Screenshot of new](screenshots/new.en.png)

「Path pattern」は正規表現で指定します。ページのパスと一致すると、コードを挿入して実行します。
「Path pattern」と「Project pattern」を使って、対象のページを指定します。
どちらも指定されていなかった場合には、全てのページが対象になります。

「Path pattern」は正規表現でパスを指定します。
「Path pattern」が設定されていた場合、ページのパスが一致しないと、コードが挿入されません。

以下は設定例です。
* `.*` : 全てのページ
* `/issues$` : チケット一覧
* `/issues/[0-9]+` : 個々のチケットの内容表示画面

「Project pattern」は正規表現でプロジェクトの識別子を指定します。v2.7.0にて追加された項目になります。
「Project pattern」が設定されていた場合、プロジェクトが一致しないと、コードが挿入されません。

「Insertion position」は、コードの挿入位置です。v1.2.0にて追加された項目になります。
* 「Head of all pages」 : 全てのページのヘッダ部分。(v1.2.0より前のバージョンと同じ位置)
* 「Bottom of issue form」 : チケットの入力欄の下部。<br>
チケットの入力欄は、トラッカーやステータスを変えると再構成されますが、「Bottom of issue form」を指定しておくと再構成された際に再度実行されるので、入力欄に対する処理はこれを指定すると便利です。
* 「Bottom of issue detail」 : チケットの詳細表示の下部。

該当ページにコードの挿入位置に該当する箇所が無かった場合、コードは埋め込まれません。たとえば、「Path pattern」`.*`で全ページを指定しても、「Insertion position」に「Bottom of issue detail」を指定していると、チケットの詳細表示画面でしか実行されないことになります
該当ページにコードの挿入位置に該当する箇所が無かった場合、コードは埋め込まれません。たとえば、「Path pattern」と「Project pattern」の設定が無く、全ページが対象となっても、「Insertion position」に「Bottom of issue detail」を指定していると、チケットの詳細表示画面でしかコードが実行されないことになります

「Type」にてコードの種類(「JavaScript」、「CSS」または「HTML」)を選択し、「Code」に実際のコードを入力します。

「Comment」にはカスタマイズに対する概要を記載できます。ここで入力した内容は、一覧表示で表示されます。(Commentが入力されていればComment、Commentが入力されていない場合はCodeが一覧に表示)

「Create」ボタン押下で新規カスタマイズの追加が完了です。

「Path pattern」に一致したページで指定のコードが実行され、画面がカスタマイズされるようになります。
「Path pattern」と「Project pattern」に一致したページで、「Insertion position」で指定した位置にコードが埋め込まれ、画面がカスタマイズされるようになります。

![Screenshot of example](screenshots/example.en.png)

Expand Down
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,19 @@ Click "New view customize" and enter items.

![Screenshot of new](screenshots/new.en.png)

"Path pattern" is specified by regular expression. If it matches the path of the page, insert the code and execute it.
Use "Path pattern" and "Project pattern" to specify the target page.
If neither is set, all pages will be targeted.

"Path pattern" is a regular expression to specify the page path.
If a path pattern was set, the code will not be inserted if the path of the page do not match.

The following is an example.
* `.*` : All pages
* `/issues$` : Issue list
* `/issues/[0-9]+` : Issue detail page

"Project pattern" is a regular expression to specify the project identifier. It becomes item added in v2.7.0.
If the project pattern was set, the code will not be inserted if the current project do not match.

"Insertion position" is the code insertion position. It becomes item added in v1.2.0.

* "Head of all pages" (The same position as the version before v1.2.0)
Expand All @@ -51,7 +57,7 @@ Issue input fields are reconstructed when trackers or statuses are changed. If "
* "Bottom of issue detail"

If there is no part corresponding to the insertion position of the code on the page, the code is not insert.
For example, even if you specify `.*` in "Path pattern", if "Bottom of issue detail" is specified for "Insertion position", it will be executed only on the issue detail page.
For example, even if there are no "Path pattern" and "Project pattern" settings and all pages are targeted, if "Bottom of issue detail" is specified for "Insertion position", it will be executed only on the issue detail page.

In "Type", select the type of code ("JavaScript", "CSS" or "HTML") and enter the actual code in "Code".

Expand All @@ -61,7 +67,7 @@ If "Comment" is not entered, "Code" is displayed in the list.

Addition is completed by clicking "Create" button.

The specified code is executed on the page that matches "Path pattern", and the page will be customized.
On the page that matches the "Path pattern" and "Project pattern", the code will be embedded at the position specified in "Insertion position", and the page will be customized.

![Screenshot of example](screenshots/example.en.png)

Expand Down
10 changes: 10 additions & 0 deletions Vagrantfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Vagrant.configure("2") do |config|
config.vm.box = "onozaty/redmine-4.1"
config.vm.network "private_network", ip: "192.168.33.10"

config.vm.synced_folder ".", "/var/lib/redmine/plugins/view_customize", mount_options: ['dmode=777','fmode=755']

config.vm.provider "virtualbox" do |vb|
vb.memory = "1024"
end
end
2 changes: 1 addition & 1 deletion app/controllers/view_customizes_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,6 @@ def find_view_customize

def view_customize_params
params.require(:view_customize)
.permit(:path_pattern, :customize_type, :code, :is_enabled, :is_private, :insertion_position, :comments)
.permit(:path_pattern, :project_pattern, :customize_type, :code, :is_enabled, :is_private, :insertion_position, :comments)
end
end
21 changes: 16 additions & 5 deletions app/models/view_customize.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
class ViewCustomize < ActiveRecord::Base
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'

validates_presence_of :path_pattern
validates_length_of :path_pattern, :maximum => 255
validates_length_of :project_pattern, :maximum => 255

validates_presence_of :code

Expand Down Expand Up @@ -61,11 +61,22 @@ def available?(user=User.current)
end

def valid_pattern
begin
Regexp.compile(path_pattern)
rescue
errors.add(:path_pattern, :invalid)
if path_pattern.present?
begin
Regexp.compile(path_pattern)
rescue
errors.add(:path_pattern, :invalid)
end
end

if project_pattern.present?
begin
Regexp.compile(project_pattern)
rescue
errors.add(:project_pattern, :invalid)
end
end

end

def initialize(attributes=nil, *args)
Expand Down
10 changes: 9 additions & 1 deletion app/views/view_customizes/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@
<div class="box tabular">
<p>
<%= form.text_field :path_pattern,
:size => 100, :required => true %>
:size => 100 %>
<em class="info">
<%= l(:text_path_pattern_info) %><br />
<%= l(:text_path_pattern_match_info) %>
</em>
</p>
<p>
<%= form.text_field :project_pattern,
:size => 100 %>
<em class="info">
<%= l(:text_project_pattern_info) %><br />
<%= l(:text_project_pattern_match_info) %>
</em>
</p>
<p>
<%= form.select :insertion_position,
options_for_select(
Expand Down
2 changes: 2 additions & 0 deletions app/views/view_customizes/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<tr>
<%= sort_header_tag('id', :caption => '#') %>
<%= sort_header_tag('path_pattern', :caption => l(:field_path_pattern)) %>
<%= sort_header_tag('project_pattern', :caption => l(:field_project_pattern)) %>
<%= sort_header_tag('insertion_position', :caption => l(:field_insertion_position)) %>
<%= sort_header_tag('customize_type', :caption => l(:field_customize_type)) %>
<%= sort_header_tag('comments', :caption => l(:field_comments) + ' / ' + l(:field_code)) %>
Expand All @@ -27,6 +28,7 @@
<tr class="<%= cycle('odd', 'even') %><%= ' disable' unless view_customize.is_enabled %><%= ' private' unless !view_customize.is_private %>">
<td class="id"><%= link_to view_customize.id, view_customize_path(view_customize.id) %></td>
<td class="path"><%=h view_customize.path_pattern %></td>
<td class="project_pattern"><%=h view_customize.project_pattern%></td>
<td class="insertion_position"><%=h l(view_customize.insertion_position_label) %></td>
<td><%=h l(view_customize.customize_type_label) %></td>
<td class="comments"><%=h view_customize.comments.present? ? view_customize.comments : truncate(view_customize.code, :length => 80) %></td>
Expand Down
4 changes: 4 additions & 0 deletions app/views/view_customizes/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
<th><%=h l(:field_path_pattern) %></th>
<td><%=h @view_customize.path_pattern %></td>
</tr>
<tr>
<th><%=h l(:field_project_pattern) %></th>
<td><%=h @view_customize.project_pattern %></td>
</tr>
<tr>
<th><%=h l(:field_insertion_position) %></th>
<td><%=h l(@view_customize.insertion_position_label) %></td>
Expand Down
6 changes: 4 additions & 2 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# English strings go here for Rails i18n
en:
label_view_customize: "View customize"
label_view_customize_plural: "View customizes"
Expand All @@ -12,6 +11,7 @@ en:
label_insertion_position_issue_form: "Bottom of issue form"
label_insertion_position_issue_show: "Bottom of issue detail"
field_path_pattern: "Path pattern"
field_project_pattern: "Project pattern"
field_insertion_position: "Insertion position"
field_customize_type: "Type"
field_code: "Code"
Expand All @@ -20,5 +20,7 @@ en:
field_is_enabled: "Enabled"
field_author: "Author"
text_path_pattern_info: "Path pattern is specified with a regular expression. (ex. /issues/[0-9]+)"
text_path_pattern_match_info: "If there is a match with the path of the page, insert the following code and execute it."
text_path_pattern_match_info: "If a path pattern was set, the code will not be inserted if the path of the page do not match."
text_project_pattern_info: "Project (identifier) pattern is specified with a regular expression. (ex. sample-proj-a|sample-proj-b)"
text_project_pattern_match_info: "If the project pattern was set, the code will not be inserted if the current project do not match."
option_create_api_access_key: "Automatically create API access key"
9 changes: 6 additions & 3 deletions config/locales/ja.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ ja:
label_insertion_position_issue_form: "チケット入力欄の下"
label_insertion_position_issue_show: "チケット詳細の下"
field_path_pattern: "パスのパターン"
field_project_pattern: "プロジェクトのパターン"
field_insertion_position: "挿入位置"
field_customize_type: "種別"
field_code: "コード"
field_comments: "コメント"
field_is_private: "プライベート"
field_is_enabled: "有効"
field_author: "作成者"
text_path_pattern_info: "パスのパターンは正規表現で指定します。 (例 /issues/[0-9]+)"
text_path_pattern_match_info: "ページのパスが一致した場合、以下のコードを挿入し、実行します。"
option_create_api_access_key: "APIアクセスキーを自動的に作成する"
text_path_pattern_info: "パスを正規表現で指定します。(例 /issues/[0-9]+)"
text_path_pattern_match_info: "パスのパターンが設定されていた場合、ページのパスが一致しないと、コードが挿入されません。"
text_project_pattern_info: "プロジェクト(識別子)を正規表現で指定します。 (例 sample-proj-a|sample-proj-b)"
text_project_pattern_match_info: "プロジェクトのパターンが設定されていた場合、プロジェクトが一致しないと、コードが挿入されません。"
option_create_api_access_key: "APIアクセスキーを自動的に作成する"
8 changes: 5 additions & 3 deletions config/locales/zh.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# Chinese strings go here for Rails i18n
zh:
label_view_customize: "自定义视图"
label_view_customize_plural: "自定义视图"
Expand All @@ -12,13 +11,16 @@ zh:
label_insertion_position_issue_form: "问题(issue)表单底部"
label_insertion_position_issue_show: "问题(issue)详情页面"
field_path_pattern: "路径表达式"
field_project_pattern: "项目表达式"
field_insertion_position: "嵌入位置"
field_customize_type: "类别"
field_code: "代码"
field_comments: "注释"
field_is_private: "私有"
field_is_enabled: "启用"
field_author: "作者"
text_path_pattern_info: "路径表达式是用于匹配特定页面链接的正则表达式。(例如 /issues/[0-9]+)"
text_path_pattern_match_info: "如果页面和表达式匹配,则在页面中嵌入下面的代码。"
text_path_pattern_info: "路径表达式使用正则表达式指定。(例如 /issues/[0-9]+)"
text_path_pattern_match_info: "如果设置了路径表达式,则如果页面的路径不匹配,则不会插入代码。"
text_project_pattern_info: "项目(标识符)表达式使用正则表达式指定。 (例如 sample-proj-a|sample-proj-b)"
text_project_pattern_match_info: "如果设置了项目表达式,则如果当前项目不匹配,则不会插入代码。"
option_create_api_access_key: "自动创建API密钥(API access key)"
11 changes: 11 additions & 0 deletions db/migrate/006_add_project_pattern_to_view_customizes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class AddProjectPatternToViewCustomizes < ActiveRecord::CompatibleLegacyMigration.migration_class
def up
add_column :view_customizes, :project_pattern, :string, :null => false, :default => ""
end

def down
remove_column :view_customizes, :project_pattern
end
end


Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class ChangePathPatternDefaultOnViewCustomizes < ActiveRecord::CompatibleLegacyMigration.migration_class
def change
change_column_default :view_customizes, :path_pattern, from: nil, to: ""
change_column_default :view_customizes, :comments, from: nil, to: ""
change_column_null :view_customizes, :comments, false, ""
end
end
2 changes: 1 addition & 1 deletion init.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
name 'View Customize plugin'
author 'onozaty'
description 'View Customize plugin for Redmine'
version '2.6.0'
version '2.7.0'
url 'https://github.com/onozaty/redmine-view-customize'
author_url 'https://github.com/onozaty'

Expand Down
47 changes: 29 additions & 18 deletions lib/view_customize/view_hook.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,56 +3,67 @@
module RedmineViewCustomize
class ViewHook < Redmine::Hook::ViewListener
def view_layouts_base_html_head(context={})

path = Redmine::CodesetUtil.replace_invalid_utf8(context[:request].path_info);

html = "\n<!-- [view customize plugin] path:#{path} -->\n"
html << stylesheet_link_tag("view_customize", plugin: "view_customize")
html << "<script type=\"text/javascript\">\n//<![CDATA[\n"
html << "ViewCustomize = { context: #{create_view_customize_context(context).to_json} };"
html << "\n//]]>\n</script>"
html << "\n//]]>\n</script>\n"

html << create_view_customize_html(path, ViewCustomize::INSERTION_POSITION_HTML_HEAD)
html << create_view_customize_html(context, ViewCustomize::INSERTION_POSITION_HTML_HEAD)

return html
end

def view_issues_form_details_bottom(context={})

path = Redmine::CodesetUtil.replace_invalid_utf8(context[:request].path_info);
return create_view_customize_html(path, ViewCustomize::INSERTION_POSITION_ISSUE_FORM)
return "\n" + create_view_customize_html(context, ViewCustomize::INSERTION_POSITION_ISSUE_FORM)
end

def view_issues_show_details_bottom(context={})

html = "<script type=\"text/javascript\">\n//<![CDATA[\n"
html = "\n<script type=\"text/javascript\">\n//<![CDATA[\n"
html << "ViewCustomize.context.issue = { id: #{context[:issue].id} };"
html << "\n//]]>\n</script>"
html << "\n//]]>\n</script>\n"

path = Redmine::CodesetUtil.replace_invalid_utf8(context[:request].path_info);
html << create_view_customize_html(path, ViewCustomize::INSERTION_POSITION_ISSUE_SHOW)
html << create_view_customize_html(context, ViewCustomize::INSERTION_POSITION_ISSUE_SHOW)

return html
end

private

def create_view_customize_html(path, insertion_position)
def create_view_customize_html(context, insertion_position)

view_customize_html_parts = match_customize(path, insertion_position).map {|item|
view_customize_html_parts = match_customize(context, insertion_position).map {|item|
to_html(item)
}
return view_customize_html_parts.join("\n").html_safe
return view_customize_html_parts.join("\n").html_safe + "\n"

end

def match_customize(path, insertion_position)
def match_customize(context, insertion_position)
path = Redmine::CodesetUtil.replace_invalid_utf8(context[:request].path_info)
project = context[:project].identifier if context[:project]

ViewCustomize.all.select {|item|
item.available? \
&& item.insertion_position == insertion_position \
&& path =~ Regexp.new(item.path_pattern)
}
ViewCustomize.all.order(:id).select {|item| target_customize?(path, project, insertion_position, item)}
end

def target_customize?(path, project, insertion_position, item)
return false unless item.available?
return false unless item.insertion_position == insertion_position

if item.path_pattern.present?
return false unless path =~ Regexp.new(item.path_pattern)
end

if item.project_pattern.present?
return false unless project.present?
return false unless project =~ Regexp.new(item.project_pattern)
end

return true
end

def to_html(view_customize)
Expand Down
Binary file modified screenshots/detail.en.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified screenshots/list_edit.en.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified screenshots/list_new.en.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified screenshots/new.en.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 8ea7cce

Please sign in to comment.