MDL-51602 tool_lp: Add ability to duplicate a competency framework

This commit is contained in:
Issam Taboubi
2016-04-18 10:58:36 +08:00
committed by Frederic Massart
parent 03c6665894
commit c61db7bb6a
11 changed files with 334 additions and 8 deletions
+1
View File
@@ -0,0 +1 @@
define(["jquery","core/templates","core/ajax","core/notification","core/str"],function(a,b,c,d,e){var f=0,g=0,h=function(c,d){a('[data-region="managecompetencies"]').replaceWith(c),b.runTemplateJS(d)},i=function(a){b.render("tool_lp/manage_competency_frameworks_page",a).done(h).fail(d.exception)},j=function(b){b.preventDefault(),g=a(this).attr("data-frameworkid");var e=c.call([{methodname:"tool_lp_duplicate_competency_framework",args:{id:g}},{methodname:"tool_lp_data_for_competency_frameworks_manage_page",args:{pagecontext:{contextid:f}}}]);e[1].done(i).fail(d.exception)},k=function(){var a=c.call([{methodname:"tool_lp_delete_competency_framework",args:{id:g}},{methodname:"tool_lp_data_for_competency_frameworks_manage_page",args:{pagecontext:{contextid:f}}}]);a[1].done(i).fail(d.exception)},l=function(b){b.preventDefault();var f=a(this).attr("data-frameworkid");g=f;var h=c.call([{methodname:"tool_lp_read_competency_framework",args:{id:g}}]);h[0].done(function(a){e.get_strings([{key:"confirm",component:"moodle"},{key:"deletecompetencyframework",component:"tool_lp",param:a.shortname},{key:"delete",component:"moodle"},{key:"cancel",component:"moodle"}]).done(function(a){d.confirm(a[0],a[1],a[2],a[3],k)}).fail(d.exception)}).fail(d.exception)};return{deleteHandler:l,duplicateHandler:j,init:function(a){f=a}}});
@@ -14,9 +14,9 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Delete competency frameworks via ajax.
* Competency frameworks actions via ajax.
*
* @module tool_lp/frameworkdelete
* @module tool_lp/frameworkactions
* @package tool_lp
* @copyright 2015 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
@@ -52,6 +52,30 @@ define(['jquery', 'core/templates', 'core/ajax', 'core/notification', 'core/str'
.fail(notification.exception);
};
/**
* Duplicate a framework and reload the page.
* @method doDuplicate
* @param {Event} e
*/
var doDuplicate = function(e) {
e.preventDefault();
frameworkid = $(this).attr('data-frameworkid');
// We are chaining ajax requests here.
var requests = ajax.call([{
methodname: 'tool_lp_duplicate_competency_framework',
args: { id: frameworkid }
}, {
methodname: 'tool_lp_data_for_competency_frameworks_manage_page',
args: {
pagecontext: {
contextid: pagecontextid
}
}
}]);
requests[1].done(reloadList).fail(notification.exception);
};
/**
* Delete a framework and reload the page.
*/
@@ -107,7 +131,7 @@ define(['jquery', 'core/templates', 'core/ajax', 'core/notification', 'core/str'
};
return /** @alias module:tool_lp/frameworkdelete */ {
return /** @alias module:tool_lp/frameworkactions */ {
// Public variables and functions.
/**
@@ -117,6 +141,13 @@ define(['jquery', 'core/templates', 'core/ajax', 'core/notification', 'core/str'
*/
deleteHandler: confirmDelete,
/**
* Expose the event handler for duplicate.
* @method duplicateHandler
* @param {Event} e
*/
duplicateHandler: doDuplicate,
/**
* Initialise the module.
* @method init
+87
View File
@@ -353,6 +353,55 @@ class api {
return $framework;
}
/**
* Duplicate a competency framework by id.
*
* Requires tool/lp:competencymanage capability at the system context.
*
* @param int $id The record to duplicate. All competencies associated and related will be duplicated.
* @return competency_framework the framework duplicated
*/
public static function duplicate_framework($id) {
$framework = new competency_framework($id);
require_capability('tool/lp:competencymanage', $framework->get_context());
// Get a uniq idnumber based on the origin framework.
$idnumber = competency_framework::get_unused_idnumber($framework->get_idnumber());
$framework->set_idnumber($idnumber);
// Adding the suffix copy to the shortname.
$framework->set_shortname(get_string('duplicateditemname', 'tool_lp', $framework->get_shortname()));
$framework->set_id(0);
$framework->create();
// Array that match the old competencies ids with the new one to use when copying related competencies.
$matchids = self::duplicate_competency_tree($framework->get_id(), competency::get_framework_tree($id), 0, 0);
// Copy the related competencies.
$relcomps = related_competency::get_multiple_relations(array_keys($matchids));
foreach ($relcomps as $relcomp) {
$compid = $relcomp->get_competencyid();
$relcompid = $relcomp->get_relatedcompetencyid();
if (isset($matchids[$compid]) && isset($matchids[$relcompid])) {
$newcompid = $matchids[$compid];
$newrelcompid = $matchids[$relcompid];
if ($newcompid < $newrelcompid) {
$relcomp->set_competencyid($newcompid);
$relcomp->set_relatedcompetencyid($newrelcompid);
} else {
$relcomp->set_competencyid($newrelcompid);
$relcomp->set_relatedcompetencyid($newcompid);
}
$relcomp->set_id(0);
$relcomp->create();
} else {
// Debugging message when there is no match found.
debugging('related competency id not found');
}
}
return $framework;
}
/**
* Delete a competency framework by id.
*
@@ -1361,4 +1410,42 @@ class api {
return false;
}
/**
* Recursively duplicate competencies from a tree, we start duplicating from parents to children to have a correct path.
* This method does not copy the related competencies.
*
* @param int $frameworkid - framework id
* @param competency[] $tree - array of competencies object
* @param int $oldparent - old parent id
* @param int $newparent - new parent id
* @return array $matchids - List of old competencies ids matched with new ids.
*/
protected static function duplicate_competency_tree($frameworkid, $tree, $oldparent = 0, $newparent = 0) {
$matchids = array();
foreach ($tree as $node) {
if ($node->competency->get_parentid() == $oldparent) {
$parentid = $node->competency->get_id();
// Create the competency.
$competency = $node->competency;
$competency->set_competencyframeworkid($frameworkid);
$competency->set_parentid($newparent);
$competency->set_path('');
$competency->set_id(0);
$competency->create();
// Match the old id with the new one.
$matchids[$parentid] = $competency->get_id();
if (!empty($node->children)) {
// Duplicate children competency.
$childrenids = self::duplicate_competency_tree($frameworkid, $node->children, $parentid, $competency->get_id());
// Array_merge does not keep keys when merging so we use the + operator.
$matchids = $matchids + $childrenids;
}
}
}
return $matchids;
}
}
+35
View File
@@ -413,4 +413,39 @@ class competency extends persistent {
return $DB->count_records_select(self::TABLE, "id $insql", $params, "COUNT(DISTINCT(competencyframeworkid))") == 1;
}
/**
* Build a framework tree with competency nodes.
*
* @param int $frameworkid the framework id
* @return node[] tree of framework competency nodes
*/
public static function get_framework_tree($frameworkid) {
$competencies = self::search('', $frameworkid);
$parentnode = new stdClass();
$parentnode->competency = new competency();
return self::build_tree($competencies, $parentnode);
}
/**
* Recursively build up the tree of nodes.
*
* @param array $all - List of all competency classes.
* @param stdClass $parent - the exported parent node
*/
protected static function build_tree($all, $parent) {
$tree = array();
foreach ($all as $one) {
if ($one->get_parentid() == $parent->competency->get_id()) {
$node = new stdClass();
$node->competency = $one;
$node->children = self::build_tree($all, $node);
$tree[] = $node;
}
}
return $tree;
}
}
@@ -343,4 +343,25 @@ class competency_framework extends persistent {
return $list;
}
/**
* Get a uniq idnumber.
*
* @param string $idnumber the framework idnumber
* @return string
*/
public static function get_unused_idnumber($idnumber) {
global $DB;
$currentidnumber = $idnumber;
$counter = 0;
// Iteratere while the idnumber exists.
while ($DB->record_exists_select(static::TABLE, 'idnumber = ?', array($currentidnumber))) {
$suffixidnumber = '_' . ++$counter;
$currentidnumber = substr($idnumber, 0, 100 - strlen($suffixidnumber)).$suffixidnumber;
}
// Return the uniq idnumber.
return $currentidnumber;
}
}
+50
View File
@@ -387,6 +387,56 @@ class external extends external_api {
return self::get_competency_framework_external_structure();
}
/**
* Returns description of duplicate_competency_framework() parameters.
*
* @return \external_function_parameters
*/
public static function duplicate_competency_framework_parameters() {
$id = new external_value(
PARAM_INT,
'Data base record id for the framework',
VALUE_REQUIRED
);
$params = array(
'id' => $id,
);
return new external_function_parameters($params);
}
/**
* Expose to AJAX
* @return boolean
*/
public static function duplicate_competency_framework_is_allowed_from_ajax() {
return true;
}
/**
* Duplicate a competency framework
*
* @param int $id The competency framework id
* @return boolean
*/
public static function duplicate_competency_framework($id) {
$params = self::validate_parameters(self::duplicate_competency_framework_parameters(),
array(
'id' => $id,
));
return api::duplicate_framework($params['id']);
}
/**
* Returns description of duplicate_competency_framework() result value.
*
* @return \external_description
*/
public static function duplicate_competency_framework_returns() {
return self::get_competency_framework_external_structure();
}
/**
* Returns description of delete_competency_framework() parameters.
*
@@ -159,4 +159,28 @@ class related_competency extends persistent {
return $competencies;
}
/**
* Get the related competencies from competency ids.
*
* @param int[] $competencyids Array of competency ids.
* @return related_competency[]
*/
public static function get_multiple_relations($competencyids) {
global $DB;
list($insql, $params) = $DB->get_in_or_equal($competencyids);
$records = $DB->get_records_select(self::TABLE,
"competencyid $insql OR relatedcompetencyid $insql",
array_merge($params, $params)
);
$relatedcompetencies = array();
foreach ($records as $record) {
unset($record->id);
$relatedcompetencies[] = new related_competency(null, $record);
}
return $relatedcompetencies;
}
}
+8
View File
@@ -44,6 +44,14 @@ $functions = array(
'type' => 'read',
'capabilities' => 'tool/lp:competencyview',
),
'tool_lp_duplicate_competency_framework' => array(
'classname' => 'tool_lp\external',
'methodname' => 'duplicate_competency_framework',
'classpath' => '',
'description' => 'Duplicate a competency framework.',
'type' => 'write',
'capabilities' => 'tool/lp:competencymanage',
),
'tool_lp_delete_competency_framework' => array(
'classname' => 'tool_lp\external',
'methodname' => 'delete_competency_framework',
@@ -61,6 +61,11 @@
{{#pix}}t/edit{{/pix}} {{#str}}edit{{/str}}
</a>
</li>
<li>
<a data-action="duplicatecompetencyframework" href="#" data-frameworkid="{{id}}">
{{#pix}}t/copy{{/pix}} {{#str}}duplicate{{/str}}
</a>
</li>
<li>
<a data-action="deletecompetencyframework" href="#" data-frameworkid="{{id}}">
{{#pix}}t/delete{{/pix}} {{#str}}delete{{/str}}
@@ -89,14 +94,15 @@
{{#js}}
// Initialise the JS.
require(['tool_lp/frameworkdelete',
require(['tool_lp/frameworkactions',
'tool_lp/menubar'],
function(deleteMod, menubar) {
function(actionsMod, menubar) {
deleteMod.init({{pagecontextid}});
actionsMod.init({{pagecontextid}});
menubar.enhance('.competencyframeworkactions', {
"[data-action='deletecompetencyframework']": deleteMod.deleteHandler
"[data-action='deletecompetencyframework']": actionsMod.deleteHandler,
"[data-action='duplicatecompetencyframework']": actionsMod.duplicateHandler,
});
});
+63
View File
@@ -241,4 +241,67 @@ class tool_lp_api_testcase extends advanced_testcase {
$this->assertEquals($framework3->get_id(), $r2->get_id());
}
/**
* Test duplicate a framework.
*/
public function test_duplicate_framework() {
$lpg = $this->getDataGenerator()->get_plugin_generator('tool_lp');
$this->resetAfterTest(true);
$this->setAdminUser();
$syscontext = context_system::instance();
$params = array(
'shortname' => 'shortname_a',
'idnumber' => 'idnumber_c',
'description' => 'description',
'descriptionformat' => FORMAT_HTML,
'visible' => true,
'contextid' => $syscontext->id
);
$framework = $lpg->create_framework($params);
$competency1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id()));
$competency2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id()));
$competency3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id()));
$competency4 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id()));
$competencyidnumbers = array($competency1->get_idnumber(),
$competency2->get_idnumber(),
$competency3->get_idnumber(),
$competency4->get_idnumber()
);
api::add_related_competency($competency1->get_id(), $competency2->get_id());
api::add_related_competency($competency3->get_id(), $competency4->get_id());
$frameworkduplicated1 = api::duplicate_framework($framework->get_id());
$frameworkduplicated2 = api::duplicate_framework($framework->get_id());
$this->assertEquals($framework->get_idnumber().'_1', $frameworkduplicated1->get_idnumber());
$this->assertEquals($framework->get_idnumber().'_2', $frameworkduplicated2->get_idnumber());
$competenciesfr1 = api::list_competencies(array('competencyframeworkid' => $frameworkduplicated1->get_id()));
$competenciesfr2 = api::list_competencies(array('competencyframeworkid' => $frameworkduplicated2->get_id()));
$competencyidsfr1 = array();
$competencyidsfr2 = array();
foreach ($competenciesfr1 as $cmp) {
$competencyidsfr1[] = $cmp->get_idnumber();
}
foreach ($competenciesfr2 as $cmp) {
$competencyidsfr2[] = $cmp->get_idnumber();
}
$this->assertEmpty(array_diff($competencyidsfr1, $competencyidnumbers));
$this->assertEmpty(array_diff($competencyidsfr2, $competencyidnumbers));
$this->assertCount(4, $competenciesfr1);
$this->assertCount(4, $competenciesfr2);
// Test the related competencies.
reset($competenciesfr1);
$compduplicated1 = current($competenciesfr1);
$relatedcompetencies = $compduplicated1->get_related_competencies();
$comprelated = current($relatedcompetencies);
$this->assertEquals($comprelated->get_idnumber(), $competency2->get_idnumber());
}
}
+1 -1
View File
@@ -25,7 +25,7 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2015052414; // The current plugin version (Date: YYYYMMDDXX).
$plugin->version = 2015052415; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2014110400; // Requires this Moodle version.
$plugin->component = 'tool_lp'; // Full name of the plugin (used for diagnostics).