commit b7ab6d1766d07a5ac8f005bf23a7ac633e4f4ae2 from: jrmu date: Fri Jul 18 18:09:02 2025 UTC Import sources commit - /dev/null commit + b7ab6d1766d07a5ac8f005bf23a7ac633e4f4ae2 blob - /dev/null blob + 9a9c3d661b475c86272bad8c4d122c0e8ca9f175 (mode 644) --- /dev/null +++ README.txt @@ -0,0 +1,51 @@ +This is the README.txt file for PmWiki, a wiki-based system for +collaborative creation and maintenance of websites. + +PmWiki is distributed with the following directories: + + docs/ Brief documentation, sample configuration scripts + local/ Configuration scripts + cookbook/ Recipes (add-ons) from the PmWiki Cookbook + pub/skins/ Layout templates ("skins" for custom look and feel) + pub/css/ Extra CSS stylesheet files + pub/guiedit/ Files for the Edit Form's GUIEdit module + scripts/ Scripts that are part of PmWiki + wikilib.d/ Bundled wiki pages, including + * a default Home Page + * PmWiki documentation pages + * Site-oriented interface and configuration pages + +After PmWiki is installed the following directories may also exist: + + wiki.d/ Wiki pages + uploads/ Uploaded files (page attachments) + +For quick installation advice, see docs/INSTALL.txt. + +For more extensive information about installing PmWiki, visit + https://pmwiki.org/wiki/PmWiki/Installation + +For information about running PmWiki in standalone mode without +requiring a webserver, visit + https://pmwiki.org/wiki/Cookbook/Standalone + +PmWiki is Copyright 2001-2022 Patrick R. Michaud +pmichaud@pobox.com +https://www.pmichaud.com/ + +Since 2009, PmWiki has been maintained and updated +by Petko Yotov 5ko@5ko.fr, https://www.pmwiki.org/petko + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +The GNU General Public License is distributed with this program +(see docs/COPYING.txt) and it is also available online at +https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt . blob - /dev/null blob + d04c086134e22fcef1d63ef3f37ffdb95702ddf0 (mode 644) --- /dev/null +++ cookbook/.htaccess @@ -0,0 +1,19 @@ +# This file is cookbook/.htaccess -- the default distribution contains this +# file to prevent cookbook/ scripts from being accessed directly by browsers +# (this is a potential, albeit very unlikely, security hole). +# +# If you alter or replace this file, it will likely be overwritten when +# you upgrade from one version of PmWiki to another. Be sure to save +# a copy of your alterations in another location so you can restore them, +# and you might try changing this file to be read-only to prevent a PmWiki +# upgrade from overwriting your altered version. + + + Order Deny,Allow + Deny from all + + + + Require all denied + + blob - /dev/null blob + 966e4d4334555e6ff49e1c5c5bd52ce1b1928588 (mode 644) Binary files /dev/null and cookbook/__MACOSX/._markdown-output differ blob - /dev/null blob + 5eea1c994450dccb2a943fa3a03bc74e4179e35d (mode 644) Binary files /dev/null and cookbook/__MACOSX/markdown-output/._.DS_Store differ blob - /dev/null blob + 3ffffc7eec06458dd1230caa9ee9e2b0998f879c (mode 644) Binary files /dev/null and cookbook/__MACOSX/markdown-output/._markdown-output.php differ blob - /dev/null blob + 966e4d4334555e6ff49e1c5c5bd52ce1b1928588 (mode 644) Binary files /dev/null and cookbook/__MACOSX/markdown-output/._markdownify differ blob - /dev/null blob + 5eea1c994450dccb2a943fa3a03bc74e4179e35d (mode 644) Binary files /dev/null and cookbook/__MACOSX/markdown-output/markdownify/._.DS_Store differ blob - /dev/null blob + 966e4d4334555e6ff49e1c5c5bd52ce1b1928588 (mode 644) Binary files /dev/null and cookbook/__MACOSX/markdown-output/markdownify/._LICENSE_LGPL.txt differ blob - /dev/null blob + 966e4d4334555e6ff49e1c5c5bd52ce1b1928588 (mode 644) Binary files /dev/null and cookbook/__MACOSX/markdown-output/markdownify/._TODO differ blob - /dev/null blob + 966e4d4334555e6ff49e1c5c5bd52ce1b1928588 (mode 644) Binary files /dev/null and cookbook/__MACOSX/markdown-output/markdownify/._example.php differ blob - /dev/null blob + 630b817d33c0dac01d94a9697fa76d3bfdaadb42 (mode 644) Binary files /dev/null and cookbook/__MACOSX/markdown-output/markdownify/._markdownify.php differ blob - /dev/null blob + 966e4d4334555e6ff49e1c5c5bd52ce1b1928588 (mode 644) Binary files /dev/null and cookbook/__MACOSX/markdown-output/markdownify/._markdownify_cli.php differ blob - /dev/null blob + 1289a3171dcd52771f5e6f7e63f42b9fc7636cbb (mode 644) Binary files /dev/null and cookbook/__MACOSX/markdown-output/markdownify/._markdownify_extra.php differ blob - /dev/null blob + 966e4d4334555e6ff49e1c5c5bd52ce1b1928588 (mode 644) Binary files /dev/null and cookbook/__MACOSX/markdown-output/markdownify/._parsehtml differ blob - /dev/null blob + 966e4d4334555e6ff49e1c5c5bd52ce1b1928588 (mode 644) Binary files /dev/null and cookbook/__MACOSX/markdown-output/markdownify/parsehtml/._parsehtml.php differ blob - /dev/null blob + 8612d279c6437c5ff71c52f316282f2fde1cbe20 (mode 644) --- /dev/null +++ cookbook/chess.php @@ -0,0 +1,255 @@ +'InterMap Chessboard:\{FEN\}')); + +## First, we define the Chessboard: InterMap shortcut +SDV($LinkFunctions['Chessboard:'], 'LinkIMap'); +SDV($IMap['Chessboard:'], "$PubDirUrl/chess/chessboard.php?fen=\$1"); + +## Define the pgn and {FEN} markups: +$PGNMovePattern = "[KQRBNP]?[a-h]?[1-8]?x?[a-h][1-8](?:=[KQRBNP])?|O-O-O|O-O"; +Markup('pgn', 'directives', + "/(\\d+)\\.\\s*($PGNMovePattern|\\.+)(?:[\\s+#?!]*($PGNMovePattern))?/", + "PGNMove"); +Markup('{FEN}', '>pgn', '/\\{FEN\\}/', 'FENBoard'); + +## The (:chessboard:) markup by default generates a table, since +## some PHP installations don't have GD support compiled in. +Markup('chessboard', '>{FEN}', + '/\\(:chessboard(\\s.*?)?:\\)/i', + "ChessTable"); + + +## $PGNMoves is an array of all moves (in PGN notation). The +## PGNMove($num, $white, $black) function adds moves for white and +## black into the array, removing any later moves that might be +## already present. +$PGNMoves = array(); +function PGNMove($m) { + $out = array_shift($m); + @list($num, $white, $black) = $m; + global $PGNMoves; + if ($white[0] != '.') { + $PGNMoves[$num*2-2] = $white; + array_splice($PGNMoves, $num*2-1); + } + if ($black) { + $PGNMoves[$num*2-1] = $black; + array_splice($PGNMoves, $num*2); + } + return Keep($out); +} + + +## FENBoard() plays out the moves given in $PGNMoves and returns +## the resulting position as a FEN record. Internally $board +## is maintained as a 10x10 character string, with $board{11} +## corresponding to a8 and $board{88} corresponding to h1. +## Since PGN generally gives us only the name of the piece being +## moved and the square it moved to, we have to figure out where +## that piece came from. Castling is handled by directly manipulating +## the $board to the result of the castle. The from position +## for pawn moves is computed directly by inspection, while all +## other pieces use the $legalmoves array to determine the relative +## square offsets where pieces could've come from. Once we +## figure out where the piece came from, we put the piece in its +## new position ($to) and change the old position ($from) to an +## empty square. + +function FENBoard() { + global $PGNMoves; + $num = count($PGNMoves) * 2; + # initialize the board and our allowed moves + $board = "-----------rnbqkbnr--pppppppp--........--........-" + . "-........--........--PPPPPPPP--RNBQKBNR-----------"; + $legalmoves = array( + 'K' => array(-11, -10, -9, -1, 1, 9, 10, 11), + 'Q' => array(-11, -10, -9, -1, 1, 9, 10, 11), + 'B' => array(-11, -9, 9, 11), + 'R' => array(-10, -1, 1, 10), + 'N' => array(-21, -19, -12, -8, 8, 12, 19, 21)); + + # now, walk through each move and update the board accordingly + for($i=0; $i<$num; $i++) { + $move = @$PGNMoves[$i]; # odd numbered + $isblack = $i % 2; # moves are black + if ($move == 'O-O') { # kingside castling + $board = ($isblack) + ? substr_replace($board, '.rk.', 15, 4) + : substr_replace($board, '.RK.', 85, 4); + continue; + } + if ($move == 'O-O-O') { # queenside castling + $board = ($isblack) + ? substr_replace($board, '..kr.', 11, 5) + : substr_replace($board, '..KR.', 81, 5); + continue; + } + if (preg_match( # all other moves + "/^([KQRBNP]?)([a-h]?)([1-8]?)(x?)([a-h])([1-8])(=[KQRBNP])?/", + $move, $match)) { + @list($m, $piece, $ff, $fr, $cap, $tf, $tr, $promotion) = $match; + $tf = strpos("abcdefgh", $tf)+1; + $ff = ($ff) ? strpos("abcdefgh", $ff)+1 : 0; + $to = (9-$tr)*10 + $tf; + if (!$piece) $piece = "P"; + $pp = ($isblack) ? strtolower($piece) : $piece; + if ($pp == 'P') { # white's pawn move + if ($cap) { # capture + $from = (9-$tr)*10+10+$ff; + if ($board[$to]=='.') $board[$to+10]='.'; # en passant + } + elseif ($board[$to+10]==$pp) $from=$to+10; # move + elseif ($tr==4 && $board[$to+20]==$pp) $from=$to+20; # first move + } elseif ($pp == 'p') { # black's pawn + if ($cap) { # capture + $from = (9-$tr)*10-10+$ff; + if ($board[$to]=='.') $board[$to-10]='.'; # en passant + } + elseif ($board[$to-10]==$pp) $from=$to-10; # move + elseif ($tr==5 && $board[$to-20]==$pp) $from=$to-20; # first move + } else { + # Here we look at squares along the lines for the piece + # being moved. $n contains an offset for each square along + # a valid line for the current piece. + foreach($legalmoves[$piece] as $n) { + for($from=$to+$n; $from>10 && $from<89; $from+=$n) { + # if we find the piece we're looking for, we're done + if ($board[$from] == $pp + && (!$ff || ($from%10==$ff)) + && (!$fr || ((int)($from/10)==(9-$fr)))) break 2; + # if we find anything but an empty square, try another line + if ($board[$from] != '.') continue 2; + + # kings and knights don't repeat offsets + if ($piece == 'K' || $piece == 'N') continue 2; + } + } + } + + # pawn promotions + if ($promotion) + $pp = ($isblack) ? strtolower($promotion[1]) : $promotion[1]; + + # move the piece + $board[$to] = $pp; $board[$from] = '.'; + } + } + + # now, convert the board to a FEN record + $board = PPRA(array('/-+/' =>'/', + '/\\.+/'=> 'chess_cb_length' + ), + substr($board, 11, 78)); + + # and return it + return $board; +} + +function chess_cb_length($m) { return strlen($m[0]); } + +## The ChessTable function takes a FEN string (or the current +## game position as returned by FENBoard() above) and creates an +## HTML table representing the current game position. +$ChessPieces = array( + 'k' => 'kd-60.png', 'q' => 'qd-60.png', 'r' => 'rd-60.png', + 'b' => 'bd-60.png', 'n' => 'nd-60.png', 'p' => 'pd-60.png', + 'K' => 'kl-60.png', 'Q' => 'ql-60.png', 'R' => 'rl-60.png', + 'B' => 'bl-60.png', 'N' => 'nl-60.png', 'P' => 'pl-60.png', + '.' => 'blank-60.png'); +$ChessChars = array( + 'k' => '♚', 'q' => '♛', 'r' => '♜', + 'b' => '♝', 'n' => '♞', 'p' => '♟', + 'K' => '♔', 'Q' => '♕', 'R' => '♖', + 'B' => '♗', 'N' => '♘', 'P' => '♙', + '.' => ' '); +SDV($HTMLStylesFmt['chess'], " + table.chesstable td.square1 { background-color: #cccccc; } + table.chesstable { border:1px solid #666666; line-height: 0; } "); + + +function ChessTable($m) { + global $ChessPieces, $ChessChars, $ChessPubDirUrlFmt, $FmtV; + + extract($GLOBALS['MarkupToHTML']); + $args = ParseArgs($m[1]); + + +# Some day: SDV($ChessSquareFmt, "\$PieceChar"); + SDV($ChessSquareFmt, + "\$PieceName"); + SDV($ChessDefaults, array( + 'width' => 240, 'class' => 'chesstable', + 'cellspacing' => 0, 'cellpadding' => 0)); + $args = array_merge($ChessDefaults, $args); + $fen = (@$args['']) ? $args[''][0] : FENBoard(); + $width = $args['width']; + $height = (@$args['height'] > 0) ? $args['height'] : $width; + + $tableargs = ""; + foreach(array('class', 'cellspacing', 'cellpadding', 'align', 'style') as $a) + if (isset($args[$a])) + $tableargs .= " $a='".str_replace("'", "'", $args[$a])."'"; + + ## build the 8x8 board from the FEN record + $board = str_repeat('.', 64); + $file = 0; $rank = 0; + $len = strlen($fen); + for($i=0; ($i<$len) && ($rank+$file<64); $i++) { + $k = $fen[$i]; + if (is_numeric($k) && $k > 0) { $file += $k; continue; } + if ($k == '/') { $rank += 8; $file = 0; continue; } + if ($ChessPieces[$k]) { $board[$rank+$file] = $k; $file++; } + } + + ## Now generate the table from the 8x8 board + $FmtV['$SquareWidth'] = $width / 8; + $FmtV['$SquareHeight'] = $height / 8; + for($i=0; $i<64; $i++) { + if ($i%8 == 0) $out[] = ""; + $FmtV['$PieceImg'] = $ChessPieces[$board[$i]]; + $FmtV['$PieceChar'] = $ChessChars[$board[$i]]; + $FmtV['$PieceName'] = $board[$i]; + $FmtV['$SquareColor'] = ($i + (int)($i/8)) % 2; + $out[] = FmtPageName($ChessSquareFmt, $pagename); + if ($i%8 == 7) $out[] = ""; + } + return "".Keep(implode('', $out))."
"; +} + blob - /dev/null blob + 5dfac74a1e91fa3ecc9cfc43f526410a6f7b1256 (mode 644) Binary files /dev/null and cookbook/chess.zip differ blob - /dev/null blob + 14e036b0a2a405b0c8a8cf0b9e28814397d6c65c (mode 644) Binary files /dev/null and cookbook/markdown-output/.DS_Store differ blob - /dev/null blob + 48f02b39df7a6829ccc1265a3fdb96301cd50b3f (mode 755) --- /dev/null +++ cookbook/markdown-output/markdown-output.php @@ -0,0 +1,62 @@ + + * + * Presents a wiki page in Markdown format instead of HTML + * + * Based on Milian Wolff's Markdownify HTML to Markdown converter, + * available from and + * licensed under the LGPL. + * + * To install, add the following line to your config file : + include_once("$FarmD/cookbook/markdown-output/markdown-output.php"); + * + * For more information, please see the online documentation at + * http://www.pmwiki.org/wiki/Cookbook/MarkdownOutput + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +$RecipeInfo['MarkdownOutput']['Version'] = '2020-08-12'; + +$HandleActions['markdown'] = 'HandleMarkdownOutput'; + +if ($action=='markdown') require_once dirname(__FILE__).'/markdownify/markdownify_extra.php'; + +function HandleMarkdownOutput( $pagename, $auth = 'read' ) { + global $DefaultPageTextFmt, $PageNotFoundHeaderFmt, $HTTPHeaders, $TmplDisplay, + $MarkdownExtra, $MarkdownLinksAfterEachParagraph, $MarkdownBodyWidth, $MarkdownKeepHTML, $MarkdownIgnoreForeignAttributes; + $page = RetrieveAuthPage( $pagename, $auth, true, READPAGE_CURRENT ); + if (!$page) Abort("?cannot markdown $pagename"); + PCache($pagename,$page); + if ( PageExists($pagename) ) $text = @$page['text']; + else { + SDV($DefaultPageTextFmt, '(:include $[{$SiteGroup}.PageNotFound]:)' ); + $text = FmtPageName( $DefaultPageTextFmt, $pagename ); + SDV($PageNotFoundHeaderFmt, 'HTTP/1.1 404 Not Found' ); + SDV($HTTPHeaders['status'], $PageNotFoundHeaderFmt ); + } + $text = '(:groupheader:)'.@$text.'(:groupfooter:)'; + $html = MarkupToHTML( $pagename, $text ); + + if ( empty($TmplDisplay['PageTitleFmt']) ) $html = "

".PageVar($pagename,'$Title')."

\n".$html; + + foreach ($HTTPHeaders as $h) { + $h = preg_replace( '!^Content-type:\\s+text/html!i', 'Content-type: text/plain', $h ); + header($h); + } + + SDV($MarkdownLinksAfterEachParagraph, FALSE ); + SDV($MarkdownBodyWidth, FALSE ); + SDV($MarkdownKeepHTML, FALSE ); + SDV($MarkdownIgnoreForeignAttributes, FALSE ); + $mdf = IsEnabled($MarkdownExtra,TRUE) ? 'Markdownify_Extra' : 'Markdownify'; + $md = new $mdf( $MarkdownLinksAfterEachParagraph, $MarkdownBodyWidth, $MarkdownKeepHTML, $MarkdownIgnoreForeignAttributes ); + + echo $md->parseString($html); +} + blob - /dev/null blob + e4800adb6e46c1b04b3500812211078c06276331 (mode 644) Binary files /dev/null and cookbook/markdown-output/markdownify/.DS_Store differ blob - /dev/null blob + 5ab7695ab8cabe0c5c8a814bb0ab1e8066578fbb (mode 644) --- /dev/null +++ cookbook/markdown-output/markdownify/LICENSE_LGPL.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + blob - /dev/null blob + 06ec8508ba6be9f3a1a920bac7316192fa924f22 (mode 644) --- /dev/null +++ cookbook/markdown-output/markdownify/TODO @@ -0,0 +1,29 @@ +Markdownify +=========== +* handle non-markdownifiable lists (i.e. `
  • asdf
`) +* organize methods better (i.e. flushlinebreaks & setlinebreaks close to each other) +* take a look at function names etc. +* is the new (in rev. 93) lastclosedtag property needed? +* word wrapping (some work is done but it's still very buggy) + + +Markdownify Extra +================= + +* handle table alignment with KEEP_HTML=false +* handle tables without headings when KEEP_HTML=false is set +* handle Markdown inside non-markdownable tags + + +Implementation Thoughts +======================= +* non-markdownifiable lists and markdown inside non-markdownable tags as well as the current + table implementation could be rewritten by using a rollback mechanism. + + example: + +
  • asdf
  • asdf
+ + we come to `
    `, know that this might fail and create a snapshot of our current parser + we keep on parsing and when we reach `
  • ` we gotta rollback and keep this + list in HTML format. blob - /dev/null blob + ef86dca83caf479a1fabd94d7f748f4bb4a4a600 (mode 644) --- /dev/null +++ cookbook/markdown-output/markdownify/example.php @@ -0,0 +1,51 @@ +parseString($_POST['input']); + } else { + $_POST['input'] = ''; + } +?> + + + + HTML to Markdown Converter + + + +
    +
    + HTML Input + +
    + + + + +
    + +

    BACK

    +
    + + + \ No newline at end of file blob - /dev/null blob + d23b4d5c14d5e61112a749c3b4e4c40435507f2a (mode 644) --- /dev/null +++ cookbook/markdown-output/markdownify/markdownify.php @@ -0,0 +1,1194 @@ +, ) + * @license LGPL, see LICENSE_LGPL.txt and the summary below + * @copyright (C) 2007 Milian Wolff + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * HTML Parser, see http://sf.net/projects/parseHTML + */ +require_once dirname(__FILE__).'/parsehtml/parsehtml.php'; + +/** + * default configuration + */ +define('MDFY_LINKS_EACH_PARAGRAPH', false); +define('MDFY_BODYWIDTH', false); +define('MDFY_KEEPHTML', true); +define('MDFY_IGNORE_FOREIGN_ATTRIBUTES', false); + +/** + * HTML to Markdown converter class + */ +class Markdownify { + /** + * html parser object + * + * @var parseHTML + */ + var $parser; + /** + * markdown output + * + * @var string + */ + var $output; + /** + * stack with tags which where not converted to html + * + * @var array + */ + var $notConverted = array(); + /** + * skip conversion to markdown + * + * @var bool + */ + var $skipConversion = false; + /* options */ + /** + * keep html tags which cannot be converted to markdown + * + * @var bool + */ + var $keepHTML = false; + /** + * wrap output, set to 0 to skip wrapping + * + * @var int + */ + var $bodyWidth = 0; + /** + * minimum body width + * + * @var int + */ + var $minBodyWidth = 25; + /** + * display links after each paragraph + * + * @var bool + */ + var $linksAfterEachParagraph = false; + /** + * constructor, set options, setup parser + * + * @param bool $linksAfterEachParagraph wether or not to flush stacked links after each paragraph + * defaults to false + * @param int $bodyWidth wether or not to wrap the output to the given width + * defaults to false + * @param bool $keepHTML wether to keep non markdownable HTML or to discard it + * defaults to true (HTML will be kept) + * @return void + */ + function __construct($linksAfterEachParagraph = MDFY_LINKS_EACH_PARAGRAPH, $bodyWidth = MDFY_BODYWIDTH, $keepHTML = MDFY_KEEPHTML, $ignoreForeignAttributes = MDFY_IGNORE_FOREIGN_ATTRIBUTES) { + $this->linksAfterEachParagraph = $linksAfterEachParagraph; + $this->keepHTML = $keepHTML; + $this->ignoreForeignAttributes = $ignoreForeignAttributes; + + if ($bodyWidth > $this->minBodyWidth) { + $this->bodyWidth = intval($bodyWidth); + } else { + $this->bodyWidth = false; + } + + $this->parser = new parseHTML; + $this->parser->noTagsInCode = true; + + # we don't have to do this every time + $search = array(); + $replace = array(); + foreach ($this->escapeInText as $s => $r) { + array_push($search, '#(?escapeInText = array( + 'search' => $search, + 'replace' => $replace + ); + } + /** + * parse a HTML string + * + * @param string $html + * @return string markdown formatted + */ + function parseString($html) { + $this->parser->html = $html; + $this->parse(); + return $this->output; + } + /** + * tags with elements which can be handled by markdown + * + * @var array + */ + var $isMarkdownable = array( + 'p' => array(), + 'ul' => array(), + 'ol' => array(), + 'li' => array(), + 'br' => array(), + 'blockquote' => array(), + 'code' => array(), + 'pre' => array(), + 'a' => array( + 'href' => 'required', + 'title' => 'optional', + ), + 'strong' => array(), + 'b' => array(), + 'em' => array(), + 'i' => array(), + 'img' => array( + 'src' => 'required', + 'alt' => 'optional', + 'title' => 'optional', + ), + 'h1' => array(), + 'h2' => array(), + 'h3' => array(), + 'h4' => array(), + 'h5' => array(), + 'h6' => array(), + 'hr' => array(), + ); + /** + * html tags to be ignored (contents will be parsed) + * + * @var array + */ + var $ignore = array( + 'html', + 'body', + ); + /** + * html tags to be dropped (contents will not be parsed!) + * + * @var array + */ + var $drop = array( + 'script', + 'head', + 'style', + 'form', + 'area', + 'object', + 'param', + 'iframe', + ); + /** + * Markdown indents which could be wrapped + * @note: use strings in regex format + * + * @var array + */ + var $wrappableIndents = array( + '\* ', # ul + '\d. ', # ol + '\d\d. ', # ol + '> ', # blockquote + '', # p + ); + /** + * list of chars which have to be escaped in normal text + * @note: use strings in regex format + * + * @var array + * + * TODO: what's with block chars / sequences at the beginning of a block? + */ + var $escapeInText = array( + '([-*_])([ ]{0,2}\1){2,}' => '\\\\$0|', # hr + '\*\*([^*\s]+)\*\*' => '\*\*$1\*\*', # strong + '\*([^*\s]+)\*' => '\*$1\*', # em + '__(?! |_)(.+)(?!<_| )__' => '\_\_$1\_\_', # em + '_(?! |_)(.+)(?!<_| )_' => '\_$1\_', # em + '`(.+)`' => '\`$1\`', # code + '\[(.+)\](\s*\()' => '\[$1\]$2', # links: [text] (url) => [text\] (url) + '\[(.+)\](\s*)\[(.*)\]' => '\[$1\]$2\[$3\]', # links: [text][id] => [text\][id\] + ); + /** + * wether last processed node was a block tag or not + * + * @var bool + */ + var $lastWasBlockTag = false; + /** + * name of last closed tag + * + * @var string + */ + var $lastClosedTag = ''; + /** + * iterate through the nodes and decide what we + * shall do with the current node + * + * @param void + * @return void + */ + function parse() { + $this->output = ''; + # drop tags + $this->parser->html = preg_replace('#<('.implode('|', $this->drop).')[^>]*>.*#sU', '', $this->parser->html); + while ($this->parser->nextNode()) { + switch ($this->parser->nodeType) { + case 'doctype': + break; + case 'pi': + case 'comment': + if ($this->keepHTML) { + $this->flushLinebreaks(); + $this->out($this->parser->node); + $this->setLineBreaks(2); + } + # else drop + break; + case 'text': + $this->handleText(); + break; + case 'tag': + if (in_array($this->parser->tagName, $this->ignore)) { + break; + } + if ($this->parser->isStartTag) { + $this->flushLinebreaks(); + } + if ($this->skipConversion) { + $this->isMarkdownable(); # update notConverted + $this->handleTagToText(); + break; + } + if (!$this->parser->keepWhitespace && $this->parser->isBlockElement && $this->parser->isStartTag) { + $this->parser->html = ltrim($this->parser->html); + } + if ($this->isMarkdownable()) { + if ($this->parser->isBlockElement && $this->parser->isStartTag && !$this->lastWasBlockTag && !empty($this->output)) { + if (!empty($this->buffer)) { + $str =& $this->buffer[count($this->buffer) -1]; + } else { + $str =& $this->output; + } + if (substr($str, -strlen($this->indent)-1) != "\n".$this->indent) { + $str .= "\n".$this->indent; + } + } + $func = 'handleTag_'.$this->parser->tagName; + $this->$func(); + if ($this->linksAfterEachParagraph && $this->parser->isBlockElement && !$this->parser->isStartTag && empty($this->parser->openTags)) { + $this->flushStacked(); + } + if (!$this->parser->isStartTag) { + $this->lastClosedTag = $this->parser->tagName; + } + } else { + $this->handleTagToText(); + $this->lastClosedTag = ''; + } + break; + default: + trigger_error('invalid node type', E_USER_ERROR); + break; + } + $this->lastWasBlockTag = $this->parser->nodeType == 'tag' && $this->parser->isStartTag && $this->parser->isBlockElement; + } + if (!empty($this->buffer)) { + trigger_error('buffer was not flushed, this is a bug. please report!', E_USER_WARNING); + while (!empty($this->buffer)) { + $this->out($this->unbuffer()); + } + } + ### cleanup + $this->output = rtrim(str_replace('&', '&', str_replace('<', '<', str_replace('>', '>', $this->output)))); + # end parsing, flush stacked tags + $this->flushStacked(); + $this->stack = array(); + } + /** + * check if current tag can be converted to Markdown + * + * @param void + * @return bool + */ + function isMarkdownable() { + if (!isset($this->isMarkdownable[$this->parser->tagName])) { + # simply not markdownable + return false; + } + if ($this->parser->isStartTag) { + $return = true; + if ($this->keepHTML) { + $diff = array_diff(array_keys($this->parser->tagAttributes), array_keys($this->isMarkdownable[$this->parser->tagName])); + if (!empty($diff) && !$this->ignoreForeignAttributes) { + # non markdownable attributes given + $return = false; + } + } + if ($return) { + foreach ($this->isMarkdownable[$this->parser->tagName] as $attr => $type) { + if ($type == 'required' && !isset($this->parser->tagAttributes[$attr])) { + # required markdown attribute not given + $return = false; + break; + } + } + } + if (!$return) { + array_push($this->notConverted, $this->parser->tagName.'::'.implode('/', $this->parser->openTags)); + } + return $return; + } else { + if (!empty($this->notConverted) && end($this->notConverted) === $this->parser->tagName.'::'.implode('/', $this->parser->openTags)) { + array_pop($this->notConverted); + return false; + } + return true; + } + } + /** + * output all stacked tags + * + * @param void + * @return void + */ + function flushStacked() { + # links + foreach ($this->stack as $tag => $a) { + if (!empty($a)) { + call_user_func(array(&$this, 'flushStacked_'.$tag)); + } + } + } + /** + * output link references (e.g. [1]: http://example.com "title"); + * + * @param void + * @return void + */ + function flushStacked_a() { + $out = false; + foreach ($this->stack['a'] as $k => $tag) { + if (!isset($tag['unstacked'])) { + if (!$out) { + $out = true; + $this->out("\n\n", true); + } else { + $this->out("\n", true); + } + $this->out(' ['.$tag['linkID'].']: '.$tag['href'].(isset($tag['title']) ? ' "'.$tag['title'].'"' : ''), true); + $tag['unstacked'] = true; + $this->stack['a'][$k] = $tag; + } + } + } + /** + * flush enqued linebreaks + * + * @param void + * @return void + */ + function flushLinebreaks() { + if ($this->lineBreaks && !empty($this->output)) { + $this->out(str_repeat("\n".$this->indent, $this->lineBreaks), true); + } + $this->lineBreaks = 0; + } + /** + * handle non Markdownable tags + * + * @param void + * @return void + */ + function handleTagToText() { + if (!$this->keepHTML) { + if (!$this->parser->isStartTag && $this->parser->isBlockElement) { + $this->setLineBreaks(2); + } + } else { + # dont convert to markdown inside this tag + /** TODO: markdown extra **/ + if (!$this->parser->isEmptyTag) { + if ($this->parser->isStartTag) { + if (!$this->skipConversion) { + $this->skipConversion = $this->parser->tagName.'::'.implode('/', $this->parser->openTags); + } + } else { + if ($this->skipConversion == $this->parser->tagName.'::'.implode('/', $this->parser->openTags)) { + $this->skipConversion = false; + } + } + } + + if ($this->parser->isBlockElement) { + if ($this->parser->isStartTag) { + if (in_array($this->parent(), array('ins', 'del'))) { + # looks like ins or del are block elements now + $this->out("\n", true); + $this->indent(' '); + } + if ($this->parser->tagName != 'pre') { + + $this->out($this->parser->node."\n".$this->indent); + if (!$this->parser->isEmptyTag) { + $this->indent(' '); + } else { + $this->setLineBreaks(1); + } + $this->parser->html = ltrim($this->parser->html); + } else { + # don't indent inside
     tags
    +            $this->out($this->parser->node);
    +            static $indent;
    +            $indent =  $this->indent;
    +            $this->indent = '';
    +          }
    +        } else {
    +          if (!$this->parser->keepWhitespace) {
    +            $this->output = rtrim($this->output);
    +          }
    +          if ($this->parser->tagName != 'pre') {
    +            $this->indent('  ');
    +            $this->out("\n".$this->indent.$this->parser->node);
    +          } else {
    +            # reset indentation
    +            $this->out($this->parser->node);
    +            static $indent;
    +            $this->indent = $indent;
    +          }
    +
    +          if (in_array($this->parent(), array('ins', 'del'))) {
    +            # ins or del was block element
    +            $this->out("\n");
    +            $this->indent('  ');
    +          }
    +          if ($this->parser->tagName == 'li') {
    +            $this->setLineBreaks(1);
    +          } else {
    +            $this->setLineBreaks(2);
    +          }
    +        }
    +      } else {
    +        $this->out($this->parser->node);
    +      }
    +      if (in_array($this->parser->tagName, array('code', 'pre'))) {
    +        if ($this->parser->isStartTag) {
    +          $this->buffer();
    +        } else {
    +          # add stuff so cleanup just reverses this
    +          $this->out(str_replace('<', '&lt;', str_replace('>', '&gt;', $this->unbuffer())));
    +        }
    +      }
    +    }
    +  }
    +  /**
    +   * handle plain text
    +   *
    +   * @param void
    +   * @return void
    +   */
    +  function handleText() {
    +    if ($this->hasParent('pre') && strpos($this->parser->node, "\n") !== false) {
    +      $this->parser->node = str_replace("\n", "\n".$this->indent, $this->parser->node);
    +    }
    +    if (!$this->hasParent('code') && !$this->hasParent('pre')) {
    +      # entity decode
    +      $this->parser->node = $this->decode($this->parser->node);
    +      if (!$this->skipConversion) {
    +        # escape some chars in normal Text
    +        $this->parser->node = preg_replace($this->escapeInText['search'], $this->escapeInText['replace'], $this->parser->node);
    +      }
    +    } else {
    +      $this->parser->node = str_replace(array('"', '&apos'), array('"', '\''), $this->parser->node);
    +    }
    +    $this->out($this->parser->node);
    +    $this->lastClosedTag = '';
    +  }
    +  /**
    +   * handle  and  tags
    +   *
    +   * @param void
    +   * @return void
    +   */
    +  function handleTag_em() {
    +    $this->out('*', true);
    +  }
    +  function handleTag_i() {
    +    $this->handleTag_em();
    +  }
    +  /**
    +   * handle  and  tags
    +   *
    +   * @param void
    +   * @return void
    +   */
    +  function handleTag_strong() {
    +    $this->out('**', true);
    +  }
    +  function handleTag_b() {
    +    $this->handleTag_strong();
    +  }
    +  /**
    +   * handle 

    tags + * + * @param void + * @return void + */ + function handleTag_h1() { + $this->handleHeader(1); + } + /** + * handle

    tags + * + * @param void + * @return void + */ + function handleTag_h2() { + $this->handleHeader(2); + } + /** + * handle

    tags + * + * @param void + * @return void + */ + function handleTag_h3() { + $this->handleHeader(3); + } + /** + * handle

    tags + * + * @param void + * @return void + */ + function handleTag_h4() { + $this->handleHeader(4); + } + /** + * handle

    tags + * + * @param void + * @return void + */ + function handleTag_h5() { + $this->handleHeader(5); + } + /** + * handle
    tags + * + * @param void + * @return void + */ + function handleTag_h6() { + $this->handleHeader(6); + } + /** + * number of line breaks before next inline output + */ + var $lineBreaks = 0; + /** + * handle header tags (

    -

    ) + * + * @param int $level 1-6 + * @return void + */ + function handleHeader($level) { + if ($this->parser->isStartTag) { + $this->out(str_repeat('#', $level).' ', true); + } else { + $this->setLineBreaks(2); + } + } + /** + * handle

    tags + * + * @param void + * @return void + */ + function handleTag_p() { + if (!$this->parser->isStartTag) { + $this->setLineBreaks(2); + } + } + /** + * handle tags + * + * @param void + * @return void + */ + function handleTag_a() { + if ($this->parser->isStartTag) { + $this->buffer(); + if (isset($this->parser->tagAttributes['title'])) { + $this->parser->tagAttributes['title'] = $this->decode($this->parser->tagAttributes['title']); + } else { + $this->parser->tagAttributes['title'] = null; + } + $this->parser->tagAttributes['href'] = $this->decode(trim($this->parser->tagAttributes['href'])); + $this->stack(); + } else { + $tag = $this->unstack(); + $buffer = $this->unbuffer(); + + if (empty($tag['href']) && empty($tag['title'])) { + # empty links... testcase mania, who would possibly do anything like that?! + $this->out('['.$buffer.']()', true); + return; + } + + if ($buffer == $tag['href'] && empty($tag['title'])) { + # + $this->out('<'.$buffer.'>', true); + return; + } + + $bufferDecoded = $this->decode(trim($buffer)); + if (substr($tag['href'], 0, 7) == 'mailto:' && 'mailto:'.$bufferDecoded == $tag['href']) { + if (is_null($tag['title'])) { + # + $this->out('<'.$bufferDecoded.'>', true); + return; + } + # [mail@example.com][1] + # ... + # [1]: mailto:mail@example.com Title + $tag['href'] = 'mailto:'.$bufferDecoded; + } + # [This link][id] + foreach ($this->stack['a'] as $tag2) { + if ($tag2['href'] == $tag['href'] && $tag2['title'] === $tag['title']) { + $tag['linkID'] = $tag2['linkID']; + break; + } + } + if (!isset($tag['linkID'])) { + $tag['linkID'] = count($this->stack['a']) + 1; + array_push($this->stack['a'], $tag); + } + + $this->out('['.$buffer.']['.$tag['linkID'].']', true); + } + } + /** + * handle tags + * + * @param void + * @return void + */ + function handleTag_img() { + if (!$this->parser->isStartTag) { + return; # just to be sure this is really an empty tag... + } + + if (isset($this->parser->tagAttributes['title'])) { + $this->parser->tagAttributes['title'] = $this->decode($this->parser->tagAttributes['title']); + } else { + $this->parser->tagAttributes['title'] = null; + } + if (isset($this->parser->tagAttributes['alt'])) { + $this->parser->tagAttributes['alt'] = $this->decode($this->parser->tagAttributes['alt']); + } else { + $this->parser->tagAttributes['alt'] = null; + } + + if (empty($this->parser->tagAttributes['src'])) { + # support for "empty" images... dunno if this is really needed + # but there are some testcases which do that... + if (!empty($this->parser->tagAttributes['title'])) { + $this->parser->tagAttributes['title'] = ' '.$this->parser->tagAttributes['title'].' '; + } + $this->out('!['.$this->parser->tagAttributes['alt'].']('.$this->parser->tagAttributes['title'].')', true); + return; + } else { + $this->parser->tagAttributes['src'] = $this->decode($this->parser->tagAttributes['src']); + } + + # [This link][id] + $link_id = false; + if (!empty($this->stack['a'])) { + foreach ($this->stack['a'] as $tag) { + if ($tag['href'] == $this->parser->tagAttributes['src'] + && $tag['title'] === $this->parser->tagAttributes['title']) { + $link_id = $tag['linkID']; + break; + } + } + } else { + $this->stack['a'] = array(); + } + if (!$link_id) { + $link_id = count($this->stack['a']) + 1; + $tag = array( + 'href' => $this->parser->tagAttributes['src'], + 'linkID' => $link_id, + 'title' => $this->parser->tagAttributes['title'] + ); + array_push($this->stack['a'], $tag); + } + + $this->out('!['.$this->parser->tagAttributes['alt'].']['.$link_id.']', true); + } + /** + * handle tags + * + * @param void + * @return void + */ + function handleTag_code() { + if ($this->hasParent('pre')) { + # ignore code blocks inside

    +      return;
    +    }
    +    if ($this->parser->isStartTag) {
    +      $this->buffer();
    +    } else {
    +      $buffer = $this->unbuffer();
    +      # use as many backticks as needed
    +      preg_match_all('#`+#', $buffer, $matches);
    +      if (!empty($matches[0])) {
    +        rsort($matches[0]);
    +
    +        $ticks = '`';
    +        while (true) {
    +          if (!in_array($ticks, $matches[0])) {
    +            break;
    +          }
    +          $ticks .= '`';
    +        }
    +      } else {
    +        $ticks = '`';
    +      }
    +      if ($buffer[0] == '`' || substr($buffer, -1) == '`') {
    +        $buffer = ' '.$buffer.' ';
    +      }
    +      $this->out($ticks.$buffer.$ticks, true);
    +    }
    +  }
    +  /**
    +   * handle 
     tags
    +   *
    +   * @param void
    +   * @return void
    +   */
    +  function handleTag_pre() {
    +    if ($this->keepHTML && $this->parser->isStartTag) {
    +      # check if a simple  follows
    +      if (!preg_match('#^\s*#Us', $this->parser->html)) {
    +        # this is no standard markdown code block
    +        $this->handleTagToText();
    +        return;
    +      }
    +    }
    +    $this->indent('    ');
    +    if (!$this->parser->isStartTag) {
    +      $this->setLineBreaks(2);
    +    } else {
    +      $this->parser->html = ltrim($this->parser->html);
    +    }
    +  }
    +  /**
    +   * handle 
    tags + * + * @param void + * @return void + */ + function handleTag_blockquote() { + $this->indent('> '); + } + /** + * handle
    \n"; + } + StopWatch("Blocklist: end $pagename"); +} + + +## BlocklistDownload() handles retrieving blocklists from +## external sources into PmWiki pages. If it's able to +## download an updated list, it uses that; otherwise it leaves +## any existing list alone. +function BlocklistDownload($pagename, $dir = '') { + global $BlocklistDownloadFmt, $BlocklistDownload, $FmtV; + + if ($dir) { flush(); chdir($dir); } + SDV($BlocklistDownloadFmt, " + [@ +## blocklist-note: NOTE: This page is automatically generated by blocklist.php +## blocklist-note: NOTE: Any edits to this page may be lost! +## blocklist-url: \$BlocklistDownloadUrl +## blocklist-when: \$CurrentTimeISO +# blocklist-format: \$BlocklistFormat +\$BlocklistData + @] +"); + + ## get the existing blocklist page + $bd = &$BlocklistDownload[$pagename]; + $page = ReadPage($pagename, READPAGE_CURRENT); + + ## try to retrieve the remote data + $blocklistdata = @file($bd['url']); + + ## if we didn't get it, and we don't already have text, save a + ## note in the page so we know what happened + if (!$blocklistdata && !@$page['text']) { + $auf = ini_get('allow_url_fopen'); + $blocklistdata = "#### Unable to download blocklist (allow_url_fopen=$auf)"; + } + + ## if we have some new text to save, let's format it and save it + if ($blocklistdata) { + $blocklistdata = implode('', (array)$blocklistdata); + $blocklistdata = preg_replace('/^##blocklist.*/m', '', $blocklistdata); + $FmtV['$BlocklistData'] = $blocklistdata; + $FmtV['$BlocklistDownloadUrl'] = $bd['url']; + $FmtV['$BlocklistFormat'] = $bd['format']; + $page['text'] = FmtPageName($BlocklistDownloadFmt, $pagename); + SDV($page['passwdread'], '@lock'); + } + + ## save our updated(?) blocklist page + WritePage($pagename, $page); +} blob - /dev/null blob + 1e3b4820a94fbd9625ddf6df964af29a3b87f17f (mode 644) --- /dev/null +++ scripts/caches.php @@ -0,0 +1,67 @@ + $LastModTime) $LastModTime = $v; } + +if (@$EnableIMSCaching) { + SDV($IMSCookie, $CookiePrefix.'imstime'); + SDV($IMSCookieExpires, $Now + 60*60*24*30); + SDV($IMSInvalidators, array('authpw', 'author')); + $LogoutCookies[] = $IMSCookie; + + if ($IMSCookie) { + $IMSTime = @$_COOKIE[$IMSCookie]; + if ($IMSTime < $LastModTime + || array_intersect($IMSInvalidators, array_keys($_POST))) { + $IMSTime = $Now; + pmsetcookie($IMSCookie, $IMSTime, $IMSCookieExpires, '/'); + } + } else $IMSTime = $LastModTime; + + if (in_array($action, (array)$CacheActions)) { + $HTTPLastMod = gmdate('D, d M Y H:i:s \G\M\T',$IMSTime); + $HTTPHeaders[] = "Cache-Control: no-cache"; + $HTTPHeaders[] = "Last-Modified: $HTTPLastMod"; + if (@$_SERVER['HTTP_IF_MODIFIED_SINCE']==$HTTPLastMod) { + header("HTTP/1.0 304 Not Modified"); + header("Cache-Control: no-cache"); + header("Expires: "); + header("Last-Modified: $HTTPLastMod"); + exit(); + } + } +} + +if ($NoHTMLCache + || !@$PageCacheDir + || count($_POST) > 0 + || count($_GET) > 1 + || (count($_GET) == 1 && !@$_GET['n'])) { $NoHTMLCache |= 1; return; } + +mkdirp($PageCacheDir); +if (!file_exists("$PageCacheDir/.htaccess") + && $fp = @fopen("$PageCacheDir/.htaccess", "w")) + { fwrite($fp, $DenyHtaccessContent); fclose($fp); } + +SDV($PageCacheFileFmt, "%s/%s,cache"); +SDV($PageCacheFile, sprintf($PageCacheFileFmt, $PageCacheDir, $pagename)); + +if (file_exists($PageCacheFile) && @filemtime($PageCacheFile) < $LastModTime) + @unlink($PageCacheFile); + blob - /dev/null blob + 50539ae8ebb78d39eaded84923246d721354cfa1 (mode 644) --- /dev/null +++ scripts/creole.php @@ -0,0 +1,82 @@ +(.+?)\\*\\*)(?!\\S)|(?$1$2
    '); + +## //emphasized// +Markup('//', 'inline', + '/(?$1
    '); + +## == Headings == +Markup('^=', 'block', + '/^(={1,6})\\s?(.*?)(\\s*=*\\s*)$/', + "MarkupCreole"); + +## Line breaks +Markup('\\\\', 'inline', '/\\\\\\\\/', '
    '); + +## Preformatted +Markup('^{{{', '[=', + "/^\\{\\{\\{\n(.*?\n)\\}\\}\\}[^\\S\n]*\n/sm", + "MarkupCreole"); +Markup('{{{', '>{{{', + '/\\{\\{\\{(.*?)\\}\\}\\}/s', + "MarkupCreole"); + +## Tables +Markup('|-table', '>^||', + '/^\\|(.*)$/', + "MarkupCreole"); + +## Images +Markup('{{', 'inline', + '/\\{\\{(?>(\\L))([^|\\]]*)(?:\\|\\s*(.*?)\\s*)?\\}\\}/', + "MarkupCreole"); + +function MarkupCreole($m) { + extract($GLOBALS["MarkupToHTML"]); # get $pagename, $markupid + switch ($markupid) { + case '^=': + return '<:block,1>'.$m[2].''; + case '^{{{': + return Keep('
    '.$m[1].'
    '); + case '{{{': + return Keep(''.$m[1].''); + case '|-table': + return FormatTableRow($m[0], '\\|'); + case '{{': + return Keep($GLOBALS['LinkFunctions'][$m[1]]($pagename, $m[1], $m[2], $m[3], + $m[1].$m[2], $GLOBALS['ImgTagFmt']),'L'); + } +} + +## GUIButtons +SDVA($GUIButtons, array( + 'em' => array(100, "//", "//", '$[Emphasized]', + '$GUIButtonDirUrlFmt/em.gif"$[Emphasized (italic)]"', + '$[ak_em]'), + 'strong' => array(110, "**", "**", '$[Strong]', + '$GUIButtonDirUrlFmt/strong.gif"$[Strong (bold)]"', + '$[ak_strong]'), + 'h2' => array(400, '\\n== ', ' ==\\n', '$[Heading]', + '$GUIButtonDirUrlFmt/h.gif"$[Heading]"'), + + )); + blob - /dev/null blob + c3af7f0808c0d1cd6cde48becbb4aa67e3d97572 (mode 644) --- /dev/null +++ scripts/crypt.php @@ -0,0 +1,43 @@ +

    + Enter password to encrypt: + + + +

    ", + $pagename); + if ($passwd) { + $crypt = pmcrypt($passwd); + echo "

    Encrypted password = $crypt

    "; + echo "

    To set a site-wide password, insert the line below + in your config.php file,
    replacing 'type' with + one of 'admin', 'read', 'edit', + or 'attr'.
    See PasswordsAdmin for more + details.

    +
      \$DefaultPasswords['type']='$crypt';
    "; + } + PrintFmt($pagename,$HTMLEndFmt); +} + blob - /dev/null blob + 67fd5a3e1fc2e00c2abadaa707b332c3c8df449a (mode 644) --- /dev/null +++ scripts/diag.php @@ -0,0 +1,174 @@ +$m) { + $out .= sprintf("%-16s %-16s %-16s %s\n",$id,@$m['cmd'],@$m['seq'], @$m['dbg']); + if (@$m['dbg']) $dbg++; + } + if ($dbg) $out .= " +[!] Markup rules possibly incompatible with PHP 5.5 or newer. + Please contact the recipe maintainer for update + or see www.pmwiki.org/wiki/PmWiki/CustomMarkup"; + return $out; +} + +$HandleActions['ruleset'] = 'HandleRuleset'; + +function HandleRuleset($pagename) { + header("Content-type: text/plain"); + print Ruleset(); +} + +function StopWatchHTML($pagename, $print = 0) { + global $StopWatch; + StopWatch('now'); + $l = strlen(count($StopWatch)); + $out = '
    ';
    +  foreach((array)$StopWatch as $i => $x)
    +    $out .= sprintf("%{$l}d: %s\n", $i, $x);
    +  $out .= '
    '; + if (is_array($StopWatch)) array_pop($StopWatch); + if ($print) print $out; + return $out; +} + +### From Cookbook:RecipeCheck +/* Copyright 2007-2019 Patrick R. Michaud (pmichaud@pobox.com) + + This recipe adds ?action=recipecheck to a site. When activated, + ?action=recipecheck fetches the current list of Cookbook recipes + and their versions from pmwiki.org. It then compares this list + with the versions of any installed recipes on the local site + and reports any differences. + + By default the recipe restricts ?action=recipecheck to admins. + + Note that not all recipes currently follow PmWiki's + recipecheck standard (feel free to report these to the pmwiki-users + mailing list). + + * 2007-04-17: Added suggestions by Feral + - explicit black text + - skip non-php files and directories + * 2019-11-28: Added to scripts/diag.php by Petko +*/ +if($action=='recipecheck') { + SDV($HandleActions['recipecheck'], 'HandleRecipeCheckCore'); + SDV($HandleAuth['recipecheck'], 'admin'); + SDV($ActionTitleFmt['recipecheck'], '| $[Recipe Check]'); + + SDV($WikiStyleApply['tr'], 'tr'); + SDV($HTMLStylesFmt['recipecheck'], ' + table.recipecheck tr.ok { color:black; background-color:#ccffcc; } + table.recipecheck tr.check { color:black; background-color:#ffffcc; } + table.recipecheck { border:1px solid #cccccc; padding:4px; } + '); + + SDV($RecipeListUrl, 'http://www.pmwiki.org/pmwiki/recipelist'); +} + +function HandleRecipeCheckCore($pagename, $auth = 'admin') { + global $RecipeListUrl, $Version, $RecipeInfo, + $RecipeCheckFmt, $PageStartFmt, $PageEndFmt; + $page = RetrieveAuthPage($pagename, $auth, true, READPAGE_CURRENT); + if (!$page) Abort('?admin access required'); + $cvinfo = GetRecipeListCore($RecipeListUrl); + if (!$cvinfo) { + $msg = "Unable to retrieve cookbook data from $RecipeListUrl\n"; + $allow_url_fopen = ini_get('allow_url_fopen'); + if (!$allow_url_fopen) $msg .= " +

    It appears that your PHP environment isn't allowing + the recipelist to be downloaded from pmwiki.org + (allow_url_fopen = $allow_url_fopen)."; + Abort($msg); + } + $rinfo['PmWiki:Upgrades'] = $Version; + ScanRecipeInfoCore('cookbook', $cvinfo); + foreach((array)$RecipeInfo as $r => $v) { + if (!@$v['Version']) continue; + $r = preg_replace('/^(?!PmWiki:)(Cookbook[.:])?/', 'Cookbook:', $r); + $rinfo[$r] = $v['Version']; + } + $markup = "!!Recipe status for {\$PageUrl}\n".RecipeTableCore($rinfo, $cvinfo); + $html = MarkupToHTML($pagename, $markup); + SDV($RecipeCheckFmt, array(&$PageStartFmt, $html, &$PageEndFmt)); + PrintFmt($pagename, $RecipeCheckFmt); +} + + +function GetRecipeListCore($list) { + $cvinfo = array(); + $fp = fopen($list, 'r'); + while ($fp && !feof($fp)) { + $line = fgets($fp, 1024); + if ($line[0] == '#') continue; + if (preg_match('/(\\S+) +(.*)/', $line, $match)) + $cvinfo[$match[1]] = trim($match[2]); + } + fclose($fp); + return $cvinfo; +} + + +function ScanRecipeInfoCore($dlistfmt, $cvinfo = NULL) { + global $RecipeInfo; + foreach((array)$dlistfmt as $dir) { + $dfp = @opendir($dir); if (!$dfp) continue; + while ( ($name = readdir($dfp)) !== false) { + if ($name[0] == '.') continue; + if (!preg_match('/\\.php/i', $name)) continue; + $text = implode('', @file("$dir/$name")); + if (preg_match("/^\\s*\\\$RecipeInfo\\['(.*?)'\\]\\['Version'\\]\\s*=\\s*'(.*?)'\\s*;/m", $text, $match)) + SDV($RecipeInfo[$match[1]]['Version'], $match[2]); + if (preg_match("/^\\s*SDV\\(\\s*\\\$RecipeInfo\\['(.*?)'\\]\\['Version'\\]\\s*,\\s*'(.*?)'\\s*\\)\\s*\\;/m", $text, $match)) + SDV($RecipeInfo[$match[1]]['Version'], $match[2]); + if (@$cvinfo[$name]) { + $r = preg_replace('/^.*:/', '', $cvinfo[$name]); + SDV($RecipeInfo[$r]['Version'], "unknown ($name)"); + } + } + closedir($dfp); + } +} + + +function RecipeTableCore($rinfo, $cvinfo) { + $fmt = "||%-40s ||%-20s ||%-20s ||\n"; + $out = "||class=recipecheck cellpadding=0 cellspacing=0 width=600\n"; + $out .= sprintf($fmt, '!Recipe', '!local', '!pmwiki.org'); + foreach($rinfo as $r => $lv) { + $cv = @$cvinfo[$r]; + $style = ($lv == $cv) ? 'ok' : 'check'; + $out .= sprintf($fmt, "%apply=tr $style%[[$r]]", $lv, $cv); + } + return $out; +} + blob - /dev/null blob + 66be36386359c1884bcea6f305571bd3f8b354be (mode 644) --- /dev/null +++ scripts/draft.php @@ -0,0 +1,75 @@ + ' '.XL('Publish').' ')); +SDVA($InputTags['e_saveeditbutton'], array('value' => ' '.XL('Save draft and edit').' ')); +SDVA($InputTags['e_savedraftbutton'], array( + ':html' => "", + 'name' => 'postdraft', 'value' => ' '.XL('Save draft').' ', + 'accesskey' => XL('ak_savedraft'))); + +## with drafts enabled, the 'post' operation requires 'publish' permissions +if ($_POST['post'] && $HandleAuth['edit'] == 'edit') + $HandleAuth['edit'] = 'publish'; + +## disable the 'publish' button if not authorized to publish +if (!CondAuth($basename, 'publish')) + SDVA($InputTags['e_savebutton'], array('disabled' => 'disabled')); + +## add the draft handler into $EditFunctions +array_unshift($EditFunctions, 'EditDraft'); +function EditDraft(&$pagename, &$page, &$new) { + global $WikiDir, $DraftSuffix, $DeleteKeyPattern, $EnableDraftAtomicDiff, + $DraftRecentChangesFmt, $RecentChangesFmt, $Now; + SDV($DeleteKeyPattern, "^\\s*delete\\s*$"); + $basename = preg_replace("/$DraftSuffix\$/", '', $pagename); + $draftname = $basename . $DraftSuffix; + if (@$_POST['postdraft'] || @$_POST['postedit']) $pagename = $draftname; + else if ($_POST['post'] && !preg_match("/$DeleteKeyPattern/", $new['text'])) { + $pagename = $basename; + if (IsEnabled($EnableDraftAtomicDiff, 0)) { + $page = ReadPage($basename); + foreach($new as $k=>$v) # delete draft history + if (preg_match('/:\\d+(:\\d+:)?$/', $k) && ! preg_match("/:$Now(:\\d+:)?$/", $k)) unset($new[$k]); + unset($new['rev']); + SDVA($new, $page); + } + $WikiDir->delete($draftname); + } + else if (PageExists($draftname) && $pagename != $draftname) + { Redirect($draftname, '$PageUrl?action=edit'); exit(); } + if ($pagename == $draftname && isset($DraftRecentChangesFmt)) + $RecentChangesFmt = $DraftRecentChangesFmt; +} blob - /dev/null blob + 7c99783e23fd037b20b1d114dc3e5ea5f6966210 (mode 644) --- /dev/null +++ scripts/feeds.php @@ -0,0 +1,546 @@ +All Rights Reserved" in the feed for + ?action=atom. If the value of an entry begins with a '<', + then feeds.php doesn't automatically add the tag around it. + Elements can also be callable functions which are called to + generate the appropriate output. + + For example, to set the RSS 2.0 element to the + value of the last author to modify a page, one can set + (in local/config.php): + + $FeedFmt['rss']['item']['author'] = '$LastModifiedBy'; + + To use the RSS 2.0 element to contain the + change summary of the most recent edit, set + + $FeedFmt['rss']['item']['description'] = '$LastModifiedSummary'; + + Feeds.php can also be combined with attachments to support + podcasting via ?action=rss. Any page such as "PageName" + that has an mp3 attachment with the same name as the page + ("PageName.mp3") will have an appropriate element + in the feed output. The set of allowed attachments can be + extended using the $RSSEnclosureFmt array: + + $RSSEnclosureFmt = array('{$Name}.mp3', '{$Name}.mp4'); + + References: + http://www.atomenabled.org/developers/syndication/ + http://dublincore.org/documents/dcmes-xml/ + http://en.wikipedia.org/wiki/Podcasting + + Script maintained by Petko YOTOV www.pmwiki.org/petko +*/ + +## Settings for ?action=atom +SDVA($FeedFmt['atom']['feed'], array( + '_header' => 'Content-type: text/xml; charset="$Charset"', + '_start' => ' +'."\n", + '_end' => "\n", + 'title' => '$WikiTitle', + 'link' => '', + 'id' => '{$PageUrl}?action=atom', + 'updated' => '$FeedISOTime', + 'author' => "$WikiTitle\n", + 'generator' => '$Version', + 'logo' => '$PageLogoUrl')); +SDVA($FeedFmt['atom']['item'], array( + '_start' => "\n", + 'id' => '{$PageUrl}', + 'title' => '{$Title}', + 'updated' => '$ItemISOTime', + 'link' => "\n", + 'author' => "{\$LastModifiedBy}\n", + 'summary' => '{$Description}', + 'category' => "\n", + '_end' => "\n")); + +## Settings for ?action=dc +SDVA($FeedFmt['dc']['feed'], array( + '_header' => 'Content-type: text/xml; charset="$Charset"', + '_start' => ' + +'."\n", + '_end' => "\n")); +SDVA($FeedFmt['dc']['item'], array( + '_start' => "\n", + 'dc:title' => '{$Title}', + 'dc:identifier' => '{$PageUrl}', + 'dc:date' => '$ItemISOTime', + 'dc:type' => 'Text', + 'dc:format' => 'text/html', + 'dc:description' => '{$Description}', + 'dc:subject' => "\$Category\n", + 'dc:publisher' => '$WikiTitle', + 'dc:author' => '{$LastModifiedBy}', + '_end' => "\n")); + +## RSS 2.0 settings for ?action=rss +SDVA($FeedFmt['rss']['feed'], array( + '_header' => 'Content-type: text/xml; charset="$Charset"', + '_start' => ' + +'."\n", + '_end' => "\n\n", + 'title' => '$WikiTitle | {$Group} / {$Title}', + 'link' => '{$PageUrl}?action=rss', + 'description' => '{$Group}.{$Title}', + 'lastBuildDate' => '$FeedRSSTime')); +SDVA($FeedFmt['rss']['item'], array( + '_start' => "\n", + '_end' => "\n", + 'title' => '{$Group} / {$Title}', + 'link' => '{$PageUrl}', + 'description' => '{$Description}', + 'dc:contributor' => '{$LastModifiedBy}', + 'dc:date' => '$ItemISOTime', + 'pubDate' => '$ItemRSSTime', + 'enclosure' => 'RSSEnclosure')); + +## RDF 1.0, for ?action=rdf +SDVA($FeedFmt['rdf']['feed'], array( + '_header' => 'Content-type: text/xml; charset="$Charset"', + '_start' => ' + + '."\n", + 'title' => '$WikiTitle | {$Group} / {$Title}', + 'link' => '{$PageUrl}?action=rdf', + 'description' => '{$Group}.{$Title}', + 'dc:date' => '$FeedISOTime', + 'items' => "\n\n\$FeedRDFSeq\n\n", + '_items' => "\n", + '_end' => "\n")); +SDVA($FeedFmt['rdf']['item'], array( + '_start' => "\n", + '_end' => "\n", + 'title' => '$WikiTitle | {$Group} / {$Title}', + 'link' => '{$PageUrl}', + 'description' => '{$Description}', + 'dc:date' => '$ItemISOTime')); + +foreach(array_keys($FeedFmt) as $k) { + SDV($HandleActions[$k], 'HandleFeed'); + SDV($HandleAuth[$k], 'read'); +} + +function HandleFeed($pagename, $auth = 'read') { + global $FeedFmt, $action, $PCache, $FmtV, $TimeISOZFmt, $RSSTimeFmt, + $FeedPageListOpt, $FeedCategoryOpt, $FeedTrailOpt, + $FeedDescPatterns, $CategoryGroup, $EntitiesTable; + SDV($RSSTimeFmt, 'D, d M Y H:i:s \G\M\T'); + SDV($FeedDescPatterns, + array('/<[^>]*$/' => ' ', '/\\w+$/' => '', '/<[^>]+>/' => '')); + $FeedPageListOpt = (array)@$FeedPageListOpt; + SDVA($FeedCategoryOpt, array('link' => $pagename)); + SDVA($FeedTrailOpt, array('trail' => $pagename, 'count' => 10)); + + $f = $FeedFmt[$action]; + $page = RetrieveAuthPage($pagename, $auth, true, READPAGE_CURRENT); + if (!$page) Abort("?cannot generate feed"); + $feedtime = $page['time']; + + # determine list of pages to display + if (@($_REQUEST['trail'] || $_REQUEST['group'] || $_REQUEST['link'] + || $_REQUEST['name'])) + $opt = $FeedPageListOpt; + else if (preg_match("/^$CategoryGroup\\./", $pagename)) + $opt = $FeedCategoryOpt; + else if ($action != 'dc') $opt = $FeedTrailOpt; + else { + PCache($pagename, $page); + $pagelist = array($pagename); + } + if (!@$pagelist) { + $opt = array_merge($opt, @$_REQUEST); + $pagelist = MakePageList($pagename, $opt, 0); + } + + # process list of pages in feed + $rdfseq = ''; + $pl = array(); + foreach($pagelist as $pn) { + if (!PageExists($pn)) continue; + if (!isset($PCache[$pn]['time'])) + { $page = ReadPage($pn, READPAGE_CURRENT); PCache($pn, $page); } + $pc = & $PCache[$pn]; + $pl[] = $pn; + $rdfseq .= FmtPageName("\n", $pn); + if ($pc['time'] > $feedtime) $feedtime = $pc['time']; + if (@$opt['count'] && count($pl) >= $opt['count']) break; + } + $pagelist = $pl; + + $FmtV['$FeedRDFSeq'] = $rdfseq; + $FmtV['$FeedISOTime'] = PSFT($TimeISOZFmt, $feedtime, null, 'GMT'); + $FmtV['$FeedRSSTime'] = gmdate($RSSTimeFmt, $feedtime); + # format start of feed + $out = FmtPageName($f['feed']['_start'], $pagename); + + # format feed elements + foreach($f['feed'] as $k => $v) { + if ($k[0] == '_' || !$v) continue; + $x = FmtPageName($v, $pagename); + if (!$x) continue; + $out .= ($v[0] == '<') ? $x : "<$k>$x\n"; + } + + # format items in feed + if (@$f['feed']['_items']) + $out .= FmtPageName($f['feed']['_items'], $pagename); + foreach($pagelist as $pn) { + $page = &$PCache[$pn]; + $FmtV['$ItemDesc'] = @$page['description']; + $FmtV['$ItemISOTime'] = gmstrftime($TimeISOZFmt, $page['time']); + $FmtV['$ItemRSSTime'] = gmdate($RSSTimeFmt, $page['time']); + + $out .= FmtPageName($f['item']['_start'], $pn); + foreach((array)@$f['item'] as $k => $v) { + if ($k[0] == '_' || !$v) continue; + if (is_callable($v)) { $out .= $v($pn, $page, $k); continue; } + if (strpos($v, '$LastModifiedBy') !== false && !@$page['author']) + continue; + if (strpos($v, '$Category') !== false) { + if (preg_match_all("/(?<=^|,)$CategoryGroup\\.([^,]+)/", + @$page['targets'], $match)) { + foreach($match[1] as $c) { + $FmtV['$Category'] = $c; + $out .= FmtPageName($v, $pn); + } + } + continue; + } + $x = FmtPageName($v, $pn); + if (!$x) continue; + $out .= ($v[0] == '<') ? $x : "<$k>$x\n"; + } + $out .= FmtPageName($f['item']['_end'], $pn); + } + $out .= FmtPageName($f['feed']['_end'], $pagename); + foreach((array)@$f['feed']['_header'] as $fmt) + header(FmtPageName($fmt, $pagename)); + print str_replace(array_keys($EntitiesTable), + array_values($EntitiesTable), $out); +} + +## RSSEnclosure is called in ?action=rss to generate +## tags for any pages that have an attached "PageName.mp3" file. +## The set of attachments to enclose is given by $RSSEnclosureFmt. +function RSSEnclosure($pagename, &$page, $k) { + global $RSSEnclosureFmt, $UploadFileFmt, $UploadExts; + if (!function_exists('MakeUploadName')) return ''; + SDV($RSSEnclosureFmt, array('{$Name}.mp3')); + $encl = ''; + foreach((array)$RSSEnclosureFmt as $fmt) { + $path = FmtPageName($fmt, $pagename); + $upname = MakeUploadName($pagename, $path); + $filepath = FmtPageName("$UploadFileFmt/$upname", $pagename); + if (file_exists($filepath)) { + $length = filesize($filepath); + $type = @$UploadExts[preg_replace('/.*\\./', '', $filepath)]; + $url = LinkUpload($pagename, 'Attach:', $path, '', '', '$LinkUrl'); + $encl .= "<$k url='$url' length='$length' type='$type' />"; + } + } + return $encl; +} + +## Since most feeds don't understand html character entities, we +## convert the common ones to their numeric form here. +SDVA($EntitiesTable, array( + # entities defined in "http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent" + ' ' => ' ', + '¡' => '¡', + '¢' => '¢', + '£' => '£', + '¤' => '¤', + '¥' => '¥', + '¦' => '¦', + '§' => '§', + '¨' => '¨', + '©' => '©', + 'ª' => 'ª', + '«' => '«', + '¬' => '¬', + '­' => '­', + '®' => '®', + '¯' => '¯', + '°' => '°', + '±' => '±', + '²' => '²', + '³' => '³', + '´' => '´', + 'µ' => 'µ', + '¶' => '¶', + '·' => '·', + '¸' => '¸', + '¹' => '¹', + 'º' => 'º', + '»' => '»', + '¼' => '¼', + '½' => '½', + '¾' => '¾', + '¿' => '¿', + 'À' => 'À', + 'Á' => 'Á', + 'Â' => 'Â', + 'Ã' => 'Ã', + 'Ä' => 'Ä', + 'Å' => 'Å', + 'Æ' => 'Æ', + 'Ç' => 'Ç', + 'È' => 'È', + 'É' => 'É', + 'Ê' => 'Ê', + 'Ë' => 'Ë', + 'Ì' => 'Ì', + 'Í' => 'Í', + 'Î' => 'Î', + 'Ï' => 'Ï', + 'Ð' => 'Ð', + 'Ñ' => 'Ñ', + 'Ò' => 'Ò', + 'Ó' => 'Ó', + 'Ô' => 'Ô', + 'Õ' => 'Õ', + 'Ö' => 'Ö', + '×' => '×', + 'Ø' => 'Ø', + 'Ù' => 'Ù', + 'Ú' => 'Ú', + 'Û' => 'Û', + 'Ü' => 'Ü', + 'Ý' => 'Ý', + 'Þ' => 'Þ', + 'ß' => 'ß', + 'à' => 'à', + 'á' => 'á', + 'â' => 'â', + 'ã' => 'ã', + 'ä' => 'ä', + 'å' => 'å', + 'æ' => 'æ', + 'ç' => 'ç', + 'è' => 'è', + 'é' => 'é', + 'ê' => 'ê', + 'ë' => 'ë', + 'ì' => 'ì', + 'í' => 'í', + 'î' => 'î', + 'ï' => 'ï', + 'ð' => 'ð', + 'ñ' => 'ñ', + 'ò' => 'ò', + 'ó' => 'ó', + 'ô' => 'ô', + 'õ' => 'õ', + 'ö' => 'ö', + '÷' => '÷', + 'ø' => 'ø', + 'ù' => 'ù', + 'ú' => 'ú', + 'û' => 'û', + 'ü' => 'ü', + 'ý' => 'ý', + 'þ' => 'þ', + 'ÿ' => 'ÿ', + # entities defined in "http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent" + '"' => '"', + #'&' => '&#38;', + #'<' => '&#60;', + #'>' => '>', + ''' => ''', + 'Œ' => 'Œ', + 'œ' => 'œ', + 'Š' => 'Š', + 'š' => 'š', + 'Ÿ' => 'Ÿ', + 'ˆ' => 'ˆ', + '˜' => '˜', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + '‌' => '‌', + '‍' => '‍', + '‎' => '‎', + '‏' => '‏', + '–' => '–', + '—' => '—', + '‘' => '‘', + '’' => '’', + '‚' => '‚', + '“' => '“', + '”' => '”', + '„' => '„', + '†' => '†', + '‡' => '‡', + '‰' => '‰', + '‹' => '‹', + '›' => '›', + '€' => '€', + # entities defined in "http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent" + 'ƒ' => 'ƒ', + 'Α' => 'Α', + 'Β' => 'Β', + 'Γ' => 'Γ', + 'Δ' => 'Δ', + 'Ε' => 'Ε', + 'Ζ' => 'Ζ', + 'Η' => 'Η', + 'Θ' => 'Θ', + 'Ι' => 'Ι', + 'Κ' => 'Κ', + 'Λ' => 'Λ', + 'Μ' => 'Μ', + 'Ν' => 'Ν', + 'Ξ' => 'Ξ', + 'Ο' => 'Ο', + 'Π' => 'Π', + 'Ρ' => 'Ρ', + 'Σ' => 'Σ', + 'Τ' => 'Τ', + 'Υ' => 'Υ', + 'Φ' => 'Φ', + 'Χ' => 'Χ', + 'Ψ' => 'Ψ', + 'Ω' => 'Ω', + 'α' => 'α', + 'β' => 'β', + 'γ' => 'γ', + 'δ' => 'δ', + 'ε' => 'ε', + 'ζ' => 'ζ', + 'η' => 'η', + 'θ' => 'θ', + 'ι' => 'ι', + 'κ' => 'κ', + 'λ' => 'λ', + 'μ' => 'μ', + 'ν' => 'ν', + 'ξ' => 'ξ', + 'ο' => 'ο', + 'π' => 'π', + 'ρ' => 'ρ', + 'ς' => 'ς', + 'σ' => 'σ', + 'τ' => 'τ', + 'υ' => 'υ', + 'φ' => 'φ', + 'χ' => 'χ', + 'ψ' => 'ψ', + 'ω' => 'ω', + 'ϑ' => 'ϑ', + 'ϒ' => 'ϒ', + 'ϖ' => 'ϖ', + '•' => '•', + '…' => '…', + '′' => '′', + '″' => '″', + '‾' => '‾', + '⁄' => '⁄', + '℘' => '℘', + 'ℑ' => 'ℑ', + 'ℜ' => 'ℜ', + '™' => '™', + 'ℵ' => 'ℵ', + '←' => '←', + '↑' => '↑', + '→' => '→', + '↓' => '↓', + '↔' => '↔', + '↵' => '↵', + '⇐' => '⇐', + '⇑' => '⇑', + '⇒' => '⇒', + '⇓' => '⇓', + '⇔' => '⇔', + '∀' => '∀', + '∂' => '∂', + '∃' => '∃', + '∅' => '∅', + '∇' => '∇', + '∈' => '∈', + '∉' => '∉', + '∋' => '∋', + '∏' => '∏', + '∑' => '∑', + '−' => '−', + '∗' => '∗', + '√' => '√', + '∝' => '∝', + '∞' => '∞', + '∠' => '∠', + '∧' => '∧', + '∨' => '∨', + '∩' => '∩', + '∪' => '∪', + '∫' => '∫', + '∴' => '∴', + '∼' => '∼', + '≅' => '≅', + '≈' => '≈', + '≠' => '≠', + '≡' => '≡', + '≤' => '≤', + '≥' => '≥', + '⊂' => '⊂', + '⊃' => '⊃', + '⊄' => '⊄', + '⊆' => '⊆', + '⊇' => '⊇', + '⊕' => '⊕', + '⊗' => '⊗', + '⊥' => '⊥', + '⋅' => '⋅', + '⌈' => '⌈', + '⌉' => '⌉', + '⌊' => '⌊', + '⌋' => '⌋', + '⟨' => '〈', + '⟩' => '〉', + '◊' => '◊', + '♠' => '♠', + '♣' => '♣', + '♥' => '♥', + '♦' => '♦')); + blob - /dev/null blob + d69f582cdcce1d0bcc35f3b5abd35668ad340e65 (mode 644) --- /dev/null +++ scripts/forms.php @@ -0,0 +1,421 @@ +"); + +foreach(array('text', 'password', 'email', 'url', 'tel', 'number', 'search', 'date') as $t) + SDV($InputTags[$t]['class'], "inputbox"); + +foreach(array('submit', 'button', 'reset') as $t) + SDV($InputTags[$t]['class'], "inputbutton"); + +foreach(array('radio', 'checkbox') as $t) + SDVA($InputTags[$t], array( + ':html' => "\$InputFormLabel", + ':args' => array('name', 'value', 'label'), + ':checked' => 'checked')); + +# (:input form:) +SDVA($InputTags['form'], array( + ':args' => array('action', 'method'), + ':html' => "
    ", + 'method' => 'post')); + +# (:input end:) +SDV($InputTags['end'][':html'], '
    '); + +# (:input textarea:) +SDVA($InputTags['textarea'], array( + ':content' => array('value'), + ':attr' => array_diff($InputAttrs, array('value')), + ':html' => "")); + +# (:input image:) +SDV($InputTags['image'][':args'], array('name', 'src', 'alt')); + +# (:input select:) +SDVA($InputTags['select-option'], array( + ':args' => array('name', 'value', 'label'), + ':content' => array('label', 'value', 'name'), + ':attr' => array('value', 'selected'), + ':checked' => 'selected', + ':html' => "")); +SDVA($InputTags['select'], array( + 'class' => 'inputbox', + ':html' => "")); + +# (:input datalist:) +SDVA($InputTags['datalist-option'], array( + ':args' => array('id', 'value'), + ':attr' => array('value'), + ':html' => "
    "); +SDV($RefCountTimeFmt," %Y-%b-%d %H:%M"); +SDV($HandleActions['refcount'], 'HandleRefCount'); + +function PrintRefCount($pagename) { + global $GroupPattern,$NamePattern,$PageRefCountFmt,$RefCountTimeFmt, $ScriptUrl; + $pagelist = ListPages(); + $grouplist = array(); + foreach($pagelist as $pname) { + if (!preg_match("/^($GroupPattern)[\\/.]($NamePattern)$/",$pname,$m)) + continue; + $grouplist[$m[1]]=$m[1]; + } + asort($grouplist); + $grouplist = array_merge(array('all' => 'all groups'),$grouplist); + + $wlist = array('all','missing','existing','orphaned'); + $tlist = isset($_REQUEST['tlist']) ? $_REQUEST['tlist'] : array('all'); + $flist = isset($_REQUEST['flist']) ? $_REQUEST['flist'] : array('all'); + $whichrefs = @$_REQUEST['whichrefs']; + $showrefs = (@$_REQUEST['showrefs']=='checked')? "checked='checked'" : ''; + $submit = @$_REQUEST['submit']; + + echo FmtPageName($PageRefCountFmt,$pagename); + echo FmtPageName(" +
    Show +
    page names in group
    +
    referenced from pages in
    +
    +

    + +


    "; + + if ($submit) { + foreach($pagelist as $pname) { + $ref = array(); + $page = ReadPage($pname, READPAGE_CURRENT); + if (!$page) continue; + $tref[$pname]['time'] = $page['time']; + if (!in_array('all',$flist) && + !in_array(FmtPageName('$Group',$pname),$flist)) continue; + $rc = preg_match('/RecentChanges$/',$pname); + foreach(explode(',',strval(@$page['targets'])) as $r) { + if ($r=='') continue; + if ($rc) @$tref[$r]['rc']++; + else { @$tref[$r]['page']++; @$pref[$r][$pname]++; } + } + } + uasort($tref,'RefCountCmp'); + echo " + + "; + reset($tref); + foreach($tref as $p=>$c) { + if (!in_array('all',$tlist) && + !in_array(FmtPageName('$Group',$p),$tlist)) continue; + if ($whichrefs=='missing' && PageExists($p)) continue; + elseif ($whichrefs=='existing' && !PageExists($p)) continue; + elseif ($whichrefs=='orphaned' && + (@$tref[$p]['page']>0 || !PageExists($p))) continue; + echo ""; + echo ""; + echo ""; + echo ""; + } + echo "
    Referring pages
    Name / TimeAllR.C.
    ",LinkPage($pagename, '', $p, '', $p); + if (@$tref[$p]['time']) echo PSFT($RefCountTimeFmt,$tref[$p]['time']); + if ($showrefs && is_array(@$pref[$p])) { + foreach($pref[$p] as $pr=>$pc) + echo "
    ", LinkPage($pagename, '', $pr, '', $pr); + } + echo "
    ",@$tref[$p]['page']+0,"",@$tref[$p]['rc']+0,"
    "; + } +} + + +function RefCountCmp($ua,$ub) { + if (@($ua['page']!=$ub['page'])) return @($ub['page']-$ua['page']); + if (@($ua['rc']!=$ub['rc'])) return @($ub['rc']-$ua['rc']); + return @($ub['time']-$ua['time']); +} + + + +function HandleRefCount($pagename, $auth='read') { + global $HandleRefCountFmt,$PageStartFmt,$PageEndFmt; + $page = RetrieveAuthPage($pagename, $auth, true, READPAGE_CURRENT); + if (!$page) Abort('?unauthorized'); + PCache($pagename, $page); + SDV($HandleRefCountFmt,array(&$PageStartFmt, + 'function:PrintRefCount',&$PageEndFmt)); + PrintFmt($pagename,$HandleRefCountFmt); +} + blob - /dev/null blob + 40bc603703d0eadc14f7f440fdeac8672998caae (mode 644) --- /dev/null +++ scripts/robots.php @@ -0,0 +1,81 @@ + tag in the head of the HTML document. + By default $MetaRobots is set so that robots do not index pages in + the Site, SiteAdmin, and PmWiki groups. + + The $RobotPattern variable is used to determine if the user agent + accessing the site is a robot, and $IsRobotAgent is set accordingly. + By default this pattern identifies Googlebot, Yahoo! Slurp, msnbot, + BecomeBot, and HTTrack as robots. + + If the agent is deemed a robot, then the $RobotActions array is + checked to see if robots are allowed to perform the given action, + and if not the robot is immediately sent an HTTP 403 Forbidden + response. + + If $EnableRobotCloakActions is set, then a pattern is added to + $FmtP to hide any "?action=" url parameters in page urls + generated by PmWiki for actions that robots aren't allowed to + access. This can greatly reduce the load on the server by + not providing the robot with links to pages that it will be + forbidden to index anyway. + + Script maintained by Petko YOTOV www.pmwiki.org/petko +*/ + +## $MetaRobots provides the value for the tag. +SDV($MetaRobots, + ($action!='browse' || !PageExists($pagename) + || preg_match('#^PmWiki[./](?!PmWiki$)|^Site(Admin)?[./]#', $pagename)) + ? 'noindex,nofollow' : 'index,follow'); +if ($MetaRobots) + $HTMLHeaderFmt['robots'] = + " \n"; + +## $RobotPattern is used to identify robots. +SDV($RobotPattern,'\\w+[-_ ]?(bot|spider|crawler)' + .'|Slurp|Teoma|ia_archiver|HTTrack|XML Sitemaps|Jabse|Yandex|PageAnalyzer|Yeti|Riddler|Aboundex|ADmantX|WikiDo' + .'|Pinterest|Qwantify|worldwebheritage|coccoc|HostWallker|Add Catalog|idmarch|MegaIndex|heritrix|SEOdiver'); +SDV($IsRobotAgent, + $RobotPattern && preg_match("!$RobotPattern!i", @$_SERVER['HTTP_USER_AGENT'])); +if (!$IsRobotAgent) return; + +## $RobotActions indicates which actions a robot is allowed to perform. +SDVA($RobotActions, array('browse' => 1, 'rss' => 1, 'dc' => 1)); +if (!@$RobotActions[$action]) { + $pagename = ResolvePageName($pagename); + if (!PageExists($pagename)) { + header("HTTP/1.1 404 Not Found"); + print("

    Not Found

    "); + exit(); + } + header("HTTP/1.1 403 Forbidden"); + print("

    Forbidden

    "); + exit(); +} + +## The following removes any ?action= parameters that robots aren't +## allowed to access. +function cb_bool($a) { return (boolean)$a; } +if (IsEnabled($EnableRobotCloakActions, 0)) { + $p = join('|', array_keys(array_filter($RobotActions, 'cb_bool'))); + $FmtPV['$PageUrl'] = + 'PUE(($EnablePathInfo) + ? "\\$ScriptUrl/$group/$name" + : "\\$ScriptUrl?n=$group.$name")'; + $FmtP["/(\\\$ScriptUrl[^#\"'\\s<>]+)\\?action=(?!$p)\\w+/"] = '$1'; +} + blob - /dev/null blob + c5fda5ea25b53173b4d80730d05393da0c2c0e49 (mode 644) --- /dev/null +++ scripts/simuledit.php @@ -0,0 +1,76 @@ += $Now + || $_POST['basetime']>=$page['time'] + || $page['text'] == $new['text']) return; + $EnablePost = 0; + $old = array(); + RestorePage($pagename,$page,$old,"diff:{$_POST['basetime']}"); + $text = Merge($new['text'],$old['text'],$page['text']); + if ($text > '') { $new['text'] = $text; $ec = '$[EditConflict]'; } + else $ec = '$[EditWarning]'; + XLSDV('en', array( + 'EditConflict' => "The page you are + editing has been modified since you started editing it. + The modifications have been merged into the text below, + you may want to verify the results of the merge before + pressing save. Conflicts the system couldn't resolve are + bracketed by <<<<<<< and + >>>>>>>.", + 'EditWarning' => "The page you are editing has been modified + since you started editing it. If you continue, your + changes will overwrite any changes that others have made.")); + $MessagesFmt[] = "

    $ec + ($[View changes]) +

    \n"; +} + blob - /dev/null blob + d5ea8fab3829fef669a037ffa7ff47d6a1dabc72 (mode 644) --- /dev/null +++ scripts/skins.php @@ -0,0 +1,244 @@ + 0)); +SDV($LocalCSSDir, 'pub/css'); +SDV($LocalCSSDirUrl, '$PubDirUrl/css'); + +## from skinchange.php +if (IsEnabled($EnableAutoSkinList, 0) || isset($PageSkinList)) { + SDV($SkinCookie, $CookiePrefix.'setskin'); + SDV($SkinCookieExpires, $Now+60*60*24*365); + + if (isset($_COOKIE[$SkinCookie])) $sk = $_COOKIE[$SkinCookie]; + if (isset($_GET['setskin'])) { + $sk = $_GET['setskin']; + pmsetcookie($SkinCookie, $sk, $SkinCookieExpires, '/'); + if(@$EnableIMSCaching) { + SDV($IMSCookie, $CookiePrefix.'imstime'); + pmsetcookie($IMSCookie, '', $Now -3600, '/'); + $EnableIMSCaching = 0; + } + } + if (isset($_GET['skin'])) $sk = $_GET['skin']; + + ## If $EnableAutoSkinList is set, then we accept any skin that + ## exists in pub/skins/ or $FarmD/pub/skins/ . + if (IsEnabled($EnableAutoSkinList, 0) + && @$sk && preg_match('/^[-\\w]+$/', $sk) + && (is_dir("pub/skins/$sk") || is_dir("$FarmD/pub/skins/$sk"))) + $Skin = $sk; + + ## If there's a specific mapping in $PageSkinList, we use it no + ## matter what. + if (@$PageSkinList[$sk]) $Skin = $PageSkinList[$sk]; +} + +# $PageTemplateFmt is deprecated +if (isset($PageTemplateFmt)) LoadPageTemplate($pagename,$PageTemplateFmt); +else { + $x = array_merge((array)@$ActionSkin[$action], (array)$Skin); + SetSkin($pagename, $x); +} + +SDV($PageCSSListFmt,array( + "$LocalCSSDir/local.css" => "$LocalCSSDirUrl/local.css", + "$LocalCSSDir/{\$Group}.css" => "$LocalCSSDirUrl/{\$Group}.css", + "$LocalCSSDir/{\$FullName}.css" => "$LocalCSSDirUrl/{\$FullName}.css")); + +foreach((array)$PageCSSListFmt as $k=>$v) + if (file_exists(FmtPageName($k,$pagename))) + $HTMLHeaderFmt[] = "\n"; + +if(IsEnabled($WikiPageCSSFmt, false)) + InsertWikiPageCSS($pagename, $WikiPageCSSFmt); + +# SetSkin changes the current skin to the first available skin from +# the $skin array. +function SetSkin($pagename, $skin) { + global $Skin, $SkinLibDirs, $SkinDir, $SkinDirUrl, + $IsTemplateLoaded, $PubDirUrl, $FarmPubDirUrl, $FarmD, $GCount; + SDV($SkinLibDirs, array( + "./pub/skins/\$Skin" => "$PubDirUrl/skins/\$Skin", + "$FarmD/pub/skins/\$Skin" => "$FarmPubDirUrl/skins/\$Skin")); + foreach((array)$skin as $sfmt) { + $Skin = FmtPageName($sfmt, $pagename); $GCount = 0; + foreach($SkinLibDirs as $dirfmt => $urlfmt) { + $SkinDir = FmtPageName($dirfmt, $pagename); + if (is_dir($SkinDir)) + { $SkinDirUrl = FmtPageName($urlfmt, $pagename); break 2; } + } + } + if (!is_dir($SkinDir)) { + unset($Skin); + Abort("?unable to find skin from list ".implode(' ',(array)$skin)); + } + $IsTemplateLoaded = 0; + if (file_exists("$SkinDir/$Skin.php")) + include_once("$SkinDir/$Skin.php"); + else if (file_exists("$SkinDir/skin.php")) + include_once("$SkinDir/skin.php"); + if ($IsTemplateLoaded) return; + if (file_exists("$SkinDir/$Skin.tmpl")) + LoadPageTemplate($pagename, "$SkinDir/$Skin.tmpl"); + else if (file_exists("$SkinDir/skin.tmpl")) + LoadPageTemplate($pagename, "$SkinDir/skin.tmpl"); + else if (($dh = opendir($SkinDir))) { + while (($fname = readdir($dh)) !== false) { + if ($fname[0] == '.') continue; + if (substr($fname, -5) != '.tmpl') continue; + if ($IsTemplateLoaded) + Abort("?unable to find unique template in $SkinDir"); + LoadPageTemplate($pagename, "$SkinDir/$fname"); + } + closedir($dh); + } + if (!$IsTemplateLoaded) Abort("Unable to load $Skin template", 'skin'); +} + +function cb_includeskintemplate($m) { + global $SkinDir, $pagename; + $x = preg_split('/\\s+/', $m[1], -1, PREG_SPLIT_NO_EMPTY); + for ($i=0; $i\n"); + SDV($PageTextEndFmt, "\n"); + SDV($SkinDirectivesPattern, + "[[<]!--((?:wiki|file|function|markup):.*?)--[]>]"); + + $sddef = array('PageEditFmt' => 0); + $k = implode('', file(FmtPageName($tfilefmt, $pagename))); + + for ($i=0; $i]/i', + 'cb_includeskintemplate', $k); + } + + if (IsEnabled($EnableSkinDiag, 0)) { + if (!preg_match('//i', $k)) + Abort("Skin template missing <!--HTMLHeader-->", 'htmlheader'); + if (!preg_match('//i', $k)) + Abort("Skin template missing <!--HTMLFooter-->", 'htmlheader'); + } + + $sect = preg_split( + '#[[<]!--(/?(?:Page[A-Za-z]+Fmt|(?:HT|X)ML(?:Head|Foot)er|HeaderText|PageText).*?)--[]>]#', + $k, 0, PREG_SPLIT_DELIM_CAPTURE); + $TmplFmt['Start'] = array_merge(array('headers:'), + preg_split("/$SkinDirectivesPattern/s", + array_shift($sect),0,PREG_SPLIT_DELIM_CAPTURE)); + $TmplFmt['End'] = array($PageTextEndFmt); + $ps = 'Start'; + while (count($sect)>0) { + $k = array_shift($sect); + $v = preg_split("/$SkinDirectivesPattern/s", + array_shift($sect),0,PREG_SPLIT_DELIM_CAPTURE); + $TmplFmt[$ps][] = ""; + if ($k[0] == '/') + { $TmplFmt[$ps][] = (count($v) > 1) ? $v : $v[0]; continue; } + @list($var, $sd) = explode(' ', $k, 2); + $GLOBALS[$var] = (count($v) > 1) ? $v : $v[0]; + if ($sd > '') $sddef[$var] = $sd; + if ($var == 'PageText') { $ps = 'End'; } + if ($var == 'HTMLHeader' || $var == 'XMLHeader') + $TmplFmt[$ps][] = &$HTMLHeaderFmt; + if ($var == 'HTMLFooter' || $var == 'XMLFooter') + $TmplFmt[$ps][] = &$HTMLFooterFmt; + ## deprecated, 2.1.16 + if ($var == 'HeaderText') { $TmplFmt[$ps][] = &$HTMLHeaderFmt; } + $TmplFmt[$ps][$var] =& $GLOBALS[$var]; + } + array_push($TmplFmt['Start'], $PageTextStartFmt); + $PageStartFmt = 'function:PrintSkin Start'; + $PageEndFmt = 'function:PrintSkin End'; + $IsTemplateLoaded = 1; + SDVA($TmplDisplay, $sddef); +} + +# This function is called to print a portion of the skin template +# according to the settings in $TmplDisplay. +function PrintSkin($pagename, $arg) { + global $TmplFmt, $TmplDisplay; + foreach ($TmplFmt[$arg] as $k => $v) + if (!isset($TmplDisplay[$k]) || $TmplDisplay[$k]) + PrintFmt($pagename, $v); +} + +# This function parses a wiki page like Site.LocalCSS +# and inserts CSS rules specific to the current page. +# Based on Cookbook:LocalCSS by Petko Yotov +function InsertWikiPageCSS($pagename, $fmt) { + global $HTMLStylesFmt, $EnableSelfWikiPageCSS, $WikiPageCSSVars; + SDV($WikiPageCSSVars,array('FarmPubDirUrl','PubDirUrl','Skin','action','SkinDirUrl')); + + $stylepagename = FmtPageName($fmt, $pagename); + if ($stylepagename == $pagename && + !IsEnabled($EnableSelfWikiPageCSS, 0)) return; + + if ($stylepagename == $pagename && @$_POST['text']) + $text = stripmagic($_POST['text']); + else { + $p = ReadPage($stylepagename, READPAGE_CURRENT); + $text = @$p['text']; + } + if (!$text) return; + + $text = str_replace(array("\r",'$','<','$='),array('','$','<','$='), $text); + $varray = array(); + + # global PHP variables as @variables + foreach($WikiPageCSSVars as $var) $varray["@$var"] = $GLOBALS[$var]; + + # get @variables from page + if (preg_match_all("/^\\s*(@\\w+):\\s*(.*?)\\s*$/m", $text, $vars) ) + foreach($vars[1] as $k=>$varname) $varray[$varname] = trim($vars[2][$k]); + + # expand nested @variables + for ($i=0; $i<10; $i++) $text = strtr($text, $varray); + + # process snippets + if (preg_match_all("/\\[@\\s*([^\\/!\\s]+)\n(.*?)\\s*@\\]/s", $text, $matches, PREG_SET_ORDER) ) + foreach($matches as $a) + if (count(MatchPageNames($pagename, trim($a[1])))) + @$HTMLStylesFmt['WikiPageCSS'] .= trim($a[2]); +} + blob - /dev/null blob + 0314a9bd6a6cc985a99765cc7a4f99c573abfd35 (mode 644) --- /dev/null +++ scripts/stdconfig.php @@ -0,0 +1,106 @@ +$v) { + if (!$k || !$v || $v<0 || $v>=50) continue; + if (function_exists($k)) $k($pagename); + elseif (file_exists($k)) include_once($k); + } +} + +if (IsEnabled($EnableRobotControl,1)) + include_once("$FarmD/scripts/robots.php"); + +if (IsEnabled($EnableCaches, 1)) + include_once("$FarmD/scripts/caches.php"); + +## Scripts that are part of a standard PmWiki distribution. +if (IsEnabled($EnableAuthorTracking,1)) { + include_once("$FarmD/scripts/author.php"); + EnableSignatures(); +} +if (IsEnabled($EnablePrefs, 1)) + include_once("$FarmD/scripts/prefs.php"); +if (IsEnabled($EnableSimulEdit, 1)) + include_once("$FarmD/scripts/simuledit.php"); +if (IsEnabled($EnableDrafts, 0)) + include_once("$FarmD/scripts/draft.php"); # after simuledit + prefs +if (IsEnabled($EnableSkinLayout,1)) + include_once("$FarmD/scripts/skins.php"); # must come after prefs +if (@$Transition || IsEnabled($EnableTransitions, 0)) + include_once("$FarmD/scripts/transition.php"); # must come after skins +if (@$LinkWikiWords || IsEnabled($EnableWikiWords, 0)) + include_once("$FarmD/scripts/wikiwords.php"); # must come before stdmarkup +if (IsEnabled($EnableStdMarkup,1)) + include_once("$FarmD/scripts/stdmarkup.php"); # must come after transition +if (($action=='diff' && @!$HandleActions['diff']) + || (IsEnabled($EnablePreviewChanges, 0) && @$_REQUEST['preview']>'')) + include_once("$FarmD/scripts/pagerev.php"); +if (IsEnabled($EnableWikiTrails,1)) + include_once("$FarmD/scripts/trails.php"); +if (IsEnabled($EnableWikiStyles,1)) + include_once("$FarmD/scripts/wikistyles.php"); +if (IsEnabled($EnableMarkupExpressions, 1) + && !function_exists('MarkupExpression')) + include_once("$FarmD/scripts/markupexpr.php"); +if (IsEnabled($EnablePageList,1)) + include_once("$FarmD/scripts/pagelist.php"); +if (IsEnabled($EnableVarMarkup,1)) + include_once("$FarmD/scripts/vardoc.php"); +if (!@$DiffFunction || !function_exists($DiffFunction)) + include_once("$FarmD/scripts/phpdiff.php"); +if ($action=='crypt') + include_once("$FarmD/scripts/crypt.php"); +if ($action=='edit') + include_once("$FarmD/scripts/guiedit.php"); +if (IsEnabled($EnableForms,1)) + include_once("$FarmD/scripts/forms.php"); # must come after prefs +if (IsEnabled($EnableUpload,0)) + include_once("$FarmD/scripts/upload.php"); # must come after forms +if (IsEnabled($EnableBlocklist, 0)) + include_once("$FarmD/scripts/blocklist.php"); +if (IsEnabled($EnableNotify,0)) + include_once("$FarmD/scripts/notify.php"); +if (IsEnabled($EnableDiag,0) || $action == 'recipecheck') + include_once("$FarmD/scripts/diag.php"); +if (IsEnabled($EnablePmUtils,1)) + include_once("$FarmD/scripts/utils.php"); + +if (IsEnabled($EnableUpgradeCheck,1) && !IsEnabled($EnableReadOnly, 0)) { + SDV($StatusPageName, "$SiteAdminGroup.Status"); + $page = ReadPage($StatusPageName, READPAGE_CURRENT); + if (@$page['updatedto'] != $VersionNum) + { $action = 'upgrade'; include_once("$FarmD/scripts/upgrades.php"); } +} blob - /dev/null blob + 5b4c49201ce7e9574f83c293e75dcb798f5ffe8b (mode 644) --- /dev/null +++ scripts/stdmarkup.php @@ -0,0 +1,721 @@ +".Keep($text)."
    "; + $text = preg_replace("/\n[^\\S\n]+$/", "\n", $text); + if ($lead == "" || $lead == "\n") + return "$lead
    ".Keep($text)."
    "; + return "$lead<:pre,1>".Keep($text); +} + +Markup('[=','_begin',"/(\n[^\\S\n]*)?\\[([=@])(.*?)\\2\\]/s", + "MarkupPreserveText"); +function MarkupPreserveText($m) {return PreserveText($m[2], $m[3], $m[1]);} + +Markup('restore','<_end',"/$KeepToken(\\d.*?)$KeepToken/", 'cb_expandkpv'); +Markup('<:', '>restore', '/<:[^>]*>/', ''); +Markup('', '/', + "
    "); +Markup('

    ', '<', + "/]*)(\\s)class=(['\"])([^>]*?)\\4)?/", + "[=', + '/\\$\\[(?>([^\\]]+))\\]/', "cb_expandxlang"); + +# {$var} substitutions +Markup('{$var}', '>$[phrase]', + '/\\{(\\*|!?[-\\w.\\/\\x80-\\xff]*)(\\$:?\\w[-\\w]*)\\}/', + "MarkupPageVar"); +function MarkupPageVar($m){ + global $IncludedPages, $Cursor; + extract($GLOBALS["MarkupToHTML"]); + if($m[1] && strpos($m[2], '$:')===0) { + $pn = isset($Cursor[$m[1]]) ? $Cursor[$m[1]] : MakePageName($pagename, $m[1]); + @$IncludedPages[$pn]++; + } + return PRR(PVSE(PageVar($pagename, $m[2], $m[1]))); +} + +# invisible (:textvar:...:) definition +Markup('textvar:', '([A-Za-z0-9]+|#\\d+|#[xX][A-Fa-f0-9]+));/', + '&$1;'); +Markup('&amp;', '<&', '/&amp;/', Keep('&')); + + +## (:if:)/(:elseif:)/(:else:) +SDV($CondTextPattern, + "/ \\(:if (\d*) (?:end)? \\b[^\n]*?:\\) + .*? + (?: \\(: (?:if\\1|if\\1end) \\s* :\\) + | (?=\\(:(?:if\\1|if\\1end)\\b[^\n]*?:\\) | $) + ) + /six"); +// SDV($CondTextReplacement, "CondText2(\$pagename, \$m[0], \$m[1])"); +SDV($CondTextReplacement, "MarkupCondText2"); +Markup('if', 'fulltext', $CondTextPattern, $CondTextReplacement); + +function MarkupCondText2($m) { + extract($GLOBALS["MarkupToHTML"]); + return CondText2($pagename, $m[0], $m[1]); +} +function CondText2($pagename, $text, $code = '') { + global $Conditions, $CondTextPattern, $CondTextReplacement; + $if = "if$code"; + $repl = str_replace('$pagename', "'$pagename'", $CondTextReplacement); + + $parts = preg_split("/\\(:(?:{$if}end|$if|else *$if|else$code)\\b\\s*(.*?)\\s*:\\)/", + $text, -1, PREG_SPLIT_DELIM_CAPTURE); + $x = array_shift($parts); + while ($parts) { + list($condspec, $condtext) = array_splice($parts, 0, 2); + if (!preg_match("/^\\s*(!?)\\s*(\\S*)\\s*(.*?)\\s*$/", $condspec, $match)) continue; + list($x, $not, $condname, $condparm) = $match; + + if (!isset($Conditions[$condname])) + return preg_replace_callback($CondTextPattern, $repl, $condtext); + $tf = @eval("return ({$Conditions[$condname]});"); + if ($tf xor $not) + return preg_replace_callback($CondTextPattern, $repl, $condtext); + } + return ''; +} + + +## (:include:) +Markup('include', '>if', + '/\\(:include\\s+(\\S.*?):\\)/i', + "MarkupRedirectInclude"); + +## (:redirect:) +Markup('redirect', 'include', + '/\\(:nogroupheader:\\)/i', + "MarkupGroupHeaderFooter"); +Markup('nogroupfooter', '>include', + '/\\(:nogroupfooter:\\)/i', + "MarkupGroupHeaderFooter"); +Markup('groupheader', '>nogroupheader', + '/\\(:groupheader:\\)/i', + "MarkupGroupHeaderFooter"); +Markup('groupfooter','>nogroupfooter', + '/\\(:groupfooter:\\)/i', + "MarkupGroupHeaderFooter"); + +function MarkupGroupHeaderFooter($m) { + extract($GLOBALS["MarkupToHTML"]); + global $GroupHeaderFmt, $GroupFooterFmt; + switch ($markupid) { + case 'nogroupheader': return PZZ($GroupHeaderFmt=''); + case 'nogroupfooter': return PZZ($GroupFooterFmt=''); + case 'groupheader': return PRR(FmtPageName($GroupHeaderFmt,$pagename)); + case 'groupfooter': return PRR(FmtPageName($GroupFooterFmt,$pagename)); + } +} +## (:nl:) +Markup('nl0','(?:\\(:nl:\\))+)([^\n])/i","$1\n$2"); +Markup('nl1','>nl0',"/\\(:nl:\\)/i",''); + +## \\$ (end of line joins) +Markup('\\$','>nl1',"/\\\\(?>(\\\\*))\n/", "MarkupEndLineJoin"); +function MarkupEndLineJoin($m) { return str_repeat('
    ',strlen($m[1])); } + +## Remove one <:vspace> after !headings +Markup('!vspace', '>\\$', "/^(!(?>[^\n]+)\n)<:vspace>/m", '$1'); + +## (:noheader:),(:nofooter:),(:notitle:)... +Markup('noheader', 'directives', '/\\(:noheader:\\)/i', "MarkupTmplDisplay"); +Markup('nofooter', 'directives', '/\\(:nofooter:\\)/i', "MarkupTmplDisplay"); +Markup('notitle', 'directives', '/\\(:notitle:\\)/i', "MarkupTmplDisplay"); +Markup('noleft', 'directives', '/\\(:noleft:\\)/i', "MarkupTmplDisplay"); +Markup('noright', 'directives', '/\\(:noright:\\)/i', "MarkupTmplDisplay"); +Markup('noaction', 'directives', '/\\(:noaction:\\)/i', "MarkupTmplDisplay"); + +function MarkupTmplDisplay($m) { + extract($GLOBALS["MarkupToHTML"]); + switch ($markupid) { + case 'noheader': return SetTmplDisplay('PageHeaderFmt',0); + case 'nofooter': return SetTmplDisplay('PageFooterFmt',0); + case 'notitle': return SetTmplDisplay('PageTitleFmt',0); + case 'noleft': return SetTmplDisplay('PageLeftFmt',0); + case 'noright': return SetTmplDisplay('PageRightFmt',0); + case 'noaction': return SetTmplDisplay('PageActionFmt',0); + } +} + +## (:spacewikiwords:) +Markup('spacewikiwords', 'directives', + '/\\(:(no)?spacewikiwords:\\)/i', + "MarkupDirectives"); + +## (:linkwikiwords:) +Markup('linkwikiwords', 'directives', + '/\\(:(no)?linkwikiwords:\\)/i', + "MarkupDirectives"); + +## (:linebreaks:) +Markup('linebreaks', 'directives', + '/\\(:(no)?linebreaks:\\)/i', + "MarkupDirectives"); + +## (:messages:) +Markup('messages', 'directives', + '/^\\(:messages:\\)/i', + "MarkupDirectives"); + +function MarkupDirectives($m) { + extract($GLOBALS["MarkupToHTML"]); + switch ($markupid) { + case 'linkwikiwords': return PZZ($GLOBALS['LinkWikiWords']=(@$m[1]!='no')); + case 'spacewikiwords': return PZZ($GLOBALS['SpaceWikiWords']=(@$m[1]!='no')); + case 'linebreaks': + return PZZ($GLOBALS['HTMLPNewline'] = (@$m[1]!='no') ? '
    ' : ''); + case 'messages': + return '<:block>'.Keep(FmtPageName( + implode('',(array)$GLOBALS['MessagesFmt']), $pagename)); + } +} + + +## (:comment:) +Markup('comment', 'directives', '/\\(:comment .*?:\\)/i', ''); + +## (:title:) +fix for PITS:00266, 00779 +$tmpwhen = IsEnabled($EnablePageTitlePriority, 0) ? ' + SetProperty($pagename, 'title', $m[1], NULL, $EnablePageTitlePriority)))); + case 'keywords': + return PZZ(SetProperty($pagename, 'keywords', $m[1], ', ')); + case 'description': + return PZZ(SetProperty($pagename, 'description', $m[1], '\n')); + } +} + +## (:keywords:), (:description:) +Markup('keywords', 'directives', "/\\(:keywords?\\s+(.+?):\\)/i", "MarkupSetProperty"); +Markup('description', 'directives', "/\\(:description\\s+(.+?):\\)/i", "MarkupSetProperty"); +$HTMLHeaderFmt['meta'] = 'function:PrintMetaTags'; +function PrintMetaTags($pagename, $args) { + global $PCache; + foreach(array('keywords', 'description') as $n) { + foreach((array)@$PCache[$pagename]["=p_$n"] as $v) { + $v = str_replace("'", ''', $v); + print "\n"; + } + } +} + +#### inline markups #### +## ''emphasis'' +Markup("''",'inline',"/''(.*?)''/",'$1'); + +## '''strong''' +Markup("'''","<''","/'''(.*?)'''/",'$1'); + +## '''''strong emphasis''''' +Markup("'''''","<'''","/'''''(.*?)'''''/",'$1'); + +## @@code@@ +Markup('@@','inline','/@@(.*?)@@/','$1'); + +## '+big+', '-small-' +Markup("'+","<'''''","/'\\+(.*?)\\+'/",'$1'); +Markup("'-","<'''''","/'\\-(.*?)\\-'/",'$1'); + +## '^superscript^', '_subscript_' +Markup("'^","<'''''","/'\\^(.*?)\\^'/",'$1'); +Markup("'_","<'''''","/'_(.*?)_'/",'$1'); + +## [+big+], [-small-] +Markup('[+','inline','/\\[(([-+])+)(.*?)\\1\\]/', + "MarkupBigSmall"); + +function MarkupBigSmall($m) { + $size = round(pow(6/5,($m[2]=='-'? -1:1)*strlen($m[1]))*100,0); + return "{$m[3]}"; +} + +## {+ins+}, {-del-} +Markup('{+','inline','/\\{\\+(.*?)\\+\\}/','$1'); +Markup('{-','inline','/\\{-(.*?)-\\}/','$1'); + +## [[<<]] (break) +Markup('[[<<]]','inline','/\\[\\[<<\\]\\]/',"
    "); + +###### Links ###### +function MarkupLinks($m){ + extract($GLOBALS["MarkupToHTML"]); + switch ($markupid) { + case '[[': + return Keep(MakeLink($pagename,$m[1],NULL,$m[2]),'L'); + case '[[|': + return Keep(MakeLink($pagename,$m[1],$m[2],$m[3]),'L'); + case '[[->': + return Keep(MakeLink($pagename,$m[2],$m[1],$m[3]),'L'); + case '[[|#': + return Keep(MakeLink($pagename,$m[1], + '['.++$GLOBALS['MarkupFrame'][0]['ref'].']'),'L'); + case '[[#': + return Keep(TrackAnchors($m[1]) ? '' : "", 'L'); + case 'urllink': + return Keep(MakeLink($pagename,$m[0],$m[0]),'L'); + case 'mailto': + return Keep(MakeLink($pagename,$m[0],$m[1]),'L'); + case 'img': + global $LinkFunctions, $ImgTagFmt; + return Keep($LinkFunctions[$m[1]]($pagename,$m[1],$m[2],@$m[4],$m[1].$m[2], + $ImgTagFmt),'L'); + } +} + + +## [[free links]] +Markup('[[','links',"/(?>\\[\\[\\s*(.*?)\\]\\])($SuffixPattern)/", "MarkupLinks"); + +## [[!Category]] +## Markup '[[!' now processed and indexed in LinkPage() +## with other link formats (PITS:01095, PITS:00447) +SDV($CategoryGroup,'Category'); +SDV($LinkCategoryFmt,"\$LinkText"); + +# This is a temporary workaround for blank category pages. +# It may be removed in a future release (Pm, 2006-01-24) +if (preg_match("/^$CategoryGroup\\./", $pagename)) { + SDV($DefaultPageTextFmt, ''); + SDV($PageNotFoundHeaderFmt, 'HTTP/1.1 200 Ok'); +} + +## [[target | text]] +Markup('[[|','<[[', + "/(?>\\[\\[([^|\\]]*)\\|\\s*)(.*?)\\s*\\]\\]($SuffixPattern)/", + "MarkupLinks"); + +## [[text -> target ]] +Markup('[[->','>[[|', + "/(?>\\[\\[([^\\]]+?)\\s*-+>\\s*)(.*?)\\]\\]($SuffixPattern)/", + "MarkupLinks"); + +## [[#anchor]] +Markup('[[#','<[[','/(?>\\[\\[#([A-Za-z][-.:\\w]*))\\]\\]/', "MarkupLinks"); +function TrackAnchors($x) { global $SeenAnchor; return @$SeenAnchor[$x]++; } + +## [[target |#]] reference links +Markup('[[|#', '<[[|', + "/(?>\\[\\[([^|\\]]+))\\|\\s*#\\s*\\]\\]/", + "MarkupLinks"); + +## [[target |+]] title links moved inside LinkPage() + +## bare urllinks +Markup('urllink','>[[', + "/\\b(?>(\\L))[^\\s$UrlExcludeChars]*[^\\s.,?!$UrlExcludeChars]/", + "MarkupLinks"); + +## mailto: links +Markup('mailto','(\\L))([^\\s$UrlExcludeChars]+$ImgExtPattern)(\"([^\"]*)\")?/", + "MarkupLinks"); + +if (IsEnabled($EnableRelativePageLinks, 1)) + SDV($QualifyPatterns['/(\\[\\[(?>[^\\]]+?->)?\\s*)([-\\w\\x80-\\xfe\\s\'()]+([|#?].*?)?\\]\\])/'], + 'cb_qualifylinks'); + +function cb_qualifylinks($m) { + extract($GLOBALS['tmp_qualify']); + return "{$m[1]}$group/{$m[2]}"; +} + +## bare wikilinks +## v2.2: markup rule moved to scripts/wikiwords.php) +Markup('wikilink', '>urllink'); + +## escaped `WikiWords +## v2.2: rule kept here for markup compatibility with 2.1 and earlier +Markup('`wikiword', ' markup (after all other block markups) +Markup('^<:','>block','/^(?=\\s*\\S)(<:([^>]+)>)?/',"MarkupBlock"); +function MarkupBlock($m) {return Block(@$m[2]);} + +## unblocked lines w/block markup become anonymous <:block> +Markup('^!<:', '<^<:', + "/^(?!<:)(?=.*(<\\/?($BlockPattern)\\b)|$KeepToken\\d+B$KeepToken)/", + '<:block>'); + +## Lines that begin with displayed images receive their own block. A +## pipe following the image indicates a "caption" (generates a linebreak). +Markup('^img', 'block', + "/^((?>(\\s+|%%|%[A-Za-z][-,=:#\\w\\s'\".]*%)*)$KeepToken(\\d+L)$KeepToken)(\\s*\\|\\s?)?(.*)$/", + "ImgCaptionDiv"); +function ImgCaptionDiv($m) { + global $KPV; + if (strpos($KPV[$m[3]], '

    $ret
    "; +} + +## Whitespace at the beginning of lines can be used to maintain the +## indent level of a previous list item, or a preformatted text block. +Markup('^ws', '<^img', '/^\\s+ #1/x', "WSIndent"); +function WSIndent($i) { + if(is_array($i)) $i = $i[0]; + global $MarkupFrame; + $icol = strlen($i); + for($depth = count(@$MarkupFrame[0]['cs']); $depth > 0; $depth--) + if (@$MarkupFrame[0]['is'][$depth] == $icol) { + $MarkupFrame[0]['idep'] = $depth; + $MarkupFrame[0]['icol'] = $icol; + return ''; + } + return $i; +} + +## The $EnableWSPre setting uses leading spaces on markup lines to indicate +## blocks of preformatted text. +SDV($EnableWSPre, 1); +Markup('^ ', 'block', + '/^\\s+ #2/x', + "MarkupWSPre"); +function MarkupWSPre($m) { + global $EnableWSPre; + return ($EnableWSPre > 0 && strlen($m[0]) >= $EnableWSPre) + ? '<:pre,1>'.$m[0] : $m[0]; +} +## bullet lists +Markup('^*','block','/^(\\*+)\\s?(\\s*)/','<:ul,$1,$0>$2'); + +## numbered lists +Markup('^#','block','/^(#+)\\s?(\\s*)/','<:ol,$1,$0>$2'); + +## indented (->) /hanging indent (-<) text +Markup('^->','block','/^(?>(-+))>\\s?(\\s*)/','<:indent,$1,$1 $2>$2'); +Markup('^-<','block','/^(?>(-+))<\\s?(\\s*)/','<:outdent,$1,$1 $2>$2'); + +## definition lists +Markup('^::','block','/^(:+)(\s*)([^:]+):/','<:dl,$1,$1$2>
    $2$3
    '); + +## Q: and A: +Markup('^Q:', 'block', '/^Q:(.*)$/', "<:block,1>

    $1

    "); +Markup('^A:', 'block', '/^A:/', Keep('')); + +## tables +function MarkupTables($m) { + extract($GLOBALS["MarkupToHTML"]); + switch ($markupid) { + case 'table': return Cells(@$m[1],@$m[2]); + case '^||||': return FormatTableRow($m[0]); + case '^||': + $GLOBALS['BlockMarkups']['table'][0] = ''; + return '<:block,1>'; + } +} + +## ||cell||, ||!header cell||, ||!caption!|| +Markup('^||||', 'block', + '/^\\|\\|.*\\|\\|.*$/', + "MarkupTables"); +## ||table attributes +Markup('^||','>^||||','/^\\|\\|(.*)$/', + "MarkupTables"); + +#### (:table:) markup (AdvancedTables) +Markup('table', '>', '><<', '<^>>', + '/^>><</', + '(:divend:)'); + +Markup('det-summ', '$3$4'); # PITS:01465 + +function SimpleTableAttr($attr) { + global $SimpleTableDefaultClassName; + $qattr = PQA($attr, true, true); + if(IsEnabled($SimpleTableDefaultClassName) && !preg_match("/(^| )class='.*?' /", $qattr)) + $qattr .= "class='$SimpleTableDefaultClassName'"; + return $qattr; +} + +#### (:table:) markup (AdvancedTables) +function Cells($name,$attr) { + global $MarkupFrame, $EnableTableAutoValignTop; + $attr = PQA($attr, true, true); + $tattr = @$MarkupFrame[0]['tattr']; + $name = strtolower($name); + $key = preg_replace('/end$/', '', $name); + if (preg_match("/^(?:head|cell)(nr)?$/", $name)) $key = 'cell'; + $out = '<:block>'.MarkupClose($key); + if (substr($name, -3) == 'end') return $out; + $cf = & $MarkupFrame[0]['closeall']; + if ($name == 'table') $MarkupFrame[0]['tattr'] = $attr; + else if ($key == 'cell') { + if (IsEnabled($EnableTableAutoValignTop, 1) && strpos($attr, "valign=")===false) + $attr .= " valign='top'"; + $t = (strpos($name, 'head')===0 ) ? 'th' : 'td'; + if (!@$cf['table']) { + $tattr = @$MarkupFrame[0]['tattr']; + $out .= "
    <$t $attr>"; + $cf['table'] = '
    '; + } else if ( preg_match("/nr$/", $name)) $out .= "<$t $attr>"; + else $out .= "<$t $attr>"; + $cf['cell'] = ""; + } else { + $tag = preg_replace('/\\d+$/', '', $key); + $tmp = "<$tag $attr>"; + if ($tag == 'details') { + $tmp = preg_replace("#($2', $tmp); + } + $out .= $tmp; + $cf[$key] = ""; + } + return $out; +} + +## headings +Markup('^!', 'block', '/^(!{1,6})\\s?(.*)$/', "MarkupHeadings"); +function MarkupHeadings($m) { + $len = strlen($m[1]); + return "<:block,1>$m[2]"; +} + +## horiz rule +Markup('^----','>^->','/^----+/','<:block,1>
    '); + +## @2022-01-08T10:07:08Z -> +Markup('