Commit a4f117fe authored by Holger Just's avatar Holger Just

Merge branch 'release-v2.5.0' into stable

parents ee543489 7104a245
...@@ -18,6 +18,10 @@ group :test do ...@@ -18,6 +18,10 @@ group :test do
platforms :mri_19, :mingw_19 do gem 'ruby-debug19', :require => 'ruby-debug' end platforms :mri_19, :mingw_19 do gem 'ruby-debug19', :require => 'ruby-debug' end
end end
group :ldap do
gem "net-ldap", '~> 0.2.2'
end
group :openid do group :openid do
gem "ruby-openid", '~> 2.1.4', :require => 'openid' gem "ruby-openid", '~> 2.1.4', :require => 'openid'
end end
......
...@@ -16,8 +16,8 @@ class UsersController < ApplicationController ...@@ -16,8 +16,8 @@ class UsersController < ApplicationController
layout 'admin' layout 'admin'
before_filter :require_admin, :except => :show before_filter :require_admin, :except => :show
before_filter :find_user, :only => [:show, :edit, :update, :edit_membership, :destroy_membership] before_filter :find_user, :only => [:show, :edit, :update, :destroy, :edit_membership, :destroy_membership]
accept_key_auth :index, :show, :create, :update accept_key_auth :index, :show, :create, :update, :destroy
include SortHelper include SortHelper
include CustomFieldsHelper include CustomFieldsHelper
...@@ -178,6 +178,24 @@ class UsersController < ApplicationController ...@@ -178,6 +178,24 @@ class UsersController < ApplicationController
redirect_to :controller => 'users', :action => 'edit', :id => @user redirect_to :controller => 'users', :action => 'edit', :id => @user
end end
verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
def destroy
# Only allow to delete users with STATUS_REGISTERED for now
# It is assumed that these users are not yet references in any way
# from other objects.
return render_403 unless @user.deletable?
@user.destroy
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_delete)
redirect_back_or_default(:action => 'index')
}
format.api { head :ok }
end
end
def edit_membership def edit_membership
@membership = Member.edit_membership(params[:membership_id], params[:membership], @user) @membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
@membership.save if request.post? @membership.save if request.post?
......
...@@ -413,7 +413,7 @@ module ApplicationHelper ...@@ -413,7 +413,7 @@ module ApplicationHelper
title = [] title = []
title << h(@project.name) if @project title << h(@project.name) if @project
title += @html_title if @html_title title += @html_title if @html_title
title << Setting.app_title title << h(Setting.app_title)
title.select {|t| !t.blank? }.join(' - ') title.select {|t| !t.blank? }.join(' - ')
else else
@html_title ||= [] @html_title ||= []
......
...@@ -37,13 +37,26 @@ module UsersHelper ...@@ -37,13 +37,26 @@ module UsersHelper
def change_status_link(user) def change_status_link(user)
url = {:controller => 'users', :action => 'update', :id => user, :page => params[:page], :status => params[:status], :tab => nil} url = {:controller => 'users', :action => 'update', :id => user, :page => params[:page], :status => params[:status], :tab => nil}
links = []
if user.locked? if user.locked?
link_to l(:button_unlock), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :put, :class => 'icon icon-unlock' links << link_to(l(:button_unlock), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :put, :class => 'icon icon-unlock')
elsif user.registered? elsif user.registered?
link_to l(:button_activate), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :put, :class => 'icon icon-unlock' links << link_to(l(:button_activate), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :put, :class => 'icon icon-unlock')
elsif user != User.current elsif user != User.current
link_to l(:button_lock), url.merge(:user => {:status => User::STATUS_LOCKED}), :method => :put, :class => 'icon icon-lock' links << link_to(l(:button_lock), url.merge(:user => {:status => User::STATUS_LOCKED}), :method => :put, :class => 'icon icon-lock')
end end
if user.deletable?
links << link_to(
l(:button_delete), {:controller => 'users', :action => 'destroy', :id => user},
:method => :delete,
:confirm => l(:text_are_you_sure),
:title => l(:button_delete),
:class => 'icon icon-del'
)
end
links.join(" ")
end end
def user_settings_tabs def user_settings_tabs
......
...@@ -108,7 +108,7 @@ class Journal < ActiveRecord::Base ...@@ -108,7 +108,7 @@ class Journal < ActiveRecord::Base
## => Try the journaled object with the same method and arguments ## => Try the journaled object with the same method and arguments
## => On error, call super ## => On error, call super
def method_missing(method, *args, &block) def method_missing(method, *args, &block)
return super if attributes[method.to_s] return super if respond_to?(method) || attributes[method.to_s]
journaled.send(method, *args, &block) journaled.send(method, *args, &block)
rescue NoMethodError => e rescue NoMethodError => e
e.name == method ? super : raise(e) e.name == method ? super : raise(e)
......
...@@ -396,8 +396,8 @@ class Mailer < ActionMailer::Base ...@@ -396,8 +396,8 @@ class Mailer < ActionMailer::Base
# if he doesn't want to receive notifications about what he does # if he doesn't want to receive notifications about what he does
@author ||= User.current @author ||= User.current
if @author.pref[:no_self_notified] if @author.pref[:no_self_notified]
recipients.delete(@author.mail) if recipients recipients((recipients.is_a?(Array) ? recipients : [recipients]) - [@author.mail]) if recipients.present?
cc.delete(@author.mail) if cc cc((cc.is_a?(Array) ? cc : [cc]) - [@author.mail]) if cc.present?
end end
notified_users = [recipients, cc].flatten.compact.uniq notified_users = [recipients, cc].flatten.compact.uniq
......
...@@ -70,6 +70,7 @@ class User < Principal ...@@ -70,6 +70,7 @@ class User < Principal
validates_length_of :mail, :maximum => 60, :allow_nil => true validates_length_of :mail, :maximum => 60, :allow_nil => true
validates_confirmation_of :password, :allow_nil => true validates_confirmation_of :password, :allow_nil => true
validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
validates_inclusion_of :status, :in => [STATUS_ANONYMOUS, STATUS_ACTIVE, STATUS_REGISTERED, STATUS_LOCKED]
named_scope :in_group, lambda {|group| named_scope :in_group, lambda {|group|
group_id = group.is_a?(Group) ? group.id : group.to_i group_id = group.is_a?(Group) ? group.id : group.to_i
...@@ -207,6 +208,11 @@ class User < Principal ...@@ -207,6 +208,11 @@ class User < Principal
update_attribute(:status, STATUS_LOCKED) update_attribute(:status, STATUS_LOCKED)
end end
def deletable?
registered? && last_login_on.nil?
end
# Returns true if +clear_password+ is the correct user's password, otherwise false # Returns true if +clear_password+ is the correct user's password, otherwise false
def check_password?(clear_password) def check_password?(clear_password)
if auth_source_id.present? if auth_source_id.present?
...@@ -526,6 +532,24 @@ class User < Principal ...@@ -526,6 +532,24 @@ class User < Principal
if !password.nil? && password.size < Setting.password_min_length.to_i if !password.nil? && password.size < Setting.password_min_length.to_i
errors.add(:password, :too_short, :count => Setting.password_min_length.to_i) errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
end end
# Status
if !new_record? && status_changed?
case status_was
when nil
# initial setting is always save
true
when STATUS_ANONYMOUS
# never allow a state change of the anonymous user
false
when STATUS_REGISTERED
[STATUS_ACTIVE, STATUS_LOCKED].include? status
when STATUS_ACTIVE
[STATUS_LOCKED].include? status
when STATUS_LOCKED
[STATUS_ACTIVE].include? status
end || errors.add(:status, :inclusion)
end
end end
private private
......
...@@ -104,7 +104,12 @@ class WikiContent < ActiveRecord::Base ...@@ -104,7 +104,12 @@ class WikiContent < ActiveRecord::Base
def text def text
@text ||= case changes["compression"] @text ||= case changes["compression"]
when "gzip" when "gzip"
Zlib::Inflate.inflate(changes["data"]) data = Zlib::Inflate.inflate(changes["data"])
if data.respond_to? :force_encoding
data.force_encoding("UTF-8")
else
data
end
else else
# uncompressed data # uncompressed data
changes["data"] changes["data"]
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
<%= render :partial => (@edit_allowed ? 'form' : 'form_update'), :locals => {:f => f} %> <%= render :partial => (@edit_allowed ? 'form' : 'form_update'), :locals => {:f => f} %>
</fieldset> </fieldset>
<% end %> <% end %>
<% if authorize_for('timelog', 'edit') %> <% if User.current.allowed_to?(:log_time, @project) %>
<fieldset class="tabular"><legend><%= l(:button_log_time) %></legend> <fieldset class="tabular"><legend><%= l(:button_log_time) %></legend>
<% fields_for :time_entry, @time_entry, { :builder => TabularFormBuilder, :lang => current_language} do |time_entry| %> <% fields_for :time_entry, @time_entry, { :builder => TabularFormBuilder, :lang => current_language} do |time_entry| %>
<div class="splitcontentleft"> <div class="splitcontentleft">
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
</div> </div>
<p><%= time_entry.text_field :comments, :size => 60 %></p> <p><%= time_entry.text_field :comments, :size => 60 %></p>
<% @time_entry.custom_field_values.each do |value| %> <% @time_entry.custom_field_values.each do |value| %>
<p><%= custom_field_tag_with_label :time_entry, value %></p> <p><%= custom_field_tag_with_label :time_entry, value %></p>
<% end %> <% end %>
<% end %> <% end %>
</fieldset> </fieldset>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head> <head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title><%=h html_title %></title> <title><%= html_title %></title>
<meta name="description" content="<%= Redmine::Info.app_name %>" /> <meta name="description" content="<%= Redmine::Info.app_name %>" />
<meta name="keywords" content="issue,bug,tracker" /> <meta name="keywords" content="issue,bug,tracker" />
<%= csrf_meta_tag %> <%= csrf_meta_tag %>
......
...@@ -8,10 +8,13 @@ ...@@ -8,10 +8,13 @@
<%= link_to(l(:label_overall_spent_time), { :controller => 'time_entries' }) + ' |' if User.current.allowed_to?(:view_time_entries, nil, :global => true) %> <%= link_to(l(:label_overall_spent_time), { :controller => 'time_entries' }) + ' |' if User.current.allowed_to?(:view_time_entries, nil, :global => true) %>
<%= link_to(l(:label_news_view_all), { :controller => 'news' }) + ' |' if User.current.allowed_to?(:view_news, nil, :global => true) %> <%= link_to(l(:label_news_view_all), { :controller => 'news' }) + ' |' if User.current.allowed_to?(:view_news, nil, :global => true) %>
<%= link_to l(:label_overall_activity), { :controller => 'activities', :action => 'index' }%> <%= link_to l(:label_overall_activity), { :controller => 'activities', :action => 'index' }%>
<%= call_hook(:view_projects_show_contextual) %>
</div> </div>
<h2><%=l(:label_project_plural)%></h2> <h2><%=l(:label_project_plural)%></h2>
<%= call_hook(:view_projects_show_top) %>
<%= render_project_hierarchy(@projects)%> <%= render_project_hierarchy(@projects)%>
<% if User.current.logged? %> <% if User.current.logged? %>
......
...@@ -97,11 +97,11 @@ Event.observe(document,"dom:loaded", apply_filters_observer); ...@@ -97,11 +97,11 @@ Event.observe(document,"dom:loaded", apply_filters_observer);
</select> </select>
<%= link_to_function image_tag('bullet_toggle_plus.png'), "toggle_multi_select('#{field}');", :style => "vertical-align: bottom;" %> <%= link_to_function image_tag('bullet_toggle_plus.png'), "toggle_multi_select('#{field}');", :style => "vertical-align: bottom;" %>
<% when :date, :date_past %> <% when :date, :date_past %>
<%= text_field_tag "v[#{field}][]", query.values_for(field), :id => "values_#{field}", :size => 3, :class => "select-small" %> <%= l(:label_day_plural) %> <%= text_field_tag "v[#{field}][]", query.values_for(field).try(:first), :id => "values_#{field}", :size => 3, :class => "select-small" %> <%= l(:label_day_plural) %>
<% when :string, :text %> <% when :string, :text %>
<%= text_field_tag "v[#{field}][]", query.values_for(field), :id => "values_#{field}", :size => 30, :class => "select-small" %> <%= text_field_tag "v[#{field}][]", query.values_for(field).try(:first), :id => "values_#{field}", :size => 30, :class => "select-small" %>
<% when :integer %> <% when :integer %>
<%= text_field_tag "v[#{field}][]", query.values_for(field), :id => "values_#{field}", :size => 3, :class => "select-small" %> <%= text_field_tag "v[#{field}][]", query.values_for(field).try(:first), :id => "values_#{field}", :size => 3, :class => "select-small" %>
<% end %> <% end %>
</div> </div>
<script type="text/javascript">toggle_filter('<%= field %>');</script> <script type="text/javascript">toggle_filter('<%= field %>');</script>
......
...@@ -960,26 +960,26 @@ bg: ...@@ -960,26 +960,26 @@ bg:
field_effective_date: Дата field_effective_date: Дата
text_default_encoding: "По подразбиране: UTF-8" text_default_encoding: "По подразбиране: UTF-8"
text_git_repo_example: a bare and local repository (e.g. /gitrepo, c:\gitrepo) text_git_repo_example: a bare and local repository (e.g. /gitrepo, c:\gitrepo)
label_notify_member_plural: Email issue updates label_notify_member_plural: Изпращане на e-mail при промени в задачите
label_path_encoding: Кодиране на пътищата label_path_encoding: Кодиране на пътищата
text_mercurial_repo_example: локално хранилище (например /hgrepo, c:\hgrepo) text_mercurial_repo_example: локално хранилище (например /hgrepo, c:\hgrepo)
label_diff: diff label_diff: diff
setting_issue_startdate_is_adddate: Use current date as start date for new issues setting_issue_startdate_is_adddate: Използване на текущата дата като начална дата за нови задачи
description_search: Searchfield description_search: Търсене
description_user_mail_notification: Mail notification settings description_user_mail_notification: Конфигурация известията по пощата
description_date_range_list: Choose range from list description_date_range_list: Изберете диапазон от списъка
description_date_to: Enter end date description_date_to: Въведете крайна дата
description_query_sort_criteria_attribute: Sort attribute description_query_sort_criteria_attribute: Атрибут на сортиране
description_message_content: Message content description_message_content: Съдържание на съобщението
description_wiki_subpages_reassign: Choose new parent page description_wiki_subpages_reassign: Изберете нова родителска страница
description_available_columns: Available Columns description_available_columns: Налични колони
description_selected_columns: Selected Columns description_selected_columns: Избрани колони
description_date_range_interval: Choose range by selecting start and end date description_date_range_interval: Изберете диапазон чрез задаване на начална и крайна дати
description_project_scope: Search scope description_project_scope: Обхват на търсенето
description_issue_category_reassign: Choose issue category description_issue_category_reassign: Изберете категория
description_query_sort_criteria_direction: Sort direction description_query_sort_criteria_direction: Посока на сортиране
description_notes: Notes description_notes: Бележки
description_filter: Filter description_filter: Филтър
description_choose_project: Projects description_choose_project: Проекти
description_date_from: Enter start date description_date_from: Въведете начална дата
label_deleted_custom_field: (deleted custom field) label_deleted_custom_field: (изтрито потребителско поле)
...@@ -19,9 +19,9 @@ rescue LoadError ...@@ -19,9 +19,9 @@ rescue LoadError
raise "Could not load the bundler gem. Install it with `gem install bundler`." raise "Could not load the bundler gem. Install it with `gem install bundler`."
end end
if Gem::Version.new(Bundler::VERSION) <= Gem::Version.new("0.9.24") if Gem::Version.new(Bundler::VERSION) < Gem::Version.new("1.0.6")
raise RuntimeError, "Your bundler version is too old for Rails 2.3." + raise RuntimeError, "Your bundler version is too old. We require " +
"Run `gem install bundler` to upgrade." "at least version 1.0.6. Run `gem install bundler` to upgrade."
end end
begin begin
...@@ -29,6 +29,6 @@ begin ...@@ -29,6 +29,6 @@ begin
ENV["BUNDLE_GEMFILE"] = File.expand_path("../../Gemfile", __FILE__) ENV["BUNDLE_GEMFILE"] = File.expand_path("../../Gemfile", __FILE__)
Bundler.setup Bundler.setup
rescue Bundler::GemNotFound rescue Bundler::GemNotFound
raise RuntimeError, "Bundler couldn't find some gems." + raise RuntimeError, "Bundler couldn't find some gems. " +
"Did you run `bundle install`?" "Did you run `bundle install`?"
end end
...@@ -137,8 +137,7 @@ ActionController::Routing::Routes.draw do |map| ...@@ -137,8 +137,7 @@ ActionController::Routing::Routes.draw do |map|
map.resources :users, :member => { map.resources :users, :member => {
:edit_membership => :post, :edit_membership => :post,
:destroy_membership => :post :destroy_membership => :post
}, }
:except => [:destroy]
# For nice "roadmap" in the url for the index action # For nice "roadmap" in the url for the index action
map.connect 'projects/:project_id/roadmap', :controller => 'versions', :action => 'index' map.connect 'projects/:project_id/roadmap', :controller => 'versions', :action => 'index'
......
This diff is collapsed.
...@@ -49,7 +49,7 @@ Authen::Simple::LDAP (and IO::Socket::SSL if LDAPS is used): ...@@ -49,7 +49,7 @@ Authen::Simple::LDAP (and IO::Socket::SSL if LDAPS is used):
PerlAccessHandler Apache::Authn::Redmine::access_handler PerlAccessHandler Apache::Authn::Redmine::access_handler
PerlAuthenHandler Apache::Authn::Redmine::authen_handler PerlAuthenHandler Apache::Authn::Redmine::authen_handler
## for mysql ## for mysql
RedmineDSN "DBI:mysql:database=databasename;host=my.db.server" RedmineDSN "DBI:mysql:database=databasename;host=my.db.server"
## for postgres ## for postgres
...@@ -227,31 +227,31 @@ my @directives = ( ...@@ -227,31 +227,31 @@ my @directives = (
}, },
); );
sub RedmineDSN { sub RedmineDSN {
my ($self, $parms, $arg) = @_; my ($self, $parms, $arg) = @_;
$self->{RedmineDSN} = $arg; $self->{RedmineDSN} = $arg;
my $query = "SELECT my $query = "SELECT
hashed_password, salt, auth_source_id, permissions hashed_password, salt, auth_source_id, permissions
FROM members, projects, users, roles, member_roles FROM members, projects, users, roles, member_roles
WHERE WHERE
projects.id=members.project_id projects.id=members.project_id
AND member_roles.member_id=members.id AND member_roles.member_id=members.id
AND users.id=members.user_id AND users.id=members.user_id
AND roles.id=member_roles.role_id AND roles.id=member_roles.role_id
AND users.status=1 AND users.status=1
AND login=? AND login=?
AND identifier=? "; AND identifier=? ";
$self->{RedmineQuery} = trim($query); $self->{RedmineQuery} = trim($query);
} }
sub RedmineDbUser { set_val('RedmineDbUser', @_); } sub RedmineDbUser { set_val('RedmineDbUser', @_); }
sub RedmineDbPass { set_val('RedmineDbPass', @_); } sub RedmineDbPass { set_val('RedmineDbPass', @_); }
sub RedmineDbWhereClause { sub RedmineDbWhereClause {
my ($self, $parms, $arg) = @_; my ($self, $parms, $arg) = @_;
$self->{RedmineQuery} = trim($self->{RedmineQuery}.($arg ? $arg : "")." "); $self->{RedmineQuery} = trim($self->{RedmineQuery}.($arg ? $arg : "")." ");
} }
sub RedmineCacheCredsMax { sub RedmineCacheCredsMax {
my ($self, $parms, $arg) = @_; my ($self, $parms, $arg) = @_;
if ($arg) { if ($arg) {
$self->{RedmineCachePool} = APR::Pool->new; $self->{RedmineCachePool} = APR::Pool->new;
...@@ -325,10 +325,10 @@ sub access_handler { ...@@ -325,10 +325,10 @@ sub access_handler {
sub authen_handler { sub authen_handler {
my $r = shift; my $r = shift;
my ($res, $redmine_pass) = $r->get_basic_auth_pw(); my ($res, $redmine_pass) = $r->get_basic_auth_pw();
return $res unless $res == OK; return $res unless $res == OK;
if (is_member($r->user, $redmine_pass, $r)) { if (is_member($r->user, $redmine_pass, $r)) {
return OK; return OK;
} else { } else {
...@@ -355,7 +355,7 @@ sub is_authentication_forced { ...@@ -355,7 +355,7 @@ sub is_authentication_forced {
} }
$sth->finish(); $sth->finish();
undef $sth; undef $sth;
$dbh->disconnect(); $dbh->disconnect();
undef $dbh; undef $dbh;
...@@ -365,7 +365,7 @@ sub is_authentication_forced { ...@@ -365,7 +365,7 @@ sub is_authentication_forced {
sub is_public_project { sub is_public_project {
my $project_id = shift; my $project_id = shift;
my $r = shift; my $r = shift;
if (is_authentication_forced($r)) { if (is_authentication_forced($r)) {
return 0; return 0;
} }
...@@ -392,12 +392,12 @@ sub is_public_project { ...@@ -392,12 +392,12 @@ sub is_public_project {
sub anonymous_role_allows_browse_repository { sub anonymous_role_allows_browse_repository {
my $r = shift; my $r = shift;
my $dbh = connect_database($r); my $dbh = connect_database($r);
my $sth = $dbh->prepare( my $sth = $dbh->prepare(
"SELECT permissions FROM roles WHERE builtin = 2;" "SELECT permissions FROM roles WHERE builtin = 2;"
); );
$sth->execute(); $sth->execute();
my $ret = 0; my $ret = 0;
if (my @row = $sth->fetchrow_array) { if (my @row = $sth->fetchrow_array) {
...@@ -409,7 +409,7 @@ sub anonymous_role_allows_browse_repository { ...@@ -409,7 +409,7 @@ sub anonymous_role_allows_browse_repository {
undef $sth; undef $sth;
$dbh->disconnect(); $dbh->disconnect();
undef $dbh; undef $dbh;
$ret; $ret;
} }
...@@ -438,10 +438,12 @@ sub is_member { ...@@ -438,10 +438,12 @@ sub is_member {
my $pass_digest = Digest::SHA1::sha1_hex($redmine_pass); my $pass_digest = Digest::SHA1::sha1_hex($redmine_pass);
my $access_mode = request_is_read_only($r) ? "R" : "W";
my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config); my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
my $usrprojpass; my $usrprojpass;
if ($cfg->{RedmineCacheCredsMax}) { if ($cfg->{RedmineCacheCredsMax}) {
$usrprojpass = $cfg->{RedmineCacheCreds}->get($redmine_user.":".$project_id); $usrprojpass = $cfg->{RedmineCacheCreds}->get($redmine_user.":".$project_id.":".$access_mode);
return 1 if (defined $usrprojpass and ($usrprojpass eq $pass_digest)); return 1 if (defined $usrprojpass and ($usrprojpass eq $pass_digest));
} }
my $query = $cfg->{RedmineQuery}; my $query = $cfg->{RedmineQuery};
...@@ -485,10 +487,10 @@ sub is_member { ...@@ -485,10 +487,10 @@ sub is_member {
if ($cfg->{RedmineCacheCredsMax} and $ret) { if ($cfg->{RedmineCacheCredsMax} and $ret) {
if (defined $usrprojpass) { if (defined $usrprojpass) {
$cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id, $pass_digest); $cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id.":".$access_mode, $pass_digest);
} else { } else {
if ($cfg->{RedmineCacheCredsCount} < $cfg->{RedmineCacheCredsMax}) { if ($cfg->{RedmineCacheCredsCount} < $cfg->{RedmineCacheCredsMax}) {
$cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id, $pass_digest); $cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id.":".$access_mode, $pass_digest);
$cfg->{RedmineCacheCredsCount}++; $cfg->{RedmineCacheCredsCount}++;
} else { } else {
$cfg->{RedmineCacheCreds}->clear(); $cfg->{RedmineCacheCreds}->clear();
...@@ -502,7 +504,7 @@ sub is_member { ...@@ -502,7 +504,7 @@ sub is_member {
sub get_project_identifier { sub get_project_identifier {
my $r = shift; my $r = shift;
my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config); my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
my $location = $r->location; my $location = $r->location;
my ($identifier) = $r->uri =~ m{$location/*([^/]+)}; my ($identifier) = $r->uri =~ m{$location/*([^/]+)};
...@@ -512,7 +514,7 @@ sub get_project_identifier { ...@@ -512,7 +514,7 @@ sub get_project_identifier {
sub connect_database { sub connect_database {
my $r = shift; my $r = shift;
my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config); my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
return DBI->connect($cfg->{RedmineDSN}, $cfg->{RedmineDbUser}, $cfg->{RedmineDbPass}); return DBI->connect($cfg->{RedmineDSN}, $cfg->{RedmineDbUser}, $cfg->{RedmineDbPass});
} }
......
...@@ -21,5 +21,19 @@ module ChiliProject ...@@ -21,5 +21,19 @@ module ChiliProject
Journal.included_modules.include?(Redmine::Acts::Journalized) Journal.included_modules.include?(Redmine::Acts::Journalized)
end end
# Is any jQuery version available on all pages?
#
# This does not take modifications into account, that may be performed by
# plugins.
#
# Released: ChiliProject 2.5.0
def self.using_jquery?
false
end
# Catch-all to be overwritten be future compatibility checks.
def self.method_missing(method, *args)
method.to_s.ends_with?('?') ? false : super
end
end end
end end
...@@ -18,7 +18,7 @@ module ChiliProject ...@@ -18,7 +18,7 @@ module ChiliProject
module VERSION #:nodoc: module VERSION #:nodoc:
MAJOR = 2 MAJOR = 2
MINOR = 4 MINOR = 5
PATCH = 0 PATCH = 0
TINY = PATCH # Redmine compat TINY = PATCH # Redmine compat
......
...@@ -100,10 +100,10 @@ Redmine::AccessControl.map do |map| ...@@ -100,10 +100,10 @@ Redmine::AccessControl.map do |map|
end end
map.project_module :time_tracking do |map| map.project_module :time_tracking do |map|
map.permission :log_time, {:timelog => [:new, :create, :edit, :update]}, :require => :loggedin map.permission :log_time, {:timelog => [:new, :create]}, :require => :loggedin
map.permission :view_time_entries, :timelog => [:index, :show], :time_entry_reports => [:report] map.permission :view_time_entries, :timelog => [:index, :show], :time_entry_reports => [:report]
map.permission :edit_time_entries, {:timelog => [:new, :create, :edit, :update, :destroy]}, :require => :member map.permission :edit_time_entries, {:timelog => [:edit, :update, :destroy]}, :require => :member
map.permission :edit_own_time_entries, {:timelog => [:new, :create, :edit, :update, :destroy]}, :require => :loggedin map.permission :edit_own_time_entries, {:timelog => [:edit, :update, :destroy]}, :require => :loggedin
map.permission :manage_project_activities, {:project_enumerations => [:update, :destroy]}, :require => :member map.permission :manage_project_activities, {:project_enumerations => [:update, :destroy]}, :require => :member
end end
......
...@@ -22,7 +22,7 @@ class TabularFormBuilder < ActionView::Helpers::FormBuilder ...@@ -22,7 +22,7 @@ class TabularFormBuilder < ActionView::Helpers::FormBuilder
super super
end end
(field_helpers - %w(radio_button hidden_field fields_for) + %w(date_select)).each do |selector|