Proyectos de Subversion Moodle

Rev

Rev 11 | Mostrar el archivo completo | | | Autoría | Ultima modificación | Ver Log |

Rev 11 Rev 1441
Línea 403... Línea 403...
403
 * @param int $itemid helps identify the file area. Can be null if there are no files yet.
403
 * @param int $itemid helps identify the file area. Can be null if there are no files yet.
404
 * @param array $options text and file options ('subdirs'=>false, 'forcehttps'=>false)
404
 * @param array $options text and file options ('subdirs'=>false, 'forcehttps'=>false)
405
 * @param string $text some html content that needs to have embedded links rewritten to point to the draft area.
405
 * @param string $text some html content that needs to have embedded links rewritten to point to the draft area.
406
 * @return string|null returns string if $text was passed in, the rewritten $text is returned. Otherwise NULL.
406
 * @return string|null returns string if $text was passed in, the rewritten $text is returned. Otherwise NULL.
407
 */
407
 */
408
function file_prepare_draft_area(&$draftitemid, $contextid, $component, $filearea, $itemid, array $options=null, $text=null) {
408
function file_prepare_draft_area(&$draftitemid, $contextid, $component, $filearea, $itemid, ?array $options=null, $text=null) {
409
    global $CFG, $USER;
409
    global $CFG, $USER;
Línea 410... Línea 410...
410
 
410
 
411
    $options = (array)$options;
411
    $options = (array)$options;
412
    if (!isset($options['subdirs'])) {
412
    if (!isset($options['subdirs'])) {
Línea 487... Línea 487...
487
 *          bool    $options.forcehttps Force the user of https
487
 *          bool    $options.forcehttps Force the user of https
488
 *          bool    $options.reverse Reverse the behaviour of the function
488
 *          bool    $options.reverse Reverse the behaviour of the function
489
 *          mixed   $options.includetoken Use a token for authentication. True for current user, int value for other user id.
489
 *          mixed   $options.includetoken Use a token for authentication. True for current user, int value for other user id.
490
 *          string  The processed text.
490
 *          string  The processed text.
491
 */
491
 */
492
function file_rewrite_pluginfile_urls($text, $file, $contextid, $component, $filearea, $itemid, array $options=null) {
492
function file_rewrite_pluginfile_urls($text, $file, $contextid, $component, $filearea, $itemid, ?array $options=null) {
493
    global $CFG, $USER;
493
    global $CFG, $USER;
Línea 494... Línea 494...
494
 
494
 
495
    $options = (array)$options;
495
    $options = (array)$options;
496
    if (!isset($options['forcehttps'])) {
496
    if (!isset($options['forcehttps'])) {
Línea 853... Línea 853...
853
    $draftfiles = file_get_drafarea_files($draftitemid, $filepath);
853
    $draftfiles = file_get_drafarea_files($draftitemid, $filepath);
854
    file_get_drafarea_folders($draftitemid, $filepath, $draftfiles);
854
    file_get_drafarea_folders($draftitemid, $filepath, $draftfiles);
Línea 855... Línea 855...
855
 
855
 
856
    if (!empty($draftfiles)) {
856
    if (!empty($draftfiles)) {
857
        foreach ($draftfiles->list as $draftfile) {
857
        foreach ($draftfiles->list as $draftfile) {
858
            if ($draftfile->type == 'file') {
858
            if ($draftfile->type !== 'folder') {
859
                $files[] = $draftfile;
859
                $files[] = $draftfile;
860
            }
860
            }
Línea 861... Línea 861...
861
        }
861
        }
Línea 1102... Línea 1102...
1102
 * @param string $text some html content that needs to have embedded links rewritten
1102
 * @param string $text some html content that needs to have embedded links rewritten
1103
 *      to the @@PLUGINFILE@@ form for saving in the database.
1103
 *      to the @@PLUGINFILE@@ form for saving in the database.
1104
 * @param bool $forcehttps force https urls.
1104
 * @param bool $forcehttps force https urls.
1105
 * @return string|null if $text was passed in, the rewritten $text is returned. Otherwise NULL.
1105
 * @return string|null if $text was passed in, the rewritten $text is returned. Otherwise NULL.
1106
 */
1106
 */
1107
function file_save_draft_area_files($draftitemid, $contextid, $component, $filearea, $itemid, array $options=null, $text=null, $forcehttps=false) {
1107
function file_save_draft_area_files($draftitemid, $contextid, $component, $filearea, $itemid, ?array $options=null, $text=null, $forcehttps=false) {
1108
    global $USER;
1108
    global $USER;
Línea 1109... Línea 1109...
1109
 
1109
 
1110
    // Do not merge files, leave it as it was.
1110
    // Do not merge files, leave it as it was.
1111
    if ($draftitemid === IGNORE_FILE_MERGE) {
1111
    if ($draftitemid === IGNORE_FILE_MERGE) {
Línea 1312... Línea 1312...
1312
        return file_rewrite_urls_to_pluginfile($text, $draftitemid, $forcehttps);
1312
        return file_rewrite_urls_to_pluginfile($text, $draftitemid, $forcehttps);
1313
    }
1313
    }
1314
}
1314
}
Línea 1315... Línea 1315...
1315
 
1315
 
-
 
1316
/**
-
 
1317
 * Clear a draft area.
-
 
1318
 *
-
 
1319
 * @param int $draftitemid Id of the draft area to clear.
-
 
1320
 * @return boolean success
-
 
1321
 */
-
 
1322
function file_clear_draft_area(int $draftitemid): bool {
-
 
1323
    global $USER;
-
 
1324
    $fs = get_file_storage();
-
 
1325
    $usercontext = context_user::instance($USER->id);
-
 
1326
    return $fs->delete_area_files($usercontext->id, 'user', 'draft', $draftitemid);
-
 
1327
}
-
 
1328
 
1316
/**
1329
/**
1317
 * Convert the draft file area URLs in some content to @@PLUGINFILE@@ tokens
1330
 * Convert the draft file area URLs in some content to @@PLUGINFILE@@ tokens
1318
 * ready to be saved in the database. Normally, this is done automatically by
1331
 * ready to be saved in the database. Normally, this is done automatically by
1319
 * {@link file_save_draft_area_files()}.
1332
 * {@link file_save_draft_area_files()}.
1320
 *
1333
 *
Línea 2211... Línea 2224...
2211
        header('Content-Type: text/plain; charset=utf-8');
2224
        header('Content-Type: text/plain; charset=utf-8');
2212
    } else {
2225
    } else {
2213
        header('Content-Type: '.$mimetype);
2226
        header('Content-Type: '.$mimetype);
2214
    }
2227
    }
Línea -... Línea 2228...
-
 
2228
 
2215
 
2229
    $isfileobj = is_object($file);
2216
    $lastmodified = is_object($file) ? $file->get_timemodified() : filemtime($file);
2230
    $lastmodified = $isfileobj ? $file->get_timemodified() : filemtime($file);
Línea 2217... Línea 2231...
2217
    header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastmodified) .' GMT');
2231
    header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastmodified) .' GMT');
2218
 
2232
 
2219
    if (is_object($file)) {
2233
    if ($isfileobj) {
2220
        header('Etag: "' . $file->get_contenthash() . '"');
2234
        header('Etag: "' . $file->get_contenthash() . '"');
2221
        if (isset($_SERVER['HTTP_IF_NONE_MATCH']) and trim($_SERVER['HTTP_IF_NONE_MATCH'], '"') === $file->get_contenthash()) {
2235
        if (isset($_SERVER['HTTP_IF_NONE_MATCH']) and trim($_SERVER['HTTP_IF_NONE_MATCH'], '"') === $file->get_contenthash()) {
2222
            header('HTTP/1.1 304 Not Modified');
2236
            header('HTTP/1.1 304 Not Modified');
2223
            return;
2237
            return;
Línea 2224... Línea 2238...
2224
        }
2238
        }
2225
    }
2239
    }
2226
 
2240
 
2227
    // if etag present for stored file rely on it exclusively
2241
    // if etag present for stored file rely on it exclusively
2228
    if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) and (empty($_SERVER['HTTP_IF_NONE_MATCH']) or !is_object($file))) {
2242
    if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && (empty($_SERVER['HTTP_IF_NONE_MATCH']) || !$isfileobj)) {
2229
        // get unixtime of request header; clip extra junk off first
2243
        // get unixtime of request header; clip extra junk off first
2230
        $since = strtotime(preg_replace('/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"]));
2244
        $since = strtotime(preg_replace('/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"]));
Línea 2239... Línea 2253...
2239
    } else {
2253
    } else {
2240
        header('Accept-Ranges: none');
2254
        header('Accept-Ranges: none');
2241
    }
2255
    }
Línea 2242... Línea 2256...
2242
 
2256
 
2243
    if ($accelerate) {
2257
    if ($accelerate) {
2244
        if (is_object($file)) {
2258
        if ($isfileobj) {
2245
            $fs = get_file_storage();
2259
            $fs = get_file_storage();
2246
            if ($fs->supports_xsendfile()) {
2260
            if ($fs->supports_xsendfile()) {
2247
                if ($fs->xsendfile_file($file)) {
2261
                if ($fs->xsendfile_file($file)) {
2248
                    return;
2262
                    return;
Línea 2256... Línea 2270...
2256
                }
2270
                }
2257
            }
2271
            }
2258
        }
2272
        }
2259
    }
2273
    }
Línea 2260... Línea 2274...
2260
 
2274
 
-
 
2275
    $filesize = $isfileobj ? $file->get_filesize() : filesize($file);
Línea 2261... Línea 2276...
2261
    $filesize = is_object($file) ? $file->get_filesize() : filesize($file);
2276
    $filename = $isfileobj ? $file->get_filename() : $file;
Línea 2262... Línea 2277...
2262
 
2277
 
Línea 2290... Línea 2305...
2290
                }
2305
                }
2291
            } else {
2306
            } else {
2292
                $ranges = false;
2307
                $ranges = false;
2293
            }
2308
            }
2294
            if ($ranges) {
2309
            if ($ranges) {
2295
                if (is_object($file)) {
2310
                if ($isfileobj) {
2296
                    $handle = $file->get_content_file_handle();
2311
                    $handle = $file->get_content_file_handle();
2297
                    if ($handle === false) {
2312
                    if ($handle === false) {
2298
                        throw new file_exception('storedfilecannotreadfile', $file->get_filename());
2313
                        throw new file_exception('storedfilecannotreadfile', $filename);
2299
                    }
2314
                    }
2300
                } else {
2315
                } else {
2301
                    $handle = fopen($file, 'rb');
2316
                    $handle = fopen($file, 'rb');
2302
                    if ($handle === false) {
2317
                    if ($handle === false) {
2303
                        throw new file_exception('cannotopenfile', $file);
2318
                        throw new file_exception('cannotopenfile', $file);
Línea 2319... Línea 2334...
2319
        $activehandler = array_pop($handlerstack);
2334
        $activehandler = array_pop($handlerstack);
2320
        if ($activehandler === 'default output handler') {
2335
        if ($activehandler === 'default output handler') {
2321
            // We do not expect any content in the buffer when we are serving files.
2336
            // We do not expect any content in the buffer when we are serving files.
2322
            $buffercontents = ob_get_clean();
2337
            $buffercontents = ob_get_clean();
2323
            if ($buffercontents !== '') {
2338
            if ($buffercontents !== '') {
-
 
2339
                // Include a preview of the first 20 characters of the output buffer to help identify
-
 
2340
                // what's causing it to be non-empty. This is useful for diagnosing unexpected output
-
 
2341
                // without exposing full content.
-
 
2342
                $buffercontentspreview = substr($buffercontents, 0, 20);
2324
                error_log('Non-empty default output handler buffer detected while serving the file ' . $file);
2343
                debugging("Non-empty default output handler buffer detected while serving the file {$filename}. " .
-
 
2344
                    "Buffer contents (first 20 characters): {$buffercontentspreview}", DEBUG_DEVELOPER);
2325
            }
2345
            }
2326
        } else {
2346
        } else {
2327
            // Some handlers such as zlib output compression may have file signature buffered - flush it.
2347
            // Some handlers such as zlib output compression may have file signature buffered - flush it.
2328
            ob_end_flush();
2348
            ob_end_flush();
2329
        }
2349
        }
2330
    }
2350
    }
Línea 2331... Línea 2351...
2331
 
2351
 
2332
    // send the whole file content
2352
    // send the whole file content
2333
    if (is_object($file)) {
2353
    if ($isfileobj) {
2334
        $file->readfile();
2354
        $file->readfile();
2335
    } else {
2355
    } else {
2336
        if (readfile_allow_large($file, $filesize) === false) {
2356
        if (readfile_allow_large($file, $filesize) === false) {
2337
            throw new file_exception('cannotopenfile', $file);
2357
            throw new file_exception('cannotopenfile', $file);
Línea 2594... Línea 2614...
2594
        $nobyteserving = true;
2614
        $nobyteserving = true;
2595
        if (is_https()) { // HTTPS sites - watch out for IE! KB812935 and KB316431.
2615
        if (is_https()) { // HTTPS sites - watch out for IE! KB812935 and KB316431.
2596
            header('Cache-Control: private, max-age=10, no-transform');
2616
            header('Cache-Control: private, max-age=10, no-transform');
2597
            header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
2617
            header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
2598
            header('Pragma: ');
2618
            header('Pragma: ');
2599
        } else { //normal http - prevent caching at all cost
2619
        } else { // Normal http - prevent caching at all cost.
2600
            header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0, no-transform');
2620
            header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0, no-transform', 'no-store');
2601
            header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
2621
            header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
2602
            header('Pragma: no-cache');
2622
            header('Pragma: no-cache');
2603
        }
2623
        }
2604
    }
2624
    }
Línea 2614... Línea 2634...
2614
    } else {
2634
    } else {
2615
        // Try to put the file through filters
2635
        // Try to put the file through filters
2616
        if ($mimetype == 'text/html' || $mimetype == 'application/xhtml+xml' || file_is_svg_image_from_mimetype($mimetype)) {
2636
        if ($mimetype == 'text/html' || $mimetype == 'application/xhtml+xml' || file_is_svg_image_from_mimetype($mimetype)) {
2617
            $options = new stdClass();
2637
            $options = new stdClass();
2618
            $options->noclean = true;
2638
            $options->noclean = true;
2619
            $options->nocache = true; // temporary workaround for MDL-5136
2639
            $options->context = context_course::instance($COURSE->id);
-
 
2640
 
2620
            if (is_object($path)) {
2641
            if (is_object($path)) {
2621
                $text = $path->get_content();
2642
                $text = $path->get_content();
2622
            } else if ($pathisstring) {
2643
            } else if ($pathisstring) {
2623
                $text = $path;
2644
                $text = $path;
2624
            } else {
2645
            } else {
2625
                $text = implode('', file($path));
2646
                $text = implode('', file($path));
2626
            }
2647
            }
2627
            $output = format_text($text, FORMAT_HTML, $options, $COURSE->id);
-
 
Línea -... Línea 2648...
-
 
2648
 
2628
 
2649
            $output = format_text($text, FORMAT_HTML, $options);
2629
            readstring_accel($output, $mimetype);
-
 
2630
 
2650
            readstring_accel($output, $mimetype);
2631
        } else if (($mimetype == 'text/plain') and ($filter == 1)) {
2651
        } else if (($mimetype == 'text/plain') and ($filter == 1)) {
2632
            // only filter text if filter all files is selected
2652
            // only filter text if filter all files is selected
2633
            $options = new stdClass();
2653
            $options = new stdClass();
2634
            $options->newlines = false;
2654
            $options->newlines = false;
-
 
2655
            $options->noclean = true;
-
 
2656
            $options->context = context_course::instance($COURSE->id);
2635
            $options->noclean = true;
2657
 
2636
            if (is_object($path)) {
2658
            if (is_object($path)) {
2637
                $text = htmlentities($path->get_content(), ENT_QUOTES, 'UTF-8');
2659
                $text = htmlentities($path->get_content(), ENT_QUOTES, 'UTF-8');
2638
            } else if ($pathisstring) {
2660
            } else if ($pathisstring) {
2639
                $text = htmlentities($path, ENT_QUOTES, 'UTF-8');
2661
                $text = htmlentities($path, ENT_QUOTES, 'UTF-8');
2640
            } else {
2662
            } else {
2641
                $text = htmlentities(implode('', file($path)), ENT_QUOTES, 'UTF-8');
2663
                $text = htmlentities(implode('', file($path)), ENT_QUOTES, 'UTF-8');
2642
            }
-
 
Línea -... Línea 2664...
-
 
2664
            }
2643
            $output = '<pre>'. format_text($text, FORMAT_MOODLE, $options, $COURSE->id) .'</pre>';
2665
 
2644
 
-
 
2645
            readstring_accel($output, $mimetype);
2666
            $output = '<pre>'. format_text($text, FORMAT_MOODLE, $options) .'</pre>';
2646
 
2667
            readstring_accel($output, $mimetype);
2647
        } else {
2668
        } else {
2648
            // send the contents
2669
            // send the contents
2649
            if ($pathisstring) {
2670
            if ($pathisstring) {
Línea 2969... Línea 2990...
2969
 * @param array $options area options (subdirs=false, maxfiles=-1, maxbytes=0, areamaxbytes=FILE_AREA_MAX_BYTES_UNLIMITED)
2990
 * @param array $options area options (subdirs=false, maxfiles=-1, maxbytes=0, areamaxbytes=FILE_AREA_MAX_BYTES_UNLIMITED)
2970
 * @see file_save_draft_area_files
2991
 * @see file_save_draft_area_files
2971
 * @since Moodle 3.2
2992
 * @since Moodle 3.2
2972
 */
2993
 */
2973
function file_merge_files_from_draft_area_into_filearea($draftitemid, $contextid, $component, $filearea, $itemid,
2994
function file_merge_files_from_draft_area_into_filearea($draftitemid, $contextid, $component, $filearea, $itemid,
2974
                                                        array $options = null) {
2995
                                                        ?array $options = null) {
2975
    // We use 0 here so file_prepare_draft_area creates a new one, finaldraftid will be updated with the new draft id.
2996
    // We use 0 here so file_prepare_draft_area creates a new one, finaldraftid will be updated with the new draft id.
2976
    $finaldraftid = 0;
2997
    $finaldraftid = 0;
2977
    file_prepare_draft_area($finaldraftid, $contextid, $component, $filearea, $itemid, $options);
2998
    file_prepare_draft_area($finaldraftid, $contextid, $component, $filearea, $itemid, $options);
2978
    file_merge_draft_area_into_draft_area($draftitemid, $finaldraftid);
2999
    file_merge_draft_area_into_draft_area($draftitemid, $finaldraftid);
2979
    file_save_draft_area_files($finaldraftid, $contextid, $component, $filearea, $itemid, $options);
3000
    file_save_draft_area_files($finaldraftid, $contextid, $component, $filearea, $itemid, $options);
Línea 3153... Línea 3174...
3153
    private $securityhelper;
3174
    private $securityhelper;
3154
    /** @var bool ignoresecurity a flag which can be supplied to the constructor, allowing security to be bypassed. */
3175
    /** @var bool ignoresecurity a flag which can be supplied to the constructor, allowing security to be bypassed. */
3155
    private $ignoresecurity;
3176
    private $ignoresecurity;
3156
    /** @var array $mockresponses For unit testing only - return the head of this list instead of making the next request. */
3177
    /** @var array $mockresponses For unit testing only - return the head of this list instead of making the next request. */
3157
    private static $mockresponses = [];
3178
    private static $mockresponses = [];
-
 
3179
    /** @var array $curlresolveinfo Resolve addresses for the URL that have passed cuRL security checks, in a CURLOPT_RESOLVE compatible format. */
-
 
3180
    private $curlresolveinfo = [];
3158
    /** @var array temporary params value if the value is not belongs to class stored_file. */
3181
    /** @var array temporary params value if the value is not belongs to class stored_file. */
3159
    public $_tmp_file_post_params = [];
3182
    public $_tmp_file_post_params = [];
Línea 3160... Línea 3183...
3160
 
3183
 
3161
    /**
3184
    /**
Línea 3750... Línea 3773...
3750
        if ($this->securityhelper->url_is_blocked($url)) {
3773
        if ($this->securityhelper->url_is_blocked($url)) {
3751
            $this->error = $this->securityhelper->get_blocked_url_string();
3774
            $this->error = $this->securityhelper->get_blocked_url_string();
3752
            return $this->error;
3775
            return $this->error;
3753
        }
3776
        }
Línea -... Línea 3777...
-
 
3777
 
-
 
3778
        // Set allowed resolve info if the URL is not blocked.
-
 
3779
        $this->curlresolveinfo = $this->securityhelper->get_resolve_info();
3754
 
3780
 
3755
        return null;
3781
        return null;
Línea 3756... Línea 3782...
3756
    }
3782
    }
3757
 
3783
 
Línea 3786... Línea 3812...
3786
        }
3812
        }
Línea 3787... Línea 3813...
3787
 
3813
 
3788
        // Set the URL as a curl option.
3814
        // Set the URL as a curl option.
Línea -... Línea 3815...
-
 
3815
        $this->setopt(array('CURLOPT_URL' => $url));
-
 
3816
 
-
 
3817
        // Force cURL to only resolve the URL from IP/port combinations that were validated by the security helper.
-
 
3818
        // This prevents re-fetching DNS data on subsequent requests, which could return un-validated hosts/ports.
3789
        $this->setopt(array('CURLOPT_URL' => $url));
3819
        $this->setopt(['CURLOPT_RESOLVE' => $this->curlresolveinfo]);
3790
 
3820
 
Línea 3791... Línea 3821...
3791
        // Create curl instance.
3821
        // Create curl instance.
3792
        $curl = curl_init();
3822
        $curl = curl_init();
Línea 3897... Línea 3927...
3897
                    }
3927
                    }
3898
                }
3928
                }
Línea 3899... Línea 3929...
3899
 
3929
 
Línea -... Línea 3930...
-
 
3930
                curl_setopt($curl, CURLOPT_URL, $redirecturl);
-
 
3931
 
-
 
3932
                // Force cURL to only resolve the URL from IP/port combinations that were validated by the security helper.
-
 
3933
                // This prevents re-fetching DNS data on subsequent requests, which could return un-validated hosts/ports.
3900
                curl_setopt($curl, CURLOPT_URL, $redirecturl);
3934
                $this->setopt(['CURLOPT_RESOLVE' => $this->curlresolveinfo]);
3901
 
3935
 
3902
                // If CURLOPT_UNRESTRICTED_AUTH is empty/false, don't send credentials to other hosts.
3936
                // If CURLOPT_UNRESTRICTED_AUTH is empty/false, don't send credentials to other hosts.
3903
                // Ref: https://curl.se/libcurl/c/CURLOPT_UNRESTRICTED_AUTH.html.
3937
                // Ref: https://curl.se/libcurl/c/CURLOPT_UNRESTRICTED_AUTH.html.
3904
                $isdifferenthost = parse_url($currenturl)['host'] !== parse_url($redirecturl)['host'];
3938
                $isdifferenthost = parse_url($currenturl)['host'] !== parse_url($redirecturl)['host'];
Línea 4745... Línea 4779...
4745
            // fix file name automatically
4779
            // fix file name automatically
4746
            if ($filename !== 'f1' and $filename !== 'f2' and $filename !== 'f3') {
4780
            if ($filename !== 'f1' and $filename !== 'f2' and $filename !== 'f3') {
4747
                $filename = 'f1';
4781
                $filename = 'f1';
4748
            }
4782
            }
Línea 4749... Línea 4783...
4749
 
4783
 
4750
            if ((!empty($CFG->forcelogin) and !isloggedin()) ||
-
 
4751
                    (!empty($CFG->forceloginforprofileimage) && (!isloggedin() || isguestuser()))) {
4784
            if (!\core\output\user_picture::allow_view($context->instanceid)) {
4752
                // protect images if login required and not logged in;
4785
                // protect images if login required and not logged in;
4753
                // also if login is required for profile images and is not logged in or guest
4786
                // also if login is required for profile images and is not logged in or guest
4754
                // do not use require_login() because it is expensive and not suitable here anyway
4787
                // do not use require_login() because it is expensive and not suitable here anyway
4755
                $theme = theme_config::load($themename);
4788
                $theme = theme_config::load($themename);