use SL::ClientJS;
use SL::DB::RequirementSpec;
+use SL::DB::RequirementSpecPredefinedText;
use SL::DB::RequirementSpecTextBlock;
use SL::Helper::Flash;
use SL::JSON;
scalar => [ qw(requirement_spec text_block) ],
);
-__PACKAGE__->run_before('load_requirement_spec_text_block', only => [qw(dragged_and_dropped)]);
+__PACKAGE__->run_before('load_requirement_spec_text_block', only => [qw(ajax_edit update dragged_and_dropped)]);
#
# actions
my $js = SL::ClientJS->new;
if (!defined($current_where) || ($new_where != $current_where)) {
- my $text_blocks = SL::DB::Manager::RequirementSpecTextBlock->get_all_sorted(where => [ output_position => $new_where ]);
- my $html = $self->render('requirement_spec_text_block/ajax_list', { output => 0 }, TEXT_BLOCKS => $text_blocks, output_position => $new_where, nownow => DateTime->now_local);
+ my $text_blocks = SL::DB::Manager::RequirementSpecTextBlock->get_all_sorted(where => [ output_position => $new_where, requirement_spec_id => $::form->{requirement_spec_id} ]);
+ my $html = $self->render('requirement_spec_text_block/ajax_list', { output => 0 }, TEXT_BLOCKS => $text_blocks, output_position => $new_where);
$js->html('#column-content', $html)
}
$self->render($js);
}
+sub action_ajax_edit {
+ my ($self) = @_;
+
+ my $js = SL::ClientJS->new;
+
+ my $current_where = $self->output_position_from_id($::form->{current_content_id}, $::form->{current_content_type}) // -1;
+ if ($self->text_block->output_position != $current_where) {
+ my $text_blocks = $self->text_block->get_full_list;
+ my $html = $self->render('requirement_spec_text_block/ajax_list', { output => 0 }, TEXT_BLOCKS => $text_blocks, output_position => $self->text_block->output_position);
+
+ $js->html('#column-content', $html)
+ ->val('#current_content_type', 'text-block')
+ ->val('#current_content_id', $self->text_block->id);
+ }
+
+ my $predefined_texts = SL::DB::Manager::RequirementSpecPredefinedText->get_all_sorted;
+ my $html = $self->render('requirement_spec_text_block/_form', { output => 0 }, PREDEFINED_TEXTS => $predefined_texts);
+
+ $js->hide('#text-block-' . $self->text_block->id)
+ ->insertAfter($html, '#text-block-' . $self->text_block->id)
+ ->jstree->select_node('#tree', '#tb-' . $self->text_block->id)
+ ->render($self);
+}
+
+sub action_update {
+ my ($self, %params) = @_;
+
+ my $prefix = $::form->{form_prefix} || 'text_block';
+ my $attributes = $::form->{$prefix} || {};
+
+ foreach (qw(requirement_spec_id output_position)) {
+ delete $attributes->{$_} if !defined $attributes->{$_};
+ }
+
+ $self->text_block->update_attributes(%{ $attributes });
+
+ my $html = $self->render('requirement_spec_text_block/_text_block', { output => 0 }, text_block => $self->text_block);
+
+ SL::ClientJS->new
+ ->remove('#' . $prefix . '_form')
+ ->replaceWith('#text-block-' . $self->text_block->id, $html)
+ ->jstree->rename_node('#tree', '#tb-' . $self->text_block->id, $self->text_block->title)
+ ->render($self);
+}
+
+
sub action_dragged_and_dropped {
my ($self) = @_;
use SL::Presenter::Part;
use SL::Presenter::Project;
use SL::Presenter::Record;
-use SL::Presenter::RequirementSpec;
use SL::Presenter::RequirementSpecItem;
+use SL::Presenter::RequirementSpecTextBlock;
use SL::Presenter::SepaExport;
use SL::Presenter::Text;
use SL::Presenter::Tag;
/* Functions used for the requirement specs tree view */
-function check_move(data) {
+function requirement_spec_tree_check_move(data) {
var dragged_type = data.o.data('type');
var dropped_type = data.r.data('type');
return (2 <= dropped_depth) && ((dragged_depth + dropped_depth) <= 4);
}
-function node_moved(event) {
+function requirement_spec_tree_node_moved(event) {
console.debug("node moved");
var move_obj = $.jstree._reference('#tree')._get_move();
var dragged = move_obj.o;
return true;
}
-function node_clicked(event) {
+function requirement_spec_tree_node_clicked(event) {
var node = $.jstree._reference('#tree')._get_node(event.target);
var type = node ? node.data('type') : undefined;
var url = 'controller.pl?action='
$.get('controller.pl', {
action: (/^textblock/ ? 'RequirementSpecTextBlock' : 'RequirementSpecItem') + '/ajax_list.js',
+ requirement_spec_id: $('#requirement_spec_id').val(),
current_content_type: $('#current_content_type').val(),
current_content_id: $('#current_content_id').val(),
clicked_type: type,
function cancel_section_form(id) {
$('#content-column').html('intentionally empty');
}
+
+function find_text_block_id(clicked_elt) {
+ console.log("id: " + $(clicked_elt).attr('id'));
+ var id = $(clicked_elt).attr('id');
+ if (/^text-block-\d+$/.test(id)) {
+ console.log("find_text_block_id: case 1: " + id.substr(11));
+ return id.substr(11) * 1;
+ }
+
+ id = $(clicked_elt).closest("[id*=text-block-]").attr('id')
+ if (/^text-block-\d+$/.test(id)) {
+ console.log("find_text_block_id: case 2: " + id.substr(11));
+ return id.substr(11) * 1;
+ }
+
+ id = $(clicked_elt).closest("[id*=tb-]").attr('id')
+ if (/^tb-\d+$/.test(id)) {
+ console.log("find_text_block_id: case 3: " + id.substr(3));
+ return id.substr(3) * 1;
+ }
+
+ console.log("find_text_block_id: case undef");
+ return undefined;
+}
+
+function find_text_block_output_position(clicked_elt) {
+ var output_position = $(clicked_elt).closest('#text-block-list-container').find('#text_block_output_position').val();
+ if (output_position)
+ return output_position;
+
+ var type = $(clicked_elt).closest('#tb-back,#tb-front').data('type');
+ if (/^textblocks-(front|back)/.test(type))
+ return type == "textblocks-front" ? 0 : 1;
+
+ return undefined;
+}
+
+function disable_edit_text_block_commands(key, opt) {
+ return find_text_block_id(opt.$trigger) == undefined;
+}
+
+function edit_text_block(key, opt) {
+ var data = {
+ action: "RequirementSpecTextBlock/ajax_edit",
+ id: find_text_block_id(opt.$trigger),
+ current_content_type: $('#current_content_type').val(),
+ current_content_id: $('#current_content_id').val()
+ };
+ $.post("controller.pl", data, eval_json_result);
+ return true;
+}
+
+function add_text_block(key, opt) {
+ return true;
+}
+
+function delete_text_block(key, opt) {
+ var data = {
+ action: "RequirementSpecTextBlock/ajax_delete",
+ id: find_text_block_id(opt.$trigger),
+ current_content_type: $('#current_content_type').val(),
+ current_content_id: $('#current_content_id').val()
+ };
+ $.post("controller.pl", data, eval_json_result);
+ return true;
+}
+
+function submit_edit_text_block_form(id_base) {
+ var url = "controller.pl?" + $('#' + id_base + '_form').serialize();
+ var data = {
+ action: 'RequirementSpecTextBlock/update.js',
+ id: $('#' + id_base + '_id').val(),
+ form_prefix: id_base
+ };
+ console.log("posting edit text block: " + url);
+ console.log(data);
+ $.post(url, data, eval_json_result);
+}
+
+function cancel_edit_text_block_form(id_base) {
+ var id = $('#' + id_base + '_id').val();
+ $('#' + id_base + '_form').remove();
+ if (id)
+ $('#text-block-' + id).show();
+}
<script type="text/javascript">
<!--
var tree_data = [
- { "data": [% JSON.json(LxERP.t8("Text blocks front")) %],
- "metadata": { "type": "textblocks-front" },
- "attr": { "id": "tb-front" },
- "children": [
+ { data: [% JSON.json(LxERP.t8("Text blocks front")) %],
+ metadata: { type: "textblocks-front" },
+ attr: { id: "tb-front", class: "text-block-context-menu" },
+ 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": [
+ { 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 %]
]
},
- { "data": [% JSON.json(LxERP.t8("Text blocks back")) %],
- "metadata": { "type": "textblocks-back" },
- "attr": { "id": "tb-back" },
- "children": [
+ { data: [% JSON.json(LxERP.t8("Text blocks back")) %],
+ metadata: { type: "textblocks-back" },
+ attr: { id: "tb-back", class: "text-block-context-menu" },
+ 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"
+ core: {
+ animation: 0,
+ initially_open: [ "tb-front", "tb-back", "sections"
[%- FOREACH section = SELF.requirement_spec.sections -%]
, "fb-[% section.id %]"
[%- FOREACH function_block = section.children -%]
[%- END -%]
]
},
- "json_data": {
- "data": tree_data
+ json_data: {
+ data: tree_data
},
- "crrm": {
- "move": {
- "check_move": check_move,
- "open_move": true
+ crrm: {
+ move: {
+ check_move: requirement_spec_tree_check_move,
+ open_move: true
}
},
- "themes": {
- "theme": "requirement-spec"
+ themes: {
+ theme: "requirement-spec"
},
- "plugins": [ "themes", "json_data", "ui", "crrm", "dnd" ]
+ plugins: [ "themes", "json_data", "ui", "crrm", "dnd" ]
})
- .bind("move_node.jstree", node_moved)
- .bind("click.jstree", node_clicked)
+ .bind("move_node.jstree", requirement_spec_tree_node_moved)
+ .bind("click.jstree", requirement_spec_tree_node_clicked)
});
+
+function ask_delete_text_block(key, opt) {
+ if (confirm("[% LxERP.t8("Are you sure?") %]"))
+ delete_text_block(key, opt);
+ return true;
+}
+
+$(function(){
+ $.contextMenu({
+ selector: '.text-block-context-menu',
+ items: {
+ add: { name: "[% LxERP.t8('Add text block') %]", icon: "add", callback: add_text_block },
+ edit: { name: "[% LxERP.t8('Edit text block') %]", icon: "edit", callback: edit_text_block, disabled: disable_edit_text_block_commands },
+ delete: { name: "[% LxERP.t8('Delete text block') %]", icon: "delete", callback: ask_delete_text_block, disabled: disable_edit_text_block_commands },
+ sep1: "---------",
+ copy: { name: "[% LxERP.t8('Copy') %]", icon: "copy", disabled: disable_edit_text_block_commands },
+ paste: { name: "[% LxERP.t8('Paste') %]", icon: "paste", disabled: disable_edit_text_block_commands }
+ }
+ });
+});
+
-->
</script>
--- /dev/null
+[%- USE LxERP -%][%- USE L -%][%- USE HTML -%][%- USE JavaScript -%][% SET style="width: 500px" %]
+[% DEFAULT id_base = 'edit_text_block_' _ SELF.text_block.id %]
+<form method="post" id="[% id_base %]_form">
+ [% L.hidden_tag(id_base _ '_id', SELF.text_block.id) %]
+ [% L.hidden_tag(id_base _ '.requirement_spec_id', SELF.text_block.requirement_spec_id) %]
+ [% L.hidden_tag(id_base _ '.output_position', SELF.text_block.output_position) %]
+
+ <table>
+ <tr>
+ <th align="right">[%- LxERP.t8("Title") %]:</th>
+ <td>[% L.input_tag(id_base _ '.title', SELF.text_block.title, style = style) %]</td>
+ </tr>
+
+ [%- IF PREDEFINED_TEXTS.size %]
+ <tr>
+ <th align="right">[%- LxERP.t8("Pre-defined text block") %]:</th>
+ <td>
+ [%- L.select_tag(id_base _ '_predefined_text_block', PREDEFINED_TEXTS, title_key='description', style=style) %]
+ <a href="#" onclick="insert_selected_predefined_text()">[%- LxERP.t8("Insert") %]</a>
+ </td>
+ </tr>
+ [%- END %]
+
+ <tr>
+ <th align="right" valign="top">[%- LxERP.t8("Description") %]:</th>
+ <td valign="top">[% L.textarea_tag(id_base _ '.text', SELF.text_block.text, style = style, rows = 10) %]</td>
+ </tr>
+ </table>
+
+ <p>
+ [% L.button_tag('submit_edit_text_block_form("' _ id_base _ '")', LxERP.t8('Save')) %]
+ <a href="#" onclick="cancel_edit_text_block_form('[% id_base %]')">[%- LxERP.t8("Cancel") %]</a>
+ </p>
+
+[%- IF PREDEFINED_TEXTS.size %]
+ <script type="text/javascript">
+<!--
+function insert_selected_predefined_text() {
+ var data = {
+[%- FOREACH pt = PREDEFINED_TEXTS %]
+ [% HTML.escape(pt.id) %]: {
+ title: "[% JavaScript.escape(pt.title) %]",
+ text: "[% JavaScript.escape(pt.text) %]"
+ }[% UNLESS loop.last %],[% END %]
+[% END %]
+ }
+
+ var id = $('#[% id_base %]_predefined_text_block').val();
+ var pt = data[id];
+ if (!pt) {
+ console.log("insert: case 1; id: " + id);
+ return false;
+ }
+
+ var title_ctrl = $('#[% id_base %]_title');
+
+ if ( ((title_ctrl.val() || '') != '')
+ && ((pt.title || '') != '')
+ && confirm('[%- LxERP.t8("Do you want to overwrite your current title?") %]'))
+ title_ctrl.val(pt.title);
+
+ if ((pt.text || '') != '') {
+ var text_ctrl = $('#[% id_base %]_text');
+ var text = text_ctrl.val() || '';
+ if (text != '')
+ text += "\n\n";
+
+ text_ctrl.val(text + pt.text);
+ }
+
+ return false;
+}
+-->
+ </script>
+[%- END %]
+</form>
[%- USE HTML -%][%- USE L -%][%- USE LxERP -%]
-<div id="text-block-[% HTML.escape(text_block.id) %]" class="requirement-spec-text-block">
- <h2 class="requirement-spec-text-block-title">[%- HTML.escape(text_block.title) %]</h2>
+<div id="text-block-[% HTML.escape(text_block.id) %]" class="requirement-spec-text-block text-block-context-menu">
+ <h2 class="requirement-spec-text-block-title">[% IF !text_block.title %]<span class="dimmed-text">[%- LxERP.t8("No title yet") %]</span>[% END %][%- HTML.escape(text_block.title) %]</h2>
- [% IF !text_block.description %]
- <div class="requirement-spec-text-block-empty-description">
- [%- LxERP.t8("No description has been entered yet.") %]
+ [% IF !text_block.text %]
+ <div class="dimmed-text">
+ [%- LxERP.t8("No text has been entered yet.") %]
</div>
[% ELSE %]
- <div class="requirement-spec-text-block-description">
- [% L.simple_format(text_block.description) %]
+ <div class="requirement-spec-text-block-text">
+ [% L.simple_format(text_block.text) %]
</div>
[% END %]
</div>
-[%- USE LxERP -%][% nownow %]
-<h1>
- [%- IF output_position == 0 %][%- LxERP.t8("Text blocks front") %][%- ELSE %][%- LxERP.t8("Text blocks back") %][%- END %]
-</h1>
+[%- USE LxERP -%][%- USE L -%]
-<div id="text-block-list-empty"[% IF TEXT_BLOCKS.size %] style="display: none"[% END %]>
- [%- LxERP.t8("No text blocks have been created for this position.") %]
-</div>
+<div id="text-block-list-container">
+ [% L.hidden_tag('text_block_output_position', output_position) %]
+ <h1 class="text-block-context-menu">
+ [%- IF output_position == 0 %][%- LxERP.t8("Text blocks front") %][%- ELSE %][%- LxERP.t8("Text blocks back") %][%- END %]
+ </h1>
+
+ <div id="text-block-list-empty" class="text-block-context-menu"[% IF TEXT_BLOCKS.size %] style="display: none"[% END %]>
+ [%- LxERP.t8("No text blocks have been created for this position.") %]
+ </div>
-<div id="text-block-list">
- [%- FOREACH text_block = TEXT_BLOCKS %]
- [%- INCLUDE 'requirement_spec_text_block/_text_block.html' %]
- [%- END %]
+ <div id="text-block-list">
+ [%- FOREACH text_block = TEXT_BLOCKS %]
+ [%- INCLUDE 'requirement_spec_text_block/_text_block.html' %]
+ [%- END %]
+ </div>
</div>
+
+<script type="text/javascript">
+ <!--
+-->
+</script>