RecipeForHackers

this is a draft - not to be used before completion, thanks

Attention please

  • This page lists some tweaking of php code. You must know that in doing this you can break or even wipe important files, or open up your system to sneaky attacks. Do what you want, but it is always at your own risk. Many wikis are full open to writers (like PmWiki.org), be specially cautious in this case. But PmWiki is very uefull as a site genrator with only very few -may be one only- writers. Some recipes should be restricted to such sites.
  • Of course, nobody expects things here to be intentionally odd, but this page is enabled for editing, anybody can write on it, even stupid or malicious things. So beware...
  • Some lines of code given here are very long. There are no carriage return because this is frequently not allowed in the code and we want you to be able to copy/paste. Update: this has been (partly) corrected.
  • See also: Cookbook:RegularExpressions and PmWiki.CustomMarkup.

For what purpose?

One can often think of a recipe as difficult, particularly when you are not an experienced programmer.

In reality what you want to do is often very easy, especially if all you need is to tweak some existing recipe. PHP is a very simple language and its documentation is very good and available online.

Don't forget PHP is only a "html generator". Its only purpose is to create a page in correct HTML. You can see the result of your work in an html page and the details by looking at the source of the page (View/Page Source on Mozilla). Be warned that inclusion of erroneous code in a php script can simply make your wiki return an empty page. Or, in the case of the example flashplayer discussed here, nothing shows up where it is supposed to appear. The html must be absolutely correct because a flashplayer is specially critical to setup.

However, when no player appears, no damage is done. Simply disable your script (write a # at the beginning of a line that includes it to make the line a comment) and your wiki is back to normal.

I will show this step by step here, in an example.

Search for a template

Suppose you want to create a flash-player recipe for PmWiki. The ones listed in the already extensive Cookbook (search for "flash" with the PmWiki search facility) don't meet your needs.

Search for an existing recipe, the closer it is to what you need, the better. Download the php script and look at the code. Here we use the script from Cookbook:Flash, named youtube.php.

Identify the directive and function names.

Open the "youtube.php" script. Skip the copyright notice, that is, don't modify it now. Of course, if the copy/modification is not allowed, stop using this (such recipes are rare).

Immediately after the copyright notice, you'll see:

  $RecipeInfo['YouTube']['Version'] = '2007-02-06';

Even a non programmer can understand this only a flag for some automated recipe listing in PmWiki, you can modify it or simply remove it if you are not going to publish your work. But of course you want to, so change it:

  $RecipeInfo['flowplayerPl']['Version'] = '2007-06-19';

Notice that, for the first time here, we see the name of the new recipe: flowplayer, because it's the name of the player involved, and Pl for "Playlist".

The following line is more complicated:

  Markup('youtube', '<img', "/\\(:youtube (.*?)\\s*:\\)/e", "ShowYouTube('$1')");

From now on, you must be very cautious not to modify anything unintentionally. All these /\\^ are not readily understandable but certainly have an important purpose. Don't disturb them...

However, the "youtube" word is self explaining. Replace it by your recipe name, flowplayerPl.

You end up with:

  Markup('flowplayerPl', '<img', "/\\(:flowplayerPl (.*?)\\s*:\\)/e", "ShowFlowPlayerPl('$1')");

Notice than "ShowYouTube" is a function, given the ('$1') argument. This $1 is the first thing the function returns. So as not to conflict with any previous or core function, use a name that doesn't already exist (here ShowFlowPlayerPl), and change this as needed.

The cookbook notice says that the syntax to use the script is:

  (:youtube tiljk_MemVM:)

For your recipe, the directive argument must be the playlist syntax of flowplayer, not the youtube code. So keep the recipe simple, you should use only one directive argument:

  (:flowplayerPl list:)

The master piece

Now on to the function needed... the current one outputs some formatted html based on the url ($url) passed to it:

  function ShowYouTube($url) {

    ## object tag
    $out = "\n<object type='application/x-shockwave-flash' style='width:425px; height:350px;' ";
    $out .= "data='http://www.youtube.com/v/$url'>";
    ## transparent so you can see through it if necessary
    $out .= "\n...

There are three things to pay attention to here. Note there are lines with "$out = " and "$out .=". That second line adds data to that from the first line. Such lines are only needed when the data doesn't fit on a single line, and when carriage returns are not allowed in the data. In html they normally are allowed!

Second, the quotes... any data must be packed within enclosing quotes, either 'my data' or "my data". If you don't know the difference between them, I suggest you use single quotes ('). Double-quotes allow you to insert variables into the string, and the effect may confuse you. When you need to include single quotes in the data itself, you must use \' (backslash followed by single quote) instead. If you wish to use double quotes to pack your data and wish to use double quotes within them, use \" instead.

Third, the above code uses double quotes. Somewhere in the data you see $url inserted. That's where the text that comes from the markup in the page goes. We'll use single quotes to pack the data, so that won't work. Instead we temporarily close the data by inserting this instead:

  '.$url.'

This does exactly the same thing for data packed in single quotes as $url in double quotes. In the same manner you can break up the data over two or more lines, just use this to temporarily suspend the data:

  '.   # end the data in double quotes and then continue on the next line:
  '

Now look at the result for flowplayer. We use both techniques above to interrupt the packing of the html data. The full function becomes:

  function ShowFlowPlayerPl($list) {

    ## object tag
    $out = '
  <object type="application/x-shockwave-flash" data="http://pathtoflowplayer/ FlowPlayer.swf" '.
    'width="400" height="330" id="FlowPlayer">
      <param name="allowScriptAccess" value="always" />
      <param name="movie" value="FlowPlayer.swf" />
      <param name="quality" value="high" />
      <param name="scaleMode" value="showAll" />
      <param name="allowfullscreen" value="true" />
      <param name="wmode" value="transparent" />
      <param name="allowNetworking" value="all" />
      <param name="flashvars" value="config=       {
            autoPlay: true,
            loop: false,
            initialScale: 'scale',
            showLoopButton: true,
            showPlayListButtons: true,
            playList:     [
                            '.$list.'
                          ]
                                          }" />
  </object>';

      return Keep($out);
  }

This of course is based on the html required by flowplayer, not the Youtube syntax, as indicated on the flowplayer website. The parameter name that was $url has also been changed to $list.

Double check

The next step is the most difficult. Even the slightest error can give a completely non-displaying wiki... so proofread your work, try the syntax first on a static html page and verify that it works.

Include the script into the PmWiki config.php

Of course, as in any recipe, you have to include your php script in PmWiki. Until you have done this, nothing (good or bad) can happen :-).

  include_once('cookbook/flowplayerPl.php');

Should work, but only if you placed your script in the cookbook folder of your wiki.

Using the script in on a page

This isn't the simplest part, because the (: :) PmWiki directive syntax doesn't allow carriage returns inside, while the suggested flowplayer syntax does use them. But in html, a space and a carriage return are identical, so we can use:

  (:flowplayerPl { url: 'http://first.flv' }, { url: 'http://second.flv' }, { url: 'http://third.flv' },:)

Here also, you need to double check... but it works (see the note below).

Note to the author of this script (and many others)

Like many scripts in the cookbook, the one you selected passes data from the page directly to the output (the generated html). This poses a security risk, though PmWiki minimizes it as much as it can by encoding html entities. Nevertheless, you'll normally want to restrict the data passed to a script (from within the (:directive data:) format used). To do that you must typically modify the regular expression from the Markup(), for example the one shown above:

  Markup('youtube', '<img', "/\\(:youtube (.*?)\\s*:\\)/e", "ShowYouTube('$1')");

Locate the (.*?) part, this says to match (:youtube, then *anything* and then :). It also passes the *anything* part to the ShowYouTube() function using $1. That function in turn passes it on, directly, to the generated html. That is the part that should change. And that's where beginners typically need to consult the PmWiki mailing list, in this case providing the allowed parameters for flowplayer.

By writing a more restrictive version of the regular expression, the result will not only be safer, but users of the wiki will also experience a benefit: directives that are not valid or illogical will (typically) not be recognized at all.

Regular expressions are a very useful tool which PmWiki exploits throughout. To gain a better understanding, go to Cookbook:RegularExpressions.

Don't forget your directive can be inserted in any of your wiki pages, making one page read only is not a protection.