[DRE-commits] [SCM] redmine.git branch, master-experimental, updated. debian/1.4.4+dfsg1-1-44-ga5600f5

Ondřej Surý ondrej at sury.org
Mon Dec 10 11:34:46 UTC 2012


The following commit has been merged in the master-experimental branch:
commit 492d89ae05453ed01e9d26182fece62b4401cbe4
Author: Ondřej Surý <ondrej at sury.org>
Date:   Mon Dec 10 12:01:56 2012 +0100

    Imported Upstream version 2.1.4

diff --git a/.gitignore b/.gitignore
index 683c0f0..1dc12fc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,7 @@
 /public/plugin_assets
 /tmp/*
 /tmp/cache/*
+/tmp/pdf/*
 /tmp/sessions/*
 /tmp/sockets/*
 /tmp/test/*
diff --git a/.hgignore b/.hgignore
index a84c9a8..cd27d0a 100644
--- a/.hgignore
+++ b/.hgignore
@@ -21,6 +21,7 @@ public/dispatch.*
 public/plugin_assets
 tmp/*
 tmp/cache/*
+tmp/pdf/*
 tmp/sessions/*
 tmp/sockets/*
 tmp/test/*
diff --git a/Gemfile b/Gemfile
index 33a2995..af1b92f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -41,7 +41,7 @@ end
 
 platforms :mri_18, :mingw_18 do
   group :mysql do
-    gem "mysql"
+    gem "mysql", "~> 2.8.1"
   end
 end
 
diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb
index e9d0608..29d06d2 100644
--- a/app/controllers/wiki_controller.rb
+++ b/app/controllers/wiki_controller.rb
@@ -239,7 +239,7 @@ class WikiController < ApplicationController
 
   # Export wiki to a single pdf or html file
   def export
-    @pages = @wiki.pages.all(:order => 'title', :include => [:content, :attachments], :limit => 75)
+    @pages = @wiki.pages.all(:order => 'title', :include => [:content, :attachments])
     respond_to do |format|
       format.html {
         export = render_to_string :action => 'export_multiple', :layout => false
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index bcccfd2..50daa5e 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -795,11 +795,10 @@ module ApplicationHelper
                 if repository && User.current.allowed_to?(:browse_repository, project)
                   name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
                   path, rev, anchor = $1, $3, $5
-                  link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:controller => 'repositories', :action => 'entry', :id => project, :repository_id => repository.identifier_param,
+                  link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:controller => 'repositories', :action => (prefix == 'export' ? 'raw' : 'entry'), :id => project, :repository_id => repository.identifier_param,
                                                           :path => to_path_param(path),
                                                           :rev => rev,
-                                                          :anchor => anchor,
-                                                          :format => (prefix == 'export' ? 'raw' : nil)},
+                                                          :anchor => anchor},
                                                          :class => (prefix == 'export' ? 'source download' : 'source')
                 end
               end
diff --git a/app/helpers/context_menus_helper.rb b/app/helpers/context_menus_helper.rb
index 15e670c..940f4ce 100644
--- a/app/helpers/context_menus_helper.rb
+++ b/app/helpers/context_menus_helper.rb
@@ -26,8 +26,8 @@ module ContextMenusHelper
     end
     if options.delete(:disabled)
       options.delete(:method)
-      options.delete(:confirm)
-      options.delete(:onclick)
+      options.delete(:data)
+      options[:onclick] = 'return false;'
       options[:class] << ' disabled'
       url = '#'
     end
diff --git a/app/models/mail_handler.rb b/app/models/mail_handler.rb
index 8a5925c..e53c877 100644
--- a/app/models/mail_handler.rb
+++ b/app/models/mail_handler.rb
@@ -124,6 +124,7 @@ class MailHandler < ActionMailer::Base
 
   def dispatch
     headers = [email.in_reply_to, email.references].flatten.compact
+    subject = email.subject.to_s
     if headers.detect {|h| h.to_s =~ MESSAGE_ID_RE}
       klass, object_id = $1, $2.to_i
       method_name = "receive_#{klass}_reply"
@@ -132,9 +133,9 @@ class MailHandler < ActionMailer::Base
       else
         # ignoring it
       end
-    elsif m = email.subject.match(ISSUE_REPLY_SUBJECT_RE)
+    elsif m = subject.match(ISSUE_REPLY_SUBJECT_RE)
       receive_issue_reply(m[1].to_i)
-    elsif m = email.subject.match(MESSAGE_REPLY_SUBJECT_RE)
+    elsif m = subject.match(MESSAGE_REPLY_SUBJECT_RE)
       receive_message_reply(m[1].to_i)
     else
       dispatch_to_default
@@ -244,9 +245,26 @@ class MailHandler < ActionMailer::Base
   def add_attachments(obj)
     if email.attachments && email.attachments.any?
       email.attachments.each do |attachment|
+        filename = attachment.filename
+        unless filename.respond_to?(:encoding)
+          # try to reencode to utf8 manually with ruby1.8
+          h = attachment.header['Content-Disposition']
+          unless h.nil?
+            begin
+              if m = h.value.match(/filename\*[0-9\*]*=([^=']+)'/)
+                filename = Redmine::CodesetUtil.to_utf8(filename, m[1])
+              elsif m = h.value.match(/filename=.*=\?([^\?]+)\?[BbQq]\?/)
+                # http://tools.ietf.org/html/rfc2047#section-4
+                filename = Redmine::CodesetUtil.to_utf8(filename, m[1])
+              end
+            rescue
+              # nop
+            end
+          end
+        end
         obj.attachments << Attachment.create(:container => obj,
                           :file => attachment.decoded,
-                          :filename => attachment.filename,
+                          :filename => filename,
                           :author => user,
                           :content_type => attachment.mime_type)
       end
@@ -373,7 +391,8 @@ class MailHandler < ActionMailer::Base
       # try to reencode to utf8 manually with ruby1.8
       begin
         if h = email.header[:subject]
-          if m = h.value.match(/^=\?([^\?]+)\?/)
+          # http://tools.ietf.org/html/rfc2047#section-4
+          if m = h.value.match(/=\?([^\?]+)\?[BbQq]\?/)
             subject = Redmine::CodesetUtil.to_utf8(subject, m[1])
           end
         end
diff --git a/app/models/principal.rb b/app/models/principal.rb
index 2b89e16..90b56b6 100644
--- a/app/models/principal.rb
+++ b/app/models/principal.rb
@@ -30,13 +30,13 @@ class Principal < ActiveRecord::Base
     if q.blank?
       {}
     else
-      q = q.to_s.downcase
+      q = q.to_s
       pattern = "%#{q}%"
-      sql = "LOWER(login) LIKE :p OR LOWER(firstname) LIKE :p OR LOWER(lastname) LIKE :p OR LOWER(mail) LIKE :p"
+      sql = "LOWER(login) LIKE LOWER(:p) OR LOWER(firstname) LIKE LOWER(:p) OR LOWER(lastname) LIKE LOWER(:p) OR LOWER(mail) LIKE LOWER(:p)"
       params = {:p => pattern}
       if q =~ /^(.+)\s+(.+)$/
         a, b = "#{$1}%", "#{$2}%"
-        sql << " OR (LOWER(firstname) LIKE :a AND LOWER(lastname) LIKE :b) OR (LOWER(firstname) LIKE :b AND LOWER(lastname) LIKE :a)"
+        sql << " OR (LOWER(firstname) LIKE LOWER(:a) AND LOWER(lastname) LIKE LOWER(:b)) OR (LOWER(firstname) LIKE LOWER(:b) AND LOWER(lastname) LIKE LOWER(:a))"
         params.merge!(:a => a, :b => b)
       end
       {:conditions => [sql, params]}
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 9a6891a..1ef73a4 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -427,5 +427,9 @@ class Repository < ActiveRecord::Base
     connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
     connection.delete("DELETE FROM #{cp} WHERE #{cp}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
     connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
+    clear_extra_info_of_changesets
+  end
+
+  def clear_extra_info_of_changesets
   end
 end
diff --git a/app/models/repository/git.rb b/app/models/repository/git.rb
index 064a674..e07e7c8 100644
--- a/app/models/repository/git.rb
+++ b/app/models/repository/git.rb
@@ -255,4 +255,15 @@ class Repository::Git < Repository
       :order => 'committed_on DESC'
     )
   end
+
+  def clear_extra_info_of_changesets
+    return if extra_info.nil?
+    v = extra_info["extra_report_last_commit"]
+    write_attribute(:extra_info, nil)
+    h = {}
+    h["extra_report_last_commit"] = v
+    merge_extra_info(h)
+    self.save
+  end
+  private :clear_extra_info_of_changesets
 end
diff --git a/app/models/version.rb b/app/models/version.rb
index cd32da9..d633289 100644
--- a/app/models/version.rb
+++ b/app/models/version.rb
@@ -33,6 +33,7 @@ class Version < ActiveRecord::Base
   validates_format_of :effective_date, :with => /^\d{4}-\d{2}-\d{2}$/, :message => :not_a_date, :allow_nil => true
   validates_inclusion_of :status, :in => VERSION_STATUSES
   validates_inclusion_of :sharing, :in => VERSION_SHARINGS
+  validate :validate_version
 
   scope :named, lambda {|arg| { :conditions => ["LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip]}}
   scope :open, :conditions => {:status => 'open'}
@@ -275,4 +276,10 @@ class Version < ActiveRecord::Base
       progress
     end
   end
+
+  def validate_version
+    if effective_date.nil? && @attributes['effective_date'].present?
+      errors.add :effective_date, :not_a_date
+    end
+  end
 end
diff --git a/app/views/common/error.html.erb b/app/views/common/error.html.erb
index 77ead9d..5bb43de 100644
--- a/app/views/common/error.html.erb
+++ b/app/views/common/error.html.erb
@@ -3,6 +3,6 @@
 <% if @message.present? %>
   <p id="errorExplanation"><%=h @message %></p>
 <% end %>
-<p><a href="javascript:history.back()">Back</a></p>
+<p><a href="javascript:history.back()"><%= l(:button_back) %></a></p>
 
 <% html_title @status %>
diff --git a/app/views/issues/_list.html.erb b/app/views/issues/_list.html.erb
index 59f5964..e877f68 100644
--- a/app/views/issues/_list.html.erb
+++ b/app/views/issues/_list.html.erb
@@ -19,7 +19,7 @@
     <tr class="group open">
       <td colspan="<%= query.columns.size + 2 %>">
         <span class="expander" onclick="toggleRowGroup(this);"> </span>
-        <%= group.blank? ? 'None' : column_content(@query.group_by_column, issue) %> <span class="count">(<%= @issue_count_by_group[group] %>)</span>
+        <%= group.blank? ? l(:label_none) : column_content(@query.group_by_column, issue) %> <span class="count">(<%= @issue_count_by_group[group] %>)</span>
         <%= link_to_function("#{l(:button_collapse_all)}/#{l(:button_expand_all)}", "toggleAllRowGroups(this)", :class => 'toggle-all') %>
       </td>
     </tr>
diff --git a/app/views/projects/settings/_repositories.html.erb b/app/views/projects/settings/_repositories.html.erb
index 37e008b..4492165 100644
--- a/app/views/projects/settings/_repositories.html.erb
+++ b/app/views/projects/settings/_repositories.html.erb
@@ -14,7 +14,7 @@
     <tr class="<%= cycle 'odd', 'even' %>">
       <td>
       <%= link_to repository.identifier, 
-            {:controller => 'repositories', :action => 'show',:id => @project, :repository_id => repository.identifier_param} if repository.identifier_param.present? %>
+            {:controller => 'repositories', :action => 'show',:id => @project, :repository_id => repository.identifier_param} if repository.identifier.present? %>
       </td>
       <td align="center"><%= checked_image repository.is_default? %></td>
       <td><%=h repository.scm_name %></td>
diff --git a/app/views/repositories/revisions.html.erb b/app/views/repositories/revisions.html.erb
index 0395e4b..76ca629 100644
--- a/app/views/repositories/revisions.html.erb
+++ b/app/views/repositories/revisions.html.erb
@@ -1,7 +1,8 @@
 <div class="contextual">
 <%= form_tag(
        {:controller => 'repositories', :action => 'revision', :id => @project,
-        :repository_id => @repository.identifier_param}
+        :repository_id => @repository.identifier_param},
+       :method => :get
      ) do %>
   <%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 8 %>
   <%= submit_tag 'OK' %>
diff --git a/config/locales/de.yml b/config/locales/de.yml
index 24ad191..f228ebd 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -844,7 +844,7 @@ de:
   button_show: Anzeigen
 
   status_active: aktiv
-  status_registered: angemeldet
+  status_registered: nicht aktivierte
   status_locked: gesperrt
 
   version_status_open: offen
@@ -1017,8 +1017,8 @@ de:
   description_date_from: Startdatum eintragen
   description_date_to: Enddatum eintragen
 
-  label_parent_revision: Parent
-  label_child_revision: Child
+  label_parent_revision: Vorgänger
+  label_child_revision: Nachfolger
   error_scm_annotate_big_text_file: Der Eintrag kann nicht umgesetzt werden, da er die maximale Textlänge überschreitet.
   setting_default_issue_start_date_to_creation_date: Aktuelles Datum als Beginn für neue Tickets verwenden
   button_edit_section: Diesen Bereich bearbeiten
@@ -1068,14 +1068,14 @@ de:
   field_timeout: Auszeit (in Sekunden)
   setting_thumbnails_enabled: Vorschaubilder von Dateianhängen anzeigen
   setting_thumbnails_size: Größe der Vorschaubilder (in Pixel)
-  label_status_transitions: Status transitions
-  label_fields_permissions: Fields permissions
+  label_status_transitions: Statusänderungen
+  label_fields_permissions: Feldberechtigungen
   label_readonly: Nur-Lese-Zugriff
   label_required: Erforderlich
   text_repository_identifier_info: 'Kleinbuchstaben (a-z), Ziffern, Binde- und Unterstriche erlaubt.<br />Einmal gespeichert, kann die Kennung nicht mehr geändert werden.'
-  field_board_parent: Parent forum
-  label_attribute_of_project: Project's %{name}
-  label_attribute_of_author: Author's %{name}
-  label_attribute_of_assigned_to: Assignee's %{name}
-  label_attribute_of_fixed_version: Target version's %{name}
-  label_copy_subtasks: Copy subtasks
+  field_board_parent: Übergeordnetes Forum
+  label_attribute_of_project: "%{name} des Projekts"
+  label_attribute_of_author: "%{name} des Autors"
+  label_attribute_of_assigned_to: "%{name} des Bearbeiters"
+  label_attribute_of_fixed_version: "%{name} der Zielversion"
+  label_copy_subtasks: Unteraufgaben kopieren
diff --git a/config/locales/ru.yml b/config/locales/ru.yml
index f9c0a23..433167b 100644
--- a/config/locales/ru.yml
+++ b/config/locales/ru.yml
@@ -1152,27 +1152,27 @@ ru:
   setting_session_lifetime: Session maximum lifetime
   setting_session_timeout: Session inactivity timeout
   label_session_expiration: Session expiration
-  permission_close_project: Close / reopen the project
-  label_show_closed_projects: View closed projects
-  button_close: Close
-  button_reopen: Reopen
-  project_status_active: active
-  project_status_closed: closed
-  project_status_archived: archived
-  text_project_closed: This project is closed and read-only.
-  notice_user_successful_create: User %{id} created.
-  field_core_fields: Standard fields
+  permission_close_project: Закрывать / открывать проекты
+  label_show_closed_projects: Просматривать закрытые проекты
+  button_close: Сделать закрытым
+  button_reopen: Сделать открытым
+  project_status_active: открытые
+  project_status_closed: закрытые
+  project_status_archived: архивированные
+  text_project_closed: Проект закрыт и находиться в режиме только для чтения.
+  notice_user_successful_create: Пользователь %{id} создан.
+  field_core_fields: Стандартные поля
   field_timeout: Timeout (in seconds)
   setting_thumbnails_enabled: Display attachment thumbnails
   setting_thumbnails_size: Thumbnails size (in pixels)
-  label_status_transitions: Status transitions
-  label_fields_permissions: Fields permissions
-  label_readonly: Read-only
-  label_required: Required
+  label_status_transitions: Статус-переходы
+  label_fields_permissions: Права на изменения полей
+  label_readonly: Не изменяется
+  label_required: Обязательное
   text_repository_identifier_info: Допускаются только строчные латинские буквы (a-z), цифры, тире и подчеркивания.<br />После сохранения идентификатор изменить нельзя.
-  field_board_parent: Parent forum
+  field_board_parent: Родительский форум
   label_attribute_of_project: Project's %{name}
-  label_attribute_of_author: Author's %{name}
+  label_attribute_of_author: Имя автора %{name}
   label_attribute_of_assigned_to: Assignee's %{name}
   label_attribute_of_fixed_version: Target version's %{name}
-  label_copy_subtasks: Copy subtasks
+  label_copy_subtasks: Копировать подзадачи
diff --git a/config/locales/zh.yml b/config/locales/zh.yml
index ebb924c..44bf2ac 100644
--- a/config/locales/zh.yml
+++ b/config/locales/zh.yml
@@ -1061,9 +1061,9 @@ zh:
   label_readonly: 只读
   label_required: 必填
   text_repository_identifier_info: 仅小写字母(a-z)、数字、破折号(-)和下划线(_)可以使用。<br />一旦保存,标识无法修改。
-  field_board_parent: Parent forum
-  label_attribute_of_project: Project's %{name}
-  label_attribute_of_author: Author's %{name}
-  label_attribute_of_assigned_to: Assignee's %{name}
-  label_attribute_of_fixed_version: Target version's %{name}
-  label_copy_subtasks: Copy subtasks
+  field_board_parent: 父论坛
+  label_attribute_of_project: 项目 %{name}
+  label_attribute_of_author: 作者 %{name}
+  label_attribute_of_assigned_to: 分配给 %{name}
+  label_attribute_of_fixed_version: 目标版本 %{name}
+  label_copy_subtasks: 复制子任务
diff --git a/doc/CHANGELOG b/doc/CHANGELOG
index 1898a7e..5095c8e 100644
--- a/doc/CHANGELOG
+++ b/doc/CHANGELOG
@@ -4,6 +4,35 @@ Redmine - project management software
 Copyright (C) 2006-2012  Jean-Philippe Lang
 http://www.redmine.org/
 
+== 2012-11-24 v2.1.4
+
+* Defect #12274: Wiki export from Index by title is truncated
+* Defect #12298: Right-click context menu unable to batch/bulk update (IE8)
+* Defect #12332: Repository identifier does not display on Project/Settings/Repositories
+* Defect #12396: Error when receiving an email without subject header
+* Defect #12399: Non ASCII attachment filename encoding broken (MOJIBAKE) in receiving mail on Ruby 1.8
+* Defect #12409: Git: changesets aren't read after clear_changesets call
+* Defect #12431: Project.rebuild! sorts root projects by id instead of name
+
+== 2012-11-17 v2.1.3
+
+* Defect #12050: :export links to repository files lead to a 404 error
+* Defect #12189: Missing tmp/pdf directory
+* Defect #12195: Javascript error with IE7 / IE8 on new issue form
+* Defect #12196: "Page not found" on OK button in SCM "View all revisions" page
+* Defect #12199: Confirmation message displayed when clicking a disabled delete link in the context menu
+* Defect #12231: Hardcoded "Back" in Repository
+* Defect #12294: Incorrect german translation for "registered" users filter
+* Defect #12349: Watchers auto-complete search on non-latin chars
+* Defect #12358: 'None' grouped issue list section should be translated
+* Defect #12359: Version date field regex validation accepts invalid date
+* Defect #12375: Receiving mail subject encoding broken (MOJIBAKE) in some cases on Ruby 1.8
+* Patch #9732: German translations
+* Patch #12021: Russian locale translations
+* Patch #12188: Simplified Chinese translation with zh.yml file based on Rev:10681
+* Patch #12235: German translation for 2.1-stable
+* Patch #12237: Added German Translation
+
 == 2012-09-30 v2.1.2
 
 * Defect #11929: XSS vulnerability in Redmine 2.1.x
diff --git a/lib/plugins/acts_as_attachable/lib/acts_as_attachable.rb b/lib/plugins/acts_as_attachable/lib/acts_as_attachable.rb
index acddf5f..344ff08 100644
--- a/lib/plugins/acts_as_attachable/lib/acts_as_attachable.rb
+++ b/lib/plugins/acts_as_attachable/lib/acts_as_attachable.rb
@@ -30,7 +30,7 @@ module Redmine
           attachable_options[:delete_permission] = options.delete(:delete_permission) || "edit_#{self.name.pluralize.underscore}".to_sym
 
           has_many :attachments, options.merge(:as => :container,
-                                               :order => "#{Attachment.table_name}.created_on",
+                                               :order => "#{Attachment.table_name}.created_on ASC, #{Attachment.table_name}.id ASC",
                                                :dependent => :destroy)
           send :include, Redmine::Acts::Attachable::InstanceMethods
           before_save :attach_saved_attachments
@@ -62,7 +62,19 @@ module Redmine
 
         def save_attachments(attachments, author=User.current)
           if attachments.is_a?(Hash)
-            attachments = attachments.values
+            attachments = attachments.stringify_keys
+            attachments = attachments.to_a.sort {|a, b|
+              if a.first.to_i > 0 && b.first.to_i > 0
+                a.first.to_i <=> b.first.to_i
+              elsif a.first.to_i > 0
+                1
+              elsif b.first.to_i > 0
+                -1
+              else
+                a.first <=> b.first
+              end
+            }
+            attachments = attachments.map(&:last)
           end
           if attachments.is_a?(Array)
             attachments.each do |attachment|
diff --git a/lib/plugins/awesome_nested_set/lib/awesome_nested_set/awesome_nested_set.rb b/lib/plugins/awesome_nested_set/lib/awesome_nested_set/awesome_nested_set.rb
index 073e13e..95d9494 100644
--- a/lib/plugins/awesome_nested_set/lib/awesome_nested_set/awesome_nested_set.rb
+++ b/lib/plugins/awesome_nested_set/lib/awesome_nested_set/awesome_nested_set.rb
@@ -186,7 +186,7 @@ module CollectiveIdea #:nodoc:
             end
 
             # Find root node(s)
-            root_nodes = where("#{quoted_parent_column_name} IS NULL").order("#{quoted_left_column_name}, #{quoted_right_column_name}, id").each do |root_node|
+            root_nodes = where("#{quoted_parent_column_name} IS NULL").order(acts_as_nested_set_options[:order]).each do |root_node|
               # setup index for this scope
               indices[scope.call(root_node)] ||= 0
               set_left_and_rights.call(root_node)
diff --git a/lib/redmine/version.rb b/lib/redmine/version.rb
index a10ed4c..6ea7db4 100644
--- a/lib/redmine/version.rb
+++ b/lib/redmine/version.rb
@@ -4,7 +4,7 @@ module Redmine
   module VERSION #:nodoc:
     MAJOR = 2
     MINOR = 1
-    TINY  = 2
+    TINY  = 4
 
     # Branch values:
     # * official release: nil
diff --git a/public/javascripts/application.js b/public/javascripts/application.js
index edbd81c..864df80 100644
--- a/public/javascripts/application.js
+++ b/public/javascripts/application.js
@@ -464,9 +464,11 @@ function updateBulkEditFrom(url) {
 }
 
 function observeAutocompleteField(fieldId, url) {
-  $('#'+fieldId).autocomplete({
-    source: url,
-    minLength: 2,
+  $(document).ready(function() {
+    $('#'+fieldId).autocomplete({
+      source: url,
+      minLength: 2
+    });
   });
 }
 
diff --git a/public/javascripts/context_menu.js b/public/javascripts/context_menu.js
index 5ec9109..6c0b364 100644
--- a/public/javascripts/context_menu.js
+++ b/public/javascripts/context_menu.js
@@ -25,7 +25,7 @@ function contextMenuClick(event) {
   }
   contextMenuHide();
   if (target.is('a') || target.is('img')) { return; }
-  if (event.which == 1 /*TODO || (navigator.appVersion.match(/\bMSIE\b/))*/) {
+  if (event.which == 1 || (navigator.appVersion.match(/\bMSIE\b/))) {
     var tr = target.parents('tr').first();
     if (tr.length && tr.hasClass('hascontextmenu')) {
       // a row was clicked, check if the click was on checkbox
diff --git a/test/fixtures/mail_handler/gmail_with_attachment_iso-8859-1.eml b/test/fixtures/mail_handler/gmail_with_attachment_iso-8859-1.eml
new file mode 100644
index 0000000..419976e
--- /dev/null
+++ b/test/fixtures/mail_handler/gmail_with_attachment_iso-8859-1.eml
@@ -0,0 +1,26 @@
+Date: Tue, 20 Nov 2012 23:08:25 +0900
+Message-ID: <CANBr5-UZM=Odz4U3Q6vHd_9cd2tCT-_P9xDd=hRJ0aoMNTWXbw at mail.gmail.com>
+Subject: test
+From: John Smith <JSmith at somenet.foo>
+To: redmine at somenet.foo
+Content-Type: multipart/mixed; boundary=14dae93a13bf76ca5d04ceedc458
+
+--14dae93a13bf76ca5d04ceedc458
+Content-Type: text/plain; charset=ISO-8859-1
+
+test
+
+--14dae93a13bf76ca5d04ceedc458
+Content-Type: text/plain; charset=US-ASCII; 
+	name="=?ISO-8859-1?B?xOTW9tz8xOTW9tz8xOTW9tz8xOTW9tz8xOTW9tw=?=
+	=?ISO-8859-1?B?/MTk1vbc/MTk1vbc/MTk1vbc/MTk1vbc/MTk1vbc?=
+	=?ISO-8859-1?B?/MTk1vbc/C50eHQ=?="
+Content-Disposition: attachment; 
+	filename="=?ISO-8859-1?B?xOTW9tz8xOTW9tz8xOTW9tz8xOTW9tz8xOTW9tw=?=
+	=?ISO-8859-1?B?/MTk1vbc/MTk1vbc/MTk1vbc/MTk1vbc/MTk1vbc?=
+	=?ISO-8859-1?B?/MTk1vbc/C50eHQ=?="
+Content-Transfer-Encoding: base64
+X-Attachment-Id: f_h9r3mcjz0
+
+dGVzdAo=
+--14dae93a13bf76ca5d04ceedc458--
diff --git a/test/fixtures/mail_handler/gmail_with_attachment_ja.eml b/test/fixtures/mail_handler/gmail_with_attachment_ja.eml
new file mode 100644
index 0000000..71a1571
--- /dev/null
+++ b/test/fixtures/mail_handler/gmail_with_attachment_ja.eml
@@ -0,0 +1,20 @@
+Date: Mon, 19 Nov 2012 10:17:45 +0900
+Message-ID: <CANBr5-U6cXMfLek5QiB2ZrBPR3vTThn9_Upvdkf3Dkod664+Xw at mail.gmail.com>
+Subject: test
+From: John Smith <JSmith at somenet.foo>
+To: redmine at somenet.foo
+Content-Type: multipart/mixed; boundary=bcaec54ee4ea84f77904cecee22e
+
+--bcaec54ee4ea84f77904cecee22e
+Content-Type: text/plain; charset=ISO-8859-1
+
+test
+
+--bcaec54ee4ea84f77904cecee22e
+Content-Type: text/plain; charset=US-ASCII; name="=?ISO-2022-JP?B?GyRCJUYlOSVIGyhCLnR4dA==?="
+Content-Disposition: attachment; filename="=?ISO-2022-JP?B?GyRCJUYlOSVIGyhCLnR4dA==?="
+Content-Transfer-Encoding: base64
+X-Attachment-Id: f_h9owndpv0
+
+dGVzdAo=
+--bcaec54ee4ea84f77904cecee22e--
diff --git a/test/fixtures/mail_handler/subject_as_iso-8859-1.eml b/test/fixtures/mail_handler/no_subject_header.eml
similarity index 84%
copy from test/fixtures/mail_handler/subject_as_iso-8859-1.eml
copy to test/fixtures/mail_handler/no_subject_header.eml
index 45f725b..e6538cb 100644
--- a/test/fixtures/mail_handler/subject_as_iso-8859-1.eml
+++ b/test/fixtures/mail_handler/no_subject_header.eml
@@ -2,7 +2,6 @@ Content-Type: application/ms-tnef; name="winmail.dat"
 Content-Transfer-Encoding: binary
 From: John Smith <JSmith at somenet.foo>
 To: "redmine at somenet.foo" <redmine at 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 at foo.bar>
 Accept-Language: de-CH, en-US
diff --git a/test/fixtures/mail_handler/subject_japanese_1.eml b/test/fixtures/mail_handler/subject_japanese_1.eml
new file mode 100644
index 0000000..ee93a68
--- /dev/null
+++ b/test/fixtures/mail_handler/subject_japanese_1.eml
@@ -0,0 +1,7 @@
+From: John Smith <JSmith at somenet.foo>
+To: "redmine at somenet.foo" <redmine at somenet.foo>
+Subject: =?iso-2022-jp?b?GyRCJUYlOSVIGyhCCg=?=
+Date: Fri, 1 Jun 2012 14:39:38 +0200
+Message-ID: <87C31D42249DD0489D1A1444E3232DD7019D6183 at foo.bar>
+
+Fixture
diff --git a/test/fixtures/mail_handler/subject_japanese_2.eml b/test/fixtures/mail_handler/subject_japanese_2.eml
new file mode 100644
index 0000000..d25d478
--- /dev/null
+++ b/test/fixtures/mail_handler/subject_japanese_2.eml
@@ -0,0 +1,7 @@
+From: John Smith <JSmith at somenet.foo>
+To: "redmine at somenet.foo" <redmine at somenet.foo>
+Subject: Re: =?iso-2022-jp?b?GyRCJUYlOSVIGyhCCg=?=
+Date: Fri, 1 Jun 2012 14:39:38 +0200
+Message-ID: <87C31D42249DD0489D1A1444E3232DD7019D6183 at foo.bar>
+
+Fixture
diff --git a/test/fixtures/mail_handler/thunderbird_with_attachment_iso-8859-1.eml b/test/fixtures/mail_handler/thunderbird_with_attachment_iso-8859-1.eml
new file mode 100644
index 0000000..2ee104c
--- /dev/null
+++ b/test/fixtures/mail_handler/thunderbird_with_attachment_iso-8859-1.eml
@@ -0,0 +1,34 @@
+Message-ID: <50AB9546.7020800 at gmail.com>
+Date: Tue, 20 Nov 2012 23:35:50 +0900
+From: John Smith <JSmith at somenet.foo>
+User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.17) Gecko/20110428 Fedora/3.1.10-1.fc13 Thunderbird/3.1.10
+MIME-Version: 1.0
+To: redmine at somenet.foo
+Subject: test
+Content-Type: multipart/mixed;
+ boundary="------------050902080306030406090208"
+
+This is a multi-part message in MIME format.
+--------------050902080306030406090208
+Content-Type: text/plain; charset=ISO-8859-1; format=flowed
+Content-Transfer-Encoding: 7bit
+
+test
+
+--------------050902080306030406090208
+Content-Type: image/png;
+ name="=?ISO-8859-1?Q?=C4=E4=D6=F6=DC=FC=C4=E4=D6=F6=DC=FC=C4=E4=D6=F6=DC=FC=C4=E4?=
+ =?ISO-8859-1?Q?=D6=F6=DC=FC=C4=E4=D6=F6=DC=FC=C4=E4=D6=F6=DC=FC=C4=E4=D6?=
+ =?ISO-8859-1?Q?=F6=DC=FC=C4=E4=D6=F6=DC=FC=C4=E4=D6=F6=DC=FC=C4=E4=D6=F6?=
+ =?ISO-8859-1?Q?=DC=FC=C4=E4=D6=F6=DC=FC=2Epng?="
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment;
+ filename*0*=ISO-8859-1''%C4%E4%D6%F6%DC%FC%C4%E4%D6%F6%DC%FC%C4%E4%D6%F6;
+ filename*1*=%DC%FC%C4%E4%D6%F6%DC%FC%C4%E4%D6%F6%DC%FC%C4%E4%D6%F6%DC%FC;
+ filename*2*=%C4%E4%D6%F6%DC%FC%C4%E4%D6%F6%DC%FC%C4%E4%D6%F6%DC%FC%C4%E4;
+ filename*3*=%D6%F6%DC%FC%C4%E4%D6%F6%DC%FC%2E%70%6E%67
+
+iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAAXNSR0IArs4c6QAAAAlwSFlz
+AAALEwAACxMBAJqcGAAAAAd0SU1FB9wLFA4fJhRKIUQAAAAUSURBVAjXY/z//z8DEmBiQAWk
+8gHq9gMHP8uZWAAAAABJRU5ErkJggg==
+--------------050902080306030406090208--
diff --git a/test/fixtures/mail_handler/thunderbird_with_attachment_ja.eml b/test/fixtures/mail_handler/thunderbird_with_attachment_ja.eml
new file mode 100644
index 0000000..88ba79f
--- /dev/null
+++ b/test/fixtures/mail_handler/thunderbird_with_attachment_ja.eml
@@ -0,0 +1,26 @@
+Message-ID: <50AA00C6.4070108 at gmail.com>
+Date: Mon, 19 Nov 2012 18:49:58 +0900
+From: John Smith <JSmith at somenet.foo>
+User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.15) Gecko/20101027 Fedora/3.0.10-1.fc12 Lightning/1.0b1 Thunderbird/3.0.10
+MIME-Version: 1.0
+To: redmine at somenet.foo
+Subject: test
+Content-Type: multipart/mixed;
+ boundary="------------030104060902010800050907"
+
+This is a multi-part message in MIME format.
+--------------030104060902010800050907
+Content-Type: text/plain; charset=ISO-2022-JP
+Content-Transfer-Encoding: 7bit
+
+test
+
+--------------030104060902010800050907
+Content-Type: text/plain;
+ name="=?ISO-2022-JP?B?GyRCJUYlOSVIGyhCLnR4dA==?="
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment;
+ filename*=ISO-2022-JP''%1B%24%42%25%46%25%39%25%48%1B%28%42%2E%74%78%74
+
+dGVzdAo=
+--------------030104060902010800050907--
diff --git a/test/functional/repositories_git_controller_test.rb b/test/functional/repositories_git_controller_test.rb
index 07c6632..423d0c1 100644
--- a/test/functional/repositories_git_controller_test.rb
+++ b/test/functional/repositories_git_controller_test.rb
@@ -532,6 +532,21 @@ class RepositoriesGitControllerTest < ActionController::TestCase
       end
     end
 
+    def test_revisions
+      assert_equal 0, @repository.changesets.count
+      @repository.fetch_changesets
+      @project.reload
+      assert_equal NUM_REV, @repository.changesets.count
+      get :revisions, :id => PRJ_ID
+      assert_response :success
+      assert_template 'revisions'
+      assert_tag :tag => 'form',
+                 :attributes => {
+                   :method => 'get',
+                   :action => '/projects/subproject1/repository/revision'
+                 }
+    end
+
     def test_revision
       assert_equal 0, @repository.changesets.count
       @repository.fetch_changesets
diff --git a/test/integration/api_test/issues_test.rb b/test/integration/api_test/issues_test.rb
index 028b9e9..2f68748 100644
--- a/test/integration/api_test/issues_test.rb
+++ b/test/integration/api_test/issues_test.rb
@@ -25,6 +25,7 @@ class ApiTest::IssuesTest < ActionController::IntegrationTest
     :member_roles,
     :issues,
     :issue_statuses,
+    :issue_relations,
     :versions,
     :trackers,
     :projects_trackers,
diff --git a/test/object_helpers.rb b/test/object_helpers.rb
index 0100a8b..3a7a0de 100644
--- a/test/object_helpers.rb
+++ b/test/object_helpers.rb
@@ -61,6 +61,8 @@ module ObjectHelpers
 
   def Issue.generate!(attributes={})
     issue = Issue.new(attributes)
+    issue.project ||= Project.find(1)
+    issue.tracker ||= issue.project.trackers.first
     issue.subject = 'Generated' if issue.subject.blank?
     issue.author ||= User.find(2)
     yield issue if block_given?
diff --git a/test/unit/helpers/application_helper_test.rb b/test/unit/helpers/application_helper_test.rb
index c431e26..9855efa 100644
--- a/test/unit/helpers/application_helper_test.rb
+++ b/test/unit/helpers/application_helper_test.rb
@@ -253,8 +253,15 @@ RAW
 
     project_url = {:controller => 'projects', :action => 'show', :id => 'subproject1'}
 
-    source_url = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}
-    source_url_with_ext = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file.ext']}
+    source_url = '/projects/ecookbook/repository/entry/some/file'
+    source_url_with_rev = '/projects/ecookbook/repository/revisions/52/entry/some/file'
+    source_url_with_ext = '/projects/ecookbook/repository/entry/some/file.ext'
+    source_url_with_rev_and_ext = '/projects/ecookbook/repository/revisions/52/entry/some/file.ext'
+
+    export_url = '/projects/ecookbook/repository/raw/some/file'
+    export_url_with_rev = '/projects/ecookbook/repository/revisions/52/raw/some/file'
+    export_url_with_ext = '/projects/ecookbook/repository/raw/some/file.ext'
+    export_url_with_rev_and_ext = '/projects/ecookbook/repository/revisions/52/raw/some/file.ext'
 
     to_test = {
       # tickets
@@ -284,12 +291,16 @@ RAW
       'source:/some/file. '         => link_to('source:/some/file', source_url, :class => 'source') + ".",
       'source:/some/file.ext. '     => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
       'source:/some/file, '         => link_to('source:/some/file', source_url, :class => 'source') + ",",
-      'source:/some/file at 52'        => link_to('source:/some/file at 52', source_url.merge(:rev => 52), :class => 'source'),
-      'source:/some/file.ext at 52'    => link_to('source:/some/file.ext at 52', source_url_with_ext.merge(:rev => 52), :class => 'source'),
-      'source:/some/file#L110'      => link_to('source:/some/file#L110', source_url.merge(:anchor => 'L110'), :class => 'source'),
-      'source:/some/file.ext#L110'  => link_to('source:/some/file.ext#L110', source_url_with_ext.merge(:anchor => 'L110'), :class => 'source'),
-      'source:/some/file at 52#L110'   => link_to('source:/some/file at 52#L110', source_url.merge(:rev => 52, :anchor => 'L110'), :class => 'source'),
-      'export:/some/file'           => link_to('export:/some/file', source_url.merge(:format => 'raw'), :class => 'source download'),
+      'source:/some/file at 52'        => link_to('source:/some/file at 52', source_url_with_rev, :class => 'source'),
+      'source:/some/file.ext at 52'    => link_to('source:/some/file.ext at 52', source_url_with_rev_and_ext, :class => 'source'),
+      'source:/some/file#L110'      => link_to('source:/some/file#L110', source_url + "#L110", :class => 'source'),
+      'source:/some/file.ext#L110'  => link_to('source:/some/file.ext#L110', source_url_with_ext + "#L110", :class => 'source'),
+      'source:/some/file at 52#L110'   => link_to('source:/some/file at 52#L110', source_url_with_rev + "#L110", :class => 'source'),
+      # export
+      'export:/some/file'           => link_to('export:/some/file', export_url, :class => 'source download'),
+      'export:/some/file.ext'       => link_to('export:/some/file.ext', export_url_with_ext, :class => 'source download'),
+      'export:/some/file at 52'        => link_to('export:/some/file at 52', export_url_with_rev, :class => 'source download'),
+      'export:/some/file.ext at 52'    => link_to('export:/some/file.ext at 52', export_url_with_rev_and_ext, :class => 'source download'),
       # forum
       'forum#2'                     => link_to('Discussion', board_url, :class => 'board'),
       'forum:Discussion'            => link_to('Discussion', board_url, :class => 'board'),
diff --git a/test/unit/helpers/issues_helper_test.rb b/test/unit/helpers/issues_helper_test.rb
index 3c465a8..d39573e 100644
--- a/test/unit/helpers/issues_helper_test.rb
+++ b/test/unit/helpers/issues_helper_test.rb
@@ -109,51 +109,55 @@ class IssuesHelperTest < ActionView::TestCase
       end
     end
 
-    def test_with_a_start_date_attribute_should_format_the_current_date
-      @detail = JournalDetail.new(
+    context "with a start_date attribute" do
+      should "format the current date" do
+        @detail = JournalDetail.new(
                    :property  => 'attr',
                    :old_value => '2010-01-01',
                    :value     => '2010-01-31',
                    :prop_key  => 'start_date'
                 )
-      with_settings :date_format => '%m/%d/%Y' do
-        assert_match "01/31/2010", show_detail(@detail, true)
+        with_settings :date_format => '%m/%d/%Y' do
+          assert_match "01/31/2010", show_detail(@detail, true)
+        end
       end
-    end
 
-    def test_with_a_start_date_attribute_should_format_the_old_date
-      @detail = JournalDetail.new(
+      should "format the old date" do
+        @detail = JournalDetail.new(
                    :property  => 'attr',
                    :old_value => '2010-01-01',
                    :value     => '2010-01-31',
                    :prop_key  => 'start_date'
                 )
-      with_settings :date_format => '%m/%d/%Y' do
-        assert_match "01/01/2010", show_detail(@detail, true)
+        with_settings :date_format => '%m/%d/%Y' do
+          assert_match "01/01/2010", show_detail(@detail, true)
+        end
       end
     end
 
-    def test_with_a_due_date_attribute_should_with_a_due_date_attribute
-      @detail = JournalDetail.new(
+    context "with a due_date attribute" do
+      should "format the current date" do
+        @detail = JournalDetail.new(
                   :property  => 'attr',
                   :old_value => '2010-01-01',
                   :value     => '2010-01-31',
                   :prop_key  => 'due_date'
                 )
-      with_settings :date_format => '%m/%d/%Y' do
-        assert_match "01/31/2010", show_detail(@detail, true)
+        with_settings :date_format => '%m/%d/%Y' do
+          assert_match "01/31/2010", show_detail(@detail, true)
+        end
       end
-    end
 
-    def test_with_a_due_date_attribute_should_format_the_old_date
-      @detail = JournalDetail.new(
+      should "format the old date" do
+        @detail = JournalDetail.new(
                   :property  => 'attr',
                   :old_value => '2010-01-01',
                   :value     => '2010-01-31',
                   :prop_key  => 'due_date'
                 )
-      with_settings :date_format => '%m/%d/%Y' do
-        assert_match "01/01/2010", show_detail(@detail, true)
+        with_settings :date_format => '%m/%d/%Y' do
+          assert_match "01/01/2010", show_detail(@detail, true)
+        end
       end
     end
 
diff --git a/test/unit/issue_relation_test.rb b/test/unit/issue_relation_test.rb
index 79efd75..d341fea 100644
--- a/test/unit/issue_relation_test.rb
+++ b/test/unit/issue_relation_test.rb
@@ -18,7 +18,17 @@
 require File.expand_path('../../test_helper', __FILE__)
 
 class IssueRelationTest < ActiveSupport::TestCase
-  fixtures :issue_relations, :issues
+  fixtures :projects,
+           :users,
+           :roles,
+           :members,
+           :member_roles,
+           :issues,
+           :issue_statuses,
+           :issue_relations,
+           :enabled_modules,
+           :enumerations,
+           :trackers
 
   def test_create
     from = Issue.find(1)
diff --git a/test/unit/issue_test.rb b/test/unit/issue_test.rb
index fc08313..d192a34 100644
--- a/test/unit/issue_test.rb
+++ b/test/unit/issue_test.rb
@@ -1576,4 +1576,18 @@ class IssueTest < ActiveSupport::TestCase
   def test_journals_after_with_blank_arg_should_return_all_journals
     assert_equal [Journal.find(1), Journal.find(2)], Issue.find(1).journals_after('')
   end
+
+  def test_save_attachments_with_hash_should_save_attachments_in_keys_order
+    set_tmp_attachments_directory
+    issue = Issue.generate!
+    issue.save_attachments({
+      'p0' => {'file' => mock_file_with_options(:original_filename => 'upload')},
+      '3' => {'file' => mock_file_with_options(:original_filename => 'bar')},
+      '1' => {'file' => mock_file_with_options(:original_filename => 'foo')}
+    })
+    issue.attach_saved_attachments
+
+    assert_equal 3, issue.reload.attachments.count
+    assert_equal %w(upload foo bar), issue.attachments.map(&:filename)
+  end
 end
diff --git a/test/unit/mail_handler_test.rb b/test/unit/mail_handler_test.rb
index 49b5d89..e88fd15 100644
--- a/test/unit/mail_handler_test.rb
+++ b/test/unit/mail_handler_test.rb
@@ -373,6 +373,80 @@ class MailHandlerTest < ActiveSupport::TestCase
     assert_equal 'caaf384198bcbc9563ab5c058acd73cd', attachment.digest
   end
 
+  def test_thunderbird_with_attachment_ja
+    issue = submit_email(
+              'thunderbird_with_attachment_ja.eml',
+              :issue => {:project => 'ecookbook'}
+            )
+    assert_kind_of Issue, issue
+    assert_equal 1, issue.attachments.size
+    ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt"
+    ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
+    attachment = issue.attachments.first
+    assert_equal ja, attachment.filename
+    assert_equal 5, attachment.filesize
+    assert File.exist?(attachment.diskfile)
+    assert_equal 5, File.size(attachment.diskfile)
+    assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
+  end
+
+  def test_gmail_with_attachment_ja
+    issue = submit_email(
+              'gmail_with_attachment_ja.eml',
+              :issue => {:project => 'ecookbook'}
+            )
+    assert_kind_of Issue, issue
+    assert_equal 1, issue.attachments.size
+    ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt"
+    ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
+    attachment = issue.attachments.first
+    assert_equal ja, attachment.filename
+    assert_equal 5, attachment.filesize
+    assert File.exist?(attachment.diskfile)
+    assert_equal 5, File.size(attachment.diskfile)
+    assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
+  end
+
+  def test_thunderbird_with_attachment_latin1
+    issue = submit_email(
+              'thunderbird_with_attachment_iso-8859-1.eml',
+              :issue => {:project => 'ecookbook'}
+            )
+    assert_kind_of Issue, issue
+    assert_equal 1, issue.attachments.size
+    u = ""
+    u.force_encoding('UTF-8') if u.respond_to?(:force_encoding)
+    u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc"
+    u1.force_encoding('UTF-8') if u1.respond_to?(:force_encoding)
+    11.times { u << u1 }
+    attachment = issue.attachments.first
+    assert_equal "#{u}.png", attachment.filename
+    assert_equal 130, attachment.filesize
+    assert File.exist?(attachment.diskfile)
+    assert_equal 130, File.size(attachment.diskfile)
+    assert_equal '4d80e667ac37dddfe05502530f152abb', attachment.digest
+  end
+
+  def test_gmail_with_attachment_latin1
+    issue = submit_email(
+              'gmail_with_attachment_iso-8859-1.eml',
+              :issue => {:project => 'ecookbook'}
+            )
+    assert_kind_of Issue, issue
+    assert_equal 1, issue.attachments.size
+    u = ""
+    u.force_encoding('UTF-8') if u.respond_to?(:force_encoding)
+    u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc"
+    u1.force_encoding('UTF-8') if u1.respond_to?(:force_encoding)
+    11.times { u << u1 }
+    attachment = issue.attachments.first
+    assert_equal "#{u}.txt", attachment.filename
+    assert_equal 5, attachment.filesize
+    assert File.exist?(attachment.diskfile)
+    assert_equal 5, File.size(attachment.diskfile)
+    assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
+  end
+
   def test_add_issue_with_iso_8859_1_subject
     issue = submit_email(
               'subject_as_iso-8859-1.eml',
@@ -382,6 +456,37 @@ class MailHandlerTest < ActiveSupport::TestCase
     assert_equal 'Testmail from Webmail: ä ö ü...', issue.subject
   end
 
+  def test_add_issue_with_japanese_subject
+    issue = submit_email(
+              'subject_japanese_1.eml',
+              :issue => {:project => 'ecookbook'}
+            )
+    assert_kind_of Issue, issue
+    ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88"
+    ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
+    assert_equal ja, issue.subject
+  end
+
+  def test_add_issue_with_no_subject_header
+    issue = submit_email(
+              'no_subject_header.eml',
+              :issue => {:project => 'ecookbook'}
+            )
+    assert_kind_of Issue, issue
+    assert_equal '(no subject)', issue.subject
+  end
+
+  def test_add_issue_with_mixed_japanese_subject
+    issue = submit_email(
+              'subject_japanese_2.eml',
+              :issue => {:project => 'ecookbook'}
+            )
+    assert_kind_of Issue, issue
+    ja = "Re: \xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88"
+    ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
+    assert_equal ja, issue.subject
+  end
+
   def test_should_ignore_emails_from_locked_users
     User.find(2).lock!
 
diff --git a/test/unit/principal_test.rb b/test/unit/principal_test.rb
index 6a4aed7..6d06a31 100644
--- a/test/unit/principal_test.rb
+++ b/test/unit/principal_test.rb
@@ -1,3 +1,5 @@
+# encoding: utf-8
+#
 # Redmine - project management software
 # Copyright (C) 2006-2012  Jean-Philippe Lang
 #
@@ -106,4 +108,11 @@ class PrincipalTest < ActiveSupport::TestCase
       assert_equal @palmer, results.first
     end
   end
+
+  def test_like_scope_with_cyrillic_name
+    user = User.generate!(:firstname => 'Соболев', :lastname => 'Денис')
+    results = Principal.like('Собо')
+    assert_equal 1, results.count
+    assert_equal user, results.first
+  end
 end
diff --git a/test/unit/project_nested_set_test.rb b/test/unit/project_nested_set_test.rb
index 07d1a8f..3bd3760 100644
--- a/test/unit/project_nested_set_test.rb
+++ b/test/unit/project_nested_set_test.rb
@@ -28,17 +28,17 @@ class ProjectNestedSetTest < ActiveSupport::TestCase
     @a2 = Project.create!(:name => 'A2', :identifier => 'projecta2')
     @a2.set_parent!(@a)
 
+    @c = Project.create!(:name => 'C', :identifier => 'projectc')
+    @c1 = Project.create!(:name => 'C1', :identifier => 'projectc1')
+    @c1.set_parent!(@c)
+
     @b = Project.create!(:name => 'B', :identifier => 'projectb')
+    @b2 = Project.create!(:name => 'B2', :identifier => 'projectb2')
+    @b2.set_parent!(@b)
     @b1 = Project.create!(:name => 'B1', :identifier => 'projectb1')
     @b1.set_parent!(@b)
     @b11 = Project.create!(:name => 'B11', :identifier => 'projectb11')
     @b11.set_parent!(@b1)
-    @b2 = Project.create!(:name => 'B2', :identifier => 'projectb2')
-    @b2.set_parent!(@b)
-
-    @c = Project.create!(:name => 'C', :identifier => 'projectc')
-    @c1 = Project.create!(:name => 'C1', :identifier => 'projectc1')
-    @c1.set_parent!(@c)
 
     @a, @a1, @a2, @b, @b1, @b11, @b2, @c, @c1 = *(Project.all.sort_by(&:name))
   end
@@ -47,6 +47,13 @@ class ProjectNestedSetTest < ActiveSupport::TestCase
     assert_valid_nested_set
   end
 
+  def test_rebuild_should_build_valid_tree
+    Project.update_all "lft = NULL, rgt = NULL"
+
+    Project.rebuild!
+    assert_valid_nested_set
+  end
+
   def test_moving_a_child_to_a_different_parent_should_keep_valid_tree
     assert_no_difference 'Project.count' do
       Project.find_by_name('B1').set_parent!(Project.find_by_name('A2'))
diff --git a/test/unit/repository_git_test.rb b/test/unit/repository_git_test.rb
index cfb0f48..03bb274 100644
--- a/test/unit/repository_git_test.rb
+++ b/test/unit/repository_git_test.rb
@@ -217,6 +217,40 @@ class RepositoryGitTest < ActiveSupport::TestCase
       assert_equal h1, h2
     end
 
+    def test_keep_extra_report_last_commit_in_clear_changesets
+      assert_nil @repository.extra_info
+      h = {}
+      h["extra_report_last_commit"] = "1"
+      @repository.merge_extra_info(h)
+      @repository.save
+      @project.reload
+
+      assert_equal 0, @repository.changesets.count
+      @repository.fetch_changesets
+      @project.reload
+
+      assert_equal NUM_REV, @repository.changesets.count
+      @repository.send(:clear_changesets)
+      assert_equal 1, @repository.extra_info.size
+      assert_equal "1", @repository.extra_info["extra_report_last_commit"]
+    end
+
+    def test_refetch_after_clear_changesets
+      assert_nil @repository.extra_info
+      assert_equal 0, @repository.changesets.count
+      @repository.fetch_changesets
+      @project.reload
+      assert_equal NUM_REV, @repository.changesets.count
+
+      @repository.send(:clear_changesets)
+      @project.reload
+      assert_equal 0, @repository.changesets.count
+
+      @repository.fetch_changesets
+      @project.reload
+      assert_equal NUM_REV, @repository.changesets.count
+    end
+
     def test_parents
       assert_equal 0, @repository.changesets.count
       @repository.fetch_changesets
diff --git a/test/unit/version_test.rb b/test/unit/version_test.rb
index d80c3d1..83fb3f9 100644
--- a/test/unit/version_test.rb
+++ b/test/unit/version_test.rb
@@ -32,7 +32,13 @@ class VersionTest < ActiveSupport::TestCase
 
   def test_invalid_effective_date_validation
     v = Version.new(:project => Project.find(1), :name => '1.1', :effective_date => '99999-01-01')
-    assert !v.save
+    assert !v.valid?
+    v.effective_date = '2012-11-33'
+    assert !v.valid?
+    v.effective_date = '2012-31-11'
+    assert !v.valid?
+    v.effective_date = 'ABC'
+    assert !v.valid?
     assert_include I18n.translate('activerecord.errors.messages.not_a_date'),
                    v.errors[:effective_date]
   end
diff --git a/tmp/test/empty b/tmp/pdf/empty
similarity index 100%
copy from tmp/test/empty
copy to tmp/pdf/empty

-- 
redmine.git



More information about the Pkg-ruby-extras-commits mailing list