* * Code may be re-used as long as the above copyright notice is retained. * See README.txt for full details. * * Written with Coda: * */ // Install PSR-4-compatible class autoloader spl_autoload_register(function($class){ require str_replace('\\', DIRECTORY_SEPARATOR, ltrim($class, '\\')).'.php'; }); // Get Markdown class use md\MarkdownExtra; // User configurable options: require_once "config.php"; // Load configured localization: require_once 'locales/' . W2_LOCALE . '.php'; /** * Get translated word * * String $label Key for locale word * String $alt_word Alternative word * return String */ function __( $label, $alt_word = null ) { global $w2_word_set; if( empty($w2_word_set[$label]) ) { return is_null($alt_word) ? $label : $alt_word; } return htmlspecialchars($w2_word_set[$label], ENT_QUOTES); } if ( REQUIRE_PASSWORD ) { ini_set('session.gc_maxlifetime', W2_SESSION_LIFETIME); session_set_cookie_params(W2_SESSION_LIFETIME); } session_name(W2_SESSION_NAME); session_start(); if ( count($allowedIPs) > 0 ) { $ip = $_SERVER['REMOTE_ADDR']; $accepted = false; foreach ( $allowedIPs as $allowed ) { if ( strncmp($allowed, $ip, strlen($allowed)) == 0 ) { $accepted = true; break; } } if ( !$accepted ) { print "Access from IP address $ip is not allowed"; exit; } } function printHeader($title, $action, $bodyclass="") { print "\n"; print "\n"; print " \n"; print " \n"; print " \n"; print " \n"; print " \n"; print " ".PAGE_TITLE."$title\n"; print " \n"; print " \n"; } function printFooter() { print " \n"; print ""; } function printDrawer() { print "
\n". " \"".__('Close')."\"\n". "
".__('Markdown Syntax Helper')."
\n". "
\n". "# ".__('Header')." 1
". "## ".__('Header')." 2
". "### ".__('Header')." 3
". "#### ".__('Header')." 4
". "##### ".__('Header')." 5
". "###### ".__('Header')." 6
". "
". "*".__('Emphasize')."* - ".__('Emphasize')."
". "_".__('Emphasize')."* - ".__('Emphasize')."
". "**".__('Bold')."** - ".__('Bold')."
". "__".__('Bold')."__ - ".__('Bold')."
". "
". "[[Link to page]]
". "<http://example.com/>
". "[link text](http://url)

". "{{image.jpg}}
". "![Alt text](/images/image.jpg)
". "![Alt text](/images/image.jpg \"Optional title\")
". "
". "- Unordered list
". "+ Unordered list
". "* Unordered list
". "1. Ordered list
". "
". "> Blockquote
".//
Blockquotes
\n". "```Code```
". //
Code
\n\n". "`inline-code`

". "*** Horizontal rule
". "--- Horizontal rule
\n". "
\n". "
\n". " \n". " \n". " \"".__('Formatting\n". " \"".__('Formatting\n". " \"".__('Formatting\n". " \n". " \n"; } if ( REQUIRE_PASSWORD && !isset($_SESSION['password']) ) { if ( !defined('W2_PASSWORD_HASH') || W2_PASSWORD_HASH == '' ) define('W2_PASSWORD_HASH', sha1(W2_PASSWORD)); if ( (isset($_POST['p'])) && (sha1($_POST['p']) == W2_PASSWORD_HASH) ) $_SESSION['password'] = W2_PASSWORD_HASH; else { printHeader( __('Log In'), '', "login"); print "

" . __('Log In') . "

\n"; print "
\n"; print " ".__('Password') . ": \n"; print " \n"; print "
\n"; printFooter(); exit; } } // Support functions function descLengthSort($val_1, $val_2) { $firstVal = strlen($val_1); $secondVal = strlen($val_2); return ( $firstVal > $secondVal ) ? -1 : ( ( $firstVal < $secondVal ) ? 1 : 0); } function getAllPageNames($path = "") { $filenames = array(); $dir = opendir(PAGES_PATH . "/$path" ); while ( $filename = readdir($dir) ) { if ( $filename[0] == "." ) { continue; } if ( is_dir( PAGES_PATH . "/$path/$filename" ) ) { array_push($filenames, ...getAllPageNames( "$path/$filename" ) ); continue; } if ( preg_match("/".PAGES_EXT."$/", $filename) != 1) { continue; } $filename = substr($filename, 0, -(strlen(PAGES_EXT)+1) ); $filenames[] = substr("$path/$filename", 1); } closedir($dir); return $filenames; } function fileNameForPage($page) { return PAGES_PATH . "/$page." . PAGES_EXT; } function imageLinkText($imgName) { return "![".__("Image Description")."](/".UPLOAD_FOLDER."/$imgName)"; } function sanitizeFilename($inFileName) { return str_replace(array('~', '..', '\\', ':', '|', '&'), '-', $inFileName); } function pageURL($page) { return SELF . VIEW . "/".str_replace("%2F", "/", str_replace("%23", "#", urlencode(sanitizeFilename($page)))); } function pageLink($page, $title, $attributes="") { return "$title"; /* wip $link_page = $match[1]; $link_filename = PAGES_PATH . "/$link_page.txt"; $link_page_exists = file_exists($link_filename); if ($link_page_exists) return "$title"; else //return "" . htmlentities($link_page) . ""; return "$title"; */ } function redirectWithMessage($page, $msg) { $_SESSION["msg"] = $msg; header("HTTP/1.1 303 See Other"); header("Location: " . pageURL($page) ); exit; } function checkedExecute(&$msg, $cmd) { $returnValue = 0; $output = ''; exec($cmd, $output, $returnValue); if ($returnValue != 0) { $msg .= "
Error executing command ".$cmd." (return value: ".$returnValue."): ".implode(" ", $output); } return ($returnValue == 0); } function gitChangeHandler($commitmsg, &$msg) { if (!GIT_COMMIT_ENABLED) { return; } if (checkedExecute($msg, "cd ".PAGES_PATH." && git add -A && git commit -m ".escapeshellarg($commitmsg))) { if (!GIT_PUSH_ENABLED) { return; } checkedExecute($msg, "cd ".PAGES_PATH." && git push"); } } function toHTMLID($noid) { // in HTML5, only spaces aren't allowed return str_replace(" ", "-", $noid); } function toHTML($inText) { $parser = new MarkdownExtra; $parser->no_markup = true; $outHTML = $parser->transform($inText); if ( AUTOLINK_PAGE_TITLES ) { $pagenames = getAllPageNames(); uasort($pagenames, "descLengthSort"); foreach ( $pagenames as $pageName ) { // match pageName, but only if it isn't inside another word or inside braces (as in "[$pageName]"). $outHTML = preg_replace("/(? 1) ? $linkTitleSplit[1] : $linkedPage; $pagePart = explode('#', $linkedPage)[0]; // split away an eventual anchor part $linkedFilename = fileNameForPage(sanitizeFilename($pagePart)); $exists = file_exists($linkedFilename); $outHTML = str_replace("[[$fullLinkText]]", pageLink($linkedPage, $linkText, ($exists? "" : " class=\"noexist\"")), $outHTML); } $outHTML = preg_replace("/\{\{(.*?)\}\}/", "\"\\1\"", $outHTML); // add an anchor in all title tags (h1/2/3/4): preg_match_all( "/(.*?)<\/h\\1>/", $outHTML, $matches, PREG_PATTERN_ORDER ); for ($i = 0; $i < count($matches[0]); $i++) { $prefix = ""; $caption = $matches[2][$i]; $suffix = substr_replace($prefix, "/", 1, 0); $outHTML = str_replace("$prefix$caption$suffix", "$prefix$caption$suffix", $outHTML); } return $outHTML; } function destroy_session() { if ( isset($_COOKIE[session_name()]) ) { setcookie(session_name(), '', time() - 42000, '/'); } session_destroy(); unset($_SESSION["password"]); unset($_SESSION); } function getPageActions($page, $action, $imgSuffix) { $pageActions = array('edit', 'delete', 'rename'); $pageActionNames = array(__('Edit'), __('Delete'), __('Rename')); $result = ''; for ($i = 0; $i < count($pageActions); $i++ ) { if ($action != $pageActions[$i]) { $result .= " \"".$pageActionNames[$i]."\"\n"; } } $result .= " \"".__('Show\n"; return $result; } // Main code $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : 'view'; $newPage = ""; $text = ""; $html = ""; if ($action === 'view' || $action === 'edit' || $action === 'save' || $action === 'rename' || $action === 'delete') { // Look for page name following the script name in the URL, like this: // http://stevenf.com/w2demo/index.php/Markdown%20Syntax // // Otherwise, get page name from 'page' request variable. $page = preg_match('@^/@', @$_SERVER["PATH_INFO"]) ? urldecode(substr($_SERVER["PATH_INFO"], 1)) : urldecode(@$_REQUEST['page']); $page = sanitizeFilename($page); if ( $page == "" ) { $page = DEFAULT_PAGE; } $filename = fileNameForPage($page); } if ($action === 'view' || $action === 'edit') { if ( file_exists($filename) ) { $text = file_get_contents($filename); } else { $newPage = $page; $action = 'new'; } } $oldgitmsg = ""; $triedSave = false; if ( $action == 'save' ) { $msg = ''; $newText = $_REQUEST['newText']; $isNew = $_REQUEST['isNew']; if ($isNew) { $page = str_replace(array('|','#'), '', $page); $filename = fileNameForPage($page); } if ($isNew && file_exists($filename)) { $msg .= "Error creating page '$page' - it already exists! Please choose a different name, or edit the existing page (this discards current text!)!\n"; $action = 'new'; $text = $newText; $newPage = $page; if (GIT_COMMIT_ENABLED) { $oldgitmsg = $_REQUEST['gitmsg']; } $triedSave = true; } else { $errLevel = error_reporting(0); if ( !file_exists( dirname($filename) ) ) { mkdir(dirname($filename), 0755, true); } $success = file_put_contents($filename, $newText); error_reporting($errLevel); if ( $success === FALSE) { $msg .= "Error saving changes! Make sure your web server has write access to " . PAGES_PATH . "\n"; $action = ($isNew ? 'new' : 'edit'); $text = $newText; $newPage = $page; if (GIT_COMMIT_ENABLED) { $oldgitmsg = $_REQUEST['gitmsg']; } $triedSave = true; } else { $msg .= ($isNew ? __('Created'): __('Saved')); $usermsg = $_REQUEST['gitmsg']; $commitmsg = $page . ($usermsg !== '' ? (": ".$usermsg) : ($isNew ? " created" : " changed")); gitChangeHandler($commitmsg, $msg); } } redirectWithMessage($page, $msg); } if ( $action === 'edit' || $action === 'new' ) { $formAction = SELF . (($action === 'edit') ? "/$page" : ""); $html .= "
\n"; if ( $action === 'edit' ) { $html .= "\n"; } else { if ($newPage != "" && !$triedSave) { $html .= "
". __('Creating new page since no page with given title exists!') ; // check if similar page exists... $pageNames = getAllPageNames(); foreach($pageNames as $pageName) { if (levenshtein(strtoupper($newPage), strtoupper($pageName)) < sqrt(min(strlen($newPage), strlen($pageName))) ) { $html .= "
Note: Found similar page ".pageLink($pageName, $pageName).". Maybe you meant to edit this instead?"; } } $html .= "
\n"; } $html .= "

" . __('Title') . ":

\n"; } $html .= "

\n"; if (GIT_COMMIT_ENABLED) { $html .= "

Message:

\n"; } $html .= "

\n"; $html .= "\n"; $html .= ''."\n"; $html .= ''."\n"; $html .= "

\n"; } else if ( $action === 'logout' ) { destroy_session(); header("Location: " . SELF); exit; } else if ( $action === 'upload' ) { if ( DISABLE_UPLOADS ) { $html .= '

' . __('Image uploading has been disabled on this installation.') . '

'; } else { $html .= "

\n". "". "\n". ''. ''. ''. ''. ''."\n". ''."\n"; "

\n"; } // list files in UPLOAD_FOLDER $path = PAGES_PATH . "/". UPLOAD_FOLDER . "/*"; $imgNames = glob($path); natcasesort($imgNames); $html .= "

".__('Total').": ".count($imgNames)." ".__('images')."

"; $html .= ""; $html .= "". /* "". "". */ "". ""; $date_format = __('date_format', TITLE_DATE); foreach ($imgNames as $imgName) { $html .= "". "". "". "". "". "\n"; } $html .= "
".(($sortBy!='name')?("Name"):"Name")."".(($sortBy!='recent')?("Modified"):"Modified")."".__("Name")."".__("Usage")."".__("Modified")."".__("Action")."
".basename($imgName)."
".imageLinkText(basename($imgName))."
".date($date_format, filemtime($imgName))."".__('Delete')."
\n"; } else if ( $action === 'uploaded' ) { if ( DISABLE_UPLOADS ) { die('Invalid access. Uploads are disabled in the configuration.'); } $dstName = sanitizeFilename($_FILES['userfile']['name']); $dstName = str_replace(" ", "_", $dstName); // image display currently doesn't like spaces! $fileType = $_FILES['userfile']['type']; preg_match('/\.([^.]+)$/', $dstName, $matches); $fileExt = isset($matches[1]) ? $matches[1] : null; $imgExts = array('jpg','jpeg','png','gif'); $msg = ''; if (in_array($fileType, explode(',', VALID_UPLOAD_TYPES)) && in_array($fileExt, explode(',', VALID_UPLOAD_EXTS))) { $path = PAGES_PATH . "/". UPLOAD_FOLDER . "/$dstName"; $resize = isset($_POST['resize']) && $_POST['resize'] === 'true'; $doResize = $resize && in_array($fileExt, $imgExts); if ($doResize) { $exif = exif_read_data($_FILES['userfile']['tmp_name']); $size = getimagesize($_FILES['userfile']['tmp_name']); $maxsize = intval($_POST['maxsize']); $doResize = ($size[0] > $maxsize || $size[1] > $maxsize); if ($doResize) { $msg .= "trying to resize"; $finalPath = $path; $path = substr($path, 0, strlen($path)-strlen($fileExt)-1) . "-tmp-resize." . $fileExt; } } $errLevel = error_reporting(0); if ( move_uploaded_file($_FILES['userfile']['tmp_name'], $path) === true ) { $msg = "File '$dstName' uploaded! "; if ($doResize) { $newSize = array(0, 0); $idx0 = ($size[0] > $size[1]) ? 0 : 1; $idx1 = ($idx0 == 0) ? 1 : 0; $newSize[$idx0] = $maxsize; $newSize[$idx1] = (int)round($size[$idx1] * $maxsize / $size[$idx0]); $src = imagecreatefromstring(file_get_contents($path)); $dst = imagecreatetruecolor($newSize[0], $newSize[1]); if (!imagecopyresampled($dst, $src, 0, 0, 0, 0, $newSize[0], $newSize[1], $size[0], $size[1])) { $msg .= "Resizing file failed!"; } imagedestroy( $src ); if(!empty($exif['Orientation'])) { switch($exif['Orientation']) { case 8: $msg .= "Image rotated by +90°. "; $rot = imagerotate($dst,90,0); break; case 3: $msg .= "Image rotated by 180°. "; $rot = imagerotate($dst,180,0); break; case 6: $msg .= "Image rotated by -90°. "; $rot = imagerotate($dst,-90,0); break; default: $msg .= "Unknown EXIF orientation specification: ".$exif['Orientation']."!"; break; } if ($rot === false) { $msg .= "Rotation failed!"; } else { imagedestroy( $dst ); $dst = $rot; } } if ($fileExt === 'png') { imagepng($dst, $finalPath); } else if ($fileExt === 'jpg' || $fileExt === 'jpeg') { imagejpeg($dst, $finalPath); } else if ($fileExt === 'gif') { imagegif($dst, $finalPath); } unlink($path); imagedestroy( $dst ); } gitChangeHandler($msg, $msg); $msg .= " ($size[0]x$size[1]".(($doResize)?", resized to $newSize[0]x$newSize[1]":"").") successfully! Use
".imageLinkText($dstName)."
to refer to it!"; } else { $error_code = $_FILES['userfile']['error']; if ( $error_code === 0 ) { // Likely a permissions issue $msg .= __('Upload error') .": Can't write to ".$path."

\n". "Check that your permissions are set correctly."; } else { // Give generic error message $msg .= __('Upload error').", error #".$error_code."

\n". "Please see here for more information.

\n". "If you see this message, please file a bug to improve w2wiki"; } } error_reporting($errLevel); } else { $msg .= __('Upload error: invalid file type'); } redirectWithMessage(DEFAULT_PAGE, $msg); } else if ( $action === 'rename' || $action === 'delete' || $action === 'imgDelete') { if ($action === 'imgDelete') { $page = sanitizeFilename(urldecode($_REQUEST['imgName'])); } $actionName = ($action === 'delete' || $action === 'imgDelete')?__('Delete'):__('Rename'); $html .= "
"; $html .= "

".$actionName." $page ". (($action==='rename')? (__('to')." ") : "?") . "

"; $html .= "

"; $html .= "\n"; $html .= ""; $html .= ""; $html .= "

"; } else if ( $action === 'renamed' || $action === 'deleted') { // TODO: prevent relative filenames from being injected $oldPageName = sanitizeFilename($_POST['oldPageName']); $newPageName = ($action === 'deleted') ? "": sanitizeFilename($_POST['newPageName']); $msg = ''; if ($action === 'deleted') { $success = unlink(fileNameForPage($oldPageName)); } else { $success = rename(fileNameForPage($oldPageName), fileNameForPage($newPageName)); } if ($success) { $message = ($action === 'deleted') ? "Removed $oldPageName." : "Renamed $oldPageName to $newPageName."; $msg .= $message; // Change links in all pages to point to new page $pagenames = getAllPageNames(); $changedPages = array(); foreach ($pagenames as $replacePage) { $content = file_get_contents(fileNameForPage($replacePage)); $count = 0; $regexSaveOldPageName = str_replace("/", "\\/", $oldPageName); $newContent = preg_replace("/\[\[$regexSaveOldPageName([|#].*\]\]|\]\])/", (($action === 'deleted') ? "" : "[[$newPageName\\1"), $content, -1, $count); if ($count > 0) // if something changed { $changedPages[] = $replacePage." ($count ".__('matches').")"; file_put_contents(fileNameForPage($replacePage), $newContent); } } if (count($changedPages) > 0) { $msg .= "
\n".__('Updated links in the following pages:')."\n
  • "; $msg .= implode("
  • ", $changedPages); $msg .= "
"; } gitChangeHandler($message, $msg); $page = $newPageName; } else { $msg .= ($action === 'deleted') ? __('Error deleting file'): __('Error renaming file'); $page = $oldPageName; } if ($action === 'deleted' && $success) { $page = DEFAULT_PAGE; } redirectWithMessage($page, $msg); } else if ( $action === 'imgDeleted') { // TODO: prevent relative filenames from being injected $oldPageName = sanitizeFilename($_REQUEST['oldPageName']); $imgPath = PAGES_PATH . "/". UPLOAD_FOLDER . "/". $oldPageName; $success = unlink($imgPath); if ($success) { $msg = __('Image deleted'); gitChangeHandler($msg, $msg); $msg .= " (".$imgPath.")"; } else { $msg = __('Error deleting image'); $msg .= " (".$imgPath.")"; } redirectWithMessage(DEFAULT_PAGE, $msg); } else if ( $action === 'all' ) { $pageNames = getAllPageNames(); $filelist = array(); $sortBy = isset($_REQUEST['sortBy']) ? $_REQUEST['sortBy'] : 'name'; if (!in_array($sortBy, array('name', 'recent'))) { $sortBy = 'name'; } if ($sortBy === 'name') { natcasesort($pageNames); foreach($pageNames as $page) { $filelist[$page] = filemtime(fileNameForPage($page)); } } else { foreach($pageNames as $page) { $filelist[$page] = filemtime(fileNameForPage($page)); } arsort($filelist, SORT_NUMERIC); } $html .= "

".__('Total').": ".count($pageNames)." ".__("pages")."

"; $html .= ""; $html .= "". "". "". "". ""; $date_format = __('date_format', TITLE_DATE); foreach ($filelist as $pageName => $pageDate) { $html .= "". "". "". //"". /* dark mode */ "". /* light mode */ "\n"; } $html .= "
".(($sortBy!='name')?("Name"):"".__('Name')."")."".(($sortBy!='recent')?("".__('Modified').""):"".__('Modified')."")."".__('Action')."
".pageLink($pageName, $pageName)."".date( $date_format, $pageDate)."".getPageActions($pageName, $action,"-dark")."".getPageActions($pageName, $action, "")."
\n"; } else if ( $action === 'search' ) { $matches = 0; $q = $_REQUEST['q']; $html .= "

Search: $q

\n"; if ( trim($q) != "" ) { $html .= "
    \n"; $pagenames = getAllPageNames(); $found = FALSE; $matchingPages = array(); foreach ($pagenames as $searchPage) { if ($searchPage === $q) { $found = TRUE; } if (preg_match("/{$q}/i", $searchPage)) { array_unshift($matchingPages, $searchPage); ++$matches; } else { $text = file_get_contents(fileNameForPage($searchPage)); if ( preg_match("/{$q}/i", $text) ) { $matchingPages[] = $searchPage; ++$matches; } } } foreach ($matchingPages as $page) { $link = pageLink($page, $page, ($page === $q)? " class=\"literalMatch\"": ""); $html .= "
  • $link
  • \n"; } if (!$found) { $html .= "
  • ".pageLink($q, __('Create page')." '$q'", " class=\"noexist\"")."
  • "; } $html .= "
\n"; } $html .= "

$matches ".__('matches')."

\n"; } else { $html .= empty($text) ? '' : toHTML($text); } $datetime = ''; if ( ($action === 'all')) { $title = __("All"); } else if ( $action === 'upload' ) { $title = __("Upload"); } else if ( $action === 'new' ) { $title = __("New"); } else if ( $action === 'search' ) { $title = __("Search"); } else if ($filename != '') { $title = (($action === 'edit')? (__('Edit').": "):"") . $page; $date_format = __('date_format', TITLE_DATE); if ( $date_format ) { $datetime = "" . date($date_format, @filemtime($filename)) . ""; } } else { $title = __($action); } // Disable caching on the client (the iPhone is pretty agressive about this // and it can cause problems with the editing function) header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Date in the past printHeader($title, $action); print "
$title$datetime"; if ($action === 'view' || $action === 'rename' || $action === 'delete' || $action === 'edit') { print(getPageActions($page, $action, "")); } print "
\n"; print "
\n"; print " \"".\n"; print " \"".\n"; print " \"".__('New')."\"\n"; if ( !DISABLE_UPLOADS ) { print " \"".__('Upload')."\"\n"; } if ( REQUIRE_PASSWORD ) { print " ". __('Log out') . ""; } print "
\n"; print " \n
\n"; if ($action === 'edit') { printDrawer(); } print "
\n"; if (SIDEBAR_PAGE != '') { print "
\n\n"; $sidebarFile = fileNameForPage(SIDEBAR_PAGE); if (file_exists($sidebarFile)) { $text = file_get_contents($sidebarFile); } else { $text = __('Sidebar file could not be found')." ($sidebarFile)"; } print toHTML($text); print "
\n"; } if ($action === 'view' && $_GET['linkshere']) { print "
".__('What links here:')."
    "; $pagenames = getAllPageNames(); foreach($pagenames as $searchPage) { $text = file_get_contents(fileNameForPage($searchPage)); $regexSavePage = str_replace("/", "\\/", $page); if ( preg_match("/\[\[$regexSavePage/i", $text) ) { $link = pageLink($searchPage, $searchPage, ""); print("
  • $link
  • \n"); } } print "
"; } print "
\n\n"; if(isset($_SESSION['msg']) && $_SESSION['msg'] != '') { print "
".$_SESSION['msg']."
"; unset($_SESSION['msg']); } print "$html\n"; print "
\n"; printFooter();