lib.php 18.3 KB
Newer Older
1
2
3
4
5
<?php
/**
 *
 * @package    mahara
 * @subpackage blocktype-externalvideo
6
 * @author     Catalyst IT Ltd
7
 * @author     Gregor Anzelj
8
9
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
 * @copyright  For copyright information on Mahara, please see the README file distributed with this software.
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 */

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

/**
 * todos before this block type can be considered complete
 *  - document this class and methods
 *  - correct category
 *  - more video url sources, and good default behaviour
 *  - block title editable
 *  - i18n
 *  - minvalue/maxvalue rules
 */
24
class PluginBlocktypeExternalvideo extends MaharaCoreBlocktype {
25

26
27
28
29
30
31
32
33
34
35
    private static $media_sources = array(
        'youtube',
        'teachertube',
        'scivee',
        'googlevideo',
        'glogster',
        'slideshare',
        'voicethread',
        'wikieducator',
        'prezi',
36
        'vimeo',
37
        'voki',
38
    );
39

40
41
42
43
    private static $embed_services = array(
        'embedly',
    );

44
45
46
47
48
49
50
51
52
    public static function get_title() {
        return get_string('title', 'blocktype.externalvideo');
    }

    public static function get_description() {
        return get_string('description', 'blocktype.externalvideo');
    }

    public static function get_categories() {
53
        return array('external' => 35000);
54
55
    }

56
57
58
59
60
61
62
63
64
65
    private static function load_media_sources() {
        static $loaded_sources = array();

        if (!empty($loaded_sources)) {
            return $loaded_sources;
        }

        foreach (self::$media_sources as $source) {
            include_once('media_sources/' . $source . '/mediasource.php');
            $sourcename = 'Media_' . $source;
66
67
68
69
70
71
72
73
74
            $mediasource = new $sourcename;
            // Any iframe output from these media sources must be
            // checked against the site-wide allowed iframe sources.
            // If a media source can only convert urls into iframes
            // that are going to be stripped, leave it out of the
            // list.
            if ($mediasource->enabled()) {
                $loaded_sources[$source] = $mediasource;
            }
75
76
77
78
        }
        return $loaded_sources;
    }

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
    private static function load_embed_services() {
        static $loaded_services = array();

        if (!empty($loaded_services)) {
            return $loaded_services;
        }

        foreach (self::$embed_services as $service) {
            include_once('embed_services/' . $service . '/embedservice.php');
            $servicename = 'Embed_' . $service;
            $embedservice = new $servicename;
            if ($embedservice->enabled()) {
                $loaded_services[$service] = $embedservice;
            }
        }
        return $loaded_services;
    }

97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
    public static function embed_code($url, $width, $height) {
        $width = (int) $width;
        $height = (int) $height;
        $url = hsc($url);
        return '<object width="' . $width . '" height="' . $height . '">'
            . '<param name="movie" value="' . $url . '"></param>'
            . '<param name="wmode" value="transparent"></param>'
            . '<param name="allowscriptaccess" value="never"></param>'
            . '<embed src="' . $url . '" '
            . 'type="application/x-shockwave-flash" wmode="transparent" width="' . $width . '" '
            . 'height="' . $height . '" allowscriptaccess="never"></embed></object>';
    }

    public static function iframe_code($url, $width, $height) {
        $width = (int) $width;
        $height = (int) $height;
        $url = hsc($url);
114
        return '<iframe class="externalvideoiframe" width="' . $width . '" height="' . $height . '" src="' . $url . '" allowfullscreen="1"></iframe>';
115
116
    }

117
118
119
120
    public static function get_blocktype_type_content_types() {
        return array('externalvideo' => array('media'));
    }

121
    public static function render_instance(BlockInstance $instance, $editing=false, $versioning=false) {
122
123
        global $THEME;

124
        $configdata = $instance->get('configdata');
125
126
        $width  = (!empty($configdata['width'])) ? hsc($configdata['width']) : 0;
        $height = (!empty($configdata['height'])) ? hsc($configdata['height']) : 0;
127

128
129
130
        if (!isset($configdata['html'])) {
            if (!isset($configdata['videoid'])) {
                return '';
131
132
            }

133
134
            // This is a legacy block where videoid contains only a url, so generate embed/iframe code.
            $url = $configdata['videoid'];
135
            if (isset($configdata['type']) && $configdata['type'] == 'embed') {
136
137
                $configdata['html'] = $configdata['videoid'] = self::embed_code($url, $width, $height);
                unset($configdata['type']);
138
139
            }
            else if (isset($configdata['type']) && $configdata['type'] == 'iframe') {
140
141
                $configdata['html'] = $configdata['videoid'] = self::iframe_code($url, $width, $height);
                unset($configdata['type']);
142
            }
143
144
            else if ($urldata = self::process_url($url, $width, $height)) {
                $configdata = $urldata;
145
            }
146
147
            else {
                $configdata['html'] = ''; // We can't do anything with this url
148
            }
149
150
151
152
            $instance->set('configdata', $configdata);
            $instance->commit();
        }

153
154
155
156
157
158
159
        // This is block that contains embed/iframe code from embed_service
        if (isset($configdata['embed']) && !empty($configdata['embed'])) {
            $service = $configdata['embed']['service'];
            include_once('embed_services/' . $service . '/embedservice.php');
            $servicename = 'Embed_' . $service;
            $embedservice = new $servicename;
            return $embedservice->embed_content($configdata['embed']);
160
        }
161
162

        $smarty = smarty_core();
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
        // don't load html for auto retracted blocks to speed up page load time
        if (!empty($configdata['retractedonload']) && !$editing) {
            $smarty->assign('html', '<div id="block_' . $instance->get('id') . '_waiting">' . get_string('loading', 'mahara') . '</div>');
            $is_src = preg_match('/src=\"(.*?)\"/', $configdata['html'], $src);
            $is_width = preg_match('/ width=\"(.*?)\"/', $configdata['html'], $widthmatch);
            $is_height = preg_match('/ height=\"(.*?)\"/', $configdata['html'], $heightmatch);
            if ($is_src) {
                $smarty->assign('jsurl', $src[1]);
                // check if is embed rather than iframe
                $is_flashvars = preg_match('/flashvars=\"(.*?)\"/', $configdata['html'], $flashvars);
                if ($is_flashvars) {
                    $smarty->assign('jsflashvars', $flashvars[1]);
                }
                if (empty($width) && !empty($widthmatch)) {
                    $width = $widthmatch[1];
                }
                if (empty($height) && !empty($heightmatch)) {
                    $height = $heightmatch[1];
                }
            }
            else {
                // need to fall back to handling this normally
                $smarty->assign('html', $configdata['html']);
            }
        }
        else {
            $smarty->assign('html', $configdata['html']);
        }
191
192
193
        $smarty->assign('width', $width);
        $smarty->assign('height', $height);
        $smarty->assign('blockid', $instance->get('id'));
194

195
        return $smarty->fetch('blocktype:externalvideo:content.tpl');
196
197
198
199
200
201
    }

    public static function has_instance_config() {
        return true;
    }

Son Nguyen's avatar
Son Nguyen committed
202
    public static function instance_config_form(BlockInstance $instance) {
203
        $configdata = $instance->get('configdata');
204

205
206
        return array(
            'videoid' => array(
207
208
209
                'type'  => 'textarea',
                'title' => get_string('urlorembedcode', 'blocktype.externalvideo'),
                'description' => get_string('videourldescription3', 'blocktype.externalvideo') .
210
                    '<br />' . get_string('validiframesites', 'blocktype.externalvideo') . ' ' . self::get_valid_iframe_html() .'<br />'.
211
212
                    get_string('validurlsites', 'blocktype.externalvideo') . ' ' . self::get_valid_url_html() .'<br />'.
                    get_string('validembedservices', 'blocktype.externalvideo') . ' ' . self::get_valid_services_html(),
213
214
                'cols' => '60',
                'rows' => '3',
215
                'defaultvalue' => isset($configdata['videoid']) ? $configdata['videoid'] : null,
216
                'rules' => array(
217
                    'required' => true,
218
219
220
221
                ),
            ),
            'width' => array(
                'type' => 'text',
Clare Lenihan's avatar
Clare Lenihan committed
222
                'title' => get_string('width','blocktype.externalvideo'),
223
                'size' => 3,
224
                'rules' => array(
225
226
227
                    'regex'  => '#\d+%?#',
                    'minvalue' => 0,
                    'maxvalue' => 2000,
228
                ),
229
                'defaultvalue' => (!empty($configdata['width'])) ? $configdata['width'] : 0,
230
231
232
            ),
            'height' => array(
                'type' => 'text',
Clare Lenihan's avatar
Clare Lenihan committed
233
                'title' => get_string('height','blocktype.externalvideo'),
234
                'description' => get_string('widthheightdescription', 'blocktype.externalvideo'),
235
                'size' => 3,
236
                'rules' => array(
237
238
239
                    'regex'  => '#\d+%?#',
                    'minvalue' => 0,
                    'maxvalue' => 2000,
240
                ),
241
                'defaultvalue' => (!empty($configdata['height'])) ? $configdata['height'] : 0,
242
            ),
243
244
245
246
247
248
249
            'tags'  => array(
                'type'         => 'tags',
                'title'        => get_string('tags'),
                'description'  => get_string('tagsdescblock'),
                'defaultvalue' => $instance->get('tags'),
                'help'         => false,
            )
250
251
252
        );
    }

253
    public static function instance_config_validate(Pieform $form, $values) {
254
        $content = trim($values['videoid']);
255

256
        if (!filter_var($content, FILTER_VALIDATE_URL)) {
257
            // Not a valid url, so assume it's embed code so check that it's within a tag
258
            if (!preg_match('/^\<.*\>$/sm', $content)) {
259
260
261
                $form->set_error('videoid', get_string('invalidurlorembed', 'blocktype.externalvideo'), false);
            }
            // And if so let it go through to htmlpurifier.
262
263
264
265
266
267
268
269
270
271
            return;
        }

        // The user entered a valid url, so check whether any of the
        // media_sources want to try and generate embed/iframe code.
        $sources = self::load_media_sources();

        foreach ($sources as $name => $source) {
            if ($source->validate_url($content)) {
                return;
272
273
            }
        }
274

275
276
277
278
279
280
281
282
283
284
        // The user entered a valid url, so check whether any of the
        // embed_services want to try and generate embed/iframe code.
        $services = self::load_embed_services();

        foreach ($services as $name => $service) {
            if ($service->validate_url($content)) {
                return;
            }
        }

285
        // Nothing recognised this url.
286
        $form->set_error('videoid', get_string('invalidurl', 'blocktype.externalvideo'), false);
287
288
    }

289
    public static function instance_config_save($values) {
290
291
292
293
294
        $values['title']   = trim($values['title']);
        $values['videoid'] = trim($values['videoid']);

        if (!filter_var($values['videoid'], FILTER_VALIDATE_URL)) {
            // Not a url, treat the input as html to be sanitised when rendered.
295
296
            $httpstr = is_https() ? 'https' : 'http';
            $values['videoid'] = preg_replace('#https?://#', $httpstr . '://', $values['videoid']);
297
            $values['html'] = $values['videoid'];
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316

            // Process user entered embed/iframe code from embed_service.
            $services = self::load_embed_services();

            foreach ($services as $name => $service) {
                if ($data = $service->process_content($values['videoid'])) {
                    // Override width set in embed/iframe code
                    if ($values['width']) {
                        $data['width'] = $values['width'];
                    }
                    // Override height set in embed/iframe code
                    if ($values['height']) {
                        $data['height'] = $values['height'];
                    }
                    $values['embed'] = $data;
                    break;
                }
            }

317
318
319
320
321
322
323
324
325
            return $values;
        }
        // If it's an unrecognised url, do nothing.
        if (!$urldata = self::process_url($values['videoid'], $values['width'], $values['height'])) {
            return $values;
        }

        // $urldata should now contain html
        return array_merge($values, $urldata);
326
327
    }

328
    public static function process_url($url, $width=0, $height=0) {
329
        $sources = self::load_media_sources();
330

331
332
        foreach ($sources as $name => $source) {
            if ($result = $source->process_url($url, $width, $height)) {
333
334
335
336
337
338
339
340
341
342
343
344
345
346
                if ($result['type'] == 'embed') {
                    $result['html'] = self::embed_code($result['videoid'], $result['width'], $result['height']);
                }
                else if ($result['type'] == 'iframe') {
                    $result['html'] = self::iframe_code($result['videoid'], $result['width'], $result['height']);
                }
                else {
                    throw new SystemException('externalvideo block: invalid embed type for url');
                }

                // From now on, forget the url and just use the embed/iframe code as html content
                unset($result['type']);
                $result['videoid'] = $result['html'];

347
                return $result;
348
349
            }
        }
350
351
352
353
354
355
356
357
358
359

        // Try with embed services
        $services = self::load_embed_services();

        foreach ($services as $name => $service) {
            if ($result = $service->process_url($url, $width, $height)) {
                return $result;
            }
        }

360
        return false;
361
    }
362
363

    /**
364
365
     * Returns a block of HTML that the external video block can use to show the
     * sites for which we will process URLs.
366
     */
367
    private static function get_valid_url_html() {
368
        $source_instances = self::load_media_sources();
369
        $wwwroot = get_config('wwwroot');
370

371
        $data = array();
372
373
        foreach ($source_instances as $name => $source) {
            $sourcestr = get_string($name, 'blocktype.externalvideo');
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
            $data[$sourcestr] = array(
                'name' => $sourcestr,
                'url'  => $source->get_base_url(),
                'icon' => $wwwroot . 'blocktype/externalvideo/media_sources/' . $name . '/favicon.png',
            );
        }

        ksort($data);

        $smarty = smarty_core();
        $smarty->assign('data', $data);
        return $smarty->fetch('blocktype:externalvideo:sitelist.tpl');
    }

    /**
     * Returns a block of HTML that the external video block can use to show the
     * sites for which iframes are allowed.
     */
    private static function get_valid_iframe_html() {
        $iframedomains = get_records_menu('iframe_source_icon', '', '', 'name');
        if (empty($iframedomains)) {
            return '';
        }

        $data = array();
        foreach ($iframedomains as $name => $host) {
            $data[$name] = array(
                'name' => $name,
                'url'  => 'http://' . $host,
                'icon' => favicon_display_url($host),
            );
405
406
        }

407
408
409
        $smarty = smarty_core();
        $smarty->assign('data', $data);
        return $smarty->fetch('blocktype:externalvideo:sitelist.tpl');
410
    }
411

412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
    /**
     * Returns a block of HTML that the external video block can use to show the
     * embed services (e.g. Embed.ly) which can be used to process URLs.
     */
    private static function get_valid_services_html() {
        global $USER;
        $service_instances = self::load_embed_services();
        $wwwroot = get_config('wwwroot');

        $data = array();
        $nodata = '';
        if (empty($service_instances)) {
            if ($USER->get('admin')) {
                $nodata = get_string('enableservices', 'blocktype.externalvideo', '<a href="' . $wwwroot . 'admin/extensions/pluginconfig.php?plugintype=blocktype&pluginname=externalvideo">', '</a>');
            }
            else {
                $nodata = get_string('none');
            }
        }
        else {
            foreach ($service_instances as $name => $service) {
                $servicestr = get_string($name, 'blocktype.externalvideo');
                $data[$servicestr] = array(
                    'name' => $servicestr,
                    'url'  => $service->get_base_url(),
                    'icon' => $wwwroot . 'blocktype/externalvideo/embed_services/' . $name . '/favicon.png',
                );
            }
        }

        $smarty = smarty_core();
        $smarty->assign('data', $data);
        $smarty->assign('nodata', $nodata);
        return $smarty->fetch('blocktype:externalvideo:servicelist.tpl');
    }

448
449
450
451
    public static function default_copy_type() {
        return 'full';
    }

452
453
454
455
    public static function postinst($prevversion) {
        if ($prevversion == 0) {
            ensure_record_exists('iframe_source_icon', (object) array('name' => 'Prezi', 'domain' => 'prezi.com'), (object) array('name' => 'Prezi', 'domain' => 'prezi.com'));
            ensure_record_exists('iframe_source', (object) array('prefix' => 'prezi.com/embed/', 'name' => 'Prezi'), (object) array('prefix' => 'prezi.com/embed/', 'name' => 'Prezi'));
456
457
            ensure_record_exists('iframe_source_icon', (object) array('name' => 'Youtube [privacy mode]', 'domain' => 'www.youtube.com'), (object) array('name' => 'Youtube [privacy mode]', 'domain' => 'www.youtube.com'));
            ensure_record_exists('iframe_source', (object) array('prefix' => 'www.youtube-nocookie.com/embed/', 'name' => 'Youtube [privacy mode]'), (object) array('prefix' => 'www.youtube-nocookie.com/embed/', 'name' => 'Youtube [privacy mode]'));
458
459
460
461
            update_safe_iframe_regex();
        }
    }

462
463
464
465
466
467
468
469
470
    /**
     * Shouldn't be linked to any artefacts via the view_artefacts table.
     *
     * @param BlockInstance $instance
     * @return multitype:
     */
    public static function get_artefacts(BlockInstance $instance) {
        return array();
    }
471
472
473
474
475
476
477
478

    public static function get_instance_javascript(BlockInstance $bi) {
        return array(
            array(
                'file'   => 'js/voki.js',
            )
        );
    }
479
}