diff --git a/classes/task/insert_missing_helptable_entries.php b/classes/task/insert_missing_helptable_entries.php index 3e0a15e..87c6d2f 100644 --- a/classes/task/insert_missing_helptable_entries.php +++ b/classes/task/insert_missing_helptable_entries.php @@ -36,15 +36,8 @@ public function get_name(): string { #[\Override] public function execute(): void { - global $pgdb, $CFG; - include_once($CFG->dirroot . '/local/lsf_unification/class_pg_lite.php'); - include_once($CFG->dirroot . '/local/lsf_unification/lib_features.php'); - $pgdb = new pg_lite(); - $connected = $pgdb->connect(); - $recourceid = pg_connection_status($pgdb->connection); - mtrace('! = unknown category found, ? = unknown linkage found;' . 'Verbindung: ' . - ($connected ? 'ja' : 'nein') . ' (' . $recourceid . ')'); + global $CFG; + require_once($CFG->dirroot . '/local/lsf_unification/lib_his.php'); insert_missing_helptable_entries(true); - $pgdb->dispose(); } } diff --git a/classes/task/insert_missing_helptable_entries_adhoc.php b/classes/task/insert_missing_helptable_entries_adhoc.php new file mode 100644 index 0000000..25bf0b1 --- /dev/null +++ b/classes/task/insert_missing_helptable_entries_adhoc.php @@ -0,0 +1,33 @@ +. + +namespace local_lsf_unification\task; + +/** + * Ad hoc task to update the lsf unification tables. + * + * @package local_lsf_unification + * @copyright 2026 Tamaro Walter + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class insert_missing_helptable_entries_adhoc extends \core\task\adhoc_task { + #[\Override] + public function execute(): void { + global $CFG; + require_once($CFG->dirroot . '/local/lsf_unification/lib_his.php'); + insert_missing_helptable_entries(true); + } +} diff --git a/db/install.xml b/db/install.xml index fe93cdd..b0da12b 100644 --- a/db/install.xml +++ b/db/install.xml @@ -25,7 +25,7 @@ - + diff --git a/helptablemanager.php b/helptablemanager.php index c87a17f..b66a2ba 100644 --- a/helptablemanager.php +++ b/helptablemanager.php @@ -21,7 +21,13 @@ * @copyright 2025 Tamaro Walter * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + +use core\output\notification; +use core\task\manager; +use local_lsf_unification\task\insert_missing_helptable_entries_adhoc; + define('NO_OUTPUT_BUFFERING', true); + require_once("../../config.php"); require_once("$CFG->libdir/adminlib.php"); require_once("./lib_his.php"); @@ -29,38 +35,52 @@ require_login(); admin_externalpage_setup('local_lsf_unification_helptable'); -echo $OUTPUT->header(); -echo $OUTPUT->heading('HISLSF Helptable Management'); - $originid = optional_param('originid', -1, PARAM_INT); // HIS category origin id. $mainid = optional_param('mainid', -1, PARAM_INT); // HIS catecory id. $mdlid = optional_param('mdlid', -1, PARAM_INT); // Moodle category id. $maxorigin = optional_param('maxorigin', 0, PARAM_INT); // Max (his origin ids). $delete = optional_param('delete', 0, PARAM_INT); // Category id where to remove a matching. +$updatehelptable = optional_param('updatehelptable', 0, PARAM_BOOL); +$basepath = '/local/lsf_unification/helptablemanager.php'; +$output = ""; if ($originid == -1) { - $updatetable = get_string('update_helptable', 'local_lsf_unification'); - $createmappings = get_string('create_mappings', 'local_lsf_unification'); - echo "

" . $OUTPUT->box('' . $updatetable . '') . "

"; - echo "

" . $OUTPUT->box('' . $createmappings . '') . "

"; + require_capability('moodle/site:config', context_system::instance()); + if ($updatehelptable) { + $existingtasks = \core\task\manager::get_adhoc_tasks(insert_missing_helptable_entries_adhoc::class); + if (empty($existingtasks)) { + $updatetime = time() + 60; + $task = new insert_missing_helptable_entries_adhoc(); + $task->set_next_run_time($updatetime); + manager::queue_adhoc_task($task, true); + $params = ['time' => userdate($updatetime), 'link' => (new moodle_url("/admin/tool/task/adhoctasks.php"))->out()]; + $info = get_string('update_helptable_notification', 'local_lsf_unification', $params); + } else { + $params = ['link' => (new moodle_url("/admin/tool/task/adhoctasks.php"))->out()]; + $info = get_string('update_helptable_already_queued', 'local_lsf_unification', $params); + } + redirect(new moodle_url($basepath), $info); + } + $mustachedata = [ + 'isadmin' => has_capability('moodle/site:config', context_system::instance()), + 'createmappings' => new moodle_url($basepath, ['originid' => 0]), + ]; + $output = $OUTPUT->render_from_template('local_lsf_unification/helptable_manager/overview', $mustachedata); } else if ($mainid == -1) { if (!empty($delete)) { set_cat_mapping($delete, 0); } - $prefix = "-"; if (empty($originid)) { $origins = implode(", ", get_his_toplevel_originids()); - $parents = ["" => "- Lehrveranstaltungen"]; - $prefix .= "-"; + $parents = ["" => "Lehrveranstaltungen"]; } else { $origins = $originid; $id = $originid; $parents = [$id => " " . get_newest_element($id)->txt]; while ($parent = get_newest_parent($id)) { foreach ($parents as $key => $txt) { - $parents[$key] = "-" . $parents[$key]; + $parents[$key] = $parents[$key]; } - $prefix .= "-"; if (($id == $parent->ueid) || ($id == $parent->origin)) { break; } @@ -68,111 +88,89 @@ $parents[$id] = " " . $parent->txt; } } - $parentstxt = ""; - foreach ($parents as $id => $txt) { + $parentstxt = []; + foreach (array_reverse($parents, true) as $id => $txt) { $navigate = get_string('navigate', 'local_lsf_unification'); - $parentstxt = " [" . $navigate . "] " . $txt . "
" . $parentstxt; + $parentstxt[] = [ + 'parentlink' => new moodle_url($basepath, ['originid' => $id]), + 'parenttext' => $txt, + ]; } $sublevels = get_newest_sublevels($origins); - $childlist = ""; + $childs = []; foreach ($sublevels as $child) { $child->mdlid = get_mdlid($child->origin); $child->name = empty($child->mdlid) ? "" : get_mdlname($child->origin); $maxorigin = ($child->origin > $maxorigin) ? $child->origin : $maxorigin; - $str = [ - 0 => get_string('navigate', 'local_lsf_unification'), - 1 => get_string('not_mapped', 'local_lsf_unification'), - 2 => get_string(empty($child->mdlid) ? 'map' : 'overwrite', 'local_lsf_unification'), - 3 => get_string('delete', 'local_lsf_unification'), + $childs[] = [ + 'sublevels' => has_sublevels($child->ueid), + 'childoriginlink' => new moodle_url($basepath, ['originid' => $child->origin]), + 'idchildorigin' => "idch_" . ($child->origin), + 'prefixchildtxt' => $child->txt, + 'emptychildmdlid' => empty($child->mdlid), + 'categorylink' => new moodle_url('/course/index.php', ['categoryid' => $child->mdlid]), + 'childname' => $child->name, + 'namechildorigin' => 'ch_' . ($child->origin), + 'deletelink' => new moodle_url($basepath, ['originid' => $originid, 'delete' => $child->origin]), ]; - $childlist .= - "" . ( - (!has_sublevels($child->ueid)) - ? " " - : (" [ - " . $str[0] . "] - ") - ) . " - - - -  [" . - (empty($child->mdlid) - ? $str[1] - : ("" . ($child->name) . "")) . "] - -  [ - - - ] - " . ( - (empty($child->mdlid)) - ? " " - : (" [ - " . $str[3] . " - ] - ") - ) . - ""; } $maincategories = get_mdl_toplevels(); - $options = ""; + $options = []; foreach ($maincategories as $id => $txt) { - $options .= ""; + $options[] = ['value' => $id, 'text' => $txt->name]; } - $catchoice = "" . get_string('main_category', 'local_lsf_unification') . ": -   - - - "; - echo "
-

" . $OUTPUT->box($parentstxt) . "

-

" . $OUTPUT->box("" . $childlist . "
") . "

-

" . $OUTPUT->box($catchoice) . "

-
"; + $mustachedata = [ + 'parents' => $parentstxt, + 'childs' => $childs, + 'options' => $options, + 'originid' => $originid, + 'maxorigin' => $maxorigin, + ]; + $output = $OUTPUT->render_from_template('local_lsf_unification/helptable_manager/childlist', $mustachedata); } else if ($mdlid == -1) { $hiddenfields = ""; - $childlist = ""; + $childs = []; + $hiddenfields = []; for ($i = 0; $i <= $maxorigin; $i++) { if (isset($_GET["ch_" . $i])) { - $hiddenfields .= ""; - $childlist .= (empty($childlist) ? "" : "
") . "-" . (get_newest_element($i)->txt) . ""; + $hiddenfields[] = ['hiddenname' => 'ch_' . $i]; + $childs[] = ['childtext' => get_newest_element($i)->txt]; } } $subcats = get_mdl_sublevels($mainid); $displaylist = []; - $displaylist = core_course_category::make_categories_list(); - $options = ""; + $displaylist = core_course_category::make_categories_list(); + $options = []; foreach ($displaylist as $id => $txt) { if (isset($subcats[$id])) { - $options .= ""; + $options[] = ['optionid' => $id, 'optiontext' => $txt]; } } - echo "
-

" . $OUTPUT->box("

" . $childlist . "

=> -

- " . get_string('sub_category', 'local_lsf_unification') . ": - -

") . - "

- - - " . - $hiddenfields . - " -
"; + // Additional variable if only 1 category is available for mapping. + $singlecat = count($options) == 1 ? ['category' => $options[0]['optiontext'], 'categoryid' => $options[0]['optionid']] : []; + $mustachedata = [ + 'childs' => $childs, + 'options' => $options, + 'originid' => $originid, + 'mainid' => $mainid, + 'maxorigin' => $maxorigin, + 'hiddenfields' => $hiddenfields, + 'single_category' => $singlecat, + ]; + $output = $OUTPUT->render_from_template('local_lsf_unification/helptable_manager/mapping_submit', $mustachedata); } else { - $mapchilds = []; - $maptxt = ""; $count = 0; for ($i = 0; $i <= $maxorigin; $i++) { if (isset($_GET["ch_" . $i])) { - $maptxt .= $i . "-" . $mdlid . "
"; set_cat_mapping($i, $mdlid); $count++; } } - echo "

" . $OUTPUT->box($maptxt . "" . $count . " " . get_string('map_done', 'local_lsf_unification')) . "

"; - echo "

" . $OUTPUT->box('' . get_string('create_mappings', 'local_lsf_unification') . '') . "

"; + $string = $count . " " . get_string('map_done', 'local_lsf_unification'); + redirect(new moodle_url($basepath, ['originid' => 0]), $string); } + +echo $OUTPUT->header(); +echo $OUTPUT->heading('HISLSF Helptable Management'); +echo $output; echo $OUTPUT->footer(); diff --git a/lang/de/local_lsf_unification.php b/lang/de/local_lsf_unification.php index a448c87..fa85cf7 100644 --- a/lang/de/local_lsf_unification.php +++ b/lang/de/local_lsf_unification.php @@ -108,6 +108,8 @@ $string['main_category'] = 'Hauptkategorie'; $string['map'] = 'zuordnen'; $string['map_done'] = 'Zuordnung(en) wurde(n) festgehalten'; +$string['map_list_empty'] = "Keine Elemente ausgewählt"; +$string['map_submit'] = 'Folgende Veranstaltungen werden zugeordnet:'; $string['mapped'] = 'zugeordnet'; $string['max_import_age'] = 'Maximales Kurs-Alter'; $string['max_import_age_description'] = 'Maximales Alter, dass ein Kurs haben darf, danach wird er nicht mehr zur Auswahl angezeigt.'; diff --git a/lang/en/local_lsf_unification.php b/lang/en/local_lsf_unification.php index f5c355d..daf6c3a 100644 --- a/lang/en/local_lsf_unification.php +++ b/lang/en/local_lsf_unification.php @@ -121,6 +121,8 @@ $string['main_category'] = 'Maincateogry'; $string['map'] = 'map'; $string['map_done'] = 'mapping(s) saved'; +$string['map_list_empty'] = "No items selected"; +$string['map_submit'] = "You are about to map the following courses:"; $string['mapped'] = 'mapped'; $string['max_import_age'] = 'Max Import Age'; $string['max_import_age_description'] = 'The maximum age, a course may have before being imported.'; @@ -177,7 +179,8 @@ $string['task_missing_helptable_entries'] = 'Update table of LSF categories.'; $string['template_from_course'] = 'Alternative {$a}: Include contents from an existing course ...'; $string['update_helptable'] = 'Update Helptable With HIS-LSF data'; -$string['update_helptable_notification'] = 'All tables of lsf_unification were updated'; +$string['update_helptable_already_queued'] = 'Task to update tables is already queued. Click here for more information.'; +$string['update_helptable_notification'] = 'The Tables will be updated at {$a->time}. Click here for more information.'; $string['warning_cannot_enrol_nologin'] = "person wasn't enrolled (no username found)"; $string['warning_cannot_enrol_nouser'] = "person wasn't enrolled (no user found)"; $string['warning_cannot_enrol_other'] = "person wasn't enrolled"; diff --git a/lib_his.php b/lib_his.php index 84295c3..c443635 100644 --- a/lib_his.php +++ b/lib_his.php @@ -301,32 +301,27 @@ function is_course_of_teacher(int $veranstid, string $username): bool { } /** - * Find_origin_category is NOT a required function for the lsf_unification plugin, it is used - * internally only + * Finds the origin category of a given category by traversing the quellid chain in memory. * - * @param int $quellid - * @return int $origin + * Categories in HIS_UEBERSCHRIFT are duplicated each semester, each with a new ueid but + * referencing their original via quellid. This function walks the quellid chain until it + * reaches the root, i.e. the category where quellid == ueid. + * + * Example: ueid=71420 (SS 2012) → quellid=66656 (WS 2011) → quellid=66656 (root) → returns 66656 + * + * @param int $ueid The ueid of the category to find the origin for. + * @param array $lsfcategories All categories indexed by ueid, as returned by pg_fetch_all + array_column. + * @return int The ueid of the origin (root) category. */ -function find_origin_category(int $quellid): int { - global $pgdb; - $origin = $quellid; +function find_origin_category(int $ueid, array $lsfcategories): int { + $origin = $ueid; do { - $quellid = $origin; - $q = pg_query( - $pgdb->connection, - "SELECT quellid FROM " . HIS_UEBERSCHRIFT . " WHERE ueid = '" . $quellid . "'" - ); - if ($hislsftitle = pg_fetch_object($q)) { - $q2 = pg_query( - $pgdb->connection, - "SELECT quellid FROM " . HIS_UEBERSCHRIFT . " WHERE ueid = '" . - ($hislsftitle->quellid) . "'" - ); - if ($hislsftitle2 = pg_fetch_object($q2)) { - $origin = $hislsftitle->quellid; - } + $ueid = $origin; + $node = $lsfcategories[$ueid] ?? null; + if ($node && isset($lsfcategories[$node->quellid])) { + $origin = $node->quellid; } - } while (!empty($origin) && $quellid != $origin); + } while (!empty($origin) && $ueid !== $origin); return $origin; } @@ -677,7 +672,7 @@ function get_courses_categories(int $veranstid, bool $updatehelptablesifnecessar $otherueidssql . ") OR ueid in (" . $ueids . ")"; $origins = implode(", ", array_map($helpfuntion1, $DB->get_records_sql($originssql))); if (!empty($origins)) { - $categoriessql = "SELECT DISTINCT lsfcat.mdlid, coursecat.name + $categoriessql = "SELECT DISTINCT lsfcat.mdlid, coursecat.name, coursecat.sortorder FROM {local_lsf_unification_category} lsfcat JOIN {course_categories} coursecat ON lsfcat.mdlid = coursecat.id WHERE lsfcat.ueid in (" . $origins . ") @@ -719,149 +714,130 @@ function get_courses_categories(int $veranstid, bool $updatehelptablesifnecessar } /** - * updates the helptables - * insert_missing_helptable_entries is a required function for the lsf_unification plugin + * Syncs missing categories and parenthood relations from HIS_UEBERSCHRIFT into lsf_unfication tables. * - * @param bool $debugoutput - * @param bool $tryeverything + * Loads all known categories and relations from the Moodle DB into memory, then fetches + * all categories from the lsf_view and inserts any missing entries: + * + * - local_lsf_unification_category: one entry per unknown ueid, including its origin + * (root of the quellid chain) and direct parent. + * - local_lsf_unification_categoryparenthood: full closure table entries for each unknown + * relation, storing the distance from a category to each of its ancestors. + * - txt2 on local_lsf_unification_category: the full path from root to category as a + * slash-separated string (e.g. "Vorlesungen/Mathematik und Informatik"). + * + * @param bool $debugoutput If true, logs a summary of inserted categories and relations via mtrace. * @return void + * @throws Exception If any DB operation fails, after rolling back the transaction. */ -function insert_missing_helptable_entries(bool $debugoutput = false, bool $tryeverything = false): void { - $a = 1; - global $pgdb, $DB; - $list1 = ""; - $list2 = ""; +function insert_missing_helptable_entries(bool $debugoutput = false): void { + global $pgdb, $DB, $CFG; + require_once($CFG->dirroot . '/local/lsf_unification/class_pg_lite.php'); + require_once($CFG->dirroot . '/local/lsf_unification/lib_features.php'); + + // Build db connection. + $pgdb = new pg_lite(); + $pgdb->connect(); + + // Get current categories and relationships between categories (parent-child) as recordsets. $records1 = $DB->get_recordset('local_lsf_unification_category', null, '', 'ueid'); $records2 = $DB->get_recordset('local_lsf_unification_categoryparenthood', null, '', 'child, parent'); - $records1unique = []; - $records2unique = []; + $knowncat = []; + $knownrelation = []; + + // Create lookup arrays. foreach ($records1 as $record1) { - $records1unique[$record1->ueid] = true; + // Save already known categories in lsf_unification. + $knowncat[$record1->ueid] = true; } + $records1->close(); foreach ($records2 as $record2) { - $records2unique[$record2->child][$record2->parent] = ($tryeverything === false); - } - $qmain = pg_query( - $pgdb->connection, - "SELECT ueid, uebergeord, uebergeord, quellid, txt, zeitstempel FROM " . HIS_UEBERSCHRIFT . - " " . - ((!empty($tryeverything)) ? ("WHERE ueid >= '" . $tryeverything . "'") : "") + // Save already known child-parent relationships (by category ueid). + $knownrelation[$record2->child][$record2->parent] = true; + } + $records2->close(); + + // Get every category (parents and childs) from the lsf_view and iterate over it. + $sql = "SELECT ueid, uebergeord, quellid, txt, zeitstempel FROM " . HIS_UEBERSCHRIFT . ";"; + $qmain = pg_query($pgdb->connection, $sql); + + // Get all categories from the lsf_view in an array 'ueid' => (object) categoryrecord. + // LEARNWEB-TODO: The use of pg_fetch_all can potentially uses a lot of RAM on big datasets (~900MB on our staging system). This + // is for now acceptable as the web server has enough RAM. Please make sure that the Cronjob has a sufficient memory limit. + // Review in the future how the memory usage can be reduced. + $lsfcategories = array_column( + array_map(fn($item) => (object)$item, pg_fetch_all($qmain) ?: []), + null, + 'ueid' ); - while ($hislsftitle = pg_fetch_object($qmain)) { - if ( - !isset($records1unique[$hislsftitle->ueid]) || (!isset( - $records2unique[$hislsftitle->ueid][$hislsftitle->uebergeord] - ) || - $records2unique[$hislsftitle->ueid][$hislsftitle->uebergeord] != true) - ) { - $a++; - echo $hislsftitle->ueid . " "; - } - if (!isset($records1unique[$hislsftitle->ueid])) { - // Create match-table-entry if not existing. - $entry = new stdClass(); - $entry->ueid = $hislsftitle->ueid; - $entry->parent = empty($hislsftitle->uebergeord) ? ($hislsftitle->ueid) : ($hislsftitle->uebergeord); - $entry->origin = find_origin_category($hislsftitle->ueid); - $entry->mdlid = 0; - $entry->timestamp = isset($hislsftitle->zeitstempel) ? strtotime($hislsftitle->zeitstempel) : null; - $entry->txt = mb_convert_encoding($hislsftitle->txt, 'UTF-8', 'ISO-8859-1'); - if ($debugoutput) { - echo "!"; + $addedcats = 0; + $addedrels = 0; + $transaction = $DB->start_delegated_transaction(); + try { + foreach ($lsfcategories as $hislsftitle) { + $categoryunkown = !isset($knowncat[$hislsftitle->ueid]); + $relationunknown = !isset($knownrelation[$hislsftitle->ueid][$hislsftitle->uebergeord]); + + if ($categoryunkown) { + // Create category if not existing. + $entry = (object) [ + 'ueid' => (int) $hislsftitle->ueid, + 'parent' => empty($hislsftitle->uebergeord) ? (int) ($hislsftitle->ueid) : (int) ($hislsftitle->uebergeord), + 'origin' => find_origin_category((int) $hislsftitle->ueid, $lsfcategories), + 'mdlid' => 0, + 'timestamp' => isset($hislsftitle->zeitstempel) ? strtotime($hislsftitle->zeitstempel) : null, + 'txt' => mb_convert_encoding($hislsftitle->txt, 'UTF-8', 'ISO-8859-1'), + ]; + $DB->insert_record("local_lsf_unification_category", $entry); + $knowncat[$hislsftitle->ueid] = true; + $addedcats++; } - try { - $DB->insert_record("local_lsf_unification_category", $entry, true); - $records1unique[$hislsftitle->ueid] = true; - if ($debugoutput) { - echo "x"; - } - } catch (Exception $e) { - try { - $entry->txt = mb_convert_encoding(delete_bad_chars($hislsftitle->txt), 'UTF-8', 'ISO-8859-1'); - $DB->insert_record("local_lsf_unification_category", $entry, true); - $records1unique[$hislsftitle->ueid] = true; - if ($debugoutput) { - echo "x"; - } - } catch (Exception $e) { - if ($debugoutput) { - print("
FEHLER1 " . var_export($e, true) . "" . var_export($DB->get_last_error(), true));
-                    }
-                }
-            }
-        }
-        if (
-            !isset($records2unique[$hislsftitle->ueid][$hislsftitle->uebergeord]) ||
-                 $records2unique[$hislsftitle->ueid][$hislsftitle->uebergeord] != true
-        ) {
-            // Create parenthood-table-entry if not existing.
-            $child = $hislsftitle->ueid;
-            $ueid = $hislsftitle->ueid;
-            $parent = $hislsftitle->ueid;
-            $fullname = "";
-            $distance = 0;
-            do {
-                $ueid = $parent;
-                $distance++;
-                $q2 = pg_query(
-                    $pgdb->connection,
-                    "SELECT ueid, uebergeord, txt FROM " . HIS_UEBERSCHRIFT . " WHERE ueid = '" .
-                    $ueid . "'"
-                );
-                if (($hislsftitle2 = pg_fetch_object($q2)) && ($hislsftitle2->uebergeord != $ueid)) {
-                    $parent = $hislsftitle2->uebergeord;
-                    $fullname = ($hislsftitle2->txt) . (empty($fullname) ? "" : ("/" . $fullname));
-                    if (!empty($parent) && !isset($records2unique[$child][$parent])) {
-                        try {
-                            $entry = new stdClass();
-                            $entry->child = $child;
-                            $entry->parent = $parent;
-                            $entry->distance = $distance;
-                            $DB->insert_record("local_lsf_unification_categoryparenthood", $entry, true);
-                            if ($debugoutput) {
-                                echo "?";
-                            }
-                        } catch (Exception $e) {
-                            if ($debugoutput) {
-                                mtrace(
-                                    "
FEHLER2 " . var_export($e, true) . "" .
-                                    var_export($DB->get_last_error(), true),
-                                    ''
-                                );
-                            }
+
+            if ($relationunknown) {
+                // Create relation if not existing.
+                $child = $hislsftitle->ueid;
+                $parent = $hislsftitle->ueid;
+                $fullname = "";
+                $distance = 0;
+                $entries = [];
+                // Save the distance from the current category to each of its parents.
+                do {
+                    $ueid = $parent;
+                    $distance++;
+                    $hislsftitle2 = $lsfcategories[$ueid] ?? null;
+                    if ($hislsftitle2 && ($hislsftitle2->uebergeord != $ueid)) {
+                        $parent = $hislsftitle2->uebergeord;
+                        $fullname = ($hislsftitle2->txt) . (empty($fullname) ? "" : ("/" . $fullname));
+                        if (!empty($parent) && !isset($knownrelation[$child][$parent])) {
+                            $entries[] = (object) ['child' => $child, 'parent' => $parent, 'distance' => $distance];
                         }
+                        $knownrelation[$child][$parent] = true;
                     }
-                    $records2unique[$child][$parent] = true;
-                }
-            } while (!empty($parent) && ($ueid != $parent));
-            $entry = $DB->get_record(
-                'local_lsf_unification_category',
-                ["ueid" => $hislsftitle->ueid,
-                ]
-            );
-            $entry->txt2 = mb_convert_encoding($fullname, 'UTF-8', 'ISO-8859-1');
-            try {
-                $DB->update_record('local_lsf_unification_category', $entry, true);
-            } catch (Exception $e) {
-                try {
-                    $entry->txt2 = delete_bad_chars($entry->txt2);
-                    $DB->update_record('local_lsf_unification_category', $entry, true);
-                } catch (Exception $e) {
-                    if ($debugoutput) {
-                        mtrace(
-                            "
FEHLER2 " . var_export($e, true) . "" .
-                            var_export($DB->get_last_error(), true),
-                            ''
-                        );
-                    }
-                }
+                } while (!empty($parent) && ($ueid != $parent));
+                // Save all path distances at once.
+                $DB->insert_records("local_lsf_unification_categoryparenthood", $entries);
+                $addedrels += count($entries);
+
+                // Update the categories full path.
+                $DB->set_field(
+                    'local_lsf_unification_category',
+                    'txt2',
+                    mb_convert_encoding($fullname, 'UTF-8', 'ISO-8859-1'),
+                    ['ueid' => $hislsftitle->ueid]
+                );
             }
         }
-        if ($debugoutput && (($a % 101) == 0)) {
-            mtrace("
  "); - $a++; - flush(); + $transaction->allow_commit(); + if ($debugoutput) { + mtrace("Sync completed: {$addedcats} categories, {$addedrels} relations inserted."); + } + } catch (Exception $e) { + if ($debugoutput) { + mtrace("Sync failed: " . $e->getMessage()); } + $transaction->rollback($e); + } finally { + $pgdb->dispose(); } } diff --git a/request.php b/request.php index e2a226c..a8d7f36 100644 --- a/request.php +++ b/request.php @@ -168,95 +168,6 @@ function print_final() { " . $linktext['course'] . "
 "); } -/** - * Print result selection. - * @return void - * @throws coding_exception - * @throws dml_exception - */ -function print_res_selection() { - global $CFG, $OUTPUT, $courseid; - - $acceptorid = get_course_acceptor($courseid); - if (get_config('local_lsf_unification', 'restore_old_courses')) { - $backupfiles = get_backup_files($acceptorid); - } - if (get_config('local_lsf_unification', 'restore_templates')) { - $templatefiles = get_template_files(); - } - if (empty($backupfiles) && empty($templatefiles)) { - print_final(); - } else if (!empty($templatefiles)) { - $alternativecounter = 1; - - // Continue with the course template. - if (get_config('local_lsf_unification', 'restore_templates') && !empty($templatefiles)) { - $cats = []; - $i = 0; - foreach ($templatefiles as $id => $fileinfo) { - $cats[$fileinfo->category][$id] = $fileinfo; - } - // If there are items without a category move them to the end. - $catkeys = array_keys($cats); - if (!empty($cats) && array_pop($catkeys) == "") { - array_unshift($cats, array_pop($cats)); - } - // Render. - $pretemplate = get_string('pre_template', 'local_lsf_unification', $alternativecounter++); - echo "" . $pretemplate . '
    '; - foreach ($cats as $name => $catfiles) { - if (!empty($name)) { - // LEARNWEB-TODO: move the two js-lines to a new file. - $jscheck = "(document.getElementById('reslist" . $i . "').style.display == 'none') ? 'block' : 'none'"; - $js = "document.getElementById('reslist" . ($i) . "').style.display=(" . $jscheck . ");"; - - $exp = "'url(" . $OUTPUT->image_url("t/expanded")->out() . ")'"; - $col = "'url(" . $OUTPUT->image_url("t/collapsed")->out() . ")'"; - $js2check = "(document.getElementById('reslist" . $i . "').style.display == 'none') ? " . $col . ":" . $exp; - $js2 = "document.getElementById('reslistselector" . ($i) . "').style.listStyleImage=(" . $js2check . ");"; - $alement = '[' . $name . ']'; - $reslist = '"; - } - } - echo "
"; - } - - // Continue with a blank course. - $notemplate = get_string('no_template', 'local_lsf_unification', $alternativecounter++); - $emptycourse = get_string('continue_with_empty_course', 'local_lsf_unification'); - $href = "request.php?courseid=" . $courseid . "&answer=7"; - echo "" . $notemplate . ""; - - // Duplicate course from the course . - if (get_config('local_lsf_unification', 'restore_old_courses') && !empty($backupfiles)) { - echo "" . get_string('template_from_course', 'local_lsf_unification', $alternativecounter++) . "
    "; - // Sortiere die Backups alphabetisch. - uasort($backupfiles, function ($a, $b) { - return strcmp($a->course->fullname, $b->course->fullname); - }); - foreach ($backupfiles as $id => $fileinfo) { - $href = "duplicate_course.php?courseid=" . $courseid . "&filetype=b&fileid=" . $id; - echo "
  • " . $fileinfo->course->fullname . " (" . $fileinfo->datetime . ")
  • "; - } - echo "
"; - } - } -} /** * Prints the remote course creation process. diff --git a/templates/helptable_manager/childlist.mustache b/templates/helptable_manager/childlist.mustache new file mode 100644 index 0000000..555bd9f --- /dev/null +++ b/templates/helptable_manager/childlist.mustache @@ -0,0 +1,105 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + 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 . +}} +{{! + @template local_lsf_unification/helptable_manager/childlist + + This template renders the Admin settings page to create/edit/delete category matching from the lsf to moodle. + + Example (json): + { + + } +}} + +
+ {{! Breadcrumb navigation }} + + +
+
+
+ + + {{#childs}} + + + + + + + {{/childs}} + +
+ {{#sublevels}} + + {{#str}} navigate, local_lsf_unification {{/str}} + + {{/sublevels}} + + + + {{#emptychildmdlid}} + + {{#str}} not_mapped, local_lsf_unification {{/str}} + + {{/emptychildmdlid}} + {{^emptychildmdlid}} + {{childname}} + {{/emptychildmdlid}} + +
+ + +
+
+ {{^emptychildmdlid}} + + {{#str}} delete, local_lsf_unification {{/str}} + + {{/emptychildmdlid}} +
+
+
+ +
+ + + + + +
+
+
diff --git a/templates/helptable_manager/mapping_submit.mustache b/templates/helptable_manager/mapping_submit.mustache new file mode 100644 index 0000000..eb6ffb0 --- /dev/null +++ b/templates/helptable_manager/mapping_submit.mustache @@ -0,0 +1,77 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + 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 . +}} +{{! + @template local_lsf_unification/helptable_manager/mapping_submit + + This template renders the mapping submit step. +}} + +
+
+
+
+ + + + + {{#single_category}} + + {{/single_category}} + + + + + {{#childs}} + + {{#single_category}} + + {{/single_category}} + {{/childs}} + {{^childs}} + + {{/childs}} + + +
{{#str}} map_submit, local_lsf_unification {{/str}}{{#str}} sub_category, local_lsf_unification {{/str}}
{{childtext}}{{category}}{{#str}} map_list_empty, local_lsf_unification {{/str}}
+
+
+ +
+ {{#single_category}} + + {{/single_category}} + {{^single_category}} + + + {{/single_category}} + +
+ + + + + {{#hiddenfields}} + + {{/hiddenfields}} +
+
diff --git a/templates/helptable_manager/overview.mustache b/templates/helptable_manager/overview.mustache new file mode 100644 index 0000000..a82734b --- /dev/null +++ b/templates/helptable_manager/overview.mustache @@ -0,0 +1,42 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + 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 . +}} +{{! + @template local_lsf_unification/helptable_manager/overview + + This template renders the Admin settings page to create/edit/delete category matching from the lsf to moodle. + + Example (json): + { + + } +}} +

+{{#isadmin}} +

+ + + +
+{{/isadmin}} +

+

+ + {{#str}} create_mappings, local_lsf_unification {{/str}} + +

diff --git a/update_helptable.php b/update_helptable.php deleted file mode 100644 index 4114850..0000000 --- a/update_helptable.php +++ /dev/null @@ -1,47 +0,0 @@ -. - -/** - * Page that shows the update_helptable process. - * - * @package local_lsf_unification - * @copyright 2025 Tamaro Walter - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -use local_lsf_unification\pg_lite; - -require_once("../../config.php"); -global $CFG; -require_once(dirname(__FILE__) . "/class_pg_lite.php"); -require_once(dirname(__FILE__) . "/lib.php"); -require_once(dirname(__FILE__) . "/lib_features.php"); - -// Check permissions. -require_admin(); - -// HIS category origin id. -$tryeverything = optional_param('tryeverything', false, PARAM_INT); - -set_time_limit(30 * 60); - -$pgdb = new pg_lite(); -$pgdb->connect(); -insert_missing_helptable_entries(false, $tryeverything); -$pgdb->dispose(); - -$returnto = new moodle_url('/local/lsf_unification/helptablemanager.php'); -redirect($returnto, get_string('update_helptable_notification', 'local_lsf_unification')); diff --git a/version.php b/version.php index ab0cb25..ae06e49 100644 --- a/version.php +++ b/version.php @@ -24,7 +24,7 @@ defined('MOODLE_INTERNAL') || die; -$plugin->version = 2025123101; +$plugin->version = 2025123102; $plugin->component = 'local_lsf_unification'; $plugin->requires = 2024100700; // Require Moodle 4.5. $plugin->supported = [405, 501];