Automatic installer.php lang files by installer_builder (20070726)
[moodle-linuxchix.git] / mod / resource / type / ims / deploy.php
blob74470d8b9aafb45be7bb122e3a7cbbd5f9d76c04
1 <?php // $Id$
3 ///////////////////////////////////////////////////////////////////////////
4 // //
5 // NOTICE OF COPYRIGHT //
6 // //
7 // Moodle - Modular Object-Oriented Dynamic Learning Environment //
8 // http://moodle.com //
9 // //
10 // Copyright (C) 2001-3001 Martin Dougiamas http://dougiamas.com //
11 // (C) 2001-3001 Eloy Lafuente (stronk7) http://contiento.com //
12 // //
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. //
17 // //
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: //
22 // //
23 // http://www.gnu.org/copyleft/gpl.html //
24 // //
25 ///////////////////////////////////////////////////////////////////////////
27 /***
28 * This page will deploy an IMS Content Package zip file,
29 * building all the structures and auxiliary files to
30 * work inside a Moodle resource.
33 /// Required stuff
34 require_once('../../../../config.php');
35 require_once('../../lib.php');
36 require_once('resource.class.php');
37 require_once('../../../../backup/lib.php');
38 require_once('../../../../lib/filelib.php');
39 require_once('../../../../lib/xmlize.php');
41 /// Load request parameters
42 $courseid = required_param ('courseid', PARAM_INT);
43 $cmid = required_param ('cmid', PARAM_INT);
44 $file = required_param ('file', PARAM_PATH);
45 $inpopup = optional_param ('inpopup', 0, PARAM_BOOL);
47 /// Fetch some records from DB
48 $course = get_record ('course', 'id', $courseid);
49 $cm = get_coursemodule_from_id('resource', $cmid);
50 $resource = get_record ('resource', 'id', $cm->instance);
52 /// Get some needed strings
53 $strdeploy = get_string('deploy','resource');
55 /// Instantiate a resource_ims object and modify its navigation
56 $resource_obj = new resource_ims ($cmid);
58 /// Print the header of the page
59 $pagetitle = strip_tags($course->shortname.': '.
60 format_string($resource->name)).': '.
61 $strdeploy;
63 if ($inpopup) {
64 print_header($pagetitle, $course->fullname);
65 } else {
67 $resource_obj->navlinks[] = array('name' => format_string($resource->name), 'link' => '', 'type' => 'activityinstance');
68 $resource_obj->navlinks[] = array('name' => $strdeploy, 'link' => '', 'type' => 'action');
69 $navigation = build_navigation($resource_obj->navlinks);
70 print_header($pagetitle, $course->fullname, $navigation,
71 '', '', true,
72 update_module_button($cm->id, $course->id, $resource_obj->strresource));
75 /// Security Constraints (sesskey and isteacheredit)
76 if (!confirm_sesskey()) {
77 error(get_string('confirmsesskeybad', 'error'));
78 } else if (!has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_COURSE, $courseid))) {
79 error(get_string('onlyeditingteachers', 'error'));
82 ///
83 /// Main process, where everything is deployed
84 ///
86 /// Set some variables
88 /// Create directories
89 if (!$resourcedir = make_upload_directory($courseid.'/'.$CFG->moddata.'/resource/'.$resource->id)) {
90 error (get_string('errorcreatingdirectory', 'error', $CFG->moddata.'/resource/'.$resource->id));
93 /// Ensure it's empty
94 if (!delete_dir_contents($resourcedir)) {
95 error (get_string('errorcleaningdirectory', 'error', $resourcedir));
98 /// Copy files
99 $origin = $CFG->dataroot.'/'.$courseid.'/'.$file;
101 if (!is_file($origin)) {
102 error (get_string('filenotfound' , 'error', $file));
104 $mimetype = mimeinfo("type", $file);
105 if ($mimetype != "application/zip") {
106 error (get_string('invalidfiletype', 'error', $file));
108 $resourcefile = $resourcedir.'/'.basename($origin);
109 if (!backup_copy_file($origin, $resourcefile)) {
110 error (get_string('errorcopyingfiles', 'error'));
113 /// Unzip files
114 if (!unzip_file($resourcefile, '', false)) {
115 error (get_string('errorunzippingfiles', 'error'));
118 /// Check for imsmanifest
119 if (!file_exists($resourcedir.'/imsmanifest.xml')) {
120 error (get_string('filenotfound', 'error', 'imsmanifest.xml'));
123 /// Load imsmanifest to memory (instead of using a full parser,
124 /// we are going to use xmlize intensively (because files aren't too big)
125 if (!$imsmanifest = ims_file2var ($resourcedir.'/imsmanifest.xml')) {
126 error (get_string ('errorreadingfile', 'error', 'imsmanifest.xml'));
129 /// Check if the first line is a proper one, because I've seen some
130 /// packages with some control characters at the beginning.
131 $inixml = strpos($imsmanifest, '<?xml ');
132 if ($inixml !== false) {
133 if ($inixml !== 0) {
134 //Strip strange chars before "<?xml "
135 $imsmanifest = substr($imsmanifest, $inixml);
137 } else {
138 error (get_string ('invalidxmlfile', 'error', 'imsmanifest.xml'));
141 /// xmlize the variable
142 $data = xmlize($imsmanifest, 0);
144 /// Extract every manifest present in the imsmanifest file.
145 /// Returns a tree structure.
146 if (!$manifests = ims_extract_manifests($data)) {
147 error (get_string('nonmeaningfulcontent', 'error'));
150 /// Process every manifest found in inverse order so every one
151 /// will be able to use its own submanifests. Not perfect because
152 /// teorically this will allow some manifests to use other non-childs
153 /// but this is supposed to be
155 /// Detect if all the manifest share a common xml:base tag
156 $manifest_base = $data['manifest']['@']['xml:base'];
158 /// Parse XML-metadata
159 /// Skip this for now (until a proper METADATA container was created in Moodle).
161 /// Parse XML-content package data
162 /// First we select an organization an load all the items
163 if (!$items = ims_process_organizations($data['manifest']['#']['organizations']['0'])) {
164 error (get_string('nonmeaningfulcontent', 'error'));
167 /// Detect if all the resources share a common xml:base tag
168 $resources_base = $data['manifest']['#']['resources']['0']['@']['xml:base'];
170 /// Now, we load all the resources available (keys are identifiers)
171 if (!$resources = ims_load_resources($data['manifest']['#']['resources']['0']['#']['resource'], $manifest_base, $resources_base)) {
172 error (get_string('nonmeaningfulcontent', 'error'));
174 ///Now we assign to each item, its resource (by identifier)
175 foreach ($items as $key=>$item) {
176 if (!empty($resources[$item->identifierref])) {
177 $items[$key]->href = $resources[$item->identifierref];
178 } else {
179 $items[$key]->href = '';
183 /// Create the INDEX (moodle_inx.ser - where the order of the pages are stored serialized) file
184 if (!ims_save_serialized_file($resourcedir.'/moodle_inx.ser', $items)) {
185 error (get_string('errorcreatingfile', 'error', 'moodle_inx.ser'));
188 /// Create the HASH file (moodle_hash.ser - where the hash of the ims is stored serialized) file
189 $hash = $resource_obj->calculatefilehash($resourcefile);
190 if (!ims_save_serialized_file($resourcedir.'/moodle_hash.ser', $hash)) {
191 error (get_string('errorcreatingfile', 'error', 'moodle_hash.ser'));
194 /// End button (go to view mode)
195 echo '<center>';
196 print_simple_box(get_string('imspackageloaded', 'resource'), 'center');
197 $link = $CFG->wwwroot.'/mod/resource/view.php';
198 $options['r'] = $resource->id;
199 $label = get_string('viewims', 'resource');
200 $method = 'post';
201 print_single_button($link, $options, $label, $method);
202 echo '</center>';
205 /// End of main process, where everything is deployed
208 /// Print the footer of the page
209 print_footer();
212 /// Common and useful functions used by the body of the script
215 /*** This function will return a tree of manifests (xmlized) as they are
216 * found and extracted from one manifest file. The first manifest in the
217 * will be the main one, while the rest will be submanifests. In the
218 * future (when IMS CP suppors it, external submanifest will be detected
219 * and retrieved here too). See IMS specs for more info.
221 function ims_extract_manifests($data) {
223 $manifest = new stdClass; //To store found manifests in a tree structure
225 /// If there are some manifests
226 if (!empty($data['manifest'])) {
227 /// Add manifest to results array
228 $manifest->data = $data['manifest'];
229 /// Look for submanifests
230 $submanifests = ims_extract_submanifests($data['manifest']['#']);
231 /// Add them as child
232 if (!empty($submanifests)) {
233 $manifest->childs = $submanifests;
236 /// Return tree of manifests found
237 return $manifest;
240 /* This function will search recursively for submanifests returning an array
241 * containing them (xmlized) following a tree structure.
243 function ims_extract_submanifests($data) {
245 $submanifests = array(); //To store found submanifests
247 /// If there are some manifests
248 if (!empty($data['manifest'])) {
249 /// Get them
250 foreach ($data['manifest'] as $submanifest) {
251 /// Create a new submanifest object
252 $submanifest_object = new stdClass;
253 $submanifest_object->data = $submanifest;
254 /// Look for more submanifests recursively
255 $moresubmanifests = ims_extract_submanifests($submanifest['#']);
256 /// Add them to results array
257 if (!empty($moresubmanifests)) {
258 $submanifest_object->childs = moresubmanifests;
260 /// Add submanifest object to results array
261 $submanifests[] = $submanifest_object;
264 /// Return array of manifests found
265 return $submanifests;
268 /*** This function will return an ordered and nested array of items
269 * that is a perfect representation of the prefered organization
271 function ims_process_organizations($data) {
273 global $CFG;
275 /// Get the default organization
276 $default_organization = $data['@']['default'];
277 debugging('default_organization: '.$default_organization);
279 /// Iterate (reverse) over organizations until we find the default one
280 if (empty($data['#']['organization'])) { /// Verify <organization> exists
281 return false;
283 $count_organizations = count($data['#']['organization']);
284 debugging('count_organizations: '.$count_organizations);
286 $current_organization = $count_organizations - 1;
287 while ($current_organization >= 0) {
288 /// Load organization and check it
289 $organization = $data['#']['organization'][$current_organization];
290 if ($organization['@']['identifier'] == $default_organization) {
291 $current_organization = -1; //Match, so exit.
293 $current_organization--;
296 /// At this point we MUST have the final organization
297 debugging('final organization: '.$organization['#']['title'][0]['#']);
298 if (empty($organization)) {
299 return false; //Error, no organization found
302 /// Extract items map from organization
303 $items = $organization['#']['item'];
304 if (empty($organization['#']['item'])) { /// Verify <item> exists
305 return false;
307 if (!$itemmap = ims_process_items($items)) {
308 return false; //Error, no items found
310 return $itemmap;
313 /*** This function gets the xmlized representation of the items
314 * and returns an array of items, ordered, with level and info
316 function ims_process_items($items, $level = 1, $id = 1, $parent = 0) {
317 global $CFG;
319 $itemmap = array();
321 /// Iterate over items from start to end
322 $count_items = count($items);
323 debugging('level '.$level.'-count_items: '.$count_items);
325 $current_item = 0;
326 while ($current_item < $count_items) {
327 /// Load item
328 $item = $items[$current_item];
329 $obj_item = new stdClass;
330 $obj_item->title = $item['#']['title'][0]['#'];
331 $obj_item->identifier = $item['@']['identifier'];
332 $obj_item->identifierref = $item['@']['identifierref'];
333 $obj_item->id = $id;
334 $obj_item->level = $level;
335 $obj_item->parent = $parent;
336 /// Only if the item has everything
337 if (!empty($obj_item->title) &&
338 !empty($obj_item->identifier)) {
339 /// Add to itemmap
340 $itemmap[$id] = $obj_item;
341 debugging('level '.$level.'-id '.$id.'-parent '.$parent.'-'.$obj_item->title);
342 /// Counters go up
343 $id++;
344 /// Check for subitems recursively
345 $subitems = $item['#']['item'];
346 if (count($subitems)) {
347 /// Recursive call
348 $subitemmap = ims_process_items($subitems, $level+1, $id, $obj_item->id);
349 /// Add at the end and counters if necessary
350 if ($count_subitems = count($subitemmap)) {
351 foreach ($subitemmap as $subitem) {
352 /// Add the subitem to the main items array
353 $itemmap[$subitem->id] = $subitem;
354 /// Counters go up
355 $id++;
360 $current_item++;
362 return $itemmap;
365 /*** This function will load an array of resources to be used later.
366 * Keys are identifiers
368 function ims_load_resources($data, $manifest_base, $resources_base) {
369 global $CFG;
371 $resources = array();
373 if (empty($data)) { /// Verify <resource> exists
374 return false;
376 $count_resources = count($data);
377 debugging('count_resources: '.$count_resources);
379 $current_resource = 0;
380 while ($current_resource < $count_resources) {
381 /// Load resource
382 $resource = $data[$current_resource];
384 /// Create a new object resource
385 $obj_resource = new stdClass;
386 $obj_resource->identifier = $resource['@']['identifier'];
387 $obj_resource->resource_base = $resource['@']['xml:base'];
388 $obj_resource->href = $resource['@']['href'];
389 if (empty($obj_resource->href)) {
390 $obj_resource->href = $resource['#']['file']['0']['@']['href'];
393 /// Some packages are poorly done and use \ in roots. This makes them
394 /// not display since the URLs are not valid.
395 if (!empty($obj_resource->href)) {
396 $obj_resource->href = strtr($obj_resource->href, "\\", '/');
399 /// Only if the resource has everything
400 if (!empty($obj_resource->identifier) &&
401 !empty($obj_resource->href)) {
402 /// Add to resources (identifier as key)
403 /// Depending of $manifest_base, $resources_base and the particular
404 /// $resource_base variable, concatenate them to build the correct href
405 $href_base = '';
406 if (!empty($manifest_base)) {
407 $href_base = $manifest_base;
409 if (!empty($resources_base)) {
410 $href_base .= $resources_base;
412 if (!empty($obj_resource->resource_base)) {
413 $href_base .= $obj_resource->resource_base;
415 $resources[$obj_resource->identifier] = $href_base.$obj_resource->href;
417 /// Counters go up
418 $current_resource++;
420 return $resources;