Nuevo plugin Redmine Wiki Lists 0.0.9
This commit is contained in:
parent
9ea9aa9cb1
commit
ae790c4ff9
8 changed files with 1130 additions and 0 deletions
3
plugins/redmine_wiki_lists/lib/redmine_wiki_lists.rb
Executable file
3
plugins/redmine_wiki_lists/lib/redmine_wiki_lists.rb
Executable file
|
@ -0,0 +1,3 @@
|
|||
module RedmineWikiLists
|
||||
# config will be here
|
||||
end
|
44
plugins/redmine_wiki_lists/lib/redmine_wiki_lists/issue_name_link.rb
Executable file
44
plugins/redmine_wiki_lists/lib/redmine_wiki_lists/issue_name_link.rb
Executable file
|
@ -0,0 +1,44 @@
|
|||
module RedmineWikiLists::IssueNameLink
|
||||
Redmine::WikiFormatting::Macros.register do
|
||||
desc 'Make a link of a issue by its subject.'
|
||||
macro :issue_name_link do |obj, args|
|
||||
out = ''
|
||||
|
||||
begin
|
||||
raise '- no parameters' if args.count.zero?
|
||||
raise '- too many parameters' if args.count > 1
|
||||
arg = args.shift
|
||||
arg.strip!
|
||||
|
||||
if arg =~ /\A([^:]*):([^:]*)\z/
|
||||
prj = Project.find_by_identifier($1)
|
||||
prj ||= Project.find_by_name($1)
|
||||
raise "- project:#{$1} is not found." unless prj
|
||||
arg = $2
|
||||
else
|
||||
prj = obj.project
|
||||
end
|
||||
|
||||
if arg =~ /\A([^\|]*)\|([^\|]*)\z/
|
||||
name = $1
|
||||
disp = $2
|
||||
else
|
||||
name = arg
|
||||
disp = arg
|
||||
end
|
||||
|
||||
issue = Issue.where(project_id: prj.id, subject: name).first
|
||||
raise "- issue:#{name} is not found in prj:#{prj.to_s}" unless issue
|
||||
Issue.find_by_subject(name)
|
||||
out << link_to("#{disp}", issue_path(issue))
|
||||
rescue => err_msg
|
||||
raise <<-TEXT.html_safe
|
||||
Parameter error: #{err_msg}<br>
|
||||
Usage: {{issue_name_link([project_name:]issue_subject[|description])}}
|
||||
TEXT
|
||||
end
|
||||
|
||||
out.html_safe
|
||||
end
|
||||
end
|
||||
end
|
225
plugins/redmine_wiki_lists/lib/redmine_wiki_lists/ref_issues.rb
Executable file
225
plugins/redmine_wiki_lists/lib/redmine_wiki_lists/ref_issues.rb
Executable file
|
@ -0,0 +1,225 @@
|
|||
module RedmineWikiLists::RefIssues
|
||||
Redmine::WikiFormatting::Macros.register do
|
||||
desc 'Displays a list of referer issues.'
|
||||
macro :ref_issues do |obj, args|
|
||||
parser = nil
|
||||
|
||||
begin
|
||||
parser = RedmineWikiLists::RefIssues::Parser.new(obj, args, @project)
|
||||
rescue => err_msg
|
||||
attributes = IssueQuery.available_columns
|
||||
msg = <<-TEXT
|
||||
- <br>parameter error: #{err_msg}<br>
|
||||
#{err_msg.backtrace[0]}<br><br>
|
||||
usage: {{ref_issues([option].., [column]..)}}<br>
|
||||
<br>[options]<br>
|
||||
-i=CustomQueryID : specify custom query by id<br>
|
||||
-q=CustomQueryName : specify custom query by name<br>
|
||||
-p[=identifier] : restrict project<br>
|
||||
-f:FILTER[=WORD[|WORD...]] : additional filter<br>
|
||||
-t[=column] : display text<br>
|
||||
-l[=column] : display linked text<br>
|
||||
-c : count issues<br>
|
||||
-0 : no display if no issues
|
||||
<br>[columns]<br> {
|
||||
TEXT
|
||||
|
||||
while attributes
|
||||
attributes[0...5].each do |a|
|
||||
msg += a.name.to_s + ', '
|
||||
end
|
||||
|
||||
attributes = attributes[5..-1]
|
||||
msg += '<br>' if attributes
|
||||
end
|
||||
|
||||
msg += 'cf_* }<br/>'
|
||||
raise msg.html_safe
|
||||
end
|
||||
|
||||
begin
|
||||
unless parser.has_search_conditions? # 検索条件がなにもなかったら
|
||||
# 検索するキーワードを取得する
|
||||
parser.search_words_w << parser.default_words(obj)
|
||||
end
|
||||
|
||||
@query = parser.query @project
|
||||
|
||||
extend SortHelper
|
||||
extend QueriesHelper
|
||||
extend IssuesHelper
|
||||
|
||||
sort_clear
|
||||
sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
|
||||
sort_update(@query.sortable_columns)
|
||||
#@issue_count_by_group = @query.issue_count_by_group
|
||||
|
||||
parser.search_words_s.each do |words|
|
||||
@query.add_filter('subject', '~', words)
|
||||
end
|
||||
|
||||
parser.search_words_d.each do |words|
|
||||
@query.add_filter('description', '~', words)
|
||||
end
|
||||
|
||||
parser.search_words_w.each do |words|
|
||||
@query.add_filter('subjectdescription', '~', words)
|
||||
end
|
||||
|
||||
models =
|
||||
{'tracker' => Tracker,
|
||||
'category' => IssueCategory,
|
||||
'status' => IssueStatus,
|
||||
'assigned_to' => User,
|
||||
'author' => User,
|
||||
'version' => Version,
|
||||
'project' => Project}
|
||||
ids =
|
||||
{'tracker' => 'tracker_id',
|
||||
'category' => 'category_id',
|
||||
'status' => 'status_id',
|
||||
'assigned_to' => 'assigned_to_id',
|
||||
'author' => 'author_id',
|
||||
'version' => 'fixed_version_id',
|
||||
'project' => 'project_id'}
|
||||
attributes =
|
||||
{'tracker' => 'name',
|
||||
'category' => 'name',
|
||||
'status' => 'name',
|
||||
'assigned_to' => 'login',
|
||||
'author' => 'login',
|
||||
'version' => 'name',
|
||||
'project' => 'name'}
|
||||
|
||||
parser.additional_filter.each do |filter_set|
|
||||
filter = filter_set[:filter]
|
||||
operator = filter_set[:operator]
|
||||
values = filter_set[:values]
|
||||
|
||||
if models.has_key?(filter)
|
||||
unless values.nil?
|
||||
tgt_objs = []
|
||||
values.each do |value|
|
||||
tgt_obj = models[filter].find_by(attributes[filter]=>value)
|
||||
raise "- can not resolve '#{value}' in #{models[filter].to_s}.#{attributes[filter]} " if tgt_obj.nil?
|
||||
tgt_objs << tgt_obj.id.to_s
|
||||
end
|
||||
values = tgt_objs
|
||||
end
|
||||
filter = ids[filter]
|
||||
end
|
||||
|
||||
res = @query.add_filter(filter , operator, values)
|
||||
|
||||
if res.nil?
|
||||
filter_str = filter_set[:filter] + filter_set[:operator] + filter_set[:values].join('|')
|
||||
cr_count = 0
|
||||
msg = "- failed add_filter: #{filter_str}<br><br>[FILTER]<br>"
|
||||
|
||||
@query.available_filters.each do |k,f|
|
||||
if cr_count >= 5
|
||||
msg += '<br>'
|
||||
cr_count = 0
|
||||
end
|
||||
|
||||
msg += k.to_s + ', '
|
||||
cr_count += 1
|
||||
end
|
||||
|
||||
models.each do |k, _m|
|
||||
if cr_count >= 5
|
||||
msg += '<br>'
|
||||
cr_count = 0
|
||||
end
|
||||
|
||||
msg += k.to_s + ', '
|
||||
cr_count += 1
|
||||
end
|
||||
|
||||
msg += '<br><br>[OPERATOR]<br>'
|
||||
cr_count = 0
|
||||
|
||||
Query.operators_labels.each do |k, l|
|
||||
if cr_count >= 5
|
||||
msg += '<br>'
|
||||
cr_count = 0
|
||||
end
|
||||
|
||||
msg += k + ':' + l + ', '
|
||||
cr_count += 1
|
||||
end
|
||||
|
||||
msg += '<br>'
|
||||
raise msg.html_safe
|
||||
end
|
||||
end
|
||||
|
||||
@query.column_names = parser.columns unless parser.columns.empty?
|
||||
@issues = @query.issues(order: sort_clause)
|
||||
|
||||
if parser.zero_flag && @issues.size == 0
|
||||
disp = ''
|
||||
elsif parser.only_text || parser.only_link
|
||||
disp = ''
|
||||
atr = parser.only_text if parser.only_text
|
||||
atr = parser.only_link if parser.only_link
|
||||
word = nil
|
||||
|
||||
@issues.each do |issue|
|
||||
if issue.attributes.has_key?(atr)
|
||||
word = issue.attributes[atr].to_s
|
||||
else
|
||||
issue.custom_field_values.each do |cf|
|
||||
if 'cf_'+cf.custom_field.id.to_s == atr || cf.custom_field.name == atr
|
||||
word = cf.value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if word.nil?
|
||||
msg = 'attributes:'
|
||||
|
||||
issue.attributes.each do |a|
|
||||
msg += a.to_s + ', '
|
||||
end
|
||||
|
||||
raise msg.html_safe
|
||||
break
|
||||
end
|
||||
|
||||
disp << ' ' if disp.size!=0
|
||||
|
||||
if parser.only_link
|
||||
disp << link_to("#{word}", issue_path(issue))
|
||||
else
|
||||
disp << textilizable(word, object: issue)
|
||||
end
|
||||
end
|
||||
elsif parser.count_flag
|
||||
disp = @issues.size.to_s
|
||||
else
|
||||
if params[:format] == 'pdf'
|
||||
disp = render(partial: 'issues/list.html', locals: {issues: @issues, query: @query})
|
||||
else
|
||||
if method(:context_menu).parameters.size > 0
|
||||
disp = context_menu(issues_context_menu_path) # < redmine 3.3.x
|
||||
else
|
||||
disp = context_menu.to_s # >= redmine 3.4.0
|
||||
end
|
||||
disp << render(partial: 'issues/list', locals: {issues: @issues, query: @query})
|
||||
end
|
||||
end
|
||||
|
||||
disp.html_safe
|
||||
rescue => err_msg
|
||||
msg = "#{err_msg}"
|
||||
if msg[0] != '-'
|
||||
err_msg.backtrace.each do |backtrace|
|
||||
msg << "<br>#{backtrace}"
|
||||
end
|
||||
end
|
||||
raise msg.html_safe
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
297
plugins/redmine_wiki_lists/lib/redmine_wiki_lists/ref_issues/parser.rb
Executable file
297
plugins/redmine_wiki_lists/lib/redmine_wiki_lists/ref_issues/parser.rb
Executable file
|
@ -0,0 +1,297 @@
|
|||
# To change this template, choose Tools | Templates
|
||||
# and open the template in the editor.
|
||||
|
||||
module RedmineWikiLists
|
||||
module RefIssues
|
||||
class Parser
|
||||
attr_reader :search_words_s, :search_words_d, :search_words_w, :columns,
|
||||
:custom_query_name, :custom_query_id, :additional_filter, :only_text, :only_link, :count_flag, :zero_flag
|
||||
|
||||
def initialize(obj, args = nil, project = nil)
|
||||
parse_args(obj, args, project) if args
|
||||
end
|
||||
|
||||
def parse_args(obj, args, project)
|
||||
args ||= []
|
||||
@project = project
|
||||
@search_words_s = []
|
||||
@search_words_d = []
|
||||
@search_words_w = []
|
||||
@columns = []
|
||||
@restrict_project = nil
|
||||
@additional_filter = []
|
||||
@only_link = nil
|
||||
@only_text = nil
|
||||
@count_flag = nil
|
||||
@zero_flag = nil
|
||||
|
||||
args.each do |arg|
|
||||
arg.strip!
|
||||
arg.gsub!('>', '>')
|
||||
arg.gsub!('<', '<')
|
||||
|
||||
if arg=~/\A\-([^\=:]*)\s*([\=:])\s*(.*)\z/
|
||||
opt = $1.strip
|
||||
sep = $2.strip
|
||||
words = $3.strip
|
||||
elsif arg=~/\A\-([^\=:]*)\z/
|
||||
opt = $1.strip
|
||||
sep = nil
|
||||
words = default_words(obj).join('|')
|
||||
else
|
||||
@columns << get_column(arg)
|
||||
next
|
||||
end
|
||||
|
||||
case opt
|
||||
when 's','sw','Dw','sDw','Dsw'
|
||||
@search_words_s.push words_to_word_array(obj, words)
|
||||
when 'd','dw','Sw','Sdw','dSw'
|
||||
@search_words_d.push words_to_word_array(obj, words)
|
||||
when 'w','sdw'
|
||||
@search_words_w.push words_to_word_array(obj, words)
|
||||
when 'q'
|
||||
if sep
|
||||
@custom_query_name = words
|
||||
else
|
||||
raise "- no CustomQuery name:#{arg}"
|
||||
end
|
||||
when 'i'
|
||||
if sep
|
||||
@custom_query_id = words
|
||||
else
|
||||
raise "- no CustomQuery ID:#{arg}"
|
||||
end
|
||||
when 'p'
|
||||
@restrict_project = sep ? Project.find(words) : project
|
||||
when 'f'
|
||||
if sep
|
||||
filter = ''
|
||||
operator = ''
|
||||
values = nil
|
||||
|
||||
if words =~ /\A([^\s]*)\s+([^\s]*)\z/
|
||||
filter = $1
|
||||
operator = refer_field(obj, $2)
|
||||
elsif words =~ /\A([^\s]*)\s+([^\s]*)\s+(.*)\z/
|
||||
filter = $1
|
||||
operator = refer_field(obj, $2)
|
||||
values = words_to_word_array(obj, $3)
|
||||
elsif words =~ /\A(.*)=(.*)\z/
|
||||
filter = $1
|
||||
operator = '='
|
||||
values = words_to_word_array(obj, $2)
|
||||
else
|
||||
filter = words
|
||||
operator = '='
|
||||
values = default_words(obj)
|
||||
end
|
||||
|
||||
@additional_filter << {:filter=>filter, :operator=>operator, :values=>values}
|
||||
else
|
||||
raise "- no additional filter:#{arg}"
|
||||
end
|
||||
when 't'
|
||||
@only_text = sep ? words : 'subject'
|
||||
when 'l'
|
||||
@only_link = sep ? words : 'subject'
|
||||
when 'c'
|
||||
@count_flag = true
|
||||
when '0'
|
||||
@zero_flag = true
|
||||
else
|
||||
raise "- unknown option:#{arg}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def has_search_conditions?
|
||||
return true if @custom_query_id
|
||||
return true if @custom_query_name
|
||||
return true if @search_words_s.present?
|
||||
return true if @search_words_d.present?
|
||||
return true if @search_words_w.present?
|
||||
return true if @additional_filter.present?
|
||||
false
|
||||
end
|
||||
|
||||
def query(project)
|
||||
# オプションにカスタムクエリがあればカスタムクエリを名前から取得
|
||||
if @custom_query_id
|
||||
@query = IssueQuery.visible.find_by_id(@custom_query_id)
|
||||
raise "- can not find CustomQuery ID:'#{@custom_query_id}'" unless @query
|
||||
elsif @custom_query_name then
|
||||
cond = 'project_id IS NULL'
|
||||
cond << " OR project_id = #{project.id}" if project
|
||||
cond = "(#{cond}) AND name = '#{@custom_query_name}'"
|
||||
@query = IssueQuery.where(cond).where(user_id: User.current.id).first
|
||||
@query = IssueQuery.where(cond).where(visibility: Query::VISIBILITY_PUBLIC).first unless @query
|
||||
raise "- can not find CustomQuery Name:'#{@custom_query_name}'" unless @query
|
||||
else
|
||||
@query = IssueQuery.new(name: '_', filters: {})
|
||||
end
|
||||
|
||||
@query.user = User.current
|
||||
if @restrict_project
|
||||
@query.project = @restrict_project
|
||||
end
|
||||
|
||||
# Queryモデルを拡張
|
||||
overwrite_sql_for_field(@query)
|
||||
@query.available_filters['description'] = {type: :text, order: 8}
|
||||
@query.available_filters['subjectdescription'] = {type: :text, order: 8}
|
||||
@query.available_filters['fixed_version_id'] = {type: :int}
|
||||
@query.available_filters['category_id'] = {type: :int}
|
||||
@query.available_filters['parent_id'] = {type: :int}
|
||||
@query.available_filters['id'] = {type: :int}
|
||||
@query.available_filters['treated'] = {type: :date}
|
||||
|
||||
@query
|
||||
end
|
||||
|
||||
def default_words(obj)
|
||||
words = []
|
||||
|
||||
if obj.class == WikiContent # Wikiの場合はページ名および別名を検索ワードにする
|
||||
words.push(obj.page.title) #ページ名
|
||||
redirects = WikiRedirect.where(redirects_to: obj.page.title) #別名query
|
||||
|
||||
redirects.each do |redirect|
|
||||
words << redirect.title #別名
|
||||
end
|
||||
elsif obj.class == Issue # チケットの場合はチケットsubjectを検索ワードにする
|
||||
words << obj.subject
|
||||
elsif obj.class == Journal && obj.journalized_type == 'Issue'
|
||||
# チケットコメントの場合もチケット番号表記を検索ワードにする
|
||||
words << '#'+obj.journalized_id.to_s
|
||||
end
|
||||
|
||||
words
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_column(name)
|
||||
name_sym = name.to_sym
|
||||
|
||||
IssueQuery.available_columns.each do |col|
|
||||
return name_sym if name_sym == col.name.to_sym
|
||||
end
|
||||
|
||||
return :assigned_to if name_sym == :assigned
|
||||
return :updated_on if name_sym == :updated
|
||||
return :created_on if name_sym == :created
|
||||
return name_sym if name =~ /\Acf_/
|
||||
raise "- unknown column:#{name}"
|
||||
end
|
||||
|
||||
# @todo Стремный патч, который сделан из-за отсутствия поминимания как работать с Query. По сути, надо патчить IssueQuery
|
||||
def overwrite_sql_for_field(query)
|
||||
def query.sql_for_field(field, operator, value, db_table, db_field, is_custom_filter=false)
|
||||
if operator == '~'
|
||||
# monkey patched for ref_issues: originally treat single value -> extend multiple value
|
||||
if db_field=='subjectdescription'
|
||||
sql = '('
|
||||
|
||||
value.each do |v|
|
||||
sql << ' OR ' if sql != '('
|
||||
sql << "LOWER(#{db_table}.subject) LIKE '%#{self.class.connection.quote_string(v.to_s.downcase)}%'"
|
||||
sql << " OR LOWER(#{db_table}.description) LIKE '%#{self.class.connection.quote_string(v.to_s.downcase)}%'"
|
||||
end
|
||||
|
||||
sql << ')'
|
||||
return sql
|
||||
else
|
||||
sql = '('
|
||||
|
||||
value.each do |v|
|
||||
sql << ' OR ' if sql != '('
|
||||
sql << "LOWER(#{db_table}.#{db_field}) LIKE '%#{self.class.connection.quote_string(v.to_s.downcase)}%'"
|
||||
end
|
||||
|
||||
sql << ')'
|
||||
return sql
|
||||
end
|
||||
elsif operator == '=='
|
||||
sql = '('
|
||||
|
||||
value.each do |v|
|
||||
sql << ' OR ' if sql != '('
|
||||
sql << "LOWER(#{db_table}.#{db_field}) = '#{self.class.connection.quote_string(v.to_s.downcase)}'"
|
||||
end
|
||||
|
||||
sql << ')'
|
||||
return sql
|
||||
elsif db_field == 'treated'
|
||||
raise "- too many values for treated" if value.length > 2
|
||||
raise "- too few values for treated" if value.length < 2
|
||||
start_date = value[0]
|
||||
end_date = value[1]
|
||||
if operator =~ /^\d+$/
|
||||
user = operator
|
||||
else
|
||||
user_obj = User.find_by_login(operator)
|
||||
raise "- can not find user <#{operator}>" if user_obj.nil?
|
||||
user = user_obj.id.to_s
|
||||
end
|
||||
|
||||
sql = '('
|
||||
sql << " (issues.author_id = #{user}"
|
||||
sql << " AND (CAST(issues.created_on AS DATE) BETWEEN '#{start_date}' AND '#{end_date}'))"
|
||||
sql << " OR ("
|
||||
sql << " (select count(*) from journals where journalized_type = 'Issue' AND journalized_id = issues.id"
|
||||
sql << " AND journals.user_id = #{user}"
|
||||
sql << " AND (CAST(journals.created_on AS DATE) BETWEEN '#{start_date}' AND '#{end_date}')"
|
||||
sql << " ) > 0"
|
||||
sql << " )"
|
||||
sql << ')'
|
||||
return sql
|
||||
end
|
||||
|
||||
return super(field, operator, value, db_table, db_field, is_custom_filter)
|
||||
end
|
||||
end
|
||||
|
||||
def words_to_word_array(obj, words)
|
||||
words.split('|').collect do |word|
|
||||
word.strip!
|
||||
refer_field(obj, word)
|
||||
end
|
||||
end
|
||||
|
||||
def refer_field(obj, word)
|
||||
if word =~ /\[current_user\]/
|
||||
return User.current.login
|
||||
end
|
||||
|
||||
if word =~ /\[current_user_id\]/
|
||||
return User.current.id.to_s
|
||||
end
|
||||
|
||||
if word =~ /\[current_project_id\]/
|
||||
return @project.id.to_s
|
||||
end
|
||||
|
||||
if word =~ /\[(.*)days_ago\]/
|
||||
return (Date.today - $1.to_i).strftime("%Y-%m-%d")
|
||||
end
|
||||
|
||||
if word =~ /\A\[(.*)\]\z/
|
||||
raise "- can not use reference '#{word}' except for issues." if obj.class != Issue
|
||||
atr = $1
|
||||
|
||||
if obj.attributes.has_key?(atr)
|
||||
word = obj.attributes[atr]
|
||||
else
|
||||
obj.custom_field_values.each do |cf|
|
||||
if 'cf_' + cf.custom_field.id.to_s == atr || cf.custom_field.name == atr
|
||||
word = cf.value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return word.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
187
plugins/redmine_wiki_lists/lib/redmine_wiki_lists/wiki_list.rb
Executable file
187
plugins/redmine_wiki_lists/lib/redmine_wiki_lists/wiki_list.rb
Executable file
|
@ -0,0 +1,187 @@
|
|||
module RedmineWikiLists::WikiList
|
||||
Redmine::WikiFormatting::Macros.register do
|
||||
desc 'Displays a list of wiki pages with text elements (only inside wiki-pages).'
|
||||
macro :wiki_list do |obj, args|
|
||||
# 引数をパース
|
||||
cond = ''
|
||||
joins = ''
|
||||
table_width = ''
|
||||
column_keys = []
|
||||
column_names = []
|
||||
|
||||
begin
|
||||
raise '- no parameters' if args.count.zero?
|
||||
|
||||
args.each do |arg|
|
||||
arg.strip!
|
||||
|
||||
if arg =~ /\A\-([^\=]*)(\=.*)?\z/ # オプション表記発見
|
||||
case $1
|
||||
when 'c' # リストアップの対象を子ページに限定する場合
|
||||
cond << ' AND ' if cond != ''
|
||||
cond << "parent_id = #{obj.page.id}"
|
||||
when 'p' # リストアップの対象を特定の別プロジェクトのWikiに限定する場合
|
||||
if arg=~/\A[^\=]+\=(.*)\z/ then # プロジェクト名を指定
|
||||
name = $1.strip
|
||||
prj = Project.find_by_name(name)
|
||||
cond << ' AND ' if cond != ''
|
||||
cond << "project_id = #{prj.id}"
|
||||
else # プロジェクト名の指定が無い場合は当該WIKIのPJに限定
|
||||
cond << ' AND ' if cond != ''
|
||||
cond << "project_id = #{obj.project.id}"
|
||||
end
|
||||
|
||||
joins << 'INNER JOIN wikis ON wiki_pages.wiki_id = wikis.id'
|
||||
when 'w' # 表の横幅
|
||||
if arg =~ /\A[^\=]+\=(.*)\z/ # 幅を取得
|
||||
width = $1.strip
|
||||
table_width = 'width="' + width + '"'
|
||||
end
|
||||
else
|
||||
raise "- unknown option: #{arg}"
|
||||
end
|
||||
else # オプションでない場合はカラム指定
|
||||
if arg =~ /\A(.*)\|(.*)\|(.*)\z/ # 抽出キーワードと別にカラム表示名と列幅の指定がある場合
|
||||
column_keys.push($1.strip)
|
||||
column_names.push($2.strip + '|' + $3.strip)
|
||||
elsif arg =~ /\A(.*)\|(.*)\z/ # 抽出キーワードと別にカラム表示名の指定がある場合
|
||||
column_keys.push($1.strip)
|
||||
column_names.push($2.strip)
|
||||
else # カラム表示名の指定が無い場合は抽出キーワードをカラム表示名にする
|
||||
column_keys.push(arg.strip)
|
||||
column_names.push(arg.strip)
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue => err_msg
|
||||
msg = <<-TEXT
|
||||
- parameter error: #{err_msg}<br>
|
||||
usage: {{wiki_list([option]*,[column]*)}}<br>
|
||||
[option]<br>
|
||||
-c : search child pages<br>
|
||||
-p=[PROJECT NAME] : restrict search pages by project<br>
|
||||
-w=[WIDTH] : table width<br>
|
||||
[column]<br>
|
||||
+title[| COLUMN_NAME] -> show page title<br>
|
||||
+alias[| COLUMN_NAME] -> show page aliases<br>
|
||||
KEYWORD[| COLUMN_NAME] -> scan KEYWORD and show following words to EOL<br>
|
||||
KEYWORD\\TERMINATOR[| COLUMN_NAME] -> scan KEYWORD and show following words to TERMINATOR
|
||||
TEXT
|
||||
raise msg.html_safe
|
||||
end
|
||||
|
||||
if column_keys.count.zero?
|
||||
column_names.push 'title'
|
||||
column_keys.push '+title'
|
||||
end
|
||||
|
||||
disp = "<table #{table_width}><tr>"
|
||||
|
||||
# カラム名(最初の行)を作成
|
||||
column_names.each do |column_name|
|
||||
if column_name =~ /\A(.*)\|(.*)\z/ then
|
||||
disp << '<th width="'+$2+'">' + $1 + '</th>'
|
||||
else
|
||||
disp << "<th>#{column_name}</th>"
|
||||
end
|
||||
end
|
||||
|
||||
disp << '</tr>'
|
||||
# Wikiページの抽出
|
||||
wiki_pages = WikiPage.joins(joins).where(cond)
|
||||
|
||||
wiki_pages.each do |wiki_page| #---------------- Wikiページ毎の処理
|
||||
next unless wiki_page.visible?
|
||||
# 1ページに抽出キーワードが複数あった場合に複数行表示するため一旦表示行を配列に記憶する
|
||||
lines_by_page = [[]] # 最初は1ページ1行からスタート
|
||||
column_num = 0
|
||||
|
||||
column_keys.each do |column_key| #---------------- カラム毎の処理
|
||||
case column_key
|
||||
when '+title' # Wikiページ名
|
||||
html =
|
||||
link_to(wiki_page.title,
|
||||
controller: 'wiki', action: 'show',
|
||||
project_id: wiki_page.project, id: wiki_page.title)
|
||||
RedmineWikiLists::WikiList.set_lines(lines_by_page, column_num, html)
|
||||
when '+alias' # Wikiページの別名
|
||||
redirects = WikiRedirect.where(wiki_id: wiki_page.wiki_id, redirects_to: wiki_page.title)
|
||||
html = ''
|
||||
|
||||
redirects.each do |redirect|
|
||||
html << '<br>' if html.present?
|
||||
html << redirect.title
|
||||
end
|
||||
|
||||
RedmineWikiLists::WikiList.set_lines(lines_by_page, column_num, html)
|
||||
when '+project' # Wikiページのプロジェクト名
|
||||
RedmineWikiLists::WikiList.set_lines(lines_by_page, column_num, wiki_page.project.to_s)
|
||||
else # それ以外はWikiページの中からキーワードで表示要素を抽出する
|
||||
new_lines = [] # カラムキーワードが抽出される毎にこの変数に表示行を追加する
|
||||
|
||||
if column_key =~ /\A(.*)\\(.*)\z/
|
||||
keyword = Regexp.escape($1.strip)
|
||||
terminator = Regexp.escape($2.strip)
|
||||
matches = wiki_page.text.scan /#{keyword}[\s\S]*?#{terminator}/ # キーワードから終端文字列までを抽出
|
||||
else
|
||||
keyword = Regexp.escape(column_key)
|
||||
terminator = false
|
||||
matches = wiki_page.text.scan /#{keyword}.*\z/ # キーワードから行末までを抽出
|
||||
end
|
||||
|
||||
matches.each do |match| # 抽出されたキーワード毎の処理
|
||||
# キーワードの後ろの文字列を抽出
|
||||
match =~
|
||||
if terminator
|
||||
/\A#{keyword}([\s\S]*)#{terminator}/
|
||||
else
|
||||
/\A#{keyword}(.*)\z/
|
||||
end
|
||||
|
||||
if $1
|
||||
html = textilizable($1.strip) # 前後の空白を覗いてWiki表記解釈
|
||||
# Wikiページ内のこれまでのカラム処理で生成されたlinesに表示内容を記入
|
||||
RedmineWikiLists::WikiList.set_lines(lines_by_page, column_num, html)
|
||||
|
||||
lines_by_page.each do |line|
|
||||
new_lines.push(line.dup) # 本カラムによって生成される行にコピーを追加
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if new_lines.length.zero? # キーワードが1つも抽出されていなかったら空文字を入れておく
|
||||
RedmineWikiLists::WikiList.set_lines(lines_by_page, column_num, '')
|
||||
else # 抽出があった場合は本カラムで作られた新しい表示行をページ表示行にする
|
||||
lines_by_page=new_lines
|
||||
end
|
||||
end # case columnKey
|
||||
|
||||
column_num += 1
|
||||
end # カラム毎の処理
|
||||
|
||||
# 配列に記憶されたページ内の表示内容をHTMLに吐き出す
|
||||
lines_by_page.each do |line|
|
||||
disp << '<tr>'
|
||||
|
||||
line.each do |column|
|
||||
disp << "<td>#{column}</td>"
|
||||
end
|
||||
|
||||
disp << '</tr>'
|
||||
end
|
||||
end # Wikiページ毎の処理
|
||||
|
||||
disp << '</table>'
|
||||
disp.html_safe
|
||||
end
|
||||
end
|
||||
|
||||
# 配列の全ての要素配列のcolumn_num番目にstrを書きこむ
|
||||
def set_lines(lines, column_num, str)
|
||||
lines.each do |line|
|
||||
line[column_num] = str
|
||||
end
|
||||
end
|
||||
|
||||
module_function :set_lines
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue