Cleaning the js in the skills tree, adding a javascript/skills.js file see #1791

skala
Julio Montoya 14 years ago
parent 53a7d3d460
commit 5721da93f6
  1. 1
      main/admin/skills.php
  2. 173
      main/inc/lib/javascript/skills.js
  3. 47
      main/social/skills_tree.php
  4. 176
      main/template/default/skill/skill_tree.tpl
  5. 416
      main/template/default/skill/skill_tree_student.tpl

@ -19,6 +19,7 @@ $this_section = SECTION_PLATFORM_ADMIN;
//Adds the JS needed to use the jqgrid
$htmlHeadXtra[] = api_get_jquery_ui_js(true);
$htmlHeadXtra[] = api_get_js('jquery.jsPlumb.all.js');
$htmlHeadXtra[] = api_get_js('skills.js');
//Display::display_header();
//Display::display_reduced_header();

@ -0,0 +1,173 @@
var exampleDropOptions = {
tolerance:'touch',
hoverClass:'dropHover',
activeClass:'dragActive'
};
var connectorPaintStyle = {
lineWidth:5,
strokeStyle:"#deea18",
joinstyle:"round"
};
// .. and this is the hover style.
var connectorHoverStyle = {
lineWidth:7,
strokeStyle:"#2e2aF8"
};
//Settings when editing stuff
var edit_arrow_color = '#ccc';
var editEndpoint = {
//connectorStyle:connectorPaintStyle,
connector:[ "Flowchart", { stub:28 } ],
hoverPaintStyle:connectorHoverStyle,
connectorHoverStyle:connectorHoverStyle,
anchors: ['BottomCenter','TopCenter'],
endpoint:"Rectangle",
paintStyle:{ width:10, height:10, fillStyle:edit_arrow_color },
isSource:true,
scope:'blue rectangle',
maxConnections:10,
connectorStyle : {
gradient:{
stops:[[0, edit_arrow_color], [0.5, edit_arrow_color], [1, edit_arrow_color]]
}, //gradient stuff
lineWidth:2,
strokeStyle: edit_arrow_color
},
isTarget:true,
dropOptions : exampleDropOptions
};
/* Clean window block classes*/
function cleanclass(obj) {
obj.removeClass('first_window');
obj.removeClass('second_window');
obj.removeClass('third_window');
}
function open_parent(parent_id, id) {
console.log("open_parent call : id " + id + " parent_id:" + parent_id);
var numeric_parent_id = parent_id.split('_')[1];
var numeric_id = id.split('_')[1];
load_parent(numeric_parent_id, numeric_id);
}
//open block function
function open_block(id) {
console.log("open_block id : " + id);
var numeric_id = id.split('_')[1];
for (var i = 0; i < skills.length; i++) {
//Remove everything except parents
if (jQuery.inArray(skills[i].element, parents) == -1) {
//console.log('deleting this'+ skills[i].element);
jsPlumb.deleteEndpoint(skills[i].endp);
$("#"+skills[i].element).remove();
//skills.splice(i,1);
//console.log('Removing '+skills[i].element);
}
}
/*if ($('#'+id).length == 0) {
$('body').append('<div id="'+id+'" class="open_block window " >'+id+'</div>');
}*/
//Modifying current block position
pos = $('#'+id).position();
left_value = center_x;
if (parents.length == 2) {
top_value = space_between_blocks_y + offset_y;
} else {
top_value = pos.left;
}
jsPlumb.animate(id, { left: left_value, top:top_value }, { duration:duration_value });
//Modifying root block position
pos_parent = $('#'+parents[0]).position();
jsPlumb.animate(parents[0], { left: center_x, top:offset_y }, { duration:duration_value });
top_value = 2*space_between_blocks_y +offset_y ;
load_children(numeric_id, top_value);
}
function load_parent(parent_id, id) {
console.log("load_parent call : id " + id + " parent_id:" + parent_id);
var ix= 0;
$.ajax({
url: url+'&a=load_direct_parents&id='+id,
async: false,
success: function(json) {
var json = jQuery.parseJSON(json);
$.each(json,function(i,item) {
left_value = center_x + space_between_blocks_x * ix;
top_value = offset_y;
$('body').append('<div id="block_'+item.id+ '" class="open_block window " >'+item.name+'</div>');
var es = prepare("block_" + item.id, editEndpoint);
var es2 = prepare("block_" + id, editEndpoint);
jsPlumb.connect({
source: es, target:es2
});
jsPlumb.animate("block_" + item.id, { left: left_value, top : top_value }, { duration : duration_value});
if (item.parent_id) {
console.log('setting hidden_parent '+item.parent_id);
hidden_parent = "block_" + item.parent_id;
} else {
console.log('setting NO--- hidden_parent ');
}
ix++;
});
}
});
}
function load_children(my_id, top_value) {
console.log("load_children call : my_id " + my_id + " top_value:" + top_value);
//Loading children
var ix = 0;
$.getJSON(url+'&a=load_children&id='+my_id, {},
function(json) {
$.each(json,function(i,item) {
left_value = ix*space_between_blocks_x + center_x/2 - block_size / 2;
//top_value = 300;
//Display::url($skill['name'], '#', array('id'=>'edit_block_'.$block_id, 'class'=>'edit_block'))
item.name = '<a href="#" class="edit_block" id="edit_block_'+item.id+'">'+item.name+'</a>';
$('body').append('<div id="block_'+item.id+ '" class="third_window open_block window " >'+item.name+'</div>');
var es = prepare("block_" + item.id, editEndpoint);
var e2 = prepare("block_" + my_id, editEndpoint);
jsPlumb.connect({
source: es, target:e2
});
jsPlumb.animate("block_" + item.id, {
left: left_value, top : top_value
}, { duration:duration_value});
ix++;
});
}
);
}
function checkLength( o, n, min, max ) {
if ( o.val().length > max || o.val().length < min ) {
o.addClass( "ui-state-error" );
updateTips( "Length of " + n + " must be between " +
min + " and " + max + "." );
return false;
} else {
return true;
}
}

@ -1 +1,46 @@
ski
<?php
/* For licensing terms, see /license.txt */
/**
* @package chamilo.admin
*/
// Language files that need to be included.
$language_file = array('admin');
$cidReset = true;
require_once '../inc/global.inc.php';
require_once api_get_path(LIBRARY_PATH).'skill.lib.php';
require_once api_get_path(LIBRARY_PATH).'skill.visualizer.lib.php';
$this_section = SECTION_MYPROFILE;
//api_protect_admin_script();
//Adds the JS needed to use the jqgrid
$htmlHeadXtra[] = api_get_jquery_ui_js(true);
$htmlHeadXtra[] = api_get_js('jquery.jsPlumb.all.js');
$htmlHeadXtra[] = api_get_js('skills.js');
$skill = new Skill();
$skills = $skill->get_all(true);
$type = 'edit'; //edit
$tree = $skill->get_skills_tree(null, true);
$skill_visualizer = new SkillVisualizer($tree, $type);
$html = $skill_visualizer->return_html();
$url = api_get_path(WEB_AJAX_PATH).'skill.ajax.php?1=1';
$tpl = new Template(null, false, false);
$tpl->assign('url', $url);
$tpl->assign('html', $html);
$tpl->assign('skill_visualizer', $skill_visualizer);
$tpl->assign('js', $skill_visualizer->return_js());
//
$content = $tpl->fetch('default/skill/skill_tree_student.tpl');
$tpl->assign('content', $content);
$tpl->display_no_layout_template();

@ -19,12 +19,7 @@ var block_size = {$skill_visualizer->block_size};
//Setting the parent by default
var parents = ['block_1'];
/* Clean window block classes*/
function cleanclass(obj) {
obj.removeClass('first_window');
obj.removeClass('second_window');
obj.removeClass('third_window');
}
jsPlumb.bind("ready", function() {
@ -114,114 +109,6 @@ jsPlumb.bind("ready", function() {
console.log('hidden_parent : ' + hidden_parent);
});
function open_parent(parent_id, id) {
console.log("open_parent call : id " + id + " parent_id:" + parent_id);
var numeric_parent_id = parent_id.split('_')[1];
var numeric_id = id.split('_')[1];
load_parent(numeric_parent_id, numeric_id);
}
//open block function
function open_block(id) {
console.log("open_block id : " + id);
var numeric_id = id.split('_')[1];
for (var i = 0; i < skills.length; i++) {
//Remove everything except parents
if (jQuery.inArray(skills[i].element, parents) == -1) {
//console.log('deleting this'+ skills[i].element);
jsPlumb.deleteEndpoint(skills[i].endp);
$("#"+skills[i].element).remove();
//skills.splice(i,1);
//console.log('Removing '+skills[i].element);
}
}
/*if ($('#'+id).length == 0) {
$('body').append('<div id="'+id+'" class="open_block window " >'+id+'</div>');
}*/
//Modifying current block position
pos = $('#'+id).position();
left_value = center_x;
if (parents.length == 2) {
top_value = space_between_blocks_y + offset_y;
} else {
top_value = pos.left;
}
jsPlumb.animate(id, { left: left_value, top:top_value }, { duration:duration_value });
//Modifying root block position
pos_parent = $('#'+parents[0]).position();
jsPlumb.animate(parents[0], { left: center_x, top:offset_y }, { duration:duration_value });
top_value = 2*space_between_blocks_y +offset_y ;
load_children(numeric_id, top_value);
}
function load_parent(parent_id, id) {
console.log("load_parent call : id " + id + " parent_id:" + parent_id);
var ix= 0;
$.ajax({
url: url+'&a=load_direct_parents&id='+id,
async: false,
success: function(json) {
var json = jQuery.parseJSON(json);
$.each(json,function(i,item) {
left_value = center_x + space_between_blocks_x * ix;
top_value = offset_y;
$('body').append('<div id="block_'+item.id+ '" class="open_block window " >'+item.name+'</div>');
var es = prepare("block_" + item.id, editEndpoint);
var es2 = prepare("block_" + id, editEndpoint);
jsPlumb.connect({
source: es, target:es2
});
jsPlumb.animate("block_" + item.id, { left: left_value, top : top_value }, { duration : duration_value});
if (item.parent_id) {
console.log('setting hidden_parent '+item.parent_id);
hidden_parent = "block_" + item.parent_id;
} else {
console.log('setting NO--- hidden_parent ');
}
ix++;
});
}
});
}
function load_children(my_id, top_value) {
console.log("load_children call : my_id " + my_id + " top_value:" + top_value);
//Loading children
var ix = 0;
$.getJSON(url+'&a=load_children&id='+my_id, {},
function(json) {
$.each(json,function(i,item) {
left_value = ix*space_between_blocks_x + center_x/2 - block_size / 2;
//top_value = 300;
//Display::url($skill['name'], '#', array('id'=>'edit_block_'.$block_id, 'class'=>'edit_block'))
item.name = '<a href="#" class="edit_block" id="edit_block_'+item.id+'">'+item.name+'</a>';
$('body').append('<div id="block_'+item.id+ '" class="third_window open_block window " >'+item.name+'</div>');
var es = prepare("block_" + item.id, editEndpoint);
var e2 = prepare("block_" + my_id, editEndpoint);
jsPlumb.connect({
source: es, target:e2
});
jsPlumb.animate("block_" + item.id, {
left: left_value, top : top_value
}, { duration:duration_value});
ix++;
});
}
);
}
$(".edit_block").live('click',function() {
var my_id = $(this).attr('id');
my_id = my_id.split('_')[2];
@ -244,12 +131,10 @@ jsPlumb.bind("ready", function() {
$("#gradebook_id option[value='"+data.id+"']").attr('selected', 'selected');
});
},
});
});
$("#gradebook_id").trigger("liszt:updated");
$("#parent_id").trigger("liszt:updated");
$("#parent_id").trigger("liszt:updated");
$("#dialog-form").dialog("open");
return false;
@ -328,49 +213,7 @@ jsPlumb.bind("ready", function() {
resetRenderMode(jsPlumb.CANVAS);
});
var exampleDropOptions = {
tolerance:'touch',
hoverClass:'dropHover',
activeClass:'dragActive'
};
var connectorPaintStyle = {
lineWidth:5,
strokeStyle:"#deea18",
joinstyle:"round"
};
// .. and this is the hover style.
var connectorHoverStyle = {
lineWidth:7,
strokeStyle:"#2e2aF8"
};
//Settings when editing stuff
var edit_arrow_color = '#ccc';
var editEndpoint = {
//connectorStyle:connectorPaintStyle,
connector:[ "Flowchart", { stub:28 } ],
hoverPaintStyle:connectorHoverStyle,
connectorHoverStyle:connectorHoverStyle,
anchors: ['BottomCenter','TopCenter'],
endpoint:"Rectangle",
paintStyle:{ width:10, height:10, fillStyle:edit_arrow_color },
isSource:true,
scope:'blue rectangle',
maxConnections:10,
connectorStyle : {
gradient:{
stops:[[0, edit_arrow_color], [0.5, edit_arrow_color], [1, edit_arrow_color]]
}, //gradient stuff
lineWidth:2,
strokeStyle: edit_arrow_color
},
isTarget:true,
dropOptions : exampleDropOptions
};
;(function() {
prepare = function(elId, endpoint) {
@ -575,16 +418,7 @@ $(document).ready( function() {
};
})();
function checkLength( o, n, min, max ) {
if ( o.val().length > max || o.val().length < min ) {
o.addClass( "ui-state-error" );
updateTips( "Length of " + n + " must be between " +
min + " and " + max + "." );
return false;
} else {
return true;
}
}
</script>
<a class="a_button gray" id="add_item_link" href="#">Add item</a>

@ -0,0 +1,416 @@
<script type="text/javascript">
//js settings
var url = '{$url}';
var skills = []; //current window divs
var parents = []; //list of parents normally there should be only 2
var hidden_parent = '';
var duration_value = 500;
//Block settings see the SkillVisualizer Class
var offset_x = {$skill_visualizer->offset_x};
var offset_y = {$skill_visualizer->offset_y};
var space_between_blocks_x = {$skill_visualizer->space_between_blocks_x};
var space_between_blocks_y = {$skill_visualizer->space_between_blocks_y};
var center_x = {$skill_visualizer->center_x};
var block_size = {$skill_visualizer->block_size};
//Setting the parent by default
var parents = ['block_1'];
/* Clean window block classes*/
function cleanclass(obj) {
obj.removeClass('first_window');
obj.removeClass('second_window');
obj.removeClass('third_window');
}
jsPlumb.bind("ready", function() {
//Open dialog
$("#dialog-form").dialog({
autoOpen: false,
modal : true,
width : 550,
height : 480,
});
//On box click -(we use live instead of bind because we're creating divs on the fly )
$(".open_block").live('click', function() {
var id = $(this).attr('id');
//if is root
if (parents[0] == id) {
parents = [id];
}
if (parents[1] != id) {
if (parents.length == 2 ) {
hidden_parent = parents[0];
//console.log('deleting: '+parents[0]);
//removing father
for (var i = 0; i < skills.length; i++) {
if ( skills[i].element == parents[0] ) {
//console.log('deleting :'+ skills[i].element + ' here ');
jsPlumb.deleteEndpoint(skills[i].endp);
$("#"+skills[i].element).remove();
//skills.splice(i,1)
}
}
parents.splice(0,1);
parents.push(id);
}
if ($(this).hasClass('first_window')) {
//show the hidden_parent
//if (hidden_parent != '') {
parents = [hidden_parent, id];
// console.log(parents);
open_parent(hidden_parent, id);
//}
}
if (jQuery.inArray(id, parents) == -1) {
parents.push(id);
}
open_block(id);
}
//Setting class
cleanclass($(this));
$(this).addClass('second_window');
parent_div = $("#"+parents[0]);
cleanclass(parent_div);
parent_div.addClass('first_window');
parent_div = $("#"+parents[1]);
cleanclass(parent_div);
parent_div.addClass('second_window');
console.log(parents);
// console.log(skills);
console.log('hidden_parent : ' + hidden_parent);
});
$(".edit_block").live('click',function() {
var my_id = $(this).attr('id');
my_id = my_id.split('_')[2];
//Cleaning selected
$("#parent_id option:selected").removeAttr('selected');
$("#gradebook_id option:selected").removeAttr('selected');
$.ajax({
url: url+'&a=get_skill_info&id='+my_id,
success: function(json) {
var skill = jQuery.parseJSON(json);
$("#name").attr('value', skill.name);
$("#id").attr('value', skill.id);
$("#description").attr('value', skill.description);
//filling parent_id
$("#parent_id option[value='"+skill.extra.parent_id+"']").attr('selected', 'selected');
//filling the gradebook_id
jQuery.each(skill.gradebooks, function(index, data) {
$("#gradebook_id option[value='"+data.id+"']").attr('selected', 'selected');
});
},
});
$("#gradebook_id").trigger("liszt:updated");
$("#parent_id").trigger("liszt:updated");
$("#dialog-form").dialog("open");
return false;
});
//Filling select
$("#add_item_link").click(function() {
$("#name").attr('value', '');
$("#description").attr('value', '');
$("#dialog-form").dialog("open");
});
$("#dialog-form").dialog({
close: function() {
$("#name").attr('value', '');
$("#description").attr('value', '');
}
});
// chrome fix.
document.onselectstart = function () { return false; };
// render mode
var resetRenderMode = function(desiredMode) {
var newMode = jsPlumb.setRenderMode(desiredMode);
jsPlumbDemo.init();
};
resetRenderMode(jsPlumb.CANVAS);
});
;(function() {
prepare = function(elId, endpoint) {
jsPlumbDemo.initHover(elId);
//jsPlumbDemo.initAnimation(elId);
var e = jsPlumb.addEndpoint(elId, endpoint);
jsPlumbDemo.initjulio(e);
skills.push({
element:elId, endp:e
});
return e;
},
window.jsPlumbDemo = {
initjulio :function(e) {
},
initHover :function(elId) {
$("#" + elId).click(function() {
var all = jsPlumb.getConnections({
source:elId
});
});
/*$("#" + elId).hover(
function() { $(this).addClass("bigdot-hover"); },
function() { $(this).removeClass("bigdot-hover"); }
);*/
},
init : function() {
jsPlumb.Defaults.DragOptions = { cursor: 'pointer', zIndex:2000 };
jsPlumb.Defaults.PaintStyle = { strokeStyle:'#666' };
jsPlumb.Defaults.EndpointStyle = { width:20, height:16, strokeStyle:'#666' };
jsPlumb.Defaults.Endpoint = "Rectangle";
jsPlumb.Defaults.Anchors = ["TopCenter", "TopCenter"];
var connections = [];
var updateConnections = function(conn, remove) {
if (!remove) connections.push(conn);
else {
var idx = -1;
for (var i = 0; i < connections.length; i++) {
if (connections[i] == conn) {
idx = i; break;
}
}
if (idx != -1) connections.splice(idx, 1);
}
if (connections.length > 0) {
var s = "<span>current connections</span><br/><br/><table><tr><th>scope</th><th>source</th><th>target</th></tr>";
for (var j = 0; j < connections.length; j++) {
s = s + "<tr><td>" + connections[j].scope + "</td>" + "<td>" + connections[j].sourceId + "</td><td>" + connections[j].targetId + "</td></tr>";
}
jsPlumbDemo.showConnectionInfo(s);
} else
jsPlumbDemo.hideConnectionInfo();
};
jsPlumb.bind("jsPlumbConnection", function(e) {
updateConnections(e.connection);
});
jsPlumb.bind("jsPlumbConnectionDetached", function(e) {
updateConnections(e.connection, true);
});
/**
first example endpoint. it's a 25x21 rectangle (the size is provided in the 'style' arg to the Endpoint), and it's both a source
and target. the 'scope' of this Endpoint is 'exampleConnection', meaning any connection starting from this Endpoint is of type
'exampleConnection' and can only be dropped on an Endpoint target that declares 'exampleEndpoint' as its drop scope, and also that
only 'exampleConnection' types can be dropped here.
the connection style for this endpoint is a Bezier curve (we didn't provide one, so we use the default), with a lineWidth of
5 pixels, and a gradient.
note the use of the '$.extend' function to setup generic connection types. this will save you a lot of typing, and probably
errors.
*/
// this is the paint style for the connecting lines..
jsPlumb.Defaults.Overlays = [
//[ "Arrow", { location:0.5 } ], if you want to add an arrow in the connection
];
jsPlumb.setMouseEventsEnabled(true);
//Default
var default_arrow_color = '#ccc';
var defaultEndpoint = {
anchors: ['BottomCenter','TopCenter'],
endpoint:"Rectangle",
paintStyle:{ width:1, height:1, fillStyle:default_arrow_color },
isSource:false,
scope:'blue rectangle',
maxConnections:10,
connectorStyle : {
gradient:{ stops:[[0, default_arrow_color], [0.5, default_arrow_color], [1, default_arrow_color]] },
lineWidth:5,
strokeStyle:default_arrow_color
},
isTarget:false,
setDraggableByDefault : false,
};
// Done end point
var done_arrow_color = '#73982C';
var doneEndpoint = {
endpoint:"Rectangle",
paintStyle:{ width:1, height:1, fillStyle:done_arrow_color},
isSource:false,
scope:'blue rectangle',
maxConnections:10,
connectorStyle : {
gradient:{ stops:[[0, done_arrow_color], [0.5, done_arrow_color], [1, done_arrow_color]] },
lineWidth:5,
strokeStyle:done_arrow_color
},
isTarget:false,
setDraggableByDefault : false,
};
{$js}
// three ways to do this - an id, a list of ids, or a selector (note the two different types of selectors shown here...anything that is valid jquery will work of course)
//jsPlumb.draggable("window1");
//jsPlumb.draggable(["window1", "window2"]);
//jsPlumb.draggable($("#window1"));
var divsWithWindowClass = jsPlumbDemo.getSelector(".window");
jsPlumb.draggable(divsWithWindowClass);
// add the third example using the '.window' class.
//jsPlumb.addEndpoint(divsWithWindowClass, exampleEndpoint3);
// each library uses different syntax for event stuff, so it is handed off
// to the draggableConnectorsDemo-<library>.js files.
jsPlumbDemo.attachBehaviour();
}
};
})();
$(document).ready( function() {
//When creating a connection see
//http://jsplumb.org/apidocs/files/jsPlumb-1-3-2-all-js.html#bind
jsPlumb.bind("jsPlumbConnection", function(conn) {
//alert("Connection created " + conn.sourceId + " to " + conn.targetId + " ");
//jsPlumb.detach(conn);
});
//When double clicking a connection
jsPlumb.bind("click", function(conn) {
if (confirm("Delete connection from " + conn.sourceId + " to " + conn.targetId + "?"))
jsPlumb.detach(conn);
});
//When double clicking a connection
jsPlumb.bind("click", function(endpoint) {
if (confirm("Delete connection from " + conn.sourceId + " to " + conn.targetId + "?"))
jsPlumb.detach(conn);
});
$(".chzn-select").chosen();
});
;(function() {
jsPlumbDemo.showConnectionInfo = function(s) {
$("#list").html(s);
$("#list").fadeIn({ complete:function() { jsPlumb.repaintEverything(); }});
};
jsPlumbDemo.hideConnectionInfo = function() {
$("#list").fadeOut({ complete:function() { jsPlumb.repaintEverything(); }});
};
jsPlumbDemo.getSelector = function(spec) {
return $(spec);
};
jsPlumbDemo.attachBehaviour = function() {
$(".hide").click(function() {
jsPlumb.toggle($(this).attr("rel"));
});
$(".drag").click(function() {
var s = jsPlumb.toggleDraggable($(this).attr("rel"));
$(this).html(s ? 'disable dragging' : 'enable dragging');
if (!s) $("#" + $(this).attr("rel")).addClass('drag-locked'); else $("#" + $(this).attr("rel")).removeClass('drag-locked');
$("#" + $(this).attr("rel")).css("cursor", s ? "pointer" : "default");
});
$(".detach").click(function() {
jsPlumb.detachAll($(this).attr("rel"));
});
$("#clear").click(function() {
jsPlumb.detachEverything(); showConnections();
});
};
})();
</script>
{$html}
<div id="dialog-form" style="display:none;">
<form id="add_item" name="form">
<input type="hidden" name="id" id="id"/>
<div class="row">
<div class="label">
<label for="name">Name</label>
</div>
<div class="formw">
<input type="text" name="name" id="name" size="40" />
</div>
</div>
<div class="row">
<div class="label">
<label for="name">Parent</label>
</div>
<div class="formw">
<select id="parent_id" name="parent_id" />
</select>
</div>
</div>
<div class="row">
<div class="label">
<label for="name">Gradebook</label>
</div>
<div class="formw">
<select id="gradebook_id" name="gradebook_id[]" multiple="multiple"/>
</select>
<span class="help-block">
Gradebook Description
</span>
</div>
</div>
<div class="row">
<div class="label">
<label for="name">Description</label>
</div>
<div class="formw">
<textarea name="description" id="description" cols="40" rows="7"></textarea>
</div>
</div>
</form>
</div>
Loading…
Cancel
Save