MDL-30005 URL : fixed typos
[moodle.git] / mod / url / locallib.php
CommitLineData
58a27a74 1<?php
2
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/>.
17
18/**
19 * Private url module utility functions
20 *
2b641d15
PS
21 * @package mod
22 * @subpackage url
23 * @copyright 2009 Petr Skoda {@link http://skodak.org}
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
58a27a74 25 */
26
2b641d15
PS
27defined('MOODLE_INTERNAL') || die;
28
58a27a74 29require_once("$CFG->libdir/filelib.php");
30require_once("$CFG->libdir/resourcelib.php");
31require_once("$CFG->dirroot/mod/url/lib.php");
32
48f69e41
PS
33/**
34 * This methods does weak url validation, we are looking for major problems only,
b2851c8d 35 * no strict RFE validation.
48f69e41
PS
36 *
37 * @param $url
38 * @return bool true is seems valid, false if definitely not valid URL
39 */
40function url_appears_valid_url($url) {
41 if (preg_match('/^(\/|https?:|ftp:)/i', $url)) {
42 // note: this is not exact validation, we look for severely malformed URLs only
43 return preg_match('/^[a-z]+:\/\/([^:@\s]+:[^@\s]+@)?[a-z0-9_\.\-]+(:[0-9]+)?(\/[^#]*)?(#.*)?$/i', $url);
44 } else {
45 return preg_match('/^[a-z]+:\/\/...*$/i', $url);
46 }
47}
48
49/**
50 * Fix common URL problems that we want teachers to see fixed
51 * the next time they edit the resource.
52 *
53 * This function does not include any XSS protection.
54 *
55 * @param string $url
56 * @return string
57 */
58function url_fix_submitted_url($url) {
59 // note: empty urls are prevented in form validation
60 $url = trim($url);
61
62 // remove encoded entities - we want the raw URI here
63 $url = html_entity_decode($url, ENT_QUOTES, 'UTF-8');
64
65 if (!preg_match('|^[a-z]+:|i', $url) and !preg_match('|^/|', $url)) {
66 // invalid URI, try to fix it by making it normal URL,
67 // please note relative urls are not allowed, /xx/yy links are ok
68 $url = 'http://'.$url;
69 }
70
71 return $url;
72}
73
58a27a74 74/**
75 * Return full url with all extra parameters
48f69e41
PS
76 *
77 * This function does not include any XSS protection.
78 *
58a27a74 79 * @param string $url
80 * @param object $cm
81 * @param object $course
48f69e41
PS
82 * @param object $config
83 * @return string url with & encoded as &amp;
58a27a74 84 */
85function url_get_full_url($url, $cm, $course, $config=null) {
86
87 $parameters = empty($url->parameters) ? array() : unserialize($url->parameters);
88
48f69e41
PS
89 // make sure there are no encoded entities, it is ok to do this twice
90 $fullurl = html_entity_decode($url->externalurl, ENT_QUOTES, 'UTF-8');
58a27a74 91
48f69e41
PS
92 if (preg_match('/^(\/|https?:|ftp:)/i', $fullurl) or preg_match('|^/|', $url)) {
93 // encode extra chars in URLs - this does not make it always valid, but it helps with some UTF-8 problems
94 $allowed = "a-zA-Z0-9".preg_quote(';/?:@=&$_.+!*(),-#%', '/');
95 $fullurl = preg_replace_callback("/[^$allowed]/", 'url_filter_callback', $fullurl);
96 } else {
97 // encode special chars only
98 $fullurl = str_replace('"', '%22', $fullurl);
99 $fullurl = str_replace('\'', '%27', $fullurl);
100 $fullurl = str_replace(' ', '%20', $fullurl);
101 $fullurl = str_replace('<', '%3C', $fullurl);
102 $fullurl = str_replace('>', '%3E', $fullurl);
58a27a74 103 }
58a27a74 104
48f69e41
PS
105 // add variable url parameters
106 if (!empty($parameters)) {
107 if (!$config) {
108 $config = get_config('url');
109 }
110 $paramvalues = url_get_variable_values($url, $cm, $course, $config);
111
112 foreach ($parameters as $parse=>$parameter) {
113 if (isset($paramvalues[$parameter])) {
114 $parameters[$parse] = rawurlencode($parse).'='.rawurlencode($paramvalues[$parameter]);
115 } else {
116 unset($parameters[$parse]);
117 }
58a27a74 118 }
58a27a74 119
48f69e41
PS
120 if (!empty($parameters)) {
121 if (stripos($fullurl, 'teamspeak://') === 0) {
122 $fullurl = $fullurl.'?'.implode('?', $parameters);
123 } else {
124 $join = (strpos($fullurl->externalurl, '?') === false) ? '?' : '&';
125 $fullurl = $fullurl.$join.implode('&', $parameters);
126 }
127 }
58a27a74 128 }
129
48f69e41
PS
130 // encode all & to &amp; entity
131 $fullurl = str_replace('&', '&amp;', $fullurl);
132
133 return $fullurl;
134}
135
136/**
137 * Unicode encoding helper callback
138 * @internal
139 * @param array $matches
140 * @return string
141 */
142function url_filter_callback($matches) {
143 return rawurlencode($matches[0]);
58a27a74 144}
145
146/**
147 * Print url header.
148 * @param object $url
149 * @param object $cm
150 * @param object $course
151 * @return void
152 */
153function url_print_header($url, $cm, $course) {
154 global $PAGE, $OUTPUT;
155
156 $PAGE->set_title($course->shortname.': '.$url->name);
157 $PAGE->set_heading($course->fullname);
158 $PAGE->set_activity_record($url);
90e9e580 159 echo $OUTPUT->header();
58a27a74 160}
161
162/**
163 * Print url heading.
164 * @param object $url
165 * @param object $cm
166 * @param object $course
167 * @param bool $ignoresettings print even if not specified in modedit
168 * @return void
169 */
170function url_print_heading($url, $cm, $course, $ignoresettings=false) {
171 global $OUTPUT;
172
173 $options = empty($url->displayoptions) ? array() : unserialize($url->displayoptions);
174
175 if ($ignoresettings or !empty($options['printheading'])) {
176 echo $OUTPUT->heading(format_string($url->name), 2, 'main', 'urlheading');
177 }
178}
179
180/**
181 * Print url introduction.
182 * @param object $url
183 * @param object $cm
184 * @param object $course
185 * @param bool $ignoresettings print even if not specified in modedit
186 * @return void
187 */
188function url_print_intro($url, $cm, $course, $ignoresettings=false) {
189 global $OUTPUT;
190
191 $options = empty($url->displayoptions) ? array() : unserialize($url->displayoptions);
192 if ($ignoresettings or !empty($options['printintro'])) {
193 if (trim(strip_tags($url->intro))) {
194 echo $OUTPUT->box_start('mod_introbox', 'urlintro');
195 echo format_module_intro('url', $url, $cm->id);
196 echo $OUTPUT->box_end();
197 }
198 }
199}
200
201/**
202 * Display url frames.
203 * @param object $url
204 * @param object $cm
205 * @param object $course
206 * @return does not return
207 */
208function url_display_frame($url, $cm, $course) {
209 global $PAGE, $OUTPUT, $CFG;
210
211 $frame = optional_param('frameset', 'main', PARAM_ALPHA);
212
213 if ($frame === 'top') {
78946b9b 214 $PAGE->set_pagelayout('frametop');
58a27a74 215 url_print_header($url, $cm, $course);
216 url_print_heading($url, $cm, $course);
217 url_print_intro($url, $cm, $course);
218 echo $OUTPUT->footer();
219 die;
220
221 } else {
222 $config = get_config('url');
223 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
5e21cac8 224 $exteurl = url_get_full_url($url, $cm, $course, $config);
58a27a74 225 $navurl = "$CFG->wwwroot/mod/url/view.php?id=$cm->id&amp;frameset=top";
8ebbb06a
SH
226 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
227 $courseshortname = format_string($course->shortname, true, array('context' => $coursecontext));
228 $title = strip_tags($courseshortname.': '.format_string($url->name));
58a27a74 229 $framesize = $config->framesize;
230 $modulename = s(get_string('modulename','url'));
e372f4c7 231 $dir = get_string('thisdirection', 'langconfig');
58a27a74 232
233 $extframe = <<<EOF
234<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
235<html dir="$dir">
236 <head>
237 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
238 <title>$title</title>
239 </head>
240 <frameset rows="$framesize,*">
241 <frame src="$navurl" title="$modulename"/>
242 <frame src="$exteurl" title="$modulename"/>
243 </frameset>
244</html>
245EOF;
246
247 @header('Content-Type: text/html; charset=utf-8');
248 echo $extframe;
249 die;
250 }
251}
252
253/**
254 * Print url info and link.
255 * @param object $url
256 * @param object $cm
257 * @param object $course
258 * @return does not return
259 */
260function url_print_workaround($url, $cm, $course) {
261 global $OUTPUT;
262
263 url_print_header($url, $cm, $course);
264 url_print_heading($url, $cm, $course, true);
265 url_print_intro($url, $cm, $course, true);
266
267 $fullurl = url_get_full_url($url, $cm, $course);
268
269 $display = url_get_final_display_type($url);
270 if ($display == RESOURCELIB_DISPLAY_POPUP) {
48f69e41 271 $jsfullurl = addslashes_js($fullurl);
58a27a74 272 $options = empty($url->displayoptions) ? array() : unserialize($url->displayoptions);
273 $width = empty($options['popupwidth']) ? 620 : $options['popupwidth'];
274 $height = empty($options['popupheight']) ? 450 : $options['popupheight'];
275 $wh = "width=$width,height=$height,toolbar=no,location=no,menubar=no,copyhistory=no,status=no,directories=no,scrollbars=yes,resizable=yes";
48f69e41 276 $extra = "onclick=\"window.open('$jsfullurl', '', '$wh'); return false;\"";
58a27a74 277
278 } else if ($display == RESOURCELIB_DISPLAY_NEW) {
279 $extra = "onclick=\"this.target='_blank';\"";
280
281 } else {
282 $extra = '';
283 }
284
285 echo '<div class="urlworkaround">';
286 print_string('clicktoopen', 'url', "<a href=\"$fullurl\" $extra>$fullurl</a>");
287 echo '</div>';
288
289 echo $OUTPUT->footer();
290 die;
291}
292
293/**
294 * Display embedded url file.
295 * @param object $url
296 * @param object $cm
297 * @param object $course
58a27a74 298 * @return does not return
299 */
300function url_display_embed($url, $cm, $course) {
d34c1e95 301 global $CFG, $PAGE, $OUTPUT;
58a27a74 302
58a27a74 303 $mimetype = resourcelib_guess_url_mimetype($url->externalurl);
304 $fullurl = url_get_full_url($url, $cm, $course);
305 $title = $url->name;
306
5e21cac8
PS
307 $link = html_writer::tag('a', $fullurl, array('href'=>str_replace('&amp;', '&', $fullurl)));
308 $clicktoopen = get_string('clicktoopen', 'url', $link);
309
fcd2cbaf
PS
310 $extension = resourcelib_get_extension($url->externalurl);
311
58a27a74 312 if (in_array($mimetype, array('image/gif','image/jpeg','image/png'))) { // It's an image
313 $code = resourcelib_embed_image($fullurl, $title);
314
315 } else if ($mimetype == 'audio/mp3') {
316 // MP3 audio file
317 $code = resourcelib_embed_mp3($fullurl, $title, $clicktoopen);
318
fcd2cbaf 319 } else if ($mimetype == 'video/x-flv' or $extension === 'f4v') {
58a27a74 320 // Flash video file
321 $code = resourcelib_embed_flashvideo($fullurl, $title, $clicktoopen);
322
323 } else if ($mimetype == 'application/x-shockwave-flash') {
324 // Flash file
325 $code = resourcelib_embed_flash($fullurl, $title, $clicktoopen);
326
327 } else if (substr($mimetype, 0, 10) == 'video/x-ms') {
328 // Windows Media Player file
329 $code = resourcelib_embed_mediaplayer($fullurl, $title, $clicktoopen);
330
331 } else if ($mimetype == 'video/quicktime') {
332 // Quicktime file
333 $code = resourcelib_embed_quicktime($fullurl, $title, $clicktoopen);
334
335 } else if ($mimetype == 'video/mpeg') {
336 // Mpeg file
337 $code = resourcelib_embed_mpeg($fullurl, $title, $clicktoopen);
338
bb802453 339 } else if ($mimetype == 'audio/x-pn-realaudio-plugin') {
58a27a74 340 // RealMedia file
341 $code = resourcelib_embed_real($fullurl, $title, $clicktoopen);
342
343 } else {
344 // anything else - just try object tag enlarged as much as possible
345 $code = resourcelib_embed_general($fullurl, $title, $clicktoopen, $mimetype);
58a27a74 346 }
347
348 url_print_header($url, $cm, $course);
349 url_print_heading($url, $cm, $course);
350
351 echo $code;
352
353 url_print_intro($url, $cm, $course);
354
d34c1e95 355 echo $OUTPUT->footer();
58a27a74 356 die;
357}
358
359/**
48f69e41 360 * Decide the best display format.
58a27a74 361 * @param object $url
362 * @return int display type constant
363 */
364function url_get_final_display_type($url) {
365 global $CFG;
366
367 if ($url->display != RESOURCELIB_DISPLAY_AUTO) {
368 return $url->display;
369 }
370
371 // detect links to local moodle pages
372 if (strpos($url->externalurl, $CFG->wwwroot) === 0) {
373 if (strpos($url->externalurl, 'file.php') === false and strpos($url->externalurl, '.php') !== false ) {
374 // most probably our moodle page with navigation
375 return RESOURCELIB_DISPLAY_OPEN;
376 }
377 }
378
379 static $download = array('application/zip', 'application/x-tar', 'application/g-zip', // binary formats
380 'application/pdf', 'text/html'); // these are known to cause trouble for external links, sorry
381 static $embed = array('image/gif', 'image/jpeg', 'image/png', 'image/svg+xml', // images
382 'application/x-shockwave-flash', 'video/x-flv', 'video/x-ms-wm', // video formats
fcd2cbaf 383 'video/quicktime', 'video/mpeg', 'video/mp4',
58a27a74 384 'audio/mp3', 'audio/x-realaudio-plugin', 'x-realaudio-plugin', // audio formats,
385 );
386
7774aca0 387 $mimetype = resourcelib_guess_url_mimetype($url->externalurl);
58a27a74 388
389 if (in_array($mimetype, $download)) {
390 return RESOURCELIB_DISPLAY_DOWNLOAD;
391 }
392 if (in_array($mimetype, $embed)) {
393 return RESOURCELIB_DISPLAY_EMBED;
394 }
395
396 // let the browser deal with it somehow
397 return RESOURCELIB_DISPLAY_OPEN;
398}
399
400/**
401 * Get the parameters that may be appended to URL
402 * @param object $config url module config options
403 * @return array array describing opt groups
404 */
405function url_get_variable_options($config) {
406 global $CFG;
407
408 $options = array();
409 $options[''] = array('' => get_string('chooseavariable', 'url'));
410
411 $options[get_string('course')] = array(
412 'courseid' => 'id',
413 'coursefullname' => get_string('fullnamecourse'),
414 'courseshortname' => get_string('shortnamecourse'),
415 'courseidnumber' => get_string('idnumbercourse'),
416 'coursesummary' => get_string('summary'),
417 'courseformat' => get_string('format'),
418 );
419
420 $options[get_string('modulename', 'url')] = array(
421 'urlinstance' => 'id',
422 'urlcmid' => 'cmid',
423 'urlname' => get_string('name'),
424 'urlidnumber' => get_string('idnumbermod'),
425 );
426
427 $options[get_string('miscellaneous')] = array(
428 'sitename' => get_string('fullsitename'),
36c3fbe3 429 'serverurl' => get_string('serverurl', 'url'),
58a27a74 430 'currenttime' => get_string('time'),
431 'lang' => get_string('language'),
432 );
433 if (!empty($config->secretphrase)) {
434 $options[get_string('miscellaneous')]['encryptedcode'] = get_string('encryptedcode');
435 }
436
437 $options[get_string('user')] = array(
438 'userid' => 'id',
439 'userusername' => get_string('username'),
440 'useridnumber' => get_string('idnumber'),
441 'userfirstname' => get_string('firstname'),
442 'userlastname' => get_string('lastname'),
443 'userfullname' => get_string('fullnameuser'),
444 'useremail' => get_string('email'),
445 'usericq' => get_string('icqnumber'),
446 'userphone1' => get_string('phone').' 1',
447 'userphone2' => get_string('phone2').' 2',
448 'userinstitution' => get_string('institution'),
449 'userdepartment' => get_string('department'),
450 'useraddress' => get_string('address'),
451 'usercity' => get_string('city'),
452 'usertimezone' => get_string('timezone'),
453 'userurl' => get_string('webpage'),
454 );
455
456 if ($config->rolesinparams) {
457 $roles = get_all_roles();
458 $roleoptions = array();
459 foreach ($roles as $role) {
460 $roleoptions['course'.$role->shortname] = get_string('yourwordforx', '', $role->name);
461 }
462 $options[get_string('roles')] = $roleoptions;
463 }
464
465 return $options;
466}
467
468/**
469 * Get the parameter values that may be appended to URL
470 * @param object $url module instance
471 * @param object $cm
472 * @param object $course
473 * @param object $config module config options
474 * @return array of parameter values
475 */
476function url_get_variable_values($url, $cm, $course, $config) {
477 global $USER, $CFG;
478
479 $site = get_site();
480
8ebbb06a
SH
481 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
482
58a27a74 483 $values = array (
484 'courseid' => $course->id,
485 'coursefullname' => format_string($course->fullname),
8ebbb06a 486 'courseshortname' => format_string($course->shortname, true, array('context' => $coursecontext)),
58a27a74 487 'courseidnumber' => $course->idnumber,
488 'coursesummary' => $course->summary,
489 'courseformat' => $course->format,
490 'lang' => current_language(),
491 'sitename' => format_string($site->fullname),
492 'serverurl' => $CFG->wwwroot,
493 'currenttime' => time(),
494 'urlinstance' => $url->id,
495 'urlcmid' => $cm->id,
496 'urlname' => format_string($url->name),
497 'urlidnumber' => $cm->idnumber,
498 );
499
4f0c2d00 500 if (isloggedin()) {
58a27a74 501 $values['userid'] = $USER->id;
502 $values['userusername'] = $USER->username;
503 $values['useridnumber'] = $USER->idnumber;
504 $values['userfirstname'] = $USER->firstname;
505 $values['userlastname'] = $USER->lastname;
506 $values['userfullname'] = fullname($USER);
507 $values['useremail'] = $USER->email;
508 $values['usericq'] = $USER->icq;
509 $values['userphone1'] = $USER->phone1;
510 $values['userphone2'] = $USER->phone2;
511 $values['userinstitution'] = $USER->institution;
512 $values['userdepartment'] = $USER->department;
513 $values['useraddress'] = $USER->address;
514 $values['usercity'] = $USER->city;
515 $values['usertimezone'] = get_user_timezone_offset();
516 $values['userurl'] = $USER->url;
517 }
518
519 // weak imitation of Single-Sign-On, for backwards compatibility only
520 // NOTE: login hack is not included in 2.0 any more, new contrib auth plugin
521 // needs to be createed if somebody needs the old functionality!
522 if (!empty($config->secretphrase)) {
523 $values['encryptedcode'] = url_get_encrypted_parameter($url, $config);
524 }
525
526 //hmm, this is pretty fragile and slow, why do we need it here??
527 if ($config->rolesinparams) {
528 $roles = get_all_roles();
529 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
530 $roles = role_fix_names($roles, $coursecontext, ROLENAME_ALIAS);
531 foreach ($roles as $role) {
532 $values['course'.$role->shortname] = $role->localname;
533 }
534 }
535
536 return $values;
537}
538
539/**
540 * BC internal function
541 * @param object $url
542 * @param object $config
543 * @return string
544 */
545function url_get_encrypted_parameter($url, $config) {
546 global $CFG;
547
548 if (file_exists("$CFG->dirroot/local/externserverfile.php")) {
549 require_once("$CFG->dirroot/local/externserverfile.php");
550 if (function_exists('extern_server_file')) {
551 return extern_server_file($url, $config);
552 }
553 }
554 return md5(getremoteaddr().$config->secretphrase);
555}
556
557/**
558 * Optimised mimetype detection from general URL
559 * @param $fullurl
560 * @return string mimetype
561 */
562function url_guess_icon($fullurl) {
563 global $CFG;
564 require_once("$CFG->libdir/filelib.php");
565
566 if (substr_count($fullurl, '/') < 3 or substr($fullurl, -1) === '/') {
567 // most probably default directory - index.php, index.html, etc.
568 return 'f/web';
569 }
570
571 $icon = mimeinfo('icon', $fullurl);
572 $icon = 'f/'.str_replace(array('.gif', '.png'), '', $icon);
573
574 if ($icon === 'f/html' or $icon === 'f/unknown') {
575 $icon = 'f/web';
576 }
577
578 return $icon;
579}