Compare commits

..

43 Commits
0.8.3 ... 0.8.7

Author SHA1 Message Date
Jean-Philippe Lang
78db2984fa tagged version 0.8.7
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/tags/0.8.7@3057 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-11-15 10:22:28 +00:00
Jean-Philippe Lang
b49aaccdd6 Set version to 0.8.7.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@3056 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-11-15 10:05:49 +00:00
Jean-Philippe Lang
5abb5bf6e1 Do not version control session_store.rb.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@3055 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-11-15 10:05:30 +00:00
Eric Davis
638a9a264a Ported the session_store.rb generation task from trunk.
Loosely based on r2493 but adapted for Rails 2.1.2.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@3054 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-11-14 20:19:09 +00:00
Eric Davis
051741f05c Merged r3051 from trunk with some changes for 0.8 sessions.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@3053 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-11-14 20:19:03 +00:00
Eric Davis
ce41d4f9b0 Merged r3050 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@3052 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-11-14 20:18:57 +00:00
Eric Davis
540d204e9a Updated CHANGELOG
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@3048 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-11-14 18:17:42 +00:00
Eric Davis
2fd4e56ecd Merged r3032 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@3047 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-11-14 18:17:37 +00:00
Eric Davis
3d926660ba Merged r3009 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@3046 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-11-14 18:17:31 +00:00
Eric Davis
b19ba93966 Merged r3030 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@3045 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-11-14 18:17:25 +00:00
Eric Davis
36cc9af651 Merged r3026 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@3044 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-11-14 18:17:20 +00:00
Jean-Philippe Lang
58c24ef2c8 Version set to 0.8.6 and CHANGELOG updated.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@3001 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-11-04 10:25:49 +00:00
Jean-Philippe Lang
ff1b7f4832 Merged r2999 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@3000 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-11-04 10:23:53 +00:00
Jean-Philippe Lang
c12b96a89f Merged r2979 and r2980 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2998 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-11-04 10:11:47 +00:00
Jean-Philippe Lang
341a3d1ed6 Merged r2261 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2997 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-11-04 10:07:20 +00:00
Jean-Philippe Lang
4be1c1ad58 Merged r2894 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2996 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-11-04 10:03:06 +00:00
Jean-Philippe Lang
eaf0ea0c23 Merged r2924 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2995 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-11-04 09:57:11 +00:00
Jean-Philippe Lang
d09afa64c8 Merged r2986 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2987 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-10-29 18:16:50 +00:00
Jean-Philippe Lang
48ee58f6b9 Merged r2983 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2984 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-10-29 17:57:10 +00:00
Eric Davis
3601c13b3b Updated CHANGELOG
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2900 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-09-23 01:39:13 +00:00
Eric Davis
88c95e824f Merged r2818 and r2819 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2899 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-09-23 01:38:15 +00:00
Jean-Philippe Lang
6ecfcd84b7 Update version to 0.8.5.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2890 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-09-17 17:49:48 +00:00
Jean-Philippe Lang
d884cc3870 CHANGELOG update
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2885 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-09-13 12:01:35 +00:00
Jean-Philippe Lang
bdfee87349 Merged r2766 and r2783 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2884 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-09-13 11:58:48 +00:00
Jean-Philippe Lang
2ec385858f Merged r2722, r2771, r2772 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2883 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-09-13 11:53:27 +00:00
Jean-Philippe Lang
94488269d1 Merged r2768, r2773, r2774, r2775, r2796 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2882 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-09-13 11:46:39 +00:00
Jean-Philippe Lang
3d0fbea9fd Merged r2797, r2804, r2814, r2820, r2837, r2838 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2881 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-09-13 11:36:09 +00:00
Eric Davis
3d68b60063 Fixed a merge error in r2830, wrong hook variables where used.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2831 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-08-01 18:28:57 +00:00
Eric Davis
27b110a61b Merged r2829 from trunk
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2830 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-08-01 18:17:46 +00:00
Jean-Philippe Lang
d6394b60a5 Merged r2794 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2795 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-06-28 11:57:04 +00:00
Eric Davis
ffd720eec9 Merged r2790 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2791 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-06-14 16:23:56 +00:00
Jean-Philippe Lang
50c0cebe96 Merged r2763 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2764 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-05-25 18:14:15 +00:00
Jean-Philippe Lang
1342228771 Translations updates.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2753 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-05-17 14:05:55 +00:00
Jean-Philippe Lang
6872c5e0b9 Updates for 0.8.4 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2748 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-05-17 09:16:23 +00:00
Jean-Philippe Lang
e27460a0a7 Backported r2740 to r2742 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2747 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-05-17 09:13:38 +00:00
Jean-Philippe Lang
3f6656a495 Merged r2697, r2720, r2723, r2724, r2727, r2745 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2746 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-05-17 08:57:38 +00:00
Jean-Philippe Lang
a3f69e248a Merged r2718 from trunk (#3291).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2719 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-05-10 08:40:51 +00:00
Eric Davis
3022d067ba Merged r2713 from trunk. #3086
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2714 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-05-06 04:57:52 +00:00
Jean-Philippe Lang
e06987ab52 Merged r2707 and r2708 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2709 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-05-04 17:39:57 +00:00
Jean-Philippe Lang
ace65df230 Merged r2705 from trunk (#3291).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2706 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-05-04 16:50:03 +00:00
Jean-Philippe Lang
c5dadfd322 Backported r2690 from trunk (#3229).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2691 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-04-25 09:09:50 +00:00
Eric Davis
7a59f2df12 Merged r2686, r2687 from trunk
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2688 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-04-24 21:30:05 +00:00
Jean-Philippe Lang
7b699af837 Merged r2664, r2665, r2670, r2674, r2677, r2679 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2681 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-04-24 16:13:35 +00:00
68 changed files with 566 additions and 91 deletions

View File

@@ -35,6 +35,10 @@ class AccountController < ApplicationController
events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
@events_by_day = events.group_by(&:event_date)
if @user != User.current && !User.current.admin? && @memberships.empty? && events.empty?
render_404 and return
end
rescue ActiveRecord::RecordNotFound
render_404
end

View File

@@ -19,10 +19,17 @@ require 'uri'
require 'cgi'
class ApplicationController < ActionController::Base
class MissingSessionSecret < Exception ; end
layout 'base'
before_filter :user_setup, :check_if_login_required, :set_localization
filter_parameter_logging :password
if session.first[:secret].blank?
raise MissingSessionSecret, "Missing session secret. Please run 'rake config/initializers/session_store.rb' to generate one"
else
protect_from_forgery :secret => session.first[:secret]
end
include Redmine::MenuManager::MenuController
helper Redmine::MenuManager::MenuHelper
@@ -82,7 +89,13 @@ class ApplicationController < ActionController::Base
def require_login
if !User.current.logged?
redirect_to :controller => "account", :action => "login", :back_url => url_for(params)
# Extract only the basic url parameters on non-GET requests
if request.get?
url = url_for(params)
else
url = url_for(:controller => params[:controller], :action => params[:action], :id => params[:id], :project_id => params[:project_id])
end
redirect_to :controller => "account", :action => "login", :back_url => url
return false
end
true

View File

@@ -23,7 +23,7 @@ class IssuesController < ApplicationController
before_filter :find_project, :only => [:new, :update_form, :preview]
before_filter :authorize, :except => [:index, :changes, :gantt, :calendar, :preview, :update_form, :context_menu]
before_filter :find_optional_project, :only => [:index, :changes, :gantt, :calendar]
accept_key_auth :index, :changes
accept_key_auth :index, :show, :changes
helper :journals
helper :projects
@@ -43,6 +43,10 @@ class IssuesController < ApplicationController
helper :timelog
include Redmine::Export::PDF
verify :method => :post,
:only => :destroy,
:render => { :nothing => true, :status => :method_not_allowed }
def index
retrieve_query
sort_init 'id', 'desc'
@@ -147,6 +151,7 @@ class IssuesController < ApplicationController
attach_files(@issue, params[:attachments])
flash[:notice] = l(:notice_successful_create)
Mailer.deliver_issue_add(@issue) if Setting.notified_events.include?('issue_added')
call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
redirect_to(params[:continue] ? { :action => 'new', :tracker_id => @issue.tracker } :
{ :action => 'show', :id => @issue })
return
@@ -194,6 +199,7 @@ class IssuesController < ApplicationController
flash[:notice] = l(:notice_successful_update)
Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
end
call_hook(:controller_issues_edit_after_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => journal})
redirect_to(params[:back_to] || {:action => 'show', :id => @issue})
end
end
@@ -343,10 +349,12 @@ class IssuesController < ApplicationController
@gantt.events = events
end
basename = (@project ? "#{@project.identifier}-" : '') + 'gantt'
respond_to do |format|
format.html { render :template => "issues/gantt.rhtml", :layout => !request.xhr? }
format.png { send_data(@gantt.to_image, :disposition => 'inline', :type => 'image/png', :filename => "#{@project.identifier}-gantt.png") } if @gantt.respond_to?('to_image')
format.pdf { send_data(gantt_to_pdf(@gantt, @project), :type => 'application/pdf', :filename => "#{@project.nil? ? '' : "#{@project.identifier}-" }gantt.pdf") }
format.png { send_data(@gantt.to_image, :disposition => 'inline', :type => 'image/png', :filename => "#{basename}.png") } if @gantt.respond_to?('to_image')
format.pdf { send_data(gantt_to_pdf(@gantt, @project), :type => 'application/pdf', :filename => "#{basename}.pdf") }
end
end

View File

@@ -46,6 +46,7 @@ class MessagesController < ApplicationController
@message.sticky = params[:message]['sticky']
end
if request.post? && @message.save
call_hook(:controller_messages_new_after_save, { :params => params, :message => @message})
attach_files(@message, params[:attachments])
redirect_to :action => 'show', :id => @message
end
@@ -58,6 +59,7 @@ class MessagesController < ApplicationController
@reply.board = @board
@topic.children << @reply
if !@reply.new_record?
call_hook(:controller_messages_reply_after_save, { :params => params, :message => @reply})
attach_files(@reply, params[:attachments])
end
redirect_to :action => 'show', :id => @topic

View File

@@ -65,6 +65,7 @@ class NewsController < ApplicationController
flash[:notice] = l(:label_comment_added)
redirect_to :action => 'show', :id => @news
else
show
render :action => 'show'
end
end

View File

@@ -88,7 +88,7 @@ class TimelogController < ApplicationController
sql << " WHERE"
sql << " (%s) AND" % @project.project_condition(Setting.display_subprojects_issues?) if @project
sql << " (%s) AND" % Project.allowed_to_condition(User.current, :view_time_entries)
sql << " (spent_on BETWEEN '%s' AND '%s')" % [ActiveRecord::Base.connection.quoted_date(@from.to_time), ActiveRecord::Base.connection.quoted_date(@to.to_time)]
sql << " (spent_on BETWEEN '%s' AND '%s')" % [ActiveRecord::Base.connection.quoted_date(@from), ActiveRecord::Base.connection.quoted_date(@to)]
sql << " GROUP BY #{sql_group_by}, tyear, tmonth, tweek, spent_on"
@hours = ActiveRecord::Base.connection.select_all(sql)
@@ -197,6 +197,9 @@ class TimelogController < ApplicationController
render_403 and return if @time_entry && !@time_entry.editable_by?(User.current)
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
@time_entry.attributes = params[:time_entry]
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
if request.post? and @time_entry.save
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :action => 'details', :project_id => @time_entry.project

View File

@@ -82,6 +82,7 @@ class WikiController < ApplicationController
@content.author = User.current
# if page is new @page.save will also save content, but not if page isn't a new record
if (@page.new_record? ? @page.save : @content.save)
call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
redirect_to :action => 'index', :id => @project, :page => @page.title
end
end

View File

@@ -159,7 +159,7 @@ module ApplicationHelper
# Truncates and returns the string as a single line
def truncate_single_line(string, *args)
truncate(string, *args).gsub(%r{[\r\n]+}m, ' ')
truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
end
def html_hours(text)
@@ -385,7 +385,7 @@ module ApplicationHelper
# export:some/file -> Force the download of the file
# Forum messages:
# message#1218 -> Link to message with id 1218
text = text.gsub(%r{([\s\(,\-\>]|^)(!)?(attachment|document|version|commit|source|export|message)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|\s|<|$)}) do |m|
text = text.gsub(%r{([\s\(,\-\>]|^)(!)?(attachment|document|version|commit|source|export|message)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|<|$)}) do |m|
leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
link = nil
if esc.nil?

View File

@@ -42,6 +42,12 @@ module QueriesHelper
when :subject
h((!@project.nil? && @project != issue.project) ? "#{issue.project.name} - " : '') +
link_to(h(value), :controller => 'issues', :action => 'show', :id => issue)
when :project
link_to(h(value), :controller => 'projects', :action => 'show', :id => value)
when :assigned_to
link_to(h(value), :controller => 'account', :action => 'show', :id => value)
when :author
link_to(h(value), :controller => 'account', :action => 'show', :id => value)
when :done_ratio
progress_bar(value, :width => '80px')
when :fixed_version

View File

@@ -147,7 +147,7 @@ module RepositoriesHelper
def subversion_field_tags(form, repository)
content_tag('p', form.text_field(:url, :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)) +
'<br />(http://, https://, svn://, file:///)') +
'<br />(file:///, http://, https://, svn://, svn+[tunnelscheme]://)') +
content_tag('p', form.text_field(:login, :size => 30)) +
content_tag('p', form.password_field(:password, :size => 30, :name => 'ignore',
:value => ((repository.new_record? || repository.password.blank?) ? '' : ('x'*15)),

View File

@@ -36,7 +36,7 @@ module WikiHelper
words_add += 1
else
del_at = pos unless del_at
deleted << ' ' + change[2]
deleted << ' ' + h(change[2])
words_del += 1
end
end

View File

@@ -65,14 +65,20 @@ class Attachment < ActiveRecord::Base
nil
end
# Copy temp file to its final location
# Copies the temporary file to its final location
# and computes its MD5 hash
def before_save
if @temp_file && (@temp_file.size > 0)
logger.debug("saving '#{self.diskfile}'")
md5 = Digest::MD5.new
File.open(diskfile, "wb") do |f|
f.write(@temp_file.read)
buffer = ""
while (buffer = @temp_file.read(8192))
f.write(buffer)
md5.update(buffer)
end
end
self.digest = self.class.digest(diskfile)
self.digest = md5.hexdigest
end
# Don't save the content type if it's longer than the authorized length
if self.content_type && self.content_type.length > 255
@@ -141,11 +147,4 @@ private
end
df
end
# Returns the MD5 digest of the file at given path
def self.digest(filename)
File.open(filename, 'rb') do |f|
Digest::MD5.hexdigest(f.read)
end
end
end

View File

@@ -112,6 +112,8 @@ class Changeset < ActiveRecord::Base
journal = issue.init_journal(user || User.anonymous, l(:text_status_changed_by_changeset, csettext))
issue.status = fix_status
issue.done_ratio = done_ratio if done_ratio
Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update,
{ :changeset => self, :issue => issue })
issue.save
Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
end

View File

@@ -168,7 +168,7 @@ class MailHandler < ActionMailer::Base
@keywords[attr]
else
@keywords[attr] = begin
if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) && plain_text_body.gsub!(/^#{attr}:[ \t]*(.+)\s*$/i, '')
if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) && plain_text_body.gsub!(/^#{attr}[ \t]*:[ \t]*(.+)\s*$/i, '')
$1.strip
elsif !@@handler_options[:issue][attr].blank?
@@handler_options[:issue][attr]

View File

@@ -368,9 +368,9 @@ class Query < ActiveRecord::Base
Time.now.at_beginning_of_week
sql = "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date(from), connection.quoted_date(from + 7.days)]
when "~"
sql = "#{db_table}.#{db_field} LIKE '%#{connection.quote_string(value.first)}%'"
sql = "LOWER(#{db_table}.#{db_field}) LIKE '%#{connection.quote_string(value.first.to_s.downcase)}%'"
when "!~"
sql = "#{db_table}.#{db_field} NOT LIKE '%#{connection.quote_string(value.first)}%'"
sql = "LOWER(#{db_table}.#{db_field}) NOT LIKE '%#{connection.quote_string(value.first.to_s.downcase)}%'"
end
return sql

View File

@@ -20,7 +20,7 @@ require 'redmine/scm/adapters/subversion_adapter'
class Repository::Subversion < Repository
attr_protected :root_url
validates_presence_of :url
validates_format_of :url, :with => /^(http|https|svn|svn\+ssh|file):\/\/.+/i
validates_format_of :url, :with => /^(http|https|svn(\+[^\s:\/\\]+)?|file):\/\/.+/i
def scm_adapter
Redmine::Scm::Adapters::SubversionAdapter

View File

@@ -15,8 +15,11 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'rails_generator/secret_key_generator'
class Token < ActiveRecord::Base
belongs_to :user
validates_uniqueness_of :value
@@validity_time = 1.day
@@ -36,9 +39,7 @@ class Token < ActiveRecord::Base
private
def self.generate_token_value
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
token_value = ''
40.times { |i| token_value << chars[rand(chars.size-1)] }
token_value
s = Rails::SecretKeyGenerator.new(object_id).generate_secret
s[0, 40]
end
end

View File

@@ -175,8 +175,14 @@ class User < ActiveRecord::Base
end
def self.find_by_autologin_key(key)
token = Token.find_by_action_and_value('autologin', key)
token && (token.created_on > Setting.autologin.to_i.day.ago) && token.user.active? ? token.user : nil
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
if (token.created_on > Setting.autologin.to_i.day.ago) && token.user && token.user.active?
token.user
end
end
end
# Makes find_by_mail case-insensitive

View File

@@ -22,6 +22,7 @@ class WikiContent < ActiveRecord::Base
belongs_to :page, :class_name => 'WikiPage', :foreign_key => 'page_id'
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
validates_presence_of :text
validates_length_of :comments, :maximum => 255, :allow_nil => true
acts_as_versioned
class Version

View File

@@ -10,7 +10,7 @@
<li><%=l(:field_mail)%>: <%= mail_to(h(@user.mail), nil, :encode => 'javascript') %></li>
<% end %>
<% for custom_value in @custom_values %>
<% if !custom_value.value.empty? %>
<% if !custom_value.value.blank? %>
<li><%= custom_value.custom_field.name%>: <%=h show_value(custom_value) %></li>
<% end %>
<% end %>
@@ -29,6 +29,7 @@
<% end %>
</ul>
<% end %>
<%= call_hook :view_account_left_bottom, :user => @user %>
</div>
<div class="splitcontentright">
@@ -64,6 +65,7 @@
<%= auto_discovery_link_tag(:atom, :controller => 'projects', :action => 'activity', :user_id => @user, :format => :atom, :key => User.current.rss_key) %>
<% end %>
<% end %>
<%= call_hook :view_account_right_bottom, :user => @user %>
</div>
<% html_title @user.name %>

View File

@@ -1,8 +1,8 @@
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do
xml.title truncate_single_line(@title, 100)
xml.link "rel" => "self", "href" => url_for(params.merge({:format => nil, :only_path => false}))
xml.link "rel" => "alternate", "href" => url_for(:controller => 'welcome', :only_path => false)
xml.link "rel" => "self", "href" => url_for(params.merge(:only_path => false))
xml.link "rel" => "alternate", "href" => url_for(params.merge(:only_path => false, :format => nil, :key => nil))
xml.id url_for(:controller => 'welcome', :only_path => false)
xml.updated((@items.first ? @items.first.event_datetime : Time.now).xmlschema)
xml.author { xml.name "#{Setting.app_title}" }
@@ -21,10 +21,10 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do
author = item.event_author if item.respond_to?(:event_author)
xml.author do
xml.name(author)
xml.email(author.mail) if author.respond_to?(:mail) && !author.mail.blank?
xml.email(author.mail) if author.is_a?(User) && !author.mail.blank? && !author.pref.hide_mail
end if author
xml.content "type" => "html" do
xml.text! textilizable(item.event_description)
xml.text! textilizable(item, :event_description, :only_path => false)
end
end
end

View File

@@ -15,7 +15,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do
xml.updated change.created_on.xmlschema
xml.author do
xml.name change.user.name
xml.email(change.user.mail)
xml.email(change.user.mail) if change.user.is_a?(User) && !change.user.mail.blank? && !change.user.pref.hide_mail
end
xml.content "type" => "html" do
xml.text! '<ul>'
@@ -23,7 +23,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do
xml.text! '<li>' + show_detail(detail, false) + '</li>'
end
xml.text! '</ul>'
xml.text! textilizable(change.notes) unless change.notes.blank?
xml.text! textilizable(change, :notes, :only_path => false) unless change.notes.blank?
end
end
end

View File

@@ -45,7 +45,7 @@
</tr>
<tr>
<% n = 0 -%>
<% @issue.custom_values.each do |value| -%>
<% @issue.custom_field_values.each do |value| -%>
<td valign="top"><b><%=h value.custom_field.name %>:</b></td><td valign="top"><%= simple_format(h(show_value(value))) %></td>
<% n = n + 1
if (n > 1)

View File

@@ -1,6 +1,6 @@
<% form_remote_tag(:url => {}, :html => { :id => "journal-#{@journal.id}-form" }) do %>
<%= text_area_tag :notes, h(@journal.notes), :class => 'wiki-edit',
:rows => (@journal.notes.blank? ? 10 : [[10, @journal.notes.length / 50].max, 100].min) %>
<%= text_area_tag :notes, @journal.notes, :class => 'wiki-edit',
:rows => (@journal.notes.blank? ? 10 : [[10, @journal.notes.length / 50].max, 100].min) %>
<%= call_hook(:view_journals_notes_form_after_notes, { :journal => @journal}) %>
<p><%= submit_tag l(:button_save) %>
<%= link_to l(:button_cancel), '#', :onclick => "Element.remove('journal-#{@journal.id}-form'); " +

View File

@@ -2,7 +2,7 @@
<ul>
<% @issues.each do |issue| -%>
<li><%=h "#{issue.project} - #{issue.tracker} ##{issue.id}: #{issue.subject}" %></li>
<li><%=h issue.project %> - <%=link_to("#{issue.tracker} ##{issue.id}", :controller => 'issues', :action => 'show', :id => issue, :only_path => false)%>: <%=h issue.subject %></li>
<% end -%>
</ul>

View File

@@ -1,6 +1,8 @@
<h2><%=l(:label_settings)%></h2>
<% tabs = project_settings_tabs %>
<% if tabs.any? %>
<% selected_tab = params[:tab] ? params[:tab].to_s : tabs.first[:name] %>
<div class="tabs">
@@ -20,5 +22,8 @@
:style => (tab[:name] != selected_tab ? 'display:none' : nil),
:class => 'tab-content') %>
<% end -%>
<% else %>
<p class="nodata"><%= l(:label_no_data) %></p>
<% end %>
<% html_title(l(:label_settings)) -%>

View File

@@ -14,7 +14,7 @@
<td><%= link_to h(version.name), :controller => 'versions', :action => 'show', :id => version %></td>
<td align="center"><%= format_date(version.effective_date) %></td>
<td><%=h version.description %></td>
<td><%= link_to(version.wiki_page_title, :controller => 'wiki', :page => Wiki.titleize(version.wiki_page_title)) unless version.wiki_page_title.blank? || @project.wiki.nil? %></td>
<td><%= link_to(h(version.wiki_page_title), :controller => 'wiki', :page => Wiki.titleize(version.wiki_page_title)) unless version.wiki_page_title.blank? || @project.wiki.nil? %></td>
<td align="center"><%= link_to_if_authorized l(:button_edit), { :controller => 'versions', :action => 'edit', :id => version }, :class => 'icon icon-edit' %></td>
<td align="center"><%= link_to_if_authorized l(:button_delete), {:controller => 'versions', :action => 'destroy', :id => version}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %></td>
</tr>

View File

@@ -11,7 +11,7 @@
<li><%=l(:field_parent)%>: <%= link_to h(@project.parent.name), :controller => 'projects', :action => 'show', :id => @project.parent %></li>
<% end %>
<% @project.custom_values.each do |custom_value| %>
<% if !custom_value.value.empty? %>
<% if !custom_value.value.blank? %>
<li><%= custom_value.custom_field.name%>: <%=h show_value(custom_value) %></li>
<% end %>
<% end %>
@@ -32,6 +32,7 @@
<p><%= link_to l(:label_issue_view_all), :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1 %></p>
</div>
<% end %>
<%= call_hook(:view_projects_show_left, :project => @project) %>
</div>
<div class="splitcontentright">
@@ -53,6 +54,7 @@
<p><%= link_to l(:label_news_view_all), :controller => 'news', :action => 'index', :project_id => @project %></p>
</div>
<% end %>
<%= call_hook(:view_projects_show_right, :project => @project) %>
</div>
<% content_for :sidebar do %>

View File

@@ -1,4 +1,4 @@
<h2><%=l(:label_role)%>: <%= @role.name %></h2>
<h2><%=l(:label_role)%>: <%=h @role.name %></h2>
<% labelled_tabular_form_for :role, @role, :url => { :action => 'edit' }, :html => {:id => 'role_form'} do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>

View File

@@ -4,6 +4,7 @@
<th><%= l(:label_project) %></th>
<th><%= l(:label_role) %></th>
<th style="width:15%"></th>
<%= call_hook(:view_users_memberships_table_header, :user => @user )%>
</thead>
<tbody>
<% @memberships.each do |membership| %>
@@ -19,6 +20,7 @@
<td align="center">
<%= link_to l(:button_delete), {:action => 'destroy_membership', :id => @user, :membership_id => membership }, :method => :post, :class => 'icon icon-del' %>
</td>
<%= call_hook(:view_users_memberships_table_row, :user => @user, :membership => membership, :roles => @roles, :projects => @projects )%>
</tr>
</tbody>
<% end; reset_cycle %>

View File

@@ -1,5 +1,6 @@
<div class="contextual">
<%= link_to_if_authorized l(:button_edit), {:controller => 'versions', :action => 'edit', :id => @version}, :class => 'icon icon-edit' %>
<%= call_hook(:view_versions_show_contextual, { :version => @version, :project => @project }) %>
</div>
<h2><%= h(@version.name) %></h2>

View File

@@ -9,6 +9,7 @@
<%= link_to l(:label_news_view_all), :controller => 'news' %>
</div>
<% end %>
<%= call_hook(:view_welcome_index_left, :projects => @projects) %>
</div>
<div class="splitcontentright">
@@ -25,6 +26,7 @@
</ul>
</div>
<% end %>
<%= call_hook(:view_welcome_index_right, :projects => @projects) %>
</div>
<% content_for :header_tags do %>

View File

@@ -10,6 +10,9 @@ ul.toc { padding: 4px; margin-left: 0; }
ul.toc li { list-style-type:none; }
ul.toc li.heading2 { margin-left: 1em; }
ul.toc li.heading3 { margin-left: 2em; }
a.wiki-anchor { display: none; margin-left: 6px; text-decoration: none; }
a.wiki-anchor:hover { color: #aaa !important; text-decoration: none; }
h1:hover a.wiki-anchor, h2:hover a.wiki-anchor, h3:hover a.wiki-anchor { display: inline; color: #ddd; }
</style>
</head>
<body>

View File

@@ -6,6 +6,13 @@
<style>
body { font:80% Verdana,Tahoma,Arial,sans-serif; }
h1, h2, h3, h4 { font-family: "Trebuchet MS",Georgia,"Times New Roman",serif; }
ul.toc { padding: 4px; margin-left: 0; }
ul.toc li { list-style-type:none; }
ul.toc li.heading2 { margin-left: 1em; }
ul.toc li.heading3 { margin-left: 2em; }
a.wiki-anchor { display: none; margin-left: 6px; text-decoration: none; }
a.wiki-anchor:hover { color: #aaa !important; text-decoration: none; }
h1:hover a.wiki-anchor, h2:hover a.wiki-anchor, h3:hover a.wiki-anchor { display: inline; color: #ddd; }
</style>
</head>
<body>

View File

@@ -20,11 +20,11 @@
<% unless @pages.empty? %>
<p class="other-formats">
<%= l(:label_export_to) %>
<span><%= link_to 'Atom', {:controller => 'projects', :action => 'activity', :id => @project, :show_wiki_pages => 1, :format => 'atom', :key => User.current.rss_key}, :class => 'feed' %></span>
<span><%= link_to 'Atom', {:controller => 'projects', :action => 'activity', :id => @project, :show_wiki_edits => 1, :format => 'atom', :key => User.current.rss_key}, :class => 'feed' %></span>
<span><%= link_to 'HTML', {:action => 'special', :page => 'export'}, :class => 'html' %></span>
</p>
<% end %>
<% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom, :controller => 'projects', :action => 'activity', :id => @project, :show_wiki_pages => 1, :format => 'atom', :key => User.current.rss_key) %>
<%= auto_discovery_link_tag(:atom, :controller => 'projects', :action => 'activity', :id => @project, :show_wiki_edits => 1, :format => 'atom', :key => User.current.rss_key) %>
<% end %>

View File

@@ -13,11 +13,11 @@
<% unless @pages.empty? %>
<p class="other-formats">
<%= l(:label_export_to) %>
<span><%= link_to 'Atom', {:controller => 'projects', :action => 'activity', :id => @project, :show_wiki_pages => 1, :format => 'atom', :key => User.current.rss_key}, :class => 'feed' %></span>
<span><%= link_to 'Atom', {:controller => 'projects', :action => 'activity', :id => @project, :show_wiki_edits => 1, :format => 'atom', :key => User.current.rss_key}, :class => 'feed' %></span>
<span><%= link_to 'HTML', {:action => 'special', :page => 'export'} %></span>
</p>
<% end %>
<% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom, :controller => 'projects', :action => 'activity', :id => @project, :show_wiki_pages => 1, :format => 'atom', :key => User.current.rss_key) %>
<%= auto_discovery_link_tag(:atom, :controller => 'projects', :action => 'activity', :id => @project, :show_wiki_edits => 1, :format => 'atom', :key => User.current.rss_key) %>
<% end %>

View File

@@ -15,3 +15,6 @@ config.action_controller.perform_caching = false
config.action_mailer.perform_deliveries = true
config.action_mailer.delivery_method = :test
# Skip protect_from_forgery in requests http://m.onkey.org/2007/9/28/csrf-protection-for-your-existing-rails-application
config.action_controller.allow_forgery_protection = false

View File

@@ -15,3 +15,7 @@ config.action_controller.perform_caching = false
config.action_mailer.perform_deliveries = true
config.action_mailer.delivery_method = :test
# Skip protect_from_forgery in requests http://m.onkey.org/2007/9/28/csrf-protection-for-your-existing-rails-application
config.action_controller.allow_forgery_protection = false

View File

@@ -15,3 +15,6 @@ config.action_controller.perform_caching = false
config.action_mailer.perform_deliveries = true
config.action_mailer.delivery_method = :test
# Skip protect_from_forgery in requests http://m.onkey.org/2007/9/28/csrf-protection-for-your-existing-rails-application
config.action_controller.allow_forgery_protection = false

View File

@@ -0,0 +1,30 @@
# Copyright (c) 2009 Michael Koziarski <michael@koziarski.com>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
require 'bigdecimal'
alias BigDecimalUnsafe BigDecimal
# This fixes CVE-2009-1904 however it removes legitimate functionality that your
# application may depend on. You are *strongly* advised to upgrade your ruby
# rather than relying on this fix for an extended period of time.
def BigDecimal(initial, digits=0)
if initial.size > 255 || initial =~ /e/i
raise "Invalid big Decimal Value"
end
BigDecimalUnsafe(initial, digits)
end

View File

@@ -4,6 +4,59 @@ Redmine - project management software
Copyright (C) 2006-2009 Jean-Philippe Lang
http://www.redmine.org/
== 2009-11-15 v0.8.7
* Fixed: Hide paragraph terminator at the end of headings on html export
* Fixed: pre tags containing "<pre*"
* Fixed: First date of the date range not included in the time report with SQLite
* Fixed: Password field not styled correctly on alternative stylesheet
* Fixed: Error when sumbitting a POST request that requires a login
* Fixed: CSRF vulnerabilities
== 2009-11-04 v0.8.6
* Change links to closed issues to be a grey color
* Change subversion adapter to not cache authentication and run non interactively
* Fixed: Custom Values with a nil value cause HTTP error 500
* Fixed: Failure to convert HTML entities when editing an Issue reply
* Fixed: Error trying to show repository when there are no comments in a changeset
* Fixed: account/show/:user_id should not be accessible for other users not in your projects
* Fixed: XSS vulnerabilities
* Fixed: IssuesController#destroy should accept POST only
* Fixed: Inline images in wiki headings
== 2009-09-13 v0.8.5
* Incoming mail handler : Allow spaces between keywords and colon
* Do not require a non-word character after a comma in Redmine links
* Include issue hyperlinks in reminder emails
* Prevent nil error when retrieving svn version
* Various plugin hooks added
* Add plugins information to script/about
* Fixed: 500 Internal Server Error is raised if add an empty comment to the news
* Fixed: Atom links for wiki pages are not correct
* Fixed: Atom feeds leak email address
* Fixed: Case sensitivity in Issue filtering
* Fixed: When reading RSS feed, the inline-embedded images are not properly shown
== 2009-05-17 v0.8.4
* Allow textile mailto links
* Fixed: memory consumption when uploading file
* Fixed: Mercurial integration doesn't work if Redmine is installed in folder path containing space
* Fixed: an error is raised when no tab is available on project settings
* Fixed: insert image macro corrupts urls with excalamation marks
* Fixed: error on cross-project gantt PNG export
* Fixed: self and alternate links in atom feeds do not respect Atom specs
* Fixed: accept any svn tunnel scheme in repository URL
* Fixed: issues/show should accept user's rss key
* Fixed: consistency of custom fields display on the issue detail view
* Fixed: wiki comments length validation is missing
* Fixed: weak autologin token generation algorithm causes duplicate tokens
== 2009-04-05 v0.8.3

View File

@@ -30,7 +30,10 @@ Optional:
rake db:migrate RAILS_ENV="production"
It will create tables and an administrator account.
5. Setting up permissions
5. Generate a session store secret. Run:
rake config/initializers/session_store.rb
6. Setting up permissions
The user who runs Redmine must have write permission on the following
subdirectories: files, log, tmp (create the last one if not present).
@@ -39,13 +42,13 @@ Optional:
sudo chown -R redmine:redmine files log tmp
sudo chmod -R 755 files log tmp
6. Test the installation by running WEBrick web server:
7. Test the installation by running WEBrick web server:
ruby script/server -e production
Once WEBrick has started, point your browser to http://localhost:3000/
You should now see the application welcome page
7. Use default administrator account to log in:
8. Use default administrator account to log in:
login: admin
password: admin

View File

@@ -681,6 +681,6 @@ text_user_mail_option: "De los proyectos no seleccionados, sólo recibirá notif
text_user_wrote: '%s escribió:'
text_wiki_destroy_confirmation: ¿Seguro que quiere borrar el wiki y todo su contenido?
text_workflow_edit: Seleccionar un flujo de trabajo para actualizar
text_plugin_assets_writable: Plugin assets directory writable
warning_attachments_not_saved: "%d file(s) could not be saved."
button_create_and_continue: Create and continue
text_plugin_assets_writable: Se puede escribir en el directorio de extensiones activas
warning_attachments_not_saved: "No pudieron guardarse %d fichero(s)."
button_create_and_continue: Crear y continuar

View File

@@ -792,7 +792,7 @@ class RedCloth3 < String
(?:\(([^)]+?)\)(?="))? # $title
":
( # $url
(\/|[a-zA-Z]+:\/\/|www\.) # $proto
(\/|[a-zA-Z]+:\/\/|www\.|mailto:) # $proto
[\w\/]\S+?
)
(\/)? # $slash
@@ -907,7 +907,7 @@ class RedCloth3 < String
end
IMAGE_RE = /
(<p>|.|^) # start of line?
(>|\s|^) # start of line?
\! # opening
(\<|\=|\>)? # optional alignment atts
(#{C}) # optional style,class atts
@@ -1011,7 +1011,7 @@ class RedCloth3 < String
end
OFFTAGS = /(code|pre|kbd|notextile)/
OFFTAG_MATCH = /(?:(<\/#{ OFFTAGS }>)|(<#{ OFFTAGS }[^>]*>))(.*?)(?=<\/?#{ OFFTAGS }|\Z)/mi
OFFTAG_MATCH = /(?:(<\/#{ OFFTAGS }>)|(<#{ OFFTAGS }[^>]*>))(.*?)(?=<\/?#{ OFFTAGS }\W|\Z)/mi
OFFTAG_OPEN = /<#{ OFFTAGS }/
OFFTAG_CLOSE = /<\/?#{ OFFTAGS }/
HASTAG_MATCH = /(<\/?\w[^\n]*?>)/m

16
lib/redmine/about.rb Normal file
View File

@@ -0,0 +1,16 @@
module Redmine
class About
def self.print_plugin_info
plugins = Redmine::Plugin.registered_plugins
if !plugins.empty?
column_with = plugins.map {|internal_name, plugin| plugin.name.length}.max
puts "\nAbout your Redmine plugins"
plugins.each do |internal_name, plugin|
puts sprintf("%-#{column_with}s %s", plugin.name, plugin.version)
end
end
end
end
end

View File

@@ -105,7 +105,7 @@ module Redmine
# makes Mercurial produce a xml output.
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
revisions = Revisions.new
cmd = "#{HG_BIN} --debug --encoding utf8 -R #{target('')} log -C --style #{self.class.template_path}"
cmd = "#{HG_BIN} --debug --encoding utf8 -R #{target('')} log -C --style #{shell_quote self.class.template_path}"
if identifier_from && identifier_to
cmd << " -r #{identifier_from.to_i}:#{identifier_to.to_i}"
elsif identifier_from

View File

@@ -37,7 +37,7 @@ module Redmine
version = nil
shellout(cmd) do |io|
# Read svn version in first returned line
if m = io.gets.match(%r{((\d+\.)+\d+)})
if m = io.gets.to_s.match(%r{((\d+\.)+\d+)})
version = m[0].scan(%r{\d+}).collect(&:to_i)
end
end
@@ -224,6 +224,7 @@ module Redmine
str = ''
str << " --username #{shell_quote(@login)}" unless @login.blank?
str << " --password #{shell_quote(@password)}" unless @login.blank? || @password.blank?
str << " --no-auth-cache --non-interactive"
str
end
end

View File

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

View File

@@ -0,0 +1,24 @@
desc 'Generates a configuration file for cookie store sessions.'
file 'config/initializers/session_store.rb' do
path = File.join(RAILS_ROOT, 'config', 'initializers', 'session_store.rb')
secret = Rails::SecretKeyGenerator.new(self).generate_secret[0,40]
File.open(path, 'w') do |f|
f.write <<"EOF"
# This file was generated by 'rake config/initializers/session_store.rb',
# and should not be made visible to public.
# If you have a load-balancing Redmine cluster, you will need to use the
# same version of this file on each machine. And be sure to restart your
# server when you modify this file.
# Your secret key for verifying cookie session data integrity. If you
# change this key, all old sessions will become invalid! Make sure the
# secret is at least 30 characters and all random, no regular words or
# you'll be exposed to dictionary attacks.
ActionController::Base.session = {
:session_key => '_redmine_session',
:secret => '#{secret}'
}
EOF
end
end

View File

@@ -195,8 +195,13 @@ task :migrate_from_mantis => :environment do
file_type
end
def read
content
def read(*args)
if @read_finished
nil
else
@read_finished = true
content
end
end
end

View File

@@ -135,8 +135,15 @@ namespace :redmine do
File.file? trac_fullpath
end
def read
File.open("#{trac_fullpath}", 'rb').read
def open
File.open("#{trac_fullpath}", 'rb') {|f|
@file = f
yield self
}
end
def read(*args)
@file.read(*args)
end
def description
@@ -506,12 +513,14 @@ namespace :redmine do
# Attachments
ticket.attachments.each do |attachment|
next unless attachment.exist?
a = Attachment.new :created_on => attachment.time
a.file = attachment
a.author = find_or_create_user(attachment.author)
a.container = i
a.description = attachment.description
migrated_ticket_attachments += 1 if a.save
attachment.open {
a = Attachment.new :created_on => attachment.time
a.file = attachment
a.author = find_or_create_user(attachment.author)
a.container = i
a.description = attachment.description
migrated_ticket_attachments += 1 if a.save
}
end
# Custom fields
@@ -556,12 +565,14 @@ namespace :redmine do
page.attachments.each do |attachment|
next unless attachment.exist?
next if p.attachments.find_by_filename(attachment.filename.gsub(/^.*(\\|\/)/, '').gsub(/[^\w\.\-]/,'_')) #add only once per page
a = Attachment.new :created_on => attachment.time
a.file = attachment
a.author = find_or_create_user(attachment.author)
a.description = attachment.description
a.container = p
migrated_wiki_attachments += 1 if a.save
attachment.open {
a = Attachment.new :created_on => attachment.time
a.file = attachment
a.author = find_or_create_user(attachment.author)
a.description = attachment.description
a.container = p
migrated_wiki_attachments += 1 if a.save
}
end
end

View File

@@ -76,7 +76,7 @@ a, a:link, a:visited{ color: #2A5685; text-decoration: none; }
a:hover, a:active{ color: #c61a1a; text-decoration: underline;}
a img{ border: 0; }
a.issue.closed, a.issue.closed:link, a.issue.closed:visited { text-decoration: line-through; }
a.issue.closed, a.issue.closed:link, a.issue.closed:visited { color: #999; text-decoration: line-through; }
/***** Tables *****/
table.list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }

View File

@@ -61,9 +61,9 @@ input[type="button"], input[type="submit"], input[type="reset"] { background-col
input[type="button"]:hover, input[type="submit"]:hover, input[type="reset"]:hover { background-color: #ccccbb; }
/* Fields */
input[type="text"], textarea, select { padding: 2px; border: 1px solid #d7d7d7; }
input[type="text"] { padding: 3px; }
input[type="text"]:focus, textarea:focus, select:focus { border: 1px solid #888866; }
input[type="text"], input[type="password"], textarea, select { padding: 2px; border: 1px solid #d7d7d7; }
input[type="text"], input[type="password"] { padding: 3px; }
input[type="text"]:focus, input[type="password"]:focus, textarea:focus, select:focus { border: 1px solid #888866; }
option { border-bottom: 1px dotted #d7d7d7; }
/* Misc */

View File

@@ -1,3 +1,6 @@
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/boot'
require 'commands/about'
$LOAD_PATH.unshift "#{RAILTIES_PATH}/builtin/rails_info"
require 'commands/about'
Redmine::About.print_plugin_info

View File

@@ -109,4 +109,16 @@ attachments_009:
filename: version_file.zip
author_id: 2
content_type: application/octet-stream
attachments_010:
created_on: 2006-07-19 21:07:27 +02:00
container_type: Issue
container_id: 2
downloads: 0
disk_filename: 060719210727_picture.jpg
digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
id: 10
filesize: 452
filename: picture.jpg
author_id: 2
content_type: image/jpeg

View File

@@ -13,4 +13,11 @@ journals_002:
journalized_type: Issue
user_id: 2
journalized_id: 1
journals_003:
created_on: <%= 1.days.ago.to_date.to_s(:db) %>
notes: "A comment with inline image: !picture.jpg!"
id: 3
journalized_type: Issue
user_id: 2
journalized_id: 2

View File

@@ -0,0 +1,43 @@
Return-Path: <jsmith@somenet.foo>
Received: from osiris ([127.0.0.1])
by OSIRIS
with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
From: "John Smith" <jsmith@somenet.foo>
To: <redmine@somenet.foo>
Subject: New ticket on a given project
Date: Sun, 22 Jun 2008 12:28:07 +0200
MIME-Version: 1.0
Content-Type: text/plain;
format=flowed;
charset="iso-8859-1";
reply-type=original
Content-Transfer-Encoding: 7bit
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2900.2869
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas imperdiet
turpis et odio. Integer eget pede vel dolor euismod varius. Phasellus
blandit eleifend augue. Nulla facilisi. Duis id diam. Class aptent taciti
sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In
in urna sed tellus aliquet lobortis. Morbi scelerisque tortor in dolor. Cras
sagittis odio eu lacus. Aliquam sem tortor, consequat sit amet, vestibulum
id, iaculis at, lectus. Fusce tortor libero, congue ut, euismod nec, luctus
eget, eros. Pellentesque tortor enim, feugiat in, dignissim eget, tristique
sed, mauris. Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. Quisque sit amet libero. In hac habitasse
platea dictumst.
Nulla et nunc. Duis pede. Donec et ipsum. Nam ut dui tincidunt neque
sollicitudin iaculis. Duis vitae dolor. Vestibulum eget massa. Sed lorem.
Nullam volutpat cursus erat. Cras felis dolor, lacinia quis, rutrum et,
dictum et, ligula. Sed erat nibh, gravida in, accumsan non, placerat sed,
massa. Sed sodales, ante fermentum ultricies sollicitudin, massa leo
pulvinar dui, a gravida orci mi eget odio. Nunc a lacus.
Project : onlinestore
Tracker: Feature request
category : Stock management
priority: Urgent

View File

@@ -96,5 +96,53 @@ users_006:
mail_notification: false
login: ''
type: AnonymousUser
users_007:
id: 7
created_on: 2006-07-19 19:33:19 +02:00
status: 1
last_login_on:
language: ''
hashed_password: 1
updated_on: 2006-07-19 19:33:19 +02:00
admin: false
mail: someone@foo.bar
lastname: One
firstname: Some
auth_source_id:
mail_notification: false
login: someone
type: User
users_008:
id: 8
created_on: 2006-07-19 19:33:19 +02:00
status: 1
last_login_on:
language: 'it'
hashed_password: 1
updated_on: 2006-07-19 19:33:19 +02:00
admin: false
mail: miscuser8@foo.bar
lastname: Misc
firstname: User
auth_source_id:
mail_notification: false
login: miscuser8
type: User
users_009:
id: 9
created_on: 2006-07-19 19:33:19 +02:00
status: 1
last_login_on:
language: 'it'
hashed_password: 1
updated_on: 2006-07-19 19:33:19 +02:00
admin: false
mail: miscuser9@foo.bar
lastname: Misc
firstname: User
auth_source_id:
mail_notification: false
login: miscuser9
type: User

View File

@@ -37,13 +37,30 @@ class AccountControllerTest < Test::Unit::TestCase
assert_template 'show'
assert_not_nil assigns(:user)
end
def test_show_should_not_fail_when_custom_values_are_nil
user = User.find(2)
# Create a custom field to illustrate the issue
custom_field = CustomField.create!(:name => 'Testing', :field_format => 'text')
custom_value = user.custom_values.build(:custom_field => custom_field).save!
get :show, :id => 2
assert_response :success
end
def test_show_inactive
get :show, :id => 5
assert_response 404
assert_nil assigns(:user)
end
def test_show_should_not_reveal_users_with_no_visible_activity_or_project
get :show, :id => 9
assert_response 404
end
def test_login_should_redirect_to_back_url_param
# request.uri is "test.host" in test environment
post :login, :username => 'jsmith', :password => 'jsmith', :back_url => 'http%3A%2F%2Ftest.host%2Fissues%2Fshow%2F1'

View File

@@ -255,6 +255,14 @@ class IssuesControllerTest < Test::Unit::TestCase
:child => { :tag => 'legend',
:content => /Notes/ } }
end
def test_show_atom
get :show, :id => 2, :format => 'atom'
assert_response :success
assert_template 'changes'
# Inline image
assert @response.body.include?("&lt;img src=\"http://test.host/attachments/download/10\" alt=\"\" /&gt;")
end
def test_show_export_to_pdf
get :show, :id => 3, :format => 'pdf'

View File

@@ -118,6 +118,15 @@ class NewsControllerTest < Test::Unit::TestCase
assert_equal User.find(2), comment.author
end
def test_empty_comment_should_not_be_added
@request.session[:user_id] = 2
assert_no_difference 'Comment.count' do
post :add_comment, :id => 1, :comment => { :comments => '' }
assert_response :success
assert_template 'show'
end
end
def test_destroy_comment
comments_count = News.find(1).comments.size
@request.session[:user_id] = 2

View File

@@ -68,6 +68,16 @@ class ProjectsControllerTest < Test::Unit::TestCase
assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
end
def test_show_should_not_fail_when_custom_values_are_nil
project = Project.find_by_identifier('ecookbook')
project.custom_values.first.update_attribute(:value, nil)
get :show, :id => 'ecookbook'
assert_response :success
assert_template 'show'
assert_not_nil assigns(:project)
assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
end
def test_private_subprojects_hidden
get :show, :id => 'ecookbook'
assert_response :success

View File

@@ -147,6 +147,14 @@ class TimelogControllerTest < Test::Unit::TestCase
assert_equal "162.90", "%.2f" % assigns(:total_hours)
end
def test_report_one_day
get :report, :project_id => 1, :columns => 'day', :from => "2007-03-23", :to => "2007-03-23", :criterias => ["member", "activity"]
assert_response :success
assert_template 'report'
assert_not_nil assigns(:total_hours)
assert_equal "4.25", "%.2f" % assigns(:total_hours)
end
def test_report_custom_field_criteria
get :report, :project_id => 1, :criterias => ['project', 'cf_1']
assert_response :success
@@ -238,6 +246,14 @@ class TimelogControllerTest < Test::Unit::TestCase
assert_equal Date.today - 7, assigns(:from)
assert_equal Date.today, assigns(:to)
end
def test_details_one_day
get :details, :project_id => 1, :from => "2007-03-23", :to => "2007-03-23"
assert_response :success
assert_template 'details'
assert_not_nil assigns(:total_hours)
assert_equal "4.25", "%.2f" % assigns(:total_hours)
end
def test_details_at_issue_level
get :details, :issue_id => 1

View File

@@ -63,4 +63,10 @@ class AdminTest < ActionController::IntegrationTest
assert_response :success
assert_template "admin/projects"
end
def test_add_a_user_as_an_anonymous_user_should_fail
post '/users/add', :user => { :login => 'psmith', :firstname => 'Paul'}, :password => "psmith09", :password_confirmation => "psmith09"
assert_response :redirect
assert_redirected_to "/login?back_url=http%3A%2F%2Fwww.example.com%2Fusers%2Fadd"
end
end

View File

@@ -22,6 +22,19 @@ class AttachmentTest < Test::Unit::TestCase
def setup
end
def test_create
a = Attachment.new(:container => Issue.find(1),
:file => test_uploaded_file("testfile.txt", "text/plain"),
:author => User.find(1))
assert a.save
assert_equal 'testfile.txt', a.filename
assert_equal 59, a.filesize
assert_equal 'text/plain', a.content_type
assert_equal 0, a.downloads
assert_equal Digest::MD5.hexdigest(test_uploaded_file("testfile.txt", "text/plain").read), a.digest
assert File.exist?(a.diskfile)
end
def test_diskfilename
assert Attachment.disk_filename("test_file.txt") =~ /^\d{12}_test_file.txt$/
@@ -30,8 +43,4 @@ class AttachmentTest < Test::Unit::TestCase
assert_equal 'f8139524ebb8f32e51976982cd20a85d', Attachment.disk_filename("test_accentué")[13..-1]
assert_equal 'cbb5b0f30978ba03731d61f9f6d10011', Attachment.disk_filename("test_accentué.ça")[13..-1]
end
def test_digest
assert_equal '1478adae0d4eb06d35897518540e25d6', Attachment.digest(Test::Unit::TestCase.fixture_path + "/files/testfile.txt")
end
end

View File

@@ -55,6 +55,8 @@ class ApplicationHelperTest < HelperTestCase
'ftp://foo.bar' => '<a class="external" href="ftp://foo.bar">ftp://foo.bar</a>',
'ftps://foo.bar' => '<a class="external" href="ftps://foo.bar">ftps://foo.bar</a>',
'sftp://foo.bar' => '<a class="external" href="sftp://foo.bar">sftp://foo.bar</a>',
# two exclamation marks
'http://example.net/path!602815048C7B5C20!302.html' => '<a class="external" href="http://example.net/path!602815048C7B5C20!302.html">http://example.net/path!602815048C7B5C20!302.html</a>',
}
to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
end
@@ -77,6 +79,19 @@ class ApplicationHelperTest < HelperTestCase
to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
end
def test_inline_images_inside_tags
raw = <<-RAW
h1. !foo.png! Heading
Centered image:
p=. !bar.gif!
RAW
assert textilizable(raw).include?('<img src="foo.png" alt="" />')
assert textilizable(raw).include?('<img src="bar.gif" alt="" />')
end
def test_acronyms
to_test = {
'this is an acronym: GPL(General Public License)' => 'this is an acronym: <acronym title="General Public License">GPL</acronym>',
@@ -105,7 +120,11 @@ class ApplicationHelperTest < HelperTestCase
'"link (Link title with "double-quotes")":http://foo.bar' => '<a href="http://foo.bar" title="Link title with &quot;double-quotes&quot;" class="external">link</a>',
"This is not a \"Link\":\n\nAnother paragraph" => "This is not a \"Link\":</p>\n\n\n\t<p>Another paragraph",
# no multiline link text
"This is a double quote \"on the first line\nand another on a second line\":test" => "This is a double quote \"on the first line<br />\nand another on a second line\":test"
"This is a double quote \"on the first line\nand another on a second line\":test" => "This is a double quote \"on the first line<br />\nand another on a second line\":test",
# mailto link
"\"system administrator\":mailto:sysadmin@example.com?subject=redmine%20permissions" => "<a href=\"mailto:sysadmin@example.com?subject=redmine%20permissions\">system administrator</a>",
# two exclamation marks
'"a link":http://example.net/path!602815048C7B5C20!302.html' => '<a href="http://example.net/path!602815048C7B5C20!302.html" class="external">a link</a>',
}
to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
end
@@ -116,6 +135,8 @@ class ApplicationHelperTest < HelperTestCase
changeset_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1},
:class => 'changeset', :title => 'My very first commit')
changeset_link2 = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
:class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1},
:class => 'document')
@@ -133,6 +154,9 @@ class ApplicationHelperTest < HelperTestCase
'#3, #3 and #3.' => "#{issue_link}, #{issue_link} and #{issue_link}.",
# changesets
'r1' => changeset_link,
'r1.' => "#{changeset_link}.",
'r1, r2' => "#{changeset_link}, #{changeset_link2}",
'r1,r2' => "#{changeset_link},#{changeset_link2}",
# documents
'document#1' => document_link,
'document:"Test document"' => document_link,
@@ -227,7 +251,29 @@ class ApplicationHelperTest < HelperTestCase
to_test.each { |text, result| assert_equal result, textilizable(text) }
end
def syntax_highlight
def test_pre_tags
raw = <<-RAW
Before
<pre>
<prepared-statement-cache-size>32</prepared-statement-cache-size>
</pre>
After
RAW
expected = <<-EXPECTED
<p>Before</p>
<pre>
&lt;prepared-statement-cache-size&gt;32&lt;/prepared-statement-cache-size&gt;
</pre>
<p>After</p>
EXPECTED
assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
end
def test_syntax_highlight
raw = <<-RAW
<pre><code class="ruby">
# Some ruby code here

View File

@@ -92,6 +92,21 @@ class MailHandlerTest < Test::Unit::TestCase
assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
end
def test_add_issue_with_spaces_between_attribute_and_separator
issue = submit_email('ticket_with_spaces_between_attribute_and_separator.eml', :allow_override => 'tracker,category,priority')
assert issue.is_a?(Issue)
assert !issue.new_record?
issue.reload
assert_equal 'New ticket on a given project', issue.subject
assert_equal User.find_by_login('jsmith'), issue.author
assert_equal Project.find(2), issue.project
assert_equal 'Feature request', issue.tracker.to_s
assert_equal 'Stock management', issue.category.to_s
assert_equal 'Urgent', issue.priority.to_s
assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
end
def test_add_issue_with_attachment_to_specific_project
issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
assert issue.is_a?(Issue)

View File

@@ -150,15 +150,17 @@ class QueryTest < Test::Unit::TestCase
def test_operator_contains
query = Query.new(:project => Project.find(1), :name => '_')
query.add_filter('subject', '~', ['string'])
assert query.statement.include?("#{Issue.table_name}.subject LIKE '%string%'")
find_issues_with_query(query)
query.add_filter('subject', '~', ['uNable'])
assert query.statement.include?("LOWER(#{Issue.table_name}.subject) LIKE '%unable%'")
result = find_issues_with_query(query)
assert result.empty?
result.each {|issue| assert issue.subject.downcase.include?('unable') }
end
def test_operator_does_not_contains
query = Query.new(:project => Project.find(1), :name => '_')
query.add_filter('subject', '!~', ['string'])
assert query.statement.include?("#{Issue.table_name}.subject NOT LIKE '%string%'")
query.add_filter('subject', '!~', ['uNable'])
assert query.statement.include?("LOWER(#{Issue.table_name}.subject) NOT LIKE '%unable%'")
find_issues_with_query(query)
end