From c02e90a26cfced50904da56fab3a58171bcd4b9a Mon Sep 17 00:00:00 2001 From: zorloc Date: Mon, 19 Apr 2004 23:13:04 +0000 Subject: [PATCH] Connect the rest of PhpWiki to the IniConfig system. Also the keyword regular expression is not a config setting git-svn-id: svn://svn.code.sf.net/p/phpwiki/code/trunk@3341 96ab9672-09ca-45d6-a79d-3d69d39ca109 --- .htaccess | 7 +- config/config-dist.ini | 11 +- index.php | 892 +-------- lib/IniConfig.php | 18 +- lib/InlineParser.php | 1369 +++++++------- lib/PageType.php | 3 +- lib/interwiki.php | 3 +- lib/loadsave.php | 19 +- lib/plugin/InterWikiSearch.php | 11 +- lib/plugin/SystemInfo.php | 13 +- lib/prepend.php | 16 +- lib/stdlib.php | 3188 ++++++++++++++++---------------- themes/Hawaiian/lib/random.php | 5 +- 13 files changed, 2360 insertions(+), 3195 deletions(-) diff --git a/.htaccess b/.htaccess index 8257ad61e..9c7a2cae8 100644 --- a/.htaccess +++ b/.htaccess @@ -1,4 +1,4 @@ -# $Id: .htaccess,v 1.6 2004-03-12 21:35:35 rurban Exp $ +# $Id: .htaccess,v 1.7 2004-04-19 23:13:02 zorloc Exp $ @@ -13,6 +13,11 @@ + + Order deny,allow + Deny from all + + # Try various wiki versions, like wiki, wikide, wikisidebar, ... # # SetHandler application/x-httpd-php diff --git a/config/config-dist.ini b/config/config-dist.ini index 0448c214d..9b60dd4e4 100644 --- a/config/config-dist.ini +++ b/config/config-dist.ini @@ -32,6 +32,9 @@ ; to leave it on, and only disable it if you have problems with it. ENABLE_USER_NEW = true +; Experimental edit feature +;JS_SEARCHREPLACE = false + ;========================================================================== ; Part One: Authentication and security settings. ; @@ -482,6 +485,12 @@ LDAP_BASE_DN = "ou=Users,o=Development,dc=mycompany.com" ; file specified in AUTH_USER_FILE. ; AUTH_USER_FILE_STORABLE = false +; Session Auth: +; Name of the session variable which holds the already authenticated username +; AUTH_SESS_USER = userid +; Which level will the user be? 1 = Bogo or 2 = Pass +; AUTH_SESS_LEVEL = 2 + ; Group membership. PHPWiki supports defining permissions for a group as ; well as for individual users. This defines how group membership information ; is obtained. Supported values are: @@ -704,7 +713,7 @@ WARN_NONPUBLIC_INTERWIKIMAP = false ; ; The default behavior is to match Category* and Topic* links. KEYWORDS = Category:Topic -KEYWORD_LINK_REGEXP = "(?<=^'. join('|^', $keywords) . ')[[:upper:]].*$" +;KEYWORD_LINK_REGEXP = "(?<=^\'. join(\'|^\', $keywords) . \')[[:upper:]].*$" ; Author and Copyright Site Navigation Links ; diff --git a/index.php b/index.php index 343f2242a..070549f80 100644 --- a/index.php +++ b/index.php @@ -36,892 +36,11 @@ along with PhpWiki; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +require_once "lib/prepend.php"; +rcs_id('$Id: index.php,v 1.141 2004-04-19 23:13:02 zorloc Exp $'); -///////////////////////////////////////////////////////////////////// -/* - This is the starting file for PhpWiki. All this file does is set - configuration options, and at the end of the file it includes() the - file lib/main.php, where the real action begins. - - This file is divided into seven parts: Parts Zero, One, Two, Three, - Four, Five and Six. Each one has different configuration settings you can - change; in all cases the default should work on your system, - however, we recommend you tailor things to your particular setting. -*/ - -///////////////////////////////////////////////////////////////////// -// Part Zero: If PHP needs help in finding where you installed the -// rest of the PhpWiki code, you can set the include_path here. - -// Define PHP's include path so that it can find the PHP source code -// for this PhpWiki. -// -// You shouldn't need to do this unless you've moved index.php out -// of the PhpWiki install directory. -// -// Note that on Windows-based servers, you should use ; rather than : -// as the path separator. -//ini_set('include_path', '.:/usr/local/httpd/phpwiki'); - -// Set DEBUG to 1 to view the XHTML and CSS validator icons, page -// processing timer, and possibly other debugging messages at the -// bottom of each page. -if (!defined('DEBUG')) define ('DEBUG', 0); -define('ENABLE_USER_NEW',true); // this will disappear with 1.4.0 - -///////////////////////////////////////////////////////////////////// -// Part Null: Don't touch this! - -define ('PHPWIKI_VERSION', '1.3.10pre'); -require "lib/prepend.php"; -rcs_id('$Id: index.php,v 1.140 2004-04-12 18:29:12 rurban Exp $'); - -///////////////////////////////////////////////////////////////////// -// -// Part One: -// Authentication and security settings. See Part Three for more. -// -///////////////////////////////////////////////////////////////////// - -// The name of your wiki. -// -// This is used to generate a keywords meta tag in the HTML templates, -// in bookmark titles for any bookmarks made to pages in your wiki, -// and during RSS generation for the of the RSS channel. -// -// To use your own logo and signature files, name them PhpWikiLogo.png -// and PhpWikiSignature.png and put them into themes/default/images -// (substituting "PhpWiki" in the filename with the name you define -// here). -// -// It is recommended this be a relatively short WikiWord like the -// InterWiki monikers found in the InterWikiMap. (For examples, see -// lib/interwiki.map). -if (!defined('WIKI_NAME')) define('WIKI_NAME', 'PhpWiki'); - -// Visitor Hostname Lookup -// -// If set, reverse dns lookups will be performed to attempt to convert -// the user's IP number into a host name, in the case where the http -// server does not do this. -if (!defined('ENABLE_REVERSE_DNS')) define('ENABLE_REVERSE_DNS', true); - -// Username and password of administrator. -// -// Set these to your preferences. For heaven's sake pick a good -// password and use the passencrypt.php tool. See: -// http://wolfram.org/writing/howto/password.html -// -// Log into the wiki with the admin user and password to lock, unlock, -// or remove pages and to perform other PhpWikiAdministration -// functions. On all other occasions you should simply log in with -// your regular WikiName. -if (!defined('ADMIN_USER')) define('ADMIN_USER', ""); -if (!defined('ADMIN_PASSWD')) define('ADMIN_PASSWD', ""); -// It is recommended to use the passencrypt.php utility to encode the -// admin password, in the unlikely event someone gains ftp or ssh -// access to the server and directory containing phpwiki. Once you -// have pasted the encrypted password into ADMIN_PASSWD, uncomment -// this next line. -//if (!defined('ENCRYPTED_PASSWD')) define('ENCRYPTED_PASSWD', true); - -// Private ZIP Dumps of All Wiki Pages -// -// If true, only the admin user can make zip dumps. Otherwise anyone -// may download all wiki pages as a single zip archive. -if (!defined('ZIPDUMP_AUTH')) define('ZIPDUMP_AUTH', false); - -// Define to false to disable the RawHtml plugin. -//if (!defined('ENABLE_RAW_HTML')) define('ENABLE_RAW_HTML', false); - -// If you define this to true, (MIME-type) page-dumps (either zip dumps, -// or "dumps to directory" will be encoded using the quoted-printable -// encoding. If you're actually thinking of mailing the raw page dumps, -// then this might be useful, since (among other things,) it ensures -// that all lines in the message body are under 80 characters in length. -// -// Also, setting this will cause a few additional mail headers -// to be generated, so that the resulting dumps are valid -// RFC 2822 e-mail messages. -// -// Probably you can just leave this set to false, in which case you get -// raw ('binary' content-encoding) page dumps. -if (!defined('STRICT_MAILABLE_PAGEDUMPS')) define('STRICT_MAILABLE_PAGEDUMPS', false); - -// Here you can change the filename suffix used for XHTML page dumps. -// If you don't want any suffix just comment this out. -$HTML_DUMP_SUFFIX = '.html'; - -// The maximum file upload size. -if (!defined('MAX_UPLOAD_SIZE')) define('MAX_UPLOAD_SIZE', 16 * 1024 * 1024); - -// If the last edit is older than MINOR_EDIT_TIMEOUT seconds, the -// default state for the "minor edit" checkbox on the edit page form -// will be off. -if (!defined('MINOR_EDIT_TIMEOUT')) define("MINOR_EDIT_TIMEOUT", 7 * 24 * 3600); - -// Actions listed in this array will not be allowed. The complete list -// of actions can be found in lib/main.php within the function -// getActionDescription. -//$DisabledActions = array('dumpserial', 'loadfile'); - -// PhpWiki can generate an access_log (in "NCSA combined log" format) -// for you. If you want one, define this to the name of the log -// file. The server must have write access to the directory specified. -//define('ACCESS_LOG', '/var/tmp/wiki_access_log'); - - -// By default PhpWiki will try to have PHP compress its output -// before sending it to the browser (if you have a recent enough -// version of PHP and the browser supports it.) -// Define COMPRESS_OUTPUT to false to prevent output compression. -// Define COMPRESS_OUTPUT to true to force output compression, -// even if we think your version of PHP does this in a buggy -// fashion. -// Leave it undefined to leave the choice up to PhpWiki. -//define('COMPRESS_OUTPUT', false); - - -// HTTP CACHE_CONTROL -// -// This controls how PhpWiki sets the HTTP cache control -// headers (Expires: and Cache-Control:) -// -// Choose one of: -// -// NONE: This is roughly the old (pre 1.3.4) behavior. PhpWiki will -// instruct proxies and browsers never to cache PhpWiki output. -// -// STRICT: Cached pages will be invalidated whenever the database global -// timestamp changes. This should behave just like NONE (modulo -// bugs in PhpWiki and your proxies and browsers), except that -// things will be slightly more efficient. -// -// LOOSE: Cached pages will be invalidated whenever they are edited, -// or, if the pages include plugins, when the plugin output could -// concievably have changed. -// -// Behavior should be much like STRICT, except that sometimes -// wikilinks will show up as undefined (with the question mark) -// when in fact they refer to (recently) created pages. -// (Hitting your browsers reload or perhaps shift-reload button -// should fix the problem.) -// -// ALLOW_STALE: Proxies and browsers will be allowed to used stale pages. -// (The timeout for stale pages is controlled by CACHE_CONTROL_MAX_AGE.) -// -// This setting will result in quirky behavior. When you edit a -// page your changes may not show up until you shift-reload the -// page, etc... -// -// This setting is generally not advisable, however it may be useful -// in certain cases (e.g. if your wiki gets lots of page views, -// and few edits by knowledgable people who won't freak over the quirks.) -// -// The default is currently LOOSE. -// -if (!defined('CACHE_CONTROL')) define('CACHE_CONTROL', 'LOOSE'); - -// Maximum page staleness, in seconds. -// -// This only has effect if CACHE_CONTROL is set to ALLOW_STALE. -if (!defined('CACHE_CONTROL_MAX_AGE')) define('CACHE_CONTROL_MAX_AGE', 600); - - -// MARKUP CACHING -// -// PhpWiki normally caches a preparsed version (i.e. mostly -// converted to HTML) of the most recent version of each page. -// (Parsing the wiki-markup takes a fair amount of CPU.) -// -// Define WIKIDB_NOCACHE_MARKUP to true to disable the -// caching of marked-up page content. -// -// Note that you can also disable markup caching on a per-page -// temporary basis by addinging a query arg of '?nocache=1' -// to the URL to the page. (Use '?nocache=purge' to completely -// discard the cached version of the page.) -// -// You can also purge the cached markup globally by using the -// "Purge Markup Cache" button on the PhpWikiAdministration page. -//if (!defined('WIKIDB_NOCACHE_MARKUP')) define ('WIKIDB_NOCACHE_MARKUP', true); - -///////////////////////////////////////////////////////////////////// -// -// Part Two: -// Database Selection -// -///////////////////////////////////////////////////////////////////// - -// -// This array holds the parameters which select the database to use. -// -// Not all of these parameters are used by any particular DB backend. -// -$DBParams = array( - // Select the database type: - // - // Choose ADODB or SQL to use an SQL database with ADODB or PEAR - // respectively (both ADODB and PEAR libraries are already included - // with PhpWiki). - // Choose dba to use one of the standard UNIX dbm libraries. - // Choose file to use a flat file database. - //'dbtype' => 'ADODB', - //'dbtype' => 'SQL', - 'dbtype' => 'dba', - //'dbtype' => 'file', - //'dbtype' => 'cvs', - - // For SQL based backends, specify the database as a DSN - // The most general form of a DSN looks like: - // - // phptype(dbsyntax)://username:password@protocol+hostspec/database - // - // For a MySQL database, the following should work: - // - // mysql://user:password@host/databasename - // - // To connect over a unix socket, use something like - // - // mysql://user:password@unix(/path/to/socket)/databasename - // - //'dsn' => 'mysql://guest@unix(/var/lib/mysql/mysql.sock)/test', - //'dsn' => 'mysql://guest:pass@localhost/test', - //'dsn' => 'pgsql://localhost/test', - - // The common table prefix (see below) is added if defined. - // Undefine this if you use dbtype = "cvs" or "file" - 'db_session_table' => 'session', - - // Used by all DB types: - - // prefix for filenames or table names - /* - * currently you MUST EDIT THE SQL file too (in the schemas/ - * directory because we aren't doing on the fly sql generation - * during the installation. - */ - //'prefix' => 'phpwiki_', - - // Used by either 'dba' or 'file' and must be writable by the web - // server If you leave this as '/tmp' you will probably lose all - // your files eventually - 'directory' => "/tmp", - - // choose the type of DB database file to use; most GNU systems have gdbm - 'dba_handler' => 'gdbm', // Either of 'gdbm' or 'db2' work great for me. - //'dba_handler' => 'db2', - //'dba_handler' => 'db3', // Works fine on Windows, but not on every linux. - //'dba_handler' => 'dbm', // On sf.net redhat there's dbm and gdbm. - // dbm suffers from limits on size of data items? - - 'timeout' => 20, - //'timeout' => 5 -); - -///////////////////////////////////////////////////////////////////// -// PHP Session settings: -// - -// Tested for dbtype: 'SQL', 'ADODB' and 'dba'. See schemas/mysql.sql, -// schemas/sqlite.sql or schemas/psql.sql. -// $DBParams['db_session_table'] must be defined. -if (!defined('USE_DB_SESSION') and - !empty($DBParams['db_session_table'])) - define('USE_DB_SESSION',true); - -// If your php was compiled with --enable-trans-sid it tries to -// add a PHPSESSID query argument to all URL strings when cookie -// support isn't detected in the client browser. For reasons -// which aren't entirely clear (PHP bug) this screws up the URLs -// generated by PhpWiki. Therefore, transparent session ids -// should be disabled. This next line does that. -// -// (At the present time, you will not be able to log-in to PhpWiki, -// unless your browser supports cookies.) -@ini_set('session.use_trans_sid', 0); - -// The login code now uses PHP's session support. Usually, the default -// configuration of PHP is to store the session state information in -// /tmp. That probably will work fine, but fails e.g. on clustered -// servers where each server has their own distinct /tmp (this is the -// case on SourceForge's project web server.) You can specify an -// alternate directory in which to store state information like so -// (whatever user your httpd runs as must have read/write permission -// in this directory): - -//ini_set('session.save_path', 'some_other_directory'); - -///////////////////////////////////////////////////////////////////// -// -// The next section controls how many old revisions of each page are -// kept in the database. -// -// There are two basic classes of revisions: major and minor. Which -// class a revision belongs in is determined by whether the author -// checked the "this is a minor revision" checkbox when they saved the -// page. -// -// There is, additionally, a third class of revisions: author -// revisions. The most recent non-mergable revision from each distinct -// author is and author revision. -// -// The expiry parameters for each of those three classes of revisions -// can be adjusted seperately. For each class there are five -// parameters (usually, only two or three of the five are actually -// set) which control how long those revisions are kept in the -// database. -// -// max_keep: If set, this specifies an absolute maximum for the -// number of archived revisions of that class. This is -// meant to be used as a safety cap when a non-zero -// min_age is specified. It should be set relatively high, -// and it's purpose is to prevent malicious or accidental -// database overflow due to someone causing an -// unreasonable number of edits in a short period of time. -// -// min_age: Revisions younger than this (based upon the supplanted -// date) will be kept unless max_keep is exceeded. The age -// should be specified in days. It should be a -// non-negative, real number, -// -// min_keep: At least this many revisions will be kept. -// -// keep: No more than this many revisions will be kept. -// -// max_age: No revision older than this age will be kept. -// -// Supplanted date: Revisions are timestamped at the instant that they -// cease being the current revision. Revision age is computed using -// this timestamp, not the edit time of the page. -// -// Merging: When a minor revision is deleted, if the preceding -// revision is by the same author, the minor revision is merged with -// the preceding revision before it is deleted. Essentially: this -// replaces the content (and supplanted timestamp) of the previous -// revision with the content after the merged minor edit, the rest of -// the page metadata for the preceding version (summary, mtime, ...) -// is not changed. -// -// Keep up to 8 major edits, but keep them no longer than a month. -$ExpireParams['major'] = array('max_age' => 32, - 'keep' => 8); -// Keep up to 4 minor edits, but keep them no longer than a week. -$ExpireParams['minor'] = array('max_age' => 7, - 'keep' => 4); -// Keep the latest contributions of the last 8 authors up to a year. -// Additionally, (in the case of a particularly active page) try to -// keep the latest contributions of all authors in the last week (even -// if there are more than eight of them,) but in no case keep more -// than twenty unique author revisions. -$ExpireParams['author'] = array('max_age' => 365, - 'keep' => 8, - 'min_age' => 7, - 'max_keep' => 20); - -///////////////////////////////////////////////////////////////////// -// -// Part Three: (optional) -// User Authentication -// -///////////////////////////////////////////////////////////////////// -// -// New user authentication configuration: -// We support three basic authentication methods and a stacked array -// of advanced auth methods to get and check the passwords: -// -// ALLOW_ANON_USER default true -// ALLOW_ANON_EDIT default true -// ALLOW_BOGO_LOGIN default true -// ALLOW_USER_PASSWORDS default true - -// allow anon users to view pages! (not edit) -if (!defined('ALLOW_ANON_USER')) define('ALLOW_ANON_USER', true); -// allow anon users to edit pages -if (!defined('ALLOW_ANON_EDIT')) define('ALLOW_ANON_EDIT', true); - -// This was replaced by ALLOW_ANON_EDIT -if (!defined('REQUIRE_SIGNIN_BEFORE_EDIT')) define('REQUIRE_SIGNIN_BEFORE_EDIT', ! ALLOW_ANON_EDIT); - -// If ALLOW_BOGO_LOGIN is true, users are allowed to login (with -// any/no password) using any userid which: -// 1) is not the ADMIN_USER, and -// 2) is a valid WikiWord (matches $WikiNameRegexp.) -// If true, users may be created by themselves. Otherwise we need seperate auth. -// If such a user will create a so called HomePage with his userid, he will -// be able to store his preferences and password there. -if (!defined('ALLOW_BOGO_LOGIN')) define('ALLOW_BOGO_LOGIN', true); - -// True User Authentication: -// To require user passwords: -// ALLOW_ANON_USER = false -// ALLOW_ANON_EDIT = false -// ALLOW_BOGO_LOGIN = false, -// ALLOW_USER_PASSWORDS = true. -// Otherwise any anon or bogo user might login without any or a wrong password. -if (!defined('ALLOW_USER_PASSWORDS')) define('ALLOW_USER_PASSWORDS', true); - -// Below we define which methods exists and in which order -// they are used: -// BogoLogin: WikiWord enough, but with PASSWORD_LENGTH_MINIMUM -// PersonalPage: Store passwords in the users homepage metadata (simple) -// Db: Use $DBAuthParams[] (see below) with the PearDB or -// ADODB only. -// LDAP: Authenticate against LDAP_AUTH_HOST with LDAP_BASE_DN -// IMAP: Authenticate against IMAP_AUTH_HOST (email account) -// POP3: Authenticate against POP3_AUTH_HOST (email account) -// File: Store username:crypted-passwords in .htaccess like files. -// Use Apache's htpasswd to manage this file. -// HttpAuth: Use the protection by the webserver (.htaccess) or -// enforce it -// Session: Re-use an existing user-session. AUTH_SESS_USER and AUTH_SESS_LEVEL - -if (defined('ALLOW_USER_PASSWORDS')) { - - // use the following order of authentication methods: - if (!isset($USER_AUTH_ORDER)) - $USER_AUTH_ORDER = - array( -// "BogoLogin", - "PersonalPage", -// "HttpAuth", - "Db", -// "LDAP", // define LDAP_AUTH_HOST and LDAP_BASE_DN -// "IMAP", // define IMAP_AUTH_HOST -// "POP3", // define POP3_AUTH_HOST -// "File", // define AUTH_USER_FILE and opt. AUTH_USER_FILE_STORABLE -// "Session", // define AUTH_SESS_USER and AUTH_SESS_LEVEL - ) ; - - if (!defined('PASSWORD_LENGTH_MINIMUM')) define('PASSWORD_LENGTH_MINIMUM', 2); - - if (!defined('USER_AUTH_POLICY')) - //We support the following auth policies: - // first-only: use only the first method in USER_AUTH_ORDER - // old: ignore USER_AUTH_ORDER and try to use all available - // methods as in the previous PhpWiki releases (slow) - // strict: check if the user exists for all methods: - // on the first existing user, try the password. - // dont try the other methods on failure then - // stacked: check the given user - password combination for all methods - // and return true on the first success. - - //define('USER_AUTH_POLICY','first-only'); - define('USER_AUTH_POLICY','old'); - //define('USER_AUTH_POLICY','strict'); - //define('USER_AUTH_POLICY','stacked'); -} - -// LDAP Auth: -if (!defined('LDAP_AUTH_HOST')) define('LDAP_AUTH_HOST', "ldap://localhost:389"); -// or "ldaps://server:636" -// The organizational or domain BASE DN: e.g. "dc=mydomain,dc=com" to restrict the search -// Note: ou=Users and ou=Groups are used for GroupLdap Membership -if (!defined('LDAP_BASE_DN')) define('LDAP_BASE_DN', "ou=Users,o=Development,dc=mycompany.com"); -// LDAP Auth Optional: -// Some LDAP servers disallow anonymous binds, and need some more options, -// such as for the Windows Active Directory Server: -// $LDAP_SET_OPTION = array('LDAP_OPT_PROTOCOL_VERSION' => 3, -// 'LDAP_OPT_REFERRALS' => 0); -// define(LDAP_AUTH_USER, "CN=ldapuser,CN=Users,DC=uai,DC=int"); -// define(LDAP_AUTH_PASSWORD, ''); -// define(LDAP_SEARCH_FIELD, 'sAMAccountName'); // might be different from uid, -// here's its a Windows/Samba account - -// IMAP auth: -// check userid/passwords from a imap server, defaults to localhost -if (!defined('IMAP_AUTH_HOST')) define('IMAP_AUTH_HOST', 'localhost:143/imap/notls'); -// Some IMAP_AUTH_HOST samples: -// "localhost", "localhost:143/imap/notls", -// "localhost:993/imap/ssl/novalidate-cert" (SuSE refuses non-SSL conections) - -// POP3 auth: -//if (!defined('POP3_AUTH_HOST')) define('POP3_AUTH_HOST', 'localhost'); -//if (!defined('POP3_AUTH_PORT')) define('POP3_AUTH_PORT', '110'); - -// File auth: -//if (!defined('AUTH_USER_FILE')) define('AUTH_USER_FILE', '/etc/shadow'); // or '/etc/httpd/.htpasswd' -// set this to true if the user may change his password into this file. -//if (!defined('AUTH_USER_FILE_STORABLE')) define('AUTH_USER_FILE_STORABLE',false); - -// Session auth: -// Name of the session variable, which holds the already authenticated username. -//if (!defined('AUTH_SESS_USER')) define('AUTH_SESS_USER', 'userid'); -// Which level will be the user? 1=Bogo or 2=Pass -//if (!defined('AUTH_SESS_LEVEL')) define('AUTH_SESS_LEVEL', 2); - -// Group membership: -//if (!defined('GROUP_METHOD')) define('GROUP_METHOD', "NONE"); -if (!defined('GROUP_METHOD')) define('GROUP_METHOD', "WIKIPAGE"); -//if (!defined('GROUP_METHOD')) define('GROUP_METHOD', "DB"); -//if (!defined('GROUP_METHOD')) define('GROUP_METHOD', "FILE"); -//if (!defined('GROUP_METHOD')) define('GROUP_METHOD', "LDAP"); -//if (!defined('AUTH_GROUP_FILE')) define('AUTH_GROUP_FILE', '/etc/groups'); // or '/etc/httpd/.htgroup' - -// Seperate DB User Authentication. -// Can be external, like radius, phpnuke, courier authmysql, -// apache auth_mysql or something else. -// The default is to store the data as metadata in the users PersonalPage. -// The most likely dsn option is the same dsn as the wikipages. -// -// Note: Order of variables important! -$DBAuthParams = array ( - // if not defined use $DBParams['dsn'] i.e. the phpwiki database - //'auth_dsn' => 'mysql://localhost/phpwiki', - - // USER => PASSWORD - // plaintext passwords: - // 'auth_check' => 'SELECT IF(passwd="$password",1,0) as ok FROM user WHERE userid="$userid"', - // database (md5) passwords (more secure): - 'auth_check' => 'SELECT IF(passwd=PASSWORD("$password"),1,0) as ok FROM user WHERE userid="$userid"', - // crypt passwords: - //'auth_check' => 'SELECT password as password FROM user WHERE userid="$userid"', - // this is only needed with auth_crypt_method plain: - 'auth_user_exists' => 'SELECT userid FROM user WHERE userid="$userid"', - - //'auth_crypt_method' => 'crypt', // 'crypt' (unix) - 'auth_crypt_method' => 'plain', // plain or secure mysql PASSWORD() - - // If 'auth_update' is not defined but 'auth_check' is defined, the user cannot - // change his password. - // $password is processed by the 'auth_crypt_method' - //'auth_update' => 'UPDATE user SET passwd="$password" WHERE userid="$userid"', - // for mysql md5 use 'auth_crypt_method' => 'plain' - 'auth_update' => 'UPDATE user SET passwd=PASSWORD("$password") WHERE userid="$userid"', - - // Let a user create himself. Generally in external databases not wanted. - // Not yet tested! - //'auth_create' => 'INSERT INTO user SET passwd=PASSWORD("$password"),userid="$userid"', - - // USER => PREFERENCES - // This can be optionally defined in the phpwiki db. - // The default is to store it the users homepage. - // If you choose the user table, only registered user get their prefs from the DB, - // self-created users not. Better use the special pref table. - //'pref_select' => 'SELECT prefs FROM user WHERE userid="$userid"', - 'pref_select' => 'SELECT prefs FROM pref WHERE userid="$userid"', - //Don't use replace with user or all other fields here get erased! (passwords e.g.) - //'pref_update' => 'UPDATE user SET prefs="$pref_blob" WHERE userid="$userid"', - // The special prefs table is safe to erase. All users can store their prefs here. - 'pref_update' => 'REPLACE INTO pref SET prefs="$pref_blob",userid="$userid"', - - // USERS <=> GROUPS - // DB methods for lib/WikiGroup.php, see also AUTH_GROUP_FILE above. - // You can define 1:n or n:m user<=>group relations, as you wish. - // Sample configurations - // only one group per user (1:n): - //'is_member' => 'SELECT user FROM user WHERE user="$userid" AND group="$groupname"', - //'group_members' => 'SELECT user FROM user WHERE group="$groupname"', - //'user_groups' => 'SELECT group FROM user WHERE user="$userid"', - // or multiple groups per user (n:m): - 'is_member' => 'SELECT userid FROM member WHERE userid="$userid" AND groupname="$groupname"', - 'group_members' => 'SELECT DISTINCT userid FROM member WHERE groupname="$groupname"', - 'user_groups' => 'SELECT groupname FROM member WHERE userid="$userid"', - - 'dummy' => false, -); - -// Old settings, only needed for ENABLE_USER_NEW = false -// -// The wiki can be protected by HTTP Auth. Use the username and password -// from there, but this is not sufficient. Try the other methods also. -if (!defined('ALLOW_HTTP_AUTH_LOGIN')) define('ALLOW_HTTP_AUTH_LOGIN', false); -// If ALLOW_USER_LOGIN is true, any defined internal and external -// authentication method is tried. If not, we don't care about -// passwords, but listen to the next two constants. Note that external -// authentication is not supported at this time, you will likely have -// to patch code yourself to get PhpWiki to recognise userids and -// passwords from an external source. -if (!defined('ALLOW_USER_LOGIN')) define('ALLOW_USER_LOGIN', false); -// also only if ENABLE_USER_NEW = false: -//if (!defined('ALLOW_LDAP_LOGIN')) define('ALLOW_LDAP_LOGIN', true and function_exists('ldap_connect')); -//if (!defined('ALLOW_IMAP_LOGIN')) define('ALLOW_IMAP_LOGIN', true and function_exists('imap_open')); - -// A interim page which gets displayed on every edit attempt -//if (!defined('EDITING_POLICY')) define('EDITING_POLICY', "EditingPolicy"); - - -///////////////////////////////////////////////////////////////////// -// -// Part Four: -// Page appearance and layout -// -///////////////////////////////////////////////////////////////////// - -/* THEME - * - * Most of the page appearance is controlled by files in the theme - * subdirectory. - * - * There are a number of pre-defined themes shipped with PhpWiki. - * Or you may create your own (e.g. by copying and then modifying one of - * stock themes.) - * - * Pick one. - */ -if (!defined('THEME')) { -define('THEME', 'default'); -//define('THEME', 'MacOSX'); -//define('THEME', 'smaller'); -//define('THEME', 'Wordpress'); -//define('THEME', 'Portland'); -//define('THEME', 'Hawaiian'); -//define('THEME', 'Sidebar'); -//define('THEME', 'SpaceWiki'); -//define('THEME', 'wikilens'); -} - -// Select a valid charset name to be inserted into the xml/html pages, -// and to reference links to the stylesheets (css). For more info see: -// <http://www.iana.org/assignments/character-sets>. Note that PhpWiki -// has been extensively tested only with the latin1 (iso-8859-1) -// character set. -// -// If you change the default from iso-8859-1 PhpWiki may not work -// properly and will require code modifications, at the very least you -// will have to convert the files in pgsrc or locale/xx/pgsrc to -// match! -// -// Character sets similar to iso-8859-1 may work with little or no -// modification depending on your setup. The database must also -// support the same charset, and of course the same is true for the -// web browser. (Some work is in progress hopefully to allow more -// flexibility in this area in the future). -// Note: For $GLOBALS['LANG']="ja" CHARSET "utf-8" must be defined. -if (!defined('CHARSET')) define("CHARSET", "iso-8859-1"); - -// Select your language/locale - default language is "en" for English. -// Other languages available: -// English "en" (English - HomePage) -// Dutch "nl" (Nederlands - ThuisPagina) -// Spanish "es" (Español - PáginaPrincipal) -// French "fr" (Français - Accueil) -// German "de" (Deutsch - StartSeite) -// Swedish "sv" (Svenska - Framsida) -// Italian "it" (Italiano - PaginaPrincipale) -// Japanese "ja" (Japanese - ¥Û¡¼¥à¥Ú¡¼¥¸) -// -// If you set DEFAULT_LANGUAGE to the empty string, your system's -// default language (as determined by the applicable environment -// variables) will be used. -// Note: The users language will be defined in $GLOBALS['LANG'], -// which overrides the DEFAULT_LANGUAGE. See wiki. -// -// Note: User-specified languages will set $GLOBALS['LANG'], this is just -// the system default. -if (!defined('DEFAULT_LANGUAGE')) define('DEFAULT_LANGUAGE', 'en'); - -/* WIKI_PGSRC -- specifies the source for the initial page contents of - * the Wiki. The setting of WIKI_PGSRC only has effect when the wiki is - * accessed for the first time (or after clearing the database.) - * WIKI_PGSRC can either name a directory or a zip file. In either case - * WIKI_PGSRC is scanned for files -- one file per page. - */ -if (!defined('WIKI_PGSRC')) define('WIKI_PGSRC', "pgsrc"); // Default (old) behavior. -//define('WIKI_PGSRC', 'wiki.zip'); // New style. -//define('WIKI_PGSRC', '../../../Logs/Hamwiki/hamwiki-20010830.zip'); // New style. - -/* - * DEFAULT_WIKI_PGSRC is only used when the language is *not* the - * default (English) and when reading from a directory: in that case - * some English pages are inserted into the wiki as well. - * DEFAULT_WIKI_PGSRC defines where the English pages reside. - */ -// FIXME: is this really needed? Can't we just copy these pages into -// the localized pgsrc? -define('DEFAULT_WIKI_PGSRC', "pgsrc"); -// These are the pages which will get loaded from DEFAULT_WIKI_PGSRC. -$GenericPages = array("ReleaseNotes", "SteveWainstead", "TestPage"); - -///////////////////////////////////////////////////////////////////// -// -// Part Five: -// Mark-up options. -// -///////////////////////////////////////////////////////////////////// - -// allowed protocols for links - be careful not to allow "javascript:" -// URL of these types will be automatically linked. -// within a named link [name|uri] one more protocol is defined: phpwiki -$AllowedProtocols = "http|https|mailto|ftp|news|nntp|ssh|gopher"; - -// URLs ending with the following extension should be inlined as images -$InlineImages = "png|jpg|gif"; - -// Perl regexp for WikiNames ("bumpy words") -// (?<!..) & (?!...) used instead of '\b' because \b matches '_' as well -$WikiNameRegexp = "(?<![[:alnum:]])(?:[[:upper:]][[:lower:]]+){2,}(?![[:alnum:]])"; - -// Defaults to '/', but '.' was also used. -if (!defined('SUBPAGE_SEPARATOR')) define('SUBPAGE_SEPARATOR', '/'); - -// InterWiki linking -- wiki-style links to other wikis on the web -// -// The map will be taken from a page name InterWikiMap. -// If that page is not found (or is not locked), or map -// data can not be found in it, then the file specified -// by INTERWIKI_MAP_FILE (if any) will be used. -define('INTERWIKI_MAP_FILE', "lib/interwiki.map"); - -// Display a warning if the internal lib/interwiki.map is used, and -// not the public InterWikiMap page. This file is not readable from outside. -//define('WARN_NONPUBLIC_INTERWIKIMAP', false); - -// Regexp used for automatic keyword extraction. -// -// Any links on a page to pages whose names match this regexp will -// be used keywords in the keywords meta tag. (This is an aid to -// classification by search engines.) The value of the match is -// used as the keyword. -// -// The default behavior is to match Category* and Topic* links. -$keywords = array("Category", "Topic"); -$KeywordLinkRegexp = '(?<=^'. join('|^', $keywords) . ')[[:upper:]].*$'; - -// Author and Copyright Site Navigation Links -// -// These will be inserted as <link rel> tags in the html header of -// every page, for search engines and for browsers like Mozilla which -// take advantage of link rel site navigation. -// -// If you have your own copyright and contact information pages change -// these as appropriate. -if (!defined('COPYRIGHTPAGE_TITLE')) define('COPYRIGHTPAGE_TITLE', - "GNU General Public License"); -if (!defined('COPYRIGHTPAGE_URL')) define('COPYRIGHTPAGE_URL', - 'http://www.gnu.org/copyleft/gpl.html#SEC1'); -if (!defined('AUTHORPAGE_TITLE')) define('AUTHORPAGE_TITLE', - "The PhpWiki Programming Team"); -if (!defined('AUTHORPAGE_URL')) define('AUTHORPAGE_URL', - 'http://phpwiki.sourceforge.net/phpwiki/ThePhpWikiProgrammingTeam'); - - -///////////////////////////////////////////////////////////////////// -// -// Part Six: -// URL options -- you can probably skip this section. -// -///////////////////////////////////////////////////////////////////// -/****************************************************************** - * - * The following section contains settings which you can use to tailor - * the URLs which PhpWiki generates. - * - * Any of these parameters which are left undefined will be deduced - * automatically. You need only set them explicitly if the - * auto-detected values prove to be incorrect. - * - * In most cases the auto-detected values should work fine, so - * hopefully you don't need to mess with this section. - * - * In case of local overrides of short placeholders, which themselves - * include index.php, we check for most constants. See '/wiki'. - * We can override DATA_PATH and PHPWIKI_DIR to support multiple phpwiki - * versions (for development), but most likely other values like - * THEME, $LANG and $DbParams for a WikiFarm. - * - ******************************************************************/ - -/* - * Canonical name and httpd port of the server on which this PhpWiki - * resides. - */ -//if (!defined('SERVER_NAME')) define('SERVER_NAME', 'some.host.com'); -//define('SERVER_PORT', 80); - -/* - * Relative URL (from the server root) of the PhpWiki - * script. - */ -//if (!defined('SCRIPT_NAME')) define('SCRIPT_NAME', '/some/where/index.php'); - -/* - * URL of the PhpWiki install directory. (You only need to set this - * if you've moved index.php out of the install directory.) This can - * be either a relative URL (from the directory where the top-level - * PhpWiki script is) or an absolute one. - */ -//if (!defined('DATA_PATH')) define('DATA_PATH', '/home/user/phpwiki'); - -/* - * Path to the PhpWiki install directory. This is the local - * filesystem counterpart to DATA_PATH. (If you have to set - * DATA_PATH, your probably have to set this as well.) This can be - * either an absolute path, or a relative path interpreted from the - * directory where the top-level PhpWiki script (normally index.php) - * resides. - */ -//if (!defined('PHPWIKI_DIR')) define('PHPWIKI_DIR', 'C:/Apache/phpwiki'); -//if (!defined('PHPWIKI_DIR')) define('PHPWIKI_DIR', '/home/user/public_html/phpwiki'); - -/* - * PhpWiki will try to use short urls to pages, eg - * http://www.example.com/index.php/HomePage - * If you want to use urls like - * http://www.example.com/index.php?pagename=HomePage - * then define 'USE_PATH_INFO' as false by uncommenting the line below. - * NB: If you are using Apache >= 2.0.30, then you may need to to use - * the directive "AcceptPathInfo On" in your Apache configuration file - * (or in an appropriate <.htaccess> file) for the short urls to work: - * See http://httpd.apache.org/docs-2.0/mod/core.html#acceptpathinfo - * - * See also http://phpwiki.sourceforge.net/phpwiki/PrettyWiki for more ideas - * on prettifying your urls. - * - * Default: PhpWiki will try to divine whether use of PATH_INFO - * is supported in by your webserver/PHP configuration, and will - * use PATH_INFO if it thinks that is possible. - */ -//if (!defined('USE_PATH_INFO')) define('USE_PATH_INFO', false); - -/* - * VIRTUAL_PATH is the canonical URL path under which your your wiki - * appears. Normally this is the same as dirname(SCRIPT_NAME), however - * using, e.g. apaches mod_actions (or mod_rewrite), you can make it - * something different. - * - * If you do this, you should set VIRTUAL_PATH here. - * - * E.g. your phpwiki might be installed at at /scripts/phpwiki/index.php, - * but * you've made it accessible through eg. /wiki/HomePage. - * - * One way to do this is to create a directory named 'wiki' in your - * server root. The directory contains only one file: an .htaccess - * file which reads something like: - * - * Action x-phpwiki-page /scripts/phpwiki/index.php - * SetHandler x-phpwiki-page - * DirectoryIndex /scripts/phpwiki/index.php - * - * In that case you should set VIRTUAL_PATH to '/wiki'. - * - * (VIRTUAL_PATH is only used if USE_PATH_INFO is true.) - */ -//if (!defined('VIRTUAL_PATH')) define('VIRTUAL_PATH', '/SomeWiki'); - -///////////////////////////////////////////////////////////////////// -// -// Part Seven: -// Miscellaneous settings -// -///////////////////////////////////////////////////////////////////// - -/* - * Disable HTTP redirects. - * - * (You probably don't need to touch this.) - * - * PhpWiki uses HTTP redirects for some of it's functionality. - * (e.g. after saving changes, PhpWiki redirects your browser to - * view the page you just saved.) - * - * Some web service providers (notably free European Lycos) don't seem to - * allow these redirects. (On Lycos the result in an "Internal Server Error" - * report.) In that case you can set DISABLE_HTTP_REDIRECT to true. - * (In which case, PhpWiki will revert to sneakier tricks to try to - * redirect the browser...) - */ -//if (!defined('DISABLE_HTTP_REDIRECT')) define ('DISABLE_HTTP_REDIRECT', true); +require_once "lib/IniConfig.php"; +IniConfig("config/config.ini"); //if (defined('WIKI_SOAP') and WIKI_SOAP) return; @@ -960,6 +79,9 @@ if (defined('VIRTUAL_PATH') and defined('USE_PATH_INFO')) { //include "lib/main.php"; // $Log: not supported by cvs2svn $ +// Revision 1.140 2004/04/12 18:29:12 rurban +// exp. Session auth for already authenticated users from another app +// // Revision 1.139 2004/04/12 16:24:28 rurban // 1.3.10pre, JS_SEARCHREPLACE => pref option // diff --git a/lib/IniConfig.php b/lib/IniConfig.php index 7f5962ba2..a14aaccd4 100644 --- a/lib/IniConfig.php +++ b/lib/IniConfig.php @@ -1,5 +1,5 @@ <?php -rcs_id('$Id: IniConfig.php,v 1.2 2004-04-19 18:33:13 zorloc Exp $'); +rcs_id('$Id: IniConfig.php,v 1.3 2004-04-19 23:13:03 zorloc Exp $'); /************************************************************************** * A configurator intended to read it's config from a PHP-style INI file, @@ -51,7 +51,8 @@ $_IC_VALID_VALUE = array('WIKI_NAME', 'ADMIN_USER', 'ADMIN_PASSWD', 'PASSWORD_LENGTH_MINIMUM', 'USER_AUTH_POLICY', 'LDAP_AUTH_HOST', 'LDAP_BASE_DN', 'LDAP_AUTH_USER', 'LDAP_AUTH_PASSWORD', 'LDAP_SEARCH_FIELD', 'IMAP_AUTH_HOST', 'POP3_AUTH_HOST', - 'POP3_AUTH_PORT', 'AUTH_USER_FILE', 'GROUP_METHOD', + 'POP3_AUTH_PORT', 'AUTH_USER_FILE', 'AUTH_SESS_USER', + 'AUTH_SESS_LEVEL', 'GROUP_METHOD', 'AUTH_GROUP_FILE', 'EDITING_POLICY', 'THEME', 'CHARSET', 'DEFAULT_LANGUAGE', 'WIKI_PGSRC', 'DEFAULT_WIKI_PGSRC', 'ALLOWED_PROTOCOLS', 'INLINE_IMAGES', 'SUBPAGE_SEPARATOR', @@ -60,9 +61,9 @@ $_IC_VALID_VALUE = array('WIKI_NAME', 'ADMIN_USER', 'ADMIN_PASSWD', 'SCRIPT_NAME', 'DATA_PATH', 'PHPWIKI_DIR', 'VIRTUAL_PATH'); // List of all valid config options to be define()d which take booleans. -$_IC_VALID_BOOL = array('DEBUG', 'ENABLE_USER_NEW', 'ENABLE_REVERSE_DNS', - 'ENCRYPTED_PASSWD', 'ZIPDUMP_AUTH', 'ENABLE_RAW_HTML', - 'STRICT_MAILABLE_PAGEDUMPS', 'COMPRESS_OUTPUT', +$_IC_VALID_BOOL = array('DEBUG', 'ENABLE_USER_NEW', 'JS_SEARCHREPLACE', + 'ENABLE_REVERSE_DNS', 'ENCRYPTED_PASSWD', 'ZIPDUMP_AUTH', + 'ENABLE_RAW_HTML', 'STRICT_MAILABLE_PAGEDUMPS', 'COMPRESS_OUTPUT', 'WIKIDB_NOCACHE_MARKUP', 'ALLOW_ANON_USER', 'ALLOW_ANON_EDIT', 'ALLOW_BOGO_LOGIN', 'ALLOW_USER_PASSWORDS', 'AUTH_USER_FILE_STORABLE', 'ALLOW_HTTP_AUTH_LOGIN', @@ -75,7 +76,7 @@ function IniConfig($file) require_once('lib/pear/Config.php'); $config = new Config(); - $root = &$config->parseConfig('config/config.ini', 'inicommented'); + $root = &$config->parseConfig($file, 'inicommented'); $out = $root->toArray(); $rs = &$out['root']; @@ -225,9 +226,10 @@ function IniConfig($file) // Another "too-tricky" redefine global $KeywordLinkRegexp; - $KeywordLinkRegexp = @$rs['KEYWORD_LINK_REGEXP']; + $keywords = preg_split('/\s*:\s*/', @$rs['KEYWORDS']); + $KeywordLinkRegexp = '(?<=' . implode('|^', $keywords) . ')[[:upper:]].*$'; global $DisabledActions; - $DisabledActions = preg_split('/\s*:\s*/', @$rs['DisabledActions']); + $DisabledActions = preg_split('/\s*:\s*/', @$rs['DISABLED_ACTIONS']); } \ No newline at end of file diff --git a/lib/InlineParser.php b/lib/InlineParser.php index 1b0d1bb83..d19975903 100644 --- a/lib/InlineParser.php +++ b/lib/InlineParser.php @@ -1,686 +1,683 @@ -<?php rcs_id('$Id: InlineParser.php,v 1.36 2004-04-19 18:27:45 rurban Exp $'); -/* Copyright (C) 2002, Geoffrey T. Dairiki <dairiki@dairiki.org> - * - * This file is part of PhpWiki. - * - * PhpWiki 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. - * - * PhpWiki 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. - * - * You should have received a copy of the GNU General Public License - * along with PhpWiki; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -/** - * This is the code which deals with the inline part of the (new-style) - * wiki-markup. - * - * @package Markup - * @author Geoffrey T. Dairiki - */ -/** - */ - -/** - * This is the character used in wiki markup to escape characters with - * special meaning. - */ -define('ESCAPE_CHAR', '~'); - -require_once('lib/HtmlElement.php'); -require_once('lib/CachedMarkup.php'); -//require_once('lib/interwiki.php'); -require_once('lib/stdlib.php'); - - -function WikiEscape($text) { - return str_replace('#', ESCAPE_CHAR . '#', $text); -} - -function UnWikiEscape($text) { - return preg_replace('/' . ESCAPE_CHAR . '(.)/', '\1', $text); -} - -/** - * Return type from RegexpSet::match and RegexpSet::nextMatch. - * - * @see RegexpSet - */ -class RegexpSet_match { - /** - * The text leading up the the next match. - */ - var $prematch; - - /** - * The matched text. - */ - var $match; - - /** - * The text following the matched text. - */ - var $postmatch; - - /** - * Index of the regular expression which matched. - */ - var $regexp_ind; -} - -/** - * A set of regular expressions. - * - * This class is probably only useful for InlineTransformer. - */ -class RegexpSet -{ - /** Constructor - * - * @param array $regexps A list of regular expressions. The - * regular expressions should not include any sub-pattern groups - * "(...)". (Anonymous groups, like "(?:...)", as well as - * look-ahead and look-behind assertions are okay.) - */ - function RegexpSet ($regexps) { - $this->_regexps = $regexps; - } - - /** - * Search text for the next matching regexp from the Regexp Set. - * - * @param string $text The text to search. - * - * @return RegexpSet_match A RegexpSet_match object, or false if no match. - */ - function match ($text) { - return $this->_match($text, $this->_regexps, '*?'); - } - - /** - * Search for next matching regexp. - * - * Here, 'next' has two meanings: - * - * Match the next regexp(s) in the set, at the same position as the last match. - * - * If that fails, match the whole RegexpSet, starting after the position of the - * previous match. - * - * @param string $text Text to search. - * - * @param RegexpSet_match $prevMatch A RegexpSet_match object. - * $prevMatch should be a match object obtained by a previous - * match upon the same value of $text. - * - * @return RegexpSet_match A RegexpSet_match object, or false if no match. - */ - function nextMatch ($text, $prevMatch) { - // Try to find match at same position. - $pos = strlen($prevMatch->prematch); - $regexps = array_slice($this->_regexps, $prevMatch->regexp_ind + 1); - if ($regexps) { - $repeat = sprintf('{%d}', $pos); - if ( ($match = $this->_match($text, $regexps, $repeat)) ) { - $match->regexp_ind += $prevMatch->regexp_ind + 1; - return $match; - } - - } - - // Failed. Look for match after current position. - $repeat = sprintf('{%d,}?', $pos + 1); - return $this->_match($text, $this->_regexps, $repeat); - } - - - function _match ($text, $regexps, $repeat) { - $pat= "/ ( . $repeat ) ( (" . join(')|(', $regexps) . ") ) /Axs"; - - if (! preg_match($pat, $text, $m)) { - return false; - } - - $match = new RegexpSet_match; - $match->postmatch = substr($text, strlen($m[0])); - $match->prematch = $m[1]; - $match->match = $m[2]; - $match->regexp_ind = count($m) - 4; - - /* DEBUGGING - PrintXML(HTML::dl(HTML::dt("input"), - HTML::dd(HTML::pre($text)), - HTML::dt("match"), - HTML::dd(HTML::pre($match->match)), - HTML::dt("regexp"), - HTML::dd(HTML::pre($regexps[$match->regexp_ind])), - HTML::dt("prematch"), - HTML::dd(HTML::pre($match->prematch)))); - */ - return $match; - } -} - - - -/** - * A simple markup rule (i.e. terminal token). - * - * These are defined by a regexp. - * - * When a match is found for the regexp, the matching text is replaced. - * The replacement content is obtained by calling the SimpleMarkup::markup method. - */ -class SimpleMarkup -{ - var $_match_regexp; - - /** Get regexp. - * - * @return string Regexp which matches this token. - */ - function getMatchRegexp () { - return $this->_match_regexp; - } - - /** Markup matching text. - * - * @param string $match The text which matched the regexp - * (obtained from getMatchRegexp). - * - * @return mixed The expansion of the matched text. - */ - function markup ($match /*, $body */) { - trigger_error("pure virtual", E_USER_ERROR); - } -} - -/** - * A balanced markup rule. - * - * These are defined by a start regexp, and and end regexp. - */ -class BalancedMarkup -{ - var $_start_regexp; - - /** Get the starting regexp for this rule. - * - * @return string The starting regexp. - */ - function getStartRegexp () { - return $this->_start_regexp; - } - - /** Get the ending regexp for this rule. - * - * @param string $match The text which matched the starting regexp. - * - * @return string The ending regexp. - */ - function getEndRegexp ($match) { - return $this->_end_regexp; - } - - /** Get expansion for matching input. - * - * @param string $match The text which matched the starting regexp. - * - * @param mixed $body Transformed text found between the starting - * and ending regexps. - * - * @return mixed The expansion of the matched text. - */ - function markup ($match, $body) { - trigger_error("pure virtual", E_USER_ERROR); - } -} - -class Markup_escape extends SimpleMarkup -{ - function getMatchRegexp () { - return ESCAPE_CHAR . '(?: [[:alnum:]]+ | .)'; - } - - function markup ($match) { - assert(strlen($match) >= 2); - return substr($match, 1); - } -} - -function LinkBracketLink($bracketlink) { - global $AllowedProtocols, $InlineImages; - - //include_once("lib/interwiki.php"); - $intermap = getInterwikiMap(); - - // $bracketlink will start and end with brackets; in between will - // be either a page name, a URL or both separated by a pipe. - - // strip brackets and leading space - preg_match('/(\#?) \[\s* (?: (.*?) \s* (?<!' . ESCAPE_CHAR . ')(\|) )? \s* (.+?) \s*\]/x', - $bracketlink, $matches); - list (, $hash, $label, $bar, $rawlink) = $matches; - - $label = UnWikiEscape($label); - /* - * Check if the user has typed a explicit URL. This solves the - * problem where the URLs have a ~ character, which would be stripped away. - * "[http:/server/~name/]" will work as expected - * "http:/server/~name/" will NOT work as expected, will remove the ~ - */ - if (strstr($rawlink, "http://") or strstr($rawlink, "https://")) - $link = $rawlink; - else - $link = UnWikiEscape($rawlink); - - // [label|link] - // if label looks like a url to an image, we want an image link. - if (preg_match("/\\.($InlineImages)$/i", $label)) { - $imgurl = $label; - if (preg_match("/^" . $intermap->getRegexp() . ":/", $label)) { - $imgurl = $intermap->link($label); - $imgurl = $imgurl->getAttr('href'); - } elseif (! preg_match("#^($AllowedProtocols):#", $imgurl)) { - // local theme linkname like 'images/next.gif'. - global $Theme; - $imgurl = $Theme->getImageURL($imgurl); - } - $label = LinkImage($imgurl, $link); - } - - if ($hash) { - // It's an anchor, not a link... - $id = MangleXmlIdentifier($link); - return HTML::a(array('name' => $id, 'id' => $id), - $bar ? $label : $link); - } - - if (preg_match("#^($AllowedProtocols):#", $link)) { - // if it's an image, embed it; otherwise, it's a regular link - if (preg_match("/\\.($InlineImages)$/i", $link)) - return LinkImage($link, $label); - else - return new Cached_ExternalLink($link, $label); - } - elseif (preg_match("/^phpwiki:/", $link)) - return new Cached_PhpwikiURL($link, $label); - /* - * Inline images in Interwiki urls's: - * [File:my_image.gif] inlines the image, - * File:my_image.gif shows a plain inter-wiki link, - * [what a pic|File:my_image.gif] shows a named inter-wiki link to the gif - * [File:my_image.gif|what a pic] shows a inlimed image linked to the page "what a pic" - */ - elseif (preg_match("/^" . $intermap->getRegexp() . ":/", $link)) { - if (empty($label) && preg_match("/\\.($InlineImages)$/i", $link)) { - // if without label => inlined image [File:xx.gif] - $imgurl = $intermap->link($link); - return LinkImage($imgurl->getAttr('href'), $label); - } - return new Cached_InterwikiLink($link, $label); - } else { - // Split anchor off end of pagename. - if (preg_match('/\A(.*)(?<!'.ESCAPE_CHAR.')#(.*?)\Z/', $rawlink, $m)) { - list(,$rawlink,$anchor) = $m; - $pagename = UnWikiEscape($rawlink); - $anchor = UnWikiEscape($anchor); - if (!$label) - $label = $link; - } - else { - $pagename = $link; - $anchor = false; - } - return new Cached_WikiLink($pagename, $label, $anchor); - } -} - -class Markup_bracketlink extends SimpleMarkup -{ - var $_match_regexp = "\\#? \\[ .*? [^]\\s] .*? \\]"; - - function markup ($match) { - $link = LinkBracketLink($match); - assert($link->isInlineElement()); - return $link; - } -} - -class Markup_url extends SimpleMarkup -{ - function getMatchRegexp () { - global $AllowedProtocols; - return "(?<![[:alnum:]]) (?:$AllowedProtocols) : [^\s<>\"']+ (?<![ ,.?; \] \) ])"; - } - - function markup ($match) { - return new Cached_ExternalLink(UnWikiEscape($match)); - } -} - - -class Markup_interwiki extends SimpleMarkup -{ - function getMatchRegexp () { - global $request; - $map = getInterwikiMap(); - return "(?<! [[:alnum:]])" . $map->getRegexp(). ": \S+ (?<![ ,.?;! \] \) \" \' ])"; - } - - function markup ($match) { - //$map = getInterwikiMap(); - return new Cached_InterwikiLink(UnWikiEscape($match)); - } -} - -class Markup_wikiword extends SimpleMarkup -{ - function getMatchRegexp () { - global $WikiNameRegexp; - return " $WikiNameRegexp"; - } - - function markup ($match) { - if ($this->_isWikiUserPage($match)) - return new Cached_UserLink($match); //$this->_UserLink($match); - else - return new Cached_WikiLink($match); - } - - // FIXME: there's probably a more useful place to put these two functions - function _isWikiUserPage ($page) { - global $request; - $dbi = $request->getDbh(); - $page_handle = $dbi->getPage($page); - if ($page_handle->get('pref')) - return true; - else - return false; - } - - function _UserLink($PageName) { - $link = HTML::a(array('href' => $PageName)); - $link->pushContent(PossiblyGlueIconToText('wikiuser', $PageName)); - $link->setAttr('class', 'wikiuser'); - return $link; - } -} - -class Markup_linebreak extends SimpleMarkup -{ - //var $_match_regexp = "(?: (?<! %) %%% (?! %) | <(?:br|BR)> | <(?:br|BR) \/> )"; - var $_match_regexp = "(?: (?<! %) %%% (?! %) | <(?:br|BR)> )"; - - function markup ($match) { - return HTML::br(); - } -} - -class Markup_old_emphasis extends BalancedMarkup -{ - var $_start_regexp = "''|__"; - - function getEndRegexp ($match) { - return $match; - } - - function markup ($match, $body) { - $tag = $match == "''" ? 'em' : 'strong'; - return new HtmlElement($tag, $body); - } -} - -class Markup_nestled_emphasis extends BalancedMarkup -{ - function getStartRegexp() { - static $start_regexp = false; - - if (!$start_regexp) { - // The three possible delimiters - // (none of which can be followed by itself.) - $i = "_ (?! _)"; - $b = "\\* (?! \\*)"; - $tt = "= (?! =)"; - - $any = "(?: ${i}|${b}|${tt})"; // any of the three. - - // Any of [_*=] is okay if preceded by space or one of [-"'/:] - $start[] = "(?<= \\s|^|[-\"'\\/:]) ${any}"; - - // _ or * is okay after = as long as not immediately followed by = - $start[] = "(?<= =) (?: ${i}|${b}) (?! =)"; - // etc... - $start[] = "(?<= _) (?: ${b}|${tt}) (?! _)"; - $start[] = "(?<= \\*) (?: ${i}|${tt}) (?! \\*)"; - - - // any delimiter okay after an opening brace ( [{<(] ) - // as long as it's not immediately followed by the matching closing - // brace. - $start[] = "(?<= { ) ${any} (?! } )"; - $start[] = "(?<= < ) ${any} (?! > )"; - $start[] = "(?<= \\( ) ${any} (?! \\) )"; - - $start = "(?:" . join('|', $start) . ")"; - - // Any of the above must be immediately followed by non-whitespace. - $start_regexp = $start . "(?= \S)"; - } - - return $start_regexp; - } - - function getEndRegexp ($match) { - $chr = preg_quote($match); - return "(?<= \S | ^ ) (?<! $chr) $chr (?! $chr) (?= \s | [-)}>\"'\\/:.,;!? _*=] | $)"; - } - - function markup ($match, $body) { - switch ($match) { - case '*': return new HtmlElement('b', $body); - case '=': return new HtmlElement('tt', $body); - case '_': return new HtmlElement('i', $body); - } - } -} - -class Markup_html_emphasis extends BalancedMarkup -{ - var $_start_regexp = "<(?: b|big|i|small|tt| - em|strong| - cite|code|dfn|kbd|samp|var| - sup|sub )>"; - //rurban: abbr|acronym need an optional title tag. - //sf.net bug #728595 - - function getEndRegexp ($match) { - return "<\\/" . substr($match, 1); - } - - function markup ($match, $body) { - $tag = substr($match, 1, -1); - return new HtmlElement($tag, $body); - } -} - -class Markup_html_abbr extends BalancedMarkup -{ - //rurban: abbr|acronym need an optional title tag. - //sf.net bug #728595 - var $_start_regexp = "<(?: abbr|acronym )(?: \stitle=[^>]*)?>"; - - function getEndRegexp ($match) { - if (substr($match,1,4) == 'abbr') - $tag = 'abbr'; - else - $tag = 'acronym'; - return "<\\/" . $tag . '>'; - } - - function markup ($match, $body) { - if (substr($match,1,4) == 'abbr') - $tag = 'abbr'; - else - $tag = 'acronym'; - $rest = substr($match,1+strlen($tag),-1); - if (!empty($rest)) { - list($key,$val) = explode("=",$rest); - $args = array($key => $val); - } else $args = array(); - return new HtmlElement($tag, $args, $body); - } -} - -// FIXME: Do away with magic phpwiki forms. (Maybe phpwiki: links too?) -// FIXME: Do away with plugin-links. They seem not to be used. -//Plugin link - - -class InlineTransformer -{ - var $_regexps = array(); - var $_markup = array(); - - function InlineTransformer ($markup_types = false) { - if (!$markup_types) - $markup_types = array('escape', 'bracketlink', 'url', - 'interwiki', 'wikiword', 'linebreak', - 'old_emphasis', 'nestled_emphasis', - 'html_emphasis', 'html_abbr'); - - foreach ($markup_types as $mtype) { - $class = "Markup_$mtype"; - $this->_addMarkup(new $class); - } - } - - function _addMarkup ($markup) { - if (isa($markup, 'SimpleMarkup')) - $regexp = $markup->getMatchRegexp(); - else - $regexp = $markup->getStartRegexp(); - - assert(!isset($this->_markup[$regexp])); - $this->_regexps[] = $regexp; - $this->_markup[] = $markup; - } - - function parse (&$text, $end_regexps = array('$')) { - $regexps = $this->_regexps; - - // $end_re takes precedence: "favor reduce over shift" - array_unshift($regexps, $end_regexps[0]); - $regexps = new RegexpSet($regexps); - - $input = $text; - $output = new XmlContent; - - $match = $regexps->match($input); - - while ($match) { - if ($match->regexp_ind == 0) { - // No start pattern found before end pattern. - // We're all done! - $output->pushContent($match->prematch); - $text = $match->postmatch; - return $output; - } - - $markup = $this->_markup[$match->regexp_ind - 1]; - //if (!$markup) return false; - $body = $this->_parse_markup_body($markup, $match->match, $match->postmatch, $end_regexps); - if (!$body) { - // Couldn't match balanced expression. - // Ignore and look for next matching start regexp. - $match = $regexps->nextMatch($input, $match); - continue; - } - - // Matched markup. Eat input, push output. - // FIXME: combine adjacent strings. - $input = $match->postmatch; - $output->pushContent($match->prematch, - $markup->markup($match->match, $body)); - - $match = $regexps->match($input); - } - - // No pattern matched, not even the end pattern. - // Parse fails. - return false; - } - - function _parse_markup_body ($markup, $match, &$text, $end_regexps) { - if (isa($markup, 'SimpleMarkup')) - return true; // Done. SimpleMarkup is simple. - - array_unshift($end_regexps, $markup->getEndRegexp($match)); - // Optimization: if no end pattern in text, we know the - // parse will fail. This is an important optimization, - // e.g. when text is "*lots *of *start *delims *with - // *no *matching *end *delims". - $ends_pat = "/(?:" . join(").*(?:", $end_regexps) . ")/xs"; - if (!preg_match($ends_pat, $text)) - return false; - return $this->parse($text, $end_regexps); - } -} - -class LinkTransformer extends InlineTransformer -{ - function LinkTransformer () { - $this->InlineTransformer(array('escape', 'bracketlink', 'url', - 'interwiki', 'wikiword')); - } -} - -function TransformInline($text, $markup = 2.0, $basepage=false) { - static $trfm; - - if (empty($trfm)) { - $trfm = new InlineTransformer; - } - - if ($markup < 2.0) { - $text = ConvertOldMarkup($text, 'inline'); - } - - if ($basepage) { - return new CacheableMarkup($trfm->parse($text), $basepage); - } - return $trfm->parse($text); -} - -function TransformLinks($text, $markup = 2.0, $basepage = false) { - static $trfm; - - if (empty($trfm)) { - $trfm = new LinkTransformer; - } - - if ($markup < 2.0) { - $text = ConvertOldMarkup($text, 'links'); - } - - if ($basepage) { - return new CacheableMarkup($trfm->parse($text), $basepage); - } - return $trfm->parse($text); -} - -// (c-file-style: "gnu") -// Local Variables: -// mode: php -// tab-width: 8 -// c-basic-offset: 4 -// c-hanging-comment-ender-p: nil -// indent-tabs-mode: nil -// End: -?> +<?php rcs_id('$Id: InlineParser.php,v 1.37 2004-04-19 23:13:03 zorloc Exp $'); +/* Copyright (C) 2002, Geoffrey T. Dairiki <dairiki@dairiki.org> + * + * This file is part of PhpWiki. + * + * PhpWiki 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. + * + * PhpWiki 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. + * + * You should have received a copy of the GNU General Public License + * along with PhpWiki; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/** + * This is the code which deals with the inline part of the (new-style) + * wiki-markup. + * + * @package Markup + * @author Geoffrey T. Dairiki + */ +/** + */ + +/** + * This is the character used in wiki markup to escape characters with + * special meaning. + */ +define('ESCAPE_CHAR', '~'); + +require_once('lib/HtmlElement.php'); +require_once('lib/CachedMarkup.php'); +//require_once('lib/interwiki.php'); +require_once('lib/stdlib.php'); + + +function WikiEscape($text) { + return str_replace('#', ESCAPE_CHAR . '#', $text); +} + +function UnWikiEscape($text) { + return preg_replace('/' . ESCAPE_CHAR . '(.)/', '\1', $text); +} + +/** + * Return type from RegexpSet::match and RegexpSet::nextMatch. + * + * @see RegexpSet + */ +class RegexpSet_match { + /** + * The text leading up the the next match. + */ + var $prematch; + + /** + * The matched text. + */ + var $match; + + /** + * The text following the matched text. + */ + var $postmatch; + + /** + * Index of the regular expression which matched. + */ + var $regexp_ind; +} + +/** + * A set of regular expressions. + * + * This class is probably only useful for InlineTransformer. + */ +class RegexpSet +{ + /** Constructor + * + * @param array $regexps A list of regular expressions. The + * regular expressions should not include any sub-pattern groups + * "(...)". (Anonymous groups, like "(?:...)", as well as + * look-ahead and look-behind assertions are okay.) + */ + function RegexpSet ($regexps) { + $this->_regexps = $regexps; + } + + /** + * Search text for the next matching regexp from the Regexp Set. + * + * @param string $text The text to search. + * + * @return RegexpSet_match A RegexpSet_match object, or false if no match. + */ + function match ($text) { + return $this->_match($text, $this->_regexps, '*?'); + } + + /** + * Search for next matching regexp. + * + * Here, 'next' has two meanings: + * + * Match the next regexp(s) in the set, at the same position as the last match. + * + * If that fails, match the whole RegexpSet, starting after the position of the + * previous match. + * + * @param string $text Text to search. + * + * @param RegexpSet_match $prevMatch A RegexpSet_match object. + * $prevMatch should be a match object obtained by a previous + * match upon the same value of $text. + * + * @return RegexpSet_match A RegexpSet_match object, or false if no match. + */ + function nextMatch ($text, $prevMatch) { + // Try to find match at same position. + $pos = strlen($prevMatch->prematch); + $regexps = array_slice($this->_regexps, $prevMatch->regexp_ind + 1); + if ($regexps) { + $repeat = sprintf('{%d}', $pos); + if ( ($match = $this->_match($text, $regexps, $repeat)) ) { + $match->regexp_ind += $prevMatch->regexp_ind + 1; + return $match; + } + + } + + // Failed. Look for match after current position. + $repeat = sprintf('{%d,}?', $pos + 1); + return $this->_match($text, $this->_regexps, $repeat); + } + + + function _match ($text, $regexps, $repeat) { + $pat= "/ ( . $repeat ) ( (" . join(')|(', $regexps) . ") ) /Axs"; + + if (! preg_match($pat, $text, $m)) { + return false; + } + + $match = new RegexpSet_match; + $match->postmatch = substr($text, strlen($m[0])); + $match->prematch = $m[1]; + $match->match = $m[2]; + $match->regexp_ind = count($m) - 4; + + /* DEBUGGING + PrintXML(HTML::dl(HTML::dt("input"), + HTML::dd(HTML::pre($text)), + HTML::dt("match"), + HTML::dd(HTML::pre($match->match)), + HTML::dt("regexp"), + HTML::dd(HTML::pre($regexps[$match->regexp_ind])), + HTML::dt("prematch"), + HTML::dd(HTML::pre($match->prematch)))); + */ + return $match; + } +} + + + +/** + * A simple markup rule (i.e. terminal token). + * + * These are defined by a regexp. + * + * When a match is found for the regexp, the matching text is replaced. + * The replacement content is obtained by calling the SimpleMarkup::markup method. + */ +class SimpleMarkup +{ + var $_match_regexp; + + /** Get regexp. + * + * @return string Regexp which matches this token. + */ + function getMatchRegexp () { + return $this->_match_regexp; + } + + /** Markup matching text. + * + * @param string $match The text which matched the regexp + * (obtained from getMatchRegexp). + * + * @return mixed The expansion of the matched text. + */ + function markup ($match /*, $body */) { + trigger_error("pure virtual", E_USER_ERROR); + } +} + +/** + * A balanced markup rule. + * + * These are defined by a start regexp, and and end regexp. + */ +class BalancedMarkup +{ + var $_start_regexp; + + /** Get the starting regexp for this rule. + * + * @return string The starting regexp. + */ + function getStartRegexp () { + return $this->_start_regexp; + } + + /** Get the ending regexp for this rule. + * + * @param string $match The text which matched the starting regexp. + * + * @return string The ending regexp. + */ + function getEndRegexp ($match) { + return $this->_end_regexp; + } + + /** Get expansion for matching input. + * + * @param string $match The text which matched the starting regexp. + * + * @param mixed $body Transformed text found between the starting + * and ending regexps. + * + * @return mixed The expansion of the matched text. + */ + function markup ($match, $body) { + trigger_error("pure virtual", E_USER_ERROR); + } +} + +class Markup_escape extends SimpleMarkup +{ + function getMatchRegexp () { + return ESCAPE_CHAR . '(?: [[:alnum:]]+ | .)'; + } + + function markup ($match) { + assert(strlen($match) >= 2); + return substr($match, 1); + } +} + +function LinkBracketLink($bracketlink) { + //include_once("lib/interwiki.php"); + $intermap = getInterwikiMap(); + + // $bracketlink will start and end with brackets; in between will + // be either a page name, a URL or both separated by a pipe. + + // strip brackets and leading space + preg_match('/(\#?) \[\s* (?: (.*?) \s* (?<!' . ESCAPE_CHAR . ')(\|) )? \s* (.+?) \s*\]/x', + $bracketlink, $matches); + list (, $hash, $label, $bar, $rawlink) = $matches; + + $label = UnWikiEscape($label); + /* + * Check if the user has typed a explicit URL. This solves the + * problem where the URLs have a ~ character, which would be stripped away. + * "[http:/server/~name/]" will work as expected + * "http:/server/~name/" will NOT work as expected, will remove the ~ + */ + if (strstr($rawlink, "http://") or strstr($rawlink, "https://")) + $link = $rawlink; + else + $link = UnWikiEscape($rawlink); + + // [label|link] + // if label looks like a url to an image, we want an image link. + if (preg_match("/\\.(" . INLINE_IMAGES . ")$/i", $label)) { + $imgurl = $label; + if (preg_match("/^" . $intermap->getRegexp() . ":/", $label)) { + $imgurl = $intermap->link($label); + $imgurl = $imgurl->getAttr('href'); + } elseif (! preg_match("#^(" . ALLOWED_PROTOCOLS . "):#", $imgurl)) { + // local theme linkname like 'images/next.gif'. + global $Theme; + $imgurl = $Theme->getImageURL($imgurl); + } + $label = LinkImage($imgurl, $link); + } + + if ($hash) { + // It's an anchor, not a link... + $id = MangleXmlIdentifier($link); + return HTML::a(array('name' => $id, 'id' => $id), + $bar ? $label : $link); + } + + if (preg_match("#^(" . ALLOWED_PROTOCOLS . "):#", $link)) { + // if it's an image, embed it; otherwise, it's a regular link + if (preg_match("/\\.(" . INLINE_IMAGES . ")$/i", $link)) + return LinkImage($link, $label); + else + return new Cached_ExternalLink($link, $label); + } + elseif (preg_match("/^phpwiki:/", $link)) + return new Cached_PhpwikiURL($link, $label); + /* + * Inline images in Interwiki urls's: + * [File:my_image.gif] inlines the image, + * File:my_image.gif shows a plain inter-wiki link, + * [what a pic|File:my_image.gif] shows a named inter-wiki link to the gif + * [File:my_image.gif|what a pic] shows a inlimed image linked to the page "what a pic" + */ + elseif (preg_match("/^" . $intermap->getRegexp() . ":/", $link)) { + if (empty($label) && preg_match("/\\.(" . INLINE_IMAGES . ")$/i", $link)) { + // if without label => inlined image [File:xx.gif] + $imgurl = $intermap->link($link); + return LinkImage($imgurl->getAttr('href'), $label); + } + return new Cached_InterwikiLink($link, $label); + } else { + // Split anchor off end of pagename. + if (preg_match('/\A(.*)(?<!'.ESCAPE_CHAR.')#(.*?)\Z/', $rawlink, $m)) { + list(,$rawlink,$anchor) = $m; + $pagename = UnWikiEscape($rawlink); + $anchor = UnWikiEscape($anchor); + if (!$label) + $label = $link; + } + else { + $pagename = $link; + $anchor = false; + } + return new Cached_WikiLink($pagename, $label, $anchor); + } +} + +class Markup_bracketlink extends SimpleMarkup +{ + var $_match_regexp = "\\#? \\[ .*? [^]\\s] .*? \\]"; + + function markup ($match) { + $link = LinkBracketLink($match); + assert($link->isInlineElement()); + return $link; + } +} + +class Markup_url extends SimpleMarkup +{ + function getMatchRegexp () { + return "(?<![[:alnum:]]) (?:" . ALLOWED_PROTOCOLS . ") : [^\s<>\"']+ (?<![ ,.?; \] \) ])"; + } + + function markup ($match) { + return new Cached_ExternalLink(UnWikiEscape($match)); + } +} + + +class Markup_interwiki extends SimpleMarkup +{ + function getMatchRegexp () { + global $request; + $map = getInterwikiMap(); + return "(?<! [[:alnum:]])" . $map->getRegexp(). ": \S+ (?<![ ,.?;! \] \) \" \' ])"; + } + + function markup ($match) { + //$map = getInterwikiMap(); + return new Cached_InterwikiLink(UnWikiEscape($match)); + } +} + +class Markup_wikiword extends SimpleMarkup +{ + function getMatchRegexp () { + global $WikiNameRegexp; + return " $WikiNameRegexp"; + } + + function markup ($match) { + if ($this->_isWikiUserPage($match)) + return new Cached_UserLink($match); //$this->_UserLink($match); + else + return new Cached_WikiLink($match); + } + + // FIXME: there's probably a more useful place to put these two functions + function _isWikiUserPage ($page) { + global $request; + $dbi = $request->getDbh(); + $page_handle = $dbi->getPage($page); + if ($page_handle->get('pref')) + return true; + else + return false; + } + + function _UserLink($PageName) { + $link = HTML::a(array('href' => $PageName)); + $link->pushContent(PossiblyGlueIconToText('wikiuser', $PageName)); + $link->setAttr('class', 'wikiuser'); + return $link; + } +} + +class Markup_linebreak extends SimpleMarkup +{ + //var $_match_regexp = "(?: (?<! %) %%% (?! %) | <(?:br|BR)> | <(?:br|BR) \/> )"; + var $_match_regexp = "(?: (?<! %) %%% (?! %) | <(?:br|BR)> )"; + + function markup ($match) { + return HTML::br(); + } +} + +class Markup_old_emphasis extends BalancedMarkup +{ + var $_start_regexp = "''|__"; + + function getEndRegexp ($match) { + return $match; + } + + function markup ($match, $body) { + $tag = $match == "''" ? 'em' : 'strong'; + return new HtmlElement($tag, $body); + } +} + +class Markup_nestled_emphasis extends BalancedMarkup +{ + function getStartRegexp() { + static $start_regexp = false; + + if (!$start_regexp) { + // The three possible delimiters + // (none of which can be followed by itself.) + $i = "_ (?! _)"; + $b = "\\* (?! \\*)"; + $tt = "= (?! =)"; + + $any = "(?: ${i}|${b}|${tt})"; // any of the three. + + // Any of [_*=] is okay if preceded by space or one of [-"'/:] + $start[] = "(?<= \\s|^|[-\"'\\/:]) ${any}"; + + // _ or * is okay after = as long as not immediately followed by = + $start[] = "(?<= =) (?: ${i}|${b}) (?! =)"; + // etc... + $start[] = "(?<= _) (?: ${b}|${tt}) (?! _)"; + $start[] = "(?<= \\*) (?: ${i}|${tt}) (?! \\*)"; + + + // any delimiter okay after an opening brace ( [{<(] ) + // as long as it's not immediately followed by the matching closing + // brace. + $start[] = "(?<= { ) ${any} (?! } )"; + $start[] = "(?<= < ) ${any} (?! > )"; + $start[] = "(?<= \\( ) ${any} (?! \\) )"; + + $start = "(?:" . join('|', $start) . ")"; + + // Any of the above must be immediately followed by non-whitespace. + $start_regexp = $start . "(?= \S)"; + } + + return $start_regexp; + } + + function getEndRegexp ($match) { + $chr = preg_quote($match); + return "(?<= \S | ^ ) (?<! $chr) $chr (?! $chr) (?= \s | [-)}>\"'\\/:.,;!? _*=] | $)"; + } + + function markup ($match, $body) { + switch ($match) { + case '*': return new HtmlElement('b', $body); + case '=': return new HtmlElement('tt', $body); + case '_': return new HtmlElement('i', $body); + } + } +} + +class Markup_html_emphasis extends BalancedMarkup +{ + var $_start_regexp = "<(?: b|big|i|small|tt| + em|strong| + cite|code|dfn|kbd|samp|var| + sup|sub )>"; + //rurban: abbr|acronym need an optional title tag. + //sf.net bug #728595 + + function getEndRegexp ($match) { + return "<\\/" . substr($match, 1); + } + + function markup ($match, $body) { + $tag = substr($match, 1, -1); + return new HtmlElement($tag, $body); + } +} + +class Markup_html_abbr extends BalancedMarkup +{ + //rurban: abbr|acronym need an optional title tag. + //sf.net bug #728595 + var $_start_regexp = "<(?: abbr|acronym )(?: \stitle=[^>]*)?>"; + + function getEndRegexp ($match) { + if (substr($match,1,4) == 'abbr') + $tag = 'abbr'; + else + $tag = 'acronym'; + return "<\\/" . $tag . '>'; + } + + function markup ($match, $body) { + if (substr($match,1,4) == 'abbr') + $tag = 'abbr'; + else + $tag = 'acronym'; + $rest = substr($match,1+strlen($tag),-1); + if (!empty($rest)) { + list($key,$val) = explode("=",$rest); + $args = array($key => $val); + } else $args = array(); + return new HtmlElement($tag, $args, $body); + } +} + +// FIXME: Do away with magic phpwiki forms. (Maybe phpwiki: links too?) +// FIXME: Do away with plugin-links. They seem not to be used. +//Plugin link + + +class InlineTransformer +{ + var $_regexps = array(); + var $_markup = array(); + + function InlineTransformer ($markup_types = false) { + if (!$markup_types) + $markup_types = array('escape', 'bracketlink', 'url', + 'interwiki', 'wikiword', 'linebreak', + 'old_emphasis', 'nestled_emphasis', + 'html_emphasis', 'html_abbr'); + + foreach ($markup_types as $mtype) { + $class = "Markup_$mtype"; + $this->_addMarkup(new $class); + } + } + + function _addMarkup ($markup) { + if (isa($markup, 'SimpleMarkup')) + $regexp = $markup->getMatchRegexp(); + else + $regexp = $markup->getStartRegexp(); + + assert(!isset($this->_markup[$regexp])); + $this->_regexps[] = $regexp; + $this->_markup[] = $markup; + } + + function parse (&$text, $end_regexps = array('$')) { + $regexps = $this->_regexps; + + // $end_re takes precedence: "favor reduce over shift" + array_unshift($regexps, $end_regexps[0]); + $regexps = new RegexpSet($regexps); + + $input = $text; + $output = new XmlContent; + + $match = $regexps->match($input); + + while ($match) { + if ($match->regexp_ind == 0) { + // No start pattern found before end pattern. + // We're all done! + $output->pushContent($match->prematch); + $text = $match->postmatch; + return $output; + } + + $markup = $this->_markup[$match->regexp_ind - 1]; + //if (!$markup) return false; + $body = $this->_parse_markup_body($markup, $match->match, $match->postmatch, $end_regexps); + if (!$body) { + // Couldn't match balanced expression. + // Ignore and look for next matching start regexp. + $match = $regexps->nextMatch($input, $match); + continue; + } + + // Matched markup. Eat input, push output. + // FIXME: combine adjacent strings. + $input = $match->postmatch; + $output->pushContent($match->prematch, + $markup->markup($match->match, $body)); + + $match = $regexps->match($input); + } + + // No pattern matched, not even the end pattern. + // Parse fails. + return false; + } + + function _parse_markup_body ($markup, $match, &$text, $end_regexps) { + if (isa($markup, 'SimpleMarkup')) + return true; // Done. SimpleMarkup is simple. + + array_unshift($end_regexps, $markup->getEndRegexp($match)); + // Optimization: if no end pattern in text, we know the + // parse will fail. This is an important optimization, + // e.g. when text is "*lots *of *start *delims *with + // *no *matching *end *delims". + $ends_pat = "/(?:" . join(").*(?:", $end_regexps) . ")/xs"; + if (!preg_match($ends_pat, $text)) + return false; + return $this->parse($text, $end_regexps); + } +} + +class LinkTransformer extends InlineTransformer +{ + function LinkTransformer () { + $this->InlineTransformer(array('escape', 'bracketlink', 'url', + 'interwiki', 'wikiword')); + } +} + +function TransformInline($text, $markup = 2.0, $basepage=false) { + static $trfm; + + if (empty($trfm)) { + $trfm = new InlineTransformer; + } + + if ($markup < 2.0) { + $text = ConvertOldMarkup($text, 'inline'); + } + + if ($basepage) { + return new CacheableMarkup($trfm->parse($text), $basepage); + } + return $trfm->parse($text); +} + +function TransformLinks($text, $markup = 2.0, $basepage = false) { + static $trfm; + + if (empty($trfm)) { + $trfm = new LinkTransformer; + } + + if ($markup < 2.0) { + $text = ConvertOldMarkup($text, 'links'); + } + + if ($basepage) { + return new CacheableMarkup($trfm->parse($text), $basepage); + } + return $trfm->parse($text); +} + +// (c-file-style: "gnu") +// Local Variables: +// mode: php +// tab-width: 8 +// c-basic-offset: 4 +// c-hanging-comment-ender-p: nil +// indent-tabs-mode: nil +// End: +?> diff --git a/lib/PageType.php b/lib/PageType.php index f9723612e..f142dc288 100644 --- a/lib/PageType.php +++ b/lib/PageType.php @@ -1,5 +1,5 @@ <?php // -*-php-*- -rcs_id('$Id: PageType.php,v 1.26 2004-04-19 18:27:45 rurban Exp $'); +rcs_id('$Id: PageType.php,v 1.27 2004-04-19 23:13:03 zorloc Exp $'); /* Copyright 1999,2000,2001,2002,2003,2004 $ThePhpWikiProgrammingTeam @@ -188,7 +188,6 @@ class PageType_interwikimap extends PageType function _parseMap ($text) { - global $AllowedProtocols; if (!preg_match_all("/^\s*(\S+)\s+(\S+)/m", $text, $matches, PREG_SET_ORDER)) return false; diff --git a/lib/interwiki.php b/lib/interwiki.php index 81f98b8da..798e62e2b 100644 --- a/lib/interwiki.php +++ b/lib/interwiki.php @@ -1,4 +1,4 @@ -<?php rcs_id('$Id: interwiki.php,v 1.23 2002-10-06 16:45:10 dairiki Exp $'); +<?php rcs_id('$Id: interwiki.php,v 1.24 2004-04-19 23:13:03 zorloc Exp $'); class InterWikiMap { function InterWikiMap (&$request) { @@ -61,7 +61,6 @@ class InterWikiMap { function _parseMap ($text) { - global $AllowedProtocols; if (!preg_match_all("/^\s*(\S+)\s+(\S+)/m", $text, $matches, PREG_SET_ORDER)) return false; diff --git a/lib/loadsave.php b/lib/loadsave.php index 3079bd60b..0bbee197e 100644 --- a/lib/loadsave.php +++ b/lib/loadsave.php @@ -1,5 +1,5 @@ <?php //-*-php-*- -rcs_id('$Id: loadsave.php,v 1.95 2004-04-18 01:11:52 rurban Exp $'); +rcs_id('$Id: loadsave.php,v 1.96 2004-04-19 23:13:03 zorloc Exp $'); /* Copyright 1999, 2000, 2001, 2002 $ThePhpWikiProgrammingTeam @@ -250,9 +250,9 @@ function DumpHtmlToDir (&$request) $dbi = $request->getDbh(); $pages = $dbi->getAllPages(); - global $HTML_DUMP_SUFFIX, $Theme; - if ($HTML_DUMP_SUFFIX) - $Theme->HTML_DUMP_SUFFIX = $HTML_DUMP_SUFFIX; + global $Theme; + if (defined('HTML_DUMP_SUFFIX')) + $Theme->HTML_DUMP_SUFFIX = HTML_DUMP_SUFFIX; $Theme->DUMP_MODE = 'HTML'; while ($page = $pages->next()) { @@ -346,9 +346,9 @@ function MakeWikiZipHtml (&$request) $dbi = $request->getDbh(); $pages = $dbi->getAllPages(); - global $HTML_DUMP_SUFFIX, $Theme; - if ($HTML_DUMP_SUFFIX) - $Theme->HTML_DUMP_SUFFIX = $HTML_DUMP_SUFFIX; + global $Theme; + if (defined('HTML_DUMP_SUFFIX')) + $Theme->HTML_DUMP_SUFFIX = HTML_DUMP_SUFFIX; while ($page = $pages->next()) { if (! $request->getArg('start_debug')) @@ -881,6 +881,11 @@ function LoadPostFile (&$request) /** $Log: not supported by cvs2svn $ + Revision 1.95 2004/04/18 01:11:52 rurban + more numeric pagename fixes. + fixed action=upload with merge conflict warnings. + charset changed from constant to global (dynamic utf-8 switching) + Revision 1.94 2004/03/14 16:36:37 rurban dont load backup files diff --git a/lib/plugin/InterWikiSearch.php b/lib/plugin/InterWikiSearch.php index a5ef0419f..6849b3b58 100644 --- a/lib/plugin/InterWikiSearch.php +++ b/lib/plugin/InterWikiSearch.php @@ -1,5 +1,5 @@ <?php // -*-php-*- -rcs_id('$Id: InterWikiSearch.php,v 1.5 2004-02-19 22:06:53 rurban Exp $'); +rcs_id('$Id: InterWikiSearch.php,v 1.6 2004-04-19 23:13:03 zorloc Exp $'); /** Copyright 1999, 2000, 2001, 2002 $ThePhpWikiProgrammingTeam @@ -37,7 +37,7 @@ extends WikiPlugin function getVersion() { return preg_replace("/[Revision: $]/", '', - "\$Revision: 1.5 $"); + "\$Revision: 1.6 $"); } function getDefaultArguments() { @@ -48,7 +48,7 @@ extends WikiPlugin $args = $this->getArgs($argstr, $request); extract($args); - if (!DEBUG) + if (defined('DEBUG') && !DEBUG) return $this->disabled("Sorry, this plugin is currently out of order."); $page = $dbi->getPage($request->getArg('pagename')); @@ -67,7 +67,7 @@ extends WikiPlugin /** * @desc */ -if (DEBUG) { +if (defined('DEBUG') && DEBUG) { class PageFormatter_searchableInterWikiMap extends PageFormatter_interwikimap {} @@ -122,6 +122,9 @@ extends PageType_interwikimap // $Log: not supported by cvs2svn $ +// Revision 1.5 2004/02/19 22:06:53 rurban +// use new class, to be able to get rid of lib/interwiki.php +// // Revision 1.4 2004/02/17 12:11:36 rurban // added missing 4th basepage arg at plugin->run() to almost all plugins. This caused no harm so far, because it was silently dropped on normal usage. However on plugin internal ->run invocations it failed. (InterWikiSearch, IncludeSiteMap, ...) // diff --git a/lib/plugin/SystemInfo.php b/lib/plugin/SystemInfo.php index 56e928a3b..cadb5d1f3 100644 --- a/lib/plugin/SystemInfo.php +++ b/lib/plugin/SystemInfo.php @@ -1,5 +1,5 @@ <?php // -*-php-*- -rcs_id('$Id: SystemInfo.php,v 1.13 2004-04-18 01:11:52 rurban Exp $'); +rcs_id('$Id: SystemInfo.php,v 1.14 2004-04-19 23:13:04 zorloc Exp $'); /** Copyright (C) 1999, 2000, 2001, 2002 $ThePhpWikiProgrammingTeam @@ -58,7 +58,7 @@ extends WikiPlugin function getVersion() { return preg_replace("/[Revision: $]/", '', - "\$Revision: 1.13 $"); + "\$Revision: 1.14 $"); } function getExpire($dbi, $argarray, $request) { @@ -290,13 +290,13 @@ extends WikiPlugin } function inlineimages () { - return implode(' ', explode('|', $GLOBALS['InlineImages'])); + return implode(' ', explode('|', INLINE_IMAGES)); } function wikinameregexp () { return $GLOBALS['WikiNameRegexp']; } function allowedprotocols () { - return implode(' ', explode('|', $GLOBALS['AllowedProtocols'])); + return implode(' ', explode('|', ALLOWED_PROTOCOLS)); } function available_plugins () { $fileset = new FileSet(FindFile('lib/plugin'), '*.php'); @@ -519,6 +519,11 @@ function stddev(&$hits, $total = false) { } // $Log: not supported by cvs2svn $ +// Revision 1.13 2004/04/18 01:11:52 rurban +// more numeric pagename fixes. +// fixed action=upload with merge conflict warnings. +// charset changed from constant to global (dynamic utf-8 switching) +// // Revision 1.12 2004/03/14 16:26:21 rurban // copyright line // diff --git a/lib/prepend.php b/lib/prepend.php index 87179b4f4..21e3099aa 100644 --- a/lib/prepend.php +++ b/lib/prepend.php @@ -5,7 +5,21 @@ */ $RCS_IDS = ''; function rcs_id ($id) { $GLOBALS['RCS_IDS'] .= "$id\n"; } -rcs_id('$Id: prepend.php,v 1.16 2004-02-28 21:14:08 rurban Exp $'); +rcs_id('$Id: prepend.php,v 1.17 2004-04-19 23:13:03 zorloc Exp $'); + +define ('PHPWIKI_VERSION', '1.3.10pre'); + +// If your php was compiled with --enable-trans-sid it tries to +// add a PHPSESSID query argument to all URL strings when cookie +// support isn't detected in the client browser. For reasons +// which aren't entirely clear (PHP bug) this screws up the URLs +// generated by PhpWiki. Therefore, transparent session ids +// should be disabled. This next line does that. +// +// (At the present time, you will not be able to log-in to PhpWiki, +// unless your browser supports cookies.) +@ini_set('session.use_trans_sid', 0); + // Used for debugging purposes class DebugTimer { diff --git a/lib/stdlib.php b/lib/stdlib.php index abbc3b202..109075f37 100644 --- a/lib/stdlib.php +++ b/lib/stdlib.php @@ -1,1591 +1,1597 @@ -<?php //rcs_id('$Id: stdlib.php,v 1.170 2004-04-19 18:27:45 rurban Exp $'); - -/* - Standard functions for Wiki functionality - WikiURL($pagename, $args, $get_abs_url) - IconForLink($protocol_or_url) - LinkURL($url, $linktext) - LinkImage($url, $alt) - - SplitQueryArgs ($query_args) - LinkPhpwikiURL($url, $text) - ConvertOldMarkup($content) - - class Stack { push($item), pop(), cnt(), top() } - - split_pagename ($page) - NoSuchRevision ($request, $page, $version) - TimezoneOffset ($time, $no_colon) - Iso8601DateTime ($time) - Rfc2822DateTime ($time) - CTime ($time) - __printf ($fmt) - __sprintf ($fmt) - __vsprintf ($fmt, $args) - better_srand($seed = '') - count_all($arg) - isSubPage($pagename) - subPageSlice($pagename, $pos) - explodePageList($input, $perm = false) - - function: LinkInterWikiLink($link, $linktext) - moved to: lib/interwiki.php - function: linkExistingWikiWord($wikiword, $linktext, $version) - moved to: lib/Theme.php - function: LinkUnknownWikiWord($wikiword, $linktext) - moved to: lib/Theme.php - function: UpdateRecentChanges($dbi, $pagename, $isnewpage) - gone see: lib/plugin/RecentChanges.php -*/ - -define('MAX_PAGENAME_LENGTH', 100); - - -/** - * Convert string to a valid XML identifier. - * - * XML 1.0 identifiers are of the form: [A-Za-z][A-Za-z0-9:_.-]* - * - * We would like to have, e.g. named anchors within wiki pages - * names like "Table of Contents" --- clearly not a valid XML - * fragment identifier. - * - * This function implements a one-to-one map from {any string} - * to {valid XML identifiers}. - * - * It does this by - * converting all bytes not in [A-Za-z0-9:_-], - * and any leading byte not in [A-Za-z] to 'xbb.', - * where 'bb' is the hexadecimal representation of the - * character. - * - * As a special case, the empty string is converted to 'empty.' - * - * @param string $str - * @return string - */ -function MangleXmlIdentifier($str) { - if (!$str) - return 'empty.'; - - return preg_replace('/[^-_:A-Za-z0-9]|(?<=^)[^A-Za-z]/e', - "'x' . sprintf('%02x', ord('\\0')) . '.'", - $str); -} - -/** - * Generates a valid URL for a given Wiki pagename. - * @param mixed $pagename If a string this will be the name of the Wiki page to link to. - * If a WikiDB_Page object function will extract the name to link to. - * If a WikiDB_PageRevision object function will extract the name to link to. - * @param array $args - * @param boolean $get_abs_url Default value is false. - * @return string The absolute URL to the page passed as $pagename. - */ -function WikiURL($pagename, $args = '', $get_abs_url = false) { - $anchor = false; - - if (is_object($pagename)) { - if (isa($pagename, 'WikiDB_Page')) { - $pagename = $pagename->getName(); - } - elseif (isa($pagename, 'WikiDB_PageRevision')) { - $page = $pagename->getPage(); - $args['version'] = $pagename->getVersion(); - $pagename = $page->getName(); - } - elseif (isa($pagename, 'WikiPageName')) { - $anchor = $pagename->anchor; - $pagename = $pagename->name; - } else { // php5 - $anchor = $pagename->anchor; - $pagename = $pagename->name; - } - } - - if (is_array($args)) { - $enc_args = array(); - foreach ($args as $key => $val) { - if (!is_array($val)) // ugly hack for getURLtoSelf() which also takes POST vars - $enc_args[] = urlencode($key) . '=' . urlencode($val); - } - $args = join('&', $enc_args); - } - - if (USE_PATH_INFO) { - $url = $get_abs_url ? SERVER_URL . VIRTUAL_PATH . "/" : ""; - $url .= preg_replace('/%2f/i', '/', rawurlencode($pagename)); - if ($args) - $url .= "?$args"; - } - else { - $url = $get_abs_url ? SERVER_URL . SCRIPT_NAME : basename(SCRIPT_NAME); - $url .= "?pagename=" . rawurlencode($pagename); - if ($args) - $url .= "&$args"; - } - if ($anchor) - $url .= "#" . MangleXmlIdentifier($anchor); - return $url; -} - -/** Convert relative URL to absolute URL. - * - * This converts a relative URL to one of PhpWiki's support files - * to an absolute one. - * - * @param string $url - * @return string Absolute URL - */ -function AbsoluteURL ($url) { - if (preg_match('/^https?:/', $url)) - return $url; - if ($url[0] != '/') { - $base = USE_PATH_INFO ? VIRTUAL_PATH : dirname(SCRIPT_NAME); - while ($base != '/' and substr($url, 0, 3) == "../") { - $url = substr($url, 3); - $base = dirname($base); - } - if ($base != '/') - $base .= '/'; - $url = $base . $url; - } - return SERVER_URL . $url; -} - -/** - * Generates icon in front of links. - * - * @param string $protocol_or_url URL or protocol to determine which icon to use. - * - * @return HtmlElement HtmlElement object that contains data to create img link to - * icon for use with url or protocol passed to the function. False if no img to be - * displayed. - */ -function IconForLink($protocol_or_url) { - global $Theme; - if (0 and $filename_suffix == false) { - // display apache style icon for file type instead of protocol icon - // - archive: unix:gz,bz2,tgz,tar,z; mac:dmg,dmgz,bin,img,cpt,sit; pc:zip; - // - document: html, htm, text, txt, rtf, pdf, doc - // - non-inlined image: jpg,jpeg,png,gif,tiff,tif,swf,pict,psd,eps,ps - // - audio: mp3,mp2,aiff,aif,au - // - multimedia: mpeg,mpg,mov,qt - } else { - list ($proto) = explode(':', $protocol_or_url, 2); - $src = $Theme->getLinkIconURL($proto); - if ($src) - return HTML::img(array('src' => $src, 'alt' => "", 'class' => 'linkicon', 'border' => 0)); - else - return false; - } -} - -/** - * Glue icon in front of text. - * - * @param string $protocol_or_url Protocol or URL. Used to determine the - * proper icon. - * @param string $text The text. - * @return XmlContent. - */ -function PossiblyGlueIconToText($proto_or_url, $text) { - global $request; - if (! $request->getPref('noLinkIcons')) { - $icon = IconForLink($proto_or_url); - if ($icon) { - if (!is_object($text)) { - preg_match('/^\s*(\S*)(.*?)\s*$/', $text, $m); - list (, $first_word, $tail) = $m; - } - else { - $first_word = $text; - $tail = false; - } - - $text = HTML::span(array('style' => 'white-space: nowrap'), - $icon, $first_word); - if ($tail) - $text = HTML($text, $tail); - } - } - return $text; -} - -/** - * Determines if the url passed to function is safe, by detecting if the characters - * '<', '>', or '"' are present. - * - * @param string $url URL to check for unsafe characters. - * @return boolean True if same, false else. - */ -function IsSafeURL($url) { - return !ereg('[<>"]', $url); -} - -/** - * Generates an HtmlElement object to store data for a link. - * - * @param string $url URL that the link will point to. - * @param string $linktext Text to be displayed as link. - * @return HtmlElement HtmlElement object that contains data to construct an html link. - */ -function LinkURL($url, $linktext = '') { - // FIXME: Is this needed (or sufficient?) - if(! IsSafeURL($url)) { - $link = HTML::strong(HTML::u(array('class' => 'baduri'), - _("BAD URL -- remove all of <, >, \""))); - } - else { - if (!$linktext) - $linktext = preg_replace("/mailto:/A", "", $url); - - $link = HTML::a(array('href' => $url), - PossiblyGlueIconToText($url, $linktext)); - - } - $link->setAttr('class', $linktext ? 'namedurl' : 'rawurl'); - return $link; -} - - -function LinkImage($url, $alt = false) { - // FIXME: Is this needed (or sufficient?) - if(! IsSafeURL($url)) { - $link = HTML::strong(HTML::u(array('class' => 'baduri'), - _("BAD URL -- remove all of <, >, \""))); - } - else { - if (empty($alt)) - $alt = $url; - $link = HTML::img(array('src' => $url, 'alt' => $alt)); - } - $link->setAttr('class', 'inlineimage'); - return $link; -} - - - -class Stack { - var $items = array(); - var $size = 0; - // var in php5.0.0.rc1 deprecated - - function push($item) { - $this->items[$this->size] = $item; - $this->size++; - return true; - } - - function pop() { - if ($this->size == 0) { - return false; // stack is empty - } - $this->size--; - return $this->items[$this->size]; - } - - function cnt() { - return $this->size; - } - - function top() { - if($this->size) - return $this->items[$this->size - 1]; - else - return ''; - } - -} -// end class definition - -function SplitQueryArgs ($query_args = '') -{ - $split_args = split('&', $query_args); - $args = array(); - while (list($key, $val) = each($split_args)) - if (preg_match('/^ ([^=]+) =? (.*) /x', $val, $m)) - $args[$m[1]] = $m[2]; - return $args; -} - -function LinkPhpwikiURL($url, $text = '', $basepage) { - $args = array(); - - if (!preg_match('/^ phpwiki: ([^?]*) [?]? (.*) $/x', $url, $m)) { - return HTML::strong(array('class' => 'rawurl'), - HTML::u(array('class' => 'baduri'), - _("BAD phpwiki: URL"))); - } - - if ($m[1]) - $pagename = urldecode($m[1]); - $qargs = $m[2]; - - if (empty($pagename) && - preg_match('/^(diff|edit|links|info)=([^&]+)$/', $qargs, $m)) { - // Convert old style links (to not break diff links in - // RecentChanges). - $pagename = urldecode($m[2]); - $args = array("action" => $m[1]); - } - else { - $args = SplitQueryArgs($qargs); - } - - if (empty($pagename)) - $pagename = $GLOBALS['request']->getArg('pagename'); - - if (isset($args['action']) && $args['action'] == 'browse') - unset($args['action']); - - /*FIXME: - if (empty($args['action'])) - $class = 'wikilink'; - else if (is_safe_action($args['action'])) - $class = 'wikiaction'; - */ - if (empty($args['action']) || is_safe_action($args['action'])) - $class = 'wikiaction'; - else { - // Don't allow administrative links on unlocked pages. - $dbi = $GLOBALS['request']->getDbh(); - $page = $dbi->getPage($basepage); - if (!$page->get('locked')) - return HTML::span(array('class' => 'wikiunsafe'), - HTML::u(_("Lock page to enable link"))); - $class = 'wikiadmin'; - } - - if (!$text) - $text = HTML::span(array('class' => 'rawurl'), $url); - - $wikipage = new WikiPageName($pagename); - if (!$wikipage->isValid()) { - global $Theme; - return $Theme->linkBadWikiWord($wikipage, $url); - } - - return HTML::a(array('href' => WikiURL($pagename, $args), - 'class' => $class), - $text); -} - -/** - * A class to assist in parsing wiki pagenames. - * - * Now with subpages and anchors, parsing and passing around - * pagenames is more complicated. This should help. - */ -class WikiPageName -{ - /** Short name for page. - * - * This is the value of $name passed to the constructor. - * (For use, e.g. as a default label for links to the page.) - */ - var $shortName; - - /** The full page name. - * - * This is the full name of the page (without anchor). - */ - var $name; - - /** The anchor. - * - * This is the referenced anchor within the page, or the empty string. - */ - var $anchor; - - /** Constructor - * - * @param mixed $name Page name. - * WikiDB_Page, WikiDB_PageRevision, or string. - * This can be a relative subpage name (like '/SubPage'), - * or can be the empty string to refer to the $basename. - * - * @param string $anchor For links to anchors in page. - * - * @param mixed $basename Page name from which to interpret - * relative or other non-fully-specified page names. - */ - function WikiPageName($name, $basename=false, $anchor=false) { - if (is_string($name)) { - $this->shortName = $name; - - if ($name == '' or $name[0] == SUBPAGE_SEPARATOR) { - if ($basename) - $name = $this->_pagename($basename) . $name; - else - $name = $this->_normalize_bad_pagename($name); - } - } - else { - $name = $this->_pagename($name); - $this->shortName = $name; - } - - $this->name = $this->_check($name); - $this->anchor = (string)$anchor; - } - - function getParent() { - $name = $this->name; - if (!($tail = strrchr($name, SUBPAGE_SEPARATOR))) - return false; - return substr($name, 0, -strlen($tail)); - } - - function isValid($strict = false) { - if ($strict) - return !isset($this->_errors); - return !empty($this->name); - } - - function getWarnings() { - $warnings = array(); - if (isset($this->_warnings)) - $warnings = array_merge($warnings, $this->_warnings); - if (isset($this->_errors)) - $warnings = array_merge($warnings, $this->_errors); - if (!$warnings) - return false; - - return sprintf(_("'%s': Bad page name: %s"), - $this->shortName, join(', ', $warnings)); - } - - function _pagename($page) { - if (isa($page, 'WikiDB_Page')) - return $page->getName(); - elseif (isa($page, 'WikiDB_PageRevision')) - return $page->getPageName(); - elseif (isa($page, 'WikiPageName')) - return $page->name; - if (!is_string($page)) { - trigger_error(sprintf("Non-string pagename '%s' (%s)(%s)", - $page, gettype($page), get_class($page)), - E_USER_NOTICE); - } - //assert(is_string($page)); - return $page; - } - - function _normalize_bad_pagename($name) { - trigger_error("Bad pagename: " . $name, E_USER_WARNING); - - // Punt... You really shouldn't get here. - if (empty($name)) { - global $request; - return $request->getArg('pagename'); - } - assert($name[0] == SUBPAGE_SEPARATOR); - return substr($name, 1); - } - - - function _check($pagename) { - // Compress internal white-space to single space character. - $pagename = preg_replace('/[\s\xa0]+/', ' ', $orig = $pagename); - if ($pagename != $orig) - $this->_warnings[] = _("White space converted to single space"); - - // Delete any control characters. - $pagename = preg_replace('/[\x00-\x1f\x7f\x80-\x9f]/', '', $orig = $pagename); - if ($pagename != $orig) - $this->_errors[] = _("Control characters not allowed"); - - // Strip leading and trailing white-space. - $pagename = trim($pagename); - - $orig = $pagename; - while ($pagename and $pagename[0] == SUBPAGE_SEPARATOR) - $pagename = substr($pagename, 1); - if ($pagename != $orig) - $this->_errors[] = sprintf(_("Leading %s not allowed"), SUBPAGE_SEPARATOR); - - if (preg_match('/[:;]/', $pagename)) - $this->_warnings[] = _("';' and ':' in pagenames are deprecated"); - - if (strlen($pagename) > MAX_PAGENAME_LENGTH) { - $pagename = substr($pagename, 0, MAX_PAGENAME_LENGTH); - $this->_errors[] = _("too long"); - } - - - if ($pagename == '.' or $pagename == '..') { - $this->_errors[] = sprintf(_("illegal pagename"), $pagename); - $pagename = ''; - } - - return $pagename; - } -} - -/** - * Convert old page markup to new-style markup. - * - * @param string $text Old-style wiki markup. - * - * @param string $markup_type - * One of: <dl> - * <dt><code>"block"</code> <dd>Convert all markup. - * <dt><code>"inline"</code> <dd>Convert only inline markup. - * <dt><code>"links"</code> <dd>Convert only link markup. - * </dl> - * - * @return string New-style wiki markup. - * - * @bugs Footnotes don't work quite as before (esp if there are - * multiple references to the same footnote. But close enough, - * probably for now.... - */ -function ConvertOldMarkup ($text, $markup_type = "block") { - - static $subs; - static $block_re; - - if (empty($subs)) { - /***************************************************************** - * Conversions for inline markup: - */ - - // escape tilde's - $orig[] = '/~/'; - $repl[] = '~~'; - - // escape escaped brackets - $orig[] = '/\[\[/'; - $repl[] = '~['; - - // change ! escapes to ~'s. - global $AllowedProtocols, $WikiNameRegexp, $request; - //include_once('lib/interwiki.php'); - $map = getInterwikiMap(); - $bang_esc[] = "(?:$AllowedProtocols):[^\s<>\[\]\"'()]*[^\s<>\[\]\"'(),.?]"; - $bang_esc[] = $map->getRegexp() . ":[^\\s.,;?()]+"; // FIXME: is this really needed? - $bang_esc[] = $WikiNameRegexp; - $orig[] = '/!((?:' . join(')|(', $bang_esc) . '))/'; - $repl[] = '~\\1'; - - $subs["links"] = array($orig, $repl); - - // Escape '<'s - //$orig[] = '/<(?!\?plugin)|(?<!^)</m'; - //$repl[] = '~<'; - - // Convert footnote references. - $orig[] = '/(?<=.)(?<!~)\[\s*(\d+)\s*\]/m'; - $repl[] = '#[|ftnt_ref_\\1]<sup>~[[\\1|#ftnt_\\1]~]</sup>'; - - // Convert old style emphases to HTML style emphasis. - $orig[] = '/__(.*?)__/'; - $repl[] = '<strong>\\1</strong>'; - $orig[] = "/''(.*?)''/"; - $repl[] = '<em>\\1</em>'; - - // Escape nestled markup. - $orig[] = '/^(?<=^|\s)[=_](?=\S)|(?<=\S)[=_*](?=\s|$)/m'; - $repl[] = '~\\0'; - - // in old markup headings only allowed at beginning of line - $orig[] = '/!/'; - $repl[] = '~!'; - - $subs["inline"] = array($orig, $repl); - - /***************************************************************** - * Patterns which match block markup constructs which take - * special handling... - */ - - // Indented blocks - $blockpats[] = '[ \t]+\S(?:.*\s*\n[ \t]+\S)*'; - - // Tables - $blockpats[] = '\|(?:.*\n\|)*'; - - // List items - $blockpats[] = '[#*;]*(?:[*#]|;.*?:)'; - - // Footnote definitions - $blockpats[] = '\[\s*(\d+)\s*\]'; - - // Plugins - $blockpats[] = '<\?plugin(?:-form)?\b.*\?>\s*$'; - - // Section Title - $blockpats[] = '!{1,3}[^!]'; - - $block_re = ( '/\A((?:.|\n)*?)(^(?:' - . join("|", $blockpats) - . ').*$)\n?/m' ); - - } - - if ($markup_type != "block") { - list ($orig, $repl) = $subs[$markup_type]; - return preg_replace($orig, $repl, $text); - } - else { - list ($orig, $repl) = $subs['inline']; - $out = ''; - while (preg_match($block_re, $text, $m)) { - $text = substr($text, strlen($m[0])); - list (,$leading_text, $block) = $m; - $suffix = "\n"; - - if (strchr(" \t", $block[0])) { - // Indented block - $prefix = "<pre>\n"; - $suffix = "\n</pre>\n"; - } - elseif ($block[0] == '|') { - // Old-style table - $prefix = "<?plugin OldStyleTable\n"; - $suffix = "\n?>\n"; - } - elseif (strchr("#*;", $block[0])) { - // Old-style list item - preg_match('/^([#*;]*)([*#]|;.*?:) */', $block, $m); - list (,$ind,$bullet) = $m; - $block = substr($block, strlen($m[0])); - - $indent = str_repeat(' ', strlen($ind)); - if ($bullet[0] == ';') { - //$term = ltrim(substr($bullet, 1)); - //return $indent . $term . "\n" . $indent . ' '; - $prefix = $ind . $bullet; - } - else - $prefix = $indent . $bullet . ' '; - } - elseif ($block[0] == '[') { - // Footnote definition - preg_match('/^\[\s*(\d+)\s*\]/', $block, $m); - $footnum = $m[1]; - $block = substr($block, strlen($m[0])); - $prefix = "#[|ftnt_${footnum}]~[[${footnum}|#ftnt_ref_${footnum}]~] "; - } - elseif ($block[0] == '<') { - // Plugin. - // HACK: no inline markup... - $prefix = $block; - $block = ''; - } - elseif ($block[0] == '!') { - // Section heading - preg_match('/^!{1,3}/', $block, $m); - $prefix = $m[0]; - $block = substr($block, strlen($m[0])); - } - else { - // AAck! - assert(0); - } - - $out .= ( preg_replace($orig, $repl, $leading_text) - . $prefix - . preg_replace($orig, $repl, $block) - . $suffix ); - } - return $out . preg_replace($orig, $repl, $text); - } -} - - -/** - * Expand tabs in string. - * - * Converts all tabs to (the appropriate number of) spaces. - * - * @param string $str - * @param integer $tab_width - * @return string - */ -function expand_tabs($str, $tab_width = 8) { - $split = split("\t", $str); - $tail = array_pop($split); - $expanded = "\n"; - foreach ($split as $hunk) { - $expanded .= $hunk; - $pos = strlen(strrchr($expanded, "\n")) - 1; - $expanded .= str_repeat(" ", ($tab_width - $pos % $tab_width)); - } - return substr($expanded, 1) . $tail; -} - -/** - * Split WikiWords in page names. - * - * It has been deemed useful to split WikiWords (into "Wiki Words") in - * places like page titles. This is rumored to help search engines - * quite a bit. - * - * @param $page string The page name. - * - * @return string The split name. - */ -function split_pagename ($page) { - - if (preg_match("/\s/", $page)) - return $page; // Already split --- don't split any more. - - // FIXME: this algorithm is Anglo-centric. - static $RE; - if (!isset($RE)) { - // This mess splits between a lower-case letter followed by - // either an upper-case or a numeral; except that it wont - // split the prefixes 'Mc', 'De', or 'Di' off of their tails. - $RE[] = '/([[:lower:]])((?<!Mc|De|Di)[[:upper:]]|\d)/'; - // This the single-letter words 'I' and 'A' from any following - // capitalized words. - $sep = preg_quote(SUBPAGE_SEPARATOR, '/'); - $RE[] = "/(?<= |${sep}|^)([AI])([[:upper:]][[:lower:]])/"; - // Split numerals from following letters. - $RE[] = '/(\d)([[:alpha:]])/'; - //TODO: Split at subpage seperators. TBD in Theme.php - //$RE[] = "/(${sep})([^${sep}]+)/"; - - foreach ($RE as $key) - $RE[$key] = pcre_fix_posix_classes($key); - } - - foreach ($RE as $regexp) { - $page = preg_replace($regexp, '\\1 \\2', $page); - } - return $page; -} - -function NoSuchRevision (&$request, $page, $version) { - $html = HTML(HTML::h2(_("Revision Not Found")), - HTML::p(fmt("I'm sorry. Version %d of %s is not in the database.", - $version, WikiLink($page, 'auto')))); - include_once('lib/Template.php'); - GeneratePage($html, _("Bad Version"), $page->getCurrentRevision()); - $request->finish(); -} - - -/** - * Get time offset for local time zone. - * - * @param $time time_t Get offset for this time. Default: now. - * @param $no_colon boolean Don't put colon between hours and minutes. - * @return string Offset as a string in the format +HH:MM. - */ -function TimezoneOffset ($time = false, $no_colon = false) { - if ($time === false) - $time = time(); - $secs = date('Z', $time); - - if ($secs < 0) { - $sign = '-'; - $secs = -$secs; - } - else { - $sign = '+'; - } - $colon = $no_colon ? '' : ':'; - $mins = intval(($secs + 30) / 60); - return sprintf("%s%02d%s%02d", - $sign, $mins / 60, $colon, $mins % 60); -} - - -/** - * Format time in ISO-8601 format. - * - * @param $time time_t Time. Default: now. - * @return string Date and time in ISO-8601 format. - */ -function Iso8601DateTime ($time = false) { - if ($time === false) - $time = time(); - $tzoff = TimezoneOffset($time); - $date = date('Y-m-d', $time); - $time = date('H:i:s', $time); - return $date . 'T' . $time . $tzoff; -} - -/** - * Format time in RFC-2822 format. - * - * @param $time time_t Time. Default: now. - * @return string Date and time in RFC-2822 format. - */ -function Rfc2822DateTime ($time = false) { - if ($time === false) - $time = time(); - return date('D, j M Y H:i:s ', $time) . TimezoneOffset($time, 'no colon'); -} - -/** - * Format time in RFC-1123 format. - * - * @param $time time_t Time. Default: now. - * @return string Date and time in RFC-1123 format. - */ -function Rfc1123DateTime ($time = false) { - if ($time === false) - $time = time(); - return gmdate('D, d M Y H:i:s \G\M\T', $time); -} - -/** Parse date in RFC-1123 format. - * - * According to RFC 1123 we must accept dates in the following - * formats: - * - * Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 - * Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 - * Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format - * - * (Though we're only allowed to generate dates in the first format.) - */ -function ParseRfc1123DateTime ($timestr) { - $timestr = trim($timestr); - if (preg_match('/^ \w{3},\s* (\d{1,2}) \s* (\w{3}) \s* (\d{4}) \s*' - .'(\d\d):(\d\d):(\d\d) \s* GMT $/ix', - $timestr, $m)) { - list(, $mday, $mon, $year, $hh, $mm, $ss) = $m; - } - elseif (preg_match('/^ \w+,\s* (\d{1,2})-(\w{3})-(\d{2}|\d{4}) \s*' - .'(\d\d):(\d\d):(\d\d) \s* GMT $/ix', - $timestr, $m)) { - list(, $mday, $mon, $year, $hh, $mm, $ss) = $m; - if ($year < 70) $year += 2000; - elseif ($year < 100) $year += 1900; - } - elseif (preg_match('/^\w+\s* (\w{3}) \s* (\d{1,2}) \s*' - .'(\d\d):(\d\d):(\d\d) \s* (\d{4})$/ix', - $timestr, $m)) { - list(, $mon, $mday, $hh, $mm, $ss, $year) = $m; - } - else { - // Parse failed. - return false; - } - - $time = strtotime("$mday $mon $year ${hh}:${mm}:${ss} GMT"); - if ($time == -1) - return false; // failed - return $time; -} - -/** - * Format time to standard 'ctime' format. - * - * @param $time time_t Time. Default: now. - * @return string Date and time. - */ -function CTime ($time = false) -{ - if ($time === false) - $time = time(); - return date("D M j H:i:s Y", $time); -} - - -/** - * Format number as kilobytes or bytes. - * Short format is used for PageList - * Long format is used in PageInfo - * - * @param $bytes int. Default: 0. - * @param $longformat bool. Default: false. - * @return class FormattedText (XmlElement.php). - */ -function ByteFormatter ($bytes = 0, $longformat = false) { - if ($bytes < 0) - return fmt("-???"); - if ($bytes < 1024) { - if (! $longformat) - $size = fmt("%s b", $bytes); - else - $size = fmt("%s bytes", $bytes); - } - else { - $kb = round($bytes / 1024, 1); - if (! $longformat) - $size = fmt("%s k", $kb); - else - $size = fmt("%s Kb (%s bytes)", $kb, $bytes); - } - return $size; -} - -/** - * Internationalized printf. - * - * This is essentially the same as PHP's built-in printf - * with the following exceptions: - * <ol> - * <li> It passes the format string through gettext(). - * <li> It supports the argument reordering extensions. - * </ol> - * - * Example: - * - * In php code, use: - * <pre> - * __printf("Differences between versions %s and %s of %s", - * $new_link, $old_link, $page_link); - * </pre> - * - * Then in locale/po/de.po, one can reorder the printf arguments: - * - * <pre> - * msgid "Differences between %s and %s of %s." - * msgstr "Der Unterschiedsergebnis von %3$s, zwischen %1$s und %2$s." - * </pre> - * - * (Note that while PHP tries to expand $vars within double-quotes, - * the values in msgstr undergo no such expansion, so the '$'s - * okay...) - * - * One shouldn't use reordered arguments in the default format string. - * Backslashes in the default string would be necessary to escape the - * '$'s, and they'll cause all kinds of trouble.... - */ -function __printf ($fmt) { - $args = func_get_args(); - array_shift($args); - echo __vsprintf($fmt, $args); -} - -/** - * Internationalized sprintf. - * - * This is essentially the same as PHP's built-in printf with the - * following exceptions: - * - * <ol> - * <li> It passes the format string through gettext(). - * <li> It supports the argument reordering extensions. - * </ol> - * - * @see __printf - */ -function __sprintf ($fmt) { - $args = func_get_args(); - array_shift($args); - return __vsprintf($fmt, $args); -} - -/** - * Internationalized vsprintf. - * - * This is essentially the same as PHP's built-in printf with the - * following exceptions: - * - * <ol> - * <li> It passes the format string through gettext(). - * <li> It supports the argument reordering extensions. - * </ol> - * - * @see __printf - */ -function __vsprintf ($fmt, $args) { - $fmt = gettext($fmt); - // PHP's sprintf doesn't support variable with specifiers, - // like sprintf("%*s", 10, "x"); --- so we won't either. - - if (preg_match_all('/(?<!%)%(\d+)\$/x', $fmt, $m)) { - // Format string has '%2$s' style argument reordering. - // PHP doesn't support this. - if (preg_match('/(?<!%)%[- ]?\d*[^- \d$]/x', $fmt)) - // literal variable name substitution only to keep locale - // strings uncluttered - trigger_error(sprintf(_("Can't mix '%s' with '%s' type format strings"), - '%1\$s','%s'), E_USER_WARNING); //php+locale error - - $fmt = preg_replace('/(?<!%)%\d+\$/x', '%', $fmt); - $newargs = array(); - - // Reorder arguments appropriately. - foreach($m[1] as $argnum) { - if ($argnum < 1 || $argnum > count($args)) - trigger_error(sprintf(_("%s: argument index out of range"), - $argnum), E_USER_WARNING); - $newargs[] = $args[$argnum - 1]; - } - $args = $newargs; - } - - // Not all PHP's have vsprintf, so... - array_unshift($args, $fmt); - return call_user_func_array('sprintf', $args); -} - -function file_mtime ($filename) { - if ($stat = stat($filename)) - return $stat[9]; - else - return false; -} - -function sort_file_mtime ($a, $b) { - $ma = file_mtime($a); - $mb = file_mtime($b); - if (!$ma or !$mb or $ma == $mb) return 0; - return ($ma > $mb) ? -1 : 1; -} - -class fileSet { - /** - * Build an array in $this->_fileList of files from $dirname. - * Subdirectories are not traversed. - * - * (This was a function LoadDir in lib/loadsave.php) - * See also http://www.php.net/manual/en/function.readdir.php - */ - function getFiles($exclude=false,$sortby=false,$limit=false) { - $list = $this->_fileList; - if ($sortby) { - switch (Pagelist::sortby($sortby,'db')) { - case 'pagename ASC': break; - case 'pagename DESC': - $list = array_reverse($list); - break; - case 'mtime ASC': - usort($list,'sort_file_mtime'); - break; - case 'mtime DESC': - usort($list,'sort_file_mtime'); - $list = array_reverse($list); - break; - } - } - if ($limit) - return array_splice($list,0,$limit); - return $list; - } - - function _filenameSelector($filename) { - if (! $this->_pattern) - return true; - else { - return glob_match ($this->_pattern, $filename, $this->_case); - } - } - - function fileSet($directory, $filepattern = false) { - $this->_fileList = array(); - $this->_pattern = $filepattern; - $this->_case = !isWindows(); - $this->_pathsep = '/'; - - if (empty($directory)) { - trigger_error(sprintf(_("%s is empty."), 'directoryname'), - E_USER_NOTICE); - return; // early return - } - - @ $dir_handle = opendir($dir=$directory); - if (empty($dir_handle)) { - trigger_error(sprintf(_("Unable to open directory '%s' for reading"), - $dir), E_USER_NOTICE); - return; // early return - } - - while ($filename = readdir($dir_handle)) { - if ($filename[0] == '.' || filetype($dir . $this->_pathsep . $filename) != 'file') - continue; - if ($this->_filenameSelector($filename)) { - array_push($this->_fileList, "$filename"); - //trigger_error(sprintf(_("found file %s"), $filename), - // E_USER_NOTICE); //debugging - } - } - closedir($dir_handle); - } -}; - -// File globbing - -// expands a list containing regex's to its matching entries -class ListRegexExpand { - var $match, $list, $index, $case_sensitive; - function ListRegexExpand (&$list, $match, $case_sensitive = true) { - $this->match = str_replace('/','\/',$match); - $this->list = &$list; - $this->case_sensitive = $case_sensitive; - //$this->index = false; - } - function listMatchCallback ($item, $key) { - if (preg_match('/' . $this->match . ($this->case_sensitive ? '/' : '/i'), $item)) { - unset($this->list[$this->index]); - $this->list[] = $item; - } - } - function expandRegex ($index, &$pages) { - $this->index = $index; - array_walk($pages, array($this, 'listMatchCallback')); - return $this->list; - } -} - -// convert fileglob to regex style -function glob_to_pcre ($glob) { - $re = preg_replace('/\./', '\\.', $glob); - $re = preg_replace(array('/\*/','/\?/'), array('.*','.'), $glob); - if (!preg_match('/^[\?\*]/',$glob)) - $re = '^' . $re; - if (!preg_match('/[\?\*]$/',$glob)) - $re = $re . '$'; - return $re; -} - -function glob_match ($glob, $against, $case_sensitive = true) { - return preg_match('/' . glob_to_pcre($glob) . ($case_sensitive ? '/' : '/i'), $against); -} - -function explodeList($input, $allnames, $glob_style = true, $case_sensitive = true) { - $list = explode(',',$input); - // expand wildcards from list of $allnames - if (preg_match('/[\?\*]/',$input)) { - // Optimizing loop invariants: - // http://phplens.com/lens/php-book/optimizing-debugging-php.php - for ($i = 0, $max = sizeof($list); $i < $max; $i++) { - $f = $list[$i]; - if (preg_match('/[\?\*]/',$f)) { - reset($allnames); - $expand = new ListRegexExpand($list, $glob_style ? glob_to_pcre($f) : $f, $case_sensitive); - $expand->expandRegex($i, $allnames); - } - } - } - return $list; -} - -// echo implode(":",explodeList("Test*",array("xx","Test1","Test2"))); -function explodePageList($input, $perm=false, $sortby='pagename', $limit=false) { - include_once("lib/PageList.php"); - return PageList::explodePageList($input,$perm,$sortby,$limit); -} - -// Class introspections - -/** Determine whether object is of a specified type. - * - * @param $object object An object. - * @param $class string Class name. - * @return bool True iff $object is a $class - * or a sub-type of $class. - */ -function isa ($object, $class) { - $lclass = strtolower($class); - - return is_object($object) - && ( get_class($object) == strtolower($lclass) - || is_subclass_of($object, $lclass) ); -} - -/** Determine whether (possible) object has method. - * - * @param $object mixed Object - * @param $method string Method name - * @return bool True iff $object is an object with has method $method. - */ -function can ($object, $method) { - return is_object($object) && method_exists($object, strtolower($method)); -} - -/** Determine whether a function is okay to use. - * - * Some providers (e.g. Lycos) disable some of PHP functions for - * "security reasons." This makes those functions, of course, - * unusable, despite the fact the function_exists() says they - * exist. - * - * This function test to see if a function exists and is not - * disallowed by PHP's disable_functions config setting. - * - * @param string $function_name Function name - * @return bool True iff function can be used. - */ -function function_usable($function_name) { - static $disabled; - if (!is_array($disabled)) { - $disabled = array(); - // Use get_cfg_var since ini_get() is one of the disabled functions - // (on Lycos, at least.) - $split = preg_split('/\s*,\s*/', trim(get_cfg_var('disable_functions'))); - foreach ($split as $f) - $disabled[strtolower($f)] = true; - } - - return ( function_exists($function_name) - and ! isset($disabled[strtolower($function_name)]) - ); -} - - -/** Hash a value. - * - * This is used for generating ETags. - */ -function hash ($x) { - if (is_scalar($x)) { - return $x; - } - elseif (is_array($x)) { - ksort($x); - return md5(serialize($x)); - } - elseif (is_object($x)) { - return $x->hash(); - } - trigger_error("Can't hash $x", E_USER_ERROR); -} - - -/** - * Seed the random number generator. - * - * better_srand() ensures the randomizer is seeded only once. - * - * How random do you want it? See: - * http://www.php.net/manual/en/function.srand.php - * http://www.php.net/manual/en/function.mt-srand.php - */ -function better_srand($seed = '') { - static $wascalled = FALSE; - if (!$wascalled) { - $seed = $seed === '' ? (double) microtime() * 1000000 : $seed; - srand($seed); - $wascalled = TRUE; - //trigger_error("new random seed", E_USER_NOTICE); //debugging - } -} - -/** - * Recursively count all non-empty elements - * in array of any dimension or mixed - i.e. - * array('1' => 2, '2' => array('1' => 3, '2' => 4)) - * See http://www.php.net/manual/en/function.count.php - */ -function count_all($arg) { - // skip if argument is empty - if ($arg) { - //print_r($arg); //debugging - $count = 0; - // not an array, return 1 (base case) - if(!is_array($arg)) - return 1; - // else call recursively for all elements $arg - foreach($arg as $key => $val) - $count += count_all($val); - return $count; - } -} - -function isSubPage($pagename) { - return (strstr($pagename, SUBPAGE_SEPARATOR)); -} - -function subPageSlice($pagename, $pos) { - $pages = explode(SUBPAGE_SEPARATOR,$pagename); - $pages = array_slice($pages,$pos,1); - return $pages[0]; -} - -/** - * Alert - * - * Class for "popping up" and alert box. (Except that right now, it doesn't - * pop up...) - * - * FIXME: - * This is a hackish and needs to be refactored. However it would be nice to - * unify all the different methods we use for showing Alerts and Dialogs. - * (E.g. "Page deleted", login form, ...) - */ -class Alert { - /** Constructor - * - * @param object $request - * @param mixed $head Header ("title") for alert box. - * @param mixed $body The text in the alert box. - * @param hash $buttons An array mapping button labels to URLs. - * The default is a single "Okay" button pointing to $request->getURLtoSelf(). - */ - function Alert($head, $body, $buttons=false) { - if ($buttons === false) - $buttons = array(); - - $this->_tokens = array('HEADER' => $head, 'CONTENT' => $body); - $this->_buttons = $buttons; - } - - /** - * Show the alert box. - */ - function show(&$request) { - global $request; - - $tokens = $this->_tokens; - $tokens['BUTTONS'] = $this->_getButtons(); - - $request->discardOutput(); - $tmpl = new Template('dialog', $request, $tokens); - $tmpl->printXML(); - $request->finish(); - } - - - function _getButtons() { - global $request; - - $buttons = $this->_buttons; - if (!$buttons) - $buttons = array(_("Okay") => $request->getURLtoSelf()); - - global $Theme; - foreach ($buttons as $label => $url) - print "$label $url\n"; - $out[] = $Theme->makeButton($label, $url, 'wikiaction'); - return new XmlContent($out); - } -} - -/** - * Returns true if current php version is at mimimum a.b.c - * Called: check_php_version(4,1) - */ -function check_php_version ($a = '0', $b = '0', $c = '0') { - global $PHP_VERSION; - if(!isset($PHP_VERSION)) - $PHP_VERSION = substr( str_pad( preg_replace('/\D/','', PHP_VERSION), 3, '0'), 0, 3); - return $PHP_VERSION >= ($a.$b.$c); -} - -function isWikiWord($word) { - global $WikiNameRegexp; - //or preg_match('/\A' . $WikiNameRegexp . '\z/', $word) ?? - return preg_match("/^$WikiNameRegexp\$/",$word); -} - -// needed to store serialized objects-values only (perm, pref) -function obj2hash ($obj, $exclude = false, $fields = false) { - $a = array(); - if (! $fields ) $fields = get_object_vars($obj); - foreach ($fields as $key => $val) { - if (is_array($exclude)) { - if (in_array($key,$exclude)) continue; - } - $a[$key] = $val; - } - return $a; -} - -// $Log: not supported by cvs2svn $ -// Revision 1.169 2004/04/15 21:29:48 rurban -// allow [0] with new markup: link to page "0" -// -// Revision 1.168 2004/04/10 02:30:49 rurban -// Fixed gettext problem with VIRTUAL_PATH scripts (Windows only probably) -// Fixed "cannot setlocale..." (sf.net problem) -// -// Revision 1.167 2004/04/02 15:06:55 rurban -// fixed a nasty ADODB_mysql session update bug -// improved UserPreferences layout (tabled hints) -// fixed UserPreferences auth handling -// improved auth stability -// improved old cookie handling: fixed deletion of old cookies with paths -// -// Revision 1.166 2004/04/01 15:57:10 rurban -// simplified Sidebar theme: table, not absolute css positioning -// added the new box methods. -// remaining problems: large left margin, how to override _autosplitWikiWords in Template only -// -// Revision 1.165 2004/03/24 19:39:03 rurban -// php5 workaround code (plus some interim debugging code in XmlElement) -// php5 doesn't work yet with the current XmlElement class constructors, -// WikiUserNew does work better than php4. -// rewrote WikiUserNew user upgrading to ease php5 update -// fixed pref handling in WikiUserNew -// added Email Notification -// added simple Email verification -// removed emailVerify userpref subclass: just a email property -// changed pref binary storage layout: numarray => hash of non default values -// print optimize message only if really done. -// forced new cookie policy: delete pref cookies, use only WIKI_ID as plain string. -// prefs should be stored in db or homepage, besides the current session. -// -// Revision 1.164 2004/03/18 21:41:09 rurban -// fixed sqlite support -// WikiUserNew: PHP5 fixes: don't assign $this (untested) -// -// Revision 1.163 2004/03/17 18:41:49 rurban -// just reformatting -// -// Revision 1.162 2004/03/16 15:43:08 rurban -// make fileSet sortable to please PageList -// -// Revision 1.161 2004/03/12 15:48:07 rurban -// fixed explodePageList: wrong sortby argument order in UnfoldSubpages -// simplified lib/stdlib.php:explodePageList -// -// Revision 1.160 2004/02/28 21:14:08 rurban -// generally more PHPDOC docs -// see http://xarch.tu-graz.ac.at/home/rurban/phpwiki/xref/ -// fxied WikiUserNew pref handling: empty theme not stored, save only -// changed prefs, sql prefs improved, fixed password update, -// removed REPLACE sql (dangerous) -// moved gettext init after the locale was guessed -// + some minor changes -// -// Revision 1.158 2004/02/19 21:54:17 rurban -// moved initerwiki code to PageType.php -// re-enabled and fixed InlineImages support, now also for InterWiki Urls -// * [File:my_image.gif] inlines the image, -// * File:my_image.gif shows a plain inter-wiki link, -// * [what a pic|File:my_image.gif] shows a named inter-wiki link to the gif -// * [File:my_image.gif|what a pic] shows a inlimed image linked to the page "what a pic" -// -// Revision 1.157 2004/02/09 03:58:12 rurban -// for now default DB_SESSION to false -// PagePerm: -// * not existing perms will now query the parent, and not -// return the default perm -// * added pagePermissions func which returns the object per page -// * added getAccessDescription -// WikiUserNew: -// * added global ->prepare (not yet used) with smart user/pref/member table prefixing. -// * force init of authdbh in the 2 db classes -// main: -// * fixed session handling (not triple auth request anymore) -// * don't store cookie prefs with sessions -// stdlib: global obj2hash helper from _AuthInfo, also needed for PagePerm -// -// Revision 1.156 2004/01/26 09:17:49 rurban -// * changed stored pref representation as before. -// the array of objects is 1) bigger and 2) -// less portable. If we would import packed pref -// objects and the object definition was changed, PHP would fail. -// This doesn't happen with an simple array of non-default values. -// * use $prefs->retrieve and $prefs->store methods, where retrieve -// understands the interim format of array of objects also. -// * simplified $prefs->get() and fixed $prefs->set() -// * added $user->_userid and class '_WikiUser' portability functions -// * fixed $user object ->_level upgrading, mostly using sessions. -// this fixes yesterdays problems with loosing authorization level. -// * fixed WikiUserNew::checkPass to return the _level -// * fixed WikiUserNew::isSignedIn -// * added explodePageList to class PageList, support sortby arg -// * fixed UserPreferences for WikiUserNew -// * fixed WikiPlugin for empty defaults array -// * UnfoldSubpages: added pagename arg, renamed pages arg, -// removed sort arg, support sortby arg -// -// Revision 1.155 2004/01/25 10:52:22 rurban -// added sortby support to explodePageList() and UnfoldSubpages -// fixes [ 758044 ] Plugin UnfoldSubpages does not sort (includes fix) -// -// Revision 1.154 2004/01/25 03:49:03 rurban -// added isWikiWord() to avoid redundancy -// added check_php_version() to check for older php versions. -// e.g. object::method calls, ... -// -// Revision 1.153 2003/11/30 18:43:18 carstenklapp -// Fixed careless mistakes in my last optimization commit. -// -// Revision 1.152 2003/11/30 18:20:34 carstenklapp -// Minor code optimization: reduce invariant loops -// -// Revision 1.151 2003/11/29 19:30:01 carstenklapp -// New function ByteFormatter. -// -// Revision 1.150 2003/09/13 22:43:00 carstenklapp -// New preference to hide LinkIcons. -// -// Revision 1.149 2003/03/26 19:37:08 dairiki -// Fix "object to string conversion" bug with external image links. -// -// Revision 1.148 2003/03/25 21:03:02 dairiki -// Cleanup debugging output. -// -// Revision 1.147 2003/03/13 20:17:05 dairiki -// Bug fix: Fix linking of pages whose names contain a hash ('#'). -// -// Revision 1.146 2003/03/07 02:46:24 dairiki -// function_usable(): New function. -// -// Revision 1.145 2003/03/04 01:55:05 dairiki -// Fix to ensure absolute URL for logo in RSS recent changes. -// -// Revision 1.144 2003/02/26 00:39:30 dairiki -// Bug fix: for magic PhpWiki URLs, "lock page to enable link" message was -// being displayed at incorrect times. -// -// Revision 1.143 2003/02/26 00:10:26 dairiki -// More/better/different checks for bad page names. -// -// Revision 1.142 2003/02/25 22:19:46 dairiki -// Add some sanity checking for pagenames. -// -// Revision 1.141 2003/02/22 20:49:55 dairiki -// Fixes for "Call-time pass by reference has been deprecated" errors. -// -// Revision 1.140 2003/02/21 23:33:29 dairiki -// Set alt="" on the link icon image tags. -// (See SF bug #675141.) -// -// Revision 1.139 2003/02/21 22:16:27 dairiki -// Get rid of MakeWikiForm, and form-style MagicPhpWikiURLs. -// These have been obsolete for quite awhile (I hope). -// -// Revision 1.138 2003/02/21 04:12:36 dairiki -// WikiPageName: fixes for new cached links. -// -// Alert: new class for displaying alerts. -// -// ExtractWikiPageLinks and friends are now gone. -// -// LinkBracketLink moved to InlineParser.php -// -// Revision 1.137 2003/02/18 23:13:40 dairiki -// Wups again. Typo fix. -// -// Revision 1.136 2003/02/18 21:52:07 dairiki -// Fix so that one can still link to wiki pages with # in their names. -// (This was made difficult by the introduction of named tags, since -// '[Page #1]' is now a link to anchor '1' in page 'Page'. -// -// Now the ~ escape for page names should work: [Page ~#1]. -// -// Revision 1.135 2003/02/18 19:17:04 dairiki -// split_pagename(): -// Bug fix. 'ThisIsABug' was being split to 'This IsA Bug'. -// Cleanup up subpage splitting code. -// -// Revision 1.134 2003/02/16 19:44:20 dairiki -// New function hash(). This is a helper, primarily for generating -// HTTP ETags. -// -// Revision 1.133 2003/02/16 04:50:09 dairiki -// New functions: -// Rfc1123DateTime(), ParseRfc1123DateTime() -// for converting unix timestamps to and from strings. -// -// These functions produce and grok the time strings -// in the format specified by RFC 2616 for use in HTTP headers -// (like Last-Modified). -// -// Revision 1.132 2003/01/04 22:19:43 carstenklapp -// Bugfix UnfoldSubpages: "Undefined offset: 1" error when plugin invoked -// on a page with no subpages (explodeList(): array 0-based, sizeof 1-based). -// - -// (c-file-style: "gnu") -// Local Variables: -// mode: php -// tab-width: 8 -// c-basic-offset: 4 -// c-hanging-comment-ender-p: nil -// indent-tabs-mode: nil -// End: -?> +<?php //rcs_id('$Id: stdlib.php,v 1.171 2004-04-19 23:13:03 zorloc Exp $'); + +/* + Standard functions for Wiki functionality + WikiURL($pagename, $args, $get_abs_url) + IconForLink($protocol_or_url) + LinkURL($url, $linktext) + LinkImage($url, $alt) + + SplitQueryArgs ($query_args) + LinkPhpwikiURL($url, $text) + ConvertOldMarkup($content) + + class Stack { push($item), pop(), cnt(), top() } + + split_pagename ($page) + NoSuchRevision ($request, $page, $version) + TimezoneOffset ($time, $no_colon) + Iso8601DateTime ($time) + Rfc2822DateTime ($time) + CTime ($time) + __printf ($fmt) + __sprintf ($fmt) + __vsprintf ($fmt, $args) + better_srand($seed = '') + count_all($arg) + isSubPage($pagename) + subPageSlice($pagename, $pos) + explodePageList($input, $perm = false) + + function: LinkInterWikiLink($link, $linktext) + moved to: lib/interwiki.php + function: linkExistingWikiWord($wikiword, $linktext, $version) + moved to: lib/Theme.php + function: LinkUnknownWikiWord($wikiword, $linktext) + moved to: lib/Theme.php + function: UpdateRecentChanges($dbi, $pagename, $isnewpage) + gone see: lib/plugin/RecentChanges.php +*/ + +define('MAX_PAGENAME_LENGTH', 100); + + +/** + * Convert string to a valid XML identifier. + * + * XML 1.0 identifiers are of the form: [A-Za-z][A-Za-z0-9:_.-]* + * + * We would like to have, e.g. named anchors within wiki pages + * names like "Table of Contents" --- clearly not a valid XML + * fragment identifier. + * + * This function implements a one-to-one map from {any string} + * to {valid XML identifiers}. + * + * It does this by + * converting all bytes not in [A-Za-z0-9:_-], + * and any leading byte not in [A-Za-z] to 'xbb.', + * where 'bb' is the hexadecimal representation of the + * character. + * + * As a special case, the empty string is converted to 'empty.' + * + * @param string $str + * @return string + */ +function MangleXmlIdentifier($str) { + if (!$str) + return 'empty.'; + + return preg_replace('/[^-_:A-Za-z0-9]|(?<=^)[^A-Za-z]/e', + "'x' . sprintf('%02x', ord('\\0')) . '.'", + $str); +} + +/** + * Generates a valid URL for a given Wiki pagename. + * @param mixed $pagename If a string this will be the name of the Wiki page to link to. + * If a WikiDB_Page object function will extract the name to link to. + * If a WikiDB_PageRevision object function will extract the name to link to. + * @param array $args + * @param boolean $get_abs_url Default value is false. + * @return string The absolute URL to the page passed as $pagename. + */ +function WikiURL($pagename, $args = '', $get_abs_url = false) { + $anchor = false; + + if (is_object($pagename)) { + if (isa($pagename, 'WikiDB_Page')) { + $pagename = $pagename->getName(); + } + elseif (isa($pagename, 'WikiDB_PageRevision')) { + $page = $pagename->getPage(); + $args['version'] = $pagename->getVersion(); + $pagename = $page->getName(); + } + elseif (isa($pagename, 'WikiPageName')) { + $anchor = $pagename->anchor; + $pagename = $pagename->name; + } else { // php5 + $anchor = $pagename->anchor; + $pagename = $pagename->name; + } + } + + if (is_array($args)) { + $enc_args = array(); + foreach ($args as $key => $val) { + if (!is_array($val)) // ugly hack for getURLtoSelf() which also takes POST vars + $enc_args[] = urlencode($key) . '=' . urlencode($val); + } + $args = join('&', $enc_args); + } + + if (USE_PATH_INFO) { + $url = $get_abs_url ? SERVER_URL . VIRTUAL_PATH . "/" : ""; + $url .= preg_replace('/%2f/i', '/', rawurlencode($pagename)); + if ($args) + $url .= "?$args"; + } + else { + $url = $get_abs_url ? SERVER_URL . SCRIPT_NAME : basename(SCRIPT_NAME); + $url .= "?pagename=" . rawurlencode($pagename); + if ($args) + $url .= "&$args"; + } + if ($anchor) + $url .= "#" . MangleXmlIdentifier($anchor); + return $url; +} + +/** Convert relative URL to absolute URL. + * + * This converts a relative URL to one of PhpWiki's support files + * to an absolute one. + * + * @param string $url + * @return string Absolute URL + */ +function AbsoluteURL ($url) { + if (preg_match('/^https?:/', $url)) + return $url; + if ($url[0] != '/') { + $base = USE_PATH_INFO ? VIRTUAL_PATH : dirname(SCRIPT_NAME); + while ($base != '/' and substr($url, 0, 3) == "../") { + $url = substr($url, 3); + $base = dirname($base); + } + if ($base != '/') + $base .= '/'; + $url = $base . $url; + } + return SERVER_URL . $url; +} + +/** + * Generates icon in front of links. + * + * @param string $protocol_or_url URL or protocol to determine which icon to use. + * + * @return HtmlElement HtmlElement object that contains data to create img link to + * icon for use with url or protocol passed to the function. False if no img to be + * displayed. + */ +function IconForLink($protocol_or_url) { + global $Theme; + if (0 and $filename_suffix == false) { + // display apache style icon for file type instead of protocol icon + // - archive: unix:gz,bz2,tgz,tar,z; mac:dmg,dmgz,bin,img,cpt,sit; pc:zip; + // - document: html, htm, text, txt, rtf, pdf, doc + // - non-inlined image: jpg,jpeg,png,gif,tiff,tif,swf,pict,psd,eps,ps + // - audio: mp3,mp2,aiff,aif,au + // - multimedia: mpeg,mpg,mov,qt + } else { + list ($proto) = explode(':', $protocol_or_url, 2); + $src = $Theme->getLinkIconURL($proto); + if ($src) + return HTML::img(array('src' => $src, 'alt' => "", 'class' => 'linkicon', 'border' => 0)); + else + return false; + } +} + +/** + * Glue icon in front of text. + * + * @param string $protocol_or_url Protocol or URL. Used to determine the + * proper icon. + * @param string $text The text. + * @return XmlContent. + */ +function PossiblyGlueIconToText($proto_or_url, $text) { + global $request; + if (! $request->getPref('noLinkIcons')) { + $icon = IconForLink($proto_or_url); + if ($icon) { + if (!is_object($text)) { + preg_match('/^\s*(\S*)(.*?)\s*$/', $text, $m); + list (, $first_word, $tail) = $m; + } + else { + $first_word = $text; + $tail = false; + } + + $text = HTML::span(array('style' => 'white-space: nowrap'), + $icon, $first_word); + if ($tail) + $text = HTML($text, $tail); + } + } + return $text; +} + +/** + * Determines if the url passed to function is safe, by detecting if the characters + * '<', '>', or '"' are present. + * + * @param string $url URL to check for unsafe characters. + * @return boolean True if same, false else. + */ +function IsSafeURL($url) { + return !ereg('[<>"]', $url); +} + +/** + * Generates an HtmlElement object to store data for a link. + * + * @param string $url URL that the link will point to. + * @param string $linktext Text to be displayed as link. + * @return HtmlElement HtmlElement object that contains data to construct an html link. + */ +function LinkURL($url, $linktext = '') { + // FIXME: Is this needed (or sufficient?) + if(! IsSafeURL($url)) { + $link = HTML::strong(HTML::u(array('class' => 'baduri'), + _("BAD URL -- remove all of <, >, \""))); + } + else { + if (!$linktext) + $linktext = preg_replace("/mailto:/A", "", $url); + + $link = HTML::a(array('href' => $url), + PossiblyGlueIconToText($url, $linktext)); + + } + $link->setAttr('class', $linktext ? 'namedurl' : 'rawurl'); + return $link; +} + + +function LinkImage($url, $alt = false) { + // FIXME: Is this needed (or sufficient?) + if(! IsSafeURL($url)) { + $link = HTML::strong(HTML::u(array('class' => 'baduri'), + _("BAD URL -- remove all of <, >, \""))); + } + else { + if (empty($alt)) + $alt = $url; + $link = HTML::img(array('src' => $url, 'alt' => $alt)); + } + $link->setAttr('class', 'inlineimage'); + return $link; +} + + + +class Stack { + var $items = array(); + var $size = 0; + // var in php5.0.0.rc1 deprecated + + function push($item) { + $this->items[$this->size] = $item; + $this->size++; + return true; + } + + function pop() { + if ($this->size == 0) { + return false; // stack is empty + } + $this->size--; + return $this->items[$this->size]; + } + + function cnt() { + return $this->size; + } + + function top() { + if($this->size) + return $this->items[$this->size - 1]; + else + return ''; + } + +} +// end class definition + +function SplitQueryArgs ($query_args = '') +{ + $split_args = split('&', $query_args); + $args = array(); + while (list($key, $val) = each($split_args)) + if (preg_match('/^ ([^=]+) =? (.*) /x', $val, $m)) + $args[$m[1]] = $m[2]; + return $args; +} + +function LinkPhpwikiURL($url, $text = '', $basepage) { + $args = array(); + + if (!preg_match('/^ phpwiki: ([^?]*) [?]? (.*) $/x', $url, $m)) { + return HTML::strong(array('class' => 'rawurl'), + HTML::u(array('class' => 'baduri'), + _("BAD phpwiki: URL"))); + } + + if ($m[1]) + $pagename = urldecode($m[1]); + $qargs = $m[2]; + + if (empty($pagename) && + preg_match('/^(diff|edit|links|info)=([^&]+)$/', $qargs, $m)) { + // Convert old style links (to not break diff links in + // RecentChanges). + $pagename = urldecode($m[2]); + $args = array("action" => $m[1]); + } + else { + $args = SplitQueryArgs($qargs); + } + + if (empty($pagename)) + $pagename = $GLOBALS['request']->getArg('pagename'); + + if (isset($args['action']) && $args['action'] == 'browse') + unset($args['action']); + + /*FIXME: + if (empty($args['action'])) + $class = 'wikilink'; + else if (is_safe_action($args['action'])) + $class = 'wikiaction'; + */ + if (empty($args['action']) || is_safe_action($args['action'])) + $class = 'wikiaction'; + else { + // Don't allow administrative links on unlocked pages. + $dbi = $GLOBALS['request']->getDbh(); + $page = $dbi->getPage($basepage); + if (!$page->get('locked')) + return HTML::span(array('class' => 'wikiunsafe'), + HTML::u(_("Lock page to enable link"))); + $class = 'wikiadmin'; + } + + if (!$text) + $text = HTML::span(array('class' => 'rawurl'), $url); + + $wikipage = new WikiPageName($pagename); + if (!$wikipage->isValid()) { + global $Theme; + return $Theme->linkBadWikiWord($wikipage, $url); + } + + return HTML::a(array('href' => WikiURL($pagename, $args), + 'class' => $class), + $text); +} + +/** + * A class to assist in parsing wiki pagenames. + * + * Now with subpages and anchors, parsing and passing around + * pagenames is more complicated. This should help. + */ +class WikiPageName +{ + /** Short name for page. + * + * This is the value of $name passed to the constructor. + * (For use, e.g. as a default label for links to the page.) + */ + var $shortName; + + /** The full page name. + * + * This is the full name of the page (without anchor). + */ + var $name; + + /** The anchor. + * + * This is the referenced anchor within the page, or the empty string. + */ + var $anchor; + + /** Constructor + * + * @param mixed $name Page name. + * WikiDB_Page, WikiDB_PageRevision, or string. + * This can be a relative subpage name (like '/SubPage'), + * or can be the empty string to refer to the $basename. + * + * @param string $anchor For links to anchors in page. + * + * @param mixed $basename Page name from which to interpret + * relative or other non-fully-specified page names. + */ + function WikiPageName($name, $basename=false, $anchor=false) { + if (is_string($name)) { + $this->shortName = $name; + + if ($name == '' or $name[0] == SUBPAGE_SEPARATOR) { + if ($basename) + $name = $this->_pagename($basename) . $name; + else + $name = $this->_normalize_bad_pagename($name); + } + } + else { + $name = $this->_pagename($name); + $this->shortName = $name; + } + + $this->name = $this->_check($name); + $this->anchor = (string)$anchor; + } + + function getParent() { + $name = $this->name; + if (!($tail = strrchr($name, SUBPAGE_SEPARATOR))) + return false; + return substr($name, 0, -strlen($tail)); + } + + function isValid($strict = false) { + if ($strict) + return !isset($this->_errors); + return !empty($this->name); + } + + function getWarnings() { + $warnings = array(); + if (isset($this->_warnings)) + $warnings = array_merge($warnings, $this->_warnings); + if (isset($this->_errors)) + $warnings = array_merge($warnings, $this->_errors); + if (!$warnings) + return false; + + return sprintf(_("'%s': Bad page name: %s"), + $this->shortName, join(', ', $warnings)); + } + + function _pagename($page) { + if (isa($page, 'WikiDB_Page')) + return $page->getName(); + elseif (isa($page, 'WikiDB_PageRevision')) + return $page->getPageName(); + elseif (isa($page, 'WikiPageName')) + return $page->name; + if (!is_string($page)) { + trigger_error(sprintf("Non-string pagename '%s' (%s)(%s)", + $page, gettype($page), get_class($page)), + E_USER_NOTICE); + } + //assert(is_string($page)); + return $page; + } + + function _normalize_bad_pagename($name) { + trigger_error("Bad pagename: " . $name, E_USER_WARNING); + + // Punt... You really shouldn't get here. + if (empty($name)) { + global $request; + return $request->getArg('pagename'); + } + assert($name[0] == SUBPAGE_SEPARATOR); + return substr($name, 1); + } + + + function _check($pagename) { + // Compress internal white-space to single space character. + $pagename = preg_replace('/[\s\xa0]+/', ' ', $orig = $pagename); + if ($pagename != $orig) + $this->_warnings[] = _("White space converted to single space"); + + // Delete any control characters. + $pagename = preg_replace('/[\x00-\x1f\x7f\x80-\x9f]/', '', $orig = $pagename); + if ($pagename != $orig) + $this->_errors[] = _("Control characters not allowed"); + + // Strip leading and trailing white-space. + $pagename = trim($pagename); + + $orig = $pagename; + while ($pagename and $pagename[0] == SUBPAGE_SEPARATOR) + $pagename = substr($pagename, 1); + if ($pagename != $orig) + $this->_errors[] = sprintf(_("Leading %s not allowed"), SUBPAGE_SEPARATOR); + + if (preg_match('/[:;]/', $pagename)) + $this->_warnings[] = _("';' and ':' in pagenames are deprecated"); + + if (strlen($pagename) > MAX_PAGENAME_LENGTH) { + $pagename = substr($pagename, 0, MAX_PAGENAME_LENGTH); + $this->_errors[] = _("too long"); + } + + + if ($pagename == '.' or $pagename == '..') { + $this->_errors[] = sprintf(_("illegal pagename"), $pagename); + $pagename = ''; + } + + return $pagename; + } +} + +/** + * Convert old page markup to new-style markup. + * + * @param string $text Old-style wiki markup. + * + * @param string $markup_type + * One of: <dl> + * <dt><code>"block"</code> <dd>Convert all markup. + * <dt><code>"inline"</code> <dd>Convert only inline markup. + * <dt><code>"links"</code> <dd>Convert only link markup. + * </dl> + * + * @return string New-style wiki markup. + * + * @bugs Footnotes don't work quite as before (esp if there are + * multiple references to the same footnote. But close enough, + * probably for now.... + */ +function ConvertOldMarkup ($text, $markup_type = "block") { + + static $subs; + static $block_re; + + if (empty($subs)) { + /***************************************************************** + * Conversions for inline markup: + */ + + // escape tilde's + $orig[] = '/~/'; + $repl[] = '~~'; + + // escape escaped brackets + $orig[] = '/\[\[/'; + $repl[] = '~['; + + // change ! escapes to ~'s. + global $WikiNameRegexp, $request; + //include_once('lib/interwiki.php'); + $map = getInterwikiMap(); + $bang_esc[] = "(?:" . ALLOWED_PROTOCOLS . "):[^\s<>\[\]\"'()]*[^\s<>\[\]\"'(),.?]"; + $bang_esc[] = $map->getRegexp() . ":[^\\s.,;?()]+"; // FIXME: is this really needed? + $bang_esc[] = $WikiNameRegexp; + $orig[] = '/!((?:' . join(')|(', $bang_esc) . '))/'; + $repl[] = '~\\1'; + + $subs["links"] = array($orig, $repl); + + // Escape '<'s + //$orig[] = '/<(?!\?plugin)|(?<!^)</m'; + //$repl[] = '~<'; + + // Convert footnote references. + $orig[] = '/(?<=.)(?<!~)\[\s*(\d+)\s*\]/m'; + $repl[] = '#[|ftnt_ref_\\1]<sup>~[[\\1|#ftnt_\\1]~]</sup>'; + + // Convert old style emphases to HTML style emphasis. + $orig[] = '/__(.*?)__/'; + $repl[] = '<strong>\\1</strong>'; + $orig[] = "/''(.*?)''/"; + $repl[] = '<em>\\1</em>'; + + // Escape nestled markup. + $orig[] = '/^(?<=^|\s)[=_](?=\S)|(?<=\S)[=_*](?=\s|$)/m'; + $repl[] = '~\\0'; + + // in old markup headings only allowed at beginning of line + $orig[] = '/!/'; + $repl[] = '~!'; + + $subs["inline"] = array($orig, $repl); + + /***************************************************************** + * Patterns which match block markup constructs which take + * special handling... + */ + + // Indented blocks + $blockpats[] = '[ \t]+\S(?:.*\s*\n[ \t]+\S)*'; + + // Tables + $blockpats[] = '\|(?:.*\n\|)*'; + + // List items + $blockpats[] = '[#*;]*(?:[*#]|;.*?:)'; + + // Footnote definitions + $blockpats[] = '\[\s*(\d+)\s*\]'; + + // Plugins + $blockpats[] = '<\?plugin(?:-form)?\b.*\?>\s*$'; + + // Section Title + $blockpats[] = '!{1,3}[^!]'; + + $block_re = ( '/\A((?:.|\n)*?)(^(?:' + . join("|", $blockpats) + . ').*$)\n?/m' ); + + } + + if ($markup_type != "block") { + list ($orig, $repl) = $subs[$markup_type]; + return preg_replace($orig, $repl, $text); + } + else { + list ($orig, $repl) = $subs['inline']; + $out = ''; + while (preg_match($block_re, $text, $m)) { + $text = substr($text, strlen($m[0])); + list (,$leading_text, $block) = $m; + $suffix = "\n"; + + if (strchr(" \t", $block[0])) { + // Indented block + $prefix = "<pre>\n"; + $suffix = "\n</pre>\n"; + } + elseif ($block[0] == '|') { + // Old-style table + $prefix = "<?plugin OldStyleTable\n"; + $suffix = "\n?>\n"; + } + elseif (strchr("#*;", $block[0])) { + // Old-style list item + preg_match('/^([#*;]*)([*#]|;.*?:) */', $block, $m); + list (,$ind,$bullet) = $m; + $block = substr($block, strlen($m[0])); + + $indent = str_repeat(' ', strlen($ind)); + if ($bullet[0] == ';') { + //$term = ltrim(substr($bullet, 1)); + //return $indent . $term . "\n" . $indent . ' '; + $prefix = $ind . $bullet; + } + else + $prefix = $indent . $bullet . ' '; + } + elseif ($block[0] == '[') { + // Footnote definition + preg_match('/^\[\s*(\d+)\s*\]/', $block, $m); + $footnum = $m[1]; + $block = substr($block, strlen($m[0])); + $prefix = "#[|ftnt_${footnum}]~[[${footnum}|#ftnt_ref_${footnum}]~] "; + } + elseif ($block[0] == '<') { + // Plugin. + // HACK: no inline markup... + $prefix = $block; + $block = ''; + } + elseif ($block[0] == '!') { + // Section heading + preg_match('/^!{1,3}/', $block, $m); + $prefix = $m[0]; + $block = substr($block, strlen($m[0])); + } + else { + // AAck! + assert(0); + } + + $out .= ( preg_replace($orig, $repl, $leading_text) + . $prefix + . preg_replace($orig, $repl, $block) + . $suffix ); + } + return $out . preg_replace($orig, $repl, $text); + } +} + + +/** + * Expand tabs in string. + * + * Converts all tabs to (the appropriate number of) spaces. + * + * @param string $str + * @param integer $tab_width + * @return string + */ +function expand_tabs($str, $tab_width = 8) { + $split = split("\t", $str); + $tail = array_pop($split); + $expanded = "\n"; + foreach ($split as $hunk) { + $expanded .= $hunk; + $pos = strlen(strrchr($expanded, "\n")) - 1; + $expanded .= str_repeat(" ", ($tab_width - $pos % $tab_width)); + } + return substr($expanded, 1) . $tail; +} + +/** + * Split WikiWords in page names. + * + * It has been deemed useful to split WikiWords (into "Wiki Words") in + * places like page titles. This is rumored to help search engines + * quite a bit. + * + * @param $page string The page name. + * + * @return string The split name. + */ +function split_pagename ($page) { + + if (preg_match("/\s/", $page)) + return $page; // Already split --- don't split any more. + + // FIXME: this algorithm is Anglo-centric. + static $RE; + if (!isset($RE)) { + // This mess splits between a lower-case letter followed by + // either an upper-case or a numeral; except that it wont + // split the prefixes 'Mc', 'De', or 'Di' off of their tails. + $RE[] = '/([[:lower:]])((?<!Mc|De|Di)[[:upper:]]|\d)/'; + // This the single-letter words 'I' and 'A' from any following + // capitalized words. + $sep = preg_quote(SUBPAGE_SEPARATOR, '/'); + $RE[] = "/(?<= |${sep}|^)([AI])([[:upper:]][[:lower:]])/"; + // Split numerals from following letters. + $RE[] = '/(\d)([[:alpha:]])/'; + //TODO: Split at subpage seperators. TBD in Theme.php + //$RE[] = "/(${sep})([^${sep}]+)/"; + + foreach ($RE as $key) + $RE[$key] = pcre_fix_posix_classes($key); + } + + foreach ($RE as $regexp) { + $page = preg_replace($regexp, '\\1 \\2', $page); + } + return $page; +} + +function NoSuchRevision (&$request, $page, $version) { + $html = HTML(HTML::h2(_("Revision Not Found")), + HTML::p(fmt("I'm sorry. Version %d of %s is not in the database.", + $version, WikiLink($page, 'auto')))); + include_once('lib/Template.php'); + GeneratePage($html, _("Bad Version"), $page->getCurrentRevision()); + $request->finish(); +} + + +/** + * Get time offset for local time zone. + * + * @param $time time_t Get offset for this time. Default: now. + * @param $no_colon boolean Don't put colon between hours and minutes. + * @return string Offset as a string in the format +HH:MM. + */ +function TimezoneOffset ($time = false, $no_colon = false) { + if ($time === false) + $time = time(); + $secs = date('Z', $time); + + if ($secs < 0) { + $sign = '-'; + $secs = -$secs; + } + else { + $sign = '+'; + } + $colon = $no_colon ? '' : ':'; + $mins = intval(($secs + 30) / 60); + return sprintf("%s%02d%s%02d", + $sign, $mins / 60, $colon, $mins % 60); +} + + +/** + * Format time in ISO-8601 format. + * + * @param $time time_t Time. Default: now. + * @return string Date and time in ISO-8601 format. + */ +function Iso8601DateTime ($time = false) { + if ($time === false) + $time = time(); + $tzoff = TimezoneOffset($time); + $date = date('Y-m-d', $time); + $time = date('H:i:s', $time); + return $date . 'T' . $time . $tzoff; +} + +/** + * Format time in RFC-2822 format. + * + * @param $time time_t Time. Default: now. + * @return string Date and time in RFC-2822 format. + */ +function Rfc2822DateTime ($time = false) { + if ($time === false) + $time = time(); + return date('D, j M Y H:i:s ', $time) . TimezoneOffset($time, 'no colon'); +} + +/** + * Format time in RFC-1123 format. + * + * @param $time time_t Time. Default: now. + * @return string Date and time in RFC-1123 format. + */ +function Rfc1123DateTime ($time = false) { + if ($time === false) + $time = time(); + return gmdate('D, d M Y H:i:s \G\M\T', $time); +} + +/** Parse date in RFC-1123 format. + * + * According to RFC 1123 we must accept dates in the following + * formats: + * + * Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 + * Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 + * Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format + * + * (Though we're only allowed to generate dates in the first format.) + */ +function ParseRfc1123DateTime ($timestr) { + $timestr = trim($timestr); + if (preg_match('/^ \w{3},\s* (\d{1,2}) \s* (\w{3}) \s* (\d{4}) \s*' + .'(\d\d):(\d\d):(\d\d) \s* GMT $/ix', + $timestr, $m)) { + list(, $mday, $mon, $year, $hh, $mm, $ss) = $m; + } + elseif (preg_match('/^ \w+,\s* (\d{1,2})-(\w{3})-(\d{2}|\d{4}) \s*' + .'(\d\d):(\d\d):(\d\d) \s* GMT $/ix', + $timestr, $m)) { + list(, $mday, $mon, $year, $hh, $mm, $ss) = $m; + if ($year < 70) $year += 2000; + elseif ($year < 100) $year += 1900; + } + elseif (preg_match('/^\w+\s* (\w{3}) \s* (\d{1,2}) \s*' + .'(\d\d):(\d\d):(\d\d) \s* (\d{4})$/ix', + $timestr, $m)) { + list(, $mon, $mday, $hh, $mm, $ss, $year) = $m; + } + else { + // Parse failed. + return false; + } + + $time = strtotime("$mday $mon $year ${hh}:${mm}:${ss} GMT"); + if ($time == -1) + return false; // failed + return $time; +} + +/** + * Format time to standard 'ctime' format. + * + * @param $time time_t Time. Default: now. + * @return string Date and time. + */ +function CTime ($time = false) +{ + if ($time === false) + $time = time(); + return date("D M j H:i:s Y", $time); +} + + +/** + * Format number as kilobytes or bytes. + * Short format is used for PageList + * Long format is used in PageInfo + * + * @param $bytes int. Default: 0. + * @param $longformat bool. Default: false. + * @return class FormattedText (XmlElement.php). + */ +function ByteFormatter ($bytes = 0, $longformat = false) { + if ($bytes < 0) + return fmt("-???"); + if ($bytes < 1024) { + if (! $longformat) + $size = fmt("%s b", $bytes); + else + $size = fmt("%s bytes", $bytes); + } + else { + $kb = round($bytes / 1024, 1); + if (! $longformat) + $size = fmt("%s k", $kb); + else + $size = fmt("%s Kb (%s bytes)", $kb, $bytes); + } + return $size; +} + +/** + * Internationalized printf. + * + * This is essentially the same as PHP's built-in printf + * with the following exceptions: + * <ol> + * <li> It passes the format string through gettext(). + * <li> It supports the argument reordering extensions. + * </ol> + * + * Example: + * + * In php code, use: + * <pre> + * __printf("Differences between versions %s and %s of %s", + * $new_link, $old_link, $page_link); + * </pre> + * + * Then in locale/po/de.po, one can reorder the printf arguments: + * + * <pre> + * msgid "Differences between %s and %s of %s." + * msgstr "Der Unterschiedsergebnis von %3$s, zwischen %1$s und %2$s." + * </pre> + * + * (Note that while PHP tries to expand $vars within double-quotes, + * the values in msgstr undergo no such expansion, so the '$'s + * okay...) + * + * One shouldn't use reordered arguments in the default format string. + * Backslashes in the default string would be necessary to escape the + * '$'s, and they'll cause all kinds of trouble.... + */ +function __printf ($fmt) { + $args = func_get_args(); + array_shift($args); + echo __vsprintf($fmt, $args); +} + +/** + * Internationalized sprintf. + * + * This is essentially the same as PHP's built-in printf with the + * following exceptions: + * + * <ol> + * <li> It passes the format string through gettext(). + * <li> It supports the argument reordering extensions. + * </ol> + * + * @see __printf + */ +function __sprintf ($fmt) { + $args = func_get_args(); + array_shift($args); + return __vsprintf($fmt, $args); +} + +/** + * Internationalized vsprintf. + * + * This is essentially the same as PHP's built-in printf with the + * following exceptions: + * + * <ol> + * <li> It passes the format string through gettext(). + * <li> It supports the argument reordering extensions. + * </ol> + * + * @see __printf + */ +function __vsprintf ($fmt, $args) { + $fmt = gettext($fmt); + // PHP's sprintf doesn't support variable with specifiers, + // like sprintf("%*s", 10, "x"); --- so we won't either. + + if (preg_match_all('/(?<!%)%(\d+)\$/x', $fmt, $m)) { + // Format string has '%2$s' style argument reordering. + // PHP doesn't support this. + if (preg_match('/(?<!%)%[- ]?\d*[^- \d$]/x', $fmt)) + // literal variable name substitution only to keep locale + // strings uncluttered + trigger_error(sprintf(_("Can't mix '%s' with '%s' type format strings"), + '%1\$s','%s'), E_USER_WARNING); //php+locale error + + $fmt = preg_replace('/(?<!%)%\d+\$/x', '%', $fmt); + $newargs = array(); + + // Reorder arguments appropriately. + foreach($m[1] as $argnum) { + if ($argnum < 1 || $argnum > count($args)) + trigger_error(sprintf(_("%s: argument index out of range"), + $argnum), E_USER_WARNING); + $newargs[] = $args[$argnum - 1]; + } + $args = $newargs; + } + + // Not all PHP's have vsprintf, so... + array_unshift($args, $fmt); + return call_user_func_array('sprintf', $args); +} + +function file_mtime ($filename) { + if ($stat = stat($filename)) + return $stat[9]; + else + return false; +} + +function sort_file_mtime ($a, $b) { + $ma = file_mtime($a); + $mb = file_mtime($b); + if (!$ma or !$mb or $ma == $mb) return 0; + return ($ma > $mb) ? -1 : 1; +} + +class fileSet { + /** + * Build an array in $this->_fileList of files from $dirname. + * Subdirectories are not traversed. + * + * (This was a function LoadDir in lib/loadsave.php) + * See also http://www.php.net/manual/en/function.readdir.php + */ + function getFiles($exclude=false,$sortby=false,$limit=false) { + $list = $this->_fileList; + if ($sortby) { + switch (Pagelist::sortby($sortby,'db')) { + case 'pagename ASC': break; + case 'pagename DESC': + $list = array_reverse($list); + break; + case 'mtime ASC': + usort($list,'sort_file_mtime'); + break; + case 'mtime DESC': + usort($list,'sort_file_mtime'); + $list = array_reverse($list); + break; + } + } + if ($limit) + return array_splice($list,0,$limit); + return $list; + } + + function _filenameSelector($filename) { + if (! $this->_pattern) + return true; + else { + return glob_match ($this->_pattern, $filename, $this->_case); + } + } + + function fileSet($directory, $filepattern = false) { + $this->_fileList = array(); + $this->_pattern = $filepattern; + $this->_case = !isWindows(); + $this->_pathsep = '/'; + + if (empty($directory)) { + trigger_error(sprintf(_("%s is empty."), 'directoryname'), + E_USER_NOTICE); + return; // early return + } + + @ $dir_handle = opendir($dir=$directory); + if (empty($dir_handle)) { + trigger_error(sprintf(_("Unable to open directory '%s' for reading"), + $dir), E_USER_NOTICE); + return; // early return + } + + while ($filename = readdir($dir_handle)) { + if ($filename[0] == '.' || filetype($dir . $this->_pathsep . $filename) != 'file') + continue; + if ($this->_filenameSelector($filename)) { + array_push($this->_fileList, "$filename"); + //trigger_error(sprintf(_("found file %s"), $filename), + // E_USER_NOTICE); //debugging + } + } + closedir($dir_handle); + } +}; + +// File globbing + +// expands a list containing regex's to its matching entries +class ListRegexExpand { + var $match, $list, $index, $case_sensitive; + function ListRegexExpand (&$list, $match, $case_sensitive = true) { + $this->match = str_replace('/','\/',$match); + $this->list = &$list; + $this->case_sensitive = $case_sensitive; + //$this->index = false; + } + function listMatchCallback ($item, $key) { + if (preg_match('/' . $this->match . ($this->case_sensitive ? '/' : '/i'), $item)) { + unset($this->list[$this->index]); + $this->list[] = $item; + } + } + function expandRegex ($index, &$pages) { + $this->index = $index; + array_walk($pages, array($this, 'listMatchCallback')); + return $this->list; + } +} + +// convert fileglob to regex style +function glob_to_pcre ($glob) { + $re = preg_replace('/\./', '\\.', $glob); + $re = preg_replace(array('/\*/','/\?/'), array('.*','.'), $glob); + if (!preg_match('/^[\?\*]/',$glob)) + $re = '^' . $re; + if (!preg_match('/[\?\*]$/',$glob)) + $re = $re . '$'; + return $re; +} + +function glob_match ($glob, $against, $case_sensitive = true) { + return preg_match('/' . glob_to_pcre($glob) . ($case_sensitive ? '/' : '/i'), $against); +} + +function explodeList($input, $allnames, $glob_style = true, $case_sensitive = true) { + $list = explode(',',$input); + // expand wildcards from list of $allnames + if (preg_match('/[\?\*]/',$input)) { + // Optimizing loop invariants: + // http://phplens.com/lens/php-book/optimizing-debugging-php.php + for ($i = 0, $max = sizeof($list); $i < $max; $i++) { + $f = $list[$i]; + if (preg_match('/[\?\*]/',$f)) { + reset($allnames); + $expand = new ListRegexExpand($list, $glob_style ? glob_to_pcre($f) : $f, $case_sensitive); + $expand->expandRegex($i, $allnames); + } + } + } + return $list; +} + +// echo implode(":",explodeList("Test*",array("xx","Test1","Test2"))); +function explodePageList($input, $perm=false, $sortby='pagename', $limit=false) { + include_once("lib/PageList.php"); + return PageList::explodePageList($input,$perm,$sortby,$limit); +} + +// Class introspections + +/** Determine whether object is of a specified type. + * + * @param $object object An object. + * @param $class string Class name. + * @return bool True iff $object is a $class + * or a sub-type of $class. + */ +function isa ($object, $class) { + $lclass = strtolower($class); + + return is_object($object) + && ( get_class($object) == strtolower($lclass) + || is_subclass_of($object, $lclass) ); +} + +/** Determine whether (possible) object has method. + * + * @param $object mixed Object + * @param $method string Method name + * @return bool True iff $object is an object with has method $method. + */ +function can ($object, $method) { + return is_object($object) && method_exists($object, strtolower($method)); +} + +/** Determine whether a function is okay to use. + * + * Some providers (e.g. Lycos) disable some of PHP functions for + * "security reasons." This makes those functions, of course, + * unusable, despite the fact the function_exists() says they + * exist. + * + * This function test to see if a function exists and is not + * disallowed by PHP's disable_functions config setting. + * + * @param string $function_name Function name + * @return bool True iff function can be used. + */ +function function_usable($function_name) { + static $disabled; + if (!is_array($disabled)) { + $disabled = array(); + // Use get_cfg_var since ini_get() is one of the disabled functions + // (on Lycos, at least.) + $split = preg_split('/\s*,\s*/', trim(get_cfg_var('disable_functions'))); + foreach ($split as $f) + $disabled[strtolower($f)] = true; + } + + return ( function_exists($function_name) + and ! isset($disabled[strtolower($function_name)]) + ); +} + + +/** Hash a value. + * + * This is used for generating ETags. + */ +function hash ($x) { + if (is_scalar($x)) { + return $x; + } + elseif (is_array($x)) { + ksort($x); + return md5(serialize($x)); + } + elseif (is_object($x)) { + return $x->hash(); + } + trigger_error("Can't hash $x", E_USER_ERROR); +} + + +/** + * Seed the random number generator. + * + * better_srand() ensures the randomizer is seeded only once. + * + * How random do you want it? See: + * http://www.php.net/manual/en/function.srand.php + * http://www.php.net/manual/en/function.mt-srand.php + */ +function better_srand($seed = '') { + static $wascalled = FALSE; + if (!$wascalled) { + $seed = $seed === '' ? (double) microtime() * 1000000 : $seed; + srand($seed); + $wascalled = TRUE; + //trigger_error("new random seed", E_USER_NOTICE); //debugging + } +} + +/** + * Recursively count all non-empty elements + * in array of any dimension or mixed - i.e. + * array('1' => 2, '2' => array('1' => 3, '2' => 4)) + * See http://www.php.net/manual/en/function.count.php + */ +function count_all($arg) { + // skip if argument is empty + if ($arg) { + //print_r($arg); //debugging + $count = 0; + // not an array, return 1 (base case) + if(!is_array($arg)) + return 1; + // else call recursively for all elements $arg + foreach($arg as $key => $val) + $count += count_all($val); + return $count; + } +} + +function isSubPage($pagename) { + return (strstr($pagename, SUBPAGE_SEPARATOR)); +} + +function subPageSlice($pagename, $pos) { + $pages = explode(SUBPAGE_SEPARATOR,$pagename); + $pages = array_slice($pages,$pos,1); + return $pages[0]; +} + +/** + * Alert + * + * Class for "popping up" and alert box. (Except that right now, it doesn't + * pop up...) + * + * FIXME: + * This is a hackish and needs to be refactored. However it would be nice to + * unify all the different methods we use for showing Alerts and Dialogs. + * (E.g. "Page deleted", login form, ...) + */ +class Alert { + /** Constructor + * + * @param object $request + * @param mixed $head Header ("title") for alert box. + * @param mixed $body The text in the alert box. + * @param hash $buttons An array mapping button labels to URLs. + * The default is a single "Okay" button pointing to $request->getURLtoSelf(). + */ + function Alert($head, $body, $buttons=false) { + if ($buttons === false) + $buttons = array(); + + $this->_tokens = array('HEADER' => $head, 'CONTENT' => $body); + $this->_buttons = $buttons; + } + + /** + * Show the alert box. + */ + function show(&$request) { + global $request; + + $tokens = $this->_tokens; + $tokens['BUTTONS'] = $this->_getButtons(); + + $request->discardOutput(); + $tmpl = new Template('dialog', $request, $tokens); + $tmpl->printXML(); + $request->finish(); + } + + + function _getButtons() { + global $request; + + $buttons = $this->_buttons; + if (!$buttons) + $buttons = array(_("Okay") => $request->getURLtoSelf()); + + global $Theme; + foreach ($buttons as $label => $url) + print "$label $url\n"; + $out[] = $Theme->makeButton($label, $url, 'wikiaction'); + return new XmlContent($out); + } +} + +/** + * Returns true if current php version is at mimimum a.b.c + * Called: check_php_version(4,1) + */ +function check_php_version ($a = '0', $b = '0', $c = '0') { + global $PHP_VERSION; + if(!isset($PHP_VERSION)) + $PHP_VERSION = substr( str_pad( preg_replace('/\D/','', PHP_VERSION), 3, '0'), 0, 3); + return $PHP_VERSION >= ($a.$b.$c); +} + +function isWikiWord($word) { + global $WikiNameRegexp; + //or preg_match('/\A' . $WikiNameRegexp . '\z/', $word) ?? + return preg_match("/^$WikiNameRegexp\$/",$word); +} + +// needed to store serialized objects-values only (perm, pref) +function obj2hash ($obj, $exclude = false, $fields = false) { + $a = array(); + if (! $fields ) $fields = get_object_vars($obj); + foreach ($fields as $key => $val) { + if (is_array($exclude)) { + if (in_array($key,$exclude)) continue; + } + $a[$key] = $val; + } + return $a; +} + +// $Log: not supported by cvs2svn $ +// Revision 1.170 2004/04/19 18:27:45 rurban +// Prevent from some PHP5 warnings (ref args, no :: object init) +// php5 runs now through, just one wrong XmlElement object init missing +// Removed unneccesary UpgradeUser lines +// Changed WikiLink to omit version if current (RecentChanges) +// +// Revision 1.169 2004/04/15 21:29:48 rurban +// allow [0] with new markup: link to page "0" +// +// Revision 1.168 2004/04/10 02:30:49 rurban +// Fixed gettext problem with VIRTUAL_PATH scripts (Windows only probably) +// Fixed "cannot setlocale..." (sf.net problem) +// +// Revision 1.167 2004/04/02 15:06:55 rurban +// fixed a nasty ADODB_mysql session update bug +// improved UserPreferences layout (tabled hints) +// fixed UserPreferences auth handling +// improved auth stability +// improved old cookie handling: fixed deletion of old cookies with paths +// +// Revision 1.166 2004/04/01 15:57:10 rurban +// simplified Sidebar theme: table, not absolute css positioning +// added the new box methods. +// remaining problems: large left margin, how to override _autosplitWikiWords in Template only +// +// Revision 1.165 2004/03/24 19:39:03 rurban +// php5 workaround code (plus some interim debugging code in XmlElement) +// php5 doesn't work yet with the current XmlElement class constructors, +// WikiUserNew does work better than php4. +// rewrote WikiUserNew user upgrading to ease php5 update +// fixed pref handling in WikiUserNew +// added Email Notification +// added simple Email verification +// removed emailVerify userpref subclass: just a email property +// changed pref binary storage layout: numarray => hash of non default values +// print optimize message only if really done. +// forced new cookie policy: delete pref cookies, use only WIKI_ID as plain string. +// prefs should be stored in db or homepage, besides the current session. +// +// Revision 1.164 2004/03/18 21:41:09 rurban +// fixed sqlite support +// WikiUserNew: PHP5 fixes: don't assign $this (untested) +// +// Revision 1.163 2004/03/17 18:41:49 rurban +// just reformatting +// +// Revision 1.162 2004/03/16 15:43:08 rurban +// make fileSet sortable to please PageList +// +// Revision 1.161 2004/03/12 15:48:07 rurban +// fixed explodePageList: wrong sortby argument order in UnfoldSubpages +// simplified lib/stdlib.php:explodePageList +// +// Revision 1.160 2004/02/28 21:14:08 rurban +// generally more PHPDOC docs +// see http://xarch.tu-graz.ac.at/home/rurban/phpwiki/xref/ +// fxied WikiUserNew pref handling: empty theme not stored, save only +// changed prefs, sql prefs improved, fixed password update, +// removed REPLACE sql (dangerous) +// moved gettext init after the locale was guessed +// + some minor changes +// +// Revision 1.158 2004/02/19 21:54:17 rurban +// moved initerwiki code to PageType.php +// re-enabled and fixed InlineImages support, now also for InterWiki Urls +// * [File:my_image.gif] inlines the image, +// * File:my_image.gif shows a plain inter-wiki link, +// * [what a pic|File:my_image.gif] shows a named inter-wiki link to the gif +// * [File:my_image.gif|what a pic] shows a inlimed image linked to the page "what a pic" +// +// Revision 1.157 2004/02/09 03:58:12 rurban +// for now default DB_SESSION to false +// PagePerm: +// * not existing perms will now query the parent, and not +// return the default perm +// * added pagePermissions func which returns the object per page +// * added getAccessDescription +// WikiUserNew: +// * added global ->prepare (not yet used) with smart user/pref/member table prefixing. +// * force init of authdbh in the 2 db classes +// main: +// * fixed session handling (not triple auth request anymore) +// * don't store cookie prefs with sessions +// stdlib: global obj2hash helper from _AuthInfo, also needed for PagePerm +// +// Revision 1.156 2004/01/26 09:17:49 rurban +// * changed stored pref representation as before. +// the array of objects is 1) bigger and 2) +// less portable. If we would import packed pref +// objects and the object definition was changed, PHP would fail. +// This doesn't happen with an simple array of non-default values. +// * use $prefs->retrieve and $prefs->store methods, where retrieve +// understands the interim format of array of objects also. +// * simplified $prefs->get() and fixed $prefs->set() +// * added $user->_userid and class '_WikiUser' portability functions +// * fixed $user object ->_level upgrading, mostly using sessions. +// this fixes yesterdays problems with loosing authorization level. +// * fixed WikiUserNew::checkPass to return the _level +// * fixed WikiUserNew::isSignedIn +// * added explodePageList to class PageList, support sortby arg +// * fixed UserPreferences for WikiUserNew +// * fixed WikiPlugin for empty defaults array +// * UnfoldSubpages: added pagename arg, renamed pages arg, +// removed sort arg, support sortby arg +// +// Revision 1.155 2004/01/25 10:52:22 rurban +// added sortby support to explodePageList() and UnfoldSubpages +// fixes [ 758044 ] Plugin UnfoldSubpages does not sort (includes fix) +// +// Revision 1.154 2004/01/25 03:49:03 rurban +// added isWikiWord() to avoid redundancy +// added check_php_version() to check for older php versions. +// e.g. object::method calls, ... +// +// Revision 1.153 2003/11/30 18:43:18 carstenklapp +// Fixed careless mistakes in my last optimization commit. +// +// Revision 1.152 2003/11/30 18:20:34 carstenklapp +// Minor code optimization: reduce invariant loops +// +// Revision 1.151 2003/11/29 19:30:01 carstenklapp +// New function ByteFormatter. +// +// Revision 1.150 2003/09/13 22:43:00 carstenklapp +// New preference to hide LinkIcons. +// +// Revision 1.149 2003/03/26 19:37:08 dairiki +// Fix "object to string conversion" bug with external image links. +// +// Revision 1.148 2003/03/25 21:03:02 dairiki +// Cleanup debugging output. +// +// Revision 1.147 2003/03/13 20:17:05 dairiki +// Bug fix: Fix linking of pages whose names contain a hash ('#'). +// +// Revision 1.146 2003/03/07 02:46:24 dairiki +// function_usable(): New function. +// +// Revision 1.145 2003/03/04 01:55:05 dairiki +// Fix to ensure absolute URL for logo in RSS recent changes. +// +// Revision 1.144 2003/02/26 00:39:30 dairiki +// Bug fix: for magic PhpWiki URLs, "lock page to enable link" message was +// being displayed at incorrect times. +// +// Revision 1.143 2003/02/26 00:10:26 dairiki +// More/better/different checks for bad page names. +// +// Revision 1.142 2003/02/25 22:19:46 dairiki +// Add some sanity checking for pagenames. +// +// Revision 1.141 2003/02/22 20:49:55 dairiki +// Fixes for "Call-time pass by reference has been deprecated" errors. +// +// Revision 1.140 2003/02/21 23:33:29 dairiki +// Set alt="" on the link icon image tags. +// (See SF bug #675141.) +// +// Revision 1.139 2003/02/21 22:16:27 dairiki +// Get rid of MakeWikiForm, and form-style MagicPhpWikiURLs. +// These have been obsolete for quite awhile (I hope). +// +// Revision 1.138 2003/02/21 04:12:36 dairiki +// WikiPageName: fixes for new cached links. +// +// Alert: new class for displaying alerts. +// +// ExtractWikiPageLinks and friends are now gone. +// +// LinkBracketLink moved to InlineParser.php +// +// Revision 1.137 2003/02/18 23:13:40 dairiki +// Wups again. Typo fix. +// +// Revision 1.136 2003/02/18 21:52:07 dairiki +// Fix so that one can still link to wiki pages with # in their names. +// (This was made difficult by the introduction of named tags, since +// '[Page #1]' is now a link to anchor '1' in page 'Page'. +// +// Now the ~ escape for page names should work: [Page ~#1]. +// +// Revision 1.135 2003/02/18 19:17:04 dairiki +// split_pagename(): +// Bug fix. 'ThisIsABug' was being split to 'This IsA Bug'. +// Cleanup up subpage splitting code. +// +// Revision 1.134 2003/02/16 19:44:20 dairiki +// New function hash(). This is a helper, primarily for generating +// HTTP ETags. +// +// Revision 1.133 2003/02/16 04:50:09 dairiki +// New functions: +// Rfc1123DateTime(), ParseRfc1123DateTime() +// for converting unix timestamps to and from strings. +// +// These functions produce and grok the time strings +// in the format specified by RFC 2616 for use in HTTP headers +// (like Last-Modified). +// +// Revision 1.132 2003/01/04 22:19:43 carstenklapp +// Bugfix UnfoldSubpages: "Undefined offset: 1" error when plugin invoked +// on a page with no subpages (explodeList(): array 0-based, sizeof 1-based). +// + +// (c-file-style: "gnu") +// Local Variables: +// mode: php +// tab-width: 8 +// c-basic-offset: 4 +// c-hanging-comment-ender-p: nil +// indent-tabs-mode: nil +// End: +?> diff --git a/themes/Hawaiian/lib/random.php b/themes/Hawaiian/lib/random.php index 0b9083593..6292383d7 100644 --- a/themes/Hawaiian/lib/random.php +++ b/themes/Hawaiian/lib/random.php @@ -1,4 +1,4 @@ -<?php rcs_id('$Id: random.php,v 1.9 2002-01-28 15:52:40 carstenklapp Exp $'); +<?php rcs_id('$Id: random.php,v 1.10 2004-04-19 23:13:04 zorloc Exp $'); /** */ class randomImage { @@ -41,8 +41,7 @@ class imageSet extends fileSet { * $InlineImages. */ function _filenameSelector($filename) { - global $InlineImages; - return preg_match("/($InlineImages)$/i", $filename); + return preg_match("/(" . INLINE_IMAGES . ")$/i", $filename); } }; -- 2.45.0