Compare commits

..

1 Commits

Author SHA1 Message Date
Jean-Philippe Lang 8a875734ca tagged version 2.0.0
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/tags/2.0.0@9698 e93f8b46-1217-0410-a6f0-8f06a7374b81
2012-05-14 22:19:50 +00:00
160 changed files with 375 additions and 1225 deletions
+3 -6
View File
@@ -1,11 +1,11 @@
source 'http://rubygems.org'
gem 'rails', '3.2.6'
gem 'rails', '3.2.3'
gem 'prototype-rails', '3.2.1'
gem "i18n", "~> 0.6.0"
gem "coderay", "~> 1.0.6"
gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby]
gem "builder", "3.0.0"
gem "builder"
# Optional gem for LDAP authentication
group :ldap do
@@ -69,14 +69,11 @@ end
group :development do
gem "rdoc", ">= 2.4.2"
gem "yard"
end
group :test do
gem "shoulda", "~> 2.11"
# Shoulda does not work nice on Ruby 1.9.3 and seems to need test-unit explicitely.
gem "test-unit", :platforms => [:mri_19]
gem "mocha", "0.12.3"
gem "mocha"
end
local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
+1 -1
View File
@@ -43,7 +43,7 @@ class ActivitiesController < ApplicationController
if events.empty? || stale?(:etag => [@activity.scope, @date_to, @date_from, @with_subprojects, @author, events.first, User.current, current_language])
respond_to do |format|
format.html {
@events_by_day = events.group_by {|event| User.current.time_to_date(event.event_datetime)}
@events_by_day = events.group_by(&:event_date)
render :layout => false if request.xhr?
}
format.atom {
+2 -2
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].to_s
params[:key]
elsif request.headers["X-Redmine-API-Key"].present?
request.headers["X-Redmine-API-Key"].to_s
request.headers["X-Redmine-API-Key"]
end
end
+2 -2
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.reorder("#{Message.table_name}.sticky DESC").order(sort_clause).all(
@topics = @board.topics.find :all, :order => ["#{Message.table_name}.sticky DESC", sort_clause].compact.join(', '),
: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?
}
+9 -17
View File
@@ -18,13 +18,12 @@
class TimelogController < ApplicationController
menu_item :issues
before_filter :find_project_for_new_time_entry, :only => [:create]
before_filter :find_project, :only => [:create]
before_filter :find_time_entry, :only => [:show, :edit, :update]
before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]
before_filter :authorize, :except => [:new, :index, :report]
before_filter :find_optional_project, :only => [:index, :report]
before_filter :find_optional_project_for_new_time_entry, :only => [:new]
before_filter :find_optional_project, :only => [:new, :index, :report]
before_filter :authorize_global, :only => [:new, :index, :report]
accept_rss_auth :index
@@ -39,7 +38,7 @@ class TimelogController < ApplicationController
def index
sort_init 'spent_on', 'desc'
sort_update 'spent_on' => ['spent_on', "#{TimeEntry.table_name}.created_on"],
sort_update 'spent_on' => 'spent_on',
'user' => 'user_id',
'activity' => 'activity_id',
'project' => "#{Project.table_name}.name",
@@ -134,13 +133,9 @@ class TimelogController < ApplicationController
flash[:notice] = l(:notice_successful_create)
if params[:continue]
if params[:project_id]
redirect_to :action => 'new', :project_id => @time_entry.project, :issue_id => @time_entry.issue,
:time_entry => {:issue_id => @time_entry.issue_id, :activity_id => @time_entry.activity_id},
:back_url => params[:back_url]
redirect_to :action => 'new', :project_id => @time_entry.project, :issue_id => @time_entry.issue, :back_url => params[:back_url]
else
redirect_to :action => 'new',
:time_entry => {:project_id => @time_entry.project_id, :issue_id => @time_entry.issue_id, :activity_id => @time_entry.activity_id},
:back_url => params[:back_url]
redirect_to :action => 'new', :back_url => params[:back_url]
end
else
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
@@ -263,7 +258,7 @@ private
end
end
def find_optional_project_for_new_time_entry
def find_project
if (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])).present?
@project = Project.find(project_id)
end
@@ -271,15 +266,12 @@ private
@issue = Issue.find(issue_id)
@project ||= @issue.project
end
rescue ActiveRecord::RecordNotFound
render_404
end
def find_project_for_new_time_entry
find_optional_project_for_new_time_entry
if @project.nil?
render_404
return false
end
rescue ActiveRecord::RecordNotFound
render_404
end
def find_optional_project
+4 -6
View File
@@ -178,7 +178,7 @@ module ApplicationHelper
end
def format_activity_day(date)
date == User.current.today ? l(:label_today).titleize : format_date(date)
date == Date.today ? l(:label_today).titleize : format_date(date)
end
def format_activity_description(text)
@@ -253,7 +253,7 @@ module ApplicationHelper
def project_tree_options_for_select(projects, options = {})
s = ''
project_tree(projects) do |project, level|
name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ') : '').html_safe
name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ').html_safe : '')
tag_options = {:value => project.id}
if project == options[:selected] || (options[:selected].respond_to?(:include?) && options[:selected].include?(project))
tag_options[:selected] = 'selected'
@@ -352,7 +352,7 @@ module ApplicationHelper
def time_tag(time)
text = distance_of_time_in_words(Time.now, time)
if @project
link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => User.current.time_to_date(time)}, :title => format_time(time))
link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => time.to_date}, :title => format_time(time))
else
content_tag('acronym', text, :title => format_time(time))
end
@@ -812,7 +812,7 @@ module ApplicationHelper
end
end
HEADING_RE = /(<h(\d)( [^>]+)?>(.+?)<\/h(\d)>)/i unless const_defined?(:HEADING_RE)
HEADING_RE = /(<h(1|2|3|4)( [^>]+)?>(.+?)<\/h(1|2|3|4)>)/i unless const_defined?(:HEADING_RE)
def parse_sections(text, project, obj, attr, only_path, options)
return unless options[:edit_section_links]
@@ -883,8 +883,6 @@ module ApplicationHelper
# Renders the TOC with given headings
def replace_toc(text, headings)
text.gsub!(TOC_RE) do
# Keep only the 4 first levels
headings = headings.select{|level, anchor, item| level <= 4}
if headings.empty?
''
else
+3 -3
View File
@@ -46,17 +46,17 @@ module RepositoriesHelper
end
def render_changeset_changes
changes = @changeset.filechanges.find(:all, :limit => 1000, :order => 'path').collect do |change|
changes = @changeset.changes.find(:all, :limit => 1000, :order => 'path').collect do |change|
case change.action
when 'A'
# Detects moved/copied files
if !change.from_path.blank?
change.action =
@changeset.filechanges.detect {|c| c.action == 'D' && c.path == change.from_path} ? 'R' : 'C'
@changeset.changes.detect {|c| c.action == 'D' && c.path == change.from_path} ? 'R' : 'C'
end
change
when 'D'
@changeset.filechanges.detect {|c| c.from_path == change.path} ? nil : change
@changeset.changes.detect {|c| c.from_path == change.path} ? nil : change
else
change
end
+4 -8
View File
@@ -45,27 +45,23 @@ module WatchersHelper
# Returns a comma separated list of users watching the given object
def watchers_list(object)
remove_allowed = User.current.allowed_to?("delete_#{object.class.name.underscore}_watchers".to_sym, object.project)
content = ''.html_safe
lis = object.watcher_users.collect do |user|
s = ''.html_safe
s << avatar(user, :size => "16").to_s
s << link_to_user(user, :class => 'user')
s = avatar(user, :size => "16").to_s + link_to_user(user, :class => 'user').to_s
if remove_allowed
url = {:controller => 'watchers',
:action => 'destroy',
:object_type => object.class.to_s.underscore,
:object_id => object.id,
:user_id => user}
s << ' '
s << link_to_remote(image_tag('delete.png'),
s += ' ' + link_to_remote(image_tag('delete.png'),
{:url => url},
:href => url_for(url),
:style => "vertical-align: middle",
:class => "delete")
end
content << content_tag('li', s)
content_tag :li, s.html_safe
end
content.present? ? content_tag('ul', content) : content
(lis.empty? ? "" : "<ul>#{ lis.join("\n") }</ul>").html_safe
end
def watchers_checkboxes(object, users, checked=nil)
-1
View File
@@ -24,7 +24,6 @@ class Attachment < ActiveRecord::Base
validates_presence_of :filename, :author
validates_length_of :filename, :maximum => 255
validates_length_of :disk_filename, :maximum => 255
validates_length_of :description, :maximum => 255
validate :validate_max_file_size
acts_as_event :title => :filename,
+1 -1
View File
@@ -20,7 +20,7 @@ require 'iconv'
class Changeset < ActiveRecord::Base
belongs_to :repository
belongs_to :user
has_many :filechanges, :class_name => 'Change', :dependent => :delete_all
has_many :changes, :dependent => :delete_all
has_and_belongs_to_many :issues
has_and_belongs_to_many :parents,
:class_name => "Changeset",
+3 -30
View File
@@ -75,7 +75,7 @@ class Issue < ActiveRecord::Base
:conditions => ["#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"]
before_create :default_assign
before_save :close_duplicates, :update_done_ratio_from_issue_status, :force_updated_on_change
before_save :close_duplicates, :update_done_ratio_from_issue_status
after_save {|issue| issue.send :after_project_change if !issue.id_changed? && issue.project_id_changed?}
after_save :reschedule_following_issues, :update_nested_set_attributes, :update_parent_attributes, :create_journal
after_destroy :update_parent_attributes
@@ -124,28 +124,6 @@ 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) : []
@@ -454,6 +432,8 @@ class Issue < ActiveRecord::Base
@custom_values_before_change = {}
self.custom_field_values.each {|c| @custom_values_before_change.store c.custom_field_id, c.value }
end
# Make sure updated_on is updated when adding a note.
updated_on_will_change!
@current_journal
end
@@ -1014,13 +994,6 @@ class Issue < ActiveRecord::Base
end
end
# Make sure updated_on is updated when adding a note
def force_updated_on_change
if @current_journal
self.updated_on = current_time_from_proper_timezone
end
end
# Saves the changes in a Journal
# Called after_save
def create_journal
+8 -28
View File
@@ -50,8 +50,8 @@ class MailHandler < ActionMailer::Base
cattr_accessor :ignored_emails_headers
@@ignored_emails_headers = {
'X-Auto-Response-Suppress' => 'oof',
'Auto-Submitted' => /^auto-/
'X-Auto-Response-Suppress' => 'OOF',
'Auto-Submitted' => 'auto-replied'
}
# Processes incoming emails
@@ -69,14 +69,11 @@ class MailHandler < ActionMailer::Base
# Ignore auto generated emails
self.class.ignored_emails_headers.each do |key, ignored_value|
value = email.header[key]
if value
value = value.to_s.downcase
if (ignored_value.is_a?(Regexp) && value.match(ignored_value)) || value == ignored_value
if logger && logger.info
logger.info "MailHandler: ignoring email with #{key}:#{value} header"
end
return false
if value && value.to_s.downcase == ignored_value.downcase
if logger && logger.info
logger.info "MailHandler: ignoring email with #{key}:#{value} header"
end
return false
end
end
@user = User.find_by_mail(sender_email) if sender_email.present?
@@ -166,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 = cleaned_up_subject
issue.subject = email.subject.to_s.chomp[0,255]
if issue.subject.blank?
issue.subject = '(no subject)'
end
@@ -226,7 +223,7 @@ class MailHandler < ActionMailer::Base
end
if !message.locked?
reply = Message.new(:subject => cleaned_up_subject.gsub(%r{^.*msg\d+\]}, '').strip,
reply = Message.new(:subject => email.subject.gsub(%r{^.*msg\d+\]}, '').strip,
:content => cleaned_up_text_body)
reply.author = user
reply.board = message.board
@@ -367,23 +364,6 @@ 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
+1 -3
View File
@@ -361,9 +361,7 @@ class Mailer < ActionMailer::Base
def self.with_synched_deliveries(&block)
saved_method = ActionMailer::Base.delivery_method
if m = saved_method.to_s.match(%r{^async_(.+)$})
synched_method = m[1]
ActionMailer::Base.delivery_method = synched_method.to_sym
ActionMailer::Base.send "#{synched_method}_settings=", ActionMailer::Base.send("async_#{synched_method}_settings")
ActionMailer::Base.delivery_method = m[1].to_sym
end
yield
ensure
+1 -1
View File
@@ -130,7 +130,7 @@ class Project < ActiveRecord::Base
end
def identifier_frozen?
errors[:identifier].blank? && !(new_record? || identifier.blank?)
errors[:identifier].nil? && !(new_record? || identifier.blank?)
end
# returns latest created projects
+6 -12
View File
@@ -174,9 +174,9 @@ class Query < ActiveRecord::Base
if values_for(field)
case type_for(field)
when :integer
add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !v.match(/^[+-]?\d+$/) }
add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !v.match(/^\d+$/) }
when :float
add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !v.match(/^[+-]?\d+(\.\d*)?$/) }
add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !v.match(/^\d+(\.\d*)?$/) }
when :date, :date_past
case operator_for(field)
when "=", ">=", "<=", "><"
@@ -848,18 +848,12 @@ class Query < ActiveRecord::Base
s = []
if from
from_yesterday = from - 1
from_yesterday_time = Time.local(from_yesterday.year, from_yesterday.month, from_yesterday.day)
if self.class.default_timezone == :utc
from_yesterday_time = from_yesterday_time.utc
end
s << ("#{table}.#{field} > '%s'" % [connection.quoted_date(from_yesterday_time.end_of_day)])
from_yesterday_utc = Time.gm(from_yesterday.year, from_yesterday.month, from_yesterday.day)
s << ("#{table}.#{field} > '%s'" % [connection.quoted_date(from_yesterday_utc.end_of_day)])
end
if to
to_time = Time.local(to.year, to.month, to.day)
if self.class.default_timezone == :utc
to_time = to_time.utc
end
s << ("#{table}.#{field} <= '%s'" % [connection.quoted_date(to_time.end_of_day)])
to_utc = Time.gm(to.year, to.month, to.day)
s << ("#{table}.#{field} <= '%s'" % [connection.quoted_date(to_utc.end_of_day)])
end
s.join(' AND ')
end
+4 -4
View File
@@ -22,7 +22,7 @@ class Repository < ActiveRecord::Base
belongs_to :project
has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
has_many :filechanges, :class_name => 'Change', :through => :changesets
has_many :changes, :through => :changesets
serialize :extra_info
@@ -37,8 +37,8 @@ class Repository < ActiveRecord::Base
validates_presence_of :identifier, :unless => Proc.new { |r| r.is_default? || r.set_as_default? }
validates_uniqueness_of :identifier, :scope => :project_id, :allow_blank => true
validates_exclusion_of :identifier, :in => %w(show entry raw changes annotate diff show stats graph)
# donwcase letters, digits, dashes, underscores but not digits only
validates_format_of :identifier, :with => /^(?!\d+$)[a-z0-9\-_]*$/, :allow_blank => true
# donwcase letters, digits, dashes but not digits only
validates_format_of :identifier, :with => /^(?!\d+$)[a-z0-9\-]*$/, :allow_blank => true
# Checks if the SCM is enabled when creating a repository
validate :repo_create_validation, :on => :create
@@ -228,7 +228,7 @@ class Repository < ActiveRecord::Base
:order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
:limit => limit)
else
filechanges.find(
changes.find(
:all,
:include => {:changeset => :user},
:conditions => ["path = ?", path.with_leading_slash],
+4 -4
View File
@@ -54,7 +54,7 @@ class Repository::Cvs < Repository
if entries
entries.each() do |entry|
if ( ! entry.lastrev.nil? ) && ( ! entry.lastrev.revision.nil? )
change = filechanges.find_by_revision_and_path(
change=changes.find_by_revision_and_path(
entry.lastrev.revision,
scm.with_leading_slash(entry.path) )
if change
@@ -94,7 +94,7 @@ class Repository::Cvs < Repository
if rev_to.to_i > 0
changeset_to = changesets.find_by_revision(rev_to)
end
changeset_from.filechanges.each() do |change_from|
changeset_from.changes.each() do |change_from|
revision_from = nil
revision_to = nil
if path.nil? || (change_from.path.starts_with? scm.with_leading_slash(path))
@@ -102,7 +102,7 @@ class Repository::Cvs < Repository
end
if revision_from
if changeset_to
changeset_to.filechanges.each() do |change_to|
changeset_to.changes.each() do |change_to|
revision_to = change_to.revision if change_to.path == change_from.path
end
end
@@ -133,7 +133,7 @@ class Repository::Cvs < Repository
# only add the change to the database, if it doen't exists. the cvs log
# is not exclusive at all.
tmp_time = revision.time.clone
unless filechanges.find_by_path_and_revision(
unless changes.find_by_path_and_revision(
scm.with_leading_slash(revision.paths[0][:path]),
revision.paths[0][:revision]
)
+1 -1
View File
@@ -79,7 +79,7 @@ class Repository::Darcs < Repository
return nil if patch_from.nil?
patch_to = changesets.find_by_revision(rev_to) if rev_to
if path.blank?
path = patch_from.filechanges.collect{|change| change.path}.join(' ')
path = patch_from.changes.collect{|change| change.path}.join(' ')
end
patch_from ? scm.diff(path, patch_from.scmid, patch_to ? patch_to.scmid : nil) : nil
end
+1 -1
View File
@@ -66,7 +66,7 @@ class TimeEntry < ActiveRecord::Base
end
}
safe_attributes 'hours', 'comments', 'issue_id', 'activity_id', 'spent_on', 'custom_field_values', 'custom_fields'
safe_attributes 'hours', 'comments', 'issue_id', 'activity_id', 'spent_on', 'custom_field_values'
def initialize(attributes=nil, *args)
super
+4 -16
View File
@@ -130,11 +130,8 @@ 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.empty?
return nil if password.to_s.empty?
user = find_by_login(login)
if user
# user is already in local database
@@ -167,7 +164,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.to_s)
tokens = Token.find_all_by_action_and_value('autologin', key)
# Make sure there's only 1 token that matches the key
if tokens.size == 1
token = tokens.first
@@ -341,12 +338,12 @@ class User < Principal
end
def self.find_by_rss_key(key)
token = Token.find_by_action_and_value('feeds', key.to_s)
token = Token.find_by_value(key)
token && token.user.active? ? token.user : nil
end
def self.find_by_api_key(key)
token = Token.find_by_action_and_value('api', key.to_s)
token = Token.find_by_action_and_value('api', key)
token && token.user.active? ? token.user : nil
end
@@ -373,15 +370,6 @@ class User < Principal
end
end
# Returns the day of +time+ according to user's time zone
def time_to_date(time)
if time_zone.nil?
time.to_date
else
time.in_time_zone(time_zone).to_date
end
end
def logged?
true
end
+1 -1
View File
@@ -143,7 +143,7 @@ class WikiPage < ActiveRecord::Base
if time = read_attribute(:updated_on)
# content updated_on was eager loaded with the page
begin
@updated_on = (self.class.default_timezone == :utc ? Time.parse(time.to_s).utc : Time.parse(time.to_s).localtime)
@updated_on = Time.zone ? Time.zone.parse(time.to_s) : Time.parse(time.to_s)
rescue
end
else
+1 -1
View File
@@ -1,4 +1,4 @@
<h2><%= @author.nil? ? l(:label_activity) : l(:label_user_activity, link_to_user(@author)).html_safe %></h2>
<h2><%= @author.nil? ? l(:label_activity) : l(:label_user_activity, link_to_user(@author)) %></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">
+1 -1
View File
@@ -10,7 +10,7 @@
<span>
<%= file_field_tag 'attachments[1][file]', :size => 30, :id => nil, :class => 'file',
:onchange => "checkFileSize(this, #{Setting.attachment_max_size.to_i.kilobytes}, '#{escape_javascript(l(:error_attachment_too_big, :max_size => number_to_human_size(Setting.attachment_max_size.to_i.kilobytes)))}');" -%>
<%= text_field_tag 'attachments[1][description]', '', :id => nil, :class => 'description', :maxlength => 255, :placeholder => l(:label_optional_description) %>
<%= text_field_tag 'attachments[1][description]', '', :id => nil, :class => 'description', :placeholder => l(:label_optional_description) %>
<%= link_to_function(image_tag('delete.png'), 'removeFileField(this)', :title => (l(:button_delete))) %>
</span>
</span>
+1 -1
View File
@@ -21,6 +21,6 @@
<% if @notes.present? %>
<label><%= radio_button_tag 'conflict_resolution', 'add_notes' %> <%= l(:text_issue_conflict_resolution_add_notes) %></label><br />
<% end %>
<label><%= radio_button_tag 'conflict_resolution', 'cancel' %> <%= l(:text_issue_conflict_resolution_cancel, :link => link_to_issue(@issue, :subject => false)).html_safe %></label>
<label><%= radio_button_tag 'conflict_resolution', 'cancel' %> <%= l(:text_issue_conflict_resolution_cancel, :link => link_to_issue(@issue, :subject => false)) %></label>
</p>
<p><%= submit_tag l(:button_submit) %></p>
+2 -2
View File
@@ -7,14 +7,14 @@
<div class="next-prev-links contextual">
<%= link_to_if @prev_issue_id,
"\xc2\xab #{l(:label_previous)}",
(@prev_issue_id ? issue_path(@prev_issue_id) : nil),
issue_path(@prev_issue_id),
:title => "##{@prev_issue_id}" %> |
<% if @issue_position && @issue_count %>
<span class="position"><%= l(:label_item_position, :position => @issue_position, :count => @issue_count) %></span> |
<% end %>
<%= link_to_if @next_issue_id,
"#{l(:label_next)} \xc2\xbb",
(@next_issue_id ? issue_path(@next_issue_id) : nil),
issue_path(@next_issue_id),
:title => "##{@next_issue_id}" %>
</div>
<% end %>
+1 -1
View File
@@ -1,3 +1,3 @@
<p><%= l(:mail_body_wiki_content_added, :id => link_to(h(@wiki_content.page.pretty_title), @wiki_content_url),
:author => h(@wiki_content.author)).html_safe %><br />
:author => h(@wiki_content.author)) %><br />
<em><%=h @wiki_content.comments %></em></p>
@@ -1,5 +1,5 @@
<p><%= l(:mail_body_wiki_content_updated, :id => link_to(h(@wiki_content.page.pretty_title), @wiki_content_url),
:author => h(@wiki_content.author)).html_safe %><br />
:author => h(@wiki_content.author)) %><br />
<em><%=h @wiki_content.comments %></em></p>
<p><%= l(:label_view_diff) %>:<br />
+1 -1
View File
@@ -31,7 +31,7 @@ entries_by_day = entries.group_by(&:spent_on)
<% entries_by_day[day].each do |entry| -%>
<tr class="time-entry" style="border-bottom: 1px solid #f5f5f5;">
<td class="activity"><%=h entry.activity %></td>
<td class="subject"><%=h entry.project %> <%= h(' - ') + link_to_issue(entry.issue, :truncate => 50) if entry.issue %></td>
<td class="subject"><%=h entry.project %> <%= ' - ' + link_to_issue(entry.issue, :truncate => 50) if entry.issue %></td>
<td class="comments"><%=h entry.comments %></td>
<td class="hours"><%= html_hours("%.2f" % entry.hours) %></td>
<td align="center">
+1 -2
View File
@@ -4,8 +4,7 @@
<%=l(:text_project_destroy_confirmation)%>
<% if @project_to_destroy.descendants.any? %>
<br /><%= l(:text_subprojects_destroy_warning,
content_tag('strong', h(@project_to_destroy.descendants.collect{|p| p.to_s}.join(', ')))).html_safe %>
<br /><%= l(:text_subprojects_destroy_warning, content_tag('strong', h(@project_to_destroy.descendants.collect{|p| p.to_s}.join(', ')))) %>
<% end %>
</p>
<p>
@@ -38,4 +38,4 @@
<% end %>
</div>
<p><%= link_to l(:label_version_new), new_project_version_path(@project, :back_url => ''), :class => 'icon icon-add' if User.current.allowed_to?(:manage_versions, @project) %></p>
<p><%= link_to l(:label_version_new), new_project_version_path(@project), :class => 'icon icon-add' if User.current.allowed_to?(:manage_versions, @project) %></p>
+1 -1
View File
@@ -1,6 +1,6 @@
<%= labelled_remote_form_for @wiki,
:as => :wiki,
:url => { :controller => 'wikis', :action => 'edit', :id => @project }, :method => 'post' do |f| %>
:url => { :controller => 'wikis', :action => 'edit', :id => @project } do |f| %>
<%= error_messages_for 'wiki' %>
+1 -1
View File
@@ -44,7 +44,7 @@
<% end %>
</fieldset>
<%= content_tag 'fieldset', :id => 'columns', :style => (query.has_default_columns? ? 'display:none;' : nil) do %>
<% content_tag 'fieldset', :id => 'columns', :style => (query.has_default_columns? ? 'display:none;' : nil) do %>
<legend><%= l(:field_column_names) %></legend>
<%= render :partial => 'queries/columns', :locals => {:query => query}%>
<% end %>
+1 -1
View File
@@ -1,5 +1,5 @@
<h2><%= l(:label_repository) %></h2>
<%= labelled_form_for :repository, @repository, :url => repository_path(@repository), :html => {:method => :put} do |f| %>
<%= labelled_form_for :repository, @repository, :url => repository_path(@path), :html => {:method => :put} do |f| %>
<%= render :partial => 'form', :locals => {:f => f} %>
<% end %>
+1 -1
View File
@@ -83,7 +83,7 @@
:id => @project,
:repository_id => @repository.identifier_param,
:path => "",
:rev => @changeset.identifier) if @changeset.filechanges.any? %></p>
:rev => @changeset.identifier) if @changeset.changes.any? %></p>
<div class="changeset-changes">
<%= render_changeset_changes %>
-1
View File
@@ -1,7 +1,6 @@
<h2><%= l(:label_spent_time) %></h2>
<%= labelled_form_for @time_entry, :url => time_entries_path do |f| %>
<%= hidden_field_tag 'project_id', params[:project_id] if params[:project_id] %>
<%= render :partial => 'form', :locals => {:f => f} %>
<%= submit_tag l(:button_create) %>
<%= submit_tag l(:button_create_and_continue), :name => 'continue' %>
+1 -1
View File
@@ -3,7 +3,7 @@
<%= select_tag 'user[mail_notification]', options_for_select(user_mail_notification_options(@user), @user.mail_notification),
:onchange => 'if (this.value == "selected") {Element.show("notified-projects")} else {Element.hide("notified-projects")}' %>
</p>
<%= content_tag 'div', :id => 'notified-projects', :style => (@user.mail_notification == 'selected' ? '' : 'display:none;') do %>
<% content_tag 'div', :id => 'notified-projects', :style => (@user.mail_notification == 'selected' ? '' : 'display:none;') do %>
<p><% @user.projects.each do |project| %>
<label><%= check_box_tag 'notified_project_ids[]', project.id, @user.notified_projects_ids.include?(project.id) %> <%=h project.name %></label><br />
<% end %></p>
-1
View File
@@ -26,7 +26,6 @@ module RedmineApp
config.active_record.observers = :message_observer, :issue_observer, :journal_observer, :news_observer, :document_observer, :wiki_content_observer, :comment_observer
config.active_record.store_full_sti_class = true
config.active_record.default_timezone = :local
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
+1 -1
View File
@@ -43,7 +43,7 @@ module ActionView
end
end
ActionView::Base.field_error_proc = Proc.new{ |html_tag, instance| html_tag || ''.html_safe }
ActionView::Base.field_error_proc = Proc.new{ |html_tag, instance| "#{html_tag}" }
require 'mail'
+2 -2
View File
@@ -76,14 +76,14 @@ ar:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "KB"
kb: "kB"
mb: "MB"
gb: "GB"
tb: "TB"
+1 -1
View File
@@ -78,7 +78,7 @@ bg:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
+1 -1
View File
@@ -89,7 +89,7 @@ bs:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
+1 -1
View File
@@ -80,7 +80,7 @@ ca:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
+2 -2
View File
@@ -81,14 +81,14 @@ cs:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
byte:
one: "Bajt"
other: "Bajtů"
kb: "KB"
kb: "kB"
mb: "MB"
gb: "GB"
tb: "TB"
+1 -1
View File
@@ -91,7 +91,7 @@ da:
format:
# separator:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
+2 -2
View File
@@ -93,7 +93,7 @@ de:
human:
format:
delimiter: ""
precision: 3
precision: 1
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: Mehrere Werte
field_multiple: Mehrer 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)
+1 -1
View File
@@ -77,7 +77,7 @@ el:
precision: 3
human:
format:
precision: 3
precision: 1
delimiter: ""
storage_units:
format: "%n %u"
+2 -2
View File
@@ -81,14 +81,14 @@ en-GB:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "KB"
kb: "kB"
mb: "MB"
gb: "GB"
tb: "TB"
+2 -2
View File
@@ -77,14 +77,14 @@ en:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "KB"
kb: "kB"
mb: "MB"
gb: "GB"
tb: "TB"
+1 -1
View File
@@ -48,7 +48,7 @@ es:
# These three are to override number.format and are optional
# separator:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
+1 -1
View File
@@ -94,7 +94,7 @@ et:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
+1 -1
View File
@@ -79,7 +79,7 @@ eu:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
+1 -1
View File
@@ -77,7 +77,7 @@ fa:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
+1 -1
View File
@@ -64,7 +64,7 @@ fi:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
+1 -1
View File
@@ -91,7 +91,7 @@ fr:
format: '%n %u'
human:
format:
precision: 3
precision: 2
storage_units:
format: "%n %u"
units:
+1 -1
View File
@@ -32,7 +32,7 @@ gl:
format:
# separator:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
+1 -1
View File
@@ -72,7 +72,7 @@ hr:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
+1 -1
View File
@@ -97,7 +97,7 @@
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
+1 -1
View File
@@ -78,7 +78,7 @@ id:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
+2 -2
View File
@@ -96,7 +96,7 @@ ja:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
@@ -758,7 +758,7 @@ ja:
label_week:
label_date_from: "日付指定: "
label_date_to: から
label_language_based: ユーザの言語の設定に従う
label_language_based: 既定の言語の設定に従う
label_sort_by: "並び替え %{value}"
label_send_test_email: テストメールを送信
label_feeds_access_key: RSSアクセスキー
+1 -1
View File
@@ -119,7 +119,7 @@ ko:
# These three are to override number.format and are optional
# separator:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
+1 -1
View File
@@ -109,7 +109,7 @@ lt:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
# Storage units output formatting.
# %u is the storage unit, %n is the number (default: 2 MB)
+1 -1
View File
@@ -72,7 +72,7 @@ lv:
human:
format:
delimiter: " "
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
+1 -1
View File
@@ -77,7 +77,7 @@ mk:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
+1 -1
View File
@@ -75,7 +75,7 @@ mn:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
+1 -1
View File
@@ -74,7 +74,7 @@ nl:
precision: 3
human:
format:
precision: 3
precision: 1
delimiter: ""
storage_units:
format: "%n %u"
+1 -1
View File
@@ -63,7 +63,7 @@
other: "nesten %{count} år"
number:
format:
precision: 3
precision: 2
separator: "."
delimiter: ","
currency:
+1 -1
View File
@@ -21,7 +21,7 @@ pl:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
+5 -5
View File
@@ -18,10 +18,10 @@ pt-BR:
time:
formats:
default: "%A, %d de %B de %Y, %H:%M h"
time: "%H:%M h"
short: "%d/%m, %H:%M h"
long: "%A, %d de %B de %Y, %H:%M h"
default: "%A, %d de %B de %Y, %H:%M hs"
time: "%H:%M hs"
short: "%d/%m, %H:%M hs"
long: "%A, %d de %B de %Y, %H:%M hs"
only_second: "%S"
datetime:
formats:
@@ -97,7 +97,7 @@ pt-BR:
delimiter: '.'
human:
format:
precision: 3
precision: 1
delimiter: '.'
storage_units:
format: "%n %u"
+1 -1
View File
@@ -89,7 +89,7 @@ pt:
delimiter: ''
human:
format:
precision: 3
precision: 1
delimiter: ''
storage_units:
format: "%n %u"
+1 -1
View File
@@ -73,7 +73,7 @@ ro:
human:
format:
precision: 3
precision: 1
delimiter: ""
storage_units:
format: "%n %u"
+35 -35
View File
@@ -68,7 +68,7 @@ ru:
human:
format:
delimiter: ""
precision: 3
precision: 2
# Rails 2.2
# storage_units: [байт, КБ, МБ, ГБ, ТБ]
@@ -1089,11 +1089,11 @@ ru:
notice_issue_successful_create: Задача %{id} создана.
label_between: между
setting_issue_group_assignment: Разрешить назначение задач группам пользователей
label_diff: Разница(diff)
label_diff: diff
text_git_repository_note: Repository is bare and local (e.g. /gitrepo, c:\gitrepo)
description_query_sort_criteria_direction: Порядок сортировки
description_query_sort_criteria_direction: Sort direction
description_project_scope: Search scope
description_filter: Фильтр
description_filter: Filter
description_user_mail_notification: Mail notification settings
description_date_from: Enter start date
description_message_content: Message content
@@ -1101,23 +1101,23 @@ ru:
description_date_range_interval: Choose range by selecting start and end date
description_issue_category_reassign: Choose issue category
description_search: Searchfield
description_notes: Примечания
description_date_range_list: Выберите диапазон из списка
description_choose_project: Проекты
description_date_to: Введите дату выполнения
description_query_sort_criteria_attribute: Критерий сортировки
description_wiki_subpages_reassign: Выбрать новую родительскую страницу
description_selected_columns: Выбранные столбцы
label_parent_revision: Родительский
label_child_revision: Дочерний
error_scm_annotate_big_text_file: Комментарий невозможен из-за превышения максимального размера текстового файла.
setting_default_issue_start_date_to_creation_date: Использовать текущую дату в качестве даты начала для новых задач
button_edit_section: Редактировать эту секцию
setting_repositories_encodings: Кодировка вложений и хранилищ
description_all_columns: Все столбцы
button_export: Экспорт
label_export_options: "%{export_format} параметры экспорта"
error_attachment_too_big: Этот файл нельзя загрузить из-за превышения максимального размера файла (%{max_size})
description_notes: Notes
description_date_range_list: Choose range from list
description_choose_project: Projects
description_date_to: Enter end date
description_query_sort_criteria_attribute: Sort attribute
description_wiki_subpages_reassign: Choose new parent page
description_selected_columns: Selected Columns
label_parent_revision: Parent
label_child_revision: Child
error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
button_edit_section: Edit this section
setting_repositories_encodings: Attachments and repositories encodings
description_all_columns: All Columns
button_export: Export
label_export_options: "%{export_format} export options"
error_attachment_too_big: This file cannot be uploaded because it exceeds the maximum allowed file size (%{max_size})
notice_failed_to_save_time_entries: "Failed to save %{count} time entrie(s) on %{total} selected: %{ids}."
label_x_issues:
zero: 0 Задач
@@ -1125,21 +1125,21 @@ ru:
few: "%{count} Задач"
many: "%{count} Задач"
other: "%{count} Задач"
label_repository_new: Новое хранилище
field_repository_is_default: Хранилище по умолчанию
label_copy_attachments: Копировать вложения
label_repository_new: New repository
field_repository_is_default: Main repository
label_copy_attachments: Copy attachments
label_item_position: "%{position}/%{count}"
label_completed_versions: Завершенные версии
text_project_identifier_info: Допускаются только строчные латинские буквы (a-z), цифры, тире и подчеркивания.<br />После сохранения идентификатор изменить нельзя.
field_multiple: Множественные значения
setting_commit_cross_project_ref: Разрешить ссылаться и исправлять задачи во всех остальных проектах
text_issue_conflict_resolution_add_notes: Добавить мои примечания и отказаться от моих изменений
text_issue_conflict_resolution_overwrite: Применить мои изменения (все предыдущие замечания будут сохранены, но некоторые изменения могут быть перезаписаны)
notice_issue_update_conflict: Кто-то изменил задачу, пока вы ее редактировали.
text_issue_conflict_resolution_cancel: Отменить мои изменения и показать задачу заново %{link}
permission_manage_related_issues: Управление связанными задачами
field_ldap_filter: Фильтр LDAP
label_search_for_watchers: Найти наблюдателей
label_completed_versions: Completed versions
text_project_identifier_info: Only lower case letters (a-z), numbers, dashes and underscores are allowed.<br />Once saved, the identifier cannot be changed.
field_multiple: Multiple values
setting_commit_cross_project_ref: Allow issues of all the other projects to be referenced and fixed
text_issue_conflict_resolution_add_notes: Add my notes and discard my other changes
text_issue_conflict_resolution_overwrite: Apply my changes anyway (previous notes will be kept but some changes may be overwritten)
notice_issue_update_conflict: The issue has been updated by an other user while you were editing it.
text_issue_conflict_resolution_cancel: Discard all my changes and redisplay %{link}
permission_manage_related_issues: Manage related issues
field_ldap_filter: LDAP filter
label_search_for_watchers: Search for watchers to add
notice_account_deleted: "Ваша учетная запись полностью удалена"
setting_unsubscribe: "Разрешить пользователям удалять свои учетные записи"
button_delete_my_account: "Удалить мою учетную запись"
+1 -1
View File
@@ -75,7 +75,7 @@ sk:
human:
format:
precision: 3
precision: 1
delimiter: ""
storage_units:
format: "%n %u"
+1 -1
View File
@@ -75,7 +75,7 @@ sl:
precision: 3
human:
format:
precision: 3
precision: 1
delimiter: ""
storage_units:
format: "%n %u"
+2 -2
View File
@@ -77,14 +77,14 @@ sq:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "KB"
kb: "kB"
mb: "MB"
gb: "GB"
tb: "TB"
+1 -1
View File
@@ -77,7 +77,7 @@ sr-YU:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
+1 -1
View File
@@ -77,7 +77,7 @@ sr:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
+2 -2
View File
@@ -47,14 +47,14 @@ sv:
# These three are to override number.format and are optional
# separator:
delimiter: ""
precision: 3
# precision: 1
storage_units:
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "KB"
kb: "kB"
mb: "MB"
gb: "GB"
tb: "TB"
+1 -1
View File
@@ -74,7 +74,7 @@ th:
precision: 3
human:
format:
precision: 3
precision: 1
delimiter: ""
storage_units:
format: "%n %u"
+1 -1
View File
@@ -99,7 +99,7 @@ tr:
format:
delimiter: '.'
separator: ','
precision: 3
precision: 2
storage_units:
format: "%n %u"
units:
+1 -1
View File
@@ -74,7 +74,7 @@ uk:
precision: 3
human:
format:
precision: 3
precision: 1
delimiter: ""
storage_units:
format: "%n %u"
+1 -1
View File
@@ -48,7 +48,7 @@ vi:
# These three are to override number.format and are optional
# separator:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
+1 -1
View File
@@ -87,7 +87,7 @@
# 下列三個選項設定, 若有設定值將會取代 number.format 成為預設值
# separator:
delimiter: ""
precision: 3
precision: 1
# 儲存單位輸出格式.
# %u 是儲存單位, %n 是數值 (預設值: 2 MB)
storage_units:
+2 -2
View File
@@ -79,14 +79,14 @@ zh:
human:
format:
delimiter: ""
precision: 3
precision: 1
storage_units:
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "KB"
kb: "kB"
mb: "MB"
gb: "GB"
tb: "TB"
-2
View File
@@ -224,7 +224,6 @@ RedmineApp::Application.routes.draw do
get 'projects/:id/repository/:repository_id/revisions', :to => 'repositories#revisions'
get 'projects/:id/repository/:repository_id/revisions/:rev/:action(/*path(.:ext))',
:controller => 'repositories',
:format => false,
:constraints => {
:action => /(browse|show|entry|raw|annotate|diff)/,
:rev => /[a-z0-9\.\-_]+/
@@ -243,7 +242,6 @@ RedmineApp::Application.routes.draw do
delete 'projects/:id/repository/revisions/:rev/issues/:issue_id', :to => 'repositories#remove_related_issue'
get 'projects/:id/repository/revisions/:rev/:action(/*path(.:ext))',
:controller => 'repositories',
:format => false,
:constraints => {
:action => /(browse|show|entry|raw|annotate|diff)/,
:rev => /[a-z0-9\.\-_]+/
-63
View File
@@ -4,69 +4,6 @@ Redmine - project management software
Copyright (C) 2006-2012 Jean-Philippe Lang
http://www.redmine.org/
== 2012-09-16 v2.0.4
* Defect #10818: Running rake in test environment causes exception
* Defect #11209: Wiki diff may generate broken HTML
* Defect #11217: Project names in drop-down are escaped twice
* Defect #11262: Link is escaped in wiki added/updated notification email
* Defect #11307: Can't filter for negative numeric custom fields
* Defect #11325: Unified diff link broken on specific file/revision diff view
* Defect #11341: Escaped link in conflict resolution form
* Defect #11365: Attachment description length is not validated
* Defect #11511: Confirmation page has broken HTML when a project folding sub project is deleted
* Defect #11533: rake redmine:plugins:test doesn't run tests in subdirectories
* Defect #11541: Version sharing is missing in the REST API
* Defect #11550: Issue reminder doesn't work when using asynchronous delivery
* Defect #11776: Can't override mailer views inside redmine plugin.
* Defect #11789: Edit section links broken with h5/h6 headings
* Feature #11338: Exclude emails with auto-submitted => auto-generated
* Patch #11299: redmine:plugins:migrate should update db/schema.rb
* Patch #11328: Fix Japanese mistranslation for 'label_language_based'
* Patch #11448: Russian translation for 1.4-stable and 2.0-stable
* Patch #11600: Fix plural form of the abbreviation for hours in Brazilian Portuguese
== 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
* Defect #11038: "Create and continue" should preserve project, issue and activity when logging time
* Defect #11046: Redmine.pm does not support "bind as user" ldap authentication
* Defect #11051: reposman.rb fails in 1.4.2 because of missing require for rubygems
* Defect #11085: Wiki start page can't be changed
* Feature #11084: Update Rails to 3.2.5
== 2012-05-28 v2.0.1
* Defect #10923: After creating a new Version Redmine jumps back to "Information"
* Defect #10932: Links to delete watchers are escaped when gravatars are enabled
* Defect #10964: Updated column doesn't get updated on issues
* Defect #10965: rake yard does not work for generating documentation.
* Defect #10972: Columns selection not displayed on the custom query form
* Defect #10991: My page > Spent time 'project' column is html-encoded
* Defect #10996: Time zones lost when upgrading from Redmine 1.4 to 2.0
* Defect #11013: Fetching Email from IMAP/POP3 - uninitialized constant RAILS_DEFAULT_LOGGER error
* Defect #11024: redmine_plugin_model generator does not create the migration
* Defect #11027: Saving new query without name causes escaping of input field
* Defect #11028: Project identifier can be updated
== 2012-05-15 v2.0.0
* Feature #4796: Rails 3 support
+2 -5
View File
@@ -13,7 +13,7 @@ http://www.redmine.org/
* A database:
* MySQL (tested with MySQL 5.1)
* PostgreSQL (tested with PostgreSQL 9.1)
* PostgreSQL (tested with PostgreSQL 8.4)
* SQLite3 (tested with SQLite 3.6)
Optional:
@@ -38,10 +38,7 @@ Optional:
3. Create an empty utf8 encoded database: "redmine" for example
4. Configure the database parameters in config/database.yml
for the "production" environment (default database is MySQL and ruby1.8)
If you're running Redmine with MySQL and ruby1.9, replace the adapter name
with `mysql2`
for the "production" environment (default database is MySQL)
5. Generate a session store secret
+2 -9
View File
@@ -366,19 +366,12 @@ sub is_member {
);
$sthldap->execute($auth_source_id);
while (my @rowldap = $sthldap->fetchrow_array) {
my $bind_as = $rowldap[3] ? $rowldap[3] : "";
my $bind_pw = $rowldap[4] ? $rowldap[4] : "";
if ($bind_as =~ m/\$login/) {
# replace $login with $redmine_user and use $redmine_pass
$bind_as =~ s/\$login/$redmine_user/g;
$bind_pw = $redmine_pass
}
my $ldap = Authen::Simple::LDAP->new(
host => ($rowldap[2] eq "1" || $rowldap[2] eq "t") ? "ldaps://$rowldap[0]:$rowldap[1]" : $rowldap[0],
port => $rowldap[1],
basedn => $rowldap[5],
binddn => $bind_as,
bindpw => $bind_pw,
binddn => $rowldap[3] ? $rowldap[3] : "",
bindpw => $rowldap[4] ? $rowldap[4] : "",
filter => "(".$rowldap[6]."=%s)"
);
my $method = $r->method;
-1
View File
@@ -3,7 +3,6 @@
require 'optparse'
require 'find'
require 'etc'
require 'rubygems'
Version = "1.4"
SUPPORTED_SCM = %w( Subversion Darcs Mercurial Bazaar Git Filesystem )
+1 -17
View File
@@ -2,23 +2,7 @@ Description:
The plugin generator creates stubs for a new Redmine plugin.
Example:
./script/rails generate redmine_plugin meetings
create plugins/meetings/app
create plugins/meetings/app/controllers
create plugins/meetings/app/helpers
create plugins/meetings/app/models
create plugins/meetings/app/views
create plugins/meetings/db/migrate
create plugins/meetings/lib/tasks
create plugins/meetings/assets/images
create plugins/meetings/assets/javascripts
create plugins/meetings/assets/stylesheets
create plugins/meetings/config/locales
create plugins/meetings/test
create plugins/meetings/test/fixtures
create plugins/meetings/test/unit
create plugins/meetings/test/functional
create plugins/meetings/test/integration
./script/generate redmine_plugin meetings
create plugins/meetings/README.rdoc
create plugins/meetings/init.rb
create plugins/meetings/config/routes.rb
@@ -2,4 +2,4 @@ Description:
Generates a plugin controller.
Example:
./script/rails generate redmine_plugin_controller meetings pools index show vote
./script/generate redmine_plugin_controller meetings pools index show vote
+1 -1
View File
@@ -2,4 +2,4 @@ Description:
Generates a plugin model.
Examples:
./script/rails generate redmine_plugin_model meetings pool
./script/generate redmine_plugin_model meetings pool
@@ -1,12 +1,6 @@
class RedminePluginModelGenerator < Rails::Generators::NamedBase
source_root File.expand_path("../templates", __FILE__)
argument :model, :type => :string
argument :attributes, :type => :array, :default => [], :banner => "field[:type][:index] field[:type][:index]"
class_option :migration, :type => :boolean
class_option :timestamps, :type => :boolean
class_option :parent, :type => :string, :desc => "The parent class for the generated model"
class_option :indexes, :type => :boolean, :default => true, :desc => "Add indexes for references and belongs_to columns"
attr_reader :plugin_path, :plugin_name, :plugin_pretty_name
@@ -16,26 +10,10 @@ class RedminePluginModelGenerator < Rails::Generators::NamedBase
@plugin_pretty_name = plugin_name.titleize
@plugin_path = "plugins/#{plugin_name}"
@model_class = model.camelize
@table_name = @model_class.tableize
@migration_filename = "create_#{@table_name}"
@migration_class_name = @migration_filename.camelize
end
def copy_templates
template 'model.rb.erb', "#{plugin_path}/app/models/#{model.underscore}.rb"
template 'unit_test.rb.erb', "#{plugin_path}/test/unit/#{model.underscore}_test.rb"
migration_filename = "%03i_#{@migration_filename}.rb" % (migration_number + 1)
template "migration.rb", "#{plugin_path}/db/migrate/#{migration_filename}"
end
def attributes_with_index
attributes.select { |a| a.has_index? || (a.reference? && options[:indexes]) }
end
def migration_number
current = Dir.glob("#{plugin_path}/db/migrate/*.rb").map do |file|
File.basename(file).split("_").first.to_i
end.max.to_i
template 'model.rb.erb', "#{plugin_path}/app/models/#{model}.rb"
template 'unit_test.rb.erb', "#{plugin_path}/test/unit/#{model}_test.rb"
end
end
@@ -1,15 +0,0 @@
class <%= @migration_class_name %> < ActiveRecord::Migration
def change
create_table :<%= @table_name %> do |t|
<% attributes.each do |attribute| -%>
t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %>
<% end -%>
<% if options[:timestamps] %>
t.timestamps
<% end -%>
end
<% attributes_with_index.each do |attribute| -%>
add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
<% end -%>
end
end
@@ -131,15 +131,6 @@ 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.
@@ -218,7 +209,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.where(conditions).reorder("#{position_column} DESC").first
acts_as_list_class.find(:first, :conditions => conditions, :order => "#{position_column} DESC")
end
# Forces item to assume the bottom position in the list.
+21 -28
View File
@@ -283,7 +283,6 @@ class TCPDF
@state ||= 0
@tableborder ||= 0
@tdbegin ||= false
@tdtext ||= ''
@tdwidth ||= 0
@tdheight ||= 0
@tdalign ||= "L"
@@ -3511,12 +3510,28 @@ class TCPDF
else
#Text
if (@tdbegin)
element.gsub!(/[\t\r\n\f]/, "");
@tdtext << element.gsub(/&nbsp;/, " ");
elsif (@href)
if (@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)
@@ -3810,7 +3825,6 @@ class TCPDF
@x += 5;
when 'table'
Ln();
if @default_table_columns < @max_table_columns[@table_id]
@table_columns = @max_table_columns[@table_id];
else
@@ -3907,11 +3921,6 @@ 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);
@@ -4070,23 +4079,6 @@ 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;
@@ -4134,6 +4126,7 @@ class TCPDF
@l_margin -= 5;
@r_margin -= 5;
@tableborder=0;
Ln();
@table_id += 1;
when 'strong'
+4 -5
View File
@@ -50,17 +50,16 @@ module Redmine
words_add += 1
else
del_at = pos unless del_at
deleted << ' ' unless deleted.empty?
deleted << h(change[2])
deleted << ' ' + h(change[2])
words_del += 1
end
end
if add_at
words[add_at] = '<span class="diff_in">'.html_safe + words[add_at]
words[add_to] = words[add_to] + '</span>'.html_safe
words[add_at] = '<span class="diff_in">' + words[add_at]
words[add_to] = words[add_to] + '</span>'
end
if del_at
words.insert del_at - del_off + dels + words_add, '<span class="diff_out">'.html_safe + deleted + '</span>'.html_safe
words.insert del_at - del_off + dels + words_add, '<span class="diff_out">' + deleted + '</span>'
dels += 1
del_off += words_del
words_del = 0
+3 -3
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 ORDER BY c.value LIMIT 1)",
@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)",
: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 ORDER BY c.value LIMIT 1)",
@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)",
: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 ORDER BY c.value LIMIT 1)",
@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)",
:format => cf.field_format,
:label => cf.name}
end
+3 -10
View File
@@ -107,13 +107,7 @@ module Redmine
#
def self.render_on(hook, options={})
define_method hook do |context|
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
context[:controller].send(:render_to_string, {:locals => context}.merge(options))
end
end
@@ -144,15 +138,14 @@ 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, :hook_caller => self}
default_context = {:controller => self, :project => @project, :request => request}
Redmine::Hook.call_hook(hook, default_context.merge(context))
else
default_context = { :project => @project, :hook_caller => self }
default_context = { :project => @project }
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
+1 -1
View File
@@ -53,7 +53,7 @@ module Redmine
private
def logger
::Rails.logger
RAILS_DEFAULT_LOGGER
end
end
end
-1
View File
@@ -82,7 +82,6 @@ module Redmine #:nodoc:
view_path = File.join(p.directory, 'app', 'views')
if File.directory?(view_path)
ActionController::Base.prepend_view_path(view_path)
ActionMailer::Base.prepend_view_path(view_path)
end
# Adds the app/{controllers,helpers,models} directories of the plugin to the autoload path

Some files were not shown because too many files have changed in this diff Show More