' . NL; $RecipeInfo['IncludeFile']['Version'] = '2025-12-01'; $RecipeInfo['IncludeFile']['Author'] = 'ThomasP'; # recipe version page variable $FmtPV['$IncludeFileVersion'] = "'IncludeFile version {$RecipeInfo['IncludeFile']['Version']}'"; // return version as a custom page variable # declare $IncludeFile for (:if enabled IncludeFile:) recipe installation check $IncludeFile = true; # enabled Markup('includefile', '" . NL; break; case (strtolower($opt['type'] ?? '') === 'html'): // Handle HTML files $x = file($fileNameFull); if ($x === false) { return "Could not open file \"$fileNameFull\"" . BR; } $filecontents = implode('', $x); $result = sanitizeHtml($filecontents); break; case (str_starts_with($mimeType, 'text/')): // Handle other text/code files $x = file($fileNameFull); if ($x === false) { return "Could not open file \"$fileNameFull\"" . BR; } $filecontents = implode('', $x); $filecontentsEscaped = PHSC($filecontents); $filecontentsEscaped = str_replace(" ", " ", $filecontentsEscaped); $filecontentsEscaped = str_replace("\n\n", BR, $filecontentsEscaped); $result = "" . $filecontentsEscaped . '' . NL; $result = '
' . NL . $result . '
' . NL; break; default: // Unsupported or binary file types return "(:includefile:) File type \"$mimeType\" not supported for display. \"$fileName\"" . BR; } # end switch if (isset($opt['header'])) { $headerTokens = explode(',', $opt['header']); $filenameMarkup = $fileName . ':'; # Base filename markup // Single open/close wrappers $openTags = ''; $closeTags = ''; // Apply bold if requested if (in_array('filenamebold', $headerTokens)) { $openTags .= ''; $closeTags = '' . $closeTags; } // Apply small if requested if (in_array('filenamesmall', $headerTokens)) { $openTags .= ''; $closeTags = '' . $closeTags; } // Prepend to result if any filename-related option is present if ( in_array('filename', $headerTokens) || in_array('filenamebold', $headerTokens) || in_array('filenamesmall', $headerTokens) ) { $result = $openTags . $filenameMarkup . $closeTags . BR . $result; } // Horizontal rule option if (in_array('hr', $headerTokens)) { $result = '
' . NL . $result; } } # end if return $result; } # end function getFormattedFileContents function includeTextAsCodeFunc($pagename, $args) { global $UploadDir, $UploadPrefixFmt; global $AuthFunction, $HandleAuth; $opt = ParseArgs($args); if ((empty($opt[''][0])) && (empty ($opt['ext']))) { return "(:includefile:) failed: no arguments given." . BR; } // retrieve filename from options and make sure it is well-behaved: $fileName = $opt[''][0] ?? ''; $fileName = basename($fileName); # no path traversal // permission check for accessing files in this page dir: if (!$AuthFunction($pagename, $HandleAuth['includefile'], false)) { return "(:includefile:) failed: Insufficient PmWiki privileges to include files from this directory." . BR; } if (empty($opt['ext'])) { $fileNameFull = FmtPageName("$UploadDir$UploadPrefixFmt", $pagename) . "/$fileName"; // This currently supports only including from the directory directly belonging to the wiki page. // No relative or absolute paths (as described on my (ThomasP) profile page) allowed at the moment. if (file_exists($fileNameFull)) { return getFormattedFileContents($fileName, $fileNameFull, $opt); } return "(:includefile \"$fileName\":) failed: Could not open \"$fileNameFull\" for page \"$pagename\"." . BR; } // have a pattern instead of a single file $matchext = '/\\.(' . implode('|', preg_split('/\\W+/', $opt['ext'], -1, PREG_SPLIT_NO_EMPTY)) . ')$/i'; $targetdir = FmtPageName("$UploadDir$UploadPrefixFmt", $pagename); $dirp = opendir($targetdir); if ($dirp === false) { return "(:includefile:) failed: Unable to open \"$targetdir\"." . BR; } $filelist = array(); while (($fileName=readdir($dirp)) !== false) { if ($fileName[0] == '.') continue; if ($matchext && !preg_match($matchext, $fileName)) continue; $stat = stat("$targetdir/$fileName"); $orderRaw = $opt['order'] ?? ''; $descending = false; if ($orderRaw !== '' && $orderRaw[0] === '-') { $descending = true; $order = substr($orderRaw, 1); // strip leading "-" } else { $order = $orderRaw; } switch ($order) { case 'size': $filelist[$stat['size'].'_'.$fileName] = $fileName; break; case 'atime': $filelist[$stat['atime'].'_'.$fileName] = $fileName; break; case 'ctime': $filelist[$stat['ctime'].'_'.$fileName] = $fileName; break; case 'mtime': case 'time': // fall-through for both $filelist[$stat['mtime'].'_'.$fileName] = $fileName; break; case 'name': $filelist[$fileName] = $fileName; break; default: $filelist[$fileName] = $fileName; break; } # switch } # end while closedir($dirp); if ($descending) { krsort($filelist); } else { ksort($filelist); } $result = ''; # initialise foreach($filelist as $sortkey=>$fileName) { $result .= getFormattedFileContents($fileName, "$targetdir/$fileName", $opt) . "\n"; } # end foreach return $result; } # end function includeTextAsCodeFunc # function sanitizeHtml(string $filecontents): string { /** * Sanitize HTML: remove active JavaScript but keep rendering content. * Allows