feat(courseware): add protected PPTX viewer
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
/**
|
||||
* File-based courseware: one .pptx file per lesson in the repo-root courseware/ directory.
|
||||
*/
|
||||
|
||||
if (!function_exists('courseware_root')) {
|
||||
function courseware_root() {
|
||||
if (isset($GLOBALS['_courseware_root']) && $GLOBALS['_courseware_root'] !== '') {
|
||||
$resolved = realpath($GLOBALS['_courseware_root']);
|
||||
return $resolved === false ? '' : $resolved;
|
||||
}
|
||||
$resolved = realpath(__DIR__ . '/../../courseware');
|
||||
return $resolved === false ? '' : $resolved;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('courseware_is_slug')) {
|
||||
function courseware_is_slug($slug) {
|
||||
if (!is_string($slug) || $slug === '' || $slug === '.' || $slug === '..') {
|
||||
return false;
|
||||
}
|
||||
if (strpos($slug, "\0") !== false) {
|
||||
return false;
|
||||
}
|
||||
if (strpos($slug, '/') !== false || strpos($slug, '\\') !== false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('courseware_is_pptx_name')) {
|
||||
function courseware_is_pptx_name($name) {
|
||||
if (!is_string($name) || strlen($name) < 6) {
|
||||
return false;
|
||||
}
|
||||
if (strpos($name, "\0") !== false || strpos($name, '/') !== false || strpos($name, '\\') !== false) {
|
||||
return false;
|
||||
}
|
||||
return strtolower(substr($name, -5)) === '.pptx';
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('courseware_inside_root')) {
|
||||
function courseware_inside_root($path, $root) {
|
||||
$root_norm = rtrim(str_replace('\\', '/', $root), '/');
|
||||
$path_norm = rtrim(str_replace('\\', '/', $path), '/');
|
||||
if ($root_norm === '' || $path_norm === '') {
|
||||
return false;
|
||||
}
|
||||
$insensitive = (DIRECTORY_SEPARATOR === '\\');
|
||||
if ($insensitive) {
|
||||
if (strcasecmp($path_norm, $root_norm) === 0) {
|
||||
return true;
|
||||
}
|
||||
$prefix = $root_norm . '/';
|
||||
return strncasecmp($path_norm, $prefix, strlen($prefix)) === 0;
|
||||
}
|
||||
return $path_norm === $root_norm || strpos($path_norm, $root_norm . '/') === 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('courseware_is_absolute_path')) {
|
||||
function courseware_is_absolute_path($path) {
|
||||
if (!is_string($path) || $path === '') {
|
||||
return false;
|
||||
}
|
||||
if ($path[0] === '/' || $path[0] === '\\') {
|
||||
return true;
|
||||
}
|
||||
return (bool) preg_match('#^[A-Za-z]:[\\\\/]#', $path);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('courseware_scan_pptx_entries')) {
|
||||
function courseware_scan_pptx_entries() {
|
||||
$root = courseware_root();
|
||||
$found = array();
|
||||
if ($root === '' || !is_dir($root)) {
|
||||
return $found;
|
||||
}
|
||||
$entries = @scandir($root);
|
||||
if ($entries === false) {
|
||||
return $found;
|
||||
}
|
||||
foreach ($entries as $name) {
|
||||
if ($name === '.' || $name === '..') {
|
||||
continue;
|
||||
}
|
||||
if (!courseware_is_pptx_name($name)) {
|
||||
continue;
|
||||
}
|
||||
$slug = substr($name, 0, -5);
|
||||
if (!courseware_is_slug($slug)) {
|
||||
continue;
|
||||
}
|
||||
if (!isset($found[$slug])) {
|
||||
$found[$slug] = $name;
|
||||
}
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('courseware_link_stays_inside')) {
|
||||
function courseware_link_stays_inside($raw, $root) {
|
||||
if (!is_link($raw)) {
|
||||
return true;
|
||||
}
|
||||
$link = @readlink($raw);
|
||||
if ($link === false || strpos($link, "\0") !== false) {
|
||||
return false;
|
||||
}
|
||||
$target = courseware_is_absolute_path($link)
|
||||
? $link
|
||||
: (dirname($raw) . DIRECTORY_SEPARATOR . $link);
|
||||
$resolved_link = realpath($target);
|
||||
if ($resolved_link === false || !courseware_inside_root($resolved_link, $root)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('courseware_resolve_pptx')) {
|
||||
function courseware_resolve_pptx($slug) {
|
||||
if (!courseware_is_slug($slug)) {
|
||||
return null;
|
||||
}
|
||||
$root = courseware_root();
|
||||
if ($root === '') {
|
||||
return null;
|
||||
}
|
||||
$entries = courseware_scan_pptx_entries();
|
||||
if (!isset($entries[$slug])) {
|
||||
return null;
|
||||
}
|
||||
$name = $entries[$slug];
|
||||
$raw = $root . DIRECTORY_SEPARATOR . $name;
|
||||
if (!courseware_link_stays_inside($raw, $root)) {
|
||||
return null;
|
||||
}
|
||||
$resolved = realpath($raw);
|
||||
if ($resolved === false || !is_file($resolved)) {
|
||||
return null;
|
||||
}
|
||||
if (!courseware_inside_root($resolved, $root)) {
|
||||
return null;
|
||||
}
|
||||
if (!courseware_is_pptx_name(basename($resolved)) && !courseware_is_pptx_name($name)) {
|
||||
return null;
|
||||
}
|
||||
return array(
|
||||
'slug' => $slug,
|
||||
'title' => $slug,
|
||||
'filename' => $name,
|
||||
'path' => $resolved,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('courseware_build_deck')) {
|
||||
function courseware_build_deck($slug) {
|
||||
$found = courseware_resolve_pptx($slug);
|
||||
if ($found === null) {
|
||||
return null;
|
||||
}
|
||||
return array(
|
||||
'slug' => $found['slug'],
|
||||
'title' => $found['title'],
|
||||
'filename' => $found['filename'],
|
||||
'url' => 'courseware-file.php?deck=' . rawurlencode($found['slug']),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('courseware_list_decks')) {
|
||||
function courseware_list_decks() {
|
||||
$decks = array();
|
||||
$seen = array();
|
||||
foreach (courseware_scan_pptx_entries() as $slug => $name) {
|
||||
unset($name);
|
||||
if (isset($seen[$slug])) {
|
||||
continue;
|
||||
}
|
||||
$deck = courseware_build_deck($slug);
|
||||
if ($deck !== null) {
|
||||
$seen[$slug] = true;
|
||||
$decks[] = $deck;
|
||||
}
|
||||
}
|
||||
usort($decks, function ($a, $b) {
|
||||
return strnatcasecmp($a['title'], $b['title']);
|
||||
});
|
||||
return $decks;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('courseware_clamp_page')) {
|
||||
function courseware_clamp_page($deck, $page) {
|
||||
$page = intval($page);
|
||||
if ($page < 1) {
|
||||
return 1;
|
||||
}
|
||||
$count = (is_array($deck) && isset($deck['page_count'])) ? intval($deck['page_count']) : 0;
|
||||
if ($count < 1) {
|
||||
return $page;
|
||||
}
|
||||
if ($page > $count) {
|
||||
return $count;
|
||||
}
|
||||
return $page;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('courseware_content_disposition')) {
|
||||
function courseware_content_disposition($filename) {
|
||||
$safe = str_replace(array("\r", "\n", '"', '\\'), '_', strval($filename));
|
||||
$ascii = preg_replace('/[^\x20-\x7E]/', '_', $safe);
|
||||
if ($ascii === '' || $ascii === '_' || $ascii === '.pptx') {
|
||||
$ascii = 'courseware.pptx';
|
||||
}
|
||||
return 'inline; filename="' . $ascii . '"; filename*=UTF-8\'\'' . rawurlencode($filename);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user