subissues-v1.73.diff
| suissues-patch/app/controllers/issues_controller.rb | ||
|---|---|---|
| 18 | 18 |
class IssuesController < ApplicationController |
| 19 | 19 |
menu_item :new_issue, :only => :new |
| 20 | 20 |
|
| 21 |
before_filter :find_issue, :only => [:show, :edit, :reply] |
|
| 21 |
before_filter :find_issue, :only => [:show, :edit, :reply, :destroy_attachment ]
|
|
| 22 | 22 |
before_filter :find_issues, :only => [:bulk_edit, :move, :destroy] |
| 23 |
before_filter :find_project, :only => [:new, :update_form, :preview]
|
|
| 24 |
before_filter :authorize, :except => [:index, :changes, :gantt, :calendar, :preview, :update_form, :context_menu] |
|
| 25 |
before_filter :find_optional_project, :only => [:index, :changes, :gantt, :calendar] |
|
| 23 |
before_filter :find_project, :only => [:new, :auto_complete_for_issue_parent, :add_subissue, :update_form, :preview ]
|
|
| 24 |
before_filter :authorize, :except => [:index, :changes, :gantt, :calendar, :preview, :update_form, :context_menu, :auto_complete_for_issue_parent ]
|
|
| 25 |
before_filter :find_optional_project, :only => [:index, :changes, :gantt, :calendar ]
|
|
| 26 | 26 |
accept_key_auth :index, :changes |
| 27 | 27 | |
| 28 | 28 |
helper :journals |
| ... | ... | |
| 41 | 41 |
include SortHelper |
| 42 | 42 |
include IssuesHelper |
| 43 | 43 |
helper :timelog |
| 44 |
include ActionView::Helpers::PrototypeHelper |
|
| 44 | 45 |
include Redmine::Export::PDF |
| 45 | 46 | |
| 47 |
def auto_complete_for_issue_parent |
|
| 48 |
@phrase = params[:issue_parent] |
|
| 49 |
@candidates = [] |
|
| 50 | ||
| 51 |
# If cross project issue relations is allowed we should get |
|
| 52 |
# candidates from every project |
|
| 53 |
if Setting.cross_project_issue_relations? |
|
| 54 |
projects_to_search = nil |
|
| 55 |
else |
|
| 56 |
projects_to_search = [ @project ] + @project.active_children |
|
| 57 |
end |
|
| 58 | ||
| 59 |
# Try to find issue by id. |
|
| 60 |
if @phrase.match(/^#?(\d+)$/) |
|
| 61 |
if Setting.cross_project_issue_relations? |
|
| 62 |
issue = Issue.find_by_id( $1) |
|
| 63 |
else |
|
| 64 |
issue = Issue.find_by_id_and_project_id( $1, projects_to_search.collect { |i| i.id})
|
|
| 65 |
end |
|
| 66 |
@candidates = [ issue ] if issue |
|
| 67 |
end |
|
| 68 | ||
| 69 |
# If finding by id is fail, try to find by searching in subject |
|
| 70 |
# and description. |
|
| 71 |
if @candidates.empty? |
|
| 72 |
# extract tokens from the question |
|
| 73 |
# eg. hello "bye bye" => ["hello", "bye bye"] |
|
| 74 |
tokens = @phrase.scan(%r{((\s|^)"[\s\w]+"(\s|$)|\S+)}).collect {|m| m.first.gsub(%r{(^\s*"\s*|\s*"\s*$)}, '')}
|
|
| 75 |
# tokens must be at least 3 character long |
|
| 76 |
tokens = tokens.uniq.select {|w| w.length > 2 }
|
|
| 77 |
like_tokens = tokens.collect {|w| "%#{w.downcase}%"}
|
|
| 78 | ||
| 79 |
@candidates, count = Issue.search( like_tokens, projects_to_search, :before => true) |
|
| 80 |
end |
|
| 81 | ||
| 82 |
render :inline => "<%= auto_complete_result_parent_issue( @candidates, @phrase) %>" |
|
| 83 |
end |
|
| 84 | ||
| 46 | 85 |
def index |
| 47 | 86 |
retrieve_query |
| 48 | 87 |
sort_init 'id', 'desc' |
| ... | ... | |
| 58 | 97 |
end |
| 59 | 98 |
@issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement) |
| 60 | 99 |
@issue_pages = Paginator.new self, @issue_count, limit, params['page'] |
| 61 |
@issues = Issue.find :all, :order => sort_clause, |
|
| 62 |
:include => [ :assigned_to, :status, :tracker, :project, :priority, :category, :fixed_version ], |
|
| 63 |
:conditions => @query.statement, |
|
| 64 |
:limit => limit, |
|
| 65 |
:offset => @issue_pages.current.offset |
|
| 100 |
@issues = Issue.find( :all, :order => sort_clause, |
|
| 101 |
:include => [ :assigned_to, |
|
| 102 |
:status, |
|
| 103 |
:tracker, |
|
| 104 |
:project, |
|
| 105 |
:priority, |
|
| 106 |
:category, |
|
| 107 |
:fixed_version ], |
|
| 108 |
:conditions => @query.statement, |
|
| 109 |
:limit => limit, |
|
| 110 |
:offset => @issue_pages.current.offset) |
|
| 111 |
|
|
| 66 | 112 |
respond_to do |format| |
| 67 | 113 |
format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
|
| 68 | 114 |
format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
|
| ... | ... | |
| 136 | 182 |
end |
| 137 | 183 |
@issue.status = default_status |
| 138 | 184 |
@allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(User.current.role_for_project(@project), @issue.tracker)).uniq |
| 139 |
|
|
| 185 |
@parent_issue = Issue.find( params[:issue_parent_issue_id]) unless params[:issue_parent_issue_id].blank? |
|
| 186 | ||
| 140 | 187 |
if request.get? || request.xhr? |
| 141 | 188 |
@issue.start_date ||= Date.today |
| 142 | 189 |
else |
| ... | ... | |
| 144 | 191 |
# Check that the user is allowed to apply the requested status |
| 145 | 192 |
@issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status |
| 146 | 193 |
if @issue.save |
| 194 |
set_parent( @issue, params[:issue_parent_issue_id]) if params[:issue_parent_issue_id] |
|
| 147 | 195 |
attach_files(@issue, params[:attachments]) |
| 148 | 196 |
flash[:notice] = l(:notice_successful_create) |
| 149 | 197 |
Mailer.deliver_issue_add(@issue) if Setting.notified_events.include?('issue_added')
|
| ... | ... | |
| 156 | 204 |
@priorities = Enumeration::get_values('IPRI')
|
| 157 | 205 |
render :layout => !request.xhr? |
| 158 | 206 |
end |
| 207 | ||
| 208 |
def add_subissue |
|
| 209 |
if params[:issue_parent_issue_id].nil? |
|
| 210 |
flash.now[:error] = 'No parent issue specified.' |
|
| 211 |
render :nothing => true, :layout => true |
|
| 212 |
return |
|
| 213 |
else |
|
| 214 |
redirect_to( :controller => 'issues', :action => 'new', |
|
| 215 |
:issue_parent_issue_id => params[:issue_parent_issue_id]) |
|
| 216 |
return |
|
| 217 |
end |
|
| 218 |
end |
|
| 159 | 219 |
|
| 160 | 220 |
# Attributes that can be updated on workflow transition (without :edit permission) |
| 161 | 221 |
# TODO: make it configurable (at least per role) |
| ... | ... | |
| 186 | 246 |
call_hook(:controller_issues_edit_before_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => journal})
|
| 187 | 247 | |
| 188 | 248 |
if (@time_entry.hours.nil? || @time_entry.valid?) && @issue.save |
| 249 |
set_parent(@issue, params[:issue_parent_issue_id]) if params[:issue_parent_issue_id] |
|
| 189 | 250 |
# Log spend time |
| 190 | 251 |
if current_role.allowed_to?(:log_time) |
| 191 | 252 |
@time_entry.save |
| ... | ... | |
| 248 | 309 |
call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
|
| 249 | 310 |
# Don't save any change to the issue if the user is not authorized to apply the requested status |
| 250 | 311 |
if (status.nil? || (issue.status.new_status_allowed_to?(status, current_role, issue.tracker) && issue.status = status)) && issue.save |
| 312 |
set_parent(issue, params[:issue_parent_issue_id]) unless params[:issue_parent_issue_id].blank? |
|
| 251 | 313 |
# Send notification for each issue (if changed) |
| 252 | 314 |
Mailer.deliver_issue_edit(journal) if journal.details.any? && Setting.notified_events.include?('issue_updated')
|
| 253 | 315 |
else |
| ... | ... | |
| 412 | 474 | |
| 413 | 475 |
def update_form |
| 414 | 476 |
@issue = Issue.new(params[:issue]) |
| 477 |
@parent_issue = @issue.parent |
|
| 415 | 478 |
render :action => :new, :layout => false |
| 416 | 479 |
end |
| 417 | 480 |
|
| ... | ... | |
| 425 | 488 |
private |
| 426 | 489 |
def find_issue |
| 427 | 490 |
@issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category]) |
| 491 |
@parent_issue = @issue.parent |
|
| 428 | 492 |
@project = @issue.project |
| 429 | 493 |
rescue ActiveRecord::RecordNotFound |
| 430 | 494 |
render_404 |
| ... | ... | |
| 481 | 545 |
@query.add_short_filter(field, params[field]) if params[field] |
| 482 | 546 |
end |
| 483 | 547 |
end |
| 484 |
session[:query] = {:project_id => @query.project_id, :filters => @query.filters}
|
|
| 548 |
if params[:view_options] and params[:view_options].is_a? Hash |
|
| 549 |
params[:view_options].each_pair do |name, value| |
|
| 550 |
@query.set_view_option( name, value) |
|
| 551 |
end |
|
| 552 |
end |
|
| 553 |
session[:query] = {
|
|
| 554 |
:project_id => @query.project_id, |
|
| 555 |
:filters => @query.filters, |
|
| 556 |
:view_options => @query.view_options |
|
| 557 |
} |
|
| 485 | 558 |
else |
| 486 | 559 |
@query = Query.find_by_id(session[:query][:id]) if session[:query][:id] |
| 487 |
@query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters]) |
|
| 560 |
@query ||= Query.new(:name => "_", |
|
| 561 |
:project => @project, |
|
| 562 |
:filters => session[:query][:filters], |
|
| 563 |
:view_options => session[:query][:view_options]) |
|
| 488 | 564 |
@query.project = @project |
| 489 | 565 |
end |
| 490 | 566 |
end |
| suissues-patch/app/controllers/projects_controller.rb | ||
|---|---|---|
| 46 | 46 |
helper :repositories |
| 47 | 47 |
include RepositoriesHelper |
| 48 | 48 |
include ProjectsHelper |
| 49 |
helper :versions |
|
| 50 |
include VersionsHelper |
|
| 49 | 51 |
|
| 50 | 52 |
# Lists visible projects |
| 51 | 53 |
def index |
| ... | ... | |
| 293 | 295 |
@selected_tracker_ids = selectable_trackers.collect {|t| t.id.to_s }
|
| 294 | 296 |
end |
| 295 | 297 |
end |
| 298 |
|
|
| 299 |
|
|
| 300 |
def sort_as_tree(issues) |
|
| 301 |
issues.sort!{|a,b| a.hierarchical_level <=> b.hierarchical_level}
|
|
| 302 |
@sorted_issues = [] |
|
| 303 |
issues.each do |issue| |
|
| 304 |
if @sorted_issues.empty? |
|
| 305 |
@sorted_issues << issue |
|
| 306 |
next |
|
| 307 |
end |
|
| 308 |
@time_to_stop = false #indicates when this task reaches its parent task (important because it has to stop between its parent task and the next aunt task |
|
| 309 |
@sorted_issues.each do |sorted_issue| |
|
| 310 |
#if same parent and smaller date, stop; if same parent, same date and smaller id, stop; after parent and before next parent, stop; |
|
| 311 |
if ((sorted_issue.parent == issue.parent) && (sorted_issue.start_date > issue.start_date)) || |
|
| 312 |
((sorted_issue.parent == issue.parent) && (sorted_issue.start_date == issue.start_date) && (sorted_issue.id > issue.id)) || |
|
| 313 |
(@time_to_stop && (sorted_issue.hierarchical_level < issue.hierarchical_level)) |
|
| 314 |
@sorted_issues.insert(@sorted_issues.index(sorted_issue), issue) |
|
| 315 |
break |
|
| 316 |
end |
|
| 317 |
@time_to_stop = true if sorted_issue == issue.parent |
|
| 318 |
end |
|
| 319 |
#if this issue's parent is the last element |
|
| 320 |
@sorted_issues << issue if @time_to_stop |
|
| 321 |
end |
|
| 322 |
@sorted_issues |
|
| 323 |
end |
|
| 324 |
|
|
| 325 |
#assumes that first level issues are ordered by date (sort_as_tree) |
|
| 326 |
def integrate_versions_with_issues_tree(issues, versions) |
|
| 327 |
versions.sort! {|x,y| x.start_date <=> y.start_date }
|
|
| 328 |
versions.each do |version| |
|
| 329 |
issues << version if issues.empty? |
|
| 330 |
issues.each do |issue| |
|
| 331 |
if ((issue.is_a? Issue && issue.root?) || (issue.is_a? Version)) && version.start_date < issue.start_date |
|
| 332 |
#insert version before a root task or another version whose date is immediately after this task's one |
|
| 333 |
issues.insert(issues.index(issue), version) |
|
| 334 |
elsif issue == issues.last |
|
| 335 |
issues << version |
|
| 336 |
end |
|
| 337 |
end |
|
| 338 |
end |
|
| 339 |
issues |
|
| 340 |
end |
|
| 341 |
|
|
| 296 | 342 |
end |
| suissues-patch/app/controllers/queries_controller.rb | ||
|---|---|---|
| 30 | 30 |
params[:fields].each do |field| |
| 31 | 31 |
@query.add_filter(field, params[:operators][field], params[:values][field]) |
| 32 | 32 |
end if params[:fields] |
| 33 |
|
|
| 33 | ||
| 34 |
params[:view_options].each_pair do |name, value| |
|
| 35 |
@query.set_view_option( name, value) |
|
| 36 |
end if params[:view_options] |
|
| 37 | ||
| 34 | 38 |
if request.post? && params[:confirm] && @query.save |
| 35 | 39 |
flash[:notice] = l(:notice_successful_create) |
| 36 | 40 |
redirect_to :controller => 'issues', :action => 'index', :project_id => @project, :query_id => @query |
| ... | ... | |
| 45 | 49 |
params[:fields].each do |field| |
| 46 | 50 |
@query.add_filter(field, params[:operators][field], params[:values][field]) |
| 47 | 51 |
end if params[:fields] |
| 52 |
params[:view_options].each_pair do |name, value| |
|
| 53 |
@query.set_view_option( name, value) |
|
| 54 |
end if params[:view_options] |
|
| 48 | 55 |
@query.attributes = params[:query] |
| 49 | 56 |
@query.project = nil if params[:query_is_for_all] |
| 50 | 57 |
@query.is_public = false unless (@query.project && current_role.allowed_to?(:manage_public_queries)) || User.current.admin? |
| suissues-patch/app/controllers/versions_controller.rb | ||
|---|---|---|
| 20 | 20 |
before_filter :find_project, :authorize |
| 21 | 21 | |
| 22 | 22 |
def show |
| 23 |
@issues = @version.fixed_issues.find(:all, |
|
| 24 |
:include => [:status, :tracker], |
|
| 25 |
:order => "#{Tracker.table_name}.position, #{Issue.table_name}.id")
|
|
| 26 |
@issues = Issue.find_with_parents( @issues.collect { |i| i.id})
|
|
| 23 | 27 |
end |
| 24 | 28 |
|
| 25 | 29 |
def edit |
| suissues-patch/app/helpers/issues_helper.rb | ||
|---|---|---|
| 19 | 19 | |
| 20 | 20 |
module IssuesHelper |
| 21 | 21 |
include ApplicationHelper |
| 22 |
|
|
| 23 |
def issue_ancestors(issue=@issue) |
|
| 24 |
ancestors = "" |
|
| 25 |
return "" if issue.parent == nil |
|
| 26 |
ancestors += "issue-#{issue.parent.id}-child " + issue_ancestors(issue.parent)
|
|
| 27 |
end |
|
| 22 | 28 | |
| 23 | 29 |
def render_issue_tooltip(issue) |
| 24 | 30 |
@cached_label_start_date ||= l(:field_start_date) |
| ... | ... | |
| 193 | 199 |
export.rewind |
| 194 | 200 |
export |
| 195 | 201 |
end |
| 202 |
|
|
| 203 |
def set_parent(issue, parent_id) |
|
| 204 |
if (issue && parent_id.to_s.size > 0) |
|
| 205 |
issue.relations_from.each do |relation| |
|
| 206 |
if relation.relation_type == IssueRelation::TYPE_PARENTS |
|
| 207 |
relation.destroy |
|
| 208 |
end |
|
| 209 |
end |
|
| 210 |
issue.reload |
|
| 211 |
IssueRelation.create do |relation| |
|
| 212 |
relation.issue_from = issue |
|
| 213 |
relation.issue_to = Issue.find(parent_id) |
|
| 214 |
relation.relation_type = IssueRelation::TYPE_PARENTS |
|
| 215 |
unless relation.save |
|
| 216 |
flash[:error] = "Can't set ##{parent_id} as parent for the issue."
|
|
| 217 |
end |
|
| 218 |
end unless parent_id == '0' |
|
| 219 |
end |
|
| 220 |
end |
|
| 221 |
|
|
| 222 |
def auto_complete_result_parent_issue(candidates, phrase) |
|
| 223 |
return "" if candidates.empty? |
|
| 224 |
candidates.map! do |c| |
|
| 225 |
content_tag("li", highlight( c.to_s, phrase), :id => String( c[:id]))
|
|
| 226 |
end |
|
| 227 |
content_tag("ul", candidates.uniq)
|
|
| 228 |
end |
|
| 196 | 229 |
end |
| suissues-patch/app/helpers/queries_helper.rb | ||
|---|---|---|
| 1 |
# -*- coding: mule-utf-8 -*- |
|
| 1 | 2 |
# redMine - project management software |
| 2 | 3 |
# Copyright (C) 2006-2007 Jean-Philippe Lang |
| 3 | 4 |
# |
| ... | ... | |
| 27 | 28 |
content_tag('th', column.caption)
|
| 28 | 29 |
end |
| 29 | 30 |
|
| 30 |
def column_content(column, issue) |
|
| 31 |
def column_content(column, issue, query)
|
|
| 31 | 32 |
if column.is_a?(QueryCustomFieldColumn) |
| 32 | 33 |
cv = issue.custom_values.detect {|v| v.custom_field_id == column.custom_field.id}
|
| 33 | 34 |
show_value(cv) |
| ... | ... | |
| 40 | 41 |
else |
| 41 | 42 |
case column.name |
| 42 | 43 |
when :subject |
| 43 |
h((@project.nil? || @project != issue.project) ? "#{issue.project.name} - " : '') +
|
|
| 44 |
link_to(h(value), :controller => 'issues', :action => 'show', :id => issue) |
|
| 44 |
subject_in_tree(issue, value, query) |
|
| 45 | 45 |
when :done_ratio |
| 46 | 46 |
progress_bar(value, :width => '80px') |
| 47 | 47 |
when :fixed_version |
| ... | ... | |
| 52 | 52 |
end |
| 53 | 53 |
end |
| 54 | 54 |
end |
| 55 |
|
|
| 56 |
def subject_in_tree(issue, value, query) |
|
| 57 |
case query.view_options['show_parents'] |
|
| 58 |
when Query::VIEW_OPTIONS_SHOW_PARENTS_NEVER |
|
| 59 |
content_tag('div', subject_text(issue, value), :class=>'issue-subject')
|
|
| 60 |
else |
|
| 61 |
content_tag('span', content_tag('div', subject_text(issue, value), :class=>'issue-subject'), :class=>"issue-subject-level-#{issue.hierarchical_level}")
|
|
| 62 |
end |
|
| 63 |
end |
|
| 64 |
|
|
| 65 |
def subject_text(issue, value) |
|
| 66 |
subject_text = link_to(h(value), :controller => 'issues', :action => 'show', :id => issue) |
|
| 67 |
h((@project.nil? || @project != issue.project) ? "#{issue.project.name} - " : '') + subject_text
|
|
| 68 |
end |
|
| 69 | ||
| 70 |
def issue_content(issue, query, options = { })
|
|
| 71 |
html = "" |
|
| 72 |
html << "<tr id=\"issue-#{issue.id}\" class=\"issue hascontextmenu " +
|
|
| 73 |
( options[:unfiltered] ? 'issue-unfiltered ' : '') + |
|
| 74 |
"status-#{issue.status.position} priority-#{issue.priority.position} " +
|
|
| 75 |
cycle('odd', 'even') + '">'
|
|
| 76 |
html << '<td class="checkbox">' + check_box_tag( "ids[]", issue.id, false, :id => nil) + '</td>' |
|
| 77 |
html << '<td>' + link_to( issue.id, :controller => 'issues', :action => 'show', :id => issue) + '</td>' |
|
| 78 |
query.columns.each do |column| |
|
| 79 |
html << content_tag( 'td', column_content(column, issue, query), :class => column.name) |
|
| 80 |
end |
|
| 81 |
html << "</tr>" |
|
| 82 |
html |
|
| 83 |
end |
|
| 84 | ||
| 85 |
def issues_family_content( parent, issues_to_show, query) |
|
| 86 |
html = "" |
|
| 87 |
html << issue_content( parent, query, :unfiltered => !( issues_to_show.include? parent)) |
|
| 88 |
unless parent.children.empty? |
|
| 89 |
parent.children.each do |child| |
|
| 90 |
if issues_to_show.include?( child) || issues_to_show.detect { |i| i.parents_hierarchy.include? child }
|
|
| 91 |
html << issues_family_content( child, issues_to_show, query) |
|
| 92 |
end |
|
| 93 |
end |
|
| 94 |
end |
|
| 95 |
html |
|
| 96 |
end |
|
| 97 |
|
|
| 55 | 98 |
end |
| suissues-patch/app/helpers/versions_helper.rb | ||
|---|---|---|
| 44 | 44 |
def status_by_options_for_select(value) |
| 45 | 45 |
options_for_select(STATUS_BY_CRITERIAS.collect {|criteria| [l("field_#{criteria}".to_sym), criteria]}, value)
|
| 46 | 46 |
end |
| 47 | ||
| 48 |
def render_list_of_related_issues( issues, version, current_level = 1) |
|
| 49 |
issues_on_current_level = issues.select { |i| i.hierarchical_level == current_level }
|
|
| 50 |
issues -= issues_on_current_level |
|
| 51 |
content_tag( 'ul') do |
|
| 52 |
html = '' |
|
| 53 |
issues_on_current_level.each do |issue| |
|
| 54 |
opts_for_issue_li = { }
|
|
| 55 |
if !issue.fixed_version or issue.fixed_version != version |
|
| 56 |
opts_for_issue_li[:class] = 'issue-unfiltered' |
|
| 57 |
end |
|
| 58 |
html << content_tag( 'li', opts_for_issue_li) do |
|
| 59 |
opts = { }
|
|
| 60 |
if issue.done_ratio == 100 |
|
| 61 |
opts[:style] = 'font-weight: bold' |
|
| 62 |
end |
|
| 63 |
link_to_issue(issue, opts) + ": " + h(issue.subject) |
|
| 64 |
end |
|
| 65 |
children_to_print = issues & issue.children |
|
| 66 |
children_to_print += issues.select { |i| i.hierarchical_level >= current_level + 2}
|
|
| 67 |
unless children_to_print.empty? |
|
| 68 |
html << render_list_of_related_issues( children_to_print, version, current_level + 1) |
|
| 69 |
end |
|
| 70 |
end |
|
| 71 |
html |
|
| 72 |
end |
|
| 73 |
end |
|
| 47 | 74 |
end |
| suissues-patch/app/models/issue.rb | ||
|---|---|---|
| 18 | 18 |
class Issue < ActiveRecord::Base |
| 19 | 19 |
belongs_to :project |
| 20 | 20 |
belongs_to :tracker |
| 21 |
belongs_to :status, :class_name => 'IssueStatus', :foreign_key => 'status_id'
|
|
| 22 |
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
|
|
| 23 |
belongs_to :assigned_to, :class_name => 'User', :foreign_key => 'assigned_to_id'
|
|
| 24 |
belongs_to :fixed_version, :class_name => 'Version', :foreign_key => 'fixed_version_id' |
|
| 25 |
belongs_to :priority, :class_name => 'Enumeration', :foreign_key => 'priority_id'
|
|
| 26 |
belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id' |
|
| 21 |
belongs_to :status, :class_name => 'IssueStatus', :foreign_key => 'status_id'
|
|
| 22 |
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
|
|
| 23 |
belongs_to :assigned_to, :class_name => 'User', :foreign_key => 'assigned_to_id'
|
|
| 24 |
belongs_to :fixed_version, :class_name => 'Version', :foreign_key => 'fixed_version_id'
|
|
| 25 |
belongs_to :priority, :class_name => 'Enumeration', :foreign_key => 'priority_id'
|
|
| 26 |
belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id'
|
|
| 27 | 27 | |
| 28 | 28 |
has_many :journals, :as => :journalized, :dependent => :destroy |
| 29 | 29 |
has_many :time_entries, :dependent => :delete_all |
| 30 | 30 |
has_and_belongs_to_many :changesets, :order => "#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC"
|
| 31 | 31 |
|
| 32 | 32 |
has_many :relations_from, :class_name => 'IssueRelation', :foreign_key => 'issue_from_id', :dependent => :delete_all |
| 33 |
has_many :relations_to, :class_name => 'IssueRelation', :foreign_key => 'issue_to_id', :dependent => :delete_all
|
|
| 33 |
has_many :relations_to, :class_name => 'IssueRelation', :foreign_key => 'issue_to_id', :dependent => :delete_all
|
|
| 34 | 34 |
|
| 35 | 35 |
acts_as_attachable :after_remove => :attachment_removed |
| 36 | 36 |
acts_as_customizable |
| ... | ... | |
| 118 | 118 |
return issue |
| 119 | 119 |
end |
| 120 | 120 |
|
| 121 |
def priority_id=(pid) |
|
| 122 |
self.priority = nil |
|
| 123 |
write_attribute(:priority_id, pid) |
|
| 124 |
end |
|
| 125 |
|
|
| 126 | 121 |
def estimated_hours=(h) |
| 127 | 122 |
write_attribute :estimated_hours, (h.is_a?(String) ? h.to_hours : h) |
| 128 | 123 |
end |
| ... | ... | |
| 139 | 134 |
if start_date && soonest_start && start_date < soonest_start |
| 140 | 135 |
errors.add :start_date, :activerecord_error_invalid |
| 141 | 136 |
end |
| 137 | ||
| 138 |
if IssueStatus.find_by_id( @attributes['status_id']).is_closed? && children.find { |i| !i.closed? }
|
|
| 139 |
errors.add :status, "Can't close parent issue while on of the children still is open." |
|
| 140 |
end |
|
| 141 | ||
| 142 |
unless children.empty? |
|
| 143 |
children_max_fixed_version = children.select { |i| i.fixed_version } .max { |a,b| a.fixed_version <=> b.fixed_version }
|
|
| 144 |
if @attributes['fixed_version_id'] && children_max_fixed_version |
|
| 145 |
if Version.find_by_id( @attributes['fixed_version_id']) < children_max_fixed_version.fixed_version |
|
| 146 |
errors.add :fixed_version, "Can't set target version of parent issue lower than any of the children." |
|
| 147 |
end |
|
| 148 |
end |
|
| 149 |
end |
|
| 142 | 150 |
end |
| 143 | 151 |
|
| 144 | 152 |
def validate_on_create |
| ... | ... | |
| 159 | 167 |
@current_journal.details << JournalDetail.new(:property => 'attr', |
| 160 | 168 |
:prop_key => c, |
| 161 | 169 |
:old_value => @issue_before_change.send(c), |
| 162 |
:value => send(c)) unless send(c)==@issue_before_change.send(c) |
|
| 170 |
:value => send(c)) unless send(c)==@issue_before_change.send(c) || (!self.edge? && %w(status_id priority_id fixed_version_id start_date due_date done_ratio estimated_hours).include?(c))
|
|
| 163 | 171 |
} |
| 164 | 172 |
# custom fields changes |
| 165 | 173 |
custom_values.each {|c|
|
| ... | ... | |
| 172 | 180 |
} |
| 173 | 181 |
@current_journal.save |
| 174 | 182 |
end |
| 183 | ||
| 184 |
# If target version is set, but "Due to" date is not, set it as |
|
| 185 |
# the same as the date of target version. |
|
| 186 |
if self.fixed_version && self.due_date.nil? |
|
| 187 |
self.due_date = self.fixed_version.due_date if self.fixed_version.due_date |
|
| 188 |
end |
|
| 189 | ||
| 175 | 190 |
# Save the issue even if the journal is not saved (because empty) |
| 176 | 191 |
true |
| 177 | 192 |
end |
| ... | ... | |
| 183 | 198 |
# Update start/due dates of following issues |
| 184 | 199 |
relations_from.each(&:set_issue_to_dates) |
| 185 | 200 |
|
| 201 |
# Set default status of parent if new status openes the issue. |
|
| 202 |
relations_from.each do |relation| |
|
| 203 |
if relation.relation_type == IssueRelation::TYPE_PARENTS |
|
| 204 |
relation.set_issue_to_default_status |
|
| 205 |
relation.set_issue_to_target_version |
|
| 206 |
end |
|
| 207 |
end |
|
| 208 | ||
| 186 | 209 |
# Close duplicates if the issue was closed |
| 187 | 210 |
if @issue_before_change && !@issue_before_change.closed? && self.closed? |
| 188 | 211 |
duplicates.each do |duplicate| |
| ... | ... | |
| 195 | 218 |
duplicate.update_attribute :status, self.status |
| 196 | 219 |
end |
| 197 | 220 |
end |
| 221 | ||
| 198 | 222 |
end |
| 199 |
|
|
| 223 | ||
| 200 | 224 |
def init_journal(user, notes = "") |
| 201 | 225 |
@current_journal ||= Journal.new(:journalized => self, :user => user, :notes => notes) |
| 202 | 226 |
@issue_before_change = self.clone |
| ... | ... | |
| 208 | 232 |
@current_journal |
| 209 | 233 |
end |
| 210 | 234 |
|
| 235 |
def priority_id=(pid) |
|
| 236 |
self.priority = nil |
|
| 237 |
write_attribute(:priority_id, pid) |
|
| 238 |
end |
|
| 239 | ||
| 211 | 240 |
# Return true if the issue is closed, otherwise false |
| 212 | 241 |
def closed? |
| 213 | 242 |
self.status.is_closed? |
| ... | ... | |
| 264 | 293 |
# Returns the due date or the target due date if any |
| 265 | 294 |
# Used on gantt chart |
| 266 | 295 |
def due_before |
| 267 |
due_date || (fixed_version ? fixed_version.effective_date : nil) |
|
| 296 |
due_date || (fixed_version ? fixed_version.effective_date : 0) |
|
| 297 |
end |
|
| 298 |
|
|
| 299 |
def duration1 |
|
| 300 |
(start_date && due_date) ? (due_date - start_date + 1) : 0 |
|
| 268 | 301 |
end |
| 269 | 302 |
|
| 270 | 303 |
def duration |
| ... | ... | |
| 275 | 308 |
@soonest_start ||= relations_to.collect{|relation| relation.successor_soonest_start}.compact.min
|
| 276 | 309 |
end |
| 277 | 310 |
|
| 311 |
def self.visible_by(usr) |
|
| 312 |
with_scope(:find => { :conditions => Project.visible_by(usr) }) do
|
|
| 313 |
yield |
|
| 314 |
end |
|
| 315 |
end |
|
| 316 |
|
|
| 317 |
def done_ratio |
|
| 318 |
if children? |
|
| 319 |
@total_planned_days ||= 0 |
|
| 320 |
@total_actual_days ||= 0 |
|
| 321 |
children.each do |child| # from every subtask get the total number of days and the number of days already "worked" |
|
| 322 |
planned_days = child.duration1 |
|
| 323 |
actual_days = child.done_ratio ? (planned_days * child.done_ratio / 100).floor : 0 |
|
| 324 |
@total_planned_days += planned_days |
|
| 325 |
@total_actual_days += actual_days |
|
| 326 |
end |
|
| 327 |
@total_done_ratio = @total_planned_days != 0 ? (@total_actual_days * 100 / @total_planned_days).floor : 0 |
|
| 328 |
else |
|
| 329 |
read_attribute(:done_ratio) |
|
| 330 |
end |
|
| 331 |
end |
|
| 332 |
|
|
| 333 |
def estimated_hours |
|
| 334 |
if children? |
|
| 335 |
is_set = false |
|
| 336 |
children.each do |child| |
|
| 337 |
if child.estimated_hours |
|
| 338 |
if is_set |
|
| 339 |
@est_hours += child.estimated_hours |
|
| 340 |
else |
|
| 341 |
@est_hours = child.estimated_hours |
|
| 342 |
is_set = true |
|
| 343 |
end |
|
| 344 |
end |
|
| 345 |
end |
|
| 346 |
@est_hours |
|
| 347 |
else |
|
| 348 |
read_attribute(:estimated_hours) |
|
| 349 |
end |
|
| 350 |
end |
|
| 351 |
|
|
| 352 |
def start_date |
|
| 353 |
calculate 'start_date' |
|
| 354 |
end |
|
| 355 |
|
|
| 356 |
def due_date |
|
| 357 |
calculate 'due_date' |
|
| 358 |
end |
|
| 359 |
|
|
| 360 |
def calculate(field) |
|
| 361 |
if children? |
|
| 362 |
@value = eval "children.first.#{field}"
|
|
| 363 |
children.each do |child| |
|
| 364 |
case field |
|
| 365 |
when 'start_date' |
|
| 366 |
if child.start_date && (!@value || @value > child.start_date) |
|
| 367 |
@value = child.start_date |
|
| 368 |
end |
|
| 369 |
when 'due_date' |
|
| 370 |
if child.due_date && (!@value || @value < child.due_date) |
|
| 371 |
@value = child.due_date |
|
| 372 |
end |
|
| 373 |
end |
|
| 374 |
end |
|
| 375 |
@value |
|
| 376 |
else |
|
| 377 |
read_attribute(eval(":#{field}"))
|
|
| 378 |
end |
|
| 379 |
end |
|
| 380 | ||
| 381 |
def children? |
|
| 382 |
children != [] |
|
| 383 |
end |
|
| 384 |
|
|
| 385 |
def children |
|
| 386 |
children = [] |
|
| 387 |
relations_to.each do |relation| |
|
| 388 |
if relation.relation_type == IssueRelation::TYPE_PARENTS |
|
| 389 |
children << relation.other_issue(self) |
|
| 390 |
end |
|
| 391 |
end |
|
| 392 |
children |
|
| 393 |
end |
|
| 394 |
|
|
| 395 |
def parent |
|
| 396 |
relations_from.each do |relation| |
|
| 397 |
if relation.relation_type == IssueRelation::TYPE_PARENTS |
|
| 398 |
return parent = relation.other_issue(self) |
|
| 399 |
end |
|
| 400 |
end |
|
| 401 |
return nil |
|
| 402 |
end |
|
| 403 | ||
| 404 |
def parents_hierarchy |
|
| 405 |
return [] unless p = parent |
|
| 406 |
parents = [ p ] |
|
| 407 |
while p = p.parent |
|
| 408 |
parents += [ p ] |
|
| 409 |
end |
|
| 410 |
parents |
|
| 411 |
end |
|
| 412 | ||
| 413 |
def parent? |
|
| 414 |
parent != nil |
|
| 415 |
end |
|
| 416 |
|
|
| 417 |
def root? |
|
| 418 |
!parent? |
|
| 419 |
end |
|
| 420 |
|
|
| 421 |
def ancestors(issue=self) |
|
| 422 |
a = [] |
|
| 423 |
return a if ! issue.parent? |
|
| 424 |
(a << issue.parent) | ancestors(issue.parent) |
|
| 425 |
end |
|
| 426 |
|
|
| 427 |
#First level tasks have hierarchical level = 1 and so on |
|
| 428 |
def hierarchical_level(issue=self) |
|
| 429 |
issue.parent? ? (1 + hierarchical_level(issue.parent)) : 1 |
|
| 430 |
end |
|
| 431 |
|
|
| 432 |
def edge? |
|
| 433 |
relations_to.each do |relation| |
|
| 434 |
if relation.relation_type == IssueRelation::TYPE_PARENTS |
|
| 435 |
return false |
|
| 436 |
end |
|
| 437 |
end |
|
| 438 |
return true |
|
| 439 |
end |
|
| 440 | ||
| 441 |
def orphan? |
|
| 442 |
root? && edge? |
|
| 443 |
end |
|
| 444 |
|
|
| 445 |
def self.find_with_parents( *args) |
|
| 446 |
issues = find( *args) |
|
| 447 |
return [] if issues.empty? |
|
| 448 |
issues.each do |i| |
|
| 449 |
while not i.root? |
|
| 450 |
issues += [ i.parent ] |
|
| 451 |
i = i.parent |
|
| 452 |
end |
|
| 453 |
end |
|
| 454 |
issues.uniq |
|
| 455 |
end |
|
| 456 |
|
|
| 278 | 457 |
def to_s |
| 279 | 458 |
"#{tracker} ##{id}: #{subject}"
|
| 280 | 459 |
end |
| suissues-patch/app/models/issue_relation.rb | ||
|---|---|---|
| 17 | 17 | |
| 18 | 18 |
class IssueRelation < ActiveRecord::Base |
| 19 | 19 |
belongs_to :issue_from, :class_name => 'Issue', :foreign_key => 'issue_from_id' |
| 20 |
belongs_to :issue_to, :class_name => 'Issue', :foreign_key => 'issue_to_id' |
|
| 20 |
belongs_to :issue_to, :class_name => 'Issue', :foreign_key => 'issue_to_id'
|
|
| 21 | 21 |
|
| 22 | 22 |
TYPE_RELATES = "relates" |
| 23 | 23 |
TYPE_DUPLICATES = "duplicates" |
| 24 | 24 |
TYPE_BLOCKS = "blocks" |
| 25 | 25 |
TYPE_PRECEDES = "precedes" |
| 26 |
TYPE_PARENTS = "parents" |
|
| 26 | 27 |
|
| 27 |
TYPES = { TYPE_RELATES => { :name => :label_relates_to, :sym_name => :label_relates_to, :order => 1 },
|
|
| 28 |
TYPE_DUPLICATES => { :name => :label_duplicates, :sym_name => :label_duplicated_by, :order => 2 },
|
|
| 29 |
TYPE_BLOCKS => { :name => :label_blocks, :sym_name => :label_blocked_by, :order => 3 },
|
|
| 30 |
TYPE_PRECEDES => { :name => :label_precedes, :sym_name => :label_follows, :order => 4 },
|
|
| 28 |
TYPES = { TYPE_RELATES => { :name => :label_relates_to, :sym_name => :label_relates_to, :order => 1 },
|
|
| 29 |
TYPE_DUPLICATES => { :name => :label_duplicates, :sym_name => :label_duplicated_by, :order => 2 },
|
|
| 30 |
TYPE_BLOCKS => { :name => :label_blocks, :sym_name => :label_blocked_by, :order => 3 },
|
|
| 31 |
TYPE_PRECEDES => { :name => :label_precedes, :sym_name => :label_follows, :order => 4 },
|
|
| 32 |
TYPE_PARENTS => { :name => :label_parents, :sym_name => :label_children, :order => 5 },
|
|
| 31 | 33 |
}.freeze |
| 32 | 34 |
|
| 33 |
validates_presence_of :issue_from, :issue_to, :relation_type |
|
| 34 |
validates_inclusion_of :relation_type, :in => TYPES.keys |
|
| 35 |
validates_numericality_of :delay, :allow_nil => true |
|
| 36 |
validates_uniqueness_of :issue_to_id, :scope => :issue_from_id |
|
| 35 |
validates_presence_of :issue_from, :issue_to, :relation_type |
|
| 36 |
validates_inclusion_of :relation_type, :in => TYPES.keys |
|
| 37 |
validates_numericality_of :delay, :allow_nil => true |
|
| 38 |
validates_uniqueness_of :issue_to_id, :scope => :issue_from_id |
|
| 39 | ||
| 40 |
# Only one parent allowed. |
|
| 41 |
validates_uniqueness_of( :relation_type, |
|
| 42 |
:scope => [:issue_from_id, :relation_type], |
|
| 43 |
:message => l(:error_issue_can_have_only_one_parent), |
|
| 44 |
:if => Proc.new { |issue_relation| issue_relation.relation_type == TYPE_PARENTS })
|
|
| 37 | 45 |
|
| 38 | 46 |
attr_protected :issue_from_id, :issue_to_id |
| 39 | 47 |
|
| ... | ... | |
| 59 | 67 |
else |
| 60 | 68 |
self.delay = nil |
| 61 | 69 |
end |
| 70 | ||
| 71 |
# Set new status to parent if new status openes the issue. |
|
| 72 |
if TYPE_PARENTS == relation_type |
|
| 73 |
set_issue_to_default_status |
|
| 74 |
set_issue_to_target_version |
|
| 75 |
end |
|
| 76 | ||
| 62 | 77 |
set_issue_to_dates |
| 63 | 78 |
end |
| 79 | ||
| 80 |
def set_issue_to_default_status |
|
| 81 |
if issue_to.closed? && !issue_from.closed? |
|
| 82 |
issue_to.update_attribute :status, IssueStatus.default |
|
| 83 |
end |
|
| 84 |
end |
|
| 85 | ||
| 86 |
def set_issue_to_target_version |
|
| 87 |
if issue_to.fixed_version.nil? && issue_from.fixed_version or |
|
| 88 |
( issue_to.fixed_version && issue_from.fixed_version and |
|
| 89 |
issue_to.fixed_version.project == issue_from.fixed_version.project and |
|
| 90 |
issue_to.fixed_version < issue_from.fixed_version ) |
|
| 91 |
issue_to.update_attribute :fixed_version, issue_from.fixed_version |
|
| 92 |
end |
|
| 93 |
end |
|
| 64 | 94 |
|
| 65 | 95 |
def set_issue_to_dates |
| 66 | 96 |
soonest_start = self.successor_soonest_start |
| suissues-patch/app/models/query.rb | ||
|---|---|---|
| 48 | 48 |
end |
| 49 | 49 |
end |
| 50 | 50 | |
| 51 |
class ViewOption |
|
| 52 |
attr_accessor :name, :available_values |
|
| 53 |
include GLoc |
|
| 54 |
|
|
| 55 |
def initialize( name, available_values) |
|
| 56 |
self.name = name |
|
| 57 |
self.available_values = available_values |
|
| 58 |
end |
|
| 59 | ||
| 60 |
def caption |
|
| 61 |
set_language_if_valid( User.current.language) |
|
| 62 |
l("label_view_option_#{name}")
|
|
| 63 |
end |
|
| 64 |
end |
|
| 65 | ||
| 51 | 66 |
class Query < ActiveRecord::Base |
| 52 | 67 |
belongs_to :project |
| 53 | 68 |
belongs_to :user |
| 54 | 69 |
serialize :filters |
| 55 | 70 |
serialize :column_names |
| 71 |
serialize :view_options |
|
| 56 | 72 |
|
| 57 | 73 |
attr_protected :project_id, :user_id |
| 58 | 74 |
|
| ... | ... | |
| 109 | 125 |
QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'),
|
| 110 | 126 |
] |
| 111 | 127 |
cattr_reader :available_columns |
| 128 | ||
| 129 |
VIEW_OPTIONS_SHOW_PARENTS_NEVER = 'do_not_show' |
|
| 130 |
VIEW_OPTIONS_SHOW_PARENTS_ALWAYS = 'show_always' |
|
| 131 |
VIEW_OPTIONS_SHOW_PARENTS_ORGANIZE_BY_PARENT = 'organize_by_parent' |
|
| 132 | ||
| 133 |
@@available_view_options = [ |
|
| 134 |
ViewOption.new( 'show_parents', [ [ l(:label_view_option_parents_do_not_show), VIEW_OPTIONS_SHOW_PARENTS_NEVER ], |
|
| 135 |
[ l(:label_view_option_parents_show_always), VIEW_OPTIONS_SHOW_PARENTS_ALWAYS ], |
|
| 136 |
[ l(:label_view_option_parents_show_and_group), VIEW_OPTIONS_SHOW_PARENTS_ORGANIZE_BY_PARENT ] ]) |
|
| 137 |
] |
|
| 138 |
cattr_reader :available_view_options |
|
| 112 | 139 |
|
| 113 | 140 |
def initialize(attributes = nil) |
| 114 | 141 |
super attributes |
| 115 | 142 |
self.filters ||= { 'status_id' => {:operator => "o", :values => [""]} }
|
| 143 |
self.view_options ||= { 'show_parents' => 'do_not_show' }
|
|
| 116 | 144 |
set_language_if_valid(User.current.language) |
| 117 | 145 |
end |
| 118 | 146 |
|
| ... | ... | |
| 317 | 345 |
|
| 318 | 346 |
(filters_clauses << project_statement).join(' AND ')
|
| 319 | 347 |
end |
| 320 |
|
|
| 348 | ||
| 349 |
def set_view_option( option, value) |
|
| 350 |
self.view_options[option] = value |
|
| 351 |
end |
|
| 352 | ||
| 353 |
def values_for_view_option( option) |
|
| 354 |
@@available_view_options.find { |vo| vo.name == option }.available_values
|
|
| 355 |
end |
|
| 356 | ||
| 357 |
def caption_for_view_option( option) |
|
| 358 |
@@available_view_options.find { |vo| vo.name == option }.caption
|
|
| 359 |
end |
|
| 360 | ||
| 321 | 361 |
private |
| 322 | 362 |
|
| 323 | 363 |
# Helper method to generate the WHERE sql for a +field+ with a +value+ |
| suissues-patch/app/models/version.rb | ||
|---|---|---|
| 27 | 27 |
validates_length_of :name, :maximum => 60 |
| 28 | 28 |
validates_format_of :effective_date, :with => /^\d{4}-\d{2}-\d{2}$/, :message => 'activerecord_error_not_a_date', :allow_nil => true
|
| 29 | 29 |
|
| 30 |
include Comparable |
|
| 31 |
|
|
| 30 | 32 |
def start_date |
| 31 | 33 |
effective_date |
| 32 | 34 |
end |
| suissues-patch/app/views/issues/_edit.rhtml | ||
|---|---|---|
| 15 | 15 |
<%= render :partial => (@edit_allowed ? 'form' : 'form_update'), :locals => {:f => f} %>
|
| 16 | 16 |
</fieldset> |
| 17 | 17 |
<% end %> |
| 18 |
<% if authorize_for('timelog', 'edit') %>
|
|
| 18 |
<% if authorize_for('timelog', 'edit') && @issue.edge? %>
|
|
| 19 | 19 |
<fieldset class="tabular"><legend><%= l(:button_log_time) %></legend> |
| 20 | 20 |
<% fields_for :time_entry, @time_entry, { :builder => TabularFormBuilder, :lang => current_language} do |time_entry| %>
|
| 21 | 21 |
<div class="splitcontentleft"> |
| suissues-patch/app/views/issues/_form.rhtml | ||
|---|---|---|
| 1 |
<!-- This function is needed for get ID from the text field with parent issue selection. --> |
|
| 2 |
<script type="text/javascript"> |
|
| 3 |
//<![CDATA[ |
|
| 4 |
function setParentIssueValue(element, value) {
|
|
| 5 |
document.getElementById('issue_parent_issue_id').value = value.id;
|
|
| 6 |
} |
|
| 7 |
//]]> |
|
| 8 |
</script> |
|
| 1 | 9 |
<% if @issue.new_record? %> |
| 2 | 10 |
<p><%= f.select :tracker_id, @project.trackers.collect {|t| [t.name, t.id]}, :required => true %></p>
|
| 3 | 11 |
<%= observe_field :issue_tracker_id, :url => { :action => :new },
|
| ... | ... | |
| 17 | 25 | |
| 18 | 26 |
<div class="attributes"> |
| 19 | 27 |
<div class="splitcontentleft"> |
| 20 |
<% if @issue.new_record? || @allowed_statuses.any? %>
|
|
| 28 |
<% if (@issue.new_record? || @allowed_statuses.any?) %>
|
|
| 21 | 29 |
<p><%= f.select :status_id, (@allowed_statuses.collect {|p| [p.name, p.id]}), :required => true %></p>
|
| 22 | 30 |
<% else %> |
| 23 | 31 |
<p><label><%= l(:field_status) %></label> <%= @issue.status.name %></p> |
| 24 | 32 |
<% end %> |
| 25 | 33 | |
| 26 | 34 |
<p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), :required => true %></p>
|
| 35 | ||
| 27 | 36 |
<p><%= f.select :assigned_to_id, (@issue.assignable_users.collect {|m| [m.name, m.id]}), :include_blank => true %></p>
|
| 28 | 37 |
<% unless @project.issue_categories.empty? %> |
| 29 | 38 |
<p><%= f.select :category_id, (@project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %>
|
| ... | ... | |
| 38 | 47 |
</div> |
| 39 | 48 | |
| 40 | 49 |
<div class="splitcontentright"> |
| 50 |
<% if @issue.edge? %> |
|
| 41 | 51 |
<p><%= f.text_field :start_date, :size => 10 %><%= calendar_for('issue_start_date') %></p>
|
| 42 | 52 |
<p><%= f.text_field :due_date, :size => 10 %><%= calendar_for('issue_due_date') %></p>
|
| 43 | 53 |
<p><%= f.text_field :estimated_hours, :size => 3 %> <%= l(:field_hours) %></p> |
| 44 | 54 |
<p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>
|
| 55 |
<% else %> |
|
| 56 |
<p><label><%= l(:field_start_date) %></label> <%= format_date(@issue.start_date) %></p> |
|
| 57 |
<p><label><%= l(:field_due_date) %></label> <%= format_date(@issue.due_date) %></p> |
|
| 58 |
<p><label><%= l(:field_done_ratio) %></label> <%= "#{@issue.done_ratio}%" %></p>
|
|
| 59 |
<% end %> |
|
| 60 |
<%= content_tag( :input, {},
|
|
| 61 |
:id => :issue_parent_issue_id, |
|
| 62 |
:type => :hidden, |
|
| 63 |
:name => 'issue_parent_issue_id', :value => @parent_issue ? @parent_issue.id : "") %> |
|
| 64 |
<p><label><%= l(:field_parent_issue) %></label> |
|
| 65 |
<% if authorize_for( 'issues', 'add_subissue') %> |
|
| 66 |
<%= text_field_with_auto_complete( :issue, :parent, |
|
| 67 |
{ :name => 'issue_parent', :value => @parent_issue || "" },
|
|
| 68 |
:url => { :action => 'auto_complete_for_issue_parent', :project_id => @project},
|
|
| 69 |
:after_update_element => 'setParentIssueValue') %> |
|
| 70 |
<% else %> |
|
| 71 |
<%= @parent_issue || "-" %> |
|
| 72 |
<% end %> |
|
| 73 |
</p> |
|
| 45 | 74 |
</div> |
| 46 | 75 | |
| 47 | 76 |
<div style="clear:both;"> </div> |
| suissues-patch/app/views/issues/_form_update.rhtml | ||
|---|---|---|
| 4 | 4 |
<p><%= f.select :assigned_to_id, (@issue.assignable_users.collect {|m| [m.name, m.id]}), :include_blank => true %></p>
|
| 5 | 5 |
</div> |
| 6 | 6 |
<div class="splitcontentright"> |
| 7 |
<% if @issue.edge? %> |
|
| 7 | 8 |
<p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>
|
| 9 |
<% else %> |
|
| 10 |
<p><label><%= l(:field_done_ratio) %></label> <%= "#{@issue.done_ratio}%" %></p>
|
|
| 11 |
<% end %> |
|
| 8 | 12 |
<%= content_tag('p', f.select(:fixed_version_id,
|
| 9 | 13 |
(@project.versions.sort.collect {|v| [v.name, v.id]}),
|
| 10 | 14 |
{ :include_blank => true })) unless @project.versions.empty? %>
|
| suissues-patch/app/views/issues/_list.rhtml | ||
|---|---|---|
| 1 |
<% form_tag({}) do -%>
|
|
| 2 |
<table class="list issues"> |
|
| 1 |
<% form_tag({}) do -%>
|
|
| 2 |
<table class="list issues">
|
|
| 3 | 3 |
<thead><tr> |
| 4 | 4 |
<th><%= link_to image_tag('toggle_check.png'), {}, :onclick => 'toggleIssuesSelection(Element.up(this, "form")); return false;',
|
| 5 |
:title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %>
|
|
| 5 |
:title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %>
|
|
| 6 | 6 |
</th> |
| 7 |
<%= sort_header_tag('id', :caption => '#', :default_order => 'desc') %>
|
|
| 7 |
<%= sort_header_tag('id', :caption => '#', :default_order => 'desc') %>
|
|
| 8 | 8 |
<% query.columns.each do |column| %> |
| 9 | 9 |
<%= column_header(column) %> |
| 10 | 10 |
<% end %> |
| 11 |
</tr></thead> |
|
| 12 |
<tbody> |
|
| 11 |
</tr></thead> |
|
| 12 |
<tbody> |
|
| 13 |
<% if query.view_options['show_parents'] == Query::VIEW_OPTIONS_SHOW_PARENTS_NEVER -%> |
|
| 13 | 14 |
<% issues.each do |issue| -%> |
| 14 |
<tr id="issue-<%= issue.id %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= css_issue_classes(issue) %>">
|
|
| 15 |
<td class="checkbox"><%= check_box_tag("ids[]", issue.id, false, :id => nil) %></td>
|
|
| 16 |
<td><%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %></td> |
|
| 17 |
<% query.columns.each do |column| %><%= content_tag 'td', column_content(column, issue), :class => column.name %><% end %> |
|
| 18 |
</tr> |
|
| 15 |
<%= issue_content( issue, query) %> |
|
| 19 | 16 |
<% end -%> |
| 20 |
</tbody> |
|
| 21 |
</table> |
|
| 17 |
<% elsif query.view_options['show_parents'] == Query::VIEW_OPTIONS_SHOW_PARENTS_ALWAYS -%> |
|
| 18 |
<% issues.each do |issue| -%> |
|
| 19 |
<% issue.parents_hierarchy.reverse.each do |parent_issue| -%> |
|
| 20 |
<%= issue_content( parent_issue, query, :unfiltered => true) %> |
|
| 21 |
<% end -%> |
|
| 22 |
<%= issue_content( issue, query) %> |
|
| 23 |
<% end -%> |
|
| 24 |
<% elsif query.view_options['show_parents'] == Query::VIEW_OPTIONS_SHOW_PARENTS_ORGANIZE_BY_PARENT -%> |
|
| 25 |
<% parents_on_first_lvl = [] |
|
| 26 |
issues.each do |i| |
|
| 27 |
if i.parent |
|
| 28 |
first_parent = i.parents_hierarchy.last |
|
| 29 |
else |
|
| 30 |
first_parent = i |
|
| 31 |
end |
|
| 32 |
parents_on_first_lvl += [ first_parent ] unless parents_on_first_lvl.include?( first_parent) |
|
| 33 |
end -%> |
|
| 34 |
<% parents_on_first_lvl.each do |parent| -%> |
|
| 35 |
<%= issues_family_content( parent, issues, query) %> |
|
| 36 |
<% end -%> |
|
| 37 |
<% end -%> |
|
| 38 |
</tbody> |
|
| 39 |
</table> |
|
| 22 | 40 |
<% end -%> |
| suissues-patch/app/views/issues/context_menu.rhtml | ||
|---|---|---|
| 15 | 15 |
<li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id)},
|
| 16 | 16 |
:class => 'icon-edit', :disabled => !@can[:edit] %></li> |
| 17 | 17 |
<% end %> |
| 18 | ||
| 19 | 18 |
<li class="folder"> |
| 20 | 19 |
<a href="#" class="submenu"><%= l(:field_priority) %></a> |
| 21 | 20 |
<ul> |
| ... | ... | |
| 64 | 63 |
</ul> |
| 65 | 64 |
</li> |
| 66 | 65 |
<% end -%> |
| 66 |
<% if @issue && @issue.edge? %> |
|
| 67 | 67 |
<li class="folder"> |
| 68 | 68 |
<a href="#" class="submenu"><%= l(:field_done_ratio) %></a> |
| 69 | 69 |
<ul> |
| ... | ... | |
| 73 | 73 |
<% end -%> |
| 74 | 74 |
</ul> |
| 75 | 75 |
</li> |
| 76 |
|
|
| 76 |
<% end %> |
|
| 77 | 77 |
<% if !@issue.nil? %> |
| 78 | 78 |
<li><%= context_menu_link l(:button_copy), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue},
|
| 79 | 79 |
:class => 'icon-copy', :disabled => !@can[:copy] %></li> |
| suissues-patch/app/views/issues/index.rhtml | ||
|---|---|---|
| 4 | 4 |
|
| 5 | 5 |
<% form_tag({ :controller => 'queries', :action => 'new' }, :id => 'query_form') do %>
|
| 6 | 6 |
<%= hidden_field_tag('project_id', @project.id) if @project %>
|
| 7 |
<fieldset id="view"><legend><%= l(:label_view) %></legend> |
|
| 8 |
<%= render :partial => 'queries/view_options', :locals => {:query => @query } %>
|
|
| 9 |
</fieldset> |
|
| 7 | 10 |
<fieldset id="filters"><legend><%= l(:label_filter_plural) %></legend> |
| 8 |
<%= render :partial => 'queries/filters', :locals => {:query => @query} %>
|
|
| 11 |
<%= render :partial => 'queries/filters', :locals => {:query => @query} %>
|
|
| 12 |
</fieldset> |
|
| 9 | 13 |
<p class="buttons"> |
| 10 | 14 |
<%= link_to_remote l(:button_apply), |
| 11 | 15 |
{ :url => { :set_filter => 1 },
|
| ... | ... | |
| 23 | 27 |
<%= link_to l(:button_save), {}, :onclick => "$('query_form').submit(); return false;", :class => 'icon icon-save' %>
|
| 24 | 28 |
<% end %> |
| 25 | 29 |
</p> |
| 26 |
</fieldset> |
|
| 27 | 30 |
<% end %> |
| 28 | 31 |
<% else %> |
| 29 | 32 |
<div class="contextual"> |
| suissues-patch/app/views/issues/show.rhtml | ||
|---|---|---|
| 1 | 1 |
<div class="contextual"> |
| 2 |
<%= link_to_if_authorized(l(:button_add_subissue), |
|
| 3 |
{ :controller => 'issues', :action => 'add_subissue',
|
|
| 4 |
:project_id => @project.id, :issue_parent_issue_id => @issue.id }, |
|
| 5 |
:class => 'icon icon-add') %> |
|
| 2 | 6 |
<%= link_to_if_authorized(l(:button_update), {:controller => 'issues', :action => 'edit', :id => @issue }, :onclick => 'showAndScrollTo("update", "notes"); return false;', :class => 'icon icon-edit', :accesskey => accesskey(:edit)) %>
|
| 3 | 7 |
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue}, :class => 'icon icon-time-add' %>
|
| 4 | 8 |
<%= watcher_tag(@issue, User.current) %> |
| ... | ... | |
| 51 | 55 |
if (n > 1) |
| 52 | 56 |
n = 0 %> |
| 53 | 57 |
</tr><tr> |
| 54 |
<%end
|
|
| 55 |
end %> |
|
| 58 |
<% end %>
|
|
| 59 |
<% end %>
|
|
| 56 | 60 |
</tr> |
| 57 | 61 |
<%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %> |
| 58 | 62 |
</table> |
| suissues-patch/app/views/projects/roadmap.rhtml | ||
|---|---|---|
| 10 | 10 |
<%= render :partial => 'versions/overview', :locals => {:version => version} %>
|
| 11 | 11 |
<%= render(:partial => "wiki/content", :locals => {:content => version.wiki_page.content}) if version.wiki_page %>
|
| 12 | 12 | |
| 13 |
<% issues = version.fixed_issues.find(:all, |
|
| 14 |
:include => [:status, :tracker], |
|
| 15 |
:conditions => ["tracker_id in (#{@selected_tracker_ids.join(',')})"],
|
|
| 16 |
:order => "#{Tracker.table_name}.position, #{Issue.table_name}.id") unless @selected_tracker_ids.empty?
|
|
| 13 |
<% |
|
| 14 |
unless @selected_tracker_ids.empty? |
|
| 15 |
issues = version.fixed_issues.find(:all, |
|
| 16 |
:include => [:status, :tracker], |
|
| 17 |
:conditions => ["tracker_id in (#{@selected_tracker_ids.join(',')})"],
|
|
| 18 |
:order => "#{Tracker.table_name}.position, #{Issue.table_name}.id")
|
|
| 19 |
issues = Issue.find_with_parents( issues.collect { |i| i.id })
|
|
| 20 |
end |
|
| 17 | 21 |
issues ||= [] |
| 18 | 22 |
%> |
| 19 | 23 |
<% if issues.size > 0 %> |
| 20 | 24 |
<fieldset class="related-issues"><legend><%= l(:label_related_issues) %></legend> |
| 21 |
<ul> |
|
| 22 |
<%- issues.each do |issue| -%> |
|
| 23 |
<li><%= link_to_issue(issue) %>: <%=h issue.subject %></li> |
|
| 24 |
<%- end -%> |
|
| 25 |
</ul> |
|
| 25 |
<%= render_list_of_related_issues( issues, version) %> |
|
| 26 | 26 |
</fieldset> |
| 27 | 27 |
<% end %> |
| 28 | 28 |
<% end %> |
| suissues-patch/app/views/queries/_form.rhtml | ||
|---|---|---|
| 21 | 21 |
:onclick => 'if (this.checked) {Element.hide("columns")} else {Element.show("columns")}' %></p>
|
| 22 | 22 |
</div> |
| 23 | 23 | |
| 24 |
<fieldset><legend><%= l(:label_view) %></legend> |
|
| 25 |
<%= render :partial => 'queries/view_options', :locals => {:query => query } %>
|
|
| 26 |
</fieldset> |
|
| 27 | ||
| 24 | 28 |
<fieldset><legend><%= l(:label_filter_plural) %></legend> |
| 25 | 29 |
<%= render :partial => 'queries/filters', :locals => {:query => query}%>
|
| 26 | 30 |
</fieldset> |
| suissues-patch/app/views/queries/_view_options.rhtml | ||
|---|---|---|
| 1 |
<% query.view_options.each_key do |voption| -%> |
|
| 2 |
<%= query.caption_for_view_option( voption) %>: |
|
| 3 |
<%= select_tag( "view_options[#{voption}]",
|
|
| 4 |
options_for_select( query.values_for_view_option( voption), |
|
| 5 |
query.view_options[voption])) %> |
|
| 6 |
<% end %> |
|
| suissues-patch/app/views/versions/show.rhtml | ||
|---|---|---|
| 31 | 31 |
<%= render :partial => 'versions/overview', :locals => {:version => @version} %>
|
| 32 | 32 |
<%= render(:partial => "wiki/content", :locals => {:content => @version.wiki_page.content}) if @version.wiki_page %>
|
| 33 | 33 | |
| 34 |
<% issues = @version.fixed_issues.find(:all, |
|
| 35 |
:include => [:status, :tracker], |
|
| 36 |
:order => "#{Tracker.table_name}.position, #{Issue.table_name}.id") %>
|
|
| 37 |
<% if issues.size > 0 %> |
|
| 34 |
<% if @issues.size > 0 %> |
|
| 38 | 35 |
<fieldset class="related-issues"><legend><%= l(:label_related_issues) %></legend> |
| 39 |
<ul> |
|
| 40 |
<% issues.each do |issue| -%> |
|
| 41 |
<li><%= link_to_issue(issue) %>: <%=h issue.subject %></li> |
|
| 42 |
<% end -%> |
|
| 43 |
</ul> |
|
| 36 |
<%= render_list_of_related_issues( @issues, @version) %> |
|
| 44 | 37 |
</fieldset> |
| 45 | 38 |
<% end %> |
| 46 | 39 |
</div> |
| suissues-patch/db/migrate/20090115162651_add_queries_view_options.rb | ||
|---|---|---|
| 1 |
class AddQueriesViewOptions < ActiveRecord::Migration |
|
| 2 |
def self.up |
|
| 3 |
add_column :queries, :view_options, :text |
|
| 4 |
end |
|
| 5 | ||
| 6 |
def self.down |
|
| 7 |
remove_column :queries, :view_options |
|
| 8 |
end |
|
| 9 |
end |
|
| suissues-patch/db/migrate/20090121172432_add_default_value_of_view_option_queries.rb | ||
|---|---|---|
| 1 |
class AddDefaultValueOfViewOptionQueries < ActiveRecord::Migration |
|
| 2 |
def self.up |
|
| 3 |
Query.find(:all).each do |q| |
|
| 4 |
q.view_options ||= { 'show_parents' => 'do_not_show' }
|
|
| 5 |
q.save! |
|
| 6 |
end |
|
| 7 |
end |
|
| 8 | ||
| 9 |
def self.down |
|
| 10 |
end |
|
| 11 |
end |
|
| suissues-patch/lang/en.yml | ||
|---|---|---|
| 84 | 84 |
error_scm_command_failed: "An error occurred when trying to access the repository: %s" |
| 85 | 85 |
error_scm_annotate: "The entry does not exist or can not be annotated." |
| 86 | 86 |
error_issue_not_found_in_project: 'The issue was not found or does not belong to this project' |
| 87 |
error_issue_can_have_only_one_parent: allows only one relation (a task can have only one parent). |
|
| 87 | 88 | |
| 88 | 89 |
warning_attachments_not_saved: "%d file(s) could not be saved." |
| 89 | 90 | |
| ... | ... | |
| 187 | 188 |
field_comments_sorting: Display comments |
| 188 | 189 |
field_parent_title: Parent page |
| 189 | 190 |
field_editable: Editable |
| 191 |
field_calendar_firstday: First day of week |
|
| 192 |
field_parent: Subproject of |
|
| 193 |
field_parent_issue: Child of |
|
| 194 |
field_parent_title: Parent page |
|
| 190 | 195 | |
| 191 | 196 |
setting_app_title: Application title |
| 192 | 197 |
setting_app_subtitle: Application subtitle |
| ... | ... | |
| 587 | 592 |
label_issue_watchers: Watchers |
| 588 | 593 |
label_example: Example |
| 589 | 594 |
label_display: Display |
| 595 |
label_children: parent of |
|
| 596 |
label_parents: child of |
|
| 597 |
label_view_option_parents_do_not_show: Never |
|
| 598 |
label_view_option_parents_show_always: Always |
|
| 599 |
label_view_option_parents_show_and_group: Organize by parent |
|
| 600 |
label_view_option_show_parents: Show parents |
|
| 590 | 601 | |
| 591 | 602 |
button_login: Login |
| 592 | 603 |
button_submit: Submit |
| ... | ... | |
| 627 | 638 |
button_update: Update |
| 628 | 639 |
button_configure: Configure |
| 629 | 640 |
button_quote: Quote |
| 641 |
button_add_subissue: Add sub-issue |
|
| 630 | 642 | |
| 631 | 643 |
status_active: active |
| 632 | 644 |
status_registered: registered |
| suissues-patch/lib/redmine.rb | ||
|---|---|---|
| 35 | 35 |
:queries => :index, |
| 36 | 36 |
:reports => :issue_report}, :public => true |
| 37 | 37 |
map.permission :add_issues, {:issues => :new}
|
| 38 |
map.permission :edit_issues, {:issues => [:edit, :reply, :bulk_edit]}
|
|
| 39 |
map.permission :manage_issue_relations, {:issue_relations => [:new, :destroy]}
|
|
| 38 |
map.permission :edit_issues, {:issues => [:edit, :reply, :bulk_edit, :update_subject]}
|
|
| 39 |
map.permission :manage_issue_relations, {:issue_relations => [:new, :destroy], :issues => :add_subissue}
|
|
| 40 | 40 |
map.permission :add_issue_notes, {:issues => [:edit, :reply]}
|
| 41 | 41 |
map.permission :edit_issue_notes, {:journals => :edit}, :require => :loggedin
|
| 42 | 42 |
map.permission :edit_own_issue_notes, {:journals => :edit}, :require => :loggedin
|
| suissues-patch/lib/redmine/version.rb | ||
|---|---|---|
| 23 | 23 |
if entries.match(%r{^\d+})
|
| 24 | 24 |
revision = $1.to_i if entries.match(%r{^\d+\s+dir\s+(\d+)\s})
|
| 25 | 25 |
else |
| 26 |
xml = REXML::Document.new(entries) |
|
| 27 |
revision = xml.elements['wc-entries'].elements[1].attributes['revision'].to_i |
|
| 28 |
end |
|
| 29 |
rescue |
|
| 30 |
# Could not find the current revision |
|
| 31 |
end |
|
| 32 |
end |
|
| 33 |
revision |
|
| 26 |
xml = REXML::Document.new(entries) |
|
| 27 |
revision = xml.elements['wc-entries'].elements[1].attributes['revision'].to_i |
|
| 28 |
end |
|
| 29 |
rescue |
|
| 30 |
# Could not find the current revision |
|
| 31 |
end |
|
| 32 |
end |
|
| 33 |
revision |
|
| 34 |
end |
|
| 35 | ||
| 36 |
def self.warecorp_revision |
|
| 37 |
begin |
|
| 38 |
tag = %x{ git name-rev --tags `git log -1 --pretty=format:'%H'` }.split[1]
|
|
| 39 |
tag = ( tag.match %r{tags/(.*)})[1]
|
|
| 40 |
rescue |
|
| 41 |
tag = "undefined" |
|
| 42 |
end |
|
| 43 |
tag |
|
| 34 | 44 |
end |
| 35 | 45 | |
| 36 | 46 |
REVISION = self.revision |
| 47 |
WARECORP_REVISION = self.warecorp_revision |
|
| 37 | 48 |
ARRAY = [MAJOR, MINOR, TINY, BRANCH, REVISION].compact |
| 38 |
STRING = ARRAY.join('.')
|
|
| 49 |
STRING = ARRAY.join('.') + " / #{WARECORP_REVISION}"
|
|
| 39 | 50 |
|
| 40 | 51 |
def self.to_a; ARRAY end |
| 41 | 52 |
def self.to_s; STRING end |
| suissues-patch/public/stylesheets/application.css | ||
|---|---|---|
| 704 | 704 |
#main { background: #fff; }
|
| 705 | 705 |
#content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; overflow: visible !important;}
|
| 706 | 706 |
} |
| 707 | ||
| 708 |
/***** Subtasks *****/ |
|
| 709 |
/*blocks composed of icon (+, - or none), project identification (if there is one) and link to the task*/ |
|
| 710 |
.issue-subject-level-1, .issue-subject-level-1 .issue-subject {
|
|
| 711 |
margin-left: 0em; |
|
| 712 |
} |
|
| 713 | ||
| 714 |
.issue-subject-level-2, .issue-subject-level-2 .issue-subject {
|
|
| 715 |
margin-left: 1em; |
|
| 716 |
} |
|
| 717 | ||
| 718 |
.issue-subject-level-3, .issue-subject-level-3 .issue-subject {
|
|
| 719 |
margin-left: 2em; |
|
| 720 |
} |
|
| 721 | ||
| 722 |
.issue-subject-level-4, .issue-subject-level-4 .issue-subject {
|
|
| 723 |
margin-left: 3em; |
|
| 724 |
} |
|
| 725 | ||
| 726 |
.issue-subject-level-5, .issue-subject-level-5 .issue-subject {
|
|
| 727 |
margin-left: 4em; |
|
| 728 |
} |
|
| 729 | ||
| 730 |
.issue-subject-level-2, .issue-subject-level-2 .issue-subject, .issue-subject-level-3, .issue-subject-level-3 .issue-subject, .issue-subject-level-4, .issue-subject-level-4 .issue-subject, .issue-subject-level-5, .issue-subject-level-5 .issue-subject {
|
|
| 731 |
background-image: url(../images/corner-dots.gif); |
|
| 732 |
background-repeat: no-repeat; |
|
| 733 |
background-position: -3px center; |
|
| 734 |
} |
|
| 735 | ||
| 736 |
/* Used to show issues which is not pass by filter, but should by |
|
| 737 |
shown as parent for other issue. */ |
|
| 738 |
.issue-unfiltered {
|
|
| 739 |
opacity: 0.5; |
|
| 740 |
filter: alpha(opacity=50); |
|
| 741 |
} |
|
| 742 | ||
| 743 |
.expanded-issue {
|
|
| 744 |
background-image: url(contract.png); |
|
| 745 |
background-repeat: no-repeat; |
|
| 746 |
} |
|
| 747 | ||
| 748 |
.contracted-issue {
|
|
| 749 |
background-image: url(expand.png); |
|
| 750 |
background-repeat: no-repeat; |
|
| 751 |
} |
|
| 752 | ||
| 753 |
.expand-icon, .contract-icon{
|
|
| 754 |
/* position: absolute; */ |
|
| 755 |
vertical-align: middle; |
|
| 756 |
} |
|
| 757 | ||
| 758 |
/*text after the icon, which needs to be indented so that all its lines stay completely after the icon*/ |
|
| 759 |
.issue-subject{
|
|
| 760 |
padding-left: 1em; |
|
| 761 |
} |
|
| 762 | ||
| suissues-patch/test/fixtures/queries.yml | ||
|---|---|---|
| 19 | 19 |
- "125" |
| 20 | 20 |
:operator: "=" |
| 21 | 21 |
|
| 22 |
view_options: | |
|
| 23 |
--- |
|
| 24 |
show_parents: "do_not_show" |
|
| 25 |
|
|
| 22 | 26 |
user_id: 1 |
| 23 | 27 |
column_names: |
| 24 | 28 |
queries_002: |
| ... | ... | |
| 37 | 41 |
- "1" |
| 38 | 42 |
:operator: o |
| 39 | 43 |
|
| 44 |
view_options: | |
|
| 45 |
--- |
|
| 46 |
show_parents: "do_not_show" |
|
| 47 |
|
|
| 40 | 48 |
user_id: 3 |
| 41 | 49 |
column_names: |
| 42 | 50 |
queries_003: |
| ... | ... | |
| 51 | 59 |
- "3" |
| 52 | 60 |
:operator: "=" |
| 53 | 61 |
|
| 62 |
view_options: | |
|
| 63 |
--- |
|
| 64 |
show_parents: "do_not_show" |
|
| 65 |
|
|
| 54 | 66 |
user_id: 3 |
| 55 | 67 |
column_names: |
| 56 | 68 |
queries_004: |
| ... | ... | |
| 65 | 77 |
- "3" |
| 66 | 78 |
:operator: "=" |
| 67 | 79 |
|
| 80 |
view_options: | |
|
| 81 |
--- |
|
| 82 |
show_parents: "do_not_show" |
|
| 83 |
|
|
| 68 | 84 |
user_id: 2 |
| 69 | 85 |
column_names: |
| suissues-patch/test/fixtures/versions.yml | ||
|---|---|---|
| 23 | 23 |
id: 3 |
| 24 | 24 |
description: Future version |
| 25 | 25 |
effective_date: |
| 26 |
onlinestore_1_0: |
|
| 27 |
created_on: 2006-07-19 21:00:33 +02:00 |
|
| 28 |
name: "1.0" |
|
| 29 |
project_id: 1 |
|
| 30 |
updated_on: 2006-07-19 21:00:33 +02:00 |
|
| 31 |
id: 4 |
|
| 32 |
description: Future version |
|
| 33 |
effective_date: |
|
| 26 | 34 |
|
| suissues-patch/test/functional/issues_controller_test.rb | ||
|---|---|---|
| 22 | 22 |
class IssuesController; def rescue_action(e) raise e end; end |
| 23 | 23 | |
| 24 | 24 |
class IssuesControllerTest < Test::Unit::TestCase |
| 25 | ||
| 25 | 26 |
fixtures :projects, |
| 26 | 27 |
:users, |
| 27 | 28 |
:roles, |
| 28 | 29 |
:members, |
| 30 |
:queries, |
|
| 29 | 31 |
:issues, |
| 30 | 32 |
:issue_statuses, |
| 31 | 33 |
:versions, |
| ... | ... | |
| 960 | 962 |
assert_equal 2, TimeEntry.find(1).issue_id |