ProcessWire Upload Formular
Siehe auch
https://gist.github.com/somatonic/5233338 ProcessWire UploadFormular Dropzone Enhanced ProcessWire - File Upload mit Blueimp jQueryFileUpload
Übersicht
Ein einfaches Upload Formular mit dem der Benutzer Dateien übermitteln kann. Für das Formular kommen aus ProcessWire der Sanitizer, die WireUpload Klasse und Session Tokens zum Einsatz. Das Formular wird validiert. Das Markup für das Formular steht direkt im Template, wird also nicht über die Formularfunktionen von ProcessWire erzeugt. Das ist oft die schnellere Variante, vor allem wenn das Markup sehr individuell ist.
Der Code basiert auf Somatonics Beispiel (s.o.) und wurde leicht angepasst.
Features:
- Dateiupload (Bilder)
- Formulareingaben als Seiten speichern (inkl. Bilder)
- Verhindert CRSF Attacken und doppelte Posts durch neu Laden
- Formular validierung mit Inline Messages
- Feldvariablen werden durch den Sanitizer bereinigt
- Kleines jQuery Beispiel um den Submit Button nach Absenden zu deaktivieren.
Aufbau
TEMPLATES basic-upload FELDER: title basic-upload-entry FELDER: title, fullname, email, message, newsletter_subscribe, images
SEITEN /basic-upload/ /basic-upload/[automatisch-erzeugte-seiten]
Das Formular liegt in der Seite
/basic-upload/
Mit dem Template 'basic-upload' (basic-upload.php)
Beim Absenden eines gültigen Formulars werden unterhalb der Formularseite weitere Seiten generiert. Diese erhalten das Template basic-upload-entry. Wenn man die erzeugten Seiten im Frontend anzeigen möchte braucht man natürlich noch ein PHP File für basic-upload-entry. Ansonsten kann man sich die Seiten im Backend anzeigen.
Code
templates/partials/basic-upload/form-process.php
<?php namespace ProcessWire;
// ------------------------------ 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();
print_r($template);
//var_dump($parent);
$uploadpage->template = $template;
$uploadpage->parent = $parent;
// add title/name and make it unique with time and uniqid
$uploadpage->title = date("Y-m-d_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();
}
}
}
}
templates/basic-upload.php
<?php namespace ProcessWire;
/**
* ### 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>";
$success = false;
// --- 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 = 2*1024*1024; // make sure PHP's upload and post max size is also set to a reasonable size
$overwrite = false;
// --- Page creation settings ---
$template = "basic-upload-entry"; // the template used to create the page
$parent = $pages->get("/basic-upload/");
$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("./partials/basic-upload/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>
<p>Creates Pages in Backend from Form including upload field.</p>
<?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>
form.js (jQuery)
<!-- ========================= FORM jQuery Script ================================== -->
<script>
$(function(){
// Avoid double posts by disabling submit button on form submit
$('#myform').submit(function(){
$("#submit").attr('disabled','disabled');
return true;
});
});
</script>