lib.php 28.3 KB
Newer Older
1
2
3
<?php
/**
 * Mahara: Electronic portfolio, weblog, resume builder and social networking
4
5
 * Copyright (C) 2006-2009 Catalyst IT Ltd and others; see:
 *                         http://wiki.mahara.org/Contributors
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @package    mahara
 * @subpackage export-html
 * @author     Catalyst IT Ltd
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL
24
 * @copyright  (C) 2006-2009 Catalyst IT Ltd http://catalyst.net.nz
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
 *
 */

defined('INTERNAL') || die();

/**
 * HTML export plugin
 */
class PluginExportHtml extends PluginExport {

    /**
     * name of resultant zipfile
     */
    protected $zipfile;

    /**
     * The name of the directory under which all the other directories and 
     * files will be placed in the export
     */
    protected $rootdir;

46
47
48
49
50
51
    /**
     * List of directories of static files provided by artefact plugins
     */
    private $pluginstaticdirs = array();

    /**
52
53
54
55
     * List of stylesheets to include in the export.
     *
     * This is keyed by artefact plugin name, the empty string key contains 
     * stylesheets that will be included on all pages.
56
     */
57
    private $stylesheets = array('' => array());
58

59
60
61
62
63
64
65
    /**
     * Whether the user requested to export just one view. In this case,
     * the generated export doesn't have the home page - just the View is 
     * exported (plus artefacts of course)
     */
    protected $exportingoneview = false;

66
67
68
69
    /**
    * constructor.  overrides the parent class
    * to set up smarty and the attachment directory
    */
70
    public function __construct(User $user, $views, $artefacts, $progresscallback=null) {
71
        global $THEME;
72
        parent::__construct($user, $views, $artefacts, $progresscallback);
73
        $this->rootdir = 'portfolio-for-' . self::text_to_path($user->get('username'));
74
75

        // Create basic required directories
76
        foreach (array('files', 'views', 'static', 'static/smilies', 'static/profileicons') as $directory) {
77
78
79
80
81
82
            $directory = "{$this->exportdir}/{$this->rootdir}/{$directory}/";
            if (!check_dir_exists($directory)) {
                throw new SystemException("Couldn't create the temporary export directory $directory");
            }
        }
        $this->zipfile = 'mahara-export-html-user'
83
            . $this->get('user')->get('id') . '-' . $this->exporttime . '.zip';
84
85

        // Find what stylesheets need to be included
86
        $themedirs = $THEME->get_path('', true, 'export/html');
87
88
89
90
        $stylesheets = array('style.css', 'print.css');
        foreach ($themedirs as $theme => $themedir) {
            foreach ($stylesheets as $stylesheet) {
                if (is_readable($themedir . 'style/' . $stylesheet)) {
91
                    array_unshift($this->stylesheets[''], 'theme/' . $theme . '/static/style/' . $stylesheet);
92
93
94
95
                }
            }
        }

96
97
98
99
100
101
        $this->exportingoneview = (
            $this->viewexportmode == PluginExport::EXPORT_LIST_OF_VIEWS &&
            $this->artefactexportmode == PluginExport::EXPORT_ARTEFACTS_FOR_VIEWS &&
            count($this->views) == 1
        );

102
        $this->notify_progress_callback(15, 'Setup complete');
103
104
    }

105
106
107
108
109
110
111
112
    public static function get_title() {
        return get_string('title', 'export.html');
    }

    public static function get_description() {
        return get_string('description', 'export.html');
    }

113
    /**
Nigel McNie's avatar
Nigel McNie committed
114
115
     * Main export routine
     */
116
    public function export() {
117
        global $THEME;
118
119
        raise_memory_limit('128M');

120
        $summaries = array();
121
        $plugins = plugins_installed('artefact', true);
122
        $exportplugins = array();
123
        $progressstart = 15;
124
        $progressend   = 25;
125
        $plugincount   = count($plugins);
126
127
128

        // First pass: find out which plugins are exporting like us, and ask 
        // them about the static data they want to include in every template
129
130
        $i = 0;
        foreach ($plugins as $plugin) {
131
            $plugin = $plugin->name;
132
            $this->notify_progress_callback(intval($progressstart + (++$i / $plugincount) * ($progressend - $progressstart)), 'Preparing ' . $plugin);
133

134
            if (safe_require('export', 'html/' . $plugin, 'lib.php', 'require_once', true)) {
135
136
                $exportplugins[] = $plugin;

137
138
139
140
141
142
143
                $classname = 'HtmlExport' . ucfirst($plugin);
                if (!is_subclass_of($classname, 'HtmlExportArtefactPlugin')) {
                    throw new SystemException("Class $classname does not extend HtmlExportArtefactPlugin as it should");
                }

                safe_require('artefact', $plugin);

144
                // Find out whether the plugin has static data for us
145
                $themestaticdirs = array_reverse($THEME->get_path('', true, 'artefact/' . $plugin . '/export/html'));
146
147
148
149
150
                foreach ($themestaticdirs as $dir) {
                    $staticdir = substr($dir, strlen(get_config('docroot') . 'artefact/'));
                    $this->pluginstaticdirs[] = $staticdir;
                    foreach (array('style.css', 'print.css') as $stylesheet) {
                        if (is_readable($dir . 'style/' . $stylesheet)) {
151
                            $this->stylesheets[$plugin][] = str_replace('export/html/', '', $staticdir) . 'style/' . $stylesheet;
152
153
                        }
                    }
154
                }
155
156
157
            }
        }

158
159
        // Second pass: actually dump data for active export plugins
        $progressstart = 25;
160
        $progressend   = 50;
161
162
163
164
165
166
167
168
169
170
171
172
        $i = 0;
        foreach ($exportplugins as $plugin) {
            $this->notify_progress_callback(intval($progressstart + (++$i / $plugincount) * ($progressend - $progressstart)), 'Exporting data for ' . $plugin);
            $classname = 'HtmlExport' . ucfirst($plugin);
            $artefactexporter = new $classname($this);
            $artefactexporter->dump_export_data();
            // If just exporting a list of views, we don't care about the summaries for each artefact plugin
            if (!($this->viewexportmode == PluginExport::EXPORT_LIST_OF_VIEWS && $this->artefactexportmode == PluginExport::EXPORT_ARTEFACTS_FOR_VIEWS)) {
                $summaries[$plugin] = array($artefactexporter->get_summary_weight(), $artefactexporter->get_summary());
            }
        }

Nigel McNie's avatar
Nigel McNie committed
173
        // Get the view data
174
        $this->notify_progress_callback(55, 'Exporting Views');
Nigel McNie's avatar
Nigel McNie committed
175
176
        $this->dump_view_export_data();

177
178
        if (!$this->exportingoneview) {
            $summaries['view'] = array(100, $this->get_view_summary());
179

180
181
182
183
184
185
186
187
188
189
            // Sort by weight (then drop the weight information)
            $this->notify_progress_callback(75, 'Building index page');
            uasort($summaries, create_function('$a, $b', 'return $a[0] > $b[0];'));
            foreach ($summaries as &$summary) {
                $summary = $summary[1];
            }

            // Build index.html
            $this->build_index_page($summaries);
        }
190
191

        // Copy all static files into the export
192
        $this->notify_progress_callback(80, 'Copying extra files');
193
        $this->copy_static_files();
194
195
196
197
198
199
200
201
202

        // Copy all resized images that were found while rewriting the HTML
        $copyproxy = HtmlExportCopyProxy::singleton();
        $copydata = $copyproxy->get_copy_data();
        foreach ($copydata as $from => $to) {
            if (!copy($from, $this->get('exportdir') . '/' . $this->get('rootdir') . $to)) {
                throw new SystemException("Could not copy static file $from");
            }
        }
203
204
205
        

        // zip everything up
206
        $this->notify_progress_callback(90, 'Creating zipfile');
207
208
209
210
211
212
213
214
215
216
217
218
219
220
        $cwd = getcwd();
        $command = sprintf('%s %s %s %s',
            get_config('pathtozip'),
            get_config('ziprecursearg'),
            escapeshellarg($this->exportdir .  $this->zipfile),
            escapeshellarg($this->rootdir)
        );
        $output = array();
        chdir($this->exportdir);
        exec($command, $output, $returnvar);
        chdir($cwd);
        if ($returnvar != 0) {
            throw new SystemException('Failed to zip the export file');
        }
221
        $this->notify_progress_callback(100, 'Done');
222
223
224
225
226
227
228
229
        return $this->zipfile;
    }

    public function cleanup() {
        // @todo remove temporary files and directories
        // @todo maybe move the zip file somewhere else - like to files/export or something
    }

230
231
232
233
234
235
236
    public function get_smarty($rootpath='', $section='') {
        if ($section && isset($this->stylesheets[$section])) {
            $stylesheets = array_merge($this->stylesheets[''], $this->stylesheets[$section]);
        }
        else {
            $stylesheets = $this->stylesheets[''];
        }
237
238
239
        $smarty = smarty_core();
        $smarty->assign('user', $this->get('user'));
        $smarty->assign('rootpath', $rootpath);
240
        $smarty->assign('export_time', $this->exporttime);
241
        $smarty->assign('sitename', get_config('sitename'));
242
        $smarty->assign('stylesheets', $stylesheets);
243
        $smarty->assign('maharalogo', $rootpath . $this->theme_path('images/logo.png'));
244
245
246
247

        return $smarty;
    }

248
249
250
251
252
253
254
    /**
     * Converts a relative path to a static file that the HTML export theme 
     * should have, to a path in the static export where the file will reside.
     *
     * This returns the path in the most appropriate theme.
     */
    private function theme_path($path) {
255
256
        global $THEME;
        $themestaticdirs = $THEME->get_path('', true, 'export/html');
257
258
259
260
261
262
263
        foreach ($themestaticdirs as $theme => $dir) {
            if (is_readable($dir . $path)) {
                return 'static/theme/' . $theme . '/static/' . $path;
            }
        }
    }

264
    /**
265
     * Converts the passed text into a a form that could be used in a URL.
266
267
268
269
270
     *
     * @param string $text The text to convert
     * @return string      The converted text
     */
    public static function text_to_path($text) {
271
        return substr(preg_replace('#[^a-zA-Z0-9_-]+#', '-', $text), 0, 255);
272
273
    }

274
275
276
277
278
279
280
281
282
283
284
    /**
     * Sanitises a string meant to be used as a filesystem path.
     *
     * Mahara allows file/folder artefact names to have slashes in them, which 
     * aren't legal on most real filesystems.
     */
    public static function sanitise_path($path) {
        return substr(str_replace('/', '_', $path), 0, 255);
    }


285
286
    private function build_index_page($summaries) {
        $smarty = $this->get_smarty();
287
        $smarty->assign('page_heading', full_name($this->get('user')));
288
289
        $smarty->assign('summaries', $summaries);
        $content = $smarty->fetch('export:html:index.tpl');
290
291
292
        if (!file_put_contents($this->exportdir . '/' . $this->rootdir . '/index.html', $content)) {
            throw new SystemException("Could not create index.html for the export");
        }
293
294
    }

Nigel McNie's avatar
Nigel McNie committed
295
296
297
298
    /**
     * Dumps all views into the HTML export
     */
    private function dump_view_export_data() {
299
300
301
302
        $progressstart = 55;
        $progressend   = 75;
        $i = 0;
        $viewcount = count($this->views);
303
304
        $rootpath = ($this->exportingoneview) ? './' : '../../';
        $smarty = $this->get_smarty($rootpath);
305
        foreach ($this->views as $viewid => $view) {
306
            $this->notify_progress_callback(intval($progressstart + (++$i / $viewcount) * ($progressend - $progressstart)), "Exporting Views ($i/$viewcount)");
307
            $smarty->assign('page_heading', $view->get('title'));
308
            $smarty->assign('viewdescription', $view->get('description'));
309

310
311
312
313
314
315
316
317
318
319
320
321
322
            if ($this->exportingoneview) {
                $smarty->assign('nobreadcrumbs', true);
                $directory = $this->exportdir . '/' . $this->rootdir;
            }
            else {
                $smarty->assign('breadcrumbs', array(
                    array('text' => get_string('Views', 'view')),
                    array('text' => $view->get('title'), 'path' => 'index.html'),
                ));
                $directory = $this->exportdir . '/' . $this->rootdir . '/views/' . self::text_to_path($view->get('title'));
                if (!check_dir_exists($directory)) {
                    throw new SystemException("Could not create directory for view $viewid");
                }
323
            }
Nigel McNie's avatar
Nigel McNie committed
324

325
            $outputfilter = new HtmlExportOutputFilter($rootpath, $this);
326
327
328
329
            $smarty->assign('view', $outputfilter->filter($view->build_columns()));
            $content = $smarty->fetch('export:html:view.tpl');
            if (!file_put_contents("$directory/index.html", $content)) {
                throw new SystemException("Could not write view page for view $viewid");
Nigel McNie's avatar
Nigel McNie committed
330
331
332
333
334
335
336
337
338
339
340
341
            }
        }
    }

    private function get_view_summary() {
        $smarty = $this->get_smarty('../');

        $views = array();
        foreach ($this->views as $view) {
            if ($view->get('type') != 'profile') {
                $views[] = array(
                    'title' => $view->get('title'),
342
                    'folder' => self::text_to_path($view->get('title')),
Nigel McNie's avatar
Nigel McNie committed
343
344
345
                );
            }
        }
346
347
348
349
        function sort_by_title($a, $b) {
            return strnatcasecmp($a['title'], $b['title']);
        }
        usort($views, 'sort_by_title');
Nigel McNie's avatar
Nigel McNie committed
350
        $smarty->assign('views', $views);
351
352
353
354
355
356
357
358
359
360

        if ($views) {
            $stryouhaveviews = (count($views) == 1)
                ? get_string('youhaveoneview', 'view')
                : get_string('youhaveviews', 'view', count($views));
        }
        else {
            $stryouhaveviews = get_string('youhavenoviews', 'view');
        }
        $smarty->assign('stryouhaveviews', $stryouhaveviews);
Nigel McNie's avatar
Nigel McNie committed
361
362

        return array(
363
            'title' => get_string('Views', 'view'),
Nigel McNie's avatar
Nigel McNie committed
364
365
366
367
            'description' => $smarty->fetch('export:html:viewsummary.tpl'),
        );
    }

368
369
370
371
    /**
     * Copies the static files (stylesheets etc.) into the export
     */
    private function copy_static_files() {
372
        global $THEME;
373
        require_once('file.php');
374
        $staticdir = $this->get('exportdir') . '/' . $this->get('rootdir') . '/static/';
375
376
377
        $directoriestocopy = array();

        // Get static directories from each theme for HTML export
378
        $themestaticdirs = $THEME->get_path('', true, 'export/html');
379
380
381
382
383
384
385
386
387
        foreach ($themestaticdirs as $theme => $dir) {
            $themedir = $staticdir . 'theme/' . $theme . '/static/';
            $directoriestocopy[$dir] = $themedir;
            if (!check_dir_exists($themedir)) {
                throw new SystemException("Could not create theme directory for theme $theme");
            }
        }

        // Smilies
388
        $directoriestocopy[get_config('docroot') . 'js/tinymce/plugins/emotions/img'] = $staticdir . 'smilies/';
389

390
391
392
        $filestocopy = array(
            get_config('docroot') . 'theme/views.css' => $staticdir . 'views.css',
        );
393

394
        foreach ($this->pluginstaticdirs as $dir) {
395
396
397
            $destinationdir = str_replace('export/html/', '', $dir);
            if (!check_dir_exists($staticdir . $destinationdir)) {
                throw new SystemException("Could not create static directory $destinationdir");
398
            }
399
            $directoriestocopy[get_config('docroot') . 'artefact/' . $dir] = $staticdir . $destinationdir;
400
401
        }

402
        foreach ($directoriestocopy as $from => $to) {
403
404
            if (!copyr($from, $to)) {
                throw new SystemException("Could not copy $from to $to");
405
406
            }
        }
407
408
409
410
411
412

        foreach ($filestocopy as $from => $to) {
            if (!copy($from, $to)) {
                throw new SystemException("Could not copy static file $from");
            }
        }
413
414
    }

415
416
417
418
419
420
421
422
423
424
425
426
427
}

abstract class HtmlExportArtefactPlugin {

    protected $exporter;

    protected $fileroot;

    public function __construct(PluginExportHTML $exporter) {
        $this->exporter = $exporter;
        $pluginname = strtolower(substr(get_class($this), strlen('HtmlExport')));
        $this->fileroot = $this->exporter->get('exportdir') . '/' . $this->exporter->get('rootdir') . '/files/' . $pluginname . '/';
        if (!check_dir_exists($this->fileroot)) {
Nigel McNie's avatar
Nigel McNie committed
428
            throw new SystemException("Could not create the temporary export directory $this->fileroot");
429
430
431
432
433
434
435
436
437
438
439
        }
    }

    abstract public function dump_export_data();

    abstract public function get_summary();

    abstract public function get_summary_weight();

}

440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
/**
 * Provides a mechanism for converting the HTML generated by views and 
 * artefacts for the HTML export.
 *
 * Mostly, this means rewriting links to artefacts to point the correct place 
 * in the export.
 */
class HtmlExportOutputFilter {

    /**
     * The relative path to the root of the generated export - used for link munging
     */
    private $basepath = '';

    /**
     * A cache of view titles. See replace_view_link()
     */
    private $viewtitles = array();

    /**
460
     * A cache of folder data. See get_folder_path_for_file()
461
462
463
     */
    private $folderdata = null;

464
465
466
467
    /**
     */
    private $htmlexportcopyproxy = null;

468
469
470
471
    /**
     */
    private $exporter = null;

472
473
474
    /**
     * @param string $basepath The relative path to the root of the generated export
     */
475
    public function __construct($basepath, &$exporter=null) {
476
        $this->basepath = preg_replace('#/$#', '', $basepath);
477
        $this->htmlexportcopyproxy = HtmlExportCopyProxy::singleton();
478
        $this->exporter = $exporter;
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
    }

    /**
     * Filters the given HTML for HTML export purposes
     *
     * @param string $html The HTML to filter
     * @return string      The filtered HTML
     */
    public function filter($html) {
        $wwwroot = preg_quote(get_config('wwwroot'));
        $html = preg_replace(
            array(
                // We don't care about javascript
                '#<script[^>]*>.*?</script>#si',
                // Fix simlies from tinymce
494
                '#<img ([^>]*)src="(' . $wwwroot . ')?/?js/tinymce/plugins/emotions/img/([^"]+)"([^>]+)>#',
495
496
                // No forms
                '#<form[^>]*>.*?</form>#si',
497
498
                // Gratuitous hack for the RSS blocktype
                '#<div id="blocktype_externalfeed_lastupdate">[^<]*</div>#',
499
500
501
            ),
            array(
                '',
502
                '<img $1src="' . $this->basepath . '/static/smilies/$3"$4>',
503
                '',
504
                '',
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
            ),
            $html
        );

        // Links to views
        $html = preg_replace_callback(
            '#' . $wwwroot . 'view/view\.php\?id=(\d+)#',
            array($this, 'replace_view_link'),
            $html
        );

        // Links to artefacts
        $html = preg_replace_callback(
            '#<a[^>]+href="(' . preg_quote(get_config('wwwroot')) . ')?/?view/artefact\.php\?artefact=(\d+)(&amp;view=\d+)?(&amp;page=\d+)?"[^>]*>([^<]*)</a>#',
            array($this, 'replace_artefact_link'),
            $html
        );

        // Links to download files
        $html = preg_replace_callback(
525
            '#(' . preg_quote(get_config('wwwroot')) . ')?/?artefact/file/download\.php\?file=(\d+)((&amp;[a-z]+=[x0-9]+)+)*#',
526
527
528
529
            array($this, 'replace_download_link'),
            $html
        );

530
        // Thumbnails
531
        require_once('file.php');
532
533
534
535
536
537
        $html = preg_replace_callback(
            '#(' . preg_quote(get_config('wwwroot')) . ')?/?thumb\.php\?type=([a-z]+)((&amp;[a-z]+=[x0-9]+)+)*#',
            array($this, 'replace_thumbnail_link'),
            $html
        );

538
539
540
541
542
543
544
        // Images out of the theme directory
        $html = preg_replace_callback(
            '#(' . preg_quote(get_config('wwwroot')) . ')?/?theme/' . get_config('theme') . '/static/images/([a-z0-9_.-]+)#',
            array($this, 'replace_theme_image_link'),
            $html
        );

545
546
547
548
549
550
551
552
553
        return $html;
    }

    /**
     * Callback to replace links to views to point to the correct location in 
     * the HTML export
     */
    private function replace_view_link($matches) {
        $viewid = $matches[1];
554
555
556
557
        // Don't rewrite links to views that are not going to be included in the export
        if (!isset($this->exporter->views[$viewid])) {
            return $matches[0];
        }
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
        if (!isset($this->viewtitles[$viewid])) {
            $this->viewtitles[$viewid] = PluginExportHtml::text_to_path(get_field('view', 'title', 'id', $viewid));
        }
        return $this->basepath . '/views/' . $this->viewtitles[$viewid] . '/index.html';
    }

    /**
     * Callback to replace links to artefact to point to the correct location 
     * in the HTML export
     */
    private function replace_artefact_link($matches) {
        $artefactid = $matches[2];
        $artefact = artefact_instance_from_id($artefactid);

        switch ($artefact->get('artefacttype')) {
        case 'blog':
            $page = ($matches[4]) ? intval(substr($matches[4], strlen('&amp;page='))) : 1;
            $page = ($page == 1) ? 'index' : $page;
            return '<a href="' . $this->basepath . '/files/blog/' . PluginExportHtml::text_to_path($artefact->get('title')) . '/' . $page . '.html">' . $matches[5] . '</a>';
        case 'file':
578
        case 'folder':
579
        case 'image':
580
        case 'archive':
581
            $folderpath = $this->get_folder_path_for_file($artefact);
582
            return '<a href="' . $this->basepath . '/files/file/' . $folderpath . PluginExportHtml::sanitise_path($artefact->get('title')) . '">' . $matches[5] . '</a>';
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
        default:
            return $matches[5];
        }
    }

    /**
     * Callback to replace links to artefact/file/download.php to point to the 
     * correct file in the HTML export
     */
    private function replace_download_link($matches) {
        $artefactid = $matches[2];
        $artefact = artefact_instance_from_id($artefactid);

        // If artefact type not something that would be served by download.php, 
        // replace link with nothing
        if ($artefact->get_plugin_name() != 'file') {
            return '';
        }

602
603
604
605
606
607
608
609
610
611
612
        $options = array();
        if (isset($matches[3])) {
            $parts = explode('&amp;', substr($matches[3], 5));
            foreach ($parts as $part) {
                list($key, $value) = explode('=', $part);
                $options[$key] = $value;
            }
        }

        $folderpath = $this->get_folder_path_for_file($artefact);
        return $this->get_export_path_for_file($artefact, $options, '/files/file/' . $folderpath);
613
614
615
    }

    /**
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
     * Callback to replace links to thumb.php to point to the correct file in 
     * the HTML export
     */
    private function replace_thumbnail_link($matches) {
        if (isset($matches[3])) {
            $type = $matches[2];

            $parts = explode('&amp;', substr($matches[3], 5));
            foreach ($parts as $part) {
                list($key, $value) = explode('=', $part);
                $options[$key] = $value;
            }

            if (!isset($options['id'])) {
                return '';
            }

            switch ($type) {
            case 'profileicon':
                // Convert the user ID to a profile icon ID
                if (!$options['id'] = get_field_sql('SELECT profileicon FROM {usr} WHERE id = ?', array($options['id']))) {
637
638
639
                    // No profile icon, get the default one
                    list($size, $prefix) = $this->get_size_from_options($options);
                    if ($from = get_dataroot_image_path('artefact/file/profileicons/no_userphoto/' . get_config('theme'), 0, $size)) {
640
                        $to = '/static/profileicons/0-' . $prefix . 'no_userphoto.png';
641
642
643
                        $this->htmlexportcopyproxy->add($from, $to);
                    }
                    return $this->basepath . $to;
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
                }
            case 'profileiconbyid':
                $icon = artefact_instance_from_id($options['id']);
                if ($icon->get_plugin_name() != 'file') {
                    return '';
                }
                $folderpath = $this->get_folder_path_for_file($icon);
                return $this->get_export_path_for_file($icon, $options, '/static/profileicons/');
            default:
                return '';
            }
        }

        return '';
    }

660
661
662
663
664
665
666
667
668
669
670
671
    /**
     * Callback
     */
    private function replace_theme_image_link($matches) {
        $file = '/theme/' . get_config('theme') . '/static/images/' . $matches[2];
        $this->htmlexportcopyproxy->add(
            get_config('docroot') . $file,
            '/static/' . $file
        );
        return $this->basepath . '/static/' . $file;
    }

672
673
    /**
     * Given a file, returns the folder path for it in the Mahara files area
674
     *
675
     * The path is pre-sanitised so it can be used when generating the export
676
     *
677
     * @param  $file The file or folder to get the folder path for
678
679
     * @return string
     */
680
    private function get_folder_path_for_file($file) {
681
682
        if ($this->folderdata === null) {
            $this->folderdata = get_records_select_assoc('artefact', "artefacttype = 'folder' AND owner = ?", array($file->get('owner')));
683
684
685
686
            if ($this->folderdata) {
                foreach ($this->folderdata as &$folder) {
                    $folder->title = PluginExportHtml::sanitise_path($folder->title);
                }
687
            }
688
689
690
691
692
        }
        $folderpath = ArtefactTypeFileBase::get_full_path($file->get('parent'), $this->folderdata);
        return $folderpath;
    }

693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
    /**
     * Generates a path, relative to the root of the export, that the given 
     * file will appear in the export.
     *
     * If the file is a thumbnail, the copy proxy is informed about it so that 
     * the image can later be copied in to place.
     *
     * @param ArtefactTypeFileBase $file The file to get the exported path for
     * @param array $options             Options from the URL that was linking 
     *                                   to the image - most importantly, size 
     *                                   related options about how the image 
     *                                   was thumbnailed, if it was.
     * @param string $basefolder         What folder in the export to dump the 
     *                                   file in
     * @return string                    The relative path to where the file 
     *                                   will be placed
     */
    private function get_export_path_for_file(ArtefactTypeFileBase $file, array $options, $basefolder) {
        unset($options['view']);
        $prefix = '';
        if ($options) {
714
            list($size, $prefix) = $this->get_size_from_options($options);
715
716
            $from = $file->get_path($size);

717
            $to = $basefolder . $file->get('id') . '-' . $prefix . PluginExportHtml::sanitise_path($file->get('title'));
718
719
720
721
722
723
724
725
726
            $this->htmlexportcopyproxy->add($from, $to);
        }
        else {
            $to = $basefolder . PluginExportHtml::sanitise_path($file->get('title'));
        }

        return $this->basepath . $to;
    }

727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
    /**
     * Helper method
     */
    private function get_size_from_options($options) {
        $prefix = '';
        foreach (array('size', 'width', 'height', 'maxsize', 'maxwidth', 'maxheight') as $param) {
            if (isset($options[$param])) {
                $$param = $options[$param];
                $prefix .= $param . '-' . $options[$param] . '-';
            }
            else {
                $$param = null;
            }
        }

        return array(imagesize_data_to_internal_form($size, $width, $height, $maxsize, $maxwidth, $maxheight), $prefix);
    }

745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
}

/**
 * Gathers a list of files that need to be copied into the export, as they're 
 * found by the HtmlExportOutputFilter
 */
class HtmlExportCopyProxy {

    private static $instance = null;
    private $copy = array();

    private function __construct() {
    }

    public static function singleton() {
        if (is_null(self::$instance)) {
            self::$instance = new HtmlExportCopyProxy();
        }
        return self::$instance;
    }

    public function add($from, $to) {
        $this->copy[$from] = $to;
    }

    public function get_copy_data() {
        return $this->copy;
    }
773
774
}

775
?>