DokuWiki

It's better when it's simple

User Tools

Site Tools


plugin:incl_form

Include PHP Forms Plugin

Compatible with DokuWiki

No compatibility info given!

plugin Safely include PHP forms

Last updated on
2006-09-02
Provides
Syntax

Plugin name contains underscore, will not generate popularity points.

Similar to phpinc

Tagged with form, include

When the http://www.puzzlers.org first moved to a wiki a few years ago, one of the challenges was to move existing interactive portions to the Wiki. I hacked the source and soon had it working well with some limitations such as a form always was placed at the top of the page, before any wiki text.

Then I tried to upgrade and found myself doing the most of the hack over again. On the second pass to upgrade, I noticed “plugins” and converted most of my previous forms into plugins. Each form became a new plugin. Time consuming work, but I was seeing the end of the tunnel.

Recently, I realized how I should have done it … I simply re-implemented the original hack as a plugin. After 30 minutes to add some tags to the pages that remained as older forms, I was able to remove the hack from the actual template, and now its a simple life on our site. The next upgrade (to 2006-03-09) took only a few hours and basically required building a new template page (and a couple small hacks of our own – some notes are at http://www.puzzlers.org/dokuwiki/doku.php?id=webmaster:history).

The advantage of this implementation is that PHP is easily added without opening PHP to all the users. Because forms can be referenced by page id, they can be used in numerous places while still requiring just one PHP file.

Now, to simplify my site, I am actually reverting some of my plugins back to their previous form files. I don't see a need to import 15 plugins for ALL pages when I can simply import one plugin and selectively import just the forms I need.

Usage

Like pageindex, this plugin supports two possible tags:

  • ~~INCL_FORM=section:namespace~~ ← Form at the specified page id.
  • ~~INCL_FORM~~ ← Form at the current page id.

Installation

  • Include the code below in lib/plugins/incl_form/syntax.php.
  • Create a folder such as data/forms. This will be your repository authorized forms.
  • Set the $conf['formdir'] in conf/local.php to something like data/forms – the same form you created above. :!: in the code listing this parameter is called 'formdir'!
  • Place your forms into namspaces within your forms directory (above). “namespaces” here is just like in the rest of the wiki. The Page [[plugin:pageindex]] is stored as data/pages/plugin/pageindex.txt. A form for the same namespace is stored as data/forms/plugin/pageindex.php
  • start adding ~~INCL_FORM~~ tags to your pages.
    • If you're on the page [[plugin:pageindex]], then you can refer to the sample above as ~~INCL_FORM~~ and it will use the relevant form.
    • From any other page, you can refer to the sample above as ~~INCL_FORM=plugin:pageindex~~



Sept 13/2006 – revised to expand the steps a bit more kite [at] puzzlers [dot] org

The Code

<?php
/**
 * Plugin Include Form: include external, approved PHP forms
 * Updated April 19, 2007 by Kite <Kite@puzzlers.org>
 *
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     Kite <Kite@puzzlers.org>
 * @based_on   "externallink" plugin by Otto Vainio <plugins@valjakko.net>
 */
 
if(!defined('DOKU_INC')) {
	define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
}
if(!defined('DOKU_PLUGIN')) {
	define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
}
 
require_once(DOKU_PLUGIN.'syntax.php');
 
 
function eval_form_php($arr) {
	global $INFO;
 
	if(0 and ($INFO['perm'] == AUTH_ADMIN)) { // Use debug output??
		ob_start();
		$content = "<!-- Content: "; 
		print_r( $arr ); 
		$content .= ob_get_contents();
		ob_end_clean();
		$content .= " -->\n";
		echo $content;  // can't return this
	}
	ob_start();
	if($INFO['perm'] == AUTH_ADMIN) {
		eval("?>$arr");              //  not $arr -- now allows parsing all PHP blocks 
	} else {
		@eval("?>$arr");             //  not $arr -- now allows parsing all PHP blocks 
	}	
	$content = ob_get_contents();
	ob_end_clean();
	return $content;
}
 
/**
 * All DokuWiki plugins to extend the parser/rendering mechanism
 * need to inherit from this class
 */
class syntax_plugin_incl_form extends DokuWiki_Syntax_Plugin {
 
    /**
     * return some info
     */
    function getInfo(){
        return array(
            'author' => 'Kite',
            'email'  => 'kite@puzzlers.org',
            'date'   => '2006-09-02',
            'name'   => 'Include Form',
            'desc'   => 'Includes an approved form into a page.',
            'url'    => 'http://www.dokuwiki.org/wiki:plugins:incl_form',
        );
    }
 
    /**
     * What kind of syntax are we?
     */
    function getType(){
        return 'substition';
    }
 
    // Just before build in links
    function getSort(){ return 299; }
 
    /**
     * What about paragraphs?
     */
    function getPType(){
        return 'block';
    }
 
    function connectTo($mode) {
       $this->Lexer->addSpecialPattern('~~INCL_FORM[^~]*~~',$mode,'plugin_incl_form');
    }
 
 
    /**
     * Handle the match
     */
    function handle($match, $state, $pos, &$handler){
	    // Remove the tag itself, and then separate the form name
	    // from the parameter set.
	    $match = preg_replace("%~~INCL_FORM(=(.*))?~~%u", "\\2", $match);
	    //echo "\n\t<!-- syntax_plugin_INCL_FORM.handle() found >> $match << -->\n";
        return $match;
    }
 
    /**
     * Create output
     */
    function render($mode, &$renderer, $data) {
        if($mode == 'xhtml'){
	    $FORM_PARAMS = split(';', $data);
            $text=$this->_inc_form($FORM_PARAMS[0], $FORM_PARAMS);
            $renderer->doc .= $text;
            return true;
        }
        return false;
    }
 
 
	//Translate an ID into a form file name
	//path is relative to the DokuWiki root (I use data/forms)
	function formFN($id,$rev=''){
	  global $conf;
	  if(empty($rev)){
	    $id = cleanID($id);
	    $id = str_replace(':','/',$id);
	    $fn = $conf['formdir'].'/'.utf8_encodeFN($id).'.php';
	  }
	  return $fn;
	} // formFN()
 
    function _inc_form($form, $PARAMS) {
	global $FORM_PARAMS;
	global $ID;
        if(strlen($form) < 1) {
	    $form = $ID;   // default to the current page ID
	}
        // Break the form parameters (if any) into name/values
	$path =  $this->formFN($form);  
	foreach($PARAMS as $item) {
	    // split the pair
	    $parts = explode('=', $item);
	    // Skip the $ID parameter
	    if(strlen($parts[0])>0) {
		$FORM_PARAMS[$parts[0]] = $parts[1];  
	    }
	}
        if(file_exists(DOKU_INC . $path)) {
	    //echo "<!-- form was found -->\n";
	    $text = io_readFile($path); 
	    $pattern = "/(<\?php)(.*?)(\?>)/is";			
	    $text = eval_form_php($text);
	} else {
	    $text = "\n\t<!-- No form found for '$form'-->\n";
	}
	return $text;
    } // _inc_form()
} // syntax_plugin_INCL_FORM
?>

Sample Form

<h1>Member Email Changes</h1>
<?php
global $conf;

if( !(isset($_GET['sent']) and ($_GET['sent'] = 1))  ) {

echo "<script type='text/javascript'> \n";
echo "	<!-- \n";
echo "	function SetSubject() \n";
echo "	{ \n";
echo "		try \n";
echo "		{ \n";
echo "			nom = document.mailform.elements['NOM'].value; \n";
echo "			document.mailform.subject.value = '[NPL] Web : E-mail Settings : ' + nom; \n";
echo "		} \n";
echo "		catch(exception) \n";
echo "		{ \n";
echo "		} \n";
echo "	} \n";
echo "	--> \n";
echo "</script> \n";
echo "<FORM METHOD='POST' ACTION='/cgi-bin/FormMail.pl' name='FormMail' enctype='application/x-www-form-urlencoded' onSubmit='SetSubject();'> \n";
echo "<TABLE> \n";
echo "  <TR> \n";
echo "  <TD ALIGN=left WIDTH='40%'> \n";
echo "	   <input type='hidden' name='subject'	  value='Web: NPL E-mail Settings' /> \n";
echo "	   <input type='hidden' name='recipient'  value='incl_form@nomail.org' /> \n";
echo "	   <input type='hidden' name='email'      value='www.puzzlers.org' /> \n";
echo "	   <input type='hidden' name='redirect'   value='http://" . $_SERVER['HTTP_HOST'] . $conf['basedir'] . "/email_change_bounce.html' /> \n";
echo "	   <input type='hidden' name='source'     value='http://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] .'&'. $_GET['ID'] . "' /> \n";
echo "	   <input type='hidden' name='required'   value='realname'/> \n";
echo "	   <input type='hidden' name='required'   value='nom'/> \n";
echo "	   <input type='hidden' name='env_report' value='REMOTE_HOST,REMOTE_ADDR,REMOTE_USER,HTTP_USER_AGENT'/> \n";
echo "	   <input type='hidden' name='nom'        value='" . $_SERVER['REMOTE_USER'] . "' /> \n";
echo "     <H4>Your nom</H4> \n";
echo "  </TD> \n";
echo "  <TD WIDTH='10%'>&nbsp;</TD> \n";
echo "  <TD>" . $_SERVER['REMOTE_USER'] . "</TD> \n";
echo "  </TR> \n";
echo "  <TR> \n";
echo "    <TD ALIGN=left WIDTH='40%'> \n";
echo "		<H4>Identity:</H4> \n";
echo "		<p>When not in super-hero garb, who are you, really?</p> \n";
echo "	</TD> \n";
echo "    <TD WIDTH='10%'>&nbsp;</TD> \n";
echo "    <TD> \n";
echo "		<p>My real name:<TT>&nbsp;</TT><br /><input type='TEXT' NAME='realname' SIZE=15 MAXLENGTH=50 width='200px'/></p> \n";
echo "	</TD> \n";
echo "  </TR> \n";
echo "  <TR> \n";
echo "    <TD ALIGN=left WIDTH='40%'> \n";
echo "		<H4>Status:</H4> \n";
echo "		<p>Tell us a bit about why you're here today:</p> \n";
echo "	</TD> \n";
echo "    <TD WIDTH='10%'>&nbsp;</TD> \n";
echo "    <TD> \n";
echo "		<input type='radio' id='status' name='status' value='new-member'>I'm a new member.</INPUT><br /> \n";
echo "		<input type='radio' id='status' name='status' value='recent-member' checked='checked'>I'm a recent member.</INPUT><br /> \n";
echo "		<input type='radio' id='status' name='status' value='Full-fledged'>I'm a Full fledged (almost card carrying) member.</INPUT><br /> \n";
echo "		<input type='radio' id='status' name='status' value='broken'>Something is broken</INPUT><br /> \n";
echo "		Description:<TT>&nbsp;</TT><input type='TEXT'name='status-broken' SIZE=15 MAXLENGTH=50 width='150px'/><br /> \n";
echo "		<input type='radio' id='status' name='status' value='other'>Something else is happening...</INPUT><br /> \n";
echo "		Description:<TT>&nbsp;</TT><input type='TEXT' name='status-other' SIZE=15 MAXLENGTH=50 width='150px'/> \n";
echo "	</TD> \n";
echo "  </TR> \n";
echo "  <TR><TD colspan='3'><hr /></TD></TR> \n";
echo "  <TR> \n";
echo "    <TD ALIGN=left width='40%'> \n";
echo "	<H3>Email Address</H3> \n";
echo "	<P>If you want to get an email forwarding alias or to change your  \n";
echo "	forwarding address,  enter your email address here.</P> \n";
echo "	</TD> \n";
echo "    <TD WIDTH='10%'>&nbsp;</TD> \n";
echo "    <TD> \n";
echo "		<p>Send my mail to:</p> \n";
echo "		<input type='TEXT' NAME='email' SIZE=30 MAXLENGTH=50></TD> \n";
echo "  </TR> \n";
echo "  <TR><TD colspan=4><p> </p><hr><p> </p></td></tr> \n";
echo "  <TR> \n";
echo "    <TD ALIGN=left width='40%'> \n";
echo "		<H3>Mail Lists </H3> \n";
echo "		<p>Would you like to be on npl-folk, npl-announce or neither? <br /> \n";
echo "		<br /> \n";
echo "		(npl-folk subscribers are automatically subscribed to \n";
echo "		npl-announce.)</p> \n";
echo "	</TD> \n";
echo "    <TD WIDTH='10%'>&nbsp;</TD> \n";
echo "    <TD> \n";
echo "	<p>Subscribe me to:</p> \n";
echo "        <input type='radio' NAME='mail-list subscribe' value='announce'>npl-announce \n";
echo "		(only official announcements)</input><br> \n";
echo "		<input type='radio' NAME='mail-list subscribe' value='npl-folk' checked='checked'>npl-folk (all email)</input><br> \n";
echo "        <input type='radio' NAME='mail-list subscribe' value='neither'>Neither</input><br /> \n";
echo "        <input type='radio' NAME='mail-list subscribe' value='no change'>No change</input> \n";
echo "    </TD> \n";
echo "  </TR> \n";
echo "  <TR><TD colspan='3'><hr></td></tr> \n";
echo "  <TR> \n";
echo "    <TD ALIGN=left width='40%'> \n";
echo "		<H3>Other Information</H3> \n";
echo "		<p>This is your chance to request or explain things if you are requesting  \n";
echo "		something different such as a special alias for a convention or an event.</p> \n";
echo "	</TD> \n";
echo "    <TD WIDTH='10%'>&nbsp;</TD> \n";
echo "    <TD> \n";
echo "	<p>Memo:</p><br /> \n";
echo "	<textarea cols='35'	rows='5' name='comments' id='comments'></textarea> \n";
echo "    </TD> \n";
echo "  </TR> \n";
echo "  <TR><TD colspan='3'><hr></td></tr> \n";
echo "  <TR> \n";
echo "    <TD colspan='2'>&nbsp;</TD--> \n";
echo "    <TD align='center'> \n";
echo "		<input  type='submit' value='Submit request'/> \n";
echo "        <input type='reset' value='Clear form'/></td> \n";
echo "  </tr> \n";
echo "</table> \n";
echo "</form> \n";

} else {   // Main IF() { form } else { thanks }

echo "<h1>Thank you.</h1> \n";
echo "<br /> \n";
echo "<p>Your request has been sent to the  \n";
echo "<a href='mailto:incl_form@nomail.org'>postmaster</a> and  \n";
echo "<a href='mailto:webmaster@nomail.org'>webmaster</a>.</p> \n";
echo "              <p>Your request will be addressed as soon as possible.</p> \n";
echo "<br /> \n";
echo "<span class='email'><a href='mailto:webmaster@nomail.org''>Webmaster@nomail.org</a></span> \n";
echo "<br /> \n";

}  // End of the Main IF() { form } else { thanks }

?>

Some notes on the Sample

Expanded Sept 30, 2006 kite [at] puzzlers [dot] org

This is a simple, sample form which we use on http://www.puzzlers.org/. Its a simple feedback for used to let members answer all the right questions for a request to the postmaster.

  • Echo is safe in the form as all output is captured and slipped into the stream where the tag was.
  • Forms can assume DokuWiki will provide the bulk of the page, so you'd never need to send <html> <head> or <body> tags. Just a properly conforming “chunk” for the middle – it will be in a <div>.
  • I check to see if the form was posted or not with !(isset($_GET['sent']) and ($_GET['sent'] = 1)) which is simply a value on the form.
  • I build my form as <FORM METHOD='POST' ACTION='/cgi-bin/FormMail.pl' name='FormMail' enctype='application/x-www-form-urlencoded' onSubmit='SetSubject();'> which refers to a global web-to-mail script provided by my ISP. Using application/x-www-form-urlencoded means the parameters are part of the URL which was a requirement for the form's consumer (which I have no control over).
  • The form also sends along its own JavaScript to use form values to build an email subject. If the client does not support scripting, then the default subject is used.
  • For forms that refer back to themselves I use a two step process:
  1. <form action=" . $_SERVER['PHP_SELF'] . " method="post" enctype="multipart/form-data"> means the form will refer back to the same script that is currently running, in my case /dokuwiki/doku.php
  2. <input type='hidden' name='id' value='" . $_GET['id'] . "'/> needs to be in the form so the page ID field is carried back in what ever request you'll process. This means /dokuwiki/doku.php will reload the previous page and this form again. Note: this is the PAGE reference, not the form. The form can be in a different namespace.
    • $_GET['id'] isn't the normal 'Wiki-ish' way to get the page id – normally your script would add a reference to global $ID and then just use $ID. But the form was simply imported from a PHP website and streamlined into our wiki with almost no changes beyond adding the reference to the page $id. At the time, I was naive and used what seemed to be (and still is) a workable option.

There is a problem or caveat about scope using forms. In a normal PHP page, you can declare a variable in one <?PHP ... ?> block, and use them later in another <?PHP ... ?> block. This plugin does not support that. Each block must be a complete code entity and can not rely on “all things before.” This is a limitation of the Eval() function which seems to always start with an untainted or uninherited environment. This may be one advantage to other PHP support plugins that use @include() to inject the PHP – they will inherit the current environment and be handled in the current context. The disadvantage of @include() is that you end up with more interaction with your environment. That means your form can break the DokuWiki, too. From a code perspective, I feel forms should be more “modular” and independent of their context. If nothing else, this makes you intentionally reference external values. :!: This limit is removed with the “one eval” fix in the discussion heading. /kite [at] puzzlers [dot] org 2006/10/9

Most of my forms tend to be a single <?PHP ... ?> block and use ECHO liberally. Some of my forms are a series of functions returning chunks of XHTML and a final, global set of calls collect the chunks in the correct series for output. For example, one form interacts with a MySQL database and it ends with these calls:

if(IsAnUpdate()) {
   SaveChanges();
}
echo EditUserForm();
if{IsADelete()) {
   DeleteUser();
}
echo DataSetTable();
  • IsAnUpdate() and IsADelete() are both simple boolean checks to see if the form was submitted with specific options set.
  • EditUserForm() builds a data entry form for each user to update their own data. This performs a query against the DB ad fills the value of all the fields from the current record.
  • DeleteUser() removes a user's record after showing the data in the user's form so they one last chance to re-save their info before it is gone.
  • DataSetTable() is a report style output for all records. It supports sorting the table's columns, too. It returns an XHTML table.

Discussion

Regular Expressions

The two regular expressions seem to raise a lot of questions.

    function connectTo($mode) {
       $this->Lexer->addSpecialPattern('~~INCL_FORM[^~]*~~',$mode,'plugin_incl_form');
    }

This merely matches the tag in the page. It can have one of two forms:

~~INCL_FORM~~
~~INCL_FORM=namspace:pageid~~

The match is literally find ”~~INCL_FORM” and any text until ”~~”. The ”[^~]*” matches anything that is not a ”~” character – I do this since PREG functions are “greedy” and, if you have two forms on a page, a PREG function would go from the start of one to the end of the second unless your expression is limited as I've done.

    /**
     * Handle the match
     */
    function handle($match, $state, $pos, &$handler){
	    // Remove the tag itself, and then separate the form name
	    // from the parameter set.
	    $match = preg_replace("%~~INCL_FORM(=(.*))?~~%u", "\\2", $match);
	    //echo "\n\t<!-- syntax_plugin_INCL_FORM.handle() found >> $match << -->\n";
        return $match;
    }

This is very similar to the “match” and that may be why it confuses people. The ”%” are just the regular expression delimiters; the second ”%” just matches the first character or the regex … they could be pretty much anything but ”%” made sense to me at the time. It's notable that these demarcation characters are NOT in the match expression.

The rest looks almost as easy as the actual match – the “\\2” back reference is the complication. In this function, you know you have possibly three parts that matched:

  • ”~~INCL_FORM” opens the tag.
  • some other text meant to be parameters (and may be empty).
  • the text ”~~” than close the tag.

The ONLY part you care about is the parameters. You already know you're in the process of including a form, so the rest of the tag is pretty useless within the plugin. preg_replace is used to extract the parameter parts of the plugin tag that was matched by the first pattern – all the ”[^~]*” text, if any. The trick is in the ( ) pairs because they allow back references.

  • ”[^~]” matches with ”(=(.*))?”. The outer group can be referenced as “\\1” and is your parameter with the ”=” prefixed, if it is there. The ”?” means it can also be empty.
  • The inner ( ) pair is around the parameter value itself and is the value “\\2”.

So, this pattern match replaces all of the matched text with just the parameter value(s).

Giving ~~INCL_FORM~~ to the preg_replace returns an empty string – no parameter text in it.

Giving ~~INCL_FORM=namspace:pageid~~ to preg_replace returns “namspace:pageid” – just the parameter.

Once I know if my result is empty or not, I know to use the current pages $ID or the parameter value to find my form. Normally, I use it without a parameter, but I have some forms I include in several places, so having the parameter means I have only one form from several places.

Kite [at] puzzlers [dot] org

Passing More Parameters

Added Sept 30, 2006 kite [at] puzzlers [dot] org

While working with one form, I wanted code in some defaults for the form. I made a minimal set of changes that seem to work. The new syntax becomes:

~~INCL_FORM~~ Form at the current page id (old).
~~INCL_FORM=section:namespace~~ Form at the specified page id (old).
~~INCL_FORM;option=value;option2=value2~~ Form at the current page id with parameters (new).
~~INCL_FORM=section:namespace;option=value;option2=value2~~ Form at the specified page id with parameters (new).

Two functions in the class need to change:

    /**
     * Create output
     */
    function render($mode, &$renderer, $data) {
        if($mode == 'xhtml'){
            $PARAMS = split(';', $data);                   // new
            $text=$this->_inc_form($PARAMS[0], $PARAMS);   // changed
            $renderer->doc .= $text;
            return true;
        }
        return false;
    }

Note, the first parameter MUST be preceded by a semicolon for this syntax or it will be mistaken for the Form ID.

    function _inc_form($form, $PARAMS) {
        //global $conf;
        global $FORM_PARAMS;                    // new
        global $ID;
        if(strlen($form) < 1) {
		    $form = $ID;   // default to the current page ID
	}
	$path =  $this->formFN($form);  
	foreach($PARAMS as $item) {             // 2006/09/30 begin <kite@puzzlers.org>
		// split the pair
		$parts = explode('=', $item);
		// Skip the $ID parameter
		if(strlen($parts[0])>0) {
		// $FORM_PARAMS['name'] = value
		$FORM_PARAMS[$parts[0]] = $parts[1];  // make the values global
        }
    }                                      // 2006/09/30 end <kite@puzzlers.org>
    if(file_exists(DOKU_INC . $path)) {
        //echo "<!-- form was found -->\n";
        $text = io_readFile($path); 
        $pattern = "/(<\?php)(.*?)(\?>)/is";			
        while(preg_match($pattern, $text)) {
            $text = preg_replace_callback($pattern, "eval_form_php", $text);
        }
        $text = "\n\t<!-- Begin form '$form' -->\n" 
            . $text
            . "\n\t<!-- End form '$form' -->\n";
	} else {
            $text = "\n\t<!-- No form found for '$form'-->\n";
        }
        return $text;
    } // _inc_form()

This revised function declares a global variable, $FORM_PARAMS and fills it with the split values from the $data portion of the plugin's data (the variable $PARAMS here). Within a form, I also declare global $FORM_PARAMS; and access them. For my use, I simply convert the parameter list into an array. This works well for me since my parameters are 'defaults' and named the same as my fields in the DB.

Feature Suggestions

I like the idea of this plugin, but I have some suggestions:

  • I want to have every page in a namespace have the same form in the footer. This requires two changes; the file data/form\/namespace.php should be looked for also and you must pass the name of the current page as a parameter to the eval().
  • Error handling. If the eval's fail fatally, then just post an error. Should probably also hide warnings.
  • The way eval's can't share variables among each other is the main problem and why I need to use include. Since this works as expected:
  eval('?>i am html<?php $a = 1 ?>more html<?php echo $a ?>')

why not just use a single eval for the whole included page with a ”?>” prefixed?


2006/10/09 kite [at] puzzlers [dot] org

  • Eval doesn't need the current page since its derived from the global $ID value in all my included code.
  • I rarely have code in a PHP form that fails fatally, and when it does, I avoid passing errors back to the user. When they do fail, it is generally because of the server having limits on execution time or memory allocations.
  • Repeated Eval are used because eval() doesn't seem to like <?PHP and ?> tags in the text passed in; I did try that after looking at the code in phpinc and wasn't able to resolve the issue. I'm guessing phpinc used @include() for this reason. Since I want to be able to freely mix HTML and PHP as I would anywhere else, a loop works well for me.
  • Yes, I agree the problem of sharing data between “chunks” is a problem and I take one of two approaches (generally the first): use only one PHP chunk that simply ECHO's the HTML in the midst, or use global variables for each form. I've never considered the syntax of prepending ?> … I'll give that some testing.

The footer question is, to me at least, easily handled: that functionality belongs in your template, not in a plugin. Your template is a global control of how every page behaves. The last time I looked, the bottom of the default template includes an upword search for _footer.html to insert. It isn't a huge leap at all to make this code search your form tree for _footer.php to apply to all pages. The “trick” then is to call a function that can do the parsing of your footer – I do it in my _inc_form() and eval_form_php(). Or your idea would remove the Regex loop so that its just suck in the text and call eval(”%>…”).

As I said in my intro, this was implemented while I was new to DokuWiki, and in general, I stop when things work as well as I need them too. I agree with, and was bothered by, many of the same problems but didn't worry about them too much because the workarounds were simple. You've given me a couple more ideas to try; I'll let you know how it turns out.

/ kite [at] puzzlers [dot] org

Later… I've eliminated the loop in my code with two small changes. My eval_form_php() now looks like this:

function eval_form_php($arr) {
	global $INFO;

	if(0 and ($INFO['perm'] == AUTH_ADMIN)) { // Use debug output??
		ob_start();
		$content = "<!-- Content: "; 
		print_r( $arr ); 
		$content .= ob_get_contents();
		ob_end_clean();
		$content .= " -->\n";
		echo $content;  // can't return this
	}
	ob_start();
	if($INFO['perm'] == AUTH_ADMIN) {
		eval("?>$arr");                // NOT $arr[2] -- now allows parsing all PHP blocks 
	} else {
		@eval("?>$arr");               // NOT $arr[2] -- now allows parsing all PHP blocks 
	}	
	$content = ob_get_contents();
	ob_end_clean();
	return $content;
}

At the bottom of the plugin, the last function in the class, _inc_form() now has the loop commented and replaced with a single call to eval():

    function _inc_form($form, $PARAMS) {
        //global $conf;
		global $FORM_PARAMS;
		global $ID;
        if(strlen($form) < 1) {
		   $form = $ID;   // default to the current page ID
		}
		$path =  $this->formFN($form);  
		foreach($PARAMS as $item) {
			// split the pair
			$parts = explode('=', $item);
			// Skip the $ID parameter
			if(strlen($parts[0])>0) {
				// $FORM_PARAMS['name'] = value
				$FORM_PARAMS[$parts[0]] = $parts[1];  
			}
	    }
		if(file_exists(DOKU_INC . $path)) {
		    //echo "<!-- form was found -->\n";
			$text = io_readFile($path); 
			$pattern = "/(<\?php)(.*?)(\?>)/is";
//------------------------------------------------------------------------------------- Oct 9/2006		
//			while(preg_match($pattern, $text)) {
//				$text = preg_replace_callback($pattern, "eval_form_php", $text);
//			}
			$text = eval_form_php($text);                            // New Oct 9, 2006
//------------------------------------------------------------------------------------- Oct 9/2006		
		} else {
			$text = "\n\t<!-- No form found for '$form'-->\n";
		}
		return $text;
	} // _inc_form()

/ kite [at] puzzlers [dot] org


bugs?

whenever I edit a page containing a form, I get an error message:

Warning: Cannot modify header information - headers already sent by (output started at /var/www/castello16.com/lib/plugins/incl_form/syntax.php:142) in /var/www/dokuwiki/inc/actions.php on line 288

The edit works if the page is reloaded, but this is not perfect…

/ richard at the-place dot net 07/02/10


Remove trailing newlines from syntax.php!

Same error here. I installed by cutting-and-pasting the code at the top of the page. Removing trailing newlines from syntax.php fixed it.

What's the canonical version?

Does the first codeblock include all the modifications discussed and reposted throughout the thread? The above version is up to date with my server as of Apr 20, 2007 / kite [at] puzzlers [dot] org

Cross-posted question from email. Any clues?

Hello,

I really like the idea of your incl_form plugin. Unfortunately, I've been unable to get it to work. I've read the directions carefully several times, updated to the newest DokuWiki, turned on debugging, twiddled permissions, etc.

e.g. if i add the following line:

~~INCL_FORM~~

to this wiki page:

wiki.x14n.org/doku.php/dbs

where ive written the document:

 forms/dbs.php

and added to local.php the line:

 $conf['formdir'] = 'forms';

I still get in the page source of dbs.php the line:

 <!-- No form found for 'dbs'-->

For security, I've moved the core DokuWiki functionality out of my webspace/docroot. I'm wondering if this interferes with the plugin, or if I'm missing something really obvious.

Any suggestions would be greatly appreciated! thanks, christian

the answer

Try a setting of

$conf['formdir'] = 'data/forms';

Yup, that works. Symlinks work fine, too.

It doesn't seem to like absolute paths (which seem to work for other settings in local.php) or being anywhere other than “data/forms”.

You need to adjust one line to compensate for your path:
if(file_exists(DOKU_INC . $path)) {...

DOKU_INC is global define() normally set in /dokuwiki/doku.php to be the folder of that file.
Without more work, this must still point to the top of your DokuWiki install since $path has
$conf['formdir'] prefixed. As long as your $FORM_DIR is an absolute path, you could use

if(file_exists($path)) {...

kite [at] puzzlers [dot] org

Update: how to disable caching

After a flash of hopelessness, i accidentally discovered something very helpful.

DokuWiki's cache seems a little broken with regard to this plugin - I edit my templates, and the changes don't show up. The easy solution?

Touch conf/local.php

Also, including the following tag works:

~~NOCACHE~~

see caching for more info

do you mean your form template, or your website template? I generally touch the website template
since doku.php knows nothing about the forms so it can't see that the cache needs to be updated.

The marking is useful on ANY page with a form in it; it allows current data to be
generated (the usual use of the form) and not cached by the web browser. Unfortunately, plugins
can't disable the wiki caching.

A brief example

“Get” worked better for me that “Post” - I'm guessing because of the scoping/inheritance discussed above. Here's my md5sum (or anything else) equality evaluator using an “lamda-style” function to compare:

<?PHP
$md5check = create_function('$a,$b', 'if ($a==$b) {return "They Match.";} else {return "No Match!";}');
echo "<FORM METHOD='GET' ;'> \n";
echo "<TABLE width=90%> \n";
echo "  <TR> \n";
echo "    <TD align='left'> \n";
echo "          Old Value: <input  name='old' type='text' size=90 value='".$_GET['old']."' /> \n";
echo "  </tr> \n";
echo "  <TR> \n";
echo "    <TD align='left'> \n";
echo "          New Value: <input  name='new' type='text' size=90 value='".$_GET['new']."' /> \n";
echo "  </tr> \n";
echo "  <TR> \n";
echo "    <TD align='left'> \n";
echo "          <input type='submit' value='Submit request' /> \n";
echo "  </tr> \n";
echo "</table> \n";
echo "</form> \n";
echo "<h3>" . $md5check($_GET['old'], $_GET['new']) . "</h3>";
?>
plugin/incl_form.txt · Last modified: 2013/03/21 13:27 by Aleksandr