Pflichtenhefte bearbeiten
authorMoritz Bunkus <m.bunkus@linet-services.de>
Fri, 8 Mar 2013 14:32:32 +0000 (15:32 +0100)
committerMoritz Bunkus <m.bunkus@linet-services.de>
Tue, 1 Apr 2014 11:02:24 +0000 (13:02 +0200)
14 files changed:
SL/Controller/RequirementSpec.pm
SL/Controller/RequirementSpecItem.pm
SL/DB/RequirementSpec.pm
SL/Presenter.pm
SL/Presenter/RequirementSpec.pm
SL/Presenter/RequirementSpecItem.pm [new file with mode: 0644]
css/requirement_spec.css
templates/webpages/requirement_spec/_version.html [new file with mode: 0644]
templates/webpages/requirement_spec/show.html [new file with mode: 0644]
templates/webpages/requirement_spec/tree.html
templates/webpages/requirement_spec_item/_section_form.html
templates/webpages/requirement_spec_item/_section_header.html [new file with mode: 0644]
templates/webpages/requirement_spec_item/_single_section.html [new file with mode: 0644]
templates/webpages/requirement_spec_item/_single_subitem.html [new file with mode: 0644]

index d7d8978..8aac6df 100644 (file)
@@ -19,11 +19,11 @@ use SL::Locale::String;
 
 use Rose::Object::MakeMethods::Generic
 (
- scalar => [ qw(requirement_spec customers projects types statuses db_args flat_filter is_template) ],
+ scalar => [ qw(requirement_spec requirement_spec_item customers projects types statuses db_args flat_filter is_template) ],
 );
 
 __PACKAGE__->run_before('setup');
-__PACKAGE__->run_before('load_requirement_spec',      only => [ qw(    edit        update destroy tree) ]);
+__PACKAGE__->run_before('load_requirement_spec',      only => [ qw(    edit        update show destroy tree) ]);
 __PACKAGE__->run_before('load_select_options',        only => [ qw(new edit create update list) ]);
 __PACKAGE__->run_before('load_search_select_options', only => [ qw(                       list) ]);
 
@@ -77,6 +77,15 @@ sub action_edit {
   $self->render('requirement_spec/form', title => t8('Edit requirement spec'));
 }
 
+sub action_show {
+  my ($self) = @_;
+
+  my $item = $::form->{requirement_spec_item_id} ? SL::DB::RequirementSpecItem->new(id => $::form->{requirement_spec_item_id})->load : @{ $self->requirement_spec->sections }[0];
+  $self->requirement_spec_item($item);
+
+  $self->render('requirement_spec/show', title => t8('Show requirement spec'));
+}
+
 sub action_create {
   my ($self) = @_;
 
@@ -122,8 +131,8 @@ sub setup {
   my ($self) = @_;
 
   $::auth->assert('config');
-  $::request->{layout}->use_stylesheet("${_}.css") for qw(requirement_spec yaml/core/base.min);
-  $::request->{layout}->use_javascript("${_}.js") for qw(jquery.jstree requirement_spec);
+  $::request->{layout}->use_stylesheet("${_}.css") for qw(jquery.contextMenu requirement_spec);
+  $::request->{layout}->use_javascript("${_}.js") for qw(jquery.jstree jquery/jquery.contextMenu requirement_spec);
   $self->is_template($::form->{is_template} ? 1 : 0);
 
   return 1;
index 6ddf3a6..9dfdad0 100644 (file)
@@ -18,7 +18,7 @@ use Rose::Object::MakeMethods::Generic
 );
 
 # __PACKAGE__->run_before('load_requirement_spec');
-__PACKAGE__->run_before('load_requirement_spec_item', only => [qw(dragged_and_dropped)]);
+__PACKAGE__->run_before('load_requirement_spec_item', only => [qw(dragged_and_dropped edit_section update_section)]);
 
 #
 # actions
@@ -64,6 +64,24 @@ sub action_dragged_and_dropped {
   $self->render(\'', { type => 'json' });
 }
 
+sub action_edit_section {
+  my ($self, %params) = @_;
+  $self->render('requirement_spec_item/_section_form', { layout => 0 });
+}
+
+sub action_update_section {
+  my ($self, %params) = @_;
+
+  $self->item->update_attributes(title => $::form->{title}, description => $::form->{description});
+
+  my $result = {
+    id          => $self->item->id,
+    header_html => $self->render('requirement_spec_item/_section_header', { layout => 0, output => 0 }, requirement_spec_item => $self->item),
+    node_name   => join(' ', map { $_ || '' } ($self->item->fb_number, $self->item->title)),
+  };
+  $self->render(\to_json($result), { type => 'json' });
+}
+
 #
 # filters
 #
index 2c2725e..3a3f4cc 100644 (file)
@@ -57,4 +57,10 @@ sub sections {
   return [ sort { $a->position <=> $b->position } grep { !$_->parent_id } @{ $self->items } ];
 }
 
+sub displayable_name {
+  my ($self) = @_;
+
+  return sprintf('%s: "%s"', $self->type->description, $self->title);
+}
+
 1;
index f0a0259..67b37df 100644 (file)
@@ -16,6 +16,7 @@ use SL::Presenter::Part;
 use SL::Presenter::Project;
 use SL::Presenter::Record;
 use SL::Presenter::RequirementSpec;
+use SL::Presenter::RequirementSpecItem;
 use SL::Presenter::SepaExport;
 use SL::Presenter::Text;
 use SL::Presenter::Tag;
index fe9df96..b8a202d 100644 (file)
@@ -5,8 +5,7 @@ use strict;
 use parent qw(Exporter);
 
 use Exporter qw(import);
-our @EXPORT = qw(requirement_spec_text_block_jstree_data
-                 requirement_spec_item_jstree_data);
+our @EXPORT = qw(requirement_spec_text_block_jstree_data);
 
 use Carp;
 
@@ -22,18 +21,4 @@ sub requirement_spec_text_block_jstree_data {
   };
 }
 
-sub requirement_spec_item_jstree_data {
-  my ($self, $item, %params) = @_;
-
-  my @children = map { $self->requirement_spec_item_jstree_data($_, %params) } @{ $item->sorted_children };
-  my $type     = !$item->parent_id ? 'section' : 'functionblock';
-
-  return {
-    data     => join(' ', map { $_ || '' } ($item->fb_number, $item->title)),
-    metadata => { id =>         $item->id, type => $type },
-    attr     => { id => "fb-" . $item->id, href => $params{href} || '#' },
-    children => \@children,
-  };
-}
-
 1;
diff --git a/SL/Presenter/RequirementSpecItem.pm b/SL/Presenter/RequirementSpecItem.pm
new file mode 100644 (file)
index 0000000..cdf05cf
--- /dev/null
@@ -0,0 +1,32 @@
+package SL::Presenter::RequirementSpecItem;
+
+use strict;
+
+use parent qw(Exporter);
+
+use Exporter qw(import);
+our @EXPORT = qw(requirement_spec_item_jstree_data requirement_spec_item_dependency_list);
+
+use Carp;
+
+sub requirement_spec_item_jstree_data {
+  my ($self, $item, %params) = @_;
+
+  my @children = map { $self->requirement_spec_item_jstree_data($_, %params) } @{ $item->sorted_children };
+  my $type     = !$item->parent_id ? 'section' : 'functionblock';
+
+  return {
+    data     => join(' ', map { $_ || '' } ($item->fb_number, $item->title)),
+    metadata => { id =>         $item->id, type => $type },
+    attr     => { id => "fb-" . $item->id, href => $params{href} || '#' },
+    children => \@children,
+  };
+}
+
+sub requirement_spec_item_dependency_list {
+  my ($self, $item) = @_;
+
+  $::locale->language_join([ map { $_->fb_number } @{ $item->dependencies } ]);
+}
+
+1;
index 08b6d2b..82b895e 100644 (file)
@@ -3,6 +3,25 @@ table.rs_input_field input, table.rs_input_field select {
   width: 300px;
 }
 
+#column-container {
+  width: 100%;
+  padding-left: 0;
+  padding-right: 0;
+  margin-left: 0;
+  margin-right: 0;
+}
+
+#tree-column {
+  float: left;
+  width: 25%;
+  border-right: 1px solid black;
+}
+
 #content-column {
-  margin-left: 10px;
+  float: left;
+  padding-left: 10px;
+}
+
+.section-empty-description {
+  color: #bbb;
 }
diff --git a/templates/webpages/requirement_spec/_version.html b/templates/webpages/requirement_spec/_version.html
new file mode 100644 (file)
index 0000000..8a73f54
--- /dev/null
@@ -0,0 +1,9 @@
+[%- USE L -%][%- USE LxERP -%][%- USE HTML -%]
+[% L.stuff %]
+[% LxERP.t8("Current version") %]:
+[% IF !requirement_spec.version_id %]
+ [% LxERP.t8("Working copy without version") %]
+[% ELSE %]
+ [% LxERP.t8("Version") %] [% HTML.escape(requirement_spec.version.version_number) %]
+ [% LxERP.t8("dated") %] [% HTML.escape(requirement_spec.version.itime.displayable_date) %]
+[%- END -%]
diff --git a/templates/webpages/requirement_spec/show.html b/templates/webpages/requirement_spec/show.html
new file mode 100644 (file)
index 0000000..f6c15c2
--- /dev/null
@@ -0,0 +1,112 @@
+[%- USE JSON -%][%- USE HTML %][%- USE L %][%- USE LxERP %][%- USE P -%]
+
+<h1>[%- HTML.escape(SELF.requirement_spec.displayable_name('format', 'with_customer')) %]
+  [% LxERP.t8("for") %]
+  [% HTML.escape(SELF.requirement_spec.customer.displayable_name) -%]
+</h1>
+
+[%- L.hidden_tag('requirement_spec_id', SELF.requirement_spec.id) -%]
+
+<div id="requirement_spec_version">
+  [%- INCLUDE 'requirement_spec/_version.html' requirement_spec=SELF.requirement_spec -%]
+</div>
+
+<div id="column-container">
+ <div id="tree-column" style="border-right: 1px solid black">
+  <div style="min-height: 32px; height: 32px;">
+   <div style="float: left">
+    [% L.button_tag("new_section_form()", LxERP.t8("New section"), id="new-section-button") %]
+   </div>
+   <div id="spinner" class="clearfix" 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>
+  </div>
+
+  <div id="tree"></div>
+ </div>
+
+ <div id="content-column" class="clearfix">
+  <div id="section-container" class="section-container">
+
+   <div id="section_content" class="section-content">
+    [%- IF SELF.requirement_spec_item && SELF.requirement_spec_item.id -%]
+     [%- INCLUDE 'requirement_spec_item/_single_section.html' requirement_spec_item=SELF.requirement_spec_item -%]
+    [%- ELSE -%]
+     no section
+     [%#- render :partial => 'requirement_spec_items/no_section' -%]
+    [%- END -%]
+   </div>
+  </div>
+ </div>
+</div>
+
+<script type="text/javascript">
+ <!--
+     var tree_data = [
+       { "data": [% JSON.json(LxERP.t8("Text blocks front")) %],
+         "metadata": { "type": "textblocks-front" },
+         "attr": { "id": "tb-front" },
+         "children": [
+[% FOREACH tb = SELF.requirement_spec.text_blocks_for_position(0) %]
+ [% P.requirement_spec_text_block_jstree_data(tb).json %][% IF !loop.last %],[% END %]
+[% END %]
+         ]
+       },
+
+       { "data": [% JSON.json(LxERP.t8("Sections")) %],
+         "metadata": { "type": "sections" },
+         "attr": { "id": "sections" },
+         "children": [
+
+[% FOREACH section = SELF.requirement_spec.sections %]
+ [% P.requirement_spec_item_jstree_data(section).json %][% IF !loop.last %],[% END %]
+[% END %]
+         ]
+       },
+
+       { "data": [% JSON.json(LxERP.t8("Text blocks back")) %],
+         "metadata": { "type": "textblocks-back" },
+         "attr": { "id": "tb-back" },
+         "children": [
+[% FOREACH tb = SELF.requirement_spec.text_blocks_for_position(1) %]
+ [% P.requirement_spec_text_block_jstree_data(tb).json %][% IF !loop.last %],[% END %]
+[% END %]
+         ]
+       }
+     ];
+
+     $(function() {
+       $('#tree').jstree({
+         "core": {
+           "animation": 0,
+           "initially_open": [ "tb-front", "tb-back", "sections"
+[%- FOREACH section = SELF.requirement_spec.sections -%]
+ , "fb-[% section.id %]"
+ [%- FOREACH function_block = section.children -%]
+  , "fb-[% function_block.id -%]"
+ [%- END -%]
+[%- END -%]
+ ]
+         },
+         "json_data": {
+           "data": tree_data
+         },
+         "crrm": {
+           "move": {
+             "check_move": check_move,
+             "open_move": true
+           }
+         },
+         "themes": {
+           "theme": "requirement-spec"
+         },
+         "plugins": [ "themes", "json_data", "ui", "crrm", "dnd" ]
+       })
+       .bind("move_node.jstree", node_moved);
+
+       $(document).ajaxSend(function() {
+         $('#spinner').show();
+       }).ajaxStop(function() {
+         $('#spinner').hide();
+       });
+     });
+  -->
+</script>
index 85ac0e3..2c5b306 100644 (file)
@@ -2,58 +2,55 @@
 
 [%- L.hidden_tag('requirement_spec_id', SELF.requirement_spec.id) -%]
 
-<div id="page" class="ym-grid ym-equalize">
- <div class="ym-g25 ym-gl" style="border-right: 1px solid black">
+<div id="column-container">
+ <div id="tree-column">
   <div style="min-height: 32px; height: 32px;">
    <div style="float: left">
     [% L.button_tag("new_section_form()", LxERP.t8("New section"), id="new-section-button") %]
    </div>
-   <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>
    <div style="clear: both"></div>
   </div>
 
   <div id="tree"></div>
  </div>
 
- <div class="ym-gl">
-  <div id="content-column">
-   <p>There's beauty in the breakdown. 0</p>
-   <p>There's beauty in the breakdown. 1</p>
-   <p>There's beauty in the breakdown. 2</p>
-   <p>There's beauty in the breakdown. 3</p>
-   <p>There's beauty in the breakdown. 4</p>
-   <p>There's beauty in the breakdown. 5</p>
-   <p>There's beauty in the breakdown. 6</p>
-   <p>There's beauty in the breakdown. 7</p>
-   <p>There's beauty in the breakdown. 8</p>
-   <p>There's beauty in the breakdown. 9</p>
-   <p>There's beauty in the breakdown. 10</p>
-   <p>There's beauty in the breakdown. 11</p>
-   <p>There's beauty in the breakdown. 12</p>
-   <p>There's beauty in the breakdown. 13</p>
-   <p>There's beauty in the breakdown. 14</p>
-   <p>There's beauty in the breakdown. 15</p>
-   <p>There's beauty in the breakdown. 16</p>
-   <p>There's beauty in the breakdown. 17</p>
-   <p>There's beauty in the breakdown. 18</p>
-   <p>There's beauty in the breakdown. 19</p>
-   <p>There's beauty in the breakdown. 20</p>
-   <p>There's beauty in the breakdown. 21</p>
-   <p>There's beauty in the breakdown. 22</p>
-   <!-- <p>There's beauty in the breakdown. 23</p> -->
-   <!-- <p>There's beauty in the breakdown. 24</p> -->
-   <!-- <p>There's beauty in the breakdown. 25</p> -->
-   <!-- <p>There's beauty in the breakdown. 26</p> -->
-   <!-- <p>There's beauty in the breakdown. 27</p> -->
-   <!-- <p>There's beauty in the breakdown. 28</p> -->
-   <!-- <p>There's beauty in the breakdown. 29</p> -->
-   <!-- <p>There's beauty in the breakdown. 30</p> -->
-   <!-- <p>There's beauty in the breakdown. 31</p> -->
-   <!-- <p>There's beauty in the breakdown. 32</p> -->
-   <!-- <p>There's beauty in the breakdown. 33</p> -->
-   <!-- <p>There's beauty in the breakdown. 34</p> -->
-   <!-- <p>There's beauty in the breakdown. 35</p> -->
-  </div>
+ <div id="content-column" class="clearfix">
+  <p>There's beauty in the breakdown. 0</p>
+  <p>There's beauty in the breakdown. 1</p>
+  <p>There's beauty in the breakdown. 2</p>
+  <p>There's beauty in the breakdown. 3</p>
+  <p>There's beauty in the breakdown. 4</p>
+  <p>There's beauty in the breakdown. 5</p>
+  <p>There's beauty in the breakdown. 6</p>
+  <p>There's beauty in the breakdown. 7</p>
+  <p>There's beauty in the breakdown. 8</p>
+  <p>There's beauty in the breakdown. 9</p>
+  <p>There's beauty in the breakdown. 10</p>
+  <p>There's beauty in the breakdown. 11</p>
+  <p>There's beauty in the breakdown. 12</p>
+  <p>There's beauty in the breakdown. 13</p>
+  <p>There's beauty in the breakdown. 14</p>
+  <p>There's beauty in the breakdown. 15</p>
+  <p>There's beauty in the breakdown. 16</p>
+  <p>There's beauty in the breakdown. 17</p>
+  <p>There's beauty in the breakdown. 18</p>
+  <p>There's beauty in the breakdown. 19</p>
+  <p>There's beauty in the breakdown. 20</p>
+  <p>There's beauty in the breakdown. 21</p>
+  <p>There's beauty in the breakdown. 22</p>
+  <!-- <p>There's beauty in the breakdown. 23</p> -->
+  <!-- <p>There's beauty in the breakdown. 24</p> -->
+  <!-- <p>There's beauty in the breakdown. 25</p> -->
+  <!-- <p>There's beauty in the breakdown. 26</p> -->
+  <!-- <p>There's beauty in the breakdown. 27</p> -->
+  <!-- <p>There's beauty in the breakdown. 28</p> -->
+  <!-- <p>There's beauty in the breakdown. 29</p> -->
+  <!-- <p>There's beauty in the breakdown. 30</p> -->
+  <!-- <p>There's beauty in the breakdown. 31</p> -->
+  <!-- <p>There's beauty in the breakdown. 32</p> -->
+  <!-- <p>There's beauty in the breakdown. 33</p> -->
+  <!-- <p>There's beauty in the breakdown. 34</p> -->
+  <!-- <p>There's beauty in the breakdown. 35</p> -->
  </div>
 </div>
 
        })
        .bind("move_node.jstree", node_moved);
      });
-
-     $(document).ajaxSend(function() {
-       $('#spinner').show();
-     }).ajaxStop(function() {
-       $('#spinner').hide();
-     });
   -->
 </script>
index 12f8e65..c2ae0da 100644 (file)
@@ -1,24 +1,25 @@
 [%- USE HTML %][%- USE L %][%- USE LxERP %]
-[%- SET id_base="section-form-" _ HTML.escape(id) %]
-[%- IF title %]
- <div class="listtop">[%- HTML.escape(title) %]</div>
-[%- END -%]
+[%- SET id_base="section-form" %]
+<div id="[% id_base %]">
+ <h1>[%- LxERP.t8("Edit section #1", SELF.item.fb_number) %]</h1>
 
-<form id="[% id_base %]">
- [% L.hidden_tag("requirment_spec_id", SELF.item.requirement_spec_id, id=(id_base _ "-requirement-spec-id")) %]
+ <form>
+  [% L.hidden_tag("requirement_spec_id", SELF.item.requirement_spec_id, id=(id_base _ "-requirement-spec-id")) %]
+  [% L.hidden_tag("id", SELF.item.id, id=(id_base _ "-requirement-spec-item-id")) %]
 
- <p>
-  [%- LxERP.t8("Title") %]:<br>
-  [% L.input_tag("title", SELF.item.title, id=(id_base _ "-title")) %]
- </p>
 <p>
+   [%- LxERP.t8("Title") %]:<br>
+   [% L.input_tag("title", SELF.item.title, id=(id_base _ "-title")) %]
 </p>
 
- <p>
-  [%- LxERP.t8("Description") %]:<br>
-  [% L.textarea_tag("description", SELF.item.description, id=(id_base _ "-title"), rows=8, cols=80) %]
- </p>
 <p>
+   [%- LxERP.t8("Description") %]:<br>
+   [% L.textarea_tag("description", SELF.item.description, id=(id_base _ "-description"), rows=8, cols=80) %]
 </p>
 
- <p>
-  [% L.button_tag("submit_section_form('" _ HTML.escape(id) _ "')", LxERP.t8("Save")) %]
-  [% L.button_tag("cancel_section_form('" _ HTML.escape(id) _ "')", LxERP.t8("Cancel")) %]
- </p>
-</form>
+  <p>
+   [% L.button_tag("submit_section_form()", LxERP.t8("Save")) %]
+   <a href="#" onclick="cancel_section_form()">[% LxERP.t8("Cancel") %]</a>
+  </p>
+ </form>
+</div>
diff --git a/templates/webpages/requirement_spec_item/_section_header.html b/templates/webpages/requirement_spec_item/_section_header.html
new file mode 100644 (file)
index 0000000..60c9deb
--- /dev/null
@@ -0,0 +1,12 @@
+[%- USE HTML -%][%- USE L -%][%- USE LxERP -%]
+<h1>
+ [%- HTML.escape(requirement_spec_item.fb_number) %]: [% HTML.escape(requirement_spec_item.title) -%]
+</h1>
+
+[% IF requirement_spec_item.description %]
+ <div class="section-description">
+  [%- L.simple_format(requirement_spec_item.description) -%]
+ </div>
+[%- ELSE %]
+ <div class="section-empty-description">[%- LxERP.t8("No description has been entered yet.") %]</div>
+[%- END %]
diff --git a/templates/webpages/requirement_spec_item/_single_section.html b/templates/webpages/requirement_spec_item/_single_section.html
new file mode 100644 (file)
index 0000000..dc49933
--- /dev/null
@@ -0,0 +1,90 @@
+[%- USE HTML -%][%- USE LxERP -%][%- USE L -%]
+<div class="section-context-menu" id="section-header">
+ [%- INCLUDE 'requirement_spec_item/_section_header.html' %]
+</div>
+
+[%- L.hidden_tag('active_section_id', requirement_spec_item.id) -%]
+
+<ul id="section" class="section function-block-context-menu">
+  [%- FOREACH subitem = requirement_spec_item.children -%]
+    [%- INCLUDE 'requirement_spec_item/_single_subitem.html' requirement_spec_item=subitem -%]
+  [%- END -%]
+</ul>
+
+<div id="new_subitem_form" class="subitem-form clearfix">
+</div>
+<div id="new_subitem_link" class="highlight-box" style="[%- 'display:none' IF requirement_spec_item.children.size -%]">
+  [%#- link_to_remote LxERP.t8("FIXME no_function_blocks_have_been_created_for_this_section_yet) + " " + t(:create_a_function_block_now"),
+                      :url    => new_requirement_spec_requirement_spec_item_url(requirement_spec_item.requirement_spec,
+                      :requirement_spec_item_id => requirement_spec_item.id),
+                      :method => :get,
+                      :update => "new_subitem_form",
+                      :loading => "$('new_subitem_link').hide();" -%]
+</div>
+
+<script type="text/javascript">
+ <!--
+$(function(){
+    $.contextMenu({
+        selector: '.section-context-menu',
+        callback: function(key, options) {
+            var m = "clicked: " + key;
+            window.console && console.log(m) || alert(m);
+        },
+        items: {
+          edit:   { name: "[% LxERP.t8('Edit section') %]", icon: "edit", callback: edit_section_header },
+          delete: { name: "[% LxERP.t8('Delete section') %]", icon: "delete" },
+          sep1:   "---------",
+          copy:   { name: "[% LxERP.t8('Copy') %]", icon: "copy" },
+          paste:  { name: "[% LxERP.t8('Paste') %]", icon: "paste" }
+        }
+      });
+
+    $.contextMenu({
+        selector: '.function-block-context-menu',
+        callback: function(key, options) {
+            var m = "clicked: " + key;
+            window.console && console.log(m) || alert(m);
+        },
+        items: {
+          new_item:     { name: "[% LxERP.t8('New function block') %]",     icon: "add"},
+          new_sub_item: { name: "[% LxERP.t8('New sub function block') %]", icon: "add"},
+          sep1:   "---------",
+          edit:   { name: "[% LxERP.t8('Edit') %]",   icon: "edit"},
+          delete: { name: "[% LxERP.t8('Delete') %]", icon: "delete"},
+          sep2:   "---------",
+          copy:   { name: "[% LxERP.t8('Copy') %]", icon: "copy" },
+          paste:  { name: "[% LxERP.t8('Paste') %]", icon: "paste" }
+        }
+    });
+
+    $('.section-context-menu').on('click', function(e){
+        console.log('clicked', this);
+    });
+});
+
+function edit_section_header() {
+  $.post("controller.pl?action=RequirementSpecItem/edit_section&id=" + encodeURIComponent($('#active_section_id').val()), function(data) {
+    var header = $('#section-header');
+    header.data('old-elements', header.children().detach());
+    header.html(data);
+  });
+}
+
+function submit_section_form() {
+  $.post("controller.pl?action=RequirementSpecItem/update_section&id=" + $('#section-form form').serialize(), function(data) {
+    var header = $('#section-header');
+    header.removeData('old-elements');
+    header.html(data['header_html']);
+    $('#tree').jstree('rename_node', '#fb-' + data['id'], data['node_name']);
+  });
+}
+
+function cancel_section_form() {
+  var header = $('#section-header');
+  header.empty();
+  header.append(header.data('old-elements'));
+  header.removeData('old-elements');
+}
+-->
+</script>
diff --git a/templates/webpages/requirement_spec_item/_single_subitem.html b/templates/webpages/requirement_spec_item/_single_subitem.html
new file mode 100644 (file)
index 0000000..c2af84c
--- /dev/null
@@ -0,0 +1,48 @@
+[%- USE HTML -%][%- USE LxERP -%][%- USE P -%]
+<li id="subitem_[%- requirement_spec_item.id -%]" class="subitem">
+
+  <div id="subitem_content_[%- requirement_spec_item.id -%]" class="subitem-content[%- IF requirement_spec_item.flagged -%] flagged[%- END -%]">
+
+    [%#- IF !@requirement_spec.project.nil? -%]
+     [%#- link_to image_tag("chronometer.png"), account_time_requirement_spec_requirement_spec_item_path(requirement_spec_item.requirement_spec, requirement_spec_item) -%]
+    [%#- END -%]
+    [%#- link_to_new_sub(requirement_spec_item.parent) -%]
+    [%#- link_to_remote image_tag("new_subsub.png"),
+                       :url       => new_requirement_spec_requirement_spec_item_url(requirement_spec_item.requirement_spec, :requirement_spec_item_id => requirement_spec_item.id),
+                       :method    => :get,
+                       :condition => "check_for_editbox()",
+                       :update    => "new_subsubitem_form_#{requirement_spec_item.id}" -%]
+
+    <b>[%- HTML.escape(requirement_spec_item.fb_number) -%]</b>
+    [%- HTML.escape(requirement_spec_item.description) -%]
+
+    <div class="subsubitem-container" id="subsubitem_container_[%- requirement_spec_item.id -%]">
+     <div class="subsubitem-header" id="subsubitem_header_[%- requirement_spec_item.id -%]"[%- IF !requirement_spec_item.children.size -%] style="display: none"[%- END -%]>
+      [%- LxERP.t8("Sub function blocks") -%]
+     </div>
+      [%- IF requirement_spec_item.children.size -%]
+        [%- FOREACH subsubitem = requirement_spec_item.children -%]
+          [%#- render :partial => "requirement_spec_items/single_subsubitem", :locals => {:requirement_spec_item => subsubitem} -%]
+        [%- END -%]
+      [%- END -%]
+    </div>
+
+    <div id="new_subsubitem_form_[%- requirement_spec_item.id -%]" class="subsubitem_form"></div>
+
+    <div class="smaller" style="text-align:right">
+      [%- IF requirement_spec_item.dependencies.size -%]
+        <span class="gray">
+          [%- LxERP.t8("Dependencies") -%]: [%- P.requirement_spec_item_dependency_list(requirement_spec_item.dependencies) -%]
+        </span><br>
+      [%- END -%]
+      <span class="gray">
+        [%- LxERP.t8("Complexity") -%]: [%- requirement_spec_item.requirement_spec_complexity.description IF requirement_spec_item.requirement_spec_complexity -%]
+        &nbsp; | &nbsp;
+        [%- LxERP.t8("Risk") -%]: [%- requirement_spec_item.requirement_spec_risk.description IF requirement_spec_item.requirement_spec_risk -%]
+        &nbsp; | &nbsp;
+        [%- LxERP.t8("Effort") -%]: [%#- render :partial => 'requirement_spec_items/time_estimation_item', :locals => { :item => requirement_spec_item } -%]
+      </span>
+
+    </div>
+  </div>
+</li>