Compare commits

..

9 Commits
0.7.3 ... 0.7.4

Author SHA1 Message Date
Jean-Philippe Lang
4e613c6c10 tagged version 0.7.4
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/tags/0.7.4@2206 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-30 11:28:31 +00:00
Jean-Philippe Lang
76bcbce2ef Updates CHANGELOG and version for 0.7.4 release.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.7-stable@2203 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-30 09:52:11 +00:00
Jean-Philippe Lang
7da7e853a3 Backported r2192 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.7-stable@2194 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-28 13:45:53 +00:00
Jean-Philippe Lang
6ae66f4dbf Merged r2183 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.7-stable@2185 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-27 14:08:09 +00:00
Jean-Philippe Lang
a98d4de1e3 Backported r2171 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.7-stable@2177 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-24 12:20:26 +00:00
Jean-Philippe Lang
5e4d66ad50 Backported r2170 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.7-stable@2176 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-24 11:56:36 +00:00
Jean-Philippe Lang
982d6bc748 Merged r2168 and r2169 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.7-stable@2175 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-24 11:44:55 +00:00
Jean-Philippe Lang
3169291039 Merged r2143 and r2144 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.7-stable@2174 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-24 11:42:05 +00:00
Jean-Philippe Lang
de86f4b965 Merged r1930 from trunk.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/0.7-stable@2173 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-12-24 11:35:57 +00:00
21 changed files with 87 additions and 42 deletions

View File

@@ -28,7 +28,7 @@ class AdminController < ApplicationController
def projects
sort_init 'name', 'asc'
sort_update
sort_update %w(name is_public created_on)
@status = params[:status] ? params[:status].to_i : 0
conditions = nil

View File

@@ -36,12 +36,13 @@ class BoardsController < ApplicationController
end
def show
sort_init "#{Message.table_name}.updated_on", "desc"
sort_update
sort_init 'updated_on', 'desc'
sort_update 'created_on' => "#{Message.table_name}.created_on",
'updated_on' => "#{Message.table_name}.updated_on"
@topic_count = @board.topics.count
@topic_pages = Paginator.new self, @topic_count, per_page_option, params['page']
@topics = @board.topics.find :all, :order => "#{Message.table_name}.sticky DESC, #{sort_clause}",
@topics = @board.topics.find :all, :order => ["#{Message.table_name}.sticky DESC", sort_clause].compact.join(', '),
:include => [:author, {:last_reply => :author}],
:limit => @topic_pages.items_per_page,
:offset => @topic_pages.current.offset

View File

@@ -45,9 +45,10 @@ class IssuesController < ApplicationController
include IssuesHelper
def index
sort_init "#{Issue.table_name}.id", "desc"
sort_update
retrieve_query
sort_init 'id', 'desc'
sort_update({'id' => "#{Issue.table_name}.id"}.merge(@query.columns.inject({}) {|h, c| h[c.name.to_s] = c.sortable; h}))
if @query.valid?
limit = per_page_option
respond_to do |format|
@@ -78,9 +79,10 @@ class IssuesController < ApplicationController
end
def changes
sort_init "#{Issue.table_name}.id", "desc"
sort_update
retrieve_query
sort_init 'id', 'desc'
sort_update({'id' => "#{Issue.table_name}.id"}.merge(@query.columns.inject({}) {|h, c| h[c.name.to_s] = c.sortable; h}))
if @query.valid?
@journals = Journal.find :all, :include => [ :details, :user, {:issue => [:project, :author, :tracker, :status]} ],
:conditions => @query.statement,

View File

@@ -204,8 +204,12 @@ class ProjectsController < ApplicationController
end
def list_files
sort_init "#{Attachment.table_name}.filename", "asc"
sort_update
sort_init 'filename', 'asc'
sort_update 'filename' => "#{Attachment.table_name}.filename",
'created_on' => "#{Attachment.table_name}.created_on",
'size' => "#{Attachment.table_name}.filesize",
'downloads' => "#{Attachment.table_name}.downloads"
@versions = @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse
render :layout => !request.xhr?
end

View File

@@ -129,7 +129,12 @@ class TimelogController < ApplicationController
def details
sort_init 'spent_on', 'desc'
sort_update
sort_update 'spent_on' => 'spent_on',
'user' => 'user_id',
'activity' => 'activity_id',
'project' => "#{Project.table_name}.name",
'issue' => 'issue_id',
'hours' => 'hours'
cond = ARCondition.new
cond << (@issue.nil? ? @project.project_condition(Setting.display_subprojects_issues?) :

View File

@@ -31,7 +31,7 @@ class UsersController < ApplicationController
def list
sort_init 'login', 'asc'
sort_update
sort_update %w(login firstname lastname mail admin created_on last_login_on)
@status = params[:status] ? params[:status].to_i : 1
conditions = "status <> 0"

View File

@@ -22,8 +22,8 @@ module QueriesHelper
end
def column_header(column)
column.sortable ? sort_header_tag(column.sortable, :caption => column.caption,
:default_order => column.default_order) :
column.sortable ? sort_header_tag(column.name.to_s, :caption => column.caption,
:default_order => column.default_order) :
content_tag('th', column.caption)
end

View File

@@ -67,23 +67,31 @@ module SortHelper
# Updates the sort state. Call this in the controller prior to calling
# sort_clause.
#
def sort_update()
if params[:sort_key]
sort = {:key => params[:sort_key], :order => params[:sort_order]}
# sort_keys can be either an array or a hash of allowed keys
def sort_update(sort_keys)
sort_key = params[:sort_key]
sort_key = nil unless (sort_keys.is_a?(Array) ? sort_keys.include?(sort_key) : sort_keys[sort_key])
sort_order = (params[:sort_order] == 'desc' ? 'DESC' : 'ASC')
if sort_key
sort = {:key => sort_key, :order => sort_order}
elsif session[@sort_name]
sort = session[@sort_name] # Previous sort.
else
sort = @sort_default
end
session[@sort_name] = sort
sort_column = (sort_keys.is_a?(Hash) ? sort_keys[sort[:key]] : sort[:key])
@sort_clause = (sort_column.blank? ? nil : "#{sort_column} #{sort[:order]}")
end
# Returns an SQL sort clause corresponding to the current sort state.
# Use this to sort the controller's table items collection.
#
def sort_clause()
session[@sort_name][:key] + ' ' + (session[@sort_name][:order] || 'ASC')
@sort_clause
end
# Returns a link which sorts by the named column.

View File

@@ -32,9 +32,9 @@
<thead><tr>
<th><%= l(:field_subject) %></th>
<th><%= l(:field_author) %></th>
<%= sort_header_tag("#{Message.table_name}.created_on", :caption => l(:field_created_on)) %>
<%= sort_header_tag('created_on', :caption => l(:field_created_on)) %>
<th><%= l(:label_reply_plural) %></th>
<%= sort_header_tag("#{Message.table_name}.updated_on", :caption => l(:label_message_last)) %>
<%= sort_header_tag('updated_on', :caption => l(:label_message_last)) %>
</tr></thead>
<tbody>
<% @topics.each do |topic| %>

View File

@@ -4,7 +4,7 @@
<th><%= link_to image_tag('toggle_check.png'), {}, :onclick => 'toggleIssuesSelection(this.up("form")); return false;',
:title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %>
</th>
<%= sort_header_tag("#{Issue.table_name}.id", :caption => '#', :default_order => 'desc') %>
<%= sort_header_tag('id', :caption => '#', :default_order => 'desc') %>
<% query.columns.each do |column| %>
<%= column_header(column) %>
<% end %>

View File

@@ -9,6 +9,6 @@
<h3><%= l(:label_query_plural) %></h3>
<% sidebar_queries.each do |query| -%>
<%= link_to query.name, :controller => 'issues', :action => 'index', :project_id => @project, :query_id => query %><br />
<%= link_to(h(query.name), :controller => 'issues', :action => 'index', :project_id => @project, :query_id => query) %><br />
<% end -%>
<% end -%>

View File

@@ -1,6 +1,6 @@
<% form_remote_tag(:url => {}, :html => { :id => "journal-#{@journal.id}-form" }) do %>
<%= text_area_tag :notes, @journal.notes, :class => 'wiki-edit',
:rows => (@journal.notes.blank? ? 10 : [[10, @journal.notes.length / 50].max, 100].min) %>
<%= text_area_tag :notes, h(@journal.notes), :class => 'wiki-edit',
:rows => (@journal.notes.blank? ? 10 : [[10, @journal.notes.length / 50].max, 100].min) %>
<p><%= submit_tag l(:button_save) %>
<%= link_to l(:button_cancel), '#', :onclick => "Element.remove('journal-#{@journal.id}-form'); " +
"Element.show('journal-#{@journal.id}-notes'); return false;" %></p>

View File

@@ -9,10 +9,10 @@
<table class="list">
<thead><tr>
<th><%=l(:field_version)%></th>
<%= sort_header_tag("#{Attachment.table_name}.filename", :caption => l(:field_filename)) %>
<%= sort_header_tag("#{Attachment.table_name}.created_on", :caption => l(:label_date), :default_order => 'desc') %>
<%= sort_header_tag("#{Attachment.table_name}.filesize", :caption => l(:field_filesize), :default_order => 'desc') %>
<%= sort_header_tag("#{Attachment.table_name}.downloads", :caption => l(:label_downloads_abbr), :default_order => 'desc') %>
<%= sort_header_tag('filename', :caption => l(:field_filename)) %>
<%= sort_header_tag('created_on', :caption => l(:label_date), :default_order => 'desc') %>
<%= sort_header_tag('size', :caption => l(:field_filesize), :default_order => 'desc') %>
<%= sort_header_tag('downloads', :caption => l(:label_downloads_abbr), :default_order => 'desc') %>
<th>MD5</th>
<% if delete_allowed %><th></th><% end %>
</tr></thead>

View File

@@ -2,10 +2,10 @@
<thead>
<tr>
<%= sort_header_tag('spent_on', :caption => l(:label_date), :default_order => 'desc') %>
<%= sort_header_tag('user_id', :caption => l(:label_member)) %>
<%= sort_header_tag('activity_id', :caption => l(:label_activity)) %>
<%= sort_header_tag("#{Project.table_name}.name", :caption => l(:label_project)) %>
<%= sort_header_tag('issue_id', :caption => l(:label_issue), :default_order => 'desc') %>
<%= sort_header_tag('user', :caption => l(:label_member)) %>
<%= sort_header_tag('activity', :caption => l(:label_activity)) %>
<%= sort_header_tag('project', :caption => l(:label_project)) %>
<%= sort_header_tag('issue', :caption => l(:label_issue), :default_order => 'desc') %>
<th><%= l(:field_comments) %></th>
<%= sort_header_tag('hours', :caption => l(:field_hours)) %>
<th></th>

View File

@@ -20,7 +20,7 @@
<th class="line-num"><%= line_num %></th>
<td class="revision"><%= link_to line[0], :controller => 'wiki', :action => 'index', :id => @project, :page => @page.title, :version => line[0] %></td>
<td class="author"><%= h(line[1]) %></td>
<td class="line-code"><pre><%= line[2] %></pre></td>
<td class="line-code"><pre><%=h line[2] %></pre></td>
</tr>
<% line_num += 1 %>
<% end -%>

View File

@@ -4,6 +4,11 @@ Redmine - project management software
Copyright (C) 2006-2008 Jean-Philippe Lang
http://www.redmine.org/
== 2008-12-30 v0.7.4
* Fixed several XSS vulnerabilities
* Fixed a SQL injection vulnerability
== 2008-07-06 v0.7.3

View File

@@ -433,12 +433,15 @@ class RedCloth < String
#
# Flexible HTML escaping
#
def htmlesc( str, mode )
def htmlesc( str, mode=:Quotes )
if str
str.gsub!( '&', '&amp;' )
str.gsub!( '"', '&quot;' ) if mode != :NoQuotes
str.gsub!( "'", '&#039;' ) if mode == :Quotes
str.gsub!( '<', '&lt;')
str.gsub!( '>', '&gt;')
end
str
end
# Search and replace for Textile glyphs (quotes, dashes, other symbols)
@@ -462,8 +465,7 @@ class RedCloth < String
style << "vertical-align:#{ v_align( $& ) };" if text =~ A_VLGN
end
style << "#{ $1 };" if not filter_styles and
text.sub!( /\{([^}]*)\}/, '' )
style << "#{ htmlesc $1 };" if text.sub!( /\{([^}]*)\}/, '' ) && !filter_styles
lang = $1 if
text.sub!( /\[([^)]+?)\]/, '' )
@@ -783,7 +785,7 @@ class RedCloth < String
atts = pba( atts )
atts = " href=\"#{ url }#{ slash }\"#{ atts }"
atts << " title=\"#{ title }\"" if title
atts << " title=\"#{ htmlesc title }\"" if title
atts = shelve( atts ) if atts
external = (url =~ /^https?:\/\//) ? ' class="external"' : ''
@@ -890,6 +892,7 @@ class RedCloth < String
def inline_textile_image( text )
text.gsub!( IMAGE_RE ) do |m|
stln,algn,atts,url,title,href,href_a1,href_a2 = $~[1..8]
htmlesc title
atts = pba( atts )
atts = " src=\"#{ url }\"#{ atts }"
atts << " title=\"#{ title }\"" if title
@@ -1027,7 +1030,7 @@ class RedCloth < String
else
htmlesc( aftertag, :NoQuotes ) if aftertag and not used_offtags['notextile']
line = "<redpre##{ @pre_list.length }>"
@pre_list << "#{ $3 }#{ aftertag }"
@pre_list << "#{ $3.gsub(/<(#{ OFFTAGS })[^>]*>/, '<\\1>') }#{ aftertag }"
end
elsif $1 and codepre > 0
if codepre - used_offtags.length > 0

View File

@@ -4,7 +4,7 @@ module Redmine
module VERSION #:nodoc:
MAJOR = 0
MINOR = 7
TINY = 3
TINY = 4
def self.revision
revision = nil

View File

@@ -32,6 +32,7 @@ module Redmine
super
self.hard_breaks=true
self.no_span_caps=true
self.filter_styles=true
end
def to_html(*rules, &block)

View File

@@ -124,6 +124,16 @@ class IssuesControllerTest < Test::Unit::TestCase
assert_equal 'application/pdf', @response.content_type
end
def test_index_sort
get :index, :sort_key => 'tracker'
assert_response :success
sort_params = @request.session['issuesindex_sort']
assert sort_params.is_a?(Hash)
assert_equal 'tracker', sort_params[:key]
assert_equal 'ASC', sort_params[:order]
end
def test_changes
get :changes, :project_id => 1
assert_response :success

View File

@@ -49,7 +49,10 @@ class ApplicationHelperTest < HelperTestCase
'!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />',
'floating !>http://foo.bar/image.jpg!' => 'floating <div style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></div>',
'with class !(some-class)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="some-class" alt="" />',
'with style !{width:100px;height100px}http://foo.bar/image.jpg!' => 'with style <img src="http://foo.bar/image.jpg" style="width:100px;height100px;" alt="" />',
# inline styles should be stripped
'with style !{width:100px;height100px}http://foo.bar/image.jpg!' => 'with style <img src="http://foo.bar/image.jpg" alt="" />',
'with title !http://foo.bar/image.jpg(This is a title)!' => 'with title <img src="http://foo.bar/image.jpg" title="This is a title" alt="This is a title" />',
'with title !http://foo.bar/image.jpg(This is a double-quoted "title")!' => 'with title <img src="http://foo.bar/image.jpg" title="This is a double-quoted &quot;title&quot;" alt="This is a double-quoted &quot;title&quot;" />',
}
to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
end
@@ -59,6 +62,7 @@ class ApplicationHelperTest < HelperTestCase
'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="/foo/bar">link</a>',
'"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>',
'"link (Link title with "double-quotes")":http://foo.bar' => '<a href="http://foo.bar" title="Link title with &quot;double-quotes&quot;" class="external">link</a>',
# no multiline link text
"This is a double quote \"on the first line\nand another on a second line\":test" => "This is a double quote \"on the first line<br />\nand another on a second line\":test"
}
@@ -146,7 +150,9 @@ class ApplicationHelperTest < HelperTestCase
"<pre><code>\nline 1\nline2</code></pre>" => "<pre><code>\nline 1\nline2</code></pre>",
"<pre><div>content</div></pre>" => "<pre>&lt;div&gt;content&lt;/div&gt;</pre>",
"HTML comment: <!-- no comments -->" => "<p>HTML comment: &lt;!-- no comments --&gt;</p>",
"<!-- opening comment" => "<p>&lt;!-- opening comment</p>"
"<!-- opening comment" => "<p>&lt;!-- opening comment</p>",
# remove attributes
"<pre class='foo'>some text</pre>" => "<pre>some text</pre>",
}
to_test.each { |text, result| assert_equal result, textilizable(text) }
end