Further work on Moodle 1.9 integration.
[moodle/mihaisucan.git] / lib / odslib.class.php
blob4d70c1b8d16e73153799c02074f5387e981385e4
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) 1999 onwards Martin Dougiamas http://dougiamas.com //
11 // (C) 2001-3001 Eloy Lafuente (stronk7) http://contiento.com //
12 // (C) 2001-3001 Petr Skoda (skodak) //
13 // //
14 // This program is free software; you can redistribute it and/or modify //
15 // it under the terms of the GNU General Public License as published by //
16 // the Free Software Foundation; either version 2 of the License, or //
17 // (at your option) any later version. //
18 // //
19 // This program is distributed in the hope that it will be useful, //
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
22 // GNU General Public License for more details: //
23 // //
24 // http://www.gnu.org/copyleft/gpl.html //
25 // //
26 ///////////////////////////////////////////////////////////////////////////
29 * The xml used here is derived from output of KSpread 1.6.1
31 * Known problems:
32 * - missing formatting
33 * - write_date() works fine in OOo, but it does not work in KOffice - it knows only date or time but not both :-(
36 class MoodleODSWorkbook {
37 var $worksheets = array();
38 var $filename;
40 function MoodleODSWorkbook($filename) {
41 $this->filename = $filename;
44 /* Create one Moodle Worksheet
45 * @param string $name Name of the sheet
47 function &add_worksheet($name = '') {
48 /// Create the Moodle Worksheet. Returns one pointer to it
49 $ws =& new MoodleODSWorksheet($name);
50 $this->worksheets[] =& $ws;
51 return $ws;
54 /* Create one Moodle Format
55 * @param array $properties array of properties [name]=value;
56 * valid names are set_XXXX existing
57 * functions without the set_ part
58 * i.e: [bold]=1 for set_bold(1)...Optional!
60 function &add_format($properties = array()) {
61 $format = new MoodleODSFormat($properties);
62 return $format;;
65 /* Close the Moodle Workbook
67 function close() {
68 global $CFG;
69 require_once($CFG->libdir.'/filelib.php');
71 $dir = 'temp/ods/'.time();
72 make_upload_directory($dir, false);
73 make_upload_directory($dir.'/META-INF', false);
74 $dir = "$CFG->dataroot/$dir";
75 $files = array();
77 $handle = fopen("$dir/mimetype", 'w');
78 fwrite($handle, get_ods_mimetype());
79 $files[] = "$dir/mimetype";
81 $handle = fopen("$dir/content.xml", 'w');
82 fwrite($handle, get_ods_content($this->worksheets));
83 $files[] = "$dir/content.xml";
85 $handle = fopen("$dir/meta.xml", 'w');
86 fwrite($handle, get_ods_meta());
87 $files[] = "$dir/meta.xml";
89 $handle = fopen("$dir/styles.xml", 'w');
90 fwrite($handle, get_ods_styles());
91 $files[] = "$dir/styles.xml";
93 $handle = fopen("$dir/META-INF/manifest.xml", 'w');
94 fwrite($handle, get_ods_manifest());
95 $files[] = "$dir/META-INF";
97 $filename = "$dir/result.ods";
98 zip_files($files, $filename);
100 $handle = fopen($filename, 'rb');
101 $contents = fread($handle, filesize($filename));
102 fclose($handle);
104 remove_dir($dir); // cleanup the temp directory
106 send_file($contents, $this->filename, 0, 0, true, true, 'application/vnd.oasis.opendocument.spreadsheet');
109 /* Not required to use
110 * @param string $name Name of the downloaded file
112 function send($filename) {
113 $this->filename = $filename;
118 class MoodleODSWorksheet {
119 var $data = array();
120 var $columns = array();
121 var $rows = array();
122 var $name;
125 /* Constructs one Moodle Worksheet.
126 * @param string $filename The name of the file
128 function MoodleODSWorksheet($name) {
129 $this->name = $name;
132 /* Write one string somewhere in the worksheet
133 * @param integer $row Zero indexed row
134 * @param integer $col Zero indexed column
135 * @param string $str The string to write
136 * @param mixed $format The XF format for the cell
138 function write_string($row, $col, $str, $format=0) {
139 if (!array_key_exists($row, $this->data)) {
140 $this->data[$row] = array();
142 $this->data[$row][$col] = new object();
143 $this->data[$row][$col]->value = $str;
144 $this->data[$row][$col]->type = 'string';
145 $this->data[$row][$col]->format = $format;
148 /* Write one number somewhere in the worksheet
149 * @param integer $row Zero indexed row
150 * @param integer $col Zero indexed column
151 * @param float $num The number to write
152 * @param mixed $format The XF format for the cell
154 function write_number($row, $col, $num, $format=0) {
155 if (!array_key_exists($row, $this->data)) {
156 $this->data[$row] = array();
158 $this->data[$row][$col] = new object();
159 $this->data[$row][$col]->value = $num;
160 $this->data[$row][$col]->type = 'float';
161 $this->data[$row][$col]->format = $format;
164 /* Write one url somewhere in the worksheet
165 * @param integer $row Zero indexed row
166 * @param integer $col Zero indexed column
167 * @param string $url The url to write
168 * @param mixed $format The XF format for the cell
170 function write_url($row, $col, $url, $format=0) {
171 if (!array_key_exists($row, $this->data)) {
172 $this->data[$row] = array();
174 $this->data[$row][$col] = new object();
175 $this->data[$row][$col]->value = $url;
176 $this->data[$row][$col]->type = 'string';
177 $this->data[$row][$col]->format = $format;
180 /* Write one date somewhere in the worksheet
181 * @param integer $row Zero indexed row
182 * @param integer $col Zero indexed column
183 * @param string $url The url to write
184 * @param mixed $format The XF format for the cell
186 function write_date($row, $col, $date, $format=0) {
187 if (!array_key_exists($row, $this->data)) {
188 $this->data[$row] = array();
190 $this->data[$row][$col] = new object();
191 $this->data[$row][$col]->value = $date;
192 $this->data[$row][$col]->type = 'date';
193 $this->data[$row][$col]->format = $format;
197 * Write one formula somewhere in the worksheet
199 * @param integer $row Zero indexed row
200 * @param integer $col Zero indexed column
201 * @param string $formula The formula to write
202 * @param mixed $format The XF format for the cell
204 function write_formula($row, $col, $formula, $format=null) {
205 // not implement
208 /* Write one blanck somewhere in the worksheet
209 * @param integer $row Zero indexed row
210 * @param integer $col Zero indexed column
211 * @param mixed $format The XF format for the cell
213 function write_blank($row, $col, $format=0) {
214 if (array_key_exists($row, $this->data)) {
215 unset($this->data[$row][$col]);
219 /* Write anything somewhere in the worksheet
220 * Type will be automatically detected
221 * @param integer $row Zero indexed row
222 * @param integer $col Zero indexed column
223 * @param mixed $token What we are writing
224 * @param mixed $format The XF format for the cell
226 function write($row, $col, $token, $format=0) {
228 /// Analyse what are we trying to send
229 if (preg_match("/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/", $token)) {
230 /// Match number
231 return $this->write_number($row, $col, $token, $format);
232 } elseif (preg_match("/^[fh]tt?p:\/\//", $token)) {
233 /// Match http or ftp URL
234 return $this->write_url($row, $col, $token, '', $format);
235 } elseif (preg_match("/^mailto:/", $token)) {
236 /// Match mailto:
237 return $this->write_url($row, $col, $token, '', $format);
238 } elseif (preg_match("/^(?:in|ex)ternal:/", $token)) {
239 /// Match internal or external sheet link
240 return $this->write_url($row, $col, $token, '', $format);
241 } elseif (preg_match("/^=/", $token)) {
242 /// Match formula
243 return $this->write_formula($row, $col, $token, $format);
244 } elseif (preg_match("/^@/", $token)) {
245 /// Match formula
246 return $this->write_formula($row, $col, $token, $format);
247 } elseif ($token == '') {
248 /// Match blank
249 return $this->write_blank($row, $col, $format);
250 } else {
251 /// Default: match string
252 return $this->write_string($row, $col, $token, $format);
256 /* Sets the height (and other settings) of one row
257 * @param integer $row The row to set
258 * @param integer $height Height we are giving to the row (null to set just format withouth setting the height)
259 * @param mixed $format The optional XF format we are giving to the row
260 * @param bool $hidden The optional hidden attribute
261 * @param integer $level The optional outline level (0-7)
263 function set_row($row, $height, $format = 0, $hidden = false, $level = 0) {
264 $this->rows[$row] = new object();
265 $this->rows[$row]->height = $height;
266 //$this->rows[$row]->format = $format; // TODO: fix and enable
267 $this->rows[$row]->hidden = $hidden;
270 /* Sets the width (and other settings) of one column
271 * @param integer $firstcol first column on the range
272 * @param integer $lastcol last column on the range
273 * @param integer $width width to set
274 * @param mixed $format The optional XF format to apply to the columns
275 * @param integer $hidden The optional hidden atribute
276 * @param integer $level The optional outline level (0-7)
278 function set_column($firstcol, $lastcol, $width, $format = 0, $hidden = false, $level = 0) {
279 for($i=$firstcol; $i<=$lastcol; $i++) {
280 $this->columns[$i] = new object();
281 $this->columns[$i]->width = $width;
282 //$this->columns[$i]->format = $format; // TODO: fix and enable
283 $this->columns[$i]->hidden = $hidden;
289 * Set the option to hide gridlines on the printed page.
291 * @access public
293 function hide_gridlines() {
294 // not implement
298 * Set the option to hide gridlines on the worksheet (as seen on the screen).
300 * @access public
302 function hide_screen_gridlines() {
303 // not implement
307 * Insert a 24bit bitmap image in a worksheet.
309 * @access public
310 * @param integer $row The row we are going to insert the bitmap into
311 * @param integer $col The column we are going to insert the bitmap into
312 * @param string $bitmap The bitmap filename
313 * @param integer $x The horizontal position (offset) of the image inside the cell.
314 * @param integer $y The vertical position (offset) of the image inside the cell.
315 * @param integer $scale_x The horizontal scale
316 * @param integer $scale_y The vertical scale
318 function insert_bitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1) {
319 // not implement
322 * Merges the area given by its arguments.
323 * merging than the normal setAlign('merge').
325 * @access public
326 * @param integer $first_row First row of the area to merge
327 * @param integer $first_col First column of the area to merge
328 * @param integer $last_row Last row of the area to merge
329 * @param integer $last_col Last column of the area to merge
331 function merge_cells($first_row, $first_col, $last_row, $last_col) {
332 // not implement
337 * Define and operate over one Format.
339 class MoodleODSFormat {
340 var $id;
341 var $properties;
343 /* Constructs one Moodle Format.
344 * @param object $workbook The internal PEAR Workbook onject we are creating
346 function MoodleODSFormat($properties = array()) {
347 static $fid = 1;
349 $this->id = $fid++;
351 foreach($properties as $property => $value) {
352 if(method_exists($this,"set_$property")) {
353 $aux = 'set_'.$property;
354 $this->$aux($value);
359 /* Set weight of the format
360 * @param integer $weight Weight for the text, 0 maps to 400 (normal text),
361 * 1 maps to 700 (bold text). Valid range is: 100-1000.
362 * It's Optional, default is 1 (bold).
364 function set_bold($weight = 1) {
365 $this->properties['bold'] = $weight;
368 /* Set underline of the format
369 * @param integer $underline The value for underline. Possible values are:
370 * 1 => underline, 2 => double underline
372 function set_underline($underline = 1) {
373 $this->properties['underline'] = $underline;
376 /* Set italic of the format
378 function set_italic() {
379 $this->properties['italic'] = true;
382 /* Set strikeout of the format
384 function set_strikeout() {
385 $this->properties['strikeout'] = true;
388 /* Set outlining of the format
390 function set_outline() {
393 /* Set shadow of the format
395 function set_shadow() {
398 /* Set the script of the text
399 * @param integer $script The value for script type. Possible values are:
400 * 1 => superscript, 2 => subscript
402 function set_script($script) {
405 /* Set color of the format
406 * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63])
408 function set_color($color) {
409 $this->properties['color'] = $this->_get_color($color);
412 /* Set foreground color of the format
413 * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63])
415 function set_fg_color($color) {
418 /* Set background color of the format
419 * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63])
421 function set_bg_color($color) {
422 $this->properties['bg_color'] = $this->_get_color($color);
425 /* Set the fill pattern of the format
426 * @param integer Optional. Defaults to 1. Meaningful values are: 0-18
427 * 0 meaning no background.
429 function set_pattern($pattern=1) {
432 /* Set text wrap of the format
434 function set_text_wrap() {
437 /* Set the cell alignment of the format
438 * @param string $location alignment for the cell ('left', 'right', etc...)
440 function set_align($location) {
441 switch ($location) {
442 case 'start':
443 case 'left':
444 $this->properties['align'] = 'start';
445 break;
446 case 'center':
447 $this->properties['align'] = 'center';
448 break;
449 case 'end':
450 case 'right':
451 $this->properties['align'] = 'end';
452 break;
453 default:
454 //ignore the rest == start
458 /* Set the cell horizontal alignment of the format
459 * @param string $location alignment for the cell ('left', 'right', etc...)
461 function set_h_align($location) {
462 set_align($location);
465 /* Set the cell vertical alignment of the format
466 * @param string $location alignment for the cell ('top', 'vleft', etc...)
468 function set_v_align($location) {
469 switch ($location) {
470 case 'top':
471 $this->properties['v_align'] = 'top';
472 break;
473 case 'bottom':
474 $this->properties['v_align'] = 'bottom';
475 break;
476 default:
477 //ignore the rest == middle
481 /* Set the top border of the format
482 * @param integer $style style for the cell. 1 => thin, 2 => thick
484 function set_top($style) {
487 /* Set the bottom border of the format
488 * @param integer $style style for the cell. 1 => thin, 2 => thick
490 function set_bottom($style) {
493 /* Set the left border of the format
494 * @param integer $style style for the cell. 1 => thin, 2 => thick
496 function set_left($style) {
499 /* Set the right border of the format
500 * @param integer $style style for the cell. 1 => thin, 2 => thick
502 function set_right($style) {
506 * Set cells borders to the same style
507 * @param integer $style style to apply for all cell borders. 1 => thin, 2 => thick.
509 function set_border($style) {
512 /* Set the numerical format of the format
513 * It can be date, time, currency, etc...
514 /* Set the numerical format of the format
515 * It can be date, time, currency, etc...
516 * @param integer $num_format The numeric format
518 function set_num_format($num_format) {
521 function _get_color($name_color = '') {
522 if (strpos($name_color, '#') === 0) {
523 return $name_color; // no conversion needed
526 $colors = array('aqua' => '#00FFFF',
527 'cyan' => '#00FFFF',
528 'black' => '#FFFFFF',
529 'blue' => '#0000FF',
530 'brown' => '#A52A2A',
531 'magenta' => '#FF00FF',
532 'fuchsia' => '#FF00FF',
533 'gray' => '#A0A0A0',
534 'grey' => '#A0A0A0',
535 'green' => '#00FF00',
536 'lime' => '#00FF00',
537 'navy' => '#000080',
538 'orange' => '#FF8000',
539 'purple' => '#800080',
540 'red' => '#FF0000',
541 'silver' => '#DCDCDC',
542 'white' => '#FFFFFF',
543 'yellow' => '#FFFF00');
545 if(array_key_exists($name_color, $colors)) {
546 return $colors[$name_color];
547 } else {
548 return false;
554 //=============================
555 // OpenDocument XML functions
556 //=============================
557 function get_ods_content(&$worksheets) {
560 // find out the size of worksheets and used styles
561 $formats = array();
562 $formatstyles = '';
563 $rowstyles = '';
564 $colstyles = '';
566 foreach($worksheets as $wsnum=>$ws) {
567 $worksheets[$wsnum]->maxr = 0;
568 $worksheets[$wsnum]->maxc = 0;
569 foreach($ws->data as $rnum=>$row) {
570 if ($rnum > $worksheets[$wsnum]->maxr) {
571 $worksheets[$wsnum]->maxr = $rnum;
573 foreach($row as $cnum=>$cell) {
574 if ($cnum > $worksheets[$wsnum]->maxc) {
575 $worksheets[$wsnum]->maxc = $cnum;
577 if (!empty($cell->format)) {
578 if (!array_key_exists($cell->format->id, $formats)) {
579 $formats[$cell->format->id] = $cell->format;
585 foreach($ws->rows as $rnum=>$row) {
586 if (!empty($row->format)) {
587 if (!array_key_exists($row->format->id, $formats)) {
588 $formats[$row->format->id] = $row->format;
591 if ($rnum > $worksheets[$wsnum]->maxr) {
592 $worksheets[$wsnum]->maxr = $rnum;
594 //define all column styles
595 if (!empty($ws->rows[$rnum])) {
596 $rowstyles .= '
597 <style:style style:name="ws'.$wsnum.'ro'.$rnum.'" style:family="table-row">
598 <style:table-row-properties style:row-height="'.$row->height.'pt"/>
599 </style:style>';
603 foreach($ws->columns as $cnum=>$col) {
604 if (!empty($col->format)) {
605 if (!array_key_exists($col->format->id, $formats)) {
606 $formats[$col->format->id] = $col->format;
609 if ($cnum > $worksheets[$wsnum]->maxc) {
610 $worksheets[$wsnum]->maxc = $cnum;
612 //define all column styles
613 if (!empty($ws->columns[$cnum])) {
614 $colstyles .= '
615 <style:style style:name="ws'.$wsnum.'co'.$cnum.'" style:family="table-column">
616 <style:table-column-properties style:column-width="'.$col->width.'pt"/>
617 </style:style>';
622 foreach($formats as $format) {
623 $textprop = '';
624 $cellprop = '';
625 $parprop = '';
626 foreach($format->properties as $pname=>$pvalue) {
627 switch ($pname) {
628 case 'bold':
629 if (!empty($pvalue)) {
630 $textprop .= ' fo:font-weight="bold"';
632 break;
633 case 'italic':
634 if (!empty($pvalue)) {
635 $textprop .= ' fo:font-style="italic"';
637 break;
638 case 'underline':
639 if (!empty($pvalue)) {
640 $textprop .= ' style:text-underline-color="font-color" style:text-underline-style="solid" style:text-underline-width="auto"';
642 break;
643 case 'strikeout':
644 if (!empty($pvalue)) {
645 $textprop .= ' style:text-line-through-style="solid"';
647 break;
648 case 'color':
649 if ($pvalue !== false) {
650 $textprop .= ' fo:color="'.$pvalue.'"';
652 break;
653 case 'bg_color':
654 if ($pvalue !== false) {
655 $cellprop .= ' fo:background-color="'.$pvalue.'"';
657 break;
658 case 'align':
659 $parprop .= ' fo:text-align="'.$pvalue.'"';
660 break;
661 case 'v_align':
662 $cellprop .= ' style:vertical-align="'.$pvalue.'"';
663 break;
666 if (!empty($textprop)) {
667 $textprop = '
668 <style:text-properties'.$textprop.'/>';
671 if (!empty($cellprop)) {
672 $cellprop = '
673 <style:table-cell-properties'.$cellprop.'/>';
676 if (!empty($parprop)) {
677 $parprop = '
678 <style:paragraph-properties'.$parprop.'/>';
681 $formatstyles .= '
682 <style:style style:name="format'.$format->id.'" style:family="table-cell">'.$textprop.$cellprop.$parprop.'
683 </style:style>';
686 /// header
687 $buffer =
688 '<?xml version="1.0" encoding="UTF-8"?>
689 <office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xlink="http://www.w3.org/1999/xlink">
690 <office:automatic-styles>
691 <style:style style:name="ta1" style:family="table" style:master-page-name="Standard1">
692 <style:table-properties table:display="true"/>
693 </style:style>
694 <style:style style:name="date0" style:family="table-cell"/>';
696 $buffer .= $formatstyles;
697 $buffer .= $rowstyles;
698 $buffer .= $colstyles;
700 $buffer .= '
701 </office:automatic-styles>
702 <office:body>
703 <office:spreadsheet>
706 foreach($worksheets as $wsnum=>$ws) {
708 /// worksheet header
709 $buffer .= '<table:table table:name="' . htmlspecialchars($ws->name) . '" table:style-name="ta1">'."\n";
711 // define column properties
712 for($c=0; $c<=$ws->maxc; $c++) {
713 if (array_key_exists($c, $ws->columns)) {
714 $extra = '';
715 if (!empty($ws->columns[$c]->format)) {
716 $extra .= ' table:default-cell-style-name="format'.$ws->columns[$c]->format->id.'"';
718 if ($ws->columns[$c]->hidden) {
719 $extra .= ' table:visibility="collapse"';
721 $buffer .= '<table:table-column table:style-name="ws'.$wsnum.'co'.$c.'"'.$extra.'/>'."\n";
722 } else {
723 $buffer .= '<table:table-column/>'."\n";
727 // print all rows
728 for($r=0; $r<=$ws->maxr; $r++) {
729 if (array_key_exists($r, $ws->rows)) {
730 $extra = '';
731 if (!empty($ws->rows[$r]->format)) {
732 $extra .= ' table:default-cell-style-name="format'.$ws->rows[$r]->format->id.'"';
734 if ($ws->rows[$r]->hidden) {
735 $extra .= ' table:visibility="collapse"';
737 $buffer .= '<table:table-row table:style-name="ws'.$wsnum.'ro'.$r.'"'.$extra.'>'."\n";
738 } else {
739 $buffer .= '<table:table-row>'."\n";
741 for($c=0; $c<=$ws->maxc; $c++) {
742 if (isset($ws->data[$r][$c])) {
743 $cell = $ws->data[$r][$c];
744 $extra = ' ';
745 if (!empty($cell->format)) {
746 $extra = ' table:style-name="format'.$cell->format->id.'"';
748 if ($cell->type == 'date') {
749 $buffer .= '<table:table-cell office:value-type="date" table:style-name="date0" office:date-value="' . strftime('%Y-%m-%dT%H:%M:%S', $cell->value) . '"'.$extra.'>'
750 . '<text:p>' . strftime('%Y-%m-%dT%H:%M:%S', $cell->value) . '</text:p>'
751 . '</table:table-cell>'."\n";
752 } else if ($cell->type == 'float') {
753 $buffer .= '<table:table-cell office:value-type="float" office:value="' . htmlspecialchars($cell->value) . '"'.$extra.'>'
754 . '<text:p>' . htmlspecialchars($cell->value) . '</text:p>'
755 . '</table:table-cell>'."\n";
756 } else if ($cell->type == 'string') {
757 $buffer .= '<table:table-cell office:value-type="string" office:string-value="' . htmlspecialchars($cell->value) . '"'.$extra.'>'
758 . '<text:p>' . htmlspecialchars($cell->value) . '</text:p>'
759 . '</table:table-cell>'."\n";
760 } else {
761 $buffer .= '<table:table-cell office:value-type="string"'.$extra.'>'
762 . '<text:p>!!Error - unknown type!!</text:p>'
763 . '</table:table-cell>'."\n";
765 } else {
766 $buffer .= '<table:table-cell/>'."\n";
769 $buffer .= '</table:table-row>'."\n";
771 /// worksheet footer
772 $buffer .= '</table:table>'."\n";
776 /// footer
777 $buffer .=
778 ' </office:spreadsheet>
779 </office:body>
780 </office:document-content>';
782 return $buffer;
785 function get_ods_mimetype() {
786 return 'application/vnd.oasis.opendocument.spreadsheet';
789 function get_ods_meta() {
790 global $CFG, $USER;
792 return
793 '<?xml version="1.0" encoding="UTF-8"?>
794 <office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xlink="http://www.w3.org/1999/xlink">
795 <office:meta>
796 <meta:generator>Moodle '.$CFG->version.'</meta:generator>
797 <meta:initial-creator>'.fullname($USER, true).'</meta:initial-creator>
798 <meta:editing-cycles>1</meta:editing-cycles>
799 <meta:creation-date>'.strftime('%Y-%m-%dT%H:%M:%S').'</meta:creation-date>
800 <dc:date>'.strftime('%Y-%m-%dT%H:%M:%S').'</dc:date>
801 <dc:creator>'.fullname($USER, true).'</dc:creator>
802 </office:meta>
803 </office:document-meta>';
806 function get_ods_styles() {
807 return
808 '<?xml version="1.0" encoding="UTF-8"?>
809 <office:document-styles xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xlink="http://www.w3.org/1999/xlink">
810 <office:styles>
811 <style:default-style style:family="table-column">
812 <style:table-column-properties style:column-width="75pt"/>
813 </style:default-style>
814 <style:default-style style:family="table-row">
815 <style:table-row-properties style:row-height="15pt"/>
816 </style:default-style>
817 <style:default-style style:family="table-cell">
818 <style:table-cell-properties fo:background-color="#ffffff" style:cell-protect="protected" style:vertical-align="middle"/>
819 <style:text-properties fo:color="#000000" fo:font-family="Arial" fo:font-size="12pt"/>
820 </style:default-style>
821 </office:styles>
822 <office:automatic-styles>
823 <style:page-layout style:name="pm1">
824 <style:page-layout-properties fo:margin-bottom="56.6930116pt" fo:margin-left="56.6930116pt" fo:margin-right="56.6930116pt" fo:margin-top="56.6930116pt" fo:page-height="841.89122226pt" fo:page-width="595.2766218pt" style:print="objects charts drawings zero-values" style:print-orientation="portrait"/>
825 </style:page-layout>
826 </office:automatic-styles>
827 <office:master-styles>
828 <style:master-page style:name="Standard1" style:page-layout-name="pm1">
829 <style:header>
830 <text:p>
831 <text:sheet-name>???</text:sheet-name>
832 </text:p>
833 </style:header><style:footer>
834 <text:p>
835 <text:sheet-name>Page </text:sheet-name>
836 <text:page-number>1</text:page-number>
837 </text:p>
838 </style:footer>
839 </style:master-page>
840 </office:master-styles>
841 </office:document-styles>
845 function get_ods_manifest() {
846 return
847 '<?xml version="1.0" encoding="UTF-8"?>
848 <manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">
849 <manifest:file-entry manifest:media-type="application/vnd.oasis.opendocument.spreadsheet" manifest:full-path="/"/>
850 <manifest:file-entry manifest:media-type="text/xml" manifest:full-path="content.xml"/>
851 <manifest:file-entry manifest:media-type="text/xml" manifest:full-path="styles.xml"/>
852 <manifest:file-entry manifest:media-type="text/xml" manifest:full-path="meta.xml"/>
853 </manifest:manifest>';