XML Fed Form Interface

This is a demonstration of how you can use XML to feed PHP to generate a form. It is helpful when a flexible form interface is needed. This example includes the name of the field, the length, a regex validation string, default value, label for the input, whether it is required or not, and error text. It could be extended have multilingual validation and error text.

<?xml version="1.0" encoding="utf-8" ?>
<interface>
        <name>Works</name>
        <fields>
                <field>
                        <name>URL</name>
                        <length>255</length>
                        <validation>[\w\.\-]{2,255}</validation>
                        <default>domain.com</default>
                        <label>URL</label>
                        <value>url.com</value>
                        <required>true</required>
                        <errortext>Letters, numbers, periods and dashes only</errortext>
                </field>
                <field>
                        <name>id</name>
                        <length>11</length>
                        <validation>[\d]{1,11}</validation>
                        <default>1</default>
                        <label>Id</label>
                        <required>true</required>
                        <errortext>Ids must be all digits</errortext>
                </field>
        </fields>
</interface>

This is the PHP that reads the XML. It uses SimpleXML, which is really nice.

<?php echo '<?xml version="1.0" encoding="utf-8" ?>' ?>
<?php
$xml=file_get_contents('bw.xml');
$xmldata = new SimpleXMLElement($xml);
$bValid=true;
/* Validate the submitted data */
if (isset($_POST['submit']))
{
        $bValid=true;
        foreach ($xmldata->fields->field as $f)
                if (isset($_POST["{$f->name}"]))
                {
                        $f->value=$_POST["{$f->name}"];
                        if (!preg_match('/^'.$f->validation.'$/',$_POST["{$f->name}"]))
                        {
                                $f->invalid=(bool)true;
                                $bValid=false;
                        }
                }
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html lang="en-US" xml:lang="en-US" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>XML Form</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
label
{
font-weight:bolder;
}
input,label
{
display:block;
}
.invalid
{
color:#f00;
}
</style>
</head>
<body>
<h1>XML Form Sourcing Demo</h1>
<hr />
<h2>Generated Inputs</h2>
<!-- 
Generates the form from the XML data.  Note the use of the invalid flag for highlighting, and the display of
errortext if appropriate.
-->  
<form action="#" method="post">
<?php foreach ($xmldata->fields->field as $f) : ?>
<label <?php if (isset($f->invalid)) echo 'class="invalid"' ?> for="<?php echo $f->name?>"><?php echo (($f->required==
'true')?'*':'').$f->label ?>
<?php if (isset($f->invalid)) echo ' '.$f->errortext; ?>
<input name="<?php echo $f->name ?>" id="<?php echo $f->name ?>"
        maxlength="<?php echo $f->length ?>"
        value="<?php echo (isset($f->value)?$f->value:$f->default) ?>" />
</label>
<?php endforeach ?>
<br />
<input name="submit" type="submit" />
</form>
<h2>Raw XML Data</h2>
<!-- Display the raw XML data, for debugging/development -->
<blockquote><?php echo nl2br(htmlentities(file_get_contents('bw.xml'))) ?></blockquote>
<h2>Parsed XML</h2>
<!-- Display how SimpleXML parsed the XML, again for debugging/development -->
<?php
echo '<pre>';
var_dump($xmldata);
echo '</pre>';
?>
<h2>Updated XML (if valid)</h2>
<!-- Show how the XML was updated, if it was valid -->
<?php
if ($bValid)
        echo '<pre>'.nl2br(htmlentities($xmldata->asXML())).'</pre>';
?>
</body>
</html>

This is well-suited for applications which must use connection data into a variety of external interfaces. In this case, the data can be presented, entered, validated, and stored, then used later by a different process.

Blocking Site Visitors by User Agent

One of my sites was receiving a lot of hits with a user agent of Mozilla/4.0 (compatible; ICS), within a very short timeframe (requests within the same second), from a huge variety of IP addresses.

A quick look around showed ‘compatible; ICS’ is probably not a person or search engine.

I checked several IP addresses that used that user agent at: Project HoneyPot, and most of them were listed as potential sources of dictionary attacks and spam senders.

A common reaction to this is to block the requests by user agent, and that’s what I did, using:

RewriteCond %{HTTP_USER_AGENT} (compatible;\ ICS)
RewriteRule ^ - [F]

These must go before any other RewriteRules.

To test the site and ensure it still runs properly, I used Bots vs. Browsers, which allows you to request pages using a specific user agent string. Be sure to check the page with user agents that include the string you want to block, and those which should be allowed.

eZ publish - Creative Commons Licensed Image - Class

Using Creative Commons licensed images allows you to include some beautiful photos and illustrations on your site.

It’s really important to add the appropriate credits when you’re using the images, to comply with the license, but more importantly, to give credit to the creator. To properly credit the work, review this.

To make it easier to credit the images in eZ publish, I created a class and supporting templates. The content class package export (the link above) includes the class definition. The templates I used are below:

design/site/override/templates/full/cc_image.tpl

{* CC_Image - Full view *}
{let sort_order=$node.parent.sort_array[0][1]
     sort_column=$node.parent.sort_array[0][0]
     sort_column_value=cond( $sort_column|eq( 'published' ), $node.object.published,
                             $sort_column|eq( 'modified' ), $node.object.modified,
                             $sort_column|eq( 'name' ), $node.object.name,
                             $sort_column|eq( 'priority' ), $node.priority,
                             $sort_column|eq( 'modified_subnode' ), $node.modified_subnode,
                             false() )
     previous_image=fetch_alias( subtree, hash( parent_node_id, $node.parent_node_id,
                                                class_filter_type, include,
                                                class_filter_array, array( 'cc_image' ),
                                                limit, 1,
                                                attribute_filter, array( and, array( $sort_column, $sort_order|choose( '>', '<' ), $sort_column_value ) ),
                                                sort_by, array( array( $sort_column, $sort_order|not ), array( 'node_id', $sort_order|not ) ) ) )
     next_image=fetch_alias( subtree, hash( parent_node_id, $node.parent_node_id,
                                            class_filter_type, include,
                                            class_filter_array, array( 'cc_image' ),
                                            limit, 1,
                                            attribute_filter, array( and, array( $sort_column, $sort_order|choose( '<', '>' ), $sort_column_value ) ),
                                            sort_by, array( array( $sort_column, $sort_order ), array( 'node_id', $sort_order ) ) ) )}

<div class="content-view-full">
    <div class="class-image">

        <h1>{$node.name|wash}</h1>

        {if is_unset( $versionview_mode )}
        <div class="content-navigator">
            {if $previous_image}
                <div class="content-navigator-previous">
                    <div class="content-navigator-arrow">&laquo;&nbsp;</div><a href={$previous_image[0].url_alias|ezurl} title="{$previous_image[0].name|wash}">{'Previous image'|i18n( 'design/base' )}</a>
                </div>
            {else}
                <div class="content-navigator-previous-disabled">
                    <div class="content-navigator-arrow">&laquo;&nbsp;</div>{'Previous image'|i18n( 'design/base' )}
                </div>
            {/if}

            {if $previous_image}
                <div class="content-navigator-separator">|</div>
            {else}
                <div class="content-navigator-separator-disabled">|</div>
            {/if}

            {let forum=$node.parent}
                <div class="content-navigator-forum-link"><a href={$forum.url_alias|ezurl}>{$forum.name|wash}</a></div>
            {/let}

            {if $next_image}
                <div class="content-navigator-separator">|</div>
            {else}
                <div class="content-navigator-separator-disabled">|</div>
            {/if}

            {if $next_image}
                <div class="content-navigator-next">
                    <a href={$next_image[0].url_alias|ezurl} title="{$next_image[0].name|wash}">{'Next image'|i18n( 'design/base' )}</a><div class="content-navigator-arrow">&nbsp;&raquo;</div>
                </div>
            {else}
                <div class="content-navigator-next-disabled">
                    {'Next image'|i18n( 'design/base' )}<div class="content-navigator-arrow">&nbsp;&raquo;</div>
                </div>
            {/if}
        </div>
        {/if}

        <div class="attribute-image">
            <p>{attribute_view_gui attribute=$node.data_map.image image_class=large}</p>
        </div>

        <div class="attribute-caption">
            {attribute_view_gui attribute=$node.data_map.caption}
        </div>

        <div class="attribute-attribution">
            {'Image credit'|i18n( 'design/base' )}&nbsp;&raquo;&nbsp;{attribute_view_gui attribute=$node.data_map.attribution}
        </div>

        <div class="attribute-creative-commons-license">
                {'Creative Commons License'|i18n( 'design/base' )}:&nbsp;{attribute_view_gui attribute=$node.data_map.creative_commons_license}
        </div>

    </div>
</div>
{/let}

design/site/override/templates/line/cc_image.tpl

{* CC_Image - Line view *}

<div class="content-view-line">
    <div class="class-image">

    <div class="content-image">
        <p style="float:left;width:125px">{attribute_view_gui attribute=$node.data_map.image image_class=small href=$node.url_alias|ezurl()}&nbsp;</p><h2>{$node.name}</h2>
        {attribute_view_gui attribute=$node.data_map.caption}
    </div>

    </div>
</div>

design/site/override/templates/galleryslide/cc_image.tpl

{* CC_Image - Gallery slide view *}

<div class="content-view-galleryslide">
    <div class="class-image">

    <h1>{$parent_name|wash()}: {$node.name|wash()}</h1>

    <div class="attribute-image">
        <p>{attribute_view_gui attribute=$node.data_map.image image_class=large}</p>
    </div>

    <div class="attribute-caption">
        {attribute_view_gui attribute=$node.data_map.caption}
    </div>

    </div>
</div>


design/site/override/templates/embed/cc_image.tpl

<div class="content-view-embeddedmedia">
<div class="class-image">

<div class="attribute-image">
<p>
{if or( is_set( $link_parameters.href ),$object.data_map.attribution.has_content )}
    {attribute_view_gui attribute=$object.data_map.image image_class=first_set( $object_parameters.size,ezini( 'ImageSettings', 'DefaultEmbedAlias', 'content.ini' ), '' ) href=first_set( $link_parameters.href,$object.main_node.url )|ezurl target=$link_parameters.target link_class=first_set( $link_parameters.class, '' ) link_id=first_set( $link_parameters['xhtml:id'], '' ) link_title=first_set( $link_parameters['xhtml:title'], $object.data_map.attribution.content.data_text, '' )}
{else}
  {if is_set($object_parameters.size)}
    {attribute_view_gui attribute=$object.data_map.image image_class=$object_parameters.size}
  {else}
    {attribute_view_gui attribute=$object.data_map.image image_class=ezini( 'ImageSettings', 'DefaultEmbedAlias', 'content.ini' )}
  {/if}
{/if}
</p>
</div>

{if $object.data_map.caption.has_content}
{if is_set($object.data_map.image.content[$object_parameters.size].width)}
<div class="attribute-caption" style="width: {$object.data_map.image.content[$object_parameters.size].width}px">
{else}
<div class="attribute-caption">
{/if}
    {attribute_view_gui attribute=$object.data_map.caption}
</div>
{/if}
</div>
</div>

Template override settings. I used the default image class for some cases.

settings/siteaccess/site/override.ini.append.php


[cc_image_full]
Source=node/view/full.tpl
MatchFile=full/cc_image.tpl
Subdir=templates
Match[class_identifier]=cc_image

[cc_image_galleryslide]
Source=node/view/galleryslide.tpl
MatchFile=galleryslide/cc_image.tpl
Subdir=templates
Match[class_identifier]=cc_image

[cc_image_embed]
Source=content/view/embed.tpl
MatchFile=embed/cc_image.tpl
Subdir=templates
Match[class_identifier]=cc_image

[cc_image_embed-inline]
Source=content/view/embed-inline.tpl
MatchFile=embed-inline/image.tpl
Subdir=templates
Match[class_identifier]=cc_image

[cc_image_embed_node]
Source=node/view/embed.tpl
MatchFile=embed/cc_image.tpl
Subdir=templates
Match[class_identifier]=cc_image

[cc_image_embed-inline_node]
Source=node/view/embed-inline.tpl
MatchFile=embed-inline/image.tpl
Subdir=templates
Match[class_identifier]=cc_image

[cc_image_line]
Source=node/view/line.tpl
MatchFile=line/cc_image.tpl
Subdir=templates
Match[class_identifier]=cc_image

[cc_image_galleryline]
Source=node/view/galleryline.tpl
MatchFile=galleryline/image.tpl
Subdir=templates
Match[class_identifier]=cc_image

I tested this with a custom site design, but it was based on one of the eZ site styles, and very few of the templates were modified. It should integrate well with most designs, but customization is ‘eZ’ enough. :)

I used the standard, default eZ publish image class as the base for the templates.

Login Access Limits

After reviewing the log files for this blog, I noticed many attempts to log into it, and send bogus contact form data.

This is my blog, registration and comments are disabled. To all those who would post helpful comments and legitimate information, I’m sorry.

I access the blog administration from a very limited set of IP addresses, so, instead of wasting my time blocking access from IPs that shouldn’t be logging in, I decided to block all accesses to the administration interface, except my IP address.

This is done using server configuration directives. Refer to the appropriate documentation on blocking access.

After making the changes, be sure to test the effect. The link above is for a nice proxy service that will allow you to visit your pages with a different IP address. The pages should display fine for all navigation through the blog, except things like logging in, and perhaps the contact form. Check anything that’s important to you.

This works if you have a site, blog, or system where the authorized users are from a limited set of IP addresses. It can’t be used to protect against ‘bots and spammers on a forum or contact form. In those cases, I recommend BotScout.

For all those who have been trying to login, please go away.

Great New Web Resource

CoderZone.org launched recently.

It’s great new resource for web people, from ‘n00bs’ to ‘w00ts’. What makes it special:

  • A great team of moderators. These guys are experienced and know the web.
  • A library of code snippets, little bits of code that will save you a tremendous amount of time. You can contribute code, too.
  • XHTML/HTML & CSS sandboxes so you can test out ideas quickly.
  • An SQL sandbox for testing queries.
  • It’s free.
  • A very cool design.
  • No ads, the forum is there to help people, not distract you with ads you aren’t going to click on anyway.