MDL-14934 fixed excel time in master branch, credits to Jonathon Fowler
[moodle.git] / lib / excellib.class.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * excellib.class.php
20  *
21  * @copyright  (C) 2001-3001 Eloy Lafuente (stronk7) {@link http://contiento.com}
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  * @package    core
24  * @subpackage lib
25  */
27 defined('MOODLE_INTERNAL') || die();
29 /** setup.php includes our hacked pear libs first */
30 require_once 'Spreadsheet/Excel/Writer.php';
32 /**
33  * Define and operate over one Moodle Workbook.
34  *
35  * A big part of this class acts as a wrapper over the PEAR
36  * Spreadsheet_Excel_Writer_Workbook and OLE libraries
37  * maintaining Moodle functions isolated from underlying code.
38  *
39  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
40  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41  * @package moodlecore
42  */
43 class MoodleExcelWorkbook {
44     /** @var object */
45     var $pear_excel_workbook;
46     /** @var bool */
47     var $latin_output;
49     /**
50      * Constructs one Moodle Workbook.
51      *
52      * @global object
53      * @param string $filename The name of the file
54      */
55     function MoodleExcelWorkbook($filename) {
56         global $CFG;
57     /// Internally, create one PEAR Spreadsheet_Excel_Writer_Workbook class
58         $this->pear_excel_workbook = new Spreadsheet_Excel_Writer($filename);
59     /// Prepare it to accept UTF-16LE data and to encode it properly
60         if (empty($CFG->latinexcelexport)) { /// Only if don't want to use latin (win1252) stronger output
61             $this->pear_excel_workbook->setVersion(8);
62             $this->latin_output = false;
63         } else { /// We want latin (win1252) output
64             $this->latin_output = true;
65         }
66     /// Choose our temporary directory - see MDL-7176, found by paulo.matos
67         make_upload_directory('temp/excel');
68         $this->pear_excel_workbook->setTempDir($CFG->dataroot.'/temp/excel');
69     }
71     /**
72      * Create one Moodle Worksheet
73      *
74      * @param string $name Name of the sheet
75      * @return object MoodleExcelWorksheet
76      */
77     function &add_worksheet($name = '') {
78     /// Create the Moodle Worksheet. Returns one pointer to it
79         $ws = new MoodleExcelWorksheet ($name, $this->pear_excel_workbook, $this->latin_output);
80         return $ws;
81     }
83     /**
84      * Create one Moodle Format
85      *
86      * @param array $properties array of properties [name]=value;
87      *                          valid names are set_XXXX existing
88      *                          functions without the set_ part
89      *                          i.e: [bold]=1 for set_bold(1)...Optional!
90      * @return object MoodleExcelFormat
91      */
92     function &add_format($properties = array()) {
93     /// Create the Moodle Format. Returns one pointer to it
94         $ft = new MoodleExcelFormat ($this->pear_excel_workbook, $properties);
95         return $ft;
96     }
98     /**
99      * Close the Moodle Workbook
100      */
101     function close() {
102         $this->pear_excel_workbook->close();
103     }
105     /**
106      * Write the correct HTTP headers
107      *
108      * @param string $filename Name of the downloaded file
109      */
110     function send($filename) {
111         $this->pear_excel_workbook->send($filename);
112     }
115 /**
116  * Define and operate over one Worksheet.
117  *
118  * A big part of this class acts as a wrapper over the PEAR
119  * Spreadsheet_Excel_Writer_Workbook and OLE libraries
120  * maintaining Moodle functions isolated from underlying code.
121  *
122  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
123  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
124  * @package moodlecore
125  */
126 class MoodleExcelWorksheet {
127     /** @var object */
128     var $pear_excel_worksheet;
129     /** @var bool Only if don't want to use latin (win1252) stronger output */
130     var $latin_output;
132     /**
133      * Constructs one Moodle Worksheet.
134      *
135      * @param string $filename The name of the file
136      * @param object $workbook The internal PEAR Workbook onject we are creating
137      * @param bool $latin_output Only if don't want to use latin (win1252) stronger output
138      */
139     function MoodleExcelWorksheet($name, &$workbook, $latin_output=false) {
141     /// Internally, add one sheet to the workbook
142         $this->pear_excel_worksheet =& $workbook->addWorksheet($name);
143         $this->latin_output = $latin_output;
144     /// Set encoding to UTF-16LE
145         if (!$this->latin_output) { /// Only if don't want to use latin (win1252) stronger output
146             $this->pear_excel_worksheet->setInputEncoding('UTF-16LE');
147         }
148     }
150     /**
151      * Write one string somewhere in the worksheet
152      *
153      * @param integer $row    Zero indexed row
154      * @param integer $col    Zero indexed column
155      * @param string  $str    The string to write
156      * @param mixed   $format The XF format for the cell
157      */
158     function write_string($row, $col, $str, $format=null) {
159     /// Calculate the internal PEAR format
160         $format = $this->MoodleExcelFormat2PearExcelFormat($format);
161     /// Loading the textlib singleton instance. We are going to need it.
162         $textlib = textlib_get_instance();
163     /// Convert the text from its original encoding to UTF-16LE
164         if (!$this->latin_output) { /// Only if don't want to use latin (win1252) stronger output
165             $str = $textlib->convert($str, 'utf-8', 'utf-16le');
166         } else { /// else, convert to latin (win1252)
167             $str = $textlib->convert($str, 'utf-8', 'windows-1252');
168         }
169     /// Add the string safely to the PEAR Worksheet
170         $this->pear_excel_worksheet->writeString($row, $col, $str, $format);
171     }
173     /**
174      * Write one number somewhere in the worksheet
175      *
176      * @param integer $row    Zero indexed row
177      * @param integer $col    Zero indexed column
178      * @param float   $num    The number to write
179      * @param mixed   $format The XF format for the cell
180      */
181     function write_number($row, $col, $num, $format=null) {
182     /// Calculate the internal PEAR format
183         $format = $this->MoodleExcelFormat2PearExcelFormat($format);
184     /// Add  the number safely to the PEAR Worksheet
185         $this->pear_excel_worksheet->writeNumber($row, $col, $num, $format);
186     }
188     /**
189      * Write one url somewhere in the worksheet
190      *
191      * @param integer $row    Zero indexed row
192      * @param integer $col    Zero indexed column
193      * @param string  $url    The url to write
194      * @param mixed   $format The XF format for the cell
195      */
196     function write_url($row, $col, $url, $format=null) {
197     /// Calculate the internal PEAR format
198         $format = $this->MoodleExcelFormat2PearExcelFormat($format);
199     /// Add  the url safely to the PEAR Worksheet
200         $this->pear_excel_worksheet->writeUrl($row, $col, $url, $format);
201     }
203     /**
204      * Write one date somewhere in the worksheet
205      * @param integer $row    Zero indexed row
206      * @param integer $col    Zero indexed column
207      * @param string  $date   The date to write in UNIX timestamp format
208      * @param mixed   $format The XF format for the cell
209      */
210     function write_date($row, $col, $date, $format=null) {
211     /// Calculate the internal PEAR format
212         $format = $this->MoodleExcelFormat2PearExcelFormat($format);
213     /// Convert the date to Excel format
214         $timezone = get_user_timezone_offset();
215         if ($timezone == 99) {
216             // system timezone offset in seconds
217             $offset = (int)date('Z');
218         } else {
219             $offset = (int)($timezone * HOURSECS * 2);
220         }
221         $value = ((usertime($date) + $offset) / 86400) + 25569;
222     /// Add  the date safely to the PEAR Worksheet
223         $this->pear_excel_worksheet->writeNumber($row, $col, $value, $format);
224     }
226     /**
227      * Write one formula somewhere in the worksheet
228      *
229      * @param integer $row    Zero indexed row
230      * @param integer $col    Zero indexed column
231      * @param string  $formula The formula to write
232      * @param mixed   $format The XF format for the cell
233      */
234     function write_formula($row, $col, $formula, $format=null) {
235     /// Calculate the internal PEAR format
236         $format = $this->MoodleExcelFormat2PearExcelFormat($format);
237     /// Add  the formula safely to the PEAR Worksheet
238         $this->pear_excel_worksheet->writeFormula($row, $col, $formula, $format);
239     }
241     /**
242      * Write one blanck somewhere in the worksheet
243      *
244      * @param integer $row    Zero indexed row
245      * @param integer $col    Zero indexed column
246      * @param mixed   $format The XF format for the cell
247      */
248     function write_blank($row, $col, $format=null) {
249     /// Calculate the internal PEAR format
250         $format = $this->MoodleExcelFormat2PearExcelFormat($format);
251     /// Add  the blank safely to the PEAR Worksheet
252         $this->pear_excel_worksheet->writeBlank($row, $col, $format);
253     }
255     /**
256      * Write anything somewhere in the worksheet
257      * Type will be automatically detected
258      *
259      * @param integer $row    Zero indexed row
260      * @param integer $col    Zero indexed column
261      * @param mixed   $token  What we are writing
262      * @param mixed   $format The XF format for the cell
263      * @return void
264      */
265     function write($row, $col, $token, $format=null) {
267     /// Analyse what are we trying to send
268         if (preg_match("/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/", $token)) {
269         /// Match number
270             return $this->write_number($row, $col, $token, $format);
271         } elseif (preg_match("/^[fh]tt?p:\/\//", $token)) {
272         /// Match http or ftp URL
273             return $this->write_url($row, $col, $token, '', $format);
274         } elseif (preg_match("/^mailto:/", $token)) {
275         /// Match mailto:
276             return $this->write_url($row, $col, $token, '', $format);
277         } elseif (preg_match("/^(?:in|ex)ternal:/", $token)) {
278         /// Match internal or external sheet link
279             return $this->write_url($row, $col, $token, '', $format);
280         } elseif (preg_match("/^=/", $token)) {
281         /// Match formula
282             return $this->write_formula($row, $col, $token, $format);
283         } elseif (preg_match("/^@/", $token)) {
284         /// Match formula
285             return $this->write_formula($row, $col, $token, $format);
286         } elseif ($token == '') {
287         /// Match blank
288             return $this->write_blank($row, $col, $format);
289         } else {
290         /// Default: match string
291             return $this->write_string($row, $col, $token, $format);
292         }
293     }
295     /**
296      * Sets the height (and other settings) of one row
297      *
298      * @param integer $row    The row to set
299      * @param integer $height Height we are giving to the row (null to set just format withouth setting the height)
300      * @param mixed   $format The optional XF format we are giving to the row
301      * @param bool    $hidden The optional hidden attribute
302      * @param integer $level  The optional outline level (0-7)
303      */
304     function set_row ($row, $height, $format = null, $hidden = false, $level = 0) {
305     /// Calculate the internal PEAR format
306         $format = $this->MoodleExcelFormat2PearExcelFormat($format);
307     /// Set the row safely to the PEAR Worksheet
308         $this->pear_excel_worksheet->setRow($row, $height, $format, $hidden, $level);
309     }
311     /**
312      * Sets the width (and other settings) of one column
313      *
314      * @param integer $firstcol first column on the range
315      * @param integer $lastcol  last column on the range
316      * @param integer $width    width to set
317      * @param mixed   $format   The optional XF format to apply to the columns
318      * @param integer $hidden   The optional hidden atribute
319      * @param integer $level    The optional outline level (0-7)
320      */
321     function set_column ($firstcol, $lastcol, $width, $format = null, $hidden = false, $level = 0) {
322     /// Calculate the internal PEAR format
323         $format = $this->MoodleExcelFormat2PearExcelFormat($format);
324     /// Set the column safely to the PEAR Worksheet
325         $this->pear_excel_worksheet->setColumn($firstcol, $lastcol, $width, $format, $hidden, $level);
326     }
328     /**
329     * Set the option to hide gridlines on the printed page.
330     *
331     * @access public
332     */
333     function hide_gridlines() {
334         $this->pear_excel_worksheet->hideGridLines();
335     }
337     /**
338     * Set the option to hide gridlines on the worksheet (as seen on the screen).
339     *
340     * @access public
341     */
342     function hide_screen_gridlines() {
343         $this->pear_excel_worksheet->hideScreenGridlines();
344     }
346     /**
347     * Insert a 24bit bitmap image in a worksheet.
348     *
349     * @access public
350     * @param integer $row     The row we are going to insert the bitmap into
351     * @param integer $col     The column we are going to insert the bitmap into
352     * @param string  $bitmap  The bitmap filename
353     * @param integer $x       The horizontal position (offset) of the image inside the cell.
354     * @param integer $y       The vertical position (offset) of the image inside the cell.
355     * @param integer $scale_x The horizontal scale
356     * @param integer $scale_y The vertical scale
357     */
358     function insert_bitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1) {
359     /// Add the bitmap safely to the PEAR Worksheet
360         $this->pear_excel_worksheet->insertBitmap($row, $col, $bitmap, $x, $y, $scale_x, $scale_y);
361     }
363     /**
364     * Merges the area given by its arguments.
365     * This is an Excel97/2000 method. It is required to perform more complicated
366     * merging than the normal setAlign('merge').
367     *
368     * @access public
369     * @param integer $first_row First row of the area to merge
370     * @param integer $first_col First column of the area to merge
371     * @param integer $last_row  Last row of the area to merge
372     * @param integer $last_col  Last column of the area to merge
373     */
374     function merge_cells($first_row, $first_col, $last_row, $last_col) {
375         /// Merge cells safely to the PEAR Worksheet
376         $this->pear_excel_worksheet->mergeCells($first_row, $first_col, $last_row, $last_col);
377     }
379     /**
380      * Returns the PEAR Excel Format for one Moodle Excel Format
381      *
382      * @param mixed $format MoodleExcelFormat object
383      * @return mixed PEAR Excel Format object
384      */
385     function MoodleExcelFormat2PearExcelFormat($format) {
386         if ($format) {
387             return $format->pear_excel_format;
388         } else {
389             return null;
390         }
391     }
395 /**
396  * Define and operate over one Format.
397  *
398  * A big part of this class acts as a wrapper over the PEAR
399  * Spreadsheet_Excel_Writer_Workbook and OLE libraries
400  * maintaining Moodle functions isolated from underlying code.
401  *
402  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
403  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
404  * @package moodlecore
405  */
406 class MoodleExcelFormat {
407     /** @var object */
408     var $pear_excel_format;
410     /**
411      * Constructs one Moodle Format.
412      *
413      * @param object $workbook The internal PEAR Workbook onject we are creating
414      * @param array $properties
415      */
416     function MoodleExcelFormat(&$workbook, $properties = array()) {
417     /// Internally, add one sheet to the workbook
418         $this->pear_excel_format =& $workbook->addFormat();
419     /// If we have something in the array of properties, compute them
420         foreach($properties as $property => $value) {
421             if(method_exists($this,"set_$property")) {
422                 $aux = 'set_'.$property;
423                 $this->$aux($value);
424             }
425         }
426     }
428     /**
429      * Set the size of the text in the format (in pixels).
430      * By default all texts in generated sheets are 10px.
431      *
432      * @param integer $size Size of the text (in pixels)
433      */
434     function set_size($size) {
435     /// Set the size safely to the PEAR Format
436         $this->pear_excel_format->setSize($size);
437     }
439     /**
440      * Set weight of the format
441      *
442      * @param integer $weight Weight for the text, 0 maps to 400 (normal text),
443      *                        1 maps to 700 (bold text). Valid range is: 100-1000.
444      *                        It's Optional, default is 1 (bold).
445      */
446     function set_bold($weight = 1) {
447     /// Set the bold safely to the PEAR Format
448         $this->pear_excel_format->setBold($weight);
449     }
451     /**
452      * Set underline of the format
453      *
454      * @param integer $underline The value for underline. Possible values are:
455      *                           1 => underline, 2 => double underline
456      */
457     function set_underline($underline) {
458     /// Set the underline safely to the PEAR Format
459         $this->pear_excel_format->setUnderline($underline);
460     }
462     /**
463      * Set italic of the format
464      */
465     function set_italic() {
466     /// Set the italic safely to the PEAR Format
467         $this->pear_excel_format->setItalic();
468     }
470     /**
471      * Set strikeout of the format
472      */
473     function set_strikeout() {
474     /// Set the strikeout safely to the PEAR Format
475         $this->pear_excel_format->setStrikeOut();
476     }
478     /**
479      * Set outlining of the format
480      */
481     function set_outline() {
482     /// Set the outlining safely to the PEAR Format
483         $this->pear_excel_format->setOutLine();
484     }
486     /**
487      * Set shadow of the format
488      */
489     function set_shadow() {
490     /// Set the shadow safely to the PEAR Format
491         $this->pear_excel_format->setShadow();
492     }
494     /**
495      * Set the script of the text
496      *
497      * @param integer $script The value for script type. Possible values are:
498      *                        1 => superscript, 2 => subscript
499      */
500     function set_script($script) {
501     /// Set the script safely to the PEAR Format
502         $this->pear_excel_format->setScript($script);
503     }
505     /**
506      * Set color of the format. Used to specify the color of the text to be formatted.
507      *
508      * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63])
509      */
510     function set_color($color) {
511     /// Set the background color safely to the PEAR Format
512         $this->pear_excel_format->setColor($color);
513     }
515     /**
516      * Set foreground color (top layer) of the format. About formatting colors note that cells backgrounds
517      * have TWO layers, in order to support patterns and paint them with two diferent colors.
518      * This method set the color of the TOP layer of the background format. So, when filling
519      * cells with plain colors (no patterns) this is the method to use.
520      *
521      * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63])
522      */
523     function set_fg_color($color) {
524     /// Set the foreground color safely to the PEAR Format
525         $this->pear_excel_format->setFgColor($color);
526     }
528     /**
529      * Set background color (bottom layer) of the format. About formatting colors note that cells backgrounds
530      * have TWO layers, in order to support patterns and paint them with two diferent colors.
531      * This method set the color of the BOTTOM layer of the background format. So, the color
532      * specified here only will be visible if using patterns. Use set_fg_color() to fill
533      * cells with plain colors (no patterns).
534      *
535      * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63])
536      */
537     function set_bg_color($color) {
538     /// Set the background color safely to the PEAR Format
539         $this->pear_excel_format->setBgColor($color);
540     }
542     /**
543      * Set the fill pattern of the format
544      * @param integer Optional. Defaults to 1. Meaningful values are: 0-18
545      *                0 meaning no background.
546      */
547     function set_pattern($pattern=1) {
548     /// Set the fill pattern safely to the PEAR Format
549         $this->pear_excel_format->setPattern($pattern);
550     }
552     /**
553      * Set text wrap of the format
554      */
555     function set_text_wrap() {
556     /// Set the shadow safely to the PEAR Format
557         $this->pear_excel_format->setTextWrap();
558     }
560     /**
561      * Set the cell alignment of the format
562      *
563      * @param string $location alignment for the cell ('left', 'right', etc...)
564      */
565     function set_align($location) {
566     /// Set the alignment of the cell safely to the PEAR Format
567         $this->pear_excel_format->setAlign($location);
568     }
570     /**
571      * Set the cell horizontal alignment of the format
572      *
573      * @param string $location alignment for the cell ('left', 'right', etc...)
574      */
575     function set_h_align($location) {
576     /// Set the alignment of the cell safely to the PEAR Format
577         $this->pear_excel_format->setHAlign($location);
578     }
580     /**
581      * Set the cell vertical alignment of the format
582      *
583      * @param string $location alignment for the cell ('top', 'vleft', etc...)
584      */
585     function set_v_align($location) {
586     /// Set the alignment of the cell safely to the PEAR Format
587         $this->pear_excel_format->setVAlign($location);
588     }
590     /**
591      * Set the top border of the format
592      *
593      * @param integer $style style for the cell. 1 => thin, 2 => thick
594      */
595     function set_top($style) {
596     /// Set the top border of the cell safely to the PEAR Format
597         $this->pear_excel_format->setTop($style);
598     }
600     /**
601      * Set the bottom border of the format
602      *
603      * @param integer $style style for the cell. 1 => thin, 2 => thick
604      */
605     function set_bottom($style) {
606     /// Set the bottom border of the cell safely to the PEAR Format
607         $this->pear_excel_format->setBottom($style);
608     }
610     /**
611      * Set the left border of the format
612      *
613      * @param integer $style style for the cell. 1 => thin, 2 => thick
614      */
615     function set_left($style) {
616     /// Set the left border of the cell safely to the PEAR Format
617         $this->pear_excel_format->setLeft($style);
618     }
620     /**
621      * Set the right border of the format
622      *
623      * @param integer $style style for the cell. 1 => thin, 2 => thick
624      */
625     function set_right($style) {
626     /// Set the right border of the cell safely to the PEAR Format
627         $this->pear_excel_format->setRight($style);
628     }
630     /**
631      * Set cells borders to the same style
632      *
633      * @param integer $style style to apply for all cell borders. 1 => thin, 2 => thick.
634      */
635     function set_border($style) {
636     /// Set all the borders of the cell safely to the PEAR Format
637         $this->pear_excel_format->setBorder($style);
638     }
640     /**
641      * Set the numerical format of the format
642      * It can be date, time, currency, etc...
643      *
644      * @param integer $num_format The numeric format
645      */
646     function set_num_format($num_format) {
647     /// Set the numerical format safely to the PEAR Format
648         $this->pear_excel_format->setNumFormat($num_format);
649     }