Project

General

Profile

Feature #38527 » 38527-v2.patch

Go MAEDA, 2023-05-08 05:16

View differences:

app/models/issue_query.rb
155 155
    ) if project.nil?
156 156
    add_available_filter(
157 157
      "tracker_id",
158
      :type => :list, :values => trackers.collect{|s| [s.name, s.id.to_s]}
158
      :type => :list_with_history, :values => trackers.collect{|s| [s.name, s.id.to_s]}
159 159
    )
160 160
    add_available_filter(
161 161
      "priority_id",
162
      :type => :list, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s]}
162
      :type => :list_with_history, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s]}
163 163
    )
164 164
    add_available_filter(
165 165
      "author_id",
......
167 167
    )
168 168
    add_available_filter(
169 169
      "assigned_to_id",
170
      :type => :list_optional, :values => lambda {assigned_to_values}
170
      :type => :list_optional_with_history, :values => lambda {assigned_to_values}
171 171
    )
172 172
    add_available_filter(
173 173
      "member_of_group",
......
179 179
    )
180 180
    add_available_filter(
181 181
      "fixed_version_id",
182
      :type => :list_optional, :values => lambda {fixed_version_values}
182
      :type => :list_optional_with_history, :values => lambda {fixed_version_values}
183 183
    )
184 184
    add_available_filter(
185 185
      "fixed_version.due_date",
......
194 194
    )
195 195
    add_available_filter(
196 196
      "category_id",
197
      :type => :list_optional,
197
      :type => :list_with_history,
198 198
      :values => lambda {project.issue_categories.collect{|s| [s.name, s.id.to_s]}}
199 199
    ) if project
200 200
    add_available_filter "subject", :type => :text
app/models/query.rb
314 314
    "!p"  => :label_no_issues_in_project,
315 315
    "*o"  => :label_any_open_issues,
316 316
    "!o"  => :label_no_open_issues,
317
    "ev"  => :label_has_ever_been,
318
    "!ev" => :label_has_never_been,
319
    "cf"  => :label_used_to_be  # "cf" stands for "changed from"
317 320
  }
318 321

  
319 322
  class_attribute :operators_by_filter_type
320 323
  self.operators_by_filter_type = {
321 324
    :list => [ "=", "!" ],
322
    :list_status => [ "o", "=", "!", "c", "*" ],
325
    :list_with_history =>  [ "=", "!", "ev", "!ev", "cf" ],
326
    :list_status => [ "o", "=", "!", "ev", "!ev", "cf", "c", "*" ],
323 327
    :list_optional => [ "=", "!", "!*", "*" ],
328
    :list_optional_with_history => [ "=", "!", "ev", "!ev", "cf", "!*", "*" ],
324 329
    :list_subprojects => [ "*", "!*", "=", "!" ],
325 330
    :date => [ "=", ">=", "<=", "><", "<t+", ">t+", "><t+", "t+", "nd", "t", "ld", "nw", "w", "lw", "l2w", "nm", "m", "lm", "y", ">t-", "<t-", "><t-", "t-", "!*", "*" ],
326 331
    :date_past => [ "=", ">=", "<=", "><", ">t-", "<t-", "><t-", "t-", "t", "ld", "w", "lw", "l2w", "m", "lm", "y", "!*", "*" ],
......
1438 1443
      sql = sql_contains("#{db_table}.#{db_field}", value.first, :starts_with => true)
1439 1444
    when "$"
1440 1445
      sql = sql_contains("#{db_table}.#{db_field}", value.first, :ends_with => true)
1446
    when "ev", "!ev", "cf"
1447
      # has ever been / has never been / used to be
1448
      neg = (operator == '!ev' ? 'NOT' : '')
1449
      subquery =
1450
        "SELECT 1 FROM #{Journal.table_name}" +
1451
        " INNER JOIN #{JournalDetail.table_name} ON #{Journal.table_name}.id = #{JournalDetail.table_name}.journal_id" +
1452
        " WHERE (#{Journal.visible_notes_condition(User.current, :skip_pre_condition => true)}" +
1453
        " AND #{Journal.table_name}.journalized_type = 'Issue'" +
1454
        " AND #{Journal.table_name}.journalized_id = #{db_table}.id" +
1455
        " AND #{JournalDetail.table_name}.property = 'attr'" +
1456
        " AND #{JournalDetail.table_name}.prop_key = '#{db_field}'" +
1457
        " AND " + queried_class.send(:sanitize_sql_array, ["#{JournalDetail.table_name}.old_value IN (?)", value]) + ")" +
1458
        (%w[ev !ev].include?(operator) ? " OR " + queried_class.send(:sanitize_sql_array, ["#{db_table}.#{db_field} IN (?)", value]) : "")
1459
      sql = "#{neg} EXISTS (#{subquery})"
1441 1460
    else
1442 1461
      raise QueryError, "Unknown query operator #{operator}"
1443 1462
    end
config/locales/en.yml
818 818
  label_no_issues_in_project: no issues in project
819 819
  label_any_open_issues: any open issues
820 820
  label_no_open_issues: no open issues
821
  label_has_ever_been: has ever been
822
  label_has_never_been: has never been
823
  label_used_to_be: used to be
821 824
  label_day_plural: days
822 825
  label_repository: Repository
823 826
  label_repository_new: New repository
public/javascripts/application.js
180 180

  
181 181
  switch (filterOptions['type']) {
182 182
  case "list":
183
  case "list_with_history":
183 184
  case "list_optional":
185
  case "list_optional_with_history":
184 186
  case "list_status":
185 187
  case "list_subprojects":
186 188
    tr.find('td.values').append(
test/unit/query_test.rb
797 797
    assert_equal [2, 14], result.map(&:id).sort
798 798
  end
799 799

  
800
  def test_operator_changed_from
801
    User.current = User.find(1)
802
    issue1 = Issue.find(2)
803
    issue1.init_journal(User.current)
804
    issue1.update(status_id: 1)  # Assigned (2) -> New
805
    issue2 = Issue.find(8)
806
    issue2.init_journal(User.current)
807
    issue2.update(status_id: 2)  # Closed (5) -> Assigned
808

  
809
    query = IssueQuery.new(
810
      :name => '_',
811
      :filters => {
812
        'status_id' => {
813
          :operator => 'cf',
814
          :values => [2, 5]  # Assigned, Closed
815
        }
816
      }
817
    )
818
    result = find_issues_with_query(query)
819
    assert_equal(
820
      [[2, 'New'], [8, 'Assigned']],
821
      result.sort_by(&:id).map {|issue| [issue.id, issue.status.name]}
822
    )
823
  end
824

  
825
  def test_operator_has_ever_been
826
    User.current = User.find(1)
827
    issue = Issue.find(8)
828
    issue.init_journal(User.current)
829
    issue.update(status_id: 2)  # Closed (5) -> Assigned
830

  
831
    query = IssueQuery.new(
832
      :name => '_',
833
      :filters => {
834
        'status_id' => {
835
          :operator => 'ev',
836
          :values => [5]  # Closed
837
        }
838
      }
839
    )
840
    result = find_issues_with_query(query)
841
    assert_equal(
842
      [[8, 'Assigned'], [11, 'Closed'], [12, 'Closed']],
843
      result.sort_by(&:id).map {|issue| [issue.id, issue.status.name]}
844
    )
845
  end
846

  
847
  def test_operator_has_never_been
848
    User.current = User.find(1)
849
    issue = Issue.find(8)
850
    issue.init_journal(User.current)
851
    issue.update(status_id: 2)  # Closed (5) -> Assigned
852

  
853
    query = IssueQuery.new(
854
      :name => '_',
855
      :filters => {
856
        'status_id' => {
857
          :operator => '!ev',
858
          :values => [5]  # Closed
859
        }
860
      }
861
    )
862
    result = find_issues_with_query(query)
863
    expected = Issue.all.order(:id).ids - [8, 11, 12]
864
    assert_equal expected, result.map(&:id).sort
865
  end
866

  
800 867
  def test_range_for_this_week_with_week_starting_on_monday
801 868
    I18n.locale = :fr
802 869
    assert_equal '1', I18n.t(:general_first_day_of_week)
(4-4/9)