All pages of the documentation


What is PmWiki?

PmWiki is a wiki-based system for collaborative creation and maintenance of websites. See PmWiki.

What can I do with it?

PmWiki pages look and act like normal web pages, except they have an "Edit" link that makes it easy to modify existing pages and add new pages into the website, using basic editing rules. You do not need to know or use any HTML or CSS. Page editing can be left open to the public or restricted to small groups of authors. Feel free to experiment with the Text Formatting Rules in the "Wiki sandbox". The website you're currently viewing is built and maintained with PmWiki.

What are the requirements?

See the PmWiki requirements page.

Where can I find documentation?

See the documentation index page.

How can I download PmWiki?

See the download page.

How do I install PmWiki?

Instructions for installation are on the installation page.

How do I get help with PmWiki?

See Mailing lists and How to get assistance.

How do you pronounce "Michaud"?

"Michaud" is french pronounced "mee show", the trailing D is silent.

Basic PmWiki editing rules

The pages on this site are wiki-based pages, which means that pages can be created and edited by multiple authors. To edit a page, click the Edit link that exists somewhere on the page, usually in the header or footer. Some pages may be password-protected, depending on the system's security policies, but many systems allow open editing of pages.

PmWiki is not WYSIWYG - When editing a page, you see the markup text that describes the content of the page. The basic rules for page markup are simple:

  1. Use a blank line to start a new paragraph more.
  2. To make a list, start each line with # for numbered (ordered) lists or * for bulleted (unordered) lists more.
  3. To make a heading, start a line with two or more ! marks; !! is a subheading, and !!! is a sub-subheading more.
  4. To emphasize text, enclose it in 2 or 3 single quotes; ''text'' for italics or '''text''' for bold more.
  5. To make a link to another page, enclose the page's name in double brackets; for example [[basic editing]] links to this page.
  6. To make a link to another site, type its address, such as [[]]. Email links must have "mailto:" before such as -> mailto:xyz [snail] example [period] com

If you want to experiment with editing a page, try it on the Wiki Sandbox. You can edit the Wiki Sandbox without affecting anything important on this site. On talk pages and discussions, it's courteous to sign your contribution; using ~~~ effectively 'signs' the name that you provide in the Author field on the Page Edit form.

Examples of common markups

The tables below demonstrate many of the common markups used to format pages. The left column shows what to write to achieve the effect, the right column shows the effect of the markup. More details are available from the text formatting rules and other documentation pages. An exhaustive list of default markup is available as the markup master index.

Paragraphs and line breaks

What to type

What it looks like

Consecutive lines
will be merged together
as part of the same paragraph.

One or more empty lines will start a new paragraph.

Consecutive lines will be merged together as part of the same paragraph.

One or more empty lines will start a new paragraph.

Two backslashes at the end of a line \\
force a line break.

Or use this markup: [[<<]] to force a break.

Two backslashes at the end of a line
force a line break.

Or use this markup:
to force a break.

Further reading:

  • text formatting rules for more information on linebreaks, indented or hanging paragraphs.
  • wiki styles for centered or right justified paragraphs and "floating" text (boxes), borders and much more.


Start each line with # for numbered (ordered) lists or * for bulleted (unordered) lists:

* Bullet list
* Another item
** More asterisks produce sub-items
** etc.
  • Bullet list
  • Another item
    • More asterisks produce sub-items
    • etc.
# Numbered lists
# Another item
## more hashes produce sub-items
  1. Numbered lists
  2. Another item
    1. more hashes produce sub-items
# List types
# can be mixed
** numbered list with unordered sub-list
  1. List types
  2. can be mixed
    • numbered list with unordered sub-list

Learn more about lists (including definition lists) and list styles.


Headings are useful for creating a "well-structured" page. They're not just for making big text.

What to type

What it looks like

!! Major Subheading
!!! Minor Subheading
!!!! And More
!!!!! Subheadings

Major Subheading

Minor Subheading

And More


Text Emphasis

To emphasize, enclose text in apostrophes (single-quote marks), not double-quotes.

What to type

What it looks like

''Emphasize'' (italics),
'''strong''' (bold), 
'''''very strong''''' (bold italics).

Emphasize (italics), strong (bold), very strong (bold italics).


To make a link to another page, enclose the page's name in double square brackets.

What to type

What it looks like

Practice editing in the [[wiki sandbox]]

Practice editing in the wiki sandbox

Note that words are automatically capitalized in page titles. The link above links to the page WikiSandbox.

Text after a pipe (|) is used as the link text:

Practice editing in the
[[WikiSandbox | practice area]].

Practice editing in the practice area.

Endings become part of the link text, parentheses hide parts of the link name:

[[wiki sandbox]]es.

[[(wiki) sandbox]].

wiki sandboxes.


When linking to a page in a different WikiGroup, provide the group name, followed by a separator, and then the page name:

[[Main.Wiki Sandbox]] shows group + name

[[Main/Wiki Sandbox]] shows only name

Main.Wiki Sandbox shows group + name

Wiki Sandbox shows only name

Links to external sites

bare url:

link text: [[ | PmWiki home]]

bare url:

link text: PmWiki home

Links as reference to external sites

bare url:

link text: [[ | #]]

bare url:

link text: [1]

Colons make InterMap (also called InterWiki?) links to other wikis:

What's an [[Wikipedia:aardvark]], anyway?

What's an Wikipedia:aardvark, anyway?

Links to nonexistent pages? are displayed specially, to invite others to create the page.

PmWiki supports more link types and a lot of display options, see Links to learn more.

Preformatted text

Preformatted text is displayed using a monospace font and not generating linebreaks except where explicitly indicated in the markup.

Note that very long lines of preformatted text can cause the whole page to be wide.

For preformatted text with markup (e.g. emphasis) being processed, start each line with a space:

 Lines that begin with a space
 are formatted exactly as typed
 in a '''fixed-width''' font.
 Lines that begin with a space
 are formatted exactly as typed
 in a fixed-width font.

If you don't want Wiki markup to be processed, use [@ @]. Can also be used inline.

Text escaped this way has
the HTML ''code'' style
Text escaped this way has
the HTML ''code'' style

Escape sequence

If you don't want Wiki markup to be processed, but lines reformatted use [= =]. Can also be used inline.

markup is ''not'' processed
but lines are reformatted

markup is ''not'' processed but lines are reformatted

Horizontal line

Four or more dashes at
the beginning of a line
produce a "horizontal rule"

Four or more dashes at the beginning of a line

produce a "horizontal rule"


Simple tables use double pipe characters to separate cells:

|| border=1
||! head 1 ||! head 2 ||! head 3 ||
|| cell 1  ||  cell 2 ||  cell 3 ||
head 1head 2head 3
cell 1cell 2cell 3

See simple tables and advanced tables to learn more about the rich feature set of PmWiki tables.


See Images

Character formatting

What to type

What it looks like

* @@Monospaced text@@
* Text with '^superscripts^'
* Text with '_subscripts_'
* deleted {-strikethrough-} text
* inserted {+underline+} text
* [+big+], [++bigger++] text
* [-small-], [--smaller--] text
  • Monospaced text
  • Text with superscripts
  • Text with subscripts
  • deleted strikethrough text
  • inserted underline text
  • big, bigger text
  • small, smaller text

Use WikiStyles to change the text color .

Page titles

The (:title:) directive sets the page's title to something other than its page name.

The name of this page is "{$Name}", and its title is "{$Title}".

The name of this page is "IncludeAll", and its title is "All pages of the documentation".

Page Description

  • The (:Description Page summary here:) directive sets the page description. The description is used by search engines, and can be displayed in search results and in page lists.
The summary description of this page is {$Description}.

The summary description of this page is .

I'm new to PmWiki, where can I find some basic help for getting started?

The Basic Editing page is a good start. From there, you can just follow the navigational links at the top or the bottom of the page (they are called Wiki Trails) to the next pages, or to the Documentation Index page, which provides an outline style index of essential documentation pages, organized from basic to advanced.

How do I include special characters such as Copyright (©) and Trademark (® or ™) on my wiki pages?

See special characters on how to insert special characters that don't appear on your keyboard.

How can I preserve line-breaks from the source text?

PmWiki normally treats consecutive lines of text as being a paragraph, and merges and wraps lines together on output. This is consistent with most other wiki packages. An author can use the (:linebreaks:) directive to cause the following lines of markup text in the page to be kept as separate lines in the output. Or a wiki administrator can set in config.php $HTMLPNewline = '<br/>'; to force literal new lines for the whole site.

Can I just enter HTML directly?

By default (and by design), PmWiki does not support the use of HTML elements in the editable markup for wiki pages. There are a number of reasons for this described in the PmWiki Philosophy and Audiences. Enabling HTML markup within wiki pages in a collaborative environment may exclude some potential authors from being able to edit pages, and pose a number of display and security issues. However, a site administrator can use the Cookbook:Enable HTML recipe to enable the use of HTML markup directly in pages.

Where can I find more documentation?

See the documentation index and the markup master index pages.


The first step to create a new page is to edit an existing page and add a link to the page you want to create.

To link to your new page, you must choose a name for it. The best names describe the page's contents well, so that everyone can remember and type the name easily.
To create a link, surround the page name with double brackets. Typing [[my new page]] will create a link to my new page?. There's a lot you can do with double bracket links.

You can see that the links to my new page? all have question marks after them. That's because my new page? hasn't been written yet. Clicking the link as second step will take you to an edit form where you could write and finally save the new page.

Another way to create a page: in your browser's address bar (where the page URL is), replace the name of the current page with the name of the page you wish to create, and hit Enter or do whatever you would normally do to go to a new location. PmWiki will then dutifully tell you that the page you entered doesn't exist, but you can click on the "Edit" link in order to create, edit, and save the new page.

The drawback to this method is that there are no links to your new page, so you're the only person who knows it exists. It will be an orphan, unread, unlinked, unloved. That's why adding a link to an existing page or to the SideBar is a better way to create a page.

Learn more:

  • You can also organize related pages into groups, and link between pages in different groups.

How do I create a new page?

Typing [[my new page]] will create a link to the new page. There's a lot you can do with double bracket links.

Why do some new pages have a title with spaces like "Creating New Pages" and others end up with a WikiWord-like title like "CreatingNewPages"?

The default page title is simply the name of page, which is normally stored as "CreatingNewPages." However, you can override a page's title by using the (:title Creating New Pages:) directive. This is especially useful when there are special characters or capitalization that you want in the title that cannot be used in the page name.


A key feature of wiki-based systems is the ease of creating hyper links (or short links) in the text of a document. PmWiki provides multiple mechanisms for creating such links.

Links to other pages in the wiki

To create an internal link to another page, simply enclose the name of the page inside double square brackets, as in [[wiki sandbox]] or [[installation]]. This results in links to wiki sandbox and installation, respectively.

PmWiki creates a link by using the text inside the double brackets. It does this by removing spaces between the words, and automatically capitalizing the first letter of each word following spaces or other punctuation (like ~). Thus [[Wiki Sandbox]], [[wiki sandbox]], and [[WikiSandbox]] all display differently but create the same link to the page titled WikiSandbox. Or in other words, PmWiki will automatically create the "link path name" using the page name in CamelCase?, but the "link text" will display in the format you have entered it.

Some PmWiki sites (default not) will recognize words written in CamelCase?, called a WikiWord, automatically as a link to a page of the same name.

Links with different link text

There are three ways to get a different link text:

  1. Hide link text. Link text within (parentheses) will not be not displayed, so that [[(wiki) sandbox]] links to WikiSandbox but displays as sandbox. For addresses actually containing parentheses, use %28 and %29
  2. Change link text. You can specify another link text after a vertical brace, as in [[WikiSandbox | a play area]], or you can use an arrow (->) to reverse the order of the link text and the target, as in [[a play area -> WikiSandbox]]. Both links displays as a play area.
  3. Show page title instead of page name. The use of special characters in the page name is not a problem for PmWiki, but on some servers it may be better to use only plain A-Z letters for the page "name" (which is also a filename), and set the page "title" to the extended or international characters with the (:title PageTitle:) directive within the page. The page title can be shown instead of the page name with the [[PageName|+]] link markup, e.g. page BasicEditing contains the directive (:title Basic PmWiki editing rules:) with the result that a link written as [[BasicEditing|+]] will display as Basic PmWiki editing rules.
    Since PmWiki version 2.2.14 this works also for those technical pages that have an entry in the XLPage?, without the need to add the (:title PageTitleName:) directive within that page (for more details see Localization.Localization).

On top of above ways, a suffix can be added to the end of a link, which becomes part of the link text but not of the target page name.
Note: This feature does currently not work with the [[PageName|+]] markup.

What to type

What it looks like

[[(wiki) sandbox]],\\
[[(wiki) sandbox]]es\\
[[WikiSandbox|wiki sandbox]],\\
[[WikiSandbox|wiki sandbox]]es\\

wiki sandbox,
wiki sandboxes
Basic PmWiki editing rules

Links with tool tip

From version 2.2.14 PmWiki can show tooltip titles with the following format:

  • External link: [["tool tip title" | external link ]]
  • InterMap link: [[Wikipedia:Wiki"tool tip title"| InterMap link ]]
  • Linked image: [[Attach:000962.png"tool tip title" | Attach: link]]
  • Inline image: Attach:000962.png"tool tip title"

PmWiki does not support tool tip titles for internal links!

Links to nonexistent pages

Links to nonexistent pages? are displayed specially, to invite others to create the page. See Creating new pages to learn more.

Links to pages in other wiki groups

Links as written above are links between pages of the same group. To create a link to a page in another group, add the name of that other group together with a dot or slash as prefix to the page name. For example, links to Main/WikiSandbox could be written as:

What to type

What it looks like

* [[Main.WikiSandbox]]
* [[Main/WikiSandbox]]
* [[(Main.Wiki)Sandbox]]
* [[Main.WikiSandbox | link text]]
* [[Main.WikiSandbox | +]]

To link to the "default home page" of a group, the name of the page can be omitted:

* [[Main.]]
* [[Main/]]

See Wiki Group to learn more about PmWiki groups.

Category links

Categories are a way to organize and find related pages. The idea is that every page that falls into a particular subject area should have a link to a shared page containing links to other pages on that subject. These shared pages are created in the special group Category, and thus these subject areas are called "categories".

Adding a page to the category Subject is simple by adding the [[!Subject]] markup somewhere on that page. This will create a link to the page Category.Subject. So [[!Subject]] is a kind of link shortcut to the page Category.Subject. See Categories to learn more.

User page links

Similar is [[~Author]] a link shortcut to the page Author in the special group Profiles. PmWiki automatically creates this type of link for the current author, when it encounters three tilde characters (~) in a row (~~~) in the page text. The current author is the name found in the "Author" field, when you create or modify a page. The current date and time is appended when four tilde characters in a row are encountered (~~~~).

So, when the Author field contains "Author":
~~~ markup will be replaced by: Author
~~~~ markup will be replaced by: Author October 10, 2010, at 04:50 PM

Link shortcuts

[[PageName|#]] creates a reference link as shown below[1].

Links to specific locations within a page -- "anchors"

To define a location, or bookmark, within a page to which you may jump directly, use the markup [[#name]]. This creates an "anchor" that uniquely identifies that location in the page. Then to have a link jump directly to that anchor, use one of

  • [[#name|link text]] within the same page, or
  • [[PageName#name]] or [[PageName#name|link text]] for a location on another page
  • The form [[PageName(#name)]] may be useful for hiding the anchor text in a link.

For example, here's a link to the Intermaps section, below.


  • The anchor itself must begin with a letter, not a number.
  • A link to an anchor must have the same capitalization as the anchor itself.
  • Spaces are not allowed in an anchor: "[[#my anchor]]" won't work, "[[#myanchor]]" will.
  • All anchor names in a page should be unique.

Links to actions

To link to a specific action for the current page use [[{$FullName}?action=actionname|linkname]].


  • [[{$FullName}?action=edit|Edit]] for editing
  • [[{$FullName}?action=diff|differences]] for differences.

Links outside the wiki

Links to external sites (URLs)

Links to external sites simply begin with a prefix such as 'http:', 'ftp:', etc. Thus and [[]] both link to Google. As with the above, an author can specify the link text by using the vertical brace or arrow syntax, as in [[ | Google]] and [[Google ->]].

It is possible to set a "tooltip title" of the external link by adding it in quotes after the address:

The tooltip title of the link is "Home of PmWiki"
[["Home of PmWiki"|link]]


If the external link includes (parentheses), escape these using %28 for "(" and %29 for ")" :

[[ | link to "Wiki (disambiguation)" ]]

link to "Wiki (disambiguation)"

The recipe Cookbook:FixURL makes it easy to encode parentheses and other special characters in link addresses.

Links to intranet (local) files

Not all browsers will follow such links (some Internet Explorer versions reportedly follow them). You can link to a file system by including the prefix 'file:///'. So file:///S:\ProjPlan.mpp and [[Shared S drive->file:///S:\]] are both valid links. On a Windows file system you may want to use network locations (eg \\server1\rootdirectory\subdirectory) rather than drive letters which may not be consistent across all users. Not all browsers will follow such links.

See also Cookbook:DirList.

Link characteristics

Links as References

Links may also be specified as References, so the target appears as an anonymous numeric reference rather than a textual reference. The following markup is provided to produce sequential reference numbering within a PmWiki page:

Formatting the link as: [[ |#]] produces: [2] as the link.

Subsequent occurrence of the reference link format on the same page will be incremented automatically as per the following example: Entering [[ |#]] produces [3], [[#intermaps |#]] produces [4], and so on for further reference links.


Inter Map links are also supported (see Inter Map). In particular, the Path: InterMap entry can be used to create links using relative or absolute paths on the current site (e.g., Path:../../somedir/foo.html or Path:/dir/something.gif).

Links that open a new browser window

To have a link open in another window, use %newwin%...%%:

You can also specify that links should open in a new window via the %target=_blank%...%% attribute:

The following link %target=_blank% %%
will open in a new window.

The following link will open in a new window.

Links that are not followed by robots

Prefix a link with %rel=nofollow% to advise robots and link checkers not to follow it.

Links and CSS Classes

PmWiki automatically gives classes to several types of links. Among other things, this enables you to format each type differently.

Note: This may be an incomplete list.

A link to the current page. Useful in sidebars to show "you are here".
A link to another page within the wiki.
A link to a page outside the wiki.


Note: The default behavior of "+" above can be overridden to display the spaced title, rather than simply the title by adding the following to config.php:

## [[target |+]] title links
Markup('[[|+', '<[[|',
  "Keep(MakeLink(\$pagename, PSS('$1'),
                 PageVar(MakePageName(\$pagename,PSS('$1')), '\$Titlespaced')

How do I create a link that will open as a new window?

Use the %newwin% wikistyle, as in:

%newwin% %%

How do I create a link that will open a new window, and configure that new window?

This requires javascript. See Cookbook:PopupWindow.

How do I place a mailing address in a page?

Use the mailto: markup, as in one of the following:

* [[]]
* [[ | email me]]
* [[ subject | email me]]
  • myaddress [snail] example [period] com
  • myaddress [snail] example [period] com
  • email me -> mailto:myaddress [snail] example [period] com
  • email me -> mailto:myaddress [snail] example [period] com?subject=Some subject

The markup [[ Subject&body=Pre-set body | display text]] =] lets you specify more parameters like the message body and more recipients (may not work in all browsers and e-mail clients).

See also Cookbook:DeObMail for information on protecting email addresses from spammers.

How can I enable links to other protocols, such as nntp:, ssh:, xmpp:, etc?

See Cookbook:Add Url schemes How to get PmWiki to recognize additional URL schemes such as irc:, nntp:, etc.

How do I make a WikiWord link to an external page instead of a WikiPage?

Use link markup. There are two formats:

[[ | WikiWord]]
[[WikiWord ->]]

How do I find all of the pages that link to another page (i.e., backlinks)?

In the wiki search form, use link=Group.Page to find all pages linking to Group.Page.

Use the link= option of the (:pagelist:) directive, as in

(:pagelist link=SomePage list=all:)   -- show all links to SomePage
(:pagelist link={$FullName} list=all:)  -- show all links to the current page

What link schemes does PmWiki support?

See PmWiki:Link schemes Link schemes supported by PmWiki

How do I open external links in a new window or mark them with an icon?

See Cookbook:External links Configure external links to open in a new window, have a "tooltip title", or use other CSS classes

How can I use an image as a link?

Use [[Page| Attach:image.jpg ]] or [[ http://site | http://site/image.jpg ]] See Images#links

Why my browser does not follow local file:// links?

For security reasons, most browsers will only enable file:// links if the page containing the link is itself on the local drive. In other words, most browsers do not allow links to file:// from pages that were fetched using http:// such as in a PmWiki site. See also Cookbook:DirList for a workaround.


To place an image into a page, enter the address (url) of the image into the markup text. Any alternate text (used for tooltips and for browsers that do not display images) is placed in double quotes immediately following the image url. A caption can follow separated by a vertical bar (|), and may include simple formatting"Paper clips" | [- %newwin% [[ Wikipedia:Paper_clips | Paper clips ]] are ''fun'' to work with. -]
Paper clips
Paper clips are fun to work with.

Images can also be specified as uploaded files (i.e., Attach:image.jpeg) and using InterMap links. By default PmWiki supports the following image types:

  gif jpg jpeg png

(See also Uploads and Notes below for image files that lack extensions.)

To create a link to an image (like as opposed to displaying the image itself), use double brackets to mark the link, as in [[]] or [[Attach:image.jpeg]].

To have an image link to another location, use the image as the link text as in

[[ |"PmWiki"]]

or [[|Attach:Groupname./image.jpeg]].


A caption can be added to an image using a vertical bar and the caption text."Paper clips" | '''Figure 1'''
Paper clips
Figure 1

Normally, images are displayed "in line" with the surrounding text. Use %center% to center an image. Use %right% to right-align an image.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. %center%"Paper clips"%%

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. %right%

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Paper clips

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Floating images

To left or right-align an image with text wrapping around it, use the %lfloat% or %rfloat% wiki styles.

%lfloat text-align=center margin-top=5px margin-right=25px margin-bottom=5px margin-left=25px% | '''Rock on!'''
'''The image is left-aligned, with margins set; the caption is centered; the text wraps on the right side of the image.'''

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat. 

Rock on!

The image is left-aligned, with margins set; the caption is centered; the text wraps on the right side of the image.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

The [[<<]] markup breaks floating text, and the text continues at the bottom of the image.

'''The image is left-aligned, and the text wraps on the right side of the image. The text after the ''[@[[<<]]@]'' markup continues below the image.'''
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 

The image is left-aligned, and the text wraps on the right side of the image. The text after the [[<<]] markup continues below the image.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Use the %lframe% or %rframe% styles to float an image and place a frame around the image and its caption. The frame can be formatted via wikistyles:

%rframe% | '''Rock on!'''
'''The image is right-aligned, and the text wraps on the left side of the image.'''

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

%cframe width=100px bgcolor=lightblue border='3px solid red' padding=20px%

Example to show failure to fully apply width setting:-
%cframe width=50px bgcolor=lightblue border='3px solid red' padding=20px%

Rock on!

The image is right-aligned, and the text wraps on the left side of the image.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Example to show failure to fully apply width setting:-

Use %block rframe% to set off multiple images and caption text to be stacked vertically in a right-floating frame.

%block rframe width=300px%\\
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\\\\\
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.%%

Text subsequent to the defined block wraps to the left of the frame. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Text subsequent to the defined block wraps to the left of the frame. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Resizing images

To resize an image via wikistyles, use %width=50px% or %height=50px% in front of an image. The %thumb% wikistyle is a helpful shortcut for %width=100px%.

%width=50px% \
%height=50px% \

Note: Resizing an image via wikistyles only affects how it is displayed in a browser; it does not reduce the transfer size of the image itself - hence resizing via wikistyles is not an acceptable method of generating a page-full of images, or 'gallery'.

If you want a resized image within a link, you have to specify the size before the link as well as close it off with a %%.

%width=60%[[ |"PmWiki"]]%% \
%height=60%[[ |"PmWiki"]]%% \
PmWiki PmWiki

To open the link in new window, you place %newwin% before the size specification.

%newwin%[[ |"PmWiki"]]%%

Resized images using %thumb% can also be floated with frames, as well as made into links.

%lframe thumb% [[ |"Burst the bubble"]] | [-Bubble-]
%lframe thumb%"Clip the ticket" | [-Paper Clips-]
%lframe thumb% [[DocumentationIndex |"Visit the Documentation Index"]] | [[DocumentationIndex | [-Rock On-]]]
Burst the bubble
Clip the ticket
Paper Clips

Images as links

To use an image as a link specify an image instead of text in the link markup.

[[PmWiki/Links |"Information about wiki links"]]
Information about wiki links


  • An image file that lacks a correct extension can be displayed by addition of a "false" extension to the URL. For example, if the url is, add a fake query string on the end with the desired extension (e.g., If query strings are unsuitable, a fragment identifier should work, e.g.
  • Relative width is possible by using percentages.
%width=10pct% \
  • Flowing text is possible, like captions, within a frame
>>lframe width=130px<<
%thumb width=130% [[ |"Burst the bubble"]] | [--Long caption for an image like [[ | the bubble]]. This is just to show some text flowing within the frame.--]
Burst the bubble
Long caption for an image like the bubble. This is just to show some text flowing within the frame.

See also

  • Cookbook:Images - adding image galleries, automatic thumbnails, background images and more.


The images on this page were obtained from and are redistributed under a Creative Commons License.

Is it possible to link an image on PmWiki without using a fully qualified URL?

Yes. For images that are attachments, the general format is Attach:Groupname./image.gif. To link to an image that is on the same server, use Path:/path/to/image.gif.

Can I attach a client image file on PmWiki?

Yes, see Uploads .

How can I include a page from another group that contains an attached image?

Include the page in the normal way, ie (:include GroupName.Pagename:). In the page to be included (that contains the image) change Attach:filename.ext to Attach:{$Group}./filename.ext.

Why, if I put an image with rframe or rfloat and immediatly after that I open a new page section with ! the section title row is below the image instead of on the left side?

Because the CSS for headings such as ! contains an element clear:both which forces this behaviour. Redefine the CSS locally if you want to stop this happening, but I think the bottom border (that underlines the heading) would need further re-definition. I just use bolding for the title, and 4 dashes below ---- to separate a new section, and it saves the effort of fiddling with the core definitions.

Unlike the lframe and rframe directives, cframe does not fully honour the width setting. While the frame itself resizes to match the request, the enclosed image does not, and retains its original width. Effect is the same in IE and Fx. I've added an example beneath the standard example above.

Is it possible to disallow all images? I already disabled uploads but I also want to disallow external images from being shown on my wiki pages.

Yes, add to config.php:

$ImgExtPattern = "$^";

How can I make it so that when I place an image in a page, the block of text it is in is a <p> (paragraph) rather than a <div> (division)?

If you just want it to happen for a single image (instead of all), then try putting [==] at the beginning of the line, as in:


Having [==] at the beginning of a line forces whatever follows to be part of a paragraph.

Is there any way to use relative paths for images?

See Cookbook:RelativeLinks and $EnableLinkPageRelative.

Is there a way to attach a BMP and have it display rather than link?

Add to config.php the following line:
$ImgExtPattern = "\\.(?:gif|jpg|jpeg|png|bmp|GIF|JPG|JPEG|PNG|BMP)";
Note that BMP images are uncompressed and quite heavy. You may wish to convert them to PNG (lossless) or JPG (lossy) format, and thus reduce 5-20 times their filesizes.

Is there a way to have a table to the left or right of an image?

Yes, see TableAndImage.


This page provides a more complete list of some of the markup sequences available in PmWiki. Note that it's easy to create and edit pages without using any of the markups below, but if you ever need them, they're here.

To experiment with the rules, please edit the Wiki Sandbox.


To create paragraphs, simply enter text. Use a blank line to start a new paragraph.

Words on two lines in a row will wrap and fill as needed (the normal XHTML behavior). To turn off the automatic filling, use the (:linebreaks:) directive above the paragraph.

  • Use \ (single backslash) at the end of a line to join the current line to the next one.
  • Use \\ (two backslashes) at the end of a line to force a line break.
  • Use \\\ (three backslashes) at the end of a line to force 2 line breaks.
  • Use [[<<]] to force a line break that will clear floating elements.

Indented Paragraphs (Quotes)

Arrows (->) at the beginning of a paragraph can be used to produce an indented paragraph. More hyphens at the beginning (--->) produce larger indents.

->Four score and seven years ago our fathers placed upon this continent a new nation, conceived in liberty and dedicated to the proposition that all men are created equal.
Four score and seven years ago our fathers placed upon this continent a new nation, conceived in liberty and dedicated to the proposition that all men are created equal.

Inverted Arrows (-<) at the beginning of a paragraph can be used to produce a paragraph with a hanging indent. Adding hyphens at the beginning (---<) causes all the text to indent.

-<Four score and seven years ago our fathers placed upon this continent a new nation, conceived in liberty and dedicated to the proposition that all men are created equal. 
Four score and seven years ago our fathers placed upon this continent a new nation, conceived in liberty and dedicated to the proposition that all men are created equal.
--<Four score and seven years ago our fathers placed upon this continent a new nation, conceived in liberty and dedicated to the proposition that all men are created equal.  And that food would be good too.
Four score and seven years ago our fathers placed upon this continent a new nation, conceived in liberty and dedicated to the proposition that all men are created equal. And that food would be good too.

Blocks of text to which (:linebreaks:) has been applied can be indented by preceding the first line of the block with indention arrows (->) and aligning subsequent lines under the first. An unindented line stops the block indentation. See Cookbook:Markup Tricks for an example.

Bulleted and Numbered Lists

Bullet lists are made by placing asterisks at the beginning of the line. Numbered lists are made by placing number-signs (#) at the beginning of the line. More asterisks/number-signs increases the level of bullet:

* First-level list item
** Second-level list item
### Order this
#### And this (optional)
### Then this
** Another second-level item
* A first-level item: cooking
## Prepare the experiment
### Unwrap the pop-tart
### Insert the pop-tart into the toaster
## Begin cooking the pop tart
## Stand back
  • First-level list item
    • Second-level list item
      1. Order this
        1. And this (optional)
      2. Then this
    • Another second-level item
  • A first-level item: cooking
    1. Prepare the experiment
      1. Unwrap the pop-tart
      2. Insert the pop-tart into the toaster
    2. Begin cooking the pop tart
    3. Stand back
# A list is terminated
by the first line that is not a list.
# Also terminate a list using the escape sequence [@[==]@]
# Continue a list item by lining
  up the text with leading whitespace.
# Use a forced linebreak \\
  to force a newline in your list item.
  1. A list is terminated

by the first line that is not a list.

  1. Also terminate a list using the escape sequence [==]

  1. Continue a list item by lining up the text with leading whitespace.
  2. Use a forced linebreak
    to force a newline in your list item.
## Text between list items can cause numbering to restart
## %item value=3% this can be dealt with
  1. Text between list items can cause numbering to restart
  2. this can be dealt with

Also see: PmWiki:ListStyles, Cookbook:WikiStylesPlus.

Definition Lists

Powerful new* feature
When you define terms using this markup
PmWiki will recognize them as PageTextVariables
that you can use on any page or PageList.
* Added in PmWiki version 2.2.0

Definition lists are made by placing colons at the left margin (and between each term and definition):

:term:definition of term
definition of term

Whitespace Rules

Whitespace indentation in lists. Any line that begins with whitespace and aligns with a previous list item (whether bulleted, numbers or definitional) is considered to be "within" that list item. Text folds and wraps as normal, and the (:linebreaks:) directive is honored.

# First-level item\\
  Whitespace used to continue item on a new line
# Another first-level item
  # Whitespace combined with a single # to create a new item one level deeper
  1. First-level item
    Whitespace used to continue item on a new line
  2. Another first-level item
    1. Whitespace combined with a single # to create a new item one level deeper

Otherwise, lines that begin with whitespace are treated as preformatted text, using a monospace font and not generating linebreaks except where explicitly indicated in the markup. Note to administrators: Starting with version 2.2.0-beta41, this feature can be modified using $EnableWSPre. (Another way to create preformatted text blocks is by using the [@...@] markup.)

Horizontal Line

Four or more dashes (----) at the beginning of a line produce a horizontal line.

Emphasis and character formatting

  • Enclose text in doubled single-quotes (''text''), i.e., two apostrophes, for emphasis (usually italics)
  • Enclose text in tripled single-quotes ('''text'''), i.e. three apostrophes, for strong (usually bold)
  • Enclose text in five single-quotes ('''''text'''''), or triples within doubles (five apostrophes), for strong emphasis (usually bold italics)
  • Enclose text in doubled at-signs (@@text@@) for monospace text
  • Use [+large+] for large text, [++larger++] for larger, [-small-] for small text, and [--smaller--] for smaller.
  • Emphasis can be used multiple times within a line, but cannot span across markup line boundaries (i.e., you can't put a paragraph break in the middle of bold text).
  • '~italic~' and '*bold*' are available if enabled in config.php

Other styling

'+big+', '-small-', '^super^', '_sub_', 

{+insert or underscore+}, 

{-delete or strikethrough or strikeout-}

big, small, super, sub,

insert or underscore,

delete or strikethrough or strikeout

  • `WikiWord WikiWord neutralisation

See also Wiki Styles for advanced text formatting options.


  • Use words and phrases in double brackets (e.g., [[text formatting rules]]) to create links to other pages on this wiki.
  • On some PmWiki installations, capitalized words joined together (e.g., WikiWords) can also be used to make references to other pages without needing the double-brackets.
  • Precede URLs with "http:", "ftp:", "gopher:", "mailto:", or "news:" to create links automatically, as in
  • URLs ending with .gif, .jpg, or .png are displayed as images in the page
  • Links with arbitrary text can be created as either [[target | text]] or [[text -> target]]. Text can be an image URL, in which case the image becomes the link to the remote url or WikiWord.
  • Anchor targets within pages (#-links) can be created using [[#target]].

See Links for details.


Headings are made by placing an exclamation mark (!) at the left margin. More exclamation marks increase the level of heading. For example,

!! Level 2 Heading
!!! Level 3 Heading
!!!! Level 4 Heading
!!!!! Level 5 Heading

Level 2 Heading

Level 3 Heading

Level 4 Heading

Level 5 Heading

Note that level 1 heading is already used as page title (at least in the PmWiki skin), so you should start with level 2 headings to create well formed, search engine optimized web pages.

See Cookbook:Numbered Headers for numbered headings.

Escape sequence

Anything placed between [= and =] is not interpreted by PmWiki, but paragraphs are reformatted. This makes it possible to turn off special formatting interpretations and neutralise WikiWords that are not links (even easier is to use a tick ` in front, like `WikiWord).

For preformatted text blocks, use the [@...@] markup. It does neither reformat paragraphs nor process wiki markup:

Code goes here like [[PmWiki.PmWiki]]
'$CurrentTime $[by] $AuthorLink:  [=$ChangeSummary=]'; #just some code
Code goes here like [[PmWiki.PmWiki]]
'$CurrentTime $[by] $AuthorLink:  [=$ChangeSummary=]'; #just some code

The multiline [@...@] is a block markup, and in order to change the styling of these preformatted text blocks, you need to apply a "block" WikiStyle.

%block blue%[@ 
  The font color of 
  this text is blue
  The font color of 
  this text is blue

It is also useful to use [= =] within other wiki structures, as this enables the inclusion of new lines in text values. The example below shows how to include a multi-line value in a hidden form field.

(:input hidden message "[=Line1


(:comment Some information:) can be very kind to subsequent authors, especially around complicated bits of markup.

Special Characters

When creating pages it's common to use commercial trademarks, copyright, umlaut, and other non-keyboard symbols. therefore it's important that you have the means to input these special characters.

ISO Standard codes

PmWiki supports the HTML special character listings by the w3c. W3C Page of Special Character codes ISO standard.

Here are some samples:

&#188;  &frac14;
&#189;  &frac12;
&#174;  &#181;  &#168;

© ¼ ¼ ½ ½ ® µ ¨

&#198;  32&#176;  Un&#239;ted St&#228;tes  &#182;  &#165;Yen  PmWiki&#8482;

Æ 32° Unïted Stätes ¶ ¥Yen PmWiki

For a nice table with all available special characters, see List of Unicode characters at Wikipedia.

Other ways to do it:

Character Map

Find the "Character Map" utility in your computer's System Tools folder. Click the symbol you're interested in, and note the keystroke information at the bottom of the box. You execute these by holding "Alt" while keying the numbers on the numerical keypad of your keyboard (not the numbers across the top of the board).

&#169; = Alt+0169 = ©
&#174; = Alt+0174 = ®
&#176; = Alt+0176 = ° (degrees)


  • Use Word or another desktop application to create your text with the special characters that you want. Copy and paste the text to the wiki page you're editing or creating.
  • Find an instance of a special character in an online document; copy and paste the character to your wiki page: ©

There's a list of special characters at PmWiki:SpecialCharactersList. There's another illustration at PmWiki:Characters


Tables are defined by enclosing cells with '||'. A cell with leading and trailing spaces is centered; a cell with leading spaces is right-aligned; all other cells are left-aligned. An empty cell will cause the previous cell to span multiple columns. (There is currently no mechanism for spanning multiple rows.) A line beginning with '||' specifies the table attributes for subsequent tables. A '!' as the first character in a cell provides emphasis that can be used to provide headings.

||border=1 width=50%
||!Left   || Center || Right||
||A       ||!  a B   ||     C||
||        || single ||      ||
||        || multi span   ||||
 multi span

See Table Directives for advanced tables.

Can't find it here?

See Markup Master Index.

Markup Master Index

This page contains the most frequently used wiki markup, briefly. Follow the links in each section to learn more.


See Links

External links
[[ | link text]]
[[link text ->]]

Page links

[[page name]]
[[page (name)]]
[[PageName | link text]]
[[PageName | + ]] (titled link)
[[PageName | # ]] (anonymous numerical reference link)
[[link text -> PageName]]
[[#anchor]] (to create an anchor)
[[#anchor | link text]] (to refer to an anchor)
[[PageName#anchor | link text]] (to refer to an anchor in another page)

See also WikiWord on how to enable WikiWord links.

WikiGroup links

See Links and Categories

[[GroupName/]] or [[Group name/]]
[[GroupName/PageName]] or [[GroupName/page name]]
[[(GroupName.)page name]]
[[~Author Name]]
[[!Category Name]]

InterMap links

See InterMap


Email links
[[ | display text]]
[[display text ->]]

Upload links

See Uploads and Images

[[Attach:file.odt | alternative text ]]
[[Attach:file with spaces.pdf]]
[[Attach:Groupname./file with spaces.pdf]]


See Images and Uploads

Images as Images"alt text"
Attach:image.gif"My image"
Attach:Groupname./image.gif"image in another group"
Attach:Groupname.Pagename/image.gif"image on another page"
%lfloat% Attach:image.gif | Caption %% (could be %rfloat%, %center%, %rframe%, %lframe% )
%width=200px% Attach:image.gif %%
%thumb% Attach:image.gif %%

Images as links

[[PageName | Attach:image.gif"alt text"]]
[[ | Attach:image.gif"alt text"]]
%rframe thumb% [[Attach:image.gif | Attach:image.gif"alt text"]] | Caption

Start-of-line markup

See Text formatting rules


See List Styles, Wiki styles and Cookbook:Outline lists

* unordered list
** deeper list
# ordered list
# %item value=#% arbitrary start number
# %decimal%, %roman%, %ROMAN%, %alpha%, %ALPHA%


Q: start a question paragraph
A: start an answer paragraph


!! Heading
!!! Deeper heading

Paragraph blocks

-> indented text
-< hanging indent
<space> preformatted text
[@...@] preformatted block
---- (horizontal rule)
blank line is vertical space
\ at end of line joins next line
\\ at end of line produces a line break
\\\ at the end of a line produces a blank line, even within a list item
[[<<]] produces a line break that clears floating content

Division blocks

See Block markup, Wiki styles and Page directives

(:div class="" style="":)

Text markup

See Text formatting rules

Character format

'''''strong emphasis'''''
[-small-], [--smaller--]
[+big+], [++bigger++]
'-small-', '+big+'
'^superscript^', '_subscript_'
{+inserted+} (underscore)
{-deleted-} (strikethrough)
[@escaped code@]
[=escaped text=]

Posting markup

~~~ (author's signature)
~~~~ (author's signature and date)
(:encrypt phrase:) -- replaced with encrypted form of phrase


Plain rows and columns of text

See Tables

||table attributes
||!table caption!||
||left aligned || centered || right aligned||
||!column heading||
||spanned columns ||||||

Structured tables

See Table directives

(:table attr:)
(:cellnr attr:)
(:cell attr:)


Page directives

See Page directives

(:redirect PageName:)
(:nl:) insert newline in the *markup* only if one isn't present. The purpose of (:nl:) is to be able to write things like:
(:include Page1:)(:nl:)(:include Page2:)
which guarantees that the first line of Page2 is treated as a separate line from the last line of Page1, but without inadvertently generating a blank line between them.


See Page directives Group headers

(:noheader:), (:nofooter:)
(:noleft:), (:noright:)
(:nogroupheader:), (:nogroupfooter:)


See Page directives, Comment markup, Page variables

(:title text:)
(:description text:)
(:comment text:)
{Group/PageName$:Var} includes from (:name:text:)


See Include other pages, Page text variables

(:include PageName:)
(:include PageName#start#end lines=n paras=n:)
(:include Page1 Page2 Page3:)
{Group/PageName$:Var} includes from (:name:text:)

Conditional markup

See Conditional markup

(:if (!) cond param:)...(:ifend:)
(:if (!) cond param:)...(:else:)...(:ifend:)
(:if (!) cond param:)...(:elseif (!) cond param:)...(:ifend:)


See Page lists

(:searchbox label=label order=-time:)
(:searchresults incl -excl group=abc fmt=def:)
(:pagelist incl -excl group=abc fmt=def:)

Other directives

See Page directives

(:PageDirectives#markup:) [=...=]
(:markup class=horiz:)...(:markupend:)
(:markup caption='...':)...(:markupend:)


See Forms

(:input form method=get action=url enctype=multipart/form-data:)
(:input default name=xyz value="abc":)
(:input text name=first value="Bob" size=20:)
(:input textarea name=xyz [=value=] rows=2 cols=80:)
(:input submit name=post value="Go" accesskey=g:)
(:input reset:)
(:input hidden name=action value=edit:)
(:input radio name=xyz value="abc" checked=1:)
(:input checkbox name=xyz value="abc" checked=1:)
(:input password name=authpw:)
(:input file name=upload:)
(:input image name=xyz src="http:..." alt="Alt Text":)
(:input select name=xyz value="val1" label="Value 1":)
(:input select name=xyz value="val2" label="Value 2":)
(:input end:)

See also PmWiki Edit forms.

Wiki trails

See Wiki trails


Page variables

See Page variables, Page text variables, Page lists

(:name:description:) sets a page text variable


See Markup expressions

{(function args)}


PmWiki can be configured to allow authors to upload and store files and images (known as attaching them). These attachments may then be referenced from any page.

Note: PmWiki is distributed with uploads disabled by default. See Uploads Admin for information about how to enable and configure the upload feature.
Note2: Uploads can be configured site-wide, by-group, or by-page; see Uploads Admin for details. This determines whether all uploads go in one directory for the site, an individual directory for each group, or an individual directory for each page. The default is to organize uploads by group.

Attach: Syntax

To add or link to an attachment, an author edits a page to include the markup "Attach:" followed by a name of an attachment (e.g., "Attach:resume.pdf"). When the page is displayed, the Attach: markup becomes one of the following:

  • A link to the named attachment (if uploaded, ie already in the upload directory)
  • A link to a form whereby the author can specify a file to be uploaded and used as the new attachment (if not yet uploaded, ie not in the upload directory)
  • If the attachment is an image file with an extension such as .gif, .jpeg, or .png, it is displayed as an image.

The behaviour of links can be modified to

  • prevent an image attachment from displaying as an image, place it in double brackets (e.g., [[Attach:image.jpg]]).
  • have a link to an attachment appear without the "Attach:" at the beginning of the link, use [[(Attach:)file.ext]].

Attachments on other pages and groups

To link to an uploaded attachment (image or file) from another group, you simply refer the group itself (make sure "Groupname" has the dot in it).

Attach:Groupname./file_name.ext (note the dot after the groupname)

If PmWiki is configured with an individual directory per page use

Attach:Pagename/file_name.ext (Pagename is in the same WikiGroup)

Names with spaces

To link to a filename with spaces in it use the bracket link notation, eg

[[Attach:a filename with spaces.txt]]

"Embedding in the page" an image with spaces is not supported: just upload the images with names without spaces, and use the markup Attach:image.jpg.

The following workaround is possible, but is unsupported and not recommended:

[[#blank | Attach:image space.jpeg]]

International characters in file names

See UploadsAdmin and $UploadNameChars.

Listing Uploaded Files On A Page

To list files that have been uploaded, use the markup: (:attachlist:)

This will list attachments to the current group or page, depending whether attachments are organised per group or per page; each instance includes a link to the attachment for viewing or downloading. A list of attachments is also shown as part of the uploads page form.

Upload Form / Upload Replacement

One can go directly to the upload form by appending "?action=upload" to the URI for any page that has file uploads enabled by the Wiki Administrator. Replace a file by simply uploading a new version of the file with the same name.

  • Be sure to clear your browser cache after replacing an upload. Otherwise, it may appear that the original upload is still on the server.

If you put $EnableUploadVersions=1; in your local/config.php, the old versions of the same files are renamed and not removed.

Type and Size Restrictions

For security reasons, the upload feature is disabled when PmWiki is first installed. When enabled uploads are restricted as to the types and sizes of files that may be uploaded to the server (see UploadsAdmin). PmWiki's default configuration limits file sizes to 50 kilobytes and file extensions to common types such as ".gif", ".jpeg", ".doc", ".txt", and ".pdf".

In addition, the administrator can configure the system to require an upload password--see Passwords and PasswordsAdmin.

By default the upload allows the following extensions. Note that by default, no file extension is required.

'gif','jpg','jpeg','png','bmp','ico','wbmp','svg','xcf'        # images

'mp3','au','wav','ogg','flac',                                 # audio
'ogv','mp4','webm','mpg','mpeg','wmf','mov','qt','avi',        # video
'zip','7z','gz','tgz','rpm','hqx','sit',                       # archives
'doc','ppt','xls','mdb',                                       # MSOffice
'exe',                                                         # executables
'pdf','psd', 'ps','ai','eps',                                  # Adobe
'htm','html','css','fla','swf',                                # web stuff
'txt','rtf','tex','dvi',                                       # text files
'odt','ods','odp','odg',                                       # 
'epub','kml','kmz',''                                          # misc


At present uploaded files can only be deleted from the server by the wiki administrator. Any uploads-authorized user may over-write an existing file by uploading another of the same name and extension to the same location.

The administrator may remove an uploaded file by accessing the server via ftp (or via a control panel, if the host offers such a feature). The recipe Cookbook:Attachtable allows the deletion of the files from the wiki.

When I upload a file, how do I make the link look like "file.doc" instead of "Attach:file.doc Δ"?

Use parentheses, as in [[(Attach:)file.doc]]. There is also a configuration change that can eliminate the Attach: -- see Cookbook:AttachLinks.

Why can't I upload files of size more than 50kB to my newly installed PmWiki?

Out of the box PmWiki limits the size of files to be uploaded to 50kB. Add

$UploadMaxSize = 1000000; # limit upload file size to 1 megabyte
to your config.php to increase limit to 1MB (for example). See UploadsAdmin for how to further customize limits. Note that both PHP and webservers also place their own limits on the size of uploaded files.

Why does my upload exit unexpectedly with "Incomplete file received"?

You may be running out of space in a 'scratch' area, used either by PmWiki or by PHP. On *nix, check that you have sufficient free space in /tmp and /var/tmp.

How do I make it so that the upload link still allows one to make another upload (if someone wants to replace the old version of a file with a newer version, for example). Currently you only get the upload link when there is no file in the upload directory.

Use the Attach page action, and click on the delta symbol (Δ) shown against each of files listed. If you can't see the attach action either uploads are not enabled, you are not authorized to upload, or the attach action has been commented out or is missing. See also available actions.

How do I hide the "Attach:" for all attachments

See Cookbook:AttachLinks, note that this does not currently work for [[Attach:my file.ext]] .

How can I link a file that have a 4-letter file extension such like 'abc.pptx'?

See Cookbook:Upload Types

How can I prevent others from using the url's of my images on their site

See Cookbook:Prevent Hotlinking Prevent hotlinking of uploaded files


Table basics

PmWiki has two types of table markup; the markup described in this page is useful for creating simple tables with lots of small cells, while table directive markups help with larger scale tables. For more possibilities with table formatting see Cookbook:Rowspan in simple tables and Cookbook:Formatting tables.

Tables are created via use of double pipe characters: ||. Lines beginning with this markup denote rows in a table or a formatting line. Within table row lines the double-pipe is used to delimit cells. In the examples below a border is added for illustration (the default is no border).

The first line in the markup contains formatting commands for the table. It only has double pipe characters at the start of the line.

Basic table
|| border=1
|| cell 1 || cell 2 || cell 3 ||
|| cell 1 || cell 2 ||
cell 1cell 2cell 3
cell 1cell 2

Header cells can be created by placing ! as the first character of a cell. Note that these are table headers, not headings, so it doesn't extend to !!, !!!, etc.

Table headers
|| border=1
||! cell 1 ||! cell 2 ||! cell 3 ||
|| cell 1  ||  cell 2 ||  cell 3 ||
cell 1cell 2cell 3
cell 1cell 2cell 3

A table can have a caption, indicated by ||!caption!||. Any caption must appear prior to other rows of the table.

Table caption
|| border=1
||! A special table !||
||! cell 1 ||! cell 2 ||! cell 3 ||
|| cell 1  ||  cell 2 ||  cell 3 ||
A special table
cell 1cell 2cell 3
cell 1cell 2cell 3

Formatting cell contents

Cell contents may be aligned left, centered, or aligned right.

  • To left-align contents, place the cell contents next to the leading ||.
  • To center contents, add a space before and after the cell contents.
  • To right-align contents, place a space before the cell contents and leave the cell contents next to the trailing ||.
Cell alignments
|| border=1 width=100%
||!cell 1      ||! cell 2  ||!       cell 3||
||left-aligned || centered || right-aligned||
cell 1cell 2cell 3
Default cell alignments
|| border=1 width=100%
||!cell default||!cell left ||
||default-aligned||left-aligned ||
cell defaultcell left

Note that header and detail cells have different default alignments.

To get a cell to span multiple columns, follow the cell with empty cells. (At present there is no markup for spanning rows.)

Column spanning
|| border=1 width=100%
|| |||| right column ||
|| || middle column ||||
|| left column ||||||
|| left column || middle column || right column ||
 right column
 middle column
left column
left columnmiddle columnright column

Table attributes

Any line that begins with || but doesn't have a closing || sets the table attributes for any tables that follow. These attributes can control the size and position of the table, borders, background color, and cell spacing. (In fact these are just standard HTML attributes that are placed in the <table> tag.)

Use the width= attribute to set a table's width, using either a percentage value, an absolute size, or *.

Table width
|| border=1 width=100% 
|| cell 1 || cell 2 || cell 3 ||
|| c1 || cellcellcellcell2 || cell 3 ||
cell 1cell 2cell 3
c1cellcellcellcell2cell 3

The border= attribute sets the size of a table's borders.

Bordered table
|| border=10 width=70%
||!cell 1      ||! cell 2  ||!       cell 3||
||left-aligned || centered || right-aligned||
cell 1cell 2cell 3
Borderless table
|| border=0 width=70%
||!cell 1      ||! cell 2  ||!       cell 3||
||left-aligned || centered || right-aligned||
cell 1cell 2cell 3

Use align=center, align=left, and align=right to center, left, or right align a table. Note that align=left and align=right create a floating table, such that text wraps around the table.

Table alignment: center
|| border=1 align=center width=50%
||!cell 1      ||! cell 2  ||!       cell 3||
||left-aligned || centered || right-aligned||
Notice how text does not wrap with a table using "align=center".
cell 1cell 2cell 3

Notice how text does not wrap with a table using "align=center".

Table alignment: left
|| border=1 align=left width=50%
||!cell 1      ||! cell 2  ||!       cell 3||
||left-aligned || centered || right-aligned||
Notice how text wraps to the right of a table using "align=left".
cell 1cell 2cell 3

Notice how text wraps to the right of a table using "align=left".

Table alignment: right
|| border=1 align=right width=50%
||!cell 1      ||! cell 2  ||!       cell 3||
||left-aligned || centered || right-aligned||
Notice how text wraps to the left of a table using "align=right".
cell 1cell 2cell 3

Notice how text wraps to the left of a table using "align=right".

Note: to get a table to align left (but not "float left") requires CSS, as in


The bgcolor= attribute sets the background color for a table. At present there is no way to specify the color of individual rows or cells in this type of table (but see Cookbook:FormattingTables).

|| border=1 align=center bgcolor=yellow width=70%
||!cell 1    ||! cell 2 ||!     cell 3||
||left-align || center  || right-align||
cell 1cell 2cell 3

How do I create a basic table?

Tables are created via use of the double pipe character: ||. Lines beginning with this markup denote rows in a table; within such lines the double-pipe is used to delimit cells. In the examples below a border is added for illustration (the default is no border).

Basic table
|| border=1 rules=rows frame=hsides
|| cell 1 || cell 2 || cell 3 ||
|| cell 1 || cell 2 || cell 3 ||
cell 1cell 2cell 3
cell 1cell 2cell 3

How do I create cell headers?

Header cells can be created by placing ! as the first character of a cell. Note that these are table headers, not headings, so it doesn't extend to !!, !!!, etc.

Table headers
|| border=1 rules=cols frame=vsides
||! cell 1 ||! cell 2 ||! cell 3 ||
|| cell 1  ||  cell 2 ||  cell 3 ||
cell 1cell 2cell 3
cell 1cell 2cell 3

How do I obtain a table with thin lines and more distance to the content?

"Thin lines" is tricky and browser dependent, but the following works for Firefox and IE (Nov. 2009):

Thin lines and cell padding
||border="1" style="border-collapse:collapse" cellpadding="5" width=66%
||!Header ||! Header || '''Header'''||
||cells   ||  with   ||      padding||
||        ||         ||             ||

How do I create an advanced table?

See table directives

My tables are by default centered. When I try to use '||align=left' they don't align left as expected.

Use ||style="margin-left:0px;" instead.

How can I specify the width of columns?

You can define the widths via custom styles, see Cookbook:FormattingTables and $TableCellAttrFmt. Add in config.php : $TableCellAttrFmt = 'class=col$TableCellCount';

And add in pub/css/local.css :
table.column td.col1 { width: 120px; }
table.column td.col3 { width: 40px; }

How can I display a double pipe "||" in cell text using basic table markup?

Escape it with [=||=] to display || unchanged.

How to I apply styles to the elements of the table, like an ID to the table row, or a class/style to the TD?

See $WikiStyleApply.

Table directives

There are six directives for table processing. All must be at the beginning of a line to have any effect.

(:table [attr...]:)

Generates a new HTML <table> tag with the attributes provided in attr.... Closes the previous table, if any. Valid attributes and values are:

  • border (a positive integer)
  • bordercolor (a color name or hex number; doesn't display in all browsers)
  • cellspacing (a positive integer indicating the space between cells)
  • cellpadding (a positive integer indicating the interior border of a cell)
  • width (a positive integer or percent)
  • bgcolor (a color name or hex number)
  • align (left, center or right)
  • summary (does not display; used primarily to help visually disabled people navigate)

(:cellnr [attr...]:), (:cell [attr...]:), (:headnr [attr...]:), (:head [attr...]:)

  • The (:head:) directive opens a new "header cell" of the table (creates <th> tag in HTML).
  • The (:cell:) directive opens a new "regular cell" of the table (creates <td> tag in HTML).
  • The directives (:headnr:) and (:cellnr:) open a new cell on a new row in the table.

These directives close any previous cell and/or row. Note, the (:head:) and (:headnr:) directives exist from PmWiki version 2.2.11 or newer.

Valid attributes and values are:

  • align (left, center or right)
  • valign (top, middle or bottom) * default is "top", see note below
  • colspan (a positive integer)
  • rowspan (a positive integer)
  • bgcolor (a color name or hex number)
  • width (a positive integer or percent)
  • class (a CSS class of the cell)
  • style (custom CSS styles of the cell)


Closes the previous table cell and closes off any table. Generates </th>, </td>, </tr>, and </table> tags as needed.

* valign attribute

If not already set, PMWiki will automatically include the attribute valign='top' with all (:cell[nr]:) and (:head[nr]:). Pm said "Table Directives were created for layout purposes and in that case it makes the most sense for each cell (column) to have its content at the top of the row. The attribute is placed in each cell and not in the row because certain browsers didn't recognize valign='top' in the row tag.


For the table, cell, and cellnr tags the author can specify any attributes that would be valid in the HTML <table> or <td> tags. Thus you can specify rowspan, colspan, etc. arguments to build arbitrary tables. However, it's not possible to nest a (:table:) inside of a (:cell:) or (:cellnr:) -- the next paragraph explains why.

Many are likely to ask why we didn't just use the standard HTML table markup (<table>, <tr>, <td>, <th>) instead of creating a new markup, and allowing nested tables as a result. There are two answers: first, the HTML table markup is very ugly for naive authors (see PmWiki.Audiences and PmWikiPhilosophy #2), and second, it'd be very easy for authors to create tables that are incorrect HTML and that display incorrectly (or not at all) on some browsers. Even seasoned web professionals sometimes get the table markup wrong, so it's a bit unrealistic to expect the average author to always get it right, or to be able to read arbitrary HTML table markup that someone else has created.

Common comment: Surely, the average or naive author would not be writing HTML directly, but using a tool, such as FrontPage, or even MSWord, to generate the HTML. This would be a lot simpler than learning even the simplest PmWiki markups.
Pm's Response: And once the HTML has been generated and posted, how is someone else going to edit or modify the table if they don't have the original FrontPage or MSWord file used to create it? Remember that we're talking about collaborative authoring. The HTML that those packages generate is among the hardest to read and edit of all!

It's difficult to write the code needed to make PmWiki understand and fix arbitrary table markup, so PmWiki uses the simplified version above. Still, this version is able to handle most table requirements (with the possible exception of nested tables).

And, this is not to say that nested HTML tables are impossible in PmWiki --they just can't be easily created by wiki authors using the default wiki markup. A site administrator can of course create header/footer HTML code and other local customizations that make use of nested tables.

Example 1. A table using table directive markup.

"&nbsp;" is a non-breaking space in html. Place it in a cell if a cell is to be empty or the border of the cell will not be drawn properly.

(:table border=1 cellpadding=5 cellspacing=0:)
(:head:) a1
(:cell:) b1
(:cell:) c1
(:cell:) d1
(:headnr:) a2
(:cell:) b2
(:cell:) c2
(:cell:) &nbsp;
a1 b1 c1 d1
a2 b2 c2  

In HTML, this is the same as

<table border='1' cellpadding='5' cellspacing='0'>

Floating Table with bulleted navigation list

What if you wanted to create a nice little table like a table of contents in a page like this? In this example, the table is floating right and contains some links in a bulleted list. This is a nice demonstration of how it's possible to build a little table of contents in the page, which might navigate to other pages just within the same wiki group. Note that having a bulleted list won't work in a ordinary table - it only works inside an table created with table directives such as the example code used here.

(:table border=1 width=30% align=right bgcolor=#cccc99 cellspacing=0 :)
'''Navigation Links'''
*[[Table directives]]

Navigation Links

(:table border=1 width=30% align=right bgcolor=#cccc99 cellspacing=0 :)
(:cellnr colspan=2 align=center:)
'''Navigation Links'''
(:cellnr align=center:)
(:cell align=center:)
[[Table directives]]

Navigation Links


Table directives

Looking at the markup here, notice that we have used a #cccc99 hex color for the table background. Also, the (:cellnr:) markup creates a new row, a new cell and closes the row at the end.

You could take this concept a little further: since you might want each page in the group to contain the same table of contents, you can make ONE table like the above and put it in its own page. Then use an include on any of your pages and bring in the table. The float (align) property will be honored in each page where it's included.

Can I define table headers using the table directive markup?

Yes, with PmWiki version 2.2.11 or newer. See also Cookbook:AdvancedTableDirectives.

Is it possible to do nested tables?

Yes, if you nest simple tables inside advanced tables. See also Cookbook:AdvancedTableDirectives.

Is it possible to add background images to tables and table cells?

Yes, see Cookbook:BackgroundImages.

Is it possible to apply styles to the elements of the table, like an ID to the table row, or a class/style to the TD?

Yes, see $WikiStyleApply.

Is it possible to automatically generate columns or rows in tables, i.e. without having to do a lot of counting?

Yes, this is possible with the Cookbook:CreateColumns recipe - it allows you to specify a certain number of columns, and/or to specify a certain number of items per column. Plus, someone has provided some similar markup on the TableDirectives-Talk page.


WikiStyle basics

WikiStyles allow authors to modify the color and other styling attributes of the contents of a page. A WikiStyle is written using percent-signs, as in %red% or %bgcolor=lightblue%.

WikiStyle attributes

The style attributes recognized within a WikiStyle specification are:

------------ CSS ------------- --HTML--
 Special: define, apply

The attributes in the first two columns correspond to the cascading style sheet (CSS) properties of the same name. The attributes in the last column apply only to specific items:

  • class= and id= assign a CSS class or identifier to an HTML element
  • target=name opens links that follow in a browser window called "name"
  • rel=name in a link identifies the relationship of a target page
  • accesskey=x uses 'x' as a shortcut key for the link that follows
  • value=9 sets the number of the current ordered list item
    • The width and height attributes have asterisks because they are handled specially for <img .../> tags. If used by themselves (i.e., without anything providing an "apply=" parameter to the WikiStyle), then they set the 'width=' and 'height=' attributes of any <img ... /> tags that follow. Otherwise, they set the 'width:' and 'height:' properties of the element being styled.
    1. margin, padding, and border can be suffixed by -left, -right, -top, and -bottom

WikiStyles versus CSS styles

WikiStyles, as written in the wiki page, are not exactly CSS styles or CSS classes. WikiStyles allow authors to use both pre-defined by the administrator CSS classes, and to define new combinations of styles, without any need to edit/update local CSS files on the server.

Note that PmWiki allows advanced authors to use of class= and style= in tables and division blocks, but these are raw HTML attributes, and not WikiStyles, knowledge of CSS is required to use them.

Text color and font

The most basic use of WikiStyles is to change text attributes such as color, background color, and font. PmWiki defines several WikiStyles for changing the text color to %black%, %white%, %red%, %yellow%, %blue%, %gray%, %silver%, %maroon%, %green%, %navy%, and %purple%.

The basket contains %red% apples, %blue% blueberries, %purple% eggplant, %green% limes, %% and more.

The basket contains apples, blueberries, eggplant, limes, and more.

For colors other than the predefined colors, use the %color=...% WikiStyle. (Note: RGB colors (#rrggbb) should always be specified with lowercase letters to avoid WikiWord conflicts.)

I'd like to have some %color=#ff7f00% tangerines%%,  too!

I'd like to have some tangerines, too!

To change the background color, use %bgcolor=...% as a WikiStyle:

This sentence contains %bgcolor=green yellow% yellow text on a green background.

This sentence contains yellow text on a green background.

See WikiStyle Colors for more color help.

Text justification

WikiStyles are used to control the text justification

%center% This text is centered. 

%right% Right justified.

This text is centered.

Right justified.

and to create floating text:

%rfloat% This text floats to the right

%rframe% floats to the right with a frame

Lorem ipsum dolor sit amet, consectetuer sadipscing elitr

This text floats to the right

floats to the right with a frame

Lorem ipsum dolor sit amet, consectetuer sadipscing elitr


WikiStyles can also specify a scope; with no scope, the style is applied to any text that follows up to the next WikiStyle specification or the end of the paragraph, whichever comes first. The apply= attribute and its shortcuts allow to change the scope as follows:

apply attributeshortcutstyle applies to...
%apply=img ...%-all images that follow until another style applied
%apply=p ...%%p ...%the current paragraph
%apply=pre ...%-the current preformatted text
%apply=list ...%%list ...%the current list
%apply=item ...%%item ...%the current list item
%apply=div ...%-the current div
%apply=block ...%%block ...%to the current block, whether it's a paragraph, list, list item, heading, or division.

Thus, %p color=blue% is the same as %apply=p color=blue%, and %list ROMAN% is the same as %apply=list list-style=upper-roman%.

Some predefined style shortcuts also make use of apply, thus %right% is a shortcut for %text-align=right apply=block%.

Example: Apply a style to a paragraph:

%p bgcolor=#ffeeee% The WikiStyle specification at the beginning of this line applies to the entire paragraph, even if there are %blue% other WikiStyle specifications %% in the middle of the paragraph.

The WikiStyle specification at the beginning of this line applies to the entire paragraph, even if there are other WikiStyle specifications in the middle of the paragraph.

Caveat: An applied WikiStyle will only take effect if it's on the line that starts the thing it's supposed to modify. In other words, a WikiStyle in the third markup line of a paragraph can't change the attributes of the paragraph:

after the first line of the paragraph,
we try to %apply=p color=blue% change color.
This does't work because the style comes after the first line of the paragraph.

after the first line of the paragraph, we try to change color. This does't work because the style comes after the first line of the paragraph.

However, this %apply=p color=red% paragraph
''will'' be in red because its block style does
occur in the first line of its text.

However, this paragraph will be in red because its block style does occur in the first line of its text.

* Here's a list item
* %list red% Oops, too late to affect the list!
  • Here's a list item
  • Oops, too late to affect the list!

Larger blocks

The >>WikiStyle<< block can be used to apply a WikiStyle to a large block of items. The style is applied until the next >><< is encountered.

>>blue font-style:italic bgcolor=#ffffcc<<
Everything after the above line is styled with blue italic text,

This includes
    preformatted %red%text%%
* lists
-> indented items

Everything after the above line is styled with blue italic text,

This includes

    preformatted text
  • lists
indented items

Note, the (:div style="..." class="...":) directive does not work the same way as >>WikiStyke<<, it can only contain the regular HTML style and class attributes.

HTML "class" and "style" attributes for tables and divisions

WikiStyles are only the commands between %...% percent signs.

Tables, table directives and (:div:) division blocks allow advanced authors to incorporate the HTML/CSS attributes class= and style=. Note that these attributes are not WikiStyles, knowledge of CSS is required to use them.

(:table style="font-style:italic; color:green; border:1px solid blue; background-color:#ffffcc":)
Everything after the above line is styled with green italic text,

This includes
    preformatted text
* lists
-> indented items

Everything after the above line is styled with green italic text,

This includes

    preformatted text
  • lists
indented items

Note, the (:div style="..." class="...":) directive does not work the same way as >>style<<, as mentioned above, it can only contain the HTML style and class attributes.

Custom style shortcuts

The define= attribute can be used to assign a shorthand name to any WikiStyle specification. This shorthand name can then be reused in later WikiStyle specifications.

%define=box block bgcolor=#ddddff border="2px dotted blue"%

%box% [@some sort of text@]

%box font-weight=bold color=green% [@some sort of text@]

some sort of text

some sort of text

Tip: It's often a good idea to put common style definitions into Group Header pages so that they can be shared among multiple pages in a group. Or, the wiki administrator can predefine styles site-wide as a local customization (see Custom WikiStyles).
Tip: Use custom style definitions to associate meanings with text instead of just colors. For example, if warnings are to be displayed as green text, set %define=warn green% and then use %warn% instead of %green% in the document. Then, if you later decide that warnings should be styled differently, it's much easier to change the (one) definition than many occurrences of %green% in the text.
Tip: Any undefined WikiStyle is automatically treated as a request for a class, thus %pre% is the same as saying %class=pre%.

Predefined style shortcuts

PmWiki defines a number of style shortcuts.

  • Text colors: black, white, red, yellow, blue, gray, silver, maroon, green, navy, purple (shortcut for %color=...%)
  • Justification: %center% and %right%
  • Images and boxes
    • Floating left or right: %rfloat% and %lfloat%
    • Framed items: %frame%, %rframe%, and %lframe%
    • Thumbnail sizing: %thumb%
  • Open link in new window: %newwin% (shortcut for %target=_blank%)
  • Comments: %comment% (shortcut for %display=none%)
  • Ordered lists: %decimal%, %roman%, %ROMAN%, %alpha%, %ALPHA% (see also Cookbook:OutlineLists)

Enabling Styles

Styles not listed above can be enabled by a PmWiki Administrator by modifying the local/config.php file. For instance to enable the "line-height" style attribute add the line

$WikiStyleCSS[] = 'line-height';

to the local/config.php file.

Defining scope for other HTML elements

You can add additional HTML elements to $WikiStyleApply to apply WikiStyles to other HTML elements. For example to allow styling on table rows, or anchor tags.

To apply styling to anchor tags, place in config.php:

$WikiStyleApply['link'] = 'a';

Then, you can apply a class or style to an anchor link:

%apply=link red%[[PmWiki.WikiStyles | test link]]

test link

Or, to apply an ID attribute to a table row TR, place in config.php:

$WikiStyleApply['row'] = 'tr';

Then, in an advanced table, you can have:

(:cellnr:) %apply=row id=myid bgcolor=pink% cell content

And also in a simple table:

|| border=1
|| %apply=row id=myrowid% 1 || 2 || 3 || 4 ||

Note, the %apply=row...% WikiStyle should be on the same line as (:cellnr:).


WikiStyle Examples contains a number of examples of ways to use WikiStyles in pages.

Known Issues

  • Percents in style definitions (like: %block width=50% %) require the use of "pct" instead of "%". PmWiki will convert the "pct" into "%" so that it becomes valid CSS.
  • If you specify multiple values for an attribute, like border="2px solid blue" make sure you place the values in quotes.
  • Be sure to use lowercase letters for red-green-blue hex colors, %color=#aa3333% will work, %color=#AA3333% may not.

See Also


See also Wiki Styles Plus and Wiki style colors.

PmWiki uses WikiStyles for styling text with color and other attributes. PmWiki 2.0 introduced the ability to control the styling further and to even place styles on blocks.

A style is specified within a pair of %-signs and styles the text that follows, as in:

This text is %color=red% red, %color=blue% blue, %% and normal (black).

This text is red, blue, and normal (black).

There are a wide number of available style properties, borrowed primarily from HTML and CSS. In addition, an author can define a style "shortcut" by using the define= property. For example, to define a style of %red%, one can use:

%define=mystyle color=red%
Here is some %mystyle% red text created using a style shortcut.

Here is some red text created using a style shortcut.

Shortcuts can be combined with other styles, including other shortcuts:

%define=lovelyred color=red%
%define=likegrapefruit bgcolor=yellow%

%red% This text is red, %red bgcolor=#ccc% red on a grey background, and %lovelyred likegrapefruit% red on a yellow background.  

This text is red, red on a grey background, and red on a yellow background.

So far, this is all basically the same as what was available in PmWiki 1.0. PmWiki 2.0 includes the capability to style blocks, by using the apply= style property. Specifying apply=block in a WikiStyle will cause that style to be applied to the entire block, instead of just the text that follows:

This entire block %apply=block bgcolor=yellow% has a yellow background, even though the `WikiStyle appears in the middle of the line.  %bgcolor=pink% Other inline (non-block) WikiStyles can appear in the middle of the line,%% as before.

This entire block has a yellow background, even though the WikiStyle appears in the middle of the line. Other inline (non-block) WikiStyles can appear in the middle of the line, as before.

This means it's now possible to do right-aligned and centered text:

%block text-align=right% The text of this paragraph is right-aligned. 

%block text-align=center% The text of this paragraph is centered.  

The text of this paragraph is right-aligned.

The text of this paragraph is centered.

In fact, PmWiki predefines %right% and %center% style shortcuts so that you can do this more simply:

%right% This is right-aligned.

%center% This is centered.

This is right-aligned.

This is centered.

Authors can define their own custom styles:

%define=Pm block bgcolor=#fdf%
%define=goofy center bgcolor=#dfd border='3px dotted green'%
%define=rediguana right bgcolor=#ffffcc border='1px dotted red' padding=5px%
%define=strike text-decoration=line-through%

%Pm% Any text that is on a light purple background is a comment from "Pm".

%goofy% Here's some text from Goofy.

%rediguana% bla bla by rediguana!

%goofy%Hello, I am %strike%upset%% %strike%disheartened%% happy to meet you.

Any text that is on a light purple background is a comment from "Pm".

Here's some text from Goofy.

bla bla by rediguana!

Hello, I am upset disheartened happy to meet you.

Styles can be applied to almost any kind of block:

* %block bgcolor=yellow% Here is a list item
* Here's another list item

* Here's more of a list

# A new list
  • Here is a list item
  • Here's another list item
  • Here's more of a list
  1. A new list

In particular, this means that outlines are now possible using the predefined %ROMAN%, %roman%, %ALPHA%, and %alpha% list-block styles. The style has to be specified on the first item in the list (and we may develop an alternate syntax for this sort of ordered list):

# %ROMAN% Top level
## %ALPHA% second-level
## second-level
## second-level
### third-level
### third-level
## second-level
### third-level
#### %alpha% fourth-level
##### %roman% fifth-level
##### fifth-level
#### fourth-level
# top-level
# top-level
  1. Top level
    1. second-level
    2. second-level
    3. second-level
      1. third-level
      2. third-level
    4. second-level
      1. third-level
        1. fourth-level
          1. fifth-level
          2. fifth-level
        2. fourth-level
  2. top-level
  3. top-level

Wiki styles can be combined with CSS stylesheets to do this automatically -- see Cookbook:OutlineLists.

Q & A

How do I get a block of preformatted text?

Use something similar to this (assuming you want markup within the block to be interpreted as wiki markup and URIs? to be recognized).

This block of text is ''preformatted'', see all   the   white-space
and   linebreaks
are preserved. Links such as [[wiki styles]] etc still work.

This block of text is preformatted, see all the white-space and linebreaks are preserved. Links such as wiki styles etc still work.

How do I get a block of preformatted text with a colored background and a border?

Use something similar to this (note that wiki markup etc is not recognized within the block):

%block bgcolor=#f0f9ff border='1px solid gray' padding=5px%[@
ip access-list extended example-acl
remark ** This is an example acl **
deny ip any host
permit ip any any
ip access-list extended example-acl
remark ** This is an example acl **
deny ip any host
permit ip any any

How do I get a block of text (including wiki markup) with a colored background and a border?

>>teal background-color:silver border:'medium dotted green'<<
Hello world
* bullet
# number

Hello world

  • bullet
  1. number

How do I get a block of text (including wiki markup) with a border that is indented on the left and does NOT extend all the way to right? I'm not interested in having later text to the right as would occur with lfloat...

You can use the indent width=50pct wikistyle.

Before indention...
>>frame indent width=50pct<<
Hello world
* bullet
# number
... after indention!

Before indention...

Hello world

  • bullet
  1. number

... after indention!


Access keys (See also Wikipedia:access keys) are keyboard shortcuts for tasks that would otherwise require a mouse click. They are part of markup that may exist on any webpage. On PmWiki steps have been taken to make it easier to use access keys throughout a site, and to make it possible to adjust key assignments to accommodate different languages and preferences.

Using access keys in different operating systems and browsers

Access keys require you to hold down two or more keys.

  • On Windows with Internet Explorer, press ALT + the access key.
  • With Firefox, press SHIFT + ALT + the access key.
  • On a Macintosh with Firefox, Omniweb, Internet Explorer, press Ctrl + the access key.
    • With Safari (Version 4.0.2) press Ctrl + Option + the access key.
  • With Opera press Shift+Esc to enter (or exit) access-key mode.
  • With Konqueror, press Ctrl to enter (or exit) access-key mode.
  • With Chrome, press SHIFT + ALT + the access key

Exceptions exist for specific browsers, and specific versions. For example,

  • Internet Explorer requires that the Enter key be pressed at the end of the sequence for versions 5 and up under Windows, but not under Macintosh (where access keys were not supported until after version 4.5).
  • Firefox versions 1.5 and earlier simply use Alt, while Firefox version 2.0 uses Shift+Alt.

Note, in cases of conflicts between the keyboard shortcuts assigned by browsers and access keys assigned by links and other markup on webpages, many browsers, including Mozilla, Netscape and Internet Explorer, allow access keys to override the browser defaults and require a different sequence to continue using overridden browser assignments (typically, by pressing and releasing the Alt key, instead of holding it down).

Access key assignments in this PmWiki installation

The following is a list of the currently defined access keys for built-in actions. Remember that the letters identified below must be used together with the combination listed above (depending on your operating system and browser). Note that some actions do not have a corresponding access key by default.

Key NameKey ValueFunction
ak_view view
ak_attach attach
ak_print print
ak_backlinks backlinks
ak_logout logout
ak_recentchangescrecent changes
ak_savessave or publish page
ak_saveeditusave and keep editing
ak_savedraftdsave draft
ak_previewppreview page
ak_textedit,jump to edit textarea
ak_em emphasize text
ak_strong strong text

Note: If the 'Key Value' is the same as the 'Key Name', the access key is currently undefined.

When can these access keys be used

  • Access keys ak_view, ak_edit, ak_history, ak_attach, ak_print, ak_backlinks, ak_logout and ak_recentchanges can be used all the time
  • Access keys ak_save, ak_saveedit, ak_savedraft, ak_preview, ak_textedit can only be used in edit mode

Following table explains which button is activated by which access key. Note that the Cancel button has no access key.

Standard Edit modeDraft Edit modeUsed Access Key
 Save draftak_savedraft
Save and editSave draft and editak_saveedit
  • Access keys ak_em and ak_strong work only in edit mode and when the GUIbuttons? are enabled in local/config.php.

admins (intermediate)

Customizing access keys

PmWiki uses the same "phrase translation" methods for access key mappings as it does for internationalization. This makes it possible for administrators, skins, language translators, and visitors to all influence the way that specific keys are mapped to actions.

See SitePreferences and Site.Preferences for more information and a template.

Note that some skins (e.g., Lean) don't use the translation mechanism. In this case one must edit the template file itself in order to change the access keys.

By convention, the translation phrases for all of the access key actions start with the characters "ak_", so that the page variable "$[ak_edit]" is replaced by the access key for editing as defined by the current preferences, language, skin default, or site default.

Implementation of access keys

Access keys are implemented in html as optional parameters that can be added to links and many other types of markup.

Example: <a href="" accesskey="x">Example</a> would create a link to that could be triggered by clicking on the linked word "example" or using the access key Akey+x. That same action key link could be created in PmWiki markup by typing %accesskey="x"%[[|Example]]%%, like this: Example. Try it and see if it works. Note that this AKey?+x access key only works this way on this page, because it is simply a shortcut for accessing the link that exists only on this page.

The list of access key assignments in default PmWiki installations generally work throughout a site because links have been created in PmWiki skins and editing screens that incorporate access key parameters using the access key translation phrases. One location where those links can be viewed is Site.PageActions. That page contains the links that the default PmWiki skin, and many other skins, use to generate links such as "View" "Edit" and "History" that appear on most pages (other than editing screens). Each of the links in that page also has an %accesskey=$[ak_xxx]% declaration in front of it, which enables a specific access key for that link.

How can I change the keyboard shortcuts for editing and saving a page?

See Customizing access keys.


PmWiki uses a number of directives to specify page titles, descriptions, page keywords, and control the display of various components. Keywords are not case sensitive.

Shows a list of attachments of the current group or page, depending on whether attachments are organised per group or per page. The attachlist is displayed at the foot of the uploads page form.
The parameter to (:attachlist:) always resolves to a pagename. The directive then displays all of the attachments currently available for that page.
(:attachlist NAME:) shows a list of attachments of the group or page NAME.
(:attachlist ext=xxx:) specifies an extension for filtering by type of file.
(:attachlist *:) shows the uploads directory and permits browsing of all uploaded files by directory (will not work if $EnableDirectDownload is set to 0).

(:description text:)
Descriptive text associated with the page. (Generates a <meta name='description' content='...' /> element in the page output.)

(:keywords word1, word2, ...:)
Identifies keywords associated with the page. These are not displayed anywhere, but are useful to help search engines locate the page. (Essentially, this generates a <meta name='keywords' content='...' /> element in the output.)

(:linebreaks:), (:nolinebreaks:)
Honors any newlines in the markup; i.e., text entered on separate lines in the markup will appear as separate lines in the output. Use (:nolinebreaks:) to cause text lines to automatically join again.

(:linkwikiwords:), (:nolinkwikiwords:)
Enables/disables WikiWord links in text. Note, this setting requires WikiWords to be enabled, see $EnableWikiWords. See also $LinkWikiWords.

(:markup:) ... (:markupend:) or (:markup:)[=...=]
Can be used for markup examples, showing first the markup and then the result of the markup.
(:markup class=horiz:) will show the markup side by side instead of one upon the other.
(:markup caption='...':) adds a caption to the markup example.
(:markupend:) is not required when using (:markup:) [=...=].
Note that the placement of newlines is very important for this markup. If you are using the [=...=] option then the opening [= MUST occur on the same line as the (:markup:). If you are using the full (:markup:) ... (:markupend:) form then your markup code must appear AFTER a newline after the initial (:markup:).

Displays messages from PmWiki or recipes, for instance from editing pages.

Turns off the section of the skin marked by <!--PageActionFmt?--> thru <!--/PageActionFmt?-->. In the pmwiki skin, this turns off the display of the actions at the top-right of the page (other skins may locate the actions in other locations).

Turns off any groupheader or groupfooter for the page. (See GroupHeaders.)

(:noheader:), (:nofooter:)
(:noleft:), (:noright:), (:notitle:)
If supported by the skin, each of these turns off the corresponding portion of the page.

(:redirect PageName:)
Redirects to another wiki page.
(:redirect PageName#anchor:)
Redirects to an anchor within a page
(:redirect PageName status=301 from=name quiet=1:)
Redirects the browser to another page, along with a redirect message. For security reasons this only redirects to other pages within the wiki and does not redirect to external urls. The status= option can be used to return a different HTTP status code as part of the redirect. The from= option limits redirects to occuring only on pages matching the wildcarded name (helpful when (:redirect:) is in another page). The quiet=1 option allows the target page not to display a link back to the original page ($EnableRedirectQuiet variable should be set to 1).

(:spacewikiwords:), (:nospacewikiwords:)
Enables/disables automatic spacing of WikiWords in text.

(:title text:)
Sets a page's title to be something other than the page's name. The title text can contain apostrophes and other special characters. If there are multiple titles in a page, the last one encountered wins (see also $EnablePageTitlePriority about how to change it).

Creates a new line if there isn't one already there. See this thread for more info.
Similar to [[<<]]

Can I get (:redirect:) to return a "moved permanently" (HTTP 301) status code?

Use (:redirect PageName status=301:).

Is there any way to prevent the "redirected from" message from showing at the top of the target page when I use (:redirect:)?

From version 2.2.1 on, set in config.php $EnableRedirectQuiet=1; and in the page (:redirect OtherPage quiet=1:) for a quiet redirect.

Is there any method for redirecting to the equivalent page in a different group, i.e. from BadGroup?/thispage => GoodGroup?/thispage using similar markup to (:redirect Goodgroup.{Name}:)?

(:redirect Goodgroup.{$Name}:) works if you want to put it in one page.
If you want it to work for the entire group, put (:redirect Goodgroup.{*$Name}:) into Badgroup.GroupHeader? - however, that only works with pages that really exist in Goodgroup; if you visit a page in Badgroup without a corresponding page of the same name in Goodgroup, instead of being redirected to a nonexistant page, you get the redirect Directive at the top of the page.
With (:if exists Goodgroup.{*$Name}:)(:redirect Goodgroup.{*$Name}:)(:ifend:) in Badgroup.GroupHeader? you get redirected to Goodgroup.Name if it exists, otherwise you get Badgroup.Name without the bit of code displayed.

How can a wiki enable linebreaks by default, i.e. without having the directive (:linebreaks:) in a page or in a GroupHeader?

Add to config.php such a line:
$HTMLPNewline = '<br/>';


The (:include:) directive makes it possible to insert (or "transclude") the contents of other pages into the current wiki page. All of the include directives below perform a straight text inclusion. In particular, any page links in the included text are assumed to link to pages in the current group if not otherwise qualified.


The basic syntax is

with pagename includes the full page from the same group.
includes a named variable from a page, Group and PageName are options

The full syntax is

includes a page according to the parameters supplied. Parameters are optional.


The directive can have multiple Name parameters with or without anchors, and multiple template variable parameters.

Named pages

(:include PageName:)
(:include Group.PageName:)
(:include Page1 Page2 Group1.Page3 Group2.Page4:)
Includes the entire text of another page into the current page. Multiple pages may be specified, but only the first available is included.

You can use the above feature to display an error message if an include fails. Create a page, eg. Site.IncludeFailed containing the error message. You can use any page name. Then, in your include markup, append this page at the end of the page list:

 (:include Page1 Page2 Page3 Site.IncludeFailed:)

A slightly more complex approach is outlined at the talk page.

#From#To anchors

(:include PageName#from#to:)include lines from PageName between the [[#from]] and [[#to]] anchors
(:include PageName#from#:)include all lines after [[#from]] to the end of the page
(:include PageName##to:)include all lines from the start of the page to [[#to]]
(:include PageName#from:)include everything between [[#from]] and the next anchor
(:include PageName#:)include everything from the top of the page to the first anchor
Note: do not put whitespace between "#from" "#to"
Note: text on the same line as a closing anchor but preceding the closing anchor will NOT be included in the text. Example Below:
[[#start]]some text on the first line
some text on the last line [[#end]]
The above, when included via (:include PageName#start:) will have the text on the first line but not the text on the last line.
(:include Page1 Page2 #from#to:)
Include from the first available of Page1, Page2 between the [[#from]] and [[#to]]
Note: put whitespace between "Page2" and "#from#to". The same anchors "#from#to" should be in both pages. If proper anchors are missing in the first available of Page1, Page2 the whole contents of the page is included.
This does not seem to work in 2.2 betas. See Cookbook:IncludeSection for a fix.
(:include Page1#from1#to1 Page2#from2#to2:)
Include the first from the first available of Page1 (between the [[#from1]] and [[#to1]]) or Page2 (between the [[#from2]] and [[#to2]] )
Note: Previous versions of PmWiki allowed whitespace between #from and #to anchors even though it was not designed to. Newer versions do not allow whitespace anymore. To re-enable this "exploited misbehavior" put this into your config.php or farmconfig.php
Markup('includeanchors', '<include', '/(\\(:include.*?#\\w+)\\s+(#\\w+)/', '$1$2');

(:include PageName lines=10:)
(:include PageName lines=5..10:)
(:include PageName lines=5..:)
Include the first 10 lines, lines 5-10, or lines 5 and up from PageName. A "line" in this context refers to a line of source. Thus a line may be a paragraph that wraps over several lines on the screen, or a completely blank line.
(:include Page1 Page2 Page3 lines=1..5:)
Include the first five lines from the first available of Page1, Page2, or Page3. (To include lines from a list of pages, use a separate include for each.)


(:include PageName self=0:)
The parameter self can be 0 or 1. It tells the include directive if it is allowed to include the current page. This is useful if PageName is a variable like {$Name} and you want to prevent the directive from including the current page.

Page text variables

Includes definition list values from an (optional) page as page text variables. These are defined using a definition list (:item:description), simple colon delimiter (item:description), or special markup ((:item:description:)).


(:include PageName basepage=BasePageName:)
Include PageName, but treat all relative links and page variables on PageName` as relative to BasePageName?.

If basepage= is provided all relative links and page variables are interpreted relative to basepage. So, if one creates TemplateName as

Name: {$:Name}
Address: {$:Address}

then the directive

(:include TemplateName basepage=PageName:)

will retrieve the contents of TemplateName, treating any page variables and links as being relative to PageName. In particular, the values for {$:Name} and {$:Address} will be taken from PageName, but things like {$Title} and {$LastModifiedBy} would also work here.

Basepage usage

The primary purpose of basepage is to allow the include of pages in a way that mimics the 2.1.x behavior where page variables and links are interpreted relative to the currently displayed page. This is done with:

(:include SomeOtherPage basepage='' :)
(:include SomeOtherPage basepage={*$FullName} :)

It also allows GroupHeader and GroupFooter to have their page variables and links be relative to the currently displayed page (instead of GroupHeader and GroupFooter):

  ## PmWiki default $GroupHeaderFmt setting
  $GroupHeaderFmt = 
    '(:include {$Group}.GroupHeader self=0 basepage={*$FullName}:)(:nl:)';

Otherwise, using IncludeAll inside of a GroupHeader would display 'GroupHeader' and not the name of the currently displayed page.

The basepage= parameter is general enough that it can also be used as a templating engine, so that we can grab a template page containing variables that are then filled in with values from another page:

(:include TemplatePage basepage=DataPage :)

And, of course, a single TemplatePage? can actually contain multiple templates delimited by anchors, so that we end up with a syntax eerily similar[5] to pagelist-templates:

    (:include TemplatePage#abc basepage=DataPage :)

So then TemplatePage can use a syntax like:

    ...template stuff here...

and it's possible to display TemplatePage as a template without it being interpreted... same as we do for Site.PageListTemplates.

[1]Okay, maybe it's not so eerie, given that the pagelist template code actually uses the same function as (:include:) to grab its templates. But it's still a useful parallel.

Specifying variables as parameters

You can also specify variable values inline with the include statement, and refer to the variables in the template using the {$$variable1} format:

(:include TemplatePage variable1="value" variable2="value2":)

This assumes that a site has $EnableRelativePageVars enabled, which is recommended in PmWiki 2.2.0 -- but was disabled by default in version 2.2.8 and earlier.

For example, on my included page ("template") I might have this:

Hi, {$$Name}, how are you today?

Hi, {$$Name}, how are you today?

Then, including that section above (that section is available via the section {$FullName}#ivars)) you get this type of behavior:

(:include {$FullName}#ivars Name=Sam:)

Hi, Sam, how are you today?

If a value contains spaces, quote it:

(:include {$FullName}#ivars Name="my friend":)

Hi, my friend, how are you today?

See also $EnableUndefinedTemplateVars.

See Also

Styling Note

By default, Included pages or lines cannot be distinguished from other text on the page. To provide a visual indication that this text is special, you can apply Wiki Styles. For example:

%define=leftborder border-left="2px solid #88f" margin-left="2px" padding="1px 0 3px 10px"%
What is PmWiki?
>>leftborder<< (:include PmWiki.PmWiki lines=1..4:) 
''Have a very nice day!''

What is PmWiki?

PmWiki is a wiki-based content-management system (CMS) for collaborative creation and maintenance of websites.

PmWiki pages look and act like normal web pages, except they have an "Edit" link that makes it easy to modify existing pages and add new pages into the website, using basic editing rules. You do not need to know or use any HTML or CSS. Page editing can be left open to the public or restricted to small groups of authors.

Have a very nice day!

Parameter References

Any parameters supplied to an include statement (whether they are keywords or not) are accessible inside the included page as a special {$$...} variable of the same name. This feature can be used to provide extra information to use when displaying the included page.


  • You can also say (:include My/Page#myanchor lines=4:) which starts from, and includes, the line with the anchor [[#myanchor]] for four lines.

Notes about use with conditional markup

The (:include ...:) markup is processed after conditional markup is evaluated.
Therefor you can include a page or page section as part of a condition, like

(:if some condition:)(:include SomePage#section:)(:if:)

But (:include SomePage#section:) doesn't look to see if [[#section]] is part of a conditional, like

(:if some condition:)[[#section]]...[[#sectionend]](:ifend:)

(:include SomePage#section:) will ignore such a condition.

When testing variables in included pages the context of the page (source or target) can be useful. See special references for details.

What's the maximum number of includes that can exist in a page?

My site seems to stop including after 48 includes. ($MaxIncludes)

By default, PmWiki places a limit of 50 include directives for any given page, to prevent runaway infinite loops and other situations that might eat up server resources. (Two of these are GroupHeader and GroupFooter.) The limit can be modified by the wiki administrator via the $MaxIncludes variable.

Is there any way to include from a group of pages without specifying by exact name, e.g. between Anchor X and Y from all pages named IFClass?-* ?

This can be achieved using page lists.

There appears to be a viewing issue when the included page contains the (:title:) directive.

In a default installation, the last title in the page overrides previous ones so you can place your (:title :) directive at the bottom of the page, after any includes. See also $EnablePageTitlePriority.


The InterMap (also called InterWiki? in some other wikis) is a system for defining links between WikiWikiWeb sites that was first developed by UseMod? and Meatball (see UseMod:InterWiki and Meatball:InterWiki). The method is to use a word shortcut that stands for a defined path. InterMap links have the form MapPrefix:PagePath, where the host prefix is converted to a partial URL based on entries in the site's intermap.txt and localmap.txt files.

The default intermap.txt

The default intermap.txt distributed with PmWiki (in the scripts/ directory) includes the following InterMap entries:

The page Site.InterMap

Site.InterMap includes the following entries:

Thus, "PmWiki:Variables" becomes "" + "Variables," a link to the PmWiki.Variables page on the official PmWiki web site, Wiki:FrontPage is a link to the front page of the first WikiWikiWeb, and Wikipedia:Stonehenge takes you to the Wikipedia article about the famous megaliths in England.

Usage in a wiki page

Like other links, you can use the double-bracket syntax to get different link text:

* [[Meatball:StartingPoints | starting points]] over at Meatball
* [[starting points -> Meatball:StartingPoints]] over at Meatball

If you want to link just to what the intermap says (e.g. for Wikipedia), then do [[Wikipedia:. | Wikipedia's main page]], which produces Wikipedia's main page. Note the . (period) after the Map: reference.

The special Path: InterMap entry can be used to create "relative urls" in links.

Custom InterMap prefixes

The actual set of InterMap links at any site is defined by the site administrator via the Site.InterMap page and the local/localmap.txt file.

An intermap entry takes the following format:


The InterMap entry can be for any of the link schemes supported by PmWiki.
You can create your own InterMap links by doing one or more of the following:

  • Modify the page called Site.InterMap and place entries like the ones above in it.
  • Create a file called local/localmap.txt and place entries like the ones above in it.
  • In a WikiFarm installation you can create a file called local/farmmap.txt and there place entries like the ones above in it. These prefixes will be common to all the wikis in the farm.
  • Ensure that there is a space after the colon.

Do not edit the file scripts/intermap.txt directly! If you do, you'll lose your changes when you upgrade PmWiki.

Variables and InterMap links

It's possible to use variables within your InterMap entries. The following entries create ThisWiki: and ThisPage: shortcuts:

ThisWiki:        $ScriptUrl
ThisPage:        {$PageUrl}

You can also define InterMap entries where the text of the entry is substituted into the middle of the URL. Just include '$1' in the URL where you want the substitution to take place. For example:


would cause Jargon:F/feature-creep to be converted to

Tips and tricks

It is possible to document your intermap prefixes directly in the page Site.InterMap. The extra text will not cause a performance penalty, nor will it break the definition of prefixes. However, be aware that anything matching a line starting with a word and a colon (:) will be considered to define a prefix.

The order in which various sources are checked for definitions of prefixes is controlled by the variable $InterMapFiles. Currently the precedence (highest to lowest is as follows):

Are InterMap names case sensitive?

Yes, thus eAdmin: is a different InterMap link than EAdmin:.

How can I achieve a localmap.txt mapping with the effect of Pics: Path:/somepathto/pics/?

Use the following:

Pics: /somepathto/pics/

How can I define an InterMap in PHP?

Use the following:

      $LinkFunctions['PmWikiHome:'] = 'LinkIMap';
      $IMap['PmWikiHome:'] = '$1';


Using the (:if:) Directive

The (:if:) directive allows portions of a page to be included or excluded from rendering. The generic forms of the (:if:) directive are

(:if cond param:) body (:ifend:)
(:if cond param:) body (:else:) body (:ifend:)
(:if cond param:) body (:elseif cond param:) body (:ifend:)

where "cond" names a condition to be tested, and "param" is a parameter or other argument to the condition.

Note that (:if:) without parameters and (:ifend:) are identical. Also note that (:if cond:) automatically closes a previous conditional. For nested multiple levels, see Nested conditionals.

Built-in Conditions

The built-in conditions include:

(:if name PAGENAME:)  -  current page is named "PAGENAME"
(:if group GROUPNAME:)  -  current group is named "GROUPNAME"
(:if auth LEVEL PAGENAME:)  -  viewer is authorized - meaning "what they are allowed to do" - matches a "LEVEL" where LEVEL can be: read, edit, upload, attr or admin; PAGENAME is optional.
(:if authid:)  -  current viewer is authenticated - meaning they have proven who they are via login - to use this the wiki must include recipe AuthUser or others which set the $AuthId variable.
(:if enabled InvalidLogin:)  -  username and password not authenticated. To use this the wiki must include recipe AuthUser.
(:if true:)  -  always include text
(:if false:)  -  always exclude text (same as a comment)
(:if attachments:)  -  current page has one or more attachments

If used in a sidebar, header, or footer the condition applies to the main page.

(:if date DATE VALUE:)  -  DATE may be year-month. year-month-day is optional.

Evaluates to true if VALUE is within DATE ("now" or "today" is assumed if VALUE is omitted, as in the following examples.) (Note that VALUE can be a recognizable date via strtotime() whereas DATE [or DATE1? and DATE2? below] have a more fixed format which explicitly must exclude spaces. Any spaces in DATE1? or DATE2? cause unpredictable results.)

(:if date DATE.. VALUE:)  -  true if VALUE (or current date if omitted) is DATE or later (unlimited)
(:if date ..DATE VALUE:)  -  true if VALUE (or current date if omitted) is DATE or earlier (unlimited)
(:if date DATE1..DATE2 VALUE:)  -  true if VALUE (or current date if omitted) is in range DATE1 to DATE2 (inclusive)

dates are in standard format yyyy-mm-dd or yyyymmdd or yyyymmddThhmm (note the "T" between the date and the hour, and also see comment above on format of VALUE).
Note the ".." cannot have leading or trailing spaces.

(:if enabled VAR:)  -  true if PHP VAR is true
(:if enabled AuthPw:)  -  true if user has entered any password during the current browser session.

- This does not mean the user has entered the correct password, just that they entered one.

(:if equal STRING1 STRING2:)  -  true if STRING1 equals STRING2, use quotes if the string or string variable contains spaces, eg "MY STRING"
(:if match REG_EXPRESSION:)  -  true if current page name matches the regular expression
(:if exists PAGENAME:)  -  true if the page pagename exists
(:if ontrail WikiTrailPage ThisPage:)  -  true if ThisPage? is in a list used as a trail on WikiTrailPage?

The name and group conditionals will work even for an included page, as the "name" and "group" conditionals always check the currently displayed page, as opposed to the page that the markup appears in.

Note: Although there is no built-in conditional markup to test ?action=, you can use (:if equal {$Action} ACTION:) to test what the current action being requested is.

Negated Conditions

Negated forms of conditions also work:

(:if !attachments:)  -  this page has no attachments
(:if ! name PAGENAME:) current page is NOT named "PAGENAME"
(:if name -PAGENAME :)

Nesting Conditions

Note that (:if cond:) automatically closes a previous conditional. Thus, the following two examples have identical meaning:

  • (:if cond1:) cond1 is true (:if cond2:) cond2 is true (:ifend:)
  • (:if cond1:) cond1 is true (:ifend:)(:if cond2:) cond2 is true (:ifend:)

Conditions can be nested from 2.2.beta 66. To have nested conditionals you need to number the if, and the matching else/ifend:

(:if cond1:)
  cond1 is true
  (:if2 cond2:)
     cond1 and cond2 are true
     cond1 is true, cond2 is not
  cond1 is false, cond2 testing was ignored

Spaces were added for better readability.

Using wildcard placeholders

The character * can be used as a wildcard to represent any character, zero, one, or multiple times.
The character ? can be used as a wildcard to represent any character exactly once.
Wildcard characters (* and ?) can be used with the name and group conditional markups, thus:

(:if name PmCal.2005* :)  -  current page is in group PmCal and begins with 2005
(:if group PmWiki* :)  -  current page is in group PmWiki or a group beginning with PmWiki
(:if name Profiles.*,-Profiles.Profiles :)  -  current page is in group Profiles but not Profiles.Profiles

Using page text variables, page variables and markup expressions

Page text variables (PTVs?), page variables (PVs?) and markup expressions can be used in conditional markup. They will be assigned/evaluated before the condition(s).

Combining conditions

Conditions (as previously defined) may be combined into more complex conditional expressions using one of these three equivalent forms:

(:if expr EXPRESSION :)
(:if [ EXPRESSION ] :)
(:if ( EXPRESSION ) :)

Conditions are combined into expressions with boolean operators and brackets. In the next table, A and B are either regular conditions or (round-)bracketed sub-expressions of regular conditions:

A and BAndTRUE if both A and B are TRUE.
A or BOrTRUE if either A or B is TRUE.
A xor BXorTRUE if either A or B is TRUE, but not both.
! ANotTRUE if A is not TRUE.
A && BAndTRUE if both A and B are TRUE.
A || BOrTRUE if either A or B is TRUE.


(:if [ name SomePage and group SomeGroup ]:)    equivalent to (:if name SomeGroup.SomePage:)

Important Notes:

  • Spaces are required around operators and brackets.
  • No specific feedback is given for syntax errors or unbalanced brackets.
  • Use round brackets (not square) for nested expressions.

Thus, the following is a valid way of building an expression that shows the following contents only when the user is either the administrator, or is logged in and the time is later than the given date:

(:if [ auth admin || ( authid && date 2006-06-01.. ) ] :)

Nesting with square brackets will silently fail to work as expected:

(:if [ auth admin || [ authid && date 2006-06-01 ] ] :)    NOTE: Doesn't Work!

A common use of these complex tests are for expressions like:

(:if expr auth admin || auth attr || auth edit :)
[[Logout -> {$Name}?action=logout]]

which provides a logout link only when the browser has admin, attr, or edit permissions.

admins (advanced)

Creating new conditions

See Cookbook:ConditionalMarkupSamples.

See also special references for the use of {*$Variables}.

Page specific variables

This page describes the "variables" that are associated with pages. Page variables have the form {$variable}, and can be used in page markup or in certain formatting strings in PmWiki. For example, the markup "{$Group}" renders in this page as "Test".

Note: Do not confuse these variables (set and used only in PmWiki pages) with PHP variables. Page variables can be read in PHP with the PageVar() function.

Note that these variables do not necessarily exist in the PHP code, because they have to be determined for a specific page. (However, they are usable in FmtPageName strings.)

There is also the form {pagename$variable}, which returns the value of the variable for another page. For example, "{MarkupMasterIndex$Title}" displays as "Markup Master Index".

Special references

Special referenced variables are used to specify the context of the variable when:

  • the variable is included into a destination (target) page
  • the variable is used in a sidebar, header, or footer

Prefixing the variable name with an asterisk (*) means the variable's value is related to the target page or main (body) page.

  • {*$PageVariablename?} - prefixed by an asterisk (*) - value reflects target page context

Without the asterisk the variable's value is that in the page from which it originates, eg source page of include, sidebar, or header or footer.

Special references are also used in page text variables and page list templates.

For example you can test to see if the page is part of another page

(:if ! name {$FullName}:) 
%comment% name of this page is not the same as the page this text was sourced from
->[[{$FullName}#anchor | more ...]]

or refer to the main page in a sidebar, footer, or header

This page is [[{*$FullName}]]

This page is Test.IncludeAll

Default page variables

The page variables defined for PmWiki are:

{$Action} - page's url action argument, as in "browse"
{$BaseName} - page's "base" form (stripping any prefixes or suffixes defined via $BaseNamePatterns) as in "Test.IncludeAll"
{$DefaultGroup} - default group name, as in "PmWiki"
{$DefaultName} - name of default page, as in "HomePage"
{$Description} - page's description from the (:description:) markup, as in ""
{$FullName} - page's full name, as in "Test.IncludeAll"
{$Group} - page's group name, as in "`Test"
{$Groupspaced} - spaced group name, as in "Test"
{$LastModified} - date page was edited, as in "December 16, 2023, at 08:21 PM"
{$LastModifiedBy} - page's last editor, as in "simon"
{$LastModifiedHost} - IP of page's last editor, as in ""
{$LastModifiedSummary} - Summary from last edit, as in "change redirected link (nb highlight struggles on this page when searching it) (-1)"
{$LastModifiedTime} - time page was edited in unix-style timestamp, as in "1702758077"
This can be used (preceded by '@') in {(ftime)} and other date/time markups.
{$Name} - page name, as in "IncludeAll"
{$Namespaced} - spaced page name, as in "Include All"
{$PageUrl} - page's url, as in "$ScriptUrl/IncludeAll"
{$PasswdRead} - "read" permissions for the page e.g. ""
{$PasswdEdit} - "edit" permissions for the page e.g. ""
{$PasswdAttr} - "attr" permissions for the page e.g. "(set by group) ****"
{$RequestedPage} - page requested in URL, used on Site.PageNotFound. e.g. "IncludeAll"
{$SiteGroup} - default group name for e.g. RecentChanges, as in "Site"
{$Title} - page title (may differ from Name), as in "All pages of the documentation"
{$Titlespaced} - either the page title (if defined), or the spaced page name, as in "All pages of the documentation"

In addition to the above, there are some page-invariant variables available through this markup:

{$Author} - the name of the person currently interacting with the site, as in ""
{$AuthId} - current authenticated id, as in "" note the lower case 'd'.
{$Version} - PmWiki version, as in "pmwiki-2.3.32"
{$VersionNum} - The internal version number, as in "2003032"
{$ScriptUrl} - The url to the pmwiki script, as in ""

Page variable security ($authpage)

The form {pagename$variable} or some PageLists, can display the values for other pages, regardless of the password protections.

If the other pages are protected and the visitor has no read permissions, PageVariables, unlike PageTextVariables, normally display the values. While most variables do not contain sensitive information, some of them could do: $Title, $Description and those starting with $LastModified.

Administrators and module developers can redefine the sensitive page variables to respect authentications, by using the "$authpage" variable instead of "$page" in the definition. The following snippet can be added in local/config.php -- it will rewrite the default possibly sensitive definitions to the secure ones.

foreach($FmtPV as $k=>$v) {
  if(preg_match('/^\\$(Title(spaced)?|LastModified(By|Host|Summary|Time)?|Description)$/', $k))
    $FmtPV[$k] = str_replace('$page', '$authpage', $v);

Custom page variables

You may add custom page variables as a local customization. In a local configuration file or a recipe script, use the variable $FmtPV:

$FmtPV['$VarName'] = "'variable definition'";
$FmtPV['$CurrentSkin'] = '$GLOBALS["Skin"]';
$FmtPV['$WikiTitle'] = '$GLOBALS["WikiTitle"]';

Defines new Page Variable of name $CurrentSkin, which can be used in the page with {$CurrentSkin} (also for Conditional markup). It's necessary to use the single quotes nested inside double-quotes as shown above (preferred) or a double-quoted string nested inside single-quotes like '"this"'.

If you want to have a Page Variable that returns the currently used password (more precisely, the last password entered), you can use

$FmtPV['$AuthPw'] = 'reset(array_keys((array)@$_SESSION["authpw"]))';

See also

Is there a variable like $LastModified, but which shows me the creation time?

No, but you can create one in config.php. For instance:

# add page variable {$PageCreationDate} in format yyyy-mm-dd
$FmtPV['$PageCreationDate'] = 'strftime("%Y-%m-%d", $page["ctime"])';

If you like the same format that you define in config.php with $TimeFmt use

 $FmtPV['$Created'] = "strftime(\$GLOBALS['TimeFmt'], \$page['ctime'])";

How can I test if a variable is set and/or not empty?

Use (:if ! equal "{$Variable}" "":) $Variable is not empty. (:ifend:). Note that undefined/inexistent variables appear as empty ones.


Page text variables are string variables created in the wiki text of a page, and can be automatically made available for inclusion in other pages.

Defining Page Text Variables

There are three ways to define automated Page Text Variables:

  • use a definition list - the normal pmwiki markup for a definition list will create a page text variable
Example definition list:
:Name: Crisses


This creates a new variable that can be accessed by {$:Name} (becomes: "Crisses") in the page.
  • use a simple colon delimiter in normal text
Example colon delimited:
Address: 1313 Mockingbird Lane


Address: 1313 Mockingbird Lane

"1313 Mockingbird Lane"

This creates the {$:Address} variable (variable markup becomes: "1313 Mockingbird Lane") in the page.
  • hidden directive form - PmWiki markup that doesn't render on the page, but defines the variable
Example directive:
(:Country: Transylvania :)

"Transylvania "

This creates the {$:Country} variable (variable markup becomes: "Transylvania ") in the page.


Usage on the same page

On the same page you can resolve page text variables through the {$:Var} format (shown above).

Usage in headers and footers: special references

If you want a GroupHeader, GroupFooter, SideBar, etc to call on page text variable in the main page, you need to include special reference information. To explicitly reference the page text variable from the page being displayed add an asterisk to the page text variable's markup: {*$:Address} on the GroupFooter or GroupHeader page.


To include a page text variable from a header or footer see usage from other pages below.

Special references also apply to page variables and page list templates.

Usage from other pages

If you want to pull the data from another page, use the {Group/PageName$:Var} format.

Suburb: Khandallah
:Mountain:Mt Ruapehu


Suburb: Khandallah

Mt Ruapehu
"Mt Ruapehu"

Usage from included pages

Page text variables are never included from their source page. See Usage from other pages above to refer to a page text variable on another page.

Usage with pagelists

Page lists can also access the page text variables:

(:pagelist group=PmWiki order=$:Summary count=6 fmt=#singleline:)

And to create pagelist formats (such as those documented at Site.Page List Templates, Page Lists, Page List Templates, Page Variables. Store custom pagelists at Site.Local Templates).

Page lists can also use page text variables to select pages :

(:pagelist group=PmWiki $:City=Paris count=8 fmt=#singleline order=-name:)
lists pages having '$:City' set to 'Paris'.
Example: multiple selections with spaces
(:pagelist group=PmWiki $:City="Addis Ababa,Paris" order=-$:Version count=8 fmt=#singleline:)
'quotes' must surround all the selections.
  • When using page text variables for selection or ordering, don't put the curly braces around the variable name. The curly forms do a replacement before the pagelist command is evaluated.

Testing if set or not set

=-PTV is set (is not empty), eg (:pagelist $:Var=- :)
=-?*PTV is not set (is empty), ie not one character followed by 0 or more characters, eg. (:pagelist $:Var=-?* :)
=*display all pages, regardless of the page text variable (slow)
=-*display no pages, regardless of the page text variable (slow)

Tip : (:if ! equal "{$:PTV}" "":) will test if PTV is empty/unset or not.

Example: Pages without a summary
(:pagelist group=PmWiki $:Summary=-?* count=6  fmt=#singleline:)

Use page text variable in a template

Display pages by Audience page text variable.

(:if ! equal '{=$:Audience}' '{<$:Audience}':)
(:pagelist group=PmWiki count=10 fmt=#byaudience order=-$:Audience:)



visitors (intermediate) :

WebFeeds AccessKeys

readers, authors (basic):


readers :




authors, admins (intermediate) :


authors, admins (advanced) :


authors (intermediate) :

MarkupCharacters Uploads Categories

Use page text variables in conditional markup

Page text variables will be assigned/evaluated before any conditional markup is evaluated. This effectively means that you cannot declare a PTV within an if...else condition; and also that a PTV will have a value even if it is set within a (:if false:)....(:if:) condition.

Usage - from within code (developers only)

The standard PageVar?($pagename,$varname) function can return page text variables, but remember to include the dollar and colon like this:


It works by caching all page (text) variables it finds in a page (in $PCache) and returns the one requested.


The {(...)} "expression markup" allows for a variety of string and formatting operations to be performed from within markup. Operations defined by this recipe include substr, ftime, strlen, rand, toupper / tolower, ucfirst, ucwords, pagename and asspaced.


The "substr" expression extracts portions of a string. The arguments are

  1. the string to be processed. Always quote the string to be processed.
  2. the initial position of the substring. Note that the initial position argument is zero-based (i.e., the first character is referenced via a "0").
  3. the number of characters to extract
 {(substr "PmWiki" 2 3)}
 {(substr "PmWiki" 2)}
 {(substr "PmWiki" 0 1)}
 {(substr "PmWiki" 0 -3)}
 {(substr "PmWiki" -3)}

To obtain the last n characters of a string use {(substr "string" -n)}
To truncate the last n characters of a string use (substr "string" 0 -n)}


"Ftime" expressions are used for date and time formatting. The generic form is

{(ftime "fmt" "when")}
{(ftime fmt="fmt" when="when")}

where fmt is a formatting string and when is the time to be formatted. The arguments can be in either order and may use the optional "fmt=" and "when=" labels.


 {(ftime fmt="%F %H:%M")}
 {(ftime %Y)}
 {(ftime fmt=%T)}
 {(ftime when=tomorrow)}
 {(ftime fmt="%Y-%m-%d" yesterday)}
 {(ftime "+1 week" %F)}
 {(ftime fmt=%D "+1 month")}
 {(ftime fmt="%a%e %b" when="next week")}
 April 13, 2024, at 03:12 PM
 2024-04-13 15:12
 April 14, 2024, at 12:00 AM
 Mon15 Apr

The fmt parameter is whatever is given by "fmt=", the first parameter containing a '%', or else the site's default. The formatting codes are described at In addition to those, '%F' produces ISO-8601 dates, and '%s' produces Unix timestamps. Some common formatting strings:

     %F                # ISO-8601 dates      "2024-04-13"
     %s                # Unix timestamp      "1713021135"
     %H:%M:%S          # time as hh:mm:ss    "15:12:15"
     %m/%d/%Y          # date as mm/dd/yyyy  "04/13/2024"
     "%A, %B %d, %Y"   # in words            "Saturday, April 13, 2024"

The when parameter understands many different date formats. The when parameter is whatever is given by "when=", or whatever parameter remains after determining the format parameter. Some examples:

    2007-04-11            # ISO-8601 dates
    20070411              # dates without hyphens, slashes, or dots
    2007-03               # months
    @1176304315           # Unix timestamps (seconds since 1-Jan-1970 00:00 UTC)
    now                   # the current time
    today                 # today @ 00:00:00
    yesterday             # yesterday @ 00:00:00
    "next Monday"         # relative dates
    "last Thursday"       # relative dates
    "-3 days"             # three days ago
    "+2 weeks"            # two weeks from now

Note: If you want to convert a Unix timestamp you must prefix with the @. Thus, "{(ftime "%A, %B %d, %Y" @1231116927)}".

The when parameter uses PHP's strtotime function to convert date strings according to the GNU date input formats; as of this writing it only understands English phrases in date specifications.

The variable $FTimeFmt can be used to override the default date format used by the "ftime" function. The default $FTimeFmt is $TimeFmt.


The "strlen" expression returns the length of a string. The first argument is the string to be measured.

 {(strlen "{$:Summary}")}


The "rand" expression returns a random integer. The first argument is the minimum number to be returned and the second argument is the maximum number to be returned. If called without the optional min, max arguments rand() returns a pseudo-random integer between 0 and RAND_MAX. If you want a random number between 5 and 15 (inclusive), for example, use (rand 5 15).

 {(rand 1 99)}

toupper / tolower

The "toupper" and "tolower" expressions convert a string into uppercase or lowercase. The first argument is the string to be processed.

 {(toupper "{$:Summary}")}
 {(tolower "{$:Summary}")}

ucfirst / ucwords

The "ucfirst" expression converts to uppercase the first character of the string, and "ucwords", the first character of each word. The first argument is the string to be processed.

 {(ucfirst "{$:Summary}")}
 {(ucwords "{$:Summary}")}


The "pagename" expression builds a pagename from a string. The first argument is the string to be processed.

 {(pagename "{$:Summary}")}


The "asspaced" expression formats wikiwords. The first argument is the string to be processed.

 {(asspaced "{$FullName}")}
 Test.Include All

Nesting expressions

Markup expressions can be nested:

 {(tolower (substr "Hello World" 2))}
 llo world


  • For PmWikis? version 2.2.33 or older, the string-processing expressions may not work properly on multibyte UTF-8 characters. Newer versions should work fine.

See also


This page explains how you can embed input forms into wiki pages.

Input forms don't actually handle processing of the form data -- the feature simply allows creation of forms inside wiki pages. Forms processing can be found in the Cookbook (see below).


Two directives are used to begin and end forms:

    (:input form "url" method:)
    (:input end:)

The (:input form:) directive starts a form that will post to url (optional action=url) using the supplied method (optional method=method). The url must be in quotes if not specified via action=. If the url is omitted, then the current page is assumed. If method is omitted then "POST" is assumed. An optional name="FormName" argument can be used to name the form. You can explicitly state action=url or method=get or you can simply use them as positional parameters.

If your site uses ?n=Group.Page to specify the pagename then having a field (:input hidden name=n value={$FullName}:) will allow your form to post to the current page as an alternative to fully specifying the action=url.

The (:input end:) directive ends the current form.

Note that this feature doesn't ensure that the form output is correct HTML -- it assumes the author knows a little bit of what he or she is doing. Notably, (:input form:) and (:input end:) shouldn't appear inside tables, and all form fields and controls should be inside an (:input form:)...(:input end:) block.

Standard input controls

The standard input controls are:

    (:input text name value size=n:)
    (:input hidden name value:)
    (:input password name value:)
    (:input radio name value:)
    (:input checkbox name value:)
    (:input select name value label:)
    (:input default default-name default-value:) 
    (:input submit name value:)
    (:input textarea name [=value=] rows=n cols=n:)
    (:input reset name label:)
    (:input file name label:)
    (:input image name "src" alt:)

Where name and value are in the HTML syntax: name="addr" value="808 W Franklin".

For most controls the markup has the form:

    (:input type name value parameter=value:)

where type is the type of input element (described below), name is the name of the control, value is its initial value, and parameters are used to specify additional attributes to the control. If value contains spaces, enclose it in quotes; if it contains newlines (for textarea and hidden elements), enclose it in [=...=].

For example, the following creates a text input control with a size of 30 characters:

(:input text authorid "Jane Doe" size=30:)

For convenience, an author can also specify name and value arguments directly using name= and value= attributes (same as HTML):

(:input text name=authorid value="Jane Doe" size=30:)

For the textarea control a value can be set from PmWiki 2.2.0beta45 onwards. Enclose the value in [=...=] if it contains spaces or new lines.

The submit control will more often be written as:

    (:input submit value=label:)

Here's a more complete example, e.g., for a login prompt:

(:input form "":)
(:input hidden action login:)
||     Name:||(:input text username:)    ||
|| Password:||(:input password password:)||
|| ||(:input submit value="Log In":) ||
(:input end:)


General form field attributes

  • (:input ... focus=1:) Setting focus=1 causes that field to receive the initial focus when the form is first opened.
  • The following advanced HTML attributes are supported: name, value, id, class, rows, cols, size, maxlength, action, method, accesskey, tabindex, multiple, checked, disabled, readonly, enctype, src, alt. For a more detailed description, see their counterparts in the w3c reference: HTML forms (not all of them can be used for all types of form fields).

(:input select ... :)

The basic form of a select box is a sequence of options:

(:input form:)
(:input select name=abc value=1 label=alpha :)
(:input select name=abc value=2 label=beta  :)
(:input select name=abc value=3 label=gamma :)
(:input submit:)
(:input end:)

The values can be specified positionally:

 (:input select abc 1 alpha :)

We can specify the size of the selection box:

 (:input select abc 1 alpha size=3 :)

You can specify a multiple select box:

 (:input select abc 1 alpha size=3 multiple:)

To have an element selected, use selected=selected:

 (:input select abc 2 beta selected=selected:)

Note that to have two select boxes inline, not only should you give them different name= parameters, but also place a separator, like a character, &nbsp; or even the null sequence [==] between them:

(:input form:)
(:input select name=FIRST value=1:)(:input select name=FIRST value=2:)[==]
(:input select name=SECOND value=3:)(:input select name=SECOND value=4:)
(:input end:)

See Also

Compatible recipes:

  • Cookbook:PmForm Form processing engine for PmWiki
  • Cookbook:Fox Form processor to add, replace, copy, delete content plus upload files and send email notifications using templates and Input markup
  • Cookbook:Wiki Forms Use a form template to create, edit and list wiki pages
  • Cookbook:ProcessForm Maintain values in fields and make PVs? of the form name/values when a form is submitted


PmWiki has support for handling the case where multiple authors attempt to edit the same page nearly simultaneously. Here's the basic scenario for systems where simultaneous edits are not handled:

  • Alice starts to edit a page.
  • Before Alice saves her edits, Bob requests an edit of the same page, and receives the page text prior to Alice's edits.
  • Bob finishes with his edits and hits "save".
  • Alice finishes editing her page, hits "save", and since she was working from a version of the page from before Bob had made his changes, she wipes out Bob's edits in the process.

PmWiki's simultaneous edit feature detects when this occurs, and instead of saving Alice's edits PmWiki presents Alice with a message that someone else changed the page while she was editing it. Furthermore, Bob's changes are merged into Alice's copy of the page, with any conflicts highlighted by <<<<<<< and >>>>>>>. Alice can then fix things as appropriate and save the updated page, or, if Alice is lazy, she can just hit "save" a second time and leave it to someone else to fix.

The simultaneous edits feature is also invoked whenever someone requests a page preview; thus if a page changes while previewing a page the author gets notification and can see the merged results.

How can I test/experiment with this feature?

  1. Open up two browser windows and select the same page to be edited in each window (e.g., try WikiSandbox?action=edit).
  2. In one browser window, make some changes to the page and then save those changes.
  3. In the second browser window, make some different changes to the same page and hit "save". Since the page changed after the edit form was loaded into the second window, there's a potential edit conflict and you'll receive the "edit conflict message".
  4. You can make any adjustments in the second window, and press "Save" again to save the changes.


Some server environments such as Windows and PHP running in safe_mode are unable to use the simultaneous edits capability distributed with PmWiki. See Cookbook:SimultaneousEdits for a solution for these environments.


Authors have a range of options to choose from when organizing a collection of wiki pages. Used in combination, these give a lot of flexibility. An effective wiki will use all of these to optimize

  • content
  • navigation

These are the two most important aspects of a website.

Wiki Word
The most powerful organizing principle is the author's choice of page names. When a search returns a list of pages, their names need to be clear enough to guide a visitor to the right place.
Providing a network of links to other points in the wiki, with or without wiki words, is the primary means of navigating a wiki.
Wiki Page
A page with text (and images), where the text can contain for instance WikiWords that automatically becomes a link to another WikiPage.
Wiki Group
PmWiki requires every page to be a member of a group. A group is like a wiki within a wiki; it can have its own presentation look, security controls and navigation aids. With default configuration, WikiWords are only searched inside the current group, and you use either OtherGroup/MyWikiWord or OtherGroup.MyWikiWord to refer to pages in other groups (see Links).
Wiki Trails
A collection of pages, either in the same group or across multiple groups, can be designated as a trail. A visitor can move from stop to stop by clicking on next and previous links.
Individual wiki pages can also be grouped by having tags and links to a common "category" page; we say that any pages that link to a common page are in a "category" defined by that page. PmWiki uses the [[!category]] markup as a shorthand to place a page into a category with other pages containing the same markup.
The shortcoming of categories is that categories do not distinguish between the declaration of a category ([[!structure]]) and the link to a category ([[Category/Structure]]).
Page text variables
A newer and more powerful concept than Categories, pages can use one of more page text variables to store page attributes. These can the be used in page lists.
Page lists
Page lists provide a powerful means of presenting lists of relevant pages, or selection of data from within a page. Lists are template based and are highly customizable.
Include other pages
The capability to include parts of other pages also provides a flexible means of sharing content between pages.
Being able to search is a fundamental requirement of a website. In PmWiki search, like pagelists is both powerful and highly customizable.


PmWiki pages are organized into groups of related pages. This feature was added to PmWiki to allow authors to create their own wiki spaces of specialized content on their own, without having to become, or rely on, wiki administrators. See Pm's post to the pmwiki-users mailing list.

By default, page links are between pages of the same group; to create a link to a page in another group, add the name of the other group and a dot or slash to the page name. For example, links to Main/WikiSandbox could be written as:

* [[Main.WikiSandbox]]
* [[Main/WikiSandbox]]
* [[(Main.Wiki)Sandbox]]
* [[Main.WikiSandbox | link text]]
* [[Main.WikiSandbox | +]]

To link to the default home page of a group, the name of the page can be omitted, like this:

* [[Main.]]
* [[Main/]]

Creating groups

Creating a new group is as easy as creating new pages; simply edit an existing page to include a link to the new group's default home page (or any page in the new group) then click on the '?' to edit the page. As a rule, group names must start with a letter (but this can be changed by the wiki administrator by adding

$GroupPattern = '[[:upper:]\\d][\\w]*(?:-\\w+)*';

in config.php).

For example, to make a default page in the group Foo, create a link to [[Foo/]] (or [[Foo.]]). To make a page called Bar in the group Foo, create a link to [[Foo/Bar]] and follow the link to edit that page.

Groups in a standard PmWiki distribution

  • Main: The default group. On many wikis, it contains most of the author-contributed content. Main.HomePage and Main.WikiSandbox come pre-installed.
  • PmWiki: An edit-protected group that contains PmWiki documentation and help pages.
  • Site: Holds a variety of utility and configuration pages used by PmWiki, including
    SideBar, Search, Preferences, Templates, and AllRecentChanges.
  • SiteAdmin?: Holds a number of password protected administration and configuration pages used by PmWiki, including
    ApprovedUrls, and Blocklist
  • To list all the groups in a site, try searching for "fmt=group".
  • To list all the pages in a group, try searching for "GroupName/".

Special Pages in a Group

By default, the Recent Changes page of each group shows only the pages that have changed within that group; the Site.All Recent Changes page shows all pages that have changed in all groups.

Each group can also have Group Header or Group Footer pages that contain text to be automatically prepended or appended to every page in the group. A group can also have a Group Attributes page that defines attributes (read and edit passwords) shared by all pages within the group.

Each page can also have its own individual read/edit password that overrides the group passwords (see Passwords).

Finally, wiki administrators can set local customizations on a per-group basis--see Group Customizations.

Group's default page

The default "start page" for a group is a page whose name is:

  1. the same as the group (Foo/Foo)
  2. HomePage (HomePage?)
  3. a name that the administrator has assigned to the {$DefaultName} variable in the configuration.php file.

Note, on this site, the value of {$DefaultName} is HomePage and, thus, the default home page would be HomePage?.

You can usefully change the default search order for an entered page name by setting the variable $PagePathFmt in config.php, eg

$PagePathFmt = array('$Group.$1', '$1.$DefaultName', '$1.$1', '$DefaultGroup.$1', 'Profiles.$1');

where "$1" is the name of the page entered.

As noted above, when linking to the default home page, authors can omit the page name and simply identify the group followed by a forward slash ([[Foo/]]).

Note the forward slash is required to ensure that the link unambiguously points to the identified group. If the slash is omitted, the link can end up being interpreted as pointing to an existing (or new) page in the current group (if the group, or its default home page, do not exist).

Subgroups? Subpages?

No, PmWiki does not have subpages. Pm's reasons for not having subgroups are described at PmWiki:Hierarchical Groups, but it comes down to not having a good page linking syntax. If you create a link or pagename like [[A.B.C]] PmWiki doesn't think of "B.C" as being in group "A", it instead thinks of "C" as being in group "AB", which is a separate group from "A". Wiki administrators can look at Cookbook:Subgroup Markup and Cookbook:Include With Edit for recipes that may be of some help with developing subgroups or subpages.

Restricting the creation of new groups

You can set PmWiki's $GroupPattern variable to only accept the group names you want to define. For example, to limit pages to the "PmWiki", "Main", "Profiles", and "Example" groups, add the following to local/config.php:

$GroupPattern = '(?:Site|SiteAdmin?|PmWiki|Main|Profiles|Example)';

With this setting, only the listed groups will be considered valid WikiGroups. You can add more groups to the list by placing additional group names separated by pipes (|).

See other solutions to this at Cookbook:Limit Wiki Groups and Cookbook:New Group Warning.

How can I get rid of the 'Main' group in urls for pages pointing to Main?

See Cookbook:Get Rid Of Main.

How can I limit the creation of new groups?

See Cookbook:Limit Wiki Groups.

Why doesn't [[St. Giles and St. James]] work as a link? (It doesn't display anything.)

Because it contains periods, and destroys PmWiki's file structure, which saves pages as Group.PageName. Adding those periods disrupts this format. Links may only contain words. If you need a link precisely as shown, the page must be named eg StGilesAndStJames? then you can use the (:title:) directive to have the page's title appear with periods (:title St. Giles and St. James:). (Although in US grammar the period is often omitted and in UK grammar the period must be omitted for contractions like St).

How can I delete a wiki group?

Normally you can't, as this requires an admin with server-side access to delete the file that makes up the group's RecentChanges page. But there is an option method of making it possible to delete RecentChanges pages from within the wiki if the admin enables the code found on Cookbook:RecentChanges Deletion.

How can I delete a wiki group's Group.RecentChanges? page?

Normally you can't, as this requires an admin with server-side access to delete a file. But there is an optional method of making it possible to delete RecentChanges pages from within the wiki if the admin enables the code found on Cookbook:RecentChanges Deletion.

GroupHeaders and GroupFooters?

Every WikiGroup can have GroupHeader and GroupFooter pages that contain markup that should be included at the beginning or end of each page within the group. This feature is useful for:

  • adding a disclaimer or heading to all of the pages of a group
  • defining custom WikiStyles that may be used for all pages in a group
  • replacing the default headers and/or footers for pages in a group (e.g., using (:noheader:) and or (:nofooter:) -- see PageDirectives).

To create a group header, just create a new page called YourGroup.GroupHeader. Group headers allow authors to create groups with custom headers and footers without having to coordinate with a wiki administrator.

The default GroupHeader or GroupFooter can be suppressed on an individual page (such as a group's HomePage) by using the (:nogroupheader:) and (:nogroupfooter:) markups on that page.

If a generic GroupHeader is used in one wikigroup (say, the Site wikigroup), then the code can be easily duplicated in the GroupHeader of any other group by using (:include Site.GroupHeader:). See IncludeOtherPages.

If you want a header or footer to appear when you print a page (action print), simply create a page called YourGroup.GroupPrintHeader or YourGroup.GroupPrintFooter and fill it with your markup.

You can also set the variable $GroupPrintHeaderFmt and $GroupPrintFooterFmt in the same way as $GroupHeaderFmt and GroupFooterFmt in order to change the header used when action=print.

See also

How do I set the same header or footer for all pages/groups?

The header and footer for each page are controlled by the variables $GroupHeaderFmt and $GroupFooterFmt. If your site-wide header and footer pages are Site.SiteHeader? and Site.SiteFooter?, you can add this in config.php:

### If you use Site.SiteHeader and Group.GroupHeader
$GroupHeaderFmt = '(:include {$SiteGroup}.SiteHeader'
  . ' basepage={*$FullName}:)(:nl:)' . $GroupHeaderFmt;

### If you use Site.SiteHeader instead of Group.GroupHeader
$GroupHeaderFmt = '(:include {$SiteGroup}.SiteHeader'
  . ' basepage={*$FullName}:)(:nl:)';

### If you use Site.SiteFooter and Group.GroupFooter
$GroupFooterFmt .= '(:nl:)(:include {$SiteGroup}.SiteFooter'
  . ' basepage={*$FullName}:)';

### If you use Site.SiteFooter instead of Group.GroupFooter
$GroupFooterFmt = '(:nl:)(:include {$SiteGroup}.SiteFooter'
  . ' basepage={*$FullName}:)';

Note that single quotes must be used in the lines above.

See also the Cookbook:AllGroupHeader recipe.

Instead of using an additional page, you could set any wiki text in $GroupHeaderFmt, for example:

$GroupHeaderFmt .= "Global message here.";


The WikiTrails feature allows wiki authors to create "trails" through sequences of pages in the wiki. You simply specify pages and their order on a "trail index", and then place the navigation markup on the pages that you will be navigating.

(Don't confuse the pagelist directive with WikiTrails - they are different animals as explained in the Q and A below.)

Creating a trail

Before you can use a trail through a group of pages, you have to create a "trail index" on a separate page, which we will call the "trail index page". On that trail index page, you simply create a numbered or bulleted list of links. (So every numbered or bulleted list of links implicitly creates a trail.) It is important that each page name (link) be the first item following each bullet; any text or formatting in front of the page name will exclude it from the trail.
If you want to format your trail (list), you can include a CSS.

An example trail index page might contain the list:

PmWiki philosophy
Design notes (The first link will, and the second link won't, be in the trail defined by (definition list))
  • Security (This won't be in the trail because its preceded by a (hidden) anchor)
  • Links (This won't be in the trail because its preceded by a (hidden) %newwin% style)
  • Troubleshooting (This won't be in the trail because its preceded by (hidden) italic style markup)

The list above creates the following "wikitrail", displayed using a pagelist:

(:pagelist trail={$FullName}#trailstart#trailend fmt={$FullName}#traillist:)


  1. In general, indentation levels in the page list don't matter -- trails are a linear sequence of pages.
  2. A page is part of the trail only if the page link immediately follows the list markup.
  3. The list itself can be delineated by the use of anchors, allowing for multiple lists on a page, or for some list items to be excluded.

Trail types

PmWiki defines 2 trail markups:

  • <<|[[Trail Index Page]]|>> displays as "<< PreviousPage | Trail Index Page | NextPage >>".
  • <|[[Trail Index Page]]|> displays as "< PreviousPage | Trail Index Page | NextPage >", except the appropriate arrow is omitted at the beginning and end of the trail.

Trail link syntax

The trail link has the same syntax as a standard link, this means for example you can specify

  • <|[[TrailIndexPage | +]]|>

Trail links can be restricted by anchors (links to a specific location within a page), this means you can have more than one trail on a page, or start a trail from a specific location in a page.

  • <|[[Trail Index Page#trailstart#trailend]]|>

Using the trail

What makes a trail "work" is adding trail markup on the pages in the trail (i.e. the pages that are listed in the bullet/numbered list on the trail index page).

To build a trail, add trail markup like <<|[[TrailIndexPage]]|>> to a page, where TrailIndexPage is the page, described above, containing the bulleted list of pages in the trail. PmWiki will display the trail markup with links to any previous and next pages in the trail.

The trail markup can be placed anywhere in a page, and a page can contain multiple trail markups. If you are adding a trail to every page in a group, consider setting the trail markup in the GroupHeader or GroupFooter pages instead of on every individual page in your group.

Path trail

^|[[TrailIndexPage]]|^ treats the list levels as a hierarchy and displays the "path" to reach the current page (i.e., a "breadcrumb" trail). In the example trail above, the markup ^|TrailIndexPage|^ on TrailPage4 would display as "TrailIndexPage | TrailPage2 | TrailPage4".

Wiki administrators can change the trail separator of the "path" trail ( ^|[[TrailIndexPage]]|^ ) from the default | by setting the variable $TrailPathSep in the config.php file. For instance $TrailPathSep = ' > '; will output "TrailIndexPage > TrailPage2 > TrailPage4".

Circular trails

Typically, a trail is a linear list with a first and a last page. However, the trail can be made "circular" by repeating the first page as the last item in the trail index:

 * [[TrailPage1]]
 * [[TrailPage2]]
 * [[TrailPageN]]
 * [[TrailPage1]]

If the trail index page is intended to be read by others, the last item can be made invisible inside an (:if false:) block:

 * [[TrailPage1]]
 * [[TrailPage2]]
 * [[TrailPageN]]
 (:if false:)
 * [[TrailPage1]]

Cross Group Trails

Before version 2.2.1, if your trail contains pages in different groups, it should use full [[Group.Name]] links instead of just [[Name]].

Other notes

  • There is no space between and [[link]] and ; same for the other trail markups.
  • Note that non-existing pages will appear in the WikiTrail as links.

Trail style

PmWiki encapsulates the trail with a wikitrail css class. This allows the wiki trail to be customised by defining CSS for the wikitrail in the local.css file.

Trail in page lists

Trails from a single page can only be displayed using the pagelist trail parameter. For example

(:pagelist trail=PmWiki/WikiTrails fmt=PmWiki.WikiTrails#traillist order=random count=3:)

A simple example of a WikiTrail

1) On the TrailIndexPage?:

* [[MyTrailPage1]]
* [[MyTrailPage2]]
* [[MyTrailPage3]]

2) On the pages MyTrailPage1?, 2, and 3:



What's the difference between a PageList and a WikiTrail?

The pagelist directive dynamically generates a list of pages. There are many ways to generate the list, including using a WikiTrail as the source. The pagelist directive then displays the pages that match the criteria using an optional template - for example displaying each page name on a separate line as a link or including the entire content. The pagelist directive currently does not have built-in navigation markup that you can put on the pages in the list. By contrast, WikiTrails are simply specified via links on an "index" page and you can put previous-next navigation markup on each page. The two serve very different purposes. WikiTrails are useful for specifying the pages in web feeds, for creating a "tour" through a predefined set of pages, and many other things.


When PmWiki is called with '?action=diff', it displays a summary of past edits on a page. Each past edit is shown in a box which shows lines added, changed or deleted during that edit in a before & after format.

Below each box is a "Restore" link. Clicking the link will open an edit box with the page as it was before that edit. You can make changes or simply click Save to restore the text.

There are two additional options specific to PageHistory:

  • Hide minor edits - hides any edit that the author marked as 'minor'. - This is done by adding "&minor=n" to "?action=diff". The default value for this is to show minor edits with "&minor=y"
  • Show changes to output - It shows changes to the rendered output (as opposed to the normal display which shows changes to the wiki-markup). This is done by adding "&source=n" to "?action=diff". You can show changes to markup (the default behavior from 2.2.13) with "&source=y".
  • You can set both by using "?action=diff&source=y&minor=y".

In the default mode "Show changes to markup", you can disable word-level highlighting of differences by adding to config.php such a line:

  $EnableDiffInline = 0;

A page's history is kept for the number of days given by the $DiffKeepDays and $DiffKeepNum variables (set by the site's wiki administrator). When a page is edited, any page history information older than both these values is automatically discarded.

Note that a specific page revision isn't removed from the page until the first edit after the time specified by $DiffKeepDays has elapsed. Thus, it's still possible for some pages to have revisions older than $DiffKeepDays -- such revisions will be removed the next time those pages are edited.

See also

Is there a way to remove page history from page files?

1. Administrators can clean page histories using the Cookbook:ExpireDiff recipe.

2. Administrators with FTP file access can download individual pages from the wiki.d directory, open them in a text editor, manually remove history, and re-upload the files to wiki.d/ directory. Care must be exercised, when manually editing a page file, to preserve the minimum required elements of the page and avoid corrupting its contents. See PageFileFormat#creating.

3. Edit the page. Select all the contents of the edit text area and cut them to the clipboard. Enter delete into the text area and click on the save and edit button. Select all the contents of the edit text area and paste the contents of the clipboard over them. Click on the save button. This will remove all of the page's history up to the final save in which the pasted material is re-added.

How can I restrict viewing the page history (?action=diff) to people with edit permission?

In the local/config.php file, set

$HandleAuth['diff'] = 'edit';

In case of this restriction is set up on a farm, and you want to allow it on a particular wiki, set in your local/config.php :

$HandleAuth['diff'] = 'read';


PmWiki has built-in support for password-protecting various areas of the wiki site. Authors generally want to be able to apply passwords to individual pages or to wiki groups. Wiki Administrators can apply passwords to individual pages, to wiki groups, or to the entire site. Setting an edit password on a page or group (or the entire site) is one of the most common ways to stop spam. As with any access control system, the password protection mechanisms described here are only a small part of overall system and wiki security.

As an author editing pages...

An author will generally set 3 types of passwords:

  1. to control who can see a page or group, use read passwords
  2. to control who can edit a page or group, use edit passwords
  3. to control who can alter the passwords used to protect a page or group, use attr passwords

If required most page actions can be password protected.

Protect an individual page

To set a password on an individual wiki page, add the page action


to the page's URL (address) to access its attributes. Using the form on the attributes page, you can set or clear the read, edit, or attr passwords on the page. In the form you enter the passwords as cleartext; PmWiki encrypts them for you automatically when it stores them.

Additional options:

  • Leaving a field blank will leave the attribute unchanged.
  • To remove a password from a page (reverting back to the group's or site's default), enter
  • To indicate that the page can be edited even if a group or site password is set, enter
  • To lock a page for everybody but the admin, enter
  • To assign the site's site-wide passwords to the read, edit, or attr password for the page, enter
@_site_edit, @_site_read or @_site_upload

Protect a wiki group of pages

To set a password on a wiki group is slightly more difficult -- you just set the passwords on a special page in each group called

First, you can get to the attributes page for GroupAttributes by entering a URL (address) like

Replace with your domain name, and GroupName? with the name of the group

Then, using the form on the attributes page, you can set or clear the read, edit, or attr passwords for the entire group. In the form you enter the passwords as cleartext; PmWiki encrypts them for you automatically.

Additional options:

  • To remove a password from a group (reverting back to the site's default), enter
  • To indicate that the group can be edited even if a site password is set, enter
  • To lock a group for everybody but the admin, enter
  • (Beginning with Ver 2.2.3) To assign the site's site-wide passwords to the read, edit, or attr password for the group, enter
@_site_edit, @_site_read or @_site_upload


Passwords may consist of any combination of characters, except double "quotes" or 'apostrophes'. Passwords with spaces or colons must be entered using quotes, eg "foo bar" or "foo:bar". Obviously longer is better, and on some systems passwords need to have 4 or more characters.

Multiple passwords

Multiple passwords for a page, group or site are allowed. Simply enter multiple passwords separated by a space. This allows you to have a read password, a write password, and have the write password allow read/write access. In other words, if the read password is


and the edit password is


then enter

Set new read password: alpha beta
Set new edit password: beta

This says that either




can be used to read pages, but only


may edit. Since PmWiki checks the passwords you've entered since the browser has been opened, entering a read password that is also a write password allows both reading and writing.

Protect the site

Passwords can be applied to the entire wiki website in config.php. See passwords administration for details.


As an administrator ...

You can set passwords on pages and groups exactly as described above for authors. You can also:

  1. set site-wide passwords for pages and groups that do not have passwords
  2. use attr passwords to control who is able to set passwords on pages
  3. use upload passwords to control access to the file upload capabilities (if uploads are enabled)
  4. use an admin password to override the passwords set for any individual page or group
  5. use SiteAdmin.AuthList to view the permissions settings for pages that have permissions set.

For more information on password options available to administrators, see PasswordsAdmin.

Which password wins?

In PmWiki, page passwords override group passwords, group passwords override the default passwords, and the admin password overrides all passwords. This gives a great deal of flexibility in controlling access to wiki pages in PmWiki.

The special page SiteAdmin.AuthList is a page list of all pages with access permissions set.

Opening access to pages in protected groups/sites

Sometimes we want to "unprotect" pages in a group or site that is otherwise protected. In these cases, the special password


is used to indicate that access should be allowed to a page without requiring a password.

For example, suppose Main.GroupAttributes has an edit password set, thus restricting the editing of all pages in Main. Now we want Main.WikiSandbox to be editable without a password. Using


for the edit password for Main.WikiSandbox doesn't unprotect the page, because the password is being set by the group. Instead, we set the edit password for Main.WikiSandbox to the special value


which tells PmWiki to ignore any site-wide or group-level passwords for that page.

How can I password protect all the pages and groups on my site? Do I really have to set passwords page by page, or group by group?

Administrators can set passwords for the entire site by editing the config.php file; they don't have to set passwords for each page or group. For example, to set the entire site to be editable only by those who know an "edit" password, an administrator can add a line like the following to local/config.php:

    $DefaultPasswords['edit'] = crypt('edit_password');

For more information about the password options that are available only to administrators, see PasswordsAdmin.

I get http error 500 "Internal Server Error" when I try to log in. What's wrong?

This can happen if the encrypted passwords are not created on the web server that hosts the PmWiki.
The crypt function changed during the PHP development, e.g. a password encrypted with PHP 5.2 can not be decrypted in PHP 5.1, but PHP 5.2 can decrypt passwords created by PHP 5.1.
This situation normally happens if you prepare everything on your local machine with the latest PHP version and you upload the passwords to a webserver which is running an older version.
The same error occurs when you add encrypted passwords to local/config.php.

Solution: Create the passwords on the system with the oldest PHP version and use them on all other systems.

How can I create private groups for users, so that each user can edit pages in their group, but no one else (other than the admin) can?

Modify the edit attribute for each group to id:username, e.g. set the edit attribute in JaneDoe.GroupAttributes? to id:JaneDoe?.

There is a more automatic solution, but it's probably not a good idea for most wikis. Administrators can use the AuthUser recipe and add the following few lines to their local/config.php file to set this up:

    $group = FmtPageName('$Group', $pagename); 
$DefaultPasswords['edit'] = 'id:'.$group;

This automatically gives edit rights to a group to every user who has the same user name as the group name. Unfortunately it also gives edit rights to such a user who is visiting a same-named group not just for pages in that group, but for any page on the wiki that relies on the site's default edit password. This can create security holes.

How come when I switch to another wiki within a farm, I keep my same authorization?

PmWiki uses PHP sessions to keep track of authentication/authorization information, and by default PHP sets things up such that all interactions with the same server are considered part of the same session.

An easy way to fix this is to make sure each wiki is using a different cookie name for its session identifier. Near the top of one of the wiki's local/config.php files, before calling authuser or any other recipes, add a line like:


You can pick any alphanumeric name for XYZSESSID; for example, for the cs559-1 wiki you might choose


This will keep the two wikis' sessions independent of each other.

Is it possible to test the password level for display and/or if condition? Example: * (:if WriterPassword:) (display Edit link) (:ifend:)

You can use (:if auth edit:). See ConditionalMarkup.


Purpose of categories

Categories (also known as "tags") are a way to organize and find related pages. Categories are implemented by default in PmWiki 2, and in most wikis they don't require any special code or markup, they're just a useful convention. The idea is that every page that falls into a particular subject area should have a link to a shared page containing links to other pages on that subject. These pages are created in the Category group, and thus these subject areas are called "categories".

Using categories

Getting categories to work requires a single step: adding links to each category. A category named Subject is created by adding a link to Category.Subject on any page. When you add the link to a page, the page can be described as being in the category "Subject".

There is a special markup for creating these links which makes categories work more smoothly: [[!Subject]] will create a link to Category.Subject. So [[!Subject]] is a kind of shortcut to the page Subject in the category group.

A Category.GroupFooter file is included in the PmWiki release that contains the line (:pagelist link={*$FullName} list=normal:) so that whenever a category page is displayed, it will show a list of links to pages that reference that page in the category group. Like any other page in wikilib.d you can modify this page and it will not get overwritten by another release.

It is worth noting that rather than using Category.GroupFooter, the pagelist directive can be added to Category.GroupHeader to similar effect; it just depends on whether you'd prefer to have the list of pages appear before or after any text that you add to the individual category pages (which can be edited just like normal pages).

Because we use the normal PageList link= markup, you can use it not only in the category group. If you want to show all pages belonging to the category Subject you can use on any wiki page (:pagelist link=Category.Subject list=normal:).

Similarly, there's no requirement that a "category page" has to be in the Category group -- any page can define a "category" of pages that link to it.

An administrator can override the default category group name of "Category" by setting the $CategoryGroup variable in config.php to another group name. (Normally a change such as this should be done during initial setup on a new wiki; changing this on a wiki with existing content can cause problems with pagelists unless each page with a category is re-saved.)

A page author can also link to a category list without adding the linking page to the category by using [[ {Category.Subject$PageUrl} | Subject ]]. This will create a link that looks like [[!Subject]] without adding the linking page to the category listing.


So, by adding the link [[!Subject]] to a page, a link to that page will automatically appear on the page Category.Subject, as long as Category.GroupFooter has been tweaked appropriately. Thus, you can create a page that automatically creates an alphabetized list of all movies discussed on your wiki by creating links to [[!Movies]] on each film's page; the resulting automatic list would be on the page Category.Movies .

authors (advanced)

Category nesting

Categories have the potential for even greater usefulness because Category.* pages can themselves be placed into categories! To follow an excellent example from John Rankin, let's suppose we have the following film pages in the categories listed to the right:

Film.ShaunOfTheDead   [[!Horror]] [[!Comedy]] [[!2003]]
Film.InMyFathersDen   [[!Drama]] [[!2004]]
Film.TheCorporation   [[!Documentary]] [[!2003]]

Now then, we can create Category.Horror, Category.Comedy, Category.Drama, and Category.Documentary, and in each one of those pages we put [[!Genre]]. In Category.2003 and Category.2004, we put [[!Year]].

So, what happens when we display Category.Genre ? We see links to "Comedy", "Drama", "Documentary", and "Horror", because they're in the Genre category. When we click on one of those links, we see all of the films listed in one of those categories. Similarly, if we click on Category.Year, we see links to "2003" and "2004", each of which in turn displays the list of films for that year.

Finally, in Category.Genre and Category.Year we can put [[!Category]], which makes them "top-level" categories reachable from the Category.Category page. Voila, we now have an instant "hierarchy":


Note however that this isn't a "strict" hierarchy--i.e., any page or category can appear simultaneously in multiple categories. For example, Category.Documentary could be a member of both the Genre and top-level category listings.

Each category page can have content text before the generated list, e.g., to give a generic description of things in the category. (Or it can be empty, which works fine.) It can also contain associations to related categories ("see also" references). For example, in a tourism wiki, the ''bed and breakfast" category might contain a see-also reference to the "self-catering" category.

administrators (intermediate)

The guts of the category markup

As mentioned, all of the necessary markup features for Categories are enabled by default in current releases of PmWiki 2.0, but here's how they work for those who are interested. The use of the Category group as the repository for all categories is determined by setting the $CategoryGroup variable, and the special [[!Subject]] markup is activated by a call to the Markup() function:

Markup('[[!','<links','/\[\[!([^\|\]] ?)\]\]/',
  "<span class='category'>[[$CategoryGroup/$1]]</span>");

Coming up with good category schemes

The hard part about using categories is choosing a good vocabulary. Site content managers may wish to follow the Guidelines for the establishment and development of monolingual thesauri (ISO 2788-1986) and the Guidelines for the establishment and development of multilingual thesauri (ISO 5964-1985). Questions to think about include:

  • whether a scheme already exists and can be reused
  • number of levels in a multilevel scheme (not too shallow, not too deep -- e.g. 3)
  • number of categories per page (not too many, not too few -- e.g. 3)
  • consistent use of singular ([[Mercury]] is a [[!planet]]) or plural ([[Mercury]] is in the [[!planets]] category)
  • disambiguation and use of phrases ([[!musical instruments]] and [[!medical instruments]]) or Cookbook:Subpage Markup ([[!Instruments*Musical]] and [[!Instruments*Medical]])

Or you can just let people use whatever category terms they find meaningful. A vocabulary (or "folksonomy") will emerge over time.

Showing a list of categories

To show a list of categories we can use a pagelist for the pages in the category group. For instance the following will list pages in the Category group, put it on page Category.Category for convenience, or on any other page:

(:pagelist group=Category list=normal fmt=#title:)

But there is a problem: Just adding a category markup to a page will not create a corresponding category page, even though following the link will show the page with a list of pages linking to it!
To have category pages automatically created in group 'Category' add the following to config.php:

$AutoCreate['/^Category\./'] = array('ctime' => $Now, 'text' => $page['text']);

Change 'Category' to the name of your category group. You can also add more definitions for more category groups, useful if you use a recipe like Cookbook:Tagger which allows multiple category groups.

See also EditVariables#AutoCreate


PmWiki comes with two directives for generating lists of pages -- (:pagelist:) and (:searchresults:). Both directives are basically the same and each accepts the parameters documented below. The primary difference between the two is that searchresults generates the "Results of search for ..." and "### pages found out of ### searched" messages around the results.

The (:searchbox:) directive generates a search form (input text box) to submit search queries. The markup generally accepts the same parameters as (:pagelist:), which makes it possible to restrict, order and format searchresults in the same ways that are described below for a (:pagelist:). For more information about the (:searchbox:) directive, and the ways in which it differs from a (:pagelist:), skip to the section below.

Basic syntax

  • (:pagelist:)
without any arguments shows a bulleted list of all pages, as links, ordered alphabetically and in groups.
shows a pagelist according to the parameters supplied. Parameters are optional.


Any argument supplied within (:pagelist:) that isn't in the form 'key=value' is treated as text that either must (or must not) exist in the page text.

The minus sign (-) can be used to indicate things that should be excluded. Thus

(:pagelist trail=PmWiki.DocumentationIndex list=normal apple -pie:)

lists all "normal" pages listed in the Documentation Index trail that contain the word "apple" but not "pie".

With page text variables

You can also use page text variables as a key to list pages according to the existence of a page text variable. Eg :

(:pagelist $:pagetextvar=avalue:)

lists pages having $:pagetextvar set to avalue.
Minus sign (-), wildcards (?*) and a comma separated list of values also works when specifying a selection based on pagetextvariables. Eg :

(:pagelist $:apagetextvar=t*,-test:)

lists pages having $:apagetextvar like 't*' but not 'test'.

PTV is set (is not empty):(:pagelist $:MyPageTextVariable=- :)
PTV is empty or not set:
 (ie, is not set to one char followed by 0 or more chars)
(:pagelist $:MyPageTextVariable=-?* :)
PTV is not VALUE:(:pagelist $:MyPageTextVariable=-VALUE :)
PTV is set and not YES:(:pagelist $:MyPageTextVariable=?*,-YES :)

Be aware that if using (:pagelist $:MyPTV=$:YourPTV :) PTVs? include PmWiki formatting, so you may not get the matches you expect. Currently the only way around this is to use wild cards, so if the formatting is embedded you may be out of luck.

With page variables (PV)

Page variables can be used within pagelists in the same way as page text variables. See Page Text Variables above for more details. Simply use $var instead of $:var.

group= and name=

The "group=" and "name=" parameters limit results to pages in a specific group or with a specific name:

All pages in the Pmwiki group: (:pagelist group=PmWiki :)
All pages except those in the PmWiki or Site groups: (:pagelist group=-PmWiki,-Site :)
All RecentChanges pages (:pagelist name=RecentChanges :)
All pages except RecentChanges (:pagelist name=-RecentChanges :)


Name and group parameters can contain wildcard characters that display only pages matching a given pattern:

  • An asterisk (*) represents zero or more characters
  • A question mark (?) represents exactly one character


All pages in any group beginning with "PmWiki" (:pagelist group=PmWiki* :)
All pages in any group beginning with "PmWiki", except for Chinese (:pagelist group=PmWiki*,-PmWikiZh :)
All pages in the PmCal group with names starting with "2005": (:pagelist name=PmCal.2005* :)
All Cookbooks with names beginning with a A and a B letter
note the different separators used for the same result
(:pagelist group=Cookbook name=A*,B*   :)
(:pagelist group=Cookbook name="A* B*" :)
(:pagelist group=Cookbook name=[AB]*   :)
(:pagelist group=Cookbook, name=[AB]*   :)

If you want to use multiples conditions in name you need to use quotes or commas to delimit the string.


The "trail=" option obtains the list of pages to be displayed from a WikiTrail:

  • Display pages in the documentation by modification time
(:pagelist trail=PmWiki.DocumentationIndex order=-time:)
  • Display five most recently changed pages
(:pagelist trail=RecentChanges count=5:)


The "list=" option allows a search to include or exclude pages according to predefined patterns set by the administrator.


The "fmt=" option determines how the resulting list should be displayed. PmWiki predefines several formats:

  • fmt=#bygroup - Display pages within groups (default format)
  • fmt=#simple - Display a simple ordered list of pages in the form Group.Name
  • fmt=#title - Display a list of pages by page title. Use "order=title" to have them sorted by title (default is to order by page name).
  • fmt=#group - Display a list of wikigroups (without listing the pages in the groups)
  • fmt=#include - Display the contents of each page in the list (note, this could take a very long time for long lists!)

These formats are defined by page list templates, which can be customized.

This format is not predefined by a page list template:

  • fmt=count - Display the number of pages in the list (note the absence of the "#"). In a trail, fmt=count counts existing and non-existing pages ; to limit count to existing pages, use : if="exists {=$FullName}" fmt=count (mailing list).


The "link=" option implements "backlinks" -- i.e., it returns a list of pages with a link to the target. It's especially useful for category pages and finding related pages.

(:pagelist link=PmWiki.DocumentationIndex:)
  • all pages with links to the current page
(:pagelist link={$FullName}:)
  • all pages in the "Skins" category
(:pagelist link=Category.Skins:)


The "count=" option provides the ability to

  • limit the pagelist to a specific number of pages
  • subsets of a list
  • return items from the end of a list, subsets of a list
  • display pages in reverse sequence
A simple bullet list of ten most recently modified pages

(:pagelist trail=Site.AllRecentChanges count=10 fmt=#simple:)

Display the first ten pages of a list

count=10 # display the first ten pages of list

Negative numbers specify pages to be displayed from the end of the list:

count=-10 # display last ten pages of list

Ranges may be specified using '..', thus:
count=1..10       # first ten pages of list
count=5..10       # 5th through 10th pages of list
Negative numbers in ranges count from the end of the list:

count=-10..-5 # 10th from end, 9th from end, ..., 5th from end

Omitting the start or end of the range uses the start or end of the list:
count=10..        # skip first ten pages
count=..10        # 1st through 10th page of list
count=-10..       # last ten pages of list
count=..-10       # all but the last nine pages
Ranges can be reversed, indicating that the order of pages in the output should likewise be reversed:
count=5..10       # 5th through 10th pages of list
count=10..5       # same as 5..10 but in reverse sequence
count=-1..1       # all pages in reverse sequence
"Reverse sequence" here refers to the sequence after any sorting has taken place. Therefore the three directives to the right are equivalent:
(:pagelist order=-name count=10:)
(:pagelist order=-name count=1..10:)
(:pagelist order=name count=-1..-10:) 


The "wrap" option has the values, none and inline.

With "wrap=inline" and "wrap=none", the output from pagelist (markup or HTML) is directly embedded in a page's markup without any surrounding <div> class=...</div> tags.

With "wrap=inline", any surrounding <ul> is continued. Without "wrap=inline", the HTML output starts a new <ul>. This is important if you want to get a second level <ul> produced by the page list since starting a new <ul> with "**" doesn't yield a second level <ul> but <dl><dd><ul>...

"wrap=inline" likely has other effects since it suppresses the call to $FPLTemplateMarkupFunction (being MarkupToHTML? by default).


By default, a pagelist has the 'fpltemplate' class. The 'bygroup', 'simple', 'group' and 'title' page list formats have specific class names fplbygroup, fplsimple etc. You can set any class using the class= parameter or by setting the $FPLFormatOpt array.


With (:pagelist [other parameters] request=1:) you can override most pagelist parameters, by providing request parameters in the URL. For example, (:pagelist order=name request=1:) will normally sort the list by name. But if the page's URL contains ?order=time, the list will be sorted by time. If the URL contains ?order=, the list will be unordered.


The req=1 parameter makes the pagelist execute only when the search terms are posted (that is, when the user presses "search" on a search form). Note that (:pagelist request=1 req=1:) works mostly like (:searchresults:) without the lines "Results of search for ..." and "X pages found out of Y pages searched". Both "request=1" and "req=1" are needed.


The "passwd" option returns only those pages that have some sort of password attribute on them.


The "if" option allows a condition to be specified as part of the pagelist processing, rather than from within the page list template. Only those pages for which the condition is true are retrieved. Anything that could go within an (:if ...:) can be used as a condition. For example

  (:pagelist if="date {(ftime %GW%V {*$Name})} {=$Name}" :)

returns all of the pages where the name is in the same week as that of the current page.

If any arguments within the quotes could contain a space they must be quoted:

  (:pagelist if="date 2009-01-01..2009-12-31 '{=$:Mydate}'" :)


The "order=" option allows the pages in the list to be sorted according to different criteria. Use a minus sign to indicate a reverse sort. Multiple sorting criteria can be specified using a comma, and you can create your own custom pagelist sort order:

  • order=name - alphabetically by name (default order)
  • order=title - alphabetically by title rather than names
  • order=time - most recently changed pages last
  • order=ctime - time of page creation (see note)
  • order=group,title - by multiple criteria, in this instance sort by title within groups
  • order=random - shuffle the pages into random sequence
  • order=$:pagetextvarname - alphabetically by page text variable value
  • order=$pagevarname - alphabetically by page variable value

Also, the order= option allows custom ordering functions to be written.

  • Note: trail= preserves the order of the pages as they appear on the trail (unless you've specified order= explicitly or there is a default order in the page list template). So PmWiki's alphabetical default order does not apply when trail= is specified.
  • Note: ctime was added to pages only from pmwiki 2.1.beta15 onwards, pages created by earlier versions don't carry a ctime attribute and can't be sorted that way.


Pagelist has the capability to cache lists which greatly speeds up processing (when $PageListCacheDir is set). Every once in a while this caching can result in undesired results. Specifying cache=0 disables caching.

Specifying variables as parameters

You can also specify variable values inline with the pagelist statement, and refer to the variables in the template using the {$$variable1} format:

(:pagelist fmt=#pagelist variable1="value" variable2="value2":)

This assumes that a site has $EnableRelativePageVars enabled, which is recommended in PmWiki 2.2.0 -- but disabled by default to help people upgrading from 2.1.x.

For example, in the template:

(:template default count=1 ParamName=Simon:)
Hi, {$$ParamName}, how are you today?

(:template default count=1 ParamName?=Simon:) Hi, {$$ParamName}, how are you today?

This gives:

(:pagelist fmt=#tvars ParamName="Sam":)

(:pagelist fmt=#tvars ParamName="Sally":)

(:pagelist fmt=#tvars:)

Hi, Sam, how are you today?

Hi, Sally, how are you today?

Hi, Simon, how are you today?

See also $EnableUndefinedTemplateVars.


Include the contents of a random page from the Banners group:

(:pagelist group=Banners order=random count=1 fmt=#include list=normal:)

Display a simple list of the last ten recently changed pages:

(:pagelist trail=Site.AllRecentChanges count=10 fmt=#simple:)

Display the "top twenty" biggest cookbook pages:

(:pagelist group=Cookbook order=-size count=20 :)

The Searchbox Directive

The (:searchbox:) directive generally accepts the same parameters as (:pagelist:) and (:input text:) directives:

  • Pagelist parameters can be added to the input text of a searchbox (or to the markup, or both)
  • Input text box parameters can be added to the searchbox markup
    • An initial search string can be specified in the searchbox markup, but it must be in the form value='search string'. That search string is displayed in the input text and can be modified by when the search is run.
    • The size of the text input field can be specified with the size parameter, where "size=40" would specify the current default value.
      • Tip: If more than one searchbox appears on a page, adding a blank initial value like this value='', to the markup for each searchbox will prevent a search string for one box from populating all of the other boxes.
  • The target page for displaying searchbox results can be set with the parameter target=GroupName.PageName?. The default is the current page.
  • The entire searchbox form can be overridden by defining the $SearchBoxFmt variable in one's configuration file. If $SearchBoxFmt is defined, then the parameters to (:searchbox:) are ignored, and the content of the $SearchBoxFmt variable are used instead.

The additional parameter label="Label" can be used to change the label of the associated submit button:

 (:searchbox label="Search this wiki":)

The Searchresults directive

The (:searchresults:) directive generally accepts the same parameters as (:pagelist:) and (:input text:) directives.

Customizing "Results of search for..." and "3 pages found out of..."

To change the text surrounding the search results, customize the following and add it to local/config.php or $FarmD/local/farmconfig.php. Note that 'en' should be changed to the localized language.

XLSDV('en', array(
        'SearchFor' => 'Results of search for <em>$Needle</em>:',
        'SearchFound' => 
                '$MatchCount pages found out of $MatchSearched pages searched.'

Alternatively, adjust the 'SearchFor?' and 'SearchFound?' phrases in your translation pages.

The $SearchResultsFmt variable can also be set in local/config.php or $FarmD/local/farmconfig.php.

SDV($SearchResultsFmt, "<div class='wikisearch'>\$[SearchFor]
  <div class='vspace'></div>\$MatchList
  <div class='vspace'></div>\$[SearchFound]</div>");

See Also


To delete a page, edit the page, select (highlight) all text in the edit textarea and replace it with the single word


Note that it may be a good idea to add a comment to the field summary explaining why you deleted the page. (The field summary is usually found just below the edit textarea).

After saving the changes the page is deleted. As an added safety feature, the deleted page still exists on the server (with a timestamp) and can be restored to the former page by the wiki administrator.

If you suspect that a page has been deleted but aren't sure, have a look at the wikigroup's RecentChanges. Erasing a page counts as editing the page, and the activity is recorded there and on Site.AllRecentChanges.

The default word used for page deletion ("delete") can be changed in config.php by setting the variable $DeleteKeyPattern (see Edit Variables). If there is a danger of malicious page deletion it may be a good idea to change the delete word to something more obscure. There is also a recipe for creating a separate delete action at Cookbook:DeleteAction.

Removing deleted pages

The deleted pages are kept in the wiki.d directory, with an extension of ,del-123456789 where 123456789 is a unique number (timestamp). A wiki administrator may log into the server via FTP or SSH and periodically remove these files.

One way to remove the files is to delete them from your file manager. If you have shell access, you could use different commands, for example, go into the wiki.d directory and type one of these lines:

  rm -f *,del-*
  find . -name '*,del-*' -delete

Alternatively, the Cookbook:CleanUp recipe can purge those unused files. See also BackupAndRestore.

Deleting Wiki Groups & Categories: Once I create a Group or Category, how can I get rid of that group or category?

An admin can just remove the unwanted pages from wiki.d/ . Normally, doing it via the wiki doesn't resolve the problem, since it counts as an "update" which causes the Recent Changes page to be re-created, but it is possible to modify the site's configuration to allow deletion of the group's RecentChanges page -- see Cookbook:RecentChangesDeletion. To delete a category, first delete the [[!Category]] links from all pages to that category, then just delete the category page as explained above.

PmWiki Installation

This page explains how to download and install PmWiki 2.1 and 2.2. Here's a list of related pages:

  • Requirements - Pre-requisites for running the PmWiki wiki engine
  • Upgrades - How to upgrade an existing PmWiki installation
  • Wiki Farms - Running multiple wikis from a single installation
  • Change Log - Log of changes made to PmWiki by Release

Improvements to these instructions are always appreciated. Just report any problems you encounter to the pmwiki-users mailing list or use the PmWiki Issue Tracking System.

Installing PmWiki

If you upgrade, please read the page Upgrades and Release notes.

1. Download

Download the latest stable version of PmWiki as a

Download the latest beta version from the PmWiki:Subversion page.

2. Unpack

Unpack the archive (tar zxvf pmwiki-latest.tgz or unzip This will create a pmwiki-x.y.z directory containing the PmWiki software. For example, the current "latest" should unpack to a directory named pmwiki-2.3.32. The files in this directory include:

  README.txt        An introductory document
  pmwiki.php        The main PmWiki script
  local/            Configuration scripts (local configuration files)
  cookbook/         Recipes (add-ons) from the Cookbook
  docs/             Brief documentation, sample configuration scripts
  pub/              Publicly accessible files
  pub/css/          Extra CSS stylesheet files [6]
  pub/skins/        Layout templates for custom look and feel
  scripts/          Scripts that are part of PmWiki
  wikilib.d/        Bundled default PmWiki pages

The pmwiki-x.y.z directory needs to be placed into a location accessible by your webserver (e.g., in a public_html directory of some sort). You can place files and directories using a number of methods -- FTP, or a Unix mv or cp command generally does the job.

Note: It is recommended to change the "pmwiki-x.y.z" directory name to be simply "pmwiki" or just "wiki".

3. Create directories

In most cases PmWiki will do this for you. Open a web browser to the pmwiki.php script on the server (i.e., not the one on your local computer or accessed using a file://... URL). PmWiki will then analyze your system configuration and provide instructions (if needed) for creating the wiki.d/ directory which will be used to hold the pages created for your site.

Otherwise, there are two ways to achieve this. (Use Filezilla or WinSCP to change FTP file/folder permissions.)

3a. You can create the wiki.d/ directory manually, and then give it full write permissions (i.e., "chmod 777 wiki.d"). Use this method when "safe mode" is activated in the server's PHP installation.
3b. On some systems you can let PmWiki create wiki.d/ by temporarily changing the permissions on the directory containing the pmwiki.php file to 2777. In Unix this is commonly done by changing to the directory containing pmwiki.php and executing the command
chmod 2777 .
(note the dot at the end). The chmod command also works in many FTP programs. Creating wiki.d/ in this manner will (1) make the directory writable so the web server can create the data directory it needs for the wiki files, (2) preserve group ownership of the directory so the installer account can manipulate the files created in this directory, and (3) make it more difficult for other accounts on the same server to access the files in wiki.d/.

After establishing directory permissions, try opening a browser to the pmwiki.php script again. If all is well, the wiki.d directory will have been created and you'll see the default home page.

Important: If you used method 3b, you should reset permissions by executing "chmod 755 ." in the directory containing pmwiki.php.

4. Initialize

Check out Initial Setup Tasks for other tasks you may want to perform to begin customizing your PmWiki installation. You might also want to peruse the Release Notes for further information.

5. Set language

If you want to use PmWiki in a different language download the international language pack as zip archive ( from Then extract it and copy the files into the wikilib.d/ directory as described above. Besides the -all file you can also download your country localization file only.

Languages available are:

There are two directories in the decompressed i18n archive, scripts and wikilib.d. Copy the files respectively contained in these directories to the scripts and wikilib.d of your PmWiki directory. For example, for French localization, PmWikiFr.* and PmWiki.* must be contained in the same directory.

Then, enable localization by adding an instruction to local/config.php to load the language translation page of your choice. For instance, XLPage('fr','PmWikiFr.XLPage'); loads the French language page PmWikiFr.XLPage.

Read more about this on Internationalizations.


  • The PmWiki distribution deliberately doesn't include an index.php file. You can easily add your own "wrapper script" in the same directory as pmwiki.php. Create a new file called index.php with the following single line of text (missing a closing " ?>" tag deliberately):
    <?php include_once('pmwiki.php');

Resist the temptation to rename pmwiki.php to index.php because if you rename the file it will not be overwritten during an upgrade.

  • If using the Unix tar command to unpack the archive in step 2 above, be sure that the files are created with sufficient permissions for the webserver to be able to access them. Usually you can ensure this by typing umask 002 on the command line before unpacking the tar file.
  • When installing on Windows you should take a look at Cookbook:SimultaneousEdits to enable simultaneous edits on that platform.
  • Additional tips can be found at Troubleshooting.

See also:

Should I rename pmwiki.php to index.php?

Renaming pmwiki.php is not recommended. Instead, create an index.php file that contains this single line

<?php include_once('pmwiki.php');

How do I make pmwiki.php the default page for a website?

Create an index.php file that runs PmWiki from a subdirectory (pmwiki/ for example) and place it in the site's web document root (the main directory for the website).

<?php chdir('pmwiki'); include_once('pmwiki.php');

Note: You will also need to explicitly set the $PubDirUrl variable (e.g. to "") in local/config.php .

How do I enable "Clean URLs?" that are shorter and look like paths to my wiki pages? Why does appear to have a directory structure rather than "?n=pagename" in URLs??

See Cookbook:CleanUrls.

How can I run PmWiki on a standalone (offline, portable) machine ?

See Cookbook:Standalone or Cookbook:WikiOnAStick.


Once you have PmWiki running on your site (see Installation), you can customize it for your particular needs.

Most PmWiki configuration is performed in files called local/config.php and pub/css/local.css. Some configuration is done on special pages in the Site and SiteAdmin? groups, such as the Site.SideBar menu.

The local configuration file (local/config.php)

When you first install PmWiki, the local/config.php file does not exist. Copy the sample-config.php file (in the docs/ directory) to local/config.php and use it as a starting point. You could create local/config.php from scratch, but sample-config.php is already populated with many of the options you might want to adjust.

Here is a simple config.php file:

<?php if (!defined('PmWiki')) exit();
$WikiTitle = "My New Wiki";
$PageLogoUrl = "";

# Uncomment these if needed
#$ScriptUrl = '';
#$PubDirUrl = '';

$DefaultPasswords['admin'] = crypt('onesecret');

$EnableUpload = 1;
$DefaultPasswords['upload'] = crypt('secrettwo');

# Uncomment and change these if needed
# putenv("TZ=EST5EDT"); # if you run PHP 5.0 or older
# date_default_timezone_set('America/New_York'); # if you run PHP 5.1 or newer

$TimeFmt = '%B %d, %Y, at %I:%M %p EST';

Note that config.php begins with <?php . The PHP end tag ?> is optional, and can be left off to improve compatibility with some operating systems. Be sure there aren't any blank lines or spaces before the <?php or after any closing ?>, or else you may get strange PHP error messages at the beginning of your wiki pages.

The config.php file above sets the value of PHP variables used by PmWiki:

  • The $WikiTitle variable gives the name of your site as it will appear in a user's browser title bar.
  • The $ScriptUrl and $PubDirUrl variables tell your wiki where it is located. Often pmwiki can guess, but if you have difficulty with links not working or skins not being found then try uncommenting these lines.
  • The $PageLogoUrl variable specifies the URL of the icon image that will appear in the upper-left corner of each wiki page.
  • The $DefaultPasswords['admin'] sets an administrative password.
  • Setting $EnableUpload to "1" enables Uploads ("Attached files"). $DefaultPasswords['upload'] sets an upload password.
  • The TZ environment variable defines a particular time zone (see Cookbook:ChangeTimeFormat). If your site runs on PHP 5.1 or newer, it is recommended to use the function date_default_timezone_set, see below.
  • The date_default_timezone_set tells PHP what the default time zone is. For other ways to set the time zone, and a list of identifiers, see the online PHP manual.
  • The $TimeFmt variable defines the appearance of time strings and (along with TZ) localizes the wiki to a specific time zone (see Cookbook:ChangeTimeFormat).

By setting these (and other) variables in local/config.php, you can change the look and feel of PmWiki from its default, sometimes substantially so. See PmWiki.Variables for a list of variables that PmWiki uses, and see PmWiki:PmWikiUsers for examples of sites that use PmWiki in customized ways.

Other common setup tasks

The following variables are often requested when preparing a new wiki

These common Cookbook recipes are also often installed immediately

If you prepare an international wiki, potentially with characters in different alphabets (Cyrillic, Greek, Chinese) or many diacritical symbols (Czech + French), please look at PmWiki.UTF-8 and Cookbook:UTF-8.


Review and set up any security required.

Setting an administrative password

The pages in the Site group (except the Site.SideBar) are locked by default. In order to edit pages in this group you need to create a site-wide admin password in local/config.php. To set the site-wide admin password to "mysecret", change the line to the following:

$DefaultPasswords['admin'] = crypt('mysecret');

You must use the crypt() function, but set the password to a value with meaning for you. See PasswordsAdmin for details about making the password more secure.

Don't modify or rename pmwiki.php

PmWiki has been designed so that all customizations can be made without changing the distribution files -- one of its design goals is to provide seamless upgrades. PmWiki never writes to files in the local/ or cookbook/ directories, so placing your customizations here makes it easier to track the changes and upgrade PmWiki without losing the changes.

When changing the configuration of your site, always change the local/config.php file or add files to the cookbook/ or pub directories. Do not change pmwiki.php or the files in the scripts/ directory because the files are supposed to be overwritten upon upgrading.

You shouldn't rename pmwiki.php either. If you rename the file it will not be overwritten during an upgrade of the software and there will be a version mismatch. Many administrators add an index.php "wrapper script" in the pmwiki directory that contains the following single line:

<?php include('pmwiki.php');

Just make an text-file. Paste <?php include('pmwiki.php'); into it. Save the file as index.php Send it via FTP to the same directory as pmwiki.php is located.

Other organisation

Upload directories

By default Pmwiki uses an upload directory for each group (see Uploads administration. Deciding on accepting the default, or choosing an alternative (eg one directory for the entire wiki, or one directory per page) is best done when setting up your wiki.

Page store directories

By default Pmwiki uses a single page store directory (wiki.d). Deciding on accepting the default (recommended), or choosing the alternative (one directory per group) is best done when setting up your wiki. [7]

Other customization

After setting up local/config.php file, you may wish to make other local customizations. See the PmWiki Cookbook for a large number of customizations that have been contributed. And don't fear Cookbook recipes - they're well prepared, so that most of them require only to download a single file, add a one-line include command to config.php, and voilà! - they're working!

If you (or others sharing your server) want to maintain more than one wiki on the same server, see Wiki Farms.

Now what?

Don't forget to join a PmWiki mailing list, where you can email other wiki administrators for help on customizing PmWiki and participate in discussions about PmWiki improvements. Once you have your site operational, be sure to add it to PmWiki:PmWikiUsers so others will know about it!


PmWiki is designed to make it easy to upgrade the PmWiki software without affecting your existing data files or installation. For most upgrades, you simply copy the files in the new release over your existing installation.

Note for PmWiki 1.0 sites: Upgrading from 1.0.x to 2.0 requires more than simply copying the 2.0 software over the 1.0 installation. See Upgrading From PmWiki 1 for more details.

To upgrade PmWiki:

1. Read the release notes

Please read carefully the ReleaseNotes before performing an upgrade, about the changes between your previous version and the new one. See if there are any significant changes or preparation tasks that must be handled before performing the upgrade.

2. Backup

It's always a good idea to have a backup copy of your existing PmWiki installation before starting. You can copy the entire directory containing your existing installation, or you can just make copies of the wiki.d/ directory and any other local customization files you may have created (e.g., config.php, localmap.txt, etc.).

3. Download and extract

Download the version of PmWiki that you want from the download page.

Extract the tar image using tar -xvzf tgzfile, where tgzfile is the tar file you downloaded above. This will create a pmwiki-x.y.z directory with the new version of the software.

4. Copy

Copy the files in pmwiki-x.y.z over the files of your existing PmWiki installation. For example, if your existing PmWiki installation is in a directory called pmwiki, then one way to copy the new files over the existing ones is to enter the command:

cp -a pmwiki-x.y.z/. pmwiki

Note that BSD systems will not have the -a option as a command-line argument for cp, but that's okay, since it's just shorthand for cp -dpR, so use that instead of -a.

Some environments have an alias established for cp that enable interactive prompts before overwriting a file. To work around this specify the absolute path to cp, such as /bin/cp.

On (some) FreeBSD servers and Mac OS X systems you need to use

cp -Rpv pmwiki-x.y.z/. pmwiki

5. Update customisations and recipes

That's it! Your base PmWiki installation is complete.

Now use the PmWiki:Site Analyzer to determine which recipes could be updated to the most recent version.

Unless you have made customizations to the pmwiki.php script or to the files in scripts/, your PmWiki installation should continue to run correctly! (Changes to these files are not recommended).

(Local customizations should go in local/config.php, pub/css, and pub/skins/yourskinname)

Note: Additional tips can be found on the PmWiki:Troubleshooting page.

How can I determine what version of PmWiki I'm running now?

See version - Determining and displaying the current version of PmWiki (pmwiki-2.3.32).

How can I test a new version of PmWiki on my wiki without changing the prior version used by visitors?

The easy way to do this is to install the new version in a separate directory, and for the new version set (in local/config.php):

    $WikiLibDirs = array(&$WikiDir,
      new PageStore('/path/to/existing/wiki.d/{$FullName}'),
      new PageStore('wikilib.d/{$FullName}'));

This lets you test the new version using existing page content without impacting the existing site or risking modification of the pages. (Of course, any recipes or local customizations have to be installed in the new version as well.)

Then, once you're comfortable that the new version seems to work as well as the old, it's safe to upgrade the old version (and one knows of any configuration or page changes that need to be made).

Backup and Restore

This page has some background information on making backups and explains some basic *nix backup and restore procedures.


Your wiki installation contains some unique data in the following directories:

    local/         Local configuration scripts
    cookbook/      Recipes obtained from the Cookbook
    pub/           Publicly accessible files
    wiki.d/        Wiki pages
    uploads/       Uploaded files (attachments)

A good backup plan will include periodically archiving these directories — or at bare minimum local/ and wiki.d/. Good practice dictates keeping your backup archives on a separate machine.

Simple Backup and Restore (*nix)

When it comes to backup, simpler is better. Since the pmwiki distribution is very small (about 1/4 megabyte), it's simplest to just archive the distribution files along with the data.

Making a Backup Archive

The following *nix command, executed from the parent directory of your wiki's directory, will put a complete backup archive of your site in your home directory.

tar -zcvf  ~/wiki-backup-`date +%Y%m`.tar.gz  wiki/

Restoring the Backup Archive

Simple Method

Your site can be restored and running in under 30 seconds with

tar -zxvf ~/wiki-backup-200512.tar.gz
find wiki/uploads/ -type d |xargs chmod 777
find wiki/wiki.d/ -type d |xargs chmod 777

A Slightly-More-Secure Method

The simple restore commands above will give you world-writable files and directories. You can avoid world-writable permissions by letting PmWiki create directories with the proper attributes (ownership and permissions) for you.

Start with

tar -zxvf ~/wiki-backup-200512.tar.gz
rm -rf wiki/wiki.d
rm -rf uploads
chmod 2777 wiki/

Now upload a file in each group that had uploads. If your site doesn't have uploads, just visit your site once so the wiki.d/ directory will be created.

Finish your installation with

chmod 755 wiki/
tar -zxvf ~/wiki-backup-200512.tar.gz


The commands on this page assume your site is in a directory called "wiki/". The test backup was made in December, 2005 so it's named accordingly.

Your site will only have an uploads/ directory if uploads are enabled.

The backup command uses a date stamp (YYYYMM) in the filename. If you automate the command via cron you'll wind up with monthly snapshots of your site. You can get a daily snapshot by appending %d to the date command (`date +%Y%m%d` will get you YYYYMMDD). Be wary of space limitations if you have a large uploads/ directory.

See Also

  • A thread [] on the pmwiki-users mailing list.
  • A Backup Pages recipe in the cookbook.


Backup via FTP

Download and install a ftp client like Filezilla

  1. Using the ftp client connect to the server where you host pmWiki using
    1. the IP address (ex: or the ftp name (ex:
    2. supply your account name (ex: mylogin) and password (ex: myp4ssw0rd)
  2. Move to your pmWiki directory (ex: /usr/mylogin/web/wiki/ or /tahi/public_html/pmwiki )
  3. Select the folder you want to backup as explained before (probably either only the data or the whole wiki directory)
    • for data you will want to backup both the directories
      • wiki.d for user page data
      • pmwikiuploads (or uploads) for your attachments (uploads)
    • for system you will want, at a minimum, to backup both the directories
      • local for configuration data
      • pub for local CSS and skins customisations
  4. Download them to a local folder
  5. Use 7zip or a similar software to build an archive of this backup

You can also very easily sync your FTP directories with your hard disc via this command line:

wget -nv -np -m

Download Wget for Windows (other systems normally have it installed).

Alternatively, you can also mirror your FTP directories with lftp:

lftp -u your_user_name,your_password -e "mirror --verbose /wiki.d /path/to/local/folder" ftp://your_host

(this will mirror only the /wiki.d folder, replace with / to mirror everything)

Using rsync

See Cookbook:BackupWithRsync and Cookbook:TwoWayMirroringWithRsync.

Uploads Administration

PmWiki includes a script called upload.php that allows users to upload files to the wiki server using a web browser. Uploaded files (also called attachments) can then be easily accessed using markup within wiki pages. This page describes how to install and configure the upload feature.

Some notes about security

PmWiki takes a somewhat, but justifiable, paranoid stance when it comes to the uploads feature. Thus, the default settings for uploads tend to try to restrict the feature as much as possible:

  • The upload function is disabled by default
  • Even if you enable it, the function is password locked by default
  • Even if you remove the password, you're restricted to uploading files with certain names, extensions, and sizes
  • The characters that may appear in upload filenames are (default) alphanumerics, hyphen, underscore, dot, and space (see also here).
  • The maximum upload size is small (50K by default)

This way the potential damage is limited until/unless the wiki administrator explicitly relaxes the restrictions.

Keep in mind that letting users (anonymously!) upload files to your web server does entail some amount of risk. The upload.php script has been designed to reduce the hazards, but wiki administrators should be aware that the potential for vulnerabilities exist, and that misconfiguration of the upload utility could lead to unwanted consequences.

By default, authorized users are able to overwrite files that have already been uploaded, without the possibility of restoring the previous version of the file. If you want to disallow users from being able to overwrite files that have already been uploaded, add the following line to config.php:

$EnableUploadOverwrite = 0;

Alternatively, an administrator can keep older versions of uploads.

An administrator can also configure PmWiki so the password mechanism controls access to uploaded files.

Basic installation

The upload.php script is automatically included from stdconfig.php if the $EnableUpload variable is true in config.php. In addition, config.php can set the $UploadDir and $UploadUrlFmt variables to specify the local directory where uploaded files should be stored, and the URL that can be used to access that directory. By default, $UploadDir and $UploadUrlFmt assume that uploads will be stored in a directory called uploads/ within the current directory (usually the one containing pmwiki.php). In addition, config.php should also set a default upload password (see PasswordsAdmin).

Thus, a basic config.php for uploads might look like:

<?php if (!defined('PmWiki')) exit();
##  Enable uploads and set a site-wide default upload password.
$EnableUpload = 1;
$DefaultPasswords['upload'] = crypt('secret');

If you have edit passwords and wish to allow all users with edit rights to upload, instead of $DefaultPasswords['upload'], you can set $HandleAuth['upload'] = 'edit'; in config.php.

Important: do NOT create the uploads directory yet! See the next paragraph.

You may also need to explicitly set which filesystem directory will hold uploads and provide a URL that corresponds to that directory like:

$UploadDir = "/home/foobar/public_html/uploads";
$UploadUrlFmt = "";

Note: In most installations, you don't need to define or change these variables, usually PmWiki can detect them (and if you do, uploads may simply not work).

Upload directory configuration

Uploads can be configured site-wide, by-group (default), or by-page by changing $UploadPrefixFmt in config.php. This determines whether all uploads go in one directory for the site, an individual directory for each group, or an individual directory for each page. The default is to organize upload by group.
It is recommended that the $UploadPrefixFmt variable defined in config.php is the same for all pages in the wiki, and not different in group or page local configuration files. Otherwise you will be unable to link to attachments in other wikigroups.

Single upload directory

For site-wide uploads, use

$UploadPrefixFmt = '';

Per page upload directories

To organize uploads by page, use:

$UploadPrefixFmt = '/$Group/$Name';

You may prefer uploads attached per-page rather than per-group or per-site if you plan to have many files attached to individual pages. This setting simplifies the management of picture galleries for example. (In a page, you can always link to attachments to other pages.)

The upload directory

For the upload feature to work properly, the directory given by $UploadDir must be writable by the web server process, and it usually must be in a location that is accessible to the web somewhere (e.g., in a subdirectory of public_html). Executing PmWiki with uploads enabled will prompt you with the set of steps required to create the uploads directory on your server (it differs from one server to the next). Note that you are likely to be required to explicitly create writable group- or page-specific subdirectories as well!

Uploading a file

Once the upload feature is enabled, users can access the upload form by adding "?action=upload" to the end of a normal PmWiki URL. The user will be prompted for an upload password similar to the way other pages ask for passwords (see Passwords and PasswordsAdmin for information about setting passwords on pages, groups, and the entire site).

Another way to access the upload form is to insert the markup "Attach:filename.ext" into an existing page, where filename.ext is the name of a new file to be uploaded. When the page is displayed, a '?-link' will be added to the end of the markup to take the author to the upload page. (See Uploads for syntax variations.)

By default, PmWiki will organize the uploaded files into separate subdirectories for each group. This can be changed by modifying the $UploadPrefixFmt variable. See Cookbook:UploadGroups for details.

Versioning Uploaded Files

PmWiki does not manage versioning of uploaded files by default. However, by setting $EnableUploadVersions=1; an administrator can have older versions of uploads preserved in the uploads directory along with the most recent version.

Upload restrictions

Restricting uploaded files for groups and pages

Uploads can be enabled only for specific groups or pages by using a group customization. Simply set $EnableUpload=1; for those groups or pages where uploading is to be enabled; alternately, set $EnableUpload=1; in the config.php file and then set $EnableUpload=0; in the per-group or per-page customization files where uploads are to be disabled.

Restricting total upload size for a group or the whole wiki

Uploads can be restricted to an overall size limit for groups. In the group configuration file (i.e., local/Group.php), add the line

$UploadPrefixQuota = 1000000; # limit group uploads to 1000KB (1MB)

This will limit the total size of uploads for that group to 1000KB --any upload that pushes the total over the limit will be rejected with an error message. This value defaults to zero (unlimited).

Uploads can also be restricted to an overall size limit for all uploads. Add the line

$UploadDirQuota = 10000000; # limit total uploads to 10000KB (10MB)

This will limit the total size of uploads for the whole wiki to 10000KB --any upload that pushes the total over the limit will be rejected with an error message. This value defaults to zero (unlimited).

Restricting uploaded files type and size

The upload script performs a number of verifications on an uploaded file before storing it in the upload directory. The basic verifications are described below.

the name for the uploaded file can contain only letters, digits, underscores, hyphens, spaces, and periods, and the name must begin and end with a letter or digit.
file extension
only files with approved extensions such as ".gif", ".jpeg", ".doc", etc. are allowed to be uploaded to the web server. This is vitally important for server security, since the web server might attempt to execute or specially process files with extensions like ".php", ".cgi", etc.
file size
By default all uploads are limited to 50K bytes, as specified by the $UploadMaxSize variable. Thus, to limit all uploads to 100KB, simply specify a new value for $UploadMaxSize in config.php:
$UploadMaxSize = 100000;

However, the default maximum file size can also be specified for each type of file uploaded. Thus, an administrator can restrict ".gif" and ".jpeg" files to 20K, ".doc" files to 200K, and all others to the size given by $UploadMaxSize. The $UploadExtSize array is used to determine which file extensions are valid and the maximum upload size (in bytes) for each file type. For example:

$UploadExtSize['gif'] = 20000; # limit .gif files to 20KB

Setting an entry to zero disables file uploads of that type altogether:

$UploadExtSize['zip'] = 0;  # disallow .zip files
$UploadExtSize[''] = 0;     # disallow files with no extension

You can limit which types of files are uploadable by disabling all defaults and specifying only desired types Setting the variable $UploadMaxSize to zero will disable all default file types. Individual file types may then be enabled by setting their maximum size with the variable $UploadExtSize.

# turns off all upload extensions
$UploadMaxSize = 0;

# enable only these file types for uploading
$aSize=100000; // 100 KB file size limitation
$UploadExtSize['jpg' ] = $aSize;
$UploadExtSize['gif' ] = $aSize;
$UploadExtSize['png' ] = $aSize;

Adding new file types to permitted uploads

To add a new extension to the list of allowed upload types, add a line like the following to a local customization file:

$UploadExts['ext'] = 'content-type';

where ext is the extension to be added, and content-type is the "MIME type", or content-type (which you may find here or on the lower part of this page) to be used for files with that extension. For example, to add the 'dxf' extension with a Content-Type of 'image/x-dxf', place the line

$UploadExts['dxf'] = 'image/x-dxf';

Each entry in $UploadExts needs to be the extension and the mime-type associated with that extension, thus:

$UploadExts = array(
  'gif' => 'image/gif',
  'jpeg' => 'image/jpeg',
  'jpg' => 'image/jpeg',
  'png' => 'image/png',
  'xxx' => 'yyyy/zzz'

For the types that PmWiki already knows about it's not necessary to repeat them here (the upload.php script adds PmWiki's defaults to whatever the administrator supplies). See also Cookbook:UploadTypes for additional types.

Other file size limits

There are other factors involved that affect upload file sizes. In Apache 2.0, there is a `LimitRequestBody directive that controls the maximum size of anything that is posted (including file uploads). Apache has this defaulted to unlimited size. However, some Linux distributions (e.g., Red Hat Linux) limit postings to 512K so this may need to be changed or increased. (Normally these settings are in an httpd.conf configuration file or in a file in /etc/httpd/conf.d.)

Problem noted on Red Hat 8.0/9.0 with Apache 2.0.x, the error "Requested content-length of 670955 is larger than the configured limit of 524288" was occurring under Apache and a "Page not found" would appear in the browser. Trying the above settings made no change with PHP, but on Red Hat 8.0/9.0 there is an additional PHP config file, /etc/httpd/conf.d/php.conf, and increasing the number on the line "LimitRequestBody 524288" solves the issue.

PHP itself has two limits on file uploads (usually located in /etc/php.ini). The first is the upload_max_filesize parameter, which is set to 2MB by default. The second is post_max_size, which is set to 6MB by default.

With the variables in place--PmWiki's maximum file size, Apache's request-size limits, and the PHP file size parameters, the maximum uploaded file size will be the smallest of the three variables.

Password protecting uploaded files

Setting a read password for pages (and groups) will prevent an attached file from being seen or accessed through the page, but to prevent direct access to the file location (the uploads/ directory) one can do the following:

  • In local/config.php set $EnableDirectDownload=0;
  • If you use per-group upload directories (PmWiki default, see $UploadPrefixFmt), add to config.php $EnableUploadGroupAuth = 1;
  • Deny public access to the uploads/ directory through moving it out of the html/ or public_html/ directory tree, or through a .htaccess file.

See Cookbook:Secure attachments Security issues for attachments

Other notes

  • If uploads doesn't seem to work, make sure that your PHP installation allows uploads. The php.ini file (usually /etc/php.ini or /usr/local/lib/php.ini) should have
file_uploads = On
  • Another source of error in the php.ini file is a not defined upload_tmp_dir. Just set this variable to your temp directory, e.g.
upload_tmp_dir = /tmp

Note that if you change this values, httpd must generally be restarted. Another way to check if uploads are allowed by the server is to set $EnableDiag to 1 in config.php, and set ?action=phpinfo on a URL. The "file_uploads" variable must have a value of 1 (if it says "no value", that means it's off).

How do I disable uploading of a certain type of file?

Here's an example of what to add to your local/config.php file to disable uploading of .zip files, or of files with no extension:

$UploadExtSize['zip'] = 0;  # Disallow uploading .zip files
$UploadExtSize[''] = 0;     # Disallow files with no extension

How do I attach uploads to individual pages or the entire site, instead of organizing them by wiki group?

Use the $UploadPrefixFmt variable (see also the Cookbook:UploadGroups recipe).

$UploadPrefixFmt = '/$FullName'; # per-page, in Group.Name directories
$UploadPrefixFmt = '/$Group/$Name'; # per-page, in Group directories with Name subdirectories
$UploadPrefixFmt = ''; # site-wide

For $UploadDirQuota - can you provide some units and numbers? Is the specification in bytes or bits? What is the number for 100K? 1 Meg? 1 Gig? 1 Terabyte?

Units are in bytes.

   $UploadDirQuota = 100*1024;         # limit uploads to 100KiB
   $UploadDirQuota = 1000*1024;        # limit uploads to 1000KiB
   $UploadDirQuota = 1024*1024;        # limit uploads to 1MiB
   $UploadDirQuota = 25*1024*1024;     # limit uploads to 25MiB
   $UploadDirQuota = 2*1024*1024*1024; # limit uploads to 2GiB

Is there a way to allow file names with Unicode or additional characters?

Yes, see $UploadNameChars

Where is the list of attachments stored?

It is generated on the fly by the


How can I find orphaned or missing attachments

For older versions of PmWiki, see Cookbook:Attachlist enhanced "How to list missing or orphaned attachments.",
for version 2.2 or higher see Cookbook:Attachtable "Actions to rename, delete & restore deleted attachments, as well as an attachlist replacement to use those actions, show file types and list attachment references."

How can I prevent hotlinking of my uploaded images

See Cookbook:Prevent Hotlinking Prevent hotlinking of uploaded files

I have limited the max upload size to 8 MB in config.php, however only files smaller than 2MB can be uploaded.

Check your php.ini for upload_max_filesize

upload_max_filesize = 8M

If you cannot access your php.ini directly try to soften server limits in the root .htaccess file:

php_value post_max_size 63M
php_value upload_max_filesize 62M
php_value memory_limit 64M
php_value max_execution_time 600
php_value default_socket_timeout 600

How can I upload multiple files at once?

See the following recipes: DragDropMultiUpload, MultiUpload, UploadForm.


Aspects of PmWiki security are found on the following pages:

Pages distributed in a PmWiki release:

  • Passwords General use of passwords and login
  • Passwords Admin More password options for the administrator
  • AuthUser Authorization system that uses usernames and passwords
  • Url Approvals Require approval of Url links
  • Site Analyzer Analyse PmWiki security and software versions
  • Blocklist Blocking IP addresses, phrases, and expressions to counteract spam and vandalism.
  • Notify How to receive email messages whenever pages are changed on the whole wiki site, individual groups or selected watchlists of pages
  • Security variables variables crucial for site security


How do I report a possible security vulnerability of PmWiki?

Pm wrote about this in a post to pmwiki-users from September 2006. In a nutshell he differentiates two cases:

  1. The possible vulnerability isn't already known publicly: In this case please contact us by private mail.
  2. The possible vulnerability is already known publicly: In this case feel free to discuss the vulnerability in public (e.g. on pmwiki-users or in the PITS).

See his post mentioned above for details and rationals.

What about the botnet security advisory at

Sites that are running with PHP's register_globals setting set to "On" and versions of PmWiki prior to 2.1.21 may be vulnerable to a botnet exploit that is taking advantage of a bug in PHP. The vulnerability can be closed by turning register_globals off, upgrading to PmWiki 2.1.21 or later, or upgrading to PHP versions 4.4.3 or 5.1.4.
In addition, there is a test at PmWiki:SiteAnalyzer that can be used to determine if your site is vulnerable.

Wiki Vandalism and Spam

you are using a Blocklist and Url approvals.
You don't want to resort to password protecting the entire wiki, that's not the point after all.
Ideally these protections will be invoked in config.php

How do I stop pages being deleted, eg password protect a page from deletion?

Use Cookbook:DeleteAction and password protect the page deletion action by adding $DefaultPasswords['delete'] = '*'; to config.php or password protect the action with $HandleAuth['delete'] = 'edit';

or $HandleAuth['delete'] = 'admin'; to require the edit or admin password respectively.

How do I stop pages being replaced with an empty (all spaces) page?

Add block: /^\s*$/ to your blocklist.

how do I stop pages being completely replaced by an inane comment such as excellent site, great information, where the content cannot be blocked?

Try using the newer automatic blocklists that pull information and IP addresses about known wiki defacers.

(OR) Try using Cookbook:Captchas or Cookbook:Captcha (note these are different).

(OR) Set an edit password, but make it publicly available on the Site.AuthForm template.

How do I password protect the creation of new groups?

See Cookbook:Limit Wiki Groups How to limit the names or number of groups in your wiki

How do I password protect the creation of new pages?

See Cookbook:Limit new pages in Wiki Groups How to limit the creation of new pages in your wiki group

How do I take a whitelist approach where users from known or trusted IP addresses can edit, and others require a password?

Put these lines to local/config.php:

## Allow passwordless editing from own turf, pass for others.
if ($action=='edit'
 && !preg_match("/^90\\.68\\./", $_SERVER['REMOTE_ADDR']) )    
 { $DefaultPasswords['edit'] = crypt('foobar'); }

Replace 90.68. with the preferred network prefix and foobar with the default password for others.

For a single IP, you may use

if($_SERVER['REMOTE_ADDR'] == '') { # your IP address here
 $_POST['authpw'] = 'xxx';                  # the admin password

Please note the security issues : this means that you have your admin passwords in clear in config.php and someone with access to the filesystem can read them (for example a technician of your hosting provider) ; your IP address may change from time to time (unless you have a fixed IP contract with your ISP). When that happens, someone with your old IP address will be logged in automatically as admin on your wiki. It is extremely unlikely to become a problem, but you should know it is possible ; if you are behind a router, all other devices which pass through that router will have the same IP address for PmWiki - your wifi phone, your wife's netbook, a neighbour using your wifi connection, etc. All these people become admins of your wiki. Again, you should evaluate if this is a security risk ; In some cases, your ISP will route your traffic through the same proxy as other people. In such a case, thousands of people may have the same IP address.

See also Cookbook:AuthDNS & Cookbook:PersistentLogin

How do I password protect page actions?

See Passwords for setting in config.php

$HandleAuth['pageactionname'] = 'pageactionname'; # along with :
$DefaultPasswords['pageactionname'] = crypt('secret phrase');


$HandleAuth['pageactionname'] = 'anotherpageactionname';

How do I moderate all postings?

Enable PmWiki.Drafts

  • Set $EnableDrafts, this relabels the "Save" button to "Publish" and a "Save draft" button appears.
  • Set $EnablePublishAttr, this adds a new "publish" authorization level to distinguish editing from publishing.

How do I make a read only wiki?

In config.php set an "edit" password.

How do I restrict access to uploaded attachments?


How do I hide the IP addresses in the "diff" pages?

If the user fills an author name, the IP address is not displayed. To require an author name, set in config.php such a line:

  $EnablePostAuthorRequired = 1;

The IP address can also be seen in a tooltip title when the mouse cursor is over the author name. To disable the tooltip, set in config.php:

$DiffStartFmt = 
  "<div class='diffbox'><div class='difftime'><a name='diff\$DiffGMT' href='#diff\$DiffGMT'>\$DiffTime</a>
   \$[by] <span class='diffauthor'>\$DiffAuthor</span> - \$DiffChangeSum</div>";



PmWiki's markup translation engine is handled by a set of rules; each rule searches for a specific pattern in the markup text and replaces it with some replacement text. Internally, this is accomplished by using PHP's "preg_replace" function.

Rules are added to the translation engine via PmWiki's Markup() function, which looks like

Markup($name, $when, $pattern, $replace);

where $name is a unique name (a string) given to the rule, $when says when the rule should be applied relative to other rules, $pattern is the pattern to be searched for in the markup text, and $replace is what the pattern should be replaced with.

For example, here's the code that creates the rule for ''emphasized text'' (in scripts/stdmarkup.php):

Markup("em", "inline", "/''(.*?)''/", "<em>$1</em>");

Basically this statement says to create a rule called "em" to be performed with the other "inline" markups, and the rule replaces any text inside two pairs of single quotes with the same text ($1) surrounded by <em> and </em>.

The first two parameters to Markup() are used to specify the sequence in which rules should be applied. The first parameter provides a name for a rule -- "em" in the example above. We could've chosen other names such as "''", or even "twosinglequotes". In general PmWiki uses the markup itself to name the rule (i.e., PmWiki uses "''" instead of "em"), but to keep this example easier to read later on we'll use a mnemonic name for now.

The second parameter says that this rule is to be done along with the other "inline" markups. PmWiki divides the translation process into a number of phases:

_begin      start of translation
fulltext    translations to be performed on the full text            
split       conversion of the full markup text into lines to be processed
directives  directive processing
inline      inline markups
links       conversion of [[links]], url-links, and WikiWords     
block       block markups
style       style handling       
_end        end of translation

This argument is normally specified as a left-angle bracket ("before") or a right-angle bracket ("after") followed by the name of another rule.

Thus, specifying "inline" for the second parameter says that this rule should be applied when the other "inline" rules are being performed. If we want a rule to be performed with the directives -- i.e., before inline rules are processed, we would specify "directives" or "<inline" for the second parameter.

A significant rule in terms of ordering is "{$var}" which substitutes variables -- if you say "<{$var}" then your markup will be processed before variables are substituted whereas if you say ">{$var}" then your markup will be processed after variables are substituted.

The third parameter is a Perl-compatible regular expression. Basically, it is a slash, a regular expression, another slash, and a set of optional modifiers.

The example uses the pattern string "/''(.*?)''/", which uses ''(.*?)'' as the regular expression and no options. (The regular expression says "find two single quotes in succession, then as few arbitrary characters as are needed to make the match find something, then two additional single quotes in succession"; the parentheses "capture" a part of the wikitext for later use.)

The fourth parameter is the replacement text that should be inserted instead of the marked-up wikitext. You can use $1, $2, etc. to insert the text from the first, second etc. parenthesised part of the regular expression.

In the example, we have "<em>$1</em>", which is an <em>, the text matched by the first parentheses (i.e. by the .*? section of the pattern), and </em>.

Here's a rule for @@monospaced@@ text:

Markup("@@", "inline", "/@@(.*?)@@/", "<code>$1</code>");

and for a [:comment ...:] directive that is simply removed from the output:

Markup("comment", "directives", "/\\[:comment .*?:\\]/", '');

Okay, now how about the rule for '''strong emphasis'''? We have to be a bit careful here, because although this translation should be performed along with other inline markup, we also have to make sure that the rule for ''' is handled before the rule for '', because ''' also contains ''. The second parameter to Markup() can be used to specify the new rule's relationship to any other rule:

Markup("strong", "<em", "/'''(.*?)'''/", "<strong>$1</strong>");

This creates a rule called "strong", and the second parameter "<em" says to be sure that this rule is processed before the "em" rule we defined above. If we wanted to do something after the "em" rule, we would use ">em" instead. Thus, it's possible to add rules at any point in PmWiki's markup translation process in an extensible manner. (In fact, the "inline", "block", "directives", etc., phases above are just placeholder rules used to provide an overall sequence for other rules. Thus one can use "<inline" to specify rules that should be handled before any other inline rules.)

If you want to disable available markup just call e.g.:


PmWiki's default markup rules are defined in the scripts/stdmarkup.php file. To see the entire translation table as the program is running, the scripts/diag.php module adds "?action=ruleset", which displays the set of defined markup rules in the sequence in which they will be processed. You can see it at CustomMarkup?action=ruleset. You must first enable the action by setting $EnableDiag = 1 in your configuration file.

Other common examples

Define a custom markup to produce a specific HTML or Javascript sequence

Suppose an admin wants to have a simple "(:example:)" markup that will always produce a fixed HTML string in the output, such as for a webring, Google AdSense? display, or Javascript. The Markup() call to do this would be:

Markup('example', 'directives',
  Keep("<div class='example'><p>Here is a 
    <a target='_blank' href=''>link</a> to
    <em></em></p></div>") );
  • The first argument is a unique name for the markup ("example").
  • The second argument says to perform this markup along with other directives.
  • The third argument is the pattern to look for "(:example:)".
  • The fourth argument is the HTML that "(:example:)" is to be replaced with. We use the Keep() function here to prevent the output from being further processed by PmWiki's markup rule -- in the above example, we don't want the url to be again converted to a link.

Define a markup to call a custom function that returns content

An 'e' option on the $pattern parameter will cause the $replace parameter to be treated as a PHP expression to be evaluated instead of replacement text. Thus, a markup to produce a random number between 1 and 100 might look like:

Markup('random', 'directives',
  "rand(1, 100)");

This calls the PHP built-in rand() function and substitutes the directive with the result. Any function can be called, including functions defined in a local customization file.

Arguments can also be passed by using regular expression capturing parentheses, thus

Markup('randomargs', 'directives',
  '/\\(:random (\\d+) (\\d+):\\)/e',
  "rand('$1', '$2')");

will cause the markup (:random 50 100:) to generate a random number between 50 and 100.

Note: Be very careful with the /e modifier in regular expressions; malicious authors may be able to pass strings that cause arbitrary and undesirable PHP functions to be executed.

For a PmWiki function to help with parsing arbitrary sequences of arguments and key=value pairs, see Cookbook:ParseArgs.

How can I embed JavaScript? into a page's output?

There are several ways to do this. The Cookbook:JavaScript recipe describes a simple means for embedding static JavaScript? into web pages using custom markup. For editing JavaScript? directly in wiki pages (which can pose various security risks), see the JavaScript-Editable recipe. For JavaScript? that is to appear in headers or footers of pages, the skin template can be modified directly, or <script> statements can be inserted using the $HTMLHeaderFmt array.

How would I create a markup ((:nodiscussion:)) that will set a page variable ({$HideDiscussion}) which can be used by (:if enabled HideDiscussion:) in .PageActions?

Add the following section of code to your config.php

SDV($HideDiscussion, 0); 	#define var name
Markup('hideDiscussion', '<{$var}',
 '/\\(:nodiscussion:\\)/e', 'setHideDiscussion(true)'); 
function setHideDiscussion($val) { 
  global $HideDiscussion; 
  $HideDiscussion = $val;

This will enable the (:if enabled HideDiscussion:) markup to be used. If you want to print the current value of {$HideDiscussion} (for testing purposes) on the page, you'll also need to add the line:
$FmtPV['$HideDiscussion'] = '$GLOBALS["HideDiscussion"]';

PmWiki only supports tool tips for external links, can I use custom markup to add tool tips to internal links?

Yes, add the following custom markup to your config.php:
Markup('%title%', 'inline', '/%title%(.*?)"(.*?)"(.*?)%%/', '<span title="$2">$1$3</span>'); # Add tool tips to internal links, Example: %title%[[link"tool tip"]]%%

Use the markup with internal links such as:
%title%[[CookBook "cool" | Example]]%%

You can apply it in other situations too, for instance:
%title%"flexible"(:if true:)write something(:else:)write something else(:ifend:)%%

See also Cookbook:LinkTitles.

It appears that (.*?) does not match newlines in these functions, making the above example inoperable if the text to be wrappen in <em> contains new lines.

If you include the "s" modifier on the regular expression then the dot (.) will match newlines. Thus your regular expression will be "/STUFF(.*?)/s". That s at the very end is what you are looking for. If you start getting into multi-line regexes you may be forced to look at the m option as well - let's anchors (^ and $) match not begin/end of strings but also begin/end of lines (i.e., right before/after a newline).

How can the text returned by my markup function be re-processed by the markup engine?

If the result of your markup contains more markup that should be processed, you have two options. First is to select a "when" argument that is processed earlier than the markup in your result. For example, if your markup may return [[links]], your "when" argument could be "<links" and your markup will be processed before the links markup. The second option is to call the PRR() function in your markup definition or inside your markup function. In this case, after your markup is processed, PmWiki will restart all markups from the beginning.

How do I get started writing recipes and creating my own custom markup?

(alternate) Introduction to custom markup for Beginners

How do I make a rule that runs once at the end of all other rule processing?

Use this statement instead of the usual Markup() call:

$MarkupFrameBase['posteval']['myfooter'] = "\$out = onetimerule(\$out);";


This page describes the predefined Wiki Styles and how a Wiki Administrator can define additional Wiki Styles as a local customization for all pages (in local/config.php) or specific groups (in local/$Group.php).

All predefined Wiki Styles are setup in the global array $WikiStyle. To define your own Wiki Styles, add the setting of the correspondent WikiStyle within the array.

Predefined Wiki Styles

The following array-values are set by scripts/wikistyles.php using the SDV()-function (so you can overwrite them by setting them prior in config.php or farmconfig.php):


text colors:
(equivalent to %define=xxxx color=xxxx%)
black$WikiStyle['black']['color'] = 'black';
white$WikiStyle['white']['color'] = 'white';
red$WikiStyle['red']['color'] = 'red';
yellow$WikiStyle['yellow']['color'] = 'yellow';
blue$WikiStyle['blue']['color'] = 'blue';
gray$WikiStyle['gray']['color'] = 'gray';
silver$WikiStyle['silver']['color'] = 'silver';
maroon$WikiStyle['maroon']['color'] = 'maroon';
green$WikiStyle['green']['color'] = 'green';
navy$WikiStyle['navy']['color'] = 'navy';
purple$WikiStyle['purple']['color'] = 'purple';

decimal$WikiStyle['decimal']['apply'] = 'list';
$WikiStyle['decimal']['list-style'] = 'decimal';
roman$WikiStyle['roman']['apply'] = 'list';
$WikiStyle['roman']['list-style'] = 'lower-roman';
ROMAN$WikiStyle['ROMAN']['apply'] = 'list';
$WikiStyle['ROMAN']['list-style'] = 'upper-roman';
alpha$WikiStyle['alpha']['apply'] = 'list';
$WikiStyle['alpha']['list-style'] = 'lower-alpha';
ALPHA$WikiStyle['ALPHA']['apply'] = 'list';
$WikiStyle['ALPHA']['list-style'] = 'upper-alpha';

open links in a new browser-window:
newwin$WikiStyle['newwin']['target'] = '_blank';
Turns markup into a comment via display:none CSS
comment$WikiStyle['comment']['display'] = 'none';

wiki styles
frameborder:1px solid #cccccc; padding:4px; background-color:#f9f9f9;
lfloatfloat:left; margin-right:0.5em;
rfloatfloat:right; margin-left:0.5em;
lframeframe lfloat
rframeframe rfloat
preblock white-space:pre
sideheadblock class:sidehead

Author-Defined Wiki Styles

  1. The first index of the array defines the style name (e.g. mynewstyle, projectentry etc)
  2. the second index defines the attribute name (e.g. color, background-color, etc.)
  3. the value set defines the attribute value (e.g. red, bold, #00ffcc, etc.)

Sample: If you want to define a (site-wide) style the same as the page style

%define=projectentry color:red%


$WikiStyle['projectentry']['color'] = 'red';

The $WikiStyle['projectentry']['apply'] variable may be defined if the wikistyle concerns a particular tag. It may be 'item' (for li|dt), 'list' (for ul|ol|dl), 'div', 'pre', 'img', 'p' or the combining 'block' (for p|div|ul|ol|dl|li|dt|pre|h[1-6]). Example:

 $WikiStyle['top']['apply'] = 'item';
 $WikiStyle['top']['class'] = 'top';

then a markup

 * %top% An important list-item

will output

 <li class="top">An important list-item</li>

Printer-Friendly Styles

If your custom-styles (in local/config.php) are getting very colorful it might be useful to disable them in print-view. This can be done easily by putting them into a condition.

if($action!="print") {
  // your custom-styles


To be done:


I tried this but background didn't work, thou border and float worked? /Vincent 2008-04-08

$WikiStyle['vMenu']['background']='#ffffcc' ;
$WikiStyle['vMenu']['float']='left' ;
$WikiStyle['vMenu']['border']='1px dotted red' ;
Try using $WikiStyle['vMenu']['background-color']='#ffffcc'; -- unlike background, background-color is defined in the $WikiStyleCSS array, which is checked for valid properties.

Q: How would i set an image to the left of a paragraph in a WikiStyle? I'd like to provide an icon for paragraphs that are notes, important, warnings, etc.


PmWiki supports internationalization (internationalisation) of web pages, allowing accented characters to appear in page names and almost complete customization of PmWiki's prompts. Most customization is provided via the XLPage?() function in PmWiki, which loads a set of translation variables from a wiki page (typically named XLPage?, but it can be named anything you wish).

The rest of this page is devoted to the installation, configuration and usage of other language(s) support. If you are looking for tools and help to localize PmWiki in your language, or how you can improve the existing translations, start on with the page Localization - The Translation Portal.

Loading Translation Pages

Pages for many other languages have already been created and maintained at the site. You can download an archive of these translations from . Simply download the appropriate language archive(s), and unpack the archive(s) into the directory containing your pmwiki.php installation. Each archive contains a number of page files that are placed in your wikilib.d/ directory, and some special scripts for translations that use a character set other than iso-8859-1 (PmWiki's default). You can also use UTF-8 charset.

Once the translation pages are installed, you enable a language by adding a call to XLPage() in your config.php file. For example, to select French language prompts, one would specify

include_once("scripts/xlpage-utf-8.php"); # optional

which says to load the translations for French ('fr') from the page PmWikiFr.XLPage. The include_once line is recommended if you start a new wiki, and it should be placed before the XLPage? line (for languages with alphabets other than the Latin, the include_once line is required). These lines should be placed near the beginning of config.php, but after any $WikiDir and $WikiLibDirs setting (if you have such setting).

It's possible to load multiple pages; so if you want to create your own local translations without changing the ones you got from an i18n archive, just create another page (see below) and load it first. Be sure that you load first the page with your local changes:

XLPage('fr','PmWikiFr.XLPageLocal');  # my local translations
XLPage('fr','PmWikiFr.XLPage');       # from i18n.tgz

If your intention is to offer multiple languages on your site, and use Wiki Groups as language selectors, you may want to place this code in local customizations files (see Group Customizations). For example, if your site is published in French and English, and the French pages are in a group called Fr, you could create a file named Fr.php in the local/ directory which contains:

<?php if (!defined('PmWiki')) exit();
##change to French language

You may wish to create a page called PmwikiFr?.php with the same content to access the French documentation in the PmwikiFr? group. En.php is not necessary in this case since English is the default language.

An alternative to the above would be to add to config.php the following, which tests if there is an XLPage? in a group, and if it finds one it gets loaded:

$xlpage = FmtPageName('$Group.XLPage', $pagename);
if (PageExists($xlpage)) XLPage($xlpage, $xlpage);

With this method you would need to copy any relevant XLPage? into any group which needs the different language support.

See also Cookbook:MultiLanguage Display content in different languages on a page by user's choice

Creating New Translations

If language pages don't exist for your desired language, it's easy to create one! An XLPage? translation file simply contains lines of the form

'phrase' => 'translated phrase',

where "phrase" is an internationalized phrase (denoted by $[phrase]) in PmWiki's $...Fmt variables, and "translated phrase" is what should be printed in your particular language. For example, the line (in PmWikiFr.XLPage)

'Search' => 'Rechercher',

converts "$[Search]" to "Rechercher" on output. The file Localization:XLPageTemplate is a good starting point for creating a new XLPage? and has most of PmWiki's key phrases already listed in it.

If you create new versions of PmWiki pages in other languages, please consider adding them to the main PmWiki site so that they can be made available to others in the i18n archives! (Be sure to check out The Localization Portal for further information on effectively internationalizing PmWiki.)

The term "i18n" is commonly used as an abbreviation for the English word "internationalization". The abbreviation is derived from the fact that there are 18 letters between the "i" and the final "n" and few people want to type them all out.

Enabling "Special" Characters in WikiLinks?

To enable "special" characters like for example German umlauts in WikiLinks?, it is necessary to configure the server locale to ensure that PmWiki uses the proper character set definition.

If this is not possible due to limited access to the server configuration, PmWiki can be configured to use a specific locale by using the XLPage? options (see XLPageTemplate).

For German umlauts, you'd need for example:

  • 'Locale' => 'deu', <- for Windows servers, see MSDN List of locale identifiers
  • 'Locale' => 'de_DE', <- for Linux servers; for the UTF-8 encoding, on some installations you may need to set 'de_DE.utf8' or 'de_DE.UTF-8'.

Note that the locale identifier depends on the operation system and perhaps on the specific installation.


If my wiki is internationalized by config.php, how do I revert a specific group to English?

Use $XLLangs = array('en'); in the group's group customization file.

If my wiki is in English and I want just one page, or group, in Spanish do I say XLPage('es','PmWikiEs.XLPage'); in the group or page configuration file?

Yes, that is usually the best method. If you were doing this with many scattered pages, or with several languages, you might find it easier to maintain if you load the translations all in config.php like this:

   $XLLangs = array('en');

Then in each group or page configuration file, you'd just use $XLLangs = array('es'); to set the language to use (in this case, Spanish). Note that though this method is easier to maintain, its somewhat slower because it loads all the dictionaries for each page view, even if they won't be used.

What does the first parameter of this function stand for? How can it be used?

The XLPage? mechanism allows multiple sets of translations to be loaded, and the first parameter is used to distinguish them.

For example, suppose I want to have translations for both normal French and "Canadian" French. Rather than maintain two entirely separate sets of pages, I could do:

    XLPage?('fr-ca', 'PmWikiFrCa.XLPage?');
    XLPage?('fr', 'PmWikiFr.XLPage');

PmWikiFr.XLPage would contain all of the standard French translations, while PmWikiFrCA.XLPage? would only need to contain "Canada-specific" translations -- i.e., those that are different from the ones in the French page.

The first parameter distinguishes the two sets of translations. In addition, a config.php script can use the $XLLangs variable to adjust the order of translation, so if there was a group or page where I only wanted the standard French translation, I can set

    $XLLangs = array('fr', 'en');

and PmWiki will use only the 'fr' and 'en' translations (in that order), no matter how many translations have been loaded with XLPage?().

How can I add a translation for an individual string in a PHP file?

Use the XLSDV() function to provide a translation for a specific (English) string. For instance, with this in config.php

    XLSDV('nl', array('my English expression'=>'mijn Nederlandse uitdrukking'));

any instance of the variable expression $[my English expression] in wiki mark-up will be displayed as my English expression in default (English) context, but as mijn Nederlandse uitdrukking in Dutch (nl) context, i.e. when XLPage('nl',...) has been called for that page in config.php or a cookbook recipe.

If you need to get a translation in a PHP file, use the XL() function:

  $local_string = XL("my English expression");

But beware: XLPage?() uses XLSDV() internally for its translation pairs, too, and only the first definition is accepted! Thus, if the Dutch XLPage? already contains a translation and you want to override that, you need to use your XLSDV('nl',...) before calling the correspondent XLPage?('nl',...). Otherwise, by using XLSDV() after XLPage?() - e.g. within a recipe that is included later in config.php - your translation will only work as long nobody defines 'my English expression' in that XLPage?.


A Wiki Administrator can make a lot of customizations simply by setting variables in the /local/config.php and defining cascading style sheets in /pub/css/local.css files. Any group or page can also have its own configuration file and configuration css file.

This page describes how customizations work in general, see PmWiki.Documentation Index for specific customizations that are commonly performed at many PmWiki installations, including:


From its inception, PmWiki has been designed so that Wiki Administrators can greatly customize the way PmWiki displays pages and the markup sequences used to generate pages. (This is even mentioned explicitly in PmWiki Philosophy #4.) As a result, the core pmwiki.php script makes extensive use of PmWiki.Variables to determine how markup sequences will be processed and what each individual page will output.

The simplest type of customization is merely setting a variable to 1 (or TRUE). Here's an example that enables ?action=diag and ?action=phpinfo actions:

$EnableDiag = 1;

You can begin a line with a "#" (an octothorpe, a.k.a. a hash symbol or pound sign) to add a comment. Additionally, some built-in PmWiki variables take values other than 1 or 0 (true or false). Here's another example that customizes the wiki's behavior with respect to search engine web robots (see Cookbook:ControllingWebRobots):

# Remove the default "rel='nofollow'" attribute for external links.
$UrlLinkFmt = "<a class='urllink' href='\$LinkUrl' title='\$LinkAlt'>\$LinkText</a>"

The scripts/ subdirectory (below the directory holding the pmwiki.php script) has many customizations. The PmWiki Cookbook contains many example customizations (recipes) that you can download into the cookbook/ subdirectory, The first few lines of each of these scripts generally contain instructions about how to enable (and use) the feature provided by the script.

These customizations are included in your config.php site configuration. For most scripts this is done by simply adding lines like:




at the end of the config.php file to enable them.

Some of the scripts are automatically enabled for you via the scripts/stdconfig.php script unless you disable it by setting $EnableStdConfig=0; in local/config.php.

Order of the commands in config.php (link)

The following order is recommended:

Note, each part is not required, but if your wiki needs it, this is the recommended order in config.php.

Character encoding of config.php

The encoding used when you save config.php has an effect. Your text editor should allow you to save config.php in the encoding of your wiki. (The default encoding of PmWiki is ISO-8859-1, for new wikis it is recommended to enable UTF-8.)

Newer operating systems like GNU/Linux, FreeBSD? and Apple generally default to saving text files in Unicode/UTF-8; in Windows the default encoding is ANSI/Windows-1252 which is almost the same as PmWiki's ISO-8859-1.

The following free/libre software text editors can edit and save a file in different encodings:

Note that if you use the UTF-8 encoding, you should save your files "without Byte Order Mask (BOM)".

Over time PmWiki will be updated to default to Unicode/UTF-8 encoding, which allows all possible alphabets and languages. See UTF-8 for more information.


You can create this file and set there some custom CSS styles which will override any styles set by skins. For example:

  h1, h2, h3, h4, h5 { color: #880000; } /*dark red titles*/
  a { text-decoration: none; } /* don't underline links */

Don't modify pmwiki.php or other core files

You should strongly resist the temptation to directly modify the pmwiki.php script or the files in the scripts/ subdirectory. Any modifications you make to these files will probably be overwritten whenever you upgrade. Instead, look at some of the sample scripts for examples of customizations that can be performed from config.php. You can even create your own script to do a customization and use include_once(...) to include it from config.php. If you do make your own customization script, you can safely put it in the cookbook/ subdirectory--it won't get overwritten by an upgrade there. You might also want to submit your customization to the pmwiki-users mailing list or the Cookbook so that others can benefit from your effort and so that it can perhaps be included in future releases of PmWiki.


There's no "config.php"; it's not even clear what a "local customisation file" is!

The "sample-config.php" file in the "docs" folder, is given as an example. Copy it to the "local" folder and rename it to "config.php". You can then remove the "#" symbols or add other commands shown in the documentation. See also Group Customizations.

Can I change the default page something other than Main.HomePage ($DefaultPage)?

Yes, just set the $DefaultPage variable to the name of the page you want to be the default. You might also look at the $DefaultGroup and $DefaultName configuration variables.

$DefaultPage = 'ABC.StartPage';

How do I get the group / page name in a local configuration file (e.g. local/config.php)?

Use the following markup in pmwiki-2.1.beta21 or newer:

## Get the group and page name
$pagename = ResolvePageName($pagename);
$page = PageVar($pagename, '$FullName');
$group = PageVar($pagename, '$Group');
$name = PageVar($pagename, '$Name');

Note the importance of the order of customizations in config.php above to avoid caching problems.

If you need the verbatim group and page name (from the request) early in config.php, $pagename is guaranteed to be set to

  1. Any value of ?n= if it's set, or
  2. Any value of ?pagename= if it's set, or
  3. The "path info" information from REQUEST_URI (whatever follows SCRIPT_NAME), or
  4. Blank otherwise

according to this posting

Can I remove items from the wikilib.d folder on my site?

The files named Site.* and SiteAdmin?.* contain parts of the interface and the configuration and they should not be removed. The other files named PmWiki* contain the documentation and could be removed.

How do I customize my own 404 error page for non-existent pages?

To change the text of the message, try editing the Site.PageNotFound page.

Is the order of customizations in config.php important? Are there certain things that should come before or after others in that file?

Yes, see Order of the commands in config.php.


One of the purposes of Wiki Groups is to allow a Wiki Administrator to customize the features of PmWiki on a per-group basis. Here is where per group customizations come into play.

  • The local/ subdirectory (typically in $FarmD) is used to hold local configuration files.
  • The pub/css/ subdirectory (typically in $FarmD) is used to hold local css files.

To perform local customizations for a particular WikiGroup,

  • place the customizations in a file called "<groupname>.php" (where <groupname> is the actual name of the page group in question) in the local/ subdirectory
  • place the css customizations in a file called "<groupname>.css" (where <groupname> is the actual name of the page group in question) in the pub/css/ subdirectory.

These files will be automatically processed after processing any local customizations in the config.php and local.css files.

For example, to change the image displayed in the upper-left corner of pages in the "GroupName?" WikiGroup, one could create GroupName?.php containing

  $PageLogoUrl = "/myimages/chess.gif";

The example's effect would cause all pages in the GroupName? Wiki Group to use "/myimages/chess.gif" as the logo image instead of the default.

To add markup to the beginning or end of each page in a wiki group, see Group headers.

Per-page customizations

PmWiki also allows per-page customizations, simply use the full name of the page to be customized instead of the group. For example, one can use the file local/Chess.HomePage.php to set local customizations for Chess.HomePage.

Almost any customization that would be placed in config.php can be used as a per-group or per-page customization.

An important exception to this is setting per-group or per-page customizations for recipe scripts included in config.php. Most recipe scripts would need any customization variables defined before the script is included. Instead of using a per-group or per-page customization php file, use a per-group or per-page conditional statement in config.php, before including the recipe script. Example:

$page = PageVar($pagename, '$FullName');
$group = PageVar($pagename, '$Group');
//per-group customizations:
if($group=='GroupName') {
   $RecipeVariable = 'valueA';
   etc. ...
//per-page customizations:
if($page=='GroupName.PageName) {
   $RecipeVariable = 'valueB';
   etc. ...
//include recipe after variables are set:

Note that this method cannot be used to set $DefaultPasswords, you should use Group or Page attributes. See Passwords and PasswordsAdmin for more information.

Processing order

For all local customizations, PmWiki first processes the local/config.php file, and then looks for a per-page customization file in the local/ subdirectory to process, followed by any per-group customization file. If no per-page or per-group customizations are loaded, then PmWiki loads local/default.php. If a per-page customization wants to have the per-group customizations loaded first, it can do so directly by using PHP's include_once() function. For more information see wiki cascades.

Custom CSS styles per group or per-page

To apply CSS styles to pages of a specific group named Group Name?, create a file named GroupName?.css in the pub/css/ directory and add the CSS style rules there. To apply styles to a specific page, create a file GroupName.PageName?.css in this directory with your style rules. Any CSS rules to be applied for all wiki pages can be put into pub/css/local.css.


  body { background: #F4C4B4; }

Preventing group-Level configurations

Any customization file can set $EnablePGCust=0; to prevent later page/group/default customizations from being automatically loaded. If a per-page customization needs to have the per-group customizations loaded first, it can do so directly by using PHP's include_once() function.


Any passwords required for a group should be set in the group's Group Attributes page (see Passwords Administration) and not in a group customization file.

Consider Wiki Farms

Wiki Groups are an easy way to host multiple sites in a single PmWiki installation by giving each site its own group. Another approach is to use Wiki Farms, which allows each site to have its own set of Wiki Group and local customization files. Read about

If you are looking for nested group levels, you may want to consider Pm's design considerations on hierarchical groups.

How can I apply CSS styles to a particular group or page?

Simply create a pub/css/Group.css or pub/css/Group.Page.css file containing the custom CSS styles for that group or page.

Why shouldn't passwords be set in group (or page) customization files? Why shouldn't group or page passwords be set in config.php?

The reason for this advice is that per-group customization files are only loaded for the current page. So, if $DefaultPasswords['read'] is set in GroupA?.php, then someone could use a page in another group to view the contents of pages in GroupA?. For example, Main.WikiSandbox could contain:

(:include GroupA.SomePage:)

and because the GroupA?.php file wasn't loaded (we're looking at Main.WikiSandbox --> local/Main.php), there's no read password set.

The same is true for page customization files.

Isn't that processing order strange? Why not load per page configuration last (that is after global configuration an per group configuration)?

Many times what we want to do is to enable a certain capability for a group of pages, but disable it on a specific page, as if it was never enabled. If the per-group config file is processed first, then it becomes very difficult/tedious for the per-page one to "undo" the effects of the per-group page. So, we load the per-page file before the per-group.

If a per-page customization wants the per-group customizations to be performed first, it can use the techniques given in PmWiki.GroupCustomizations (using include_once() or setting $EnablePGCust = 0).


What's a skin?

A skin changes the look and feel of a PmWiki page, Group of pages, or the entire wiki. To see this try some skins out using the links below.

As you see, all skins show the same page contents, but the other elements such as the sidebar, header, and footer, have changed. For example, different skins may display the sidebar on the left, on the right, or even not at all. Some skins have action links and features that others do not, especially if they were designed to take advantage of particular cookbook recipes.

So, a skin is just the set of files that determine how pages are displayed in PmWiki. Skins are stored as subfolders of pub/skins/. For example you might create the example skin in pub/skins/example/. Each skin typically has one or more of the following kinds of files:

  • A template file, such as skin.tmpl or example.tmpl. The template is written in HTML or XHTML, and is the skeleton for the skin. It contains special markers that tell PmWiki where to insert the page's contents.
  • CSS stylesheet files, which can control the skin's appearance, such as pmwiki.css or example.css.
  • Image files, for decorating a page with images.
  • PHP files, such as skin.php or example.php. These let skins provide extra customization setting or capabilities that HTML and CSS alone cannot.
  • Documentation files for the administrator, usually with names like readme.txt, install.txt or skinname.txt. These usually give you information about any special installation steps or nifty features the skin has.

Where do I get skins?

Skins are available in the Skins:Skins collection. The skins in the collection have been contributed by many PmWiki administrators for all to use, and typically have their own set of customization possibilities. When you find a skin you like, follow the link to download the skin package. You can also make your own skin.

How do I use or install a skin?

Most skin packages are .zip, .tgz, or .tar.gz files. You should be able to unpack these with most archiving software.

  1. Unpack the skin to pub/skins/ inside your pmwiki folder. Most well-designed skin packages will create a subfolder in pub/skins/ named after the skin.
    • If the skin did not make a folder of its own, create one and move the skin files to it.
  2. Open up your local/config.php file, and add a line like
$Skin = 'example';

where example is the name of the skin's folder.

Reload a page from your wiki in the browser, and you should be able to see the difference.

If you'd like to let your site's visitors choose one skin from a selection of skins you've installed, look at the Skins:Skin Change recipe. (That's what we used for the demo above.)

How can I modify an existing skin?

There are a number of ways to further customize the appearance of a skin, including

  • adding statements to /local/config.php that are compatible with your chosen skin;
  • adding css files to /pub/css/, such as local.css (for your entire wiki) and MyGroup?.css (for MyGroup?); and
  • directly editing the skin's files.

If the skin is updated regularly, you probably will want to avoid editing the files in the skin's folder. Check the skin's page in the Cookbook for specific suggestions.

If you want to modify the default pmwiki or print skins included with the PmWiki distribution, you should copy the pub/skins/pmwiki/ and pub/skins/print/ directories to another name and then use those skins instead of the default ones. While the name of the skin.tmpl and skin.css files don't usually matter, the optional skin.php file MUST match the name of the skin.

How can I make a skin?

The best way to make your first skin is to modify a copy of PmWiki's default skin.

  1. Make a copy of the folder pub/skins/pmwiki and name it whatever your new skin should be named.
  2. In your local/config.php file, set $Skin to be the name of your new skin.
  3. Modify the template and CSS files to suit you.
  4. Test your new skin.
  5. Repeat steps 3 and 4 until you're happy with the results.

The reason we recommend starting with the default PmWiki skin is that it's quite a simple skin, much more so than many of the skins you'll find in Skins:Skins. The starting point is the template (.tmpl) file, which provides the overall layout of the page. Inside of the template file are a number of special substitutions and directives that provide places for PmWiki to insert the data relevant to the current page being displayed. Skin Templates describes the format and directives in more detail. There are also skin guidelines available on

It's beyond the scope of this page to explain how to write HTML (hypertext markup language), XHTML (extensible HTML, which is a bit newer) or CSS (cascading style sheets), but there are many good tutorials on the web for all three of them. One caution: if you run into an HTML tutorial that explains about how to use <font> or <blink> tags, or spacer gifs, it's at least five years out of date, so skip it and find another one.

You should test your skin on a variety of browsers -- ideally as many as you can, on as many different platforms as you can -- but at minimum you should be testing on Internet Explorer 8, Firefox 3, and Chrome, since those are the most common, and have different bugs, it is also useful to test on Opera and Safari. Don't forget to do things like resize windows and change text size during your testing.

Print Skins

By default your new skin will use the standard /pub/skins/print/ skin.

To over-ride this add the following to local/config.php:

$ActionSkin['print'] = 'yourprintskin';

This says to use 'yourprintskin' for ?action=print instead of the default.

Tools that you'll need

There are good examples of all these programs available for free.

HTML and CSS editor(s). There are two types of editors: graphical (WYSIWYG, or "what you see is what you get"), and hand-coding or programmer's editors. Graphical editors are less intimidating to novices, but you won't learn as much, or know your code as intimately as you will by using a hand-coding editor. Whichever you choose, get one that has syntax highlighting for the code, because it will help you spot mistakes. Also, live preview features are not that helpful when writing a PmWiki skin, because PmWiki does stuff that the live preview won't, such as substitute values for variables and insert sidebar content.

Test wiki. You don't want to be wreaking havoc on your skin while visitors can see your site. It's a better idea to set up a test wiki, either on your real webserver or on your own machine. Linux or MacOS computer owners may have webservers and PHP already running on their machines, but Windows users often don't. If that describes you, then you might want to take a look at the Cookbook:Standalone recipe, which runs PmWiki without needing a complex webserver, or Cookbook:InstallOnIIS. Or, you can find many local server packages which install a webserver, PHP, and other stuff (e.g. MySQL), all configured to work together. Try to get a package that has the same software and versions as used on your live setup, since then there will be less to go wrong when the site goes live.

FTP client to transfer files to your webserver. You probably had one of these already.

Color picker. Your editor might include one, or you could pick up a standalone application. Extremely helpful for creating and saving color palettes.

See also

How do I change the Wiki's default name in the upper left corner of the Main Page?

Put the following config.php

$WikiTitle = 'My Wiki Site';

The docs/sample-config.php file has an example of changing the title.

How can I embed PmWiki pages inside a web page?

Source them through a PHP page, or place them in a frame.

How do I change the font or background color of the hints block on the Edit Page?

Add a CSS style to pub/css/local.css: .quickref {background:...; color:... }. The hints are provided by the Site.EditQuickReference page, which is in the PmWiki or Site wikigroup. Edit that page, and change the "bgcolor" or specify the font "color" to get the contrast you need.


This page describes the skin template files (.tmpl) that are used to create PmWiki skins, and how PmWiki uses them. As described in the skins page, a skin is a collection of files that specifies the layout for PmWiki pages. Each skin must include a template file that provides the skeleton for displaying a PmWiki page.

Finding and Processing Templates

When you set the value of the $Skin variable in a configuration file like local/config.php, like this

## Use the Foo Skin.
$Skin = 'foo';

it tells PmWiki to search for a skin of that name, and use it. The usual result of the search is for PmWiki to load a template file from the appropriate skin directory. In this example, that would probably be the file pub/skins/foo/foo.tmpl.

The actual processing that PmWiki goes through to find a template file is important for those who are making complex skins, so its worth mentioning what those steps are:

Security Note

The default value for $SkinLibDirs? has server-side and client-side files stored in the same publicly-accessible directory. That is, $SkinDir and $SkinDirUrl point to the same place. This is done for convenience (both for the skin user, and the skin writer), but it is not necessary.

It has the side effect that its possible to construct a URL (like this one) that will let you look at the contents of the the .tmpl or .php files that a skin uses. This is usually not an issue as skin files should not contain any sensitive information.

Still, a purist might want to move their .tmpl and .php files out of the directories that are accessible as URLs?, and modify their $SkinLibDirs? array to reflect this.

  1. When $PageTemplateFmt is blank (as it should be), PmWiki gathers the names of all candidate skins. It starts with any action-specific skin that is specified in $ActionSkin[$action]. Thus, if the current action is 'login', and $ActionSkin['login'] is 'Bar', then PmWiki will look for a skin named 'Bar'.
  2. If no skin has been found yet, it looks for the skin(s) named in the $Skin variable (which is allowed to be an array) and uses the first skin it can find. If it gets to the end of the list without finding a skin, it issues an error.
  3. To attempt to find a skin, PmWiki first consults the $SkinLibDirs variable to know where to look. Skins consist of server-side files that are loaded by PmWiki (such as .php and .tmpl files) and client-side files (such as .css files and images) that will be requested by the user's browser when they look at a skinned PmWiki page. $SkinLibDirs is an array of key/value pairs. The key is a directory to look in for the server-side files, while the corresponding value is a URL that points to the public client-side resources used by the skin. The default value of $SkinLibDirs? is:
    $SkinLibDirs = array(
       "./pub/skins/\$Skin"      => "$PubDirUrl/skins/\$Skin",
       "$FarmD/pub/skins/\$Skin" => "$FarmPubDirUrl/skins/\$Skin");
    So, using the above definitions, PmWiki would try to find the skin 'foo' by looking for a directory called ./pub/skins/foo and then for $FarmD/pub/skins/foo (with the value of $FarmD replaced by the root server directory for Farm files). The first such directory that was found would be assumed to contain the skin it was looking for. It would then set $SkinDir to the name of this directory and $SkinDirUrl to the corresponding URL.
  4. Once a valid skin directory has been found, PmWiki starts processing the files in that directory, looking for a .php skin file to run. It first looks for one with the same name as the skin. So, if the skin is 'foo', it looks for foo.php. If no such file is found, it then checks for a file named skin.php. If one of these .php files is found, PmWiki loads and runs it. This allows a skin to define custom markup, or custom configuration parameters. It also allows a skin to choose between which of several different .tmpl files to load.
    To specify which .tmpl file to load, simply call LoadPageTemplate?() inside the skin .php file, with the name of the .tmpl file to be loaded:
    For example, a skin might specify a special template to be used if the action is 'print':
    if ($GLOBALS['action'] == 'print')
      LoadPageTemplate($pagename, "$SkinDir/print.tmpl");
    When the action is something else, PmWiki will fall back to loading the default .tmpl file instead.
  5. If no appropriate .php file is found, or if that file doesn't load a template, then PmWiki falls back to looking for a template with the same name as the skin, or, failing that, any .tmpl file at all, so long as its the only one in the directory. If it finds one, it will load and process it. If not, it will issue an error.

Template file format

A template file is basically an HTML file that also contains variable substitutions (indicated by '$') and special directives embedded in HTML comments. The following special directives are required in the template file.

  1. The directive <!--PageText--> belongs to the <body> section of the HTML document, and tells PmWiki where the main content of each wiki page should be placed.
  2. The directive <!--HTMLHeader-->, which goes somewhere in the <head> section of the HTML document.
  3. The directive <!--HTMLFooter--> directive, which typically goes before the final </body> tag and is used by some recipes to insert things at the end of the HTML document. Prior to PmWiki 2.2.0 the <!--HTMLFooter--> directive was optional.

When PmWiki displays a page, it replaces the directives and variable substitutions with the values appropriate to the current page. For example, the <!--PageText--> directive is replaced with the page's contents, while any instances of $PageUrl are replaced with the url (address) of the current page.

There is a long list of variables available for substitution in pages; some of the most useful include:

$PageUrl         the url of the current page
$ScriptUrl       the base url to the pmwiki.php script
$Title           the page's title (e.g., "`SkinTemplates")
$Titlespaced     the page's title with spaces (e.g., "Skin Templates")
$Group           the name of the current group  (e.g., "`PmWiki")
$FullName        the page's full name (e.g., "`PmWiki.SkinTemplates")
$LastModified    the page's last modification time
$PageLogoUrl     the url of a site logo
$WikiTitle       the site's title
$SkinDirUrl      the url of the skin's folder

This last variable, $SkinDirUrl, is particularly useful in templates as it allows the skin designer to refer to other files (such as images or style sheets) in the skin folder without having to know the exact url.

The template is not limited to using the variables listed here; nearly any PHP global variable that begins with a capital letter can be used in a skin template. Page variables can also be used in templates.

Skin directives

Besides the required <!--PageText--> and <!--HTMLHeader--> directives, PmWiki provides other built-in directives for generating page output. It's not necessary to use any of these directives, but they can often add capabilities to a skin

The <!--wiki:Main.SomePage--> directive outputs the contents of Main.SomePage. $-substitutions are allowed in directives, thus a directive like <!--wiki:$Group.SomePage--> will include "SomePage" of the current group.
If multiple pages are listed in the directive, then only the first available page is used. Thus <!--wiki:$Group.SomePage Site.SomePage--> will display the contents of SomePage in the current group if it exists, and Site.SomePage if it doesn't. To always display Site.SomePage, even if $Group.SomePage exists, use two consecutive <!--wiki:...--> directives.
The <!--wiki:...--> directive only displays pages for which the browser has read permissions. The <!--page:...--> directive displays pages even if the browser doesn't have read permission.
The directive <!--file:somefile.txt--> outputs the contents of another file (on the local filesystem) at the point of the directive. If the file to be included is a .php script, then the PHP script is executed and its output is sent to the browser. Like the <!--wiki:...--> directive above, $-substitutions are available to be able to output files based on the current page name or group.
The markup directive processes any text that follows the colon as wiki markup and displays that in the output.
<!--function:SomeFunction args-->
This directive calls a PHP function named "SomeFunction", passing the current page's name and the text following the function name as arguments. PHP functions called in this manner are typically defined in a local customization file. Args allows only one argument, which has to be splitted then. <!--function:SomeFunction arg1 arg2 arg3--> generates one parameter "arg1 arg2 arg3". However variables can be used (like $LastModifiedBy).

Page sections

A template file can designate "sections" that are included or excluded from the output based on page directives or other criteria. A section always begins with <!--Page...Fmt--> and continues to the next section, the end of the template file, or <!--/Page...Fmt-->. For example, a template can specify a <!--PageLeftFmt--> section that is excluded from the output whenever the (:noleft:) directive is encountered in the page's contents. PmWiki's predefined sections (and their corresponding page directives) are:

<!--PageHeaderFmt-->          (:noheader:)
<!--PageFooterFmt-->          (:nofooter:)
<!--PageTitleFmt-->           (:notitle:)
<!--PageLeftFmt-->            (:noleft:)
<!--PageRightFmt-->           (:noright:)
<!--PageActionFmt-->          (:noaction:)

Skin designers can define custom sections and markups, but currently all section names in the template must begin with "Page" and end with "Fmt". As mentioned you also have to define the corresponding markup (for example in your config.php) like this:

Markup('noxyz', 'directives',  '/\\(:noxyz:\\)/ei',

Internationalization (i18n)

Skins can also be internationalized by using $[...] substitutions. Any string placed inside of $[...] is treated as a "translatable phrase", and the phrase is looked up in the current translation tables for a corresponding output phrase. If a translation is available, then the translated phrase is substituted at that point, otherwise the original phrase is left intact.

For example, the substitution $[Edit] will display the current translation of "Edit" if it is known, otherwise it displays "Edit". Thus, the same template can be used for multiple languages, displaying "Editer" when French translations are loaded, "Bearbeiten" when German translations are loaded, and "Edit" when no translation is available.

How do I customize the CSS styling of my PmWiki layout?

See Skins for how to change the default PmWiki skin. See also Skins?, where you will find pre-made templates you can use to customize the appearance of your site. You can also create a file called local.css in the pub/css/ directory and add CSS selectors there (this file gets automatically loaded if it exists). Or, styles can be added directly into a local customization file by using something like:

$HTMLStylesFmt[] = '.foo { color:blue; }';

Where can the mentioned "translation table" be found for adding translated phrases?

See Internationalizations.

Is it possible to have the edit form in full page width, with no sidebar?

If the sidebar is marked with <!--PageLeftFmt-->, adding (:noleft:) to Site.EditForm will hide it when a page is edited.

Can I easily hide the Home Page title from the homepage?

Yes, you can use in the wiki page either (:title Some other title:) to change it or (:notitle:) to hide it.

Is it possible to hide the Search-Bar in the default PmWiki Skin?

Yes, please see Cookbook:HideSearchBar.


The page Site.Preferences contains customisable browser preference settings. These include access keys (keyboard shortcuts to certain actions like edit, history, browse) and settings of the Site.EditForm (width and height of the edit textarea) as well as the name of the edit form in use.

A different page than Site.Preferences can be chosen by making a copy of that page under a new name, customising it, and setting a cookie which will point to this page for the browser being used, through


SomeGroup.CustomPreferences being the name of the new customised preference page.

Notes and Comments

Note that in order to enable parsing of Site.Preferences by default, a line like the following needs to be added to local/config.php:

 XLPage?('prefs', "Site.Preferences");


Web feeds are a convenient mechanism to let visitors be notified of changes to a site. Instead of repeatedly checking RecentChanges every day to see what is new, a visitor can use a news aggregator to quickly see what pages of interest have changed on a site. Web feeds are commonly recognized by terms such as RSS, Atom, and web syndication. They are also the foundation for podcasting.

In its simplest form, web feeds in PmWiki are built on WikiTrails. Using a feed action such as ?action=rss or ?action=atom on a trail generates a web feed (often called a "channel") where each page on the trail is an item in the feed. Since the RecentChanges and Site.AllRecentChanges pages are effectively trails, one can easily get an RSS feed for a group or site by simply adding ?action=rss to the url for a RecentChanges page. For example, to get the site feed for, one would use

Authors can also create custom feeds by simply creating a wiki trail of the pages they want included in the feed. Feeds can also be generated from groups, categories, and backlinks, and the order and number of items in the feed can be changed using options in the feed url. Thus, one can obtain a feed for the Skins category (sorted with most recent items first) by using

PmWiki is able to generate feeds in many formats, including RSS 2.0 (?action=rss), Atom 1.0 (?action=atom), and RSS 1.0 (?action=rdf). In addition, although it is not normally considered a web feed, PmWiki can generate metadata information using the Dublin Core Metadata extensions (?action=dc).

How to read a PmWiki syndicated feed

  1. You'll need a news aggregator, which is a piece of software designed to read news feeds. Many different news aggregators are available. Some run on your own computer, either on their own or as plugins for email clients, web browsers, or newsreaders. Others are web applications that you can use from any Internet-connected computer. Some are in between (technically web applications, but ones designed to run on your computer, not some remote server). Get one that you like.
  2. Subscribe to the WikiTrail you desire by supplying the feed url to the aggregator. The feed url will be the name of a trail page with ?action=rss or ?action=atom added to the end of the url.

Feed options

Add any of the following options to the end of a PmWiki web feed url to change its output (basically any pagelist option is available for web feeds):

Limit feed to n items (default 10)
Display most recently changed items first (default: the order of the trail, or by name; in RecentChanges pages the trail is already ordered by -time)
Obtain items from trail on page (default: the trail on the current page)
Limit feed to pages in group
Limit feed to pages with specific name
Create feed from pages linked to page
Exclude things like RecentChanges, AllRecentChanges?, etc.

authors (intermediate)

Configure PmWiki for feeds

This section describes how to syndicate portions of a wiki to appear in a web feed. It does not describe how to display a web feed within a wiki page -- for that, see Cookbook:RssFeedDisplay.

To enable web feed generation for a site, add one or more of the following to a local customization file:

if ($action == 'rss') include_once("$FarmD/scripts/feeds.php");
if ($action == 'atom') include_once("$FarmD/scripts/feeds.php");
if ($action == 'rdf') include_once("$FarmD/scripts/feeds.php");
if ($action == 'dc') include_once("$FarmD/scripts/feeds.php");

or you can combine multiple feeds into a single expression using "||" to separate each feed type. For example, if you want to enable RSS and Atom feeds you would use

if ($action == 'rss'  ||
    $action == 'atom' ||
    $action == 'rdf'  ||
    $action == 'dc') include_once("$FarmD/scripts/feeds.php");

Configure feed content

Web feeds are highly configurable, new elements can be easily added to feeds via the $FeedFmt array. Elements in $FeedFmt look like

$FeedFmt['atom']['feed']['rights'] = 'All Rights Reserved';

where the first index corresponds to the action (?action=atom), the second index indicates a per-feed or per-item element, and the third index is the name of the element being generated. The above setting would therefore generate a "<rights>All Rights Reserved</rights>" in the feed for ?action=atom.

If the value of an entry begins with a '<', then feeds.php doesn't automatically add the tag around it. Elements can also be callable functions which are called to generate the appropriate output. See RSS specification or other feed specifications for what feed content you can use.

You can also change an existing element rather than add a new one. You can use the following lines to ensure that changes made to the wiki will be picked up by some RSS readers that wouldn't otherwise "notice" a page has been changed:

# Change the link URL when an item is edited.
$FeedFmt['rss']['item']['link'] = '{$PageUrl}?when=$ItemISOTime';
$FeedFmt['atom']['item']['link'] =
   "<link rel=\"alternate\" href=\"{\$PageUrl}?when=\$ItemISOTime\" />\n";

See Also

How do I include text from the page (whole page, or first X characters) in the feed body? (note: markup NOT digested)

    function MarkupExcerpt?($pagename) {
      $page = RetrieveAuthPage?($pagename, 'read', false);
      return substr(@$page['text'], 0, 200);

    $FmtPV['$MarkupExcerpt'] = 'MarkupExcerpt?($pn)';
    $FeedFmt['rss']['item']['description'] = '$MarkupExcerpt';

Q:Does this mean if I want to include the time in the rss title and "summary" to rss body I call $FeedFmt twice like so:

$FeedFmt['rss']['item']['description'] = '$LastSummary'; 
$FeedFmt['rss']['item']['title'] = '{$Group} / {$Title} @ $ItemISOTime';
From mailing list Feb 13,2007, a response by Pm: Yes

How can I use the RSS <enclosure> tag for podcasting?

For podcasting of mp3 files, simply attach an mp3 file to the page with the same name as the page (i.e., for a page named Podcast.Episode4, one would attach to that page a file named "Episode4.mp3"). The file is automatically picked up by ?action=rss and used as an enclosure.

The set of potential enclosures is given by the $RSSEnclosureFmt array, thus

$RSSEnclosureFmt = array('{$Name}.mp3', '{$Name}.wma', '{$Name}.ogg');

allows podcasting in mp3, wma, and ogg formats.

How to add "summary" to the title in a rss feed (ie. with ?action=rss)?

Add this line in you local/config.php

$FeedFmt['rss']['item']['title'] = '{$Group} / {$Title} : $LastModifiedSummary';

How to add "description" to the title in an rss feed, and summary to the body?

Add these lines to your local/config.php

$FeedFmt['rss']['item']['title'] = '{$Group} / {$Title} : {$Description}';
$FeedFmt['rss']['item']['description'] = '$LastModifiedSummary';


  • you need to replicate these lines for each type (atom, rdf, dc) of feed you provide.
  • the RSS description-tag is not equivalent to the pmWiki $Description variable, despite the confusing similarity.

Some of my password-protected pages aren't appearing in the feed... how do I work around this?

From a similar question on the newsgroup, Pm's reply:

The last time I checked, RSS and other syndication protocols didn't really have a well-established interface or mechanism for performing access control (i.e., authentication). As far as I know this is still the case.

PmWiki's WebFeeds capability is built on top of pagelists, so it could simply be that the $EnablePageListProtect option is preventing the updated pages from appearing in the feed. You might try setting $EnablePageListProtect=0; and see if the password-protected pages start appearing in the RSS feed.

The "downside" to setting $EnablePageListProtect to zero is that anyone doing a search on your site will see the existence of the pages in the locked section. They won't be able to read any of them, but they'll know they are there!

You could also set $EnablePageListProtect to zero only if ?action=rss:

    if ($action == 'rss') $EnablePageListProtect = 0;

This limits the ability to see the protected pages to RSS feeds; normal pagelists and searches wouldn't see them.

Lastly, it's also possible to configure the webfeeds to obtain the authentication information from the url directly, as in:


The big downside to this is that the cleartext password will end up traveling across the net with every RSS request, and may end up being recorded in Apache's access logs.

How to add feed image?

Add the following to local/config.php (this example is for ?action=rss):

$FeedFmt['rss']['feed']['image'] =
" <title>Logo title</title>
Do not forget NOT to start with a '<' as there would be no <image> tag around this... See here.

How do I insert RSS news feeds into PmWiki pages?

See Cookbook:RssFeedDisplay.

How can I specify default feed options in a configuration file instead of always placing them in the url?

For example, if you want ?action=rss to default to ?action=rss&group=News&order=-time&count=10, try the following in a local customization file:

   if ($action == 'rss')
     SDVA($_REQUEST, array(
       'group' => 'News',
       'order' => '-time',
       'count' => 10));

Are there ways to let people easily subscribe to a feed?

On some browsers (Mozilla Firefox), the visitor can see an orange RSS icon in the address bar, and subscribe to the feed by clicking on it. To enable the RSS icon, add this to config.php :

$HTMLHeaderFmt['feedlinks'] = '<link rel="alternate" type="application/rss+xml" 
  title="$WikiTitle" href="$ScriptUrl?n=Site.AllRecentChanges&amp;action=rss" />
<link rel="alternate" type="application/atom+xml" title="$WikiTitle"
  href="$ScriptUrl?n=Site.AllRecentChanges&amp;action=atom" />';

You can also add such a link, for example in your SideBar, [[Site.AllRecentChanges?action=atom | Subscribe to feed]].

Can I create an RSS feed for individual page histories?

See Cookbook:PageFeed.

How do I create a custom FeedPage? similar to RecentChanges or AllRecentChanges?, but with only certain groups or pages recorded?

See Cookbook:CustomRecentChanges. In a nutshell, you'll declare a $RecentChangesFmt variable with your dedicated FeedPage?, and then wrap it in a condition of your choice. For example:

   if (PageVar($pagename, '$Group')!='ForbiddenGroup') {
     $RecentChangesFmt['Site.MyFeedPage'] =
       '* [[{$FullName}]]  . . . $CurrentTime $[by] $AuthorLink: [=$ChangeSummary=]';

How can I update my RSS feed to show every edit for pages on that feed, not just new pages added to the feed?

Add unique guid links for each edit to your to congif.php file (see PITS entry):

   $FeedFmt['rss']['item']['guid'] = '{$PageUrl}?guid=$ItemISOTime';

Alternatively, you can create the option for edit monitoring by adding a qualifier for RSS links. This allows the user to choose between default new pages RSS feeds and new edits RSS feeds ( has this option enabled).

   ## For new pages updates:
   ## For edits updates:
   if(@$_REQUEST['edits'] && $action == 'rss')
     $FeedFmt['rss']['item']['guid'] = '{$PageUrl}?guid=$ItemISOTime';


Also see: Cookbook:Farm Setup By Example, Cookbook:Wiki Farm Alternative

A WikiFarm is a collection of two or more wikis running on the same web server and sharing a set of common components. The term is based on the computing phrase "server farm".

This page provides some background information about WikiFarms and describes how to turn a "normal" configuration into a farm by adding a wiki. There are many ways to configure wiki farms; this page describes only one, in an effort to make it as simple as possible for the administrator who is creating a farm for the first time.

Why use a farm?

The primary motivation for using a wiki farm is to reduce the amount of administrative work involved in managing several wikis. In a farm, most of the PmWiki code is stored in one place and is shared by all the wikis. An administrator can (for example) upgrade to a new version of PmWiki on every wiki in the farm by simply updating the shared components in a single location.

From a reader's point of view, each wiki in a farm is completely independent, and appears as a separate web site. Each wiki in a farm:

  • has its own URL, and the URLs? can be in different domains
  • can have its own look and feel by using different skins
  • can have its own add-ons or "recipes" from the Cookbook
  • can have its own administrator responsible for local configuration

Why not to use a farm

Because the wikis in a farm are all independent, it is difficult (but not impossible) to provide services that require access to more than one wiki. For example, the PmWiki search function can only search within one wiki. Using a farm as a way of subdividing related content is generally a bad idea. A much better way to subdivide content is to use WikiGroups.

I still can't decide if I need a farm ...

The good news is that you don't have to decide in advance. In fact, the recommended procedure is to first do a "normal" or single installation of PmWiki. Use it for a while. Create pages and edit them. Get to know how to add recipes. Be sure to try out WikiGroups (they may be all you need).

Once you have decided that you need another wiki, you have two basic choices:

  1. Do a complete installation of PmWiki in a new directory. This gives you two totally independent wikis that are completely self-contained. This is not a wiki farm.
  2. Create a wiki farm using your existing wiki as the "home wiki" where most of the shared PmWiki components will live.

Choice number 1 can be a good choice for several reasons:

  • it is not a wiki farm, and requires no additional administrative knowledge - it's just two installations
  • if you decide to move one of the wikis to another server, you can simply copy the wiki directory structure to the second server, and it will work (assuming there is a web server and PHP in place).
  • you can run different versions of PmWiki on each wiki (good for testing new versions)
  • no matter how badly you mess up one installation, it doesn't affect the other

If you choose to create a wiki farm, then read on ...


Before you create a farm, make sure that:

Creating the home wiki

You do have a working installation of PmWiki at this point, don't you? That's good, because your existing wiki is about to become the home wiki of your farm.

In the directory that contains your existing wiki, create the file local/farmconfig.php. This file is used to hold any local customizations that apply across the whole farm. For example, you could assign an admin password in farmconfig.php that will be used by all of the wikis in your farm.

If the URL used to access your existing wiki is then a minimal farmconfig.php file would look like this:

<?php if (!defined('PmWiki')) exit();
$FarmPubDirUrl = '';

This loads the variable $FarmPubDirUrl with the URL location of your home wiki's pub/ directory. All of the wikis in your farm share this pub/ directory. The pub/ directory holds skin definitions and GUI-edit buttons to be shared by all the wikis in the farm.

Amazing as it may sound, this completes all of the changes you need to make in order to turn your existing wiki into the home wiki of your farm.

Creating an additional wiki in your farm

1. Create a directory to hold the new wiki. This directory must be web-accessible, just like the directory that holds your home wiki.
2. Create a file called index.php in the directory with the following contents:
<?php include_once('path/to/pmwiki.php');
This allows your new wiki to share the PmWiki code stored in your home wiki. The path/to/pmwiki.php is the file path to pmwiki.php in your home wiki. Use an absolute file path (/home/username/pmwiki/pmwiki.php) or a relative file path (../pmwiki/pmwiki.php). Do not use a url path - there should not be an 'http://' in it anywhere. For a web server running under Windows, you need to use a complete file path as in C:/Apache Group/Apache2 /www/mynewwiki/.
3. Open a web browser and browse the URL of the new wiki. This will be a web address starting with 'http://'. PmWiki will attempt to automatically create a writable wiki.d/ directory where the wiki's pages will be stored. If you see an error message, follow the instructions. If you choose the option for a "slightly more secure installation" be sure to execute both commands.

Your new wiki is now set up, and your farm now contains 2 wikis. To add more wikis, just repeat these 3 steps.


Each wiki in a farm inherits the settings stored in farmconfig.php. Do any customization that you want to apply farm-wide (to all the wikis) in farmconfig.php.

Create a local/ directory within each wiki's directory to hold local customizations that apply only to that wiki. You should at least create the local/config.php file with a new title, like so :

<?php if (!defined('PmWiki')) exit();
  ## Title of your farmed wiki
  $WikiTitle = 'New Wiki';

Farm-wide customizations are processed before the individual wiki local customizations.

The PmWiki variable $FarmD points to the directory in which pmwiki.php is installed, and your home wiki, and it is used as a prefix to allow the other wikis to share PmWiki components. For example:

  • $FarmD/scripts/ points to the shared scripts/ directory
  • $FarmD/pub/ points to the shared pub/ directory
  • $FarmD/cookbook/ points to the shared cookbook/ directory

Any scripts you include in farmconfig.php must be included with a line such as:

Note the double quotes - single quotes may work for per farm inclusions, but they will not work for $FarmD.


  • The terminology used to describe wiki farms is not used consistently. See WikiFarmTerminology for more info.
  • It is important to remember that not all of the recipes in the Cookbook have been written for or tested with farms. Be sure to look for instructions on how to use a recipe on a farm.
  • There are many, many more things you can do with farms. Some are described on PmWiki:WikiFarmsAdvanced which also contains links to step-by-step examples of setting up a farm.

Password use/authorization on farm wikis:

How come when I switch to another wiki within a farm, I keep my same authorization?

PmWiki uses PHP sessions to keep track of authentication/authorization information, and by default PHP sets things up such that all interactions with the same server are considered part of the same session.

An easy way to fix this is to make sure each wiki is using a different cookie name for its session identifier. Near the top of one of the wiki's local/config.php files, before calling authuser or any other recipes, add a line like:


You can pick any alphanumeric name for XYZSESSID; for example, for the cs559-1 wiki you might choose


This will keep the two wikis' sessions independent of each other.

Categories: WikiFarms


This page will attempt to summarize some of the more commonly asked questions. The answers are on the corresponding pages (see link). If you have a question which isn't answered here, you can leave your question on the Questions page or search for documentation using the search facility. More documentation can be found on the documentation index page.


Pmwiki is pretty robust and can automatically adapt to a very wide variety of environments. However, sometimes things don't go as we expect, so we're cataloging common errors and their fixes here.

Troubleshooting Frequently Asked Questions

Note: This page on is probably not the best place to post questions. Consider seeking assistance from the pmwiki-users mailing list, or post your question on the PmWiki:Questions page.

Why am I seeing strange errors after upgrading?

Make sure all of the files were updated, in particular pmwiki.php.

This question sometimes arises when an administrator hasn't followed the advice, which used to be less prominent, on the installation and initial setup tasks pages and has renamed pmwiki.php instead of creating an index.php wrapper script. If you have renamed pmwiki.php to index.php, then the upgrade procedure won't have updated your index.php file. Delete the old version and create a wrapper script so it won't happen again.

Sometimes an FTP or other copy program will fail to transfer all of the files properly. One way to check for this is by comparing file sizes.

Be sure all of the files in the wikilib.d/ directory were also upgraded. Sometimes it's a good idea to simply delete the wikilib.d/ directory before upgrading. (Local copies of pages are stored in wiki.d/ and not wikilib.d/.)

Make sure that the file permissions are correct. The official files have a restricted set of permissions that might not match your site's needs.

If you use a custom pattern for $GroupPattern make sure that it includes Site ($SiteGroup) and since PMWiki 2.2 also SiteAdmin? ($SiteAdminGroup). Otherwise migration may fail (e.g. missing SiteAdmin? for PMWiki 2.2 and later) and/or login does not work.
Additionally Main ($DefaultGroup) should be included too.

I'm suddenly getting messages like "Warning: fopen(wiki.d/.flock): failed to open stream: Permission denied..." and "Cannot acquire lockfile"... what's wrong?

Something (or someone) has changed the permissions on the wiki.d/.flock file or the wiki.d/ directory such that the webserver is no longer able to write the lockfile. The normal solution is to simply delete the .flock file from the wiki.d/ directory -- PmWiki will then create a new one. Also be sure to check the permissions on the wiki.d/ directory itself. (One can easily check and modify permissions of the wiki.d/ directory in FileZilla (open-source FTP app) by right-clicking on the file > File attributes)

My links in the sidebar seem to be pointing to non-existent pages, even though I know I created the pages. Where are the pages?

Links in the sidebar normally need to be qualified by a WikiGroup in order to work properly (use [[Group.Page]] instead of [[Page]]).
Also: Make sure you type SideBar with a capital B.

Why am I seeing "Warning: Cannot modify header information - headers already sent ..." messages at the top of my page.

If this is the first or only error message you're seeing, it's usually an indication that there are blank lines or spaces before the <?php or after the ?> in a local customization file. Double-check the file and make sure there aren't any blank lines or spaces before the initial <?php. It's often easiest and safest to eliminate any closing ?> altogether. On Windows, it may be necessary to use a hex editor to convert LFCR line endings to LF line endings in the local\config.php file.

If the warning is appearing after some other warning or error message, then resolve the other error and this warning may go away.

How do I make a PHP Warning about function.session-write-close go away?

If you are seeing an error similar to this

Warning: session_write_close() [function.session-write-close]:
open(/some/filesystem/path/to/a/directory/sess_[...]) failed: No such file
or directory (2) in /your/filesystem/path/to/pmwiki.php on line NNN

PmWiki sometimes does session-tracking using PHP's session-handling functions. For session-tracking to work, some information needs to be written in a directory on the server. That directory needs to exist and be writable by the webserver software. For this example, the webserver software is configured to write sessions in this directory


but the directory doesn't exist. The solution is to do at least one of these:

  • Create the directory and make sure it's writable by the webserver software
  • Provide a session_save_path value that points to a directory that is writable by the server, e.g. in config.php:
session_save_path('/home/someuser/tmp/sessions'); # unix-type OS
session_save_path('C:/server/tmp/sessions'); # Windows

Why is PmWiki prompting me multiple times for a password I've already entered?

This could happen like out of nowhere if your hosting provider upgrades to PHP version 5.3, and you run an older PmWiki release. Recent PmWiki releases fix this problem.

Alternatively, this may be an indication that the browser isn't accepting cookies, or that PHP's session handling functions on the server aren't properly configured. If the browser is accepting cookies, then try setting $EnableDiag=1; in local/config.php, run PmWiki using ?action=phpinfo, and verify that sessions are enabled and that the session.save_path has a reasonable value. Note that several versions of PHP under Windows require that a session_save_path be explicitly set (this can be done in the local/config.php file). You might also try setting session.auto_start to 1 in your php.ini.

See also the question I have to log in twice below.

I edited config.php, but when I look at my wiki pages, all I see is "Parse error: parse error, unexpected T_VARIABLE in somefile on line number."

You've made a mistake in writing the PHP that goes into the config.php file. The most common mistake that causes the T_VARIABLE error is forgetting the semi-colon (;) at the end of a line that you added. The line number and file named are where you should look for the mistake.

Searches and pagelists stopped working after I upgraded -- no errors are reported, but links to other pages do not appear (or do not appear as they should) -- what gives?

Be sure all of the files in the wikilib.d/ directory were also upgraded. In particular, it sounds as if the Site.PageListTemplates page is either missing (if no links are displayed) or is an old version (if the links do not appear as they should). Also make sure that read-permissions (attr) are set for the pages Site.PageListTemplates and Site.Search.

Some of my posts are coming back with "403 Forbidden" errors, "Not Acceptable", or "Internal Server Error". This happens with some posts but not others.

Your webserver probably has mod_security enabled. The mod_security "feature" scans all incoming posts for forbidden words or phrases that might indicate someone is trying to hack the system, and if any of them are present then Apache returns the 403 Forbidden error. Common phrases that tend to trigger mod_security include "curl ", "wget", "file(", and "system(", although there are many others.

Since mod_security intercepts the requests and sends the "forbidden" message before PmWiki ever gets a chance to run, it's not a bug in PmWiki, and there's little that PmWiki can do about it. Instead, one has to alter the webserver configuration to disable mod_security or reconfigure it to allow whatever word it is forbidding. Some sites may be able to disable mod_security by placing SecFilterEngine off in a .htaccess file.

I get the following message when attempting to upload an image, what do I do?

Warning: move_uploaded_file(): SAFE MODE Restriction in effect. The script whose uid is 1929 is not allowed to access /home/onscolre/public_html/pmwikiuploads/Photos owned by uid 33 in /home/onscolre/public_html/pmwiki/scripts/upload.php on line 198

PmWiki can't process your request

?cannot move uploaded file to /home/onscolre/public_html/pmwikiuploads/FoundationPupilsIn1958?.jpeg

We are sorry for any inconvenience.

Your server is configured with PHP Safe Mode enabled. Configure your wiki to use a site-wide uploads prefix, then create the uploads/ directory manually and set 777 permissions on it (rather than letting PmWiki create the directory).

I'm starting to see "Division by zero error in pmwiki.php..." on my site. What's wrong?

It's a bug in PmWiki that occurs only with the tables markup and only for versions of PHP >= 4.4.6 or >= 5.2.0. Often it seems to occur "out of nowhere" because the server administrator has upgraded PHP. Try upgrading to a later version of PmWiki to remove the error, or try setting the following in local/config.php:

    $TableRowIndexMax = 1;

I have to log in twice (two times) (2 times). -or- My password is not being required even though it should. -or- I changed the password but the old password is still active. -or- My config.php password is not over-riding my farmconfig.php password.

It could happen if (farm)config.php, or an included recipe, directly calls the functions CondAuth(), or RetrieveAuthPage?(), PageTextVar?(), PageVar?() and possibly others, before defining all passwords and before including AuthUser (if required).

The order of config.php is very significant.


Page actions are applied to wiki pages, as a query string appended to the URL. Security can be applied to all default actions, and script actions with one exception, but not diag actions, through the use of passwords.

Also documented are all other URL queries.

PmWiki Actions

See also site page actions.

displays dialog for setting/changing password of the specified page or group of pages, see passwords, see also $EnablePostAttrClearSession if you do not want to have the session cleared after validating change General use of passwords and login

display the specified page (default action if no ?action= is present)

displays a form for generating hashed passwords out of clear text for usage in your config.php

show a change history of the specified page, see page history History of previous edits to a page

retrieve the page's attachment named file.ext, see $EnableDirectDownload

edit the specified page, see basic editing PmWiki's basic edit syntax

prompt visitor for username/password, by default using Site.AuthForm

remove author, password, and login information

display the specified page using the skin specified by $ActionSkin['print']

bring up the reference count form, which allows the user to generate a list of links (all, missing, existing or orphaned) in or from specified groups. See Ref Count Link references counts on pages . Part of the core distribution but must be enabled by the administrator.

displays searchbox on current page, see search Targeting and customizing search results
performs search with searchterm and displays results on current page
performs backlinks search with ''pagename' and displays results on current page

show page source

If web feeds are enabled, returns a syndication feed based on the contents of the page or other options provided by the url, see web feeds Web feed notification of changes

display a form to upload an attachment for the current group, see uploads Uploading and linking to attachments

Query string parameters

?from=page name
use when a page is redirected

?n=page name
display page

sets cookie to custom preferences page. See site preferences Customisable browser setting preferences: Access keys, edit form

Actions enabled by $EnableDiag

The following actions are available only if you set $EnableDiag = 1; in your configuration file. They can be used for debugging and should not be set in a production environment.

displays a list of all markups in 3 columns:
  • column 1 = markup-name (1. parameter of markup() )
  • column 2 = when will rule apply (2. parameter of markup() )
  • column 3 = PmWiki's internal sort key (derived from #2)
(see Custom Markup Using the Markup() function for custom wiki syntax; migration to PHP 5.5 ).
To see more than what ?action=ruleset gives you, apply the Cookbook:MarkupRulesetDebugging recipe: it can also show the pattern and the replacement strings.
  • doesn't make use of PmWiki's authorization mechanisms.

displays the output of phpinfo() and exits. no page will be processed
  • doesn't make use of PmWiki's authorization mechanisms.

displays a dump of all global vars and exits. no page will be processed
  • doesn't make use of PmWiki's authorization mechanisms.

Actions enabled by PmWiki Scripts

see Site Analyzer and Analyze Results

see Url approvals Require approval of Url links
  • doesn't make use of PmWiki's authorization mechanisms.

Actions enabled by recipes

(more information about Custom Actions)

see Cookbook:UserAuth2 A user-based permission granting and authentication module
see Cookbook:BackupPages Automatically back up the wiki.d directory to a .zip file
see Cookbook:SearchCloud Creates a list of search terms used on a PmWiki site.
see Cookbook:CommentBox Adds a simple form to post comments
see Cookbook:Comments Comment addon - comments in separate files
see Cookbook:CommentDb Comment recipe - with pagination and RSS feed
see Cookbook:ROEPatterns Replace On Edit
Cookbook:ConvertTable Convert table action
see Cookbook:MovePage Move and copy wiki pages
see CSVAction Adds a ?action=csv capability to pmwiki to output tables as a CSV
Cookbook:Attachtable Actions to rename, delete & restore deleted attachments, as well as an attachlist replacement to use those actions, show file types and list attachment references.
see Cookbook:DeleteAction
see Cookbook:DiscussionTab Provide a skin with a "discussion" tab and "article" tab, etc.
see Cookbook:DownloadManager How can I know how many times a file was downloaded from my wiki?
see Cookbook:ExpireDiff How to remove a page's history
see Cookbook:ImportText Import text files as PmWiki pages
see Cookbook:MovePage Move and copy wiki pages
see Cookbook:CommentBoxPlus Simple styled form to post comments, plus comment counter
see Cookbook:ListCategories use categories as tags
see Cookbook:GeneratePDF Generate PDF versions of pages (?action=pdf) or Cookbook:PmWiki2PDF Generate a PDF; back up all wiki pages in PDF format
see Cookbook:UploadForm Alternative file upload form using (:input file:)
see Cookbook:PublishPDF Typesets wiki page collections into PDF (finalist: New Zealand open source awards 2008)
see Cookbook:ASCIIMath Display MathML? rendered ascii formula into PmWiki 2.x pages
see Cookbook:UserAuth2 A user-based permission granting and authentication module
(the imgtpl action is called automatically and should not be called by a link in a wiki page)
(the createthumb action is called automatically and should not be called by a link in a wiki page)
(this action is called automatically and should not be called by a link in a wiki page)
see Cookbook:ThumbList A thumbnail picture gallery for PmWiki
see Cookbook:Mini Simple, lightweight, un-bloated gallery with thumbnail generator
see Cookbook:RecipeCheck Check for new versions of recipes on
see Cookbook:PageRegenerate Make PmWiki regenerate a page, as if someone had done an edit+save sequence.
see Cookbook:RenamePage Rename a wiki page from a browser
see Cookbook:SharedPages Share selected pages among several wikis on a common server, as in WikiFarms
see Cookbook:Sitemapper Adds a dynamically generated sitemap to PmWiki.
see Cookbook:TotalCounter A statistic counter - counts page views, users, languages, browsers, operating systems, referers, locations and web bots
see Cookbook:Trash "safely delete" pages so that they can be restored and listed with pagelists
see Cookbook:WebAdmin PHP file manager, works without ftp client
see Cookbook:ZAP The ZAP forms processor handles data and file management, page insertions (forums, blogs), email & newsletters, e-commerce, and more.

Query string parameters enabled by recipes



see Cookbook:ChoiceColorChanger {ChoiceColorChanger $:Summary}
see SkinChange change skin via query or cookie setting

Custom actions


AuthUser is PmWiki's identity-based authorization system that allows access to pages to be controlled through the use of usernames and passwords. AuthUser can be used in addition to the password-based scheme that is PmWiki's default configuration.

AuthUser is a very flexible system for managing access control on pages, but flexibility can also bring complexity and increased maintenance overhead to the wiki administrator. This is why PmWiki defaults to the simpler password-based system. For some thoughts about the relative merits of the two approaches, see PmWiki:ThoughtsOnAccessControl.

See also: Cookbook:Quick Start for AuthUser.

Activating AuthUser

To activate PmWiki's identity-based system, add the following line to local/config.php:


Ensure that you have set a site wide admin password, otherwise you will not be able to edit SiteAdmin.AuthUser.

Note: Older versions of PmWiki (before 2.2.0-beta58) use Site.AuthUser.

PmWiki caches some group and page authorization levels when a page is accessed. For this reason, it is better to include authuser.php quite early in config.php, notably

(If you don't use a custom PageStore? class and i18n, include authuser.php first thing in config.php.)

All other recipes should be included after these.

Creating user accounts

Most of AuthUser's configuration is performed via the SiteAdmin.AuthUser page. To change the AuthUser configuration, simply edit this page like any other wiki page (you'll typically need to use the site's admin password for this).

To create a login account, simply add lines to SiteAdmin.AuthUser that look like:

    username: (:encrypt password:)

For example, to create a login account for "alice" with a password of "wonderland", enter:

    alice: (:encrypt wonderland:)

When the page is saved, the "(:encrypt wonderland:)" part of the text will be replaced by an encrypted form of the password "wonderland". This encryption is done so that someone looking at the SiteAdmin.AuthUser page cannot easily determine the passwords stored in the page.

To change or reset an account's password, simply replace the encrypted string with another (:encrypt:) directive.

The password cannot contain spaces, tabs, new lines, columns ":" and equals "="; on some systems it should contain at least 4 characters. Usernames and passwords are case sensitive, eg. "User" is not the same as "user".

Controlling access to pages by login

Pages and groups can be protected based on login account by using "passwords" of the form id:username in the password fields of ?action=attr (see PmWiki.Passwords). For example, to restrict a page to being edited by Alice, one would set the password to "id:alice".

It's possible to use multiple "id:" declarations and passwords in the ?action=attr form, thus the following setting would allow access to Alice, Carol, and anyone who knows the password "quick":

    quick id:alice,carol

To allow access to anyone who has successfully logged in, use "id:*".

One can also perform site-wide restrictions based on identity in the $DefaultPasswords array: e.g.

    # require valid login before viewing pages
    $DefaultPasswords['read'] = 'id:*';
    # Alice and carol may edit
    $DefaultPasswords['edit'] = 'id:alice,carol';
    # All admins and Fred may edit
    $DefaultPasswords['edit'] = array('@admins', 'id:Fred');

You can change the $DefaultPasswords array in local customization files such as:

  • local/config.php (for entire wiki)
  • farmconfig.php (for entire wikifarm)

Organizing accounts into groups

AuthUser also makes it possible to group login accounts together into authorization groups, indicated by a leading "@" sign. As with login accounts, group memberships are maintained by editing the SiteAdmin.AuthUser page. Group memberships can be specified by either listing the groups for a login account (person belongs to groups) or the login accounts for a group (group includes people). You can repeat or mix-and-match the two kinds as desired:

    @writers: alice, bob
    carol: @writers, @editors
    @admins: alice, dave

Then, to restrict page access to a particular group, simply use "@group" as the "password" in ?action=attr or the $DefaultPasswords array, similar to the way that "id:username" is used to restrict access to specific login accounts.

Excluding individuals from password groups

Group password memberships are maintained by editing the SiteAdmin.AuthUser page. To specify a password group that allows access to anyone who is authenticated, you can specify:

    @wholeoffice: *

If you need to keep "Fred" out of this password group :

    @wholeoffice: *,-Fred

To allow all users except Fred to change page attributes, for example, you can add to config.php :

    $DefaultPasswords['attr'] = array('id:*,-Fred');

Getting account names and passwords from external sources

The AuthUser script has the capability of obtaining username/password pairs from places other than the SiteAdmin.AuthUser page, such as passwd-formatted files (usually called '.htpasswd' on Apache servers), LDAP servers, or even the local/config.php file.

Passwd-formatted files (.htpasswd/.htgroup)

Passwd-formatted files, commonly called .htpasswd files in Apache, are text files where each line contains a username and an encrypted password separated by a colon. A typical .htpasswd file might look like:


To get AuthUser to obtain usernames and passwords from a .htaccess file, add the following line to SiteAdmin.AuthUser, replacing "/path/to/.htpasswd" with the filesystem path of the .htpasswd file:

    htpasswd: /path/to/.htpasswd

Creation and maintenance of the .htpasswd file can be performed using a text editor, or any number of other third-party tools available for maintaining .htpasswd files. The Apache web server typically includes an htpasswd command for creating accounts in .htpasswd:

    $ htpasswd /path/to/.htpasswd alice
    New password:
    Re-type new password:
    Adding password for user alice

Similarly, one can use .htgroup formatted files to specify group memberships. Each line has the name of a group (without the "@"), followed by a colon, followed by a space separated list of usernames in the group.

    writers: carol
    editors: alice carol bob
    admins: alice dave

Note that the groups are still "@writers", "@editors", and "@admins" in PmWiki even though the file doesn't specify the @ signs. To get AuthUser to load these groups, use a line in SiteAdmin.AuthUser like:

    htgroup: /path/to/.htgroup

Configuration via local/config.php

AuthUser configuration settings can also be made from the local/config.php file in addition to the SiteAdmin.AuthUser page. Such settings are placed in the $AuthUser array, and must be set prior to including the authuser.php script. Some examples:

    # set a password for alice
    $AuthUser['alice'] = crypt('wonderland');
    # set a password for carol
    $AuthUser['carol'] = '$1$CknC8zAs$dC8z2vu3UvnIXMfOcGDON0';
    # define the @editors group
    $AuthUser['@editors'] = array('alice', 'carol', 'bob');
    # Use local/.htpasswd for usernames/passwords
    $AuthUser['htpasswd'] = 'local/.htpasswd';
    # Use local/.htgroup for group memberships
    $AuthUser['htgroup'] = 'local/.htgroup';

Configuration via LDAP

Authentication can be performed via an external LDAP server -- simply set an entry for "ldap" in either SiteAdmin.AuthUser or the local/config.php file.

    # use for authentication
    $AuthUser['ldap'] = 'ldap://,o=Airius?cn?sub';

Make sure to include AuthUser below the entry for the ldap server:

    # Want to use AuthUser so we can use ldap for passwords

And remember to assign the Security Variables for edit and history (or whatever):

    #Security Variables set login for edit & history page
    # to let anyone edit that has an ldap entry:
    $HandleAuth['diff'] = 'edit';
    $DefaultPasswords['edit'] = 'id:*';
    $Author = $AuthId;

LDAP authentication in AuthUser closely follows the model used by Apache 2.0's mod_auth_ldap module; see especially the documentation for AuthLDAPUrl for a description of the url format.

For servers that don't allow anonymous binds, AuthUser provides $AuthLDAPBindDN and $AuthLDAPBindPassword variables to specify the binding to be used for searching.

See also Cookbook:AuthUser via Microsoft LDAP

Setting the Author Name

By default, PmWiki will use a login name in the Author field of the edit form, but allows the author to change this value prior to saving. To force the login name to always be used as the author name, use the following sequence in config.php to activate AuthUser:

    $Author = $AuthId; # after include_once()

To allow more flexibility, but still enable changes to be linked to the authorized user, one can give the author name a prefix of the $AuthId instead:

    if ($Author) {
	if (strstr($Author, '-') != false) {
	    $Author = "$AuthId-" . preg_replace('/^[^-]*-/', '', $Author);
	} else if ($Author != $AuthId) {
	    $Author = $AuthId . '-' . $Author;
	} else {
	    $Author = $AuthId;
    } else {
	$Author = $AuthId;
    $AuthorLink = "[[~$Author]]";

The above will allow the user to put in the author name of their choice, but that will always be replaced by that name prefixed with "$AuthId-". The reason why $AuthorLink needs to be set is that, if it isn't, the RecentChanges page will have the wrong link in it.

Removing the "Author" edit field

To force users to edit with their AuthID? instead of having a field they can place any name in. This enables administration to keep track of who is doing what better. This line also links the Author name to their Profile.
Go to Site.EditForm, remove the line
$[Author]: (:input e_author:)
or replace it with
$[Author]: [[Profiles/{$Author}]]

Authorization, Sessions, and WikiFarms

PmWiki uses PHP sessions to keep track of any user authorization information. By default PHP is configured so that all interactions with the same server (as identified by the server's domain name) are treated as part of the same session.

What this means for PmWiki is that if there are multiple wikis running within the same domain name, PHP will treat a login to one wiki as being valid for all wikis in the same domain. The easiest fix is to tell each wiki to have use a different "session cookie". Near the top of a wiki's local/config.php file, before calling authuser or other recipes, add a line like:


The XYZSESSID can be any unique name (letters only is safest).

See Also

Can I specify authorization group memberships from with local/config.php?

Yes -- put the group definition into the $AuthUser array (in config.php):

        $AuthUser['@editors'] = array('alice', 'carol', 'bob');

Can I have multiple admin groups?

Yes, define the groups with array('@admins', '@moderators'); like this:

  $DefaultPasswords['admin'] = array( crypt('masterpass'), # global password
    '@admins', '@moderators', # +users in these groups
    'id:Fred', 'id:Barney');  # +users Fred and Barney

I'm running multiple wikis under the same domain name, and logins from one wiki are appearing on other wikis. Shouldn't they be independent?

This is caused by the way that PHP treats sessions. See PmWiki.AuthUser#sessions for more details.

Is there any way to record the time of the last login for each user when using AuthUser? I need a way to look for stale accounts.

See Cookbook:UserLastAction.

Though every setting seems correct, authentication against LDAP is not working. There is nothing in ldap log, what's wrong?

Be sure ldap php module is installed ( on debian apt-get install php(4|5)-ldap ; apache(2)ctl graceful )

The login form asks for username and password, but only password matters.

Username can be left blank and it still signs in under the account. Is this intentional and if so, can I change it so that the username and password must both be entered? - X 1/18/07 Never mind I think this has something to do with using the admin password. I created a test account and it's working ok.

Make sure you are not entering the admin password when testing the account because, if the password is equal to the admin password, it will authenticate directly through the config.php file and skip any other system.

Do note that even with AuthUser activated you can still log in with a blank username and only entering the password. In that case any password you enter will be "accepted" but only passwords which authenticate in the given context will actually give you any authorization rights. Using this capability AuthUser comfortably coexists with the default password-based system.

If you want to require both username and password, then you need to set an admin id before including authuser.php:

## Define usernames and passwords.
$AuthUser['carol'] = '$1$CknC8zAs$dC8z2vu3UvnIXMfOcGDON0';

## Enable authentication based on username.

# $DefaultPasswords['admin'] = crypt('secret');
$DefaultPasswords['admin'] = 'id:carol';

A username and password will then be required before login is successful.

Is there any way to hide IP addresses once someone has logged in so that registered users can keep their IP addresses invisible to everyone except administrators? - X 1/18/07

Yes, see solution provided at PITS:00400.

Is there a way that people could self-register through AuthUser?

You can see HtpasswdForm and AuthUserSignup for two recipes providing this feature.

I would like it that after I have AuthUser turned and a user is authenticated to get on my site, that if I have a password put on a particular page or group that they don't get the AuthUser form to show up (username and password), but only the typical field for password?

See this thread of the mailing list.


The block list is one of a number of security measures that can be taken to protect your wiki from spam and other unwelcome postings.

Unfortunately, the open-editability of many wiki systems often makes them attractive targets for "link spam" or "wikispam", in which links are added to pages in an effort to increase search engine rankings or drive traffic to other sites. Also, many link spammers have developed automated systems to locate sites that accept visitor input and attempt to flood the site with unwanted links. Also, and harder to deal with, is just plain wiki vandalism where nonsense changes are made, often replacing entire pages.

By far the best countermeasure against wikispam is to restrict editing through the use of passwords (see Passwords and Passwords Admin). Experience has shown that passwords can be effective even if the password is widely known, and even if the password is publicly available on the site itself. However, there are many cases where passwording may be an impediment, so these will generally want to use some form of blocklist.

Blocklist basics

A blocklist is a list of IP addresses, phrases, and expressions which are prevented from being added into pages on the website. PmWiki is distributed with a built-in blocklisting capability; blocklists can be enabled by adding the following line to local/config.php:

$EnableBlocklist = 1;

This tells PmWiki to scan the SiteAdmin.Blocklist page and the "SiteAdmin?.Blocklist-Farm" page (and possibly other pages -- see below) looking for phrases and IP addresses to be excluded from posting to the site.

Blocking by word or phrase

The simplest form of block is simply a line containing "block:" followed by a word or phrase to be excluded from postings. For example, a line like

in SiteAdmin?.Blocklist will block any posts containing the string "" (case-insensitive) anywhere in the post.

Blocking by IP address

Sometimes we wish to restrict posts coming from particular addresses or address ranges that are known as sources of wikispam. If a blocklist page contains IP addresses of the form "a.b.c.d" or "a.b.c.*", then any posts coming from that address or range will be blocked.

To find an author's IP address, try hovering the mouse over the author name in the page history for a page.

Blocking by regular expression or pattern

Blocking on simple words can sometimes pose difficulties; for example, a simple "block:cial" entry will also block the word "specialist". For these cases it's often helpful to use a regular expression, as in:


This says to block "cial" only if it doesn't occur in the middle of a larger word. The leading slash (/) after "block:" tells PmWiki to use a regular expression match instead of a simple string match. (Blocklist uses PCRE or "Perl Compatible Regular Expressions"; see for more information.)

Regular expression to block 'href'

If you want to block 'href', you can use the following markup:


which blocks 'href', but neither '\href' nor 'toughref'.

The regular expression can be interpreted as follows: Match any character that is neither a word character nor a '\', followed by href which ends in a word boundary.

Letting authors know why they've been blocked

By default, blocklist only tells an author that a particular edit has been blocked, but doesn't give a specific reason for the blocking (e.g., the offending phrase). Setting the following in a local customization file will also provide the reasons for the block:

$EnableWhyBlocked = 1;

Managing multiple blocklists

PmWiki allows blocklist entries to come from multiple pages by setting the $BlocklistPages variable. By default $BlocklistPages is set to "SiteAdmin?.Blocklist", as well as any automatically downloaded blocklists as described below. PmWiki will use all entries in all the blocklists for filtering wikispam. Setting a value of $BlocklistPages changes the default:

$BlocklistPages = array('Main.Blocklist', 'Test.Blocklist');

The order of blocklists really doesn't matter -- all of the blocklist pages ultimately get used, and the unblock: entries are processed after all of the blocklist pages have been loaded.

Automatically downloaded blocklists

Maintaining blocklists is relatively easy to do, but can become tedious over time. Several groups have formed and maintain "shared blocklists", where a common blocklist is made available to all. PmWiki's blocklist capability has built-in features for automatically downloading and updating such shared blocklists.

If you're just in a hurry to make use of some standard blocklists, make the following setting in local/config.php:

$EnableBlocklist = 10;

This tells PmWiki to not only enable blocklists on the site, but to also configure itself to automatically retrieve and maintain local copies of well-known blocklists such as and MoinMaster. These local copies will be saved in SiteAdmin?.Blocklist-Chongqed and SiteAdmin?.Blocklist-MoinMaster? and refreshed once per day (as determined by the value of $BlocklistDownloadRefresh).

To automatically retrieve the SiteAdmin.Blocklist page used at, add the following setting in local/config.php:

$BlocklistDownload["$SiteAdminGroup.Blocklist-PmWiki"] = array('format' => 'pmwiki');

Ignoring specific entries in a blocklist (unblock)

When using a large master blocklist or blocklists automatically refreshed from external sites, it may be that some entries in the blocklists are inappropriate or overeager and block legitimate content. In this case a wikiadministrator can use "unblock" in a blocklist page to ignore an entry from the blocklist. For example, to allow "" even if another blocklist has a block entry for it:

In order for unblocking to work the phrase or pattern following "unblock:" must be exactly the same as the original.

Permissions on blocklist pages

In general, an administrator will want to edit-protect the SiteAdmin?.Blocklist and any other blocklist pages to prevent arbitrary changes to the blocklist (see Passwords). Since most pages in the SiteAdmin?.* group are edit-protected by default anyway, this usually isn't a problem.

Administrators may also wish to read-protect the various blocklist pages so that others do not know the exact phrases and/or IP addresses that are being blocked. (By their nature blocklists tend to contain phrases or terms that may be offensive or inappropriate to some.)

Any pages created via automatic download (see above) are automatically locked against viewing except by administrators.

administrators (intermediate)

Detailed configuration of automatically downloaded blocklists

Automatic downloading of blocklist information is controlled by the $BlocklistDownload array. An entry for MoinMaster? might look like:

$BlocklistDownload["$SiteAdminGroup.Blocklist-MoinMaster?"] = array(
'format' => 'regex',
'refresh' => 86400);

This says to download the blocklist data from the given url into the SiteAdmin?.Blocklist-MoinMaster? page, that the entries in the blocklist are regular expressions, and to refresh the information every 86,400 seconds (one day).

If 'refresh' is omitted, then the page will be refreshed at the time interval given by $BlocklistDownloadRefresh (default one day). If 'format' is omitted, the page is assumed to have PmWiki-formatted entries as described above. If 'url' is omitted, then the blocklist information is downloaded from a standard location on the site.

To force a refresh of an automatically downloaded blocklist, simply delete the existing page -- a new version will be installed upon the next blocklist scan. Blocklist pages are checked for download in response to any ?action=edit request.

If you are specifying your Blocklist-Pages in the config.php you have to specify the automatically updated pages too, else they won't be updated or created even if you use $EnableBlocklist = 10; .

Farm-wide blocklist

A blocklist can be applied farm-wide (see SharedPages). After these pages are created they can be moved into the farm shared.d/ directory:

Blocklist Variables

The following variables help control the configuration and operation of blocklists:

%apply=item id=EnableBlocklist?%$EnableBlocklist
If set to a non-zero value, then blocklists are enabled on the site. If set to a value of ten or higher, then add entries for automatic downloads of standard blocklists.
$EnableBlocklist = 1; # enable blocklists
$EnableBlocklist = 10; # auto-configure standard blocklists
%apply=item id=EnableWhyBlocked?%$EnableWhyBlocked
By default, authors are not told which particular phrases or IP addresses are causing a particular post to be blocked; setting $EnableWhyBlocked to 1 provides this information.
$EnableWhyBlocked = 1; # give reasons for blocking
%apply=item id=BlocklistPages?%$BlocklistPages
An array of pages to be checked for blocklist entries. The elements of the array may contain page variables. Defaults to "Site.Blocklist", plus any other automatically downloaded blocklist pages.
%apply=item id=BlocklistMessageFmt?%$BlocklistMessageFmt
The message to provide the author whenever a post has been blocked.
%apply=item id=BlockedMessagesFmt?%$BlockedMessagesFmt
If $EnableWhyBlocked is set, defines the text to use for each type of block being performed. Currently only 'ip' and 'text' are recognized.
BlockedMessagesFmt?['ip'] = "IP address blocked from posting: ";
$BlockedMessagesFmt['text'] = "Text blocked from posting: ";
%apply=item id=BlocklistDownload?%$BlocklistDownload
An array of automatically-downloaded blocklists. The keys of the array are the pages in which the blocklists should be stored, the values contain the url, format, and refresh interval for the downloaded blocklist.
  # Download the MoinMaster blocklist every twelve hours
  $BlocklistDownload["$SiteAdminGroup.Blocklist-MoinMaster"] = array(
    'url' => '',
    'format' => 'regex',
    'refresh' => 43200);
  # Download a shared blocklist from every day
  $BlocklistDownload["$SiteAdminGroup.Blocklist-Shared"] = array(
    'format' => 'pmwiki');
%apply=item id=BlocklistDownloadRefresh?%$BlocklistDownloadRefresh
The default refresh interval for any $BlocklistDownload entries that don't explicitly specify a 'refresh' value.
# perform automatic downloads once per week by default
$BlocklistDownloadRefresh = 86400 * 7;
%apply=item id=BlocklistDownloadFmt?%$BlocklistDownloadFmt
The format to use when saving automatically downloaded blocklists.
%apply=item id=EnableBlocklistImmediate?%$EnableBlocklistImmediate
Some cookbook recipes update pages with author input but don't use the built-in data posting routines. If $EnableBlocklistImmediate is set (default) and the current action is listed in $BlocklistActions (below), then an immediate blocklist scan is performed on the incoming text.
%apply=item id=BlocklistActions?%$BlocklistActions
A list of actions for which immediate blocklist checks should be performed (see $EnableBlocklistImmediate above).
# perform immediate checks for ?action=comment
$BlocklistActions['comment'] = 1;
# perform immediate checks for ?action=postdata
$BlocklistActions['postdata'] = 1;


The notify.php script allows a site administrator to configure PmWiki to send email messages whenever pages are changed on the wiki site. Notifications can be configured so that multiple page changes over a short period of time are combined into a single email message (to avoid flooding mailboxes).

This feature is useful for sites and pages that have infrequent updates, as it eliminates the need to frequently check RecentChanges pages just to see if anything has changed.

In order for notifications to work, the notify.php script must be enabled in the site's local customization. Usually this is as simple as placing the following in local/config.php:

Notification configuration

Once enabled, the notification system gets its configuration from the SiteAdmin.NotifyList wiki page. The SiteAdmin.NotifyList page contains entries of the form:

This says that information about page changes should be periodically emailed to The SiteAdmin.NotifyList page can contain multiple "notify=" lines to cause notifications to be sent to multiple addresses; the "notify=" lines can be concealed by placing them inside of an (:if false:) conditional section on the page.

NOTE: Do not put any spaces around the equal sign! Notifications will fail silently if you have... This is a really easy mistake to make because all of the other assignments have spaces around the equal sign. rather than notify =

Notification options

The basic syntax is

notify=email@address name=abc group=def trail=ghi squelch=123 delay=123

A number of options exist for limiting the pages that result in a notification. The group= and name= parameters can be used to restrict notifications to specific pages or groups:

# send notifications about the Main group to group=Main

# notify of any changes to the home page name=Main.HomePage
# notify of changes to pages except in Main group=-Main

(Note: The options are similar to the PageList syntax.)

For maintaining arbitrary lists of pages, i.e., "watchlists", it's generally easier to build a trail of pages to be watched. The following entry in SiteAdmin.NotifyList will send an email containing changes to any of the pages listed in the Profiles.Alice trail:

# notify Alice of changes to pages listed in Profiles.Alice trail=Profiles.Alice

Note that once this entry has been added to SiteAdmin.NotifyList, Alice can easily change her watchlist by editing the Profiles.Alice page, and doesn't need to edit the SiteAdmin.NotifyList page. In particular, this means that an administrator can restrict editing of SiteAdmin.NotifyList, yet allow individuals to maintain custom watchlists in other pages.

Limitations of this feature:

  • only manually-added links on a trail will be acknowledged by the Notify List (no "group=" or other pagelist syntax, nor any "Group.RecentChanges?" links, will generate notifications)
  • using an (:include:) directive on the page SiteAdmin.NotifyList is not an operational work-around.

This is probably a good place to point out that edit access to SiteAdmin.NotifyList should be controlled, otherwise malicious persons can use the notification capability to flood others' electronic mailboxes. By default, SiteAdmin.NotifyList is blocked against reading or edits except by the admin (as is the case for most pages in the SiteAdmin? group).

Adding notification entries via local customizations

Notification entries can also be added via the $NotifyList array in local/config.php. Simply add a line like the following:

$NotifyList[] = ' group=Main';
$NotifyList[] = ' name=Main.HomePage';

Controlling notification frequency

To prevent flooding of recipients' mailboxes, the notify script uses a "squelch" value as the minimum amount of time that must elapse between messages sent to any given email address. The default squelch setting is 10800 seconds (three hours), which means that once a recipient address is sent a notification message, it will not receive another for at least three hours. Any edits that occur during the squelch interval are queued for the next notification message.

The site administrator can change the default squelch interval via the $NotifySquelch parameter

# enable notifications
$NotifySquelch = 86400; # wait at least one day (in seconds) between notifications

In addition, individual addresses can specify a custom squelch parameter in the SiteAdmin.NotifyList page:

# Alice receives at most one email per day squelch=86400
# Bob can get notifications hourly trail=Profiles.Bob squelch=3600
# Charles uses the site default squelch

Controlling notification delay

Because a page will often receive several edits in rapid succession (e.g., a long post followed by several minor edits), a site administrator can also set a $NotifyDelay value that specifies how long to wait after an initial post before sending notifications:

# enable notifications
$NotifySquelch = 86400; # wait at least one day between notifications
$NotifyDelay = 300; # wait five minutes after initial post

Note that the squelch and delay values are minimums; notifications are sent on the first execution of PmWiki after the delay period has expired. For inactive sites, this could be much longer than the specified delay periods. This isn't really considered an issue since timely notifications are less important on relatively inactive sites. However, changes within the squelch time after the last notification will remain unnoticed if the wiki is not even visited for a long period after. If this matters it might be necessary to make the server call pmwiki.php regularly (e.g. cron job).

Custom delay parameters cannot be specified for individual addresses in the SiteAdmin.NotifyList page:

# the delay= parameter will be ignored trail=Profiles.Edgar delay=600

Note for Windows installations

Sites running PHP under Windows may not have PHP's mail function configured correctly. Such sites may need to add a line like


to config.php, where is the name of your host's preferred outgoing mail server. You may also need to set the sendmail_from value if that is not configured:


Notify Variables

%apply=item id=EnableNotify?%$EnableNotify
Tells stdconfig.php to enable the notify script.
$EnableNotify = 1; # enable notify
$EnableNotify = 0; # disable notify
%apply=item id=NotifyFrom?%$NotifyFrom
Return email address to be used in the sent email.
$NotifyFrom = '';
$NotifyFrom = 'Wiki server <>';
%apply=item id=NotifyDelay?%$NotifyDelay
The length of time (seconds) to wait before sending mail after the first post. Defaults to zero - posts are sent as soon as any squelch period has expired.
$NotifyDelay = 300; # send mail 5+ min after first post
%apply=item id=NotifySquelch?%$NotifySquelch
The default minimum time (seconds) that must elapse between sending mail messages. Useful when $NotifyDelay is set to a small value to keep the number of mail notification messages down. Defaults to 10800 (three hours). Individual recipients can override this value in the SiteAdmin.NotifyList page.
$NotifySquelch = 43200; # wait 12+ hours between mailings
%apply=item id=NotifyItemFmt?%$NotifyItemFmt
The text to be sent for each changed item in the post. The string "$PostTime" is substituted with the time of the post (controlled by $NotifyTimeFmt below).
# default
$NotifyItemFmt = ' * $FullName . . . $PostTime by $Author';
# include the page's URL in the message
$NotifyItemFmt =
" * \$FullName . . . \$PostTime by \$Author\n \$PageUrl";
# include the change summary and link to the page's history in the message
$NotifyItemFmt =
" * {\$FullName} . . . \$PostTime by {\$Author}
\n Summary: {\$LastModifiedSummary}\n {\$PageUrl}?action=diff";
%apply=item id=NotifyTimeFmt?%$NotifyTimeFmt
The format for dates/times in $PostTime above. Defaults to the value of $TimeFmt.
$NotifyTimeFmt = '%Y-%m-%d %H:%M'; # 2004-03-20 17:44
%apply=item id=NotifyBodyFmt?%$NotifyBodyFmt
The body of the message to be sent. The string "$NotifyItems" is replaced with the list of posts (as formatted by $NotifyItemFmt above). Use single quotation marks ' to prevent substring "$NotifyItems" from being untimely evaluated as variable in config.php.
$NotifyBodyFmt = "Changed items:\n\n" . '$NotifyItems' . "\n\n Best regards...";
%apply=item id=NotifySubjectFmt?%$NotifySubjectFmt
The subject line of the mail to be sent.
%apply=item id=NotifyHeaders?%$NotifyHeaders
String of extra mail headers to be passed to the mail() function.
%apply=item id=NotifyParameters?%$NotifyParameters
String of additional parameters to be passed to PHP's mail() function [8].
%apply=item id=NotifyFile?%$NotifyFile
The scratch file where Notify keeps track of recent posting information. Defaults to "$WorkDir/.notifylist". Note that this file must generally be writable by the webserver process.
%apply=item id=NotifyListPageFmt?%$NotifyListPageFmt
The name of the page containing notify= lines for use by notify.php. Defaults to $SiteAdminGroup.NotifyList.
%apply=item id=NotifyList?%$NotifyList
An array of notify= specifications that can be specified from a local customization file (used in addition to entries in SiteAdmin.NotifyList).
# send notifications to
$NotifyList[] = '';
%apply=item id=EnableNotifySubjectEncode?%$EnableNotifySubjectEncode
Apply a standard (base64) encoding for the e-mail subject. Notify e-mails from international wikis may otherwise have unreadable subjects (added for version 2.2.2).
$EnableNotifySubjectEncode = 1; # encode subject
$EnableNotifySubjectEncode = 0; # use subject as is (default)
To fix encodings with the message body, add to config.php the following line (after XLPage? and/or UTF-8):
$NotifyHeaders = "Content-type: text/plain; charset=$Charset";

Notification only for major edits

It is possible to send notifications only in case of major edits. In your config.php, replace "$EnableNotify=1;" with the following:

if ( @$_POST['diffclass'] != 'minor' ) $EnableNotify=1;

This way, only 'major' edits send notify messages (when the author doesn't select the checkbox for minor edit). If you want minor edits and not major edits to send the message then you would use:

if ( @$_POST['diffclass'] == 'minor' ) $EnableNotify=1;


Disabling notifications for downloads

If you use "$EnableDirectDownloads=0;" (eg. for privacy on a password-protected wiki) then attached images may generate duplicate notification messages. To prevent that disable notifications for downloads via

if ( $action != 'download' ) $EnableNotify=1;

That way, only page views (and not images within the page) will generate notifications. See PITS:01159 for more information.


PmWiki has built-in support for password-protecting various areas of the wiki site. Passwords can be applied to individual pages, to Wiki Groups, or to the entire wiki site. Note that the password protection mechanisms described here are only a small part of overall system (and wiki) security, see PmWiki.Security for more discussion of this.

Authors can use PmWiki to add passwords to individual pages and WikiGroups as described in Passwords. However, WikiAdministrators can also set passwords in local/config.php as described below. (Please note that one cannot set passwords reliably in per group or per page customization files. See the FAQ section for details.)

Password basics

PmWiki supports several levels of access to wiki pages, known as authorisation level:

  • read passwords allow viewing the contents of wiki pages
  • edit passwords control editing and modification of wiki pages (effective against spam)
  • attr passwords control who is able to set passwords on pages (and potentially other future attributes)
  • upload password, if uploads are enabled, controls uploading of files and attachments
  • in addition all available actions can be password authorised
  • admin password allows an administrator to override the passwords set for any individual page or group.

By default, PmWiki has the following password settings:

  • The admin and upload passwords are locked by default.
  • The Main and PmWiki groups have a locked attr password (in their respective GroupAttributes pages).
  • The pages in the Site group except Site.SideBar are locked against editing; by default the Site.SideBar page requires the admin or the site-wide edit password.

An admin password can be used to overcome "locked" passwords, other than that, no password will allow access.

See Passwords for information about setting per-page and per-group passwords. The remainder of this page describes setting site-wide passwords from the local/config.php file.

Setting site-wide passwords

One of the first things an admin should do is set an admin password for the site. This is done via a line like the following in the local/config.php file:

$DefaultPasswords['admin'] = crypt('secret_password');

Note that the crypt() call is required for this -- PmWiki stores and processes all passwords internally as encrypted strings. See the crypt section below for details about eliminating the cleartext password from the configuration file.

To set the entire site to be editable only by those who know an "edit" password, add a line like the following to local/config.php:

$DefaultPasswords['edit'] = crypt('edit_password');

Similarly, you can set a password for any available action, viz $DefaultPasswords['read'], $DefaultPasswords['edit'], and $DefaultPasswords['upload'] to control default read, edit, and upload passwords for the entire site. The default passwords are used only for pages and groups which do not have passwords set. Also, each of the $DefaultPasswords values may be arrays of encrypted passwords:

$DefaultPasswords['read'] = array(crypt('alpha'), crypt('beta'));
$DefaultPasswords['edit'] = crypt('beta');

This says that either "alpha" or "beta" can be used to read pages, but only the "beta" password will allow someone to edit a page. Since PmWiki remembers any passwords entered during the current session, the "beta" password will allow both reading and writing of pages, while the "alpha" password allows reading only. A person without either password would be unable to view pages at all.

Setting passwords by reference

Setting passwords by reference allows you to change the password for a whole set of pages as easily as you can change site-wide passwords. (Otherwise you would have to update each page's attributes individually.) Enter in the Page Attributes or Group Attributes:


And in the local configuration file set the actual password with lines like this:

$DefaultPasswords['MyPassword2?'] = array(crypt('secret'), '@admins');
$DefaultPasswords['MyPassword9?'] = array('$1$NuBV/Mcc$GG3J60h.TLczUTRKhoVPM?.');

Identity-based authorization (username/password logins, AuthUser)

Unlike many systems which have identity-based systems for controlling access to pages (e.g., using a separate username and password for each person), PmWiki defaults to a password-based system as described above. In general password-based systems are often easier to maintain because they avoid the administrative overheads of creating user accounts, recovering lost passwords, and mapping usernames to permitted actions.

However, PmWiki's authuser.php script augments the password-based system to allow access to pages based on a username and password combination. See AuthUser for more details on controlling access to pages based on user identity.

Security holes ...

Administrators need to carefully plan where passwords are applied to avoid opening inadvertent security holes. If your wiki is open (anyone can read and edit), this would not seem to be a concern, except, a malicious or confused user could apply a read password to a group and make the group completely unavailable to all other users. At the very least, even an open wiki should have a site-wide "admin" password and a site-wide "attr" password set in config.php. The sample-config.php file distributed with PmWiki indicates that the PmWiki and Main groups have "attr" locked by default, but if anyone creates a new group, "attr" is unlocked. Administrators must remember to set "attr" passwords for each new group (if desired) in this case. An easier solution is to include these lines in config.php :

$DefaultPasswords['admin'] = crypt('youradminpassword');
$DefaultPasswords['attr'] = crypt('yourattrpassword');

Encrypting passwords in config.php

One drawback to using the crypt() function directly to set passwords in config.php is that anyone able to view the file will see the unencrypted password. For example, if config.php contains

$DefaultPasswords['admin'] = crypt('mysecret');

then the "mysecret" password is in plain text for others to see. However, a wiki administrator can obtain and use an encrypted form of the password directly by using ?action=crypt on any PmWiki url (or just jump to IncludeAll?action=crypt). This action presents a form that generates encrypted versions of passwords for use in the config.php file. For example, when ?action=crypt is given the password "mysecret", PmWiki will return a string like


The string returned from ?action=crypt can then be placed directly into config.php, as in:

$DefaultPasswords['admin'] = '$1$hMMhCdfT$mZSCh.BJOidMRn4SOUUSi1';

Note that in the encrypted form the crypt keyword and parentheses are removed, since the password is already encrypted. Also, the encrypted password must be in single quotes. In this example the password is still "mysecret", but somebody looking at config.php won't be able to see that just from looking at the encrypted form. Crypt may give you different encryptions for the same password--this is normal (and makes it harder for someone else to determine the original password).

Removing passwords

To remove a site password entirely, such as the default locked password for uploads, just set it to empty:

$DefaultPasswords['upload'] = '';

You can also use the special password "@nopass" via ?action=attr to have a non-password protected page within a password-protected group, or a non-password protected group with a site-wide default password set.

Revoking or invalidating passwords

If a password is compromised and the wiki administrator wants to quickly invalidate all uses of that password on a site, a quick solution is the following in local/config.php:

$ForbiddenPasswords = array('secret', 'tanstaafl');
if (in_array(@$_POST['authpw'], $ForbiddenPasswords)) 

This prevents "secret" and "tanstaafl" from ever being accepted as a valid authorization password, regardless of what pages may be using it.

See Also

Protecting actions (example)

Each action can be password protected. Cookbook authors providing scripts with own actions can use this also, but I'll limit the example to a (by default) not protected ?action=source. This action shows the wikisource of the actual page. Sometimes you don't want that especially to Cookbook:protect email or when using some conditional markup which should not be discovered easily or only by persons that are allowed to edit the page.

There are several solutions for that:

  1. Limit "source" only to editors add the following to your local/config.php:
    $HandleAuth['source'] ='edit';
  2. For using "source" with an own password, then add:
    $HandleAuth['source'] ='source';
    $DefaultPasswords['source'] = crypt('secret'); # see above

If you additionally want to set the password in the attributes page add:

$PageAttributes['passwdsource'] = "$['Set new source password']";

In general, adding the prefix 'passwd' to an action name in the $PageAttributes array indicates that you wish for the given field to be encrypted when saved to disk.

The full set of steps to add new password handling for an action such as "diff" would be:

# add a new (encrypted) field to the attr page
$PageAttributes['passwddiff'] = '$[Set new history password:]';

# clear the default password for 'diff'
$DefaultPasswords['diff'] = '';

# Tell PmWiki that the 'diff' password allows action 'diff'.
$HandleAuth['diff'] = 'diff';

# Tell PmWiki that a 'read' password 
# (or optionally the 'edit') password
# is also sufficient to enable 'diff'.
# Of course, the 'admin' password will work too.
$AuthCascade['diff'] = 'read';    ## or 'edit'

There seems to be a default password. What is it?

There isn't any valid password until you set one. Passwords admin describes how to set one.

PmWiki comes "out of the box" with $DefaultPasswords['admin'] set to '*'. This doesn't mean the password is an asterisk, it means that default admin password has to be something that encrypts to an asterisk. Since it's impossible for the crypt() function to ever return a 1-character encrypted value, the admin password is effectively locked until the admin sets one in config.php.

How do I use passwd-formatted files (like .htpasswd) for authentication?

See AuthUser, Cookbook:HtpasswdForm or Cookbook:UserAuth.

Is there anything I can enter in a GroupAttributes field to say 'same as the admin password'? If not, is there anything I can put into the config.php file to have the same effect?

Enter '@lock' in GroupAttributes?action=attr to require an admin password for that group.

How do I edit protect, say, all RecentChanges pages?

see Security#wikivandalism.

How can I read password protect all pages in a group except the HomePage using configuration files?

As described in PmWiki.GroupCustomizations per-group or per-page configuration files should not be used for defining passwords. The reason is that per-group (or per-page) customization files are only loaded for the current page. So, if $DefaultPasswords['read'] is set in GroupA?.php, then someone could use a page in another group to view the contents of pages in GroupA?. For example, Main.WikiSandbox could contain:

(:include GroupA.SomePage:)

and because the GroupA?.php file wasn't loaded (we're looking at Main.WikiSandbox --> local/Main.php), there's no read password set.

How can I password protect the creation of new pages?

See Cookbook:LimitWikiGroups, Cookbook:NewGroupWarning, Cookbook:LimitNewPagesInWikiGroups.

How do I change the password prompt screen?

If your question is about how to make changes to that page... edit Site.AuthForm. If your question is about how to change which page you are sent to when prompted for a password, you might check out the Cookbook:CustomAuthForm for help.

How do I change the prompt on the attributes (?action=attr) screen?

Simply create a new page at Site.AttrForm?, and add the following line of code to config.php:

$PageAttrFmt = 'page:Site.AttrForm?';

Note that this only changes the text above the password inputs on the attributes page, but doesn't change the inputs themselves - the inputs have to be dealt with separately. See Cookbook:CustomAttrForm for more info.

I get http error 500 "Internal Server Error" when I try to log in. What's wrong?

This can happen if the encrypted passwords are not created on the web server that hosts the PmWiki.
The crypt function changed during the PHP development, e.g. a password encrypted with PHP 5.2 can not be decrypted in PHP 5.1, but PHP 5.2 can decrypt passwords created by PHP 5.1.
This situation normally happens if you prepare everything on your local machine with the latest PHP version and you upload the passwords to a webserver which is running an older version.
The same error occurs when you add encrypted passwords to local/config.php.

Solution: Create the passwords on the system with the oldest PHP version and use them on all other systems.

I only want users to have to create an 'edit' password, which is automatically used for their 'upload' & 'attr' passwords (without them having to set those independently). How do I do this?

By setting $HandleAuth like so:

      $HandleAuth['upload'] = 'edit';
      // And to prevent a WikiSandbox from having it's 'attr' permissions changed 
      // except by the admin (but allowing editors to change it on their own pages/group)
      if(($group=="Site") || ($group=="Main") || ($group=="Category") || 
             ($group=="SiteAdmin?") || ($group=="PmWiki") ) {
	$HandleAuth['attr'] = 'admin';  // for all main admin pages, set 'attr' to 'admin' password
      } else { 
	$HandleAuth['attr'] = 'edit';  // if you can edit, then you can set attr


RefCount performs link reference counts on pages in the PmWiki database (i.e., counts of links between pages). Before using RefCount, it must be enabled by the wiki administrator by placing the following line in a local customization file:


To use refcount add ?action=refcount to the URL of any wiki page to bring up the reference count form. For example:

The refcount form contains the following controls:

  • Show ~ This selects which pages will appear in the output
    • all ~ Shows all references
    • missing ~ Shows only references to pages that don't exist
    • existing ~ Shows only references to pages that do exist
    • orphaned ~ Shows pages that exist but don't have any references to them. There is no way to browse to an orphaned page.
  • page names in group ~ Selects which group(s) to the referenced pages can be in
  • referenced from pages in ~ Selects which group(s) the referencing pages can be in
  • Display referencing pages ~ Includes a link to the referencing page -- this can make for a very long output unless you limit the groups searched

The output is a table where each row of the table contains a page name or link reference, the number of (non-RecentChanges) pages that contain links to the page and the number of Recent Changes pages with links to the page.


This page explains how to discourage "link spamming" on your wiki site using PmWiki's urlapprove.php script. This script is already included in PmWiki files, but not activated by default.

Using urlapprove.php

Occasionally spammers may try to add large number of (sometimes hidden) URLs to pages because they think it will improve their search engine rankings -- which it probably won't. The urlapprove.php script works against these spammers' purpose by

  • requiring approval of links to Internet sites before a link to them are created in the wiki, and
  • allowing you to limit the number of unapproved links that may be added to a page.

To enable urlapprove.php, add the following line to a configuration file:


By default, unapproved links display what ever should be displayed normally (the URL or a text), but not linked and next to it a link (approve links). A click on the link will approve all unapproved URLs on the page, but only if you are authorized to edit the SiteAdmin.ApprovedUrls page. You may also pre-approve sites by by adding them directly to the SiteAdmin.ApprovedUrls page.

Limiting unapproved urls per page

You can limit the number of unapproved links per page. If the limit is exceeded, the page cannot be saved. This is useful because spammers like to write long link lists, which is rare for normal authors.

Example: To set the limit to 5 unapproved links, add the following line to a configuration file:

$UnapprovedLinkCountMax = 5;

Note that $UnapprovedLinkCountMax must be set before including the urlapprove.php script.

Handling of Unapproved Links

You can also change the disapproval message defined in the $UnapprovedLinkFmt variable, for example:

$UnapprovedLinkFmt =
 "[$[Link requires approval]]<a class='apprlink'

"Link requires approval" is whatever you want to see in place of the unapproved link and "(approve)" is the blue text. Using this feature may prove usefull if you want to always hide the unapproved link.

If you wish to totally forbid unapproved links you can use

$UnapprovedLinkFmt = "<b>external link not allowed</b>";

SideBar caveat

Please note that in general you need to go to the sidebar page in order to approve links in the sidebar. The reason for this is that the approve mechanism only approves links on the current page.

Initial setup

After initial setup all existing links become unapproved. You need to visit your pages and approve all links, where needed. See AllRecentChanges for a list of all pages that were created on your wiki.

Password approval of URLs?

To approve external links, an author needs permissions to edit the page SiteAdmin.ApprovedUrls.

Technical tips

URL Whitelist

Urls can also be approved by adding them to a "white list", defined in the variable $WhiteUrlPatterns, which is set in the local/config.php file.
To add multiples urls, use the separator | (vertical bar). For example:

$WhiteUrlPatterns =

To add all urls from, say New Zealand and Australia, use:

$WhiteUrlPatterns[] = 'http://[^/]+\\.nz';
$WhiteUrlPatterns[] = 'http://[^/]+\\.au';

Change Approved URLs? page name

If you want to change the default name of SiteAdmin.ApprovedUrls, set the following in local/config.php:

$ApprovedUrlPagesFmt = array('OtherGroup.OtherName');

Previewing the unapproved URL

To see what link is to be approved without editing the page a tool tip can be displayed when the cursor hovers over the (approve links) link that displays the URL. e.g. [(approve links) edit diff].

Add the following setting in your local/config.php:

$UnapprovedLinkFmt =
  "\$LinkText<a class='apprlink' href='\$PageUrl?action=approvesites'
   title='\$LinkUrl'>$[(approve links)]</a>";
Some browsers show only the link and not the tooltip title. In this case, you can use the following code to see the unapproved link at the end of the tooltip :
$UnapprovedLinkFmt =
  "\$LinkText<a class='apprlink' href='\$PageUrl?action=approvesites&XES_url=\$LinkUrl'
   title='\$LinkUrl'>$[(approve sites)]</a>";

About rel='nofollow'

By default, PmWiki creates external links that are not followed by search engines. Here are release notes from pmwiki-2.0.beta20 (30-Jan-2005):

First, the $UrlLinkFmt variable has been modified so that links to external urls automatically have a rel='nofollow' attribute added to them, to help combat wiki spam as described in Site administrators can customize $UrlLinkFmt and $UnapprovedLinkFmt to supply or omit rel='nofollow' as appropriate.

See Also


List of documented PHP variables

VariableDocumented in
where is this documented?

This page documents the PHP variables available in PmWiki for local customizations. Much of this documentation is still incomplete but people are working on it now. Feel free to add placeholders for variables you want to have documented if you don't know what the variable does.

The variables documentation is divided into several pages:

The following functions are also controlled by several variables:

The following variables are used in page markup.

  • Page Variables - variables that are associated with pages
  • Page TextVariables - Testing the functionality of page text variables, especially leading and trailing spaces, and construction of variable names

An complete index of documented PHP variables is given below.

In general, variables with names ending in 'Fmt' (such as $PageLayoutFmt) have their values processed for $-variable substitutions prior to being output. Thus strings such as {$Name} and {$PageUrl} are replaced with the name and URL of the page when the string is printed.

Note: The automatic variable index and link generation is done by scripts/vardoc.php using $VarPagesFmt to find the pages containing trails of pages with the variable documentation.

There is a slight discrepancy between index generation and link generation: The index generation finds lines starting with a colon followed by "$" and an uppercase word. In contrast, the automatic link generation works only with WikiWords ($WikiWordPattern) preceded by "$". Therefore all "non WikiWord" variables are shown as link only in the list below, but not elsewhere in PmWiki, as $Author, $Version and $XL.

See Also

  • Functions - How some of the functions in pmwiki.php work

Categories: PmWiki Developer


This page describes some of the internal workings of PmWiki by explaining how some of the functions in pmwiki.php work. For a more brief list/overview on functions useful to for instance cookbook writers, see Cookbook:Functions.

To use this functions you have to make sure that all relevant internal variables have been initialized correctly. See Custom Markup and Custom Actions for more information on how these functions are typically called via Markup() or $HandleActions[].


The PSS() function removes the backslashes that are automatically inserted in front of quotation marks by the /e option of PHP's preg_replace function. PSS() is most commonly used in replacement arguments to Markup(), when the pattern specifies /e and one or more of the parenthesized subpatterns could contain a quote or backslash. ("PSS" stands for "PmWiki Strip Slashes".)

From PM: PmWiki expects PSS() to always occur inside of double-quoted strings and to contain single quoted strings internally. The reason for this is that we don't want the $1 or $2 to accidentally contain characters that would then be interpreted inside of the double-quoted string when the PSS is evaluated.
Markup('foo', 'inline', '/(something)/e', 'Foo(PSS("$1"))'); # wrong
Markup('foo', 'inline', '/(something)/e', "Foo(PSS('$1'))"); # right


This is a fictitious example where PSS() should be used. Let us assume that you wish to define a directive (:example:) such that (:example "A horse":) results in the HTML

<div>"A horse"</div>.

Here is how the markup rule can be created:

Markup('example', 'directives',

We need to use PSS() around the '$1' because the matched text could contain quotation marks, and the /e will add backslashes in front of them.


This function should be used when processing the contents of $_POST or _GET variables when they could contain quotes or backslashes. It verifies get_magic_quotes(), if true, strips the automatically inserted escapes from the string.

FmtPageName($fmt, $pagename)

Returns $fmt, with $variable and $[internationalisation] substitutions performed, under the assumption that the current page is pagename. See PmWiki.Variables for an (incomplete) list of available variables, PmWiki.Internationalizations for internationalisation. Security: not to be run on user-supplied data.

This is one of the major functions in PmWiki, see PmWiki.FmtPageName for lots of details.

Markup($name, $when, $pattern, $replace)

Adds a new markup to the conversion table. Described in greater detail at PmWiki.CustomMarkup.

This function is used to insert translation rules into the PmWiki's translation engine. The arguments to Markup() are all strings, where:

The string names the rule that is inserted. If a rule of the same name already exists, then this rule is ignored.
This string is used to control when a rule is to be applied relative to other rules. A specification of "<xyz" says to apply this rule prior to the rule named "xyz", while ">xyz" says to apply this rule after the rule "xyz". See CustomMarkup for more details on the order of rules.
This string is a regular expression that is used by the translation engine to look for occurences of this rule in the markup source.
This string will replace the matched text when a match occurs.

Also see: PmWiki.CustomMarkup and Cookbook:Functions#Markup

MarkupToHTML($pagename, $str)

Converts the string $str containing PmWiki markup into the corresponding HTML code, assuming the current page is $pagename.

Also see: Cookbook:Functions#MarkupToHTML


The function mkdirp($dir) creates a directory, $dir, if it doesn't already exist, including any parent directories that might be needed. For each directory created, it checks that the permissions on the directory are sufficient to allow PmWiki scripts to read and write files in that directory. This includes checking for restrictions imposed by PHP's safe_mode setting. If mkdirp() is unable to successfully create a read/write directory, mkdirp() aborts with an error message telling the administrator the steps to take to either create $dir manually or give PmWiki sufficient permissions to be able to do it.

MakeLink($pagename, $target, $txt, $suffix, $fmt)

The function MakeLink($pagename, $target, $txt, $suffix, $fmt) returns a ???. Its arguments are as follows:

 $pagename is the source page
 $target is where the link should go
 $txt is the value to use for '$LinkText' in the output 
 $suffix is any suffix string to be added to $txt
 $fmt is a format string to use

If $txt is NULL or not specified, then it is automatically computed from $target.

If $fmt is NULL or not specified, then MakeLink uses the default format as specified by the type of link. For page links this means the $LinkPageExistsFmt and $LinkPageCreateFmt variables, for intermap-style links it comes from either the $IMapLinkFmt array or from $UrlLinkFmt. Inside of the formatting strings, $LinkUrl is replaced by the resolved url for the link, $LinkText is replaced with the appropriate text, and $LinkAlt is replaced by any "title" (alternate text) information associated with the link.

Also see: PmWiki:MakeLink and Cookbook:Functions#MakeLink

MakeUploadName($pagename, $x)

MakeUploadName?() simply takes a string $x (representing an attachment's name) and converts it to a valid name by removing any unwanted characters. It also requires the name to begin and end with an alphanumeric character, and as of 2.0.beta28 it forces any file extensions to lowercase. This function is defined in scripts/upload.php and only used when uploads are enabled.

SessionAuth($pagename, $auth=NULL)

SessionAuth?() manages keeping authentication via cookie-sessions. Session contains ever password or vaidated id and associated groups from previous calls.It adds elements passed by $auth to session. It also writes every element saved in session to $AuthPw(passwords) and $AuthList(ids and groups).

IsAuthorized($chal, $source, &$from)

IsAuthorized? takes a pageattributesstring (e. g. "id:user1 $1$Ff3w34HASH...") in $chal. $source is simply returned and used for building the authcascade (pageattributes - groupattributes - $DefaultPassword). $from will be returned if $chal is empty, because it is not checked before calling IsAuthorized?(), this is needed for the authcascade. IsAuthorized?() returns an array with three values: $auth 1 - authenticated, 0 - not authenticated, -1 - refused; $passwd; $source from the parameter list.

CondAuth ($pagename, 'auth level')

CondAuth implements the ConditionalMarkup for (:if auth level:). For instance CondAuth($pagename,'edit') is true if authorization level is 'edit'. Use inside local configuration files to build conditionals with a check of authorization level, similar to using (:if auth level:) on a wiki page.

Note that CondAuth() should be called after all authorization levels and passwords have been defined. For example, if you use it with Drafts, you should include the draft.php script before calling CondAuth():

   $EnableDrafts = 1;
   $DefaultPasswords['publish'] = crypt('secret');
   if (! CondAuth($pagename, 'edit')) { /* whatever */ }

Best is to use CondAuth() near the bottom of your config.php script.

RetrieveAuthPage?($pagename, $level, $authprompt=true, $since=0)

Pm words as said in where:

   $pagename   - name of page to be read
   $level      - authorization level required (read/edit/auth/upload)
   $authprompt - true if user should be prompted for a password if needed
   $since      - how much of the page history to read
                 0 == read entire page including all of history
                 READPAGE_CURRENT == read page without loading history
                 timestamp == read history only back through timestamp

The $since parameter allows PmWiki to stop reading from a page file as soon as it has whatever information is needed -- i.e., if an operation such as browsing isn't going to need the page's history, then specifying READPAGE_CURRENT can result in a much faster loading time. (This can be especially important for things such as searching and page listings.)

Use e.g. $page = @RetrieveAuthPage('Main.MyPage', 'read') to obtain a page object that contains all the information of the correspondent file in separate keys, e.g. $page['text'] will contain a string with the current wiki markup of Main.MyPage?. Use this generally in preference to the alternative function ReadPage($pagename, $since=0) since it respects the authorisation of the user, i.e. it checks the authorisation level before loading the page, or it can be set to do so. ReadPage() reads a page regardless of permission.

Passing 'ALWAYS' as the authorization level (instead of 'read', 'edit', etc.) will cause RetrieveAuthPage? to always read and return the page, even if it happens to be protected by a read password.

RetrieveAuthSection?($pagename, $pagesection, $list=NULL, $auth='read')

RetrieveAuthSection? extracts a section of text from a page. If $pagesection starts with anything other than '#', it identifies the page to extract text from. Otherwise RetrieveAuthSection? looks in the pages given by $list, or in $pagename if $list is not specified.

  • The selected page is placed in the global $RASPageName variable.
  • The caller is responsible for calling Qualify() as needed.

Provides a way to limit the array that is returned by ReadPage?, so that it only pulls the content up to a specific section marker. For example, pulling from start of page to '##blogend':

function FeedText($pagename, &$page, $tag) {
  $text = RetrieveAuthSection($pagename, '##blogend');
  $content = MarkupToHTML($pagename, $text);
  return "<$tag><![CDATA[$content]]></$tag>";

The '##blogend' argument says to read from the beginning of the page to just before the line containing the marker. See IncludeOtherPages for more information about the section specifications.

This version won't read text from pages that are read-protected; if you want to get text even from read-protected pages, then

  $text = RetrieveAuthSection($pagename, '##blogend', NULL, 'ALWAYS');

UpdatePage($pagename, $old (page object), $new (page object));

More Technical Notes

UpdatePage() allows cookbook recipes to mimic the behavior of editing wiki pages via the browser. Internally, PmWiki does several house keeping tasks which are accessible via this function (preserving history/diff information, updating page revision numbers, updating RecentChanges pages, sending email notifications, etc._

  • "Page object" refers to an array pulled from ReadPage($pagename); Note that $new['text'] should contain all page data for the new version of the page.
  • If a page doesn't exist, UpdatePage() will attempt to create it.
  • Ignoring $old (e.g. UpdatePage($pagename, '', $new);) will erase all historical page data---a tabula rasa.

UpdatePage() cannot be called directly from config.php because there are necessary initializations which occur later in pmwiki.php. It is not enough to just load stdconfig.php. If you want to use UpdatePage() you will need to do it within a custom markup, a custom markup expression, or a custom action.

Categories: PmWiki Developer


You may have many documents that you would like to use a local program to format in a format PmWiki can display.

You could open each document and copy/paste the content to new pmwiki pages or you could format the document in advance and upload it using an FTP client.

Only two lines are necessary in a PmWiki page file:

version=pmwiki-2.1.0 urlencoded=1
text=Markup text

"version=" tells PmWiki that the values are urlencoded. The actual value doesn't matter, as long as "urlencoded=1" appears somewhere in the line.

"text=" needs to have the markup text with newlines converted to "%0a" and percent signs converted to "%25".

In addition, PmWiki writes pages with '<' encoded as "%3c" (to help with security), but it doesn't require that <'s be encoded that way in order to be able to read the page. More conversions are possible to be added in the future.

In order to let the (:pagelist :) markup work, make sure the filename begins with an uppercase letter.

In order to have the (:pagelist link= ... :) markup on other pages list this page, a third attribute is required:


"targets=" is a comma delimited list of all links from the current page.

Keys you could see in a raw PmWiki file:

Version of PmWiki used to create the file More??? (ordered, urlencoded)
Author's browser when saving the page
Last author to save page
The character encoding of the page text, may be used by future upgrades
Change summary
Page creation time
Page description. Used to fill <meta name='description' /> if set via(:description page'sdecription text:)
Host created this page
Name of the page (e.g., Main.WikiSandbox)
encrypted version of the password required to change attributes
encrypted version of the password required to edit
encrypted version of the password required to read
encrypted version of the password required to upload
Number of times the page has been edited
Targets for links in the page
The page's wiki markup
Time the page was last saved (seconds since 1 Jan 1970 00:00 UTC)
Page title set via (:title The Page Title:).
Character used for newlines (deprecated)
The version to which PmWiki has been updated to by upgrades.php (only on SiteAdmin.Status)

Below these you will see information used to keep track of the page's revision history.

Creating a Page for Distribution

A simple way to create a wikipage file to use for distribution (for example with a recipe or a skin) is to create the page with PmWiki and then use a text editor to delete all lines but version, text, and ctime. Example:

version=pmwiki-2.1.0 ordered=1 urlencoded=1
text=This is a line.%0aThis is another.

Keeping track of page history

Inside of a page file, PmWiki stores the latest version of the markup text, and uses this to render the page. The page history is kept as a sequence of differences between the latest version of the page and each previous version.

PmWiki normally puts the page history at the end of each page file in reverse chronological sequence, and sets the "ordered=1" items in the header. If an operation needs only the most recent version of a page, then PmWiki will stop reading and processing a page file at the point where the history begins, potentially saving a lot of time and memory. If the "ordered=1" flag isn't present, PmWiki makes no assumptions about the ordering of items in the pagefile and processes the entire file.

Load pages from text files

See Cookbook: Import text. Import text files as PmWiki pages

Unix utility to extract wiki text

The following unix script (tested on MacOSX?) will extract and decode the current text from a wiki file:

# wtext - extract wiki text
# wtext wikifile > output

set fn = "$1"
if ("$fn" == "") then
  echo "need input file parameter"
  exit 999
if (! -f $fn) then
  echo "$fn does not exist"
  exit 999
rm sedin.$$ >& /dev/null
set ch = `grep ^newline= $fn | cut -d= -f2`
if ("$ch" == "") set ch = "%0a"
cat <<eof > sedin.$$
grep "^text=" "$1" | sed -f sedin.$$
rm sedin.$$ >& /dev/null

See also

Categories: PmWiki Developer


This page contains Patrick Michaud's comments regarding the "audiences" for which PmWiki was designed. As such, many people are reluctant to modify the page, because it is a statement of his opinions and describes some of the thought that went into creating PmWiki. (And we all thank him for that!)

Patrick's comments

I think of PmWiki in terms of two audiences:

  • Authors are the people who generate web content using PmWiki, and
  • wiki administrators are the folks who install, configure, and maintain a PmWiki installation on a web server.

In some senses it could be claimed that as the primary developer of PmWiki I should only have wiki administrators as my target audience, and that authors are the target audience for the administrators. But what really makes PmWiki useful to wiki administrators is that I've put a lot of consideration into creating a tool that is usable by authors, so I have to keep the needs of both audiences in mind as I'm designing and adding new features to PmWiki.

Within the authoring audience I see that there are "naive authors" and "experienced authors".

"Naive authors" are the folks who use wiki to generate content but may know next-to-nothing about HTML, much less style sheets or PHP or the like. Naive authors are easily discouraged from generating web content if they have to wade through markup text that has lots of funny and cryptic symbols in them. So, if we want a site with lots of contributors, we have to be very careful not to do things that will cause this group to exclude themselves from participating.

"Experienced authors" are the folks who know a lot about HTML and could write their content as HTML, but have chosen to use wiki because of its other useful features (ease of linking, collaboration, ease of updates, revision histories, etc.) or because they want to collaborate with naive authors. Experienced authors usually don't have any problem with documents with lots of ugly markup in them; after all, they already know HTML. Experienced authors are sometimes frustrated with wiki because it doesn't have markup that would let them do something they know they can do in HTML (e.g., tables, stylesheets, colored text, etc.). And, they sometimes have difficulty understanding why naive authors would turn away from documents that have lots of markup sequences in them.

For the wiki administrator audience--the folks who install and may want to customize PmWiki--their backgrounds and goals are often quite diverse. PmWiki is designed so that it can be installed and be useful with minimal HTML/PHP knowledge, but it doesn't restrict people who know HTML/PHP from doing some fairly complex things. For one, PmWiki allows a site administrator to build-in markup sequences and features customized to his/her needs (and the needs of his/her audiences).

The separate needs of these audiences are behind most of the PmWikiPhilosophies. The people who develop PmWiki software must continually keep naive authors in mind as new features are requested and proposed by expert authors and Wiki Administrators. Sometimes it may seem to these latter groups that it's okay to implement the complex features because "naive authors don't have to use them", but the truth is that if complex/ugly markup sequences are available then they will eventually be used by someone, and once used they become a barrier to the naive authors. So, if I see that a feature could become a barrier to a naive author I don't include it in the base implementation of PmWiki, but instead find ways to let Wiki Administrators include it as a local customization.


Here's a list of contributors to PmWiki development and improvement. My apologies if I've forgotten anyone -- feel free to add your name if you've been left out, feel free to remove your name if you don't want to be associated with these people. :-)

  • GNUZoo? - Several recipes, some security and bug fixes
  • Scott Duff - pmwe, simple-journal.php, all-around Pm sanity checker
  • Ross Kowalski - uploads and printable page research
  • John Rankin - WikiTrails, Links, EditQuickReference, notify.php, documentation, debugging
  • Joachim Durchholz - hacking documentation, general pest
  • Jessica Tishmack - uploads, testing
  • Jean-Claude Gorichon - voting
  • Janice Heinold - early PmWiki testing and suggestions, documentation
  • James Davis - WikiStyles markup, testing
  • Isabelle Michaud - floating images markup, Wiki Groups, uploads/attachments
  • Glenn Blalock - WikiStyles suggestions, testing, documentation
  • Dawn Green - WikiStyles suggestions, uploads, documentation
  • Christian Ridderström - pmwiki-mode for Emacs and some other hacks/modifications.
  • Carlo Strozzi - Internationalization, PmWiki on Boa, HTML redirection
  • Michael Weiner - Modifications to the ToDo?, RssFeedDisplay?, MyPmWiki?, and CommentBox? recipes
  • Criss Ittermann (aka Crisses/XES) - ye old best seller Blocklist2 that topped the charts for a while and many other recipes
  • Rev. Dr. Ian MacGregor? - I've contributed with monetary donations, skins, bug reports and continued testing. My home page is powered by PmWiki.
  • Petko Yotov - I have been the PmWiki core developer and webmaster since January 2009 (after having worked with it since 2004). My contributions are at the Change log page, in the PITS issue tracking system and in the mailing lists. My cookbook recipes can be found at my profile page.


There are several mailing lists available for PmWiki.

[ pmwiki-users ]
This is a great resource where a very helpful group of people will answer questions and discuss PmWiki development. Traffic is around 20-40 messages a day (on slow days).
If you ask a question on the list and it doesn't get answered, don't feel let down. Just ask it again. It probably slipped by unnoticed.
[ pmwiki-users-de ]
A mailing list for german-speaking users of PmWiki. Archived at
[ pmwiki-users-es ]
Lista de usuarios PmWiki en Español.
[ pmwiki-users-fr ]
A mailing list for french-speaking users of PmWiki.
[ pmwiki-devel ]
This list was created to lower the traffic on pmwiki-users, it focuses on discussions surrounding code development for PmWiki (both core and recipe development).
[ pmwiki-announce ]
Announcements of new version releases and urgent information. If you use PmWiki in a production environment, this low-volume list is highly recommended. The archive is at:


  • If you reply to a digest message, please remove the messages irrelevant to your reply before sending it back to the list.
    • It's also helpful (but less important) to change "Re: pmwiki-users Digest, Vol [...]" to "Re: [the original subject]" because some mail programs determine threads based on the subject.
  • If you address a reply to a single list member, please take the [pmwiki-users] off the subject line, or it's possible for your message to get lost in the mailing list traffic. Many people filter list traffic to a separate mailbox.

Changing mail list settings

Here are some tips regarding changing the mailing list settings:

  • Logging in...
    • First go to and enter your e-mail address in the field at the bottom of the page, to the left of the button Unsubscribe or edit options.
    • Next you need to enter your password. As you've probably forgotten this, use the button Remind at the bottom of the page to get a new password.
    • Finally enter the password you should get momentarily via e-mail.
  • You can directly go to the options web page through a URI such as the following:<user>%40<domain>
where <user> is everything before the @ in an e-mail address, and <domain> is everything after ( For those who wonder, the %40 in the URI just stands for '@'.
  • You can also obtain various help by sending an email to pmwiki-users-request [snail] pmichaud [period] com with the text help in either the subject or the body.

Newsgroups (NNTP)

You may be interested, that the lists are also accessible as newsgroups.

The NNTP server is:

  • [9]

The groups are:



This page describes some of the ideas that guide the design and implementation of PmWiki. Patrick Michaud doesn't claim that anything listed below is an original idea; these are just what drive the development of PmWiki. You're welcome to express your disagreement with anything listed below. PmWiki.Audiences also describes much of the reasoning behind the ideas given below.

1. Favor writers over readers
At its heart, PmWiki is a collaborative authoring system for hyperlinked documents. It's hard enough to get people (including Pm) to contribute written material; making authors deal with HTML markup and linking issues places more obstacles to active contribution. So, PmWiki aims to make it easier to author documents, even if doing so limits the types of documents being authored.

2. Don't try to replace HTML
PmWiki doesn't make any attempt to do everything that can be done in HTML. There are good reasons that people don't use web browsers to edit HTML--it's just not very effective. If you need to be writing lots of funky HTML in a web page, then PmWiki is not what you should be using to create it. What PmWiki does try to do is make it easy to link PmWiki to other "non-wiki" web documents, to embed PmWiki pages inside of complex web pages, and to allow other web documents to easily link to PmWiki.
This principle also follows from the "favor writers over readers" principle above--every new feature added to PmWiki requires some sort of additional markup to support it. Pretty soon the source document looks pretty ugly and we'd all be better off just writing HTML.
Another reason for avoiding arbitrary HTML is that ill-formed HTML can cause pages to stop displaying completely, and arbitrary HTML can be a security risk--more so when pages can be created anonymously. See for more information.

3. Avoid gratuitous features (or "creeping featurism")
In general PmWiki features are implemented in response to specific needs, rather than because someone identifies something that "might be useful". In any sort of useful system, it's hard to change a poorly designed feature once people have built a lot of structure based on it. (Need an example? Look at MS-DOS or Windows.) One way to avoid poor design is to resist the temptation to implement something until you have a clearer idea of how it will be used.

4. Support collaborative maintenance of public web pages
Although this wasn't at all the original intent of PmWiki, it became quickly obvious that WikiWikiWeb principles could be used to make it easier for groups to collaboratively design and maintain a public web site presence. PmWiki allows individual pages to be password protected, and a couple of local customizations makes it easy to protect large sections of PmWiki pages. Furthermore, in many ways PmWiki provides "style sheets on steroids": you can quickly change the headers, footers, and other elements on a large group of pages without ever having to touch the individual page contents. Finally, it's relatively easy to add custom markup for specialized applications.

5. Be easy to install, configure, and maintain
With a gzip-compressed file size of just around 400K, uploading PmWiki to your server is a speedy operation. Do a chmod or two, update a few settings in config.php and you should be up and running. PmWiki stores all data in flat files, so there is no need for MySQL or other utilities. Upgrading is usually a simple matter of copying the latest version's files over the files of your existing PmWiki installation. (One of the biggest reasons for the creation of PmWiki was that other wiki engines at the time required modifications to the distribution files, so admins ended up losing their customizations on every upgrade.)


Here are some of the features and notes about PmWiki's design decisions. Many of these derive directly from the PmWiki Philosophy and lots of discussion on the mailing lists.

Why doesn't PmWiki use hierarchical / nested groups?

It essentially comes down to figuring out how to handle page links between nested groups; if someone can figure out an obvious, intuitive way for authors to do that, then nested groups become plausible. See Design Notes and PmWiki:Hierarchical Groups.

Why don't PmWiki's scripts have a closing ?> tag?

All of PmWiki's scripts now omit the closing ?> tag. The tag is not required, and it avoids problems with unnoticed spaces or blank lines at the end of the file. Also, some file transfer protocols may change the newline character(s) in the file, which can also cause problems. See also the Instruction separation page in the PHP manual.

Does PmWiki support WYSIWYG editing (or something like the FCKEditor?)?

Short answer: PmWiki provides GUI buttons in a toolbar for common markups, but otherwise does not have WYSIWYG editing. For the reasons why, see PmWiki:WYSIWYG.

Categories: PmWiki Developer

Release Notes

See also: Change log and Road map.

Version 2.2.44 (2012-10-21)

This version improves the display of consecutive whitespaces in page histories, and fixes the definition of PageTextVariables containing a dash. The documentation was updated.

Version 2.2.43 (2012-09-20)

This version makes it possible to use HTML attribute names to contain dashes, and removes a warning when editing and previewing Site.EditForm. The documentation was updated.

Version 2.2.42 (2012-08-20)

This version provides a workaround for cases when a wiki page contains a character nonexistent in the active encoding. The documentation was updated.

Version 2.2.41 (2012-08-12)

This version changes the internal $KeepToken separator to be compatible with more encodings. The documentation was updated.

Version 2.2.40 (2012-07-21)

This version provides a helper function replacing htmlspecialchars() and compatible with PHP 5.4. The documentation was updated.

Version 2.2.39 (2012-06-25)

This version provides a fix for links to attachments containing international characters. The documentation was updated.

Version 2.2.38 (2012-05-21)

This version fixes a "parameter count" warning which appeared on some websites.

Version 2.2.37 (2012-05-01)

This version provides a workaround for installations with broken iconv() function, while optimizing the recode function. This should fix the "Unable to retrieve edit form" problem in some wikis. Dots in sections are now better supported, PageVariables are expanded in PageList? template defaults, and the documentation is updated.

Version 2.2.36 (2011-12-28)

This version fixes the recode function to try to recover Windows-1252 characters in ISO-8859-1 files. A new variable $EnableOldCharset enables the $page["=oldcharset"] entry which will be used in the future. A couple of minor bugs were fixed and the documentation was updated.

Version 2.2.35 (2011-11-11)

This release fixes a critical PHP injection vulnerability, reported today by Egidio Romano. PmWiki versions 2.2.X, 2.1.X, 2.0.X and 2.0.beta33 and newer are vulnerable. When you upgrade, please read carefully the Release notes for all PmWiki versions since yours.

If you cannot upgrade, it is recommended to disable Searches at the earliest opportunity (even if your wiki skin doesn't have a search form). Add to config.php such a line:

  if ($action == 'search') $action = 'browse';

If your old version wiki allows editing by not entirely trusted visitors, even on limited pages like a WikiSandbox, you should also disable PageLists. Add to config.php this line:

  $EnablePageList = 0;

This version has an important change for international wikis: the XLPage?() function no longer loads encoding scripts such as xlpage-utf-8.php. When you upgrade, you need to include those scripts from config.php, before calling XLPage?():

  include_once("scripts/xlpage-utf-8.php"); # if your wiki uses UTF-8

All links can now have tooltip titles. Previously, only images and external links could have tooltip titles, now this feature is enabled for internal links. To set a tooltip title, add it in quotes after the link address:

  [[Main.HomePage"This is a tooltip title"]]
  [[Main.HomePage"This is a tooltip title"|Home]]
  [["Home of PmWiki"]]
  Attach:image.jpg"Tooltip title of the image"

The following new upload extensions were added: svg, xcf, ogg, flac, ogv, mp4, webm, odg, epub. A couple of minor optimizations were added (MarkupExpressions and rendering of page history) and the documentation was updated.

Version 2.2.34 (2011-10-10)

This version resets the timestamps of the default pages Site(Admin).AuthUser which are expected in case of upgrades from the versions 2.1.*. Core MarkupExpressions which manipulate strings should now work better with international characters. The documentation was updated to its latest state from

Version 2.2.33 (2011-09-23)

This version fixes a security bug introduced in 2.2.32 which left the groups Site and SiteAdmin? open for reading and editing because the pages Site.GroupAttributes and SiteAdmin.GroupAttributes didn't have all necessary attributes.

All wikis running 2.2.32 should upgrade. If you cannot immediately upgrade, you can set the attributes from your wiki:

  • open the attributes page [[SiteAdmin.GroupAttributes?action=attr]] and set a "read" and an "edit" password, @lock is recommended.
  • open the attributes page [[Site.GroupAttributes?action=attr]] and set an "edit" password, @lock is recommended. Do not set a "read" password here.

The release also fixes the refcount.php script to produce valid HTML, and updates intermap.txt entries PITS: and Wikipedia: to point to their current locations.

Version 2.2.32 (2011-09-18)

This is the first version shipping with the core documentation in the UTF-8 encoding. PmWiki will automatically convert it on the fly for wikis using an older encoding.

It is recommended that all new PmWiki installations enable UTF-8. Migration of existing wikis from an older encoding to UTF-8 shouldn't be rushed: it is not trivial and will be documented in the future.

A required HTML xmlns attribute was added to the print skin template. The history rendering is now faster when many lines are added or removed.

Note: Due to a manipulation error, a version 2.2.31 was created before it was ready for a release.

Version 2.2.30 (2011-08-13)

This version fixes a $Charset definition in international iso-8859-*.php files. This will help for a future transition to UTF-8.

A variable $EnableRangeMatchUTF8 was added, set it to 1 to enable range matches of pagenames in UTF-8 like [A-D]. Previously the range matches were always enabled in UTF-8, but we found out that on some installations this feature breaks all pagelists, even those without range matches. In case the feature worked for you, you can re-enable it.

Version 2.2.29 (2011-07-24)

This release fixes Attach links that were broken with the Path fix in 2.2.28 earlier today.

Version 2.2.28 (2011-07-24)

This release fixes 2 potential XSS vulnerabilities and a bug with Path: links.

Version 2.2.27 (2011-06-19)

This release fixes a validation bug on pages after a redirection. A new block WikiStyle %justify% was added, allowing left and right aligned text. The page history now accepts a URL parameter ?nodiff=1 which hides the rendering of edit differences, showing only timestamps, authors, summaries and "Restore" links; it allows to restore a vandalized page with a huge contents or history which otherwise would break the memory or time limits of the server.

Version 2.2.26 (2011-05-21)

This release fixes a redundant removal of link hashes from WikiTrails, and updates the documentation to the most recent version from

Version 2.2.25 (2011-03-22)

This release only updates the documentation to the latest state on

Version 2.2.24 (2011-02-15)

This version reverts the way existing PageVariables are processed, like version 2.2.21 or earlier, but it adds a special variable $authpage which can be used in PageVar? definitions. It is the same as the $page array, but exists only if the visitor has read permissions. For example, an administrator can set to config.php:

  $FmtPV['$LastModifiedSummary'] = '@$authpage["csum"]'; # instead of '@$page["csum"]'

Then, the edit summary metadata will only be available if the user has read permissions.

Version 2.2.23 (2011-01-25)

This version sets the default value of $EnablePageVarAuth to 0 until we investigate a reported problem with authentication.

Version 2.2.22 (2011-01-16)

This version adds the variable $EnableXLPageScriptLoad which, if set to 0, will prevent authors to load scripts from XLPage? and to accidentally change the encoding of the wiki. If you use it, make sure you include the required files, eg. xlpage-utf-8.php from local config files.

PageVariables should now respect authentications: without read permissions, the title, description, change summary, author of a protected page are unavailable. PageVariables that are computed without reading the page are still available (eg. $Group, $Namespaced, $Version etc.). Administrators can revert the previous behavior by adding to config.php such a line:

$EnablePageVarAuth = 0;

Version 2.2.21 (2010-12-14)

Due to a mis-configuration of a local svn repository, some of the changes intended for 2.2.20 didn't make it in the correct branch. This release corrects this.

Version 2.2.20 (2010-12-14)

This version fixes a potential XSS vulnerability, reported today. An AuthUser bug with excluding users from authgroups was fixed. A new InterMap prefix PmL10n?: was added, it leads to the Localization section on and should help the work of translators. A couple of other minor bugs were fixed and the documentation was updated.

Version 2.2.19 (2010-11-10)

This is a documentation-update release.

Version 2.2.18 (2010-09-04)

This version fixes 3 minor bugs, and updates the documentation.

Version 2.2.17 (2010-06-20)

This version adds a variable $PostConfig containing functions and scripts to be loaded after stdconfig.php. Tabindex was added as a valid form field attribute. Protected downloads now respect existing browser caches. AuthUser now allows more flexible cookbook recipe integration. A couple of bugs were fixed and the documentation was updated.

Version 2.2.16 (2010-05-10)

This version fixes a bug with parsing html attributes which could allow XSS injection. Wikis allowing unprotected editing are encouraged to upgrade.

A bug with the "center" button of the GUI edit toolbar was corrected.

The "exists" conditional now accepts wildcards, for example:

  (:if exists Main.*:)There are pages in the Main group (:if:)

The documentation was updated.

Version 2.2.15 (2010-03-27)

This version adds some minor bugfixes and optimizations notably a bug with (:template none:) introduced in the last version 2.2.14.

Version 2.2.14 (2010-02-27)

This release corrects inline styles for WikiTrail links. Undefined include/template {$$variables} are now removed from the included section, like Page(Text)Variables, and can be used in conditional expressions. If needed, this change can be reverted by adding to config.php such a line:

  $EnableUndefinedTemplateVars = 1; # keep and display unset {$$variables}

PageList? templates now accept the sections !first and !last for markup to appear for every page in list except the first or last one.

"Title" attributes were added to external links. You can have tooltip titles on external links, including InterMap and attachments, by adding the link title in double quotes after the URL:

  [["Home of PmWiki"| External link]]

For international wikis, PmWiki now automatically translates the titles of technical pages like GroupAttributes or RecentChanges -- just define these strings as usual in XLPage?, for example, in French:

  'AllRecentChanges?' => 'Tous les changements récents',

Some minor optimizations were done and the documentation was updated.

Version 2.2.13 (2010-02-21)

This release fixes a bug with $DiffKeepNum introduced in 2.2.10 -- the count of revisions was incorrect and a page could drop more revisions than it should.

The page history layout was modified with a rough consensus in the community. The history now defaults to "source" view with word-level highlighting of the differences. Authors can see the changes in rendered output by clicking on the link "Show changes to output". Admins can switch back the default by adding such a line to config.php:

  $DiffShow['source'] = (@$_REQUEST['source']=='y')?'y':'n';

To disable word-level highlighting and show plain text changes:

  $EnableDiffInline = 0;

In the page history rendering, a few minor bugs were fixed and the code was slightly optimized.

The documentation was updated.

Version 2.2.12 (2010-02-17)

This release adds simple word-level highlighting of differences in the page history, when "Show changes to markup" is selected. To enable the feature, add to config.php such a line:

  $EnableDiffInline = 1;

This feature is like what the InlineDiff? recipe provides, but not exactly the same, and the implementation is simpler. It is enabled on and can be improved -- your comments are welcome.

Version 2.2.11 (2010-02-14)

This release adds two new table directives for header cells, (:head:) and (:headnr:). They work the same way as (:cell:) and (:cellnr:) except that create <th> instead of <td> html tags.

The pagerev.php script was refactored into separate functions to allow easier integration of recipes displaying the page history.

A couple of minor bugs were fixed and the documentation was updated.

Version 2.2.9, 2.2.10 (2010-01-17)

Most important in this release is the official change of $EnableRelativePageVars to 1. The change is about how {$Variable} in included pages is understood by PmWiki.

  • When $EnableRelativePageVars is set to 0, {$Name} displays the name of the currently browsed page. Even if {$Name} is in an included page, it will display the name of the browsed page.
  • When $EnableRelativePageVars is set to 1, {$Name} displays the name of the physical page where it written. If {$Name} is in an included page, it will display the name of the included page.
  • {*$Name} always displays the name of the currently browsed page, regardless of $EnableRelativePageVars.

So, if your wiki relies on page variables from included pages, and doesn't have $EnableRelativePageVars set to 1, after upgrading to 2.2.9, you can revert to the previous behavior by adding to config.php such a line:

  $EnableRelativePageVars = 0;

More information about page variables can be found at:

This release adds a new variable $EnablePageTitlePriority which defines how to treat multiple (:title..:) directives. If set to 1, the first title directive will be used, and if a page defines a title, directives from included pages cannot override it. PmWiki default is 0, for years, the last title directive was used (it could come from an included page or GroupFooter).

This release also adds a new variable $DiffKeepNum, specifying the minimum number (default 20) of edits that will be kept even if some of them are older than the limit of $DiffKeepDays.

A number of bugs were fixed and the documentation was updated.

Version 2.2.8 (2009-12-07)

This release fixes another PHP 5.3 compatibility issue with conditional markup. The Author field now handles apostrophes correctly. The documentation was updated.

Version 2.2.7 (2009-11-08)

This release fixes most PHP 5.3 compatibility issues. Unfortunately some specific builds for Windows may still have problems, which are unrelated to PmWiki. Notably, on Windows, all passwords need to be 4 characters or longer.

Upload names with spaces are now correctly quoted. The documentation was updated.

Version 2.2.6 (2009-10-04)

With this release it is now possible to display recently uploaded files to the RecentChanges pages -- if you hav