Revision 54daa586
Von Moritz Bunkus vor mehr als 10 Jahren hinzugefügt
SL/Controller/RequirementSpec.pm | ||
---|---|---|
23 | 23 |
); |
24 | 24 |
|
25 | 25 |
__PACKAGE__->run_before('setup'); |
26 |
__PACKAGE__->run_before('load_requirement_spec', only => [ qw( edit update destroy) ]); |
|
26 |
__PACKAGE__->run_before('load_requirement_spec', only => [ qw( edit update destroy tree) ]);
|
|
27 | 27 |
__PACKAGE__->run_before('load_select_options', only => [ qw(new edit create update list) ]); |
28 | 28 |
__PACKAGE__->run_before('load_search_select_options', only => [ qw( list) ]); |
29 | 29 |
|
... | ... | |
109 | 109 |
$self->render('1;', { type => 'js', inline => 1 }); |
110 | 110 |
} |
111 | 111 |
|
112 |
sub action_tree { |
|
113 |
my ($self) = @_; |
|
114 |
my $r = $self->render('requirement_spec/tree', now => DateTime->now); |
|
115 |
} |
|
116 |
|
|
112 | 117 |
# |
113 | 118 |
# filters |
114 | 119 |
# |
... | ... | |
117 | 122 |
my ($self) = @_; |
118 | 123 |
|
119 | 124 |
$::auth->assert('config'); |
120 |
$::request->{layout}->use_stylesheet("requirement_spec.css"); |
|
125 |
$::request->{layout}->use_stylesheet("${_}.css") for qw(requirement_spec yaml/core/base.min); |
|
126 |
$::request->{layout}->use_javascript("${_}.js") for qw(jquery.jstree requirement_spec); |
|
121 | 127 |
$self->is_template($::form->{is_template} ? 1 : 0); |
122 | 128 |
|
123 | 129 |
return 1; |
124 | 130 |
} |
125 | 131 |
|
132 |
sub load_requirement_spec { |
|
133 |
my ($self) = @_; |
|
134 |
$self->{requirement_spec} = SL::DB::RequirementSpec->new(id => $::form->{id})->load || die "No such requirement spec"; |
|
135 |
} |
|
136 |
|
|
137 |
sub load_select_options { |
|
138 |
my ($self) = @_; |
|
139 |
|
|
140 |
my @filter = ('!obsolete' => 1); |
|
141 |
if ($self->requirement_spec && $self->requirement_spec->customer_id) { |
|
142 |
@filter = ( or => [ @filter, id => $self->requirement_spec->customer_id ] ); |
|
143 |
} |
|
144 |
|
|
145 |
$self->customers(SL::DB::Manager::Customer->get_all_sorted(where => \@filter)); |
|
146 |
$self->statuses( SL::DB::Manager::RequirementSpecStatus->get_all_sorted); |
|
147 |
$self->types( SL::DB::Manager::RequirementSpecType->get_all_sorted); |
|
148 |
} |
|
149 |
|
|
150 |
sub load_search_select_options { |
|
151 |
my ($self) = @_; |
|
152 |
|
|
153 |
$self->projects(SL::DB::Manager::Project->get_all_sorted); |
|
154 |
} |
|
155 |
|
|
126 | 156 |
# |
127 | 157 |
# helpers |
128 | 158 |
# |
... | ... | |
149 | 179 |
$self->redirect_to(action => 'list'); |
150 | 180 |
} |
151 | 181 |
|
152 |
sub load_requirement_spec { |
|
153 |
my ($self) = @_; |
|
154 |
$self->{requirement_spec} = SL::DB::RequirementSpec->new(id => $::form->{id})->load; |
|
155 |
} |
|
156 |
|
|
157 |
sub load_select_options { |
|
158 |
my ($self) = @_; |
|
159 |
|
|
160 |
my @filter = ('!obsolete' => 1); |
|
161 |
if ($self->requirement_spec && $self->requirement_spec->customer_id) { |
|
162 |
@filter = ( or => [ @filter, id => $self->requirement_spec->customer_id ] ); |
|
163 |
} |
|
164 |
|
|
165 |
$self->customers(SL::DB::Manager::Customer->get_all_sorted(where => \@filter)); |
|
166 |
$self->statuses( SL::DB::Manager::RequirementSpecStatus->get_all_sorted); |
|
167 |
$self->types( SL::DB::Manager::RequirementSpecType->get_all_sorted); |
|
168 |
} |
|
169 |
|
|
170 |
sub load_search_select_options { |
|
171 |
my ($self) = @_; |
|
172 |
|
|
173 |
$self->projects(SL::DB::Manager::Project->get_all_sorted); |
|
174 |
} |
|
175 |
|
|
176 | 182 |
sub setup_db_args_from_filter { |
177 | 183 |
my ($self) = @_; |
178 | 184 |
|
SL/Controller/RequirementSpecItem.pm | ||
---|---|---|
1 |
package SL::Controller::RequirementSpecItem; |
|
2 |
|
|
3 |
use strict; |
|
4 |
|
|
5 |
use parent qw(SL::Controller::Base); |
|
6 |
|
|
7 |
use Time::HiRes (); |
|
8 |
|
|
9 |
use SL::DB::RequirementSpec; |
|
10 |
use SL::DB::RequirementSpecItem; |
|
11 |
use SL::Helper::Flash; |
|
12 |
use SL::JSON; |
|
13 |
use SL::Locale::String; |
|
14 |
|
|
15 |
use Rose::Object::MakeMethods::Generic |
|
16 |
( |
|
17 |
scalar => [ qw(requirement_spec item) ], |
|
18 |
); |
|
19 |
|
|
20 |
# __PACKAGE__->run_before('load_requirement_spec'); |
|
21 |
__PACKAGE__->run_before('load_requirement_spec_item', only => [qw(dragged_and_dropped)]); |
|
22 |
|
|
23 |
# |
|
24 |
# actions |
|
25 |
# |
|
26 |
|
|
27 |
sub action_new { |
|
28 |
my ($self) = @_; |
|
29 |
|
|
30 |
eval { |
|
31 |
my $type = ($::form->{item_type} || '') =~ m/^ (?: section | (?: sub-)? function-block ) $/x ? $::form->{item_type} : die "Invalid item_type"; |
|
32 |
$self->{item} = SL::DB::RequirementSpecItem->new(requirement_spec_id => $::form->{requirement_spec_id}); |
|
33 |
my $section_form = $self->presenter->render("requirement_spec_item/_${type}_form", id => create_random_id(), title => t8('Create a new section')); |
|
34 |
|
|
35 |
$self->render(\to_json({ status => 'ok', html => $section_form }), { type => 'json' }); |
|
36 |
1; |
|
37 |
} or do { |
|
38 |
$self->render(\to_json({ status => 'failed', error => "Exception:\n" . format_exception() }), { type => 'json' }); |
|
39 |
} |
|
40 |
} |
|
41 |
|
|
42 |
sub action_create { |
|
43 |
my ($self) = @_; |
|
44 |
|
|
45 |
my $type = ($::form->{item_type} || '') =~ m/^ (?: section | (?: sub-)? function-block ) $/x ? $::form->{item_type} : die "Invalid item_type"; |
|
46 |
|
|
47 |
$self->render(\to_json({ status => 'failed', error => 'not good, not good' }), { type => 'json' }); |
|
48 |
} |
|
49 |
|
|
50 |
sub action_dragged_and_dropped { |
|
51 |
my ($self) = @_; |
|
52 |
|
|
53 |
$::lxdebug->dump(0, "form", $::form); |
|
54 |
|
|
55 |
my $dropped_item = SL::DB::RequirementSpecItem->new(id => $::form->{dropped_id})->load || die "No such dropped item"; |
|
56 |
my $position = $::form->{position} =~ m/^ (?: before | after | last ) $/x ? $::form->{position} : die "Unknown 'position' parameter"; |
|
57 |
|
|
58 |
$self->item->db->do_transaction(sub { |
|
59 |
$self->item->remove_from_list; |
|
60 |
$self->item->parent_id($position =~ m/before|after/ ? $dropped_item->parent_id : $dropped_item->id); |
|
61 |
$self->item->add_to_list(position => $position, reference => $dropped_item->id); |
|
62 |
}); |
|
63 |
|
|
64 |
$self->render(\'', { type => 'json' }); |
|
65 |
} |
|
66 |
|
|
67 |
# |
|
68 |
# filters |
|
69 |
# |
|
70 |
|
|
71 |
sub load_requirement_spec { |
|
72 |
my ($self) = @_; |
|
73 |
$self->requirement_spec(SL::DB::RequirementSpec->new(id => $::form->{requirement_spec_id})->load || die "No such requirement spec"); |
|
74 |
} |
|
75 |
|
|
76 |
sub load_requirement_spec_item { |
|
77 |
my ($self) = @_; |
|
78 |
$self->item(SL::DB::RequirementSpecItem->new(id => $::form->{id})->load || die "No such requirement spec item"); |
|
79 |
} |
|
80 |
|
|
81 |
# |
|
82 |
# helpers |
|
83 |
# |
|
84 |
|
|
85 |
sub create_random_id { |
|
86 |
return join '-', Time::HiRes::gettimeofday(); |
|
87 |
} |
|
88 |
|
|
89 |
sub format_exception { |
|
90 |
return join "\n", (split m/\n/, $@)[0..4]; |
|
91 |
} |
|
92 |
|
|
93 |
1; |
SL/Controller/RequirementSpecTextBlock.pm | ||
---|---|---|
1 |
package SL::Controller::RequirementSpecTextBlock; |
|
2 |
|
|
3 |
use strict; |
|
4 |
|
|
5 |
use parent qw(SL::Controller::Base); |
|
6 |
|
|
7 |
use SL::DB::RequirementSpec; |
|
8 |
use SL::DB::RequirementSpecTextBlock; |
|
9 |
use SL::Helper::Flash; |
|
10 |
use SL::Locale::String; |
|
11 |
|
|
12 |
use Rose::Object::MakeMethods::Generic |
|
13 |
( |
|
14 |
scalar => [ qw(requirement_spec text_block) ], |
|
15 |
); |
|
16 |
|
|
17 |
# __PACKAGE__->run_before('load_requirement_spec'); |
|
18 |
__PACKAGE__->run_before('load_requirement_spec_text_block', only => [qw(dragged_and_dropped)]); |
|
19 |
|
|
20 |
# |
|
21 |
# actions |
|
22 |
# |
|
23 |
|
|
24 |
sub action_dragged_and_dropped { |
|
25 |
my ($self) = @_; |
|
26 |
|
|
27 |
$::lxdebug->dump(0, "form", $::form); |
|
28 |
|
|
29 |
my $position = $::form->{position} =~ m/^ (?: before | after | last ) $/x ? $::form->{position} : die "Unknown 'position' parameter"; |
|
30 |
my $dropped_text_block = $position =~ m/^ (?: before | after ) $/x ? SL::DB::RequirementSpecTextBlock->new(id => $::form->{dropped_id})->load : undef; |
|
31 |
|
|
32 |
my $dropped_type = $position ne 'last' ? undef : $::form->{dropped_type} =~ m/^ textblocks- (?:front|back) $/x ? $::form->{dropped_type} : die "Unknown 'dropped_type' parameter"; |
|
33 |
|
|
34 |
$self->text_block->db->do_transaction(sub { |
|
35 |
1; |
|
36 |
$self->text_block->remove_from_list; |
|
37 |
$self->text_block->output_position($position =~ m/before|after/ ? $dropped_text_block->output_position : $::form->{dropped_type} eq 'textblocks-front' ? 0 : 1); |
|
38 |
$self->text_block->add_to_list(position => $position, reference => $dropped_text_block ? $dropped_text_block->id : undef); |
|
39 |
}); |
|
40 |
|
|
41 |
$self->render(\'', { type => 'json' }); |
|
42 |
} |
|
43 |
|
|
44 |
# |
|
45 |
# filters |
|
46 |
# |
|
47 |
|
|
48 |
sub load_requirement_spec { |
|
49 |
my ($self) = @_; |
|
50 |
$self->requirement_spec(SL::DB::RequirementSpec->new(id => $::form->{requirement_spec_id})->load || die "No such requirement spec"); |
|
51 |
} |
|
52 |
|
|
53 |
sub load_requirement_spec_text_block { |
|
54 |
my ($self) = @_; |
|
55 |
$self->text_block(SL::DB::RequirementSpecTextBlock->new(id => $::form->{id})->load || die "No such requirement spec text block"); |
|
56 |
} |
|
57 |
|
|
58 |
# |
|
59 |
# helpers |
|
60 |
# |
|
61 |
|
|
62 |
1; |
SL/Presenter.pm | ||
---|---|---|
15 | 15 |
use SL::Presenter::Part; |
16 | 16 |
use SL::Presenter::Project; |
17 | 17 |
use SL::Presenter::Record; |
18 |
use SL::Presenter::RequirementSpec; |
|
18 | 19 |
use SL::Presenter::SepaExport; |
19 | 20 |
use SL::Presenter::Text; |
20 | 21 |
use SL::Presenter::Tag; |
SL/Presenter/RequirementSpec.pm | ||
---|---|---|
1 |
package SL::Presenter::RequirementSpec; |
|
2 |
|
|
3 |
use strict; |
|
4 |
|
|
5 |
use parent qw(Exporter); |
|
6 |
|
|
7 |
use Exporter qw(import); |
|
8 |
our @EXPORT = qw(requirement_spec_text_block_jstree_data |
|
9 |
requirement_spec_item_jstree_data); |
|
10 |
|
|
11 |
use Carp; |
|
12 |
|
|
13 |
use SL::JSON; |
|
14 |
|
|
15 |
sub requirement_spec_text_block_jstree_data { |
|
16 |
my ($self, $text_block, %params) = @_; |
|
17 |
|
|
18 |
return { |
|
19 |
data => $text_block->title || '', |
|
20 |
metadata => { id => $text_block->id, type => 'textblock' }, |
|
21 |
attr => { id => "tb-" . $text_block->id, href => $params{href} || '#' }, |
|
22 |
}; |
|
23 |
} |
|
24 |
|
|
25 |
sub requirement_spec_item_jstree_data { |
|
26 |
my ($self, $item, %params) = @_; |
|
27 |
|
|
28 |
my @children = map { $self->requirement_spec_item_jstree_data($_, %params) } @{ $item->sorted_children }; |
|
29 |
my $type = !$item->parent_id ? 'section' : 'functionblock'; |
|
30 |
|
|
31 |
return { |
|
32 |
data => join(' ', map { $_ || '' } ($item->fb_number, $item->title)), |
|
33 |
metadata => { id => $item->id, type => $type }, |
|
34 |
attr => { id => "fb-" . $item->id, href => $params{href} || '#' }, |
|
35 |
children => \@children, |
|
36 |
}; |
|
37 |
} |
|
38 |
|
|
39 |
1; |
css/kivitendo/jquery-ui.custom.css | ||
---|---|---|
118 | 118 |
text-align: left; |
119 | 119 |
font-size:125%; |
120 | 120 |
} |
121 |
|
|
122 |
/* jstree */ |
|
123 |
.jstree a { |
|
124 |
border-bottom: none; |
|
125 |
} |
css/requirement_spec.css | ||
---|---|---|
2 | 2 |
table.rs_input_field input, table.rs_input_field select { |
3 | 3 |
width: 300px; |
4 | 4 |
} |
5 |
|
|
6 |
#content-column { |
|
7 |
margin-left: 10px; |
|
8 |
} |
js/requirement_spec.js | ||
---|---|---|
1 |
/* Functions used for the requirement specs tree view */ |
|
2 |
|
|
3 |
function check_move(data) { |
|
4 |
var dragged_type = data.o.data('type'); |
|
5 |
var dropped_type = data.r.data('type'); |
|
6 |
|
|
7 |
// console.debug("dragged " + dragged_type + " dropped " + dropped_type + " dir " + data.p); |
|
8 |
|
|
9 |
if ((dragged_type == "sections") || (dragged_type == "textblocks-front") || (dragged_type == "textblocks-back")) |
|
10 |
return false; |
|
11 |
|
|
12 |
if (dragged_type == "textblock") { |
|
13 |
if ((dropped_type == "textblocks-front") || (dropped_type == "textblocks-back")) |
|
14 |
return (data.p == "inside") || (data.p == "last"); |
|
15 |
if (dropped_type == "textblock") |
|
16 |
return (data.p == "before") || (data.p == "after"); |
|
17 |
|
|
18 |
return false; |
|
19 |
} |
|
20 |
|
|
21 |
if (dragged_type == "section") { |
|
22 |
if (dropped_type == "sections") |
|
23 |
return (data.p == "inside") || (data.p == "last"); |
|
24 |
if (dropped_type == "section") |
|
25 |
return (data.p == "before") || (data.p == "after"); |
|
26 |
|
|
27 |
return false; |
|
28 |
} |
|
29 |
|
|
30 |
// dragged_type == (sub) function blocks |
|
31 |
if ((dropped_type == "textblock") || (dropped_type == "textblocks-front") || (dropped_type == "textblocks-back")) |
|
32 |
return false; |
|
33 |
|
|
34 |
var dropped_depth = dropped_type == "sections" ? 0 : dropped_type == "section" ? 1 : data.r.parent().parent().data('type') != "functionblock" ? 2 : 3; |
|
35 |
if ((data.p == "inside") || (data.p == "last")) |
|
36 |
dropped_depth++; |
|
37 |
|
|
38 |
var dragged_depth = 1 + data.o.children('ul').size(); |
|
39 |
|
|
40 |
// console.debug("dropped_depth " + dropped_depth + " dragged_depth " + dragged_depth); |
|
41 |
|
|
42 |
return (2 <= dropped_depth) && ((dragged_depth + dropped_depth) <= 4); |
|
43 |
} |
|
44 |
|
|
45 |
function node_moved(event) { |
|
46 |
console.debug("node moved"); |
|
47 |
var move_obj = $.jstree._reference('#tree')._get_move(); |
|
48 |
var dragged = move_obj.o; |
|
49 |
var dropped = move_obj.r; |
|
50 |
var controller = dragged.data("type") == "textblock" ? "RequirementSpecTextBlock" : "RequirementSpecItem"; |
|
51 |
var data = { |
|
52 |
action: controller + "/dragged_and_dropped", |
|
53 |
requirement_spec_id: $('#requirement_spec_id').val(), |
|
54 |
id: dragged.data("id"), |
|
55 |
dropped_id: dropped.data("id"), |
|
56 |
dropped_type: dropped.data("type"), |
|
57 |
position: move_obj.p |
|
58 |
}; |
|
59 |
// console.debug("controller: " + controller); |
|
60 |
// console.debug(data); |
|
61 |
|
|
62 |
$.ajax({ url: "controller.pl", data: data }); |
|
63 |
} |
|
64 |
|
|
65 |
function section_form_requested(data) { |
|
66 |
$('#new-section-button').removeAttr('disabled'); |
|
67 |
if (data.status == "ok") |
|
68 |
$('#content-column').html(data.html); |
|
69 |
else |
|
70 |
alert('oh yeah response: ' + data.status + "\n" + data.error); |
|
71 |
} |
|
72 |
|
|
73 |
function section_form_submitted(data) { |
|
74 |
alert('oh yeah response: ' + data.status); |
|
75 |
} |
|
76 |
|
|
77 |
function server_side_error(things_to_enable) { |
|
78 |
alert('Server-side error.'); |
|
79 |
if (things_to_enable) |
|
80 |
$(things_to_enable).removeAttr('disabled'); |
|
81 |
} |
|
82 |
|
|
83 |
function new_section_form() { |
|
84 |
$('#new-section-button').attr('disabled', 'disabled'); |
|
85 |
$.ajax({ |
|
86 |
type: 'POST', |
|
87 |
url: 'controller.pl', |
|
88 |
data: 'action=RequirementSpecItem/new.json&requirement_spec_id=' + $('#requirement_spec_id').val() + '&item_type=section', |
|
89 |
success: section_form_requested, |
|
90 |
error: function() { server_side_error('#new-section-button'); } |
|
91 |
}); |
|
92 |
} |
|
93 |
|
|
94 |
function submit_section_form(id) { |
|
95 |
$.ajax({ |
|
96 |
type: 'POST', |
|
97 |
url: 'controller.pl', |
|
98 |
data: 'action=RequirementSpecItem/create.json&' + $('section-form-' + id).serialize(), |
|
99 |
success: section_form_submitted |
|
100 |
}); |
|
101 |
} |
|
102 |
|
|
103 |
function cancel_section_form(id) { |
|
104 |
$('#content-column').html('intentionally empty'); |
|
105 |
} |
js/themes/requirement-spec/style.css | ||
---|---|---|
1 |
/* |
|
2 |
* jsTree default theme 1.0 |
|
3 |
* Supported features: dots/no-dots, icons/no-icons, focused, loading |
|
4 |
* Supported plugins: ui (hovered, clicked), checkbox, contextmenu, search |
|
5 |
*/ |
|
6 |
|
|
7 |
.jstree-requirement-spec li, |
|
8 |
.jstree-requirement-spec ins { background-image:url("d.png"); background-repeat:no-repeat; background-color:transparent; } |
|
9 |
.jstree-requirement-spec li { background-position:-90px 0; background-repeat:repeat-y; } |
|
10 |
.jstree-requirement-spec li.jstree-last { background:transparent; } |
|
11 |
.jstree-requirement-spec .jstree-open > ins { background-position:-72px 0; } |
|
12 |
.jstree-requirement-spec .jstree-closed > ins { background-position:-54px 0; } |
|
13 |
.jstree-requirement-spec .jstree-leaf > ins { background-position:-36px 0; } |
|
14 |
|
|
15 |
.jstree-requirement-spec .jstree-hovered { background:#e7f4f9; border:1px solid #d8f0fa; padding:0 2px 0 1px; } |
|
16 |
.jstree-requirement-spec .jstree-clicked { background:#beebff; border:1px solid #99defd; padding:0 2px 0 1px; } |
|
17 |
.jstree-requirement-spec a .jstree-icon { background-position:-56px -19px; } |
|
18 |
.jstree-requirement-spec a.jstree-loading .jstree-icon { background:url("throbber.gif") center center no-repeat !important; } |
|
19 |
|
|
20 |
.jstree-requirement-spec.jstree-focused { } |
|
21 |
|
|
22 |
.jstree-requirement-spec .jstree-no-dots li, |
|
23 |
.jstree-requirement-spec .jstree-no-dots .jstree-leaf > ins { background:transparent; } |
|
24 |
.jstree-requirement-spec .jstree-no-dots .jstree-open > ins { background-position:-18px 0; } |
|
25 |
.jstree-requirement-spec .jstree-no-dots .jstree-closed > ins { background-position:0 0; } |
|
26 |
|
|
27 |
.jstree-requirement-spec .jstree-no-icons a .jstree-icon { display:none; } |
|
28 |
|
|
29 |
.jstree-requirement-spec .jstree-search { font-style:italic; } |
|
30 |
|
|
31 |
.jstree-requirement-spec .jstree-no-icons .jstree-checkbox { display:inline-block; } |
|
32 |
.jstree-requirement-spec .jstree-no-checkboxes .jstree-checkbox { display:none !important; } |
|
33 |
.jstree-requirement-spec .jstree-checked > a > .jstree-checkbox { background-position:-38px -19px; } |
|
34 |
.jstree-requirement-spec .jstree-unchecked > a > .jstree-checkbox { background-position:-2px -19px; } |
|
35 |
.jstree-requirement-spec .jstree-undetermined > a > .jstree-checkbox { background-position:-20px -19px; } |
|
36 |
.jstree-requirement-spec .jstree-checked > a > .jstree-checkbox:hover { background-position:-38px -37px; } |
|
37 |
.jstree-requirement-spec .jstree-unchecked > a > .jstree-checkbox:hover { background-position:-2px -37px; } |
|
38 |
.jstree-requirement-spec .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-20px -37px; } |
|
39 |
|
|
40 |
#vakata-dragged.jstree-requirement-spec ins { background:transparent !important; } |
|
41 |
#vakata-dragged.jstree-requirement-spec .jstree-ok { background:url("d.png") -2px -53px no-repeat !important; } |
|
42 |
#vakata-dragged.jstree-requirement-spec .jstree-invalid { background:url("d.png") -18px -53px no-repeat !important; } |
|
43 |
#jstree-marker.jstree-requirement-spec { background:url("d.png") -41px -57px no-repeat !important; text-indent:-100px; } |
|
44 |
|
|
45 |
.jstree-requirement-spec a.jstree-search { color:aqua; } |
|
46 |
.jstree-requirement-spec .jstree-locked a { color:silver; cursor:default; } |
|
47 |
|
|
48 |
#vakata-contextmenu.jstree-requirement-spec-context, |
|
49 |
#vakata-contextmenu.jstree-requirement-spec-context li ul { background:#f0f0f0; border:1px solid #979797; -moz-box-shadow: 1px 1px 2px #999; -webkit-box-shadow: 1px 1px 2px #999; box-shadow: 1px 1px 2px #999; } |
|
50 |
#vakata-contextmenu.jstree-requirement-spec-context li { } |
|
51 |
#vakata-contextmenu.jstree-requirement-spec-context a { color:black; } |
|
52 |
#vakata-contextmenu.jstree-requirement-spec-context a:hover, |
|
53 |
#vakata-contextmenu.jstree-requirement-spec-context .vakata-hover > a { padding:0 5px; background:#e8eff7; border:1px solid #aecff7; color:black; -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; } |
|
54 |
#vakata-contextmenu.jstree-requirement-spec-context li.jstree-contextmenu-disabled a, |
|
55 |
#vakata-contextmenu.jstree-requirement-spec-context li.jstree-contextmenu-disabled a:hover { color:silver; background:transparent; border:0; padding:1px 4px; } |
|
56 |
#vakata-contextmenu.jstree-requirement-spec-context li.vakata-separator { background:white; border-top:1px solid #e0e0e0; margin:0; } |
|
57 |
#vakata-contextmenu.jstree-requirement-spec-context li ul { margin-left:-4px; } |
|
58 |
|
|
59 |
/* IE6 BEGIN */ |
|
60 |
.jstree-requirement-spec li, |
|
61 |
.jstree-requirement-spec ins, |
|
62 |
#vakata-dragged.jstree-requirement-spec .jstree-invalid, |
|
63 |
#vakata-dragged.jstree-requirement-spec .jstree-ok, |
|
64 |
#jstree-marker.jstree-requirement-spec { _background-image:url("d.gif"); } |
|
65 |
.jstree-requirement-spec .jstree-open ins { _background-position:-72px 0; } |
|
66 |
.jstree-requirement-spec .jstree-closed ins { _background-position:-54px 0; } |
|
67 |
.jstree-requirement-spec .jstree-leaf ins { _background-position:-36px 0; } |
|
68 |
.jstree-requirement-spec a ins.jstree-icon { _background-position:-56px -19px; } |
|
69 |
#vakata-contextmenu.jstree-requirement-spec-context ins { _display:none; } |
|
70 |
#vakata-contextmenu.jstree-requirement-spec-context li { _zoom:1; } |
|
71 |
.jstree-requirement-spec .jstree-undetermined a .jstree-checkbox { _background-position:-20px -19px; } |
|
72 |
.jstree-requirement-spec .jstree-checked a .jstree-checkbox { _background-position:-38px -19px; } |
|
73 |
.jstree-requirement-spec .jstree-unchecked a .jstree-checkbox { _background-position:-2px -19px; } |
|
74 |
/* IE6 END */ |
locale/de/all | ||
---|---|---|
1898 | 1898 |
'Searchable' => 'Durchsuchbar', |
1899 | 1899 |
'Secondary sorting' => 'Untersortierung', |
1900 | 1900 |
'Section "#1"' => 'Abschnitt "#1"', |
1901 |
'Sections' => 'Abschnitte', |
|
1901 | 1902 |
'Select' => 'auswählen', |
1902 | 1903 |
'Select a Customer' => 'Endkunde auswählen', |
1903 | 1904 |
'Select a customer' => 'Einen Kunden auswählen', |
... | ... | |
2119 | 2120 |
'Terms missing in row ' => '+Tage fehlen in Zeile ', |
2120 | 2121 |
'Test and preview' => 'Test und Vorschau', |
2121 | 2122 |
'Test database connectivity' => 'Datenbankverbindung testen', |
2123 |
'Text blocks back' => 'Textblöcke hinten', |
|
2124 |
'Text blocks front' => 'Textblöcke vorne', |
|
2122 | 2125 |
'Text field' => 'Textfeld', |
2123 | 2126 |
'Text field variables: \'WIDTH=w HEIGHT=h\' sets the width and height of the text field. They default to 30 and 5 respectively.' => 'Textfelder: \'WIDTH=w HEIGHT=h\' setzen die Breite und die Höhe des Textfeldes. Wenn nicht anders angegeben, so werden sie 30 Zeichen breit und fünf Zeichen hoch dargestellt.', |
2124 | 2127 |
'Text variables: \'MAXLENGTH=n\' sets the maximum entry length to \'n\'.' => 'Textzeilen: \'MAXLENGTH=n\' setzt eine Maximallänge von n Zeichen.', |
templates/webpages/requirement_spec/tree.html | ||
---|---|---|
1 |
[%- USE HTML %][%- USE L %][%- USE LxERP %][% USE P %][% USE JSON %] |
|
2 |
|
|
3 |
[%- L.hidden_tag('requirement_spec_id', SELF.requirement_spec.id) -%] |
|
4 |
|
|
5 |
<div id="page" class="ym-grid ym-equalize"> |
|
6 |
<div class="ym-g25 ym-gl" style="border-right: 1px solid black"> |
|
7 |
<div style="min-height: 32px; height: 32px;"> |
|
8 |
<div style="float: left"> |
|
9 |
[% L.button_tag("new_section_form()", LxERP.t8("New section"), id="new-section-button") %] |
|
10 |
</div> |
|
11 |
<div id="spinner" style="float: right; display: none; background:url('js/themes/requirement-spec/throbber.gif') center center no-repeat !important; min-height: 32px; height: 32px; min-width: 32px; width: 32px;"></div> |
|
12 |
<div style="clear: both"></div> |
|
13 |
</div> |
|
14 |
|
|
15 |
<div id="tree"></div> |
|
16 |
</div> |
|
17 |
|
|
18 |
<div class="ym-gl"> |
|
19 |
<div id="content-column"> |
|
20 |
<p>There's beauty in the breakdown. 0</p> |
|
21 |
<p>There's beauty in the breakdown. 1</p> |
|
22 |
<p>There's beauty in the breakdown. 2</p> |
|
23 |
<p>There's beauty in the breakdown. 3</p> |
|
24 |
<p>There's beauty in the breakdown. 4</p> |
|
25 |
<p>There's beauty in the breakdown. 5</p> |
|
26 |
<p>There's beauty in the breakdown. 6</p> |
|
27 |
<p>There's beauty in the breakdown. 7</p> |
|
28 |
<p>There's beauty in the breakdown. 8</p> |
|
29 |
<p>There's beauty in the breakdown. 9</p> |
|
30 |
<p>There's beauty in the breakdown. 10</p> |
|
31 |
<p>There's beauty in the breakdown. 11</p> |
|
32 |
<p>There's beauty in the breakdown. 12</p> |
|
33 |
<p>There's beauty in the breakdown. 13</p> |
|
34 |
<p>There's beauty in the breakdown. 14</p> |
|
35 |
<p>There's beauty in the breakdown. 15</p> |
|
36 |
<p>There's beauty in the breakdown. 16</p> |
|
37 |
<p>There's beauty in the breakdown. 17</p> |
|
38 |
<p>There's beauty in the breakdown. 18</p> |
|
39 |
<p>There's beauty in the breakdown. 19</p> |
|
40 |
<p>There's beauty in the breakdown. 20</p> |
|
41 |
<p>There's beauty in the breakdown. 21</p> |
|
42 |
<p>There's beauty in the breakdown. 22</p> |
|
43 |
<!-- <p>There's beauty in the breakdown. 23</p> --> |
|
44 |
<!-- <p>There's beauty in the breakdown. 24</p> --> |
|
45 |
<!-- <p>There's beauty in the breakdown. 25</p> --> |
|
46 |
<!-- <p>There's beauty in the breakdown. 26</p> --> |
|
47 |
<!-- <p>There's beauty in the breakdown. 27</p> --> |
|
48 |
<!-- <p>There's beauty in the breakdown. 28</p> --> |
|
49 |
<!-- <p>There's beauty in the breakdown. 29</p> --> |
|
50 |
<!-- <p>There's beauty in the breakdown. 30</p> --> |
|
51 |
<!-- <p>There's beauty in the breakdown. 31</p> --> |
|
52 |
<!-- <p>There's beauty in the breakdown. 32</p> --> |
|
53 |
<!-- <p>There's beauty in the breakdown. 33</p> --> |
|
54 |
<!-- <p>There's beauty in the breakdown. 34</p> --> |
|
55 |
<!-- <p>There's beauty in the breakdown. 35</p> --> |
|
56 |
</div> |
|
57 |
</div> |
|
58 |
</div> |
|
59 |
|
|
60 |
<script type="text/javascript"> |
|
61 |
<!-- |
|
62 |
var tree_data = [ |
|
63 |
{ "data": [% JSON.json(LxERP.t8("Text blocks front")) %], |
|
64 |
"metadata": { "type": "textblocks-front" }, |
|
65 |
"attr": { "id": "tb-front" }, |
|
66 |
"children": [ |
|
67 |
[% FOREACH tb = SELF.requirement_spec.text_blocks_for_position(0) %] |
|
68 |
[% P.requirement_spec_text_block_jstree_data(tb).json %][% IF !loop.last %],[% END %] |
|
69 |
[% END %] |
|
70 |
] |
|
71 |
}, |
|
72 |
|
|
73 |
{ "data": [% JSON.json(LxERP.t8("Sections")) %], |
|
74 |
"metadata": { "type": "sections" }, |
|
75 |
"attr": { "id": "sections" }, |
|
76 |
"children": [ |
|
77 |
|
|
78 |
[% FOREACH section = SELF.requirement_spec.sections %] |
|
79 |
[% P.requirement_spec_item_jstree_data(section).json %][% IF !loop.last %],[% END %] |
|
80 |
[% END %] |
|
81 |
] |
|
82 |
}, |
|
83 |
|
|
84 |
{ "data": [% JSON.json(LxERP.t8("Text blocks back")) %], |
|
85 |
"metadata": { "type": "textblocks-back" }, |
|
86 |
"attr": { "id": "tb-back" }, |
|
87 |
"children": [ |
|
88 |
[% FOREACH tb = SELF.requirement_spec.text_blocks_for_position(1) %] |
|
89 |
[% P.requirement_spec_text_block_jstree_data(tb).json %][% IF !loop.last %],[% END %] |
|
90 |
[% END %] |
|
91 |
] |
|
92 |
} |
|
93 |
]; |
|
94 |
|
|
95 |
$(function() { |
|
96 |
$('#tree').jstree({ |
|
97 |
"core": { |
|
98 |
"animation": 0, |
|
99 |
"initially_open": [ "tb-front", "tb-back", "sections" |
|
100 |
[%- FOREACH section = SELF.requirement_spec.sections -%] |
|
101 |
, "fb-[% section.id %]" |
|
102 |
[%- FOREACH function_block = section.children -%] |
|
103 |
, "fb-[% function_block.id -%]" |
|
104 |
[%- END -%] |
|
105 |
[%- END -%] |
|
106 |
] |
|
107 |
}, |
|
108 |
"json_data": { |
|
109 |
"data": tree_data |
|
110 |
}, |
|
111 |
"crrm": { |
|
112 |
"move": { |
|
113 |
"check_move": check_move, |
|
114 |
"open_move": true |
|
115 |
} |
|
116 |
}, |
|
117 |
"themes": { |
|
118 |
"theme": "requirement-spec" |
|
119 |
}, |
|
120 |
"plugins": [ "themes", "json_data", "ui", "crrm", "dnd" ] |
|
121 |
}) |
|
122 |
.bind("move_node.jstree", node_moved); |
|
123 |
}); |
|
124 |
|
|
125 |
$(document).ajaxSend(function() { |
|
126 |
$('#spinner').show(); |
|
127 |
}).ajaxStop(function() { |
|
128 |
$('#spinner').hide(); |
|
129 |
}); |
|
130 |
--> |
|
131 |
</script> |
templates/webpages/requirement_spec_item/_section_form.html | ||
---|---|---|
1 |
[%- USE HTML %][%- USE L %][%- USE LxERP %] |
|
2 |
[%- SET id_base="section-form-" _ HTML.escape(id) %] |
|
3 |
[%- IF title %] |
|
4 |
<div class="listtop">[%- HTML.escape(title) %]</div> |
|
5 |
[%- END -%] |
|
6 |
|
|
7 |
<form id="[% id_base %]"> |
|
8 |
[% L.hidden_tag("requirment_spec_id", SELF.item.requirement_spec_id, id=(id_base _ "-requirement-spec-id")) %] |
|
9 |
|
|
10 |
<p> |
|
11 |
[%- LxERP.t8("Title") %]:<br> |
|
12 |
[% L.input_tag("title", SELF.item.title, id=(id_base _ "-title")) %] |
|
13 |
</p> |
|
14 |
|
|
15 |
<p> |
|
16 |
[%- LxERP.t8("Description") %]:<br> |
|
17 |
[% L.textarea_tag("description", SELF.item.description, id=(id_base _ "-title"), rows=8, cols=80) %] |
|
18 |
</p> |
|
19 |
|
|
20 |
<p> |
|
21 |
[% L.button_tag("submit_section_form('" _ HTML.escape(id) _ "')", LxERP.t8("Save")) %] |
|
22 |
[% L.button_tag("cancel_section_form('" _ HTML.escape(id) _ "')", LxERP.t8("Cancel")) %] |
|
23 |
</p> |
|
24 |
</form> |
Auch abrufbar als: Unified diff
Pflichtenhefte: Erste Version Baumansicht Textblöcke/Abschnitte/Funktionsblöcke