diff --git a/app/views/issues/_form.rhtml b/app/views/issues/_form.rhtml
index fac2d6a..869c804 100644
--- a/app/views/issues/_form.rhtml
+++ b/app/views/issues/_form.rhtml
@@ -1,3 +1,11 @@
+
+
<% if @issue.new_record? %>
<%= f.select :tracker_id, @project.trackers.collect {|t| [t.name, t.id]}, :required => true %>
<%= observe_field :issue_tracker_id, :url => { :action => :new },
@@ -17,13 +25,14 @@
+<% if @issue.leaf? %>
<%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %>
+<% else %>
+
<%= "#{@issue.done_ratio}%" %>
+<% end %>
<%= content_tag('p', f.select(:fixed_version_id,
(@project.versions.sort.collect {|v| [v.name, v.id]}),
{ :include_blank => true })) unless @project.versions.empty? %>
diff --git a/app/views/issues/_list.rhtml b/app/views/issues/_list.rhtml
index 9326760..7884bce 100644
--- a/app/views/issues/_list.rhtml
+++ b/app/views/issues/_list.rhtml
@@ -1,22 +1,40 @@
-<% form_tag({}) do -%>
-
+<% form_tag({}) do -%>
+
| <%= link_to image_tag('toggle_check.png'), {}, :onclick => 'toggleIssuesSelection(Element.up(this, "form")); return false;',
- :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %>
+ :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %>
|
- <%= sort_header_tag('id', :caption => '#', :default_order => 'desc') %>
+ <%= sort_header_tag('id', :caption => '#', :default_order => 'desc') %>
<% query.columns.each do |column| %>
<%= column_header(column) %>
<% end %>
-
-
+
+
+ <% if query.view_options['show_parents'] == Query::VIEW_OPTIONS_SHOW_PARENTS_NEVER -%>
<% issues.each do |issue| -%>
-
+ <%= issue_content( issue, query) %>
<% end -%>
-
-
+ <% elsif query.view_options['show_parents'] == Query::VIEW_OPTIONS_SHOW_PARENTS_ALWAYS -%>
+ <% issues.each do |issue| -%>
+ <% issue.ancestors.reverse.each do |parent_issue| -%>
+ <%= issue_content( parent_issue, query, :unfiltered => true) %>
+ <% end -%>
+ <%= issue_content( issue, query) %>
+ <% end -%>
+ <% elsif query.view_options['show_parents'] == Query::VIEW_OPTIONS_SHOW_PARENTS_ORGANIZE_BY_PARENT -%>
+ <% parents_on_first_lvl = []
+ issues.each do |i|
+ if i.parent
+ first_parent = i.root
+ else
+ first_parent = i
+ end
+ parents_on_first_lvl += [ first_parent ] unless parents_on_first_lvl.include?( first_parent)
+ end -%>
+ <% parents_on_first_lvl.each do |parent| -%>
+ <%= issues_family_content( parent, issues, query) %>
+ <% end -%>
+ <% end -%>
+
+
<% end -%>
diff --git a/app/views/issues/context_menu.rhtml b/app/views/issues/context_menu.rhtml
index ae9a1af..ff7660f 100644
--- a/app/views/issues/context_menu.rhtml
+++ b/app/views/issues/context_menu.rhtml
@@ -17,7 +17,6 @@
<%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id)},
:class => 'icon-edit', :disabled => !@can[:edit] %>
<% end %>
-
<% end -%>
+ <% if @issue && @issue.leaf? %>
@@ -75,7 +75,7 @@
<% end -%>
-
+ <% end %>
<% if !@issue.nil? %>
<%= context_menu_link l(:button_copy), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue},
:class => 'icon-copy', :disabled => !@can[:copy] %>
diff --git a/app/views/issues/index.rhtml b/app/views/issues/index.rhtml
index 7c381d8..e855c28 100644
--- a/app/views/issues/index.rhtml
+++ b/app/views/issues/index.rhtml
@@ -4,8 +4,12 @@
<% form_tag({ :controller => 'queries', :action => 'new' }, :id => 'query_form') do %>
<%= hidden_field_tag('project_id', @project.to_param) if @project %>
+
<%= link_to_remote l(:button_apply),
{ :url => { :set_filter => 1 },
@@ -23,7 +27,6 @@
<%= link_to l(:button_save), {}, :onclick => "$('query_form').submit(); return false;", :class => 'icon icon-save' %>
<% end %>
-
<% end %>
<% else %>
diff --git a/app/views/issues/show.rhtml b/app/views/issues/show.rhtml
index ed14fe3..bd892c3 100644
--- a/app/views/issues/show.rhtml
+++ b/app/views/issues/show.rhtml
@@ -1,4 +1,8 @@
+<%= link_to_if_authorized(l(:button_add_subissue),
+ { :controller => 'issues', :action => 'add_subissue',
+ :project_id => @project.id, :issue => { :parent_id => @issue.id }},
+ :class => 'icon icon-add') %>
<%= 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)) %>
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue}, :class => 'icon icon-time-add' %>
<%= watcher_tag(@issue, User.current) %>
@@ -51,8 +55,8 @@
if (n > 1)
n = 0 %>
- <%end
-end %>
+ <% end %>
+<% end %>
<%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %>
diff --git a/app/views/projects/roadmap.rhtml b/app/views/projects/roadmap.rhtml
index bcba13a..7a5e2eb 100644
--- a/app/views/projects/roadmap.rhtml
+++ b/app/views/projects/roadmap.rhtml
@@ -10,19 +10,19 @@
<%= render :partial => 'versions/overview', :locals => {:version => version} %>
<%= render(:partial => "wiki/content", :locals => {:content => version.wiki_page.content}) if version.wiki_page %>
- <% issues = version.fixed_issues.find(:all,
- :include => [:status, :tracker],
- :conditions => ["tracker_id in (#{@selected_tracker_ids.join(',')})"],
- :order => "#{Tracker.table_name}.position, #{Issue.table_name}.id") unless @selected_tracker_ids.empty?
+ <%
+ unless @selected_tracker_ids.empty?
+ issues = version.fixed_issues.find(:all,
+ :include => [:status, :tracker],
+ :conditions => ["tracker_id in (#{@selected_tracker_ids.join(',')})"],
+ :order => "#{Tracker.table_name}.position, #{Issue.table_name}.id")
+ issues = Issue.find_with_parents( issues.collect { |i| i.id })
+ end
issues ||= []
%>
<% if issues.size > 0 %>
<% end %>
<%= call_hook :view_projects_roadmap_version_bottom, :version => version %>
diff --git a/app/views/queries/_form.rhtml b/app/views/queries/_form.rhtml
index 7c227a9..23e68fa 100644
--- a/app/views/queries/_form.rhtml
+++ b/app/views/queries/_form.rhtml
@@ -21,6 +21,10 @@
:onclick => 'if (this.checked) {Element.hide("columns")} else {Element.show("columns")}' %>
+
+
diff --git a/app/views/queries/_view_options.rhtml b/app/views/queries/_view_options.rhtml
new file mode 100644
index 0000000..dea7db8
--- /dev/null
+++ b/app/views/queries/_view_options.rhtml
@@ -0,0 +1,6 @@
+<% query.view_options.each_key do |voption| -%>
+ <%= query.caption_for_view_option( voption) %>:
+ <%= select_tag( "view_options[#{voption}]",
+ options_for_select( query.values_for_view_option( voption),
+ query.view_options[voption])) %>
+<% end %>
diff --git a/app/views/versions/show.rhtml b/app/views/versions/show.rhtml
index ef53751..d721b7c 100644
--- a/app/views/versions/show.rhtml
+++ b/app/views/versions/show.rhtml
@@ -31,16 +31,9 @@
<%= render :partial => 'versions/overview', :locals => {:version => @version} %>
<%= render(:partial => "wiki/content", :locals => {:content => @version.wiki_page.content}) if @version.wiki_page %>
-<% issues = @version.fixed_issues.find(:all,
- :include => [:status, :tracker],
- :order => "#{Tracker.table_name}.position, #{Issue.table_name}.id") %>
-<% if issues.size > 0 %>
+<% if @issues.size > 0 %>
<% end %>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index b60329f..8f69dc5 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -136,7 +136,8 @@ en:
error_scm_command_failed: "An error occurred when trying to access the repository: {{value}}"
error_scm_annotate: "The entry does not exist or can not be annotated."
error_issue_not_found_in_project: 'The issue was not found or does not belong to this project'
-
+ error_issue_can_have_only_one_parent: allows only one relation (a task can have only one parent).
+
warning_attachments_not_saved: "{{count}} file(s) could not be saved."
mail_subject_lost_password: "Your {{value}} password"
@@ -242,6 +243,10 @@ en:
field_watcher: Watcher
field_identity_url: OpenID URL
field_content: Content
+ field_calendar_firstday: First day of week
+ field_parent: Subproject of
+ field_parent_issue: Child of
+ field_parent_title: Parent page
setting_app_title: Application title
setting_app_subtitle: Application subtitle
@@ -664,6 +669,12 @@ en:
label_issue_watchers: Watchers
label_example: Example
label_display: Display
+ label_children: parent of
+ label_parents: child of
+ label_view_option_parents_do_not_show: Never
+ label_view_option_parents_show_always: Always
+ label_view_option_parents_show_and_group: Organize by parent
+ label_view_option_show_parents: Show parents
label_sort: Sort
label_ascending: Ascending
label_descending: Descending
@@ -708,7 +719,8 @@ en:
button_update: Update
button_configure: Configure
button_quote: Quote
-
+ button_add_subissue: Add sub-issue
+
status_active: active
status_registered: registered
status_locked: locked
diff --git a/db/migrate/20090115162651_add_queries_view_options.rb b/db/migrate/20090115162651_add_queries_view_options.rb
new file mode 100644
index 0000000..cfd0377
--- /dev/null
+++ b/db/migrate/20090115162651_add_queries_view_options.rb
@@ -0,0 +1,9 @@
+class AddQueriesViewOptions < ActiveRecord::Migration
+ def self.up
+ add_column :queries, :view_options, :text
+ end
+
+ def self.down
+ remove_column :queries, :view_options
+ end
+end
diff --git a/db/migrate/20090121172432_add_default_value_of_view_option_queries.rb b/db/migrate/20090121172432_add_default_value_of_view_option_queries.rb
new file mode 100644
index 0000000..8c40200
--- /dev/null
+++ b/db/migrate/20090121172432_add_default_value_of_view_option_queries.rb
@@ -0,0 +1,11 @@
+class AddDefaultValueOfViewOptionQueries < ActiveRecord::Migration
+ def self.up
+ Query.find(:all).each do |q|
+ q.view_options ||= { 'show_parents' => 'do_not_show' }
+ q.save!
+ end
+ end
+
+ def self.down
+ end
+end
diff --git a/db/migrate/20090406213813_add_issues_nested_sets.rb b/db/migrate/20090406213813_add_issues_nested_sets.rb
new file mode 100644
index 0000000..3e86392
--- /dev/null
+++ b/db/migrate/20090406213813_add_issues_nested_sets.rb
@@ -0,0 +1,36 @@
+class AddIssuesNestedSets < ActiveRecord::Migration
+ # IssueRelation::TYPE_PARENTS was deleted
+ TYPE_PARENTS = "parents"
+
+ def self.up
+ add_column :issues, :parent_id, :integer, :default => nil
+ add_column :issues, :lft, :integer
+ add_column :issues, :rgt, :integer
+ Issue.reset_column_information
+
+ say_with_time "fixing invalid issues" do
+ Issue.find( :all).each do |issue|
+ if !issue.valid? && issue.errors.on( :due_date)
+ issue.due_date = issue.start_date
+ issue.save!
+ end
+ end
+ end
+
+ say_with_time "rebuilding left & right indexes" do
+ Issue.rebuild!
+ end
+
+ say_with_time(
+ "converting subissues for using parent_id instead of IssueRelation") do
+ IssueRelation.find_all_by_relation_type( TYPE_PARENTS).each do |rel|
+ rel.issue_from.move_to_child_of rel.issue_to.id
+ rel.delete
+ end
+ end
+ end
+
+ def self.down
+ raise ActiveRecord::IrreversibleMigration
+ end
+end
diff --git a/lang/en.yml b/lang/en.yml
new file mode 100644
index 0000000..973a75c
--- /dev/null
+++ b/lang/en.yml
@@ -0,0 +1,720 @@
+_gloc_rule_default: '|n| n==1 ? "" : "_plural" '
+
+actionview_datehelper_select_day_prefix:
+actionview_datehelper_select_month_names: January,February,March,April,May,June,July,August,September,October,November,December
+actionview_datehelper_select_month_names_abbr: Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec
+actionview_datehelper_select_month_prefix:
+actionview_datehelper_select_year_prefix:
+actionview_datehelper_time_in_words_day: 1 day
+actionview_datehelper_time_in_words_day_plural: %d days
+actionview_datehelper_time_in_words_hour_about: about an hour
+actionview_datehelper_time_in_words_hour_about_plural: about %d hours
+actionview_datehelper_time_in_words_hour_about_single: about an hour
+actionview_datehelper_time_in_words_minute: 1 minute
+actionview_datehelper_time_in_words_minute_half: half a minute
+actionview_datehelper_time_in_words_minute_less_than: less than a minute
+actionview_datehelper_time_in_words_minute_plural: %d minutes
+actionview_datehelper_time_in_words_minute_single: 1 minute
+actionview_datehelper_time_in_words_second_less_than: less than a second
+actionview_datehelper_time_in_words_second_less_than_plural: less than %d seconds
+actionview_instancetag_blank_option: Please select
+
+activerecord_error_inclusion: is not included in the list
+activerecord_error_exclusion: is reserved
+activerecord_error_invalid: is invalid
+activerecord_error_confirmation: doesn't match confirmation
+activerecord_error_accepted: must be accepted
+activerecord_error_empty: can't be empty
+activerecord_error_blank: can't be blank
+activerecord_error_too_long: is too long
+activerecord_error_too_short: is too short
+activerecord_error_wrong_length: is the wrong length
+activerecord_error_taken: has already been taken
+activerecord_error_not_a_number: is not a number
+activerecord_error_not_a_date: is not a valid date
+activerecord_error_greater_than_start_date: must be greater than start date
+activerecord_error_not_same_project: doesn't belong to the same project
+activerecord_error_circular_dependency: This relation would create a circular dependency
+
+general_fmt_age: %d yr
+general_fmt_age_plural: %d yrs
+general_fmt_date: %%m/%%d/%%Y
+general_fmt_datetime: %%m/%%d/%%Y %%I:%%M %%p
+general_fmt_datetime_short: %%b %%d, %%I:%%M %%p
+general_fmt_time: %%I:%%M %%p
+general_text_No: 'No'
+general_text_Yes: 'Yes'
+general_text_no: 'no'
+general_text_yes: 'yes'
+general_lang_name: 'English'
+general_csv_separator: ','
+general_csv_decimal_separator: '.'
+general_csv_encoding: ISO-8859-1
+general_pdf_encoding: ISO-8859-1
+general_day_names: Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
+general_first_day_of_week: '7'
+
+notice_account_updated: Account was successfully updated.
+notice_account_invalid_creditentials: Invalid user or password
+notice_account_password_updated: Password was successfully updated.
+notice_account_wrong_password: Wrong password
+notice_account_register_done: Account was successfully created. To activate your account, click on the link that was emailed to you.
+notice_account_unknown_email: Unknown user.
+notice_can_t_change_password: This account uses an external authentication source. Impossible to change the password.
+notice_account_lost_email_sent: An email with instructions to choose a new password has been sent to you.
+notice_account_activated: Your account has been activated. You can now log in.
+notice_successful_create: Successful creation.
+notice_successful_update: Successful update.
+notice_successful_delete: Successful deletion.
+notice_successful_connection: Successful connection.
+notice_file_not_found: The page you were trying to access doesn't exist or has been removed.
+notice_locking_conflict: Data has been updated by another user.
+notice_not_authorized: You are not authorized to access this page.
+notice_email_sent: An email was sent to %s
+notice_email_error: An error occurred while sending mail (%s)
+notice_feeds_access_key_reseted: Your RSS access key was reset.
+notice_failed_to_save_issues: "Failed to save %d issue(s) on %d selected: %s."
+notice_no_issue_selected: "No issue is selected! Please, check the issues you want to edit."
+notice_account_pending: "Your account was created and is now pending administrator approval."
+notice_default_data_loaded: Default configuration successfully loaded.
+notice_unable_delete_version: Unable to delete version.
+
+error_can_t_load_default_data: "Default configuration could not be loaded: %s"
+error_scm_not_found: "The entry or revision was not found in the repository."
+error_scm_command_failed: "An error occurred when trying to access the repository: %s"
+error_scm_annotate: "The entry does not exist or can not be annotated."
+error_issue_not_found_in_project: 'The issue was not found or does not belong to this project'
+error_issue_can_have_only_one_parent: allows only one relation (a task can have only one parent).
+
+warning_attachments_not_saved: "%d file(s) could not be saved."
+
+mail_subject_lost_password: Your %s password
+mail_body_lost_password: 'To change your password, click on the following link:'
+mail_subject_register: Your %s account activation
+mail_body_register: 'To activate your account, click on the following link:'
+mail_body_account_information_external: You can use your "%s" account to log in.
+mail_body_account_information: Your account information
+mail_subject_account_activation_request: %s account activation request
+mail_body_account_activation_request: 'A new user (%s) has registered. The account is pending your approval:'
+mail_subject_reminder: "%d issue(s) due in the next days"
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+
+gui_validation_error: 1 error
+gui_validation_error_plural: %d errors
+
+field_name: Name
+field_description: Description
+field_summary: Summary
+field_is_required: Required
+field_firstname: Firstname
+field_lastname: Lastname
+field_mail: Email
+field_filename: File
+field_filesize: Size
+field_downloads: Downloads
+field_author: Author
+field_created_on: Created
+field_updated_on: Updated
+field_field_format: Format
+field_is_for_all: For all projects
+field_possible_values: Possible values
+field_regexp: Regular expression
+field_min_length: Minimum length
+field_max_length: Maximum length
+field_value: Value
+field_category: Category
+field_title: Title
+field_project: Project
+field_issue: Issue
+field_status: Status
+field_notes: Notes
+field_is_closed: Issue closed
+field_is_default: Default value
+field_tracker: Tracker
+field_subject: Subject
+field_due_date: Due date
+field_assigned_to: Assigned to
+field_priority: Priority
+field_fixed_version: Target version
+field_user: User
+field_role: Role
+field_homepage: Homepage
+field_is_public: Public
+field_parent: Subproject of
+field_is_in_chlog: Issues displayed in changelog
+field_is_in_roadmap: Issues displayed in roadmap
+field_login: Login
+field_mail_notification: Email notifications
+field_admin: Administrator
+field_last_login_on: Last connection
+field_language: Language
+field_effective_date: Date
+field_password: Password
+field_new_password: New password
+field_password_confirmation: Confirmation
+field_version: Version
+field_type: Type
+field_host: Host
+field_port: Port
+field_account: Account
+field_base_dn: Base DN
+field_attr_login: Login attribute
+field_attr_firstname: Firstname attribute
+field_attr_lastname: Lastname attribute
+field_attr_mail: Email attribute
+field_onthefly: On-the-fly user creation
+field_start_date: Start
+field_done_ratio: %% Done
+field_auth_source: Authentication mode
+field_hide_mail: Hide my email address
+field_comments: Comment
+field_url: URL
+field_start_page: Start page
+field_subproject: Subproject
+field_hours: Hours
+field_activity: Activity
+field_spent_on: Date
+field_identifier: Identifier
+field_is_filter: Used as a filter
+field_issue_to_id: Related issue
+field_delay: Delay
+field_assignable: Issues can be assigned to this role
+field_redirect_existing_links: Redirect existing links
+field_estimated_hours: Estimated time
+field_column_names: Columns
+field_time_zone: Time zone
+field_searchable: Searchable
+field_default_value: Default value
+field_comments_sorting: Display comments
+field_parent_title: Parent page
+field_editable: Editable
+field_calendar_firstday: First day of week
+field_parent: Subproject of
+field_parent_issue: Child of
+field_parent_title: Parent page
+
+setting_app_title: Application title
+setting_app_subtitle: Application subtitle
+setting_welcome_text: Welcome text
+setting_default_language: Default language
+setting_login_required: Authentication required
+setting_self_registration: Self-registration
+setting_attachment_max_size: Attachment max. size
+setting_issues_export_limit: Issues export limit
+setting_mail_from: Emission email address
+setting_bcc_recipients: Blind carbon copy recipients (bcc)
+setting_plain_text_mail: Plain text mail (no HTML)
+setting_host_name: Host name and path
+setting_text_formatting: Text formatting
+setting_wiki_compression: Wiki history compression
+setting_feeds_limit: Feed content limit
+setting_default_projects_public: New projects are public by default
+setting_autofetch_changesets: Autofetch commits
+setting_sys_api_enabled: Enable WS for repository management
+setting_commit_ref_keywords: Referencing keywords
+setting_commit_fix_keywords: Fixing keywords
+setting_autologin: Autologin
+setting_date_format: Date format
+setting_time_format: Time format
+setting_cross_project_issue_relations: Allow cross-project issue relations
+setting_issue_list_default_columns: Default columns displayed on the issue list
+setting_repositories_encodings: Repositories encodings
+setting_commit_logs_encoding: Commit messages encoding
+setting_emails_footer: Emails footer
+setting_protocol: Protocol
+setting_per_page_options: Objects per page options
+setting_user_format: Users display format
+setting_activity_days_default: Days displayed on project activity
+setting_display_subprojects_issues: Display subprojects issues on main projects by default
+setting_enabled_scm: Enabled SCM
+setting_mail_handler_api_enabled: Enable WS for incoming emails
+setting_mail_handler_api_key: API key
+setting_sequential_project_identifiers: Generate sequential project identifiers
+setting_gravatar_enabled: Use Gravatar user icons
+setting_diff_max_lines_displayed: Max number of diff lines displayed
+setting_repository_log_display_limit: Maximum number of revisions displayed on file log
+
+permission_edit_project: Edit project
+permission_select_project_modules: Select project modules
+permission_manage_members: Manage members
+permission_manage_versions: Manage versions
+permission_manage_categories: Manage issue categories
+permission_add_issues: Add issues
+permission_edit_issues: Edit issues
+permission_manage_issue_relations: Manage issue relations
+permission_add_issue_notes: Add notes
+permission_edit_issue_notes: Edit notes
+permission_edit_own_issue_notes: Edit own notes
+permission_move_issues: Move issues
+permission_delete_issues: Delete issues
+permission_manage_public_queries: Manage public queries
+permission_save_queries: Save queries
+permission_view_gantt: View gantt chart
+permission_view_calendar: View calender
+permission_view_issue_watchers: View watchers list
+permission_add_issue_watchers: Add watchers
+permission_log_time: Log spent time
+permission_view_time_entries: View spent time
+permission_edit_time_entries: Edit time logs
+permission_edit_own_time_entries: Edit own time logs
+permission_manage_news: Manage news
+permission_comment_news: Comment news
+permission_manage_documents: Manage documents
+permission_view_documents: View documents
+permission_manage_files: Manage files
+permission_view_files: View files
+permission_manage_wiki: Manage wiki
+permission_rename_wiki_pages: Rename wiki pages
+permission_delete_wiki_pages: Delete wiki pages
+permission_view_wiki_pages: View wiki
+permission_view_wiki_edits: View wiki history
+permission_edit_wiki_pages: Edit wiki pages
+permission_delete_wiki_pages_attachments: Delete attachments
+permission_protect_wiki_pages: Protect wiki pages
+permission_manage_repository: Manage repository
+permission_browse_repository: Browse repository
+permission_view_changesets: View changesets
+permission_commit_access: Commit access
+permission_manage_boards: Manage boards
+permission_view_messages: View messages
+permission_add_messages: Post messages
+permission_edit_messages: Edit messages
+permission_edit_own_messages: Edit own messages
+permission_delete_messages: Delete messages
+permission_delete_own_messages: Delete own messages
+
+project_module_issue_tracking: Issue tracking
+project_module_time_tracking: Time tracking
+project_module_news: News
+project_module_documents: Documents
+project_module_files: Files
+project_module_wiki: Wiki
+project_module_repository: Repository
+project_module_boards: Boards
+
+label_user: User
+label_user_plural: Users
+label_user_new: New user
+label_project: Project
+label_project_new: New project
+label_project_plural: Projects
+label_project_all: All Projects
+label_project_latest: Latest projects
+label_issue: Issue
+label_issue_new: New issue
+label_issue_plural: Issues
+label_issue_view_all: View all issues
+label_issues_by: Issues by %s
+label_issue_added: Issue added
+label_issue_updated: Issue updated
+label_document: Document
+label_document_new: New document
+label_document_plural: Documents
+label_document_added: Document added
+label_role: Role
+label_role_plural: Roles
+label_role_new: New role
+label_role_and_permissions: Roles and permissions
+label_member: Member
+label_member_new: New member
+label_member_plural: Members
+label_tracker: Tracker
+label_tracker_plural: Trackers
+label_tracker_new: New tracker
+label_workflow: Workflow
+label_issue_status: Issue status
+label_issue_status_plural: Issue statuses
+label_issue_status_new: New status
+label_issue_category: Issue category
+label_issue_category_plural: Issue categories
+label_issue_category_new: New category
+label_custom_field: Custom field
+label_custom_field_plural: Custom fields
+label_custom_field_new: New custom field
+label_enumerations: Enumerations
+label_enumeration_new: New value
+label_information: Information
+label_information_plural: Information
+label_please_login: Please log in
+label_register: Register
+label_password_lost: Lost password
+label_home: Home
+label_my_page: My page
+label_my_account: My account
+label_my_projects: My projects
+label_administration: Administration
+label_login: Sign in
+label_logout: Sign out
+label_help: Help
+label_reported_issues: Reported issues
+label_assigned_to_me_issues: Issues assigned to me
+label_last_login: Last connection
+label_last_updates: Last updated
+label_last_updates_plural: %d last updated
+label_registered_on: Registered on
+label_activity: Activity
+label_overall_activity: Overall activity
+label_user_activity: "%s's activity"
+label_new: New
+label_logged_as: Logged in as
+label_environment: Environment
+label_authentication: Authentication
+label_auth_source: Authentication mode
+label_auth_source_new: New authentication mode
+label_auth_source_plural: Authentication modes
+label_subproject_plural: Subprojects
+label_and_its_subprojects: %s and its subprojects
+label_min_max_length: Min - Max length
+label_list: List
+label_date: Date
+label_integer: Integer
+label_float: Float
+label_boolean: Boolean
+label_string: Text
+label_text: Long text
+label_attribute: Attribute
+label_attribute_plural: Attributes
+label_download: %d Download
+label_download_plural: %d Downloads
+label_no_data: No data to display
+label_change_status: Change status
+label_history: History
+label_attachment: File
+label_attachment_new: New file
+label_attachment_delete: Delete file
+label_attachment_plural: Files
+label_file_added: File added
+label_report: Report
+label_report_plural: Reports
+label_news: News
+label_news_new: Add news
+label_news_plural: News
+label_news_latest: Latest news
+label_news_view_all: View all news
+label_news_added: News added
+label_change_log: Change log
+label_settings: Settings
+label_overview: Overview
+label_version: Version
+label_version_new: New version
+label_version_plural: Versions
+label_confirmation: Confirmation
+label_export_to: 'Also available in:'
+label_read: Read...
+label_public_projects: Public projects
+label_open_issues: open
+label_open_issues_plural: open
+label_closed_issues: closed
+label_closed_issues_plural: closed
+label_total: Total
+label_permissions: Permissions
+label_current_status: Current status
+label_new_statuses_allowed: New statuses allowed
+label_all: all
+label_none: none
+label_nobody: nobody
+label_next: Next
+label_previous: Previous
+label_used_by: Used by
+label_details: Details
+label_add_note: Add a note
+label_per_page: Per page
+label_calendar: Calendar
+label_months_from: months from
+label_gantt: Gantt
+label_internal: Internal
+label_last_changes: last %d changes
+label_change_view_all: View all changes
+label_personalize_page: Personalize this page
+label_comment: Comment
+label_comment_plural: Comments
+label_comment_add: Add a comment
+label_comment_added: Comment added
+label_comment_delete: Delete comments
+label_query: Custom query
+label_query_plural: Custom queries
+label_query_new: New query
+label_filter_add: Add filter
+label_filter_plural: Filters
+label_equals: is
+label_not_equals: is not
+label_in_less_than: in less than
+label_in_more_than: in more than
+label_in: in
+label_today: today
+label_all_time: all time
+label_yesterday: yesterday
+label_this_week: this week
+label_last_week: last week
+label_last_n_days: last %d days
+label_this_month: this month
+label_last_month: last month
+label_this_year: this year
+label_date_range: Date range
+label_less_than_ago: less than days ago
+label_more_than_ago: more than days ago
+label_ago: days ago
+label_contains: contains
+label_not_contains: doesn't contain
+label_day_plural: days
+label_repository: Repository
+label_repository_plural: Repositories
+label_browse: Browse
+label_modification: %d change
+label_modification_plural: %d changes
+label_revision: Revision
+label_revision_plural: Revisions
+label_associated_revisions: Associated revisions
+label_added: added
+label_modified: modified
+label_copied: copied
+label_renamed: renamed
+label_deleted: deleted
+label_latest_revision: Latest revision
+label_latest_revision_plural: Latest revisions
+label_view_revisions: View revisions
+label_max_size: Maximum size
+label_on: 'on'
+label_sort_highest: Move to top
+label_sort_higher: Move up
+label_sort_lower: Move down
+label_sort_lowest: Move to bottom
+label_roadmap: Roadmap
+label_roadmap_due_in: Due in %s
+label_roadmap_overdue: %s late
+label_roadmap_no_issues: No issues for this version
+label_search: Search
+label_result_plural: Results
+label_all_words: All words
+label_wiki: Wiki
+label_wiki_edit: Wiki edit
+label_wiki_edit_plural: Wiki edits
+label_wiki_page: Wiki page
+label_wiki_page_plural: Wiki pages
+label_index_by_title: Index by title
+label_index_by_date: Index by date
+label_current_version: Current version
+label_preview: Preview
+label_feed_plural: Feeds
+label_changes_details: Details of all changes
+label_issue_tracking: Issue tracking
+label_spent_time: Spent time
+label_f_hour: %.2f hour
+label_f_hour_plural: %.2f hours
+label_time_tracking: Time tracking
+label_change_plural: Changes
+label_statistics: Statistics
+label_commits_per_month: Commits per month
+label_commits_per_author: Commits per author
+label_view_diff: View differences
+label_diff_inline: inline
+label_diff_side_by_side: side by side
+label_options: Options
+label_copy_workflow_from: Copy workflow from
+label_permissions_report: Permissions report
+label_watched_issues: Watched issues
+label_related_issues: Related issues
+label_applied_status: Applied status
+label_loading: Loading...
+label_relation_new: New relation
+label_relation_delete: Delete relation
+label_relates_to: related to
+label_duplicates: duplicates
+label_duplicated_by: duplicated by
+label_blocks: blocks
+label_blocked_by: blocked by
+label_precedes: precedes
+label_follows: follows
+label_end_to_start: end to start
+label_end_to_end: end to end
+label_start_to_start: start to start
+label_start_to_end: start to end
+label_stay_logged_in: Stay logged in
+label_disabled: disabled
+label_show_completed_versions: Show completed versions
+label_me: me
+label_board: Forum
+label_board_new: New forum
+label_board_plural: Forums
+label_topic_plural: Topics
+label_message_plural: Messages
+label_message_last: Last message
+label_message_new: New message
+label_message_posted: Message added
+label_reply_plural: Replies
+label_send_information: Send account information to the user
+label_year: Year
+label_month: Month
+label_week: Week
+label_date_from: From
+label_date_to: To
+label_language_based: Based on user's language
+label_sort_by: Sort by %s
+label_send_test_email: Send a test email
+label_feeds_access_key_created_on: RSS access key created %s ago
+label_module_plural: Modules
+label_added_time_by: Added by %s %s ago
+label_updated_time_by: Updated by %s %s ago
+label_updated_time: Updated %s ago
+label_jump_to_a_project: Jump to a project...
+label_file_plural: Files
+label_changeset_plural: Changesets
+label_default_columns: Default columns
+label_no_change_option: (No change)
+label_bulk_edit_selected_issues: Bulk edit selected issues
+label_theme: Theme
+label_default: Default
+label_search_titles_only: Search titles only
+label_user_mail_option_all: "For any event on all my projects"
+label_user_mail_option_selected: "For any event on the selected projects only..."
+label_user_mail_option_none: "Only for things I watch or I'm involved in"
+label_user_mail_no_self_notified: "I don't want to be notified of changes that I make myself"
+label_registration_activation_by_email: account activation by email
+label_registration_manual_activation: manual account activation
+label_registration_automatic_activation: automatic account activation
+label_display_per_page: 'Per page: %s'
+label_age: Age
+label_change_properties: Change properties
+label_general: General
+label_more: More
+label_scm: SCM
+label_plugins: Plugins
+label_ldap_authentication: LDAP authentication
+label_downloads_abbr: D/L
+label_optional_description: Optional description
+label_add_another_file: Add another file
+label_preferences: Preferences
+label_chronological_order: In chronological order
+label_reverse_chronological_order: In reverse chronological order
+label_planning: Planning
+label_incoming_emails: Incoming emails
+label_generate_key: Generate a key
+label_issue_watchers: Watchers
+label_example: Example
+label_display: Display
+label_children: parent of
+label_parents: child of
+label_view_option_parents_do_not_show: Never
+label_view_option_parents_show_always: Always
+label_view_option_parents_show_and_group: Organize by parent
+label_view_option_show_parents: Show parents
+
+button_login: Login
+button_submit: Submit
+button_save: Save
+button_check_all: Check all
+button_uncheck_all: Uncheck all
+button_delete: Delete
+button_create: Create
+button_create_and_continue: Create and continue
+button_test: Test
+button_edit: Edit
+button_add: Add
+button_change: Change
+button_apply: Apply
+button_clear: Clear
+button_lock: Lock
+button_unlock: Unlock
+button_download: Download
+button_list: List
+button_view: View
+button_move: Move
+button_back: Back
+button_cancel: Cancel
+button_activate: Activate
+button_sort: Sort
+button_log_time: Log time
+button_rollback: Rollback to this version
+button_watch: Watch
+button_unwatch: Unwatch
+button_reply: Reply
+button_archive: Archive
+button_unarchive: Unarchive
+button_reset: Reset
+button_rename: Rename
+button_change_password: Change password
+button_copy: Copy
+button_annotate: Annotate
+button_update: Update
+button_configure: Configure
+button_quote: Quote
+button_add_subissue: Add sub-issue
+
+status_active: active
+status_registered: registered
+status_locked: locked
+
+text_select_mail_notifications: Select actions for which email notifications should be sent.
+text_regexp_info: eg. ^[A-Z0-9]+$
+text_min_max_length_info: 0 means no restriction
+text_project_destroy_confirmation: Are you sure you want to delete this project and related data ?
+text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
+text_workflow_edit: Select a role and a tracker to edit the workflow
+text_are_you_sure: Are you sure ?
+text_journal_changed: changed from %s to %s
+text_journal_set_to: set to %s
+text_journal_deleted: deleted
+text_tip_task_begin_day: task beginning this day
+text_tip_task_end_day: task ending this day
+text_tip_task_begin_end_day: task beginning and ending this day
+text_project_identifier_info: 'Only lower case letters (a-z), numbers and dashes are allowed.
Once saved, the identifier can not be changed.'
+text_caracters_maximum: %d characters maximum.
+text_caracters_minimum: Must be at least %d characters long.
+text_length_between: Length between %d and %d characters.
+text_tracker_no_workflow: No workflow defined for this tracker
+text_unallowed_characters: Unallowed characters
+text_comma_separated: Multiple values allowed (comma separated).
+text_issues_ref_in_commit_messages: Referencing and fixing issues in commit messages
+text_issue_added: Issue %s has been reported by %s.
+text_issue_updated: Issue %s has been updated by %s.
+text_wiki_destroy_confirmation: Are you sure you want to delete this wiki and all its content ?
+text_issue_category_destroy_question: Some issues (%d) are assigned to this category. What do you want to do ?
+text_issue_category_destroy_assignments: Remove category assignments
+text_issue_category_reassign_to: Reassign issues to this category
+text_user_mail_option: "For unselected projects, you will only receive notifications about things you watch or you're involved in (eg. issues you're the author or assignee)."
+text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
+text_load_default_configuration: Load the default configuration
+text_status_changed_by_changeset: Applied in changeset %s.
+text_issues_destroy_confirmation: 'Are you sure you want to delete the selected issue(s) ?'
+text_select_project_modules: 'Select modules to enable for this project:'
+text_default_administrator_account_changed: Default administrator account changed
+text_file_repository_writable: Attachments directory writable
+text_plugin_assets_writable: Plugin assets directory writable
+text_rmagick_available: RMagick available (optional)
+text_destroy_time_entries_question: %.02f hours were reported on the issues you are about to delete. What do you want to do ?
+text_destroy_time_entries: Delete reported hours
+text_assign_time_entries_to_project: Assign reported hours to the project
+text_reassign_time_entries: 'Reassign reported hours to this issue:'
+text_user_wrote: '%s wrote:'
+text_enumeration_destroy_question: '%d objects are assigned to this value.'
+text_enumeration_category_reassign_to: 'Reassign them to this value:'
+text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+text_repository_usernames_mapping: "Select or update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
+text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
+text_custom_field_possible_values_info: 'One line for each value'
+
+default_role_manager: Manager
+default_role_developper: Developer
+default_role_reporter: Reporter
+default_tracker_bug: Bug
+default_tracker_feature: Feature
+default_tracker_support: Support
+default_issue_status_new: New
+default_issue_status_assigned: Assigned
+default_issue_status_resolved: Resolved
+default_issue_status_feedback: Feedback
+default_issue_status_closed: Closed
+default_issue_status_rejected: Rejected
+default_doc_category_user: User documentation
+default_doc_category_tech: Technical documentation
+default_priority_low: Low
+default_priority_normal: Normal
+default_priority_high: High
+default_priority_urgent: Urgent
+default_priority_immediate: Immediate
+default_activity_design: Design
+default_activity_development: Development
+
+enumeration_issue_priorities: Issue priorities
+enumeration_doc_categories: Document categories
+enumeration_activities: Activities (time tracking)
diff --git a/lib/redmine.rb b/lib/redmine.rb
index 5ac32b2..4134f46 100644
--- a/lib/redmine.rb
+++ b/lib/redmine.rb
@@ -35,8 +35,8 @@ Redmine::AccessControl.map do |map|
:queries => :index,
:reports => :issue_report}, :public => true
map.permission :add_issues, {:issues => :new}
- map.permission :edit_issues, {:issues => [:edit, :reply, :bulk_edit]}
- map.permission :manage_issue_relations, {:issue_relations => [:new, :destroy]}
+ map.permission :edit_issues, {:issues => [:edit, :reply, :bulk_edit, :update_subject]}
+ map.permission :manage_issue_relations, {:issue_relations => [:new, :destroy], :issues => :add_subissue}
map.permission :add_issue_notes, {:issues => [:edit, :reply]}
map.permission :edit_issue_notes, {:journals => :edit}, :require => :loggedin
map.permission :edit_own_issue_notes, {:journals => :edit}, :require => :loggedin
diff --git a/lib/redmine/version.rb b/lib/redmine/version.rb
index f51d174..41bdd68 100644
--- a/lib/redmine/version.rb
+++ b/lib/redmine/version.rb
@@ -23,19 +23,30 @@ module Redmine
if entries.match(%r{^\d+})
revision = $1.to_i if entries.match(%r{^\d+\s+dir\s+(\d+)\s})
else
- xml = REXML::Document.new(entries)
- revision = xml.elements['wc-entries'].elements[1].attributes['revision'].to_i
- end
- rescue
- # Could not find the current revision
- end
- end
- revision
+ xml = REXML::Document.new(entries)
+ revision = xml.elements['wc-entries'].elements[1].attributes['revision'].to_i
+ end
+ rescue
+ # Could not find the current revision
+ end
+ end
+ revision
+ end
+
+ def self.warecorp_revision
+ begin
+ tag = %x{ git name-rev --tags `git log -1 --pretty=format:'%H'` }.split[1]
+ tag = ( tag.match %r{tags/(.*)})[1]
+ rescue
+ tag = "undefined"
+ end
+ tag
end
REVISION = self.revision
+ WARECORP_REVISION = self.warecorp_revision
ARRAY = [MAJOR, MINOR, TINY, BRANCH, REVISION].compact
- STRING = ARRAY.join('.')
+ STRING = ARRAY.join('.') + " / #{WARECORP_REVISION}"
def self.to_a; ARRAY end
def self.to_s; STRING end
diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css
index a8d8736..a3e7c9d 100644
--- a/public/stylesheets/application.css
+++ b/public/stylesheets/application.css
@@ -757,3 +757,59 @@ h2 img { vertical-align:middle; }
#content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; overflow: visible !important;}
#wiki_add_attachment { display:none; }
}
+
+/***** Subtasks *****/
+/*blocks composed of icon (+, - or none), project identification (if there is one) and link to the task*/
+.issue-subject-level-1, .issue-subject-level-1 .issue-subject {
+ margin-left: 0em;
+}
+
+.issue-subject-level-2, .issue-subject-level-2 .issue-subject {
+ margin-left: 1em;
+}
+
+.issue-subject-level-3, .issue-subject-level-3 .issue-subject {
+ margin-left: 2em;
+}
+
+.issue-subject-level-4, .issue-subject-level-4 .issue-subject {
+ margin-left: 3em;
+}
+
+.issue-subject-level-5, .issue-subject-level-5 .issue-subject {
+ margin-left: 4em;
+}
+
+.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 {
+ background-image: url(../images/corner-dots.gif);
+ background-repeat: no-repeat;
+ background-position: -3px center;
+}
+
+/* Used to show issues which is not pass by filter, but should by
+ shown as parent for other issue. */
+.issue-unfiltered {
+ opacity: 0.5;
+ filter: alpha(opacity=50);
+}
+
+.expanded-issue {
+ background-image: url(contract.png);
+ background-repeat: no-repeat;
+}
+
+.contracted-issue {
+ background-image: url(expand.png);
+ background-repeat: no-repeat;
+}
+
+.expand-icon, .contract-icon{
+/* position: absolute; */
+ vertical-align: middle;
+}
+
+/*text after the icon, which needs to be indented so that all its lines stay completely after the icon*/
+.issue-subject{
+ padding-left: 1em;
+}
+
diff --git a/test/fixtures/issues.yml b/test/fixtures/issues.yml
index 921ba40..35fa713 100644
--- a/test/fixtures/issues.yml
+++ b/test/fixtures/issues.yml
@@ -15,6 +15,8 @@ issues_001:
status_id: 1
start_date: <%= 1.day.ago.to_date.to_s(:db) %>
due_date: <%= 10.day.from_now.to_date.to_s(:db) %>
+ lft: "13"
+ rgt: "14"
issues_002:
created_on: 2006-07-19 21:04:21 +02:00
project_id: 1
@@ -31,6 +33,8 @@ issues_002:
status_id: 2
start_date: <%= 2.day.ago.to_date.to_s(:db) %>
due_date:
+ lft: "17"
+ rgt: "18"
issues_003:
created_on: 2006-07-19 21:07:27 +02:00
project_id: 1
@@ -47,6 +51,8 @@ issues_003:
status_id: 1
start_date: <%= 1.day.from_now.to_date.to_s(:db) %>
due_date: <%= 40.day.ago.to_date.to_s(:db) %>
+ lft: "15"
+ rgt: "16"
issues_004:
created_on: <%= 5.days.ago.to_date.to_s(:db) %>
project_id: 2
@@ -61,6 +67,8 @@ issues_004:
assigned_to_id:
author_id: 2
status_id: 1
+ lft: "19"
+ rgt: "20"
issues_005:
created_on: <%= 5.days.ago.to_date.to_s(:db) %>
project_id: 3
@@ -75,6 +83,8 @@ issues_005:
assigned_to_id:
author_id: 2
status_id: 1
+ lft: "21"
+ rgt: "22"
issues_006:
created_on: <%= 1.minute.ago.to_date.to_s(:db) %>
project_id: 5
@@ -91,6 +101,8 @@ issues_006:
status_id: 1
start_date: <%= Date.today.to_s(:db) %>
due_date: <%= 1.days.from_now.to_date.to_s(:db) %>
+ lft: "23"
+ rgt: "24"
issues_007:
created_on: <%= 10.days.ago.to_date.to_s(:db) %>
project_id: 1
@@ -108,6 +120,8 @@ issues_007:
start_date: <%= 10.days.ago.to_s(:db) %>
due_date: <%= Date.today.to_s(:db) %>
lock_version: 0
+ lft: "25"
+ rgt: "26"
issues_008:
created_on: <%= 10.days.ago.to_date.to_s(:db) %>
project_id: 1
@@ -125,4 +139,127 @@ issues_008:
start_date:
due_date:
lock_version: 0
-
\ No newline at end of file
+ lft: "27"
+ rgt: "28"
+issues_root:
+ created_on: <%= 10.days.ago.to_date.to_s(:db) %>
+ project_id: 1
+ updated_on: <%= 10.days.ago.to_date.to_s(:db) %>
+ priority_id: 3
+ subject: root
+ id: 9
+ fixed_version_id:
+ category_id:
+ description: This is root of subissues.
+ tracker_id: 1
+ assigned_to_id:
+ author_id: 2
+ status_id: 1
+ start_date:
+ due_date:
+ lock_version: 0
+ lft: "1"
+ rgt: "12"
+ parent_id:
+
+issues_child001:
+ created_on: <%= 10.days.ago.to_date.to_s(:db) %>
+ project_id: 1
+ updated_on: <%= 10.days.ago.to_date.to_s(:db) %>
+ priority_id: 3
+ subject: child001
+ id: 10
+ fixed_version_id:
+ category_id:
+ description: This is child001 of root.
+ tracker_id: 1
+ assigned_to_id:
+ author_id: 2
+ status_id: 1
+ start_date:
+ due_date:
+ lock_version: 0
+ parent_id: 9
+ lft: "2"
+ rgt: "7"
+issues_child002:
+ created_on: <%= 10.days.ago.to_date.to_s(:db) %>
+ project_id: 1
+ updated_on: <%= 10.days.ago.to_date.to_s(:db) %>
+ priority_id: 3
+ subject: child002
+ id: 11
+ fixed_version_id:
+ category_id:
+ description: This is child002 of root.
+ tracker_id: 1
+ assigned_to_id:
+ author_id: 2
+ status_id: 1
+ start_date:
+ due_date:
+ lock_version: 0
+ parent_id: 9
+ lft: "8"
+ rgt: "11"
+issues_subchild001:
+ created_on: <%= 10.days.ago.to_date.to_s(:db) %>
+ project_id: 1
+ updated_on: <%= 10.days.ago.to_date.to_s(:db) %>
+ priority_id: 3
+ subject: subchild001
+ id: 12
+ fixed_version_id:
+ category_id:
+ description: This is subchild001 of child001.
+ tracker_id: 1
+ assigned_to_id:
+ author_id: 2
+ status_id: 1
+ start_date:
+ due_date:
+ lock_version: 0
+ parent_id: 10
+ lft: "3"
+ rgt: "4"
+issues_subchild002:
+ created_on: <%= 10.days.ago.to_date.to_s(:db) %>
+ project_id: 1
+ updated_on: <%= 10.days.ago.to_date.to_s(:db) %>
+ priority_id: 3
+ subject: subchild002
+ id: 13
+ fixed_version_id:
+ category_id:
+ description: This is subchild002 of child001.
+ tracker_id: 1
+ assigned_to_id:
+ author_id: 2
+ status_id: 1
+ start_date:
+ due_date:
+ lock_version: 0
+ parent_id: 10
+ lft: "5"
+ rgt: "6"
+issues_subchild003:
+ created_on: <%= 10.days.ago.to_date.to_s(:db) %>
+ project_id: 1
+ updated_on: <%= 10.days.ago.to_date.to_s(:db) %>
+ priority_id: 3
+ subject: subchild003
+ id: 14
+ fixed_version_id:
+ category_id:
+ description: This is subchild003 of child002.
+ tracker_id: 1
+ assigned_to_id:
+ author_id: 2
+ status_id: 1
+ start_date:
+ due_date:
+ lock_version: 0
+ parent_id: 11
+ lft: "9"
+ rgt: "10"
+
diff --git a/test/fixtures/queries.yml b/test/fixtures/queries.yml
index a274ce3..6405b4c 100644
--- a/test/fixtures/queries.yml
+++ b/test/fixtures/queries.yml
@@ -19,6 +19,10 @@ queries_001:
- "125"
:operator: "="
+ view_options: |
+ ---
+ show_parents: "do_not_show"
+
user_id: 1
column_names:
queries_002:
@@ -37,6 +41,10 @@ queries_002:
- "1"
:operator: o
+ view_options: |
+ ---
+ show_parents: "do_not_show"
+
user_id: 3
column_names:
queries_003:
@@ -51,6 +59,10 @@ queries_003:
- "3"
:operator: "="
+ view_options: |
+ ---
+ show_parents: "do_not_show"
+
user_id: 3
column_names:
queries_004:
@@ -65,6 +77,10 @@ queries_004:
- "3"
:operator: "="
+ view_options: |
+ ---
+ show_parents: "do_not_show"
+
user_id: 2
column_names:
queries_005:
@@ -86,4 +102,7 @@ queries_005:
- desc
- - tracker
- asc
-
\ No newline at end of file
+ view_options: |
+ ---
+ show_parents: "do_not_show"
+
diff --git a/test/fixtures/versions.yml b/test/fixtures/versions.yml
index 62c5e6f..1b8060b 100644
--- a/test/fixtures/versions.yml
+++ b/test/fixtures/versions.yml
@@ -23,4 +23,12 @@ versions_003:
id: 3
description: Future version
effective_date:
+onlinestore_1_0:
+ created_on: 2006-07-19 21:00:33 +02:00
+ name: "1.0"
+ project_id: 2
+ updated_on: 2006-07-19 21:00:33 +02:00
+ id: 4
+ description: Future version
+ effective_date:
\ No newline at end of file
diff --git a/test/functional/issues_controller_test.rb b/test/functional/issues_controller_test.rb
index 0df66ab..581c3c9 100644
--- a/test/functional/issues_controller_test.rb
+++ b/test/functional/issues_controller_test.rb
@@ -22,10 +22,12 @@ require 'issues_controller'
class IssuesController; def rescue_action(e) raise e end; end
class IssuesControllerTest < Test::Unit::TestCase
+
fixtures :projects,
:users,
:roles,
:members,
+ :queries,
:issues,
:issue_statuses,
:versions,
@@ -1035,4 +1037,64 @@ class IssuesControllerTest < Test::Unit::TestCase
assert_equal 2, TimeEntry.find(1).issue_id
assert_equal 2, TimeEntry.find(2).issue_id
end
+
+ def test_new_child_issue
+ child_issue_subject = 'This is the test_new child issue'
+ parent_issue = Issue.find(1)
+ @request.session[:user_id] = 2
+ post :new, :project_id => 1,
+ :issue => {:tracker_id => 3,
+ :subject => child_issue_subject,
+ :description => 'This is the description',
+ :priority_id => 5,
+ :estimated_hours => '',
+ :parent_id => 1,
+ :custom_field_values => {'2' => 'Value for field 2'} }
+
+ child = Issue.find_by_subject( child_issue_subject)
+ assert_redirected_to "issues/#{child.id}"
+ assert child.parent == parent_issue
+ end
+
+ def test_add_subissue_should_redirect_to_action_new
+ @request.session[:user_id] = 2
+ get( :add_subissue, :project_id => 1,
+ :issue => {
+ :tracker_id => 3,
+ :subject => "test_add_subissue",
+ :description => "test_add_subissue",
+ :priority_id => 5,
+ :estimated_hours => '' },
+ :parent_issue_id => 1)
+ assert_redirected_to :action => "new"
+ end
+
+ def test_add_subissue_with_invalid_parent_id_should_render_404
+ @request.session[:user_id] = 2
+ get( :add_subissue, :project_id => 1,
+ :issue => {
+ :tracker_id => 3,
+ :subject => "test_add_subissue",
+ :description => "test_add_subissue",
+ :priority_id => 5,
+ :estimated_hours => ''},
+ :parent_issue_id => 'invalid_id')
+ assert_template 'common/404', :status => 404
+ end
+
+ def test_index_view_option_always_show_parents
+ @request.session[:user_id] = 2
+ get( :index, :project_id => 1,
+ :view_options => { :show_parents => "show_always"})
+ assert_response :success
+ assert_tag( :tag => 'span',
+ :attributes => { :class => 'issue-subject-level-3'},
+ :content => /subchild001/)
+ assert_tag( :tag => 'span',
+ :attributes => { :class => 'issue-subject-level-2'},
+ :content => /child001/)
+ assert_tag( :tag => 'span',
+ :attributes => { :class => 'issue-subject-level-1'},
+ :content => /root/)
+ end
end
diff --git a/test/unit/issue_test.rb b/test/unit/issue_test.rb
index ae3fa5b..3b96e25 100644
--- a/test/unit/issue_test.rb
+++ b/test/unit/issue_test.rb
@@ -22,7 +22,7 @@ class IssueTest < Test::Unit::TestCase
:trackers, :projects_trackers,
:issue_statuses, :issue_categories,
:enumerations,
- :issues,
+ :issues, :versions,
:custom_fields, :custom_fields_projects, :custom_fields_trackers, :custom_values,
:time_entries
@@ -233,7 +233,157 @@ class IssueTest < Test::Unit::TestCase
assert_nil Issue.find_by_id(1)
assert_nil TimeEntry.find_by_issue_id(1)
end
+
+ def test_should_update_target_version_of_parent_issue
+ create_family_of_issues 'Target version of parent updates test'
+
+ version_0_1 = Version.find( versions( :versions_001).id)
+ version_1_0 = Version.find( versions( :versions_002).id)
+
+ # set target version for child
+ @issue2.fixed_version = version_0_1
+ assert @issue2.save
+ assert @issue1.reload.fixed_version == @issue2.fixed_version
+
+ # set target version for child of child larger than target version
+ # of child. so, target version of parents should be updated.
+ @issue3.fixed_version = version_1_0
+ assert @issue3.save
+ assert @issue1.reload.fixed_version == @issue3.fixed_version
+ end
+
+ def test_should_not_allowed_close_parent_issue_while_one_of_children_open
+ create_family_of_issues 'Closing parent issue when some children is open test.'
+
+ closed_status = issue_statuses( :issue_statuses_005)
+ @issue3.status = closed_status
+ assert @issue3.save
+
+ assert_raise ActiveRecord::RecordInvalid do
+ @issue1.reload.status = closed_status
+ @issue1.save!
+ end
+
+ end
+
+ def test_should_change_status_of_parent_when_some_children_is_open
+ create_family_of_issues 'Changing status of parent from closed to open when some of children is open.'
+
+ open_status = issue_statuses( :issue_statuses_001)
+ closed_status = issue_statuses( :issue_statuses_005)
+
+ @issue3.status = closed_status
+ @issue2.status = closed_status
+ @issue1.status = closed_status
+ assert @issue3.save
+ assert @issue2.save
+ assert @issue1.save
+ assert @issue1.reload.closed?
+ assert @issue2.reload.closed?
+ assert @issue3.reload.closed?
+
+ # set status of children to open status. this should update status
+ # of parent and set it to open state.
+ @issue2.status = open_status
+ assert @issue2.save
+ assert !@issue2.reload.closed?
+ assert !@issue1.reload.closed?
+ end
+
+ def test_should_update_targetversion_of_parent_if_children_have_bigger_targetversion
+ create_family_of_issues 'Update target version of parent if children have bigger target version.'
+
+ # set parent version to 1.
+ @issue1.fixed_version = versions( :versions_001)
+ assert @issue1.save
+ assert @issue1.reload.fixed_version == versions( :versions_001)
+
+ # set children to version higher that parent.
+ @issue2.fixed_version = versions( :versions_002)
+ assert @issue2.save
+ assert @issue1.reload.fixed_version == versions( :versions_002)
+ end
+
+ def test_should_set_target_version_of_parent_if_children_have_a_target_version
+ create_family_of_issues 'Update target version of parent if children have a target version.'
+
+ @issue2.fixed_version = versions( :versions_001)
+ assert @issue2.save
+ assert @issue2.reload.fixed_version == versions( :versions_001)
+ assert @issue1.reload.fixed_version == @issue2.fixed_version
+ end
+
+ def test_should_not_allow_to_set_targetversion_of_parent_lower_than_any_of_the_children
+ create_family_of_issues 'Not allowing to set target version of parent lower than any of the children.'
+
+ [ @issue1, @issue2, @issue3 ].each do |issue|
+ issue.update_attribute :fixed_version, versions( :versions_002)
+ end
+
+ assert_raise ActiveRecord::RecordInvalid do
+ @issue1.fixed_version = versions( :versions_001)
+ @issue1.save!
+ end
+ end
+
+ def test_should_not_set_target_version_of_parent_if_child_on_another_project
+ create_family_of_issues 'Should not set target version of parnet if child on another project.'
+
+ @issue2.fixed_version = versions( :versions_001)
+ assert @issue2.save
+ assert @issue1.reload.fixed_version == versions( :versions_001)
+
+ online_store = projects( :projects_002)
+ assert @issue2.move_to( online_store)
+ @issue2.reload.fixed_version = versions( :onlinestore_1_0)
+ assert @issue2.save
+ assert @issue1.reload.fixed_version == versions( :versions_001)
+ assert @issue2.reload.fixed_version == versions( :onlinestore_1_0)
+ end
+
+ def test_should_update_due_to_date_if_target_version_is_set_but_due_to_is_not
+ @issue = Issue.new( :project_id => 1, :tracker_id => 1,
+ :author_id => 1, :status_id => 1,
+ :priority => Enumeration.priorities.first,
+ :subject => 'issue for test hook which set due_to when sets target version.',
+ :description => 'issue for test hook which set due_to when sets target version.')
+
+ assert @issue.save!
+ assert @issue.reload.due_date == nil
+ @issue.fixed_version = versions( :versions_001)
+ assert @issue.save!
+ assert @issue.reload.due_date == @issue.reload.fixed_version.due_date
+ end
+ private
+
+ def create_family_of_issues( subject)
+ # Create 3 issues
+ @issue1 = Issue.new( :project_id => 1, :tracker_id => 1,
+ :author_id => 1, :status_id => 1,
+ :priority => Enumeration.priorities.first,
+ :subject => subject,
+ :description => subject)
+ assert @issue1.save
+ @issue2 = @issue1.clone
+ assert @issue2.save
+ @issue3 = @issue1.clone
+ assert @issue3.save
+
+ # 2 is a child of 1
+ @issue2.move_to_child_of @issue1
+
+ # And 3 is a child of 2
+ @issue3.move_to_child_of @issue2
+
+ @issue1.reload
+ @issue2.reload
+ @issue3.reload
+
+ assert @issue2.parent == @issue1
+ assert @issue2.children.include?( @issue3)
+ end
+
def test_overdue
assert Issue.new(:due_date => 1.day.ago.to_date).overdue?
assert !Issue.new(:due_date => Date.today).overdue?
@@ -249,4 +399,5 @@ class IssueTest < Test::Unit::TestCase
assert issue.save
assert_equal 1, ActionMailer::Base.deliveries.size
end
+
end