Revision history for AdvancedFormOpen


Revision [18710]

Last edited on 2008-01-28 00:12:31 by CyneBeald [Modified links pointing to docs server]
Additions:
>>The [[Docs:WikkaCore Wikka core]] has a ##""FormOpen()""## method that creates the opening tag for a form. However, it has a number of limitations, such as no way to specify an ##id## and/or ##class## attribute, and not supporting ##enctype## needed for a file upload form. This leads to ugly workarounds and inconsistent (and sometimes invalid) code.
~-The [[Docs:EmailpasswordActionInfo email password]] action can now use ##""FormOpen()""## (and ##""FormClose()""##) to generate a valid form (it isn't valid now!).
There is one exception: the [[Docs:GoogleFormActionInfo google form]] action needs a form with an **external** action URI; that isn't handled by ##""FormOpen()""## which generates forms only for the installed Wikka system. I think we can live with that. :)
Deletions:
>>The [[WikkaCore Wikka core]] has a ##""FormOpen()""## method that creates the opening tag for a form. However, it has a number of limitations, such as no way to specify an ##id## and/or ##class## attribute, and not supporting ##enctype## needed for a file upload form. This leads to ugly workarounds and inconsistent (and sometimes invalid) code.
~-The [[EmailpasswordActionInfo email password]] action can now use ##""FormOpen()""## (and ##""FormClose()""##) to generate a valid form (it isn't valid now!).
There is one exception: the [[GoogleFormActionInfo google form]] action needs a form with an **external** action URI; that isn't handled by ##""FormOpen()""## which generates forms only for the installed Wikka system. I think we can live with that. :)


Revision [12352]

Edited on 2005-12-20 16:08:43 by CyneBeald [Suggestion about JS-validated forms]
Additions:
~& How about supporting JS form validation using the onSubmit event? I know you still have to do it server side, but sometimes it's just a bit more convenient for the user if he knows right away that something is wrong. On my site, I require the edit note field to be filled in, and I wrote a quick hack for it because I didn't want to hardcode the form into edit.php. It's based on the //old// ""FormOpen"" method (and I didn't give too much thought to the directories), but the basic idea is like this:
~&%%(php)
function ValidFormOpen($method = "", $tag = "", $formMethod = "post")
{
$result = "<form action=\"".$this->Href($method, $tag)."\" method=\"".$formMethod."\" onSubmit=\"return isFormValid(this);\">\n";
$result .= '<script language="JavaScript" src="scripts/'.$method.'formvalid.js"></script>'."\n";
if (!$this->config["rewrite_mode"]) $result .= "<input type=\"hidden\" name=\"wakka\" value=\"".$this->MiniHref($method, $tag)."\" />\n";
~&--CyneBeald


Revision [10119]

Edited on 2005-07-18 16:38:57 by JavaWoman [minor]
Additions:
$attrMethod = ' method="post"'; # required for file upload
Deletions:
$attrMethod = ' method="post"'; # required for file upload


Revision [10115]

Edited on 2005-07-18 13:59:00 by JavaWoman [fix code (lowercase needed!!)]
Additions:
* always converted to lowercase
function FormOpen($method='',$tag='',$formMethod='post',$id='',$class='',$file=FALSE)
$attrMethod = ' method="post"'; # required for file upload
$attrMethod = ' method="post"'; # ...but generate lowercase
Deletions:
* always converted to uppercase
function FormOpen($method='',$tag='',$formMethod='POST',$id='',$class='',$file=FALSE)
$attrMethod = ' method="POST"'; # required for file upload
$attrMethod = ' method="POST"'; # ...but generate lowercase


Revision [9961]

Edited on 2005-07-14 16:53:34 by JavaWoman [minor fix to code: form method should always be uppercase]
Additions:
* always converted to uppercase
function FormOpen($method='',$tag='',$formMethod='POST',$id='',$class='',$file=FALSE)
$attrMethod = ' method="POST"'; # required for file upload
$attrMethod = ' method="POST"'; # ...but generate lowercase
Deletions:
* always converted to lowercase
function FormOpen($method='',$tag='',$formMethod='post',$id='',$class='',$file=FALSE)
$attrMethod = ' method="post"'; # required for file upload
$attrMethod = ' method="post"'; # ...but generate lowercase


Revision [9152]

Edited on 2005-06-12 17:48:28 by JavaWoman [ref to beta features]
Additions:
//Installed as a [[WikkaBetaFeatures beta feature]] on this server as of 2005-06-12.//


Revision [9140]

Edited on 2005-06-12 08:37:01 by JavaWoman [lowercase method value]
Additions:
The folowing code should replace the ##""FormOpen()""## method in ##wikka.php## (at line 694 in the 1.1.6.0. release version
~-''modified to make use of the new ##[[GenerateUniqueId makeId()]]## method''
~-''modified to use lowercase form method values (required by XHTML)''
* anything but POST is ignored and considered as GET;
* always converted to lowercase
function FormOpen($method='',$tag='',$formMethod='post',$id='',$class='',$file=FALSE)
$attrMethod = ''; # no method for HTML default 'get'
// form method (ignore anything but post) and enctype
$attrMethod = ' method="post"'; # required for file upload
elseif (preg_match('/^post$/i',$formMethod)) # ignore case...
$attrMethod = ' method="post"'; # ...but generate lowercase
Deletions:
The folowing code should replace the ##""FormOpen()""## method in ##wikka.php## (at line 694 in the 1.1.6.0. release version (''modified to make use of the new ##[[GenerateUniqueId makeId()]]## method''):
* anything but POST is ignored and considered as GET
function FormOpen($method='',$tag='',$formMethod='POST',$id='',$class='',$file=FALSE)
$attrMethod = ''; # no method for HTML default GET
// form method (ignore anything but POST) and enctype
$attrMethod = ' method="POST"'; # required for file upload
elseif (preg_match('/^POST$/i',$formMethod)) # ignore case...
$attrMethod = ' method="POST"'; # ...but generate uppercase


Revision [8562]

Edited on 2005-05-28 10:50:24 by JavaWoman [move to subcategory]
Additions:
CategoryDevelopmentCore
Deletions:
CategoryDevelopment


Revision [8379]

Edited on 2005-05-22 11:16:07 by JavaWoman [minor]
Additions:
Since this constant is also required for the ##""makeId()""## method, code and instructions for that can also be found on the [[GenerateUniqueId makeId() development page]] - see the **Supporting code** section.
Deletions:
Since this constant is also required for the ##""makeId()""## method, code and instructions for that can also be found on the [[GenerateUniqueId makeId() development page]].


Revision [8370]

Edited on 2005-05-22 09:22:28 by JavaWoman [FormOpen() adapted to make use of makeId()]
Additions:
~-GenerateUniqueId
The folowing code should replace the ##""FormOpen()""## method in ##wikka.php## (at line 694 in the 1.1.6.0. release version (''modified to make use of the new ##[[GenerateUniqueId makeId()]]## method''):
* @uses makeId()
if ('' == $id) # if no id given, generate one based on other parameters
$id = substr(md5($method.$tag.$formMethod.$class),0,ID_LENGTH);
$attrId = ' id="'.$this->makeId('form',$id).'"'; # make sure we have a unique id
As can be seen (and is documented in the docblock) the new ##""FormOpen()""## method uses two methods and a constant that don't exist yet in ##wikka.php##.
===New ##""makeId()""## method===
This method takes care of generating a valid and unique id. Since this has much wider application than just for ##""FormOpen()""##, this has its own [[GenerateUniqueId development page]]. See GenerateUniqueId for code and installation instructions.
Since this constant is also required for the ##""makeId()""## method, code and instructions for that can also be found on the [[GenerateUniqueId makeId() development page]].
Insert in the ##""//MISC""## section of ##wikka.php##, right after the new ##""makeId()""## method:
~-The ##referrers.php## and ##review_blacklist.php## files files will no longer need to surround the filter form with a ##div## just to be able to define an id for a styling 'hook': the (same) id can now be defined on the form itself. See AdvancedReferrersHandler.
Deletions:
The folowing code should replace the ##""FormOpen()""## method in ##wikka.php## (at line 694 in the 1.1.6.0. release version:
static $seq = 1;
static $aIds = array();
$validId = preg_match('/^[A-Za-z][A-Za-z0-9_:.-]*$/',$id); # http://www.w3.org/TR/html4/types.html#type-id
if ('' == $id || !$validId || in_array($id,$aIds)) # ignore specified id if it is invalid or exists already
$id = substr(md5($method.$tag.$formMethod.$class),0,ID_LENGTH); # @@@ maybe make length configurable
if (in_array($id,$aIds))
$id .= '_'.++$seq; # add suffiX to make ID unique
$attrId = ' id="form_'.$id.'"';
else
$attrId = ' id="form_'.$id.'"';
$aIds[] = $id; # keep track of both specified and generated ids!
As can be seen (and is documented in the docblock) the new ##""FormOpen()""## method uses a method and a constant that don't exist yet in ##wikka.php##.
To avoid excessively long id strings when an id is generated, we take a substring of a shorter length; this length is set via ##ID_LENGTH##. Alternatively, it could be made a configurable value via the configuration file - but for now I've just chosen a reasonable length.
To define it, find this in ##wikka.php##:
define("WAKKA_VERSION", "1.1.6.0");
%% ---
and insert the following code right after that line:
// other constants
/**
* Length to use for generated part of id attribute.
*/
define('ID_LENGTH',10); # @@@ maybe make configurable
Insert in the ##""//MISC""## section of ##wikka.php##, right after the ##""ReturnSafeHTML()""## method:
~-The ##referrers.php## and ##review_blacklist.php## files files will no longer need to surround the filter form with a ##div## just to be able to define an id for a styling 'hook': the (same) id can now be ddefined on the form itself. See AdvancedReferrersHandler.


Revision [8367]

Edited on 2005-05-22 08:32:14 by JavaWoman [added 'see also' link]
Additions:
~-WikkaCore


Revision [8359]

Edited on 2005-05-20 23:23:19 by JavaWoman [fixing small typo in existsHandler()]
Additions:
if (file_exists($dirpath.DIRSEP.$handler.'.php'))
Deletions:
if (file_exists($dirpath.DIRSEP.$handler.'php'))


Revision [8316]

Edited on 2005-05-19 06:12:04 by JavaWoman [category added]
Additions:
----
CategoryDevelopment


Revision [8296]

Edited on 2005-05-18 16:03:27 by JavaWoman [improved _initSystem() using PHP constants]
Additions:
define('PATHSEP', PATH_SEPARATOR); # system-dependent include path separator
define('DIRSEP', DIRECTORY_SEPARATOR); # system-dependent directory separator
As it is, this now only uses PHP built-in constants (providing a more convenient short form); it may later be extended with other platform-dependent values and maybe functions.
Deletions:
define('PATHSEP', (substr(php_uname('s'), 0, 7) == 'Windows') ? ';' : ':'); # system-dependent include path separator
define('DIRSEP', (substr(php_uname('s'), 0, 7) == 'Windows') ? '\\' : '/');# system-dependent directory separator


Revision [8293]

Edited on 2005-05-18 14:25:10 by JavaWoman [minor]
Additions:
>>The [[WikkaCore Wikka core]] has a ##""FormOpen()""## method that creates the opening tag for a form. However, it has a number of limitations, such as no way to specify an ##id## and/or ##class## attribute, and not supporting ##enctype## needed for a file upload form. This leads to ugly workarounds and inconsistent (and sometimes invalid) code.
The following replacement for ##""FormOpen()""## addresses these issues and makes sure the generated code is valid XHTML. It uses a number of new supporting methods that will be more generally useful as well.
Deletions:
>>The [[WikkaCore Wikka core]] has a ##""FormOpen()""## method that creates the opening tag for a form. However it has a number of limitations, such as no way to specify an ##id## and/or ##class## attribute, and not supporting ##enctype## needed for a file upload form. This leads to ugly workarounds and inconsitent (and conceivably invalid) code.
The following replacement for ##""FormOpen()""## addresses these issues and makes sure the generated code is valid XHTML. It uses a number of new supporting methods that may have more general usefulness as well.


Revision [8289]

Edited on 2005-05-18 13:36:15 by JavaWoman [summing up advantages, links to referenced pages]
Additions:
~-Mod015fFilesAction
~-EmailpasswordActionInfo
~-GrabCodeHandler
This new ##""FormOpen()""## method will solve a number of existing problems in current released and beta code.
==Released (1.1.6.0)==
~-The [[Mod015fFilesAction {{files}}]] action no longer needs to construct its file upload form itself: ##""FormOpen()""## now generates the necessary ##enctype## if told it's to be used for file upload.
~-The [[EmailpasswordActionInfo email password]] action can now use ##""FormOpen()""## (and ##""FormClose()""##) to generate a valid form (it isn't valid now!).
==Beta features==
~-The ##referrers.php## and ##review_blacklist.php## files files will no longer need to surround the filter form with a ##div## just to be able to define an id for a styling 'hook': the (same) id can now be ddefined on the form itself. See AdvancedReferrersHandler.
~-The [[GrabCodeHandler Grab code]] handler can now specify a class for the "grab" button form so they can be properly styled (and avoid the current inline styling that can't do it all without being able to style the form itself).
==User contributions==
And, of course, user-contributed extensions can use the new method to easily generate valid and consistent forms that can be easily styled.
==Exception==
There is one exception: the [[GoogleFormActionInfo google form]] action needs a form with an **external** action URI; that isn't handled by ##""FormOpen()""## which generates forms only for the installed Wikka system. I think we can live with that. :)
====Tests? Comments?====
Tests (even harsh tests) and comments are very welcome.
--JavaWoman
Deletions:
//later//


Revision [8282]

Edited on 2005-05-18 12:54:46 by JavaWoman [adding supporting code]
Additions:
====New ##""FormOpen()""## method====
====Supporting methods and other code====
As can be seen (and is documented in the docblock) the new ##""FormOpen()""## method uses a method and a constant that don't exist yet in ##wikka.php##.
===##ID_LENGTH## constant===
To avoid excessively long id strings when an id is generated, we take a substring of a shorter length; this length is set via ##ID_LENGTH##. Alternatively, it could be made a configurable value via the configuration file - but for now I've just chosen a reasonable length.
To define it, find this in ##wikka.php##:
%%(php)
define("WAKKA_VERSION", "1.1.6.0");
%% ---
and insert the following code right after that line:
%%(php)
// other constants
/**
* Length to use for generated part of id attribute.
*/
define('ID_LENGTH',10); # @@@ maybe make configurable
===New ##""existsHandler()""## method===
This method parallels the ##""existsPage()""## method: it checks whether a handler actually exists. It takes as input what in Wikka is called ##$method## which can be a handler name followed by an (optional) query string; the method chops off the query string and goes looking in the configured handlers path whether a handler file by the specified name exists.
Insert in the ##""//MISC""## section of ##wikka.php##, right after the ##""ReturnSafeHTML()""## method:
%%(php)
* Check if a handler (specified after page name) really exists.
* May be passed as handler plus query string; we'll need to look at handler only
* so we strip off any querystring first.
* @uses _recurseDirs()
* @uses DIRSEP
* @param string $method "method" which starts with name of handler to check existence of
* @return boolean TRUE if handler is found, FALSE otherwise
function existsHandler($method)
$exists = FALSE;
// initialize class constants
if (!defined('DIRSEP'))
$this->_initSystem();
// first strip off any query string
$parts = preg_split('/&/',$method,1); # return only one part
$handler = $parts[0];
// then check if rest corresponds to a file in the /handlers tree
$handlersdirtree = $this->_recurseDirs(realpath($this->config['handler_path']));
foreach ($handlersdirtree as $dirpath)
if (file_exists($dirpath.DIRSEP.$handler.'php'))
$exists = TRUE;
break;
return $exists;
==More supporting code==
This public method in turn uses two new private methods, ##""_recurseDirs()""## and ##""_initSystem()""##.
===New ##""_recurseDirs()""## method===
This recursive utility method will build a list of directory paths starting with a given (relative) directory name. This is handy for finding a file with a particular name when it is not known which subdirectory it might live in. We use it now to check for the existence of a handler by a particular name //without// assuming it is a "page" handler and must be in the ##page## subdirectory (!) but it could also be used for a "smart include" where we store bits of include code categorized in a directory tree without needing to specify a full path for the include or endlessly extending the PHP include path.
Insert right after the ##""existsHandler()""## method:
%%(php)
* Build a list of all subdirectories off a specified base directory (including that "base").
* The algorithm uses a recursive "depth first" algorithm to find all subdirectories.
* @copyright Copyright © 2004, Marjolein Katsma
* @access private
* @uses _recurseDirs() (recursion!)
* @uses DIRSEP
* @todo maybe allow a "breadth first" algorithm as well
* @param string $dir required: absolute path of the directory to search
* @return array list of directories found (including the base directory as first element)
function _recurseDirs($dir)
// array to gather names while recursing
static $aDirList = array(); # static: gather results through recursion
$aDirList[] = $dir; # add current dir to list
$dh = opendir($dir);
while (FALSE !== ($thing = readdir($dh)))
$next = $dir.DIRSEP.$thing;
if (is_dir($next) && '.' != $thing && '..' != $thing)
$this->_recurseDirs($next); # ignore return value here
closedir($dh);
// return result
return $aDirList; # only return final result
==Naming==
Note that because this is a private method, I've chosen to use a _ prefix for the method name - a common convention for naming private methods.
===New ##""_initSystem()""## method===
Some things PHP may need to work with are actually platform-dependent. We'd like to treat them as "constants" but they need to be derived once. The ##""_initSystem()""## method handles this: by checking whether one of the constants is defined already we can call this method only when needed.
Insert right after the ##""_recurseDirs()""## method:
%%(php)
* Initialize constants (pseudo class constants).
* Constants for system-dependent values like separators are derived here.
* @access private
function _initSystem()
define('PATHSEP', (substr(php_uname('s'), 0, 7) == 'Windows') ? ';' : ':'); # system-dependent include path separator
define('DIRSEP', (substr(php_uname('s'), 0, 7) == 'Windows') ? '\\' : '/');# system-dependent directory separator
==Naming==
As with ##""_recurseDirs()""##, the _ prefix signals a private method.
====Advantages====
//later//
Deletions:
===New ##""FormOpen()""## method===


Revision [8279]

Edited on 2005-05-18 11:38:47 by JavaWoman [first chunk of code]
Additions:
/**
* Build an opening form tag with specified or generated attributes.
*
* This method builds an opening form tag, taking care that the result is valid XHTML
* no matter where the parameters come from: invalid parameters are ignored and defaults used.
* This enables this method to be used with user-provided parameter values.
*
* The form will always have the required action attribute and an id attribute to provide
* a 'hook' for styling and scripting. This method tries its best to ensure the id attribute
* is unique, among other things by adding a 'form_' prefix to make it different from ids for
* other elements.
* For a file upload form ($file=TRUE) the appropriate method and enctype attributes are generated.
*
* When rewriting is not active, a hidden field is attached as well to pass on the page name.
* NOTE: is this really needed??
*
* @author {@link http://wikka.jsnx.com/JavaWoman JavaWoman}
* @copyright Copyright © 2005, Marjolein Katsma
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*
* @access public
* @uses existsHandler()
* @uses existsPage()
* @uses Href()
* @uses MiniHref()
* @uses ID_LENGTH
*
* @param string $method optional: "method" which consists of handler and possibly a query string
* to be used as part of action attribute
* @param string $tag optional: page name to be used for action attribute;
* if not specified, the current page will be used
* @param string $formMethod optional: method attribute; must be POST (default) or GET;
* anything but POST is ignored and considered as GET
* @param string $id optional: id attribute
* @param string $class optional: class attribute
* @param boolean $file optional: specifies whether there will be a file upload field;
* default: FALSE; if TRUE sets method attribute to POST and generates
* appropriate enctype attribute
* @return string opening form tag and hidden input field when not rewriting.
*/
function FormOpen($method='',$tag='',$formMethod='POST',$id='',$class='',$file=FALSE)
{
// initializations
static $seq = 1;
static $aIds = array();
$attrMethod = ''; # no method for HTML default GET
$attrClass = '';
$attrEnctype = ''; # default no enctype -> HTML default application/x-www-form-urlencoded
$hiddenval = '';
// validations
$validMethod = $this->existsHandler($method);
$validPage = $this->existsPage($tag);
$validId = preg_match('/^[A-Za-z][A-Za-z0-9_:.-]*$/',$id); # http://www.w3.org/TR/html4/types.html#type-id
// derivations (MiniHref supplies current page name if none specified)
$page = ($validPage) ? $tag : '';
$method = ($validMethod) ? $method : '';
// form action (action is a required attribute!)
$attrAction = ' action="'.$this->Href($method, $page).'"';
// form method (ignore anything but POST) and enctype
if (TRUE === $file)
{
$attrMethod = ' method="POST"'; # required for file upload
$attrEnctype = ' enctype="multipart/form-data"'; # required for file upload
}
elseif (preg_match('/^POST$/i',$formMethod)) # ignore case...
{
$attrMethod = ' method="POST"'; # ...but generate uppercase
}
// form id
if ('' == $id || !$validId || in_array($id,$aIds)) # ignore specified id if it is invalid or exists already
{
$id = substr(md5($method.$tag.$formMethod.$class),0,ID_LENGTH); # @@@ maybe make length configurable
if (in_array($id,$aIds))
{
$id .= '_'.++$seq; # add suffiX to make ID unique
}
$attrId = ' id="form_'.$id.'"';
}
else
{
$attrId = ' id="form_'.$id.'"';
}
$aIds[] = $id; # keep track of both specified and generated ids!
// form class
if ('' != $class)
{
$attrClass = ' class="'.$class.'"';
}
// build HTML fragment
$result = '<form'.$attrAction.$attrMethod.$attrEnctype.$attrId.$attrClass.'>'."\n";
if (!$this->config['rewrite_mode']) # @@@ is this bit really necessary?
{
$hiddenval = $this->MiniHref($method, $page);
$result .= '<fieldset class="hidden"><input type="hidden" name="wakka" value="'.$hiddenval.'" /></fieldset>'."\n";
}
return $result;
}
Deletions:
//later//


Revision [8278]

The oldest known version of this page was created on 2005-05-18 11:11:45 by JavaWoman [first chunk of code]
Valid XHTML :: Valid CSS: :: Powered by WikkaWiki