NOTE: This is a work in progress, so please don’t expect to find all your answers here yet.

Day 1:

We needed an open-source, server-delivered HTML form builder for non-programmers.  Not so easy to find!  The ones we narrowed it down to have a terrible (non-intuitive) interface: CKEditor and phpFormGenerator.  So I set out to build one in Flash.

This is an interesting project in MXML.  It involves reading a pre-defined XML that references HTML form elements and outputting a GUI.  The GUI then will allow the user to drag-drop form elements into place to create an HTML form.  The form (currently, at least) will be delivered and managed via PHP, and stored in (again, currently) a generated PHP file.  Accessing this file will be from a external websites, so Apache will be used to control who gets to see and use the form.  There will have to be a separate login system for form editing.

I chose to use the php-form-builder-class project.  It’s license is what we need, and all the major interesting form elements are there – including rudimentary jQuery support, which I may need eventually.  The problem is that the documentation is terrible, and there is no overall list of attributes/parameters available.  I added the ability to output XML from an array using this code which uses SimpleXML (there are othersand – and , and they do seem better, but I only needed a framework).  Here is the PHP conversion code, but note that it is now very specialized (and I left all of my experiments in it, too, so ignore some of the code):

class ArrayToXML
{
    /**
     * The main function for converting to an XML document.
     * Pass in a multi dimensional array and this recrusively loops through and builds up an XML document.
     *
     * @param array $data
     * @param string $rootNodeName - what you want the root node to be - defaultsto data.
     * @param SimpleXMLElement $xml - should only be used recursively
     * @return string XML
     */

    //public static $test = "test";

    public static function toXml($data, $prefix = true, $rootNodeName = 'data', $xml=null, $indent=0)
    {
        // turn off compatibility mode as simple xml throws a wobbly if you don't.
        if (ini_get('zend.ze1_compatibility_mode') == 1)
        {
            ini_set ('zend.ze1_compatibility_mode', 0);
        }

        echo (string)$xml;

        if ($xml == null)
        {
            $xml = simplexml_load_string("<?xml version='1.0' encoding='utf-8'?><$rootNodeName />");
        }

        // loop through the data passed in.
        foreach($data as $key => $value)
        {
            //echo "$key\n";

            // replace anything not alpha numeric
            $key = preg_replace('/[^a-z0-9]/i', '', $key);

            // no numeric keys in our xml please!
            //if (is_numeric($key))
            //{
                // make string key...
                //$key = "unknownNode_". $key;
            //}
            //else
            //{
            //}

            // if there is another array found recrusively call this function
            if (is_array($value))
            {
                $node = $xml->addChild($key);
                // recrusive call.
                ArrayToXML::toXml($value, $prefix, $rootNodeName, $node, $indent++);
            }
            else
            {
                // add single node.
                $value = htmlentities($value);
                if(is_numeric($key))
                {
                    if($value != "xml:lang")
                    {
                        if(strpos(' ', $value) === false)
                        {
                            $xml->addAttribute("item", $value);
                        }
                        else
                        {
                            $xml->addAttribute($value, "");
                        }
                    }
                }
                else
                {
                    $xml->addChild($key,$value);
                }
            }

        }
        // pass back as string. or simple xml object if you want!
        return $xml->asXML();
    }
}

This code is called using a public static function that I added to the form class in class.form.php (don’t forget the include!):

public static function convertArrayToXML($arr, $prefix = true)
{
    if( $prefix )
    {
        $sep = "\n================================\n";
        echo $sep . "xml:" . $sep;
    }

    $ax = (string)ArrayToXML::toXml($arr, $prefix, 'data', form::$xmlData);

    $ax = preg_replace('/<\?xml version=\"1\.0\" encoding=\"utf-8\"\?\>/', '', $ax);
    $ax = preg_replace('/<\/*data>/', '', $ax);
    //$ax = preg_replace('/^$/m', '</element><element>', $ax);

    echo
        '<element>'
        . (string)$ax
        . '</element>';
    //form::$xmlData = ArrayToXML::toXml($arr, $prefix, 'data', form::$xmlData);
    //echo (string)form::$xmlData;
}

I call the form::convertArrayToXML in the method in the pfbc::$debug method, which in turn gets called in the pfbc::$setAttributes method, at the tail end of the first if-then conditional:

function debug() {
    //echo "DEBUG: <pre>", print_r($this, true), "</pre>";
    //echo "DEBUG: <br/>\n";
    form::convertArrayToXML($this, false);
}

The result will depend on the PHP form elements, but this post shows is what I think is the complete list of (current) elements.

However, I forgot to list out the “form” element’s attributes/parameters.  Since these are (I think) stored in class variables, using get_class_vars() on the form class (in the constructor?) will probably deliver the full set of current attributes/parameters.

Further, it’s pretty clear that I should not be listing out any array elements that are simply “options” within some other element.

Finally, I am not incrementing the item attribute under the option element, so only one option exists.  However, since the option element contains an array, maybe this is better dealt with in Flash.

I still have to build the Flash (MXML/AS3) interface.  So . . . more to come. . .

Day 2-3:

I have been having some trouble with Flex in certain areas.

The first was with XML.  For an empty element, using someElement.elements()[0] causes returns a (I think) string which is an XML chunk, which must somewhere in code have been converted back to an XML object – however, I cannot cast that as an XMLList object, and so on.   It’s just wierd, probably a bug (or my lack of knowledge).  So I changed the original XML output from PHP to include an “id” attribute with the same name as the element (I will probably change the attr name, since “id” is used elsewhere), which then allowed me to use the attribute instead of the element itself.  This may be a problem later when trying to load/save the data, but we’ll see.

The second was getting the Accordian control to have scrolling NavigatorContent.  It took a while, and it was only in the example of Adobe’s docs for IViewport that I finally figured it out: to set a scrollbar for a component in Actionscript (not MXML), create a new Scroller, and set scroller.viewport to the Group.  I wrote up the instructions in a separate post.

– 30 –

Advertisements