2023-05-17 21:19:14 +02:00
< ? php
2023-08-24 23:02:41 +02:00
// This file is part of the Studyplan plugin for Moodle
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
/**
*
* @ package local_treestudyplan
* @ copyright 2023 P . M . Kuipers
* @ license https :// www . gnu . org / copyleft / gpl . html GNU GPL v3 or later
*/
2023-05-17 21:19:14 +02:00
namespace local_treestudyplan ;
require_once ( $CFG -> libdir . '/externallib.php' );
class studyplan {
const TABLE = " local_treestudyplan " ;
private static $STUDYPLAN_CACHE = [];
2023-08-24 23:02:41 +02:00
private $r ; // Holds database record.
2023-05-17 21:19:14 +02:00
private $id ;
private $aggregator ;
2023-08-24 23:02:41 +02:00
private $context = null ; // Hold context object once retrieved.
private $linked_userids = null ; // cache lookup of linked users (saves queries).
2023-07-23 16:25:08 +02:00
private $page_cache = null ;
2023-05-17 21:19:14 +02:00
2023-08-24 23:02:41 +02:00
public function aggregator () {
2023-05-17 21:19:14 +02:00
return $this -> aggregator ;
}
// Cache constructors to avoid multiple creation events in one session.
public static function findById ( $id ) : self {
2023-08-24 23:02:41 +02:00
if ( ! array_key_exists ( $id , self :: $STUDYPLAN_CACHE )) {
2023-05-17 21:19:14 +02:00
self :: $STUDYPLAN_CACHE [ $id ] = new self ( $id );
2023-08-24 23:02:41 +02:00
}
2023-05-17 21:19:14 +02:00
return self :: $STUDYPLAN_CACHE [ $id ];
}
private function __construct ( $id ) {
global $DB ;
$this -> id = $id ;
2023-08-24 23:02:41 +02:00
$this -> r = $DB -> get_record ( self :: TABLE , [ 'id' => $id ]);
2023-05-17 21:19:14 +02:00
$this -> aggregator = aggregator :: createOrDefault ( $this -> r -> aggregation , $this -> r -> aggregation_config );
}
2023-08-24 23:02:41 +02:00
public function id () {
2023-05-17 21:19:14 +02:00
return $this -> id ;
}
2023-08-24 23:02:41 +02:00
public function shortname () {
2023-06-26 13:03:50 +02:00
return $this -> r -> shortname ;
}
2023-08-24 23:02:41 +02:00
public function name () {
2023-05-17 21:19:14 +02:00
return $this -> r -> name ;
}
2023-08-24 23:02:41 +02:00
public function pages () {
2023-07-23 16:25:08 +02:00
// cached version of find_studyplan_children.
2023-08-24 23:02:41 +02:00
// (may be premature optimization, since .
// find_studyplan_children also does some caching).
if ( empty ( $this -> page_cache )) {
2023-07-23 16:25:08 +02:00
$this -> page_cache = studyplanpage :: find_studyplan_children ( $this );
}
return $this -> page_cache ;
}
2023-05-17 21:19:14 +02:00
/**
* Return the context this studyplan is associated to
*/
public function context () : \context {
2023-08-24 23:02:41 +02:00
if ( ! isset ( $this -> context )) {
2023-08-24 23:09:20 +02:00
try {
2023-05-17 21:19:14 +02:00
$this -> context = contextinfo :: by_id ( $this -> r -> context_id ) -> context ;
}
2023-08-24 23:02:41 +02:00
catch ( \dml_missing_record_exception $x ) {
throw new \InvalidArgumentException ( " Context { $this -> r -> context_id } not available " ); // Just throw it up again. catch is included here to make sure we know it throws this exception.
2023-05-17 21:19:14 +02:00
}
}
return $this -> context ;
}
2023-08-24 23:02:41 +02:00
public static function simple_structure ( $value = VALUE_REQUIRED ) {
2023-05-17 21:19:14 +02:00
return new \external_single_structure ([
" id " => new \external_value ( PARAM_INT , 'id of studyplan' ),
" name " => new \external_value ( PARAM_TEXT , 'name of studyplan' ),
" shortname " => new \external_value ( PARAM_TEXT , 'shortname of studyplan' ),
2023-08-09 12:20:05 +02:00
" idnumber " => new \external_value ( PARAM_TEXT , 'idnumber of curriculum' ),
2023-05-17 21:19:14 +02:00
" context_id " => new \external_value ( PARAM_INT , 'context_id of studyplan' ),
" description " => new \external_value ( PARAM_TEXT , 'description of studyplan' ),
" aggregation " => new \external_value ( PARAM_TEXT , 'selected aggregator' ),
" aggregation_config " => new \external_value ( PARAM_TEXT , 'config string for aggregator' ),
" aggregation_info " => aggregator :: basic_structure (),
2023-08-24 23:02:41 +02:00
" pages " => new \external_multiple_structure ( studyplanpage :: simple_structure (), 'pages' ),
], 'Basic studyplan info' , $value );
2023-05-17 21:19:14 +02:00
}
2023-08-24 23:02:41 +02:00
public function simple_model () {
2023-07-27 12:28:04 +02:00
$pages = [];
2023-08-24 23:02:41 +02:00
foreach ( $this -> pages () as $p ) {
2023-07-27 12:28:04 +02:00
$pages [] = $p -> simple_model ();
}
2023-05-17 21:19:14 +02:00
return [
'id' => $this -> r -> id ,
'name' => $this -> r -> name ,
'shortname' => $this -> r -> shortname ,
2023-08-09 12:20:05 +02:00
'idnumber' => $this -> r -> idnumber ,
2023-05-17 21:19:14 +02:00
'context_id' => $this -> context () -> id ,
'description' => $this -> r -> description ,
2023-07-27 12:28:04 +02:00
'aggregation' => $this -> r -> aggregation ,
'aggregation_config' => $this -> aggregator -> config_string (),
2023-05-17 21:19:14 +02:00
'aggregation_info' => $this -> aggregator -> basic_model (),
2023-07-27 12:28:04 +02:00
'pages' => $pages ,
2023-05-17 21:19:14 +02:00
];
}
2023-08-24 23:02:41 +02:00
public static function editor_structure ( $value = VALUE_REQUIRED ) {
2023-05-17 21:19:14 +02:00
return new \external_single_structure ([
" id " => new \external_value ( PARAM_INT , 'id of studyplan' ),
" name " => new \external_value ( PARAM_TEXT , 'name of studyplan' ),
" shortname " => new \external_value ( PARAM_TEXT , 'shortname of studyplan' ),
2023-08-09 12:20:05 +02:00
" idnumber " => new \external_value ( PARAM_TEXT , 'idnumber of curriculum' ),
2023-05-17 21:19:14 +02:00
" description " => new \external_value ( PARAM_TEXT , 'description of studyplan' ),
" context_id " => new \external_value ( PARAM_INT , 'context_id of studyplan' ),
" aggregation " => new \external_value ( PARAM_TEXT , 'selected aggregator' ),
" aggregation_config " => new \external_value ( PARAM_TEXT , 'config string for aggregator' ),
" aggregation_info " => aggregator :: basic_structure (),
2023-07-23 16:25:08 +02:00
" pages " => new \external_multiple_structure ( studyplanpage :: editor_structure ()),
2023-05-17 21:19:14 +02:00
" advanced " => new \external_single_structure ([
" force_scales " => new \external_single_structure ([
" scales " => new \external_multiple_structure ( new \external_single_structure ([
" id " => new \external_value ( PARAM_INT , 'id of scale' ),
" name " => new \external_value ( PARAM_TEXT , 'name of scale' ),
])),
2023-08-24 23:02:41 +02:00
], " Scale forcing on stuff " , VALUE_OPTIONAL ),
], " Advanced features available " , VALUE_OPTIONAL ),
], 'Studyplan full structure' , $value );
2023-05-17 21:19:14 +02:00
}
2023-08-24 23:02:41 +02:00
public function editor_model () {
2023-05-17 21:19:14 +02:00
global $DB ;
$model = [
'id' => $this -> r -> id ,
'name' => $this -> r -> name ,
'shortname' => $this -> r -> shortname ,
2023-08-09 09:48:06 +02:00
'idnumber' => $this -> r -> idnumber ,
2023-08-09 12:20:05 +02:00
'description' => $this -> r -> description ,
2023-05-17 21:19:14 +02:00
'context_id' => $this -> context () -> id ,
" aggregation " => $this -> r -> aggregation ,
" aggregation_config " => $this -> aggregator -> config_string (),
2023-08-24 23:02:41 +02:00
'aggregation_info' => $this -> aggregator -> basic_model (),
2023-07-23 16:25:08 +02:00
'pages' => [],
2023-05-17 21:19:14 +02:00
];
2023-08-24 23:02:41 +02:00
foreach ( $this -> pages () as $p ) {
2023-07-23 16:25:08 +02:00
$model [ 'pages' ][] = $p -> editor_model ();
2023-05-17 21:19:14 +02:00
}
2023-08-24 23:02:41 +02:00
if ( has_capability ( 'local/treestudyplan:forcescales' , \context_system :: instance ())) {
if ( ! array_key_exists ( 'advanced' , $model )) {
// Create advanced node if it does not exist.
2023-05-17 21:19:14 +02:00
$model [ 'advanced' ] = [];
}
2023-08-24 23:02:41 +02:00
// get a list of available scales.
$scales = array_map ( function ( $scale ) {
return [ " id " => $scale -> id , " name " => $scale -> name , ];
2023-05-17 21:19:14 +02:00
}, \grade_scale :: fetch_all ( array ( 'courseid' => 0 )) ) ;
$model [ 'advanced' ][ 'force_scales' ] = [
'scales' => $scales ,
];
}
return $model ;
}
2023-08-24 23:02:41 +02:00
public static function add ( $fields , $bare = false ) {
2023-05-17 21:19:14 +02:00
global $CFG , $DB ;
2023-08-24 23:02:41 +02:00
$addable = [ 'name' , 'shortname' , 'description' , 'idnumber' , 'context_id' , 'aggregation' , 'aggregation_config' ];
2023-05-17 21:19:14 +02:00
$info = [ 'enddate' => null ];
2023-08-24 23:02:41 +02:00
foreach ( $addable as $f ) {
if ( array_key_exists ( $f , $fields )) {
2023-05-17 21:19:14 +02:00
$info [ $f ] = $fields [ $f ];
}
}
$id = $DB -> insert_record ( self :: TABLE , $info );
2023-08-24 23:02:41 +02:00
$plan = self :: findById ( $id ); // make sure the new studyplan is immediately cached.
2023-07-23 16:47:02 +02:00
2023-08-24 23:02:41 +02:00
// Start temporary skräpp code.
// Add a single page and copy the names.This keeps the data sane until the upgrade to .
// real page management is done.
2023-08-07 23:07:59 +02:00
// On import, adding an empty page messes things up for now, so we have an option to skip this....
2023-08-24 23:02:41 +02:00
// TODO: Remove this when proper page management is implemented.
if ( ! $bare ) {
2023-08-07 23:07:59 +02:00
2023-08-24 23:02:41 +02:00
$pageaddable = [ 'name' , 'shortname' , 'description' , 'periods' , 'startdate' , 'enddate' ];
2023-08-07 23:07:59 +02:00
$pageinfo = [ 'studyplan_id' => $id ];
2023-08-24 23:02:41 +02:00
foreach ( $pageaddable as $f ) {
if ( array_key_exists ( $f , $fields )) {
if ( $f == " name " ) {
2023-08-07 23:07:59 +02:00
$pageinfo [ " fullname " ] = $fields [ $f ];
} else {
$pageinfo [ $f ] = $fields [ $f ];
}
2023-07-23 16:47:02 +02:00
}
}
2023-08-07 23:07:59 +02:00
$page = studyplanpage :: add ( $pageinfo );
$plan -> page_cache = [ $page ];
2023-07-23 16:47:02 +02:00
}
2023-08-24 23:02:41 +02:00
// End temporary skräpp code.
2023-07-23 16:47:02 +02:00
return $plan ;
2023-05-17 21:19:14 +02:00
}
2023-08-24 23:02:41 +02:00
public function edit ( $fields ) {
2023-05-17 21:19:14 +02:00
global $DB ;
2023-08-24 23:02:41 +02:00
$editable = [ 'name' , 'shortname' , 'description' , 'idnumber' , 'context_id' , 'aggregation' , 'aggregation_config' ];
$info = [ 'id' => $this -> id , ];
foreach ( $editable as $f ) {
if ( array_key_exists ( $f , $fields )) {
2023-05-17 21:19:14 +02:00
$info [ $f ] = $fields [ $f ];
}
}
$DB -> update_record ( self :: TABLE , $info );
2023-08-24 23:02:41 +02:00
//reload record after edit.
$this -> r = $DB -> get_record ( self :: TABLE , [ 'id' => $this -> id ], " * " , MUST_EXIST );
2023-05-17 21:19:14 +02:00
//reload the context...
2023-08-24 23:02:41 +02:00
$this -> context = null ;
2023-05-17 21:19:14 +02:00
$this -> context ();
2023-08-24 23:02:41 +02:00
// reload aggregator.
$this -> aggregator = aggregator :: createOrDefault ( $this -> r -> aggregation , $this -> r -> aggregation_config );
2023-07-23 16:47:02 +02:00
2023-08-24 23:02:41 +02:00
// Start temporary skräpp code.
// TODO: Until proper page editing is implemented, copy data from studyplan to it's first page.
2023-07-23 16:47:02 +02:00
// This keeps the data sane until the upgrade is done.
2023-08-24 23:02:41 +02:00
if ( count ( $this -> pages ()) == 1 ) {
// update the info to the page as well.
2023-07-23 16:47:02 +02:00
$page = $this -> pages ()[ 0 ];
2023-08-24 23:02:41 +02:00
$pageeditable = [ 'name' , 'shortname' , 'description' , 'periods' , 'startdate' , 'enddate' ];
2023-07-23 16:47:02 +02:00
$pageinfo = [];
2023-08-24 23:02:41 +02:00
foreach ( $pageeditable as $f ) {
if ( array_key_exists ( $f , $fields )) {
if ( $f == " name " ) {
2023-07-23 16:47:02 +02:00
$pageinfo [ " fullname " ] = $fields [ $f ];
} else {
$pageinfo [ $f ] = $fields [ $f ];
}
}
}
$page -> edit ( $pageinfo );
}
2023-08-24 23:02:41 +02:00
// End temporary skräpp code.
2023-07-23 16:47:02 +02:00
2023-05-17 21:19:14 +02:00
return $this ;
}
2023-08-24 23:02:41 +02:00
public function delete ( $force = false ) {
2023-05-17 21:19:14 +02:00
global $DB ;
2023-08-24 23:02:41 +02:00
if ( $force ) {
2023-07-23 16:25:08 +02:00
$children = studyplanpage :: find_studyplan_children ( $this );
2023-08-24 23:02:41 +02:00
foreach ( $children as $c ) {
2023-05-17 21:19:14 +02:00
$c -> delete ( $force );
}
}
2023-08-24 23:02:41 +02:00
if ( $DB -> count_records ( 'local_treestudyplan_page' , [ 'studyplan_id' => $this -> id ]) > 0 ) {
2023-07-23 16:25:08 +02:00
return success :: fail ( 'cannot delete studyplan that still has pages' );
2023-08-24 23:09:20 +02:00
} else
2023-05-17 21:19:14 +02:00
{
$DB -> delete_records ( 'local_treestudyplan' , [ 'id' => $this -> id ]);
return success :: success ();
}
}
2023-08-24 23:02:41 +02:00
public static function find_all ( $contextid =- 1 ) {
2023-05-17 21:19:14 +02:00
global $DB , $USER ;
$list = [];
2023-05-19 16:45:15 +02:00
2023-08-24 23:02:41 +02:00
if ( $contextid <= 0 ) {
$ids = $DB -> get_fieldset_select ( self :: TABLE , " id " , " " );
2023-08-24 23:09:20 +02:00
} else {
2023-08-24 23:02:41 +02:00
if ( $contextid == 1 ) {
2023-05-19 16:45:15 +02:00
$contextid = 1 ;
$where = " context_id <= :contextid OR context_id IS NULL " ;
} else {
$where = " context_id = :contextid " ;
}
2023-08-24 23:02:41 +02:00
$ids = $DB -> get_fieldset_select ( self :: TABLE , " id " , $where , [ " contextid " => $contextid ]);
2023-05-19 16:45:15 +02:00
}
2023-08-24 23:02:41 +02:00
foreach ( $ids as $id ) {
2023-05-17 21:19:14 +02:00
$list [] = studyplan :: findById ( $id );
}
return $list ;
}
public static function find_by_shortname ( $shortname , $contextid = 0 ) : array {
global $DB ;
$list = [];
$where = " shortname = :shortname AND context_id = :contextid " ;
2023-08-24 23:02:41 +02:00
if ( $contextid == 0 ) {
2023-05-17 21:19:14 +02:00
$where .= " OR context_id IS NULL " ;
}
2023-08-24 23:02:41 +02:00
$ids = $DB -> get_fieldset_select ( self :: TABLE , " id " , $where , [ " shortname " => $shortname , " contextid " => $contextid ]);
foreach ( $ids as $id ) {
2023-05-17 21:19:14 +02:00
$list [] = studyplan :: findById ( $id );
}
return $list ;
}
2023-08-24 23:02:41 +02:00
public static function find_for_user ( $userid ) {
2023-05-17 21:19:14 +02:00
global $DB ;
2023-08-24 23:02:41 +02:00
$sql = " SELECT s.id FROM { local_treestudyplan} s
2023-05-17 21:19:14 +02:00
INNER JOIN { local_treestudyplan_cohort } j ON j . studyplan_id = s . id
INNER JOIN { cohort_members } cm ON j . cohort_id = cm . cohortid
WHERE cm . userid = : userid " ;
$cohort_plan_ids = $DB -> get_fieldset_sql ( $sql , [ 'userid' => $userid ]);
2023-08-24 23:02:41 +02:00
$sql = " SELECT s.id FROM { local_treestudyplan} s
2023-05-17 21:19:14 +02:00
INNER JOIN { local_treestudyplan_user } j ON j . studyplan_id = s . id
WHERE j . user_id = : userid " ;
$user_plan_ids = $DB -> get_fieldset_sql ( $sql , [ 'userid' => $userid ]);
$plans = [];
2023-08-24 23:02:41 +02:00
foreach ( $cohort_plan_ids as $id ) {
2023-05-17 21:19:14 +02:00
$plans [ $id ] = self :: findById ( $id );
}
2023-08-24 23:02:41 +02:00
foreach ( $user_plan_ids as $id ) {
if ( ! array_key_exists ( $id , $plans )) {
2023-05-17 21:19:14 +02:00
$plans [ $id ] = self :: findById ( $id );
}
}
return $plans ;
}
2023-08-24 23:02:41 +02:00
static public function exist_for_user ( $userid ) {
2023-05-17 21:19:14 +02:00
global $DB ;
$count = 0 ;
2023-08-24 23:02:41 +02:00
$sql = " SELECT s.* FROM { local_treestudyplan} s
2023-05-17 21:19:14 +02:00
INNER JOIN { local_treestudyplan_cohort } j ON j . studyplan_id = s . id
INNER JOIN { cohort_members } cm ON j . cohort_id = cm . cohortid
WHERE cm . userid = : userid " ;
$count += $DB -> count_records_sql ( $sql , [ 'userid' => $userid ]);
2023-08-24 23:02:41 +02:00
$sql = " SELECT s.* FROM { local_treestudyplan} s
2023-05-17 21:19:14 +02:00
INNER JOIN { local_treestudyplan_user } j ON j . studyplan_id = s . id
WHERE j . user_id = : userid " ;
$count += $DB -> count_records_sql ( $sql , [ 'userid' => $userid ]);
return ( $count > 0 );
}
2023-08-24 23:02:41 +02:00
/**
2023-05-17 21:19:14 +02:00
* Retrieve the users linked to this studyplan .
* @ return array of User objects
*/
2023-08-24 23:02:41 +02:00
public function find_linked_users () {
2023-05-17 21:19:14 +02:00
global $DB ;
$users = [];
$uids = $this -> find_linked_userids ();
2023-08-24 23:02:41 +02:00
foreach ( $uids as $uid ) {
$users [] = $DB -> get_record ( " user " , [ " id " => $uid ]);
2023-05-17 21:19:14 +02:00
}
return $users ;
}
2023-08-24 23:02:41 +02:00
/**
2023-05-17 21:19:14 +02:00
* Retrieve the user id ' s of the users linked to this studyplan .
* @ return array of int ( User Id )
*/
2023-06-16 23:12:17 +02:00
public function find_linked_userids () : array {
2023-05-17 21:19:14 +02:00
global $DB ;
2023-08-24 23:02:41 +02:00
if ( $this -> linked_userids === null ) {
2023-06-16 23:12:17 +02:00
$uids = [];
2023-08-24 23:02:41 +02:00
// First get directly linked userids.
2023-06-16 23:12:17 +02:00
$sql = " SELECT j.user_id FROM { local_treestudyplan_user} j
WHERE j . studyplan_id = : planid " ;
$ulist = $DB -> get_fieldset_sql ( $sql , [ 'planid' => $this -> id ]);
2023-05-17 21:19:14 +02:00
2023-08-24 23:02:41 +02:00
$uids = array_merge ( $uids , $ulist );
foreach ( $ulist as $uid ) {
$users [] = $DB -> get_record ( " user " , [ " id " => $uid ]);
2023-06-16 23:12:17 +02:00
}
2023-05-17 21:19:14 +02:00
2023-08-24 23:02:41 +02:00
// Next het users linked though cohort.
$sql = " SELECT cm.userid FROM { local_treestudyplan_cohort} j
2023-06-16 23:12:17 +02:00
INNER JOIN { cohort_members } cm ON j . cohort_id = cm . cohortid
WHERE j . studyplan_id = : planid " ;
$ulist = $DB -> get_fieldset_sql ( $sql , [ 'planid' => $this -> id ]);
2023-05-17 21:19:14 +02:00
2023-08-24 23:02:41 +02:00
$uids = array_merge ( $uids , $ulist );
2023-05-17 21:19:14 +02:00
2023-06-16 23:12:17 +02:00
$this -> linked_userids = array_unique ( $uids );
}
return $this -> linked_userids ;
2023-05-17 21:19:14 +02:00
}
/** Check if this studyplan is linked to a particular user
2023-08-24 23:02:41 +02:00
* @ param bool | stdClass $user The userid or user record of the user
2023-05-17 21:19:14 +02:00
*/
2023-08-24 23:02:41 +02:00
public function has_linked_user ( $user ) {
if ( is_int ( $user )) {
2023-05-17 21:19:14 +02:00
$userid = $user ;
} else {
$userid = $user -> id ;
}
$uids = $this -> find_linked_userids ();
2023-08-24 23:02:41 +02:00
if ( in_array ( $userid , $uids )) {
2023-05-17 21:19:14 +02:00
return true ;
} else {
return false ;
}
}
2023-08-24 23:02:41 +02:00
public static function user_structure ( $value = VALUE_REQUIRED ) {
2023-05-17 21:19:14 +02:00
return new \external_single_structure ([
" id " => new \external_value ( PARAM_INT , 'id of studyplan' ),
" name " => new \external_value ( PARAM_TEXT , 'name of studyplan' ),
" shortname " => new \external_value ( PARAM_TEXT , 'shortname of studyplan' ),
" description " => new \external_value ( PARAM_TEXT , 'description of studyplan' ),
2023-08-09 09:48:06 +02:00
" idnumber " => new \external_value ( PARAM_TEXT , 'idnumber of curriculum' ),
2023-07-23 16:25:08 +02:00
" pages " => new \external_multiple_structure ( studyplanpage :: user_structure ()),
2023-05-17 21:19:14 +02:00
" aggregation_info " => aggregator :: basic_structure (),
2023-08-24 23:02:41 +02:00
], 'Studyplan with user info' , $value );
2023-05-17 21:19:14 +02:00
}
2023-08-24 23:02:41 +02:00
public function user_model ( $userid ) {
2023-05-17 21:19:14 +02:00
$model = [
'id' => $this -> r -> id ,
'name' => $this -> r -> name ,
'shortname' => $this -> r -> shortname ,
'description' => $this -> r -> description ,
2023-08-09 09:48:06 +02:00
'idnumber' => $this -> r -> idnumber ,
2023-07-23 16:25:08 +02:00
'pages' => [],
2023-05-17 21:19:14 +02:00
'aggregation_info' => $this -> aggregator -> basic_model (),
];
2023-08-24 23:02:41 +02:00
foreach ( $this -> pages () as $p ) {
2023-07-23 16:25:08 +02:00
$model [ 'pages' ][] = $p -> user_model ( $userid );
2023-05-17 21:19:14 +02:00
}
return $model ;
}
2023-08-24 23:02:41 +02:00
public static function duplicate_plan ( $plan_id , $name , $shortname ) {
2023-05-17 21:19:14 +02:00
$ori = self :: findById ( $plan_id );
2023-08-24 23:02:41 +02:00
$new = $ori -> duplicate ( $name , $shortname );
2023-05-17 21:19:14 +02:00
return $new -> simple_model ();
}
2023-08-24 23:02:41 +02:00
public function duplicate ( $name , $shortname ) {
// First duplicate the studyplan structure.
2023-08-07 23:07:59 +02:00
$newplan = studyplan :: add ([
2023-05-17 21:19:14 +02:00
'name' => $name ,
'shortname' => $shortname ,
'description' => $this -> r -> description ,
]);
2023-08-24 23:02:41 +02:00
// next, copy the studylines.
foreach ( $this -> pages () as $p ) {
2023-08-07 23:07:59 +02:00
$newchild = $p -> duplicate ( $newplan );
2023-05-17 21:19:14 +02:00
}
2023-08-07 23:07:59 +02:00
return $newplan ;
2023-08-24 23:02:41 +02:00
}
2023-05-17 21:19:14 +02:00
2023-08-24 23:02:41 +02:00
public static function export_structure () {
2023-05-17 21:19:14 +02:00
return new \external_single_structure ([
" format " => new \external_value ( PARAM_TEXT , 'format of studyplan export' ),
" content " => new \external_value ( PARAM_TEXT , 'exported studyplan content' ),
2023-08-24 23:02:41 +02:00
], 'Exported studyplan' );
2023-05-17 21:19:14 +02:00
}
2023-08-24 23:02:41 +02:00
public function export_plan () {
2023-05-17 21:19:14 +02:00
$model = $this -> export_model ();
$json = json_encode ([
" type " => " studyplan " ,
2023-07-23 16:25:08 +02:00
" version " => 2.0 ,
2023-05-17 21:19:14 +02:00
" studyplan " => $model
2023-08-24 23:02:41 +02:00
], \JSON_PRETTY_PRINT );
2023-05-17 21:19:14 +02:00
return [ " format " => " application/json " , " content " => $json ];
}
2023-08-24 23:02:41 +02:00
public function export_model () {
2023-05-17 21:19:14 +02:00
$model = [
'name' => $this -> r -> name ,
'shortname' => $this -> r -> shortname ,
'description' => $this -> r -> description ,
" aggregation " => $this -> r -> aggregation ,
" aggregation_config " => json_decode ( $this -> aggregator -> config_string ()),
'aggregation_info' => $this -> aggregator -> basic_model (),
2023-07-23 16:25:08 +02:00
'pages' => $this -> export_pages_model (),
2023-05-17 21:19:14 +02:00
];
return $model ;
}
2023-08-24 23:02:41 +02:00
public function export_pages_model () {
2023-07-23 16:25:08 +02:00
$pages = [];
2023-08-24 23:02:41 +02:00
foreach ( $this -> pages () as $p ) {
2023-07-23 16:25:08 +02:00
$pages [] = $p -> export_model ();
2023-05-17 21:19:14 +02:00
}
2023-07-23 16:25:08 +02:00
return $pages ;
2023-05-17 21:19:14 +02:00
}
2023-08-24 23:02:41 +02:00
public static function import_studyplan ( $content , $format = " application/json " , $context_id = 1 ) {
if ( $format != " application/json " ) { return false ;}
$content = json_decode ( $content , true );
if ( $content [ " type " ] == " studyplan " && $content [ " version " ] >= 2.0 ) {
// Make sure the aggregation_config is re-encoded as json text.
2023-05-17 21:19:14 +02:00
$content [ " studyplan " ][ " aggregation_config " ] = json_encode ( $content [ " studyplan " ][ " aggregation_config " ]);
2023-08-24 23:02:41 +02:00
// And make sure the context_id is set to the provided context for import.
2023-05-19 16:45:15 +02:00
$content [ " studyplan " ][ " context_id " ] = $context_id ;
2023-07-23 16:25:08 +02:00
2023-08-24 23:02:41 +02:00
// Create a new plan, based on the given parameters - this is the import studyplan part.
$plan = self :: add ( $content [ " studyplan " ], true );
// Now import each page.
2023-07-23 16:25:08 +02:00
return $plan -> import_pages_model ( $content [ " studyplan " ][ " pages " ]);
2023-05-17 21:19:14 +02:00
2023-08-24 23:09:20 +02:00
} else {
2023-05-17 21:19:14 +02:00
error_log ( " Invalid format and type: { $content [ 'type' ] } version { $content [ 'version' ] } " );
return false ;
}
}
2023-08-24 23:02:41 +02:00
public function import_pages ( $content , $format = " application/json " ) {
if ( $format != " application/json " ) { return false ;}
$content = json_decode ( $content , true );
if ( $content [ " version " ] >= 2.0 ) {
if ( $content [ " type " ] == " studyplanpage " ) {
// import single page from a studyplanpage (wrapped in array of one page).
2023-07-23 16:25:08 +02:00
return $this -> import_pages_model ([ $content [ " page " ]]);
2023-08-24 23:09:20 +02:00
} else if ( $content [ " type " ] == " studyplan " ) {
2023-08-24 23:02:41 +02:00
// Import all pages from the studyplan.
2023-07-23 16:25:08 +02:00
return $this -> import_pages_model ( $content [ " studyplan " ][ " pages " ]);
}
2023-08-24 23:09:20 +02:00
} else {
2023-05-17 21:19:14 +02:00
return false ;
}
}
2023-08-24 23:02:41 +02:00
protected function import_pages_model ( $model ) {
2023-07-23 16:25:08 +02:00
$this -> pages (); // make sure the page cache is initialized, since we will be adding to it.
2023-08-24 23:02:41 +02:00
foreach ( $model as $p ) {
2023-07-23 16:25:08 +02:00
$p [ " studyplan_id " ] = $this -> id ();
$page = studyplanpage :: add ( $p );
$this -> page_cache [] = $page ;
2023-07-27 16:58:23 +02:00
$page -> import_periods_model ( $p [ " perioddesc " ]);
2023-07-23 16:25:08 +02:00
$page -> import_studylines_model ( $p [ " studylines " ]);
2023-05-17 21:19:14 +02:00
}
return true ;
}
2023-06-27 07:33:27 +02:00
/**
* Mark the studyplan as changed regarding courses and associated cohorts
*/
2023-08-24 23:02:41 +02:00
public function mark_csync_changed () {
2023-06-27 07:33:27 +02:00
global $DB ;
2023-08-24 23:02:41 +02:00
$DB -> update_record ( self :: TABLE , [ 'id' => $this -> id , " csync_flag " => 1 ]);
$this -> r -> csync_flag = 1 ; //manually set it in the cache, if something unexpected happened, an exception has already been thrown anyway.
2023-06-27 07:33:27 +02:00
}
/**
* Clear the csync mark
*/
2023-08-24 23:02:41 +02:00
public function clear_csync_changed () {
2023-06-27 07:33:27 +02:00
global $DB ;
2023-08-24 23:02:41 +02:00
$DB -> update_record ( self :: TABLE , [ 'id' => $this -> id , " csync_flag " => 0 ]);
$this -> r -> csync_flag = 0 ; //manually set it in the cache, if something unexpected happened, an exception has already been thrown anyway.
2023-06-27 07:33:27 +02:00
}
2023-08-24 23:02:41 +02:00
public function has_csync_changed () {
2023-06-27 07:33:27 +02:00
return ( $this -> r -> csync_flag > 0 ) ? true : false ;
}
2023-06-16 13:49:47 +02:00
/**
* See if the specified course id is linked in this studyplan
*/
2023-08-24 23:02:41 +02:00
public function course_linked ( $courseid ) {
2023-06-16 13:49:47 +02:00
global $DB ;
$sql = " SELECT COUNT(i.id)
FROM { local_treestudyplan }
INNER JOIN { local_treestudyplan_line } l ON p . id = l . studyplan_id
INNER JOIN { local_treestudyplan_item } i ON l . id = i . line_id
2023-08-24 23:02:41 +02:00
WHERE p . id = : planid
2023-06-16 13:49:47 +02:00
AND i . course_id = : courseid " ;
2023-08-24 23:02:41 +02:00
$count = $DB -> get_field_sql ( $sql , [ " courseid " => $courseid , " planid " => $this -> id ]);
2023-06-16 13:49:47 +02:00
return ( $count > 0 ) ? true : false ;
}
2023-06-16 23:12:17 +02:00
/**
* List the course id is linked in this studyplan
* Used for cohort enrolment cascading
*/
2023-08-24 23:02:41 +02:00
public function get_linked_course_ids () {
2023-06-16 23:12:17 +02:00
global $DB ;
$sql = " SELECT i.course_id
2023-06-19 22:48:33 +02:00
FROM { local_treestudyplan } p
2023-08-07 22:20:45 +02:00
INNER JOIN { local_treestudyplan_page } pg ON p . id = pg . studyplan_id
INNER JOIN { local_treestudyplan_line } l ON pg . id = l . page_id
2023-06-16 23:12:17 +02:00
INNER JOIN { local_treestudyplan_item } i ON l . id = i . line_id
2023-06-19 22:48:33 +02:00
WHERE p . id = : studyplan_id AND i . type = : itemtype " ;
2023-08-24 23:02:41 +02:00
$fields = $DB -> get_fieldset_sql ( $sql , [ " studyplan_id " => $this -> id , " itemtype " => studyitem :: COURSE ]);
2023-06-19 22:48:33 +02:00
return $fields ;
}
2023-06-16 23:12:17 +02:00
2023-06-19 22:48:33 +02:00
/**
* List the cohort id ' s associated with this studyplan
*/
2023-08-24 23:02:41 +02:00
public function get_linked_cohort_ids () {
2023-06-19 22:48:33 +02:00
global $CFG , $DB ;
$sql = " SELECT DISTINCT j.cohort_id FROM { local_treestudyplan_cohort} j
WHERE j . studyplan_id = : studyplan_id " ;
2023-06-26 21:44:31 +02:00
$fields = $DB -> get_fieldset_sql ( $sql , [ 'studyplan_id' => $this -> id ]);
2023-06-16 23:12:17 +02:00
return $fields ;
}
2023-06-30 12:14:11 +02:00
/**
* List the user id ' s explicitly associated with this studyplan
*/
2023-08-24 23:02:41 +02:00
public function get_linked_user_ids () {
2023-06-30 12:14:11 +02:00
global $CFG , $DB ;
2023-06-19 22:48:33 +02:00
2023-06-30 12:14:11 +02:00
$sql = " SELECT DISTINCT j.user_id FROM { local_treestudyplan_user} j
WHERE j . studyplan_id = : studyplan_id " ;
$fields = $DB -> get_fieldset_sql ( $sql , [ 'studyplan_id' => $this -> id ]);
return $fields ;
}
2023-06-16 13:49:47 +02:00
/**
* See if the specified course id is linked in this studyplan
*/
2023-08-24 23:02:41 +02:00
public function badge_linked ( $badgeid ) {
2023-06-16 13:49:47 +02:00
global $DB ;
$sql = " SELECT COUNT(i.id)
FROM { local_treestudyplan }
INNER JOIN { local_treestudyplan_line } l ON p . id = l . studyplan_id
INNER JOIN { local_treestudyplan_item } i ON l . id = i . line_id
2023-08-24 23:02:41 +02:00
WHERE p . id = : planid
2023-06-16 13:49:47 +02:00
AND i . badge_id = : badgeid " ;
2023-08-24 23:02:41 +02:00
$count = $DB -> get_field_sql ( $sql , [ " badgeid " => $badgeid , " planid " => $this -> id ]);
2023-06-16 13:49:47 +02:00
return ( $count > 0 ) ? true : false ;
}
2023-05-17 21:19:14 +02:00
}