Project

General

Profile

Feature #44015 » 0001-Add-groups_then_users_by_group-to-Assignee-List-Disp.patch

Go MAEDA, 2026-05-02 07:08

View differences:

app/helpers/application_helper.rb
687 687
      s << content_tag('option', "<< #{l(:label_me)} >>", :value => User.current.id)
688 688
    end
689 689

  
690
    involved_principals_html = +''
690
    involved_principals = []
691 691
    # This optgroup is displayed only when editing a single issue
692 692
    if @issue.present? && !@issue.new_record?
693
      involved_principals = [@issue.author, @issue.prior_assigned_to].uniq.compact
694
      involved_principals_html = involved_principals.map do |p|
695
        content_tag('option', p.name, value: p.id, disabled: !collection.include?(p))
696
      end.join
693
      involved_principals =
694
        [@issue.author, @issue.prior_assigned_to].uniq.compact.map do |principal|
695
          [principal, {:disabled => !collection.include?(principal)}]
696
        end
697 697
    end
698 698

  
699
    users_html = +''
700
    groups_html = +''
701
    collection.sort.each do |element|
702
      if option_value_selected?(element, selected) || element.id.to_s == selected
703
        selected_attribute = ' selected="selected"'
704
      end
705
      (element.is_a?(Group) ? groups_html : users_html) <<
706
        %(<option value="#{element.id}"#{selected_attribute}>#{h element.name}</option>)
707
    end
708
    if involved_principals_html.blank? && groups_html.blank?
709
      s << users_html
699
    users, groups = collection.sort.partition {|principal| principal.is_a?(User)}
700
    if involved_principals.blank? && groups.blank?
701
      s << principals_option_tags(users, selected)
710 702
    else
711
      principal_optgroups = case Setting.assignee_dropdown_display_format.to_s
712
                            when 'groups_then_users'
713
                              [
714
                                [l(:label_group_plural), groups_html],
715
                                [l(:label_user_plural), users_html]
716
                              ]
717
                            else
718
                              [
719
                                [l(:label_user_plural), users_html],
720
                                [l(:label_group_plural), groups_html]
721
                              ]
722
                            end
723
      ([[l(:label_involved_principals), involved_principals_html]] + principal_optgroups).each do |label, options_html|
703
      optgroups = [[l(:label_involved_principals), involved_principals]]
704
      optgroups.concat(
705
        case Setting.assignee_dropdown_display_format.to_s
706
        when 'groups_then_users'
707
          [
708
            [l(:label_group_plural), groups],
709
            [l(:label_user_plural), users]
710
          ]
711
        when 'users_by_group'
712
          principal_users_by_group_optgroups_for_select(users, groups)
713
        else
714
          # Default to 'users_then_groups'
715
          [
716
            [l(:label_user_plural), users],
717
            [l(:label_group_plural), groups]
718
          ]
719
        end
720
      )
721

  
722
      optgroups.each do |label, principals|
723
        options_html = principals_option_tags(principals, selected)
724 724
        s << %(<optgroup label="#{h(label)}">#{options_html}</optgroup>) if options_html.present?
725 725
      end
726 726
    end
727 727
    s.html_safe
728 728
  end
729 729

  
730
  # Renders option tags for users and groups, preserving per-option attributes.
731
  def principals_option_tags(principals, selected)
732
    principals.map do |principal, options|
733
      options ||= {}
734
      selected_attribute = %( selected="selected") if option_value_selected?(principal, selected) || principal.id.to_s == selected
735
      disabled_attribute = %( disabled="disabled") if options[:disabled]
736

  
737
      %(<option value="#{principal.id}"#{selected_attribute}#{disabled_attribute}>#{h principal.name}</option>)
738
    end.join
739
  end
740

  
741
  # Builds optgroups that list groups first, then each group's users, then ungrouped users.
742
  def principal_users_by_group_optgroups_for_select(users, groups)
743
    users_by_group_optgroups =
744
      groups.filter_map do |group|
745
        group_user_ids = group.users.ids
746
        group_users = users.select {|user| group_user_ids.include?(user.id)}
747
        [group, group_users] if group_users.present?
748
      end
749

  
750
    users_by_group_ids = users_by_group_optgroups.flat_map {|_, principals| principals.map(&:id)}.uniq
751
    ungrouped_users = users.reject {|user| users_by_group_ids.include?(user.id)}
752

  
753
    [[l(:label_group_plural), groups]] +
754
      users_by_group_optgroups.map {|group, principals| [group.name, principals]} +
755
      [[l(:label_user_plural), ungrouped_users]]
756
  end
757

  
730 758
  def option_tag(name, text, value, selected=nil, options={})
731 759
    content_tag 'option', value, options.merge(:value => value, :selected => (value == selected))
732 760
  end
app/helpers/settings_helper.rb
203 203
  def assignee_dropdown_display_format_options
204 204
    options = [
205 205
      [:label_assignee_dropdown_display_format_users_then_groups, 'users_then_groups'],
206
      [:label_assignee_dropdown_display_format_groups_then_users, 'groups_then_users']
206
      [:label_assignee_dropdown_display_format_groups_then_users, 'groups_then_users'],
207
      [:label_assignee_dropdown_display_format_users_by_group, 'users_by_group']
207 208
    ]
208 209

  
209 210
    options.map {|label, value| [l(label), value.to_s]}
config/locales/en.yml
1175 1175
  label_involved_principals: Author / Previous assignee
1176 1176
  label_assignee_dropdown_display_format_users_then_groups: Users then groups
1177 1177
  label_assignee_dropdown_display_format_groups_then_users: Groups then users
1178
  label_assignee_dropdown_display_format_users_by_group: Users by group
1178 1179
  label_progressbar: Progress bar
1179 1180
  label_oauth_permission_admin: Administrate this Redmine
1180 1181
  label_oauth_admin_access: Administration
test/functional/issues_controller_test.rb
4369 4369
    end
4370 4370
  end
4371 4371

  
4372
  def test_new_should_render_users_by_group_in_assignee_select_when_configured
4373
    project = Project.find(1)
4374
    group_a = Group.find(10)
4375
    group_b = Group.find(11)
4376
    project.members << Member.new(:principal => group_a, :roles => [Role.givable.first])
4377
    project.members << Member.new(:principal => group_b, :roles => [Role.givable.first])
4378

  
4379
    with_settings :issue_group_assignment => '1', :assignee_dropdown_display_format => 'users_by_group' do
4380
      @request.session[:user_id] = 2
4381
      get :new, :params => {:project_id => project.id}
4382
      assert_response :success
4383
    end
4384

  
4385
    assert_select 'select[name=?]', 'issue[assigned_to_id]' do
4386
      assert_select %(optgroup:nth-of-type(1)[label="#{l(:label_group_plural)}"]) do
4387
        assert_select 'option[value="10"]', text: 'A Team'
4388
        assert_select 'option[value="11"]', text: 'B Team'
4389
      end
4390
      assert_select 'optgroup:nth-of-type(2)[label="A Team"]' do
4391
        assert_select 'option[value="8"]', text: 'User Misc'
4392
      end
4393
      assert_select 'optgroup:nth-of-type(3)[label="B Team"]' do
4394
        assert_select 'option[value="8"]', text: 'User Misc'
4395
      end
4396
      assert_select %(optgroup:nth-of-type(4)[label="#{l(:label_user_plural)}"]) do
4397
        assert_select 'option[value="2"]', text: 'John Smith'
4398
        assert_select 'option[value="8"]', 0
4399
      end
4400
    end
4401
  end
4402

  
4372 4403
  def test_post_create_without_start_date_and_default_start_date_is_not_creation_date
4373 4404
    with_settings :default_issue_start_date_to_creation_date  => 0 do
4374 4405
      @request.session[:user_id] = 2
test/helpers/application_helper_test.rb
1956 1956
    end
1957 1957
  end
1958 1958

  
1959
  def test_principals_options_for_select_with_users_and_groups_with_users_by_group
1960
    User.current = nil
1961
    set_language_if_valid 'en'
1962
    principals = [User.find(2), User.find(8), Group.find(11), Group.find(10)]
1963

  
1964
    with_settings :assignee_dropdown_display_format => 'users_by_group' do
1965
      result = principals_options_for_select(principals)
1966

  
1967
      assert_select_in result, 'optgroup:nth-of-type(1)[label="Groups"]' do
1968
        assert_select 'option[value="10"]', text: 'A Team'
1969
        assert_select 'option[value="11"]', text: 'B Team'
1970
      end
1971
      assert_select_in result, 'optgroup:nth-of-type(2)[label="A Team"]' do
1972
        assert_select 'option[value="8"]', text: 'User Misc'
1973
      end
1974
      assert_select_in result, 'optgroup:nth-of-type(3)[label="B Team"]' do
1975
        assert_select 'option[value="8"]', text: 'User Misc'
1976
      end
1977
      assert_select_in result, 'optgroup:nth-of-type(4)[label="Users"]' do
1978
        assert_select 'option[value="2"]', text: 'John Smith'
1979
        assert_select 'option[value="8"]', 0
1980
      end
1981
    end
1982
  end
1983

  
1959 1984
  def test_principals_options_for_select_with_empty_collection
1960 1985
    assert_equal '', principals_options_for_select([])
1961 1986
  end
(3-3/3)