1
<?php // $Id: repository_deploy.php,v 1.2 2006/04/25 23:47:46 stronk7 Exp $
3
///////////////////////////////////////////////////////////////////////////
5
// NOTICE OF COPYRIGHT //
7
// Moodle - Modular Object-Oriented Dynamic Learning Environment //
8
// http://moodle.com //
10
// Copyright (C) 2001-3001 Martin Dougiamas http://dougiamas.com //
11
// (C) 2001-3001 Eloy Lafuente (stronk7) http://contiento.com //
13
// This program is free software; you can redistribute it and/or modify //
14
// it under the terms of the GNU General Public License as published by //
15
// the Free Software Foundation; either version 2 of the License, or //
16
// (at your option) any later version. //
18
// This program is distributed in the hope that it will be useful, //
19
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
20
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
21
// GNU General Public License for more details: //
23
// http://www.gnu.org/copyleft/gpl.html //
25
///////////////////////////////////////////////////////////////////////////
28
* This page will deploy an IMS Content Package from repository.
29
* Just adds hash file.
31
* - file directory containing CP to deploy
32
* - all if not set, will deploy 1 package
33
* if = true, will recursively deploy all packages
34
* found in directory file.
35
* if = force, same as above but will redeploy too.
39
require_once('../../../../config.php');
40
require_once('../../lib.php');
41
require_once('resource.class.php');
42
require_once('../../../../backup/lib.php');
43
require_once('../../../../lib/filelib.php');
44
require_once('../../../../lib/xmlize.php');
46
require_once('repository_config.php');
48
/// Security - Admin Only
53
$file = required_param ('file', PARAM_PATH);
54
$all = optional_param ('all', '', PARAM_ALPHA);
58
ims_deploy_file($file);
63
ims_deploy_folder($file, $all);
67
/// Deploys all packages found in the folder recursively.
68
function ims_deploy_folder($file, $all='') {
71
$dirpath = "$CFG->repository/$file";
72
$dir = opendir($dirpath);
73
while (false != ($filename = readdir($dir))) {
74
if ($filename != '.' && $filename != '..') {
75
$path = $dirpath.'/'.$filename;
76
if (is_dir($path) && file_exists("$path/imsmanifest.xml")) {
77
if ($all == 'force' || !file_exists("$path/moodle_inx.ser")) {
78
echo "DEPLOYING $path<br>";
79
ims_deploy_file($file.'/'.$filename, $all);
82
else if (is_dir($path)) {
83
echo "DEPLOYING $path<br>";
84
ims_deploy_folder($file.'/'.$filename, $all);
87
echo "WONT DEPLOY $path<br>";
94
function ims_deploy_file($file, $all='') {
97
/// Load request parameters
98
$resourcedir = "$CFG->repository/$file";
100
/// Get some needed strings
101
$strdeploy = get_string('deploy','resource');
104
/// Main process, where everything is deployed
107
/// Load imsmanifest to memory (instead of using a full parser,
108
/// we are going to use xmlize intensively (because files aren't too big)
109
if (!$imsmanifest = ims_file2var ($resourcedir.'/imsmanifest.xml')) {
110
error (get_string ('errorreadingfile', 'error', 'imsmanifest.xml'));
113
/// Check if the first line is a proper one, because I've seen some
114
/// packages with some control characters at the beginning.
115
$inixml = strpos($imsmanifest, '<?xml ');
116
if ($inixml !== false) {
118
//Strip strange chars before "<?xml "
119
$imsmanifest = substr($imsmanifest, $inixml);
123
(ord($imsmanifest[0]) == 0xFF && ord($imsmanifest[1]) == 0xFE) ||
124
(ord($imsmanifest[0]) == 0xFE && ord($imsmanifest[1]) == 0xFF)) {
125
echo " UTF-16 - CAN'T DEPLOY.";
129
error (get_string ('invalidxmlfile', 'error', 'imsmanifest.xml'));
133
/// xmlize the variable
134
$data = xmlize($imsmanifest, 0);
136
/// traverse_xmlize($data);
137
$title = ims_get_cp_title($data);
138
/// foreach ($GLOBALS['traverse_array'] as $line) echo $line;
140
/// Extract every manifest present in the imsmanifest file.
141
/// Returns a tree structure.
142
if (!$manifests = ims_extract_manifests($data)) {
143
error (get_string('nonmeaningfulcontent', 'error'));
146
/// Process every manifest found in inverse order so every one
147
/// will be able to use its own submanifests. Not perfect because
148
/// teorically this will allow some manifests to use other non-childs
149
/// but this is supposed to be
151
/// Detect if all the manifest share a common xml:base tag
152
$manifest_base = $data['manifest']['@']['xml:base'];
154
/// Parse XML-metadata
155
/// Skip this for now (until a proper METADATA container was created in Moodle).
157
/// Parse XML-content package data
158
/// First we select an organization an load all the items
160
if (!$items = ims_process_organizations($data['manifest']['#']['organizations']['0'])) {
161
if ($all == 'force') return; else error (get_string('nonmeaningfulcontent', 'error'));
164
/// Detect if all the resources share a common xml:base tag
165
$resources_base = $data['manifest']['#']['resources']['0']['@']['xml:base'];
167
/// Now, we load all the resources available (keys are identifiers)
168
if (!$resources = ims_load_resources($data['manifest']['#']['resources']['0']['#']['resource'], $manifest_base, $resources_base)) {
169
error (get_string('nonmeaningfulcontent', 'error'));
171
///Now we assign to each item, its resource (by identifier)
172
foreach ($items as $key=>$item) {
173
if (!empty($resources[$item->identifierref])) {
174
$items[$key]->href = $resources[$item->identifierref];
176
$items[$key]->href = '';
180
/// Create the INDEX (moodle_inx.ser - where the order of the pages are stored serialized) file
181
$items['title'] = $title;
182
if (!ims_save_serialized_file($resourcedir.'/moodle_inx.ser', $items)) {
183
error (get_string('errorcreatingfile', 'error', 'moodle_inx.ser'));
186
/// No zip so no HASH
188
/// End button (go to view mode)
190
print_simple_box(get_string('imspackageloaded', 'resource'), 'center');
191
$link = $CFG->wwwroot.'/mod/resource/type/ims/preview.php';
192
$options['directory'] = $file;
193
$label = get_string('viewims', 'resource');
195
print_single_button($link, $options, $label, $method);
199
/// End of main process, where everything is deployed
203
/// Common and useful functions used by the body of the script
206
/*** This function will return a tree of manifests (xmlized) as they are
207
* found and extracted from one manifest file. The first manifest in the
208
* will be the main one, while the rest will be submanifests. In the
209
* future (when IMS CP suppors it, external submanifest will be detected
210
* and retrieved here too). See IMS specs for more info.
212
function ims_extract_manifests($data) {
214
$manifest = new stdClass; //To store found manifests in a tree structure
216
/// If there are some manifests
217
if (!empty($data['manifest'])) {
218
/// Add manifest to results array
219
$manifest->data = $data['manifest'];
220
/// Look for submanifests
221
$submanifests = ims_extract_submanifests($data['manifest']['#']);
222
/// Add them as child
223
if (!empty($submanifests)) {
224
$manifest->childs = $submanifests;
227
/// Return tree of manifests found
231
/* This function will search recursively for submanifests returning an array
232
* containing them (xmlized) following a tree structure.
234
function ims_extract_submanifests($data) {
236
$submanifests = array(); //To store found submanifests
238
/// If there are some manifests
239
if (!empty($data['manifest'])) {
241
foreach ($data['manifest'] as $submanifest) {
242
/// Create a new submanifest object
243
$submanifest_object = new stdClass;
244
$submanifest_object->data = $submanifest;
245
/// Look for more submanifests recursively
246
$moresubmanifests = ims_extract_submanifests($submanifest['#']);
247
/// Add them to results array
248
if (!empty($moresubmanifests)) {
249
$submanifest_object->childs = moresubmanifests;
251
/// Add submanifest object to results array
252
$submanifests[] = $submanifest_object;
255
/// Return array of manifests found
256
return $submanifests;
259
/*** This function will return an ordered and nested array of items
260
* that is a perfect representation of the prefered organization
262
function ims_process_organizations($data) {
266
/// Get the default organization
267
$default_organization = $data['@']['default'];
268
if ($CFG->debug > 7) print_object('default_organization: '.$default_organization);
270
/// Iterate (reverse) over organizations until we find the default one
271
if (empty($data['#']['organization'])) { /// Verify <organization> exists
274
$count_organizations = count($data['#']['organization']);
275
if ($CFG->debug > 7) print_object('count_organizations: '.$count_organizations);
277
$current_organization = $count_organizations - 1;
278
while ($current_organization >= 0) {
279
/// Load organization and check it
280
$organization = $data['#']['organization'][$current_organization];
281
if ($organization['@']['identifier'] == $default_organization) {
282
$current_organization = -1; //Match, so exit.
284
$current_organization--;
287
/// At this point we MUST have the final organization
288
if ($CFG->debug > 7) print_object('final organization: '.$organization['#']['title'][0]['#']);
289
if (empty($organization)) {
290
return false; //Error, no organization found
293
/// Extract items map from organization
294
$items = $organization['#']['item'];
295
if (empty($organization['#']['item'])) { /// Verify <item> exists
298
if (!$itemmap = ims_process_items($items)) {
299
return false; //Error, no items found
304
/*** This function gets the xmlized representation of the items
305
* and returns an array of items, ordered, with level and info
307
function ims_process_items($items, $level = 1, $id = 1, $parent = 0) {
312
/// Iterate over items from start to end
313
$count_items = count($items);
314
if ($CFG->debug > 7) print_object('level '.$level.'-count_items: '.$count_items);
317
while ($current_item < $count_items) {
319
$item = $items[$current_item];
320
$obj_item = new stdClass;
321
$obj_item->title = $item['#']['title'][0]['#'];
322
$obj_item->identifier = $item['@']['identifier'];
323
$obj_item->identifierref = $item['@']['identifierref'];
325
$obj_item->level = $level;
326
$obj_item->parent = $parent;
327
/// Only if the item has everything
328
if (!empty($obj_item->title) &&
329
!empty($obj_item->identifier)) {
331
$itemmap[$id] = $obj_item;
332
if ($CFG->debug > 7) print_object('level '.$level.'-id '.$id.'-parent '.$parent.'-'.$obj_item->title);
335
/// Check for subitems recursively
336
$subitems = $item['#']['item'];
337
if (count($subitems)) {
339
$subitemmap = ims_process_items($subitems, $level+1, $id, $obj_item->id);
340
/// Add at the end and counters if necessary
341
if ($count_subitems = count($subitemmap)) {
342
foreach ($subitemmap as $subitem) {
343
/// Add the subitem to the main items array
344
$itemmap[$subitem->id] = $subitem;
356
/*** This function will load an array of resources to be used later.
357
* Keys are identifiers
359
function ims_load_resources($data, $manifest_base, $resources_base) {
362
$resources = array();
364
if (empty($data)) { /// Verify <resource> exists
367
$count_resources = count($data);
368
if ($CFG->debug > 7) print_object('count_resources: '.$count_resources);
370
$current_resource = 0;
371
while ($current_resource < $count_resources) {
373
$resource = $data[$current_resource];
375
/// Create a new object resource
376
$obj_resource = new stdClass;
377
$obj_resource->identifier = $resource['@']['identifier'];
378
$obj_resource->resource_base = $resource['@']['xml:base'];
379
$obj_resource->href = $resource['@']['href'];
380
if (empty($obj_resource->href)) {
381
$obj_resource->href = $resource['#']['file']['0']['@']['href'];
384
/// Some packages are poorly done and use \ in roots. This makes them
385
/// not display since the URLs are not valid.
386
if (!empty($obj_resource->href)) {
387
$obj_resource->href = strtr($obj_resource->href, "\\", '/');
390
/// Only if the resource has everything
391
if (!empty($obj_resource->identifier) &&
392
!empty($obj_resource->href)) {
393
/// Add to resources (identifier as key)
394
/// Depending of $manifest_base, $resources_base and the particular
395
/// $resource_base variable, concatenate them to build the correct href
397
if (!empty($manifest_base)) {
398
$href_base = $manifest_base;
400
if (!empty($resources_base)) {
401
$href_base .= $resources_base;
403
if (!empty($obj_resource->resource_base)) {
404
$href_base .= $obj_resource->resource_base;
406
$resources[$obj_resource->identifier] = $href_base.$obj_resource->href;
414
/*** This function finds out the title of the resource from the XML.
415
* First 2 conditions cover nearly all cases. The third is a fair guess
416
* if no metadata is supplied. This is eventually saved in the serialized
417
* hash as $items['title'].
419
function ims_get_cp_title($xmlobj) {
420
$md = $xmlobj['manifest']['#']['metadata']['0']['#'];
421
if (isset($md['imsmd:lom'])) {
422
return $md['imsmd:lom']['0']['#']['imsmd:general']['0']['#']['imsmd:title']['0']['#']['imsmd:langstring']['0']['#'];
424
else if (isset($md['imsmd:record'])) {
425
return $md['imsmd:record']['0']['#']['imsmd:general']['0']['#']['imsmd:title']['0']['#']['imsmd:langstring']['0']['#'];
427
else if ($title = $xmlobj['manifest']['#']['organizations']['0']['#']['organization']['0']['#']['title']['0']['#']) {
431
return "NO TITLE FOUND";