Skip to content

Commit 41af887

Browse files
Maintenance: Implement MBZ file generation for Moodle 3/4 backups - refs BT#21977
Author: @christianbeeznest
1 parent 120e79e commit 41af887

20 files changed

+3860
-4
lines changed

main/course_info/download.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
$content_type = '';
2828
$_cid = api_get_course_int_id();
2929

30-
if (in_array($extension, ['xml', 'csv', 'imscc']) &&
31-
(api_is_platform_admin(true) || api_is_drh() || (CourseManager::is_course_teacher(api_get_user_id(), api_get_course_id())))
30+
if (in_array($extension, ['xml', 'csv', 'imscc', 'mbz']) &&
31+
(api_is_platform_admin(true) || api_is_drh() || CourseManager::is_course_teacher(api_get_user_id(), api_get_course_id()))
3232
) {
3333
$content_type = 'application/force-download';
3434
} elseif ('zip' === $extension && $_cid && (api_is_platform_admin(true) || api_is_course_admin())) {

main/course_info/maintenance.php

+6
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@
4747
</a><br/>
4848
<?php echo get_lang('ImportFromMoodleInfo'); ?>
4949
</li>
50+
<li>
51+
<a href="../coursecopy/export_moodle.php?<?php echo api_get_cidreq(); ?>">
52+
<?php echo get_lang('ExportToMoodle'); ?>
53+
</a><br/>
54+
<?php echo get_lang('ExportToMoodleInfo'); ?>
55+
</li>
5056
</ul>
5157
</div>
5258

main/coursecopy/export_moodle.php

+179
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
<?php
2+
3+
/* For licensing terms, see /license.txt */
4+
5+
use Chamilo\CourseBundle\Component\CourseCopy\CourseBuilder;
6+
use Chamilo\CourseBundle\Component\CourseCopy\CourseSelectForm;
7+
use moodleexport\MoodleExport;
8+
9+
/**
10+
* Create a Moodle export.
11+
*
12+
*/
13+
require_once __DIR__.'/../inc/global.inc.php';
14+
require_once api_get_path(SYS_PATH).'main/work/work.lib.php';
15+
16+
$current_course_tool = TOOL_COURSE_MAINTENANCE;
17+
18+
api_protect_course_script(true);
19+
20+
// Check access rights (only teachers are allowed here)
21+
if (!api_is_allowed_to_edit()) {
22+
api_not_allowed(true);
23+
}
24+
25+
api_check_archive_dir();
26+
api_set_more_memory_and_time_limits();
27+
28+
// Section for the tabs
29+
$this_section = SECTION_COURSES;
30+
31+
// Breadcrumbs
32+
$interbreadcrumb[] = [
33+
'url' => api_get_path(WEB_CODE_PATH).'course_info/maintenance.php',
34+
'name' => get_lang('Maintenance'),
35+
];
36+
37+
// Displaying the header
38+
$nameTools = get_lang('ExportToMoodle');
39+
Display::display_header($nameTools);
40+
41+
// Display the tool title
42+
echo Display::page_header($nameTools);
43+
$action = isset($_POST['action']) ? $_POST['action'] : '';
44+
$exportOption = isset($_POST['export_option']) ? $_POST['export_option'] : '';
45+
46+
// Handle course selection form submission
47+
if ($action === 'course_select_form' && Security::check_token('post')) {
48+
// Handle the selected resources and continue with export
49+
$selectedResources = $_POST['resource'] ?? null;
50+
51+
if (!empty($selectedResources)) {
52+
// Rebuild the course object based on selected resources
53+
$cb = new CourseBuilder('partial');
54+
$course = $cb->build(0, null, false, array_keys($selectedResources), $selectedResources);
55+
56+
// Get admin details
57+
$adminId = (int) $_POST['admin_id'];
58+
$adminUsername = filter_var($_POST['admin_username'], FILTER_SANITIZE_STRING);
59+
if (!preg_match('/^[a-zA-Z0-9_]+$/', $adminUsername)) {
60+
echo Display::return_message(get_lang('PleaseEnterValidLogin'), 'error');
61+
exit();
62+
}
63+
64+
$adminEmail = filter_var($_POST['admin_email'], FILTER_SANITIZE_EMAIL);
65+
if (!filter_var($adminEmail, FILTER_VALIDATE_EMAIL)) {
66+
echo Display::return_message(get_lang('PleaseEnterValidEmail'), 'error');
67+
exit();
68+
}
69+
70+
$exporter = new MoodleExport($course);
71+
$exporter->setAdminUserData($adminId, $adminUsername, $adminEmail);
72+
73+
// Perform export
74+
$courseId = api_get_course_id();
75+
$exportDir = 'moodle_export_' . $courseId;
76+
try {
77+
$moodleVersion = isset($_POST['moodle_version']) ? (int) $_POST['moodle_version'] : 3;
78+
$mbzFile = $exporter->export($courseId, $exportDir, $moodleVersion);
79+
80+
echo Display::return_message(get_lang('MoodleExportCreated'), 'confirm');
81+
echo '<br />';
82+
echo Display::url(
83+
get_lang('Download'),
84+
api_get_path(WEB_CODE_PATH).'course_info/download.php?archive_path=1&archive='.basename($mbzFile).'&'.api_get_cidreq(),
85+
['class' => 'btn btn-primary btn-large']
86+
);
87+
} catch (Exception $e) {
88+
echo Display::return_message(get_lang('ErrorCreatingExport').': '.$e->getMessage(), 'error');
89+
}
90+
exit();
91+
} else {
92+
echo Display::return_message(get_lang('NoResourcesSelected'), 'warning');
93+
}
94+
} else {
95+
$form = new FormValidator(
96+
'create_export_form',
97+
'post',
98+
api_get_self().'?'.api_get_cidreq()
99+
);
100+
$form->addElement('radio', 'export_option', '', get_lang('CreateFullExport'), 'full_export');
101+
$form->addElement('radio', 'export_option', '', get_lang('LetMeSelectItems'), 'select_items');
102+
$form->addElement('select', 'moodle_version', get_lang('MoodleVersion'), [
103+
'3' => 'Moodle 3.x',
104+
'4' => 'Moodle 4.x',
105+
]);
106+
107+
$form->addElement('text', 'admin_id', get_lang('AdminID'), ['maxlength' => 10, 'size' => 10]);
108+
$form->addElement('text', 'admin_username', get_lang('AdminUsername'), ['maxlength' => 100, 'size' => 50]);
109+
$form->addElement('text', 'admin_email', get_lang('AdminEmail'), ['maxlength' => 100, 'size' => 50]);
110+
111+
// Add validation rules
112+
$form->addRule('admin_id', get_lang('ThisFieldIsRequired'), 'required');
113+
$form->addRule('admin_username', get_lang('ThisFieldIsRequired'), 'required');
114+
$form->addRule('admin_email', get_lang('ThisFieldIsRequired'), 'required');
115+
$form->addRule('admin_email', get_lang('EnterValidEmail'), 'email');
116+
117+
$values['export_option'] = 'select_items';
118+
$form->setDefaults($values);
119+
120+
// Add buttons
121+
$form->addButtonSave(get_lang('CreateExport'));
122+
$form->addProgress();
123+
124+
if ($form->validate()) {
125+
$values = $form->exportValues();
126+
$adminId = (int) $values['admin_id'];
127+
$adminUsername = $values['admin_username'];
128+
$adminEmail = $values['admin_email'];
129+
130+
if ($values['export_option'] === 'full_export') {
131+
$cb = new CourseBuilder('complete');
132+
$course = $cb->build();
133+
134+
$exporter = new MoodleExport($course);
135+
$exporter->setAdminUserData($adminId, $adminUsername, $adminEmail);
136+
137+
$courseId = api_get_course_id(); // Get course ID
138+
$exportDir = 'moodle_export_' . $courseId;
139+
140+
try {
141+
$moodleVersion = isset($values['moodle_version']) ? $values['moodle_version'] : '3';
142+
$mbzFile = $exporter->export($courseId, $exportDir, $moodleVersion);
143+
echo Display::return_message(get_lang('MoodleExportCreated'), 'confirm');
144+
echo '<br />';
145+
echo Display::url(
146+
get_lang('Download'),
147+
api_get_path(WEB_CODE_PATH).'course_info/download.php?archive_path=1&archive='.basename($mbzFile).'&'.api_get_cidreq(),
148+
['class' => 'btn btn-primary btn-large']
149+
);
150+
} catch (Exception $e) {
151+
echo Display::return_message(get_lang('ErrorCreatingExport').': '.$e->getMessage(), 'error');
152+
}
153+
} elseif ($values['export_option'] === 'select_items') {
154+
// Partial export - go to the item selection step
155+
$cb = new CourseBuilder('partial');
156+
$course = $cb->build();
157+
if ($course->has_resources()) {
158+
// Add token to Course select form
159+
$hiddenFields['sec_token'] = Security::get_token();
160+
$hiddenFields['admin_id'] = $adminId;
161+
$hiddenFields['admin_username'] = $adminUsername;
162+
$hiddenFields['admin_email'] = $adminEmail;
163+
164+
CourseSelectForm::display_form($course, $hiddenFields, false, true);
165+
} else {
166+
echo Display::return_message(get_lang('NoResourcesToExport'), 'warning');
167+
}
168+
}
169+
} else {
170+
echo '<div class="row">';
171+
echo '<div class="col-md-12">';
172+
echo '<div class="tool-export">';
173+
$form->display();
174+
echo '</div>';
175+
echo '</div>';
176+
}
177+
}
178+
179+
Display::display_footer();

main/inc/lib/document.lib.php

+60
Original file line numberDiff line numberDiff line change
@@ -7533,6 +7533,66 @@ private static function getButtonDelete(
75337533
return $btn;
75347534
}
75357535

7536+
/**
7537+
* Retrieves all documents in a course by their parent folder ID.
7538+
*
7539+
* @param array $courseInfo Information about the course.
7540+
* @param int $parentId The ID of the parent folder.
7541+
* @param int $toGroupId (Optional) The ID of the group to filter by. Default is 0.
7542+
* @param int|null $toUserId (Optional) The ID of the user to filter by. Default is null.
7543+
* @param bool $canSeeInvisible (Optional) Whether to include invisible documents. Default is false.
7544+
* @param bool $search (Optional) Whether to perform a search or fetch all documents. Default is true.
7545+
* @param int $sessionId (Optional) The session ID to filter by. Default is 0.
7546+
*
7547+
* @return array List of documents that match the criteria.
7548+
*/
7549+
public static function getAllDocumentsByParentId(
7550+
$courseInfo,
7551+
$parentId,
7552+
$toGroupId = 0,
7553+
$toUserId = null,
7554+
$canSeeInvisible = false,
7555+
$search = true,
7556+
$sessionId = 0
7557+
) {
7558+
if (empty($courseInfo)) {
7559+
return [];
7560+
}
7561+
7562+
$tblDocument = Database::get_course_table(TABLE_DOCUMENT);
7563+
7564+
$parentId = (int) $parentId;
7565+
7566+
$sql = "SELECT path, filetype
7567+
FROM $tblDocument
7568+
WHERE id = $parentId
7569+
AND c_id = {$courseInfo['real_id']}";
7570+
7571+
$result = Database::query($sql);
7572+
7573+
if ($result === false || Database::num_rows($result) == 0) {
7574+
return [];
7575+
}
7576+
7577+
$parentRow = Database::fetch_array($result, 'ASSOC');
7578+
$parentPath = $parentRow['path'];
7579+
$filetype = $parentRow['filetype'];
7580+
7581+
if ($filetype !== 'folder') {
7582+
return [];
7583+
}
7584+
7585+
return self::getAllDocumentData(
7586+
$courseInfo,
7587+
$parentPath,
7588+
$toGroupId,
7589+
$toUserId,
7590+
$canSeeInvisible,
7591+
$search,
7592+
$sessionId
7593+
);
7594+
}
7595+
75367596
/**
75377597
* Include MathJax script in document.
75387598
*

0 commit comments

Comments
 (0)