Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 491
- Log:
Jan 2024 bug tracker updates
- Author:
- rool
- Date:
- Sun Jan 28 09:33:07 +0000 2024
- Size:
- 5276 Bytes
1 | class Ticket < ActiveRecord::Base |
2 | belongs_to :milestone |
3 | belongs_to :part |
4 | belongs_to :severity |
5 | belongs_to :status |
6 | belongs_to :release |
7 | has_many :ticket_changes, :order => 'created_at', :dependent => true |
8 | |
9 | attr_protected :author |
10 | |
11 | def before_save |
12 | self.status ||= Status.find(1) |
13 | self.severity ||= Severity.find(1) |
14 | end |
15 | |
16 | # Overriding save to allow for creating the TicketChange#log if we're |
17 | # editing a ticket |
18 | # Returns the normal save if +params+ is nil or self.new_record=true |
19 | def save(params=nil) |
20 | return super() if self.new_record? || params.nil? |
21 | self.attributes = params[:ticket] |
22 | return false unless super() |
23 | |
24 | change = TicketChange.new(params[:change]) |
25 | |
26 | self.ticket_changes << change |
27 | change.log = @log |
28 | change.attach(params[:change][:attachment]) unless params[:change][:attachment].blank? |
29 | |
30 | if change.empty? |
31 | self.errors.add_to_base 'No changes has been made' |
32 | self.ticket_changes.delete(change) |
33 | change.destroy |
34 | return nil |
35 | else |
36 | change.save |
37 | end |
38 | end |
39 | |
40 | def next |
41 | Ticket.find(:first, :conditions => ['id > ?', id], :order =>'id ASC') |
42 | end |
43 | |
44 | def previous |
45 | Ticket.find(:first, :conditions => ['id < ?', id], :order => 'id desc') |
46 | end |
47 | |
48 | class << self |
49 | # Returns am array of "normalized" hashes, useful for mixing display of search result from |
50 | # other resources (token finder code based on things found in Typo) |
51 | def search(query) |
52 | if !query.to_s.strip.empty? |
53 | tokens = query.split.collect {|c| "%#{c.downcase}%"} |
54 | findings = find( :all, |
55 | :conditions => [(["(LOWER(summary) LIKE ? OR LOWER(content) LIKE ?)"] * tokens.size).join(" AND "), |
56 | *tokens.collect { |token| [token] * 2 }.flatten], |
57 | :order => 'created_at DESC') |
58 | findings.collect do |f| |
59 | { |
60 | :title => f.summary, |
61 | :content => f.content, |
62 | :link => { :controller => '/tickets', :action => 'show', :id => f.id }, |
63 | :status => (f.status.name rescue 'Unknown') |
64 | } |
65 | end |
66 | else |
67 | [] |
68 | end |
69 | end |
70 | |
71 | # Find a bunch of Tickets from a hash of SQL-like fragments. Silently discards |
72 | # everything we don't want. Accepts +order_by+, +direction+ and +limit+ to limit/order |
73 | # the results (eg from a table sort etc) |
74 | def find_by_filter(params = nil, order_by = 'created_at desc', limit = '') |
75 | filters = [] |
76 | good_fields = %w{milestone part severity release status} |
77 | params.each do |field, value| |
78 | operator = ' = ' |
79 | if good_fields.include? field |
80 | if field == 'status' && (value.to_i) == -1 |
81 | operator = ' >= ' |
82 | value = 2 |
83 | end |
84 | filters << "tickets.#{field}_id" + operator + sanitize(value.to_i) |
85 | end |
86 | end |
87 | filters = 'WHERE ' + filters.join(' AND ') unless filters.empty? |
88 | |
89 | #order_by = 'created_at' unless %w{created_at name id}.include? order_by |
90 | direction = 'DESC' unless %w{ASC DESC}.include? direction |
91 | |
92 | find_by_sql %{SELECT tickets.*, |
93 | status.name AS status_name, |
94 | severities.name AS severity_name, |
95 | parts.name AS part_name, |
96 | milestones.name AS milestone_name, |
97 | releases.name AS release_name |
98 | FROM tickets |
99 | LEFT OUTER JOIN severities ON severities.id = tickets.severity_id |
100 | LEFT OUTER JOIN status ON status.id = tickets.status_id |
101 | LEFT OUTER JOIN parts ON parts.id = tickets.part_id |
102 | LEFT OUTER JOIN milestones ON milestones.id = tickets.milestone_id |
103 | LEFT OUTER JOIN releases ON releases.id = tickets.release_id |
104 | #{filters.to_s unless filters.empty?} |
105 | ORDER BY tickets.#{order_by} } |
106 | end |
107 | end |
108 | |
109 | protected |
110 | validates_presence_of :author, :summary, :content |
111 | |
112 | LOG_MAP = { |
113 | #'assigned_user_id' => ['Assigned', 'Unspecified', lambda{|v| User.find(v).username if v > 0}], |
114 | 'part_id' => ['Part', 'Unspecified', lambda{|v| Part.find(v).name if v > 0}], |
115 | 'release_id' => ['Release', 'Unspecified', lambda{|v| Release.find(v).name if v > 0}], |
116 | 'severity_id' => ['Severity', nil, lambda{|v| Severity.find(v).name if v > 0}], |
117 | 'status_id' => ['Status', nil, lambda{|v| Status.find(v).name if v > 0}], |
118 | 'milestone_id' => ['Milestone', 'Unspecified', lambda{|v| Milestone.find(v).name if v > 0}], |
119 | 'summary' => ['Summary', nil, lambda{|v| v unless v.empty?}], |
120 | } |
121 | |
122 | # This cool write_attribute override is courtesy of Kent Sibilev |
123 | def write_attribute(name, value) |
124 | @log ||= {} |
125 | |
126 | if converter = LOG_MAP[name] |
127 | old_value = read_attribute(name) |
128 | |
129 | column = column_for_attribute(name) |
130 | if column |
131 | value = column.type_cast(value) rescue value |
132 | end |
133 | |
134 | loader = lambda{ |v| |
135 | result = converter[2][v] if v |
136 | result = converter[1] unless result |
137 | result |
138 | } |
139 | |
140 | if old_value != value |
141 | @log[converter[0]] = [loader[old_value], loader[value]] |
142 | end |
143 | end |
144 | |
145 | super |
146 | end |
147 | end |