Revision 55e399ab
Von Moritz Bunkus vor mehr als 10 Jahren hinzugefügt
SL/Controller/RequirementSpec.pm | ||
---|---|---|
26 | 26 |
use Rose::Object::MakeMethods::Generic |
27 | 27 |
( |
28 | 28 |
scalar => [ qw(requirement_spec_item customers types statuses db_args flat_filter visible_item visible_section) ], |
29 |
'scalar --get_set_init' => [ qw(requirement_spec complexities risks projects copy_source js) ], |
|
29 |
'scalar --get_set_init' => [ qw(requirement_spec complexities risks projects copy_source js current_text_block_output_position) ],
|
|
30 | 30 |
); |
31 | 31 |
|
32 | 32 |
__PACKAGE__->run_before('setup'); |
... | ... | |
218 | 218 |
$self->render('requirement_spec/select_template_to_paste', { layout => 0 }, TEMPLATES => \@templates); |
219 | 219 |
} |
220 | 220 |
|
221 |
sub action_paste_template { |
|
222 |
my ($self, %params) = @_; |
|
223 |
|
|
224 |
my $template = SL::DB::RequirementSpec->new(id => $::form->{template_id})->load; |
|
225 |
my %result = $self->requirement_spec->paste_template($template); |
|
226 |
|
|
227 |
return $self->js->error($self->requirement_spec->error)->render($self) if !%result; |
|
228 |
|
|
229 |
$self->render_pasted_text_block($_) for sort { $a->position <=> $b->position } @{ $result{text_blocks} }; |
|
230 |
$self->render_pasted_section($_) for sort { $a->position <=> $b->position } @{ $result{sections} }; |
|
231 |
|
|
232 |
if (@{ $result{sections} } && (($::form->{current_content_type} || 'sections') eq 'sections') && !$::form->{current_content_id}) { |
|
233 |
$self->render_first_pasted_section_as_list($result{sections}->[0]); |
|
234 |
} |
|
235 |
|
|
236 |
$self->invalidate_version->render($self); |
|
237 |
} |
|
238 |
|
|
221 | 239 |
# |
222 | 240 |
# filters |
223 | 241 |
# |
... | ... | |
263 | 281 |
$self->js(SL::ClientJS->new); |
264 | 282 |
} |
265 | 283 |
|
284 |
sub init_current_text_block_output_position { |
|
285 |
my ($self) = @_; |
|
286 |
$self->current_text_block_output_position($::form->{current_content_type} !~ m/^(?:text-blocks|tb)-(front|back)/ ? -1 : $1 eq 'front' ? 0 : 1); |
|
287 |
} |
|
288 |
|
|
266 | 289 |
sub load_select_options { |
267 | 290 |
my ($self) = @_; |
268 | 291 |
|
... | ... | |
414 | 437 |
return $self->js->html('#requirement_spec_version', $html); |
415 | 438 |
} |
416 | 439 |
|
440 |
sub render_pasted_text_block { |
|
441 |
my ($self, $text_block, %params) = @_; |
|
442 |
|
|
443 |
if ($self->current_text_block_output_position == $text_block->output_position) { |
|
444 |
my $html = $self->render('requirement_spec_text_block/_text_block', { output => 0 }, text_block => $text_block); |
|
445 |
$self->js |
|
446 |
->appendTo($html, '#text-block-list') |
|
447 |
->hide('#text-block-list-empty'); |
|
448 |
} |
|
449 |
|
|
450 |
my $node = $self->presenter->requirement_spec_text_block_jstree_data($text_block); |
|
451 |
my $front_back = $text_block->output_position == 0 ? 'front' : 'back'; |
|
452 |
$self->js |
|
453 |
->jstree->create_node('#tree', "#tb-${front_back}", 'last', $node) |
|
454 |
->jstree->open_node( '#tree', "#tb-${front_back}"); |
|
455 |
} |
|
456 |
|
|
457 |
sub render_pasted_section { |
|
458 |
my ($self, $item, $parent_id) = @_; |
|
459 |
|
|
460 |
my $node = $self->presenter->requirement_spec_item_jstree_data($item); |
|
461 |
$self->js |
|
462 |
->jstree->create_node('#tree', $parent_id ? "#fb-${parent_id}" : '#sections', 'last', $node) |
|
463 |
->jstree->open_node( '#tree', $parent_id ? "#fb-${parent_id}" : '#sections'); |
|
464 |
|
|
465 |
$self->render_pasted_section($_, $item->id) for @{ $item->children_sorted }; |
|
466 |
} |
|
467 |
|
|
468 |
sub render_first_pasted_section_as_list { |
|
469 |
my ($self, $section, %params) = @_; |
|
470 |
|
|
471 |
my $html = $self->render('requirement_spec_item/_section', { output => 0 }, requirement_spec_item => $section); |
|
472 |
$self->js |
|
473 |
->html('#column-content', $html) |
|
474 |
->val( '#current_content_type', $section->item_type) |
|
475 |
->val( '#current_content_id', $section->id) |
|
476 |
->jstree->select_node('#tree', '#fb-' . $section->id); |
|
477 |
} |
|
478 |
|
|
417 | 479 |
1; |
SL/DB/RequirementSpec.pm | ||
---|---|---|
111 | 111 |
} |
112 | 112 |
|
113 | 113 |
sub _copy_from { |
114 |
my ($self, $source, %attributes) = @_; |
|
114 |
my ($self, $params, %attributes) = @_; |
|
115 |
|
|
116 |
my $source = $params->{source}; |
|
115 | 117 |
|
116 | 118 |
croak "Missing parameter 'source'" unless $source; |
117 | 119 |
|
118 | 120 |
# Copy attributes. |
119 |
$self->assign_attributes(map({ ($_ => $source->$_) } qw(type_id status_id customer_id project_id title hourly_rate net_sum previous_section_number previous_fb_number is_template)), |
|
120 |
%attributes); |
|
121 |
if (!$params->{paste_template}) { |
|
122 |
$self->assign_attributes(map({ ($_ => $source->$_) } qw(type_id status_id customer_id project_id title hourly_rate net_sum previous_section_number previous_fb_number is_template)), |
|
123 |
%attributes); |
|
124 |
} |
|
125 |
|
|
126 |
my %paste_template_result; |
|
121 | 127 |
|
122 | 128 |
# Clone text blocks. |
123 |
$self->text_blocks(map { Rose::DB::Object::Helpers::clone_and_reset($_) } @{ $source->text_blocks }); |
|
129 |
my $clone_text_block = sub { |
|
130 |
my ($text_block) = @_; |
|
131 |
my $cloned = Rose::DB::Object::Helpers::clone_and_reset($text_block); |
|
132 |
$cloned->position(undef); |
|
133 |
return $cloned; |
|
134 |
}; |
|
135 |
|
|
136 |
$paste_template_result{text_blocks} = [ map { $clone_text_block->($_) } @{ $source->text_blocks_sorted } ]; |
|
137 |
|
|
138 |
if (!$params->{paste_template}) { |
|
139 |
$self->text_blocks($paste_template_result{text_blocks}); |
|
140 |
} else { |
|
141 |
$self->add_text_blocks($paste_template_result{text_blocks}); |
|
142 |
} |
|
124 | 143 |
|
125 | 144 |
# Save new object -- we need its ID for the items. |
126 | 145 |
$self->save; |
... | ... | |
133 | 152 |
my ($item) = @_; |
134 | 153 |
my $cloned = Rose::DB::Object::Helpers::clone_and_reset($item); |
135 | 154 |
$cloned->requirement_spec_id($self->id); |
155 |
$cloned->position(undef); |
|
136 | 156 |
$cloned->children(map { $clone_item->($_) } @{ $item->children }); |
137 | 157 |
|
138 | 158 |
$id_to_clone{ $item->id } = $cloned; |
... | ... | |
140 | 160 |
return $cloned; |
141 | 161 |
}; |
142 | 162 |
|
143 |
$self->items(map { $clone_item->($_) } @{ $source->sections }); |
|
163 |
$paste_template_result{sections} = [ map { $clone_item->($_) } @{ $source->sections_sorted } ]; |
|
164 |
|
|
165 |
if (!$params->{paste_template}) { |
|
166 |
$self->items($paste_template_result{sections}); |
|
167 |
} else { |
|
168 |
$self->add_items($paste_template_result{sections}); |
|
169 |
} |
|
144 | 170 |
|
145 | 171 |
# Save the items -- need to do that before setting dependencies. |
146 | 172 |
$self->save; |
... | ... | |
151 | 177 |
$id_to_clone{ $item->id }->update_attributes(dependencies => [ map { $id_to_clone{$_->id} } @{ $item->dependencies } ]); |
152 | 178 |
} |
153 | 179 |
|
154 |
$self->update_attributes(%attributes); |
|
180 |
$self->update_attributes(%attributes) unless $params->{paste_template};
|
|
155 | 181 |
|
156 |
return $self;
|
|
182 |
return %paste_template_result;
|
|
157 | 183 |
} |
158 | 184 |
|
159 | 185 |
sub copy_from { |
160 | 186 |
my ($self, $source, %attributes) = @_; |
161 | 187 |
|
162 |
$self->db->with_transaction(sub { $self->_copy_from($source, %attributes); }); |
|
188 |
$self->db->with_transaction(sub { $self->_copy_from({ source => $source, paste_template => 0 }, %attributes); }); |
|
189 |
} |
|
190 |
|
|
191 |
sub paste_template { |
|
192 |
my ($self, $template) = @_; |
|
193 |
|
|
194 |
$self->db->with_transaction(sub { $self->_copy_from({ source => $template, paste_template => 1 }); }); |
|
163 | 195 |
} |
164 | 196 |
|
165 | 197 |
sub highest_version { |
js/requirement_spec.js | ||
---|---|---|
269 | 269 |
return ns.handle_item_popup_menu_markings(opt, false); |
270 | 270 |
}; |
271 | 271 |
|
272 |
// ------------------------------------------------------------------------- |
|
273 |
// ------------------------------- templates ------------------------------- |
|
274 |
// ------------------------------------------------------------------------- |
|
275 |
|
|
272 | 276 |
ns.paste_template = function(key, opt, other_data) { |
273 | 277 |
open_jqm_window({ url: 'controller.pl?action=RequirementSpec/select_template_to_paste' }); |
274 | 278 |
}; |
275 | 279 |
|
280 |
ns.paste_selected_template = function(template_id) { |
|
281 |
$('#jqm_popup_dialog').jqmClose(); |
|
282 |
|
|
283 |
var data = { |
|
284 |
action: "RequirementSpec/paste_template", |
|
285 |
id: $('#requirement_spec_id').val(), |
|
286 |
template_id: template_id, |
|
287 |
current_content_type: $('#current_content_type').val(), |
|
288 |
current_content_id: $('#current_content_id').val() |
|
289 |
}; |
|
290 |
|
|
291 |
// console.log("I would normally POST the following now:"); |
|
292 |
// console.log(data); |
|
293 |
$.post("controller.pl", data, kivi.eval_json_result); |
|
294 |
|
|
295 |
return true; |
|
296 |
}; |
|
297 |
|
|
276 | 298 |
// ------------------------------------------------------------------------- |
277 | 299 |
// -------------------------- time/cost estimate --------------------------- |
278 | 300 |
// ------------------------------------------------------------------------- |
templates/webpages/requirement_spec/select_template_to_paste.html | ||
---|---|---|
37 | 37 |
<span style="display: none;">[-]</span> |
38 | 38 |
</a> |
39 | 39 |
</td> |
40 |
<td><a href="#" onclick="paste_selected_template([% template.id %]);">[%- LxERP.t8("Paste template") %]</a></td> |
|
40 |
<td><a href="#" onclick="kivi.requirement_spec.paste_selected_template([% template.id %]);">[%- LxERP.t8("Paste template") %]</a></td>
|
|
41 | 41 |
<td>[%- HTML.escape(template.title) %]</td> |
42 | 42 |
<td>[% template.mtime ? template.mtime.to_kivitendo(precision='minute') : template.itime.to_kivitendo(precision='minute') %]</td> |
43 | 43 |
</tr> |
Auch abrufbar als: Unified diff
Pflichtenheftvorlagen einfügen