Compare commits
302 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
908dcc4eb4 | ||
|
|
20bdba13cf | ||
|
|
46c0d72782 | ||
|
|
36b5d4f6af | ||
|
|
b366375e7e | ||
|
|
629c106a46 | ||
|
|
f19ae06a72 | ||
|
|
69b99aa518 | ||
|
|
cb5d0c469d | ||
|
|
57d10ed893 | ||
|
|
28e4ff8957 | ||
|
|
3d1bd79ffb | ||
|
|
7f957653ad | ||
|
|
9a9141041e | ||
|
|
bc4249e3d3 | ||
|
|
fae04f3ae1 | ||
|
|
b13ef64794 | ||
|
|
683e1c5d73 | ||
|
|
1c1755d278 | ||
|
|
5e6ff86f47 | ||
|
|
764393aa6a | ||
|
|
3155b8ccad | ||
|
|
567c8ed9b0 | ||
|
|
49b6f9e4dd | ||
|
|
c9d4d3a2be | ||
|
|
31178553f3 | ||
|
|
dea10c54f9 | ||
|
|
6abd32be9e | ||
|
|
56318aab53 | ||
|
|
babb14dd94 | ||
|
|
3b90141543 | ||
|
|
bce764d9bc | ||
|
|
b65d546245 | ||
|
|
9f12151ac0 | ||
|
|
78cac6f02b | ||
|
|
16b85bda32 | ||
|
|
733987fbb6 | ||
|
|
7389b4bed0 | ||
|
|
4570fcb7a2 | ||
|
|
0a709660d2 | ||
|
|
633e026e44 | ||
|
|
618ab6004e | ||
|
|
644f03b834 | ||
|
|
1154141ee7 | ||
|
|
bf42d9b706 | ||
|
|
e2050d61fd | ||
|
|
1dfe63093b | ||
|
|
472ec31662 | ||
|
|
e66945cbd8 | ||
|
|
8a0a25fceb | ||
|
|
cf77c93cb4 | ||
|
|
956437b7fe | ||
|
|
a464d26e73 | ||
|
|
ea77929d62 | ||
|
|
996b2a5c1d | ||
|
|
50794b08a9 | ||
|
|
1b4f0c38ce | ||
|
|
5f9b1b0e0b | ||
|
|
93c04f2666 | ||
|
|
218e1bb267 | ||
|
|
1ce2ed065a | ||
|
|
d3386a5af7 | ||
|
|
5330ca9b8c | ||
|
|
84247e7272 | ||
|
|
e7b6a56a97 | ||
|
|
9572699301 | ||
|
|
84e70634fb | ||
|
|
94899e0339 | ||
|
|
abacc2754e | ||
|
|
2fa8030afc | ||
|
|
0b2299c7d1 | ||
|
|
5f3f2e756b | ||
|
|
dfc7a94578 | ||
|
|
0399b85ab6 | ||
|
|
9e5edfb728 | ||
|
|
b8b473244c | ||
|
|
6e777b7453 | ||
|
|
fce4615f10 | ||
|
|
b5fcea9e74 | ||
|
|
5014b23c2a | ||
|
|
a6b6dc60f4 | ||
|
|
9b1efcfaab | ||
|
|
546b98a118 | ||
|
|
7a441c1bc4 | ||
|
|
c54f15e35f | ||
|
|
db37e578f2 | ||
|
|
9e1192a54d | ||
|
|
06266c8fec | ||
|
|
af7e586b06 | ||
|
|
0b94f4afa9 | ||
|
|
5a51412e6a | ||
|
|
752d574b49 | ||
|
|
a303b5e2d5 | ||
|
|
e61916ea91 | ||
|
|
86504acb4c | ||
|
|
3fdc273252 | ||
|
|
9882217847 | ||
|
|
5362a85f2b | ||
|
|
f9ce4ff559 | ||
|
|
fefc6e6bec | ||
|
|
97252c26ee | ||
|
|
94b5bbcb5d | ||
|
|
9c09fd20fb | ||
|
|
79b1347ba5 | ||
|
|
0cef17ce44 | ||
|
|
d1b1f5977a | ||
|
|
33274b8d48 | ||
|
|
7c1d8ac00b | ||
|
|
243027e63a | ||
|
|
9d810f0059 | ||
|
|
eaedf9ed52 | ||
|
|
c894f74c5e | ||
|
|
8d8315d8f6 | ||
|
|
7f63f9a931 | ||
|
|
27af58ef1a | ||
|
|
fb4bab748f | ||
|
|
d771847078 | ||
|
|
3f4defe482 | ||
|
|
737bbb3d3d | ||
|
|
7a05f8ed66 | ||
|
|
cbacc71dff | ||
|
|
ad97165f1b | ||
|
|
8be1597ad0 | ||
|
|
1169ff05af | ||
|
|
876a9b6ffb | ||
|
|
d545ca7360 | ||
|
|
1802fa9348 | ||
|
|
29bad1dc95 | ||
|
|
6a8be88ad6 | ||
|
|
e8d0c26e43 | ||
|
|
4ed95ffff6 | ||
|
|
6f68a36679 | ||
|
|
79c33bbc83 | ||
|
|
f6b2be81b9 | ||
|
|
486ecc6661 | ||
|
|
5166213fd3 | ||
|
|
57a4ee318a | ||
|
|
d63746ab3e | ||
|
|
7619c286cc | ||
|
|
6157d20ec0 | ||
|
|
858eba2f1c | ||
|
|
365d92ebf4 | ||
|
|
11b9ceb054 | ||
|
|
9352bb527b | ||
|
|
47475acdaf | ||
|
|
9ae6e60c26 | ||
|
|
077723c90a | ||
|
|
81eee10d5b | ||
|
|
63c72fe87f | ||
|
|
006337fb0e | ||
|
|
4581baa5c0 | ||
|
|
203cf6333e | ||
|
|
b7c1a5c0d5 | ||
|
|
0ecca9d8f1 | ||
|
|
c58298835d | ||
|
|
42452a535f | ||
|
|
d32b9b92cf | ||
|
|
de31cd9ef6 | ||
|
|
a9295a7740 | ||
|
|
8b3a8ac1b4 | ||
|
|
b1cb9cd3bd | ||
|
|
76d33e1fb7 | ||
|
|
0dc6a41832 | ||
|
|
34b86628a0 | ||
|
|
66b9a76c7e | ||
|
|
bbc43e71ee | ||
|
|
ba20a67873 | ||
|
|
048fa5bd77 | ||
|
|
3bc2a5cf2f | ||
|
|
41dba2db86 | ||
|
|
e85a9f8609 | ||
|
|
b84dd8ec83 | ||
|
|
01fd8e685b | ||
|
|
1a6595aa17 | ||
|
|
9373f429d0 | ||
|
|
b3442a7d39 | ||
|
|
ed314caf7d | ||
|
|
1399f3dd12 | ||
|
|
7c99f8ad11 | ||
|
|
67159c12c5 | ||
|
|
0f1787ea0d | ||
|
|
6cd4f0c07e | ||
|
|
d143019dbb | ||
|
|
a3b9a5aa5f | ||
|
|
9b624fd1d6 | ||
|
|
8236563d6b | ||
|
|
5f9f6ea2e1 | ||
|
|
738fc579f0 | ||
|
|
0316fde06b | ||
|
|
bcde2f4dce | ||
|
|
48ae6f38e1 | ||
|
|
16eda4c5c9 | ||
|
|
b4101c8b65 | ||
|
|
d967507dcb | ||
|
|
d1b5bc1e82 | ||
|
|
6c54b0ba3b | ||
|
|
bdbc7b337f | ||
|
|
ff449b197f | ||
|
|
a0c29d8f99 | ||
|
|
58fbf5e66f | ||
|
|
ccbe48d779 | ||
|
|
64e67f754d | ||
|
|
6810972762 | ||
|
|
e160a3eec1 | ||
|
|
f16aae74dc | ||
|
|
bc77cc2c5c | ||
|
|
a4b07a36ed | ||
|
|
1894bef48a | ||
|
|
2ed9aa13f3 | ||
|
|
52e422da61 | ||
|
|
8b0016d71b | ||
|
|
df761e01fa | ||
|
|
07962e2840 | ||
|
|
7fe7677cde | ||
|
|
c4419e268f | ||
|
|
cf1ad65cd2 | ||
|
|
193ed689fb | ||
|
|
f2f54a711d | ||
|
|
0092a54e3b | ||
|
|
1ffa468240 | ||
|
|
ae0ed7fd05 | ||
|
|
b0e62e37ae | ||
|
|
3544d6398a | ||
|
|
5083e92772 | ||
|
|
a37af7a226 | ||
|
|
85711f1d54 | ||
|
|
050d0d046a | ||
|
|
925ef8f4f0 | ||
|
|
ad06bec6b6 | ||
|
|
433f2a043e | ||
|
|
394fc9c109 | ||
|
|
21979cf68a | ||
|
|
0177f732a2 | ||
|
|
a3cf5dcded | ||
|
|
8bc89b6e73 | ||
|
|
2e7e26fbb4 | ||
|
|
9131cdf6b9 | ||
|
|
fbf2646426 | ||
|
|
edf6757a75 | ||
|
|
8cb75be0db | ||
|
|
b09a18972d | ||
|
|
8a356baf3e | ||
|
|
e1c4659752 | ||
|
|
72c9884dfc | ||
|
|
7afdb01f00 | ||
|
|
1d542a5113 | ||
|
|
199428804f | ||
|
|
6854827f1a | ||
|
|
5452fd2f5a | ||
|
|
701e00d865 | ||
|
|
4e36bd2ce1 | ||
|
|
124ea898d5 | ||
|
|
07b69fd7fc | ||
|
|
952ff4184e | ||
|
|
5c449f844e | ||
|
|
16e09bfd77 | ||
|
|
9e7bce6a94 | ||
|
|
710ac5344d | ||
|
|
36aeeb99d9 | ||
|
|
713ce06e40 | ||
|
|
fcbd2b5228 | ||
|
|
14b4afeec9 | ||
|
|
3520961eae | ||
|
|
0961cdab31 | ||
|
|
a987649b1a | ||
|
|
9afaf26d66 | ||
|
|
a07a6d4aa4 | ||
|
|
25ed19cb35 | ||
|
|
1f862dbadb | ||
|
|
3ef92341fd | ||
|
|
225b6f4dc9 | ||
|
|
094520ab20 | ||
|
|
90dc93b81c | ||
|
|
323a26030b | ||
|
|
653d2aacbb | ||
|
|
f6b05ea3fb | ||
|
|
3041041a62 | ||
|
|
691b09fd72 | ||
|
|
f30a369d35 | ||
|
|
487ba400fc | ||
|
|
2e0cd9f196 | ||
|
|
59c961425e | ||
|
|
570d7e494e | ||
|
|
1f1fd9e034 | ||
|
|
35bb9307dc | ||
|
|
47679629f4 | ||
|
|
06f4b371ee | ||
|
|
c3040104ff | ||
|
|
455abea320 | ||
|
|
cc643ce932 | ||
|
|
6ef6459630 | ||
|
|
827e14eafe | ||
|
|
8fac9233de | ||
|
|
7385722441 | ||
|
|
b73141a153 | ||
|
|
8d6b32645c | ||
|
|
a592d6c40d | ||
|
|
fb151463eb | ||
|
|
586f4e3831 | ||
|
|
2986afc05e | ||
|
|
a8e3ddf382 | ||
|
|
de7d367820 |
@@ -1,5 +1,5 @@
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2009 Jean-Philippe Lang
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006-2007 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
|
||||
@@ -15,19 +15,19 @@
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module Redmine
|
||||
module Views
|
||||
class OtherFormatsBuilder
|
||||
def initialize(view)
|
||||
@view = view
|
||||
end
|
||||
|
||||
def link_to(name, options={})
|
||||
url = { :format => name.to_s.downcase }.merge(options.delete(:url) || {})
|
||||
caption = options.delete(:caption) || name
|
||||
html_options = { :class => name.to_s.downcase, :rel => 'nofollow' }.merge(options)
|
||||
@view.content_tag('span', @view.link_to(caption, url, html_options))
|
||||
end
|
||||
end
|
||||
end
|
||||
class AWSProjectWithRepository < ActionWebService::Struct
|
||||
member :id, :int
|
||||
member :identifier, :string
|
||||
member :name, :string
|
||||
member :is_public, :bool
|
||||
member :repository, Repository
|
||||
end
|
||||
|
||||
class SysApi < ActionWebService::API::Base
|
||||
api_method :projects_with_repository_enabled,
|
||||
:expects => [],
|
||||
:returns => [[AWSProjectWithRepository]]
|
||||
api_method :repository_created,
|
||||
:expects => [:string, :string, :string],
|
||||
:returns => [:int]
|
||||
end
|
||||
@@ -46,10 +46,24 @@ class AccountController < ApplicationController
|
||||
self.logged_user = nil
|
||||
else
|
||||
# Authenticate user
|
||||
if Setting.openid? && using_open_id?
|
||||
open_id_authenticate(params[:openid_url])
|
||||
user = User.try_to_login(params[:username], params[:password])
|
||||
if user.nil?
|
||||
# Invalid credentials
|
||||
flash.now[:error] = l(:notice_account_invalid_creditentials)
|
||||
elsif user.new_record?
|
||||
# Onthefly creation failed, display the registration form to fill/fix attributes
|
||||
@user = user
|
||||
session[:auth_source_registration] = {:login => user.login, :auth_source_id => user.auth_source_id }
|
||||
render :action => 'register'
|
||||
else
|
||||
password_authentication
|
||||
# Valid user
|
||||
self.logged_user = user
|
||||
# generate a key and set cookie if autologin
|
||||
if params[:autologin] && Setting.autologin?
|
||||
token = Token.create(:user => user, :action => 'autologin')
|
||||
cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now }
|
||||
end
|
||||
redirect_back_or_default :controller => 'my', :action => 'page'
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -122,14 +136,31 @@ class AccountController < ApplicationController
|
||||
else
|
||||
@user.login = params[:user][:login]
|
||||
@user.password, @user.password_confirmation = params[:password], params[:password_confirmation]
|
||||
|
||||
case Setting.self_registration
|
||||
when '1'
|
||||
register_by_email_activation(@user)
|
||||
# Email activation
|
||||
token = Token.new(:user => @user, :action => "register")
|
||||
if @user.save and token.save
|
||||
Mailer.deliver_register(token)
|
||||
flash[:notice] = l(:notice_account_register_done)
|
||||
redirect_to :action => 'login'
|
||||
end
|
||||
when '3'
|
||||
register_automatically(@user)
|
||||
# Automatic activation
|
||||
@user.status = User::STATUS_ACTIVE
|
||||
if @user.save
|
||||
self.logged_user = @user
|
||||
flash[:notice] = l(:notice_account_activated)
|
||||
redirect_to :controller => 'my', :action => 'account'
|
||||
end
|
||||
else
|
||||
register_manually_by_administrator(@user)
|
||||
# Manual activation by the administrator
|
||||
if @user.save
|
||||
# Sends an email to the administrators
|
||||
Mailer.deliver_account_activation_request(@user)
|
||||
flash[:notice] = l(:notice_account_pending)
|
||||
redirect_to :action => 'login'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -160,119 +191,4 @@ private
|
||||
session[:user_id] = nil
|
||||
end
|
||||
end
|
||||
|
||||
def password_authentication
|
||||
user = User.try_to_login(params[:username], params[:password])
|
||||
if user.nil?
|
||||
# Invalid credentials
|
||||
flash.now[:error] = l(:notice_account_invalid_creditentials)
|
||||
elsif user.new_record?
|
||||
# Onthefly creation failed, display the registration form to fill/fix attributes
|
||||
@user = user
|
||||
session[:auth_source_registration] = {:login => user.login, :auth_source_id => user.auth_source_id }
|
||||
render :action => 'register'
|
||||
else
|
||||
# Valid user
|
||||
successful_authentication(user)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def open_id_authenticate(openid_url)
|
||||
authenticate_with_open_id(openid_url, :required => [:nickname, :fullname, :email], :return_to => signin_url) do |result, identity_url, registration|
|
||||
if result.successful?
|
||||
user = User.find_or_initialize_by_identity_url(identity_url)
|
||||
if user.new_record?
|
||||
# Self-registration off
|
||||
redirect_to(home_url) && return unless Setting.self_registration?
|
||||
|
||||
# Create on the fly
|
||||
user.login = registration['nickname'] unless registration['nickname'].nil?
|
||||
user.mail = registration['email'] unless registration['email'].nil?
|
||||
user.firstname, user.lastname = registration['fullname'].split(' ') unless registration['fullname'].nil?
|
||||
user.random_password
|
||||
user.status = User::STATUS_REGISTERED
|
||||
|
||||
case Setting.self_registration
|
||||
when '1'
|
||||
register_by_email_activation(user) do
|
||||
onthefly_creation_failed(user, {:login => user.login, :identity_url => identity_url })
|
||||
end
|
||||
when '3'
|
||||
register_automatically(user) do
|
||||
onthefly_creation_failed(user, {:login => user.login, :identity_url => identity_url })
|
||||
end
|
||||
else
|
||||
register_manually_by_administrator(user) do
|
||||
onthefly_creation_failed(user, {:login => user.login, :identity_url => identity_url })
|
||||
end
|
||||
end
|
||||
else
|
||||
# Existing record
|
||||
successful_authentication(user)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def successful_authentication(user)
|
||||
# Valid user
|
||||
self.logged_user = user
|
||||
# generate a key and set cookie if autologin
|
||||
if params[:autologin] && Setting.autologin?
|
||||
token = Token.create(:user => user, :action => 'autologin')
|
||||
cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now }
|
||||
end
|
||||
redirect_back_or_default :controller => 'my', :action => 'page'
|
||||
end
|
||||
|
||||
# Onthefly creation failed, display the registration form to fill/fix attributes
|
||||
def onthefly_creation_failed(user, auth_source_options = { })
|
||||
@user = user
|
||||
session[:auth_source_registration] = auth_source_options unless auth_source_options.empty?
|
||||
render :action => 'register'
|
||||
end
|
||||
|
||||
# Register a user for email activation.
|
||||
#
|
||||
# Pass a block for behavior when a user fails to save
|
||||
def register_by_email_activation(user, &block)
|
||||
token = Token.new(:user => user, :action => "register")
|
||||
if user.save and token.save
|
||||
Mailer.deliver_register(token)
|
||||
flash[:notice] = l(:notice_account_register_done)
|
||||
redirect_to :action => 'login'
|
||||
else
|
||||
yield if block_given?
|
||||
end
|
||||
end
|
||||
|
||||
# Automatically register a user
|
||||
#
|
||||
# Pass a block for behavior when a user fails to save
|
||||
def register_automatically(user, &block)
|
||||
# Automatic activation
|
||||
user.status = User::STATUS_ACTIVE
|
||||
if user.save
|
||||
self.logged_user = user
|
||||
flash[:notice] = l(:notice_account_activated)
|
||||
redirect_to :controller => 'my', :action => 'account'
|
||||
else
|
||||
yield if block_given?
|
||||
end
|
||||
end
|
||||
|
||||
# Manual activation by the administrator
|
||||
#
|
||||
# Pass a block for behavior when a user fails to save
|
||||
def register_manually_by_administrator(user, &block)
|
||||
if user.save
|
||||
# Sends an email to the administrators
|
||||
Mailer.deliver_account_activation_request(user)
|
||||
flash[:notice] = l(:notice_account_pending)
|
||||
redirect_to :action => 'login'
|
||||
else
|
||||
yield if block_given?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -26,6 +26,9 @@ class AdminController < ApplicationController
|
||||
end
|
||||
|
||||
def projects
|
||||
sort_init 'name', 'asc'
|
||||
sort_update %w(name is_public created_on)
|
||||
|
||||
@status = params[:status] ? params[:status].to_i : 1
|
||||
c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
|
||||
|
||||
@@ -34,8 +37,14 @@ class AdminController < ApplicationController
|
||||
c << ["LOWER(identifier) LIKE ? OR LOWER(name) LIKE ?", name, name]
|
||||
end
|
||||
|
||||
@projects = Project.find :all, :order => 'lft',
|
||||
:conditions => c.conditions
|
||||
@project_count = Project.count(:conditions => c.conditions)
|
||||
@project_pages = Paginator.new self, @project_count,
|
||||
per_page_option,
|
||||
params['page']
|
||||
@projects = Project.find :all, :order => sort_clause,
|
||||
:conditions => c.conditions,
|
||||
:limit => @project_pages.items_per_page,
|
||||
:offset => @project_pages.current.offset
|
||||
|
||||
render :action => "projects", :layout => false if request.xhr?
|
||||
end
|
||||
|
||||
@@ -29,7 +29,7 @@ class AttachmentsController < ApplicationController
|
||||
elsif @attachment.is_text?
|
||||
@content = File.new(@attachment.diskfile, "rb").read
|
||||
render :action => 'file'
|
||||
else
|
||||
elsif
|
||||
download
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2009 Jean-Philippe Lang
|
||||
# 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
|
||||
@@ -19,22 +19,34 @@ class CustomFieldsController < ApplicationController
|
||||
before_filter :require_admin
|
||||
|
||||
def index
|
||||
list
|
||||
render :action => 'list' unless request.xhr?
|
||||
end
|
||||
|
||||
def list
|
||||
@custom_fields_by_type = CustomField.find(:all).group_by {|f| f.class.name }
|
||||
@tab = params[:tab] || 'IssueCustomField'
|
||||
render :action => "list", :layout => false if request.xhr?
|
||||
end
|
||||
|
||||
def new
|
||||
@custom_field = begin
|
||||
if params[:type].to_s.match(/.+CustomField$/)
|
||||
params[:type].to_s.constantize.new(params[:custom_field])
|
||||
end
|
||||
rescue
|
||||
end
|
||||
redirect_to(:action => 'index') and return unless @custom_field.is_a?(CustomField)
|
||||
|
||||
case params[:type]
|
||||
when "IssueCustomField"
|
||||
@custom_field = IssueCustomField.new(params[:custom_field])
|
||||
@custom_field.trackers = Tracker.find(params[:tracker_ids]) if params[:tracker_ids]
|
||||
when "UserCustomField"
|
||||
@custom_field = UserCustomField.new(params[:custom_field])
|
||||
when "ProjectCustomField"
|
||||
@custom_field = ProjectCustomField.new(params[:custom_field])
|
||||
when "TimeEntryCustomField"
|
||||
@custom_field = TimeEntryCustomField.new(params[:custom_field])
|
||||
else
|
||||
redirect_to :action => 'list'
|
||||
return
|
||||
end
|
||||
if request.post? and @custom_field.save
|
||||
flash[:notice] = l(:notice_successful_create)
|
||||
redirect_to :action => 'index', :tab => @custom_field.class.name
|
||||
redirect_to :action => 'list', :tab => @custom_field.class.name
|
||||
end
|
||||
@trackers = Tracker.find(:all, :order => 'position')
|
||||
end
|
||||
@@ -42,8 +54,11 @@ class CustomFieldsController < ApplicationController
|
||||
def edit
|
||||
@custom_field = CustomField.find(params[:id])
|
||||
if request.post? and @custom_field.update_attributes(params[:custom_field])
|
||||
if @custom_field.is_a? IssueCustomField
|
||||
@custom_field.trackers = params[:tracker_ids] ? Tracker.find(params[:tracker_ids]) : []
|
||||
end
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
redirect_to :action => 'index', :tab => @custom_field.class.name
|
||||
redirect_to :action => 'list', :tab => @custom_field.class.name
|
||||
end
|
||||
@trackers = Tracker.find(:all, :order => 'position')
|
||||
end
|
||||
@@ -60,14 +75,14 @@ class CustomFieldsController < ApplicationController
|
||||
when 'lowest'
|
||||
@custom_field.move_to_bottom
|
||||
end if params[:position]
|
||||
redirect_to :action => 'index', :tab => @custom_field.class.name
|
||||
redirect_to :action => 'list', :tab => @custom_field.class.name
|
||||
end
|
||||
|
||||
def destroy
|
||||
@custom_field = CustomField.find(params[:id]).destroy
|
||||
redirect_to :action => 'index', :tab => @custom_field.class.name
|
||||
redirect_to :action => 'list', :tab => @custom_field.class.name
|
||||
rescue
|
||||
flash[:error] = "Unable to delete custom field"
|
||||
redirect_to :action => 'index'
|
||||
redirect_to :action => 'list'
|
||||
end
|
||||
end
|
||||
|
||||
@@ -21,9 +21,6 @@ class IssueRelationsController < ApplicationController
|
||||
def new
|
||||
@relation = IssueRelation.new(params[:relation])
|
||||
@relation.issue_from = @issue
|
||||
if params[:relation] && !params[:relation][:issue_to_id].blank?
|
||||
@relation.issue_to = Issue.visible.find_by_id(params[:relation][:issue_to_id])
|
||||
end
|
||||
@relation.save if request.post?
|
||||
respond_to do |format|
|
||||
format.html { redirect_to :controller => 'issues', :action => 'show', :id => @issue }
|
||||
|
||||
@@ -147,7 +147,6 @@ class IssuesController < ApplicationController
|
||||
attach_files(@issue, params[:attachments])
|
||||
flash[:notice] = l(:notice_successful_create)
|
||||
Mailer.deliver_issue_add(@issue) if Setting.notified_events.include?('issue_added')
|
||||
call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
|
||||
redirect_to(params[:continue] ? { :action => 'new', :tracker_id => @issue.tracker } :
|
||||
{ :action => 'show', :id => @issue })
|
||||
return
|
||||
@@ -195,7 +194,6 @@ class IssuesController < ApplicationController
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
|
||||
end
|
||||
call_hook(:controller_issues_edit_after_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => journal})
|
||||
redirect_to(params[:back_to] || {:action => 'show', :id => @issue})
|
||||
end
|
||||
end
|
||||
@@ -232,7 +230,6 @@ class IssuesController < ApplicationController
|
||||
assigned_to = (params[:assigned_to_id].blank? || params[:assigned_to_id] == 'none') ? nil : User.find_by_id(params[:assigned_to_id])
|
||||
category = (params[:category_id].blank? || params[:category_id] == 'none') ? nil : @project.issue_categories.find_by_id(params[:category_id])
|
||||
fixed_version = (params[:fixed_version_id].blank? || params[:fixed_version_id] == 'none') ? nil : @project.versions.find_by_id(params[:fixed_version_id])
|
||||
custom_field_values = params[:custom_field_values] ? params[:custom_field_values].reject {|k,v| v.blank?} : nil
|
||||
|
||||
unsaved_issue_ids = []
|
||||
@issues.each do |issue|
|
||||
@@ -244,7 +241,6 @@ class IssuesController < ApplicationController
|
||||
issue.start_date = params[:start_date] unless params[:start_date].blank?
|
||||
issue.due_date = params[:due_date] unless params[:due_date].blank?
|
||||
issue.done_ratio = params[:done_ratio] unless params[:done_ratio].blank?
|
||||
issue.custom_field_values = custom_field_values if custom_field_values && !custom_field_values.empty?
|
||||
call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
|
||||
# Don't save any change to the issue if the user is not authorized to apply the requested status
|
||||
if (status.nil? || (issue.status.new_status_allowed_to?(status, current_role, issue.tracker) && issue.status = status)) && issue.save
|
||||
@@ -266,7 +262,6 @@ class IssuesController < ApplicationController
|
||||
# Find potential statuses the user could be allowed to switch issues to
|
||||
@available_statuses = Workflow.find(:all, :include => :new_status,
|
||||
:conditions => {:role_id => current_role.id}).collect(&:new_status).compact.uniq.sort
|
||||
@custom_fields = @project.issue_custom_fields.select {|f| f.field_format == 'list'}
|
||||
end
|
||||
|
||||
def move
|
||||
@@ -274,7 +269,7 @@ class IssuesController < ApplicationController
|
||||
# find projects to which the user is allowed to move the issue
|
||||
if User.current.admin?
|
||||
# admin is allowed to move issues to any active (visible) project
|
||||
@allowed_projects = Project.find(:all, :conditions => Project.visible_by(User.current))
|
||||
@allowed_projects = Project.find(:all, :conditions => Project.visible_by(User.current), :order => 'name')
|
||||
else
|
||||
User.current.memberships.each {|m| @allowed_projects << m.project if m.role.allowed_to?(:move_issues)}
|
||||
end
|
||||
@@ -286,7 +281,7 @@ class IssuesController < ApplicationController
|
||||
unsaved_issue_ids = []
|
||||
@issues.each do |issue|
|
||||
issue.init_journal(User.current)
|
||||
unsaved_issue_ids << issue.id unless issue.move_to(@target_project, new_tracker, params[:copy_options])
|
||||
unsaved_issue_ids << issue.id unless issue.move_to(@target_project, new_tracker)
|
||||
end
|
||||
if unsaved_issue_ids.empty?
|
||||
flash[:notice] = l(:notice_successful_update) unless @issues.empty?
|
||||
@@ -350,7 +345,7 @@ class IssuesController < ApplicationController
|
||||
|
||||
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.nil? ? '' : "#{@project.identifier}-" }gantt.png") } if @gantt.respond_to?('to_image')
|
||||
format.png { send_data(@gantt.to_image, :disposition => 'inline', :type => 'image/png', :filename => "#{@project.identifier}-gantt.png") } if @gantt.respond_to?('to_image')
|
||||
format.pdf { send_data(gantt_to_pdf(@gantt, @project), :type => 'application/pdf', :filename => "#{@project.nil? ? '' : "#{@project.identifier}-" }gantt.pdf") }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -19,7 +19,6 @@ class MyController < ApplicationController
|
||||
before_filter :require_login
|
||||
|
||||
helper :issues
|
||||
helper :custom_fields
|
||||
|
||||
BLOCKS = { 'issuesassignedtome' => :label_assigned_to_me_issues,
|
||||
'issuesreportedbyme' => :label_reported_issues,
|
||||
|
||||
@@ -29,12 +29,6 @@ class ProjectsController < ApplicationController
|
||||
before_filter :require_admin, :only => [ :add, :archive, :unarchive, :destroy ]
|
||||
accept_key_auth :activity
|
||||
|
||||
after_filter :only => [:add, :edit, :archive, :unarchive, :destroy] do |controller|
|
||||
if controller.request.post?
|
||||
controller.send :expire_action, :controller => 'welcome', :action => 'robots.txt'
|
||||
end
|
||||
end
|
||||
|
||||
helper :sort
|
||||
include SortHelper
|
||||
helper :custom_fields
|
||||
@@ -49,14 +43,17 @@ class ProjectsController < ApplicationController
|
||||
|
||||
# Lists visible projects
|
||||
def index
|
||||
projects = Project.find :all,
|
||||
:conditions => Project.visible_by(User.current),
|
||||
:include => :parent
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
@projects = Project.visible.find(:all, :order => 'lft')
|
||||
@project_tree = projects.group_by {|p| p.parent || p}
|
||||
@project_tree.keys.each {|p| @project_tree[p] -= [p]}
|
||||
}
|
||||
format.atom {
|
||||
projects = Project.visible.find(:all, :order => 'created_on DESC',
|
||||
:limit => Setting.feeds_limit.to_i)
|
||||
render_feed(projects, :title => "#{Setting.app_title}: #{l(:label_project_latest)}")
|
||||
render_feed(projects.sort_by(&:created_on).reverse.slice(0, Setting.feeds_limit.to_i),
|
||||
:title => "#{Setting.app_title}: #{l(:label_project_latest)}")
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -65,6 +62,9 @@ class ProjectsController < ApplicationController
|
||||
def add
|
||||
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
|
||||
@trackers = Tracker.all
|
||||
@root_projects = Project.find(:all,
|
||||
:conditions => "parent_id IS NULL AND status = #{Project::STATUS_ACTIVE}",
|
||||
:order => 'name')
|
||||
@project = Project.new(params[:project])
|
||||
if request.get?
|
||||
@project.identifier = Project.next_identifier if Setting.sequential_project_identifiers?
|
||||
@@ -74,7 +74,6 @@ class ProjectsController < ApplicationController
|
||||
else
|
||||
@project.enabled_module_names = params[:enabled_modules]
|
||||
if @project.save
|
||||
@project.set_parent!(params[:project]['parent_id']) if User.current.admin? && params[:project].has_key?('parent_id')
|
||||
flash[:notice] = l(:notice_successful_create)
|
||||
redirect_to :controller => 'admin', :action => 'projects'
|
||||
end
|
||||
@@ -89,20 +88,19 @@ class ProjectsController < ApplicationController
|
||||
end
|
||||
|
||||
@members_by_role = @project.members.find(:all, :include => [:user, :role], :order => 'position').group_by {|m| m.role}
|
||||
@subprojects = @project.children.visible
|
||||
@ancestors = @project.ancestors.visible
|
||||
@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")
|
||||
@trackers = @project.rolled_up_trackers
|
||||
|
||||
cond = @project.project_condition(Setting.display_subprojects_issues?)
|
||||
|
||||
@open_issues_by_tracker = Issue.visible.count(:group => :tracker,
|
||||
Issue.visible_by(User.current) do
|
||||
@open_issues_by_tracker = Issue.count(:group => :tracker,
|
||||
:include => [:project, :status, :tracker],
|
||||
:conditions => ["(#{cond}) AND #{IssueStatus.table_name}.is_closed=?", false])
|
||||
@total_issues_by_tracker = Issue.visible.count(:group => :tracker,
|
||||
@total_issues_by_tracker = Issue.count(:group => :tracker,
|
||||
:include => [:project, :status, :tracker],
|
||||
:conditions => cond)
|
||||
|
||||
end
|
||||
TimeEntry.visible_by(User.current) do
|
||||
@total_hours = TimeEntry.sum(:hours,
|
||||
:include => :project,
|
||||
@@ -112,6 +110,9 @@ class ProjectsController < ApplicationController
|
||||
end
|
||||
|
||||
def settings
|
||||
@root_projects = Project.find(:all,
|
||||
:conditions => ["parent_id IS NULL AND status = #{Project::STATUS_ACTIVE} AND id <> ?", @project.id],
|
||||
:order => 'name')
|
||||
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
|
||||
@issue_category ||= IssueCategory.new
|
||||
@member ||= @project.members.new
|
||||
@@ -125,7 +126,6 @@ class ProjectsController < ApplicationController
|
||||
if request.post?
|
||||
@project.attributes = params[:project]
|
||||
if @project.save
|
||||
@project.set_parent!(params[:project]['parent_id']) if User.current.admin? && params[:project].has_key?('parent_id')
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
redirect_to :action => 'settings', :id => @project
|
||||
else
|
||||
|
||||
@@ -61,7 +61,7 @@ class ReportsController < ApplicationController
|
||||
render :template => "reports/issue_report_details"
|
||||
when "subproject"
|
||||
@field = "project_id"
|
||||
@rows = @project.descendants.active
|
||||
@rows = @project.active_children
|
||||
@data = issues_by_subproject
|
||||
@report_title = l(:field_subproject)
|
||||
render :template => "reports/issue_report_details"
|
||||
@@ -72,7 +72,7 @@ class ReportsController < ApplicationController
|
||||
@categories = @project.issue_categories
|
||||
@assignees = @project.members.collect { |m| m.user }
|
||||
@authors = @project.members.collect { |m| m.user }
|
||||
@subprojects = @project.descendants.active
|
||||
@subprojects = @project.active_children
|
||||
issues_by_tracker
|
||||
issues_by_version
|
||||
issues_by_priority
|
||||
@@ -229,8 +229,8 @@ private
|
||||
#{Issue.table_name} i, #{IssueStatus.table_name} s
|
||||
where
|
||||
i.status_id=s.id
|
||||
and i.project_id IN (#{@project.descendants.active.collect{|p| p.id}.join(',')})
|
||||
group by s.id, s.is_closed, i.project_id") if @project.descendants.active.any?
|
||||
and i.project_id IN (#{@project.active_children.collect{|p| p.id}.join(',')})
|
||||
group by s.id, s.is_closed, i.project_id") if @project.active_children.any?
|
||||
@issues_by_subproject ||= []
|
||||
end
|
||||
end
|
||||
|
||||
@@ -88,7 +88,7 @@ class RepositoriesController < ApplicationController
|
||||
def changes
|
||||
@entry = @repository.entry(@path, @rev)
|
||||
show_error_not_found and return unless @entry
|
||||
@changesets = @repository.changesets_for_path(@path, :limit => Setting.repository_log_display_limit.to_i)
|
||||
@changesets = @repository.changesets_for_path(@path)
|
||||
@properties = @repository.properties(@path, @rev)
|
||||
end
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ class SearchController < ApplicationController
|
||||
when 'my_projects'
|
||||
User.current.memberships.collect(&:project)
|
||||
when 'subprojects'
|
||||
@project ? (@project.self_and_descendants.active) : nil
|
||||
@project ? ([ @project ] + @project.active_children) : nil
|
||||
else
|
||||
@project
|
||||
end
|
||||
|
||||
@@ -41,7 +41,7 @@ class SettingsController < ApplicationController
|
||||
@deliveries = ActionMailer::Base.perform_deliveries
|
||||
|
||||
@guessed_host_and_path = request.host_with_port.dup
|
||||
@guessed_host_and_path << ('/'+ Redmine::Utils.relative_url_root.gsub(%r{^\/}, '')) unless Redmine::Utils.relative_url_root.blank?
|
||||
@guessed_host_and_path << ('/'+ request.relative_url_root.gsub(%r{^\/}, '')) unless request.relative_url_root.blank?
|
||||
end
|
||||
|
||||
def plugin
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2009 Jean-Philippe Lang
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006-2007 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
|
||||
@@ -16,35 +16,31 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
class SysController < ActionController::Base
|
||||
before_filter :check_enabled
|
||||
wsdl_service_name 'Sys'
|
||||
web_service_api SysApi
|
||||
web_service_scaffold :invoke
|
||||
|
||||
def projects
|
||||
p = Project.active.has_module(:repository).find(:all, :include => :repository, :order => 'identifier')
|
||||
render :xml => p.to_xml(:include => :repository)
|
||||
end
|
||||
before_invocation :check_enabled
|
||||
|
||||
def create_project_repository
|
||||
project = Project.find(params[:id])
|
||||
if project.repository
|
||||
render :nothing => true, :status => 409
|
||||
else
|
||||
logger.info "Repository for #{project.name} was reported to be created by #{request.remote_ip}."
|
||||
project.repository = Repository.factory(params[:vendor], params[:repository])
|
||||
if project.repository && project.repository.save
|
||||
render :xml => project.repository, :status => 201
|
||||
else
|
||||
render :nothing => true, :status => 422
|
||||
end
|
||||
end
|
||||
# Returns the projects list, with their repositories
|
||||
def projects_with_repository_enabled
|
||||
Project.has_module(:repository).find(:all, :include => :repository, :order => 'identifier')
|
||||
end
|
||||
|
||||
protected
|
||||
# Registers a repository for the given project identifier
|
||||
def repository_created(identifier, vendor, url)
|
||||
project = Project.find_by_identifier(identifier)
|
||||
# Do not create the repository if the project has already one
|
||||
return 0 unless project && project.repository.nil?
|
||||
logger.debug "Repository for #{project.name} was created"
|
||||
repository = Repository.factory(vendor, :project => project, :url => url)
|
||||
repository.save
|
||||
repository.id || 0
|
||||
end
|
||||
|
||||
def check_enabled
|
||||
User.current = nil
|
||||
unless Setting.sys_api_enabled?
|
||||
render :nothing => 'Access denied. Repository management WS is disabled.', :status => 403
|
||||
return false
|
||||
end
|
||||
protected
|
||||
|
||||
def check_enabled(name, args)
|
||||
Setting.sys_api_enabled?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -40,10 +40,8 @@ class TrackersController < ApplicationController
|
||||
end
|
||||
flash[:notice] = l(:notice_successful_create)
|
||||
redirect_to :action => 'list'
|
||||
return
|
||||
end
|
||||
@trackers = Tracker.find :all, :order => 'position'
|
||||
@projects = Project.find(:all)
|
||||
end
|
||||
|
||||
def edit
|
||||
@@ -51,9 +49,7 @@ class TrackersController < ApplicationController
|
||||
if request.post? and @tracker.update_attributes(params[:tracker])
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
redirect_to :action => 'list'
|
||||
return
|
||||
end
|
||||
@projects = Project.find(:all)
|
||||
end
|
||||
|
||||
def move
|
||||
|
||||
@@ -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]))
|
||||
@@ -83,7 +87,7 @@ class UsersController < ApplicationController
|
||||
end
|
||||
@auth_sources = AuthSource.find(:all)
|
||||
@roles = Role.find_all_givable
|
||||
@projects = Project.active.find(:all, :order => 'lft')
|
||||
@projects = Project.find(:all, :order => 'name', :conditions => "status=#{Project::STATUS_ACTIVE}") - @user.projects
|
||||
@membership ||= Member.new
|
||||
@memberships = @user.memberships
|
||||
end
|
||||
|
||||
@@ -16,15 +16,9 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
class WelcomeController < ApplicationController
|
||||
caches_action :robots
|
||||
|
||||
def index
|
||||
@news = News.latest User.current
|
||||
@projects = Project.latest User.current
|
||||
end
|
||||
|
||||
def robots
|
||||
@projects = Project.public.active
|
||||
render :layout => false, :content_type => 'text/plain'
|
||||
end
|
||||
end
|
||||
|
||||
@@ -45,11 +45,11 @@ class WikiController < ApplicationController
|
||||
return
|
||||
end
|
||||
@content = @page.content_for_version(params[:version])
|
||||
if params[:format] == 'html'
|
||||
if params[:export] == 'html'
|
||||
export = render_to_string :action => 'export', :layout => false
|
||||
send_data(export, :type => 'text/html', :filename => "#{@page.title}.html")
|
||||
return
|
||||
elsif params[:format] == 'txt'
|
||||
elsif params[:export] == 'txt'
|
||||
send_data(@content.text, :type => 'text/plain', :filename => "#{@page.title}.txt")
|
||||
return
|
||||
end
|
||||
|
||||
@@ -20,12 +20,4 @@ module AdminHelper
|
||||
options_for_select([[l(:label_all), ''],
|
||||
[l(:status_active), 1]], selected)
|
||||
end
|
||||
|
||||
def css_project_classes(project)
|
||||
s = 'project'
|
||||
s << ' root' if project.root?
|
||||
s << ' child' if project.child?
|
||||
s << (project.leaf? ? ' leaf' : ' parent')
|
||||
s
|
||||
end
|
||||
end
|
||||
|
||||
@@ -116,7 +116,7 @@ module ApplicationHelper
|
||||
end
|
||||
|
||||
def format_activity_description(text)
|
||||
h(truncate(text.to_s, 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')).gsub(/[\r\n]+/, "<br />")
|
||||
h(truncate(text.to_s, 250).gsub(%r{<(pre|code)>.*$}m, '...'))
|
||||
end
|
||||
|
||||
def distance_of_date_in_words(from_date, to_date = 0)
|
||||
@@ -156,69 +156,6 @@ module ApplicationHelper
|
||||
end
|
||||
s
|
||||
end
|
||||
|
||||
# Renders the project quick-jump box
|
||||
def render_project_jump_box
|
||||
# Retrieve them now to avoid a COUNT query
|
||||
projects = User.current.projects.all
|
||||
if projects.any?
|
||||
s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
|
||||
"<option selected='selected'>#{ l(:label_jump_to_a_project) }</option>" +
|
||||
'<option disabled="disabled">---</option>'
|
||||
s << project_tree_options_for_select(projects) do |p|
|
||||
{ :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
|
||||
end
|
||||
s << '</select>'
|
||||
s
|
||||
end
|
||||
end
|
||||
|
||||
def project_tree_options_for_select(projects, options = {})
|
||||
s = ''
|
||||
project_tree(projects) do |project, level|
|
||||
name_prefix = (level > 0 ? (' ' * 2 * level + '» ') : '')
|
||||
tag_options = {:value => project.id, :selected => ((project == options[:selected]) ? 'selected' : nil)}
|
||||
tag_options.merge!(yield(project)) if block_given?
|
||||
s << content_tag('option', name_prefix + h(project), tag_options)
|
||||
end
|
||||
s
|
||||
end
|
||||
|
||||
# Yields the given block for each project with its level in the tree
|
||||
def project_tree(projects, &block)
|
||||
ancestors = []
|
||||
projects.sort_by(&:lft).each do |project|
|
||||
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
|
||||
ancestors.pop
|
||||
end
|
||||
yield project, ancestors.size
|
||||
ancestors << project
|
||||
end
|
||||
end
|
||||
|
||||
def project_nested_ul(projects, &block)
|
||||
s = ''
|
||||
if projects.any?
|
||||
ancestors = []
|
||||
projects.sort_by(&:lft).each do |project|
|
||||
if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
|
||||
s << "<ul>\n"
|
||||
else
|
||||
ancestors.pop
|
||||
s << "</li>"
|
||||
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
|
||||
ancestors.pop
|
||||
s << "</ul></li>\n"
|
||||
end
|
||||
end
|
||||
s << "<li>"
|
||||
s << yield(project).to_s
|
||||
ancestors << project
|
||||
end
|
||||
s << ("</li></ul>\n" * ancestors.size)
|
||||
end
|
||||
s
|
||||
end
|
||||
|
||||
# Truncates and returns the string as a single line
|
||||
def truncate_single_line(string, *args)
|
||||
@@ -263,40 +200,43 @@ module ApplicationHelper
|
||||
def pagination_links_full(paginator, count=nil, options={})
|
||||
page_param = options.delete(:page_param) || :page
|
||||
url_param = params.dup
|
||||
# don't reuse query params if filters are present
|
||||
url_param.merge!(:fields => nil, :values => nil, :operators => nil) if url_param.delete(:set_filter)
|
||||
# don't reuse params if filters are present
|
||||
url_param.clear if url_param.has_key?(:set_filter)
|
||||
|
||||
html = ''
|
||||
if paginator.current.previous
|
||||
html << link_to_remote_content_update('« ' + l(:label_previous), url_param.merge(page_param => paginator.current.previous)) + ' '
|
||||
end
|
||||
html << link_to_remote(('« ' + l(:label_previous)),
|
||||
{:update => 'content',
|
||||
:url => url_param.merge(page_param => paginator.current.previous),
|
||||
:complete => 'window.scrollTo(0,0)'},
|
||||
{:href => url_for(:params => url_param.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
|
||||
|
||||
html << (pagination_links_each(paginator, options) do |n|
|
||||
link_to_remote_content_update(n.to_s, url_param.merge(page_param => n))
|
||||
link_to_remote(n.to_s,
|
||||
{:url => {:params => url_param.merge(page_param => n)},
|
||||
:update => 'content',
|
||||
:complete => 'window.scrollTo(0,0)'},
|
||||
{:href => url_for(:params => url_param.merge(page_param => n))})
|
||||
end || '')
|
||||
|
||||
if paginator.current.next
|
||||
html << ' ' + link_to_remote_content_update((l(:label_next) + ' »'), url_param.merge(page_param => paginator.current.next))
|
||||
end
|
||||
|
||||
html << ' ' + link_to_remote((l(:label_next) + ' »'),
|
||||
{:update => 'content',
|
||||
:url => url_param.merge(page_param => paginator.current.next),
|
||||
:complete => 'window.scrollTo(0,0)'},
|
||||
{:href => url_for(:params => url_param.merge(page_param => paginator.current.next))}) if paginator.current.next
|
||||
|
||||
unless count.nil?
|
||||
html << [
|
||||
" (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})",
|
||||
per_page_links(paginator.items_per_page)
|
||||
].compact.join(' | ')
|
||||
html << [" (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})", per_page_links(paginator.items_per_page)].compact.join(' | ')
|
||||
end
|
||||
|
||||
html
|
||||
end
|
||||
|
||||
|
||||
def per_page_links(selected=nil)
|
||||
url_param = params.dup
|
||||
url_param.clear if url_param.has_key?(:set_filter)
|
||||
|
||||
links = Setting.per_page_options_array.collect do |n|
|
||||
n == selected ? n : link_to_remote(n, {:update => "content",
|
||||
:url => params.dup.merge(:per_page => n),
|
||||
:method => :get},
|
||||
n == selected ? n : link_to_remote(n, {:update => "content", :url => params.dup.merge(:per_page => n)},
|
||||
{:href => url_for(url_param.merge(:per_page => n))})
|
||||
end
|
||||
links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
|
||||
@@ -306,12 +246,6 @@ module ApplicationHelper
|
||||
elements = args.flatten
|
||||
elements.any? ? content_tag('p', args.join(' » ') + ' » ', :class => 'breadcrumb') : nil
|
||||
end
|
||||
|
||||
def other_formats_links(&block)
|
||||
concat('<p class="other-formats">' + l(:label_export_to), block.binding)
|
||||
yield Redmine::Views::OtherFormatsBuilder.new(self)
|
||||
concat('</p>', block.binding)
|
||||
end
|
||||
|
||||
def html_title(*args)
|
||||
if args.empty?
|
||||
@@ -418,7 +352,7 @@ module ApplicationHelper
|
||||
:class => ('wiki-page' + (wiki_page ? '' : ' new')))
|
||||
else
|
||||
# project or wiki doesn't exist
|
||||
all
|
||||
title || page
|
||||
end
|
||||
else
|
||||
all
|
||||
@@ -553,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 << "« " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " » " + l(msg) unless attr == "custom_values"
|
||||
full_messages << "« " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " » " + l(*msg) unless attr == "custom_values"
|
||||
end
|
||||
end
|
||||
# retrieve custom values error messages
|
||||
@@ -565,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 << "« " + v.custom_field.name + " » " + l(msg)
|
||||
msg = [msg] unless msg.is_a?(Array)
|
||||
full_messages << "« " + v.custom_field.name + " » " + l(*msg)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -690,12 +624,4 @@ module ApplicationHelper
|
||||
extend helper
|
||||
return self
|
||||
end
|
||||
|
||||
def link_to_remote_content_update(text, url_params)
|
||||
link_to_remote(text,
|
||||
{:url => url_params, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'},
|
||||
{:href => url_for(:params => url_params)}
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -33,39 +33,4 @@ module ProjectsHelper
|
||||
]
|
||||
tabs.select {|tab| User.current.allowed_to?(tab[:action], @project)}
|
||||
end
|
||||
|
||||
def parent_project_select_tag(project)
|
||||
options = '<option></option>' + project_tree_options_for_select(project.possible_parents, :selected => project.parent)
|
||||
content_tag('select', options, :name => 'project[parent_id]')
|
||||
end
|
||||
|
||||
# Renders a tree of projects as a nested set of unordered lists
|
||||
# The given collection may be a subset of the whole project tree
|
||||
# (eg. some intermediate nodes are private and can not be seen)
|
||||
def render_project_hierarchy(projects)
|
||||
s = ''
|
||||
if projects.any?
|
||||
ancestors = []
|
||||
projects.each do |project|
|
||||
if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
|
||||
s << "<ul class='projects #{ ancestors.empty? ? 'root' : nil}'>\n"
|
||||
else
|
||||
ancestors.pop
|
||||
s << "</li>"
|
||||
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
|
||||
ancestors.pop
|
||||
s << "</ul></li>\n"
|
||||
end
|
||||
end
|
||||
classes = (ancestors.empty? ? 'root' : 'child')
|
||||
s << "<li class='#{classes}'><div class='#{classes}'>" +
|
||||
link_to(h(project), {:controller => 'projects', :action => 'show', :id => project}, :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}")
|
||||
s << "<div class='wiki description'>#{textilizable(project.short_description, :project => project)}</div>" unless project.description.blank?
|
||||
s << "</div>\n"
|
||||
ancestors << project
|
||||
end
|
||||
s << ("</li></ul>\n" * ancestors.size)
|
||||
end
|
||||
s
|
||||
end
|
||||
end
|
||||
|
||||
@@ -152,8 +152,7 @@ module RepositoriesHelper
|
||||
content_tag('p', form.password_field(:password, :size => 30, :name => 'ignore',
|
||||
:value => ((repository.new_record? || repository.password.blank?) ? '' : ('x'*15)),
|
||||
:onfocus => "this.value=''; this.name='repository[password]';",
|
||||
:onchange => "this.name='repository[password]';")) +
|
||||
content_tag('p', form.check_box(:cache))
|
||||
:onchange => "this.name='repository[password]';"))
|
||||
end
|
||||
|
||||
def darcs_field_tags(form, repository)
|
||||
|
||||
@@ -44,7 +44,7 @@ module SearchHelper
|
||||
def project_select_tag
|
||||
options = [[l(:label_project_all), 'all']]
|
||||
options << [l(:label_my_projects), 'my_projects'] unless User.current.memberships.empty?
|
||||
options << [l(:label_and_its_subprojects, @project.name), 'subprojects'] unless @project.nil? || @project.descendants.active.empty?
|
||||
options << [l(:label_and_its_subprojects, @project.name), 'subprojects'] unless @project.nil? || @project.active_children.empty?
|
||||
options << [@project.name, ''] unless @project.nil?
|
||||
select_tag('scope', options_for_select(options, params[:scope].to_s)) if options.size > 1
|
||||
end
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
module SettingsHelper
|
||||
def administration_settings_tabs
|
||||
tabs = [{:name => 'general', :partial => 'settings/general', :label => :label_general},
|
||||
{:name => 'display', :partial => 'settings/display', :label => :label_display},
|
||||
{:name => 'authentication', :partial => 'settings/authentication', :label => :label_authentication},
|
||||
{:name => 'projects', :partial => 'settings/projects', :label => :label_project_plural},
|
||||
{:name => 'issues', :partial => 'settings/issues', :label => :label_issue_tracking},
|
||||
|
||||
@@ -84,7 +84,7 @@ module SortHelper
|
||||
session[@sort_name] = sort
|
||||
|
||||
sort_column = (sort_keys.is_a?(Hash) ? sort_keys[sort[:key]] : sort[:key])
|
||||
@sort_clause = (sort_column.blank? ? nil : [sort_column].flatten.collect {|s| "#{s} #{sort[:order]}"}.join(','))
|
||||
@sort_clause = (sort_column.blank? ? nil : "#{sort_column} #{sort[:order]}")
|
||||
end
|
||||
|
||||
# Returns an SQL sort clause corresponding to the current sort state.
|
||||
@@ -120,11 +120,8 @@ module SortHelper
|
||||
# don't reuse params if filters are present
|
||||
url_options = params.has_key?(:set_filter) ? sort_options : params.merge(sort_options)
|
||||
|
||||
# Add project_id to url_options
|
||||
url_options = url_options.merge(:project_id => params[:project_id]) if params.has_key?(:project_id)
|
||||
|
||||
link_to_remote(caption,
|
||||
{:update => "content", :url => url_options, :method => :get},
|
||||
{:update => "content", :url => url_options},
|
||||
{:href => url_for(url_options)}) +
|
||||
(icon ? nbsp(2) + image_tag(icon) : '')
|
||||
end
|
||||
|
||||
@@ -25,10 +25,15 @@ module UsersHelper
|
||||
end
|
||||
|
||||
# Options for the new membership projects combo-box
|
||||
def options_for_membership_project_select(user, projects)
|
||||
def projects_options_for_select(projects)
|
||||
options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---")
|
||||
options << project_tree_options_for_select(projects) do |p|
|
||||
{:disabled => (user.projects.include?(p))}
|
||||
projects_by_root = projects.group_by(&:root)
|
||||
projects_by_root.keys.sort.each do |root|
|
||||
options << content_tag('option', h(root.name), :value => root.id, :disabled => (!projects.include?(root)))
|
||||
projects_by_root[root].sort.each do |project|
|
||||
next if project == root
|
||||
options << content_tag('option', '» ' + h(project.name), :value => project.id)
|
||||
end
|
||||
end
|
||||
options
|
||||
end
|
||||
|
||||
@@ -23,8 +23,8 @@ class Changeset < ActiveRecord::Base
|
||||
has_many :changes, :dependent => :delete_all
|
||||
has_and_belongs_to_many :issues
|
||||
|
||||
acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.revision}" + (o.short_comments.blank? ? '' : (': ' + o.short_comments))},
|
||||
:description => :long_comments,
|
||||
acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.revision}" + (o.comments.blank? ? '' : (': ' + o.comments))},
|
||||
:description => :comments,
|
||||
:datetime => :committed_on,
|
||||
:url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project_id, :rev => o.revision}}
|
||||
|
||||
@@ -122,14 +122,6 @@ class Changeset < ActiveRecord::Base
|
||||
self.issues = referenced_issues.uniq
|
||||
end
|
||||
|
||||
def short_comments
|
||||
@short_comments || split_comments.first
|
||||
end
|
||||
|
||||
def long_comments
|
||||
@long_comments || split_comments.last
|
||||
end
|
||||
|
||||
# Returns the previous changeset
|
||||
def previous
|
||||
@previous ||= Changeset.find(:first, :conditions => ['id < ? AND repository_id = ?', self.id, self.repository_id], :order => 'id DESC')
|
||||
@@ -147,12 +139,6 @@ class Changeset < ActiveRecord::Base
|
||||
|
||||
private
|
||||
|
||||
def split_comments
|
||||
comments =~ /\A(.+?)\r?\n(.*)$/m
|
||||
@short_comments = $1 || comments
|
||||
@long_comments = $2.to_s.strip
|
||||
return @short_comments, @long_comments
|
||||
end
|
||||
|
||||
def self.to_utf8(str)
|
||||
return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
|
||||
|
||||
@@ -41,6 +41,8 @@ class CustomField < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def before_validation
|
||||
# remove empty values
|
||||
self.possible_values = self.possible_values.collect{|v| v unless v.empty?}.compact
|
||||
# make sure these fields are not searchable
|
||||
self.searchable = false if %w(int float date bool).include?(field_format)
|
||||
true
|
||||
@@ -57,49 +59,11 @@ class CustomField < ActiveRecord::Base
|
||||
v.custom_field.is_required = false
|
||||
errors.add(:default_value, :activerecord_error_invalid) unless v.valid?
|
||||
end
|
||||
|
||||
# Makes possible_values accept a multiline string
|
||||
def possible_values=(arg)
|
||||
if arg.is_a?(Array)
|
||||
write_attribute(:possible_values, arg.compact.collect(&:strip).select {|v| !v.blank?})
|
||||
else
|
||||
self.possible_values = arg.to_s.split(/[\n\r]+/)
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a ORDER BY clause that can used to sort customized
|
||||
# objects by their value of the custom field.
|
||||
# Returns false, if the custom field can not be used for sorting.
|
||||
def order_statement
|
||||
case field_format
|
||||
when 'string', 'text', 'list', 'date', 'bool'
|
||||
# COALESCE is here to make sure that blank and NULL values are sorted equally
|
||||
"COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" +
|
||||
" WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
|
||||
" AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
|
||||
" AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
|
||||
when 'int', 'float'
|
||||
# Make the database cast values into numeric
|
||||
# Postgresql will raise an error if a value can not be casted!
|
||||
# CustomValue validations should ensure that it doesn't occur
|
||||
"(SELECT CAST(cv_sort.value AS decimal(60,3)) FROM #{CustomValue.table_name} cv_sort" +
|
||||
" WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
|
||||
" AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
|
||||
" AND cv_sort.custom_field_id=#{id} AND cv_sort.value <> '' AND cv_sort.value IS NOT NULL LIMIT 1)"
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def <=>(field)
|
||||
position <=> field.position
|
||||
end
|
||||
|
||||
def self.customized_class
|
||||
self.name =~ /^(.+)CustomField$/
|
||||
begin; $1.constantize; rescue nil; end
|
||||
end
|
||||
|
||||
# to move in project_custom_field
|
||||
def self.for_all
|
||||
find(:all, :conditions => ["is_for_all=?", true], :order => 'position')
|
||||
|
||||
@@ -30,18 +30,6 @@ class CustomValue < ActiveRecord::Base
|
||||
self.value == '1'
|
||||
end
|
||||
|
||||
def editable?
|
||||
custom_field.editable?
|
||||
end
|
||||
|
||||
def required?
|
||||
custom_field.is_required?
|
||||
end
|
||||
|
||||
def to_s
|
||||
value.to_s
|
||||
end
|
||||
|
||||
protected
|
||||
def validate
|
||||
if value.blank?
|
||||
|
||||
@@ -40,8 +40,7 @@ class Issue < ActiveRecord::Base
|
||||
# sort by id so that limited eager loading doesn't break with postgresql
|
||||
:order_column => "#{table_name}.id"
|
||||
acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id}: #{o.subject}"},
|
||||
:url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}},
|
||||
:type => Proc.new {|o| 'issue' + (o.closed? ? ' closed' : '') }
|
||||
:url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}}
|
||||
|
||||
acts_as_activity_provider :find_options => {:include => [:project, :author, :tracker]},
|
||||
:author_key => :author_id
|
||||
@@ -51,14 +50,6 @@ class Issue < ActiveRecord::Base
|
||||
validates_inclusion_of :done_ratio, :in => 0..100
|
||||
validates_numericality_of :estimated_hours, :allow_nil => true
|
||||
|
||||
named_scope :visible, lambda {|*args| { :include => :project,
|
||||
:conditions => Project.allowed_to_condition(args.first || User.current, :view_issues) } }
|
||||
|
||||
# Returns true if usr or current user is allowed to view the issue
|
||||
def visible?(usr=nil)
|
||||
(usr || User.current).allowed_to?(:view_issues, self.project)
|
||||
end
|
||||
|
||||
def after_initialize
|
||||
if new_record?
|
||||
# set default values for new records only
|
||||
@@ -79,43 +70,34 @@ class Issue < ActiveRecord::Base
|
||||
self
|
||||
end
|
||||
|
||||
# Moves/copies an issue to a new project and tracker
|
||||
# Returns the moved/copied issue on success, false on failure
|
||||
def move_to(new_project, new_tracker = nil, options = {})
|
||||
options ||= {}
|
||||
issue = options[:copy] ? self.clone : self
|
||||
# Move an issue to a new project and tracker
|
||||
def move_to(new_project, new_tracker = nil)
|
||||
transaction do
|
||||
if new_project && issue.project_id != new_project.id
|
||||
if new_project && project_id != new_project.id
|
||||
# delete issue relations
|
||||
unless Setting.cross_project_issue_relations?
|
||||
issue.relations_from.clear
|
||||
issue.relations_to.clear
|
||||
self.relations_from.clear
|
||||
self.relations_to.clear
|
||||
end
|
||||
# issue is moved to another project
|
||||
# reassign to the category with same name if any
|
||||
new_category = issue.category.nil? ? nil : new_project.issue_categories.find_by_name(issue.category.name)
|
||||
issue.category = new_category
|
||||
issue.fixed_version = nil
|
||||
issue.project = new_project
|
||||
new_category = category.nil? ? nil : new_project.issue_categories.find_by_name(category.name)
|
||||
self.category = new_category
|
||||
self.fixed_version = nil
|
||||
self.project = new_project
|
||||
end
|
||||
if new_tracker
|
||||
issue.tracker = new_tracker
|
||||
self.tracker = new_tracker
|
||||
end
|
||||
if options[:copy]
|
||||
issue.custom_field_values = self.custom_field_values.inject({}) {|h,v| h[v.custom_field_id] = v.value; h}
|
||||
issue.status = self.status
|
||||
end
|
||||
if issue.save
|
||||
unless options[:copy]
|
||||
# Manually update project_id on related time entries
|
||||
TimeEntry.update_all("project_id = #{new_project.id}", {:issue_id => id})
|
||||
end
|
||||
if save
|
||||
# Manually update project_id on related time entries
|
||||
TimeEntry.update_all("project_id = #{new_project.id}", {:issue_id => id})
|
||||
else
|
||||
Issue.connection.rollback_db_transaction
|
||||
rollback_db_transaction
|
||||
return false
|
||||
end
|
||||
end
|
||||
return issue
|
||||
return true
|
||||
end
|
||||
|
||||
def priority_id=(pid)
|
||||
@@ -215,7 +197,7 @@ class Issue < ActiveRecord::Base
|
||||
|
||||
# Returns true if the issue is overdue
|
||||
def overdue?
|
||||
!due_date.nil? && (due_date < Date.today) && !status.is_closed?
|
||||
!due_date.nil? && (due_date < Date.today)
|
||||
end
|
||||
|
||||
# Users the issue can be assigned to
|
||||
@@ -275,6 +257,12 @@ class Issue < ActiveRecord::Base
|
||||
@soonest_start ||= relations_to.collect{|relation| relation.successor_soonest_start}.compact.min
|
||||
end
|
||||
|
||||
def self.visible_by(usr)
|
||||
with_scope(:find => { :conditions => Project.visible_by(usr) }) do
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{tracker} ##{id}: #{subject}"
|
||||
end
|
||||
|
||||
@@ -35,8 +35,6 @@ class IssueRelation < ActiveRecord::Base
|
||||
validates_numericality_of :delay, :allow_nil => true
|
||||
validates_uniqueness_of :issue_to_id, :scope => :issue_from_id
|
||||
|
||||
attr_protected :issue_from_id, :issue_to_id
|
||||
|
||||
def validate
|
||||
if issue_from && issue_to
|
||||
errors.add :issue_to_id, :activerecord_error_invalid if issue_from_id == issue_to_id
|
||||
|
||||
@@ -40,7 +40,7 @@ class MailHandler < ActionMailer::Base
|
||||
# Processes incoming emails
|
||||
def receive(email)
|
||||
@email = email
|
||||
@user = User.active.find_by_mail(email.from.first.to_s.strip)
|
||||
@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
|
||||
@@ -53,24 +53,11 @@ class MailHandler < ActionMailer::Base
|
||||
|
||||
private
|
||||
|
||||
MESSAGE_ID_RE = %r{^<redmine\.([a-z0-9_]+)\-(\d+)\.\d+@}
|
||||
ISSUE_REPLY_SUBJECT_RE = %r{\[[^\]]+#(\d+)\]}
|
||||
MESSAGE_REPLY_SUBJECT_RE = %r{\[[^\]]+msg(\d+)\]}
|
||||
|
||||
def dispatch
|
||||
headers = [email.in_reply_to, email.references].flatten.compact
|
||||
if headers.detect {|h| h.to_s =~ MESSAGE_ID_RE}
|
||||
klass, object_id = $1, $2.to_i
|
||||
method_name = "receive_#{klass}_reply"
|
||||
if self.class.private_instance_methods.include?(method_name)
|
||||
send method_name, object_id
|
||||
else
|
||||
# ignoring it
|
||||
end
|
||||
elsif m = email.subject.match(ISSUE_REPLY_SUBJECT_RE)
|
||||
receive_issue_reply(m[1].to_i)
|
||||
elsif m = email.subject.match(MESSAGE_REPLY_SUBJECT_RE)
|
||||
receive_message_reply(m[1].to_i)
|
||||
if m = email.subject.match(ISSUE_REPLY_SUBJECT_RE)
|
||||
receive_issue_update(m[1].to_i)
|
||||
else
|
||||
receive_issue
|
||||
end
|
||||
@@ -130,7 +117,7 @@ class MailHandler < ActionMailer::Base
|
||||
end
|
||||
|
||||
# Adds a note to an existing issue
|
||||
def receive_issue_reply(issue_id)
|
||||
def receive_issue_update(issue_id)
|
||||
status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status)))
|
||||
|
||||
issue = Issue.find_by_id(issue_id)
|
||||
@@ -152,33 +139,6 @@ class MailHandler < ActionMailer::Base
|
||||
journal
|
||||
end
|
||||
|
||||
# Reply will be added to the issue
|
||||
def receive_journal_reply(journal_id)
|
||||
journal = Journal.find_by_id(journal_id)
|
||||
if journal && journal.journalized_type == 'Issue'
|
||||
receive_issue_reply(journal.journalized_id)
|
||||
end
|
||||
end
|
||||
|
||||
# Receives a reply to a forum message
|
||||
def receive_message_reply(message_id)
|
||||
message = Message.find_by_id(message_id)
|
||||
if message
|
||||
message = message.root
|
||||
if user.allowed_to?(:add_messages, message.project) && !message.locked?
|
||||
reply = Message.new(:subject => email.subject.gsub(%r{^.*msg\d+\]}, '').strip,
|
||||
:content => plain_text_body)
|
||||
reply.author = user
|
||||
reply.board = message.board
|
||||
message.children << reply
|
||||
add_attachments(reply)
|
||||
reply
|
||||
else
|
||||
raise UnauthorizedAction
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add_attachments(obj)
|
||||
if email.has_attachments?
|
||||
email.attachments.each do |attachment|
|
||||
@@ -235,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
|
||||
|
||||
@@ -22,12 +22,17 @@ 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
|
||||
message_id issue
|
||||
recipients issue.recipients
|
||||
cc(issue.watcher_recipients - @recipients)
|
||||
subject "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] (#{issue.status.name}) #{issue.subject}"
|
||||
@@ -41,8 +46,6 @@ 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
|
||||
message_id journal
|
||||
references issue
|
||||
@author = journal.user
|
||||
recipients issue.recipients
|
||||
# Watchers in cc
|
||||
@@ -98,7 +101,6 @@ class Mailer < ActionMailer::Base
|
||||
|
||||
def news_added(news)
|
||||
redmine_headers 'Project' => news.project.identifier
|
||||
message_id news
|
||||
recipients news.project.recipients
|
||||
subject "[#{news.project.name}] #{l(:label_news)}: #{news.title}"
|
||||
body :news => news,
|
||||
@@ -108,10 +110,8 @@ class Mailer < ActionMailer::Base
|
||||
def message_posted(message, recipients)
|
||||
redmine_headers 'Project' => message.project.identifier,
|
||||
'Topic-Id' => (message.parent_id || message.id)
|
||||
message_id message
|
||||
references message.parent unless message.parent.nil?
|
||||
recipients(recipients)
|
||||
subject "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] #{message.subject}"
|
||||
subject "[#{message.board.project.name} - #{message.board.name}] #{message.subject}"
|
||||
body :message => message,
|
||||
:message_url => url_for(:controller => 'messages', :action => 'show', :board_id => message.board_id, :id => message.root)
|
||||
end
|
||||
@@ -133,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
|
||||
@@ -162,15 +171,7 @@ class Mailer < ActionMailer::Base
|
||||
return false if (recipients.nil? || recipients.empty?) &&
|
||||
(cc.nil? || cc.empty?) &&
|
||||
(bcc.nil? || bcc.empty?)
|
||||
|
||||
# Set Message-Id and References
|
||||
if @message_id_object
|
||||
mail.message_id = self.class.message_id_for(@message_id_object)
|
||||
end
|
||||
if @references_objects
|
||||
mail.references = @references_objects.collect {|o| self.class.message_id_for(o)}
|
||||
end
|
||||
super(mail)
|
||||
super
|
||||
end
|
||||
|
||||
# Sends reminders to issue assignees
|
||||
@@ -203,12 +204,6 @@ 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 Redmine::Utils.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,
|
||||
@@ -240,7 +235,7 @@ class Mailer < ActionMailer::Base
|
||||
|
||||
# Renders a message with the corresponding layout
|
||||
def render_message(method_name, body)
|
||||
layout = method_name.to_s.match(%r{text\.html\.(rhtml|rxml)}) ? 'layout.text.html.rhtml' : 'layout.text.plain.rhtml'
|
||||
layout = method_name.match(%r{text\.html\.(rhtml|rxml)}) ? 'layout.text.html.rhtml' : 'layout.text.plain.rhtml'
|
||||
body[:content_for_layout] = render(:file => method_name, :body => body)
|
||||
ActionView::Base.new(template_root, body, self).render(:file => "mailer/#{layout}", :use_full_path => true)
|
||||
end
|
||||
@@ -264,34 +259,4 @@ class Mailer < ActionMailer::Base
|
||||
def self.controller_path
|
||||
''
|
||||
end unless respond_to?('controller_path')
|
||||
|
||||
# Returns a predictable Message-Id for the given object
|
||||
def self.message_id_for(object)
|
||||
# id + timestamp should reduce the odds of a collision
|
||||
# as far as we don't send multiple emails for the same object
|
||||
hash = "redmine.#{object.class.name.demodulize.underscore}-#{object.id}.#{object.created_on.strftime("%Y%m%d%H%M%S")}"
|
||||
host = Setting.mail_from.to_s.gsub(%r{^.*@}, '')
|
||||
host = "#{::Socket.gethostname}.redmine" if host.empty?
|
||||
"<#{hash}@#{host}>"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def message_id(object)
|
||||
@message_id_object = object
|
||||
end
|
||||
|
||||
def references(object)
|
||||
@references_objects ||= []
|
||||
@references_objects << object
|
||||
end
|
||||
end
|
||||
|
||||
# Patch TMail so that message_id is not overwritten
|
||||
module TMail
|
||||
class Mail
|
||||
def add_message_id( fqdn = nil )
|
||||
self.message_id ||= ::TMail::new_message_id(fqdn)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -43,7 +43,7 @@ class Project < ActiveRecord::Base
|
||||
:join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}",
|
||||
:association_foreign_key => 'custom_field_id'
|
||||
|
||||
acts_as_nested_set :order => 'name', :dependent => :destroy
|
||||
acts_as_tree :order => "name", :counter_cache => true
|
||||
acts_as_attachable :view_permission => :view_files,
|
||||
:delete_permission => :manage_files
|
||||
|
||||
@@ -66,9 +66,6 @@ class Project < ActiveRecord::Base
|
||||
before_destroy :delete_all_members
|
||||
|
||||
named_scope :has_module, lambda { |mod| { :conditions => ["#{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name=?)", mod.to_s] } }
|
||||
named_scope :active, { :conditions => "#{Project.table_name}.status = #{STATUS_ACTIVE}"}
|
||||
named_scope :public, { :conditions => { :is_public => true } }
|
||||
named_scope :visible, lambda { { :conditions => Project.visible_by(User.current) } }
|
||||
|
||||
def identifier=(identifier)
|
||||
super unless identifier_frozen?
|
||||
@@ -81,7 +78,7 @@ class Project < ActiveRecord::Base
|
||||
def issues_with_subprojects(include_subprojects=false)
|
||||
conditions = nil
|
||||
if include_subprojects
|
||||
ids = [id] + descendants.collect(&:id)
|
||||
ids = [id] + child_ids
|
||||
conditions = ["#{Project.table_name}.id IN (#{ids.join(',')}) AND #{Project.visible_by}"]
|
||||
end
|
||||
conditions ||= ["#{Project.table_name}.id = ?", id]
|
||||
@@ -121,7 +118,7 @@ class Project < ActiveRecord::Base
|
||||
end
|
||||
if options[:project]
|
||||
project_statement = "#{Project.table_name}.id = #{options[:project].id}"
|
||||
project_statement << " OR (#{Project.table_name}.lft > #{options[:project].lft} AND #{Project.table_name}.rgt < #{options[:project].rgt})" if options[:with_subprojects]
|
||||
project_statement << " OR #{Project.table_name}.parent_id = #{options[:project].id}" if options[:with_subprojects]
|
||||
base_statement = "(#{project_statement}) AND (#{base_statement})"
|
||||
end
|
||||
if user.admin?
|
||||
@@ -144,7 +141,7 @@ class Project < ActiveRecord::Base
|
||||
|
||||
def project_condition(with_subprojects)
|
||||
cond = "#{Project.table_name}.id = #{id}"
|
||||
cond = "(#{cond} OR (#{Project.table_name}.lft > #{lft} AND #{Project.table_name}.rgt < #{rgt}))" if with_subprojects
|
||||
cond = "(#{cond} OR #{Project.table_name}.parent_id = #{id})" if with_subprojects
|
||||
cond
|
||||
end
|
||||
|
||||
@@ -167,7 +164,6 @@ class Project < ActiveRecord::Base
|
||||
self.status == STATUS_ACTIVE
|
||||
end
|
||||
|
||||
# Archives the project and its descendants recursively
|
||||
def archive
|
||||
# Archive subprojects if any
|
||||
children.each do |subproject|
|
||||
@@ -176,62 +172,21 @@ class Project < ActiveRecord::Base
|
||||
update_attribute :status, STATUS_ARCHIVED
|
||||
end
|
||||
|
||||
# Unarchives the project
|
||||
# All its ancestors must be active
|
||||
def unarchive
|
||||
return false if ancestors.detect {|a| !a.active?}
|
||||
return false if parent && !parent.active?
|
||||
update_attribute :status, STATUS_ACTIVE
|
||||
end
|
||||
|
||||
# Returns an array of projects the project can be moved to
|
||||
def possible_parents
|
||||
@possible_parents ||= (Project.active.find(:all) - self_and_descendants)
|
||||
def active_children
|
||||
children.select {|child| child.active?}
|
||||
end
|
||||
|
||||
# Sets the parent of the project
|
||||
# Argument can be either a Project, a String, a Fixnum or nil
|
||||
def set_parent!(p)
|
||||
unless p.nil? || p.is_a?(Project)
|
||||
if p.to_s.blank?
|
||||
p = nil
|
||||
else
|
||||
p = Project.find_by_id(p)
|
||||
return false unless p
|
||||
end
|
||||
end
|
||||
if p == parent && !p.nil?
|
||||
# Nothing to do
|
||||
true
|
||||
elsif p.nil? || (p.active? && move_possible?(p))
|
||||
# Insert the project so that target's children or root projects stay alphabetically sorted
|
||||
sibs = (p.nil? ? self.class.roots : p.children)
|
||||
to_be_inserted_before = sibs.detect {|c| c.name.to_s.downcase > name.to_s.downcase }
|
||||
if to_be_inserted_before
|
||||
move_to_left_of(to_be_inserted_before)
|
||||
elsif p.nil?
|
||||
if sibs.empty?
|
||||
# move_to_root adds the project in first (ie. left) position
|
||||
move_to_root
|
||||
else
|
||||
move_to_right_of(sibs.last) unless self == sibs.last
|
||||
end
|
||||
else
|
||||
# move_to_child_of adds the project in last (ie.right) position
|
||||
move_to_child_of(p)
|
||||
end
|
||||
true
|
||||
else
|
||||
# Can not move to the given target
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
# Returns an array of the trackers used by the project and its active sub projects
|
||||
# Returns an array of the trackers used by the project and its sub projects
|
||||
def rolled_up_trackers
|
||||
@rolled_up_trackers ||=
|
||||
Tracker.find(:all, :include => :projects,
|
||||
:select => "DISTINCT #{Tracker.table_name}.*",
|
||||
:conditions => ["#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status = #{STATUS_ACTIVE}", lft, rgt],
|
||||
:conditions => ["#{Project.table_name}.id = ? OR #{Project.table_name}.parent_id = ?", id, id],
|
||||
:order => "#{Tracker.table_name}.position")
|
||||
end
|
||||
|
||||
@@ -270,7 +225,7 @@ class Project < ActiveRecord::Base
|
||||
|
||||
# Returns a short description of the projects (first lines)
|
||||
def short_description(length = 255)
|
||||
description.gsub(/^(.{#{length}}[^\n\r]*).*$/m, '\1...').strip if description
|
||||
description.gsub(/^(.{#{length}}[^\n]*).*$/m, '\1').strip if description
|
||||
end
|
||||
|
||||
def allows_to?(action)
|
||||
@@ -302,6 +257,8 @@ class Project < ActiveRecord::Base
|
||||
|
||||
protected
|
||||
def validate
|
||||
errors.add(parent_id, " must be a root project") if parent and parent.parent
|
||||
errors.add_to_base("A project with subprojects can't be a subproject") if parent and children.size > 0
|
||||
errors.add(:identifier, :activerecord_error_invalid) if !identifier.blank? && identifier.match(/^\d*$/)
|
||||
end
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ class QueryCustomFieldColumn < QueryColumn
|
||||
|
||||
def initialize(custom_field)
|
||||
self.name = "cf_#{custom_field.id}".to_sym
|
||||
self.sortable = custom_field.order_statement || false
|
||||
self.sortable = false
|
||||
@cf = custom_field
|
||||
end
|
||||
|
||||
@@ -98,10 +98,10 @@ class Query < ActiveRecord::Base
|
||||
QueryColumn.new(:priority, :sortable => "#{Enumeration.table_name}.position", :default_order => 'desc'),
|
||||
QueryColumn.new(:subject, :sortable => "#{Issue.table_name}.subject"),
|
||||
QueryColumn.new(:author),
|
||||
QueryColumn.new(:assigned_to, :sortable => ["#{User.table_name}.lastname", "#{User.table_name}.firstname"]),
|
||||
QueryColumn.new(:assigned_to, :sortable => "#{User.table_name}.lastname"),
|
||||
QueryColumn.new(:updated_on, :sortable => "#{Issue.table_name}.updated_on", :default_order => 'desc'),
|
||||
QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name"),
|
||||
QueryColumn.new(:fixed_version, :sortable => ["#{Version.table_name}.effective_date", "#{Version.table_name}.name"], :default_order => 'desc'),
|
||||
QueryColumn.new(:fixed_version, :sortable => "#{Version.table_name}.effective_date", :default_order => 'desc'),
|
||||
QueryColumn.new(:start_date, :sortable => "#{Issue.table_name}.start_date"),
|
||||
QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date"),
|
||||
QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours"),
|
||||
@@ -165,10 +165,6 @@ class Query < ActiveRecord::Base
|
||||
end
|
||||
@available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => user_values } unless user_values.empty?
|
||||
@available_filters["author_id"] = { :type => :list, :order => 5, :values => user_values } unless user_values.empty?
|
||||
|
||||
if User.current.logged?
|
||||
@available_filters["watcher_id"] = { :type => :list, :order => 15, :values => [["<< #{l(:label_me)} >>", "me"]] }
|
||||
end
|
||||
|
||||
if project
|
||||
# project specific filters
|
||||
@@ -178,8 +174,8 @@ class Query < ActiveRecord::Base
|
||||
unless @project.versions.empty?
|
||||
@available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => @project.versions.sort.collect{|s| [s.name, s.id.to_s] } }
|
||||
end
|
||||
unless @project.descendants.active.empty?
|
||||
@available_filters["subproject_id"] = { :type => :list_subprojects, :order => 13, :values => @project.descendants.visible.collect{|s| [s.name, s.id.to_s] } }
|
||||
unless @project.active_children.empty?
|
||||
@available_filters["subproject_id"] = { :type => :list_subprojects, :order => 13, :values => @project.active_children.collect{|s| [s.name, s.id.to_s] } }
|
||||
end
|
||||
add_custom_fields_filters(@project.all_issue_custom_fields)
|
||||
else
|
||||
@@ -261,7 +257,7 @@ class Query < ActiveRecord::Base
|
||||
|
||||
def project_statement
|
||||
project_clauses = []
|
||||
if project && !@project.descendants.active.empty?
|
||||
if project && !@project.active_children.empty?
|
||||
ids = [project.id]
|
||||
if has_filter?("subproject_id")
|
||||
case operator_for("subproject_id")
|
||||
@@ -272,10 +268,10 @@ class Query < ActiveRecord::Base
|
||||
# main project only
|
||||
else
|
||||
# all subprojects
|
||||
ids += project.descendants.collect(&:id)
|
||||
ids += project.child_ids
|
||||
end
|
||||
elsif Setting.display_subprojects_issues?
|
||||
ids += project.descendants.collect(&:id)
|
||||
ids += project.child_ids
|
||||
end
|
||||
project_clauses << "#{Project.table_name}.id IN (%s)" % ids.join(',')
|
||||
elsif project
|
||||
@@ -292,34 +288,31 @@ class Query < ActiveRecord::Base
|
||||
next if field == "subproject_id"
|
||||
v = values_for(field).clone
|
||||
next unless v and !v.empty?
|
||||
operator = operator_for(field)
|
||||
|
||||
# "me" value subsitution
|
||||
if %w(assigned_to_id author_id watcher_id).include?(field)
|
||||
v.push(User.current.logged? ? User.current.id.to_s : "0") if v.delete("me")
|
||||
end
|
||||
|
||||
|
||||
sql = ''
|
||||
is_custom_filter = false
|
||||
if field =~ /^cf_(\d+)$/
|
||||
# custom field
|
||||
db_table = CustomValue.table_name
|
||||
db_field = 'value'
|
||||
is_custom_filter = true
|
||||
sql << "#{Issue.table_name}.id IN (SELECT #{Issue.table_name}.id FROM #{Issue.table_name} LEFT OUTER JOIN #{db_table} ON #{db_table}.customized_type='Issue' AND #{db_table}.customized_id=#{Issue.table_name}.id AND #{db_table}.custom_field_id=#{$1} WHERE "
|
||||
sql << sql_for_field(field, operator, v, db_table, db_field, true) + ')'
|
||||
elsif field == 'watcher_id'
|
||||
db_table = Watcher.table_name
|
||||
db_field = 'user_id'
|
||||
sql << "#{Issue.table_name}.id #{ operator == '=' ? 'IN' : 'NOT IN' } (SELECT #{db_table}.watchable_id FROM #{db_table} WHERE #{db_table}.watchable_type='Issue' AND "
|
||||
sql << sql_for_field(field, '=', v, db_table, db_field) + ')'
|
||||
else
|
||||
# regular field
|
||||
db_table = Issue.table_name
|
||||
db_field = field
|
||||
sql << '(' + sql_for_field(field, operator, v, db_table, db_field) + ')'
|
||||
sql << '('
|
||||
end
|
||||
filters_clauses << sql
|
||||
|
||||
# "me" value subsitution
|
||||
if %w(assigned_to_id author_id).include?(field)
|
||||
v.push(User.current.logged? ? User.current.id.to_s : "0") if v.delete("me")
|
||||
end
|
||||
|
||||
sql = sql + sql_for_field(field, v, db_table, db_field, is_custom_filter)
|
||||
|
||||
sql << ')'
|
||||
filters_clauses << sql
|
||||
end if filters and valid?
|
||||
|
||||
(filters_clauses << project_statement).join(' AND ')
|
||||
@@ -327,10 +320,10 @@ class Query < ActiveRecord::Base
|
||||
|
||||
private
|
||||
|
||||
# Helper method to generate the WHERE sql for a +field+, +operator+ and a +value+
|
||||
def sql_for_field(field, operator, value, db_table, db_field, is_custom_filter=false)
|
||||
# Helper method to generate the WHERE sql for a +field+ with a +value+
|
||||
def sql_for_field(field, value, db_table, db_field, is_custom_filter)
|
||||
sql = ''
|
||||
case operator
|
||||
case operator_for field
|
||||
when "="
|
||||
sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
|
||||
when "!"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
||||
#
|
||||
@@ -20,14 +19,14 @@ class Repository < ActiveRecord::Base
|
||||
belongs_to :project
|
||||
has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
|
||||
has_many :changes, :through => :changesets
|
||||
|
||||
|
||||
# Raw SQL to delete changesets and changes in the database
|
||||
# has_many :changesets, :dependent => :destroy is too slow for big repositories
|
||||
before_destroy :clear_changesets
|
||||
|
||||
|
||||
# Checks if the SCM is enabled when creating a repository
|
||||
validate_on_create { |r| r.errors.add(:type, :activerecord_error_invalid) unless Setting.enabled_scm.include?(r.class.name.demodulize) }
|
||||
|
||||
|
||||
# Removes leading and trailing whitespace
|
||||
def url=(arg)
|
||||
write_attribute(:url, arg ? arg.to_s.strip : nil)
|
||||
@@ -39,8 +38,7 @@ class Repository < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def scm
|
||||
init_cache if cache_path.blank? and respond_to?(:init_cache)
|
||||
@scm ||= self.scm_adapter.new(url, root_url, login, password, cache_path)
|
||||
@scm ||= self.scm_adapter.new url, root_url, login, password
|
||||
update_attribute(:root_url, @scm.root_url) if root_url.blank?
|
||||
@scm
|
||||
end
|
||||
@@ -78,12 +76,11 @@ class Repository < ActiveRecord::Base
|
||||
end
|
||||
|
||||
# Default behaviour: we search in cached changesets
|
||||
def changesets_for_path(path, options={})
|
||||
def changesets_for_path(path)
|
||||
path = "/#{path}" unless path.starts_with?('/')
|
||||
Change.find(:all, :include => {:changeset => :user},
|
||||
:conditions => ["repository_id = ? AND path = ?", id, path],
|
||||
:order => "committed_on DESC, #{Changeset.table_name}.id DESC",
|
||||
:limit => options[:limit]).collect(&:changeset)
|
||||
:conditions => ["repository_id = ? AND path = ?", id, path],
|
||||
:order => "committed_on DESC, #{Changeset.table_name}.id DESC").collect(&:changeset)
|
||||
end
|
||||
|
||||
# Returns a path relative to the url of the repository
|
||||
@@ -164,31 +161,9 @@ class Repository < ActiveRecord::Base
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
|
||||
def remove_cache
|
||||
scm.remove_cache if cache
|
||||
end
|
||||
|
||||
def create_or_sync_cache
|
||||
begin
|
||||
scm.create_cache
|
||||
rescue => e
|
||||
# clean if problem in creation
|
||||
scm.remove_cache
|
||||
end
|
||||
scm.synchronize
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def repositories_cache_directory
|
||||
unless @cache_directory
|
||||
@cache_directory = Setting.repositories_cache_directory.gsub(/^([^#{File::SEPARATOR}].*)/, RAILS_ROOT + '/\1/')
|
||||
Dir.mkdir(@cache_directory, File.umask(0077)) unless File.directory?(@cache_directory)
|
||||
end
|
||||
@cache_directory
|
||||
end
|
||||
|
||||
|
||||
def before_save
|
||||
# Strips url and root_url
|
||||
url.strip!
|
||||
|
||||
@@ -21,17 +21,6 @@ class Repository::Git < Repository
|
||||
attr_protected :root_url
|
||||
validates_presence_of :url
|
||||
|
||||
before_destroy :remove_cache
|
||||
|
||||
def init_cache
|
||||
return unless dir = repositories_cache_directory
|
||||
# we need to use a cache only if repository isn't local and dir exists
|
||||
if url[/^(rsync|https?|git|ssh):\/\//]
|
||||
update_attribute(:cache_path, dir + project.identifier)
|
||||
update_attribute(:cache, true)
|
||||
end
|
||||
end
|
||||
|
||||
def scm_adapter
|
||||
Redmine::Scm::Adapters::GitAdapter
|
||||
end
|
||||
@@ -40,16 +29,13 @@ class Repository::Git < Repository
|
||||
'Git'
|
||||
end
|
||||
|
||||
def changesets_for_path(path, options={})
|
||||
def changesets_for_path(path)
|
||||
Change.find(:all, :include => {:changeset => :user},
|
||||
:conditions => ["repository_id = ? AND path = ?", id, path],
|
||||
:order => "committed_on DESC, #{Changeset.table_name}.revision DESC",
|
||||
:limit => options[:limit]).collect(&:changeset)
|
||||
:order => "committed_on DESC, #{Changeset.table_name}.revision DESC").collect(&:changeset)
|
||||
end
|
||||
|
||||
def fetch_changesets
|
||||
create_or_sync_cache if cache
|
||||
|
||||
scm_info = scm.info
|
||||
if scm_info
|
||||
# latest revision found in database
|
||||
|
||||
@@ -21,15 +21,6 @@ class Repository::Mercurial < Repository
|
||||
attr_protected :root_url
|
||||
validates_presence_of :url
|
||||
|
||||
def init_cache
|
||||
return unless dir = repositories_cache_directory
|
||||
# we need to use a cache only if repository isn't local and dir exists
|
||||
if url[/^(|https?|ssh):\/\//]
|
||||
update_attribute(:cache_path, dir + project.identifier)
|
||||
update_attribute(:cache, true)
|
||||
end
|
||||
end
|
||||
|
||||
def scm_adapter
|
||||
Redmine::Scm::Adapters::MercurialAdapter
|
||||
end
|
||||
@@ -62,8 +53,6 @@ class Repository::Mercurial < Repository
|
||||
end
|
||||
|
||||
def fetch_changesets
|
||||
create_or_sync_cache if cache
|
||||
|
||||
scm_info = scm.info
|
||||
if scm_info
|
||||
# latest revision found in database
|
||||
|
||||
@@ -22,16 +22,6 @@ class Repository::Subversion < Repository
|
||||
validates_presence_of :url
|
||||
validates_format_of :url, :with => /^(http|https|svn|svn\+ssh|file):\/\/.+/i
|
||||
|
||||
before_destroy :remove_cache
|
||||
|
||||
def init_cache
|
||||
return unless dir = repositories_cache_directory
|
||||
# we need to use a cache only if repository isn't local and dir exists
|
||||
if cache and url[/^(svn|https?|svn\+ssh):\/\//]
|
||||
update_attribute(:cache_path, dir + project.identifier)
|
||||
end
|
||||
end
|
||||
|
||||
def scm_adapter
|
||||
Redmine::Scm::Adapters::SubversionAdapter
|
||||
end
|
||||
@@ -40,8 +30,8 @@ class Repository::Subversion < Repository
|
||||
'Subversion'
|
||||
end
|
||||
|
||||
def changesets_for_path(path, options={})
|
||||
revisions = scm.revisions(path, nil, nil, :limit => options[:limit])
|
||||
def changesets_for_path(path)
|
||||
revisions = scm.revisions(path)
|
||||
revisions ? changesets.find_all_by_revision(revisions.collect(&:identifier), :order => "committed_on DESC", :include => :user) : []
|
||||
end
|
||||
|
||||
@@ -51,8 +41,6 @@ class Repository::Subversion < Repository
|
||||
end
|
||||
|
||||
def fetch_changesets
|
||||
create_or_sync_cache if cache
|
||||
|
||||
scm_info = scm.info
|
||||
if scm_info
|
||||
# latest revision found in database
|
||||
|
||||
@@ -140,10 +140,6 @@ class Setting < ActiveRecord::Base
|
||||
per_page_options.split(%r{[\s,]}).collect(&:to_i).select {|n| n > 0}.sort
|
||||
end
|
||||
|
||||
def self.openid?
|
||||
Object.const_defined?(:OpenID) && self['openid'].to_s == '1'
|
||||
end
|
||||
|
||||
# Checks if settings have changed since the values were read
|
||||
# and clears the cache hash if it's the case
|
||||
# Called once per request
|
||||
@@ -165,14 +161,4 @@ private
|
||||
setting = find_by_name(name)
|
||||
setting ||= new(:name => name, :value => @@available_settings[name]['default']) if @@available_settings.has_key? name
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def validate
|
||||
if self.name.to_s == "repositories_cache_directory" and not File.directory?(self.value.to_s)
|
||||
logger.error("Le repertoire #{self.value.to_s} n'existe pas")
|
||||
errors.add("Le repertoire #{self.value.to_s} n'existe pas")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -54,7 +54,7 @@ class User < ActiveRecord::Base
|
||||
|
||||
validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
|
||||
validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }
|
||||
validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
|
||||
validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }
|
||||
# Login must contain lettres, numbers, underscores only
|
||||
validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
|
||||
validates_length_of :login, :maximum => 30
|
||||
@@ -80,15 +80,6 @@ class User < ActiveRecord::Base
|
||||
super
|
||||
end
|
||||
|
||||
def identity_url=(url)
|
||||
begin
|
||||
self.write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
|
||||
rescue InvalidOpenId
|
||||
# Invlaid url, don't save
|
||||
end
|
||||
self.read_attribute(:identity_url)
|
||||
end
|
||||
|
||||
# Returns the user that matches provided login and password, or nil
|
||||
def self.try_to_login(login, password)
|
||||
# Make sure no one can sign in with an empty password
|
||||
@@ -147,18 +138,6 @@ class User < ActiveRecord::Base
|
||||
def check_password?(clear_password)
|
||||
User.hash_password(clear_password) == self.hashed_password
|
||||
end
|
||||
|
||||
# Generate and set a random password. Useful for automated user creation
|
||||
# Based on Token#generate_token_value
|
||||
#
|
||||
def random_password
|
||||
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
|
||||
password = ''
|
||||
40.times { |i| password << chars[rand(chars.size-1)] }
|
||||
self.password = password
|
||||
self.password_confirmation = password
|
||||
self
|
||||
end
|
||||
|
||||
def pref
|
||||
self.preference ||= UserPreference.new(:user => self)
|
||||
|
||||
@@ -51,20 +51,20 @@ class Version < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def completed_pourcent
|
||||
if issues_count == 0
|
||||
if fixed_issues.count == 0
|
||||
0
|
||||
elsif open_issues_count == 0
|
||||
100
|
||||
else
|
||||
issues_progress(false) + issues_progress(true)
|
||||
(closed_issues_count * 100 + Issue.sum('done_ratio', :include => 'status', :conditions => ["fixed_version_id = ? AND is_closed = ?", id, false]).to_f) / fixed_issues.count
|
||||
end
|
||||
end
|
||||
|
||||
def closed_pourcent
|
||||
if issues_count == 0
|
||||
if fixed_issues.count == 0
|
||||
0
|
||||
else
|
||||
issues_progress(false)
|
||||
closed_issues_count * 100.0 / fixed_issues.count
|
||||
end
|
||||
end
|
||||
|
||||
@@ -73,11 +73,6 @@ class Version < ActiveRecord::Base
|
||||
effective_date && (effective_date < Date.today) && (open_issues_count > 0)
|
||||
end
|
||||
|
||||
# Returns assigned issues count
|
||||
def issues_count
|
||||
@issue_count ||= fixed_issues.count
|
||||
end
|
||||
|
||||
def open_issues_count
|
||||
@open_issues_count ||= Issue.count(:all, :conditions => ["fixed_version_id = ? AND is_closed = ?", self.id, false], :include => :status)
|
||||
end
|
||||
@@ -109,35 +104,4 @@ private
|
||||
def check_integrity
|
||||
raise "Can't delete version" if self.fixed_issues.find(:first)
|
||||
end
|
||||
|
||||
# Returns the average estimated time of assigned issues
|
||||
# or 1 if no issue has an estimated time
|
||||
# Used to weigth unestimated issues in progress calculation
|
||||
def estimated_average
|
||||
if @estimated_average.nil?
|
||||
average = fixed_issues.average(:estimated_hours).to_f
|
||||
if average == 0
|
||||
average = 1
|
||||
end
|
||||
@estimated_average = average
|
||||
end
|
||||
@estimated_average
|
||||
end
|
||||
|
||||
# Returns the total progress of open or closed issues
|
||||
def issues_progress(open)
|
||||
@issues_progress ||= {}
|
||||
@issues_progress[open] ||= begin
|
||||
progress = 0
|
||||
if issues_count > 0
|
||||
ratio = open ? 'done_ratio' : 100
|
||||
done = fixed_issues.sum("COALESCE(estimated_hours, #{estimated_average}) * #{ratio}",
|
||||
:include => :status,
|
||||
:conditions => ["is_closed = ?", !open]).to_f
|
||||
|
||||
progress = done / (estimated_average * issues_count)
|
||||
end
|
||||
progress
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,12 +10,6 @@
|
||||
<td align="right"><label for="password"><%=l(:field_password)%>:</label></td>
|
||||
<td align="left"><%= password_field_tag 'password', nil, :size => 40 %></td>
|
||||
</tr>
|
||||
<% if Setting.openid? %>
|
||||
<tr>
|
||||
<td align="right"><label for="openid_url"><%=l(:field_identity_url)%></label></td>
|
||||
<td align="left"><%= text_field_tag "openid_url" %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td align="left">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<h2><%=l(:label_register)%> <%=link_to l(:label_login_with_open_id_option), signin_url if Setting.openid? %></h2>
|
||||
<h2><%=l(:label_register)%></h2>
|
||||
|
||||
<% form_tag({:action => 'register'}, :class => "tabular") do %>
|
||||
<%= error_messages_for 'user' %>
|
||||
@@ -29,12 +29,7 @@
|
||||
<p><label for="user_language"><%=l(:field_language)%></label>
|
||||
<%= select("user", "language", lang_options_for_select) %></p>
|
||||
|
||||
<% if Setting.openid? %>
|
||||
<p><label for="user_identity_url"><%=l(:field_identity_url)%></label>
|
||||
<%= text_field 'user', 'identity_url' %></p>
|
||||
<% end %>
|
||||
|
||||
<% @user.custom_field_values.select {|v| v.editable? || v.required?}.each do |value| %>
|
||||
<% @user.custom_field_values.each do |value| %>
|
||||
<p><%= custom_field_tag_with_label :user, value %></p>
|
||||
<% end %>
|
||||
<!--[eoform:user]-->
|
||||
|
||||
@@ -55,9 +55,10 @@
|
||||
<% end -%>
|
||||
</div>
|
||||
|
||||
<% other_formats_links do |f| %>
|
||||
<%= f.link_to 'Atom', :url => {:controller => 'projects', :action => 'activity', :id => nil, :user_id => @user, :key => User.current.rss_key} %>
|
||||
<% end %>
|
||||
<p class="other-formats">
|
||||
<%= l(:label_export_to) %>
|
||||
<%= link_to 'Atom', {:controller => 'projects', :action => 'activity', :user_id => @user, :format => :atom, :key => User.current.rss_key}, :class => 'feed' %>
|
||||
</p>
|
||||
|
||||
<% content_for :header_tags do %>
|
||||
<%= auto_discovery_link_tag(:atom, :controller => 'projects', :action => 'activity', :user_id => @user, :format => :atom, :key => User.current.rss_key) %>
|
||||
|
||||
@@ -17,20 +17,22 @@
|
||||
|
||||
<table class="list">
|
||||
<thead><tr>
|
||||
<th><%=l(:label_project)%></th>
|
||||
<%= sort_header_tag('name', :caption => l(:label_project)) %>
|
||||
<th><%=l(:field_description)%></th>
|
||||
<th><%=l(:field_is_public)%></th>
|
||||
<th><%=l(:field_created_on)%></th>
|
||||
<th><%=l(:label_subproject_plural)%></th>
|
||||
<%= sort_header_tag('is_public', :caption => l(:field_is_public), :default_order => 'desc') %>
|
||||
<%= sort_header_tag('created_on', :caption => l(:field_created_on), :default_order => 'desc') %>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<% for project in @projects %>
|
||||
<tr class="<%= cycle("odd", "even") %> <%= css_project_classes(project) %>">
|
||||
<td class="name" style="padding-left: <%= project.level %>em;"><%= project.active? ? link_to(h(project.name), :controller => 'projects', :action => 'settings', :id => project) : h(project.name) %></td>
|
||||
<td><%= textilizable project.short_description, :project => project %></td>
|
||||
<td align="center"><%= image_tag 'true.png' if project.is_public? %></td>
|
||||
<td align="center"><%= format_date(project.created_on) %></td>
|
||||
<tr class="<%= cycle("odd", "even") %>">
|
||||
<td><%= project.active? ? link_to(h(project.name), :controller => 'projects', :action => 'settings', :id => project) : h(project.name) %>
|
||||
<td><%= textilizable project.short_description, :project => project %>
|
||||
<td align="center"><%= project.children.size %>
|
||||
<td align="center"><%= image_tag 'true.png' if project.is_public? %>
|
||||
<td align="center"><%= format_date(project.created_on) %>
|
||||
<td align="center" style="width:10%">
|
||||
<small>
|
||||
<%= link_to(l(:button_archive), { :controller => 'projects', :action => 'archive', :id => project }, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-lock') if project.active? %>
|
||||
@@ -45,4 +47,6 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p class="pagination"><%= pagination_links_full @project_pages, @project_count %></p>
|
||||
|
||||
<% html_title(l(:label_project_plural)) -%>
|
||||
|
||||
@@ -29,9 +29,11 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<% other_formats_links do |f| %>
|
||||
<%= f.link_to 'Atom', :url => {:controller => 'projects', :action => 'activity', :id => @project, :show_messages => 1, :key => User.current.rss_key} %>
|
||||
<% end %>
|
||||
<p class="other-formats">
|
||||
<%= l(:label_export_to) %>
|
||||
<span><%= link_to 'Atom', {:controller => 'projects', :action => 'activity', :id => @project, :format => 'atom', :show_messages => 1, :key => User.current.rss_key},
|
||||
:class => 'feed' %></span>
|
||||
</p>
|
||||
|
||||
<% content_for :header_tags do %>
|
||||
<%= auto_discovery_link_tag(:atom, {:controller => 'projects', :action => 'activity', :id => @project, :format => 'atom', :show_messages => 1, :key => User.current.rss_key}) %>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<%= link_to_if_authorized l(:label_message_new),
|
||||
{:controller => 'messages', :action => 'new', :board_id => @board},
|
||||
:class => 'icon icon-add',
|
||||
:onclick => 'Element.show("add-message"); Form.Element.focus("message_subject"); return false;' %>
|
||||
:onclick => 'Element.show("add-message"); return false;' %>
|
||||
<%= watcher_tag(@board, User.current) %>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -49,6 +49,23 @@ function toggle_custom_field_format() {
|
||||
}
|
||||
}
|
||||
|
||||
function addValueField() {
|
||||
var f = $$('p#custom_field_possible_values span');
|
||||
p = document.getElementById("custom_field_possible_values");
|
||||
var v = f[0].cloneNode(true);
|
||||
v.childNodes[0].value = "";
|
||||
p.appendChild(v);
|
||||
}
|
||||
|
||||
function deleteValueField(e) {
|
||||
var f = $$('p#custom_field_possible_values span');
|
||||
if (f.length == 1) {
|
||||
e.parentNode.childNodes[0].value = "";
|
||||
} else {
|
||||
Element.remove(e.parentNode);
|
||||
}
|
||||
}
|
||||
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
@@ -59,22 +76,22 @@ function toggle_custom_field_format() {
|
||||
<%= f.text_field :min_length, :size => 5, :no_label => true %> -
|
||||
<%= f.text_field :max_length, :size => 5, :no_label => true %><br>(<%=l(:text_min_max_length_info)%>)</p>
|
||||
<p><%= f.text_field :regexp, :size => 50 %><br>(<%=l(:text_regexp_info)%>)</p>
|
||||
<p id="custom_field_possible_values"><%= f.text_area :possible_values, :value => @custom_field.possible_values.to_a.join("\n"),
|
||||
:cols => 20,
|
||||
:rows => 15 %>
|
||||
<br /><em><%= l(:text_custom_field_possible_values_info) %></em></p>
|
||||
<p id="custom_field_possible_values"><label><%= l(:field_possible_values) %> <%= image_to_function "add.png", "addValueField();return false" %></label>
|
||||
<% (@custom_field.possible_values.to_a + [""]).each do |value| %>
|
||||
<span><%= text_field_tag 'custom_field[possible_values][]', value, :size => 30 %> <%= image_to_function "delete.png", "deleteValueField(this);return false" %><br /></span>
|
||||
<% end %>
|
||||
</p>
|
||||
<p><%= @custom_field.field_format == 'bool' ? f.check_box(:default_value) : f.text_field(:default_value) %></p>
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<% case @custom_field.class.name
|
||||
<% case @custom_field.type.to_s
|
||||
when "IssueCustomField" %>
|
||||
|
||||
<fieldset><legend><%=l(:label_tracker_plural)%></legend>
|
||||
<% for tracker in @trackers %>
|
||||
<%= check_box_tag "custom_field[tracker_ids][]", tracker.id, (@custom_field.trackers.include? tracker) %> <%= tracker.name %>
|
||||
<%= check_box_tag "tracker_ids[]", tracker.id, (@custom_field.trackers.include? tracker) %> <%= tracker.name %>
|
||||
<% end %>
|
||||
<%= hidden_field_tag "custom_field[tracker_ids][]", '' %>
|
||||
</fieldset>
|
||||
|
||||
<p><%= f.check_box :is_required %></p>
|
||||
@@ -84,7 +101,6 @@ when "IssueCustomField" %>
|
||||
|
||||
<% when "UserCustomField" %>
|
||||
<p><%= f.check_box :is_required %></p>
|
||||
<p><%= f.check_box :editable %></p>
|
||||
|
||||
<% when "ProjectCustomField" %>
|
||||
<p><%= f.check_box :is_required %></p>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<%= link_to_if_authorized l(:label_document_new),
|
||||
{:controller => 'documents', :action => 'new', :project_id => @project},
|
||||
:class => 'icon icon-add',
|
||||
:onclick => 'Element.show("add-document"); Form.Element.focus("document_title"); return false;' %>
|
||||
:onclick => 'Element.show("add-document"); return false;' %>
|
||||
</div>
|
||||
|
||||
<div id="add-document" style="display:none;">
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
:class => 'wiki-edit' %></p>
|
||||
</div>
|
||||
|
||||
<div class="attributes">
|
||||
<div class="splitcontentleft">
|
||||
<% if @issue.new_record? || @allowed_statuses.any? %>
|
||||
<p><%= f.select :status_id, (@allowed_statuses.collect {|p| [p.name, p.id]}), :required => true %></p>
|
||||
@@ -46,7 +45,6 @@
|
||||
|
||||
<div style="clear:both;"> </div>
|
||||
<%= render :partial => 'form_custom_fields' %>
|
||||
</div>
|
||||
|
||||
<% if @issue.new_record? %>
|
||||
<p><label><%=l(:label_attachment_plural)%></label><%= render :partial => 'attachments/form' %></p>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
<div class="attributes">
|
||||
<div class="splitcontentleft">
|
||||
<p><%= f.select :status_id, (@allowed_statuses.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>
|
||||
@@ -9,4 +8,3 @@
|
||||
(@project.versions.sort.collect {|v| [v.name, v.id]}),
|
||||
{ :include_blank => true })) unless @project.versions.empty? %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<table class="list issues">
|
||||
<thead><tr>
|
||||
<th>#</th>
|
||||
<th><%=l(:field_project)%></th>
|
||||
<th><%=l(:field_tracker)%></th>
|
||||
<th><%=l(:field_subject)%></th>
|
||||
</tr></thead>
|
||||
@@ -11,14 +10,14 @@
|
||||
<% for issue in issues %>
|
||||
<tr id="issue-<%= issue.id %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= css_issue_classes(issue) %>">
|
||||
<td class="id">
|
||||
<%= check_box_tag("ids[]", issue.id, false, :style => 'display:none;') %>
|
||||
<%= check_box_tag("ids[]", issue.id, false, :style => 'display:none;') %>
|
||||
<%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %>
|
||||
</td>
|
||||
<td class="project"><%=h issue.project %></td>
|
||||
<td class="tracker"><%=h issue.tracker %></td>
|
||||
<td><%=h issue.project.name %> - <%= issue.tracker.name %><br />
|
||||
<%= issue.status.name %> - <%= format_time(issue.updated_on) %></td>
|
||||
<td class="subject">
|
||||
<%= link_to h(truncate(issue.subject, 60)), :controller => 'issues', :action => 'show', :id => issue %> (<%=h issue.status %>)
|
||||
</td>
|
||||
<%= link_to h(issue.subject), :controller => 'issues', :action => 'show', :id => issue %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
<% if @issue.relations.any? %>
|
||||
<table style="width:100%">
|
||||
<% @issue.relations.select {|r| r.other_issue(@issue).visible? }.each do |relation| %>
|
||||
<% @issue.relations.each do |relation| %>
|
||||
<tr>
|
||||
<td><%= l(relation.label_for(@issue)) %> <%= "(#{lwr(:actionview_datehelper_time_in_words_day, relation.delay)})" if relation.delay && relation.delay != 0 %>
|
||||
<%= h(relation.other_issue(@issue).project) + ' - ' if Setting.cross_project_issue_relations? %> <%= link_to_issue relation.other_issue(@issue) %></td>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<label><%= l(:field_fixed_version) %>:
|
||||
<%= select_tag('fixed_version_id', content_tag('option', l(:label_no_change_option), :value => '') +
|
||||
content_tag('option', l(:label_none), :value => 'none') +
|
||||
options_from_collection_for_select(@project.versions.sort, :id, :name)) %></label>
|
||||
options_from_collection_for_select(@project.versions, :id, :name)) %></label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -38,13 +38,6 @@
|
||||
<label><%= l(:field_done_ratio) %>:
|
||||
<%= select_tag 'done_ratio', options_for_select([[l(:label_no_change_option), '']] + (0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></label>
|
||||
</p>
|
||||
|
||||
<% @custom_fields.each do |custom_field| %>
|
||||
<p><label><%= h(custom_field.name) %></label>
|
||||
<%= select_tag "custom_field_values[#{custom_field.id}]", options_for_select([[l(:label_no_change_option), '']] + custom_field.possible_values) %></label>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<%= call_hook(:view_issues_bulk_edit_details_bottom, { :issues => @issues }) %>
|
||||
</fieldset>
|
||||
|
||||
|
||||
@@ -79,10 +79,7 @@
|
||||
:class => 'icon-copy', :disabled => !@can[:copy] %></li>
|
||||
<% if @can[:log_time] -%>
|
||||
<li><%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue},
|
||||
:class => 'icon-time-add' %></li>
|
||||
<% end %>
|
||||
<% if User.current.logged? %>
|
||||
<li><%= watcher_link(@issue, User.current) %></li>
|
||||
:class => 'icon-time' %></li>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
|
||||
@@ -241,10 +241,13 @@ if Date.today >= @gantt.date_from and Date.today <= @gantt.date_to %>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<% other_formats_links do |f| %>
|
||||
<%= f.link_to 'PDF', :url => @gantt.params %>
|
||||
<%= f.link_to('PNG', :url => @gantt.params) if @gantt.respond_to?('to_image') %>
|
||||
<p class="other-formats">
|
||||
<%= l(:label_export_to) %>
|
||||
<span><%= link_to 'PDF', @gantt.params.merge(:format => 'pdf'), :class => 'pdf' %></span>
|
||||
<% if @gantt.respond_to?('to_image') %>
|
||||
<span><%= link_to 'PNG', @gantt.params.merge(:format => 'png'), :class => 'image' %></span>
|
||||
<% end %>
|
||||
</p>
|
||||
<% end # query.valid? %>
|
||||
|
||||
<% content_for :sidebar do %>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<% html_title(l(:label_issue_plural)) %>
|
||||
|
||||
<% form_tag({ :controller => 'queries', :action => 'new' }, :id => 'query_form') do %>
|
||||
<%= hidden_field_tag('project_id', @project.to_param) if @project %>
|
||||
<%= hidden_field_tag('project_id', @project.id) if @project %>
|
||||
<fieldset id="filters"><legend><%= l(:label_filter_plural) %></legend>
|
||||
<%= render :partial => 'queries/filters', :locals => {:query => @query} %>
|
||||
<p class="buttons">
|
||||
@@ -14,8 +14,7 @@
|
||||
}, :class => 'icon icon-checked' %>
|
||||
|
||||
<%= link_to_remote l(:button_clear),
|
||||
{ :url => { :set_filter => 1, :project_id => @project },
|
||||
:method => :get,
|
||||
{ :url => { :set_filter => 1 },
|
||||
:update => "content",
|
||||
}, :class => 'icon icon-reload' %>
|
||||
|
||||
@@ -43,14 +42,14 @@
|
||||
<% else %>
|
||||
<%= render :partial => 'issues/list', :locals => {:issues => @issues, :query => @query} %>
|
||||
<p class="pagination"><%= pagination_links_full @issue_pages, @issue_count %></p>
|
||||
|
||||
<% other_formats_links do |f| %>
|
||||
<%= f.link_to 'Atom', :url => {:query_id => (@query.new_record? ? nil : @query), :key => User.current.rss_key} %>
|
||||
<%= f.link_to 'CSV' %>
|
||||
<%= f.link_to 'PDF' %>
|
||||
<% end %>
|
||||
|
||||
<% end %>
|
||||
<p class="other-formats">
|
||||
<%= l(:label_export_to) %>
|
||||
<span><%= link_to 'Atom', {:query_id => @query, :format => 'atom', :key => User.current.rss_key}, :class => 'feed' %></span>
|
||||
<span><%= link_to 'CSV', {:format => 'csv'}, :class => 'csv' %></span>
|
||||
<span><%= link_to 'PDF', {:format => 'pdf'}, :class => 'pdf' %></span>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<% content_for :sidebar do %>
|
||||
|
||||
@@ -6,19 +6,16 @@
|
||||
<%= @issues.collect {|i| hidden_field_tag('ids[]', i.id)}.join %>
|
||||
|
||||
<div class="box tabular">
|
||||
<p><label for="new_project_id"><%=l(:field_project)%>:</label>
|
||||
<p><label for="new_project_id"><%=l(:field_project)%> :</label>
|
||||
<%= select_tag "new_project_id",
|
||||
project_tree_options_for_select(@allowed_projects, :selected => @target_project),
|
||||
options_from_collection_for_select(@allowed_projects, 'id', 'name', @target_project.id),
|
||||
:onchange => remote_function(:url => { :action => 'move' },
|
||||
:method => :get,
|
||||
:update => 'content',
|
||||
:with => "Form.serialize('move_form')") %></p>
|
||||
|
||||
<p><label for="new_tracker_id"><%=l(:field_tracker)%>:</label>
|
||||
<p><label for="new_tracker_id"><%=l(:field_tracker)%> :</label>
|
||||
<%= select_tag "new_tracker_id", "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@trackers, "id", "name") %></p>
|
||||
|
||||
<p><label for="copy_options_copy"><%= l(:button_copy)%></label>
|
||||
<%= check_box_tag "copy_options[copy]", "1" %></p>
|
||||
</div>
|
||||
|
||||
<%= submit_tag l(:button_move) %>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="contextual">
|
||||
<%= link_to_if_authorized(l(:button_update), {:controller => 'issues', :action => 'edit', :id => @issue }, :onclick => 'showAndScrollTo("update", "notes"); return false;', :class => 'icon icon-edit', :accesskey => accesskey(:edit)) %>
|
||||
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue}, :class => 'icon icon-time-add' %>
|
||||
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue}, :class => 'icon icon-time' %>
|
||||
<%= watcher_tag(@issue, User.current) %>
|
||||
<%= link_to_if_authorized l(:button_copy), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue }, :class => 'icon icon-copy' %>
|
||||
<%= link_to_if_authorized l(:button_move), {:controller => 'issues', :action => 'move', :id => @issue }, :class => 'icon icon-move' %>
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
<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>
|
||||
@@ -34,7 +34,7 @@
|
||||
<td class="category"><b><%=l(:field_category)%>:</b></td><td><%=h @issue.category ? @issue.category.name : "-" %></td>
|
||||
<% if User.current.allowed_to?(:view_time_entries, @project) %>
|
||||
<td class="spent-time"><b><%=l(:label_spent_time)%>:</b></td>
|
||||
<td class="spent-hours"><%= @issue.spent_hours > 0 ? (link_to lwr(:label_f_hour, @issue.spent_hours), {:controller => 'timelog', :action => 'details', :project_id => @project, :issue_id => @issue}) : "-" %></td>
|
||||
<td class="spent-hours"><%= @issue.spent_hours > 0 ? (link_to lwr(:label_f_hour, @issue.spent_hours), {:controller => 'timelog', :action => 'details', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time') : "-" %></td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -108,10 +108,11 @@ end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% other_formats_links do |f| %>
|
||||
<%= f.link_to 'Atom', :url => {:key => User.current.rss_key} %>
|
||||
<%= f.link_to 'PDF' %>
|
||||
<% end %>
|
||||
<p class="other-formats">
|
||||
<%= l(:label_export_to) %>
|
||||
<span><%= link_to 'Atom', {:format => 'atom', :key => User.current.rss_key}, :class => 'feed' %></span>
|
||||
<span><%= link_to 'PDF', {:format => 'pdf'}, :class => 'pdf' %></span>
|
||||
</p>
|
||||
|
||||
<% html_title "#{@issue.tracker.name} ##{@issue.id}: #{@issue.subject}" %>
|
||||
|
||||
|
||||
12
app/views/layouts/_project_selector.rhtml
Normal file
12
app/views/layouts/_project_selector.rhtml
Normal file
@@ -0,0 +1,12 @@
|
||||
<% user_projects_by_root = User.current.projects.find(:all, :include => :parent).group_by(&:root) %>
|
||||
<select onchange="if (this.value != '') { window.location = this.value; }">
|
||||
<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, :jump => current_menu_item)) %>
|
||||
<% user_projects_by_root[root].sort.each do |project| %>
|
||||
<% next if project == root %>
|
||||
<%= content_tag('option', ('» ' + h(project.name)), :value => url_for(:controller => 'projects', :action => 'show', :id => project, :jump => current_menu_item)) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</select>
|
||||
@@ -34,7 +34,7 @@
|
||||
<%= link_to l(:label_search), {:controller => 'search', :action => 'index', :id => @project}, :accesskey => accesskey(:search) %>:
|
||||
<%= text_field_tag 'q', @question, :size => 20, :class => 'small', :accesskey => accesskey(:quick_search) %>
|
||||
<% end %>
|
||||
<%= render_project_jump_box %>
|
||||
<%= render :partial => 'layouts/project_selector' if User.current.memberships.any? %>
|
||||
</div>
|
||||
|
||||
<h1><%= h(@project && !@project.new_record? ? @project.name : Setting.app_title) %></h1>
|
||||
|
||||
2
app/views/mailer/account_activated.text.html.rhtml
Normal file
2
app/views/mailer/account_activated.text.html.rhtml
Normal file
@@ -0,0 +1,2 @@
|
||||
<p><%= l(:notice_account_activated) %></p>
|
||||
<p><%= l(:label_login) %>: <%= link_to @login_url, @login_url %></p>
|
||||
2
app/views/mailer/account_activated.text.plain.rhtml
Normal file
2
app/views/mailer/account_activated.text.plain.rhtml
Normal file
@@ -0,0 +1,2 @@
|
||||
<%= l(:notice_account_activated) %>
|
||||
<%= l(:label_login) %>: <%= @login_url %>
|
||||
@@ -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;
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
<h3 class="icon22 icon22-comment"><%= l(:label_reply_plural) %></h3>
|
||||
<% @replies.each do |message| %>
|
||||
<a name="<%= "message-#{message.id}" %>"></a>
|
||||
<div class="contextual">
|
||||
<%= link_to_remote_if_authorized image_tag('comment.png'), { :url => {:action => 'quote', :id => message} }, :title => l(:button_quote) %>
|
||||
<%= link_to(image_tag('edit.png'), {:action => 'edit', :id => message}, :title => l(:button_edit)) if message.editable_by?(User.current) %>
|
||||
<%= link_to(image_tag('delete.png'), {:action => 'destroy', :id => message}, :method => :post, :confirm => l(:text_are_you_sure), :title => l(:button_delete)) if message.destroyable_by?(User.current) %>
|
||||
</div>
|
||||
<div class="message reply">
|
||||
<div class="contextual">
|
||||
<%= link_to_remote_if_authorized image_tag('comment.png'), { :url => {:action => 'quote', :id => message} }, :title => l(:button_quote) %>
|
||||
<%= link_to(image_tag('edit.png'), {:action => 'edit', :id => message}, :title => l(:button_edit)) if message.editable_by?(User.current) %>
|
||||
<%= link_to(image_tag('delete.png'), {:action => 'destroy', :id => message}, :method => :post, :confirm => l(:text_are_you_sure), :title => l(:button_delete)) if message.destroyable_by?(User.current) %>
|
||||
</div>
|
||||
<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, :author => false %>
|
||||
|
||||
@@ -15,13 +15,6 @@
|
||||
<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>
|
||||
<% if Setting.openid? %>
|
||||
<p><%= f.text_field :identity_url %></p>
|
||||
<% end %>
|
||||
|
||||
<% @user.custom_field_values.select(&:editable?).each do |value| %>
|
||||
<p><%= custom_field_tag_with_label :user, value %></p>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<%= submit_tag l(:button_save) %>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<h3><%=l(:label_assigned_to_me_issues)%></h3>
|
||||
<% assigned_issues = Issue.visible.find(:all,
|
||||
:conditions => ["assigned_to_id=? AND #{IssueStatus.table_name}.is_closed=?", user.id, false],
|
||||
<% assigned_issues = Issue.find(:all,
|
||||
:conditions => ["assigned_to_id=? AND #{IssueStatus.table_name}.is_closed=? AND #{Project.table_name}.status=#{Project::STATUS_ACTIVE}", user.id, false],
|
||||
:limit => 10,
|
||||
:include => [ :status, :project, :tracker, :priority ],
|
||||
:order => "#{Enumeration.table_name}.position DESC, #{Issue.table_name}.updated_on DESC") %>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<h3><%=l(:label_reported_issues)%></h3>
|
||||
<% reported_issues = Issue.visible.find(:all,
|
||||
:conditions => { :author_id => user.id },
|
||||
<% reported_issues = Issue.find(:all,
|
||||
:conditions => ["author_id=? AND #{Project.table_name}.status=#{Project::STATUS_ACTIVE}", user.id],
|
||||
:limit => 10,
|
||||
:include => [ :status, :project, :tracker ],
|
||||
:order => "#{Issue.table_name}.updated_on DESC") %>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<h3><%=l(:label_watched_issues)%></h3>
|
||||
<% watched_issues = Issue.visible.find(:all,
|
||||
<% watched_issues = Issue.find(:all,
|
||||
:include => [:status, :project, :tracker, :watchers],
|
||||
:limit => 10,
|
||||
:conditions => ["#{Watcher.table_name}.user_id = ?", user.id],
|
||||
:conditions => ["#{Watcher.table_name}.user_id = ? AND #{Project.table_name}.status=#{Project::STATUS_ACTIVE}", user.id],
|
||||
:order => "#{Issue.table_name}.updated_on DESC") %>
|
||||
<%= render :partial => 'issues/list_simple', :locals => { :issues => watched_issues } %>
|
||||
<% if watched_issues.length > 0 %>
|
||||
<p class="small"><%= link_to l(:label_issue_view_all), :controller => 'issues', :action => 'index', :set_filter => 1, :watcher_id => 'me' %></p>
|
||||
<p><%=lwr(:label_last_updates, watched_issues.length)%></p>
|
||||
<% end %>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<%= link_to_if_authorized(l(:label_news_new),
|
||||
{:controller => 'news', :action => 'new', :project_id => @project},
|
||||
:class => 'icon icon-add',
|
||||
:onclick => 'Element.show("add-news"); Form.Element.focus("news_title"); return false;') if @project %>
|
||||
:onclick => 'Element.show("add-news"); return false;') if @project %>
|
||||
</div>
|
||||
|
||||
<div id="add-news" style="display:none;">
|
||||
@@ -39,9 +39,10 @@
|
||||
<% end %>
|
||||
<p class="pagination"><%= pagination_links_full @news_pages %></p>
|
||||
|
||||
<% other_formats_links do |f| %>
|
||||
<%= f.link_to 'Atom', :url => {:project_id => @project, :key => User.current.rss_key} %>
|
||||
<% end %>
|
||||
<p class="other-formats">
|
||||
<%= l(:label_export_to) %>
|
||||
<span><%= link_to 'Atom', {:format => 'atom', :key => User.current.rss_key}, :class => 'feed' %></span>
|
||||
</p>
|
||||
|
||||
<% content_for :header_tags do %>
|
||||
<%= auto_discovery_link_tag(:atom, params.merge({:format => 'atom', :page => nil, :key => User.current.rss_key})) %>
|
||||
|
||||
@@ -48,10 +48,8 @@
|
||||
<% if authorize_for 'news', 'add_comment' %>
|
||||
<p><%= toggle_link l(:label_comment_add), "add_comment_form", :focus => "comment_comments" %></p>
|
||||
<% form_tag({:action => 'add_comment', :id => @news}, :id => "add_comment_form", :style => "display:none;") do %>
|
||||
<div class="box">
|
||||
<%= text_area 'comment', 'comments', :cols => 80, :rows => 15, :class => 'wiki-edit' %>
|
||||
<%= wikitoolbar_for 'comment_comments' %>
|
||||
</div>
|
||||
<%= text_area 'comment', 'comments', :cols => 80, :rows => 15, :class => 'wiki-edit' %>
|
||||
<%= wikitoolbar_for 'comment_comments' %>
|
||||
<p><%= submit_tag l(:button_add) %></p>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
<!--[form:project]-->
|
||||
<p><%= f.text_field :name, :required => true %><br /><em><%= l(:text_caracters_maximum, 30) %></em></p>
|
||||
|
||||
<% if User.current.admin? && !@project.possible_parents.empty? %>
|
||||
<p><label><%= l(:field_parent) %></label><%= parent_project_select_tag(@project) %></p>
|
||||
<% if User.current.admin? and !@root_projects.empty? %>
|
||||
<p><%= f.select :parent_id, (@root_projects.collect {|p| [p.name, p.id]}), { :include_blank => true } %></p>
|
||||
<% end %>
|
||||
|
||||
<p><%= f.text_area :description, :rows => 5, :class => 'wiki-edit' %></p>
|
||||
|
||||
@@ -22,20 +22,21 @@
|
||||
|
||||
<div style="float:left;">
|
||||
<%= link_to_remote(('« ' + l(:label_previous)),
|
||||
{:update => "content", :url => params.merge(:from => @date_to - @days - 1), :method => :get, :complete => 'window.scrollTo(0,0)'},
|
||||
{:update => "content", :url => params.merge(:from => @date_to - @days - 1), :complete => 'window.scrollTo(0,0)'},
|
||||
{:href => url_for(params.merge(:from => @date_to - @days - 1)),
|
||||
:title => "#{l(:label_date_from)} #{format_date(@date_to - 2*@days)} #{l(:label_date_to).downcase} #{format_date(@date_to - @days - 1)}"}) %>
|
||||
</div>
|
||||
<div style="float:right;">
|
||||
<%= link_to_remote((l(:label_next) + ' »'),
|
||||
{:update => "content", :url => params.merge(:from => @date_to + @days - 1), :method => :get, :complete => 'window.scrollTo(0,0)'},
|
||||
{:update => "content", :url => params.merge(:from => @date_to + @days - 1), :complete => 'window.scrollTo(0,0)'},
|
||||
{:href => url_for(params.merge(:from => @date_to + @days - 1)),
|
||||
:title => "#{l(:label_date_from)} #{format_date(@date_to)} #{l(:label_date_to).downcase} #{format_date(@date_to + @days - 1)}"}) unless @date_to >= Date.today %>
|
||||
</div>
|
||||
|
||||
<% other_formats_links do |f| %>
|
||||
<%= f.link_to 'Atom', :url => params.merge(:from => nil, :key => User.current.rss_key) %>
|
||||
<% end %>
|
||||
<p class="other-formats">
|
||||
<%= l(:label_export_to) %>
|
||||
<%= link_to 'Atom', params.merge(:format => :atom, :from => nil, :key => User.current.rss_key), :class => 'feed' %>
|
||||
</p>
|
||||
|
||||
<% content_for :header_tags do %>
|
||||
<%= auto_discovery_link_tag(:atom, params.merge(:format => 'atom', :from => nil, :key => User.current.rss_key)) %>
|
||||
@@ -45,9 +46,11 @@
|
||||
<% 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.descendants.active.any? %>
|
||||
<% if @project && @project.active_children.any? %>
|
||||
<p><label><%= check_box_tag 'with_subprojects', 1, @with_subprojects %> <%=l(:label_subproject_plural)%></label></p>
|
||||
<%= hidden_field_tag 'with_subprojects', 0 %>
|
||||
<% end %>
|
||||
|
||||
@@ -13,5 +13,4 @@
|
||||
</fieldset>
|
||||
|
||||
<%= submit_tag l(:button_save) %>
|
||||
<%= javascript_tag "Form.Element.focus('project_name');" %>
|
||||
<% end %>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<% end %>
|
||||
|
||||
<% content_for :sidebar do %>
|
||||
<% form_tag({},:method => :get) do %>
|
||||
<% form_tag do %>
|
||||
<h3><%= l(:label_change_log) %></h3>
|
||||
<% @trackers.each do |tracker| %>
|
||||
<label><%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s) %>
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
<p><strong><%=h @project_to_destroy %></strong><br />
|
||||
<%=l(:text_project_destroy_confirmation)%>
|
||||
|
||||
<% if @project_to_destroy.descendants.any? %>
|
||||
<br /><%= l(:text_subprojects_destroy_warning, content_tag('strong', h(@project_to_destroy.descendants.collect{|p| p.to_s}.join(', ')))) %>
|
||||
<% if @project_to_destroy.children.any? %>
|
||||
<br /><%= l(:text_subprojects_destroy_warning, content_tag('strong', h(@project_to_destroy.children.sort.collect{|p| p.to_s}.join(', ')))) %>
|
||||
<% end %>
|
||||
</p>
|
||||
<p>
|
||||
|
||||
@@ -6,16 +6,26 @@
|
||||
|
||||
<h2><%=l(:label_project_plural)%></h2>
|
||||
|
||||
<%= render_project_hierarchy(@projects)%>
|
||||
<% @project_tree.keys.sort.each do |project| %>
|
||||
<h3><%= link_to h(project.name), {:action => 'show', :id => project}, :class => (User.current.member_of?(project) ? "icon icon-fav" : "") %></h3>
|
||||
<%= textilizable(project.short_description, :project => project) %>
|
||||
|
||||
<% if @project_tree[project].any? %>
|
||||
<p><%= l(:label_subproject_plural) %>:
|
||||
<%= @project_tree[project].sort.collect {|subproject|
|
||||
link_to(h(subproject.name), {:action => 'show', :id => subproject}, :class => (User.current.member_of?(subproject) ? "icon icon-fav" : ""))}.join(', ') %></p>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% if User.current.logged? %>
|
||||
<p style="text-align:right;">
|
||||
<span class="my-project"><%= l(:label_my_projects) %></span>
|
||||
<span class="icon icon-fav"><%= l(:label_my_projects) %></span>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<% other_formats_links do |f| %>
|
||||
<%= f.link_to 'Atom', :url => {:key => User.current.rss_key} %>
|
||||
<% end %>
|
||||
<p class="other-formats">
|
||||
<%= l(:label_export_to) %>
|
||||
<span><%= link_to 'Atom', {:format => 'atom', :key => User.current.rss_key}, :class => 'feed' %></span>
|
||||
</p>
|
||||
|
||||
<% html_title(l(:label_project_plural)) -%>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<% delete_allowed = User.current.allowed_to?(:manage_files, @project) %>
|
||||
|
||||
<table class="list">
|
||||
<table class="list files">
|
||||
<thead><tr>
|
||||
<%= sort_header_tag('filename', :caption => l(:field_filename)) %>
|
||||
<%= sort_header_tag('created_on', :caption => l(:label_date), :default_order => 'desc') %>
|
||||
@@ -19,15 +19,19 @@
|
||||
<% @containers.each do |container| %>
|
||||
<% next if container.attachments.empty? -%>
|
||||
<% if container.is_a?(Version) -%>
|
||||
<tr><th colspan="6" align="left"><span class="icon icon-package"><b><%=h container %></b></span></th></tr>
|
||||
<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="<%= cycle("odd", "even") %>">
|
||||
<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>
|
||||
<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(image_tag('delete.png'), {:controller => 'attachments', :action => 'destroy', :id => file},
|
||||
:confirm => l(:text_are_you_sure), :method => :post) if delete_allowed %>
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
</ul>
|
||||
</fieldset>
|
||||
<% end %>
|
||||
<%= call_hook :view_projects_roadmap_version_bottom, :version => version %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -4,13 +4,11 @@
|
||||
<%= textilizable @project.description %>
|
||||
<ul>
|
||||
<% unless @project.homepage.blank? %><li><%=l(:field_homepage)%>: <%= link_to(h(@project.homepage), @project.homepage) %></li><% end %>
|
||||
<% if @subprojects.any? %>
|
||||
<li><%=l(:label_subproject_plural)%>:
|
||||
<%= @subprojects.collect{|p| link_to(h(p), :action => 'show', :id => p)}.join(", ") %></li>
|
||||
<% end %>
|
||||
<% if @ancestors.any? %>
|
||||
<li><%=l(:field_parent)%>:
|
||||
<%= @ancestors.collect {|p| link_to(h(p), :action => 'show', :id => p)}.join(" » ") %></li>
|
||||
<% if @subprojects.any? %>
|
||||
<li><%=l(:label_subproject_plural)%>: <%= @subprojects.collect{|p| link_to(h(p.name), :action => 'show', :id => p)}.join(", ") %></li>
|
||||
<% end %>
|
||||
<% if @project.parent %>
|
||||
<li><%=l(:field_parent)%>: <%= link_to h(@project.parent.name), :controller => 'projects', :action => 'show', :id => @project.parent %></li>
|
||||
<% end %>
|
||||
<% @project.custom_values.each do |custom_value| %>
|
||||
<% if !custom_value.value.empty? %>
|
||||
|
||||
@@ -95,10 +95,7 @@ function toggle_multi_select(field) {
|
||||
</td>
|
||||
<td class="add-filter">
|
||||
<%= l(:label_filter_add) %>:
|
||||
<%= select_tag 'add_filter_select', options_for_select([["",""]] + query.available_filters.sort{|a,b| a[1][:order]<=>b[1][:order]}.collect{|field| [ field[1][:name] || l(("field_"+field[0].to_s.gsub(/_id$/, "")).to_sym), field[0]] unless query.has_filter?(field[0])}.compact),
|
||||
:onchange => "add_filter();",
|
||||
:class => "select-small",
|
||||
:name => nil %>
|
||||
<%= select_tag 'add_filter_select', options_for_select([["",""]] + query.available_filters.sort{|a,b| a[1][:order]<=>b[1][:order]}.collect{|field| [ field[1][:name] || l(("field_"+field[0].to_s.gsub(/\_id$/, "")).to_sym), field[0]] unless query.has_filter?(field[0])}.compact), :onchange => "add_filter();", :class => "select-small" %>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -11,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>
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
<h2><%= l(:label_revision) %> <%= format_revision(@rev) %> <%= @path.gsub(/^.*\//, '') %></h2>
|
||||
|
||||
<!-- Choose view type -->
|
||||
<% form_tag({}, :method => 'get') do %>
|
||||
<%= hidden_field_tag('rev', params[:rev]) if params[:rev] %>
|
||||
<%= hidden_field_tag('rev_to', params[:rev_to]) if params[:rev_to] %>
|
||||
<% form_tag({ :controller => 'repositories', :action => 'diff'}, :method => 'get') do %>
|
||||
<% params.each do |k, p| %>
|
||||
<% if k != "type" %>
|
||||
<%= hidden_field_tag(k,p) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<p><label><%= l(:label_view_diff) %></label>
|
||||
<%= select_tag 'type', options_for_select([[l(:label_diff_inline), "inline"], [l(:label_diff_side_by_side), "sbs"]], @diff_type), :onchange => "if (this.value != '') {this.form.submit()}" %></p>
|
||||
<% end %>
|
||||
@@ -12,9 +15,10 @@
|
||||
<%= render :partial => 'common/diff', :locals => {:diff => @diff, :diff_type => @diff_type} %>
|
||||
<% end -%>
|
||||
|
||||
<% other_formats_links do |f| %>
|
||||
<%= f.link_to 'Diff', :url => params, :caption => 'Unified diff' %>
|
||||
<% end %>
|
||||
<p class="other-formats">
|
||||
<%= l(:label_export_to) %>
|
||||
<span><%= link_to 'Unified diff', params.merge(:format => 'diff') %></span>
|
||||
</p>
|
||||
|
||||
<% html_title(with_leading_slash(@path), 'Diff') -%>
|
||||
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
<%= auto_discovery_link_tag(:atom, params.merge({:format => 'atom', :page => nil, :key => User.current.rss_key})) %>
|
||||
<% end %>
|
||||
|
||||
<% other_formats_links do |f| %>
|
||||
<%= f.link_to 'Atom', :url => {:key => User.current.rss_key} %>
|
||||
<% end %>
|
||||
<p class="other-formats">
|
||||
<%= l(:label_export_to) %>
|
||||
<span><%= link_to 'Atom', {:format => 'atom', :key => User.current.rss_key}, :class => 'feed' %></span>
|
||||
</p>
|
||||
|
||||
<% html_title(l(:label_revision_plural)) -%>
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
<% content_for :header_tags do %>
|
||||
<%= auto_discovery_link_tag(:atom, params.merge({:format => 'atom', :action => 'revisions', :id => @project, :page => nil, :key => User.current.rss_key})) %>
|
||||
<% end %>
|
||||
|
||||
<% other_formats_links do |f| %>
|
||||
<%= f.link_to 'Atom', :url => {:action => 'revisions', :id => @project, :key => User.current.rss_key} %>
|
||||
<% end %>
|
||||
<p class="other-formats">
|
||||
<%= l(:label_export_to) %>
|
||||
<span><%= link_to 'Atom', {:action => 'revisions', :id => @project, :format => 'atom', :key => User.current.rss_key}, :class => 'feed' %></span>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<% content_for :header_tags do %>
|
||||
|
||||
@@ -17,9 +17,6 @@
|
||||
|
||||
<p><label><%= l(:label_password_lost) %></label>
|
||||
<%= check_box_tag 'settings[lost_password]', 1, Setting.lost_password? %><%= hidden_field_tag 'settings[lost_password]', 0 %></p>
|
||||
|
||||
<p><label><%= l(:setting_openid) %></label>
|
||||
<%= check_box_tag 'settings[openid]', 1, Setting.openid?, :disabled => !Object.const_defined?(:OpenID) %><%= hidden_field_tag 'settings[openid]', 0 %></p>
|
||||
</div>
|
||||
|
||||
<div style="float:right;">
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<% form_tag({:action => 'edit', :tab => 'display'}) do %>
|
||||
|
||||
<div class="box tabular settings">
|
||||
<p><label><%= l(:label_theme) %></label>
|
||||
<%= select_tag 'settings[ui_theme]', options_for_select( ([[l(:label_default), '']] + Redmine::Themes.themes.collect {|t| [t.name, t.id]}), Setting.ui_theme) %></p>
|
||||
|
||||
<p><label><%= l(:setting_default_language) %></label>
|
||||
<%= select_tag 'settings[default_language]', options_for_select( lang_options_for_select(false), Setting.default_language) %></p>
|
||||
|
||||
<p><label><%= l(:setting_date_format) %></label>
|
||||
<%= select_tag 'settings[date_format]', options_for_select( [[l(:label_language_based), '']] + Setting::DATE_FORMATS.collect {|f| [Date.today.strftime(f), f]}, Setting.date_format) %></p>
|
||||
|
||||
<p><label><%= l(:setting_time_format) %></label>
|
||||
<%= select_tag 'settings[time_format]', options_for_select( [[l(:label_language_based), '']] + Setting::TIME_FORMATS.collect {|f| [Time.now.strftime(f), f]}, Setting.time_format) %></p>
|
||||
|
||||
<p><label><%= l(:setting_user_format) %></label>
|
||||
<%= select_tag 'settings[user_format]', options_for_select( @options[:user_format], Setting.user_format.to_s ) %></p>
|
||||
|
||||
<p><label><%= l(:setting_gravatar_enabled) %></label>
|
||||
<%= check_box_tag 'settings[gravatar_enabled]', 1, Setting.gravatar_enabled? %><%= hidden_field_tag 'settings[gravatar_enabled]', 0 %></p>
|
||||
</div>
|
||||
|
||||
<%= submit_tag l(:button_save) %>
|
||||
<% end %>
|
||||
@@ -8,6 +8,21 @@
|
||||
<%= text_area_tag 'settings[welcome_text]', Setting.welcome_text, :cols => 60, :rows => 5, :class => 'wiki-edit' %></p>
|
||||
<%= wikitoolbar_for 'settings[welcome_text]' %>
|
||||
|
||||
<p><label><%= l(:label_theme) %></label>
|
||||
<%= select_tag 'settings[ui_theme]', options_for_select( ([[l(:label_default), '']] + Redmine::Themes.themes.collect {|t| [t.name, t.id]}), Setting.ui_theme) %></p>
|
||||
|
||||
<p><label><%= l(:setting_default_language) %></label>
|
||||
<%= select_tag 'settings[default_language]', options_for_select( lang_options_for_select(false), Setting.default_language) %></p>
|
||||
|
||||
<p><label><%= l(:setting_date_format) %></label>
|
||||
<%= select_tag 'settings[date_format]', options_for_select( [[l(:label_language_based), '']] + Setting::DATE_FORMATS.collect {|f| [Date.today.strftime(f), f]}, Setting.date_format) %></p>
|
||||
|
||||
<p><label><%= l(:setting_time_format) %></label>
|
||||
<%= select_tag 'settings[time_format]', options_for_select( [[l(:label_language_based), '']] + Setting::TIME_FORMATS.collect {|f| [Time.now.strftime(f), f]}, Setting.time_format) %></p>
|
||||
|
||||
<p><label><%= l(:setting_user_format) %></label>
|
||||
<%= select_tag 'settings[user_format]', options_for_select( @options[:user_format], Setting.user_format.to_s ) %></p>
|
||||
|
||||
<p><label><%= l(:setting_attachment_max_size) %></label>
|
||||
<%= text_field_tag 'settings[attachment_max_size]', Setting.attachment_max_size, :size => 6 %> KB</p>
|
||||
|
||||
@@ -35,6 +50,9 @@
|
||||
|
||||
<p><label><%= l(:setting_diff_max_lines_displayed) %></label>
|
||||
<%= text_field_tag 'settings[diff_max_lines_displayed]', Setting.diff_max_lines_displayed, :size => 6 %></p>
|
||||
|
||||
<p><label><%= l(:setting_gravatar_enabled) %></label>
|
||||
<%= check_box_tag 'settings[gravatar_enabled]', 1, Setting.gravatar_enabled? %><%= hidden_field_tag 'settings[gravatar_enabled]', 0 %></p>
|
||||
</div>
|
||||
|
||||
<%= submit_tag l(:button_save) %>
|
||||
|
||||
@@ -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) %>
|
||||
|
||||
@@ -17,14 +17,8 @@
|
||||
<p><label><%= l(:setting_repositories_encodings) %></label>
|
||||
<%= text_field_tag 'settings[repositories_encodings]', Setting.repositories_encodings, :size => 60 %><br /><em><%= l(:text_comma_separated) %></em></p>
|
||||
|
||||
<p><label><%= l(:setting_repositories_cache_directory) %></label>
|
||||
<%= text_field_tag 'settings[repositories_cache_directory]', Setting.repositories_cache_directory, :size => 60 %></p>
|
||||
|
||||
<p><label><%= l(:setting_commit_logs_encoding) %></label>
|
||||
<%= select_tag 'settings[commit_logs_encoding]', options_for_select(Setting::ENCODINGS, Setting.commit_logs_encoding) %></p>
|
||||
|
||||
<p><label><%= l(:setting_repository_log_display_limit) %></label>
|
||||
<%= text_field_tag 'settings[repository_log_display_limit]', Setting.repository_log_display_limit, :size => 6 %></p>
|
||||
</div>
|
||||
|
||||
<fieldset class="box tabular settings"><legend><%= l(:text_issues_ref_in_commit_messages) %></legend>
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
<div class="contextual">
|
||||
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time-add' %>
|
||||
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time' %>
|
||||
</div>
|
||||
|
||||
<%= render_timelog_breadcrumb %>
|
||||
|
||||
<h2><%= l(:label_spent_time) %></h2>
|
||||
|
||||
<% form_remote_tag( :url => {}, :html => {:method => :get}, :method => :get, :update => 'content' ) do %>
|
||||
<%# TOOD: remove the project_id and issue_id hidden fields, that information is
|
||||
already in the URI %>
|
||||
<% form_remote_tag( :url => {}, :method => :get, :update => 'content' ) do %>
|
||||
<%= hidden_field_tag 'project_id', params[:project_id] %>
|
||||
<%= hidden_field_tag 'issue_id', params[:issue_id] if @issue %>
|
||||
<%= render :partial => 'date_range' %>
|
||||
@@ -22,10 +20,11 @@ already in the URI %>
|
||||
<%= render :partial => 'list', :locals => { :entries => @entries }%>
|
||||
<p class="pagination"><%= pagination_links_full @entry_pages, @entry_count %></p>
|
||||
|
||||
<% other_formats_links do |f| %>
|
||||
<%= f.link_to 'Atom', :url => params.merge({:issue_id => @issue, :key => User.current.rss_key}) %>
|
||||
<%= f.link_to 'CSV', :url => params %>
|
||||
<% end %>
|
||||
<p class="other-formats">
|
||||
<%= l(:label_export_to) %>
|
||||
<span><%= link_to 'Atom', {:issue_id => @issue, :format => 'atom', :key => User.current.rss_key}, :class => 'feed' %></span>
|
||||
<span><%= link_to 'CSV', params.merge(:format => 'csv'), :class => 'csv' %></span>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<% html_title l(:label_spent_time), l(:label_details) %>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<h2><%= l(:label_spent_time) %></h2>
|
||||
|
||||
<% labelled_tabular_form_for :time_entry, @time_entry, :url => {:action => 'edit', :id => @time_entry, :project_id => @time_entry.project} do |f| %>
|
||||
<% labelled_tabular_form_for :time_entry, @time_entry, :url => {:action => 'edit', :project_id => @time_entry.project} do |f| %>
|
||||
<%= error_messages_for 'time_entry' %>
|
||||
<%= back_url_hidden_field_tag %>
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
<div class="contextual">
|
||||
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time-add' %>
|
||||
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time' %>
|
||||
</div>
|
||||
|
||||
<%= render_timelog_breadcrumb %>
|
||||
|
||||
<h2><%= l(:label_spent_time) %></h2>
|
||||
|
||||
<% form_remote_tag(:url => {}, :html => {:method => :get}, :method => :get, :update => 'content') do %>
|
||||
<% form_remote_tag(:url => {}, :update => 'content') do %>
|
||||
<% @criterias.each do |criteria| %>
|
||||
<%= hidden_field_tag 'criterias[]', criteria, :id => nil %>
|
||||
<% end %>
|
||||
<%# TODO: get rid of the project_id field, that should already be in the URL %>
|
||||
<%= hidden_field_tag 'project_id', params[:project_id] %>
|
||||
<%= render :partial => 'date_range' %>
|
||||
|
||||
@@ -26,7 +25,6 @@
|
||||
:id => nil,
|
||||
:disabled => (@criterias.length >= 3)) %>
|
||||
<%= link_to_remote l(:button_clear), {:url => {:project_id => @project, :period_type => params[:period_type], :period => params[:period], :from => @from, :to => @to, :columns => @columns},
|
||||
:method => :get,
|
||||
:update => 'content'
|
||||
}, :class => 'icon icon-reload' %></p>
|
||||
<% end %>
|
||||
@@ -65,9 +63,10 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<% other_formats_links do |f| %>
|
||||
<%= f.link_to 'CSV', :url => params %>
|
||||
<% end %>
|
||||
<p class="other-formats">
|
||||
<%= l(:label_export_to) %>
|
||||
<span><%= link_to 'CSV', params.merge({:format => 'csv'}), :class => 'csv' %></span>
|
||||
</p>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<%= error_messages_for 'tracker' %>
|
||||
|
||||
<div class="splitcontentleft">
|
||||
<div class="box tabular">
|
||||
<div class="box">
|
||||
<!--[form:tracker]-->
|
||||
<p><%= f.text_field :name, :required => true %></p>
|
||||
<p><%= f.check_box :is_in_chlog %></p>
|
||||
@@ -12,16 +10,3 @@
|
||||
<% end %>
|
||||
<!--[eoform:tracker]-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="splitcontentright">
|
||||
<% if @projects.any? %>
|
||||
<fieldset class="box" id="tracker_project_ids"><legend><%= l(:label_project_plural) %></legend>
|
||||
<%= project_nested_ul(@projects) do |p|
|
||||
content_tag('label', check_box_tag('tracker[project_ids][]', p.id, @tracker.projects.include?(p), :id => nil) + ' ' + h(p))
|
||||
end %>
|
||||
<%= hidden_field_tag('tracker[project_ids][]', '', :id => nil) %>
|
||||
<p><%= check_all_links 'tracker_project_ids' %></p>
|
||||
</fieldset>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<h2><%=l(:label_tracker)%></h2>
|
||||
|
||||
<% form_for :tracker, @tracker, :url => { :action => 'edit' }, :builder => TabularFormBuilder do |f| %>
|
||||
<% labelled_tabular_form_for :tracker, @tracker, :url => { :action => 'edit' } do |f| %>
|
||||
<%= render :partial => 'form', :locals => { :f => f } %>
|
||||
<%= submit_tag l(:button_save) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -1,6 +1,6 @@
|
||||
<h2><%=l(:label_tracker_new)%></h2>
|
||||
|
||||
<% form_for :tracker, @tracker, :url => { :action => 'new' }, :builder => TabularFormBuilder do |f| %>
|
||||
<% labelled_tabular_form_for :tracker, @tracker, :url => { :action => 'new' } do |f| %>
|
||||
<%= render :partial => 'form', :locals => { :f => f } %>
|
||||
<%= submit_tag l(:button_create) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -7,9 +7,6 @@
|
||||
<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>
|
||||
<% if Setting.openid? %>
|
||||
<p><%= f.text_field :identity_url %></p>
|
||||
<% end %>
|
||||
|
||||
<% @user.custom_field_values.each do |value| %>
|
||||
<p><%= custom_field_tag_with_label :user, value %></p>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user