Craeckerffm
3/24/2013 - 8:16 PM

ProcessWire example front-end form with file upload and fields

ProcessWire example front-end form with file upload and fields

<!-- ========================= FORM jQuery Script  ================================== -->

<script src="<?php echo $config->urls->templates . "scripts/jquery-1.7.1.min.js"; ?>"></script>
<script>
    $(function(){
        // Avoid double posts by disabling submit button on form submit
        $('#myform').submit(function(){
            $("#submit").attr('disabled','disabled');
            return true;
        });

    });
</script>
<!-- ========================= FORM Styling  ================================== -->

<style>
    /* basic CSS styling */
    .content { width: 30em; margin: 0 auto ;}
    .error { color: red;}
    .myform { padding: 1.618em 0; margin: 0 0 1em 0; border: 1px solid #bbb;}
    .myform p.error { margin: 0.5em 0; border: 1px solid red; padding: 2px 5px;}
    .myform .row { padding: 1.618em 0 0; margin: 0 1.618em 1.618em; border-top: 1px solid #aaa; }
    .myform input[type="text"] { padding: 0.8em; background-color: #eee; border: none; width: 100%; }
    .myform textarea { padding: 0.8em; background-color: #eee; border: none; width: 100%; }
</style>
<?php

/**
 * ### Example front-end form template with file upload and fields ###
 *
 * - with files (images) upload to page field
 * - adds new page on the fly and adds uploaded images
 * - prevents CRSF attacks, this also prevents double post by refresh page after submit
 * - has required fields with error messages inline
 * - sanitizing and saving values to a page
 * - jquery example with disabled submit button on form submit
 *
 * Edit add or remove form markup below and configure this section according to what you need.
 *
 */


// ------------------------------ FORM Configuration ---------------------------------------

// --- Some default variables ---
$success_message   = "<p class='message'>Thanks for your message!</p>";

// --- All form fields as nested array ---
// using html form field name => template field nam, from the page you're going to create
$form_fields = array(
    'fullname'              => array('type' => 'text', 'value' => '', 'required' => true),
    'email'                 => array('type' => 'email', 'value' => '', 'required' => true),
    'message'               => array('type' => 'textarea', 'value' => '', 'required' => true),
    'newsletter_subscribe'  => array('type' => 'checkbox', 'value' => 0, 'required' => false),
    'images'                => array('type' => 'file', 'required' => true)
);

// --- WireUpload settings ---
$upload_path        = $config->paths->assets . "files/.tmp_uploads/"; // tmp upload folder
$file_extensions    = array('jpg', 'jpeg', 'gif', 'png');
$max_files          = 3;
$max_upload_size    = 1*1024*1024; // make sure PHP's upload and post max size is also set to a reasonable size
$overwrite          = false;

// --- Page creation settings ---
$template           = "upload-entry"; // the template used to create the page
$parent             = $pages->get("/uploads/");
$file_field         = "images";
$page_fields        = array('fullname','email','message','newsletter_subscribe');

// $page_fields = define the fields (except file) you want to save value to a page
// this is for the form process to populate page fields.
// Your page template must have the same field names existent


// ------------------------------ FORM Processing ---------------------------------------

include("./form-process.php");

?>

<!-- ========================= FORM HTML markup  ================================== -->

<?php

/**
 * Some vars used on the form markup for error and population of fields
 *
 * $errors[fieldname]; to get errors
 * $form_fields[fieldname]['value'];
 *
 * Some helper function to get error markup
 * echo showError(string);
 *
 * Prevent CSRF attacks by adding hidden field with name and value
 * you an get by using $session->CSRF
 * $session->CSRF->getTokenName();
 * $session->CSRF->getTokenValue();
 *
 * $errors['csrf']; used to check for CSRF error
 *
 */

?>

<div class="content">

    <h2>Upload Images to Page Example Form</h2>

<?php if(!$success) : ?>

    <?php if(!empty($errors)) echo showError("Form contains errors"); ?>
    <?php if(!empty($errors['csrf'])) echo showError($errors['csrf']); ?>

    <form name="myform" class="myform" id="myform" method="post" action="./" enctype="multipart/form-data">

        <input type="hidden" name="<?php echo $session->CSRF->getTokenName(); ?>" value="<?php echo $session->CSRF->getTokenValue(); ?>"/>

        <div class="row <?php if(isset($errors['fullname'])) echo "error";?>">
            <label for="fullname">Name* </label><br/>
            <input type="text" name="fullname" id="fullname" value="<?php echo $sanitizer->entities($form_fields['fullname']['value']); ?>"/>
            <?php if(isset($errors['fullname'])) echo showError($errors['fullname']); ?>
        </div>

        <div class="row <?php if(isset($errors['email'])) echo "error";?>">
            <label for="email">Email* </label><br/>
            <input type="text" name="email" id="email" value="<?php echo $sanitizer->entities($form_fields['email']['value']); ?>"/>
            <?php if(isset($errors['email'])) echo showError($errors['email']); ?>
        </div>

        <div class="row <?php if(isset($errors['message'])) echo "error";?>">
            <label for="message">Message* </label><br/>
            <textarea type="text" name="message" id="message"><?php echo $sanitizer->entities($form_fields['message']['value']); ?></textarea>
            <?php if(isset($errors['message'])) echo showError($errors['message']); ?>
        </div>

        <div class="row <?php if(isset($errors['newsletter_subscribe'])) echo "error";?>">
            <label for="newsletter_subscribe">Newsletter* </label><br/>
            <input type="checkbox" name="newsletter_subscribe" id="newsletter_subscribe"
                <?php echo $form_fields['newsletter_subscribe']['value'] ? "checked='checked'" : "" ; ?>
            />
            <?php if(isset($errors['newsletter_subscribe'])) echo showError($errors['newsletter_subscribe']); ?>
        </div>

        <div class="row <?php if(isset($errors['images'])) echo "error";?>">
            <label for="images">Images* </label><br/>
            <input type="file" name="images[]" id="images" multiple="multiple" size="40" accept="image/jpg,image/jpeg,image/gif,image/png"/>
            <?php
            // show upload errors
            if(isset($errors['images'])){
                // if multiple errors
                if(is_array($errors['images'])){
                    foreach($errors['images'] as $e){
                        echo showError($e);
                    }
                } else { // if single error
                    echo showError($errors['images']);
                }
            }
            ?>
        </div>
        <div class="row">
            <input type="hidden" name="action" id="action" value="send"/>
            <input type="submit" name="submit" id="submit" value="Submit"/>
        </div>
    </form>

<?php else: ?>

    <p><?php echo $success_message; ?></p>

<?php endif; ?>

</div>
<?php

// ------------------------------ FORM Processing ---------------------------------------

$errors            = null;
$success           = false;

// helper function to format form errors
function showError($e){
    return "<p class='error'>$e</p>";
}

// dump some variables
// var_dump($_FILES,$_POST,$_SESSION);

/**
 * Cast and save field values in array $form_fields
 * this is also done even form not submited to make populating the form later easier.
 *
 * Also used for pupulating page when form was valid
 */
$required_fields = array();
foreach($form_fields as $key => $f){
    if($f['type'] == 'text'){
        $form_fields[$key]['value'] = $sanitizer->text($input->post->$key);
    }
    if($f['type'] == 'textarea'){
        $form_fields[$key]['value'] = $sanitizer->textarea($input->post->$key);
    }
    if($f['type'] == 'email'){
        $form_fields[$key]['value'] = $sanitizer->email($input->post->$key);
    }
    if($f['type'] == 'checkbox'){
        $form_fields[$key]['value'] = isset($input->post->$key) ? 1 : 0;
    }
    // store required fields in array
    if($f['required']) $required_fields[] = $key;
}




/**
 * form was submitted, start processing the form
 */

if($input->post->action == 'send'){

    // validate CSRF token first to check if it's a valid request
    if(!$session->CSRF->hasValidToken()){
        $errors['csrf'] = "Form submit was not valid, please try again.";
    }

    /**
     * Ceck for required fields and make sure they have a value
     */
    foreach($required_fields as $req){

        // required upload file field
        if($form_fields[$req]['type'] == 'file'){
            if(empty($_FILES[$req]['name'][0])){
                $errors[$req] = "Select files to upload.";
            }
        // reqired checkbox fields
        } else if($form_fields[$req]['type'] == 'checkbox'){
            if($form_fields[$req]['value'] == 0){
                $errors[$req] = "Field required";
            }
        // reqired text fields
        } else if($form_fields[$req]['type'] == 'text'
                  || $form_fields[$req]['type'] == 'textarea'
                  || $form_fields[$req]['type'] == 'email'){
            if(!strlen($form_fields[$req]['value'])){
                $errors[$req] = "Field required";
            }
            // reqired email fields
            if($form_fields[$req]['type'] == 'email'){
                if($form_fields[$req]['value'] != $input->post->$req){
                    $errors[$req] = "Please enter a valid Email address.";
                }
            }
        }
    }

    /**
     * if no required errors found yet continue file upload form processing
     */
    if(empty($errors)) {

        // RC: create temp path if it isn't there already
        if(!is_dir($upload_path)) {
            if(!wireMkdir($upload_path)) throw new WireException("No upload path!");
        }

        // setup new wire upload
        $u = new WireUpload($file_field);
        $u->setMaxFiles($max_files);
        $u->setMaxFileSize($max_upload_size);
        $u->setOverwrite($overwrite);
        $u->setDestinationPath($upload_path);
        $u->setValidExtensions($file_extensions);

        // start the upload of the files
        $files = $u->execute();

        // if no errors when uploading files
        if(!$u->getErrors()){

            // create the new page to add field values and uploaded images
            $uploadpage = new Page();
            $uploadpage->template = $template;
            $uploadpage->parent = $parent;

            // add title/name and make it unique with time and uniqid
            $uploadpage->title = date("d-m-Y H:i:s") . " - " . uniqid();

            // populate page fields with values using $page_fields array
            foreach($page_fields as $pf){
                if($templates->get($template)->hasField($pf)){
                    $uploadpage->$pf = $form_fields[$pf]['value'];
                } else {
                    throw new WireException("Template '$template' has no field: $pf");
                }
            }

            // RC: for safety, only add user uploaded files to an unpublished page, for later approval
            // RC: also ensure that using v2.3+, and $config->pagefileSecure=true; in your /site/config.php
            $uploadpage->addStatus(Page::statusUnpublished);
            $uploadpage->save();

            // Now page is created we can add images upload to the page file field
            foreach($files as $filename) {
                $uploadpage->$file_field = $upload_path . $filename;
                // remove tmp file uploaded
                unlink($upload_path . $filename);
            }
            $uploadpage->save();

            // $success_message .= "<p>Page created: <a href='$uploadpage->url'>$uploadpage->title</a></p>";
            $success = true;

            // reset the token so no double posts happen
            // also prevent submit button to from double clicking is a good pratice
            $session->CSRF->resetToken();

        } else {
            // errors found
            $success = false;

            // remove files uploaded
            foreach($files as $filename) unlink($upload_path . $filename);

            // get the errors
            if(count($u->getErrors()) > 1){ // if multiple error
                foreach($u->getErrors() as $e) {
                    $errors[$file_field][] = $e;
                }
            } else { // if single error
                $errors[$file_field] = $u->getErrors();
            }
        }
    }
}