Revision 1298:4f746d8966dd lib/redmine
| lib/redmine/about.rb | ||
|---|---|---|
| 1 |
module Redmine |
|
| 2 |
class About |
|
| 3 |
def self.print_plugin_info |
|
| 4 |
plugins = Redmine::Plugin.registered_plugins |
|
| 5 |
|
|
| 6 |
if !plugins.empty? |
|
| 7 |
column_with = plugins.map {|internal_name, plugin| plugin.name.length}.max
|
|
| 8 |
puts "\nAbout your Redmine plugins" |
|
| 9 |
|
|
| 10 |
plugins.each do |internal_name, plugin| |
|
| 11 |
puts sprintf("%-#{column_with}s %s", plugin.name, plugin.version)
|
|
| 12 |
end |
|
| 13 |
end |
|
| 14 |
end |
|
| 15 |
end |
|
| 16 |
end |
|
| lib/redmine/access_control.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/access_keys.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/activity.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/activity/fetcher.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/ciphering.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/codeset_util.rb | ||
|---|---|---|
| 1 |
require 'iconv' |
|
| 1 |
if RUBY_VERSION < '1.9' |
|
| 2 |
require 'iconv' |
|
| 3 |
end |
|
| 2 | 4 |
|
| 3 | 5 |
module Redmine |
| 4 | 6 |
module CodesetUtil |
| ... | ... | |
| 100 | 102 |
end |
| 101 | 103 |
encodings = Setting.repositories_encodings.split(',').collect(&:strip)
|
| 102 | 104 |
encodings.each do |encoding| |
| 103 |
begin |
|
| 104 |
return Iconv.conv('UTF-8', encoding, str)
|
|
| 105 |
rescue Iconv::Failure |
|
| 106 |
# do nothing here and try the next encoding |
|
| 105 |
if str.respond_to?(:force_encoding) |
|
| 106 |
begin |
|
| 107 |
str.force_encoding(encoding) |
|
| 108 |
utf8 = str.encode('UTF-8')
|
|
| 109 |
return utf8 if utf8.valid_encoding? |
|
| 110 |
rescue |
|
| 111 |
# do nothing here and try the next encoding |
|
| 112 |
end |
|
| 113 |
else |
|
| 114 |
begin |
|
| 115 |
return Iconv.conv('UTF-8', encoding, str)
|
|
| 116 |
rescue Iconv::Failure |
|
| 117 |
# do nothing here and try the next encoding |
|
| 118 |
end |
|
| 107 | 119 |
end |
| 108 | 120 |
end |
| 109 | 121 |
str = self.replace_invalid_utf8(str) |
| lib/redmine/configuration.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| ... | ... | |
| 20 | 20 |
|
| 21 | 21 |
# Configuration default values |
| 22 | 22 |
@defaults = {
|
| 23 |
'email_delivery' => nil |
|
| 23 |
'email_delivery' => nil, |
|
| 24 |
'max_concurrent_ajax_uploads' => 2 |
|
| 24 | 25 |
} |
| 25 | 26 |
|
| 26 | 27 |
@config = nil |
| lib/redmine/core_ext/active_record.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| ... | ... | |
| 38 | 38 |
end |
| 39 | 39 |
end |
| 40 | 40 |
end |
| 41 |
|
|
| 42 |
class DateValidator < ActiveModel::EachValidator |
|
| 43 |
def validate_each(record, attribute, value) |
|
| 44 |
before_type_cast = record.attributes_before_type_cast[attribute.to_s] |
|
| 45 |
if before_type_cast.is_a?(String) && before_type_cast.present? |
|
| 46 |
# TODO: #*_date_before_type_cast returns a Mysql::Time with ruby1.8+mysql gem |
|
| 47 |
unless before_type_cast =~ /\A\d{4}-\d{2}-\d{2}( 00:00:00)?\z/ && value
|
|
| 48 |
record.errors.add attribute, :not_a_date |
|
| 49 |
end |
|
| 50 |
end |
|
| 51 |
end |
|
| 52 |
end |
|
| lib/redmine/core_ext/date/calculations.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/core_ext/string/conversions.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/core_ext/string/inflections.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/custom_field_format.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/default_data/loader.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| ... | ... | |
| 26 | 26 |
# Returns true if no data is already loaded in the database |
| 27 | 27 |
# otherwise false |
| 28 | 28 |
def no_data? |
| 29 |
!Role.find(:first, :conditions => {:builtin => 0}) &&
|
|
| 30 |
!Tracker.find(:first) &&
|
|
| 31 |
!IssueStatus.find(:first) &&
|
|
| 32 |
!Enumeration.find(:first)
|
|
| 29 |
!Role.where(:builtin => 0).exists? &&
|
|
| 30 |
!Tracker.exists? &&
|
|
| 31 |
!IssueStatus.exists? &&
|
|
| 32 |
!Enumeration.exists?
|
|
| 33 | 33 |
end |
| 34 | 34 |
|
| 35 | 35 |
# Loads the default data |
| ... | ... | |
| 139 | 139 |
rejected = IssueStatus.create!(:name => l(:default_issue_status_rejected), :is_closed => true, :is_default => false, :position => 6) |
| 140 | 140 |
|
| 141 | 141 |
# Workflow |
| 142 |
Tracker.find(:all).each { |t|
|
|
| 143 |
IssueStatus.find(:all).each { |os|
|
|
| 144 |
IssueStatus.find(:all).each { |ns|
|
|
| 142 |
Tracker.all.each { |t|
|
|
| 143 |
IssueStatus.all.each { |os|
|
|
| 144 |
IssueStatus.all.each { |ns|
|
|
| 145 | 145 |
WorkflowTransition.create!(:tracker_id => t.id, :role_id => manager.id, :old_status_id => os.id, :new_status_id => ns.id) unless os == ns |
| 146 | 146 |
} |
| 147 | 147 |
} |
| 148 | 148 |
} |
| 149 | 149 |
|
| 150 |
Tracker.find(:all).each { |t|
|
|
| 150 |
Tracker.all.each { |t|
|
|
| 151 | 151 |
[new, in_progress, resolved, feedback].each { |os|
|
| 152 | 152 |
[in_progress, resolved, feedback, closed].each { |ns|
|
| 153 | 153 |
WorkflowTransition.create!(:tracker_id => t.id, :role_id => developer.id, :old_status_id => os.id, :new_status_id => ns.id) unless os == ns |
| ... | ... | |
| 155 | 155 |
} |
| 156 | 156 |
} |
| 157 | 157 |
|
| 158 |
Tracker.find(:all).each { |t|
|
|
| 158 |
Tracker.all.each { |t|
|
|
| 159 | 159 |
[new, in_progress, resolved, feedback].each { |os|
|
| 160 | 160 |
[closed].each { |ns|
|
| 161 | 161 |
WorkflowTransition.create!(:tracker_id => t.id, :role_id => reporter.id, :old_status_id => os.id, :new_status_id => ns.id) unless os == ns |
| lib/redmine/export/pdf.rb | ||
|---|---|---|
| 1 | 1 |
# encoding: utf-8 |
| 2 | 2 |
# |
| 3 | 3 |
# Redmine - project management software |
| 4 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 4 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 5 | 5 |
# |
| 6 | 6 |
# This program is free software; you can redistribute it and/or |
| 7 | 7 |
# modify it under the terms of the GNU General Public License |
| ... | ... | |
| 17 | 17 |
# along with this program; if not, write to the Free Software |
| 18 | 18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 19 | 19 |
|
| 20 |
require 'iconv' |
|
| 21 | 20 |
require 'tcpdf' |
| 22 | 21 |
require 'fpdf/chinese' |
| 23 | 22 |
require 'fpdf/japanese' |
| 24 | 23 |
require 'fpdf/korean' |
| 25 | 24 |
|
| 25 |
if RUBY_VERSION < '1.9' |
|
| 26 |
require 'iconv' |
|
| 27 |
end |
|
| 28 |
|
|
| 26 | 29 |
module Redmine |
| 27 | 30 |
module Export |
| 28 | 31 |
module PDF |
| ... | ... | |
| 86 | 89 |
|
| 87 | 90 |
def SetTitle(txt) |
| 88 | 91 |
txt = begin |
| 89 |
utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)
|
|
| 92 |
utf16txt = to_utf16(txt)
|
|
| 90 | 93 |
hextxt = "<FEFF" # FEFF is BOM |
| 91 | 94 |
hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
|
| 92 | 95 |
hextxt << ">" |
| ... | ... | |
| 116 | 119 |
html |
| 117 | 120 |
end |
| 118 | 121 |
|
| 122 |
# Encodes an UTF-8 string to UTF-16BE |
|
| 123 |
def to_utf16(str) |
|
| 124 |
if str.respond_to?(:encode) |
|
| 125 |
str.encode('UTF-16BE')
|
|
| 126 |
else |
|
| 127 |
Iconv.conv('UTF-16BE', 'UTF-8', str)
|
|
| 128 |
end |
|
| 129 |
end |
|
| 130 |
|
|
| 119 | 131 |
def RDMCell(w ,h=0, txt='', border=0, ln=0, align='', fill=0, link='') |
| 120 | 132 |
Cell(w, h, fix_text_encoding(txt), border, ln, align, fill, link) |
| 121 | 133 |
end |
| ... | ... | |
| 160 | 172 |
|
| 161 | 173 |
def bookmark_title(txt) |
| 162 | 174 |
txt = begin |
| 163 |
utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)
|
|
| 175 |
utf16txt = to_utf16(txt)
|
|
| 164 | 176 |
hextxt = "<FEFF" # FEFF is BOM |
| 165 | 177 |
hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
|
| 166 | 178 |
hextxt << ">" |
| ... | ... | |
| 368 | 380 |
col_width |
| 369 | 381 |
end |
| 370 | 382 |
|
| 371 |
def render_table_header(pdf, query, col_width, row_height, col_id_width, table_width)
|
|
| 383 |
def render_table_header(pdf, query, col_width, row_height, table_width) |
|
| 372 | 384 |
# headers |
| 373 | 385 |
pdf.SetFontStyle('B',8)
|
| 374 | 386 |
pdf.SetFillColor(230, 230, 230) |
| ... | ... | |
| 377 | 389 |
base_x = pdf.GetX |
| 378 | 390 |
base_y = pdf.GetY |
| 379 | 391 |
max_height = issues_to_pdf_write_cells(pdf, query.inline_columns, col_width, row_height, true) |
| 380 |
pdf.Rect(base_x, base_y, table_width + col_id_width, max_height, 'FD');
|
|
| 392 |
pdf.Rect(base_x, base_y, table_width, max_height, 'FD'); |
|
| 381 | 393 |
pdf.SetXY(base_x, base_y); |
| 382 | 394 |
|
| 383 | 395 |
# write the cells on page |
| 384 |
pdf.RDMCell(col_id_width, row_height, "#", "T", 0, 'C', 1) |
|
| 385 | 396 |
issues_to_pdf_write_cells(pdf, query.inline_columns, col_width, row_height, true) |
| 386 |
issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
|
|
| 397 |
issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, 0, col_width)
|
|
| 387 | 398 |
pdf.SetY(base_y + max_height); |
| 388 | 399 |
|
| 389 | 400 |
# rows |
| ... | ... | |
| 405 | 416 |
# Landscape A4 = 210 x 297 mm |
| 406 | 417 |
page_height = 210 |
| 407 | 418 |
page_width = 297 |
| 419 |
left_margin = 10 |
|
| 408 | 420 |
right_margin = 10 |
| 409 | 421 |
bottom_margin = 20 |
| 410 |
col_id_width = 10 |
|
| 411 | 422 |
row_height = 4 |
| 412 | 423 |
|
| 413 | 424 |
# column widths |
| 414 |
table_width = page_width - right_margin - 10 # fixed left margin
|
|
| 425 |
table_width = page_width - right_margin - left_margin
|
|
| 415 | 426 |
col_width = [] |
| 416 | 427 |
unless query.inline_columns.empty? |
| 417 |
col_width = calc_col_width(issues, query, table_width - col_id_width, pdf)
|
|
| 428 |
col_width = calc_col_width(issues, query, table_width, pdf) |
|
| 418 | 429 |
table_width = col_width.inject(0) {|s,v| s += v}
|
| 419 | 430 |
end |
| 420 | 431 |
|
| 421 |
# use full width if the description is displayed
|
|
| 432 |
# use full width if the description is displayed
|
|
| 422 | 433 |
if table_width > 0 && query.has_column?(:description) |
| 423 |
col_width = col_width.map {|w| w = w * (page_width - right_margin - 10 - col_id_width) / table_width}
|
|
| 434 |
col_width = col_width.map {|w| w * (page_width - right_margin - left_margin) / table_width}
|
|
| 424 | 435 |
table_width = col_width.inject(0) {|s,v| s += v}
|
| 425 | 436 |
end |
| 426 | 437 |
|
| ... | ... | |
| 428 | 439 |
pdf.SetFontStyle('B',11)
|
| 429 | 440 |
pdf.RDMCell(190,10, title) |
| 430 | 441 |
pdf.Ln |
| 431 |
render_table_header(pdf, query, col_width, row_height, col_id_width, table_width)
|
|
| 442 |
render_table_header(pdf, query, col_width, row_height, table_width) |
|
| 432 | 443 |
previous_group = false |
| 433 | 444 |
issue_list(issues) do |issue, level| |
| 434 | 445 |
if query.grouped? && |
| ... | ... | |
| 437 | 448 |
group_label = group.blank? ? 'None' : group.to_s.dup |
| 438 | 449 |
group_label << " (#{query.issue_count_by_group[group]})"
|
| 439 | 450 |
pdf.Bookmark group_label, 0, -1 |
| 440 |
pdf.RDMCell(table_width + col_id_width, row_height * 2, group_label, 1, 1, 'L')
|
|
| 451 |
pdf.RDMCell(table_width, row_height * 2, group_label, 1, 1, 'L') |
|
| 441 | 452 |
pdf.SetFontStyle('',8)
|
| 442 | 453 |
previous_group = group |
| 443 | 454 |
end |
| ... | ... | |
| 456 | 467 |
space_left = page_height - base_y - bottom_margin |
| 457 | 468 |
if max_height > space_left |
| 458 | 469 |
pdf.AddPage("L")
|
| 459 |
render_table_header(pdf, query, col_width, row_height, col_id_width, table_width)
|
|
| 470 |
render_table_header(pdf, query, col_width, row_height, table_width) |
|
| 460 | 471 |
base_x = pdf.GetX |
| 461 | 472 |
base_y = pdf.GetY |
| 462 | 473 |
end |
| 463 | 474 |
|
| 464 | 475 |
# write the cells on page |
| 465 |
pdf.RDMCell(col_id_width, row_height, issue.id.to_s, "T", 0, 'C', 1) |
|
| 466 | 476 |
issues_to_pdf_write_cells(pdf, col_values, col_width, row_height) |
| 467 |
issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
|
|
| 477 |
issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, 0, col_width)
|
|
| 468 | 478 |
pdf.SetY(base_y + max_height); |
| 469 | 479 |
|
| 470 | 480 |
if query.has_column?(:description) && issue.description? |
| ... | ... | |
| 501 | 511 |
end |
| 502 | 512 |
|
| 503 | 513 |
# Draw lines to close the row (MultiCell border drawing in not uniform) |
| 514 |
# |
|
| 515 |
# parameter "col_id_width" is not used. it is kept for compatibility. |
|
| 504 | 516 |
def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y, |
| 505 |
id_width, col_widths) |
|
| 506 |
col_x = top_x + id_width
|
|
| 517 |
col_id_width, col_widths)
|
|
| 518 |
col_x = top_x |
|
| 507 | 519 |
pdf.Line(col_x, top_y, col_x, lower_y) # id right border |
| 508 | 520 |
col_widths.each do |width| |
| 509 | 521 |
col_x += width |
| lib/redmine/helpers/calendar.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/helpers/diff.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/helpers/gantt.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| ... | ... | |
| 23 | 23 |
include Redmine::I18n |
| 24 | 24 |
include Redmine::Utils::DateCalculation |
| 25 | 25 |
|
| 26 |
# Relation types that are rendered |
|
| 27 |
DRAW_TYPES = {
|
|
| 28 |
IssueRelation::TYPE_BLOCKS => { :landscape_margin => 16, :color => '#F34F4F' },
|
|
| 29 |
IssueRelation::TYPE_PRECEDES => { :landscape_margin => 20, :color => '#628FEA' }
|
|
| 30 |
}.freeze |
|
| 31 |
|
|
| 26 | 32 |
# :nodoc: |
| 27 | 33 |
# Some utility methods for the PDF export |
| 28 | 34 |
class PDF |
| ... | ... | |
| 136 | 142 |
) |
| 137 | 143 |
end |
| 138 | 144 |
|
| 145 |
# Returns a hash of the relations between the issues that are present on the gantt |
|
| 146 |
# and that should be displayed, grouped by issue ids. |
|
| 147 |
def relations |
|
| 148 |
return @relations if @relations |
|
| 149 |
if issues.any? |
|
| 150 |
issue_ids = issues.map(&:id) |
|
| 151 |
@relations = IssueRelation. |
|
| 152 |
where(:issue_from_id => issue_ids, :issue_to_id => issue_ids, :relation_type => DRAW_TYPES.keys). |
|
| 153 |
group_by(&:issue_from_id) |
|
| 154 |
else |
|
| 155 |
@relations = {}
|
|
| 156 |
end |
|
| 157 |
end |
|
| 158 |
|
|
| 139 | 159 |
# Return all the project nodes that will be displayed |
| 140 | 160 |
def projects |
| 141 | 161 |
return @projects if @projects |
| ... | ... | |
| 277 | 297 |
pdf_task(options, coords, :label => label, :markers => true, :height => 0.8) |
| 278 | 298 |
end |
| 279 | 299 |
else |
| 280 |
ActiveRecord::Base.logger.debug "Gantt#line_for_project was not given a project with a start_date" |
|
| 281 | 300 |
'' |
| 282 | 301 |
end |
| 283 | 302 |
end |
| ... | ... | |
| 289 | 308 |
html_class << 'icon icon-package ' |
| 290 | 309 |
html_class << (version.behind_schedule? ? 'version-behind-schedule' : '') << " " |
| 291 | 310 |
html_class << (version.overdue? ? 'version-overdue' : '') |
| 311 |
html_class << ' version-closed' unless version.open? |
|
| 312 |
if version.start_date && version.due_date && version.completed_pourcent |
|
| 313 |
progress_date = calc_progress_date(version.start_date, |
|
| 314 |
version.due_date, version.completed_pourcent) |
|
| 315 |
html_class << ' behind-start-date' if progress_date < self.date_from |
|
| 316 |
html_class << ' over-end-date' if progress_date > self.date_to |
|
| 317 |
end |
|
| 292 | 318 |
s = view.link_to_version(version).html_safe |
| 293 | 319 |
subject = view.content_tag(:span, s, |
| 294 | 320 |
:class => html_class).html_safe |
| 295 |
html_subject(options, subject, :css => "version-name") |
|
| 321 |
html_subject(options, subject, :css => "version-name", |
|
| 322 |
:id => "version-#{version.id}")
|
|
| 296 | 323 |
when :image |
| 297 | 324 |
image_subject(options, version.to_s_with_project) |
| 298 | 325 |
when :pdf |
| ... | ... | |
| 303 | 330 |
|
| 304 | 331 |
def line_for_version(version, options) |
| 305 | 332 |
# Skip versions that don't have a start_date |
| 306 |
if version.is_a?(Version) && version.start_date && version.due_date
|
|
| 333 |
if version.is_a?(Version) && version.due_date && version.start_date
|
|
| 307 | 334 |
options[:zoom] ||= 1 |
| 308 | 335 |
options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom] |
| 309 | 336 |
coords = coordinates(version.start_date, |
| 310 |
version.due_date, version.completed_pourcent,
|
|
| 337 |
version.due_date, version.completed_percent,
|
|
| 311 | 338 |
options[:zoom]) |
| 312 |
label = "#{h version} #{h version.completed_pourcent.to_i.to_s}%"
|
|
| 339 |
label = "#{h version} #{h version.completed_percent.to_i.to_s}%"
|
|
| 313 | 340 |
label = h("#{version.project} -") + label unless @project && @project == version.project
|
| 314 | 341 |
case options[:format] |
| 315 | 342 |
when :html |
| 316 |
html_task(options, coords, :css => "version task", :label => label, :markers => true) |
|
| 343 |
html_task(options, coords, :css => "version task", |
|
| 344 |
:label => label, :markers => true, :version => version) |
|
| 317 | 345 |
when :image |
| 318 | 346 |
image_task(options, coords, :label => label, :markers => true, :height => 3) |
| 319 | 347 |
when :pdf |
| 320 | 348 |
pdf_task(options, coords, :label => label, :markers => true, :height => 0.8) |
| 321 | 349 |
end |
| 322 | 350 |
else |
| 323 |
ActiveRecord::Base.logger.debug "Gantt#line_for_version was not given a version with a start_date" |
|
| 324 | 351 |
'' |
| 325 | 352 |
end |
| 326 | 353 |
end |
| ... | ... | |
| 336 | 363 |
css_classes << ' issue-overdue' if issue.overdue? |
| 337 | 364 |
css_classes << ' issue-behind-schedule' if issue.behind_schedule? |
| 338 | 365 |
css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to |
| 366 |
css_classes << ' issue-closed' if issue.closed? |
|
| 367 |
if issue.start_date && issue.due_before && issue.done_ratio |
|
| 368 |
progress_date = calc_progress_date(issue.start_date, |
|
| 369 |
issue.due_before, issue.done_ratio) |
|
| 370 |
css_classes << ' behind-start-date' if progress_date < self.date_from |
|
| 371 |
css_classes << ' over-end-date' if progress_date > self.date_to |
|
| 372 |
end |
|
| 339 | 373 |
s = "".html_safe |
| 340 | 374 |
if issue.assigned_to.present? |
| 341 | 375 |
assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name |
| ... | ... | |
| 347 | 381 |
s << view.link_to_issue(issue).html_safe |
| 348 | 382 |
subject = view.content_tag(:span, s, :class => css_classes).html_safe |
| 349 | 383 |
html_subject(options, subject, :css => "issue-subject", |
| 350 |
:title => issue.subject) + "\n" |
|
| 384 |
:title => issue.subject, :id => "issue-#{issue.id}") + "\n"
|
|
| 351 | 385 |
when :image |
| 352 | 386 |
image_subject(options, issue.subject) |
| 353 | 387 |
when :pdf |
| ... | ... | |
| 378 | 412 |
pdf_task(options, coords, :label => label) |
| 379 | 413 |
end |
| 380 | 414 |
else |
| 381 |
ActiveRecord::Base.logger.debug "GanttHelper#line_for_issue was not given an issue with a due_before" |
|
| 382 | 415 |
'' |
| 383 | 416 |
end |
| 384 | 417 |
end |
| ... | ... | |
| 611 | 644 |
coords[:bar_end] = self.date_to - self.date_from + 1 |
| 612 | 645 |
end |
| 613 | 646 |
if progress |
| 614 |
progress_date = start_date + (end_date - start_date + 1) * (progress / 100.0)
|
|
| 647 |
progress_date = calc_progress_date(start_date, end_date, progress)
|
|
| 615 | 648 |
if progress_date > self.date_from && progress_date > start_date |
| 616 | 649 |
if progress_date < self.date_to |
| 617 | 650 |
coords[:bar_progress_end] = progress_date - self.date_from |
| ... | ... | |
| 638 | 671 |
coords |
| 639 | 672 |
end |
| 640 | 673 |
|
| 641 |
# Sorts a collection of issues by start_date, due_date, id for gantt rendering |
|
| 674 |
def calc_progress_date(start_date, end_date, progress) |
|
| 675 |
start_date + (end_date - start_date + 1) * (progress / 100.0) |
|
| 676 |
end |
|
| 677 |
|
|
| 678 |
# TODO: Sorts a collection of issues by start_date, due_date, id for gantt rendering |
|
| 642 | 679 |
def sort_issues!(issues) |
| 643 | 680 |
issues.sort! { |a, b| gantt_issue_compare(a, b) }
|
| 644 | 681 |
end |
| ... | ... | |
| 678 | 715 |
def html_subject(params, subject, options={})
|
| 679 | 716 |
style = "position: absolute;top:#{params[:top]}px;left:#{params[:indent]}px;"
|
| 680 | 717 |
style << "width:#{params[:subject_width] - params[:indent]}px;" if params[:subject_width]
|
| 681 |
output = view.content_tag('div', subject,
|
|
| 718 |
output = view.content_tag(:div, subject,
|
|
| 682 | 719 |
:class => options[:css], :style => style, |
| 683 |
:title => options[:title]) |
|
| 720 |
:title => options[:title], |
|
| 721 |
:id => options[:id]) |
|
| 684 | 722 |
@subjects << output |
| 685 | 723 |
output |
| 686 | 724 |
end |
| ... | ... | |
| 705 | 743 |
params[:image].text(params[:indent], params[:top] + 2, subject) |
| 706 | 744 |
end |
| 707 | 745 |
|
| 746 |
def issue_relations(issue) |
|
| 747 |
rels = {}
|
|
| 748 |
if relations[issue.id] |
|
| 749 |
relations[issue.id].each do |relation| |
|
| 750 |
(rels[relation.relation_type] ||= []) << relation.issue_to_id |
|
| 751 |
end |
|
| 752 |
end |
|
| 753 |
rels |
|
| 754 |
end |
|
| 755 |
|
|
| 708 | 756 |
def html_task(params, coords, options={})
|
| 709 | 757 |
output = '' |
| 710 | 758 |
# Renders the task bar, with progress and late |
| ... | ... | |
| 714 | 762 |
style << "top:#{params[:top]}px;"
|
| 715 | 763 |
style << "left:#{coords[:bar_start]}px;"
|
| 716 | 764 |
style << "width:#{width}px;"
|
| 717 |
output << view.content_tag(:div, ' '.html_safe, |
|
| 718 |
:style => style, |
|
| 719 |
:class => "#{options[:css]} task_todo")
|
|
| 765 |
html_id = "task-todo-issue-#{options[:issue].id}" if options[:issue]
|
|
| 766 |
html_id = "task-todo-version-#{options[:version].id}" if options[:version]
|
|
| 767 |
content_opt = {:style => style,
|
|
| 768 |
:class => "#{options[:css]} task_todo",
|
|
| 769 |
:id => html_id} |
|
| 770 |
if options[:issue] |
|
| 771 |
rels = issue_relations(options[:issue]) |
|
| 772 |
if rels.present? |
|
| 773 |
content_opt[:data] = {"rels" => rels.to_json}
|
|
| 774 |
end |
|
| 775 |
end |
|
| 776 |
output << view.content_tag(:div, ' '.html_safe, content_opt) |
|
| 720 | 777 |
if coords[:bar_late_end] |
| 721 | 778 |
width = coords[:bar_late_end] - coords[:bar_start] - 2 |
| 722 | 779 |
style = "" |
| ... | ... | |
| 733 | 790 |
style << "top:#{params[:top]}px;"
|
| 734 | 791 |
style << "left:#{coords[:bar_start]}px;"
|
| 735 | 792 |
style << "width:#{width}px;"
|
| 793 |
html_id = "task-done-issue-#{options[:issue].id}" if options[:issue]
|
|
| 794 |
html_id = "task-done-version-#{options[:version].id}" if options[:version]
|
|
| 736 | 795 |
output << view.content_tag(:div, ' '.html_safe, |
| 737 | 796 |
:style => style, |
| 738 |
:class => "#{options[:css]} task_done")
|
|
| 797 |
:class => "#{options[:css]} task_done",
|
|
| 798 |
:id => html_id) |
|
| 739 | 799 |
end |
| 740 | 800 |
end |
| 741 | 801 |
# Renders the markers |
| lib/redmine/helpers/time_report.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| ... | ... | |
| 18 | 18 |
module Redmine |
| 19 | 19 |
module Helpers |
| 20 | 20 |
class TimeReport |
| 21 |
attr_reader :criteria, :columns, :from, :to, :hours, :total_hours, :periods
|
|
| 21 |
attr_reader :criteria, :columns, :hours, :total_hours, :periods |
|
| 22 | 22 |
|
| 23 |
def initialize(project, issue, criteria, columns, from, to)
|
|
| 23 |
def initialize(project, issue, criteria, columns, time_entry_scope)
|
|
| 24 | 24 |
@project = project |
| 25 | 25 |
@issue = issue |
| 26 | 26 |
|
| ... | ... | |
| 30 | 30 |
@criteria = @criteria[0,3] |
| 31 | 31 |
|
| 32 | 32 |
@columns = (columns && %w(year month week day).include?(columns)) ? columns : 'month' |
| 33 |
@from = from |
|
| 34 |
@to = to |
|
| 33 |
@scope = time_entry_scope |
|
| 35 | 34 |
|
| 36 | 35 |
run |
| 37 | 36 |
end |
| ... | ... | |
| 44 | 43 |
|
| 45 | 44 |
def run |
| 46 | 45 |
unless @criteria.empty? |
| 47 |
scope = TimeEntry.visible.spent_between(@from, @to) |
|
| 48 |
if @issue |
|
| 49 |
scope = scope.on_issue(@issue) |
|
| 50 |
elsif @project |
|
| 51 |
scope = scope.on_project(@project, Setting.display_subprojects_issues?) |
|
| 52 |
end |
|
| 53 | 46 |
time_columns = %w(tyear tmonth tweek spent_on) |
| 54 | 47 |
@hours = [] |
| 55 |
scope.sum(:hours, :include => :issue, :group => @criteria.collect{|criteria| @available_criteria[criteria][:sql]} + time_columns).each do |hash, hours|
|
|
| 48 |
@scope.sum(:hours, |
|
| 49 |
:include => [:issue, :activity], |
|
| 50 |
:group => @criteria.collect{|criteria| @available_criteria[criteria][:sql]} + time_columns,
|
|
| 51 |
:joins => @criteria.collect{|criteria| @available_criteria[criteria][:joins]}.compact).each do |hash, hours|
|
|
| 56 | 52 |
h = {'hours' => hours}
|
| 57 | 53 |
(@criteria + time_columns).each_with_index do |name, i| |
| 58 | 54 |
h[name] = hash[i] |
| ... | ... | |
| 67 | 63 |
when 'month' |
| 68 | 64 |
row['month'] = "#{row['tyear']}-#{row['tmonth']}"
|
| 69 | 65 |
when 'week' |
| 70 |
row['week'] = "#{row['tyear']}-#{row['tweek']}"
|
|
| 66 |
row['week'] = "#{row['spent_on'].cwyear}-#{row['tweek']}"
|
|
| 71 | 67 |
when 'day' |
| 72 | 68 |
row['day'] = "#{row['spent_on']}"
|
| 73 | 69 |
end |
| 74 | 70 |
end |
| 75 | 71 |
|
| 76 |
if @from.nil? |
|
| 77 |
min = @hours.collect {|row| row['spent_on']}.min
|
|
| 78 |
@from = min ? min.to_date : Date.today |
|
| 79 |
end |
|
| 72 |
min = @hours.collect {|row| row['spent_on']}.min
|
|
| 73 |
@from = min ? min.to_date : Date.today |
|
| 80 | 74 |
|
| 81 |
if @to.nil? |
|
| 82 |
max = @hours.collect {|row| row['spent_on']}.max
|
|
| 83 |
@to = max ? max.to_date : Date.today |
|
| 84 |
end |
|
| 75 |
max = @hours.collect {|row| row['spent_on']}.max
|
|
| 76 |
@to = max ? max.to_date : Date.today |
|
| 85 | 77 |
|
| 86 | 78 |
@total_hours = @hours.inject(0) {|s,k| s = s + k['hours'].to_f}
|
| 87 | 79 |
|
| ... | ... | |
| 98 | 90 |
@periods << "#{date_from.year}-#{date_from.month}"
|
| 99 | 91 |
date_from = (date_from + 1.month).at_beginning_of_month |
| 100 | 92 |
when 'week' |
| 101 |
@periods << "#{date_from.year}-#{date_from.to_date.cweek}"
|
|
| 93 |
@periods << "#{date_from.to_date.cwyear}-#{date_from.to_date.cweek}"
|
|
| 102 | 94 |
date_from = (date_from + 7.day).at_beginning_of_week |
| 103 | 95 |
when 'day' |
| 104 | 96 |
@periods << "#{date_from.to_date}"
|
| ... | ... | |
| 121 | 113 |
'category' => {:sql => "#{Issue.table_name}.category_id",
|
| 122 | 114 |
:klass => IssueCategory, |
| 123 | 115 |
:label => :field_category}, |
| 124 |
'member' => {:sql => "#{TimeEntry.table_name}.user_id",
|
|
| 116 |
'user' => {:sql => "#{TimeEntry.table_name}.user_id",
|
|
| 125 | 117 |
:klass => User, |
| 126 |
:label => :label_member},
|
|
| 118 |
:label => :label_user},
|
|
| 127 | 119 |
'tracker' => {:sql => "#{Issue.table_name}.tracker_id",
|
| 128 | 120 |
:klass => Tracker, |
| 129 | 121 |
:label => :label_tracker}, |
| ... | ... | |
| 135 | 127 |
:label => :label_issue} |
| 136 | 128 |
} |
| 137 | 129 |
|
| 130 |
# Add time entry custom fields |
|
| 131 |
custom_fields = TimeEntryCustomField.all |
|
| 132 |
# Add project custom fields |
|
| 133 |
custom_fields += ProjectCustomField.all |
|
| 134 |
# Add issue custom fields |
|
| 135 |
custom_fields += (@project.nil? ? IssueCustomField.for_all : @project.all_issue_custom_fields) |
|
| 136 |
# Add time entry activity custom fields |
|
| 137 |
custom_fields += TimeEntryActivityCustomField.all |
|
| 138 |
|
|
| 138 | 139 |
# Add list and boolean custom fields as available criteria |
| 139 |
custom_fields = (@project.nil? ? IssueCustomField.for_all : @project.all_issue_custom_fields) |
|
| 140 | 140 |
custom_fields.select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
|
| 141 |
@available_criteria["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'Issue' AND c.customized_id = #{Issue.table_name}.id ORDER BY c.value LIMIT 1)",
|
|
| 142 |
:format => cf.field_format, |
|
| 143 |
:label => cf.name} |
|
| 144 |
end if @project |
|
| 145 |
|
|
| 146 |
# Add list and boolean time entry custom fields |
|
| 147 |
TimeEntryCustomField.find(:all).select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
|
|
| 148 |
@available_criteria["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'TimeEntry' AND c.customized_id = #{TimeEntry.table_name}.id ORDER BY c.value LIMIT 1)",
|
|
| 149 |
:format => cf.field_format, |
|
| 150 |
:label => cf.name} |
|
| 151 |
end |
|
| 152 |
|
|
| 153 |
# Add list and boolean time entry activity custom fields |
|
| 154 |
TimeEntryActivityCustomField.find(:all).select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
|
|
| 155 |
@available_criteria["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'Enumeration' AND c.customized_id = #{TimeEntry.table_name}.activity_id ORDER BY c.value LIMIT 1)",
|
|
| 141 |
@available_criteria["cf_#{cf.id}"] = {:sql => "#{cf.join_alias}.value",
|
|
| 142 |
:joins => cf.join_for_order_statement, |
|
| 156 | 143 |
:format => cf.field_format, |
| 157 | 144 |
:label => cf.name} |
| 158 | 145 |
end |
| lib/redmine/hook.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/i18n.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/imap.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/menu_manager.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| ... | ... | |
| 190 | 190 |
|
| 191 | 191 |
# Checks if a user is allowed to access the menu item by: |
| 192 | 192 |
# |
| 193 |
# * Checking the url target (project only) |
|
| 193 | 194 |
# * Checking the conditions of the item |
| 194 |
# * Checking the url target (project only) |
|
| 195 | 195 |
def allowed_node?(node, user, project) |
| 196 |
if project && user && !user.allowed_to?(node.url, project) |
|
| 197 |
return false |
|
| 198 |
end |
|
| 196 | 199 |
if node.condition && !node.condition.call(project) |
| 197 | 200 |
# Condition that doesn't pass |
| 198 | 201 |
return false |
| 199 | 202 |
end |
| 200 |
|
|
| 201 |
if project |
|
| 202 |
return user && user.allowed_to?(node.url, project) |
|
| 203 |
else |
|
| 204 |
# outside a project, all menu items allowed |
|
| 205 |
return true |
|
| 206 |
end |
|
| 203 |
return true |
|
| 207 | 204 |
end |
| 208 | 205 |
end |
| 209 | 206 |
|
| lib/redmine/mime_type.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/pagination.rb | ||
|---|---|---|
| 1 |
# encoding: utf-8 |
|
| 2 |
# |
|
| 3 |
# Redmine - project management software |
|
| 4 |
# Copyright (C) 2006-2013 Jean-Philippe Lang |
|
| 5 |
# |
|
| 6 |
# This program is free software; you can redistribute it and/or |
|
| 7 |
# modify it under the terms of the GNU General Public License |
|
| 8 |
# as published by the Free Software Foundation; either version 2 |
|
| 9 |
# of the License, or (at your option) any later version. |
|
| 10 |
# |
|
| 11 |
# This program is distributed in the hope that it will be useful, |
|
| 12 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 14 |
# GNU General Public License for more details. |
|
| 15 |
# |
|
| 16 |
# You should have received a copy of the GNU General Public License |
|
| 17 |
# along with this program; if not, write to the Free Software |
|
| 18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
| 19 |
|
|
| 20 |
module Redmine |
|
| 21 |
module Pagination |
|
| 22 |
class Paginator |
|
| 23 |
attr_reader :item_count, :per_page, :page, :page_param |
|
| 24 |
|
|
| 25 |
def initialize(*args) |
|
| 26 |
if args.first.is_a?(ActionController::Base) |
|
| 27 |
args.shift |
|
| 28 |
ActiveSupport::Deprecation.warn "Paginator no longer takes a controller instance as the first argument. Remove it from #new arguments." |
|
| 29 |
end |
|
| 30 |
item_count, per_page, page, page_param = *args |
|
| 31 |
|
|
| 32 |
@item_count = item_count |
|
| 33 |
@per_page = per_page |
|
| 34 |
page = (page || 1).to_i |
|
| 35 |
if page < 1 |
|
| 36 |
page = 1 |
|
| 37 |
end |
|
| 38 |
@page = page |
|
| 39 |
@page_param = page_param || :page |
|
| 40 |
end |
|
| 41 |
|
|
| 42 |
def offset |
|
| 43 |
(page - 1) * per_page |
|
| 44 |
end |
|
| 45 |
|
|
| 46 |
def first_page |
|
| 47 |
if item_count > 0 |
|
| 48 |
1 |
|
| 49 |
end |
|
| 50 |
end |
|
| 51 |
|
|
| 52 |
def previous_page |
|
| 53 |
if page > 1 |
|
| 54 |
page - 1 |
|
| 55 |
end |
|
| 56 |
end |
|
| 57 |
|
|
| 58 |
def next_page |
|
| 59 |
if last_item < item_count |
|
| 60 |
page + 1 |
|
| 61 |
end |
|
| 62 |
end |
|
| 63 |
|
|
| 64 |
def last_page |
|
| 65 |
if item_count > 0 |
|
| 66 |
(item_count - 1) / per_page + 1 |
|
| 67 |
end |
|
| 68 |
end |
|
| 69 |
|
|
| 70 |
def first_item |
|
| 71 |
item_count == 0 ? 0 : (offset + 1) |
|
| 72 |
end |
|
| 73 |
|
|
| 74 |
def last_item |
|
| 75 |
l = first_item + per_page - 1 |
|
| 76 |
l > item_count ? item_count : l |
|
| 77 |
end |
|
| 78 |
|
|
| 79 |
def linked_pages |
|
| 80 |
pages = [] |
|
| 81 |
if item_count > 0 |
|
| 82 |
pages += [first_page, page, last_page] |
|
| 83 |
pages += ((page-2)..(page+2)).to_a.select {|p| p > first_page && p < last_page}
|
|
| 84 |
end |
|
| 85 |
pages = pages.compact.uniq.sort |
|
| 86 |
if pages.size > 1 |
|
| 87 |
pages |
|
| 88 |
else |
|
| 89 |
[] |
|
| 90 |
end |
|
| 91 |
end |
|
| 92 |
|
|
| 93 |
def items_per_page |
|
| 94 |
ActiveSupport::Deprecation.warn "Paginator#items_per_page will be removed. Use #per_page instead." |
|
| 95 |
per_page |
|
| 96 |
end |
|
| 97 |
|
|
| 98 |
def current |
|
| 99 |
ActiveSupport::Deprecation.warn "Paginator#current will be removed. Use .offset instead of .current.offset." |
|
| 100 |
self |
|
| 101 |
end |
|
| 102 |
end |
|
| 103 |
|
|
| 104 |
# Paginates the given scope or model. Returns a Paginator instance and |
|
| 105 |
# the collection of objects for the current page. |
|
| 106 |
# |
|
| 107 |
# Options: |
|
| 108 |
# :parameter name of the page parameter |
|
| 109 |
# |
|
| 110 |
# Examples: |
|
| 111 |
# @user_pages, @users = paginate User.where(:status => 1) |
|
| 112 |
# |
|
| 113 |
def paginate(scope, options={})
|
|
| 114 |
options = options.dup |
|
| 115 |
finder_options = options.extract!( |
|
| 116 |
:conditions, |
|
| 117 |
:order, |
|
| 118 |
:joins, |
|
| 119 |
:include, |
|
| 120 |
:select |
|
| 121 |
) |
|
| 122 |
if scope.is_a?(Symbol) || finder_options.values.compact.any? |
|
| 123 |
return deprecated_paginate(scope, finder_options, options) |
|
| 124 |
end |
|
| 125 |
|
|
| 126 |
paginator = paginator(scope.count, options) |
|
| 127 |
collection = scope.limit(paginator.per_page).offset(paginator.offset).to_a |
|
| 128 |
|
|
| 129 |
return paginator, collection |
|
| 130 |
end |
|
| 131 |
|
|
| 132 |
def deprecated_paginate(arg, finder_options, options={})
|
|
| 133 |
ActiveSupport::Deprecation.warn "#paginate with a Symbol and/or find options is depreceted and will be removed. Use a scope instead." |
|
| 134 |
klass = arg.is_a?(Symbol) ? arg.to_s.classify.constantize : arg |
|
| 135 |
scope = klass.scoped(finder_options) |
|
| 136 |
paginate(scope, options) |
|
| 137 |
end |
|
| 138 |
|
|
| 139 |
def paginator(item_count, options={})
|
|
| 140 |
options.assert_valid_keys :parameter, :per_page |
|
| 141 |
|
|
| 142 |
page_param = options[:parameter] || :page |
|
| 143 |
page = (params[page_param] || 1).to_i |
|
| 144 |
per_page = options[:per_page] || per_page_option |
|
| 145 |
Paginator.new(item_count, per_page, page, page_param) |
|
| 146 |
end |
|
| 147 |
|
|
| 148 |
module Helper |
|
| 149 |
include Redmine::I18n |
|
| 150 |
|
|
| 151 |
# Renders the pagination links for the given paginator. |
|
| 152 |
# |
|
| 153 |
# Options: |
|
| 154 |
# :per_page_links if set to false, the "Per page" links are not rendered |
|
| 155 |
# |
|
| 156 |
def pagination_links_full(*args) |
|
| 157 |
pagination_links_each(*args) do |text, parameters, options| |
|
| 158 |
if block_given? |
|
| 159 |
yield text, parameters, options |
|
| 160 |
else |
|
| 161 |
link_to text, params.merge(parameters), options |
|
| 162 |
end |
|
| 163 |
end |
|
| 164 |
end |
|
| 165 |
|
|
| 166 |
# Yields the given block with the text and parameters |
|
| 167 |
# for each pagination link and returns a string that represents the links |
|
| 168 |
def pagination_links_each(paginator, count=nil, options={}, &block)
|
|
| 169 |
options.assert_valid_keys :per_page_links |
|
| 170 |
|
|
| 171 |
per_page_links = options.delete(:per_page_links) |
|
| 172 |
per_page_links = false if count.nil? |
|
| 173 |
page_param = paginator.page_param |
|
| 174 |
|
|
| 175 |
html = '' |
|
| 176 |
if paginator.previous_page |
|
| 177 |
# \xc2\xab(utf-8) = « |
|
| 178 |
text = "\xc2\xab " + l(:label_previous) |
|
| 179 |
html << yield(text, {page_param => paginator.previous_page}, :class => 'previous') + ' '
|
|
| 180 |
end |
|
| 181 |
|
|
| 182 |
previous = nil |
|
| 183 |
paginator.linked_pages.each do |page| |
|
| 184 |
if previous && previous != page - 1 |
|
| 185 |
html << content_tag('span', '...', :class => 'spacer') + ' '
|
|
| 186 |
end |
|
| 187 |
if page == paginator.page |
|
| 188 |
html << content_tag('span', page.to_s, :class => 'current page')
|
|
| 189 |
else |
|
| 190 |
html << yield(page.to_s, {page_param => page}, :class => 'page')
|
|
| 191 |
end |
|
| 192 |
html << ' ' |
|
| 193 |
previous = page |
|
| 194 |
end |
|
| 195 |
|
|
| 196 |
if paginator.next_page |
|
| 197 |
# \xc2\xbb(utf-8) = » |
|
| 198 |
text = l(:label_next) + " \xc2\xbb" |
|
| 199 |
html << yield(text, {page_param => paginator.next_page}, :class => 'next') + ' '
|
|
| 200 |
end |
|
| 201 |
|
|
| 202 |
html << content_tag('span', "(#{paginator.first_item}-#{paginator.last_item}/#{paginator.item_count})", :class => 'items') + ' '
|
|
| 203 |
|
|
| 204 |
if per_page_links != false && links = per_page_links(paginator, &block) |
|
| 205 |
html << content_tag('span', links.to_s, :class => 'per-page')
|
|
| 206 |
end |
|
| 207 |
|
|
| 208 |
html.html_safe |
|
| 209 |
end |
|
| 210 |
|
|
| 211 |
# Renders the "Per page" links. |
|
| 212 |
def per_page_links(paginator, &block) |
|
| 213 |
values = per_page_options(paginator.per_page, paginator.item_count) |
|
| 214 |
if values.any? |
|
| 215 |
links = values.collect do |n| |
|
| 216 |
if n == paginator.per_page |
|
| 217 |
content_tag('span', n.to_s)
|
|
| 218 |
else |
|
| 219 |
yield(n, :per_page => n, paginator.page_param => nil) |
|
| 220 |
end |
|
| 221 |
end |
|
| 222 |
l(:label_display_per_page, links.join(', ')).html_safe
|
|
| 223 |
end |
|
| 224 |
end |
|
| 225 |
|
|
| 226 |
def per_page_options(selected=nil, item_count=nil) |
|
| 227 |
options = Setting.per_page_options_array |
|
| 228 |
if item_count && options.any? |
|
| 229 |
if item_count > options.first |
|
| 230 |
max = options.detect {|value| value >= item_count} || item_count
|
|
| 231 |
else |
|
| 232 |
max = item_count |
|
| 233 |
end |
|
| 234 |
options = options.select {|value| value <= max || value == selected}
|
|
| 235 |
end |
|
| 236 |
if options.empty? || (options.size == 1 && options.first == selected) |
|
| 237 |
[] |
|
| 238 |
else |
|
| 239 |
options |
|
| 240 |
end |
|
| 241 |
end |
|
| 242 |
end |
|
| 243 |
end |
|
| 244 |
end |
|
| lib/redmine/platform.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/plugin.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| ... | ... | |
| 145 | 145 |
File.join(self.class.public_directory, id.to_s) |
| 146 | 146 |
end |
| 147 | 147 |
|
| 148 |
def to_param |
|
| 149 |
id |
|
| 150 |
end |
|
| 151 |
|
|
| 148 | 152 |
def assets_directory |
| 149 | 153 |
File.join(directory, 'assets') |
| 150 | 154 |
end |
| lib/redmine/pop3.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/safe_attributes.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/scm/adapters/abstract_adapter.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| ... | ... | |
| 17 | 17 |
|
| 18 | 18 |
require 'cgi' |
| 19 | 19 |
|
| 20 |
if RUBY_VERSION < '1.9' |
|
| 21 |
require 'iconv' |
|
| 22 |
end |
|
| 23 |
|
|
| 20 | 24 |
module Redmine |
| 21 | 25 |
module Scm |
| 22 | 26 |
module Adapters |
| ... | ... | |
| 214 | 218 |
Rails.logger |
| 215 | 219 |
end |
| 216 | 220 |
|
| 221 |
# Path to the file where scm stderr output is logged |
|
| 222 |
# Returns nil if the log file is not writable |
|
| 223 |
def self.stderr_log_file |
|
| 224 |
if @stderr_log_file.nil? |
|
| 225 |
writable = false |
|
| 226 |
path = Redmine::Configuration['scm_stderr_log_file'].presence |
|
| 227 |
path ||= Rails.root.join("log/#{Rails.env}.scm.stderr.log").to_s
|
|
| 228 |
if File.exists?(path) |
|
| 229 |
if File.file?(path) && File.writable?(path) |
|
| 230 |
writable = true |
|
| 231 |
else |
|
| 232 |
logger.warn("SCM log file (#{path}) is not writable")
|
|
| 233 |
end |
|
| 234 |
else |
|
| 235 |
begin |
|
| 236 |
File.open(path, "w") {}
|
|
| 237 |
writable = true |
|
| 238 |
rescue => e |
|
| 239 |
logger.warn("SCM log file (#{path}) cannot be created: #{e.message}")
|
|
| 240 |
end |
|
| 241 |
end |
|
| 242 |
@stderr_log_file = writable ? path : false |
|
| 243 |
end |
|
| 244 |
@stderr_log_file || nil |
|
| 245 |
end |
|
| 246 |
|
|
| 217 | 247 |
def self.shellout(cmd, options = {}, &block)
|
| 218 | 248 |
if logger && logger.debug? |
| 219 | 249 |
logger.debug "Shelling out: #{strip_credential(cmd)}"
|
| 220 |
end
|
|
| 221 |
if Rails.env == 'development'
|
|
| 222 |
# Capture stderr when running in dev environment
|
|
| 223 |
cmd = "#{cmd} 2>>#{shell_quote(Rails.root.join('log/scm.stderr.log').to_s)}"
|
|
| 250 |
# Capture stderr in a log file
|
|
| 251 |
if stderr_log_file
|
|
| 252 |
cmd = "#{cmd} 2>>#{shell_quote(stderr_log_file)}"
|
|
| 253 |
end
|
|
| 224 | 254 |
end |
| 225 | 255 |
begin |
| 226 | 256 |
mode = "r+" |
| lib/redmine/scm/adapters/bazaar_adapter.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| ... | ... | |
| 104 | 104 |
re = %r{^V\s+(#{Regexp.escape(prefix)})?(\/?)([^\/]+)(\/?)\s+(\S+)\r?$}
|
| 105 | 105 |
io.each_line do |line| |
| 106 | 106 |
next unless line =~ re |
| 107 |
name_locale = $3.strip
|
|
| 107 |
name_locale, slash, revision = $3.strip, $4, $5.strip
|
|
| 108 | 108 |
name = scm_iconv('UTF-8', @path_encoding, name_locale)
|
| 109 | 109 |
entries << Entry.new({:name => name,
|
| 110 | 110 |
:path => ((path.empty? ? "" : "#{path}/") + name),
|
| 111 |
:kind => ($4.blank? ? 'file' : 'dir'),
|
|
| 111 |
:kind => (slash.blank? ? 'file' : 'dir'),
|
|
| 112 | 112 |
:size => nil, |
| 113 |
:lastrev => Revision.new(:revision => $5.strip)
|
|
| 113 |
:lastrev => Revision.new(:revision => revision)
|
|
| 114 | 114 |
}) |
| 115 | 115 |
end |
| 116 | 116 |
end |
| lib/redmine/scm/adapters/darcs_adapter.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/scm/adapters/filesystem_adapter.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# FileSystem adapter |
| 5 | 5 |
# File written by Paul Rivier, at Demotera. |
| lib/redmine/scm/adapters/git_adapter.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/scm/adapters/mercurial_adapter.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/scm/adapters/subversion_adapter.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/scm/base.rb | ||
|---|---|---|
| 4 | 4 |
class << self |
| 5 | 5 |
|
| 6 | 6 |
def all |
| 7 |
@scms |
|
| 7 |
@scms || []
|
|
| 8 | 8 |
end |
| 9 | 9 |
|
| 10 | 10 |
# Add a new SCM adapter and repository |
| lib/redmine/search.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/subclass_factory.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/syntax_highlighting.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| ... | ... | |
| 33 | 33 |
|
| 34 | 34 |
module CodeRay |
| 35 | 35 |
require 'coderay' |
| 36 |
require 'coderay/helpers/file_type' |
|
| 37 | 36 |
|
| 38 | 37 |
class << self |
| 39 | 38 |
# Highlights +text+ as the content of +filename+ |
| lib/redmine/themes.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/thumbnail.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/unified_diff.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| ... | ... | |
| 28 | 28 |
lines = 0 |
| 29 | 29 |
@truncated = false |
| 30 | 30 |
diff_table = DiffTable.new(diff_type, diff_style) |
| 31 |
diff.each do |line| |
|
| 32 |
line_encoding = nil |
|
| 33 |
if line.respond_to?(:force_encoding) |
|
| 34 |
line_encoding = line.encoding |
|
| 35 |
# TODO: UTF-16 and Japanese CP932 which is imcompatible with ASCII |
|
| 36 |
# In Japan, diffrence between file path encoding |
|
| 37 |
# and file contents encoding is popular. |
|
| 38 |
line.force_encoding('ASCII-8BIT')
|
|
| 39 |
end |
|
| 40 |
unless diff_table.add_line line |
|
| 41 |
line.force_encoding(line_encoding) if line_encoding |
|
| 31 |
diff.each do |line_raw| |
|
| 32 |
line = Redmine::CodesetUtil.to_utf8_by_setting(line_raw) |
|
| 33 |
unless diff_table.add_line(line) |
|
| 42 | 34 |
self << diff_table if diff_table.length > 0 |
| 43 | 35 |
diff_table = DiffTable.new(diff_type, diff_style) |
| 44 | 36 |
end |
| ... | ... | |
| 83 | 75 |
@parsing = true |
| 84 | 76 |
end |
| 85 | 77 |
else |
| 86 |
if line =~ /^[^\+\-\s@\\]/
|
|
| 78 |
if line =~ %r{^[^\+\-\s@\\]}
|
|
| 87 | 79 |
@parsing = false |
| 88 | 80 |
return false |
| 89 | 81 |
elsif line =~ /^@@ (\+|\-)(\d+)(,\d+)? (\+|\-)(\d+)(,\d+)? @@/ |
| ... | ... | |
| 207 | 199 |
while starting < max && line_left[starting] == line_right[starting] |
| 208 | 200 |
starting += 1 |
| 209 | 201 |
end |
| 202 |
if (! "".respond_to?(:force_encoding)) && starting < line_left.size |
|
| 203 |
while line_left[starting].ord.between?(128, 191) && starting > 0 |
|
| 204 |
starting -= 1 |
|
| 205 |
end |
|
| 206 |
end |
|
| 210 | 207 |
ending = -1 |
| 211 | 208 |
while ending >= -(max - starting) && line_left[ending] == line_right[ending] |
| 212 | 209 |
ending -= 1 |
| 213 | 210 |
end |
| 211 |
if (! "".respond_to?(:force_encoding)) && ending > (-1 * line_left.size) |
|
| 212 |
while line_left[ending].ord.between?(128, 191) && ending > -1 |
|
| 213 |
ending -= 1 |
|
| 214 |
end |
|
| 215 |
end |
|
| 214 | 216 |
unless starting == 0 && ending == -1 |
| 215 | 217 |
[starting, ending] |
| 216 | 218 |
end |
| ... | ... | |
| 268 | 270 |
private |
| 269 | 271 |
|
| 270 | 272 |
def line_to_html(line, offsets) |
| 273 |
html = line_to_html_raw(line, offsets) |
|
| 274 |
html.force_encoding('UTF-8') if html.respond_to?(:force_encoding)
|
|
| 275 |
html |
|
| 276 |
end |
|
| 277 |
|
|
| 278 |
def line_to_html_raw(line, offsets) |
|
| 271 | 279 |
if offsets |
| 272 | 280 |
s = '' |
| 273 | 281 |
unless offsets.first == 0 |
| lib/redmine/utils.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/version.rb | ||
|---|---|---|
| 3 | 3 |
module Redmine |
| 4 | 4 |
module VERSION #:nodoc: |
| 5 | 5 |
MAJOR = 2 |
| 6 |
MINOR = 2
|
|
| 7 |
TINY = 4
|
|
| 6 |
MINOR = 3
|
|
| 7 |
TINY = 1
|
|
| 8 | 8 |
|
| 9 | 9 |
# Branch values: |
| 10 | 10 |
# * official release: nil |
| lib/redmine/views/api_template_handler.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| lib/redmine/views/builders.rb | ||
|---|---|---|
| 1 | 1 |
# Redmine - project management software |
| 2 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 3 | 3 |
# |
| 4 | 4 |
# This program is free software; you can redistribute it and/or |
| 5 | 5 |
# modify it under the terms of the GNU General Public License |
| ... | ... | |
| 15 | 15 |
# along with this program; if not, write to the Free Software |
| 16 | 16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 17 | 17 |
|
| 18 |
require 'redmine/views/builders/json' |
|
| 19 |
require 'redmine/views/builders/xml' |
|
| 20 |
|
|
Also available in: Unified diff