Compare commits

...
30 Commits
Author SHA1 Message Date
Jean-Philippe Lang 0bdef7d797 tagged version 0.8.5
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/tags/0.8.5@2886 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-09-13 12:39:40 +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
Jean-Philippe Lang babbe88a86 Updates for 0.8.3 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2657 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-04-05 12:45:51 +00:00
Jean-Philippe Lang 51a9f6a6eb Merged r2655 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2656 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-04-05 12:35:34 +00:00
Jean-Philippe Lang 16810acf01 Merged r2610, r2611 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2654 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-04-05 12:16:34 +00:00
Jean-Philippe Lang e28b5e1f08 Merged r2643 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2653 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-04-05 12:09:59 +00:00
Jean-Philippe Lang aa38f755c6 Backported r2609 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2652 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-04-05 12:07:55 +00:00
Jean-Philippe Lang 2679cc2045 Merged r2574, r2578, r2589, r2615, r2641, r2645, r2646 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2651 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-04-05 12:03:03 +00:00
Jean-Philippe Lang b6600d5fe9 Merged r2563, r2566, r2567, r2568, r2569, r2582 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2650 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-04-05 11:52:49 +00:00
Jean-Philippe Lang 37da5ba187 Backported r2573, r2591 and r2592 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2593 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-03-15 10:10:39 +00:00
73 changed files with 440 additions and 109 deletions
+1
View File
@@ -63,6 +63,7 @@ class AccountController < ApplicationController
token = Token.create(:user => user, :action => 'autologin') token = Token.create(:user => user, :action => 'autologin')
cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now } cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now }
end end
call_hook(:controller_account_success_authentication_after, {:user => user })
redirect_back_or_default :controller => 'my', :action => 'page' redirect_back_or_default :controller => 'my', :action => 'page'
end end
end end
@@ -46,6 +46,7 @@ class CustomFieldsController < ApplicationController
end end
if request.post? and @custom_field.save if request.post? and @custom_field.save
flash[:notice] = l(:notice_successful_create) flash[:notice] = l(:notice_successful_create)
call_hook(:controller_custom_fields_new_after_save, :params => params, :custom_field => @custom_field)
redirect_to :action => 'list', :tab => @custom_field.class.name redirect_to :action => 'list', :tab => @custom_field.class.name
end end
@trackers = Tracker.find(:all, :order => 'position') @trackers = Tracker.find(:all, :order => 'position')
@@ -58,6 +59,7 @@ class CustomFieldsController < ApplicationController
@custom_field.trackers = params[:tracker_ids] ? Tracker.find(params[:tracker_ids]) : [] @custom_field.trackers = params[:tracker_ids] ? Tracker.find(params[:tracker_ids]) : []
end end
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
call_hook(:controller_custom_fields_edit_after_save, :params => params, :custom_field => @custom_field)
redirect_to :action => 'list', :tab => @custom_field.class.name redirect_to :action => 'list', :tab => @custom_field.class.name
end end
@trackers = Tracker.find(:all, :order => 'position') @trackers = Tracker.find(:all, :order => 'position')
+5 -3
View File
@@ -23,7 +23,7 @@ class IssuesController < ApplicationController
before_filter :find_project, :only => [:new, :update_form, :preview] before_filter :find_project, :only => [:new, :update_form, :preview]
before_filter :authorize, :except => [:index, :changes, :gantt, :calendar, :preview, :update_form, :context_menu] before_filter :authorize, :except => [:index, :changes, :gantt, :calendar, :preview, :update_form, :context_menu]
before_filter :find_optional_project, :only => [:index, :changes, :gantt, :calendar] before_filter :find_optional_project, :only => [:index, :changes, :gantt, :calendar]
accept_key_auth :index, :changes accept_key_auth :index, :show, :changes
helper :journals helper :journals
helper :projects helper :projects
@@ -343,10 +343,12 @@ class IssuesController < ApplicationController
@gantt.events = events @gantt.events = events
end end
basename = (@project ? "#{@project.identifier}-" : '') + 'gantt'
respond_to do |format| respond_to do |format|
format.html { render :template => "issues/gantt.rhtml", :layout => !request.xhr? } 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.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 => "#{@project.nil? ? '' : "#{@project.identifier}-" }gantt.pdf") } format.pdf { send_data(gantt_to_pdf(@gantt, @project), :type => 'application/pdf', :filename => "#{basename}.pdf") }
end end
end end
+2
View File
@@ -46,6 +46,7 @@ class MessagesController < ApplicationController
@message.sticky = params[:message]['sticky'] @message.sticky = params[:message]['sticky']
end end
if request.post? && @message.save if request.post? && @message.save
call_hook(:controller_messages_new_after_save, { :params => params, :message => @message})
attach_files(@message, params[:attachments]) attach_files(@message, params[:attachments])
redirect_to :action => 'show', :id => @message redirect_to :action => 'show', :id => @message
end end
@@ -58,6 +59,7 @@ class MessagesController < ApplicationController
@reply.board = @board @reply.board = @board
@topic.children << @reply @topic.children << @reply
if !@reply.new_record? if !@reply.new_record?
call_hook(:controller_messages_reply_after_save, { :params => params, :message => @reply})
attach_files(@reply, params[:attachments]) attach_files(@reply, params[:attachments])
end end
redirect_to :action => 'show', :id => @topic redirect_to :action => 'show', :id => @topic
+1
View File
@@ -65,6 +65,7 @@ class NewsController < ApplicationController
flash[:notice] = l(:label_comment_added) flash[:notice] = l(:label_comment_added)
redirect_to :action => 'show', :id => @news redirect_to :action => 'show', :id => @news
else else
show
render :action => 'show' render :action => 'show'
end end
end end
+3
View File
@@ -197,6 +197,9 @@ class TimelogController < ApplicationController
render_403 and return if @time_entry && !@time_entry.editable_by?(User.current) 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 ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
@time_entry.attributes = params[:time_entry] @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 if request.post? and @time_entry.save
flash[:notice] = l(:notice_successful_update) flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :action => 'details', :project_id => @time_entry.project redirect_back_or_default :action => 'details', :project_id => @time_entry.project
+1
View File
@@ -82,6 +82,7 @@ class WikiController < ApplicationController
@content.author = User.current @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 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) 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 redirect_to :action => 'index', :id => @project, :page => @page.title
end end
end end
+1 -1
View File
@@ -385,7 +385,7 @@ module ApplicationHelper
# export:some/file -> Force the download of the file # export:some/file -> Force the download of the file
# Forum messages: # Forum messages:
# message#1218 -> Link to message with id 1218 # 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 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
link = nil link = nil
if esc.nil? if esc.nil?
+2
View File
@@ -38,6 +38,8 @@ module IssuesHelper
s = "issue status-#{issue.status.position} priority-#{issue.priority.position}" s = "issue status-#{issue.status.position} priority-#{issue.priority.position}"
s << ' closed' if issue.closed? s << ' closed' if issue.closed?
s << ' overdue' if issue.overdue? s << ' overdue' if issue.overdue?
s << ' created-by-me' if User.current.logged? && issue.author_id == User.current.id
s << ' assigned-to-me' if User.current.logged? && issue.assigned_to_id == User.current.id
s s
end end
+7 -1
View File
@@ -40,8 +40,14 @@ module QueriesHelper
else else
case column.name case column.name
when :subject when :subject
h((@project.nil? || @project != issue.project) ? "#{issue.project.name} - " : '') + h((!@project.nil? && @project != issue.project) ? "#{issue.project.name} - " : '') +
link_to(h(value), :controller => 'issues', :action => 'show', :id => issue) 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 when :done_ratio
progress_bar(value, :width => '80px') progress_bar(value, :width => '80px')
when :fixed_version when :fixed_version
+1 -1
View File
@@ -147,7 +147,7 @@ module RepositoriesHelper
def subversion_field_tags(form, repository) def subversion_field_tags(form, repository)
content_tag('p', form.text_field(:url, :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)) + 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.text_field(:login, :size => 30)) +
content_tag('p', form.password_field(:password, :size => 30, :name => 'ignore', content_tag('p', form.password_field(:password, :size => 30, :name => 'ignore',
:value => ((repository.new_record? || repository.password.blank?) ? '' : ('x'*15)), :value => ((repository.new_record? || repository.password.blank?) ? '' : ('x'*15)),
+5 -1
View File
@@ -35,7 +35,11 @@ module TimelogHelper
end end
def select_hours(data, criteria, value) def select_hours(data, criteria, value)
data.select {|row| row[criteria] == value} if value.to_s.empty?
data.select {|row| row[criteria].blank? }
else
data.select {|row| row[criteria] == value}
end
end end
def sum_hours(data) def sum_hours(data)
+1 -1
View File
@@ -36,7 +36,7 @@ module WikiHelper
words_add += 1 words_add += 1
else else
del_at = pos unless del_at del_at = pos unless del_at
deleted << ' ' + change[2] deleted << ' ' + h(change[2])
words_del += 1 words_del += 1
end end
end end
+10 -11
View File
@@ -33,7 +33,7 @@ class Attachment < ActiveRecord::Base
:author_key => :author_id, :author_key => :author_id,
:find_options => {:select => "#{Attachment.table_name}.*", :find_options => {:select => "#{Attachment.table_name}.*",
:joins => "LEFT JOIN #{Version.table_name} ON #{Attachment.table_name}.container_type='Version' AND #{Version.table_name}.id = #{Attachment.table_name}.container_id " + :joins => "LEFT JOIN #{Version.table_name} ON #{Attachment.table_name}.container_type='Version' AND #{Version.table_name}.id = #{Attachment.table_name}.container_id " +
"LEFT JOIN #{Project.table_name} ON #{Version.table_name}.project_id = #{Project.table_name}.id"} "LEFT JOIN #{Project.table_name} ON #{Version.table_name}.project_id = #{Project.table_name}.id OR ( #{Attachment.table_name}.container_type='Project' AND #{Attachment.table_name}.container_id = #{Project.table_name}.id )"}
acts_as_activity_provider :type => 'documents', acts_as_activity_provider :type => 'documents',
:permission => :view_documents, :permission => :view_documents,
@@ -65,14 +65,20 @@ class Attachment < ActiveRecord::Base
nil nil
end end
# Copy temp file to its final location # Copies the temporary file to its final location
# and computes its MD5 hash
def before_save def before_save
if @temp_file && (@temp_file.size > 0) if @temp_file && (@temp_file.size > 0)
logger.debug("saving '#{self.diskfile}'") logger.debug("saving '#{self.diskfile}'")
md5 = Digest::MD5.new
File.open(diskfile, "wb") do |f| 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 end
self.digest = self.class.digest(diskfile) self.digest = md5.hexdigest
end end
# Don't save the content type if it's longer than the authorized length # Don't save the content type if it's longer than the authorized length
if self.content_type && self.content_type.length > 255 if self.content_type && self.content_type.length > 255
@@ -141,11 +147,4 @@ private
end end
df df
end 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 end
+2
View File
@@ -112,6 +112,8 @@ class Changeset < ActiveRecord::Base
journal = issue.init_journal(user || User.anonymous, l(:text_status_changed_by_changeset, csettext)) journal = issue.init_journal(user || User.anonymous, l(:text_status_changed_by_changeset, csettext))
issue.status = fix_status issue.status = fix_status
issue.done_ratio = done_ratio if done_ratio 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 issue.save
Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated') Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
end end
+1 -1
View File
@@ -38,7 +38,7 @@ class Journal < ActiveRecord::Base
:conditions => "#{Journal.table_name}.journalized_type = 'Issue' AND" + :conditions => "#{Journal.table_name}.journalized_type = 'Issue' AND" +
" (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')"} " (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')"}
def save def save(*args)
# Do not save an empty journal # Do not save an empty journal
(details.empty? && notes.blank?) ? false : super (details.empty? && notes.blank?) ? false : super
end end
+1 -1
View File
@@ -168,7 +168,7 @@ class MailHandler < ActionMailer::Base
@keywords[attr] @keywords[attr]
else else
@keywords[attr] = begin @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 $1.strip
elsif !@@handler_options[:issue][attr].blank? elsif !@@handler_options[:issue][attr].blank?
@@handler_options[:issue][attr] @@handler_options[:issue][attr]
+3 -1
View File
@@ -207,7 +207,9 @@ class Mailer < ActionMailer::Base
# Common headers # Common headers
headers 'X-Mailer' => 'Redmine', headers 'X-Mailer' => 'Redmine',
'X-Redmine-Host' => Setting.host_name, 'X-Redmine-Host' => Setting.host_name,
'X-Redmine-Site' => Setting.app_title 'X-Redmine-Site' => Setting.app_title,
'Precedence' => 'bulk',
'Auto-Submitted' => 'auto-generated'
end end
# Appends a Redmine header field (name is prepended with 'X-Redmine-') # Appends a Redmine header field (name is prepended with 'X-Redmine-')
+1 -1
View File
@@ -24,7 +24,7 @@ class News < ActiveRecord::Base
validates_length_of :title, :maximum => 60 validates_length_of :title, :maximum => 60
validates_length_of :summary, :maximum => 255 validates_length_of :summary, :maximum => 255
acts_as_searchable :columns => ['title', "#{table_name}.description"], :include => :project acts_as_searchable :columns => ['title', 'summary', "#{table_name}.description"], :include => :project
acts_as_event :url => Proc.new {|o| {:controller => 'news', :action => 'show', :id => o.id}} acts_as_event :url => Proc.new {|o| {:controller => 'news', :action => 'show', :id => o.id}}
acts_as_activity_provider :find_options => {:include => [:project, :author]}, acts_as_activity_provider :find_options => {:include => [:project, :author]},
:author_key => :author_id :author_key => :author_id
+1 -1
View File
@@ -60,7 +60,7 @@ class Project < ActiveRecord::Base
validates_associated :repository, :wiki validates_associated :repository, :wiki
validates_length_of :name, :maximum => 30 validates_length_of :name, :maximum => 30
validates_length_of :homepage, :maximum => 255 validates_length_of :homepage, :maximum => 255
validates_length_of :identifier, :in => 2..20 validates_length_of :identifier, :in => 1..20
validates_format_of :identifier, :with => /^[a-z0-9\-]*$/ validates_format_of :identifier, :with => /^[a-z0-9\-]*$/
before_destroy :delete_all_members before_destroy :delete_all_members
+7 -3
View File
@@ -93,6 +93,7 @@ class Query < ActiveRecord::Base
cattr_reader :operators_by_filter_type cattr_reader :operators_by_filter_type
@@available_columns = [ @@available_columns = [
QueryColumn.new(:project, :sortable => "#{Project.table_name}.name"),
QueryColumn.new(:tracker, :sortable => "#{Tracker.table_name}.position"), QueryColumn.new(:tracker, :sortable => "#{Tracker.table_name}.position"),
QueryColumn.new(:status, :sortable => "#{IssueStatus.table_name}.position"), QueryColumn.new(:status, :sortable => "#{IssueStatus.table_name}.position"),
QueryColumn.new(:priority, :sortable => "#{Enumeration.table_name}.position", :default_order => 'desc'), QueryColumn.new(:priority, :sortable => "#{Enumeration.table_name}.position", :default_order => 'desc'),
@@ -234,7 +235,10 @@ class Query < ActiveRecord::Base
def columns def columns
if has_default_columns? if has_default_columns?
available_columns.select {|c| Setting.issue_list_default_columns.include?(c.name.to_s) } available_columns.select do |c|
# Adds the project column by default for cross-project lists
Setting.issue_list_default_columns.include?(c.name.to_s) || (c.name == :project && project.nil?)
end
else else
# preserve the column_names order # preserve the column_names order
column_names.collect {|name| available_columns.find {|col| col.name == name}}.compact column_names.collect {|name| available_columns.find {|col| col.name == name}}.compact
@@ -364,9 +368,9 @@ class Query < ActiveRecord::Base
Time.now.at_beginning_of_week 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)] sql = "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date(from), connection.quoted_date(from + 7.days)]
when "~" 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 "!~" 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 end
return sql return sql
+4 -4
View File
@@ -20,7 +20,7 @@ require 'redmine/scm/adapters/subversion_adapter'
class Repository::Subversion < Repository class Repository::Subversion < Repository
attr_protected :root_url attr_protected :root_url
validates_presence_of :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 def scm_adapter
Redmine::Scm::Adapters::SubversionAdapter Redmine::Scm::Adapters::SubversionAdapter
@@ -54,8 +54,8 @@ class Repository::Subversion < Repository
# loads changesets by batches of 200 # loads changesets by batches of 200
identifier_to = [identifier_from + 199, scm_revision].min identifier_to = [identifier_from + 199, scm_revision].min
revisions = scm.revisions('', identifier_to, identifier_from, :with_paths => true) revisions = scm.revisions('', identifier_to, identifier_from, :with_paths => true)
transaction do revisions.reverse_each do |revision|
revisions.reverse_each do |revision| transaction do
changeset = Changeset.create(:repository => self, changeset = Changeset.create(:repository => self,
:revision => revision.identifier, :revision => revision.identifier,
:committer => revision.author, :committer => revision.author,
@@ -68,7 +68,7 @@ class Repository::Subversion < Repository
:path => change[:path], :path => change[:path],
:from_path => change[:from_path], :from_path => change[:from_path],
:from_revision => change[:from_revision]) :from_revision => change[:from_revision])
end end unless changeset.new_record?
end end
end unless revisions.nil? end unless revisions.nil?
identifier_from = identifier_to + 1 identifier_from = identifier_to + 1
+5 -4
View File
@@ -15,8 +15,11 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'rails_generator/secret_key_generator'
class Token < ActiveRecord::Base class Token < ActiveRecord::Base
belongs_to :user belongs_to :user
validates_uniqueness_of :value
@@validity_time = 1.day @@validity_time = 1.day
@@ -36,9 +39,7 @@ class Token < ActiveRecord::Base
private private
def self.generate_token_value def self.generate_token_value
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a s = Rails::SecretKeyGenerator.new(object_id).generate_secret
token_value = '' s[0, 40]
40.times { |i| token_value << chars[rand(chars.size-1)] }
token_value
end end
end end
+8 -2
View File
@@ -175,8 +175,14 @@ class User < ActiveRecord::Base
end end
def self.find_by_autologin_key(key) def self.find_by_autologin_key(key)
token = Token.find_by_action_and_value('autologin', key) tokens = Token.find_all_by_action_and_value('autologin', key)
token && (token.created_on > Setting.autologin.to_i.day.ago) && token.user.active? ? token.user : nil # 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 end
# Makes find_by_mail case-insensitive # Makes find_by_mail case-insensitive
+1
View File
@@ -22,6 +22,7 @@ class WikiContent < ActiveRecord::Base
belongs_to :page, :class_name => 'WikiPage', :foreign_key => 'page_id' belongs_to :page, :class_name => 'WikiPage', :foreign_key => 'page_id'
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id' belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
validates_presence_of :text validates_presence_of :text
validates_length_of :comments, :maximum => 255, :allow_nil => true
acts_as_versioned acts_as_versioned
class Version class Version
+2
View File
@@ -29,6 +29,7 @@
<% end %> <% end %>
</ul> </ul>
<% end %> <% end %>
<%= call_hook :view_account_left_bottom, :user => @user %>
</div> </div>
<div class="splitcontentright"> <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) %> <%= auto_discovery_link_tag(:atom, :controller => 'projects', :action => 'activity', :user_id => @user, :format => :atom, :key => User.current.rss_key) %>
<% end %> <% end %>
<% end %> <% end %>
<%= call_hook :view_account_right_bottom, :user => @user %>
</div> </div>
<% html_title @user.name %> <% html_title @user.name %>
+1 -1
View File
@@ -11,7 +11,7 @@ while day <= calendar.enddt %>
<p class="day-num"><%= day.day %></p> <p class="day-num"><%= day.day %></p>
<% calendar.events_on(day).each do |i| %> <% calendar.events_on(day).each do |i| %>
<% if i.is_a? Issue %> <% if i.is_a? Issue %>
<div class="tooltip"> <div class="<%= css_issue_classes(i) %> tooltip">
<%= if day == i.start_date && day == i.due_date <%= if day == i.start_date && day == i.due_date
image_tag('arrow_bw.png') image_tag('arrow_bw.png')
elsif day == i.start_date elsif day == i.start_date
+4 -4
View File
@@ -1,8 +1,8 @@
xml.instruct! xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do
xml.title truncate_single_line(@title, 100) xml.title truncate_single_line(@title, 100)
xml.link "rel" => "self", "href" => url_for(params.merge({:format => nil, :only_path => false})) xml.link "rel" => "self", "href" => url_for(params.merge(:only_path => false))
xml.link "rel" => "alternate", "href" => url_for(:controller => 'welcome', :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.id url_for(:controller => 'welcome', :only_path => false)
xml.updated((@items.first ? @items.first.event_datetime : Time.now).xmlschema) xml.updated((@items.first ? @items.first.event_datetime : Time.now).xmlschema)
xml.author { xml.name "#{Setting.app_title}" } 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) author = item.event_author if item.respond_to?(:event_author)
xml.author do xml.author do
xml.name(author) 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 end if author
xml.content "type" => "html" do xml.content "type" => "html" do
xml.text! textilizable(item.event_description) xml.text! textilizable(item, :event_description, :only_path => false)
end end
end end
end end
+2
View File
@@ -82,6 +82,7 @@ function deleteValueField(e) {
<% end %> <% end %>
</p> </p>
<p><%= @custom_field.field_format == 'bool' ? f.check_box(:default_value) : f.text_field(:default_value) %></p> <p><%= @custom_field.field_format == 'bool' ? f.check_box(:default_value) : f.text_field(:default_value) %></p>
<%= call_hook(:view_custom_fields_form_upper_box, :custom_field => @custom_field, :form => f) %>
</div> </div>
<div class="box"> <div class="box">
@@ -109,5 +110,6 @@ when "IssueCustomField" %>
<p><%= f.check_box :is_required %></p> <p><%= f.check_box :is_required %></p>
<% end %> <% end %>
<%= call_hook(:"view_custom_fields_form_#{@custom_field.type.to_s.underscore}", :custom_field => @custom_field, :form => f) %>
</div> </div>
<%= javascript_tag "toggle_custom_field_format();" %> <%= javascript_tag "toggle_custom_field_format();" %>
+3 -1
View File
@@ -11,5 +11,7 @@
<p><label for="issue_status_is_default"><%=l(:field_is_default)%></label> <p><label for="issue_status_is_default"><%=l(:field_is_default)%></label>
<%= check_box 'issue_status', 'is_default' %></p> <%= check_box 'issue_status', 'is_default' %></p>
<%= call_hook(:view_issue_statuses_form, :issue_status => @issue_status) %>
<!--[eoform:issue_status]--> <!--[eoform:issue_status]-->
</div> </div>
+2 -2
View File
@@ -7,8 +7,8 @@
<%= call_hook(:view_issues_sidebar_issues_bottom) %> <%= call_hook(:view_issues_sidebar_issues_bottom) %>
<% planning_links = [] <% planning_links = []
planning_links << link_to(l(:label_calendar), :action => 'calendar', :project_id => @project) if User.current.allowed_to?(:view_calendar, @project, :global => true) planning_links << link_to(l(:label_calendar), :controller => 'issues', :action => 'calendar', :project_id => @project) if User.current.allowed_to?(:view_calendar, @project, :global => true)
planning_links << link_to(l(:label_gantt), :action => 'gantt', :project_id => @project) if User.current.allowed_to?(:view_gantt, @project, :global => true) planning_links << link_to(l(:label_gantt), :controller => 'issues', :action => 'gantt', :project_id => @project) if User.current.allowed_to?(:view_gantt, @project, :global => true)
%> %>
<% unless planning_links.empty? %> <% unless planning_links.empty? %>
<h3><%= l(:label_planning) %></h3> <h3><%= l(:label_planning) %></h3>
+2 -2
View File
@@ -15,7 +15,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do
xml.updated change.created_on.xmlschema xml.updated change.created_on.xmlschema
xml.author do xml.author do
xml.name change.user.name 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 end
xml.content "type" => "html" do xml.content "type" => "html" do
xml.text! '<ul>' 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>' xml.text! '<li>' + show_detail(detail, false) + '</li>'
end end
xml.text! '</ul>' 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 end
end end
+4
View File
@@ -1,4 +1,6 @@
<ul> <ul>
<%= call_hook(:view_issues_context_menu_start, {:issues => @issues, :can => @can, :back => @back }) %>
<% if !@issue.nil? -%> <% if !@issue.nil? -%>
<li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue}, <li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue},
:class => 'icon-edit', :disabled => !@can[:edit] %></li> :class => 'icon-edit', :disabled => !@can[:edit] %></li>
@@ -87,4 +89,6 @@
:class => 'icon-move', :disabled => !@can[:move] %></li> :class => 'icon-move', :disabled => !@can[:move] %></li>
<li><%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :ids => @issues.collect(&:id)}, <li><%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :ids => @issues.collect(&:id)},
:method => :post, :confirm => l(:text_issues_destroy_confirmation), :class => 'icon-del', :disabled => !@can[:delete] %></li> :method => :post, :confirm => l(:text_issues_destroy_confirmation), :class => 'icon-del', :disabled => !@can[:delete] %></li>
<%= call_hook(:view_issues_context_menu_end, {:issues => @issues, :can => @can, :back => @back }) %>
</ul> </ul>
+4 -2
View File
@@ -9,7 +9,7 @@
<h2><%= @issue.tracker.name %> #<%= @issue.id %></h2> <h2><%= @issue.tracker.name %> #<%= @issue.id %></h2>
<div class="<%= css_issue_classes(@issue) %>"> <div class="<%= css_issue_classes(@issue) %> details">
<%= avatar(@issue.author, :size => "64") %> <%= avatar(@issue.author, :size => "64") %>
<h3><%=h @issue.subject %></h3> <h3><%=h @issue.subject %></h3>
<p class="author"> <p class="author">
@@ -45,7 +45,7 @@
</tr> </tr>
<tr> <tr>
<% n = 0 -%> <% 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> <td valign="top"><b><%=h value.custom_field.name %>:</b></td><td valign="top"><%= simple_format(h(show_value(value))) %></td>
<% n = n + 1 <% n = n + 1
if (n > 1) if (n > 1)
@@ -69,6 +69,8 @@ end %>
<%= link_to_attachments @issue %> <%= link_to_attachments @issue %>
<%= call_hook(:view_issues_show_description_bottom, :issue => @issue) %>
<% if authorize_for('issue_relations', 'new') || @issue.relations.any? %> <% if authorize_for('issue_relations', 'new') || @issue.relations.any? %>
<hr /> <hr />
<div id="relations"> <div id="relations">
+1 -1
View File
@@ -1,3 +1,3 @@
<%= yield %> <%= yield %>
---------------------------------------- --
<%= Setting.emails_footer %> <%= Setting.emails_footer %>
+1 -1
View File
@@ -2,7 +2,7 @@
<ul> <ul>
<% @issues.each do |issue| -%> <% @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 -%> <% end -%>
</ul> </ul>
+1
View File
@@ -15,6 +15,7 @@
<p><%= f.text_field :lastname, :required => true %></p> <p><%= f.text_field :lastname, :required => true %></p>
<p><%= f.text_field :mail, :required => true %></p> <p><%= f.text_field :mail, :required => true %></p>
<p><%= f.select :language, lang_options_for_select %></p> <p><%= f.select :language, lang_options_for_select %></p>
<%= call_hook(:view_my_account, :user => @user, :form => f) %>
</div> </div>
<%= submit_tag l(:button_save) %> <%= submit_tag l(:button_save) %>
+1 -1
View File
@@ -11,7 +11,7 @@
<p><%= f.text_area :description, :rows => 5, :class => 'wiki-edit' %></p> <p><%= f.text_area :description, :rows => 5, :class => 'wiki-edit' %></p>
<p><%= f.text_field :identifier, :required => true, :disabled => @project.identifier_frozen? %> <p><%= f.text_field :identifier, :required => true, :disabled => @project.identifier_frozen? %>
<% unless @project.identifier_frozen? %> <% unless @project.identifier_frozen? %>
<br /><em><%= l(:text_length_between, 2, 20) %> <%= l(:text_project_identifier_info) %></em> <br /><em><%= l(:text_length_between, 1, 20) %> <%= l(:text_project_identifier_info) %></em>
<% end %></p> <% end %></p>
<p><%= f.text_field :homepage, :size => 60 %></p> <p><%= f.text_field :homepage, :size => 60 %></p>
<p><%= f.check_box :is_public %></p> <p><%= f.check_box :is_public %></p>
+5
View File
@@ -1,6 +1,8 @@
<h2><%=l(:label_settings)%></h2> <h2><%=l(:label_settings)%></h2>
<% tabs = project_settings_tabs %> <% tabs = project_settings_tabs %>
<% if tabs.any? %>
<% selected_tab = params[:tab] ? params[:tab].to_s : tabs.first[:name] %> <% selected_tab = params[:tab] ? params[:tab].to_s : tabs.first[:name] %>
<div class="tabs"> <div class="tabs">
@@ -20,5 +22,8 @@
:style => (tab[:name] != selected_tab ? 'display:none' : nil), :style => (tab[:name] != selected_tab ? 'display:none' : nil),
:class => 'tab-content') %> :class => 'tab-content') %>
<% end -%> <% end -%>
<% else %>
<p class="nodata"><%= l(:label_no_data) %></p>
<% end %>
<% html_title(l(:label_settings)) -%> <% html_title(l(:label_settings)) -%>
+2
View File
@@ -32,6 +32,7 @@
<p><%= link_to l(:label_issue_view_all), :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1 %></p> <p><%= link_to l(:label_issue_view_all), :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1 %></p>
</div> </div>
<% end %> <% end %>
<%= call_hook(:view_projects_show_left, :project => @project) %>
</div> </div>
<div class="splitcontentright"> <div class="splitcontentright">
@@ -53,6 +54,7 @@
<p><%= link_to l(:label_news_view_all), :controller => 'news', :action => 'index', :project_id => @project %></p> <p><%= link_to l(:label_news_view_all), :controller => 'news', :action => 'index', :project_id => @project %></p>
</div> </div>
<% end %> <% end %>
<%= call_hook(:view_projects_show_right, :project => @project) %>
</div> </div>
<% content_for :sidebar do %> <% content_for :sidebar do %>
+1 -1
View File
@@ -7,7 +7,7 @@
<h2><%= l(:label_spent_time) %></h2> <h2><%= l(:label_spent_time) %></h2>
<% form_remote_tag( :url => {}, :method => :get, :update => 'content' ) do %> <% form_remote_tag( :url => {}, :method => :get, :update => 'content' ) do %>
<%= hidden_field_tag 'project_id', params[:project_id] %> <%= hidden_field_tag('project_id', params[:project_id]) if @project %>
<%= hidden_field_tag 'issue_id', params[:issue_id] if @issue %> <%= hidden_field_tag 'issue_id', params[:issue_id] if @issue %>
<%= render :partial => 'date_range' %> <%= render :partial => 'date_range' %>
<% end %> <% end %>
+1 -1
View File
@@ -10,7 +10,7 @@
<% @criterias.each do |criteria| %> <% @criterias.each do |criteria| %>
<%= hidden_field_tag 'criterias[]', criteria, :id => nil %> <%= hidden_field_tag 'criterias[]', criteria, :id => nil %>
<% end %> <% end %>
<%= hidden_field_tag 'project_id', params[:project_id] %> <%= hidden_field_tag('project_id', params[:project_id]) if @project %>
<%= render :partial => 'date_range' %> <%= render :partial => 'date_range' %>
<p><%= l(:label_details) %>: <%= select_tag 'columns', options_for_select([[l(:label_year), 'year'], <p><%= l(:label_details) %>: <%= select_tag 'columns', options_for_select([[l(:label_year), 'year'],
+1
View File
@@ -13,6 +13,7 @@
<% end %> <% end %>
<p><%= f.check_box :admin, :disabled => (@user == User.current) %></p> <p><%= f.check_box :admin, :disabled => (@user == User.current) %></p>
<%= call_hook(:view_users_form, :user => @user, :form => f) %>
</div> </div>
<div class="box"> <div class="box">
+2
View File
@@ -4,6 +4,7 @@
<th><%= l(:label_project) %></th> <th><%= l(:label_project) %></th>
<th><%= l(:label_role) %></th> <th><%= l(:label_role) %></th>
<th style="width:15%"></th> <th style="width:15%"></th>
<%= call_hook(:view_users_memberships_table_header, :user => @user )%>
</thead> </thead>
<tbody> <tbody>
<% @memberships.each do |membership| %> <% @memberships.each do |membership| %>
@@ -19,6 +20,7 @@
<td align="center"> <td align="center">
<%= link_to l(:button_delete), {:action => 'destroy_membership', :id => @user, :membership_id => membership }, :method => :post, :class => 'icon icon-del' %> <%= link_to l(:button_delete), {:action => 'destroy_membership', :id => @user, :membership_id => membership }, :method => :post, :class => 'icon icon-del' %>
</td> </td>
<%= call_hook(:view_users_memberships_table_row, :user => @user, :membership => membership, :roles => @roles, :projects => @projects )%>
</tr> </tr>
</tbody> </tbody>
<% end; reset_cycle %> <% end; reset_cycle %>
+1
View File
@@ -1,5 +1,6 @@
<div class="contextual"> <div class="contextual">
<%= link_to_if_authorized l(:button_edit), {:controller => 'versions', :action => 'edit', :id => @version}, :class => 'icon icon-edit' %> <%= 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> </div>
<h2><%= h(@version.name) %></h2> <h2><%= h(@version.name) %></h2>
+2
View File
@@ -9,6 +9,7 @@
<%= link_to l(:label_news_view_all), :controller => 'news' %> <%= link_to l(:label_news_view_all), :controller => 'news' %>
</div> </div>
<% end %> <% end %>
<%= call_hook(:view_welcome_index_left, :projects => @projects) %>
</div> </div>
<div class="splitcontentright"> <div class="splitcontentright">
@@ -25,6 +26,7 @@
</ul> </ul>
</div> </div>
<% end %> <% end %>
<%= call_hook(:view_welcome_index_right, :projects => @projects) %>
</div> </div>
<% content_for :header_tags do %> <% content_for :header_tags do %>
+2
View File
@@ -31,6 +31,7 @@
<%= link_to_attachments @page %> <%= link_to_attachments @page %>
<% if @editable && authorize_for('wiki', 'add_attachment') %> <% if @editable && authorize_for('wiki', 'add_attachment') %>
<div id="wiki_add_attachment">
<p><%= link_to l(:label_attachment_new), {}, :onclick => "Element.show('add_attachment_form'); Element.hide(this); Element.scrollTo('add_attachment_form'); return false;", <p><%= link_to l(:label_attachment_new), {}, :onclick => "Element.show('add_attachment_form'); Element.hide(this); Element.scrollTo('add_attachment_form'); return false;",
:id => 'attach_files_link' %></p> :id => 'attach_files_link' %></p>
<% form_tag({ :controller => 'wiki', :action => 'add_attachment', :page => @page.title }, :multipart => true, :id => "add_attachment_form", :style => "display:none;") do %> <% form_tag({ :controller => 'wiki', :action => 'add_attachment', :page => @page.title }, :multipart => true, :id => "add_attachment_form", :style => "display:none;") do %>
@@ -40,6 +41,7 @@
<%= submit_tag l(:button_add) %> <%= submit_tag l(:button_add) %>
<%= link_to l(:button_cancel), {}, :onclick => "Element.hide('add_attachment_form'); Element.show('attach_files_link'); return false;" %> <%= link_to l(:button_cancel), {}, :onclick => "Element.hide('add_attachment_form'); Element.show('attach_files_link'); return false;" %>
<% end %> <% end %>
</div>
<% end %> <% end %>
<p class="other-formats"> <p class="other-formats">
+2 -2
View File
@@ -20,11 +20,11 @@
<% unless @pages.empty? %> <% unless @pages.empty? %>
<p class="other-formats"> <p class="other-formats">
<%= l(:label_export_to) %> <%= 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> <span><%= link_to 'HTML', {:action => 'special', :page => 'export'}, :class => 'html' %></span>
</p> </p>
<% end %> <% end %>
<% content_for :header_tags do %> <% 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 %> <% end %>
+2 -2
View File
@@ -13,11 +13,11 @@
<% unless @pages.empty? %> <% unless @pages.empty? %>
<p class="other-formats"> <p class="other-formats">
<%= l(:label_export_to) %> <%= 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> <span><%= link_to 'HTML', {:action => 'special', :page => 'export'} %></span>
</p> </p>
<% end %> <% end %>
<% content_for :header_tags do %> <% 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 %> <% end %>
@@ -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
+50
View File
@@ -5,6 +5,56 @@ Copyright (C) 2006-2009 Jean-Philippe Lang
http://www.redmine.org/ http://www.redmine.org/
== 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
* Separate project field and subject in cross-project issue view
* Ability to set language for redmine:load_default_data task using REDMINE_LANG environment variable
* Rescue Redmine::DefaultData::DataAlreadyLoaded in redmine:load_default_data task
* CSS classes to highlight own and assigned issues
* Hide "New file" link on wiki pages from printing
* Flush buffer when asking for language in redmine:load_default_data task
* Minimum project identifier length set to 1
* Include headers so that emails don't trigger vacation auto-responders
* Fixed: Time entries csv export links for all projects are malformed
* Fixed: Files without Version aren't visible in the Activity page
* Fixed: Commit logs are centered in the repo browser
* Fixed: News summary field content is not searchable
* Fixed: Journal#save has a wrong signature
* Fixed: Email footer signature convention
* Fixed: Timelog report do not show time for non-versioned issues
== 2009-03-07 v0.8.2 == 2009-03-07 v0.8.2
* Send an email to the user when an administrator activates a registered user * Send an email to the user when an administrator activates a registered user
+3 -3
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_user_wrote: '%s escribió:'
text_wiki_destroy_confirmation: ¿Seguro que quiere borrar el wiki y todo su contenido? 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_workflow_edit: Seleccionar un flujo de trabajo para actualizar
text_plugin_assets_writable: Plugin assets directory writable text_plugin_assets_writable: Se puede escribir en el directorio de extensiones activas
warning_attachments_not_saved: "%d file(s) could not be saved." warning_attachments_not_saved: "No pudieron guardarse %d fichero(s)."
button_create_and_continue: Create and continue button_create_and_continue: Crear y continuar
+2 -2
View File
@@ -792,7 +792,7 @@ class RedCloth3 < String
(?:\(([^)]+?)\)(?="))? # $title (?:\(([^)]+?)\)(?="))? # $title
": ":
( # $url ( # $url
(\/|[a-zA-Z]+:\/\/|www\.) # $proto (\/|[a-zA-Z]+:\/\/|www\.|mailto:) # $proto
[\w\/]\S+? [\w\/]\S+?
) )
(\/)? # $slash (\/)? # $slash
@@ -907,7 +907,7 @@ class RedCloth3 < String
end end
IMAGE_RE = / IMAGE_RE = /
(<p>|.|^) # start of line? (<p>|\s|^) # start of line?
\! # opening \! # opening
(\<|\=|\>)? # optional alignment atts (\<|\=|\>)? # optional alignment atts
(#{C}) # optional style,class atts (#{C}) # optional style,class atts
+16
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
@@ -105,7 +105,7 @@ module Redmine
# makes Mercurial produce a xml output. # makes Mercurial produce a xml output.
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}) def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
revisions = Revisions.new 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 if identifier_from && identifier_to
cmd << " -r #{identifier_from.to_i}:#{identifier_to.to_i}" cmd << " -r #{identifier_from.to_i}:#{identifier_to.to_i}"
elsif identifier_from elsif identifier_from
@@ -37,7 +37,7 @@ module Redmine
version = nil version = nil
shellout(cmd) do |io| shellout(cmd) do |io|
# Read svn version in first returned line # 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) version = m[0].scan(%r{\d+}).collect(&:to_i)
end end
end end
+1 -1
View File
@@ -4,7 +4,7 @@ module Redmine
module VERSION #:nodoc: module VERSION #:nodoc:
MAJOR = 0 MAJOR = 0
MINOR = 8 MINOR = 8
TINY = 2 TINY = 4
# Branch values: # Branch values:
# * official release: nil # * official release: nil
+18 -12
View File
@@ -1,26 +1,32 @@
desc 'Load Redmine default configuration data' desc 'Load Redmine default configuration data. Language is chosen interactively or by setting REDMINE_LANG environment variable.'
namespace :redmine do namespace :redmine do
task :load_default_data => :environment do task :load_default_data => :environment do
include GLoc include GLoc
set_language_if_valid('en') set_language_if_valid('en')
puts
while true envlang = ENV['REDMINE_LANG']
print "Select language: " if !envlang || !set_language_if_valid(envlang)
print GLoc.valid_languages.sort {|x,y| x.to_s <=> y.to_s }.join(", ") puts
print " [#{GLoc.current_language}] " while true
lang = STDIN.gets.chomp! print "Select language: "
break if lang.empty? print GLoc.valid_languages.sort {|x,y| x.to_s <=> y.to_s }.join(", ")
break if set_language_if_valid(lang) print " [#{GLoc.current_language}] "
puts "Unknown language!" STDOUT.flush
lang = STDIN.gets.chomp!
break if lang.empty?
break if set_language_if_valid(lang)
puts "Unknown language!"
end
STDOUT.flush
puts "===================================="
end end
puts "===================================="
begin begin
Redmine::DefaultData::Loader.load(current_language) Redmine::DefaultData::Loader.load(current_language)
puts "Default configuration data loaded." puts "Default configuration data loaded."
rescue Redmine::DefaultData::DataAlreadyLoaded => error
puts error
rescue => error rescue => error
puts "Error: " + error puts "Error: " + error
puts "Default configuration data was not loaded." puts "Default configuration data was not loaded."
+7 -2
View File
@@ -195,8 +195,13 @@ task :migrate_from_mantis => :environment do
file_type file_type
end end
def read def read(*args)
content if @read_finished
nil
else
@read_finished = true
content
end
end end
end end
+25 -14
View File
@@ -135,8 +135,15 @@ namespace :redmine do
File.file? trac_fullpath File.file? trac_fullpath
end end
def read def open
File.open("#{trac_fullpath}", 'rb').read File.open("#{trac_fullpath}", 'rb') {|f|
@file = f
yield self
}
end
def read(*args)
@file.read(*args)
end end
def description def description
@@ -506,12 +513,14 @@ namespace :redmine do
# Attachments # Attachments
ticket.attachments.each do |attachment| ticket.attachments.each do |attachment|
next unless attachment.exist? next unless attachment.exist?
a = Attachment.new :created_on => attachment.time attachment.open {
a.file = attachment a = Attachment.new :created_on => attachment.time
a.author = find_or_create_user(attachment.author) a.file = attachment
a.container = i a.author = find_or_create_user(attachment.author)
a.description = attachment.description a.container = i
migrated_ticket_attachments += 1 if a.save a.description = attachment.description
migrated_ticket_attachments += 1 if a.save
}
end end
# Custom fields # Custom fields
@@ -556,12 +565,14 @@ namespace :redmine do
page.attachments.each do |attachment| page.attachments.each do |attachment|
next unless attachment.exist? next unless attachment.exist?
next if p.attachments.find_by_filename(attachment.filename.gsub(/^.*(\\|\/)/, '').gsub(/[^\w\.\-]/,'_')) #add only once per page next if p.attachments.find_by_filename(attachment.filename.gsub(/^.*(\\|\/)/, '').gsub(/[^\w\.\-]/,'_')) #add only once per page
a = Attachment.new :created_on => attachment.time attachment.open {
a.file = attachment a = Attachment.new :created_on => attachment.time
a.author = find_or_create_user(attachment.author) a.file = attachment
a.description = attachment.description a.author = find_or_create_user(attachment.author)
a.container = p a.description = attachment.description
migrated_wiki_attachments += 1 if a.save a.container = p
migrated_wiki_attachments += 1 if a.save
}
end end
end end
+4 -3
View File
@@ -104,9 +104,9 @@ tr.entry.file td.filename a { margin-left: 16px; }
tr.changeset td.author { text-align: center; width: 15%; } tr.changeset td.author { text-align: center; width: 15%; }
tr.changeset td.committed_on { text-align: center; width: 15%; } tr.changeset td.committed_on { text-align: center; width: 15%; }
tr.file td { text-align: center; } table.files tr.file td { text-align: center; }
tr.file td.filename { text-align: left; padding-left: 24px; } table.files tr.file td.filename { text-align: left; padding-left: 24px; }
tr.file td.digest { font-size: 80%; } table.files tr.file td.digest { font-size: 80%; }
tr.message { height: 2.6em; } tr.message { height: 2.6em; }
tr.message td.last_message { font-size: 80%; } tr.message td.last_message { font-size: 80%; }
@@ -683,4 +683,5 @@ h2 img { vertical-align:middle; }
#top-menu, #header, #main-menu, #sidebar, #footer, .contextual, .other-formats { display:none; } #top-menu, #header, #main-menu, #sidebar, #footer, .contextual, .other-formats { display:none; }
#main { background: #fff; } #main { background: #fff; }
#content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; overflow: visible !important;} #content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; overflow: visible !important;}
#wiki_add_attachment { display:none; }
} }
+4 -1
View File
@@ -1,3 +1,6 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/boot' 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
+12
View File
@@ -109,4 +109,16 @@ attachments_009:
filename: version_file.zip filename: version_file.zip
author_id: 2 author_id: 2
content_type: application/octet-stream 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
+7
View File
@@ -13,4 +13,11 @@ journals_002:
journalized_type: Issue journalized_type: Issue
user_id: 2 user_id: 2
journalized_id: 1 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
@@ -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
+12
View File
@@ -51,6 +51,8 @@ class IssuesControllerTest < Test::Unit::TestCase
end end
def test_index def test_index
Setting.default_language = 'en'
get :index get :index
assert_response :success assert_response :success
assert_template 'index.rhtml' assert_template 'index.rhtml'
@@ -61,6 +63,8 @@ class IssuesControllerTest < Test::Unit::TestCase
# private projects hidden # private projects hidden
assert_no_tag :tag => 'a', :content => /Issue of a private subproject/ assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
assert_no_tag :tag => 'a', :content => /Issue on project 2/ assert_no_tag :tag => 'a', :content => /Issue on project 2/
# project column
assert_tag :tag => 'th', :content => /Project/
end end
def test_index_should_not_list_issues_when_module_disabled def test_index_should_not_list_issues_when_module_disabled
@@ -251,6 +255,14 @@ class IssuesControllerTest < Test::Unit::TestCase
:child => { :tag => 'legend', :child => { :tag => 'legend',
:content => /Notes/ } } :content => /Notes/ } }
end 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 def test_show_export_to_pdf
get :show, :id => 3, :format => 'pdf' get :show, :id => 3, :format => 'pdf'
+9
View File
@@ -118,6 +118,15 @@ class NewsControllerTest < Test::Unit::TestCase
assert_equal User.find(2), comment.author assert_equal User.find(2), comment.author
end 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 def test_destroy_comment
comments_count = News.find(1).comments.size comments_count = News.find(1).comments.size
@request.session[:user_id] = 2 @request.session[:user_id] = 2
+13 -1
View File
@@ -18,7 +18,7 @@
require File.dirname(__FILE__) + '/../test_helper' require File.dirname(__FILE__) + '/../test_helper'
class ActivityTest < Test::Unit::TestCase class ActivityTest < Test::Unit::TestCase
fixtures :projects, :versions, :users, :roles, :members, :issues, :journals, :journal_details, fixtures :projects, :versions, :attachments, :users, :roles, :members, :issues, :journals, :journal_details,
:trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages
def setup def setup
@@ -72,6 +72,18 @@ class ActivityTest < Test::Unit::TestCase
assert_nil(events.detect {|e| e.event_author != user}) assert_nil(events.detect {|e| e.event_author != user})
end end
def test_files_activity
f = Redmine::Activity::Fetcher.new(User.anonymous, :project => Project.find(1))
f.scope = ['files']
events = f.events
assert_kind_of Array, events
assert events.include?(Attachment.find_by_container_type_and_container_id('Project', 1))
assert events.include?(Attachment.find_by_container_type_and_container_id('Version', 1))
assert_equal [Attachment], events.collect(&:class).uniq
assert_equal %w(Project Version), events.collect(&:container_type).uniq.sort
end
private private
def find_events(user, options={}) def find_events(user, options={})
+13 -4
View File
@@ -22,6 +22,19 @@ class AttachmentTest < Test::Unit::TestCase
def setup def setup
end 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 def test_diskfilename
assert Attachment.disk_filename("test_file.txt") =~ /^\d{12}_test_file.txt$/ 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 'f8139524ebb8f32e51976982cd20a85d', Attachment.disk_filename("test_accentué")[13..-1]
assert_equal 'cbb5b0f30978ba03731d61f9f6d10011', Attachment.disk_filename("test_accentué.ça")[13..-1] assert_equal 'cbb5b0f30978ba03731d61f9f6d10011', Attachment.disk_filename("test_accentué.ça")[13..-1]
end end
def test_digest
assert_equal '1478adae0d4eb06d35897518540e25d6', Attachment.digest(Test::Unit::TestCase.fixture_path + "/files/testfile.txt")
end
end end
+12 -1
View File
@@ -55,6 +55,8 @@ class ApplicationHelperTest < HelperTestCase
'ftp://foo.bar' => '<a class="external" href="ftp://foo.bar">ftp://foo.bar</a>', '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>', '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>', '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) } to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
end end
@@ -105,7 +107,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>', '"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", "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 # 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) } to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
end end
@@ -116,6 +122,8 @@ class ApplicationHelperTest < HelperTestCase
changeset_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1}, changeset_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1},
:class => 'changeset', :title => 'My very first commit') :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}, document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1},
:class => 'document') :class => 'document')
@@ -133,6 +141,9 @@ class ApplicationHelperTest < HelperTestCase
'#3, #3 and #3.' => "#{issue_link}, #{issue_link} and #{issue_link}.", '#3, #3 and #3.' => "#{issue_link}, #{issue_link} and #{issue_link}.",
# changesets # changesets
'r1' => changeset_link, 'r1' => changeset_link,
'r1.' => "#{changeset_link}.",
'r1, r2' => "#{changeset_link}, #{changeset_link2}",
'r1,r2' => "#{changeset_link},#{changeset_link2}",
# documents # documents
'document#1' => document_link, 'document#1' => document_link,
'document:"Test document"' => document_link, 'document:"Test document"' => document_link,
+15
View File
@@ -92,6 +92,21 @@ class MailHandlerTest < Test::Unit::TestCase
assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
end 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 def test_add_issue_with_attachment_to_specific_project
issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'}) issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
assert issue.is_a?(Issue) assert issue.is_a?(Issue)
+10
View File
@@ -86,6 +86,16 @@ class MailerTest < Test::Unit::TestCase
# restore it # restore it
ActionController::AbstractRequest.relative_url_root = relative_url_root ActionController::AbstractRequest.relative_url_root = relative_url_root
end end
def test_email_headers
ActionMailer::Base.deliveries.clear
issue = Issue.find(1)
Mailer.deliver_issue_add(issue)
mail = ActionMailer::Base.deliveries.last
assert_not_nil mail
assert_equal 'bulk', mail.header_string('Precedence')
assert_equal 'auto-generated', mail.header_string('Auto-Submitted')
end
def test_plain_text_mail def test_plain_text_mail
Setting.plain_text_mail = 1 Setting.plain_text_mail = 1
+7 -5
View File
@@ -150,15 +150,17 @@ class QueryTest < Test::Unit::TestCase
def test_operator_contains def test_operator_contains
query = Query.new(:project => Project.find(1), :name => '_') query = Query.new(:project => Project.find(1), :name => '_')
query.add_filter('subject', '~', ['string']) query.add_filter('subject', '~', ['uNable'])
assert query.statement.include?("#{Issue.table_name}.subject LIKE '%string%'") assert query.statement.include?("LOWER(#{Issue.table_name}.subject) LIKE '%unable%'")
find_issues_with_query(query) result = find_issues_with_query(query)
assert result.empty?
result.each {|issue| assert issue.subject.downcase.include?('unable') }
end end
def test_operator_does_not_contains def test_operator_does_not_contains
query = Query.new(:project => Project.find(1), :name => '_') query = Query.new(:project => Project.find(1), :name => '_')
query.add_filter('subject', '!~', ['string']) query.add_filter('subject', '!~', ['uNable'])
assert query.statement.include?("#{Issue.table_name}.subject NOT LIKE '%string%'") assert query.statement.include?("LOWER(#{Issue.table_name}.subject) NOT LIKE '%unable%'")
find_issues_with_query(query) find_issues_with_query(query)
end end