Commit d61ad013 authored by Holger Just's avatar Holger Just

Merge branch 'release-v3.1.0' into stable

parents 11e93ff3 f681ed8c
......@@ -29,3 +29,5 @@ doc/app
/Gemfile.lock
/Gemfile.local
/.rvmrc*
/*.iml
/.idea
......@@ -11,6 +11,7 @@ gem "liquid", "~> 2.3.0"
gem "acts-as-taggable-on", "= 2.1.0"
# Needed only on RUBY_VERSION = 1.8, ruby 1.9+ compatible interpreters should bring their csv
gem "fastercsv", "~> 1.5.0", :platforms => [:ruby_18, :jruby, :mingw_18]
gem "tzinfo", "~> 0.3.31" # Fixes #903. Not required for Rails >= 3.2
group :test do
gem 'shoulda', '~> 2.10.3'
......@@ -57,7 +58,7 @@ platforms :mri, :mingw do
end
group :postgres do
gem "pg", "~> 0.9.0"
gem "pg"
# gem "postgres-pr"
end
end
......
......@@ -33,7 +33,7 @@ class ActivitiesController < ApplicationController
:with_subprojects => @with_subprojects,
:author => @author)
@activity.scope_select {|t| !params["show_#{t}"].nil?}
@activity.scope = (@author.nil? ? :default : :all) if @activity.scope.empty?
@activity.scope = (@author.nil? ? :default : :all) if @activity.scope.empty? unless params[:set_filter]
events = @activity.events(@date_from, @date_to)
......
......@@ -64,7 +64,6 @@ class ApplicationController < ActionController::Base
before_filter :user_setup, :check_if_login_required, :set_localization
filter_parameter_logging :password
rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
# FIXME: This doesn't work with Rails >= 3.0 anymore
# Possible workaround: https://github.com/rails/rails/issues/671#issuecomment-1780159
rescue_from ActionController::RoutingError, :with => proc{render_404}
......@@ -336,13 +335,6 @@ class ApplicationController < ActionController::Base
request.xhr? ? false : 'base'
end
def invalid_authenticity_token
if api_request?
logger.error "Form authenticity token is missing or is invalid. API calls must include a proper Content-type header (text/xml or text/json)."
end
render_error "Invalid form authenticity token."
end
def render_feed(items, options={})
@items = items || []
@items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
......
......@@ -21,7 +21,10 @@ class CommentsController < ApplicationController
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
def create
@comment = Comment.new(params[:comment])
raise Unauthorized unless @news.commentable?
@comment = Comment.new
@comment.safe_attributes = params[:comment]
@comment.author = User.current
if @news.comments << @comment
flash[:notice] = l(:label_comment_added)
......
......@@ -43,7 +43,8 @@ class DocumentsController < ApplicationController
end
def new
@document = @project.documents.build(params[:document])
@document = @project.documents.build
@document.safe_attributes = params[:document]
if request.post?
if User.current.allowed_to?(:add_document_watchers, @project) && params[:document]['watcher_user_ids'].present?
@document.watcher_user_ids = params[:document]['watcher_user_ids']
......
......@@ -23,7 +23,8 @@ class IssueCategoriesController < ApplicationController
verify :method => :post, :only => :destroy
def new
@category = @project.issue_categories.build(params[:category])
@category = @project.issue_categories.build
@category.safe_attributes = params[:category]
if request.post?
if @category.save
respond_to do |format|
......@@ -50,7 +51,8 @@ class IssueCategoriesController < ApplicationController
end
def edit
if request.post? and @category.update_attributes(params[:category])
@category.safe_attributes = params[:category]
if request.post? and @category.save
flash[:notice] = l(:notice_successful_update)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'categories', :id => @project
end
......
......@@ -21,17 +21,19 @@ class MembersController < ApplicationController
def new
members = []
if params[:member] && request.post?
attrs = params[:member].dup
if (user_ids = attrs.delete(:user_ids))
if params[:member]
if params[:member][:user_ids]
attrs = params[:member].dup
user_ids = attrs.delete(:user_ids)
user_ids.each do |user_id|
members << Member.new(attrs.merge(:user_id => user_id))
members << Member.new(:role_ids => params[:member][:role_ids], :user_id => user_id)
end
else
members << Member.new(attrs)
members << Member.new(:role_ids => params[:member][:role_ids], :user_id => params[:member][:user_id])
end
@project.members << members
end
respond_to do |format|
if members.present? && members.all? {|m| m.valid? }
......
......@@ -48,26 +48,26 @@ class MessagesController < ApplicationController
# Create a new topic
def new
@message = Message.new(params[:message])
@message = Message.new
@message.author = User.current
@message.board = @board
if params[:message] && User.current.allowed_to?(:edit_messages, @project)
@message.locked = params[:message]['locked']
@message.sticky = params[:message]['sticky']
end
if request.post? && @message.save
call_hook(:controller_messages_new_after_save, { :params => params, :message => @message})
attachments = Attachment.attach_files(@message, params[:attachments])
render_attachment_warning_if_needed(@message)
redirect_to :action => 'show', :id => @message
@message.safe_attributes = params[:message]
if request.post?
if @message.save
call_hook(:controller_messages_new_after_save, { :params => params, :message => @message})
attachments = Attachment.attach_files(@message, params[:attachments])
render_attachment_warning_if_needed(@message)
redirect_to :action => 'show', :id => @message
end
end
end
# Reply to a topic
def reply
@reply = Message.new(params[:reply])
@reply = Message.new
@reply.author = User.current
@reply.board = @board
@reply.safe_attributes = params[:reply]
@topic.children << @reply
if !@reply.new_record?
call_hook(:controller_messages_reply_after_save, { :params => params, :message => @reply})
......@@ -80,11 +80,8 @@ class MessagesController < ApplicationController
# Edit a message
def edit
(render_403; return false) unless @message.editable_by?(User.current)
if params[:message]
@message.locked = params[:message]['locked']
@message.sticky = params[:message]['sticky']
end
if request.post? && @message.update_attributes(params[:message])
@message.safe_attributes = params[:message]
if request.post? && @message.save
attachments = Attachment.attach_files(@message, params[:attachments])
render_attachment_warning_if_needed(@message)
flash[:notice] = l(:notice_successful_update)
......
......@@ -59,14 +59,12 @@ class NewsController < ApplicationController
def create
@news = News.new(:project => @project, :author => User.current)
if request.post?
@news.attributes = params[:news]
if @news.save
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'news', :action => 'index', :project_id => @project
else
render :action => 'new'
end
@news.safe_attributes = params[:news]
if @news.save
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => 'news', :action => 'index', :project_id => @project
else
render :action => 'new'
end
end
......@@ -74,7 +72,8 @@ class NewsController < ApplicationController
end
def update
if request.put? and @news.update_attributes(params[:news])
@news.safe_attributes = params[:news]
if @news.save
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'show', :id => @news
else
......
......@@ -59,7 +59,8 @@ class ProjectsController < ApplicationController
def new
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.all
@project = Project.new(params[:project])
@project = Project.new
@project.safe_attributes = params[:project]
end
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
......
......@@ -97,7 +97,7 @@ class TimelogController < ApplicationController
def new
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
@time_entry.attributes = params[:time_entry]
@time_entry.safe_attributes = params[:time_entry]
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
render :action => 'edit'
......@@ -106,7 +106,7 @@ class TimelogController < ApplicationController
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
def create
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
@time_entry.attributes = params[:time_entry]
@time_entry.safe_attributes = params[:time_entry]
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
......@@ -127,14 +127,14 @@ class TimelogController < ApplicationController
end
def edit
@time_entry.attributes = params[:time_entry]
@time_entry.safe_attributes = params[:time_entry]
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
end
verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
def update
@time_entry.attributes = params[:time_entry]
@time_entry.safe_attributes = params[:time_entry]
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
......
......@@ -56,7 +56,7 @@ class VersionsController < ApplicationController
if params[:version]
attributes = params[:version].dup
attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing'])
@version.attributes = attributes
@version.safe_attributes = attributes
end
end
......@@ -66,7 +66,7 @@ class VersionsController < ApplicationController
if params[:version]
attributes = params[:version].dup
attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing'])
@version.attributes = attributes
@version.safe_attributes = attributes
end
if request.post?
......@@ -101,7 +101,8 @@ class VersionsController < ApplicationController
if request.put? && params[:version]
attributes = params[:version].dup
attributes.delete('sharing') unless @version.allowed_sharings.include?(attributes['sharing'])
if @version.update_attributes(attributes)
@version.safe_attributes = attributes
if @version.save
flash[:notice] = l(:notice_successful_update)
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
else
......
......@@ -19,7 +19,7 @@ class WikisController < ApplicationController
# Create or update a project's wiki
def edit
@wiki = @project.wiki || Wiki.new(:project => @project)
@wiki.attributes = params[:wiki]
@wiki.safe_attributes = params[:wiki]
@wiki.save if request.post?
render(:update) {|page| page.replace_html "tab-content-wiki", :partial => 'projects/settings/wiki'}
end
......
......@@ -502,7 +502,7 @@ module ApplicationHelper
@parsed_headings = []
text = parse_non_pre_blocks(text) do |text|
[:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_headings].each do |method_name|
[:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_headings, :parse_relative_urls].each do |method_name|
send method_name, text, project, obj, attr, only_path, options
end
end
......@@ -543,6 +543,41 @@ module ApplicationHelper
parsed
end
RELATIVE_LINK_RE = %r{
<a
(?:
(\shref=
(?: # the href and link
(?:'(\/[^>]+?)')|
(?:"(\/[^>]+?)")
)
)|
[^>]
)*
>
[^<]*?<\/a> # content and closing link tag.
}x unless const_defined?(:RELATIVE_LINK_RE)
def parse_relative_urls(text, project, obj, attr, only_path, options)
return if only_path
text.gsub!(RELATIVE_LINK_RE) do |m|
href, relative_url = $1, $2 || $3
next m unless href.present?
if defined?(request) && request.present?
# we have a request!
protocol, host_with_port = request.protocol, request.host_with_port
elsif @controller
# use the same methods as url_for in the Mailer
url_opts = @controller.class.default_url_options
next m unless url_opts && url_opts[:protocol] && url_opts[:host]
protocol, host_with_port = "#{url_opts[:protocol]}://", url_opts[:host]
else
next m
end
m.sub href, " href=\"#{protocol}#{host_with_port}#{relative_url}\""
end
end
def parse_inline_attachments(text, project, obj, attr, only_path, options)
# when using an image link, try to use an attachment, if possible
if options[:attachments] || (obj && obj.respond_to?(:attachments))
......@@ -816,7 +851,7 @@ module ApplicationHelper
def back_url_hidden_field_tag
back_url = params[:back_url] || request.env['HTTP_REFERER']
back_url = CGI.unescape(back_url.to_s)
hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
hidden_field_tag('back_url', CGI.escape(back_url), :id => nil) unless back_url.blank?
end
def check_all_links(form_name)
......@@ -850,7 +885,7 @@ module ApplicationHelper
def context_menu(url)
unless @context_menu_included
content_for :header_tags do
javascript_include_tag('context_menu') +
javascript_include_tag('context_menu.jquery') +
stylesheet_link_tag('context_menu')
end
if l(:direction) == 'rtl'
......@@ -860,7 +895,7 @@ module ApplicationHelper
end
@context_menu_included = true
end
javascript_tag "new ContextMenu('#{ url_for(url) }')"
javascript_tag "jQuery(document).ContextMenu('#{ url_for(url) }')"
end
def context_menu_link(name, url, options={})
......
......@@ -79,6 +79,22 @@ module IssuesHelper
s
end
def render_parents_and_subtree(issue)
return if issue.leaf? && !issue.parent
s = '<form><table id="issue_tree" class="list">'
issue_list(issue.self_and_ancestors.sort_by(&:lft) + issue.descendants.sort_by(&:lft)) do |el, level|
s << content_tag('tr',
content_tag('td', check_box_tag("ids[]", el.id, false, :id => nil), :class => 'checkbox') +
content_tag('td', link_to_issue(el, :truncate => 60), :class => 'subject') +
content_tag('td', h(el.status)) +
content_tag('td', link_to_user(el.assigned_to)) +
content_tag('td', progress_bar(el.done_ratio, :width => '80px')),
:class => "issue issue-#{el.id} #{"self" if el == issue} hascontextmenu #{level > 0 ? "idnt idnt-#{level}" : nil}")
end
s << '</table></form>'
s
end
def render_custom_fields_rows(issue)
return if issue.custom_field_values.empty?
ordered_values = []
......
......@@ -13,8 +13,11 @@
#++
class Comment < ActiveRecord::Base
include Redmine::SafeAttributes
belongs_to :commented, :polymorphic => true, :counter_cache => true
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
validates_presence_of :commented, :author, :comments
safe_attributes 'comments'
end
......@@ -13,6 +13,7 @@
#++
class Document < ActiveRecord::Base
include Redmine::SafeAttributes
belongs_to :project
belongs_to :category, :class_name => "DocumentCategory", :foreign_key => "category_id"
acts_as_attachable :delete_permission => :manage_documents
......@@ -32,6 +33,8 @@ class Document < ActiveRecord::Base
named_scope :visible, lambda {|*args| { :include => :project,
:conditions => Project.allowed_to_condition(args.first || User.current, :view_documents) } }
safe_attributes 'category_id', 'title', 'description'
def visible?(user=User.current)
!user.nil? && user.allowed_to?(:view_documents, project)
end
......
......@@ -48,4 +48,9 @@ class Group < Principal
:conditions => ["#{Member.table_name}.user_id = ? AND #{MemberRole.table_name}.inherited_from IN (?)", user.id, member.member_role_ids]).each(&:destroy)
end
end
def self.human_attribute_name(attribute_name)
attribute_name = "name" if attribute_name == "lastname"
super(attribute_name)
end
end
......@@ -707,6 +707,15 @@ class Issue < ActiveRecord::Base
projects
end
# Overrides Redmine::Acts::Journalized::Permissions
#
# The default assumption is that journals have the same permissions
# as the journaled object, issue notes have separate permissions though
def journal_editable_by?(journal, user)
return true if journal.author == user && user.allowed_to?(:edit_own_issue_notes, project)
user.allowed_to? :edit_issue_notes, project
end
private
def update_nested_set_attributes
......
......@@ -13,6 +13,7 @@
#++
class IssueCategory < ActiveRecord::Base
include Redmine::SafeAttributes
belongs_to :project
belongs_to :assigned_to, :class_name => 'User', :foreign_key => 'assigned_to_id'
has_many :issues, :foreign_key => 'category_id', :dependent => :nullify
......@@ -21,6 +22,8 @@ class IssueCategory < ActiveRecord::Base
validates_uniqueness_of :name, :scope => [:project_id]
validates_length_of :name, :maximum => 30
safe_attributes 'name', 'assigned_to_id'
alias :destroy_without_reassign :destroy
# Destroy the category
......
......@@ -76,7 +76,7 @@ class Journal < ActiveRecord::Base
end
def editable_by?(user)
journaled.journal_editable_by?(user)
journaled.journal_editable_by?(self, user)
end
def details
......
......@@ -13,6 +13,7 @@
#++
class Message < ActiveRecord::Base
include Redmine::SafeAttributes
belongs_to :board
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
acts_as_tree :counter_cache => :replies_count, :order => "#{Message.table_name}.created_on ASC"
......@@ -49,6 +50,12 @@ class Message < ActiveRecord::Base
named_scope :visible, lambda {|*args| { :include => {:board => :project},
:conditions => Project.allowed_to_condition(args.first || User.current, :view_messages) } }
safe_attributes 'subject', 'content'
safe_attributes 'locked', 'sticky',
:if => lambda {|message, user|
user.allowed_to?(:edit_messages, message.project)
}
def visible?(user=User.current)
!user.nil? && user.allowed_to?(:view_messages, project)
end
......
......@@ -13,6 +13,7 @@
#++
class News < ActiveRecord::Base
include Redmine::SafeAttributes
belongs_to :project
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
has_many :comments, :as => :commented, :dependent => :delete_all, :order => "created_on"
......@@ -21,7 +22,8 @@ class News < ActiveRecord::Base
validates_length_of :title, :maximum => 60
validates_length_of :summary, :maximum => 255
acts_as_journalized :event_url => Proc.new {|o| {:controller => 'news', :action => 'show', :id => o.journaled_id} }
acts_as_journalized :event_url => Proc.new {|o| {:controller => 'news', :action => 'show', :id => o.journaled_id} },
:event_description => :description
acts_as_searchable :columns => ["#{table_name}.title", "#{table_name}.summary", "#{table_name}.description"], :include => :project
acts_as_watchable
......@@ -32,10 +34,17 @@ class News < ActiveRecord::Base
:conditions => Project.allowed_to_condition(args.first || User.current, :view_news)
}}
safe_attributes 'title', 'summary', 'description'
def visible?(user=User.current)
!user.nil? && user.allowed_to?(:view_news, project)
end
# Returns true if the news can be commented by user
def commentable?(user=User.current)
user.allowed_to?(:comment_news, project)
end
# returns latest news for projects visible by user
def self.latest(user = User.current, count = 5)
find(:all, :limit => count, :conditions => Project.allowed_to_condition(user, :view_news), :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
......
......@@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'redmine/scm/adapters/bazaar_adapter'
require_dependency 'redmine/scm/adapters/bazaar_adapter'
class Repository::Bazaar < Repository
attr_protected :root_url
......
......@@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'redmine/scm/adapters/cvs_adapter'
require_dependency 'redmine/scm/adapters/cvs_adapter'
require 'digest/sha1'
class Repository::Cvs < Repository
......
......@@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'redmine/scm/adapters/darcs_adapter'
require_dependency 'redmine/scm/adapters/darcs_adapter'
class Repository::Darcs < Repository
validates_presence_of :url, :log_encoding
......
......@@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'redmine/scm/adapters/filesystem_adapter'
require_dependency 'redmine/scm/adapters/filesystem_adapter'
class Repository::Filesystem < Repository
attr_protected :root_url
......
......@@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'redmine/scm/adapters/git_adapter'
require_dependency 'redmine/scm/adapters/git_adapter'
class Repository::Git < Repository
attr_protected :root_url
......
......@@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'redmine/scm/adapters/mercurial_adapter'
require_dependency 'redmine/scm/adapters/mercurial_adapter'
class Repository::Mercurial < Repository
# sort changesets by revision number
......
......@@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'redmine/scm/adapters/subversion_adapter'
require_dependency 'redmine/scm/adapters/subversion_adapter'
class Repository::Subversion < Repository
attr_protected :root_url
......
......@@ -13,6 +13,7 @@
#++
class TimeEntry < ActiveRecord::Base
include Redmine::SafeAttributes
# could have used polymorphic association
# project association here allows easy loading of time entries at project level with one database trip
belongs_to :project
......@@ -37,6 +38,8 @@ class TimeEntry < ActiveRecord::Base
:conditions => Project.allowed_to_condition(args.first || User.current, :view_time_entries)
}}
safe_attributes 'hours', 'comments', 'issue_id', 'activity_id', 'spent_on', 'custom_field_values'
def after_initialize
if new_record? && self.activity.nil?
if default_activity = TimeEntryActivity.default
......
......@@ -64,10 +64,9 @@ class User < Principal
validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
# Login must contain lettres, numbers, underscores only
validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
validates_length_of :login, :maximum => 30
validates_length_of :firstname, :lastname, :maximum => 30
validates_length_of :login, :firstname, :lastname, :maximum => 255
validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
validates_length_of :mail, :maximum => 60, :allow_nil => true
validates_length_of :mail, :maximum => 255, :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 :status, :in => [STATUS_ANONYMOUS, STATUS_ACTIVE, STATUS_REGISTERED, STATUS_LOCKED]
......
......@@ -16,7 +16,7 @@ class UserPreference < ActiveRecord::Base
belongs_to :user
serialize :others
attr_protected :others
attr_protected :others, :user_id
def initialize(attributes = nil)
super
......
......@@ -13,6 +13,7 @@
#++
class Version < ActiveRecord::Base
include Redmine::SafeAttributes
after_update :update_issues_from_sharing_change
belongs_to :project
has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id', :dependent => :nullify
......@@ -34,6 +35,15 @@ class Version < ActiveRecord::Base
named_scope :visible, lambda {|*args| { :include => :project,
:conditions => Project.allowed_to_condition(args.first || User.current, :view_issues) } }
safe_attributes 'name',
'description',
'effective_date',
'due_date',
'wiki_page_title',
'status',
'sharing',
'custom_field_values'
# Returns true if +user+ or current user is allowed to view the version
def visible?(user=User.current)
user.allowed_to?(:view_issues, self.project)
......
......@@ -13,6 +13,7 @@
#++
class Wiki < ActiveRecord::Base
include Redmine::SafeAttributes
belongs_to :project
has_many :pages, :class_name => 'WikiPage', :dependent => :destroy, :order => 'title'
has_many :redirects, :class_name => 'WikiRedirect', :dependent => :delete_all
......@@ -22,6 +23,8 @@ class Wiki < ActiveRecord::Base
validates_presence_of :start_page
validates_format_of :start_page, :with => /^[^,\.\/\?\;\|\:]*$/
safe_attributes 'start_page'
def visible?(user=User.current)
!user.nil? && user.allowed_to?(:view_wiki_pages, project)
end
......
<div id="nav-login-content">
<% form_tag({:controller => "account", :action=> "login"}) do %>
<%= hidden_field_tag('back_url', CGI.escape(request.url)) %>
<%= hidden_field_tag 'back_url', CGI.escape(request.url), :id => nil %>
<table>
<tr>
<td><label for="username-pulldown"><%= l(:field_login) %></label></td>
<td><label for="password-pulldown"><%= l(:field_password) %></label></td>
<td></td>
</tr>
<tr>
<td><%= text_field_tag 'username', nil, :tabindex => '1', :id => 'username-pulldown' %></td>
......
......@@ -42,6 +42,7 @@
<% content_for :sidebar do %>
<% form_tag({}, :method => :get) do %>
<h3><%= l(:label_activity) %></h3>
<%= hidden_field_tag "set_filter", 1, :id => nil %>
<p><% @activity.event_types.each do |t| %>
<%= check_box_tag "show_#{t}", 1, @activity.scope.include?(t) %>
<label for="show_<%=t%>"><%= link_to(l("label_#{t.singularize}_plural"), {"show_#{t}" => 1, :user_id => params[:user_id]})%></label>
......
......@@ -72,7 +72,7 @@ t_height = g_height + headers_height
<p class="warning"><%= l(:notice_gantt_chart_truncated, :max => @gantt.max_rows) %></p>
<% end %>
<table width="100%" style="border:0; border-collapse: collapse;">
<table style="width:100%; border:0; border-collapse: collapse;">
<tr>
<td style="width:<%= subject_width %>px; padding:0px;">
......@@ -98,7 +98,7 @@ month_f = @gantt.date_from
left = 0
height = (show_weeks ? header_heigth : header_heigth + g_height)
@gantt.months.times do
width = ((month_f >> 1) - month_f) * zoom - 1
width = (((month_f >> 1) - month_f) * zoom - 1).to_i
%>
<div style="left:<%= left %>px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">
<%= link_to h("#{month_f.year}-#{month_f.month}"), @gantt.params.merge(:year => month_f.year, :month => month_f.month), :title => "#{month_name(month_f.month)} #{month_f.year}"%>
......@@ -176,7 +176,7 @@ if Date.today >= @gantt.date_from and Date.today <= @gantt.date_to %>
</tr>
</table>
<table width="100%">
<table style="width:100%">
<tr>
<td align="left"><%= link_to_content_update('&#171; ' + l(:label_previous), params.merge(@gantt.params_previous)) %></td>
<td align="right"><%= link_to_content_update(l(:label_next) + ' &#187;', params.merge(@gantt.params_next)) %></td>
......
......@@ -12,7 +12,7 @@
<% html_title "Wiki Syntax Quick Reference" %>
<h1>Wiki Syntax Quick Reference</h1>
<table width="100%">
<table style="width:100%">
<tr><th colspan="3">Font Styles</th></tr>
<tr><th><img src="../images/jstoolbar/bt_strong.png" style="border: 1px solid #bbb;" alt="Strong" /></th><td width="50%">*Strong*</td><td width="50%"><strong>Strong</strong></td></tr>
<tr><th><img src="../images/jstoolbar/bt_em.png" style="border: 1px solid #bbb;" alt="Italic" /></th><td>_Italic_</td><td><em>Italic</em></td></tr>
......
<% form_tag({}) do -%>
<%= hidden_field_tag 'back_url', url_for(params) %>
<%= hidden_field_tag 'back_url', url_for(params), :id => nil %>
<div class="autoscroll">
<table class="list issues">
<thead><tr>
......
<% if !@issue.leaf? || @issue.parent || User.current.allowed_to?(:manage_subtasks, @project) %>
<hr />
<p>
<strong><%= l(:label_issue_hierarchy) %></strong>
<% if User.current.allowed_to?(:manage_subtasks, @project) %>
(<%= link_to(l(:label_subtask_add), {:controller => 'issues', :action => 'new', :project_id => @project, :issue => {:parent_issue_id => @issue}}) %>)
<% end %>
</p>
<% end %>
<%= render_parents_and_subtree @issue %>
......@@ -39,12 +39,16 @@
<% if User.current.allowed_to?(:view_time_entries, @project) %>
<th class="spent-time"><%=l(:label_spent_time)%>:</th>
<td class="spent-time"><%= @issue.spent_hours > 0 ? (link_to l_hours(@issue.spent_hours), {:controller => 'timelog', :action => 'index', :project_id => @project, :issue_id => @issue}) : "-" %></td>
<% else %>
<th></th><td></td>
<% end %>
</tr>
<tr>
<th class="fixed-version"><%=l(:field_fixed_version)%>:</th><td class="fixed-version"><%= @issue.fixed_version ? link_to_version(@issue.fixed_version) : "-" %></td>
<% if @issue.estimated_hours %>
<th class="estimated-hours"><%=l(:field_estimated_hours)%>:</th><td class="estimated-hours"><%= l_hours(@issue.estimated_hours) %></td>
<% else %>
<th></th><td></td>
<% end %>
</tr>
<%= render_custom_fields_rows(@issue) %>
......@@ -74,26 +78,16 @@
<%= call_hook(:view_issues_show_description_bottom, :issue => @issue) %>
<% if !@issue.leaf? || User.current.allowed_to?(:manage_subtasks, @project) %>
<hr />
<div id="issue_tree">
<p>
<strong><%=l(:label_subtask_plural)%></strong>
(<%= link_to(l(:button_add), {:controller => 'issues', :action => 'new', :project_id => @project, :issue => {:parent_issue_id => @issue}}) if User.current.allowed_to?(:manage_subtasks, @project) %>)
</p>
<%= render_descendants_tree(@issue) unless @issue.leaf? %>
</div>
<% end %>
<%= render :partial => 'tree_simple' %>
<% if authorize_for('issue_relations', 'new') || @issue.relations.present? %>
<hr />
<div id="relations">
<%= render :partial => 'relations' %>
</div>
<hr />
<% end %>
<hr />
</div>
<% if @changesets.present? %>
......@@ -148,9 +142,8 @@
<% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@issue.project} - #{@issue.tracker} ##{@issue.id}: #{@issue.subject}") %>
<%= stylesheet_link_tag 'scm' %>
<%= javascript_include_tag 'context_menu' %>
<%= javascript_include_tag 'context_menu.jquery' %>
<%= stylesheet_link_tag 'context_menu' %>
<%= stylesheet_link_tag 'context_menu_rtl' if l(:direction) == 'rtl' %>
<% end %>
<div id="context-menu" style="display: none;"></div>
<%= javascript_tag "new ContextMenu('#{issues_context_menu_path}')" %>
<%= javascript_tag "jQuery(document).ContextMenu('#{issues_context_menu_path}')" %>
......@@ -74,7 +74,6 @@
</ul>
</li>
<% end %>
<li>
<%= render_menu_node(help_menu_item) %>
<% unless User.current.logged? %>
<% if Setting.self_registration? %>
......@@ -98,7 +97,6 @@
</ul>
</li>
<% end %>
</li>
</ul>
<% end %>
</div>
......
<table style="border-collapse: collapse; border:0;">
<table class="query-columns">
<tr>
<td style="padding-left:0">
<%= label_tag "available_columns", l(:description_available_columns) %>
<br \>
<br />
<%= select_tag 'available_columns',
options_for_select((query.available_columns - query.columns).collect {|column| [column.caption, column.name]}),
:multiple => true, :size => 10, :style => "width:150px" %>
</td>
<td align="center" valign="middle">
<td class="buttons">
<input type="button" value="&#8594;"
onclick="moveOptions(this.form.available_columns, this.form.selected_columns);" /><br />
<input type="button" value="&#8592;"
......@@ -15,12 +15,12 @@
</td>
<td>
<%= label_tag "selected_columns", l(:description_selected_columns) %>
<br \>
<br />
<%= select_tag 'c[]',
options_for_select(query.columns.collect {|column| [column.caption, column.name]}),
:id => 'selected_columns', :multiple => true, :size => 10, :style => "width:150px" %>
</td>
<td align="center" valign="middle">
<td class="buttons">
<input type="button" value="&#8593;" onclick="moveOptionUp(this.form.selected_columns);" /><br />
<input type="button" value="&#8595;" onclick="moveOptionDown(this.form.selected_columns);" />
</td>
......
......@@ -100,7 +100,7 @@ Event.observe(document,"dom:loaded", apply_filters_observer);
//]]>
</script>
<table width="100%">
<table style="width:100%">
<tr>
<td>
<table>
......@@ -113,7 +113,7 @@ Event.observe(document,"dom:loaded", apply_filters_observer);
<label for="cb_<%= field %>"><%= filter[1][:name] || l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) %></label>
</td>
<td style="width:150px;">
<%= label_tag "op_#{field}", l(:description_filter), :class => "hidden-for-sighted" %>
<%= label_tag "operators_#{field}", l(:description_filter), :class => "hidden-for-sighted" %>
<%= select_tag "op[#{field}]", options_for_select(operators_for_select(options[:type]), query.operator_for(field)), :id => "operators_#{field}", :onchange => "toggle_operator('#{field}');", :class => "select-small", :style => "vertical-align: top;" %>
</td>
<td>
......
......@@ -53,17 +53,16 @@
#
# ==== SMTP server at using TLS (GMail)
#
# This requires some additional configuration. See the article at:
# http://redmineblog.com/articles/setup-redmine-to-send-email-using-gmail/
# This might require some additional configuration. See the guides at:
# https://www.chiliproject.org/projects/chiliproject/wiki/Email_Delivery#SMTP-server-using-TLS-GMail
#
# production:
# email_delivery:
# delivery_method: :smtp
# smtp_settings:
# tls: true
# enable_starttls_auto: true
# address: "smtp.gmail.com"
# port: 587
# domain: "smtp.gmail.com" # 'your.domain.com' for GoogleApps
# authentication: :plain
# user_name: "your_email@gmail.com"
# password: "your_password"
......
......@@ -997,3 +997,8 @@ bg:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1011,3 +1011,8 @@ bs:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1000,3 +1000,8 @@ ca:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1221,3 +1221,8 @@ cs:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1013,3 +1013,8 @@ da:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -796,6 +796,8 @@ de:
label_api_access_key_created_on: Der API-Zugriffsschlüssel wurde vor %{value} erstellt
label_profile: Profil
label_subtask_plural: Unteraufgaben
label_subtask_add: Unteraufgabe hinzufügen
label_issue_hierarchy: Tickethierarchie
label_project_copy_notifications: Sende Mailbenachrichtigungen beim Kopieren des Projekts.
label_principal_search: "Nach Benutzer oder Gruppe suchen:"
label_user_search: "Nach Benutzer suchen:"
......@@ -1014,3 +1016,6 @@ de:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
......@@ -997,3 +997,8 @@ el:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1001,3 +1001,8 @@ en-GB:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -309,6 +309,9 @@ en:
field_visible: Visible
field_warn_on_leaving_unsaved: "Warn me when leaving a page with unsaved text"
field_custom_filter: Custom LDAP filter
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
setting_app_title: Application title
setting_app_subtitle: Application subtitle
......@@ -805,6 +808,8 @@ en:
label_api_access_key_created_on: "API access key created %{value} ago"
label_profile: Profile
label_subtask_plural: Subtasks
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
label_project_copy_notifications: Send email notifications during the project copy
label_principal_search: "Search for user or group:"
label_user_search: "Search for user:"
......
......@@ -1034,3 +1034,8 @@ es:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1001,3 +1001,8 @@ eu:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1000,3 +1000,8 @@ fa:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1018,3 +1018,8 @@ fi:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1015,3 +1015,8 @@ fr:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1009,3 +1009,8 @@ gl:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1002,3 +1002,8 @@ he:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1004,3 +1004,8 @@ hr:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1016,3 +1016,8 @@
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1005,3 +1005,8 @@ id:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -998,3 +998,8 @@ it:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1019,3 +1019,8 @@ ja:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1049,3 +1049,8 @@ ko:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1057,3 +1057,8 @@ lt:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -992,3 +992,8 @@ lv:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -997,3 +997,8 @@ mk:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -998,3 +998,8 @@ mn:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -979,3 +979,8 @@ nl:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -984,3 +984,8 @@
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1014,3 +1014,8 @@ pl:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1021,3 +1021,8 @@ pt-BR:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1001,3 +1001,8 @@ pt:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -990,3 +990,8 @@ ro:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1110,3 +1110,8 @@ ru:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -992,3 +992,8 @@ sk:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -993,3 +993,8 @@ sl:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -997,3 +997,8 @@ sr-YU:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -998,3 +998,8 @@ sr:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1039,3 +1039,8 @@ sv:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -994,3 +994,8 @@ th:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1016,3 +1016,8 @@ tr:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -993,3 +993,8 @@ uk:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1048,3 +1048,8 @@ vi:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1079,3 +1079,8 @@
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
......@@ -1011,3 +1011,8 @@ zh:
label_mail_handler_failure: "Failed email submission: %{subject}"
notice_not_authorized_action: You are not authorized to perform this action.
text_mail_handler_confirmation_successful: Your email has been successful added at the following url
field_issue_summary: Issue summary
field_new_saved_query: New saved query
field_issue_view_all_open: View all open issues
label_subtask_add: Add a subtask
label_issue_hierarchy: Issue hierarchy
#-- encoding: UTF-8
#-- copyright
# ChiliProject is a project management system.
#
# Copyright (C) 2010-2012 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
class UpUserFieldsLengthLimits < ActiveRecord::Migration
def self.up
change_column :users, :login, :string, :limit => nil
change_column :users, :mail, :string, :limit => nil
change_column :users, :firstname, :string, :limit => nil
change_column :users, :lastname, :string, :limit => nil
end
def self.down
change_column :users, :login, :string, :limit => 30
change_column :users, :mail, :string, :limit => 60
change_column :users, :firstname, :string, :limit => 30
change_column :users, :lastname, :string, :limit => 30
end
end
= ChiliProject Changelog
== 2012-04-04 v3.1.0
* Bug #739: Relative textile links not converted to full URLs in emails
* Bug #828: uninitialized constant Redmine::Scm::Adapters::CommandFailed in RepositoryController
* Bug #861: Apply Filter does not work on all-project-activities view
* Bug #868: Done bar has no filling
* Bug #869: Issue option list stacked vertically instead of horizontally
* Bug #873: Incorrect error message text for groups
* Bug #882: Right click context menu doesn't show submenu icon.
* Bug #887: Stacked month (top row)
* Bug #888: Cannot edit note
* Bug #891: quotes around path when shelling out does not work on Windows
* Bug #892: CP code or test assumes ordering where none is guaranteed
* Bug #896: Enabling "Authentication required" mode returns 404s
* Bug #903: ActionView::TemplateError (undefined method `new0' for DateTime:Class)
* Bug #911: Sub-sub (and deeper) issues CSS rules are overridden
* Bug #914: comments gets striked through, when description changes before
* Bug #922: Mass assignment
* Bug #927: Reposman script problem
* Bug #929: Missing links in Issues section in left menu bar
* Bug #933: News RSS Feed tag not populating
* Bug #939: GMail documentation in configuration.yml.default out of date
* Feature #559: Group Menus
* Feature #899: Create a jQuery verison of the context menu
* Feature #906: Add Link back to Parent of Subtask
* Feature #915: default bundle install installs old pg version
* Feature #928: Increase username length limit from 30 to 60
== 2012-02-06 v3.0.0
* Bug #826: Top right toolbar items overlap for custom issues query
......
......@@ -199,6 +199,7 @@ end
class Project < ActiveResource::Base
self.headers["User-agent"] = "Redmine repository manager/#{Version}"
self.format = :xml
end
log("querying Redmine for projects...", :level => 1);
......
......@@ -18,7 +18,7 @@ module ChiliProject
module VERSION #:nodoc:
MAJOR = 3
MINOR = 0
MINOR = 1
PATCH = 0
TINY = PATCH # Redmine compat
......@@ -43,7 +43,7 @@ module ChiliProject
git_dir = Rails.root.join('.git')
if File.directory? git_dir
git.send(:shellout, "#{git.sq_bin} --git-dir='#{git_dir}' rev-parse --short=9 HEAD") { |io| io.read }.to_s.chomp
git.send(:shellout, "#{git.sq_bin} --git-dir=#{git.shell_quote git_dir.to_s} rev-parse --short=9 HEAD") { |io| io.read }.to_s.chomp
end
end
end
......
......@@ -212,25 +212,178 @@ Redmine::MenuManager.map :admin_menu do |menu|
end
Redmine::MenuManager.map :project_menu do |menu|
menu.push :overview, { :controller => 'projects', :action => 'show' }
menu.push :activity, { :controller => 'activities', :action => 'index' }
menu.push :roadmap, { :controller => 'versions', :action => 'index' }, :param => :project_id,
:if => Proc.new { |p| p.shared_versions.any? }
menu.push :issues, { :controller => 'issues', :action => 'index' }, :param => :project_id, :caption => :label_issue_plural
menu.push :new_issue, { :controller => 'issues', :action => 'new' }, :param => :project_id, :caption => :label_issue_new,
include ProjectsHelper
# TODO: refactor to a helper that is available before app/helpers along with the other procs.
issue_query_proc = Proc.new { |p|
##### Taken from IssuesHelper
# User can see public queries and his own queries
visible = ARCondition.new(["is_public = ? OR user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
# Project specific queries and global queries
visible << (p.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", p.id])
sidebar_queries = Query.find(:all,
:select => 'id, name',
:order => "name ASC",
:conditions => visible.conditions)
sidebar_queries.collect do |query|
Redmine::MenuManager::MenuItem.new("query-#{query.id}".to_sym, { :controller => 'issues', :action => 'index', :project_id => p, :query_id => query }, {
:caption => query.name,
:param => :project_id,
:parent => :issues
})
end
}
menu.push(:overview, { :controller => 'projects', :action => 'show' })
menu.push(:activity, { :controller => 'activities', :action => 'index' })
menu.push(:roadmap, { :controller => 'versions', :action => 'index' }, {
:param => :project_id,
:if => Proc.new { |p| p.shared_versions.any? },
:children => Proc.new { |p|
versions = p.shared_versions.sort
versions.reject! {|version| version.closed? || version.completed? }
versions.collect do |version|
Redmine::MenuManager::MenuItem.new("version-#{version.id}".to_sym,
{ :controller => 'versions', :action => 'show', :id => version },
{
:caption => version.name,
:parent => :roadmap
})
end
}
})
menu.push(:issues, { :controller => 'issues', :action => 'index' }, {
:param => :project_id,
:caption => :label_issue_plural,
:children => issue_query_proc
})
menu.push(:new_issue, { :controller => 'issues', :action => 'new' }, {
:param => :project_id,
:caption => :label_issue_new,
:parent => :issues,
:html => { :accesskey => Redmine::AccessKeys.key_for(:new_issue) }
menu.push :gantt, { :controller => 'gantts', :action => 'show' }, :param => :project_id, :caption => :label_gantt
menu.push :calendar, { :controller => 'calendars', :action => 'show' }, :param => :project_id, :caption => :label_calendar
menu.push :news, { :controller => 'news', :action => 'index' }, :param => :project_id, :caption => :label_news_plural
menu.push :documents, { :controller => 'documents', :action => 'index' }, :param => :project_id, :caption => :label_document_plural
menu.push :wiki, { :controller => 'wiki', :action => 'show', :id => nil }, :param => :project_id,
})
menu.push(:all_open_issues, { :controller => 'issues', :action => 'index', :set_filter => 1 }, {
:param => :project_id,
:caption => :field_issue_view_all_open,
:parent => :issues
})
menu.push(:new_query, { :controller => 'queries', :action => 'new'}, {
:param => :project_id,
:caption => :field_new_saved_query,
:parent => :issues
})
menu.push(:issue_summary, { :controller => 'reports', :action => 'issue_report' }, {
:caption => :field_issue_summary,
:parent => :issues
})
menu.push(:time_entries, { :controller => 'timelog', :action => 'index' }, {
:param => :project_id,
:if => Proc.new {|p| User.current.allowed_to?(:view_time_entries, p) }
});
menu.push(:new_time_entry, { :controller => 'timelog', :action => 'new' }, {
:param => :project_id,
:if => Proc.new {|p| User.current.allowed_to?(:log_time, p) },
:parent => :time_entries
})
menu.push(:time_entry_report, { :controller => 'time_entry_reports', :action => 'report' }, {
:param => :project_id,
:if => Proc.new {|p| User.current.allowed_to?(:view_time_entries, p) },
:parent => :time_entries
})
menu.push(:gantt, { :controller => 'gantts', :action => 'show' }, {
:param => :project_id,
:caption => :label_gantt
})
menu.push(:calendar, { :controller => 'calendars', :action => 'show' }, {
:param => :project_id,
:caption => :label_calendar
})
menu.push(:news, { :controller => 'news', :action => 'index' }, {
:param => :project_id,
:caption => :label_news_plural
})
menu.push(:new_news, {:controller => 'news', :action => 'new' }, {
:param => :project_id,
:caption => :label_news_new,
:parent => :news,
:if => Proc.new {|p| User.current.allowed_to?(:manage_news, p) }
})
menu.push(:documents, { :controller => 'documents', :action => 'index' }, {
:param => :project_id,
:caption => :label_document_plural
})
menu.push(:new_document, { :controller => 'documents', :action => 'new' }, {
:param => :project_id,
:caption => :label_document_new,
:parent => :documents,
:if => Proc.new {|p| User.current.allowed_to?(:manage_documents, p) }
})
menu.push(:wiki, { :controller => 'wiki', :action => 'show', :id => nil }, {
:param => :project_id,
:if => Proc.new { |p| p.wiki && !p.wiki.new_record? }
})
menu.push(:wiki_by_title, { :controller => 'wiki', :action => 'index' }, {
:caption => :label_index_by_title,
:parent => :wiki,
:param => :project_id,
:if => Proc.new { |p| p.wiki && !p.wiki.new_record? }
})
menu.push(:wiki_by_date, { :controller => 'wiki', :action => 'date_index'}, {
:caption => :label_index_by_date,
:parent => :wiki,
:param => :project_id,
:if => Proc.new { |p| p.wiki && !p.wiki.new_record? }
menu.push :boards, { :controller => 'boards', :action => 'index', :id => nil }, :param => :project_id,
:if => Proc.new { |p| p.boards.any? }, :caption => :label_board_plural
menu.push :files, { :controller => 'files', :action => 'index' }, :caption => :label_file_plural, :param => :project_id
menu.push :repository, { :controller => 'repositories', :action => 'show' },
})
menu.push(:boards, { :controller => 'boards', :action => 'index', :id => nil }, {
:param => :project_id,
:caption => :label_board_plural,
:if => Proc.new { |p| p.boards.any? },
:children => Proc.new {|project|
project.boards.collect do |board|
Redmine::MenuManager::MenuItem.new(
"board-#{board.id}".to_sym,
{ :controller => 'boards', :action => 'show', :id => board },
{
:caption => board.name # is h() in menu_helper.rb
})
end
}
})
menu.push(:new_board, { :controller => 'boards', :action => 'new' }, {
:caption => :label_board_new,
:param => :project_id,
:parent => :boards,
:if => Proc.new {|p| User.current.allowed_to?(:manage_boards, p) }
})
menu.push(:files, { :controller => 'files', :action => 'index' }, {
:caption => :label_file_plural,
:param => :project_id
})
menu.push(:new_file, { :controller => 'files', :action => 'new' }, {
:caption => :label_attachment_new,
:param => :project_id,
:parent => :files,
:if => Proc.new {|p| User.current.allowed_to?(:manage_files, p) }
})
menu.push(:repository, { :controller => 'repositories', :action => 'show' }, {
:if => Proc.new { |p| p.repository && !p.repository.new_record? }
menu.push :settings, { :controller => 'projects', :action => 'settings' }, :last => true
})
menu.push(:settings, { :controller => 'projects', :action => 'settings' }, {
:last => true,
:children => Proc.new { |p|
@project = p # @project used in the helper
project_settings_tabs.collect do |tab|
Redmine::MenuManager::MenuItem.new("settings-#{tab[:name]}".to_sym,
{ :controller => 'projects', :action => 'settings', :id => p, :tab => tab[:name] },
{
:caption => tab[:label]
})
end
}
})
end
Redmine::Activity.map do |activity|
......
......@@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'redmine/scm/adapters/abstract_adapter'
require_dependency 'redmine/scm/adapters/abstract_adapter'
module Redmine
module Scm
......
......@@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'redmine/scm/adapters/abstract_adapter'
require_dependency 'redmine/scm/adapters/abstract_adapter'
module Redmine
module Scm
......
......@@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'redmine/scm/adapters/abstract_adapter'
require_dependency 'redmine/scm/adapters/abstract_adapter'
require 'rexml/document'
module Redmine
......
......@@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'redmine/scm/adapters/abstract_adapter'
require_dependency 'redmine/scm/adapters/abstract_adapter'
require 'find'
module Redmine
......
......@@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'redmine/scm/adapters/abstract_adapter'
require_dependency 'redmine/scm/adapters/abstract_adapter'
module Redmine
module Scm
......
......@@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'redmine/scm/adapters/abstract_adapter'
require_dependency 'redmine/scm/adapters/abstract_adapter'
require 'cgi'
module Redmine
......
......@@ -12,7 +12,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'redmine/scm/adapters/abstract_adapter'
require_dependency 'redmine/scm/adapters/abstract_adapter'
require 'uri'
module Redmine
......
......@@ -470,13 +470,12 @@ jQuery.ajaxSetup({
'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript")}
})
// Show/hide the ajax indicators
jQuery("#ajax-indicator").ajaxStart(function(){ jQuery(this).show().css('z-index', '9999'); });
jQuery("#ajax-indicator").ajaxStop(function(){ jQuery(this).hide(); });
/* TODO: integrate with existing code and/or refactor */
jQuery(document).ready(function($) {
// Show/hide the ajax indicators
jQuery("#ajax-indicator").ajaxStart(function(){ jQuery(this).show().css('z-index', '9999'); });
jQuery("#ajax-indicator").ajaxStop(function(){ jQuery(this).hide(); });
// file table thumbnails
$("table a.has-thumb").hover(function() {
......
(function($) {
var ContextMenuClass = function(el, options) {
var element = el;
var opts = options;
var observingContextMenuClick;
var lastSelected = null;
var menu;
var menuId = 'context-menu';
var selectorName = 'hascontextmenu';
var contextMenuSelectionClass = 'context-menu-selection';
var reverseXClass = 'reverse-x';
var reverseYClass = 'reverse-y';
var methods = {
createMenu: function() {
if(!menu) {
$('#wrapper').append('<div id="' + menuId + '" style="display:none"></div>');
menu = $('#' + menuId);
}
},
click: function(e) {
var target = $(e.target);
if(target.is('a')) {
return;
}
switch(e.which) {
case 1:
if(e.type === 'click') {
methods.hideMenu();
methods.leftClick(e);
break;
}
case 3:
if(e.type === 'contextmenu') {
methods.hideMenu();
methods.rightClick(e);
break;
}
default:
return;
}
},
leftClick: function(e) {
var target = $(e.target);
var tr = target.parents('tr');
if((tr.size() > 0) && tr.hasClass(selectorName))
{
// a row was clicked, check if the click was on checkbox
if(target.is('input'))
{
// a checkbox may be clicked
if (target.is(':checked')) {
tr.addClass(contextMenuSelectionClass);
} else {
tr.removeClass(contextMenuSelectionClass);
}
}
else
{
if (e.ctrlKey || e.metaKey)
{
methods.toggleSelection(tr);
}
else if (e.shiftKey)
{
if (lastSelected !== null)
{
var toggling = false;
var rows = $(selectorName);
for (i = 0; i < rows.length; i++)
{
if (toggling || rows[i] == tr)
{
methods.addSelection(rows[i]);
}
if (rows[i] == tr || rows[i] == lastSelected)
{
toggling = !toggling;
}
}
} else {
methods.addSelection(tr);
}
} else {
methods.unselectAll();
methods.addSelection(tr);
}
lastSelected = tr;
}
}
else
{
// click is outside the rows
if (target.is('a') === false) {
this.unselectAll();
} else {
if (target.hasClass('disabled') || target.hasClass('submenu')) {
e.preventDefault();
}
}
}
},
rightClick: function(e) {
var target = $(e.target);
var tr = target.parents('tr');
if((tr.size() === 0) || !(tr.hasClass(selectorName))) {
return;
}
e.preventDefault();
if(!methods.isSelected(tr)) {
methods.unselectAll();
methods.addSelection(tr);
lastSelected = tr;
}
methods.showMenu(e);
},
unselectAll: function() {
var rows = $('.' + contextMenuSelectionClass);
rows.each(function() {
methods.removeSelection($(this));
});
},
hideMenu: function() {
menu.hide();
},
showMenu: function(e) {
var target = $(e.target);
var params = target.parents('form').serialize();
var mouseX = e.pageX;
var mouseY = e.pageY;
var renderX = mouseX;
var renderY = mouseY;
$.ajax({
url: opts.url,
data: params,
success: function(response, success) {
menu.html(response);
var maxWidth = mouseX + (2 * menu.width());
var maxHeight = mouseY + menu.height();
if(maxWidth > $(window).width()) {
renderX -= menu.width();
menu.addClass(reverseXClass);
} else {
menu.removeClass(reverseXClass);
}
if(maxHeight > $(window).height()) {
renderY =+ menu.height();
menu.addClass(reverseYClass);
} else {
menu.removeClass(reverseYClass);
}
if(renderX <= 0) {
renderX = 1;
}
if(renderY <= 0) {
renderY = 1;
}
menu.css('top', renderY).css('left', renderX);
menu.show();
}
});
},
addSelection: function(element) {
element.addClass(contextMenuSelectionClass);
methods.checkSelectionBox(element, true);
},
isSelected: function(element) {
return element.hasClass(contextMenuSelectionClass);
},
toggleSelection: function(element) {
if(methods.isSelected(element)) {
methods.removeSelection(element);
} else {
methods.addSelection(element);
}
},
removeSelection: function(element) {
element.removeClass(contextMenuSelectionClass);
methods.checkSelectionBox(element, false);
},
checkSelectionBox: function(element, checked) {
var inputs = element.find('input');
inputs.each(function() {
inputs.attr('checked', checked ? 'checked' : false);
});
}
};
methods.createMenu();
if(!observingContextMenuClick) {
element.bind('click.contextMenu', methods.click);
element.bind('contextmenu.contextMenu', methods.click);
observingContextMenuClick = true;
}
methods.unselectAll();
};
$.fn.ContextMenu = function(u) {
return this.each(function() {
new ContextMenuClass($(this), {url: u});
});
};
})(jQuery);
/**
* Wrapper function to support the old way of creating aa context menu.
*/
function ContextMenu() {
jQuery(document).ContextMenu(arguments);
}
......@@ -121,16 +121,16 @@ tr.issue td.subject, tr.issue td.category, td.assigned_to { white-space: normal;
tr.issue td.subject { text-align: left; }
tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;}
tr.issue.idnt td.subject a {background: url(../images/bullet_arrow_right.png) no-repeat 0 50%; padding-left: 16px;}
tr.issue.idnt-1 td.subject {padding-left: 0.5em;}
tr.issue.idnt-2 td.subject {padding-left: 2em;}
tr.issue.idnt-3 td.subject {padding-left: 3.5em;}
tr.issue.idnt-4 td.subject {padding-left: 5em;}
tr.issue.idnt-5 td.subject {padding-left: 6.5em;}
tr.issue.idnt-6 td.subject {padding-left: 8em;}
tr.issue.idnt-7 td.subject {padding-left: 9.5em;}
tr.issue.idnt-8 td.subject {padding-left: 11em;}
tr.issue.idnt-9 td.subject {padding-left: 12.5em;}
#content tr.issue.idnt td.subject a {background: url(../images/bullet_arrow_right.png) no-repeat 0 50%; padding-left: 16px;}
#content tr.issue.idnt-1 td.subject {padding-left: 0.5em;}
#content tr.issue.idnt-2 td.subject {padding-left: 2em;}
#content tr.issue.idnt-3 td.subject {padding-left: 3.5em;}
#content tr.issue.idnt-4 td.subject {padding-left: 5em;}
#content tr.issue.idnt-5 td.subject {padding-left: 6.5em;}
#content tr.issue.idnt-6 td.subject {padding-left: 8em;}
#content tr.issue.idnt-7 td.subject {padding-left: 9.5em;}
#content tr.issue.idnt-8 td.subject {padding-left: 11em;}
#content tr.issue.idnt-9 td.subject {padding-left: 12.5em;}
tr.entry { border: 1px solid #f8f8f8; }
tr.entry td { white-space: nowrap; }
......@@ -204,6 +204,16 @@ table.attributes td { vertical-align: top; }
table.boards a.board, h3.comments { background: url(../images/comment.png) no-repeat 0% 50%; padding-left: 20px; }
table.query-columns {
border-collapse: collapse;
border: 0;
}
table.query-columns td.buttons {
vertical-align: middle;
text-align: center;
}
td.center {text-align:center;}
h3.version { background: url(../images/package.png) no-repeat 0% 50%; padding-left: 20px; }
......@@ -264,8 +274,10 @@ div.issue div.subject p {margin: 0; margin-bottom: 0.1em; font-size: 90%; color:
div.issue div.subject>div>p { margin-top: 0.5em; }
div.issue div.subject h3 {margin: 0; margin-bottom: 0.1em;}
#issue_tree {border: none;}
#issue_tree table.issues { border: 0; }
#issue_tree td.checkbox {display:none;}
#issue_tree tr.self a.issue {color: inherit;}
#content fieldset#filters {
padding-bottom:10px;
......@@ -351,7 +363,7 @@ div#issue-changesets div.changeset { border-bottom: 1px solid #ddd; }
div#issue-changesets p { margin-top: 0; margin-bottom: 1em;}
div#activity dl, #search-results { margin-left: 2em; }
div#activity dd, #search-results dd { margin-bottom: 1em; padding-left: 18px; font-size: 0.9em; }
div#activity dd, #search-results dd { margin-bottom: 1em; font-size: 0.9em; overflow: hidden; }
div#activity dt, #search-results dt { margin-bottom: 0px; padding-left: 20px; line-height: 18px; background-position: 0 50%; background-repeat: no-repeat; }
div#activity dt.me .time { border-bottom: 1px solid #999; }
div#activity dt .time { color: #777; font-size: 80%; }
......@@ -624,7 +636,7 @@ color:#505050;
}
/***** Progress bar *****/
table.progress {
#content table.progress {
border: 1px solid #D7D7D7;
border-collapse: collapse;
border-spacing: 0pt;
......@@ -634,12 +646,12 @@ table.progress {
margin: 1px 6px 1px 0px;
}
table.progress td { height: 0.9em; }
table.progress td.closed { background: #BAE0BA none repeat scroll 0%; }
table.progress td.done { background: #DEF0DE none repeat scroll 0%; }
table.progress td.open { background: #FFF none repeat scroll 0%; }
p.pourcent {font-size: 80%;}
p.progress-info {clear: left; font-style: italic; font-size: 80%;}
#content table.progress td { height: 0.9em; }
#content table.progress td.closed { background: #BAE0BA none repeat scroll 0%; }
#content table.progress td.done { background: #DEF0DE none repeat scroll 0%; }
#content table.progress td.open { background: #FFF none repeat scroll 0%; }
#content p.pourcent {font-size: 80%;}
#content p.progress-info {clear: left; font-style: italic; font-size: 80%;}
/***** Tabs *****/
#content .tabs {height: 2.6em; margin-bottom:1.2em; position:relative; overflow:hidden;}
......@@ -1703,6 +1715,7 @@ form#issue-list {
.title-bar .title-bar-actions .contextual a.icon {
color:#000000;
margin-right: 0px;
float: none;
}
.title-bar .update {
right:0;
......
......@@ -39,7 +39,6 @@
}
#context-menu li>a { width:auto; } /* others */
#context-menu a.disabled, #context-menu a.disabled:hover {color: #ccc;}
#context-menu li a.submenu { background:url("../images/bullet_arrow_right.png") right no-repeat; }
#context-menu li:hover { border:1px solid gray; background-color:#eee; }
#context-menu a:hover {color:#2A5685;}
#context-menu li.folder:hover { z-index:40; }
......@@ -49,3 +48,15 @@
/* selected element */
.context-menu-selection { background-color:#507AAA !important; color:#000 !important; }
.context-menu-selection:hover { background-color:#507AAA !important; color:#000 !important; }
#context-menu .submenu {
border: transparent solid 5px;
border-left-color: #6A0406;
position: absolute;
top: 5px;
right: 3px;
}
#context-menu .folder:hover .submenu {
border-left-color: #2A5685;
}
......@@ -98,4 +98,12 @@ class ActivitiesControllerTest < ActionController::TestCase
:attributes => {:href => 'http://test.host/issues/11'}}
end
def test_index_without_filters
get :index, :set_filter => 1
assert_response :success
assert_template 'index'
assert_tag :tag => 'p',
:attributes => {:class => /nodata/},
:content => "No data to display"
end
end
......@@ -45,7 +45,7 @@ class TrackersControllerTest < ActionController::TestCase
assert_redirected_to :action => 'index'
tracker = Tracker.find_by_name('New tracker')
assert_equal [1], tracker.project_ids.sort
assert_equal [1, 6], tracker.custom_field_ids
assert_equal [1, 6], tracker.custom_field_ids.sort
assert_equal 0, tracker.workflows.count
end
......
......@@ -38,7 +38,7 @@ class VersionsControllerTest < ActionController::TestCase
# Completed version doesn't appear
assert !assigns(:versions).include?(Version.find(1))
# Context menu on issues
assert_select "script", :text => Regexp.new(Regexp.escape("new ContextMenu('/issues/context_menu')"))
assert_select "script", :text => Regexp.new(Regexp.escape("jQuery(document).ContextMenu('/issues/context_menu')"))
end
def test_index_with_completed_versions
......
......@@ -16,11 +16,20 @@ require File.expand_path('../../test_helper', __FILE__)
class GroupTest < ActiveSupport::TestCase
fixtures :all
include Redmine::I18n
def test_create
g = Group.new(:lastname => 'New group')
assert g.save
end
def test_blank_name_error_message
set_language_if_valid 'en'
new_group = Group.new
assert !new_group.valid?
assert new_group.errors.full_messages.include? "Name can't be blank"
end
def test_roles_given_to_new_user
group = Group.find(11)
user = User.find(9)
......
......@@ -27,6 +27,10 @@ class ApplicationHelperTest < ActionView::TestCase
super
end
def request
@request ||= ActionController::TestRequest.new
end
context "#link_to_if_authorized" do
context "authorized user" do
should "be tested"
......@@ -144,6 +148,34 @@ RAW
to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
end
def test_textile_relative_to_full_links_in_a_controller
# we have a request here
{
# shouldn't change non-relative links
'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
'This is an intern "link":/foo/bar' => 'This is an intern <a href="http://test.host/foo/bar">link</a>',
'This is an intern "link":/foo/bar and an extern "link":http://foo.bar' => 'This is an intern <a href="http://test.host/foo/bar">link</a> and an extern <a href="http://foo.bar" class="external">link</a>',
}.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :only_path => false) }
end
def test_textile_relative_to_full_links_in_the_mailer
# we don't a request here
undef request
# mimic the mailer default_url_options
@controller.class.class_eval {
def self.default_url_options
::Mailer.default_url_options
end
}
{
# shouldn't change non-relative links
'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
'This is an intern "link":/foo/bar' => 'This is an intern <a href="http://localhost:3000/foo/bar">link</a>',
'This is an intern "link":/foo/bar and an extern "link":http://foo.bar' => 'This is an intern <a href="http://localhost:3000/foo/bar">link</a> and an extern <a href="http://foo.bar" class="external">link</a>',
}.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :only_path => false) }
end
def test_redmine_links
issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3},
:class => 'issue status-1 priority-1 overdue', :title => 'Error 281 when updating a recipe (New)')
......
......@@ -73,8 +73,8 @@ class RedmineTest < ActiveSupport::TestCase
assert_menu_contains_item_named :project_menu, :settings
end
def test_new_issue_should_have_root_as_a_parent
def test_new_issue_should_have_issues_as_a_parent
new_issue = get_menu_item(:project_menu, :new_issue)
assert_equal :root, new_issue.parent.name
assert_equal :issues, new_issue.parent.name
end
end
......@@ -181,7 +181,7 @@ module Redmine
end
options[:type] ||= self.name.underscore.dasherize # Make sure the name of the journalized model and not the name of the journal is used for events
options[:author] ||= :user
{ :description => :notes }.reverse_merge options
options.reverse_merge({:description => :notes})
end
end
......
......@@ -114,23 +114,27 @@ module JournalFormatter
end
end
def format_html_detail(label, old_value, value)
def format_html_detail(label, key, old_value, value, options = {})
label = content_tag('strong', label)
old_value = content_tag("i", h(old_value)) if old_value && !old_value.blank?
old_value = content_tag("strike", old_value) if old_value and value.blank?
value = content_tag("i", h(value)) if value.present?
# Sanitize values
old_value, value = h(old_value), h(value)
# Truncate as needed
old_value, value = truncate(old_value, :length => 80), truncate(value, :length => 80) if options[:truncate].present?
# Style
old_value = content_tag("i", old_value) if old_value.present?
old_value = content_tag("strike", old_value) if old_value.present? and value.blank?
value = content_tag("i", value) if value.present?
value ||= ""
[label, old_value, value]
end
# Formats a detail to be used with a Journal diff
#
# Truncates the content. Adds a link to view a diff.
def format_html_diff_detail(key, label, old_value, value)
link = link_to(l(:label_more), {:controller => 'journals', :action => 'diff', :id => id, :field => key.to_s}, :class => 'lightbox-ajax')
old_value = truncate(old_value, :length => 80)
value = truncate(value, :length => 80) + " " + link
[old_value, value]
# More link
if options[:more_link].present?
value += " " + link_to(l(:label_more), {:controller => 'journals', :action => 'diff', :id => id, :field => key.to_s}, :class => 'lightbox-ajax')
end
[label, old_value, value]
end
def property(detail)
......@@ -195,11 +199,10 @@ module JournalFormatter
label, old_value, value = [label, old_value, value].collect(&:to_s)
unless no_html
label, old_value, value = *format_html_detail(label, old_value, value)
options = {}
options = options.merge({:truncate => true, :more_link => true}) if property(detail) == :attribute && key == "description"
label, old_value, value = *format_html_detail(label, key, old_value, value, options)
value = format_html_attachment_detail(key.sub("attachments", ""), value) if attachment_detail
if property(detail) == :attribute && key == "description"
old_value, value = *format_html_diff_detail(key, label, old_value, value)
end
end
unless value.blank?
......
......@@ -22,7 +22,7 @@ module Redmine::Acts::Journalized
module Permissions
# Default implementation of journal editing permission
# Is overridden if defined in the journalized model directly
def journal_editable_by?(user)
def journal_editable_by?(journal, user)
return true if user.admin?
if respond_to? :editable_by?
editable_by? user
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment