186 |
end |
end |
187 |
|
|
188 |
def remove_project(project) |
def remove_project(project) |
189 |
q("DELETE FROM projects WHERE name = '#{e(project)}'") |
begin |
190 |
q("DROP TABLE #{e(bug_table(project))}, \ |
q("LOCK TABLES projects WRITE, \ |
191 |
#{e(attachment_table(project))}, \ |
#{e(bug_table(project))} WRITE, \ |
192 |
#{e(followup_table(project))}, \ |
#{e(attachment_table(project))} WRITE, \ |
193 |
#{e(sf_table(project))}, \ |
#{e(followup_table(project))} WRITE, \ |
194 |
#{e(tf_table(project))}, \ |
#{e(sf_table(project))} WRITE, \ |
195 |
#{e(admins_table(project))}, \ |
#{e(tf_table(project))} WRITE, \ |
196 |
#{e(lists_table(project))}") |
#{e(admins_table(project))} WRITE, \ |
197 |
|
#{e(lists_table(project))} WRITE") |
198 |
|
q("DELETE FROM projects WHERE name = '#{e(project)}'") |
199 |
|
q("DROP TABLE #{e(bug_table(project))}, \ |
200 |
|
#{e(attachment_table(project))}, \ |
201 |
|
#{e(followup_table(project))}, \ |
202 |
|
#{e(sf_table(project))}, \ |
203 |
|
#{e(tf_table(project))}, \ |
204 |
|
#{e(admins_table(project))}, \ |
205 |
|
#{e(lists_table(project))}") |
206 |
|
ensure |
207 |
|
q('UNLOCK TABLES') |
208 |
|
end |
209 |
|
end |
210 |
|
|
211 |
|
def config_project(project, actions) |
212 |
|
begin |
213 |
|
lock(project, false) |
214 |
|
|
215 |
|
# First, make sure that no modification was done after the requst, |
216 |
|
# and no conflict exists. |
217 |
|
actions.each do |action| |
218 |
|
case action |
219 |
|
when ChangeAdminsAction |
220 |
|
if admins(project) != action.old_admins |
221 |
|
raise MalformedDataError, |
222 |
|
"admins were modified after the request" |
223 |
|
end |
224 |
|
when ChangeListsAction |
225 |
|
if lists(project) != action.old_lists |
226 |
|
raise MalformedDataError, |
227 |
|
"lists were modified after the request" |
228 |
|
end |
229 |
|
when ChangeFieldAction |
230 |
|
case action.old_field |
231 |
|
when SelectionField |
232 |
|
f = selection_field(project, action.old_field.name) |
233 |
|
if f.nil? |
234 |
|
raise MalformedDataError, |
235 |
|
"the selection field `#{action.old_field.name}' doesn't exist any longer" |
236 |
|
elsif f != action.old_field |
237 |
|
raise MalformedDataError, |
238 |
|
"the selection field `#{action.old_field.name}' was modified after the request" |
239 |
|
end |
240 |
|
when TextField |
241 |
|
f = text_field(project, action.old_field.name) |
242 |
|
if f.nil? |
243 |
|
raise MalformedDataError, |
244 |
|
"the text field `#{action.old_field.name}' doesn't exist any longer" |
245 |
|
elsif f != action.old_field |
246 |
|
raise MalformedDataError, |
247 |
|
"the text field `#{action.old_field.name}' was modified after the request" |
248 |
|
end |
249 |
|
else |
250 |
|
raise MalformedDataError, |
251 |
|
"unknown field object `#{action.old_field.inspect}'" |
252 |
|
end |
253 |
|
when AddFieldAction |
254 |
|
if selection_field?(project, action.field.name) or |
255 |
|
text_field?(project, action.field.name) |
256 |
|
"the field `#{action.field.name}' is already present" |
257 |
|
end |
258 |
|
when RemoveFieldAction |
259 |
|
case action.field |
260 |
|
when SelectionField |
261 |
|
f = selection_field(project, action.field.name) |
262 |
|
if f.nil? |
263 |
|
raise MalformdDataError, |
264 |
|
"the selection field `#{action.field.name}' doesn't exist any longer" |
265 |
|
elsif f != action.field |
266 |
|
raise MalformedDataError, |
267 |
|
"the selection field `#{action.field.name}' was modified after the request" |
268 |
|
end |
269 |
|
when TextField |
270 |
|
f = text_field(project, action.field.name) |
271 |
|
if f.nil? |
272 |
|
raise MalformedDataError, |
273 |
|
"the text field `#{action.field.name}' doesn't exist any longer" |
274 |
|
elsif f != action.field |
275 |
|
raise MalformedDataError, |
276 |
|
"the text field `#{action.field.name}' was modified after the request" |
277 |
|
end |
278 |
|
else |
279 |
|
raise MalformedDataError, |
280 |
|
"unknown field object `#{action.field.inspect}'" |
281 |
|
end |
282 |
|
end |
283 |
|
end |
284 |
|
|
285 |
|
# Last, commit the changes. |
286 |
|
actions.each do |action| |
287 |
|
case action |
288 |
|
when ChangeAdminsAction |
289 |
|
removed = action.old_admins - action.new_admins |
290 |
|
added = action.new_admins - action.old_admins |
291 |
|
unless removed.empty? |
292 |
|
q("DELETE FROM #{e(admins_table(project))} WHERE address IN (" + |
293 |
|
removed.collect {|a| "'#{e(a)}'"}.join(',') + |
294 |
|
")") |
295 |
|
end |
296 |
|
unless added.empty? |
297 |
|
q("INSERT INTO #{e(admins_table(project))} VALUES (" + |
298 |
|
added.collect {|a| "'#{e(a)}'"}.join('),(') + |
299 |
|
")") |
300 |
|
end |
301 |
|
when ChangeListsAction |
302 |
|
removed = action.old_lists - action.new_lists |
303 |
|
added = action.new_lists - action.old_lists |
304 |
|
unless removed.empty? |
305 |
|
q("DELETE FROM #{e(lists_table(project))} WHERE address IN (" + |
306 |
|
removed.collect {|a| "'#{e(a)}'"}.join(',') + |
307 |
|
")") |
308 |
|
end |
309 |
|
unless added.empty? |
310 |
|
q("INSERT INTO #{e(lists_table(project))} VALUES (" + |
311 |
|
added.collect {|a| "'#{e(a)}'"}.join('),(') + |
312 |
|
")") |
313 |
|
end |
314 |
|
when ChangeFieldAction |
315 |
|
f = action.new_field |
316 |
|
case f |
317 |
|
when SelectionField |
318 |
|
keywords = f.keywords.join(',') |
319 |
|
q("UPDATE #{e(sf_table(project))} SET \ |
320 |
|
display_name='#{e(f.display_name)}', \ |
321 |
|
note='#{e(f.note)}', \ |
322 |
|
admin_only=#{if f.admin_only? then 1 else 0 end}, \ |
323 |
|
searchable=#{if f.searchable? then 1 else 0 end}, \ |
324 |
|
constant=#{if f.constant? then 1 else 0 end}, \ |
325 |
|
keywords='#{e(keywords)}', \ |
326 |
|
always_set=#{if f.always_set? then 1 else 0 end} \ |
327 |
|
WHERE name = '#{e(f.name)}'") |
328 |
|
when TextField |
329 |
|
q("UPDATE #{e(tf_table(project))} SET \ |
330 |
|
display_name='#{e(f.display_name)}', \ |
331 |
|
note='#{e(f.note)}', \ |
332 |
|
admin_only=#{if f.admin_only? then 1 else 0 end}, \ |
333 |
|
searchable=#{if f.searchable? then 1 else 0 end}, \ |
334 |
|
constant=#{if f.constant? then 1 else 0 end}, \ |
335 |
|
can_be_empty=#{if f.can_be_empty? then 1 else 0 end}, \ |
336 |
|
multiple_lines=#{if f.multiple_lines? then 1 else 0 end}, \ |
337 |
|
test_re='#{e(f.regexp)}' \ |
338 |
|
WHERE name = '#{e(f.name)}'") |
339 |
|
if action.old_field.multiple_lines != f.multiple_lines |
340 |
|
q("ALTER TABLE #{e(bug_table(project))} MODIFY \ |
341 |
|
tf_#{e(f.name)} " + |
342 |
|
if f.multiple_lines? then 'MEDIUMTEXT' else 'TINYTEXT' end + |
343 |
|
' NOT NULL') |
344 |
|
end |
345 |
|
end |
346 |
|
when AddFieldAction |
347 |
|
f = action.field |
348 |
|
case f |
349 |
|
when SelectionField |
350 |
|
keywords = f.keywords.join(',') |
351 |
|
q("INSERT INTO #{e(sf_table(project))} VALUES ( \ |
352 |
|
'#{e(f.name)}', '#{e(f.display_name)}', '#{e(f.note)}', \ |
353 |
|
#{if f.admin_only? then 1 else 0 end}, \ |
354 |
|
#{if f.searchable? then 1 else 0 end}, \ |
355 |
|
#{if f.constant? then 1 else 0 end}, \ |
356 |
|
#{if f.required? then 1 else 0 end}, \ |
357 |
|
'#{e(keywords)}', \ |
358 |
|
#{if f.always_set? then 1 else 0 end}, \ |
359 |
|
0 \ |
360 |
|
)") |
361 |
|
q("ALTER TABLE #{e(bug_table(project))} ADD \ |
362 |
|
sf_#{e(f.name)} TINYTEXT NOT NULL") |
363 |
|
else |
364 |
|
q("INSERT INTO #{e(tf_table(project))} VALUES ( \ |
365 |
|
'#{e(f.name)}', '#{e(f.display_name)}', '#{e(f.note)}', \ |
366 |
|
#{if f.admin_only? then 1 else 0 end}, \ |
367 |
|
#{if f.searchable? then 1 else 0 end}, \ |
368 |
|
#{if f.constant? then 1 else 0 end}, \ |
369 |
|
#{if f.required? then 1 else 0 end}, \ |
370 |
|
#{if f.can_be_empty? then 1 else 0 end}, \ |
371 |
|
#{if f.multiple_lines? then 1 else 0 end}, \ |
372 |
|
'#{e(f.regexp)}', \ |
373 |
|
#{if f.searched_by_default? then 1 else 0 end}, \ |
374 |
|
0 \ |
375 |
|
)") |
376 |
|
q("ALTER TABLE #{e(bug_table(project))} ADD \ |
377 |
|
tf_#{e(f.name)} " + |
378 |
|
if f.multiple_lines? then 'MEDIUMTEXT' else 'TINYTEXT' end + |
379 |
|
' NOT NULL') |
380 |
|
end |
381 |
|
when RemoveFieldAction |
382 |
|
f = action.field |
383 |
|
case f |
384 |
|
when SelectionField |
385 |
|
q("DELETE FROM #{e(sf_table(project))} WHERE \ |
386 |
|
name = '#{e(f.name)}'") |
387 |
|
q("ALTER TABLE #{e(bug_table(project))} DROP sf_#{e(f.name)}") |
388 |
|
when TextField |
389 |
|
q("DELETE FROM #{e(tf_table(project))} WHERE \ |
390 |
|
name = '#{e(f.name)}'") |
391 |
|
q("ALTER TABLE #{e(bug_table(project))} DROP tf_#{e(f.name)}") |
392 |
|
end |
393 |
|
end |
394 |
|
end |
395 |
|
ensure |
396 |
|
unlock() |
397 |
|
end |
398 |
end |
end |
399 |
|
|
400 |
public |
public |
401 |
|
|
402 |
def lock(project = nil, read_only = true) |
def lock(project = nil, read_only = true) |
403 |
query = 'LOCK TABLES ' |
query = 'LOCK TABLES ' |
404 |
if project or read_only |
if read_only |
405 |
query += 'projects READ, confirmations READ' |
query += 'projects READ, confirmations READ' |
406 |
else |
else |
407 |
query += 'projects WRITE, confirmations WRITE' |
query += 'projects WRITE, confirmations WRITE' |
465 |
add_project(action.project, action.admins, action.lists) |
add_project(action.project, action.admins, action.lists) |
466 |
when RemoveProjectAction |
when RemoveProjectAction |
467 |
remove_project(action.project) |
remove_project(action.project) |
468 |
|
when ConfigProjectAction |
469 |
|
config_project(action.project, action.actions) |
470 |
else |
else |
471 |
raise MalformedDataError, "unknown action `#{action.inspect}'" |
raise MalformedDataError, "unknown action `#{action.inspect}'" |
472 |
end |
end |
533 |
rand(100) |
rand(100) |
534 |
end |
end |
535 |
|
|
536 |
|
def selection_field(project, name) |
537 |
|
q("SELECT * FROM #{e(sf_table(project))} WHERE name = '#{e(name)}'") |
538 |
|
a = r().fetch_row() |
539 |
|
return nil if a.nil? |
540 |
|
SelectionField.new(a[0], a[1], a[2], |
541 |
|
a[3].to_i == 1, a[4].to_i == 1, |
542 |
|
a[5].to_i == 1, a[6].to_i == 1, |
543 |
|
a[7].split(/\s*,\s*/), a[8].to_i == 1) |
544 |
|
end |
545 |
|
|
546 |
|
def selection_field?(project, name) |
547 |
|
q("SELECT * FROM #{e(sf_table(project))} WHERE name = '#{e(name)}'") |
548 |
|
case r().num_rows() |
549 |
|
when 0 |
550 |
|
false |
551 |
|
when 1 |
552 |
|
true |
553 |
|
else |
554 |
|
raise MalformedDataError, "the selection field `#{name}' is duplicated" |
555 |
|
end |
556 |
|
end |
557 |
|
|
558 |
def selection_fields(project) |
def selection_fields(project) |
559 |
q("SELECT * FROM #{e(sf_table(project))} ORDER BY priority") |
q("SELECT * FROM #{e(sf_table(project))} ORDER BY priority") |
560 |
r().collect do |a| |
r().collect do |a| |
565 |
end |
end |
566 |
end |
end |
567 |
|
|
568 |
|
def text_field(project, name) |
569 |
|
q("SELECT * FROM #{e(tf_table(project))} WHERE name = '#{e(name)}'") |
570 |
|
a = r().fetch_row() |
571 |
|
return nil if a.nil? |
572 |
|
TextField.new(a[0], a[1], a[2], |
573 |
|
a[3].to_i == 1, a[4].to_i == 1, |
574 |
|
a[5].to_i == 1, a[6].to_i == 1, |
575 |
|
a[7].to_i == 1, a[8].to_i == 1, a[9], a[10].to_i == 1) |
576 |
|
end |
577 |
|
|
578 |
|
def text_field?(project, name) |
579 |
|
q("SELECT * FROM #{e(tf_table(project))} WHERE name = '#{e(name)}'") |
580 |
|
case r().num_rows() |
581 |
|
when 0 |
582 |
|
false |
583 |
|
when 1 |
584 |
|
true |
585 |
|
else |
586 |
|
raise MalformedDataError, "the text field `#{name}' is duplicated" |
587 |
|
end |
588 |
|
end |
589 |
|
|
590 |
def text_fields(project) |
def text_fields(project) |
591 |
q("SELECT * FROM #{e(tf_table(project))} ORDER BY priority") |
q("SELECT * FROM #{e(tf_table(project))} ORDER BY priority") |
592 |
r().collect do |a| |
r().collect do |a| |