Compare commits

...

57 Commits
0.8.0 ... 0.8.5

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
Jean-Philippe Lang
20bdba13cf Updates for 0.8.2 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2560 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-03-07 12:07:06 +00:00
Jean-Philippe Lang
46c0d72782 Merged r2259 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2559 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-03-07 12:02:08 +00:00
Jean-Philippe Lang
36b5d4f6af Backported r2429, r2430, r248 to r2491 and r2522 from trunk (request and controller objects added to the hooks by default.).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2558 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-03-07 11:43:38 +00:00
Jean-Philippe Lang
b366375e7e Merged r2553 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2557 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-03-07 11:22:56 +00:00
Jean-Philippe Lang
629c106a46 Merged r2515, r2516, r2527, r2534, r2555 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2556 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-03-07 11:17:23 +00:00
Eric Davis
f19ae06a72 Merged r2428 from trunk
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2548 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-03-04 00:03:06 +00:00
Jean-Philippe Lang
69b99aa518 Merged r2520 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2521 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-02-23 17:36:32 +00:00
Jean-Philippe Lang
cb5d0c469d Merged r2487, r2488 and r2507 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2514 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-02-22 10:00:25 +00:00
Jean-Philippe Lang
57d10ed893 Merged r2262, r2341 and r2486 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2513 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-02-21 19:29:33 +00:00
Jean-Philippe Lang
28e4ff8957 Merged r2426 and r2484 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2512 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-02-21 19:22:31 +00:00
Jean-Philippe Lang
3d1bd79ffb Set version to 0.8.1
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2470 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-02-15 09:08:26 +00:00
Jean-Philippe Lang
7f957653ad Merged r2463, r2465, r2467, r2468 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2469 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-02-15 09:04:24 +00:00
Jean-Philippe Lang
9a9141041e Merged r2461 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2462 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-02-13 17:32:27 +00:00
Jean-Philippe Lang
bc4249e3d3 Merged r2270, r2344, r2359, r2360, r2362, r2363, r2415, r2423, r2424 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2425 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-02-09 18:46:00 +00:00
Jean-Philippe Lang
fae04f3ae1 Merged r2246 and r2248 from trunk (#2456).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2310 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-01-24 13:01:31 +00:00
Jean-Philippe Lang
b13ef64794 Merged r2265 from trunk (#2523).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2309 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-01-24 12:27:56 +00:00
Jean-Philippe Lang
683e1c5d73 Merged r2266 from trunk (#2522).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2308 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-01-24 12:13:16 +00:00
Jean-Philippe Lang
1c1755d278 Merged r2280 from trunk (#2506).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2307 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-01-24 12:11:52 +00:00
Jean-Philippe Lang
5e6ff86f47 Merged r2302 from trunk (#2466).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2306 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-01-24 12:10:24 +00:00
Jean-Philippe Lang
764393aa6a Merged r2250 and r2251 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2252 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-01-10 10:51:59 +00:00
Jean-Philippe Lang
3155b8ccad Merged r2226 and r2233 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2245 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-01-05 18:16:14 +00:00
Jean-Philippe Lang
567c8ed9b0 Fixed: Darcs adapter works on Win32 only (wrong quotes in commands #2402).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2231 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-01-04 13:24:56 +00:00
Jean-Philippe Lang
49b6f9e4dd Merged r2218 to r2225 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2230 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-01-04 13:10:16 +00:00
Jean-Philippe Lang
c9d4d3a2be Merged r2209 to r2211 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2229 e93f8b46-1217-0410-a6f0-8f06a7374b81
2009-01-04 13:03:34 +00:00
Jean-Philippe Lang
31178553f3 Merged r2116, r2117 and r2187 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2217 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-31 11:40:03 +00:00
Jean-Philippe Lang
dea10c54f9 Merged r2164 to r2167, r2188 and r2189 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2216 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-31 11:31:02 +00:00
Jean-Philippe Lang
6abd32be9e Merged r2212 to r2214 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.8-stable@2215 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-31 10:47:19 +00:00
179 changed files with 2118 additions and 932 deletions

View File

@@ -63,6 +63,7 @@ class AccountController < ApplicationController
token = Token.create(:user => user, :action => 'autologin')
cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now }
end
call_hook(:controller_account_success_authentication_after, {:user => user })
redirect_back_or_default :controller => 'my', :action => 'page'
end
end

View File

@@ -86,6 +86,7 @@ class AdminController < ApplicationController
@flags = {
:default_admin_changed => User.find(:first, :conditions => ["login=? and hashed_password=?", 'admin', User.hash_password('admin')]).nil?,
:file_repository_writable => File.writable?(Attachment.storage_path),
:plugin_assets_writable => File.writable?(Engines.public_directory),
:rmagick_available => Object.const_defined?(:Magick)
}
end

View File

@@ -175,6 +175,7 @@ class ApplicationController < ActionController::Base
# TODO: move to model
def attach_files(obj, attachments)
attached = []
unsaved = []
if attachments && attachments.is_a?(Hash)
attachments.each_value do |attachment|
file = attachment['file']
@@ -183,7 +184,10 @@ class ApplicationController < ActionController::Base
:file => file,
:description => attachment['description'].to_s.strip,
:author => User.current)
attached << a unless a.new_record?
a.new_record? ? (unsaved << a) : (attached << a)
end
if unsaved.any?
flash[:warning] = l(:warning_attachments_not_saved, unsaved.size)
end
end
attached

View File

@@ -1,5 +1,5 @@
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
# Redmine - project management software
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,7 +17,11 @@
class AttachmentsController < ApplicationController
before_filter :find_project
before_filter :read_authorize, :except => :destroy
before_filter :delete_authorize, :only => :destroy
verify :method => :post, :only => :destroy
def show
if @attachment.is_diff?
@diff = File.new(@attachment.diskfile, "rb").read
@@ -31,25 +35,40 @@ class AttachmentsController < ApplicationController
end
def download
@attachment.increment_download if @attachment.container.is_a?(Version)
if @attachment.container.is_a?(Version) || @attachment.container.is_a?(Project)
@attachment.increment_download
end
# images are sent inline
send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
:type => @attachment.content_type,
:disposition => (@attachment.image? ? 'inline' : 'attachment')
end
def destroy
# Make sure association callbacks are called
@attachment.container.attachments.delete(@attachment)
redirect_to :back
rescue ::ActionController::RedirectBackError
redirect_to :controller => 'projects', :action => 'show', :id => @project
end
private
def find_project
@attachment = Attachment.find(params[:id])
# Show 404 if the filename in the url is wrong
raise ActiveRecord::RecordNotFound if params[:filename] && params[:filename] != @attachment.filename
@project = @attachment.project
permission = @attachment.container.is_a?(Version) ? :view_files : "view_#{@attachment.container.class.name.underscore.pluralize}".to_sym
allowed = User.current.allowed_to?(permission, @project)
allowed ? true : (User.current.logged? ? render_403 : require_login)
rescue ActiveRecord::RecordNotFound
render_404
end
def read_authorize
@attachment.visible? ? true : deny_access
end
def delete_authorize
@attachment.deletable? ? true : deny_access
end
end

View File

@@ -46,6 +46,7 @@ class CustomFieldsController < ApplicationController
end
if request.post? and @custom_field.save
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
end
@trackers = Tracker.find(:all, :order => 'position')
@@ -58,6 +59,7 @@ class CustomFieldsController < ApplicationController
@custom_field.trackers = params[:tracker_ids] ? Tracker.find(params[:tracker_ids]) : []
end
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
end
@trackers = Tracker.find(:all, :order => 'position')

View File

@@ -71,11 +71,6 @@ class DocumentsController < ApplicationController
Mailer.deliver_attachments_added(attachments) if !attachments.empty? && Setting.notified_events.include?('document_added')
redirect_to :action => 'show', :id => @document
end
def destroy_attachment
@document.attachments.find(params[:attachment_id]).destroy
redirect_to :action => 'show', :id => @document
end
private
def find_project

View File

@@ -18,20 +18,18 @@
class IssuesController < ApplicationController
menu_item :new_issue, :only => :new
before_filter :find_issue, :only => [:show, :edit, :reply, :destroy_attachment]
before_filter :find_issue, :only => [:show, :edit, :reply]
before_filter :find_issues, :only => [:bulk_edit, :move, :destroy]
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
include ProjectsHelper
helper :custom_fields
include CustomFieldsHelper
helper :ifpdf
include IfpdfHelper
helper :issue_relations
include IssueRelationsHelper
helper :watchers
@@ -43,6 +41,7 @@ class IssuesController < ApplicationController
include SortHelper
include IssuesHelper
helper :timelog
include Redmine::Export::PDF
def index
retrieve_query
@@ -68,7 +67,7 @@ class IssuesController < ApplicationController
format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
format.csv { send_data(issues_to_csv(@issues, @project).read, :type => 'text/csv; header=present', :filename => 'export.csv') }
format.pdf { send_data(render(:template => 'issues/index.rfpdf', :layout => false), :type => 'application/pdf', :filename => 'export.pdf') }
format.pdf { send_data(issues_to_pdf(@issues, @project), :type => 'application/pdf', :filename => 'export.pdf') }
end
else
# Send html if the query is not valid
@@ -106,7 +105,7 @@ class IssuesController < ApplicationController
respond_to do |format|
format.html { render :template => 'issues/show.rhtml' }
format.atom { render :action => 'changes', :layout => false, :content_type => 'application/atom+xml' }
format.pdf { send_data(render(:template => 'issues/show.rfpdf', :layout => false), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
end
end
@@ -123,7 +122,10 @@ class IssuesController < ApplicationController
render :nothing => true, :layout => true
return
end
@issue.attributes = params[:issue]
if params[:issue].is_a?(Hash)
@issue.attributes = params[:issue]
@issue.watcher_user_ids = params[:issue]['watcher_user_ids'] if User.current.allowed_to?(:add_issue_watchers, @project)
end
@issue.author = User.current
default_status = IssueStatus.default
@@ -145,7 +147,8 @@ 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')
redirect_to :controller => 'issues', :action => 'show', :id => @issue
redirect_to(params[:continue] ? { :action => 'new', :tracker_id => @issue.tracker } :
{ :action => 'show', :id => @issue })
return
end
end
@@ -183,7 +186,7 @@ class IssuesController < ApplicationController
if (@time_entry.hours.nil? || @time_entry.valid?) && @issue.save
# Log spend time
if current_role.allowed_to?(:log_time)
if User.current.allowed_to?(:log_time, @project)
@time_entry.save
end
if !journal.new_record?
@@ -315,17 +318,6 @@ class IssuesController < ApplicationController
@issues.each(&:destroy)
redirect_to :action => 'index', :project_id => @project
end
def destroy_attachment
a = @issue.attachments.find(params[:attachment_id])
a.destroy
journal = @issue.init_journal(User.current)
journal.details << JournalDetail.new(:property => 'attachment',
:prop_key => a.id,
:old_value => a.filename)
journal.save
redirect_to :action => 'show', :id => @issue
end
def gantt
@gantt = Redmine::Helpers::Gantt.new(params)
@@ -351,10 +343,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(render(:template => "issues/gantt.rfpdf", :layout => false), :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

@@ -33,8 +33,6 @@ class ProjectsController < ApplicationController
include SortHelper
helper :custom_fields
include CustomFieldsHelper
helper :ifpdf
include IfpdfHelper
helper :issues
helper IssuesHelper
helper :queries
@@ -84,6 +82,11 @@ class ProjectsController < ApplicationController
# Show @project
def show
if params[:jump]
# try to redirect to the requested menu item
redirect_to_project_menu_item(@project, params[:jump]) && return
end
@members_by_role = @project.members.find(:all, :include => [:user, :role], :order => 'position').group_by {|m| m.role}
@subprojects = @project.children.find(:all, :conditions => Project.visible_by(User.current))
@news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
@@ -188,10 +191,13 @@ class ProjectsController < ApplicationController
def add_file
if request.post?
@version = @project.versions.find_by_id(params[:version_id])
attachments = attach_files(@version, params[:attachments])
Mailer.deliver_attachments_added(attachments) if !attachments.empty? && Setting.notified_events.include?('file_added')
container = (params[:version_id].blank? ? @project : @project.versions.find_by_id(params[:version_id]))
attachments = attach_files(container, params[:attachments])
if !attachments.empty? && Setting.notified_events.include?('file_added')
Mailer.deliver_attachments_added(attachments)
end
redirect_to :controller => 'projects', :action => 'list_files', :id => @project
return
end
@versions = @project.versions.sort
end
@@ -203,7 +209,8 @@ class ProjectsController < ApplicationController
'size' => "#{Attachment.table_name}.filesize",
'downloads' => "#{Attachment.table_name}.downloads"
@versions = @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse
@containers = [ Project.find(@project.id, :include => :attachments, :order => sort_clause)]
@containers += @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse
render :layout => !request.xhr?
end

View File

@@ -127,6 +127,9 @@ class RepositoriesController < ApplicationController
end
def annotate
@entry = @repository.entry(@path, @rev)
show_error_not_found and return unless @entry
@annotate = @repository.scm.annotate(@path, @rev)
render_error l(:error_scm_annotate) and return if @annotate.nil? || @annotate.empty?
end

View File

@@ -40,7 +40,7 @@ class SettingsController < ApplicationController
@options[:user_format] = User::USER_FORMATS.keys.collect {|f| [User.current.name(f), f.to_s] }
@deliveries = ActionMailer::Base.perform_deliveries
@guessed_host_and_path = request.host_with_port
@guessed_host_and_path = request.host_with_port.dup
@guessed_host_and_path << ('/'+ request.relative_url_root.gsub(%r{^\/}, '')) unless request.relative_url_root.blank?
end

View File

@@ -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

@@ -75,7 +75,11 @@ class UsersController < ApplicationController
@user.admin = params[:user][:admin] if params[:user][:admin]
@user.login = params[:user][:login] if params[:user][:login]
@user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless params[:password].nil? or params[:password].empty? or @user.auth_source_id
if @user.update_attributes(params[:user])
@user.attributes = params[:user]
# Was the account actived ? (do it before User#save clears the change)
was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
if @user.save
Mailer.deliver_account_activated(@user) if was_activated
flash[:notice] = l(:notice_successful_update)
# Give a string to redirect_to otherwise it would use status param as the response code
redirect_to(url_for(:action => 'list', :status => params[:status], :page => params[:page]))

View File

@@ -37,12 +37,6 @@ class VersionsController < ApplicationController
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
end
def destroy_file
@version.attachments.find(params[:attachment_id]).destroy
flash[:notice] = l(:notice_successful_delete)
redirect_to :controller => 'projects', :action => 'list_files', :id => @project
end
def status_by
respond_to do |format|
format.html { render :action => 'show' }

View File

@@ -21,7 +21,7 @@ class WikiController < ApplicationController
before_filter :find_wiki, :authorize
before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy]
verify :method => :post, :only => [:destroy, :destroy_attachment, :protect], :redirect_to => { :action => :index }
verify :method => :post, :only => [:destroy, :protect], :redirect_to => { :action => :index }
helper :attachments
include AttachmentsHelper
@@ -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
@@ -181,13 +182,6 @@ class WikiController < ApplicationController
redirect_to :action => 'index', :page => @page.title
end
def destroy_attachment
@page = @wiki.find_page(params[:page])
return render_403 unless editable?
@page.attachments.find(params[:attachment_id]).destroy
redirect_to :action => 'index', :page => @page.title
end
private
def find_wiki

View File

@@ -147,6 +147,15 @@ module ApplicationHelper
end
content
end
# Renders flash messages
def render_flash_messages
s = ''
flash.each do |k,v|
s << content_tag('div', v, :class => "flash #{k}")
end
s
end
# Truncates and returns the string as a single line
def truncate_single_line(string, *args)
@@ -282,16 +291,15 @@ module ApplicationHelper
attachments = attachments.sort_by(&:created_on).reverse
text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(bmp|gif|jpg|jpeg|png))!/i) do |m|
style = $1
filename = $6
rf = Regexp.new(Regexp.escape(filename), Regexp::IGNORECASE)
filename = $6.downcase
# search for the picture in attachments
if found = attachments.detect { |att| att.filename =~ rf }
if found = attachments.detect { |att| att.filename.downcase == filename }
image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
alt = desc.blank? ? nil : "(#{desc})"
"!#{style}#{image_url}#{alt}!"
else
"!#{style}#{filename}!"
m
end
end
end
@@ -377,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?
@@ -479,11 +487,11 @@ module ApplicationHelper
full_messages = []
object.errors.each do |attr, msg|
next if msg.nil?
msg = msg.first if msg.is_a? Array
msg = [msg] unless msg.is_a?(Array)
if attr == "base"
full_messages << l(msg)
full_messages << l(*msg)
else
full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(*msg) unless attr == "custom_values"
end
end
# retrieve custom values error messages
@@ -491,8 +499,8 @@ module ApplicationHelper
object.custom_values.each do |v|
v.errors.each do |attr, msg|
next if msg.nil?
msg = msg.first if msg.is_a? Array
full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
msg = [msg] unless msg.is_a?(Array)
full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(*msg)
end
end
end
@@ -526,6 +534,7 @@ module ApplicationHelper
def back_url_hidden_field_tag
back_url = params[:back_url] || request.env['HTTP_REFERER']
back_url = CGI.unescape(back_url.to_s)
hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
end

View File

@@ -16,10 +16,15 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module AttachmentsHelper
# displays the links to a collection of attachments
def link_to_attachments(attachments, options = {})
if attachments.any?
render :partial => 'attachments/links', :locals => {:attachments => attachments, :options => options}
# Displays view/delete links to the attachments of the given object
# Options:
# :author -- author names are not displayed if set to false
def link_to_attachments(container, options = {})
options.assert_valid_keys(:author)
if container.attachments.any?
options = {:deletable => container.attachments_deletable?, :author => true}.merge(options)
render :partial => 'attachments/links', :locals => {:attachments => container.attachments, :options => options}
end
end

View File

@@ -1,85 +0,0 @@
# redMine - project management software
# Copyright (C) 2006 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'iconv'
require 'rfpdf/chinese'
module IfpdfHelper
class IFPDF < FPDF
include GLoc
attr_accessor :footer_date
def initialize(lang)
super()
set_language_if_valid lang
case current_language.to_s
when 'ja'
extend(PDF_Japanese)
AddSJISFont()
@font_for_content = 'SJIS'
@font_for_footer = 'SJIS'
when 'zh'
extend(PDF_Chinese)
AddGBFont()
@font_for_content = 'GB'
@font_for_footer = 'GB'
when 'zh-tw'
extend(PDF_Chinese)
AddBig5Font()
@font_for_content = 'Big5'
@font_for_footer = 'Big5'
else
@font_for_content = 'Arial'
@font_for_footer = 'Helvetica'
end
SetCreator(Redmine::Info.app_name)
SetFont(@font_for_content)
end
def SetFontStyle(style, size)
SetFont(@font_for_content, style, size)
end
def Cell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='')
@ic ||= Iconv.new(l(:general_pdf_encoding), 'UTF-8')
# these quotation marks are not correctly rendered in the pdf
txt = txt.gsub(/[“”]/, '"') if txt
txt = begin
# 0x5c char handling
txtar = txt.split('\\')
txtar << '' if txt[-1] == ?\\
txtar.collect {|x| @ic.iconv(x)}.join('\\').gsub(/\\/, "\\\\\\\\")
rescue
txt
end || ''
super w,h,txt,border,ln,align,fill,link
end
def Footer
SetFont(@font_for_footer, 'I', 8)
SetY(-15)
SetX(15)
Cell(0, 5, @footer_date, 0, 0, 'L')
SetY(-15)
SetX(-30)
Cell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
end
end
end

View File

@@ -36,7 +36,10 @@ module IssuesHelper
# Returns a string of css classes that apply to the given issue
def css_issue_classes(issue)
s = "issue status-#{issue.status.position} priority-#{issue.priority.position}"
s << ' closed' if issue.closed?
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
end

View File

@@ -40,8 +40,14 @@ module QueriesHelper
else
case column.name
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)
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

@@ -16,6 +16,8 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module TimelogHelper
include ApplicationHelper
def render_timelog_breadcrumb
links = []
links << link_to(l(:label_project_all), {:project_id => nil, :issue_id => nil})
@@ -33,7 +35,11 @@ module TimelogHelper
end
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
def sum_hours(data)
@@ -81,7 +87,7 @@ module TimelogHelper
csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
# csv lines
entries.each do |entry|
fields = [l_date(entry.spent_on),
fields = [format_date(entry.spent_on),
entry.user,
entry.activity,
entry.project,

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

@@ -33,7 +33,7 @@ class Attachment < ActiveRecord::Base
:author_key => :author_id,
: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 " +
"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',
:permission => :view_documents,
@@ -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
@@ -98,6 +104,14 @@ class Attachment < ActiveRecord::Base
container.project
end
def visible?(user=User.current)
container.attachments_visible?(user)
end
def deletable?(user=User.current)
container.attachments_deletable?(user)
end
def image?
self.filename =~ /\.(jpe?g|gif|png)$/i
end
@@ -133,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

@@ -18,7 +18,7 @@
class Document < ActiveRecord::Base
belongs_to :project
belongs_to :category, :class_name => "Enumeration", :foreign_key => "category_id"
has_many :attachments, :as => :container, :dependent => :destroy
acts_as_attachable :delete_permission => :manage_documents
acts_as_searchable :columns => ['title', "#{table_name}.description"], :include => :project
acts_as_event :title => Proc.new {|o| "#{l(:label_document)}: #{o.title}"},

View File

@@ -26,13 +26,13 @@ class Issue < ActiveRecord::Base
belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id'
has_many :journals, :as => :journalized, :dependent => :destroy
has_many :attachments, :as => :container, :dependent => :destroy
has_many :time_entries, :dependent => :delete_all
has_and_belongs_to_many :changesets, :order => "#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC"
has_many :relations_from, :class_name => 'IssueRelation', :foreign_key => 'issue_from_id', :dependent => :delete_all
has_many :relations_to, :class_name => 'IssueRelation', :foreign_key => 'issue_to_id', :dependent => :delete_all
acts_as_attachable :after_remove => :attachment_removed
acts_as_customizable
acts_as_watchable
acts_as_searchable :columns => ['subject', "#{table_name}.description", "#{Journal.table_name}.notes"],
@@ -45,7 +45,7 @@ class Issue < ActiveRecord::Base
acts_as_activity_provider :find_options => {:include => [:project, :author, :tracker]},
:author_key => :author_id
validates_presence_of :subject, :description, :priority, :project, :tracker, :author, :status
validates_presence_of :subject, :priority, :project, :tracker, :author, :status
validates_length_of :subject, :maximum => 255
validates_inclusion_of :done_ratio, :in => 0..100
validates_numericality_of :estimated_hours, :allow_nil => true
@@ -266,4 +266,15 @@ class Issue < ActiveRecord::Base
def to_s
"#{tracker} ##{id}: #{subject}"
end
private
# Callback on attachment deletion
def attachment_removed(obj)
journal = init_journal(User.current)
journal.details << JournalDetail.new(:property => 'attachment',
:prop_key => obj.id,
:old_value => obj.filename)
journal.save
end
end

View File

@@ -38,7 +38,7 @@ class Journal < ActiveRecord::Base
:conditions => "#{Journal.table_name}.journalized_type = 'Issue' AND" +
" (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')"}
def save
def save(*args)
# Do not save an empty journal
(details.empty? && notes.blank?) ? false : super
end

View File

@@ -40,7 +40,7 @@ class MailHandler < ActionMailer::Base
# Processes incoming emails
def receive(email)
@email = email
@user = User.active.find(:first, :conditions => ["LOWER(mail) = ?", email.from.first.to_s.strip.downcase])
@user = User.active.find(:first, :conditions => ["LOWER(mail) = ?", email.from.to_a.first.to_s.strip.downcase])
unless @user
# Unknown user => the email is ignored
# TODO: ability to create the user's account
@@ -90,6 +90,13 @@ class MailHandler < ActionMailer::Base
end
issue.subject = email.subject.chomp.toutf8
issue.description = plain_text_body
# custom fields
issue.custom_field_values = issue.available_custom_fields.inject({}) do |h, c|
if value = get_keyword(c.name, :override => true)
h[c.id] = value
end
h
end
issue.save!
add_attachments(issue)
logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger && logger.info
@@ -155,11 +162,18 @@ class MailHandler < ActionMailer::Base
end
end
def get_keyword(attr)
if @@handler_options[:allow_override].include?(attr.to_s) && plain_text_body =~ /^#{attr}:[ \t]*(.+)$/i
$1.strip
elsif !@@handler_options[:issue][attr].blank?
@@handler_options[:issue][attr]
def get_keyword(attr, options={})
@keywords ||= {}
if @keywords.has_key?(attr)
@keywords[attr]
else
@keywords[attr] = begin
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]
end
end
end
end
@@ -181,5 +195,6 @@ class MailHandler < ActionMailer::Base
@plain_text_body = plain_text_part.body.to_s
end
@plain_text_body.strip!
@plain_text_body
end
end

View File

@@ -22,12 +22,19 @@ class Mailer < ActionMailer::Base
include ActionController::UrlWriter
def self.default_url_options
h = Setting.host_name
h = h.to_s.gsub(%r{\/.*$}, '') unless Redmine::Utils.relative_url_root.blank?
{ :host => h, :protocol => Setting.protocol }
end
def issue_add(issue)
redmine_headers 'Project' => issue.project.identifier,
'Issue-Id' => issue.id,
'Issue-Author' => issue.author.login
redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
recipients issue.recipients
cc(issue.watcher_recipients - @recipients)
subject "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] (#{issue.status.name}) #{issue.subject}"
body :issue => issue,
:issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue)
@@ -39,6 +46,7 @@ class Mailer < ActionMailer::Base
'Issue-Id' => issue.id,
'Issue-Author' => issue.author.login
redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
@author = journal.user
recipients issue.recipients
# Watchers in cc
cc(issue.watcher_recipients - @recipients)
@@ -73,6 +81,9 @@ class Mailer < ActionMailer::Base
added_to = ''
added_to_url = ''
case container.class.name
when 'Project'
added_to_url = url_for(:controller => 'projects', :action => 'list_files', :id => container)
added_to = "#{l(:label_project)}: #{container}"
when 'Version'
added_to_url = url_for(:controller => 'projects', :action => 'list_files', :id => container.project_id)
added_to = "#{l(:label_version)}: #{container.name}"
@@ -122,6 +133,15 @@ class Mailer < ActionMailer::Base
:url => url_for(:controller => 'users', :action => 'index', :status => User::STATUS_REGISTERED, :sort_key => 'created_on', :sort_order => 'desc')
end
# A registered user's account was activated by an administrator
def account_activated(user)
set_language_if_valid user.language
recipients user.mail
subject l(:mail_subject_register, Setting.app_title)
body :user => user,
:login_url => url_for(:controller => 'account', :action => 'login')
end
def lost_password(token)
set_language_if_valid(token.user.language)
recipients token.user.mail
@@ -184,16 +204,12 @@ class Mailer < ActionMailer::Base
set_language_if_valid Setting.default_language
from Setting.mail_from
# URL options
h = Setting.host_name
h = h.to_s.gsub(%r{\/.*$}, '') unless ActionController::AbstractRequest.relative_url_root.blank?
default_url_options[:host] = h
default_url_options[:protocol] = Setting.protocol
# Common headers
headers 'X-Mailer' => 'Redmine',
'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
# Appends a Redmine header field (name is prepended with 'X-Redmine-')
@@ -205,9 +221,10 @@ class Mailer < ActionMailer::Base
def create_mail
# Removes the current user from the recipients and cc
# if he doesn't want to receive notifications about what he does
if User.current.pref[:no_self_notified]
recipients.delete(User.current.mail) if recipients
cc.delete(User.current.mail) if cc
@author ||= User.current
if @author.pref[:no_self_notified]
recipients.delete(@author.mail) if recipients
cc.delete(@author.mail) if cc
end
# Blind carbon copy recipients
if Setting.bcc_recipients?

View File

@@ -19,11 +19,11 @@ class Message < ActiveRecord::Base
belongs_to :board
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
acts_as_tree :counter_cache => :replies_count, :order => "#{Message.table_name}.created_on ASC"
has_many :attachments, :as => :container, :dependent => :destroy
acts_as_attachable
belongs_to :last_reply, :class_name => 'Message', :foreign_key => 'last_reply_id'
acts_as_searchable :columns => ['subject', 'content'],
:include => {:board, :project},
:include => {:board => :project},
:project_key => 'project_id',
:date_column => "#{table_name}.created_on"
acts_as_event :title => Proc.new {|o| "#{o.board.name}: #{o.subject}"},

View File

@@ -24,7 +24,7 @@ class News < ActiveRecord::Base
validates_length_of :title, :maximum => 60
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_activity_provider :find_options => {:include => [:project, :author]},
:author_key => :author_id

View File

@@ -44,6 +44,8 @@ class Project < ActiveRecord::Base
:association_foreign_key => 'custom_field_id'
acts_as_tree :order => "name", :counter_cache => true
acts_as_attachable :view_permission => :view_files,
:delete_permission => :manage_files
acts_as_customizable
acts_as_searchable :columns => ['name', 'description'], :project_key => 'id', :permission => nil
@@ -58,7 +60,7 @@ class Project < ActiveRecord::Base
validates_associated :repository, :wiki
validates_length_of :name, :maximum => 30
validates_length_of :homepage, :maximum => 255
validates_length_of :identifier, :in => 3..20
validates_length_of :identifier, :in => 1..20
validates_format_of :identifier, :with => /^[a-z0-9\-]*$/
before_destroy :delete_all_members

View File

@@ -93,6 +93,7 @@ class Query < ActiveRecord::Base
cattr_reader :operators_by_filter_type
@@available_columns = [
QueryColumn.new(:project, :sortable => "#{Project.table_name}.name"),
QueryColumn.new(:tracker, :sortable => "#{Tracker.table_name}.position"),
QueryColumn.new(:status, :sortable => "#{IssueStatus.table_name}.position"),
QueryColumn.new(:priority, :sortable => "#{Enumeration.table_name}.position", :default_order => 'desc'),
@@ -234,7 +235,10 @@ class Query < ActiveRecord::Base
def 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
# preserve the column_names order
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
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
@@ -54,8 +54,8 @@ class Repository::Subversion < Repository
# loads changesets by batches of 200
identifier_to = [identifier_from + 199, scm_revision].min
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,
:revision => revision.identifier,
:committer => revision.author,
@@ -68,7 +68,7 @@ class Repository::Subversion < Repository
:path => change[:path],
:from_path => change[:from_path],
:from_revision => change[:from_revision])
end
end unless changeset.new_record?
end
end unless revisions.nil?
identifier_from = identifier_to + 1

View File

@@ -31,9 +31,9 @@ class Role < ActiveRecord::Base
raise "Can not copy workflow from a #{role.class}" unless role.is_a?(Role)
raise "Can not copy workflow from/to an unsaved role" if proxy_owner.new_record? || role.new_record?
clear
connection.insert "INSERT INTO workflows (tracker_id, old_status_id, new_status_id, role_id)" +
connection.insert "INSERT INTO #{Workflow.table_name} (tracker_id, old_status_id, new_status_id, role_id)" +
" SELECT tracker_id, old_status_id, new_status_id, #{proxy_owner.id}" +
" FROM workflows" +
" FROM #{Workflow.table_name}" +
" WHERE role_id = #{role.id}"
end
end

View File

@@ -32,7 +32,7 @@ class TimeEntry < ActiveRecord::Base
:description => :comments
validates_presence_of :user_id, :activity_id, :project_id, :hours, :spent_on
validates_numericality_of :hours, :allow_nil => true
validates_numericality_of :hours, :allow_nil => true, :message => :activerecord_error_invalid
validates_length_of :comments, :maximum => 255, :allow_nil => true
def after_initialize
@@ -54,7 +54,7 @@ class TimeEntry < ActiveRecord::Base
end
def hours=(h)
write_attribute :hours, (h.is_a?(String) ? h.to_hours : h)
write_attribute :hours, (h.is_a?(String) ? (h.to_hours || h) : h)
end
# tyear, tmonth, tweek assigned where setting spent_on attributes

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

@@ -23,9 +23,9 @@ class Tracker < ActiveRecord::Base
raise "Can not copy workflow from a #{tracker.class}" unless tracker.is_a?(Tracker)
raise "Can not copy workflow from/to an unsaved tracker" if proxy_owner.new_record? || tracker.new_record?
clear
connection.insert "INSERT INTO workflows (tracker_id, old_status_id, new_status_id, role_id)" +
connection.insert "INSERT INTO #{Workflow.table_name} (tracker_id, old_status_id, new_status_id, role_id)" +
" SELECT #{proxy_owner.id}, old_status_id, new_status_id, role_id" +
" FROM workflows" +
" FROM #{Workflow.table_name}" +
" WHERE tracker_id = #{tracker.id}"
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

@@ -19,7 +19,8 @@ class Version < ActiveRecord::Base
before_destroy :check_integrity
belongs_to :project
has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id'
has_many :attachments, :as => :container, :dependent => :destroy
acts_as_attachable :view_permission => :view_files,
:delete_permission => :manage_files
validates_presence_of :name
validates_uniqueness_of :name, :scope => [:project_id]

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

@@ -21,7 +21,7 @@ require 'enumerator'
class WikiPage < ActiveRecord::Base
belongs_to :wiki
has_one :content, :class_name => 'WikiContent', :foreign_key => 'page_id', :dependent => :destroy
has_many :attachments, :as => :container, :dependent => :destroy
acts_as_attachable :delete_permission => :delete_wiki_pages_attachments
acts_as_tree :order => 'title'
acts_as_event :title => Proc.new {|o| "#{l(:label_wiki)}: #{o.title}"},
@@ -111,6 +111,10 @@ class WikiPage < ActiveRecord::Base
def editable_by?(usr)
!protected? || usr.allowed_to?(:protect_wiki_pages, wiki.project)
end
def attachments_deletable?(usr=User.current)
editable_by?(usr) && super(usr)
end
def parent_title
@parent_title || (self.parent && self.parent.pretty_title)

View File

@@ -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

@@ -4,7 +4,8 @@
<table class="list">
<tr class="odd"><td><%= l(:text_default_administrator_account_changed) %></td><td><%= image_tag (@flags[:default_admin_changed] ? 'true.png' : 'false.png'), :style => "vertical-align:bottom;" %></td></tr>
<tr class="even"><td><%= l(:text_file_repository_writable) %></td><td><%= image_tag (@flags[:file_repository_writable] ? 'true.png' : 'false.png'), :style => "vertical-align:bottom;" %></td></tr>
<tr class="even"><td><%= l(:text_file_repository_writable) %> (<%= Attachment.storage_path %>)</td><td><%= image_tag (@flags[:file_repository_writable] ? 'true.png' : 'false.png'), :style => "vertical-align:bottom;" %></td></tr>
<tr class="even"><td><%= l(:text_plugin_assets_writable) %> (<%= Engines.public_directory %>)</td><td><%= image_tag (@flags[:plugin_assets_writable] ? 'true.png' : 'false.png'), :style => "vertical-align:bottom;" %></td></tr>
<tr class="odd"><td><%= l(:text_rmagick_available) %></td><td><%= image_tag (@flags[:rmagick_available] ? 'true.png' : 'false.png'), :style => "vertical-align:bottom;" %></td></tr>
</table>

View File

@@ -3,14 +3,14 @@
<p><%= link_to_attachment attachment, :class => 'icon icon-attachment' -%>
<%= h(" - #{attachment.description}") unless attachment.description.blank? %>
<span class="size">(<%= number_to_human_size attachment.filesize %>)</span>
<% if options[:delete_url] %>
<%= link_to image_tag('delete.png'), options[:delete_url].update({:attachment_id => attachment}),
<% if options[:deletable] %>
<%= link_to image_tag('delete.png'), {:controller => 'attachments', :action => 'destroy', :id => attachment},
:confirm => l(:text_are_you_sure),
:method => :post,
:class => 'delete',
:title => l(:button_delete) %>
<% end %>
<% unless options[:no_author] %>
<% if options[:author] %>
<span class="author"><%= attachment.author %>, <%= format_time(attachment.created_on) %></span>
<% end %>
</p>

View File

@@ -11,7 +11,7 @@ while day <= calendar.enddt %>
<p class="day-num"><%= day.day %></p>
<% calendar.events_on(day).each do |i| %>
<% if i.is_a? Issue %>
<div class="tooltip">
<div class="<%= css_issue_classes(i) %> tooltip">
<%= if day == i.start_date && day == i.due_date
image_tag('arrow_bw.png')
elsif day == i.start_date

View File

@@ -3,7 +3,7 @@
<tbody>
<% line_num = 1 %>
<% syntax_highlight(filename, to_utf8(content)).each_line do |line| %>
<tr><th class="line-num" id="L<%= line_num %>"><%= line_num %></th><td class="line-code"><pre><%= line %></pre></td></tr>
<tr><th class="line-num" id="L<%= line_num %>"><a href="#L<%= line_num %>"><%= line_num %></a></th><td class="line-code"><pre><%= line %></pre></td></tr>
<% line_num += 1 %>
<% end %>
</tbody>

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

@@ -82,6 +82,7 @@ function deleteValueField(e) {
<% end %>
</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 class="box">
@@ -109,5 +110,6 @@ when "IssueCustomField" %>
<p><%= f.check_box :is_required %></p>
<% end %>
<%= call_hook(:"view_custom_fields_form_#{@custom_field.type.to_s.underscore}", :custom_field => @custom_field, :form => f) %>
</div>
<%= javascript_tag "toggle_custom_field_format();" %>

View File

@@ -12,7 +12,7 @@
</div>
<h3><%= l(:label_attachment_plural) %></h3>
<%= link_to_attachments @attachments, :delete_url => (authorize_for('documents', 'destroy_attachment') ? {:controller => 'documents', :action => 'destroy_attachment', :id => @document} : nil) %>
<%= link_to_attachments @document %>
<% if authorize_for('documents', '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;",

View File

@@ -11,5 +11,7 @@
<p><label for="issue_status_is_default"><%=l(:field_is_default)%></label>
<%= check_box 'issue_status', 'is_default' %></p>
<%= call_hook(:view_issue_statuses_form, :issue_status => @issue_status) %>
<!--[eoform:issue_status]-->
</div>
</div>

View File

@@ -8,7 +8,7 @@
<div id="issue_descr_fields" <%= 'style="display:none"' unless @issue.new_record? || @issue.errors.any? %>>
<p><%= f.text_field :subject, :size => 80, :required => true %></p>
<p><%= f.text_area :description, :required => true,
<p><%= f.text_area :description,
:cols => 60,
:rows => (@issue.description.blank? ? 10 : [[10, @issue.description.length / 50].max, 100].min),
:accesskey => accesskey(:edit),
@@ -24,11 +24,13 @@
<p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), :required => true %></p>
<p><%= f.select :assigned_to_id, (@issue.assignable_users.collect {|m| [m.name, m.id]}), :include_blank => true %></p>
<% unless @project.issue_categories.empty? %>
<p><%= f.select :category_id, (@project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %>
<%= prompt_to_remote(l(:label_issue_category_new),
l(:label_issue_category_new), 'category[name]',
{:controller => 'projects', :action => 'add_issue_category', :id => @project},
:class => 'small', :tabindex => 199) if authorize_for('projects', 'add_issue_category') %></p>
<% end %>
<%= content_tag('p', f.select(:fixed_version_id,
(@project.versions.sort.collect {|v| [v.name, v.id]}),
{ :include_blank => true })) unless @project.versions.empty? %>
@@ -48,6 +50,14 @@
<p><label><%=l(:label_attachment_plural)%></label><%= render :partial => 'attachments/form' %></p>
<% end %>
<% if @issue.new_record? && User.current.allowed_to?(:add_issue_watchers, @project) -%>
<p><label><%= l(:label_issue_watchers) %></label>
<% @issue.project.users.sort.each do |user| -%>
<label class="floating"><%= check_box_tag 'issue[watcher_user_ids][]', user.id, @issue.watcher_user_ids.include?(user.id) %> <%=h user %></label>
<% end -%>
</p>
<% end %>
<%= call_hook(:view_issues_form_details_bottom, { :issue => @issue, :form => f }) %>
<%= wikitoolbar_for 'issue_description' %>

View File

@@ -7,8 +7,8 @@
<%= call_hook(:view_issues_sidebar_issues_bottom) %>
<% 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_gantt), :action => 'gantt', :project_id => @project) if User.current.allowed_to?(:view_gantt, @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), :controller => 'issues', :action => 'gantt', :project_id => @project) if User.current.allowed_to?(:view_gantt, @project, :global => true)
%>
<% unless planning_links.empty? %>
<h3><%= l(:label_planning) %></h3>

View File

@@ -44,6 +44,7 @@
<fieldset><legend><%= l(:field_notes) %></legend>
<%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %>
<%= wikitoolbar_for 'notes' %>
</fieldset>
</div>
<p><%= submit_tag l(:button_submit) %>

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

@@ -1,4 +1,6 @@
<ul>
<%= call_hook(:view_issues_context_menu_start, {:issues => @issues, :can => @can, :back => @back }) %>
<% if !@issue.nil? -%>
<li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue},
:class => 'icon-edit', :disabled => !@can[:edit] %></li>
@@ -87,4 +89,6 @@
:class => 'icon-move', :disabled => !@can[:move] %></li>
<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>
<%= call_hook(:view_issues_context_menu_end, {:issues => @issues, :can => @can, :back => @back }) %>
</ul>

View File

@@ -1,188 +0,0 @@
<%
pdf=IfpdfHelper::IFPDF.new(current_language)
pdf.SetTitle("#{l(:label_gantt)} #{@project}")
pdf.AliasNbPages
pdf.footer_date = format_date(Date.today)
pdf.AddPage("L")
pdf.SetFontStyle('B',12)
pdf.SetX(15)
pdf.Cell(70, 20, @project.to_s)
pdf.Ln
pdf.SetFontStyle('B',9)
subject_width = 70
header_heigth = 5
headers_heigth = header_heigth
show_weeks = false
show_days = false
if @gantt.months < 7
show_weeks = true
headers_heigth = 2*header_heigth
if @gantt.months < 3
show_days = true
headers_heigth = 3*header_heigth
end
end
g_width = 210
zoom = (g_width) / (@gantt.date_to - @gantt.date_from + 1)
g_height = 120
t_height = g_height + headers_heigth
y_start = pdf.GetY
#
# Months headers
#
month_f = @gantt.date_from
left = subject_width
height = header_heigth
@gantt.months.times do
width = ((month_f >> 1) - month_f) * zoom
pdf.SetY(y_start)
pdf.SetX(left)
pdf.Cell(width, height, "#{month_f.year}-#{month_f.month}", "LTR", 0, "C")
left = left + width
month_f = month_f >> 1
end
#
# Weeks headers
#
if show_weeks
left = subject_width
height = header_heigth
if @gantt.date_from.cwday == 1
# @gantt.date_from is monday
week_f = @gantt.date_from
else
# find next monday after @gantt.date_from
week_f = @gantt.date_from + (7 - @gantt.date_from.cwday + 1)
width = (7 - @gantt.date_from.cwday + 1) * zoom-1
pdf.SetY(y_start + header_heigth)
pdf.SetX(left)
pdf.Cell(width + 1, height, "", "LTR")
left = left + width+1
end
while week_f <= @gantt.date_to
width = (week_f + 6 <= @gantt.date_to) ? 7 * zoom : (@gantt.date_to - week_f + 1) * zoom
pdf.SetY(y_start + header_heigth)
pdf.SetX(left)
pdf.Cell(width, height, (width >= 5 ? week_f.cweek.to_s : ""), "LTR", 0, "C")
left = left + width
week_f = week_f+7
end
end
#
# Days headers
#
if show_days
left = subject_width
height = header_heigth
wday = @gantt.date_from.cwday
pdf.SetFontStyle('B',7)
(@gantt.date_to - @gantt.date_from + 1).to_i.times do
width = zoom
pdf.SetY(y_start + 2 * header_heigth)
pdf.SetX(left)
pdf.Cell(width, height, day_name(wday).first, "LTR", 0, "C")
left = left + width
wday = wday + 1
wday = 1 if wday > 7
end
end
pdf.SetY(y_start)
pdf.SetX(15)
pdf.Cell(subject_width+g_width-15, headers_heigth, "", 1)
#
# Tasks
#
top = headers_heigth + y_start
pdf.SetFontStyle('B',7)
@gantt.events.each do |i|
pdf.SetY(top)
pdf.SetX(15)
if i.is_a? Issue
pdf.Cell(subject_width-15, 5, "#{i.tracker.name} #{i.id}: #{i.subject}".sub(/^(.{30}[^\s]*\s).*$/, '\1 (...)'), "LR")
else
pdf.Cell(subject_width-15, 5, "#{l(:label_version)}: #{i.name}", "LR")
end
pdf.SetY(top)
pdf.SetX(subject_width)
pdf.Cell(g_width, 5, "", "LR")
pdf.SetY(top+1.5)
if i.is_a? Issue
i_start_date = (i.start_date >= @gantt.date_from ? i.start_date : @gantt.date_from )
i_end_date = (i.due_before <= @gantt.date_to ? i.due_before : @gantt.date_to )
i_done_date = i.start_date + ((i.due_before - i.start_date+1)*i.done_ratio/100).floor
i_done_date = (i_done_date <= @gantt.date_from ? @gantt.date_from : i_done_date )
i_done_date = (i_done_date >= @gantt.date_to ? @gantt.date_to : i_done_date )
i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
i_left = ((i_start_date - @gantt.date_from)*zoom)
i_width = ((i_end_date - i_start_date + 1)*zoom)
d_width = ((i_done_date - i_start_date)*zoom)
l_width = ((i_late_date - i_start_date+1)*zoom) if i_late_date
l_width ||= 0
pdf.SetX(subject_width + i_left)
pdf.SetFillColor(200,200,200)
pdf.Cell(i_width, 2, "", 0, 0, "", 1)
if l_width > 0
pdf.SetY(top+1.5)
pdf.SetX(subject_width + i_left)
pdf.SetFillColor(255,100,100)
pdf.Cell(l_width, 2, "", 0, 0, "", 1)
end
if d_width > 0
pdf.SetY(top+1.5)
pdf.SetX(subject_width + i_left)
pdf.SetFillColor(100,100,255)
pdf.Cell(d_width, 2, "", 0, 0, "", 1)
end
pdf.SetY(top+1.5)
pdf.SetX(subject_width + i_left + i_width)
pdf.Cell(30, 2, "#{i.status.name} #{i.done_ratio}%")
else
i_left = ((i.start_date - @gantt.date_from)*zoom)
pdf.SetX(subject_width + i_left)
pdf.SetFillColor(50,200,50)
pdf.Cell(2, 2, "", 0, 0, "", 1)
pdf.SetY(top+1.5)
pdf.SetX(subject_width + i_left + 3)
pdf.Cell(30, 2, "#{i.name}")
end
top = top + 5
pdf.SetDrawColor(200, 200, 200)
pdf.Line(15, top, subject_width+g_width, top)
if pdf.GetY() > 180
pdf.AddPage("L")
top = 20
pdf.Line(15, top, subject_width+g_width, top)
end
pdf.SetDrawColor(0, 0, 0)
end
pdf.Line(15, top, subject_width+g_width, top)
%>
<%= pdf.Output %>

View File

@@ -1,50 +0,0 @@
<% pdf=IfpdfHelper::IFPDF.new(current_language)
title = @project ? "#{@project.name} - #{l(:label_issue_plural)}" : "#{l(:label_issue_plural)}"
pdf.SetTitle(title)
pdf.AliasNbPages
pdf.footer_date = format_date(Date.today)
pdf.AddPage("L")
row_height = 7
#
# title
#
pdf.SetFontStyle('B',11)
pdf.Cell(190,10, title)
pdf.Ln
#
# headers
#
pdf.SetFontStyle('B',10)
pdf.SetFillColor(230, 230, 230)
pdf.Cell(15, row_height, "#", 0, 0, 'L', 1)
pdf.Cell(30, row_height, l(:field_tracker), 0, 0, 'L', 1)
pdf.Cell(30, row_height, l(:field_status), 0, 0, 'L', 1)
pdf.Cell(30, row_height, l(:field_priority), 0, 0, 'L', 1)
pdf.Cell(40, row_height, l(:field_assigned_to), 0, 0, 'L', 1)
pdf.Cell(25, row_height, l(:field_updated_on), 0, 0, 'L', 1)
pdf.Cell(0, row_height, l(:field_subject), 0, 0, 'L', 1)
pdf.Line(10, pdf.GetY, 287, pdf.GetY)
pdf.Ln
pdf.Line(10, pdf.GetY, 287, pdf.GetY)
pdf.SetY(pdf.GetY() + 1)
#
# rows
#
pdf.SetFontStyle('',9)
pdf.SetFillColor(255, 255, 255)
@issues.each do |issue|
pdf.Cell(15, row_height, issue.id.to_s, 0, 0, 'L', 1)
pdf.Cell(30, row_height, issue.tracker.name, 0, 0, 'L', 1)
pdf.Cell(30, row_height, issue.status.name, 0, 0, 'L', 1)
pdf.Cell(30, row_height, issue.priority.name, 0, 0, 'L', 1)
pdf.Cell(40, row_height, issue.assigned_to ? issue.assigned_to.name : '', 0, 0, 'L', 1)
pdf.Cell(25, row_height, format_date(issue.updated_on), 0, 0, 'L', 1)
pdf.MultiCell(0, row_height, (@project == issue.project ? issue.subject : "#{issue.project.name} - #{issue.subject}"))
pdf.Line(10, pdf.GetY, 287, pdf.GetY)
pdf.SetY(pdf.GetY() + 1)
end
%>
<%= pdf.Output %>

View File

@@ -42,6 +42,7 @@
<% else %>
<%= render :partial => 'issues/list', :locals => {:issues => @issues, :query => @query} %>
<p class="pagination"><%= pagination_links_full @issue_pages, @issue_count %></p>
<% end %>
<p class="other-formats">
<%= l(:label_export_to) %>
@@ -50,7 +51,6 @@
<span><%= link_to 'PDF', {:format => 'pdf'}, :class => 'pdf' %></span>
</p>
<% end %>
<% end %>
<% content_for :sidebar do %>
<%= render :partial => 'issues/sidebar' %>

View File

@@ -7,6 +7,7 @@
<%= render :partial => 'issues/form', :locals => {:f => f} %>
</div>
<%= submit_tag l(:button_create) %>
<%= submit_tag l(:button_create_and_continue), :name => 'continue' %>
<%= link_to_remote l(:label_preview),
{ :url => { :controller => 'issues', :action => 'preview', :project_id => @project },
:method => 'post',
@@ -14,6 +15,12 @@
:with => "Form.serialize('issue-form')",
:complete => "Element.scrollTo('preview')"
}, :accesskey => accesskey(:preview) %>
<%= javascript_tag "Form.Element.focus('issue_subject');" %>
<% end %>
<div id="preview" class="wiki"></div>
<% content_for :header_tags do %>
<%= stylesheet_link_tag 'scm' %>
<% end %>

View File

@@ -1,126 +0,0 @@
<% pdf=IfpdfHelper::IFPDF.new(current_language)
pdf.SetTitle("#{@project.name} - ##{@issue.tracker.name} #{@issue.id}")
pdf.AliasNbPages
pdf.footer_date = format_date(Date.today)
pdf.AddPage
pdf.SetFontStyle('B',11)
pdf.Cell(190,10, "#{@issue.project} - #{@issue.tracker} # #{@issue.id}: #{@issue.subject}")
pdf.Ln
y0 = pdf.GetY
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_status) + ":","LT")
pdf.SetFontStyle('',9)
pdf.Cell(60,5, @issue.status.name,"RT")
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_priority) + ":","LT")
pdf.SetFontStyle('',9)
pdf.Cell(60,5, @issue.priority.name,"RT")
pdf.Ln
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_author) + ":","L")
pdf.SetFontStyle('',9)
pdf.Cell(60,5, @issue.author.name,"R")
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_category) + ":","L")
pdf.SetFontStyle('',9)
pdf.Cell(60,5, (@issue.category ? @issue.category.name : "-"),"R")
pdf.Ln
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_created_on) + ":","L")
pdf.SetFontStyle('',9)
pdf.Cell(60,5, format_date(@issue.created_on),"R")
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_assigned_to) + ":","L")
pdf.SetFontStyle('',9)
pdf.Cell(60,5, (@issue.assigned_to ? @issue.assigned_to.name : "-"),"R")
pdf.Ln
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_updated_on) + ":","LB")
pdf.SetFontStyle('',9)
pdf.Cell(60,5, format_date(@issue.updated_on),"RB")
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_due_date) + ":","LB")
pdf.SetFontStyle('',9)
pdf.Cell(60,5, format_date(@issue.due_date),"RB")
pdf.Ln
for custom_value in @issue.custom_values
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, custom_value.custom_field.name + ":","L")
pdf.SetFontStyle('',9)
pdf.MultiCell(155,5, (show_value custom_value),"R")
end
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_subject) + ":","LTB")
pdf.SetFontStyle('',9)
pdf.Cell(155,5, @issue.subject,"RTB")
pdf.Ln
pdf.SetFontStyle('B',9)
pdf.Cell(35,5, l(:field_description) + ":")
pdf.SetFontStyle('',9)
pdf.MultiCell(155,5, @issue.description,"BR")
pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY)
pdf.Line(pdf.GetX, pdf.GetY, 170, pdf.GetY)
pdf.Ln
if @issue.changesets.any? && User.current.allowed_to?(:view_changesets, @issue.project)
pdf.SetFontStyle('B',9)
pdf.Cell(190,5, l(:label_associated_revisions), "B")
pdf.Ln
for changeset in @issue.changesets
pdf.SetFontStyle('B',8)
pdf.Cell(190,5, format_time(changeset.committed_on) + " - " + changeset.author.to_s)
pdf.Ln
unless changeset.comments.blank?
pdf.SetFontStyle('',8)
pdf.MultiCell(190,5, changeset.comments)
end
pdf.Ln
end
end
pdf.SetFontStyle('B',9)
pdf.Cell(190,5, l(:label_history), "B")
pdf.Ln
for journal in @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
pdf.SetFontStyle('B',8)
pdf.Cell(190,5, format_time(journal.created_on) + " - " + journal.user.name)
pdf.Ln
pdf.SetFontStyle('I',8)
for detail in journal.details
pdf.Cell(190,5, "- " + show_detail(detail, true))
pdf.Ln
end
if journal.notes?
pdf.SetFontStyle('',8)
pdf.MultiCell(190,5, journal.notes)
end
pdf.Ln
end
if @issue.attachments.any?
pdf.SetFontStyle('B',9)
pdf.Cell(190,5, l(:label_attachment_plural), "B")
pdf.Ln
for attachment in @issue.attachments
pdf.SetFontStyle('',8)
pdf.Cell(80,5, attachment.filename)
pdf.Cell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
pdf.Cell(25,5, format_date(attachment.created_on),0,0,"R")
pdf.Cell(65,5, attachment.author.name,0,0,"R")
pdf.Ln
end
end
%>
<%= pdf.Output %>

View File

@@ -9,7 +9,7 @@
<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") %>
<h3><%=h @issue.subject %></h3>
<p class="author">
@@ -19,11 +19,11 @@
<table width="100%">
<tr>
<td style="width:15%" class="status"><b><%=l(:field_status)%>:</b></td><td style="width:35%" class="status status-<%= @issue.status.name %>"><%= @issue.status.name %></td>
<td style="width:15%" class="status"><b><%=l(:field_status)%>:</b></td><td style="width:35%" class="status"><%= @issue.status.name %></td>
<td style="width:15%" class="start-date"><b><%=l(:field_start_date)%>:</b></td><td style="width:35%"><%= format_date(@issue.start_date) %></td>
</tr>
<tr>
<td class="priority"><b><%=l(:field_priority)%>:</b></td><td class="priority priority-<%= @issue.priority.name %>"><%= @issue.priority.name %></td>
<td class="priority"><b><%=l(:field_priority)%>:</b></td><td class="priority"><%= @issue.priority.name %></td>
<td class="due-date"><b><%=l(:field_due_date)%>:</b></td><td class="due-date"><%= format_date(@issue.due_date) %></td>
</tr>
<tr>
@@ -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)
@@ -59,7 +59,7 @@ end %>
<hr />
<div class="contextual">
<%= link_to_remote_if_authorized l(:button_quote), { :url => {:action => 'reply', :id => @issue} }, :class => 'icon icon-comment' %>
<%= link_to_remote_if_authorized(l(:button_quote), { :url => {:action => 'reply', :id => @issue} }, :class => 'icon icon-comment') unless @issue.description.blank? %>
</div>
<p><strong><%=l(:field_description)%></strong></p>
@@ -67,9 +67,9 @@ end %>
<%= textilizable @issue, :description, :attachments => @issue.attachments %>
</div>
<% if @issue.attachments.any? %>
<%= link_to_attachments @issue.attachments, :delete_url => (authorize_for('issues', 'destroy_attachment') ? {:controller => 'issues', :action => 'destroy_attachment', :id => @issue} : nil) %>
<% end %>
<%= link_to_attachments @issue %>
<%= call_hook(:view_issues_show_description_bottom, :issue => @issue) %>
<% if authorize_for('issue_relations', 'new') || @issue.relations.any? %>
<hr />

View File

@@ -3,10 +3,10 @@
<option selected="selected"><%= l(:label_jump_to_a_project) %></option>
<option disabled="disabled">---</option>
<% user_projects_by_root.keys.sort.each do |root| %>
<%= content_tag('option', h(root.name), :value => url_for(:controller => 'projects', :action => 'show', :id => root)) %>
<%= content_tag('option', h(root.name), :value => url_for(:controller => 'projects', :action => 'show', :id => root, :jump => current_menu_item)) %>
<% user_projects_by_root[root].sort.each do |project| %>
<% next if project == root %>
<%= content_tag('option', ('&#187; ' + h(project.name)), :value => url_for(:controller => 'projects', :action => 'show', :id => project)) %>
<%= content_tag('option', ('&#187; ' + h(project.name)), :value => url_for(:controller => 'projects', :action => 'show', :id => project, :jump => current_menu_item)) %>
<% end %>
<% end %>
</select>

View File

@@ -47,19 +47,20 @@
<%= tag('div', {:id => 'main', :class => (has_content?(:sidebar) ? '' : 'nosidebar')}, true) %>
<div id="sidebar">
<%= yield :sidebar %>
<%= call_hook :view_layouts_base_sidebar %>
</div>
<div id="content">
<%= content_tag('div', flash[:error], :class => 'flash error') if flash[:error] %>
<%= content_tag('div', flash[:notice], :class => 'flash notice') if flash[:notice] %>
<%= render_flash_messages %>
<%= yield %>
<%= call_hook :view_layouts_base_content %>
</div>
</div>
<div id="ajax-indicator" style="display:none;"><span><%= l(:label_loading) %></span></div>
<div id="footer">
Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> &copy; 2006-2008 Jean-Philippe Lang
Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> &copy; 2006-2009 Jean-Philippe Lang
</div>
</div>
<%= call_hook :view_layouts_base_body_bottom %>

View File

@@ -0,0 +1,2 @@
<p><%= l(:notice_account_activated) %></p>
<p><%= l(:label_login) %>: <%= link_to @login_url, @login_url %></p>

View File

@@ -0,0 +1,2 @@
<%= l(:notice_account_activated) %>
<%= l(:label_login) %>: <%= @login_url %>

View File

@@ -6,17 +6,12 @@ body {
font-size: 0.8em;
color:#484848;
}
h1 {
font-family: "Trebuchet MS", Verdana, sans-serif;
font-size: 1.2em;
margin: 0px;
}
a, a:link, a:visited {
color: #2A5685;
}
a:hover, a:active {
color: #c61a1a;
}
h1, h2, h3 { font-family: "Trebuchet MS", Verdana, sans-serif; margin: 0px; }
h1 { font-size: 1.2em; }
h2, h3 { font-size: 1.1em; }
a, a:link, a:visited { color: #2A5685;}
a:hover, a:active { color: #c61a1a; }
a.wiki-anchor { display: none; }
hr {
width: 100%;
height: 1px;

View File

@@ -1,3 +1,3 @@
<%= yield %>
----------------------------------------
--
<%= Setting.emails_footer %>

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

@@ -15,7 +15,7 @@
<div class="wiki">
<%= textilizable(@topic.content, :attachments => @topic.attachments) %>
</div>
<%= link_to_attachments @topic.attachments, :no_author => true %>
<%= link_to_attachments @topic, :author => false %>
</div>
<br />
@@ -31,7 +31,7 @@
<div class="message reply">
<h4><%=h message.subject %> - <%= authoring message.created_on, message.author %></h4>
<div class="wiki"><%= textilizable message, :content, :attachments => message.attachments %></div>
<%= link_to_attachments message.attachments, :no_author => true %>
<%= link_to_attachments message, :author => false %>
</div>
<% end %>
<% end %>

View File

@@ -15,6 +15,7 @@
<p><%= f.text_field :lastname, :required => true %></p>
<p><%= f.text_field :mail, :required => true %></p>
<p><%= f.select :language, lang_options_for_select %></p>
<%= call_hook(:view_my_account, :user => @user, :form => f) %>
</div>
<%= submit_tag l(:button_save) %>

View File

@@ -47,6 +47,6 @@ entries_by_day = entries.group_by(&:spent_on)
</tr>
<% end -%>
<% end -%>
</tbdoy>
</tbody>
</table>
<% end %>

View File

@@ -11,7 +11,7 @@
<p><%= f.text_area :description, :rows => 5, :class => 'wiki-edit' %></p>
<p><%= f.text_field :identifier, :required => true, :disabled => @project.identifier_frozen? %>
<% unless @project.identifier_frozen? %>
<br /><em><%= l(:text_length_between, 3, 20) %> <%= l(:text_project_identifier_info) %></em>
<br /><em><%= l(:text_length_between, 1, 20) %> <%= l(:text_project_identifier_info) %></em>
<% end %></p>
<p><%= f.text_field :homepage, :size => 60 %></p>
<p><%= f.check_box :is_public %></p>

View File

@@ -46,7 +46,9 @@
<% form_tag({}, :method => :get) do %>
<h3><%= l(:label_activity) %></h3>
<p><% @activity.event_types.each do |t| %>
<label><%= check_box_tag "show_#{t}", 1, @activity.scope.include?(t) %> <%= l("label_#{t.singularize}_plural")%></label><br />
<%= check_box_tag "show_#{t}", 1, @activity.scope.include?(t) %>
<%= link_to(l("label_#{t.singularize}_plural"), {"show_#{t}" => 1, :user_id => params[:user_id]})%>
<br />
<% end %></p>
<% if @project && @project.active_children.any? %>
<p><label><%= check_box_tag 'with_subprojects', 1, @with_subprojects %> <%=l(:label_subproject_plural)%></label></p>

View File

@@ -4,10 +4,13 @@
<div class="box">
<% form_tag({ :action => 'add_file', :id => @project }, :multipart => true, :class => "tabular") do %>
<p><label for="version_id"><%=l(:field_version)%> <span class="required">*</span></label>
<%= select_tag "version_id", options_from_collection_for_select(@versions, "id", "name") %></p>
<% if @versions.any? %>
<p><label for="version_id"><%=l(:field_version)%></label>
<%= select_tag "version_id", content_tag('option', '') +
options_from_collection_for_select(@versions, "id", "name") %></p>
<% end %>
<p><label><%=l(:label_attachment_plural)%></label><%= render :partial => 'attachments/form' %></p>
</div>
<%= submit_tag l(:button_add) %>
<% end %>
<% end %>

View File

@@ -4,39 +4,41 @@
<h2><%=l(:label_attachment_plural)%></h2>
<% delete_allowed = authorize_for('versions', 'destroy_file') %>
<% delete_allowed = User.current.allowed_to?(:manage_files, @project) %>
<table class="list">
<table class="list files">
<thead><tr>
<th><%=l(:field_version)%></th>
<%= sort_header_tag('filename', :caption => l(:field_filename)) %>
<%= sort_header_tag('created_on', :caption => l(:label_date), :default_order => 'desc') %>
<%= sort_header_tag('size', :caption => l(:field_filesize), :default_order => 'desc') %>
<%= sort_header_tag('downloads', :caption => l(:label_downloads_abbr), :default_order => 'desc') %>
<th>MD5</th>
<% if delete_allowed %><th></th><% end %>
<th></th>
</tr></thead>
<tbody>
<% for version in @versions %>
<% unless version.attachments.empty? %>
<tr><th colspan="7" align="left"><span class="icon icon-package"><b><%= version.name %></b></span></th></tr>
<% for file in version.attachments %>
<tr class="<%= cycle("odd", "even") %>">
<td></td>
<td><%= link_to_attachment file, :download => true, :title => file.description %></td>
<td align="center"><%= format_time(file.created_on) %></td>
<td align="center"><%= number_to_human_size(file.filesize) %></td>
<td align="center"><%= file.downloads %></td>
<td align="center"><small><%= file.digest %></small></td>
<% if delete_allowed %>
<% @containers.each do |container| %>
<% next if container.attachments.empty? -%>
<% if container.is_a?(Version) -%>
<tr>
<th colspan="6" align="left">
<%= link_to(h(container), {:controller => 'versions', :action => 'show', :id => container}, :class => "icon icon-package") %>
</th>
</tr>
<% end -%>
<% container.attachments.each do |file| %>
<tr class="file <%= cycle("odd", "even") %>">
<td class="filename"><%= link_to_attachment file, :download => true, :title => file.description %></td>
<td class="created_on"><%= format_time(file.created_on) %></td>
<td class="filesize"><%= number_to_human_size(file.filesize) %></td>
<td class="downloads"><%= file.downloads %></td>
<td class="digest"><%= file.digest %></td>
<td align="center">
<%= link_to_if_authorized image_tag('delete.png'), {:controller => 'versions', :action => 'destroy_file', :id => version, :attachment_id => file}, :confirm => l(:text_are_you_sure), :method => :post %>
<%= link_to(image_tag('delete.png'), {:controller => 'attachments', :action => 'destroy', :id => file},
:confirm => l(:text_are_you_sure), :method => :post) if delete_allowed %>
</td>
<% end %>
</tr>
<% end
reset_cycle %>
<% end %>
<% end %>
</tbody>
</table>

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

@@ -17,7 +17,6 @@
<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 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>
</td>
</tr>
<% end; reset_cycle %>
</tbody>

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>
</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

@@ -0,0 +1,10 @@
<p>
<% if @repository.supports_cat? %>
<%= link_to_if action_name != 'entry', l(:button_view), {:action => 'entry', :id => @project, :path => to_path_param(@path), :rev => @rev } %> |
<% end %>
<% if @repository.supports_annotate? %>
<%= link_to_if action_name != 'annotate', l(:button_annotate), {:action => 'annotate', :id => @project, :path => to_path_param(@path), :rev => @rev } %> |
<% end %>
<%= link_to(l(:button_download), {:action => 'entry', :id => @project, :path => to_path_param(@path), :rev => @rev, :format => 'raw' }) if @repository.supports_cat? %>
<%= "(#{number_to_human_size(@entry.size)})" if @entry.size %>
</p>

View File

@@ -1,5 +1,7 @@
<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => 'file', :revision => @rev } %></h2>
<p><%= render :partial => 'link_to_functions' %></p>
<% colors = Hash.new {|k,v| k[v] = (k.size % 12) } %>
<div class="autoscroll">
@@ -9,7 +11,7 @@
<% syntax_highlight(@path, to_utf8(@annotate.content)).each_line do |line| %>
<% revision = @annotate.revisions[line_num-1] %>
<tr class="bloc-<%= revision.nil? ? 0 : colors[revision.identifier || revision.revision] %>">
<th class="line-num"><%= line_num %></th>
<th class="line-num" id="L<%= line_num %>"><a href="#L<%= line_num %>"><%= line_num %></a></th>
<td class="revision">
<%= (revision.identifier ? link_to(format_revision(revision.identifier), :action => 'revision', :id => @project, :rev => revision.identifier) : format_revision(revision.revision)) if revision %></td>
<td class="author"><%= h(revision.author.to_s.split('<').first) if revision %></td>

View File

@@ -1,15 +1,6 @@
<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => (@entry ? @entry.kind : nil), :revision => @rev } %></h2>
<p>
<% if @repository.supports_cat? %>
<%= link_to l(:button_view), {:action => 'entry', :id => @project, :path => to_path_param(@path), :rev => @rev } %> |
<% end %>
<% if @repository.supports_annotate? %>
<%= link_to l(:button_annotate), {:action => 'annotate', :id => @project, :path => to_path_param(@path), :rev => @rev } %> |
<% end %>
<%= link_to(l(:button_download), {:action => 'entry', :id => @project, :path => to_path_param(@path), :rev => @rev, :format => 'raw' }) if @repository.supports_cat? %>
<%= "(#{number_to_human_size(@entry.size)})" if @entry.size %>
</p>
<p><%= render :partial => 'link_to_functions' %></p>
<%= render_properties(@properties) %>

View File

@@ -1,5 +1,7 @@
<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => 'file', :revision => @rev } %></h2>
<p><%= render :partial => 'link_to_functions' %></p>
<%= render :partial => 'common/file', :locals => {:filename => @path, :content => @content} %>
<% content_for :header_tags do %>

View File

@@ -11,12 +11,12 @@
<%= text_field_tag 'settings[issues_export_limit]', Setting.issues_export_limit, :size => 6 %></p>
</div>
<fieldset class="box"><legend><%= l(:setting_issue_list_default_columns) %></legend>
<fieldset class="box settings"><legend><%= l(:setting_issue_list_default_columns) %></legend>
<%= hidden_field_tag 'settings[issue_list_default_columns][]', '' %>
<p><% Query.new.available_columns.each do |column| %>
<% Query.new.available_columns.each do |column| %>
<label><%= check_box_tag 'settings[issue_list_default_columns][]', column.name, Setting.issue_list_default_columns.include?(column.name.to_s) %>
<%= column.caption %></label>
<% end %></p>
<%= column.caption %></label><br />
<% end %>
</fieldset>
<%= submit_tag l(:button_save) %>

View File

@@ -7,7 +7,7 @@
<h2><%= l(:label_spent_time) %></h2>
<% 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 %>
<%= render :partial => 'date_range' %>
<% end %>

View File

@@ -10,7 +10,7 @@
<% @criterias.each do |criteria| %>
<%= hidden_field_tag 'criterias[]', criteria, :id => nil %>
<% end %>
<%= hidden_field_tag 'project_id', params[:project_id] %>
<%= hidden_field_tag('project_id', params[:project_id]) if @project %>
<%= render :partial => 'date_range' %>
<p><%= l(:label_details) %>: <%= select_tag 'columns', options_for_select([[l(:label_year), 'year'],

View File

@@ -13,6 +13,7 @@
<% end %>
<p><%= f.check_box :admin, :disabled => (@user == User.current) %></p>
<%= call_hook(:view_users_form, :user => @user, :form => f) %>
</div>
<div class="box">

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

@@ -5,7 +5,7 @@
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<style>
body { font:80% Verdana,Tahoma,Arial,sans-serif; }
h1, h2, h3, h4 { font-family: Trebuchet MS,Georgia,"Times New Roman",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; }

View File

@@ -5,7 +5,7 @@
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<style>
body { font:80% Verdana,Tahoma,Arial,sans-serif; }
h1, h2, h3, h4 { font-family: Trebuchet MS,Georgia,"Times New Roman",serif; }
h1, h2, h3, h4 { font-family: "Trebuchet MS",Georgia,"Times New Roman",serif; }
</style>
</head>
<body>

View File

@@ -28,9 +28,10 @@
<%= render(:partial => "wiki/content", :locals => {:content => @content}) %>
<%= link_to_attachments @page.attachments, :delete_url => ((@editable && authorize_for('wiki', 'destroy_attachment')) ? {:controller => 'wiki', :action => 'destroy_attachment', :page => @page.title} : nil) %>
<%= link_to_attachments @page %>
<% 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;",
: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 %>
@@ -40,6 +41,7 @@
<%= submit_tag l(:button_add) %>
<%= link_to l(:button_cancel), {}, :onclick => "Element.hide('add_attachment_form'); Element.show('attach_files_link'); return false;" %>
<% end %>
</div>
<% end %>
<p class="other-formats">

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

@@ -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

@@ -2,9 +2,10 @@ class SetTopicAuthorsAsWatchers < ActiveRecord::Migration
def self.up
# Sets active users who created/replied a topic as watchers of the topic
# so that the new watch functionality at topic level doesn't affect notifications behaviour
Message.connection.execute("INSERT INTO watchers (watchable_type, watchable_id, user_id)" +
" SELECT DISTINCT 'Message', COALESCE(messages.parent_id, messages.id), messages.author_id FROM messages, users" +
" WHERE messages.author_id = users.id AND users.status = 1")
Message.connection.execute("INSERT INTO #{Watcher.table_name} (watchable_type, watchable_id, user_id)" +
" SELECT DISTINCT 'Message', COALESCE(m.parent_id, m.id), m.author_id" +
" FROM #{Message.table_name} m, #{User.table_name} u" +
" WHERE m.author_id = u.id AND u.status = 1")
end
def self.down

View File

@@ -1,10 +1,105 @@
== Redmine changelog
Redmine - project management software
Copyright (C) 2006-2008 Jean-Philippe Lang
Copyright (C) 2006-2009 Jean-Philippe Lang
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
* Send an email to the user when an administrator activates a registered user
* Strip keywords from received email body
* Footer updated to 2009
* Show RSS-link even when no issues is found
* One click filter action in activity view
* Clickable/linkable line #'s while browsing the repo or viewing a file
* Links to versions on files list
* Added request and controller objects to the hooks by default
* Fixed: exporting an issue with attachments to PDF raises an error
* Fixed: "too few arguments" error may occur on activerecord error translation
* Fixed: "Default columns Displayed on the Issues list" setting is not easy to read
* Fixed: visited links to closed tickets are not striked through with IE6
* Fixed: MailHandler#plain_text_body returns nil if there was nothing to strip
* Fixed: MailHandler raises an error when processing an email without From header
== 2009-02-15 v0.8.1
* Select watchers on new issue form
* Issue description is no longer a required field
* Files module: ability to add files without version
* Jump to the current tab when using the project quick-jump combo
* Display a warning if some attachments were not saved
* Import custom fields values from emails on issue creation
* Show view/annotate/download links on entry and annotate views
* Admin Info Screen: Display if plugin assets directory is writable
* Adds a 'Create and continue' button on the new issue form
* IMAP: add options to move received emails
* Do not show Category field when categories are not defined
* Lower the project identifier limit to a minimum of two characters
* Add "closed" html class to closed entries in issue list
* Fixed: broken redirect URL on login failure
* Fixed: Deleted files are shown when using Darcs
* Fixed: Darcs adapter works on Win32 only
* Fixed: syntax highlight doesn't appear in new ticket preview
* Fixed: email notification for changes I make still occurs when running Repository.fetch_changesets
* Fixed: no error is raised when entering invalid hours on the issue update form
* Fixed: Details time log report CSV export doesn't honour date format from settings
* Fixed: invalid css classes on issue details
* Fixed: Trac importer creates duplicate custom values
* Fixed: inline attached image should not match partial filename
== 2008-12-30 v0.8.0
* Setting added in order to limit the number of diff lines that should be displayed

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