Revision 3cae447e
Von Moritz Bunkus vor mehr als 10 Jahren hinzugefügt
SL/Controller/RequirementSpecItem.pm | ||
---|---|---|
9 | 9 |
use List::Util qw(first); |
10 | 10 |
use Time::HiRes (); |
11 | 11 |
|
12 |
use SL::Clipboard; |
|
12 | 13 |
use SL::DB::RequirementSpec; |
13 | 14 |
use SL::DB::RequirementSpecComplexity; |
14 | 15 |
use SL::DB::RequirementSpecItem; |
... | ... | |
19 | 20 |
|
20 | 21 |
use Rose::Object::MakeMethods::Generic |
21 | 22 |
( |
22 |
scalar => [ qw(item visible_item visible_section) ], |
|
23 |
scalar => [ qw(item visible_item visible_section clicked_item sections) ],
|
|
23 | 24 |
'scalar --get_set_init' => [ qw(complexities risks) ], |
24 | 25 |
); |
25 | 26 |
|
26 |
__PACKAGE__->run_before('load_requirement_spec_item', only => [ qw(dragged_and_dropped ajax_update ajax_edit ajax_delete ajax_flag) ]); |
|
27 |
__PACKAGE__->run_before('load_requirement_spec_item', only => [ qw(dragged_and_dropped ajax_update ajax_edit ajax_delete ajax_flag ajax_copy) ]);
|
|
27 | 28 |
__PACKAGE__->run_before('init_visible_section'); |
28 | 29 |
|
29 | 30 |
# |
... | ... | |
52 | 53 |
$self->render($js); |
53 | 54 |
} |
54 | 55 |
|
56 |
sub insert_new_item_in_section_view { |
|
57 |
my ($self, $js) = @_; |
|
58 |
|
|
59 |
$js->hide('#section-list-empty'); |
|
60 |
|
|
61 |
my $new_type = $self->item->item_type; |
|
62 |
my $id_prefix = $new_type eq 'sub-function-block' ? 'sub-' : ''; |
|
63 |
my $template = 'requirement_spec_item/_' . (apply { s/-/_/g; $_ } $new_type); |
|
64 |
my $html = "" . $self->render($template, { output => 0 }, requirement_spec_item => $self->item); |
|
65 |
my $next_item = $self->item->get_next_in_list; |
|
66 |
|
|
67 |
if ($next_item) { |
|
68 |
$js->insertBefore($html, '#' . $id_prefix . 'function-block-' . $next_item->id); |
|
69 |
} else { |
|
70 |
my $parent_is_section = $self->item->parent->item_type eq 'section'; |
|
71 |
$js->appendTo($html, $parent_is_section ? '#section-list' : '#sub-function-block-container-' . $self->item->parent_id); |
|
72 |
$js->show('#sub-function-block-container-' . $self->item->parent_id) if !$parent_is_section; |
|
73 |
} |
|
74 |
|
|
75 |
$self->replace_bottom($js, $self->item->parent) if $new_type eq 'sub-function-block'; |
|
76 |
} |
|
77 |
|
|
55 | 78 |
sub action_dragged_and_dropped { |
56 | 79 |
my ($self) = @_; |
57 | 80 |
|
... | ... | |
105 | 128 |
} |
106 | 129 |
|
107 | 130 |
if ($old_visible_section->id == $new_section->id) { |
108 |
$js->hide('#section-list-empty'); |
|
109 |
|
|
110 |
my $id_prefix = $new_type eq 'sub-function-block' ? 'sub-' : ''; |
|
111 |
my $template = 'requirement_spec_item/_' . (apply { s/-/_/g; $_ } $new_type); |
|
112 |
my $html = "" . $self->render($template, { output => 0 }, requirement_spec_item => $self->item); |
|
113 |
my $next_item = $self->item->get_next_in_list; |
|
114 |
|
|
115 |
if ($next_item) { |
|
116 |
$js->insertBefore($html, '#' . $id_prefix . 'function-block-' . $next_item->id); |
|
117 |
} else { |
|
118 |
my $parent_is_section = $self->item->parent->item_type eq 'section'; |
|
119 |
$js->appendTo($html, $parent_is_section ? '#section-list' : '#sub-function-block-container-' . $self->item->parent_id); |
|
120 |
$js->show('#sub-function-block-container-' . $self->item->parent_id) if !$parent_is_section; |
|
121 |
} |
|
122 |
|
|
123 |
$self->replace_bottom($js, $self->item->parent) if $new_type eq 'sub-function-block'; |
|
131 |
$self->insert_new_item_in_section_view($js); |
|
124 | 132 |
} |
125 | 133 |
|
126 | 134 |
# $::lxdebug->dump(0, "js", $js->to_array); |
... | ... | |
203 | 211 |
|
204 | 212 |
my $js = SL::ClientJS->new; |
205 | 213 |
|
206 |
if (!$self->visible_section || ($self->visible_section->id != $self->item->section->id)) {
|
|
214 |
if (!$self->is_item_visible) {
|
|
207 | 215 |
# Show section/item to edit if it is not visible. |
208 | 216 |
|
209 | 217 |
my $html = $self->render('requirement_spec_item/_section', { output => 0 }, requirement_spec_item => $self->item); |
... | ... | |
315 | 323 |
->val('#current_content_id', '') |
316 | 324 |
} |
317 | 325 |
|
318 |
} elsif ($self->visible_section && ($self->visible_section->id == $self->item->section->id)) {
|
|
326 |
} elsif ($self->is_item_visible) {
|
|
319 | 327 |
# Item in currently visible section is deleted. |
320 | 328 |
|
321 | 329 |
my $type = $self->item->item_type; |
... | ... | |
342 | 350 |
|
343 | 351 |
$self->item->update_attributes(is_flagged => !$self->item->is_flagged); |
344 | 352 |
|
345 |
my $is_visible = $self->visible_section && ($self->visible_section->id == $self->item->section->id); |
|
346 |
|
|
347 | 353 |
SL::ClientJS->new |
348 |
->action_if($is_visible, 'toggleClass', '#' . $self->item->item_type . '-' . $self->item->id, 'flagged')
|
|
354 |
->action_if($self->is_item_visible, 'toggleClass', '#' . $self->item->item_type . '-' . $self->item->id, 'flagged')
|
|
349 | 355 |
->toggleClass('#fb-' . $self->item->id, 'flagged') |
350 | 356 |
->render($self); |
351 | 357 |
} |
352 | 358 |
|
359 |
sub action_ajax_copy { |
|
360 |
my ($self, %params) = @_; |
|
361 |
|
|
362 |
SL::Clipboard->new->copy($self->item); |
|
363 |
SL::ClientJS->new->render($self); |
|
364 |
} |
|
365 |
|
|
366 |
sub determine_paste_position { |
|
367 |
my ($self) = @_; |
|
368 |
|
|
369 |
if ($self->item->item_type eq 'section') { |
|
370 |
# Sections are always pasted either directly after the |
|
371 |
# clicked-upon section or at the very end. |
|
372 |
return $self->clicked_item ? (undef, $self->clicked_item->section->id) : (); |
|
373 |
|
|
374 |
} elsif ($self->item->item_type eq 'function-block') { |
|
375 |
# A function block: |
|
376 |
# - paste on section list: insert into last section as last element |
|
377 |
# - paste on section: insert into that section as last element |
|
378 |
# - paste on function block: insert after clicked-upon element |
|
379 |
# - paste on sub function block: insert after parent function block of clicked-upon element |
|
380 |
return !$self->clicked_item ? ( $self->sections->[-1]->id, undef ) |
|
381 |
: $self->clicked_item->item_type eq 'section' ? ( $self->clicked_item->id, undef ) |
|
382 |
: $self->clicked_item->item_type eq 'function-block' ? ( $self->clicked_item->parent_id, $self->clicked_item->id ) |
|
383 |
: ( $self->clicked_item->parent->parent_id, $self->clicked_item->parent_id ); |
|
384 |
|
|
385 |
} else { # sub-function-block |
|
386 |
# A sub function block: |
|
387 |
# - paste on section list: promote to function block and insert into last section as last element |
|
388 |
# - paste on section: promote to function block and insert into that section as last element |
|
389 |
# - paste on function block: insert as last element in clicked-upon element |
|
390 |
# - paste on sub function block: insert after clicked-upon element |
|
391 |
|
|
392 |
# Promote sub function blocks to function blocks when pasting on a |
|
393 |
# section or the section list. |
|
394 |
$self->item->item_type('function-block') if !$self->clicked_item || ($self->clicked_item->item_type eq 'section'); |
|
395 |
|
|
396 |
return !$self->clicked_item ? ( $self->sections->[-1]->id, undef ) |
|
397 |
: $self->clicked_item->item_type eq 'section' ? ( $self->clicked_item->id, undef ) |
|
398 |
: $self->clicked_item->item_type eq 'function-block' ? ( $self->clicked_item->id, undef ) |
|
399 |
: ( $self->clicked_item->parent_id, $self->clicked_item->id ); |
|
400 |
} |
|
401 |
} |
|
402 |
|
|
403 |
sub assign_requirement_spec_id_rec { |
|
404 |
my ($self, $item) = @_; |
|
405 |
|
|
406 |
$item->requirement_spec_id($::form->{requirement_spec_id}); |
|
407 |
$self->assign_requirement_spec_id_rec($_) for @{ $item->children || [] }; |
|
408 |
|
|
409 |
return $item; |
|
410 |
} |
|
411 |
|
|
412 |
sub create_and_insert_node_rec { |
|
413 |
my ($self, $js, $item, $new_parent_id, $insert_after) = @_; |
|
414 |
|
|
415 |
my $node = $self->presenter->requirement_spec_item_jstree_data($item); |
|
416 |
$js->jstree->create_node('#tree', $insert_after ? ('#fb-' . $insert_after, 'after') : $new_parent_id ? ('#fb-' . $new_parent_id, 'last') : ('#sections', 'last'), $node); |
|
417 |
|
|
418 |
$self->create_and_insert_node_rec($js, $_, $item->id) for @{ $item->children || [] }; |
|
419 |
|
|
420 |
$js->jstree->open_node('#tree', '#fb-' . $item->id); |
|
421 |
} |
|
422 |
|
|
423 |
sub action_ajax_paste { |
|
424 |
my ($self, %params) = @_; |
|
425 |
|
|
426 |
my $js = SL::ClientJS->new; |
|
427 |
my $copied = SL::Clipboard->new->get_entry(qr/^RequirementSpecItem$/); |
|
428 |
|
|
429 |
if (!$copied) { |
|
430 |
return $js->error(t8("The clipboard does not contain anything that can be pasted here.")) |
|
431 |
->render($self); |
|
432 |
} |
|
433 |
|
|
434 |
$self->item($self->assign_requirement_spec_id_rec($copied->to_object)); |
|
435 |
my $req_spec = SL::DB::RequirementSpec->new(id => $::form->{requirement_spec_id})->load; |
|
436 |
$self->sections($req_spec->sections); |
|
437 |
|
|
438 |
if (($self->item->item_type ne 'section') && !@{ $self->sections }) { |
|
439 |
return $js->error(t8("You cannot paste function blocks or sub function blocks if there is no section.")) |
|
440 |
->render($self); |
|
441 |
} |
|
442 |
|
|
443 |
$self->clicked_item($::form->{id} ? SL::DB::RequirementSpecItem->new(id => $::form->{id})->load : undef); |
|
444 |
|
|
445 |
my ($new_parent_id, $insert_after) = $self->determine_paste_position; |
|
446 |
|
|
447 |
# Store result in database. |
|
448 |
$self->item->update_attributes(requirement_spec_id => $::form->{requirement_spec_id}, parent_id => $new_parent_id); |
|
449 |
$self->item->add_to_list(position => 'after', reference => $insert_after) if $insert_after; |
|
450 |
|
|
451 |
# Update the tree: create the node for all pasted objects. |
|
452 |
$self->create_and_insert_node_rec($js, $self->item, $new_parent_id, $insert_after); |
|
453 |
|
|
454 |
# Pasting the very first section? |
|
455 |
if (!@{ $self->sections }) { |
|
456 |
my $html = $self->render('requirement_spec_item/_section', { output => 0 }, requirement_spec_item => $self->item); |
|
457 |
$js->html('#column-content', $html) |
|
458 |
->jstree->select_node('#tree', '#fb-' . $self->item->id) |
|
459 |
} |
|
460 |
|
|
461 |
# Update the current view if required. |
|
462 |
$self->insert_new_item_in_section_view($js) if $self->is_item_visible; |
|
463 |
|
|
464 |
$js->render($self); |
|
465 |
} |
|
466 |
|
|
353 | 467 |
# |
354 | 468 |
# filters |
355 | 469 |
# |
... | ... | |
483 | 597 |
my $js = SL::ClientJS->new; |
484 | 598 |
|
485 | 599 |
my $new_section = $self->item->section; |
486 |
if (!$self->visible_section || ($self->visible_section->id != $new_section->id)) {
|
|
600 |
if (!$self->is_item_visible) {
|
|
487 | 601 |
# Show section/item to edit if it is not visible. |
488 | 602 |
|
489 | 603 |
$html = $self->render('requirement_spec_item/_section', { output => 0 }, requirement_spec_item => $new_section); |
... | ... | |
503 | 617 |
$js->render($self); |
504 | 618 |
} |
505 | 619 |
|
620 |
sub is_item_visible { |
|
621 |
my ($self, $item) = @_; |
|
622 |
|
|
623 |
$item ||= $self->item; |
|
624 |
return $self->visible_section && ($self->visible_section->id == $item->section->id); |
|
625 |
} |
|
626 |
|
|
506 | 627 |
1; |
Auch abrufbar als: Unified diff
Pflichtenhefte: Copy & Paste für Textblöcke & Items