Wiki source for GraphMaker
****====Graph Maker====
Graph maker allows a user to place graphs on a wiki page using (and requiring on the server) GD2. There are three files. (1) ##graphentry.php## is an action that creates a form on a wiki page that allows a user to easily create a wiki action call to embed on a page so that a graph might be placed there. It provides a preview of the graph so that the user does not have to embed the code on a wiki page to see the output. (2) ##graph.php## is the action that reads the generated code and renders the graph on the page. Both previous actions should be placed in the ##actions## directory. (3) Both of these previous reactions rely on [[http://www.phpclasses.org/browse/package/1993.html | class.graphic.php]] which should be renamed "##graphmaker.php##" and placed in a directory called ##scripts## in the wiki root (no other alterations are necessary). http://gmbtst.msvu.ca/wikitest/graphmaker.jpg
##graphentry.php##
%%(php)
<?
// graphmaker.php Version 1 -- Jan 9, 2005
// Original parts of this script were developed by G. Michael Bowen for a SSHRC research project using wikka wiki.
// This action generates graph images & code that can be placed on a wikka page to place the image there.
// REQUIRES graph.php to place the code on a page as an action AND
// REQUIRES class.graphic.php (http://www.phpclasses.org/browse/package/1993.html)
// to be renamed graphmaker.php & placed in a directory called "scripts" in the wikka root.
// REQUIRES GD2 on the server
// Parts of the code on this page derive from class.graphic.php
// Non-derivative code copyright GMBowen. Released to public domain under GPL. Modify, improve, change as you wish.
// NOTE that implementation may require the value "16" to be changed 6 lines down.
// TODO - a save feature is forthcoming, "if" statements to reduce code output to only parameters with data
$site_base = $this->GetConfigValue("base_url");
// The CODE LINE below is assuming that your statement LIKE wakka.php?wakka= in the config file is 16 characters long
// IF IT IS NOT 16 CHARACTERS LONG, then count the characters & replace the 16 in the line below.
$site_base = substr($site_base,0,-16);
$site_base = $site_base."scripts/";
$url = $site_base."graphmaker.php";
$grftitle = $_POST["title"];
$xaxis = $_POST["axis_x"];
$yaxis = $_POST["axis_y"];
$vartitle1 = $_POST["graphic_1"];
$vartitle2 = $_POST["graphic_2"];
$grftype = $_POST["type"];
$grfskin = $_POST["skin"];
$grphc2val = $_POST["graphic_2_values"];
$grfx0 = $_POST["x0"];
$grfy0 = $_POST["y0"];
$grfz0 = $_POST["z0"];
$grfx = array($_POST[x0], $_POST[x1], $_POST[x2], $_POST[x3], $_POST[x4], $_POST[x5], $_POST[x6], $_POST[x7], $_POST[x8], $_POST[x9], $_POST[x10], $_POST[x11], $_POST[x12], $_POST[x13], $_POST[x14], $_POST[x15], $_POST[x16], $_POST[x17], $_POST[x18], $_POST[x19]);
$grfy = array($_POST[y0], $_POST[y1], $_POST[y2], $_POST[y3], $_POST[y4], $_POST[y5], $_POST[y6], $_POST[y7], $_POST[y8], $_POST[y9], $_POST[y10], $_POST[y11], $_POST[y12], $_POST[y13], $_POST[y14], $_POST[y15], $_POST[y16], $_POST[y17], $_POST[y18], $_POST[y19]);
$grfz = array($_POST[z0], $_POST[z1], $_POST[z2], $_POST[z3], $_POST[z4], $_POST[z5], $_POST[z6], $_POST[z7], $_POST[z8], $_POST[z9], $_POST[z10], $_POST[z11], $_POST[z12], $_POST[z13], $_POST[z14], $_POST[z15], $_POST[z16], $_POST[z17], $_POST[z18], $_POST[z19]);
$numrowsx = $_POST["amountofdata"];
if (empty($numrowsx)) {$numrowsx=9;}
$numcolsx = $_POST["numcols"];
if (empty($numcolsx)) {$numcolsx=1;}
if ($numcolsx==1){$_POST[z0]=""; $_POST[z1]=""; $_POST[z2]=""; $_POST[z3]=""; $_POST[z4]=""; $_POST[z5]=""; $_POST[z6]=""; $_POST[z7]=""; $_POST[z8]=""; $_POST[z9]=""; $_POST[z10]=""; $_POST[z11]=""; $_POST[z12]=""; $_POST[z13]=""; $_POST[z14]=""; $_POST[z15]=""; $_POST[z16]=""; $_POST[z17]=""; $_POST[z18]=""; $_POST[z19]="";}
$urlstrng="title=".$grftitle."&axis_x=".$xaxis."&axis_y=".$yaxis."&graphic_1=".$vartitle1."&graphic_2=".$vartitle2
."&type=".$grftype."&skin=".$grfskin."&graphic_2_values=".$grphc2val."&x0=".$_POST[x0]."&x1=".$_POST[x1]."&x2=".$_POST[x2]
."&x3=".$_POST[x3]."&x4=".$_POST[x4]."&x5=".$_POST[x5]."&x6=".$_POST[x6]."&x7=".$_POST[x7]."&x8=".$_POST[x8]."&x9=".$_POST[x9]
."&x10=".$_POST[x10]."&x11=".$_POST[x11]."&x12=".$_POST[x12]."&x13=".$_POST[x13]."&x14=".$_POST[x14]."&x15=".$_POST[x15]
."&x16=".$_POST[x16]."&x17=".$_POST[x17]."&x18=".$_POST[x18]."&x19=".$_POST[x19]."&y0=".$_POST[y0]."&y1=".$_POST[y1]
."&y2=".$_POST[y2]."&y3=".$_POST[y3]."&y4=".$_POST[y4]."&y5=".$_POST[y5]."&y6=".$_POST[y6]."&y7=".$_POST[y7]."&y8=".$_POST[y8]
."&y9=".$_POST[y9]."&y10=".$_POST[y10]."&y11=".$_POST[y11]."&y12=".$_POST[y12]."&y13=".$_POST[y13]."&y14=".$_POST[y14]
."&y15=".$_POST[y15]."&y16=".$_POST[y16]."&y17=".$_POST[y17]."&y18=".$_POST[y18]."&y19=".$_POST[y19]."&z0=".$_POST[z0]
."&z1=".$_POST[z1]."&z2=".$_POST[z2]."&z3=".$_POST[z3]."&z4=".$_POST[z4]."&z5=".$_POST[z5]."&z6=".$_POST[z6]."&z7=".$_POST[z7]
."&z8=".$_POST[z8]."&z9=".$_POST[z9]."&z10=".$_POST[z10]."&z11=".$_POST[z11]."&z12=".$_POST[z12]."&z13=".$_POST[z13]
."&z14=".$_POST[z14]."&z15=".$_POST[z15]."&z16=".$_POST[z16]."&z17=".$_POST[z17]."&z18=".$_POST[z18]."&z19=".$_POST[z19];
?>
<style type="text/css">
<!--
table {
font-size: 14px;
font-family: verdana;
color: #666666;
}
input, select {
font-size: 12px;
font-family: verdana;
color: #333333;
border: 1px solid #aaaaaa;
}
-->
</style>
<script type="text/javascript">
<!--
function numbers() {
key = event.keyCode;
if ((key >= 48 && key <= 57) || key == 46 || key == 13) { return true; } else { return false; }
}
//-->
</script>
<FORM ACTION="" METHOD=POST name="form_data" id="form_data">
<P><TABLE BORDER=0>
<TR>
<TD WIDTH=334>
<P># of data sets: 5
<INPUT TYPE=radio NAME=amountofdata VALUE=4 <? if (($numrowsx==4)||($numrowsx=="")){echo "CHECKED";} ?> style="border-width: 0px;"> 10<INPUT TYPE=radio NAME=amountofdata VALUE=9 <? if ($numrowsx==9) {echo "CHECKED";} ?> style="border-width: 0px;"> 15<INPUT TYPE=radio NAME=amountofdata VALUE=14 <? if ($numrowsx==14) {echo "CHECKED";} ?> style="border-width: 0px;"> 20<INPUT TYPE=radio NAME=amountofdata VALUE=19 <? if ($numrowsx==19) {echo "CHECKED";} ?> style="border-width: 0px;">
<BR># of columns of data: 1 column<INPUT TYPE=radio NAME=numcols VALUE=1 <? if (($numcolsx==1)||($numcolsx=="")) {echo "CHECKED";} ?> style="border-width: 0px;"> 2 columns<INPUT TYPE=radio NAME=numcols VALUE=2 <? if ($numcolsx==2) {echo "CHECKED";} ?> style="border-width: 0px;"></P>
</TD></TR><TR>
<TD>
<P><INPUT TYPE=submit NAME=dataform VALUE="Submit table rules" style="cursor: pointer; margin: 10px 0px 0px 60px;"></P>
</TD>
</TR>
</TABLE>
<br>
<table><tr><td ROWSPAN=2 valign="top">
<div style="margin-bottom: 10px;">
Title: <input type="text" name="title" id="title" size="30" value="<? echo $grftitle; ?>" style="margin: 0px 0px 0px 11px;" />
</div>
Axis X: <input type="text" value="<? echo $xaxis; ?>" name="axis_x" id="axis_x" size="30" /> <br />
Axis Y: <input type="text" value="<? echo $yaxis; ?>" name="axis_y" id="axis_y" size="30" /> <br />
<div style="margin: 10px 0px 10px 0px;">
Variable 1: <input type="text" name="graphic_1" value="<? echo $vartitle1; ?>" id="graphic_1" style="width: 172px;" /> <br />
Variable 2: <input type="text" name="graphic_2" value="<? echo $vartitle2; ?>" id="graphic_2" style="width: 172px;" /> <br />
</div>
Type: Vertical Bars <INPUT TYPE=radio NAME=type VALUE=1 <? if (($grftype==1)||($grftype=="")){echo "CHECKED";} ?> style="border-width: 0px;"> Horizontal Bars<INPUT TYPE=radio NAME=type VALUE=2 <? if ($grftype==2) {echo "CHECKED";} ?> style="border-width: 0px;"><br> Dots<INPUT TYPE=radio NAME=type VALUE=3 <? if ($grftype==3) {echo "CHECKED";} ?> style="border-width: 0px;"> Lines<INPUT TYPE=radio NAME=type VALUE=4 <? if ($grftype==4) {echo "CHECKED";} ?> style="border-width: 0px;"> Pie<INPUT TYPE=radio NAME=type VALUE=5 <? if ($grftype==5) {echo "CHECKED";} ?> style="border-width: 0px;"> Donut<INPUT TYPE=radio NAME=type VALUE=6 <? if ($grftype==6) {echo "CHECKED";} ?> style="border-width: 0px;"><br \>
<!-- <OPTION VALUE=1>Vertical Bars
<OPTION VALUE=2>Horizontal Bars
<OPTION VALUE=3>Dots
<OPTION VALUE=4>Lines
<OPTION VALUE=5>Pie
<OPTION VALUE=6>Donut</select> <br />
-->
Color: Office<INPUT TYPE=radio NAME=skin VALUE=1 <? if (($grfskin==1)||($grfskin=="")) {echo "CHECKED";} ?> style="border-width: 0px;"> Matrix<INPUT TYPE=radio NAME=skin VALUE=2 <? if ($grfskin==2) {echo "CHECKED";} ?> style="border-width: 0px;"> Spring<INPUT TYPE=radio NAME=skin VALUE=3 <? if ($grfskin==3) {echo "CHECKED";} ?> style="border-width: 0px;"><br \>
<!--<select name="skin" id="skin">;
<OPTION VALUE=1>Office
<OPTION VALUE=2>Matrix
<OPTION VALUE=3>Spring
</select>
-->
<? echo '
<div style="margin-top: 20px;" id="parameters">
<span style="margin-left: 30px;">Label</span>
<span style="margin-left: 102px;">Variable 1</span>';
if ($numcolsx==2) {echo '<span id="value_2" style="margin-left: 15px;">Variable 2</span>';}
for ($i = 0; $i <= $numrowsx; $i++) {
echo '<div style="margin-left: 8px;"> '.($i+1).'. <input type="text" value="'.$grfx[$i].'" name="x'.$i.'" id="x'.$i.'" size="20" /> ';
echo ' <input type="text" name="y'.$i.'" id="y'.$i.'" size="10" value="'.$grfy[$i].'" onkeypress="return numbers();" /> ';
if ($numcolsx==2) {echo ' <input type="text" name="z'.$i.'" id="z'.$i.'" size="10" value="'.$grfz[$i].'" onkeypress="return numbers();" />';}
echo '</div>';
}
echo ' <input type="submit" value="Create Graph" style="cursor: pointer; margin: 10px 0px 0px 60px;" /></form>';
?>
</td>
<td valign="top" TD HEIGHT="220">
<?
$urlstrng = trim($urlstrng); // Get rid of space on left/right side if any
$urlstrng = str_replace(' ', ' ', $urlstrng); // Make a double space a single space (just in case)
$urlstrng = str_replace(' ', '+', $urlstrng); // Make a single space a '+'
if(((!empty($_POST[y0]))||(!empty($_POST[z0])))&&(!empty($_POST[x0]))) {print "<img src=".$url."?".$urlstrng.">";}
?>
</td>
</tr>
<TR><TD valign="top">
<? if(((!empty($_POST[y0]))||(!empty($_POST[z0])))&&(!empty($_POST[x0]))) {$enterstrng="{{graph code=\"".$urlstrng."\"}}";} else {$urlstrng="";}
?>
<TEXTAREA NAME=codeoutput ROWS=11 COLS=65 WRAP=virtual value="5"><? echo $enterstrng; ?> </TEXTAREA><br>
Copy & paste this code into your wiki page to have this graph appear on that page.<br>
</TD
</tr></table>
%%
~&Four problems are immediately apparent here:
~~1) All input seems to be accepted without any kind of validation (security risk!)
~~~& would running $urlstrng through safehtml solve that? Everything is concatenated into that statement essentially.
~~~~&Well, you certanly don't want **any** HTML in there (not just "safe" HTML) so strip_tags() would be more appropriate (it strips HTML as well as PHP); ideally you should validate each input value before accepting it though.
~~~&okay, I'll work on adding strip_tags
~~1) You don't seem to be using the class (which provides a method to generate the form) at all.
~~~&I didn't want to alter his class to make it work in wikka because I knew he was updating it so I used the form code from his class that generates the output to make the graphs, altered it to work in wikka, and had the output passed to his class for processing.
~~~~&But why you would need to alter the class at all? If you need a slightly different method you can override it by subclassing it. (If you want to use classes, learn how to use classes. :))
~~1) You cannot embed a stylesheet in the body of a page.
~~~&That is just what the class statement was doing....ya think I could come up with something like that myself?? Geez, you've got a higher view of my programming skills than is warranted. I removed the bits of the style stuff that seemed to conflict with wikka & tested cross-browser.
~~~~&Never mind if it "works" (it shouldn't and is totally invalid HTML - in every version, but certainly in XHTML). You could accept (after sanitizing) a class attribute to inline it in the table or form tag; then just **extend** the Wikka stylesheet with the the appropriate styles for that class. (Oh, and writing HTML isn't programming. :))
~~~&uh, okay, "coding" then?? :)
~~1) The code for the form isn't XHTML-compliant (form and table code): at the very least all tags and attributes need to be in lowercase, all attribute values must be enclosed in quotes, and all tags must be closed (input, option, br ...)
~~~& okay, I'll work on that....blame the output from Claris Homepage that I use to help me figure out how to generate output. LOL. Thanks for the feedback. -- Mike
~~~~&Well, don't use crappy software that generates an ancient HTML dialect if you need to generate XHTML. ;-)
~~~&Ah, but it's what I own. It's fast, doesn't crash, has "Mike understandble" help files, & is simple enough I "get" what's going on (or even "POST" what's going on these days. I'll work on the conformance issues. :} --mike
~&--JavaWoman
##graph.php##
%%(php)
<?php
// graph.php Version 1 -- Jan 9, 2005
// This script was developed by G. Michael Bowen for a SSHRC research project using wikka wiki.
// Contributions by JavaWoman (I can't close tags worth a darn in php apparently)
// This action generates graph images by calling on the class.graphic.php (renamed)
// REQUIRES class.graphic.php (http://www.phpclasses.org/browse/package/1993.html)
// to be renamed graphmaker.php & placed in a directory called "scripts" in the wikka root.
// REQUIRES GD2 on the server
// Code copyright GMBowen. Released to public domain under GPL. Modify, improve, change as you wish.
echo '<img src="./scripts/graphmaker.php?'.$code.'" /><br />';
?>
%%
**REMEMBER** to place a copy of [[http://www.phpclasses.org/browse/package/1993.html | class.graphic.php]] in a directory in wikka root called ##scripts## after renaming the file to ##graphmaker.php##. (you will probably have to register with phpclasses to access the file...I've not had any negative consequences arise from doing so myself)
~&Why are you renaming the file? The name class.graphic.php properly indicates that it //is// a class. --JavaWoman
~~&So that when people look in their directory they know what the file is for. Nothing bugs me more than having obscurely named files in directories. Carlos' english isn't great (not surprisingly, because it's not his first language....you'll notice I relabeled several things in the form), so I renamed the file so that its purpose was clear...it's a class for graphing, not for graphics (which is what is implied in his file name) and I renamed it to reflect that. I suspect I'll have to provide the file code here anyways once I incorporate a file save component because that's not part of his code release anyways (he added "save" to a version he emailed me when we were chatting about further development & my using it with the wiki). Apart from that, and it may be that I don't really know what a "class" is, but the formatting etc in this, and that it did not just 'plug-in', makes me suspect that this doesn't really strictly conform to what a "class" is anyways (are classes supposed to be standalone programs as well?) & so I took that out of the file name. So, from my view, two reasons for renaming the file to use it....both dealing with clarifying what it was.
~~~&If you'd adopt my proposed directory structure, each file needed by an action would sit in its own subdirectory named after the action (**below** ##script## or ##aux## or ##library## or whetever - we haven't decided that yet!): that way people would immediately know what it's used for. :) The file name does make its purpose clear, and if you put it in a subdirectory named after your action, it's clear what it's used **//by//**. On cursory inspection, the class code looks valid enough to me - but you're not using it **as** a class at all. There's a reason a class file has 'class' in its name - that way you can immediately see it //is// a class. Renaming it hides that important property. And this thing about adding the "save" function and other changes - like I said, you can do that by subclassing it, and leaving the original class untouched. That's the beauty of OO... --JavaWoman
~~&Ah, well the problem with the proposed structure is that I have code other than that which I release here that already uses ##scripts##. Re-working all of it to something other than ##scripts## will be a lot of work for me, and right now it's not possible. I think the structure you propose is actually good. If I had a student programmer working for me that I trusted then I'd have them do the recoding work....but the only ones I can find I don't trust to do that sort of work in the slightest frankly. I spent thousands last year for a spreadsheet script that still isn't working....and I **need** it. Anyhow, enough whinging on my part. I agree with what you say, and will work towards that, ok? (also, it's the "we haven't decided that part yet!" bit that means that I keep producing things to be placed in ##scripts## until final structure is decided on....then re-factoring makes sense). WRT classes & their use....wouldn't surprise me I'm missing the boat on using it properly at all. I'm probably supposed to include_once it somehow?? Is that right (like, to produce the graph on the form page?)? The thing is, I "get" how to "GET", I just learned how to "POST", and those make sense to me & I could get them working. How to do the rest is still coming. And I don't even know what sub-classing means. :) -- GmBowen
~~~& I agree you would not need to refactor until we have decided on a code directory structure.--- As to using classes - many (including class.graph.php) come with examples for how to use them. Try working through such examples first, so you get an idea of **how** to use a class - and do that **outside** the context of Wikka. You could also look at the php.net documentation about PHP's OO capabilities (can't give you the URL now, the site seems to be having a problem beyond the home page at this time). Also, some of Wikka's 3rdparty extensions are classes, too - you could look at how they are used. I think you learn by example, mostly, so that should get you at least //started// on your new learning curve. ;-) --JavaWoman
**FUTURE DEVELOPMENT**: (i) a graph save feature is forthcoming in the next while (using a copy of class.graphic.php altered by its author Carlos Reche for use in wikka). (ii) "if" statements to reduce code output to only parameters which contain data. (iii) I'm considering adding code to save data to a database table so that users may re-load their code to re-factor the graph. This would necessitate another action to list a users graphs. Another, and perhaps simpler, possibility, is a parser so that code can be placed in the textbox and parsed into the data boxes. I think this latter method is almost preferable, but don't have a clue how to implement it. (iv) any alterations others deem necessary. ;)
**Future plans** for class.graphic.php: Carlos Reche has indicated to me in e-mail conversations that he is further developing this code so that it will accept more than two variables (columns of data) and so that it will render true scatterplots as a charting/graphing option.
~&Well, OK, I've written my own version of a "makegraph" action - using the class **as a class**, and providing user-friendly parameter. Maybe that will give you a clue how to integrate a class with Wikka. ;-) The code looks like this (still needs to be documented better): %%(php)<?php
/**
* Make Graph action - using the class Power Graphic by Carlos Reche.
*
* @author {@link http://wikka.jsnx.com/JavaWoman JavaWoman}
*/
// path needs to be browser-accessible (in or below web server's docroot) for img src attribute!
define('CLASS_PATH', 'library/makegraph/class.graphic.php'); # relative path
// init utility variables
$aTypes = array('vertbar','horbar','dots','lines','pie','donut');
$aSkins = array('office','matrix','spring');
// init parameter values
$lTitle = '';
$lX = ''; # legend for X-axis
$lY = ''; # legend for Y-axis
$lG1 = ''; # name of valueset 1
$lG2 = ''; # name of valueset 2
$lType = 'vertbar';
$lSkin = 'office';
$lCredits = 0;
$laX = array();
$laY = array();
$laZ = array();
// one default data point to guard against missing data: this will give us at least some output
$laX[0] = 'thing'; # dummy default
$laY[0] = 1; # dummy default - if a single data point is provided it must have a value != 0
// get parameters
foreach ($vars as $param => $value)
{
switch ($param)
{
case title:
$lTitle = strip_tags($value);
break;
case x;
$lX = strip_tags($value);
break;
case y;
$lY = strip_tags($value);
break;
case g1;
$lG1 = strip_tags($value);
break;
case g2;
$lG2 = strip_tags($value);
break;
case type:
if (in_array($value, $aTypes)) $lType = $value;
break;
case skin;
if (in_array($value, $aSkins)) $lSkin = $value;
break;
case credits:
if ('0' == $value || '1' == $value) $lCredits = $value;
break;
case data:
$items = explode('|',$value);
$count = count($items);
for ($i = 0; $i < $count; $i++)
{
$points = explode(':', $items[$i]);
$laX[$i] = $points[0];
$laY[$i] = $points[1];
$laZ[$i] = $points[2];
}
break;
}
}
// get the class
require_once(CLASS_PATH);
// create an object from the class
$PG =& new PowerGraphic;
$PG->reset_values(); # needed before each graph when there are multiple ones; not really needed in this action
// feed it the (interpreted) action parameters
if ('' != $lTitle) $PG->title = $lTitle;
if ('' != $lX) $PG->axis_x = $lX;
if ('' != $lY) $PG->axis_y = $lY;
if ('' != $lG1) $PG->graphic_1 = $lG1;
if ('' != $lG2) $PG->graphic_2 = $lG2;
$PG->type = array_search($lType, $aTypes) + 1;
$PG->skin = array_search($lSkin, $aSkins) + 1;
$PG->credits = $lCredits;
if (!isset($count)) $count = count($laX);
for ($i = 0; $i < $count; $i++)
{
$PG->x[$i] = $laX[$i];
$PG->y[$i] = $laY[$i];
if (isset($laZ[$i])) $PG->z[$i] = $laZ[$i];
}
// now create the query string, which is what we intended all along ;-)
$query = $PG->create_query_string();
// and finally show image that's built from that query string
echo '<img src="'.CLASS_PATH.'?'.$query.'" />'."\n";
?>%%
~&Now you can use the action like this, for example:
~~""{{makegraph title="The sexes" x="gender" y="number" type="horbar" skin="matrix" g1="2004" g2="2003" data="male:50:51|female:55:53|neuter:2:1" credits="1"}}""
~&or (using more defaults, with the same data):
~~""{{makegraph x="gender" y="number" g1="2004" g2="2003" data="male:50:51|female:55:53|neuter:2:1"}}""
~&--JavaWoman
----
CategoryUserContributions PluginsInDevelopment
Graph maker allows a user to place graphs on a wiki page using (and requiring on the server) GD2. There are three files. (1) ##graphentry.php## is an action that creates a form on a wiki page that allows a user to easily create a wiki action call to embed on a page so that a graph might be placed there. It provides a preview of the graph so that the user does not have to embed the code on a wiki page to see the output. (2) ##graph.php## is the action that reads the generated code and renders the graph on the page. Both previous actions should be placed in the ##actions## directory. (3) Both of these previous reactions rely on [[http://www.phpclasses.org/browse/package/1993.html | class.graphic.php]] which should be renamed "##graphmaker.php##" and placed in a directory called ##scripts## in the wiki root (no other alterations are necessary). http://gmbtst.msvu.ca/wikitest/graphmaker.jpg
##graphentry.php##
%%(php)
<?
// graphmaker.php Version 1 -- Jan 9, 2005
// Original parts of this script were developed by G. Michael Bowen for a SSHRC research project using wikka wiki.
// This action generates graph images & code that can be placed on a wikka page to place the image there.
// REQUIRES graph.php to place the code on a page as an action AND
// REQUIRES class.graphic.php (http://www.phpclasses.org/browse/package/1993.html)
// to be renamed graphmaker.php & placed in a directory called "scripts" in the wikka root.
// REQUIRES GD2 on the server
// Parts of the code on this page derive from class.graphic.php
// Non-derivative code copyright GMBowen. Released to public domain under GPL. Modify, improve, change as you wish.
// NOTE that implementation may require the value "16" to be changed 6 lines down.
// TODO - a save feature is forthcoming, "if" statements to reduce code output to only parameters with data
$site_base = $this->GetConfigValue("base_url");
// The CODE LINE below is assuming that your statement LIKE wakka.php?wakka= in the config file is 16 characters long
// IF IT IS NOT 16 CHARACTERS LONG, then count the characters & replace the 16 in the line below.
$site_base = substr($site_base,0,-16);
$site_base = $site_base."scripts/";
$url = $site_base."graphmaker.php";
$grftitle = $_POST["title"];
$xaxis = $_POST["axis_x"];
$yaxis = $_POST["axis_y"];
$vartitle1 = $_POST["graphic_1"];
$vartitle2 = $_POST["graphic_2"];
$grftype = $_POST["type"];
$grfskin = $_POST["skin"];
$grphc2val = $_POST["graphic_2_values"];
$grfx0 = $_POST["x0"];
$grfy0 = $_POST["y0"];
$grfz0 = $_POST["z0"];
$grfx = array($_POST[x0], $_POST[x1], $_POST[x2], $_POST[x3], $_POST[x4], $_POST[x5], $_POST[x6], $_POST[x7], $_POST[x8], $_POST[x9], $_POST[x10], $_POST[x11], $_POST[x12], $_POST[x13], $_POST[x14], $_POST[x15], $_POST[x16], $_POST[x17], $_POST[x18], $_POST[x19]);
$grfy = array($_POST[y0], $_POST[y1], $_POST[y2], $_POST[y3], $_POST[y4], $_POST[y5], $_POST[y6], $_POST[y7], $_POST[y8], $_POST[y9], $_POST[y10], $_POST[y11], $_POST[y12], $_POST[y13], $_POST[y14], $_POST[y15], $_POST[y16], $_POST[y17], $_POST[y18], $_POST[y19]);
$grfz = array($_POST[z0], $_POST[z1], $_POST[z2], $_POST[z3], $_POST[z4], $_POST[z5], $_POST[z6], $_POST[z7], $_POST[z8], $_POST[z9], $_POST[z10], $_POST[z11], $_POST[z12], $_POST[z13], $_POST[z14], $_POST[z15], $_POST[z16], $_POST[z17], $_POST[z18], $_POST[z19]);
$numrowsx = $_POST["amountofdata"];
if (empty($numrowsx)) {$numrowsx=9;}
$numcolsx = $_POST["numcols"];
if (empty($numcolsx)) {$numcolsx=1;}
if ($numcolsx==1){$_POST[z0]=""; $_POST[z1]=""; $_POST[z2]=""; $_POST[z3]=""; $_POST[z4]=""; $_POST[z5]=""; $_POST[z6]=""; $_POST[z7]=""; $_POST[z8]=""; $_POST[z9]=""; $_POST[z10]=""; $_POST[z11]=""; $_POST[z12]=""; $_POST[z13]=""; $_POST[z14]=""; $_POST[z15]=""; $_POST[z16]=""; $_POST[z17]=""; $_POST[z18]=""; $_POST[z19]="";}
$urlstrng="title=".$grftitle."&axis_x=".$xaxis."&axis_y=".$yaxis."&graphic_1=".$vartitle1."&graphic_2=".$vartitle2
."&type=".$grftype."&skin=".$grfskin."&graphic_2_values=".$grphc2val."&x0=".$_POST[x0]."&x1=".$_POST[x1]."&x2=".$_POST[x2]
."&x3=".$_POST[x3]."&x4=".$_POST[x4]."&x5=".$_POST[x5]."&x6=".$_POST[x6]."&x7=".$_POST[x7]."&x8=".$_POST[x8]."&x9=".$_POST[x9]
."&x10=".$_POST[x10]."&x11=".$_POST[x11]."&x12=".$_POST[x12]."&x13=".$_POST[x13]."&x14=".$_POST[x14]."&x15=".$_POST[x15]
."&x16=".$_POST[x16]."&x17=".$_POST[x17]."&x18=".$_POST[x18]."&x19=".$_POST[x19]."&y0=".$_POST[y0]."&y1=".$_POST[y1]
."&y2=".$_POST[y2]."&y3=".$_POST[y3]."&y4=".$_POST[y4]."&y5=".$_POST[y5]."&y6=".$_POST[y6]."&y7=".$_POST[y7]."&y8=".$_POST[y8]
."&y9=".$_POST[y9]."&y10=".$_POST[y10]."&y11=".$_POST[y11]."&y12=".$_POST[y12]."&y13=".$_POST[y13]."&y14=".$_POST[y14]
."&y15=".$_POST[y15]."&y16=".$_POST[y16]."&y17=".$_POST[y17]."&y18=".$_POST[y18]."&y19=".$_POST[y19]."&z0=".$_POST[z0]
."&z1=".$_POST[z1]."&z2=".$_POST[z2]."&z3=".$_POST[z3]."&z4=".$_POST[z4]."&z5=".$_POST[z5]."&z6=".$_POST[z6]."&z7=".$_POST[z7]
."&z8=".$_POST[z8]."&z9=".$_POST[z9]."&z10=".$_POST[z10]."&z11=".$_POST[z11]."&z12=".$_POST[z12]."&z13=".$_POST[z13]
."&z14=".$_POST[z14]."&z15=".$_POST[z15]."&z16=".$_POST[z16]."&z17=".$_POST[z17]."&z18=".$_POST[z18]."&z19=".$_POST[z19];
?>
<style type="text/css">
<!--
table {
font-size: 14px;
font-family: verdana;
color: #666666;
}
input, select {
font-size: 12px;
font-family: verdana;
color: #333333;
border: 1px solid #aaaaaa;
}
-->
</style>
<script type="text/javascript">
<!--
function numbers() {
key = event.keyCode;
if ((key >= 48 && key <= 57) || key == 46 || key == 13) { return true; } else { return false; }
}
//-->
</script>
<FORM ACTION="" METHOD=POST name="form_data" id="form_data">
<P><TABLE BORDER=0>
<TR>
<TD WIDTH=334>
<P># of data sets: 5
<INPUT TYPE=radio NAME=amountofdata VALUE=4 <? if (($numrowsx==4)||($numrowsx=="")){echo "CHECKED";} ?> style="border-width: 0px;"> 10<INPUT TYPE=radio NAME=amountofdata VALUE=9 <? if ($numrowsx==9) {echo "CHECKED";} ?> style="border-width: 0px;"> 15<INPUT TYPE=radio NAME=amountofdata VALUE=14 <? if ($numrowsx==14) {echo "CHECKED";} ?> style="border-width: 0px;"> 20<INPUT TYPE=radio NAME=amountofdata VALUE=19 <? if ($numrowsx==19) {echo "CHECKED";} ?> style="border-width: 0px;">
<BR># of columns of data: 1 column<INPUT TYPE=radio NAME=numcols VALUE=1 <? if (($numcolsx==1)||($numcolsx=="")) {echo "CHECKED";} ?> style="border-width: 0px;"> 2 columns<INPUT TYPE=radio NAME=numcols VALUE=2 <? if ($numcolsx==2) {echo "CHECKED";} ?> style="border-width: 0px;"></P>
</TD></TR><TR>
<TD>
<P><INPUT TYPE=submit NAME=dataform VALUE="Submit table rules" style="cursor: pointer; margin: 10px 0px 0px 60px;"></P>
</TD>
</TR>
</TABLE>
<br>
<table><tr><td ROWSPAN=2 valign="top">
<div style="margin-bottom: 10px;">
Title: <input type="text" name="title" id="title" size="30" value="<? echo $grftitle; ?>" style="margin: 0px 0px 0px 11px;" />
</div>
Axis X: <input type="text" value="<? echo $xaxis; ?>" name="axis_x" id="axis_x" size="30" /> <br />
Axis Y: <input type="text" value="<? echo $yaxis; ?>" name="axis_y" id="axis_y" size="30" /> <br />
<div style="margin: 10px 0px 10px 0px;">
Variable 1: <input type="text" name="graphic_1" value="<? echo $vartitle1; ?>" id="graphic_1" style="width: 172px;" /> <br />
Variable 2: <input type="text" name="graphic_2" value="<? echo $vartitle2; ?>" id="graphic_2" style="width: 172px;" /> <br />
</div>
Type: Vertical Bars <INPUT TYPE=radio NAME=type VALUE=1 <? if (($grftype==1)||($grftype=="")){echo "CHECKED";} ?> style="border-width: 0px;"> Horizontal Bars<INPUT TYPE=radio NAME=type VALUE=2 <? if ($grftype==2) {echo "CHECKED";} ?> style="border-width: 0px;"><br> Dots<INPUT TYPE=radio NAME=type VALUE=3 <? if ($grftype==3) {echo "CHECKED";} ?> style="border-width: 0px;"> Lines<INPUT TYPE=radio NAME=type VALUE=4 <? if ($grftype==4) {echo "CHECKED";} ?> style="border-width: 0px;"> Pie<INPUT TYPE=radio NAME=type VALUE=5 <? if ($grftype==5) {echo "CHECKED";} ?> style="border-width: 0px;"> Donut<INPUT TYPE=radio NAME=type VALUE=6 <? if ($grftype==6) {echo "CHECKED";} ?> style="border-width: 0px;"><br \>
<!-- <OPTION VALUE=1>Vertical Bars
<OPTION VALUE=2>Horizontal Bars
<OPTION VALUE=3>Dots
<OPTION VALUE=4>Lines
<OPTION VALUE=5>Pie
<OPTION VALUE=6>Donut</select> <br />
-->
Color: Office<INPUT TYPE=radio NAME=skin VALUE=1 <? if (($grfskin==1)||($grfskin=="")) {echo "CHECKED";} ?> style="border-width: 0px;"> Matrix<INPUT TYPE=radio NAME=skin VALUE=2 <? if ($grfskin==2) {echo "CHECKED";} ?> style="border-width: 0px;"> Spring<INPUT TYPE=radio NAME=skin VALUE=3 <? if ($grfskin==3) {echo "CHECKED";} ?> style="border-width: 0px;"><br \>
<!--<select name="skin" id="skin">;
<OPTION VALUE=1>Office
<OPTION VALUE=2>Matrix
<OPTION VALUE=3>Spring
</select>
-->
<? echo '
<div style="margin-top: 20px;" id="parameters">
<span style="margin-left: 30px;">Label</span>
<span style="margin-left: 102px;">Variable 1</span>';
if ($numcolsx==2) {echo '<span id="value_2" style="margin-left: 15px;">Variable 2</span>';}
for ($i = 0; $i <= $numrowsx; $i++) {
echo '<div style="margin-left: 8px;"> '.($i+1).'. <input type="text" value="'.$grfx[$i].'" name="x'.$i.'" id="x'.$i.'" size="20" /> ';
echo ' <input type="text" name="y'.$i.'" id="y'.$i.'" size="10" value="'.$grfy[$i].'" onkeypress="return numbers();" /> ';
if ($numcolsx==2) {echo ' <input type="text" name="z'.$i.'" id="z'.$i.'" size="10" value="'.$grfz[$i].'" onkeypress="return numbers();" />';}
echo '</div>';
}
echo ' <input type="submit" value="Create Graph" style="cursor: pointer; margin: 10px 0px 0px 60px;" /></form>';
?>
</td>
<td valign="top" TD HEIGHT="220">
<?
$urlstrng = trim($urlstrng); // Get rid of space on left/right side if any
$urlstrng = str_replace(' ', ' ', $urlstrng); // Make a double space a single space (just in case)
$urlstrng = str_replace(' ', '+', $urlstrng); // Make a single space a '+'
if(((!empty($_POST[y0]))||(!empty($_POST[z0])))&&(!empty($_POST[x0]))) {print "<img src=".$url."?".$urlstrng.">";}
?>
</td>
</tr>
<TR><TD valign="top">
<? if(((!empty($_POST[y0]))||(!empty($_POST[z0])))&&(!empty($_POST[x0]))) {$enterstrng="{{graph code=\"".$urlstrng."\"}}";} else {$urlstrng="";}
?>
<TEXTAREA NAME=codeoutput ROWS=11 COLS=65 WRAP=virtual value="5"><? echo $enterstrng; ?> </TEXTAREA><br>
Copy & paste this code into your wiki page to have this graph appear on that page.<br>
</TD
</tr></table>
%%
~&Four problems are immediately apparent here:
~~1) All input seems to be accepted without any kind of validation (security risk!)
~~~& would running $urlstrng through safehtml solve that? Everything is concatenated into that statement essentially.
~~~~&Well, you certanly don't want **any** HTML in there (not just "safe" HTML) so strip_tags() would be more appropriate (it strips HTML as well as PHP); ideally you should validate each input value before accepting it though.
~~~&okay, I'll work on adding strip_tags
~~1) You don't seem to be using the class (which provides a method to generate the form) at all.
~~~&I didn't want to alter his class to make it work in wikka because I knew he was updating it so I used the form code from his class that generates the output to make the graphs, altered it to work in wikka, and had the output passed to his class for processing.
~~~~&But why you would need to alter the class at all? If you need a slightly different method you can override it by subclassing it. (If you want to use classes, learn how to use classes. :))
~~1) You cannot embed a stylesheet in the body of a page.
~~~&That is just what the class statement was doing....ya think I could come up with something like that myself?? Geez, you've got a higher view of my programming skills than is warranted. I removed the bits of the style stuff that seemed to conflict with wikka & tested cross-browser.
~~~~&Never mind if it "works" (it shouldn't and is totally invalid HTML - in every version, but certainly in XHTML). You could accept (after sanitizing) a class attribute to inline it in the table or form tag; then just **extend** the Wikka stylesheet with the the appropriate styles for that class. (Oh, and writing HTML isn't programming. :))
~~~&uh, okay, "coding" then?? :)
~~1) The code for the form isn't XHTML-compliant (form and table code): at the very least all tags and attributes need to be in lowercase, all attribute values must be enclosed in quotes, and all tags must be closed (input, option, br ...)
~~~& okay, I'll work on that....blame the output from Claris Homepage that I use to help me figure out how to generate output. LOL. Thanks for the feedback. -- Mike
~~~~&Well, don't use crappy software that generates an ancient HTML dialect if you need to generate XHTML. ;-)
~~~&Ah, but it's what I own. It's fast, doesn't crash, has "Mike understandble" help files, & is simple enough I "get" what's going on (or even "POST" what's going on these days. I'll work on the conformance issues. :} --mike
~&--JavaWoman
##graph.php##
%%(php)
<?php
// graph.php Version 1 -- Jan 9, 2005
// This script was developed by G. Michael Bowen for a SSHRC research project using wikka wiki.
// Contributions by JavaWoman (I can't close tags worth a darn in php apparently)
// This action generates graph images by calling on the class.graphic.php (renamed)
// REQUIRES class.graphic.php (http://www.phpclasses.org/browse/package/1993.html)
// to be renamed graphmaker.php & placed in a directory called "scripts" in the wikka root.
// REQUIRES GD2 on the server
// Code copyright GMBowen. Released to public domain under GPL. Modify, improve, change as you wish.
echo '<img src="./scripts/graphmaker.php?'.$code.'" /><br />';
?>
%%
**REMEMBER** to place a copy of [[http://www.phpclasses.org/browse/package/1993.html | class.graphic.php]] in a directory in wikka root called ##scripts## after renaming the file to ##graphmaker.php##. (you will probably have to register with phpclasses to access the file...I've not had any negative consequences arise from doing so myself)
~&Why are you renaming the file? The name class.graphic.php properly indicates that it //is// a class. --JavaWoman
~~&So that when people look in their directory they know what the file is for. Nothing bugs me more than having obscurely named files in directories. Carlos' english isn't great (not surprisingly, because it's not his first language....you'll notice I relabeled several things in the form), so I renamed the file so that its purpose was clear...it's a class for graphing, not for graphics (which is what is implied in his file name) and I renamed it to reflect that. I suspect I'll have to provide the file code here anyways once I incorporate a file save component because that's not part of his code release anyways (he added "save" to a version he emailed me when we were chatting about further development & my using it with the wiki). Apart from that, and it may be that I don't really know what a "class" is, but the formatting etc in this, and that it did not just 'plug-in', makes me suspect that this doesn't really strictly conform to what a "class" is anyways (are classes supposed to be standalone programs as well?) & so I took that out of the file name. So, from my view, two reasons for renaming the file to use it....both dealing with clarifying what it was.
~~~&If you'd adopt my proposed directory structure, each file needed by an action would sit in its own subdirectory named after the action (**below** ##script## or ##aux## or ##library## or whetever - we haven't decided that yet!): that way people would immediately know what it's used for. :) The file name does make its purpose clear, and if you put it in a subdirectory named after your action, it's clear what it's used **//by//**. On cursory inspection, the class code looks valid enough to me - but you're not using it **as** a class at all. There's a reason a class file has 'class' in its name - that way you can immediately see it //is// a class. Renaming it hides that important property. And this thing about adding the "save" function and other changes - like I said, you can do that by subclassing it, and leaving the original class untouched. That's the beauty of OO... --JavaWoman
~~&Ah, well the problem with the proposed structure is that I have code other than that which I release here that already uses ##scripts##. Re-working all of it to something other than ##scripts## will be a lot of work for me, and right now it's not possible. I think the structure you propose is actually good. If I had a student programmer working for me that I trusted then I'd have them do the recoding work....but the only ones I can find I don't trust to do that sort of work in the slightest frankly. I spent thousands last year for a spreadsheet script that still isn't working....and I **need** it. Anyhow, enough whinging on my part. I agree with what you say, and will work towards that, ok? (also, it's the "we haven't decided that part yet!" bit that means that I keep producing things to be placed in ##scripts## until final structure is decided on....then re-factoring makes sense). WRT classes & their use....wouldn't surprise me I'm missing the boat on using it properly at all. I'm probably supposed to include_once it somehow?? Is that right (like, to produce the graph on the form page?)? The thing is, I "get" how to "GET", I just learned how to "POST", and those make sense to me & I could get them working. How to do the rest is still coming. And I don't even know what sub-classing means. :) -- GmBowen
~~~& I agree you would not need to refactor until we have decided on a code directory structure.--- As to using classes - many (including class.graph.php) come with examples for how to use them. Try working through such examples first, so you get an idea of **how** to use a class - and do that **outside** the context of Wikka. You could also look at the php.net documentation about PHP's OO capabilities (can't give you the URL now, the site seems to be having a problem beyond the home page at this time). Also, some of Wikka's 3rdparty extensions are classes, too - you could look at how they are used. I think you learn by example, mostly, so that should get you at least //started// on your new learning curve. ;-) --JavaWoman
**FUTURE DEVELOPMENT**: (i) a graph save feature is forthcoming in the next while (using a copy of class.graphic.php altered by its author Carlos Reche for use in wikka). (ii) "if" statements to reduce code output to only parameters which contain data. (iii) I'm considering adding code to save data to a database table so that users may re-load their code to re-factor the graph. This would necessitate another action to list a users graphs. Another, and perhaps simpler, possibility, is a parser so that code can be placed in the textbox and parsed into the data boxes. I think this latter method is almost preferable, but don't have a clue how to implement it. (iv) any alterations others deem necessary. ;)
**Future plans** for class.graphic.php: Carlos Reche has indicated to me in e-mail conversations that he is further developing this code so that it will accept more than two variables (columns of data) and so that it will render true scatterplots as a charting/graphing option.
~&Well, OK, I've written my own version of a "makegraph" action - using the class **as a class**, and providing user-friendly parameter. Maybe that will give you a clue how to integrate a class with Wikka. ;-) The code looks like this (still needs to be documented better): %%(php)<?php
/**
* Make Graph action - using the class Power Graphic by Carlos Reche.
*
* @author {@link http://wikka.jsnx.com/JavaWoman JavaWoman}
*/
// path needs to be browser-accessible (in or below web server's docroot) for img src attribute!
define('CLASS_PATH', 'library/makegraph/class.graphic.php'); # relative path
// init utility variables
$aTypes = array('vertbar','horbar','dots','lines','pie','donut');
$aSkins = array('office','matrix','spring');
// init parameter values
$lTitle = '';
$lX = ''; # legend for X-axis
$lY = ''; # legend for Y-axis
$lG1 = ''; # name of valueset 1
$lG2 = ''; # name of valueset 2
$lType = 'vertbar';
$lSkin = 'office';
$lCredits = 0;
$laX = array();
$laY = array();
$laZ = array();
// one default data point to guard against missing data: this will give us at least some output
$laX[0] = 'thing'; # dummy default
$laY[0] = 1; # dummy default - if a single data point is provided it must have a value != 0
// get parameters
foreach ($vars as $param => $value)
{
switch ($param)
{
case title:
$lTitle = strip_tags($value);
break;
case x;
$lX = strip_tags($value);
break;
case y;
$lY = strip_tags($value);
break;
case g1;
$lG1 = strip_tags($value);
break;
case g2;
$lG2 = strip_tags($value);
break;
case type:
if (in_array($value, $aTypes)) $lType = $value;
break;
case skin;
if (in_array($value, $aSkins)) $lSkin = $value;
break;
case credits:
if ('0' == $value || '1' == $value) $lCredits = $value;
break;
case data:
$items = explode('|',$value);
$count = count($items);
for ($i = 0; $i < $count; $i++)
{
$points = explode(':', $items[$i]);
$laX[$i] = $points[0];
$laY[$i] = $points[1];
$laZ[$i] = $points[2];
}
break;
}
}
// get the class
require_once(CLASS_PATH);
// create an object from the class
$PG =& new PowerGraphic;
$PG->reset_values(); # needed before each graph when there are multiple ones; not really needed in this action
// feed it the (interpreted) action parameters
if ('' != $lTitle) $PG->title = $lTitle;
if ('' != $lX) $PG->axis_x = $lX;
if ('' != $lY) $PG->axis_y = $lY;
if ('' != $lG1) $PG->graphic_1 = $lG1;
if ('' != $lG2) $PG->graphic_2 = $lG2;
$PG->type = array_search($lType, $aTypes) + 1;
$PG->skin = array_search($lSkin, $aSkins) + 1;
$PG->credits = $lCredits;
if (!isset($count)) $count = count($laX);
for ($i = 0; $i < $count; $i++)
{
$PG->x[$i] = $laX[$i];
$PG->y[$i] = $laY[$i];
if (isset($laZ[$i])) $PG->z[$i] = $laZ[$i];
}
// now create the query string, which is what we intended all along ;-)
$query = $PG->create_query_string();
// and finally show image that's built from that query string
echo '<img src="'.CLASS_PATH.'?'.$query.'" />'."\n";
?>%%
~&Now you can use the action like this, for example:
~~""{{makegraph title="The sexes" x="gender" y="number" type="horbar" skin="matrix" g1="2004" g2="2003" data="male:50:51|female:55:53|neuter:2:1" credits="1"}}""
~&or (using more defaults, with the same data):
~~""{{makegraph x="gender" y="number" g1="2004" g2="2003" data="male:50:51|female:55:53|neuter:2:1"}}""
~&--JavaWoman
----
CategoryUserContributions PluginsInDevelopment