AC_4897, AC_4898, AC_4899: Multifile uploader fixes.
[acontent.git] / docs / home / ims / ims_export.php
1 <?php
2 /************************************************************************/
3 /* AContent                                                             */
4 /************************************************************************/
5 /* Copyright (c) 2010                                                   */
6 /* Inclusive Design Institute                                           */
7 /*                                                                      */
8 /* This program is free software. You can redistribute it and/or        */
9 /* modify it under the terms of the GNU General Public License          */
10 /* as published by the Free Software Foundation.                        */
11 /************************************************************************/
12
13 /** Commented by Cindy Li on Feb 2, 2010
14  * Modified from ATutor mods/_core/imscp/ims_export.php, SVN revision 9126
15  */
16
17 define('TR_INCLUDE_PATH', '../../include/');
18
19 if (isset($_REQUEST['to_tile']) && !isset($_POST['cancel'])) {
20         /* for TILE */
21
22         /* redirect to TILE import servlet */
23         if (!authenticate(TR_PRIV_ADMIN, TR_PRIV_RETURN)) {
24                 /* user can't be authenticated */
25                 header('HTTP/1.1 404 Not Found');
26                 echo 'Document not found.';
27                 exit;
28         }
29
30         $m = md5(DB_PASSWORD . 'x' . ADMIN_PASSWORD . 'x' . $_SERVER['SERVER_ADDR'] . 'x' . $cid . 'x' . $_SESSION['course_id'] . 'x' . date('Ymd'));
31
32         header('Location: '.TR_TILE_IMPORT. '?cp='.urlencode(TR_BASE_HREF. 'home/ims/ims_export.php?cid='.$cid.'&c='.$_SESSION['course_id'].'&m='.$m));
33         exit;
34 } else if (isset($_GET['m'])) {
35         /* for TILE */
36
37         /* request (hopefully) coming from a TILE server, send the content package */
38
39         $_user_location = 'public';
40         require_once(TR_INCLUDE_PATH.'vitals.inc.php');
41         $m = md5(DB_PASSWORD . 'x' . ADMIN_PASSWORD . 'x' . $_SERVER['SERVER_ADDR'] . 'x' . $cid . 'x' . $c . 'x' . date('Ymd'));
42         if (($m != $_GET['m']) || !$c) {
43                 header('HTTP/1.1 404 Not Found');
44                 echo 'Document not found.';
45                 exit;
46         }
47         
48         $course_id = $c;
49
50 } else {
51         $use_a4a = false;
52         if (isset($_REQUEST['to_a4a'])){
53                 $use_a4a = true;
54         }
55         require_once(TR_INCLUDE_PATH.'vitals.inc.php');
56         global $_course_id, $_content_id;
57
58         $_course_id = $course_id = (isset($_REQUEST['course_id']) ? intval($_REQUEST['course_id']) : $_course_id);
59         $_content_id = $cid = isset($_REQUEST['cid']) ? intval($_REQUEST['cid']) : $_content_id; /* content id of an optional chapter */
60         $c   = isset($_REQUEST['c'])   ? intval($_REQUEST['c'])   : 0;
61 }
62
63 if ($course_id == 0 && $cid == 0)
64 {
65         $msg->addError('MISSING_COURSE_ID');
66         header('Location: ../index.php');
67         exit;   
68 }
69
70 require_once(TR_INCLUDE_PATH.'../home/classes/ContentManager.class.php');  /* content management class */
71 require_once(TR_INCLUDE_PATH.'classes/DAO/CoursesDAO.class.php');
72 require_once(TR_INCLUDE_PATH.'classes/DAO/ContentDAO.class.php');
73 require_once(TR_INCLUDE_PATH.'classes/DAO/UsersDAO.class.php');
74
75 //load the following after vitals is included
76 require(TR_INCLUDE_PATH.'classes/testQuestions.class.php');
77 require(TR_INCLUDE_PATH.'classes/A4a/A4aExport.class.php');
78
79 $coursesDAO = new CoursesDAO();
80 $course_row = $coursesDAO->get($course_id);
81
82 $contentManager = new ContentManager($course_id);
83
84 $instructor_id   = $course_row['user_id'];
85 $course_desc     = htmlspecialchars($course_row['description'], ENT_QUOTES, 'UTF-8');
86 $course_title    = htmlspecialchars($course_row['title'], ENT_QUOTES, 'UTF-8');
87 $course_language = $course_row['primary_language'];
88
89 $courseLanguage =& $languageManager->getLanguage($course_language);
90 //If course language cannot be found, use UTF-8 English
91 //@author harris, Oct 30,2008
92 if (!isset($courseLanguage) || $courseLanguage == null){
93         $courseLanguage =& $languageManager->getLanguage(DEFAULT_LANGUAGE_CODE);
94 }
95
96 $course_language_charset = $courseLanguage->getCharacterSet();
97 $course_language_code = $courseLanguage->getCode();
98
99 require(TR_INCLUDE_PATH.'classes/zipfile.class.php');                           /* for zipfile */
100 require(TR_INCLUDE_PATH.'classes/vcard.php');                                           /* for vcard */
101 require(TR_INCLUDE_PATH.'classes/XML/XML_HTMLSax/XML_HTMLSax.php');     /* for XML_HTMLSax */
102 require(TR_INCLUDE_PATH.'../home/ims/include/ims_template.inc.php');/* for ims templates + print_organizations() */
103
104 if (isset($_POST['cancel'])) {
105         $msg->addFeedback('EXPORT_CANCELLED');
106         header('Location: ../index.php');
107         exit;
108 }
109
110
111 $zipfile = new zipfile();
112 $zipfile->create_dir('resources/');
113
114 /*
115         the following resources are to be identified:
116         even if some of these can't be images, they can still be files in the content dir.
117         theoretically the only urls we wouldn't deal with would be for a <!DOCTYPE and <form>
118
119         img             => src
120         a               => href                         // ignore if href doesn't exist (ie. <a name>)
121         object  => data | classid       // probably only want data
122         applet  => classid | archive                    // whatever these two are should double check to see if it's a valid file (not a dir)
123         link    => href
124         script  => src
125         form    => action
126         input   => src
127         iframe  => src
128
129 */
130 class MyHandler {
131     function MyHandler(){}
132     function openHandler(& $parser,$name,$attrs) {
133                 global $my_files;
134
135                 $name = strtolower($name);
136                 $attrs = array_change_key_case($attrs, CASE_LOWER);
137
138                 $elements = array(      'img'           => 'src',
139                                                         'a'                     => 'href',                              
140                                                         'object'        => array('data', 'classid'),
141                                                         'applet'        => array('classid', 'archive'),
142                                                         'link'          => 'href',
143                                                         'script'        => 'src',
144                                                         'form'          => 'action',
145                                                         'input'         => 'src',
146                                                         'iframe'        => 'src',
147                                                         'embed'         => 'src',
148                                                         'param'         => 'value');
149         
150                 /* check if this attribute specifies the files in different ways: (ie. java) */
151                 if (is_array($elements[$name])) {
152                         $items = $elements[$name];
153
154                         foreach ($items as $item) {
155                                 if ($attrs[$item] != '') {
156
157                                         /* some attributes allow a listing of files to include seperated by commas (ie. applet->archive). */
158                                         if (strpos($attrs[$item], ',') !== false) {
159                                                 $files = explode(',', $attrs[$item]);
160                                                 foreach ($files as $file) {
161                                                         $my_files[] = trim($file);
162                                                 }
163                                         } else {
164                                                 $my_files[] = $attrs[$item];
165                                         }
166                                 }
167                         }
168                 } else if (isset($elements[$name]) && ($attrs[$elements[$name]] != '')) {
169                         /* we know exactly which attribute contains the reference to the file. */
170                         $my_files[] = $attrs[$elements[$name]];
171                 }
172     }
173     function closeHandler(& $parser,$name) { }
174 }
175
176 /* get all the content */
177 $content = array();
178 $paths   = array();
179 $top_content_parent_id = 0;
180
181 $handler=new MyHandler();
182 $parser = new XML_HTMLSax();
183 $parser->set_object($handler);
184 $parser->set_element_handler('openHandler','closeHandler');
185
186 $contentDAO = new ContentDAO();
187 $rows = $contentDAO->getContentByCourseID($course_id);
188
189 //if (authenticate(TR_PRIV_CONTENT, TR_PRIV_RETURN)) {
190 //      $sql = "SELECT *, UNIX_TIMESTAMP(last_modified) AS u_ts FROM ".TABLE_PREFIX."content WHERE course_id=$course_id ORDER BY content_parent_id, ordering";
191 //} else {
192 //      $sql = "SELECT *, UNIX_TIMESTAMP(last_modified) AS u_ts FROM ".TABLE_PREFIX."content WHERE course_id=$course_id ORDER BY content_parent_id, ordering";
193 //}
194 //$result = mysql_query($sql, $db);
195 //while ($row = mysql_fetch_assoc($result)) {
196 //      if (authenticate(TR_PRIV_CONTENT, TR_PRIV_RETURN) || $contentManager->isReleased($row['content_id']) === TRUE) {
197
198 if (is_array($rows)) {
199         foreach ($rows as $row) {
200                 $content[$row['content_parent_id']][] = $row;
201                 if ($cid == $row['content_id']) {
202                         $top_content = $row;
203                         $top_content_parent_id = $row['content_parent_id'];
204                 }
205         }
206 }
207
208 if ($cid) {
209         /* filter out the top level sections that we don't want */
210         $top_level = $content[$top_content_parent_id];
211         foreach($top_level as $page) {
212                 if ($page['content_id'] == $cid) {
213                         $content[$top_content_parent_id] = array($page);
214                 } else {
215                         /* this is a page we don't want, so might as well remove it's children too */
216                         unset($content[$page['content_id']]);
217                 }
218         }
219         $ims_course_title = $course_title . ' - ' . $content[$top_content_parent_id][0]['title'];
220 } else {
221         $ims_course_title = $course_title;
222 }
223
224
225 /* generate the imsmanifest.xml header attributes */
226 $imsmanifest_xml = str_replace(array('{COURSE_TITLE}', '{COURSE_DESCRIPTION}', '{COURSE_PRIMARY_LANGUAGE_CHARSET}', '{COURSE_PRIMARY_LANGUAGE_CODE}'), 
227                                                           array($ims_course_title, $course_desc, $course_language_charset, $course_language_code),
228                                                           $ims_template_xml['header']);
229 //debug($imsmanifest_xml);
230 //exit;
231
232 /* get the first content page to default the body frame to */
233 $first = $content[$top_content_parent_id][0];
234
235 $test_ids = array();    //global array to store all the test ids
236 //if ($my_files == null) 
237 //$my_files = array();
238
239 /* generate the IMS QTI resource and files */
240 /*
241 /* in print_organizations
242 foreach ($content[0] as $content_box){
243         $content_test_rs = $contentManager->getContentTestsAssoc($content_box['content_id']);   
244         while ($content_test_row = mysql_fetch_assoc($content_test_rs)){
245                 //export
246                 $test_ids[] = $content_test_row['test_id'];
247                 //the 'added_files' is for adding into the manifest file in this zip
248                 $added_files = test_qti_export($content_test_row['test_id'], '', $zipfile);
249
250                 //Save all the xml files in this array, and then print_organizations will add it to the manifest file.
251                 foreach($added_files as $filename=>$file_array){
252                         $my_files[] = $filename;
253                         foreach ($file_array as $garbage=>$filename2){
254                                 $my_files[] = $filename2;
255                         }
256                 }
257         }
258 }
259 */
260
261 ob_start();
262 print_organizations($top_content_parent_id, $content, 0, '', array(), $toc_html);
263 $organizations_str = ob_get_contents();
264 ob_end_clean();
265
266 // Modified by Cindy Qi Li on Jun 3, 2010
267 // Transformable does not support glossary
268 /* generate the resources and save the HTML files */
269 /*
270 $used_glossary_terms = array();
271 if (count($used_glossary_terms)) {
272         $used_glossary_terms = array_unique($used_glossary_terms);
273         sort($used_glossary_terms);
274         reset($used_glossary_terms);
275
276         $terms_xml = '';
277         foreach ($used_glossary_terms as $term) {
278                 $term_key = urlencode($term);
279                 $glossary[$term_key] = htmlentities($glossary[$term_key], ENT_QUOTES, 'UTF-8');
280                 $glossary[$term_key] = str_replace('&', '&amp;', $glossary[$term_key]);
281                 $escaped_term = str_replace('&', '&amp;', $term);
282                 $terms_xml .= str_replace(      array('{TERM}', '{DEFINITION}'),
283                                                                         array($escaped_term, $glossary[$term_key]),
284                                                                         $glossary_term_xml);
285
286                 $terms_html .= str_replace(     array('{ENCODED_TERM}', '{TERM}', '{DEFINITION}'),
287                                                                         array($term_key, $term, $glossary[$term_key]),
288                                                                         $glossary_term_html);
289         }
290
291         $glossary_body_html = str_replace('{BODY}', $terms_html, $glossary_body_html);
292
293         $glossary_xml = str_replace(array('{GLOSSARY_TERMS}', '{COURSE_PRIMARY_LANGUAGE_CHARSET}'),
294                                                             array($terms_xml, $course_language_charset),
295                                                                 $glossary_xml);
296         $glossary_html = str_replace(   array('{CONTENT}', '{KEYWORDS}', '{TITLE}', '{COURSE_PRIMARY_LANGUAGE_CHARSET}', '{COURSE_PRIMARY_LANGUAGE_CODE}'),
297                                                                         array($glossary_body_html, '', 'Glossary', $course_language_charset, $course_language_code),
298                                                                         $html_template);
299         $toc_html .= '<ul><li><a href="glossary.html" target="body">'._AT('glossary').'</a></li></ul>';
300 } else {
301         unset($glossary_xml);
302 }
303 */
304 // END OF Modified by Cindy Qi Li on Jun 3, 2010
305
306 $toc_html = str_replace(array('{TOC}', '{COURSE_PRIMARY_LANGUAGE_CHARSET}', '{COURSE_PRIMARY_LANGUAGE_CODE}'),
307                                             array($toc_html, $course_language_charset, $course_language_code),
308                                                 $html_toc);
309
310 if ($first['content_path']) {
311         $first['content_path'] .= '/';
312 }
313 $frame = str_replace(   array('{COURSE_TITLE}',         '{FIRST_ID}', '{PATH}', '{COURSE_PRIMARY_LANGUAGE_CHARSET}', '{COURSE_PRIMARY_LANGUAGE_CODE}'),
314                                                 array($ims_course_title, $first['content_id'], $first['content_path'], $course_language_charset, $course_language_code),
315                                                 $html_frame);
316
317 $html_mainheader = str_replace(array('{COURSE_TITLE}', '{COURSE_PRIMARY_LANGUAGE_CHARSET}', '{COURSE_PRIMARY_LANGUAGE_CODE}'),
318                                                            array($ims_course_title, $course_language_charset, $course_language_code),
319                                                            $html_mainheader);
320
321 /* append the Organizations and Resources to the imsmanifest */
322 $imsmanifest_xml .= str_replace(        array('{ORGANIZATIONS}',        '{RESOURCES}', '{COURSE_TITLE}'),
323                                                                         array($organizations_str,       $resources, $ims_course_title),
324                                                                         $ims_template_xml['final']);
325
326 /* generate the vcard for the instructor/author */
327 //$sql = "SELECT first_name, last_name, email, website, login, phone FROM ".TABLE_PREFIX."members WHERE member_id=$instructor_id";
328 //$result = mysql_query($sql, $db);
329 $usersDAO = new UsersDAO();
330 $row = $usersDAO->getUserByID($instructor_id);
331                                                                         
332 $vcard = new vCard();
333 if (isset($row)) {
334         $vcard->setName($row['last_name'], $row['first_name'], $row['login']);
335         $vcard->setEmail($row['email']);
336         $vcard->setNote('Originated from an AContent at '.TR_BASE_HREF.'. See ATutor.ca for additional information.');
337         $vcard->setURL($row['website']);
338
339         $imsmanifest_xml = str_replace('{VCARD}', $vcard->getVCard(), $imsmanifest_xml);
340 } else {
341         $imsmanifest_xml = str_replace('{VCARD}', '', $imsmanifest_xml);
342 }
343
344 /* save the imsmanifest.xml file */
345
346 $zipfile->add_file($frame,                       'index.html');
347 $zipfile->add_file($toc_html,            'toc.html');
348 $zipfile->add_file($imsmanifest_xml, 'imsmanifest.xml');
349 $zipfile->add_file($html_mainheader, 'header.html');
350
351 // Modified by Cindy Qi Li on Jun 3, 2010
352 // AContent does not support glossary
353 /*
354 if ($glossary_xml) {
355         $zipfile->add_file($glossary_xml,  'glossary.xml');
356         $zipfile->add_file($glossary_html, 'glossary.html');
357 }
358 */
359 // END OF Modified by Cindy Qi Li on Jun 3, 2010
360 $zipfile->add_file(file_get_contents(TR_INCLUDE_PATH.'../home/ims/include/adlcp_rootv1p2.xsd'), 'adlcp_rootv1p2.xsd');
361 $zipfile->add_file(file_get_contents(TR_INCLUDE_PATH.'../home/ims/include/ims_xml.xsd'), 'ims_xml.xsd');
362 $zipfile->add_file(file_get_contents(TR_INCLUDE_PATH.'../home/ims/include/imscp_rootv1p1p2.xsd'), 'imscp_rootv1p1p2.xsd');
363 $zipfile->add_file(file_get_contents(TR_INCLUDE_PATH.'../home/ims/include/imsmd_rootv1p2p1.xsd'), 'imsmd_rootv1p2p1.xsd');
364 $zipfile->add_file(file_get_contents(TR_INCLUDE_PATH.'../home/ims/include/ims.css'), 'ims.css');
365 $zipfile->add_file(file_get_contents(TR_INCLUDE_PATH.'../home/ims/include/footer.html'), 'footer.html');
366 $zipfile->add_file(file_get_contents('../../images/logo.png'), 'logo.png');
367
368 $zipfile->close(); // this is optional, since send_file() closes it anyway
369
370 $ims_course_title = str_replace(array(' ', ':'), '_', $ims_course_title);
371 /**
372  * A problem here with the preg_replace below.
373  * Originally was designed to remove all werid symbols to avoid file corruptions.
374  * In UTF-8, all non-english chars are considered to be 'werid symbols'
375  * We can still replace it as is, or add fileid to the filename to avoid these problems
376  * Well then again people won't be able to tell what this file is about
377  * If we are going to take out the preg_replace, some OS might not be able to understand
378  * these characters and will have problems importing.
379  */
380 $ims_course_title = preg_replace("{[^a-zA-Z0-9._-]}","", trim($ims_course_title));
381 $zipfile->send_file($ims_course_title . '_ims');
382
383 exit;
384 ?>