Compare commits
13
Commits
0.8.6
..
0.8-stable
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90602ac6dc | ||
|
|
8bab5b4730 | ||
|
|
be146d492a | ||
|
|
b49aaccdd6 | ||
|
|
5abb5bf6e1 | ||
|
|
638a9a264a | ||
|
|
051741f05c | ||
|
|
ce41d4f9b0 | ||
|
|
540d204e9a | ||
|
|
2fd4e56ecd | ||
|
|
3d926660ba | ||
|
|
b19ba93966 | ||
|
|
36cc9af651 |
@@ -188,12 +188,12 @@ class AccountController < ApplicationController
|
|||||||
|
|
||||||
private
|
private
|
||||||
def logged_user=(user)
|
def logged_user=(user)
|
||||||
|
reset_session
|
||||||
if user && user.is_a?(User)
|
if user && user.is_a?(User)
|
||||||
User.current = user
|
User.current = user
|
||||||
session[:user_id] = user.id
|
session[:user_id] = user.id
|
||||||
else
|
else
|
||||||
User.current = User.anonymous
|
User.current = User.anonymous
|
||||||
session[:user_id] = nil
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -19,10 +19,17 @@ require 'uri'
|
|||||||
require 'cgi'
|
require 'cgi'
|
||||||
|
|
||||||
class ApplicationController < ActionController::Base
|
class ApplicationController < ActionController::Base
|
||||||
|
class MissingSessionSecret < Exception ; end
|
||||||
layout 'base'
|
layout 'base'
|
||||||
|
|
||||||
before_filter :user_setup, :check_if_login_required, :set_localization
|
before_filter :user_setup, :check_if_login_required, :set_localization
|
||||||
filter_parameter_logging :password
|
filter_parameter_logging :password
|
||||||
|
|
||||||
|
if session.first[:secret].blank?
|
||||||
|
raise MissingSessionSecret, "Missing session secret. Please run 'rake config/initializers/session_store.rb' to generate one"
|
||||||
|
else
|
||||||
|
protect_from_forgery :secret => session.first[:secret]
|
||||||
|
end
|
||||||
|
|
||||||
include Redmine::MenuManager::MenuController
|
include Redmine::MenuManager::MenuController
|
||||||
helper Redmine::MenuManager::MenuHelper
|
helper Redmine::MenuManager::MenuHelper
|
||||||
@@ -82,7 +89,13 @@ class ApplicationController < ActionController::Base
|
|||||||
|
|
||||||
def require_login
|
def require_login
|
||||||
if !User.current.logged?
|
if !User.current.logged?
|
||||||
redirect_to :controller => "account", :action => "login", :back_url => url_for(params)
|
# Extract only the basic url parameters on non-GET requests
|
||||||
|
if request.get?
|
||||||
|
url = url_for(params)
|
||||||
|
else
|
||||||
|
url = url_for(:controller => params[:controller], :action => params[:action], :id => params[:id], :project_id => params[:project_id])
|
||||||
|
end
|
||||||
|
redirect_to :controller => "account", :action => "login", :back_url => url
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
true
|
true
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ class TimelogController < ApplicationController
|
|||||||
sql << " WHERE"
|
sql << " WHERE"
|
||||||
sql << " (%s) AND" % @project.project_condition(Setting.display_subprojects_issues?) if @project
|
sql << " (%s) AND" % @project.project_condition(Setting.display_subprojects_issues?) if @project
|
||||||
sql << " (%s) AND" % Project.allowed_to_condition(User.current, :view_time_entries)
|
sql << " (%s) AND" % Project.allowed_to_condition(User.current, :view_time_entries)
|
||||||
sql << " (spent_on BETWEEN '%s' AND '%s')" % [ActiveRecord::Base.connection.quoted_date(@from.to_time), ActiveRecord::Base.connection.quoted_date(@to.to_time)]
|
sql << " (spent_on BETWEEN '%s' AND '%s')" % [ActiveRecord::Base.connection.quoted_date(@from), ActiveRecord::Base.connection.quoted_date(@to)]
|
||||||
sql << " GROUP BY #{sql_group_by}, tyear, tmonth, tweek, spent_on"
|
sql << " GROUP BY #{sql_group_by}, tyear, tmonth, tweek, spent_on"
|
||||||
|
|
||||||
@hours = ActiveRecord::Base.connection.select_all(sql)
|
@hours = ActiveRecord::Base.connection.select_all(sql)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
<p><%= f.select :category_id, (@project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %>
|
<p><%= f.select :category_id, (@project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %>
|
||||||
<%= prompt_to_remote(l(:label_issue_category_new),
|
<%= prompt_to_remote(l(:label_issue_category_new),
|
||||||
l(:label_issue_category_new), 'category[name]',
|
l(:label_issue_category_new), 'category[name]',
|
||||||
{:controller => 'projects', :action => 'add_issue_category', :id => @project},
|
{:controller => 'projects', :action => 'add_issue_category', :id => @project, :authenticity_token => form_authenticity_token},
|
||||||
:class => 'small', :tabindex => 199) if authorize_for('projects', 'add_issue_category') %></p>
|
:class => 'small', :tabindex => 199) if authorize_for('projects', 'add_issue_category') %></p>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= content_tag('p', f.select(:fixed_version_id,
|
<%= content_tag('p', f.select(:fixed_version_id,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<% @entries.each do |entry| %>
|
<% @entries.each do |entry| %>
|
||||||
<% tr_id = Digest::MD5.hexdigest(entry.path)
|
<% tr_id = Digest::MD5.hexdigest(entry.path)
|
||||||
depth = params[:depth].to_i %>
|
depth = params[:depth].to_i %>
|
||||||
<tr id="<%= tr_id %>" class="<%= params[:parent_id] %> entry <%= entry.kind %>">
|
<tr id="<%= tr_id %>" class="<%= h params[:parent_id] %> entry <%= entry.kind %>">
|
||||||
<td style="padding-left: <%=18 * depth%>px;" class="filename">
|
<td style="padding-left: <%=18 * depth%>px;" class="filename">
|
||||||
<% if entry.is_dir? %>
|
<% if entry.is_dir? %>
|
||||||
<span class="expander" onclick="<%= remote_function :url => {:action => 'browse', :id => @project, :path => to_path_param(entry.path), :rev => @rev, :depth => (depth + 1), :parent_id => tr_id},
|
<span class="expander" onclick="<%= remote_function :url => {:action => 'browse', :id => @project, :path => to_path_param(entry.path), :rev => @rev, :depth => (depth + 1), :parent_id => tr_id},
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ ul.toc { padding: 4px; margin-left: 0; }
|
|||||||
ul.toc li { list-style-type:none; }
|
ul.toc li { list-style-type:none; }
|
||||||
ul.toc li.heading2 { margin-left: 1em; }
|
ul.toc li.heading2 { margin-left: 1em; }
|
||||||
ul.toc li.heading3 { margin-left: 2em; }
|
ul.toc li.heading3 { margin-left: 2em; }
|
||||||
|
a.wiki-anchor { display: none; margin-left: 6px; text-decoration: none; }
|
||||||
|
a.wiki-anchor:hover { color: #aaa !important; text-decoration: none; }
|
||||||
|
h1:hover a.wiki-anchor, h2:hover a.wiki-anchor, h3:hover a.wiki-anchor { display: inline; color: #ddd; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -6,6 +6,13 @@
|
|||||||
<style>
|
<style>
|
||||||
body { font:80% Verdana,Tahoma,Arial,sans-serif; }
|
body { font:80% Verdana,Tahoma,Arial,sans-serif; }
|
||||||
h1, h2, h3, h4 { font-family: "Trebuchet MS",Georgia,"Times New Roman",serif; }
|
h1, h2, h3, h4 { font-family: "Trebuchet MS",Georgia,"Times New Roman",serif; }
|
||||||
|
ul.toc { padding: 4px; margin-left: 0; }
|
||||||
|
ul.toc li { list-style-type:none; }
|
||||||
|
ul.toc li.heading2 { margin-left: 1em; }
|
||||||
|
ul.toc li.heading3 { margin-left: 2em; }
|
||||||
|
a.wiki-anchor { display: none; margin-left: 6px; text-decoration: none; }
|
||||||
|
a.wiki-anchor:hover { color: #aaa !important; text-decoration: none; }
|
||||||
|
h1:hover a.wiki-anchor, h2:hover a.wiki-anchor, h3:hover a.wiki-anchor { display: inline; color: #ddd; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -15,3 +15,6 @@ config.action_controller.perform_caching = false
|
|||||||
|
|
||||||
config.action_mailer.perform_deliveries = true
|
config.action_mailer.perform_deliveries = true
|
||||||
config.action_mailer.delivery_method = :test
|
config.action_mailer.delivery_method = :test
|
||||||
|
|
||||||
|
# Skip protect_from_forgery in requests http://m.onkey.org/2007/9/28/csrf-protection-for-your-existing-rails-application
|
||||||
|
config.action_controller.allow_forgery_protection = false
|
||||||
|
|||||||
@@ -15,3 +15,7 @@ config.action_controller.perform_caching = false
|
|||||||
|
|
||||||
config.action_mailer.perform_deliveries = true
|
config.action_mailer.perform_deliveries = true
|
||||||
config.action_mailer.delivery_method = :test
|
config.action_mailer.delivery_method = :test
|
||||||
|
|
||||||
|
# Skip protect_from_forgery in requests http://m.onkey.org/2007/9/28/csrf-protection-for-your-existing-rails-application
|
||||||
|
config.action_controller.allow_forgery_protection = false
|
||||||
|
|
||||||
|
|||||||
@@ -15,3 +15,6 @@ config.action_controller.perform_caching = false
|
|||||||
|
|
||||||
config.action_mailer.perform_deliveries = true
|
config.action_mailer.perform_deliveries = true
|
||||||
config.action_mailer.delivery_method = :test
|
config.action_mailer.delivery_method = :test
|
||||||
|
|
||||||
|
# Skip protect_from_forgery in requests http://m.onkey.org/2007/9/28/csrf-protection-for-your-existing-rails-application
|
||||||
|
config.action_controller.allow_forgery_protection = false
|
||||||
|
|||||||
@@ -4,6 +4,16 @@ Redmine - project management software
|
|||||||
Copyright (C) 2006-2009 Jean-Philippe Lang
|
Copyright (C) 2006-2009 Jean-Philippe Lang
|
||||||
http://www.redmine.org/
|
http://www.redmine.org/
|
||||||
|
|
||||||
|
== 2009-11-15 v0.8.7
|
||||||
|
|
||||||
|
* Fixed: Hide paragraph terminator at the end of headings on html export
|
||||||
|
* Fixed: pre tags containing "<pre*"
|
||||||
|
* Fixed: First date of the date range not included in the time report with SQLite
|
||||||
|
* Fixed: Password field not styled correctly on alternative stylesheet
|
||||||
|
* Fixed: Error when sumbitting a POST request that requires a login
|
||||||
|
* Fixed: CSRF vulnerabilities
|
||||||
|
|
||||||
|
|
||||||
== 2009-11-04 v0.8.6
|
== 2009-11-04 v0.8.6
|
||||||
|
|
||||||
* Change links to closed issues to be a grey color
|
* Change links to closed issues to be a grey color
|
||||||
|
|||||||
+6
-3
@@ -30,7 +30,10 @@ Optional:
|
|||||||
rake db:migrate RAILS_ENV="production"
|
rake db:migrate RAILS_ENV="production"
|
||||||
It will create tables and an administrator account.
|
It will create tables and an administrator account.
|
||||||
|
|
||||||
5. Setting up permissions
|
5. Generate a session store secret. Run:
|
||||||
|
rake config/initializers/session_store.rb
|
||||||
|
|
||||||
|
6. Setting up permissions
|
||||||
The user who runs Redmine must have write permission on the following
|
The user who runs Redmine must have write permission on the following
|
||||||
subdirectories: files, log, tmp (create the last one if not present).
|
subdirectories: files, log, tmp (create the last one if not present).
|
||||||
|
|
||||||
@@ -39,13 +42,13 @@ Optional:
|
|||||||
sudo chown -R redmine:redmine files log tmp
|
sudo chown -R redmine:redmine files log tmp
|
||||||
sudo chmod -R 755 files log tmp
|
sudo chmod -R 755 files log tmp
|
||||||
|
|
||||||
6. Test the installation by running WEBrick web server:
|
7. Test the installation by running WEBrick web server:
|
||||||
ruby script/server -e production
|
ruby script/server -e production
|
||||||
|
|
||||||
Once WEBrick has started, point your browser to http://localhost:3000/
|
Once WEBrick has started, point your browser to http://localhost:3000/
|
||||||
You should now see the application welcome page
|
You should now see the application welcome page
|
||||||
|
|
||||||
7. Use default administrator account to log in:
|
8. Use default administrator account to log in:
|
||||||
login: admin
|
login: admin
|
||||||
password: admin
|
password: admin
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1011,7 +1011,7 @@ class RedCloth3 < String
|
|||||||
end
|
end
|
||||||
|
|
||||||
OFFTAGS = /(code|pre|kbd|notextile)/
|
OFFTAGS = /(code|pre|kbd|notextile)/
|
||||||
OFFTAG_MATCH = /(?:(<\/#{ OFFTAGS }>)|(<#{ OFFTAGS }[^>]*>))(.*?)(?=<\/?#{ OFFTAGS }|\Z)/mi
|
OFFTAG_MATCH = /(?:(<\/#{ OFFTAGS }>)|(<#{ OFFTAGS }[^>]*>))(.*?)(?=<\/?#{ OFFTAGS }\W|\Z)/mi
|
||||||
OFFTAG_OPEN = /<#{ OFFTAGS }/
|
OFFTAG_OPEN = /<#{ OFFTAGS }/
|
||||||
OFFTAG_CLOSE = /<\/?#{ OFFTAGS }/
|
OFFTAG_CLOSE = /<\/?#{ OFFTAGS }/
|
||||||
HASTAG_MATCH = /(<\/?\w[^\n]*?>)/m
|
HASTAG_MATCH = /(<\/?\w[^\n]*?>)/m
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ module Redmine
|
|||||||
module VERSION #:nodoc:
|
module VERSION #:nodoc:
|
||||||
MAJOR = 0
|
MAJOR = 0
|
||||||
MINOR = 8
|
MINOR = 8
|
||||||
TINY = 6
|
TINY = 7
|
||||||
|
|
||||||
# Branch values:
|
# Branch values:
|
||||||
# * official release: nil
|
# * official release: nil
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
desc 'Generates a configuration file for cookie store sessions.'
|
||||||
|
|
||||||
|
file 'config/initializers/session_store.rb' do
|
||||||
|
path = File.join(RAILS_ROOT, 'config', 'initializers', 'session_store.rb')
|
||||||
|
secret = Rails::SecretKeyGenerator.new(self).generate_secret[0,40]
|
||||||
|
File.open(path, 'w') do |f|
|
||||||
|
f.write <<"EOF"
|
||||||
|
# This file was generated by 'rake config/initializers/session_store.rb',
|
||||||
|
# and should not be made visible to public.
|
||||||
|
# If you have a load-balancing Redmine cluster, you will need to use the
|
||||||
|
# same version of this file on each machine. And be sure to restart your
|
||||||
|
# server when you modify this file.
|
||||||
|
|
||||||
|
# Your secret key for verifying cookie session data integrity. If you
|
||||||
|
# change this key, all old sessions will become invalid! Make sure the
|
||||||
|
# secret is at least 30 characters and all random, no regular words or
|
||||||
|
# you'll be exposed to dictionary attacks.
|
||||||
|
ActionController::Base.session = {
|
||||||
|
:session_key => '_redmine_session',
|
||||||
|
:secret => '#{secret}'
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -69,7 +69,8 @@ function setPredecessorFieldsVisibility() {
|
|||||||
function promptToRemote(text, param, url) {
|
function promptToRemote(text, param, url) {
|
||||||
value = prompt(text + ':');
|
value = prompt(text + ':');
|
||||||
if (value) {
|
if (value) {
|
||||||
new Ajax.Request(url + '?' + param + '=' + encodeURIComponent(value), {asynchronous:true, evalScripts:true});
|
var sep = (url.indexOf('?') < 0 ? '?' : '&' )
|
||||||
|
new Ajax.Request(url + sep + param + '=' + encodeURIComponent(value), {asynchronous:true, evalScripts:true});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,9 +61,9 @@ input[type="button"], input[type="submit"], input[type="reset"] { background-col
|
|||||||
input[type="button"]:hover, input[type="submit"]:hover, input[type="reset"]:hover { background-color: #ccccbb; }
|
input[type="button"]:hover, input[type="submit"]:hover, input[type="reset"]:hover { background-color: #ccccbb; }
|
||||||
|
|
||||||
/* Fields */
|
/* Fields */
|
||||||
input[type="text"], textarea, select { padding: 2px; border: 1px solid #d7d7d7; }
|
input[type="text"], input[type="password"], textarea, select { padding: 2px; border: 1px solid #d7d7d7; }
|
||||||
input[type="text"] { padding: 3px; }
|
input[type="text"], input[type="password"] { padding: 3px; }
|
||||||
input[type="text"]:focus, textarea:focus, select:focus { border: 1px solid #888866; }
|
input[type="text"]:focus, input[type="password"]:focus, textarea:focus, select:focus { border: 1px solid #888866; }
|
||||||
option { border-bottom: 1px dotted #d7d7d7; }
|
option { border-bottom: 1px dotted #d7d7d7; }
|
||||||
|
|
||||||
/* Misc */
|
/* Misc */
|
||||||
|
|||||||
@@ -147,6 +147,14 @@ class TimelogControllerTest < Test::Unit::TestCase
|
|||||||
assert_equal "162.90", "%.2f" % assigns(:total_hours)
|
assert_equal "162.90", "%.2f" % assigns(:total_hours)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_report_one_day
|
||||||
|
get :report, :project_id => 1, :columns => 'day', :from => "2007-03-23", :to => "2007-03-23", :criterias => ["member", "activity"]
|
||||||
|
assert_response :success
|
||||||
|
assert_template 'report'
|
||||||
|
assert_not_nil assigns(:total_hours)
|
||||||
|
assert_equal "4.25", "%.2f" % assigns(:total_hours)
|
||||||
|
end
|
||||||
|
|
||||||
def test_report_custom_field_criteria
|
def test_report_custom_field_criteria
|
||||||
get :report, :project_id => 1, :criterias => ['project', 'cf_1']
|
get :report, :project_id => 1, :criterias => ['project', 'cf_1']
|
||||||
assert_response :success
|
assert_response :success
|
||||||
@@ -238,6 +246,14 @@ class TimelogControllerTest < Test::Unit::TestCase
|
|||||||
assert_equal Date.today - 7, assigns(:from)
|
assert_equal Date.today - 7, assigns(:from)
|
||||||
assert_equal Date.today, assigns(:to)
|
assert_equal Date.today, assigns(:to)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_details_one_day
|
||||||
|
get :details, :project_id => 1, :from => "2007-03-23", :to => "2007-03-23"
|
||||||
|
assert_response :success
|
||||||
|
assert_template 'details'
|
||||||
|
assert_not_nil assigns(:total_hours)
|
||||||
|
assert_equal "4.25", "%.2f" % assigns(:total_hours)
|
||||||
|
end
|
||||||
|
|
||||||
def test_details_at_issue_level
|
def test_details_at_issue_level
|
||||||
get :details, :issue_id => 1
|
get :details, :issue_id => 1
|
||||||
|
|||||||
@@ -147,6 +147,24 @@ class AccountTest < ActionController::IntegrationTest
|
|||||||
assert user.hashed_password.blank?
|
assert user.hashed_password.blank?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_login_and_logout_should_clear_session
|
||||||
|
get '/login'
|
||||||
|
sid = session.session_id
|
||||||
|
|
||||||
|
post '/login', :username => 'admin', :password => 'admin'
|
||||||
|
assert_redirected_to 'my/page'
|
||||||
|
assert_not_equal sid, session.session_id, "login should reset session"
|
||||||
|
assert_equal 1, session[:user_id]
|
||||||
|
sid = session.session_id
|
||||||
|
|
||||||
|
get '/'
|
||||||
|
assert_equal sid, session.session_id
|
||||||
|
|
||||||
|
get '/logout'
|
||||||
|
assert_not_equal sid, session.session_id, "logout should reset session"
|
||||||
|
assert_nil session[:user_id]
|
||||||
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
puts 'Mocha is missing. Skipping tests.'
|
puts 'Mocha is missing. Skipping tests.'
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -63,4 +63,10 @@ class AdminTest < ActionController::IntegrationTest
|
|||||||
assert_response :success
|
assert_response :success
|
||||||
assert_template "admin/projects"
|
assert_template "admin/projects"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_add_a_user_as_an_anonymous_user_should_fail
|
||||||
|
post '/users/add', :user => { :login => 'psmith', :firstname => 'Paul'}, :password => "psmith09", :password_confirmation => "psmith09"
|
||||||
|
assert_response :redirect
|
||||||
|
assert_redirected_to "/login?back_url=http%3A%2F%2Fwww.example.com%2Fusers%2Fadd"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -251,7 +251,29 @@ RAW
|
|||||||
to_test.each { |text, result| assert_equal result, textilizable(text) }
|
to_test.each { |text, result| assert_equal result, textilizable(text) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def syntax_highlight
|
def test_pre_tags
|
||||||
|
raw = <<-RAW
|
||||||
|
Before
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<prepared-statement-cache-size>32</prepared-statement-cache-size>
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
After
|
||||||
|
RAW
|
||||||
|
|
||||||
|
expected = <<-EXPECTED
|
||||||
|
<p>Before</p>
|
||||||
|
<pre>
|
||||||
|
<prepared-statement-cache-size>32</prepared-statement-cache-size>
|
||||||
|
</pre>
|
||||||
|
<p>After</p>
|
||||||
|
EXPECTED
|
||||||
|
|
||||||
|
assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_syntax_highlight
|
||||||
raw = <<-RAW
|
raw = <<-RAW
|
||||||
<pre><code class="ruby">
|
<pre><code class="ruby">
|
||||||
# Some ruby code here
|
# Some ruby code here
|
||||||
|
|||||||
Reference in New Issue
Block a user