Compare commits

..

21 Commits
2.0.2 ... 2.0.3

Author SHA1 Message Date
Jean-Philippe Lang
87e8b02828 tagged version 2.0.3
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/tags/2.0.3@9868 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-18 20:22:13 +00:00
Jean-Philippe Lang
342b3302cf Merged r9864 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.0-stable@9865 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-18 18:45:14 +00:00
Jean-Philippe Lang
32e3d6e1b1 Merged r9861 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.0-stable@9862 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-18 18:34:01 +00:00
Jean-Philippe Lang
8d2d46bd8e Merged r9858 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.0-stable@9859 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-18 18:17:49 +00:00
Toshi MARUYAMA
7f03576b8d Merged r9853 from trunk (#10688)
fix PDF export tables problems.

Contributed by Jun NAITOH.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.0-stable@9854 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-17 13:10:48 +00:00
Toshi MARUYAMA
e757600b39 Merge r9750 from trunk
scm: git: skip Latin-1 path tests on Git for Windows above 1.7.10

Git for Windows (msysGit) changed internal API from ANSI to Unicode in 1.7.10.
http://code.google.com/p/msysgit/issues/detail?id=80

So, Latin-1 path tests fail on Japanese Windows.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.0-stable@9851 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-17 10:52:18 +00:00
Jean-Philippe Lang
68560c13fe Merged r9830 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.0-stable@9848 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-17 09:02:24 +00:00
Jean-Philippe Lang
309f7b75fd Merged r9837 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.0-stable@9846 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-17 08:59:42 +00:00
Jean-Philippe Lang
dbb93692ff Merged r9836 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.0-stable@9845 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-17 08:58:20 +00:00
Jean-Philippe Lang
5f6289d6be Merged r9831 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.0-stable@9843 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-17 08:56:11 +00:00
Jean-Philippe Lang
081ee54bee Merged r9822 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.0-stable@9840 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-17 08:52:40 +00:00
Jean-Philippe Lang
1d0fb85179 Merged r9796 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.0-stable@9839 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-17 08:51:04 +00:00
Etienne Massip
887e7f8647 Merged r9832 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.0-stable@9834 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-14 21:09:56 +00:00
Toshi MARUYAMA
df05edff3d Merged r9814 from trunk
fix test_user_index of activities controller test fails at Japanese morning and afternoon.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.0-stable@9818 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-11 06:56:11 +00:00
Jean-Philippe Lang
67057ea3e9 Merged r9798 to r9801 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.0-stable@9802 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-10 13:39:42 +00:00
Jean-Philippe Lang
381e7156c2 Merged r9782, r9784, r9794 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.0-stable@9795 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-09 14:19:13 +00:00
Jean-Philippe Lang
f9ee57f2c1 Merged r9781 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.0-stable@9793 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-09 13:58:40 +00:00
Jean-Philippe Lang
fc13aef5bb Merged r9785 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.0-stable@9792 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-09 13:56:56 +00:00
Jean-Philippe Lang
4973350243 Merged r9783 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.0-stable@9789 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-09 13:45:55 +00:00
Jean-Philippe Lang
fcbb8acdb7 Merged r9786 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.0-stable@9788 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-09 13:44:32 +00:00
Toshi MARUYAMA
d79258f17f Merged r9777 from trunk (#11113)
fix German "field_multiple" translation glitch by Andreas Deininger.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/2.0-stable@9778 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-06-08 07:49:16 +00:00
79 changed files with 410 additions and 114 deletions

View File

@@ -1,6 +1,6 @@
source 'http://rubygems.org'
gem 'rails', '3.2.5'
gem 'rails', '3.2.6'
gem 'prototype-rails', '3.2.1'
gem "i18n", "~> 0.6.0"
gem "coderay", "~> 1.0.6"

View File

@@ -445,9 +445,9 @@ class ApplicationController < ActionController::Base
# Returns the API key present in the request
def api_key_from_request
if params[:key].present?
params[:key]
params[:key].to_s
elsif request.headers["X-Redmine-API-Key"].present?
request.headers["X-Redmine-API-Key"]
request.headers["X-Redmine-API-Key"].to_s
end
end

View File

@@ -43,10 +43,10 @@ class BoardsController < ApplicationController
@topic_count = @board.topics.count
@topic_pages = Paginator.new self, @topic_count, per_page_option, params['page']
@topics = @board.topics.find :all, :order => ["#{Message.table_name}.sticky DESC", sort_clause].compact.join(', '),
@topics = @board.topics.reorder("#{Message.table_name}.sticky DESC").order(sort_clause).all(
:include => [:author, {:last_reply => :author}],
:limit => @topic_pages.items_per_page,
:offset => @topic_pages.current.offset
:offset => @topic_pages.current.offset)
@message = Message.new(:board => @board)
render :action => 'show', :layout => !request.xhr?
}

View File

@@ -39,7 +39,7 @@ class TimelogController < ApplicationController
def index
sort_init 'spent_on', 'desc'
sort_update 'spent_on' => 'spent_on',
sort_update 'spent_on' => ['spent_on', "#{TimeEntry.table_name}.created_on"],
'user' => 'user_id',
'activity' => 'activity_id',
'project' => "#{Project.table_name}.name",

View File

@@ -124,6 +124,28 @@ class Issue < ActiveRecord::Base
end
end
# AR#Persistence#destroy would raise and RecordNotFound exception
# if the issue was already deleted or updated (non matching lock_version).
# This is a problem when bulk deleting issues or deleting a project
# (because an issue may already be deleted if its parent was deleted
# first).
# The issue is reloaded by the nested_set before being deleted so
# the lock_version condition should not be an issue but we handle it.
def destroy
super
rescue ActiveRecord::RecordNotFound
# Stale or already deleted
begin
reload
rescue ActiveRecord::RecordNotFound
# The issue was actually already deleted
@destroyed = true
return freeze
end
# The issue was stale, retry to destroy
super
end
# Overrides Redmine::Acts::Customizable::InstanceMethods#available_custom_fields
def available_custom_fields
(project && tracker) ? (project.all_issue_custom_fields & tracker.custom_fields.all) : []

View File

@@ -163,7 +163,7 @@ class MailHandler < ActionMailer::Base
issue = Issue.new(:author => user, :project => project)
issue.safe_attributes = issue_attributes_from_keywords(issue)
issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
issue.subject = email.subject.to_s.chomp[0,255]
issue.subject = cleaned_up_subject
if issue.subject.blank?
issue.subject = '(no subject)'
end
@@ -223,7 +223,7 @@ class MailHandler < ActionMailer::Base
end
if !message.locked?
reply = Message.new(:subject => email.subject.gsub(%r{^.*msg\d+\]}, '').strip,
reply = Message.new(:subject => cleaned_up_subject.gsub(%r{^.*msg\d+\]}, '').strip,
:content => cleaned_up_text_body)
reply.author = user
reply.board = message.board
@@ -364,6 +364,23 @@ class MailHandler < ActionMailer::Base
cleanup_body(plain_text_body)
end
def cleaned_up_subject
subject = email.subject.to_s
unless subject.respond_to?(:encoding)
# try to reencode to utf8 manually with ruby1.8
begin
if h = email.header[:subject]
if m = h.value.match(/^=\?([^\?]+)\?/)
subject = Redmine::CodesetUtil.to_utf8(subject, m[1])
end
end
rescue
# nop
end
end
subject.strip[0,255]
end
def self.full_sanitizer
@full_sanitizer ||= HTML::FullSanitizer.new
end

View File

@@ -66,7 +66,7 @@ class TimeEntry < ActiveRecord::Base
end
}
safe_attributes 'hours', 'comments', 'issue_id', 'activity_id', 'spent_on', 'custom_field_values'
safe_attributes 'hours', 'comments', 'issue_id', 'activity_id', 'spent_on', 'custom_field_values', 'custom_fields'
def initialize(attributes=nil, *args)
super

View File

@@ -130,8 +130,11 @@ class User < Principal
# Returns the user that matches provided login and password, or nil
def self.try_to_login(login, password)
login = login.to_s
password = password.to_s
# Make sure no one can sign in with an empty password
return nil if password.to_s.empty?
return nil if password.empty?
user = find_by_login(login)
if user
# user is already in local database
@@ -164,7 +167,7 @@ class User < Principal
# Returns the user who matches the given autologin +key+ or nil
def self.try_to_autologin(key)
tokens = Token.find_all_by_action_and_value('autologin', key)
tokens = Token.find_all_by_action_and_value('autologin', key.to_s)
# Make sure there's only 1 token that matches the key
if tokens.size == 1
token = tokens.first
@@ -338,12 +341,12 @@ class User < Principal
end
def self.find_by_rss_key(key)
token = Token.find_by_value(key)
token = Token.find_by_action_and_value('feeds', key.to_s)
token && token.user.active? ? token.user : nil
end
def self.find_by_api_key(key)
token = Token.find_by_action_and_value('api', key)
token = Token.find_by_action_and_value('api', key.to_s)
token && token.user.active? ? token.user : nil
end

View File

@@ -1,4 +1,4 @@
<h2><%= @author.nil? ? l(:label_activity) : l(:label_user_activity, link_to_user(@author)) %></h2>
<h2><%= @author.nil? ? l(:label_activity) : l(:label_user_activity, link_to_user(@author)).html_safe %></h2>
<p class="subtitle"><%= l(:label_date_from_to, :start => format_date(@date_to - @days), :end => format_date(@date_to-1)) %></p>
<div id="activity">

View File

@@ -76,14 +76,14 @@ ar:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "kB"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"

View File

@@ -78,7 +78,7 @@ bg:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -89,7 +89,7 @@ bs:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -80,7 +80,7 @@ ca:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -81,14 +81,14 @@ cs:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:
byte:
one: "Bajt"
other: "Bajtů"
kb: "kB"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"

View File

@@ -91,7 +91,7 @@ da:
format:
# separator:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -93,7 +93,7 @@ de:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:
@@ -1036,7 +1036,7 @@ de:
label_copy_attachments: Anhänge Kopieren
label_item_position: "%{position}/%{count}"
label_completed_versions: Abgeschlossene Versionen
field_multiple: Mehrer Werte
field_multiple: Mehrere Werte
setting_commit_cross_project_ref: Erlauben auf Tickets aller anderen Projekte zu referenzieren
text_issue_conflict_resolution_add_notes: Meine Änderungen übernehmen und alle anderen Änderungen verwerfen
text_issue_conflict_resolution_overwrite: Meine Änderungen trotzdem übernehmen (vorherige Notizen bleiben erhalten aber manche können überschrieben werden)

View File

@@ -77,7 +77,7 @@ el:
precision: 3
human:
format:
precision: 1
precision: 3
delimiter: ""
storage_units:
format: "%n %u"

View File

@@ -81,14 +81,14 @@ en-GB:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "kB"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"

View File

@@ -77,14 +77,14 @@ en:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "kB"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"

View File

@@ -48,7 +48,7 @@ es:
# These three are to override number.format and are optional
# separator:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -94,7 +94,7 @@ et:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -79,7 +79,7 @@ eu:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -77,7 +77,7 @@ fa:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -64,7 +64,7 @@ fi:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -91,7 +91,7 @@ fr:
format: '%n %u'
human:
format:
precision: 2
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -32,7 +32,7 @@ gl:
format:
# separator:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -72,7 +72,7 @@ hr:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -97,7 +97,7 @@
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -78,7 +78,7 @@ id:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -96,7 +96,7 @@ ja:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -119,7 +119,7 @@ ko:
# These three are to override number.format and are optional
# separator:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -109,7 +109,7 @@ lt:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
# Storage units output formatting.
# %u is the storage unit, %n is the number (default: 2 MB)

View File

@@ -72,7 +72,7 @@ lv:
human:
format:
delimiter: " "
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -77,7 +77,7 @@ mk:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -75,7 +75,7 @@ mn:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -74,7 +74,7 @@ nl:
precision: 3
human:
format:
precision: 1
precision: 3
delimiter: ""
storage_units:
format: "%n %u"

View File

@@ -63,7 +63,7 @@
other: "nesten %{count} år"
number:
format:
precision: 2
precision: 3
separator: "."
delimiter: ","
currency:

View File

@@ -21,7 +21,7 @@ pl:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -97,7 +97,7 @@ pt-BR:
delimiter: '.'
human:
format:
precision: 1
precision: 3
delimiter: '.'
storage_units:
format: "%n %u"

View File

@@ -89,7 +89,7 @@ pt:
delimiter: ''
human:
format:
precision: 1
precision: 3
delimiter: ''
storage_units:
format: "%n %u"

View File

@@ -73,7 +73,7 @@ ro:
human:
format:
precision: 1
precision: 3
delimiter: ""
storage_units:
format: "%n %u"

View File

@@ -68,7 +68,7 @@ ru:
human:
format:
delimiter: ""
precision: 2
precision: 3
# Rails 2.2
# storage_units: [байт, КБ, МБ, ГБ, ТБ]

View File

@@ -75,7 +75,7 @@ sk:
human:
format:
precision: 1
precision: 3
delimiter: ""
storage_units:
format: "%n %u"

View File

@@ -75,7 +75,7 @@ sl:
precision: 3
human:
format:
precision: 1
precision: 3
delimiter: ""
storage_units:
format: "%n %u"

View File

@@ -77,14 +77,14 @@ sq:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "kB"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"

View File

@@ -77,7 +77,7 @@ sr-YU:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -77,7 +77,7 @@ sr:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -47,14 +47,14 @@ sv:
# These three are to override number.format and are optional
# separator:
delimiter: ""
# precision: 1
precision: 3
storage_units:
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "kB"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"

View File

@@ -74,7 +74,7 @@ th:
precision: 3
human:
format:
precision: 1
precision: 3
delimiter: ""
storage_units:
format: "%n %u"

View File

@@ -99,7 +99,7 @@ tr:
format:
delimiter: '.'
separator: ','
precision: 2
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -74,7 +74,7 @@ uk:
precision: 3
human:
format:
precision: 1
precision: 3
delimiter: ""
storage_units:
format: "%n %u"

View File

@@ -48,7 +48,7 @@ vi:
# These three are to override number.format and are optional
# separator:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:

View File

@@ -87,7 +87,7 @@
# 下列三個選項設定, 若有設定值將會取代 number.format 成為預設值
# separator:
delimiter: ""
precision: 1
precision: 3
# 儲存單位輸出格式.
# %u 是儲存單位, %n 是數值 (預設值: 2 MB)
storage_units:

View File

@@ -79,14 +79,14 @@ zh:
human:
format:
delimiter: ""
precision: 1
precision: 3
storage_units:
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "kB"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"

View File

@@ -4,6 +4,24 @@ Redmine - project management software
Copyright (C) 2006-2012 Jean-Philippe Lang
http://www.redmine.org/
== 2012-06-18 v2.0.3
* Defect #10688: PDF export from Wiki - Problems with tables
* Defect #11061: Cannot choose commit versions to view differences in Git/Mercurial repository view
* Defect #11065: E-Mail submitted tickets: German umlauts in 'Subject' get malformed (ruby 1.8)
* Defect #11098: Default priorities have the same position and can't be reordered
* Defect #11105: <% content_for :header_tags do %> doesn't work inside hook
* Defect #11112: REST API - custom fields in POST/PUT ignored for time_entries
* Defect #11118: "Maximum file size" displayed on upload forms is incorrect
* Defect #11124: Link to user is escaped in activity title
* Defect #11133: Wiki-page section edit link can point to incorrect section
* Defect #11160: SQL Error on time report if a custom field has multiple values for an entry
* Defect #11170: Topics sort order is broken in Redmine 2.x
* Defect #11178: Spent time sorted by date-descending order lists same-date entries in physical order (not-reverse)
* Defect #11185: Redmine fails to delete a project with parent/child task
* Feature #11162: Upgrade to Rails 3.2.6
* Patch #11113: Small glitch in German localization
== 2012-06-05 v2.0.2
* Defect #11032: Project list is not shown when "For any event on the selected projects only..." is selected on user edit panel

View File

@@ -131,6 +131,15 @@ module ActiveRecord
when 'lowest'
move_to_bottom
end
reset_positions_in_list
end
def reset_positions_in_list
acts_as_list_class.where(scope_condition).reorder("#{position_column} ASC, id ASC").each_with_index do |item, i|
unless item.send(position_column) == (i + 1)
acts_as_list_class.update_all({position_column => (i + 1)}, {:id => item.id})
end
end
end
# Removes the item from the list.
@@ -209,7 +218,7 @@ module ActiveRecord
def bottom_item(except = nil)
conditions = scope_condition
conditions = "#{conditions} AND #{self.class.primary_key} != #{except.id}" if except
acts_as_list_class.find(:first, :conditions => conditions, :order => "#{position_column} DESC")
acts_as_list_class.where(conditions).reorder("#{position_column} DESC").first
end
# Forces item to assume the bottom position in the list.

View File

@@ -283,6 +283,7 @@ class TCPDF
@state ||= 0
@tableborder ||= 0
@tdbegin ||= false
@tdtext ||= ''
@tdwidth ||= 0
@tdheight ||= 0
@tdalign ||= "L"
@@ -3510,28 +3511,12 @@ class TCPDF
else
#Text
if (@href)
if (@tdbegin)
element.gsub!(/[\t\r\n\f]/, "");
@tdtext << element.gsub(/&nbsp;/, " ");
elsif (@href)
element.gsub!(/[\t\r\n\f]/, "");
addHtmlLink(@href, element, fill);
elsif (@tdbegin)
element.gsub!(/[\t\r\n\f]/, "");
element.gsub!(/&nbsp;/, " ");
base_page = @page;
base_x = @x;
base_y = @y;
MultiCell(@tdwidth, @tdheight, unhtmlentities(element.strip), @tableborder, @tdalign, @tdfill, 1);
tr_end = @t_cells[@table_id][@tr_id][@td_id]['j1'] + 1;
if @max_td_page[tr_end].nil? or (@max_td_page[tr_end] < @page)
@max_td_page[tr_end] = @page
@max_td_y[tr_end] = @y
elsif (@max_td_page[tr_end] == @page)
@max_td_y[tr_end] = @y if @max_td_y[tr_end].nil? or (@max_td_y[tr_end] < @y)
end
@page = base_page;
@x = base_x + @tdwidth;
@y = base_y;
elsif (@pre_state == true and element.length > 0)
Write(@lasth, unhtmlentities(element), '', fill);
elsif (element.strip.length > 0)
@@ -3825,6 +3810,7 @@ class TCPDF
@x += 5;
when 'table'
Ln();
if @default_table_columns < @max_table_columns[@table_id]
@table_columns = @max_table_columns[@table_id];
else
@@ -3921,6 +3907,11 @@ class TCPDF
when 'img'
if (!attrs['src'].nil?)
# Don't generates image inside table tag
if (@tdbegin)
@tdtext << attrs['src'];
return
end
# Only generates image include a pdf if RMagick is avalaible
unless Object.const_defined?(:Magick)
Write(@lasth, attrs['src'], '', fill);
@@ -4079,6 +4070,23 @@ class TCPDF
Ln();
when 'td','th'
base_page = @page;
base_x = @x;
base_y = @y;
MultiCell(@tdwidth, @tdheight, unhtmlentities(@tdtext.strip), @tableborder, @tdalign, @tdfill, 1);
tr_end = @t_cells[@table_id][@tr_id][@td_id]['j1'] + 1;
if @max_td_page[tr_end].nil? or (@max_td_page[tr_end] < @page)
@max_td_page[tr_end] = @page
@max_td_y[tr_end] = @y
elsif (@max_td_page[tr_end] == @page)
@max_td_y[tr_end] = @y if @max_td_y[tr_end].nil? or (@max_td_y[tr_end] < @y)
end
@page = base_page;
@x = base_x + @tdwidth;
@y = base_y;
@tdtext = '';
@tdbegin = false;
@tdwidth = 0;
@tdheight = 0;
@@ -4126,7 +4134,6 @@ class TCPDF
@l_margin -= 5;
@r_margin -= 5;
@tableborder=0;
Ln();
@table_id += 1;
when 'strong'

View File

@@ -138,21 +138,21 @@ module Redmine
# Add list and boolean custom fields as available criteria
custom_fields = (@project.nil? ? IssueCustomField.for_all : @project.all_issue_custom_fields)
custom_fields.select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
@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)",
@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)",
:format => cf.field_format,
:label => cf.name}
end if @project
# Add list and boolean time entry custom fields
TimeEntryCustomField.find(:all).select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
@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)",
@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)",
:format => cf.field_format,
:label => cf.name}
end
# Add list and boolean time entry activity custom fields
TimeEntryActivityCustomField.find(:all).select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
@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)",
@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)",
:format => cf.field_format,
:label => cf.name}
end

View File

@@ -107,7 +107,13 @@ module Redmine
#
def self.render_on(hook, options={})
define_method hook do |context|
context[:controller].send(:render_to_string, {:locals => context}.merge(options))
if context[:hook_caller].respond_to?(:render)
context[:hook_caller].send(:render, {:locals => context}.merge(options))
elsif context[:controller].is_a?(ActionController::Base)
context[:controller].send(:render_to_string, {:locals => context}.merge(options))
else
raise "Cannot render #{self.name} hook from #{context[:hook_caller].class.name}"
end
end
end
@@ -138,14 +144,15 @@ module Redmine
# * project => current project
# * request => Request instance
# * controller => current Controller instance
# * hook_caller => object that called the hook
#
module Helper
def call_hook(hook, context={})
if is_a?(ActionController::Base)
default_context = {:controller => self, :project => @project, :request => request}
default_context = {:controller => self, :project => @project, :request => request, :hook_caller => self}
Redmine::Hook.call_hook(hook, default_context.merge(context))
else
default_context = { :project => @project }
default_context = { :project => @project, :hook_caller => self }
default_context[:controller] = controller if respond_to?(:controller)
default_context[:request] = request if respond_to?(:request)
Redmine::Hook.call_hook(hook, default_context.merge(context)).join(' ').html_safe

View File

@@ -4,7 +4,7 @@ module Redmine
module VERSION #:nodoc:
MAJOR = 2
MINOR = 0
TINY = 2
TINY = 3
# Branch values:
# * official release: nil

View File

@@ -69,7 +69,7 @@ module Redmine
l = 1
started = false
ended = false
text.scan(/(((?:.*?)(\A|\r?\n\r?\n))(h(\d+)(#{A}#{C})\.(?::(\S+))? (.*?)$)|.*)/m).each do |all, content, lf, heading, level|
text.scan(/(((?:.*?)(\A|\r?\n\s*\r?\n))(h(\d+)(#{A}#{C})\.(?::(\S+))? (.*?)$)|.*)/m).each do |all, content, lf, heading, level|
if heading.nil?
if ended
after << all

View File

@@ -4,7 +4,7 @@ table.revision-info td {
padding: 0px;
}
div.revision-graph { position: absolute; }
div.revision-graph { position: absolute; min-width: 1px; }
div.changeset-changes ul { margin: 0; padding: 0; }
div.changeset-changes ul > ul { margin-left: 18px; padding: 0; }

View File

@@ -0,0 +1,11 @@
Content-Type: application/ms-tnef; name="winmail.dat"
Content-Transfer-Encoding: binary
From: John Smith <JSmith@somenet.foo>
To: "redmine@somenet.foo" <redmine@somenet.foo>
Subject: =?iso-8859-1?Q?Testmail_from_Webmail:_=E4_=F6_=FC...?=
Date: Fri, 1 Jun 2012 14:39:38 +0200
Message-ID: <87C31D42249DD0489D1A1444E3232DD7019D6183@foo.bar>
Accept-Language: de-CH, en-US
Content-Language: de-CH
Fixture

View File

@@ -73,13 +73,19 @@ class ActivitiesControllerTest < ActionController::TestCase
end
def test_user_index
@request.session[:user_id] = 1
get :index, :user_id => 2
assert_response :success
assert_template 'index'
assert_not_nil assigns(:events_by_day)
assert_select 'h2 a[href=/users/2]', :text => 'John Smith'
i1 = Issue.find(1)
d1 = User.find(1).time_to_date(i1.created_on)
assert_tag :tag => "h3",
:content => /#{3.day.ago.to_date.day}/,
:content => /#{d1.day}/,
:sibling => { :tag => "dl",
:child => { :tag => "dt",
:attributes => { :class => /issue/ },

View File

@@ -55,6 +55,20 @@ class BoardsControllerTest < ActionController::TestCase
assert_not_nil assigns(:topics)
end
def test_show_should_display_sticky_messages_first
Message.update_all(:sticky => 0)
Message.update_all({:sticky => 1}, {:id => 1})
get :show, :project_id => 1, :id => 1
assert_response :success
topics = assigns(:topics)
assert_not_nil topics
assert topics.size > 1, "topics size was #{topics.size}"
assert topics.first.sticky?
assert topics.first.updated_on < topics.second.updated_on
end
def test_show_with_permission_should_display_the_new_message_form
@request.session[:user_id] = 2
get :show, :project_id => 1, :id => 1

View File

@@ -87,6 +87,16 @@ class RepositoriesGitControllerTest < ActionController::TestCase
end
if File.directory?(REPOSITORY_PATH)
## Ruby uses ANSI api to fork a process on Windows.
## Japanese Shift_JIS and Traditional Chinese Big5 have 0x5c(backslash) problem
## and these are incompatible with ASCII.
## Git for Windows (msysGit) changed internal API from ANSI to Unicode in 1.7.10
## http://code.google.com/p/msysgit/issues/detail?id=80
## So, Latin-1 path tests fail on Japanese Windows
WINDOWS_PASS = (Redmine::Platform.mswin? &&
Redmine::Scm::Adapters::GitAdapter.client_version_above?([1, 7, 10]))
WINDOWS_SKIP_STR = "TODO: This test fails in Git for Windows above 1.7.10"
def test_get_new
@request.session[:user_id] = 1
@project.repository.destroy
@@ -214,6 +224,8 @@ class RepositoriesGitControllerTest < ActionController::TestCase
def test_entry_show_latin_1
if @ruby19_non_utf8_pass
puts_ruby19_non_utf8_pass()
elsif WINDOWS_PASS
puts WINDOWS_SKIP_STR
elsif JRUBY_SKIP
puts JRUBY_SKIP_STR
else
@@ -435,6 +447,8 @@ class RepositoriesGitControllerTest < ActionController::TestCase
def test_annotate_latin_1
if @ruby19_non_utf8_pass
puts_ruby19_non_utf8_pass()
elsif WINDOWS_PASS
puts WINDOWS_SKIP_STR
elsif JRUBY_SKIP
puts JRUBY_SKIP_STR
else

View File

@@ -80,6 +80,16 @@ class TimeEntryReportsControllerTest < ActionController::TestCase
assert_equal "162.90", "%.2f" % assigns(:report).total_hours
end
def test_report_custom_field_criteria_with_multiple_values
field = TimeEntryCustomField.create!(:name => 'multi', :field_format => 'list', :possible_values => ['value1', 'value2'])
entry = TimeEntry.create!(:project => Project.find(1), :hours => 1, :activity_id => 10, :user => User.find(2), :spent_on => Date.today)
CustomValue.create!(:customized => entry, :custom_field => field, :value => 'value1')
CustomValue.create!(:customized => entry, :custom_field => field, :value => 'value2')
get :report, :project_id => 1, :columns => 'day', :criteria => ["cf_#{field.id}"]
assert_response :success
end
def test_report_one_day
get :report, :project_id => 1, :columns => 'day', :from => "2007-03-23", :to => "2007-03-23", :criteria => ["member", "activity"]
assert_response :success

View File

@@ -568,6 +568,20 @@ class TimelogControllerTest < ActionController::TestCase
:attributes => {:action => "/projects/ecookbook/issues/1/time_entries", :id => 'query_form'}
end
def test_index_should_sort_by_spent_on_and_created_on
t1 = TimeEntry.create!(:user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-16', :created_on => '2012-06-16 20:00:00', :activity_id => 10)
t2 = TimeEntry.create!(:user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-16', :created_on => '2012-06-16 20:05:00', :activity_id => 10)
t3 = TimeEntry.create!(:user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-15', :created_on => '2012-06-16 20:10:00', :activity_id => 10)
get :index, :project_id => 1, :from => '2012-06-15', :to => '2012-06-16'
assert_response :success
assert_equal [t2, t1, t3], assigns(:entries)
get :index, :project_id => 1, :from => '2012-06-15', :to => '2012-06-16', :sort => 'spent_on'
assert_response :success
assert_equal [t3, t1, t2], assigns(:entries)
end
def test_index_atom_feed
get :index, :project_id => 1, :format => 'atom'
assert_response :success

View File

@@ -79,6 +79,21 @@ class ApiTest::TimeEntriesTest < ActionController::IntegrationTest
assert_equal 3.5, entry.hours
assert_equal TimeEntryActivity.find(11), entry.activity
end
should "accept custom fields" do
field = TimeEntryCustomField.create!(:name => 'Test', :field_format => 'string')
assert_difference 'TimeEntry.count' do
post '/time_entries.xml', {:time_entry => {
:issue_id => '1', :spent_on => '2010-12-02', :hours => '3.5', :activity_id => '11', :custom_fields => [{:id => field.id.to_s, :value => 'accepted'}]
}}, credentials('jsmith')
end
assert_response :created
assert_equal 'application/xml', @response.content_type
entry = TimeEntry.first(:order => 'id DESC')
assert_equal 'accepted', entry.custom_field_value(field)
end
end
context "with project_id" do

View File

@@ -35,6 +35,17 @@ class MenuManagerTest < ActionController::IntegrationTest
end
end
class ContentForInsideHook < Redmine::Hook::ViewListener
render_on :view_welcome_index_left, :inline => <<-VIEW
<% content_for :header_tags do %>
<%= javascript_include_tag 'test_plugin.js', :plugin => 'test_plugin' %>
<%= stylesheet_link_tag 'test_plugin.css', :plugin => 'test_plugin' %>
<% end %>
<p>ContentForInsideHook content</p>
VIEW
end
def setup
Redmine::Hook.clear_listeners
end
@@ -64,4 +75,16 @@ class MenuManagerTest < ActionController::IntegrationTest
assert_select 'div#main'
assert_select 'div#main.nosidebar', 0
end
def test_hook_with_content_for_should_append_content
Redmine::Hook.add_listener(ContentForInsideHook)
get '/'
assert_response :success
assert_select 'p', :text => 'ContentForInsideHook content'
assert_select 'head' do
assert_select 'script[src=/plugin_assets/test_plugin/javascripts/test_plugin.js]'
assert_select 'link[href=/plugin_assets/test_plugin/stylesheets/test_plugin.css]'
end
end
end

View File

@@ -34,5 +34,30 @@ class IssuePriorityTest < ActiveSupport::TestCase
def test_option_name
assert_equal :enumeration_issue_priorities, IssuePriority.new.option_name
end
end
def test_should_be_created_at_last_position
IssuePriority.delete_all
priorities = [1, 2, 3].map {|i| IssuePriority.create!(:name => "P#{i}")}
assert_equal [1, 2, 3], priorities.map(&:position)
end
def test_reset_positions_in_list_should_set_sequential_positions
IssuePriority.delete_all
priorities = [1, 2, 3].map {|i| IssuePriority.create!(:name => "P#{i}")}
priorities[0].update_attribute :position, 4
priorities[1].update_attribute :position, 2
priorities[2].update_attribute :position, 7
assert_equal [4, 2, 7], priorities.map(&:reload).map(&:position)
priorities[0].reset_positions_in_list
assert_equal [2, 1, 3], priorities.map(&:reload).map(&:position)
end
def test_moving_in_list_should_reset_positions
priority = IssuePriority.first
priority.expects(:reset_positions_in_list).once
priority.move_to = 'higher'
end
end

View File

@@ -761,6 +761,30 @@ class IssueTest < ActiveSupport::TestCase
assert_nil TimeEntry.find_by_issue_id(1)
end
def test_destroying_a_deleted_issue_should_not_raise_an_error
issue = Issue.find(1)
Issue.find(1).destroy
assert_nothing_raised do
assert_no_difference 'Issue.count' do
issue.destroy
end
assert issue.destroyed?
end
end
def test_destroying_a_stale_issue_should_not_raise_an_error
issue = Issue.find(1)
Issue.find(1).update_attribute :subject, "Updated"
assert_nothing_raised do
assert_difference 'Issue.count', -1 do
issue.destroy
end
assert issue.destroyed?
end
end
def test_blocked
blocked_issue = Issue.find(9)
blocking_issue = Issue.find(10)

View File

@@ -89,7 +89,7 @@ class Redmine::Hook::ManagerTest < ActionView::TestCase
def test_call_hook_with_context
@hook_module.add_listener(TestHook3)
assert_equal ['Context keys: bar, controller, foo, project, request.'],
assert_equal ['Context keys: bar, controller, foo, hook_caller, project, request.'],
hook_helper.call_hook(:view_layouts_base_html_head, :foo => 1, :bar => 'a')
end

View File

@@ -121,7 +121,8 @@ class Redmine::I18nTest < ActiveSupport::TestCase
valid_languages.each do |lang|
set_language_if_valid lang
assert_nothing_raised "#{lang} failure" do
number_to_human_size(1024*1024*4)
size = number_to_human_size(257024)
assert_match /251/, size
end
end
end

View File

@@ -13,8 +13,12 @@ begin
## Ruby uses ANSI api to fork a process on Windows.
## Japanese Shift_JIS and Traditional Chinese Big5 have 0x5c(backslash) problem
## and these are incompatible with ASCII.
# WINDOWS_PASS = Redmine::Platform.mswin?
WINDOWS_PASS = false
## Git for Windows (msysGit) changed internal API from ANSI to Unicode in 1.7.10
## http://code.google.com/p/msysgit/issues/detail?id=80
## So, Latin-1 path tests fail on Japanese Windows
WINDOWS_PASS = (Redmine::Platform.mswin? &&
Redmine::Scm::Adapters::GitAdapter.client_version_above?([1, 7, 10]))
WINDOWS_SKIP_STR = "TODO: This test fails in Git for Windows above 1.7.10"
## Git, Mercurial and CVS path encodings are binary.
## Subversion supports URL encoding for path.
@@ -391,7 +395,7 @@ begin
def test_latin_1_path
if WINDOWS_PASS
#
puts WINDOWS_SKIP_STR
elsif JRUBY_SKIP
puts JRUBY_SKIP_STR
else
@@ -453,7 +457,7 @@ begin
def test_entries_latin_1_dir
if WINDOWS_PASS
#
puts WINDOWS_SKIP_STR
elsif JRUBY_SKIP
puts JRUBY_SKIP_STR
else

View File

@@ -394,6 +394,31 @@ Nulla nunc nisi, egestas in ornare vel, posuere ac libero."]
@formatter.new(text).update_section(3, replacement)
end
def test_get_section_should_support_lines_with_spaces_before_heading
# the lines after Content 2 and Heading 4 contain a space
text = <<-STR
h1. Heading 1
Content 1
h1. Heading 2
Content 2
h1. Heading 3
Content 3
h1. Heading 4
Content 4
STR
[1, 2, 3, 4].each do |index|
assert_match /\Ah1. Heading #{index}.+Content #{index}/m, @formatter.new(text).get_section(index).first
end
end
private
def assert_html_output(to_test, expect_paragraph = true)

View File

@@ -347,6 +347,15 @@ class MailHandlerTest < ActiveSupport::TestCase
assert_equal 'caaf384198bcbc9563ab5c058acd73cd', attachment.digest
end
def test_add_issue_with_iso_8859_1_subject
issue = submit_email(
'subject_as_iso-8859-1.eml',
:issue => {:project => 'ecookbook'}
)
assert_kind_of Issue, issue
assert_equal 'Testmail from Webmail: ä ö ü...', issue.subject
end
def test_should_ignore_emails_from_locked_users
User.find(2).lock!

View File

@@ -190,6 +190,18 @@ class ProjectTest < ActiveSupport::TestCase
assert_nil Issue.first(:conditions => {:project_id => @ecookbook.id})
end
def test_destroy_should_destroy_subtasks
issues = (0..2).to_a.map {Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :subject => 'test')}
issues[0].update_attribute :parent_issue_id, issues[1].id
issues[2].update_attribute :parent_issue_id, issues[1].id
assert_equal 2, issues[1].children.count
assert_nothing_raised do
Project.find(1).destroy
end
assert Issue.find_all_by_id(issues.map(&:id)).empty?
end
def test_destroying_root_projects_should_clear_data
Project.roots.each do |root|
root.destroy

View File

@@ -31,12 +31,6 @@ class RepositoryGitTest < ActiveSupport::TestCase
FELIX_HEX = "Felix Sch\xC3\xA4fer"
CHAR_1_HEX = "\xc3\x9c"
## Ruby uses ANSI api to fork a process on Windows.
## Japanese Shift_JIS and Traditional Chinese Big5 have 0x5c(backslash) problem
## and these are incompatible with ASCII.
# WINDOWS_PASS = Redmine::Platform.mswin?
WINDOWS_PASS = false
## Git, Mercurial and CVS path encodings are binary.
## Subversion supports URL encoding for path.
## Redmine Mercurial adapter and extension use URL encoding.
@@ -85,6 +79,16 @@ class RepositoryGitTest < ActiveSupport::TestCase
end
if File.directory?(REPOSITORY_PATH)
## Ruby uses ANSI api to fork a process on Windows.
## Japanese Shift_JIS and Traditional Chinese Big5 have 0x5c(backslash) problem
## and these are incompatible with ASCII.
## Git for Windows (msysGit) changed internal API from ANSI to Unicode in 1.7.10
## http://code.google.com/p/msysgit/issues/detail?id=80
## So, Latin-1 path tests fail on Japanese Windows
WINDOWS_PASS = (Redmine::Platform.mswin? &&
Redmine::Scm::Adapters::GitAdapter.client_version_above?([1, 7, 10]))
WINDOWS_SKIP_STR = "TODO: This test fails in Git for Windows above 1.7.10"
def test_scm_available
klass = Repository::Git
assert_equal "Git", klass.scm_name
@@ -400,7 +404,9 @@ class RepositoryGitTest < ActiveSupport::TestCase
'61b685fbe55ab05b5ac68402d5720c1a6ac973d1',
], changesets.collect(&:revision)
if JRUBY_SKIP
if WINDOWS_PASS
puts WINDOWS_SKIP_STR
elsif JRUBY_SKIP
puts JRUBY_SKIP_STR
else
# latin-1 encoding path
@@ -421,7 +427,7 @@ class RepositoryGitTest < ActiveSupport::TestCase
def test_latest_changesets_latin_1_dir
if WINDOWS_PASS
#
puts WINDOWS_SKIP_STR
elsif JRUBY_SKIP
puts JRUBY_SKIP_STR
else