|
Cookbook /
WikiForms RecipesSummary: Various recipes for the WikiForms recipe
Version:
Prerequisites:
Status:
Maintainer:
Categories: Forms
Assign and reference titles for numeric page namesThis is a 2 part feature: assign titles to pages with numeric names; and reference a numeric page by its title. To assign a title, declare one of the fields to be type
Now we need a way to reference a page by its title. formtitle.phpΔ answers this requirement. This adds [[?page title]] markup to resolve references to pages that include the page's title in the RecentChanges change summary. You need to tell it the location of a page in your forms group where the new entry form is:
$NewGlossaryPage = 'Glossary.NewEntry'; //for example
include_once("$FarmD/cookbook/formtitle.php");
If you now write
Note that this markup assumes you only want to do this for one forms group per wiki. It needed a special and unambiguous markup and I chose Pagelist/searchbox template to search through Wikiform groupsDescriptionI'm using this template to search through multiple Wikiform groups from a searchbox directive. The results are displayed as a table with the following structure (links in example are fakes):
The title information is taken from paragraph 9 on my Wikiform pages. It would be cool if I could suppress the :title: markup that precedes the actual document title (documents are stored as attachments of the wikiform pages), but I don't know how to do that. A slight problem is that pages in the searched groups that are not Wikiform pages are included in the list as well, leading to random lines to be included in the title field. Another slight problem is that the number of titles displayed is limited by $maxincludes, which might not always be enough. (I have set the value to 250, but there are ca. 3200 documents in my application.) I could exclude the non-document pages as they do not start with a numeric character, but that would load up the searchbox with a rather lengthy string (searching pages 0*, 1*, 2*, 3*, ..., 9*), but I haven't implemented it that way yet because it seems crude. I'll look for a smarter way (if there is any). --Henning October 19, 2006, at 10:27 AM Template
[[#headerinclude]]
(:if equal {<$Group}:)
(:table cellspacing:5px:)
(:cell:)'''Group'''
(:cell:)'''Page'''
(:cell:)'''Title'''
(:cell:)
(:if:)
(:cellnr:)[[{=$Group}]]
(:cell:)[[{=$FullName}|{=$Title}]]
(:cell:)(:include {=$FullName} lines=9..9:)
(:if equal {>$Group}:)
(:tableend:)
----
(:if:)
[[#headerincludeend]]
authorplain field typeThis field type allows you to populate it with the author's name automatically, but without the profile markup. In EntryForm(), insert the following clause (may need to appear before the one matching "author"):
} elseif (preg_match('/^authorplain(?:=(\\d+))?$/',$f[$i]['etype'],$m)) {
$col = ($m[2]) ? $m[2] : 32;
if ($editing) $author = DeLink($fv[$f[$i]['element']]);
else $author = FmtPageName('$Author',$pagename);
$out[] = "<input type='text' size='$col' name='".$f[$i]['element'].
"' value='$author' />";
Also, in FormData(), insert the following clause:
if (preg_match('/^authorplain/',$f[$i]['etype'])) {
$v = $_REQUEST[$f[$i]['element']];
}
shi December 20, 2006, at 02:44 PM Added in version 1.0.52 jr March 22, 2007, at 06:38 PM
buttondate field typeI added this field type to allow the convenience of a button for setting today's date while editing an existing form, e.g. "date closed" of an issue tracking system. Note that the "Today" button is only shown while the date is undefined, to ensure that if the user changes an existing date, s/he really intended to do so. Insert the following code near the top of wikiform.php:
## Add set date script to Header
$HTMLHeaderFmt['todays_date']= <<<SET_DATE
<script language="JavaScript">
<!-- Begin
function SetDate(elementName, y, m, d)
{
// NOTE: we require that the Y/M/D elements appear
// in that order in the form
var date = new Array(y, m, d);
var index = 0;
elements = document.getElementsByName(elementName);
len = elements.length;
for(i = 0; i < len; i++)
{
elements[i].value = String(date[index]);
index += 1;
}
return;
}
// End -->
</script>
SET_DATE;
In EntryForm(), insert the following clause:
} elseif (preg_match('/^buttondate$/',$f[$i]['etype'],$m)) {
if ($editing)
$date = ($fv[$f[$i]['element']]) ? $fv[$f[$i]['element']] : 'yyyy-mm-dd';
else
$date = 'yyyy-mm-dd';
$y = substr($date,0,4);
$m = substr($date,5,2);
$d = substr($date,8,2);
$out[] = "<input type='text' size='5' maxlength='4' name='".
$f[$i]['element']."[]' value='$y' /> - ".
"<input type='text' size='3' maxlength='2' name='".
$f[$i]['element']."[]' value='$m' /> - ".
"<input type='text' size='3' maxlength='2' name='".
$f[$i]['element']."[]' value='$d' />";
// only show "today" button if date does not yet have a value
if ('yyyy' == $y)
{
$date = strftime('%Y-%m-%d',time());
$y = substr($date,0,4);
$m = substr($date,5,2);
$d = substr($date,8,2);
$element_name = $f[$i]['element'] . "[]";
$out[] .= "<input type=button value='Today' onClick=\"SetDate('$element_name', $y, $m, $d)\">";
}
Also, in FormData(), insert the following clause:
elseif (preg_match('/^buttondate$/',$f[$i]['etype']))
$v = str_replace('yyyy-mm-dd','',
implode('-',$_REQUEST[$f[$i]['element']]));
shi December 20, 2006, at 02:44 PM Added as a "today's date" checkbox in version 1.0.52 (no Javascript required). jr March 22, 2007, at 06:38 PM
attach field typeThis creates a new field type 'attach' which silently adds "Attach:" markup to the input data. This makes attaching files to the page easier, since you don't have to put in the "Attach:" by hand, which is helpful for naieve users. In EntryForm(), insert the following clause:
} elseif (preg_match('/^attach(?:=(\\d+))?$/', $f[$i]['etype'], $m)) {
$col = ($m[1]) ? $m[1] : 54;
$out[] = "<input type='text' size='$col' name='".
$f[$i]['element']."' value='".
(($editing) ? DeFile($fv[$f[$i]['element']]) : $default).
"' />";
}
Also, in FormData(), insert the following clause:
elseif (preg_match('/^attach(?:=(\\d+))?$/',$f[$i]['etype'],$m)) {
$v = $_REQUEST[$f[$i]['element']];
if ($v) {
$v = str_replace('Attach:', '', $v);
$v = "Attach:$v";
}
}
Kathryn Andersen February 06, 2007, at 05:39 PM
query against field valuesHere is one way to implement a query to search given field values and return them in the
(Note that this example query form, above, is non-functional without the data and php code to go along with it -- this is just to give you an idea of what it looks like) As you look at the following example here are some points to give you context:
In the page containing the
(:messages:)\\
[+[[Add | Add a new book]]+]
(:input form "http://www.ccl-al.org/pmwiki/pmwiki.php?n=Alb.BookCatalog" method="GET":)
(:input hidden name=n "Alb.BookCatalog":)
|| border=0
|| Original Author Query:||(:input text name=origauthorquery:)|| Albanian Author Query:||(:input text name=albauthorquery:)||
|| Original Title Query:||(:input text name=origtitlequery:)|| Albanian Title Query:||(:input text name=albtitlequery:)||
|| Original Publisher Query:||(:input text name=origpubquery:)|| Albanian Publisher Query:||(:input text name=albpubquery:)||
|| Type Query:||(:input checkbox name=typebookquery value=1:) Book\\
(:input checkbox name=typebookletquery value=1:) Booklet\\
(:input checkbox name=typetractquery value=1:) Tract|| Availability:||(:input select name=availability value=1 label="BOTH in print and out of print":)
(:input select name=availability value=2 label="ONLY books still in print":)
(:input select name=availability value=3 label="ONLY books out of print":) ||
(:input submit Search:)
(:input end:)
All Items matching original author={$origauthorquery} original title={$origtitlequery} type=={$typequery} inprint=={$availablequery}
(:wikilist engauthor="{$origauthorquery}" \
engtitle="{$origtitlequery}" \
albauthor="{$albauthorquery}" \
albtitle="{$albtitlequery}" \
origpublisher="{$origpubquery}" \
albpublisher="{$albpubquery}" \
type="={$typequery}" \
inprint="={$availablequery}" :)
In the local/ directory create a custom configuration file (named Alb.BookCatalog.php in my case) with this php code:
<?php
foreach ($_GET as $k=>$v) {
$foo = htmlspecialchars($v);
# This keeps the field values current with the form from submission to
# submission, but has nothing to do with PTV
$InputValues[$k] = $foo;
# This creates a PTV
$FmtPV['$'.$k] = "'$foo'";
}
# If all (tract/book/booklet) are blank that's equivalent to all being
# selected -- none doesn't make any sense.
if ($FmtPV['$typebookquery'] != "'1'" && $FmtPV['$typebookletquery'] != "'1'" && $FmtPV['$typetractquery'] != "'1'") {
$FmtPV['$typebookquery'] = "'1'";
$FmtPV['$typebookletquery'] = "'1'";
$FmtPV['$typetractquery'] = "'1'";
$InputValues['typebookquery'] = 1;
$InputValues['typebookletquery'] = 1;
$InputValues['typetractquery'] = 1;
}
# Now from 3 variables ($type . <book|booklet|tract> . query) we need
# to form one variable ($typequery) to be used in the actual query with
# a value such as "book" or "book|tract" or etc
$typequery = '';
if ($FmtPV['$typebookquery'] == "'1'") {
$typequery = 'Book';
}
if ($FmtPV['$typebookletquery'] == "'1'") {
if ($typequery != '') {
$typequery .= '|';
}
$typequery .= "Booklet";
}
if ($FmtPV['$typetractquery'] == "'1'") {
if ($typequery != '') {
$typequery .= '|';
}
$typequery .= "Tract";
}
$FmtPV['$typequery'] = "'$typequery'";
# Now from the $availability variable we need to make a query-able variable
# called $availablequery containing either blank (out of print) or "Yes" (in print
# or "|Yes" (either in print or out of print)
if ($FmtPV['$availability'] == "'3'") {
$FmtPV['$availablequery'] = "''";
} else {
if ($FmtPV['$availability'] == "'2'") {
$FmtPV['$availablequery'] = "'Yes'";
} else {
# this is the default - whether the value is "1" (after form submission)
# or whether it is blank (upon initial form access)
$FmtPV['$availablequery'] = "'|Yes'";
}
}
Peter Bowers December 12, 2007 Note the presence of Cookbook:ProcessForm which takes care of the basic creation of PVs as well as maintaining form values between submissions. Once you have that recipe installed you can put forms like this on any page without messing with group-specific or page-specific code. The only time you would need the page-specific code would be to do various validations. Peter Bowers March 06, 2008, at 11:40 AM
Exporting to CSV or TSVPeriodically it is convenient to gather data on the web via wikiforms but then to collect it into a spreadsheet for local processing (mailmerge, etc.). Many of the things you would do with the spreadsheet could probably be done through other related recipes (publishpdf, etc.), but often the PC-based tools are more familiar to non-technical users. This recipe is not highly polished, but it will get your data into a CSV format.
$HandleActions['export'] = 'HandleExport';
function HandleExport($pagename, $auth = 'read')
{
global $WikiFormViewFmt, $WikiFormPageFmt;
$group = FmtPagename('{$Group}', $pagename);
#echo "DEBUG: group=$group<br>\n";
$pagelist = ListPages("$group.0*");
#echo "DEBUG: pagelist=<pre>".print_r($pagelist,true)."</pre><br>\n";
$wikipage = $pagelist[0];
$f = FormFields($wikipage,$WikiFormPageFmt);
#echo "DEBUG: f=<pre>".print_r($f,true)."</pre><br>\n";
# hdr=element --> variable name
# hdr=eprompt --> use the prompt
switch (@$_REQUEST['headertype']) {
case 'var':
case 'variable':
case 'element':
$hdr = 'element';
break;
case 'prompt':
case 'eprompt':
$hdr = 'eprompt';
break;
default:
if (@$_REQUEST['headertype']) $hdr = $_REQUEST['headertype'];
else $hdr = 'eprompt'; // default to using prompt
break;
}
switch (@$_REQUEST['delimiter']) {
case 'double-quote':
case 'doublequote':
$delimiter = '"';
break;
default:
if (@$_REQUEST['delimiter'])
$delimiter = html_entity_decode($_REQUEST['delimiter']);
else
$delimiter = '"'; // default to double-quote
}
switch (@$_REQUEST['separator']) {
case 'comma':
$separator = ',';
break;
case 'tab':
$separator = "\t";
break;
case 'semi-colon':
case 'semi':
case 'semicolon':
$separator = ';';
break;
default:
if (@$_REQUEST['separator'])
$separator = html_entity_decode($_REQUEST['separator']);
else
$separator = ','; // default to comma
}
$escaped_delimiter = $delimiter.$delimiter; // parameterize this later
$fldlist = array();
foreach ($f as $fields)
$fldlist[] = $fields[$hdr];
CSVout($fldlist, array(), $delimiter, $escaped_delimiter, $separator, "");
foreach ($pagelist as $wikipage) {
$fve = FormValues($wikipage,$f,'read');
#echo "DEBUG: fve=<pre>".print_r($fve,true)."</pre><br>\n";
CSVout($fve['fv'], $f, $delimiter, $escaped_delimiter, $separator);
}
}
function CSVout($rec, $flds, $delimiter='"', $escaped_delimiter='""', $separator=',', $init_sep="\n")
{
$sep=$init_sep;
$idx = 0;
foreach ($rec as $val) {
#echo "delimiter=$delimiter, escaped_delimiter=$escaped_delimiter<br>\n";
# Strip off the "<newline>(:title...:)" markup
$val = html_entity_decode($val);
if (@$flds[$idx++]['etype'] == 'title')
$val = preg_replace("/\n\(:title .*:\)$/", '', $val);
# Replace delimiters with double-delimiter and
# single-quote html entity with single-quotes
$val = str_replace(array($delimiter,
'''),
array($escaped_delimiter,
"'"),
$val);
echo "$sep".$delimiter.trim($val).$delimiter;
$sep = $separator;
}
}
Note: In step #2 above, you can specify several other options to change the format of the exported data (each option specified would begin with an ampersand (
&)):
Obviously if you change options to reformat your data then your import into your spreadsheet will have to be similarly modified. There's some way to bypass the saving into a CSV file -- just pasting directly into the spreadsheet and then executing some command. Unfortunately I don't remember -- feel free to modify this description if you know how to do that. —Peter Bowers April 24, 2011, at 09:52 AM |