[Rt-commit] rt branch, 4.6/lifecycle-ui-dev, repushed
Craig Kaiser
craig at bestpractical.com
Thu Dec 5 09:12:58 EST 2019
The branch 4.6/lifecycle-ui-dev was deleted and repushed:
was 69448e40f2d4b6b34846128d94c41438e2ff8c36
now a62f12eca6b3838acf3630e4992a14c19269b9d0
1: 69448e40f2 ! 1: a62f12eca6 Core Lifecycle-UI
@@ -856,12 +856,14 @@
+<script type="text/javascript" src="<%RT->Config->Get('WebPath')%>/static/js/farbtastic.js"></script>
+
+<form action="<%RT->Config->Get('WebPath')%>/Admin/Lifecycles/Modify.html" name="ModifyLifecycle" method="post" enctype="multipart/form-data">
-+<input type="hidden" class="hidden" name="Name" value="<% $LifecycleObj->Name %>" />
-+<input type="hidden" class="hidden" name="Type" value="<% $LifecycleObj->Type %>" />
-+
-+<& /Elements/Lifecycle/Graph, Lifecycle => $LifecycleObj->Name &>
-+<& /Elements/Submit, Label => loc('Save Changes') &>
-+
++ <div class="form-row">
++ <input type="hidden" class="hidden" name="Name" value="<% $LifecycleObj->Name %>" />
++ <input type="hidden" class="hidden" name="Type" value="<% $LifecycleObj->Type %>" />
++ <div class="col-md-12">
++ <& /Elements/Submit, Label => loc('Save Changes') &>
++ </div>
++ <& /Elements/Lifecycle/Graph, Lifecycle => $LifecycleObj->Name &>
++ </div>
+</form>
+<%INIT>
+my ($title, @results);
@@ -874,12 +876,14 @@
+$title = loc("Modify lifecycle [_1]", $LifecycleObj->Name);
+
+if ($Config) {
++ my $LifecycleAttribute = JSON::from_json($LifecycleAttribute);
+ my ($ok, $msg) = RT::Lifecycle->UpdateLifecycle(
+ CurrentUser => $session{CurrentUser},
+ LifecycleObj => $LifecycleObj,
+ NewConfig => JSON::from_json($Config),
++ Attribute => $LifecycleAttribute,
+ );
-+ push @results, $msg;
++ #push @results, $msg;
+}
+
+# This code does automatic redirection if any updates happen.
@@ -889,9 +893,10 @@
+);
+</%INIT>
+<%ARGS>
-+$Name => undef
-+$Type => undef
-+$Config => undef
++$Name => undef
++$Type => undef
++$Config => undef
++$LifecycleAttribute => undef
+</%ARGS>
diff --git a/share/html/Admin/Lifecycles/index.html b/share/html/Admin/Lifecycles/index.html
@@ -951,23 +956,23 @@
+
+<h1><&|/l&>Lifecycles</&></h1>
+
-+<table cellspacing="0" class="collection collection-as-table">
-+<tr class="collection-as-table">
-+ <th class="collection-as-table"><&|/l&>Name</&></th>
-+ <th class="collection-as-table"><&|/l&>Type</&></th>
-+ <th class="collection-as-table"><&|/l&>Display</&></th>
-+</tr>
++<div cellspacing="0" class="collection collection-as-table">
++ <div class="form-row">
++ <div class="col-md-2 collection-as-table"><&|/l&>Name</&></div>
++ <div class="col-md-auto collection-as-table"><&|/l&>Type</&></div>
++ <div class="col-md-auto collection-as-table"><&|/l&>Display</&></div>
++ </div>
+% my $i = 0;
+% for my $lifecycle (@lifecycles) {
+% ++$i;
-+<tr class="<% $i % 2 ? 'oddline' : 'evenline' %>">
-+<td class="collection-as-table"><a href="<% RT->Config->Get('WebURL') %>Admin/Lifecycles/Modify.html?Type=<% $lifecycle->Type |u %>&Name=<% $lifecycle->Name |u %>"><% $lifecycle->Name %></a></td>
-+<td class="collection-as-table"><% loc($lifecycle->Type) %></td>
++ <div class="<% $i % 2 ? 'oddline' : 'evenline' %> form-row">
++ <div class="collection-as-table col-md-2"><a href="<% RT->Config->Get('WebURL') %>Admin/Lifecycles/Modify.html?Type=<% $lifecycle->Type |u %>&Name=<% $lifecycle->Name |u %>"><% $lifecycle->Name %></a></div>
++ <div class="collection-as-table col-md-auto"><% loc($lifecycle->Type) %></div>
+% my $display = $lifecycle->Type eq 'ticket' ? ($lifecycle->{data}{ticket_display} || 'hidden') : 'hidden';
-+<td class="collection-as-table"><% loc($display) %></td>
-+</tr>
++ <div class="collection-as-table col-md-auto"><% loc($display) %></div>
++ </div>
+% }
-+</table>
++</div>
+<%INIT>
+my @types = List::MoreUtils::uniq(
+ 'ticket',
@@ -1039,8 +1044,7 @@
+%# END BPS TAGGED BLOCK }}}
+<div class="lifecycle-ui" id="lifecycle-<% $id %>">
+ <div class="row">
-+ <div class="col-md-7"></div>
-+ <div class="col-md-5">
++ <div class="col-md-12">
+ <div id="lifeycycle-ui-edit-node" class="lifeycycle-ui-edit-node collapse card card-body">
+ <input type="hidden" name="id" />
+ <div class="row">
@@ -1080,21 +1084,33 @@
+ <script type="text/javascript">
+ jQuery(function () {
+ var container = document.getElementById('lifecycle-<% $id %>'),
-+ config = <% JSON($config) |n %>,
-+ name = <% $Lifecycle | j%>;
-+
-+ var editor = new RT.NewEditor( container, config );
++ config = <% JSON($config) |n %>,
++ name = <% $Lifecycle | j%>,
++ attribute = <% $attribute |n %>;
++
++ var editor = new RT.NewEditor( container, config, attribute );
+ });
+ </script>
-+ <div><p>
-+ Click in the open space to <strong>add a node</strong>, drag from one node to another while holding control to <strong>add an edge</strong>.
-+ <br>
-+ Drag a node to <strong>move the graph layout</strong>.
-+ <br>
-+ Click a nodes text to <strong>edit attributes</stong> of the node.
-+ <br>
-+ <strong>Click</strong> an edge to interact with it.
-+ </p></div>
++ <div>
++ <label class="switch">Disable Simulation
++ <input type="checkbox" name="enableSimulation" id="enableSimulation">
++ </label>
++ <p>
++ Click in the open space to <strong>add a node</strong>.
++ <br>
++ To <strong>delete</strong> a node click on the node to select it and press the 'd' key.
++ <br>
++ Drag from one node to another while holding <strong>control</strong> to <strong>add an edge</strong>.
++ <br>
++ Drag a node to <strong>move the graph layout</strong>.
++ <br>
++ Click a nodes text to <strong>edit attributes</strong> of the node.
++ <br>
++ <strong>Click an edge</strong> to interact with it.
++ <br>
++ To disable the simulation and drag nodes to a location without force acting on them click the "Disable simulation" checkbox.
++ </p>
++ </div>
+</div>
+
+<%INIT>
@@ -1103,6 +1119,11 @@
+
+my $config = RT->Config->Get('Lifecycles')->{$Lifecycle};
+Abort("Invalid Lifecycle") if !$Lifecycle || !$config;
++
++my $attributes = RT::Attributes->new( RT->SystemUser );
++$attributes->Limit( FIELD => 'Description', VALUE => "LifecycleAttribute-$Lifecycle" );
++my $attribute = $attributes->First;
++$attribute = $attribute ? JSON($attribute->Content) : "{}";
+
+my $id = $Lifecycle . '-' . int(rand(2**31));
+</%INIT>
@@ -1520,22 +1541,39 @@
@@
+jQuery( document ).ready(function () {
+ RT.NewEditor = class LifecycleEditorNew extends LifecycleModel {
-+ constructor(container, config) {
++ constructor(container, config, attribute) {
+ super("LifecycleModel");
+
+ var self = this;
+ self.width = 900;
-+ self.height = 500;
++ self.height = 350;
+ self.node_radius = 35;
++ self.attribute = attribute;
++ self.initial = 1;
++
++ if ( self.attribute['checked'] ) {
++ jQuery('#enableSimulation').prop( "checked", true );
++ self.checked = 1;
++ }
+
+ jQuery("#SaveNode").click(function( event ) {
+ event.preventDefault();
+ self.UpdateNode();
+ });
+
-+ self.svg = d3.select(container).select('svg')
++ self.svg = d3.select(container).select('svg')
+ .attr("preserveAspectRatio", "xMinYMin meet")
-+ .attr("viewBox", "0 0 900 500");
++ .attr("viewBox", "0 0 "+self.width+" "+self.height)
++ .attr("border", 1);
++
++ self.svg.append("rect")
++ .attr("x", 0)
++ .attr("y", 0)
++ .attr("height", self.height)
++ .attr("width", self.width)
++ .style("stroke", 'black')
++ .style("fill", "none")
++ .style("stroke-width", 1);
+
+ self.config = config;
+ self.links = [];
@@ -1563,15 +1601,32 @@
+ if(link) link.start = true;
+ else self.links.push({id: ++self.links_seq, source: source, target: target, start: false, end: true});
+ });
++ if ( self.checked ) {
++ if (self.attribute[source.name][0]) source.x = parseFloat(self.attribute[source.name][0]);
++ if (self.attribute[source.name][1]) source.y = parseFloat(self.attribute[source.name][1]);
++ }
+ });
+
-+ self.simulation = d3.forceSimulation()
-+ .force("link", d3.forceLink().distance(350).strength(0.2))
-+ .force("charge", d3.forceManyBody().strength(-400))
-+ .force("center", d3.forceCenter(self.width / 2, self.height / 2))
-+ .force('collision', d3.forceCollide().radius(function(d) {
++ self.simulation = d3.forceSimulation();
++ const link_size = self.nodes.length > 10 ? 300 : self.nodes.length * 35;
++ if ( self.checked ) {
++ self.simulation
++ .force("link", d3.forceLink().distance(link_size < 100 ? 200 : link_size).strength(0))
++ .force("charge", d3.forceManyBody().strength(0))
++ .force("center", d3.forceCenter(self.width / 2, self.height / 2))
++ .force('collision', d3.forceCollide().radius(function(d) {
++ return null;
++ }));
++ }
++ else {
++ self.simulation
++ .force("link", d3.forceLink().distance(link_size < 100 ? 200 : link_size).strength(0.2))
++ .force("charge", d3.forceManyBody().strength(-200))
++ .force("center", d3.forceCenter(self.width / 2, self.height / 2))
++ .force('collision', d3.forceCollide().radius(function(d) {
+ return d.radius
+ }));
++ }
+
+ self.SetUp();
+ self.RenderNode();
@@ -1581,11 +1636,21 @@
+ .nodes(self.nodes)
+ .on("tick", ( t => {
+ this.node.attr("transform", (d => {
++
+ var x = d.x, y = d.y;
+ if ( d.x + self.node_radius / 2 > self.width ) x = self.width - self.node_radius;
+ if ( d.x - self.node_radius / 2 <= 0 ) x = self.node_radius;
+ if ( d.y + self.node_radius / 2 > self.height ) y = self.height - self.node_radius;
+ if ( d.y - self.node_radius / 2 <= 0 ) y = self.node_radius;
++
++ if ( self.checked ) {
++ d.fx = x;
++ d.fy = y;
++ }
++ else {
++ d.fx = null;
++ d.fy = null;
++ }
+
+ return "translate(" + x + "," + y + ")";
+ }));
@@ -1629,6 +1694,18 @@
+ var field = jQuery('<input type="hidden" name="Config">');
+ field.val(JSON.stringify(self.config));
+ form.append(field);
++
++ var pos = {};
++ self.nodes.forEach( d => {
++ pos[d.name] = [d.x, d.y];
++ });
++
++ var attribute = jQuery('<input name="LifecycleAttribute" type="hidden">');
++ attribute.val(JSON.stringify(pos));
++ form.append(attribute);
++
++ self.initial = 0;
++ self.ExportAsConfiguration();
+ }
+
+ SetUp() {
@@ -1706,6 +1783,10 @@
+ });
+ }
+ })
++
++ jQuery('#enableSimulation').click(function(){
++ self.ToggleSimulation();
++ });
+ }
+
+ RenderNode() {
@@ -1719,13 +1800,14 @@
+
+ // Add new nodes and draw them
+ var nodeEnter = self.node.enter().append("g")
-+ .attr("class", "node")
++ .attr("class", "node");
+
+ nodeEnter.append("circle");
+ nodeEnter.append("text");
+ nodeEnter.append("title");
+
-+ self.node = nodeEnter.merge(self.node);
++ self.node = nodeEnter.merge(self.node)
++ .attr("id", d => { d.id });
+
+ self.node.call(d3.drag()
+ .on("start", (d => {
@@ -1737,7 +1819,9 @@
+ }))
+ .on("end", (d => {
+ if (!d3.event.active) this.simulation.alphaTarget(0);
-+ d.fx = null, d.fy = null;
++ if ( !self.checked ) {
++ d.fx = null, d.fy = null;
++ }
+ })));
+
+ // Add our circle to our new node
@@ -1825,12 +1909,24 @@
+ jQuery(item).val(element[item.name]);
+ // Can we make this check for the select bootstrap class instead of hard coding the known fields?
+ if ( item.name === 'type' ) {
-+ jQuery(".bootstrap-select .filter-option").text(element[item.name])
++ var type = jQuery(".bootstrap-select .filter-option");
++ if ( type ) {
++ type.text(element[item.name]);
++ }
+ }
+ }
+ self.editing_node = element;
+ }
+ else {
++ var name = document.getElementsByName('name')[0].value;
++ if ( ( this.nodes.findIndex(x => x.name == name ) > 0 && this.nodes.findIndex(x => x.name == name ) <= 1 ) || name === '' ) {
++ // FIXME
++ // var form = jQuery('#lifeycycle-ui-edit-node');
++ // var field = jQuery('<div class="alert alert-warning removing">Name invalid</div>');
++ // form.append(field);
++ return;
++ }
++
+ var values = {};
+ for (let item of list) {
+ if ( item.name === 'id' ) {
@@ -1889,14 +1985,21 @@
+ Refresh() {
+ var self = this;
+
++ const link_size = self.nodes.length > 10 ? 300 : self.nodes.length * 35;
++ self.simulation
++ .force("link", d3.forceLink().distance(link_size < 100 ? 200 : link_size).strength(0.2))
++
+ self.simulation
+ .nodes(self.nodes)
+ .force("link")
-+ .links(self.links)
-+ .id(d => d.id);
++ .links(self.links)
++ .id(d => d.id);
+
+ self.RenderLink();
+ self.RenderNode();
++
++ jQuery('.removing').remove();
++
+ // This is our "cooling" factor
+ self.simulation.alpha(0.05).restart();
+ }
@@ -1912,6 +2015,7 @@
+ Deselect() {
+ if ( jQuery("#lifeycycle-ui-edit-node").is(':visible') ) {
+ jQuery("#lifeycycle-ui-edit-node").toggle();
++ jQuery('.removing').remove();
+ }
+
+ this.editing_node = null;
@@ -1967,6 +2071,36 @@
+ self.mousedown_link = null;
+ self.mousedown_node = null;
+ self.mouseup_node = null;
++ }
++
++ ToggleSimulation(){
++ var self = this;
++ self.checked = jQuery('#enableSimulation').is(":checked");
++
++ const link_size = self.nodes.length > 10 ? 300 : self.nodes.length * 35;
++ if ( self.checked ) {
++ self.simulation
++ .force("link", d3.forceLink().distance(link_size < 100 ? 200 : link_size).strength(0))
++ .force("charge", d3.forceManyBody().strength(0))
++ .force("center", d3.forceCenter(self.width / 2, self.height / 2))
++ .force('collision', d3.forceCollide().radius(function(d) {
++ return null;
++ }));
++ }
++ else {
++ self.nodes.forEach(function(d) {
++ d.fx = null, d.fy = null;
++ });
++
++ self.simulation
++ .force("link", d3.forceLink().distance(link_size < 100 ? 200 : link_size).strength(0.2))
++ .force("charge", d3.forceManyBody().strength(-200))
++ .force("center", d3.forceCenter(self.width / 2, self.height / 2))
++ .force('collision', d3.forceCollide().radius(function(d) {
++ return d.radius
++ }));
++ }
++ self.ExportAsConfiguration();
+ }
+ }
+});
@@ -2160,5 +2294,20 @@
+
+ var field = jQuery('input[name="Config"]');
+ field.val(JSON.stringify(self.config));
++
++ var pos = {};
++ if ( jQuery('#enableSimulation').is(":checked") ) {
++ pos["checked"] = 1;
++ self.nodes.forEach( d => {
++ pos[d.name] = [d.fx, d.fy];
++ });
++ }
++ else {
++ pos = JSON.parse(jQuery('input[name="LifecycleAttribute"]').val())
++ pos["checked"] = 0;
++ console.log(pos);
++ }
++ var attribute = jQuery('input[name="LifecycleAttribute"]');
++ attribute.val(JSON.stringify(pos));
+ };
+}
More information about the rt-commit
mailing list