Jump to content


Photo

Module want: form builder


  • Please log in to reply
75 replies to this topic

#1 apeisa

apeisa

    Hero Member

  • Moderators
  • 2,526 posts
  • 855

  • LocationVihti, Finland

Posted 28 January 2011 - 05:08 PM

I am looking for something similar that webform is in Drupal. Some way to admins to build forms (contact form, order form etc). Entries are saved to database and send email if wanted.

Easy to use, most basic fields only (text input, textarea, email etc) at first.

Any thoughts how this should be done?

#2 adamkiss

adamkiss

    Master of the universe

  • Moderators
  • 1,078 posts
  • 289

Posted 28 January 2011 - 05:22 PM

First: It should be done.

Next: I think we could do this the very same way templates are done: Fields have their definitions and their 'administration-form-look', ui. If we add 'simple-form' and 'validation-rules' for this, we can actually reuse the very same fields we use in templates [or better yet, the very same types of fields].

This idea is just created: I wasn't thinking about any implications, just think about this process:

  • 1 – add new 'form-template'
  • 2 – define fields
  • 3 – define 'action' php file
  • 4 – include some tag or php code [tag is here better, because you may need to add this into wysiwyg]

Then in the 'action' file you can have these pseudo-functions [ideas of priceless functions:]
<?php
  //note: baic validation rules [required, e-mail, etc.] were defined in editor
  //note: also you checked 'sanitize' for fields you want have sanitized, so they are already
  $form->addValidationRule( /*description here*/ );
  $form->addValidationRule( '{x} + {y} >= 40' ); //some math validation rules?
  $form->addValidationRule( /* another one */ );

  if ($form->validate){
    $mail = new Mail();
    $db_do = new DBRecord();
    $form->redirect_after = '/page/url';
  }

Note: if no 'redirect_after' found, system returns to 'form' page. Show errors routine if not validate can be automated: just go back to form and show errors.

Note 2: As I see it, this is close to 0, if we talk about how much is done. But outlined model of form editor is very close to whole idea of PW – does the tedious stuff for you without taking away any freedom [anything you might need to customize – HTML, CSS – it's just not shown here, but we'll think it through]

After longer thinking, we might come with all posiible outcomes of forms – there isn't much really – you either show message 'ok', go to 'thank you' page or go to another step [if multiple forms]. There isn't anything in between. :)
   

#3 ryan

ryan

    Hero Member

  • Administrators
  • 5,780 posts
  • 3124

  • LocationAtlanta, GA

Posted 28 January 2011 - 05:26 PM

I would like something like that too, though don't know how it could be done without being something that generates markup. But this is one of the cases where markup generation is probably desirable. I am thinking the best bet will be to base it on ProcessWire's Inputfield modules, since they are already used for this purpose in the page editor (and throughout ProcessWire). And they already generate their own markup and retrieve and sanitize their own values. The main thing will be to write an editor to construct the form (similar to the template editor) and then something to save submitted entries to the DB (or optionally, page records). I haven't actually tried Drupal's form builder yet, I need to get a look at that.

Edit: I see Adam is thinking the same thing (and typing at the same time as me). :) It may be that the existing Templates system can be used for creating these forms as you mentioned.

#4 adamkiss

adamkiss

    Master of the universe

  • Moderators
  • 1,078 posts
  • 289

Posted 28 January 2011 - 05:35 PM

Ryan: I accidentally sent my post without finishing. Please, check modified [finished] post and reply what do you think. I also thought about what to do after, maybe you find some answers in there.

The last sentence was re: send mail/save into db action if everything's ok – I think we might not automatize this part – just give the authors really good API to use pages, DB & send mails at first.

Then, in the second part [when PW will be a little more user-centric (I think we are now 'web people – centric' ;D)], we can add some default editable actions and leave the advanced stuff optional.

#5 ryan

ryan

    Hero Member

  • Administrators
  • 5,780 posts
  • 3124

  • LocationAtlanta, GA

Posted 28 January 2011 - 05:41 PM

Adam, sounds like you have some good ideas.

I have to build a contact form for a site that's going up tomorrow and think I'm going to use the Inputfield modules to do it (manually) just to see how it might work. I'll post the template and result here tomorrow if you guys want to see it. I think it'll lead to some more thoughts on a form builder.

#6 apeisa

apeisa

    Hero Member

  • Moderators
  • 2,526 posts
  • 855

  • LocationVihti, Finland

Posted 28 January 2011 - 06:18 PM

Great to hear that you guys think this is important too.

My two cents
  • Default markup is needed here, it should be plug and play
  • Of course it would be nice to have full control of markup when needed
  • As Ryan said, template editor is actually form building (we define fields, admin site shows form for us). I think that the UI with current template editor is easy enough for non-technical content editors - so we should use that as much as possible

Very interesting to see your findings Ryan about using Inputfield modules to create front end forms.



#7 martinluff

martinluff

    Full Member

  • Members
  • PipPipPip
  • 79 posts
  • 2

  • LocationChristchurch NZ

Posted 29 January 2011 - 01:22 AM

I'd add a vote for this too, it's such a common request and seems a pity if client always has to hire a dev to build a few simple forms. As you say Ryan you've essentially a form builder in there in any case for the admin... ;) Look forward to seeing your sample code for a contact form too.

#8 ryan

ryan

    Hero Member

  • Administrators
  • 5,780 posts
  • 3124

  • LocationAtlanta, GA

Posted 29 January 2011 - 03:24 PM

I worked to make a module early this AM before everyone woke up, and have been banned from the computer since. :) But got a minute to post it so figured I would, and then I'll post a live example tomorrow.

This is just a start on this module... lots more to cover obviously, but just wanted to get the momentum going. And it is fully functional even in this state, if anyone wants to try it out. I just experimented with simple forms having "name, email and message" text fields, so far.  

Here are usage instructions below (pulled from the module file), and the .module file is attached if anyone wants to try it or work with it. Just place it in: /site/modules/FormTemplateProcessor.module

/**
 * Module to let you use templates as web contact forms.
 *
 * Can send you email and/or save the submission to a page in your site. 
 *
 * Usage:
 *
 * 1. In admin, create the fields you want to be part of the form.
 * 2. Create a new template and assign your fields to this template.
 * 3. Create another template for your contact form page (if you don't already have one).
 * 4. Use the example below as a starting point for this contact form page: 
 *
 * $form = $modules->get('FormTemplateProcessor'); 
 * $form->template = $templates->get('my_contact_form_template'); // required
 * $form->requiredFields = array('fullname', 'email');
 * $form->email = 'your@email.com'; // optional, sends form as email
 * $form->parent = $page; // optional, saves form as page
 * echo $form->render(); // draw form or process submitted form
 *
 * 5. Use CSS to style the fields. See the bottom of this file for suggestions.
 *
 */


Download at GitHub:
https://github.com/r...mplateProcessor



#9 apeisa

apeisa

    Hero Member

  • Moderators
  • 2,526 posts
  • 855

  • LocationVihti, Finland

Posted 29 January 2011 - 03:55 PM

Ryan: nice!

Tested it quickly: got this error

Call to undefined method stdClass::render() (line 19 of C:\apache\htdocs\pw\site\templates\page.php)

This is what I have on template file:
$form = $modules->get('FormTemplateProcessor');
$form->template = $templates->get('contact_form'); // required
$form->requiredFields = array('title');
$form->parent = $page; // optional, saves form as page
echo $form->render(); // draw form or process submitted form

I have template called contact_form, which has only two fields (title & body).

There might also be typo on your example: last $form is $contactForm

#10 ryan

ryan

    Hero Member

  • Administrators
  • 5,780 posts
  • 3124

  • LocationAtlanta, GA

Posted 29 January 2011 - 06:12 PM

You are right, that is a typo it should be $form not $contactForm. Do you still get the error after that correction? The error message seems to indicate that typo may be the problem. if not, can you double check that FormTemplateProcessor is installed in Admin > Modules? Thanks -Ryan

#11 apeisa

apeisa

    Hero Member

  • Moderators
  • 2,526 posts
  • 855

  • LocationVihti, Finland

Posted 29 January 2011 - 06:33 PM

FormTemplateProcessor doesn't show up on Admin > Modules at all. I have the file on /site/modules/ folder.

#12 ryan

ryan

    Hero Member

  • Administrators
  • 5,780 posts
  • 3124

  • LocationAtlanta, GA

Posted 29 January 2011 - 06:58 PM

Can you check at the bottom of the modules screen, there should be a "check for new modules" button (just added last week, so it wasn't there before). This was so that we could cache modules (faster) but you need to click the button for it to find them.

#13 apeisa

apeisa

    Hero Member

  • Moderators
  • 2,526 posts
  • 855

  • LocationVihti, Finland

Posted 29 January 2011 - 07:06 PM

Yep, that was it! Tested it quickly and seems to work very well. Endless possibilities here again, very nice!



#14 ryan

ryan

    Hero Member

  • Administrators
  • 5,780 posts
  • 3124

  • LocationAtlanta, GA

Posted 29 January 2011 - 07:58 PM

Great! Glad that worked. Like I say it's just a start, but a good proof of concept hopefully.

#15 Jim Yost

Jim Yost

    Jr. Member

  • Members
  • PipPip
  • 24 posts
  • 2

Posted 11 February 2011 - 10:07 AM

Hey Ryan,
Your module works great! How would I go about overriding the default markup created by each InputField? Obviously without messing up the admin template. Is this currently possible?

-Jim

#16 ryan

ryan

    Hero Member

  • Administrators
  • 5,780 posts
  • 3124

  • LocationAtlanta, GA

Posted 11 February 2011 - 10:26 AM

Hi Jim,

The purpose of Inputfields is to generate markup for inputs (and retrieve the resulting value). This is why they are separate from Fieldtypes. Since they were originally built just for the PW Admin use, I haven't made them configurable from a markup aspect. Though I do plan to change that, now that the utility of these Inputfields is expanding.

Currently, there is only one markup generation method in most Inputfields, and that is the ___render() method. So you can extend any Inputfield, and just override the ___render() method to change the markup it generates. But I'm guessing it's not the actual Inputfields that you want to override so much as the container code generated by the InputfieldWrapper class (with it's jquery-ui classes and list items and stuff). The reason I say that is because the Inputfield types just generate the actual form inputs, like an <input ...> tag. Whereas InputfieldWrapper generates everything else.

Until I can modify this class to make it's markup configurable, I would recommend styling the existing markup using the CSS classes in the comments at the bottom of the .module file as a starting point. 

But I think it will be relatively easy for me to make the InputfieldWrapper class have configurable markup, so it's something I can do quickly if you are interested. But I want to make sure that I'm understanding your needs correctly before I do that. If you just need to modify a single Inputfield's output or something, then we'll focus on that instead.

#17 Jim Yost

Jim Yost

    Jr. Member

  • Members
  • PipPip
  • 24 posts
  • 2

Posted 11 February 2011 - 11:04 AM

Hey Ryan,
I have updated my CSS to use what you have to start and it works well. I mainly use site templates built by external designers for my work, so many times it's easier to just render forms like the designer's examples and have them work and function great. This way I don't have to spend a ton of time updating their css, js, etc.

What I would like to do is be able to override the wrapper class within a template rather than creating a new module to override the wrapper.

Maybe something like: (from your module...I have just changed it a bit so it's not a contact form anymore)
<?php
// get the collection of inputs that can populate this page's fields
$inputfields = $page->getInputfields();

// set values for fields and add them to the form.
foreach($inputfields as $inputfield) {
	if ($inputfield->name == 'title') continue;
	// populate values 
	if ($page->name != 'create') {
		$inputfield_name = $inputfield->name;
		$inputfield->value = $this->page->$inputfield_name;
	}
	// *****Maybe some markup here?*****
        $inputfield->markup("./markup/{$inputfield->type}");

	if(in_array($inputfield->name, $required_fields)) $inputfield->required = true;
	$form->add($inputfield);
}

Just a thought :)

-Jim

#18 apeisa

apeisa

    Hero Member

  • Moderators
  • 2,526 posts
  • 855

  • LocationVihti, Finland

Posted 01 May 2011 - 06:53 AM

Ryan: any quick tutorial how to use inputfields outside of admin? I am actually building an alternative admin view (so these won't be public forms), but need to keep current admin as it is. I have build few of these with custom forms, but now I would need so many forms that it would be great to use all the great inputfield modules.

What I would like to achieve is something where I could just say that "render form for that $page and process it when it is submitted". It would then use all the inputfields that are set to those fields (like AsmSelect, radio etc). And I think sometimes I would need to process forms by myself so that is not requirement. But ability to easily render forms and their input "widgets" is what I need.

I tried to figure out how the things work from current admin, but got confused at some point (I am pretty new to oo programming).

#19 apeisa

apeisa

    Hero Member

  • Moderators
  • 2,526 posts
  • 855

  • LocationVihti, Finland

Posted 01 May 2011 - 08:05 AM

Ok, this gives me form easily, but it doesn't include any of the required .js or .css files:

<?php 
$form = $this->modules->get('InputfieldForm');
$fields = $somepage->getInputfields();
$form->append($fields);
echo $form->render();

I have added these lines to head:

<?php foreach($config->styles->unique() as $file) echo "\n\t<link type='text/css' href='$file' rel='stylesheet' />"; ?>
<?php foreach($config->scripts->unique() as $file) echo "\n\t<script type='text/javascript' src='$file'></script>"; ?>

But only thing that gets included are JQueryFancyBox.css & .js, I have images field, AsmSelect etc.

#20 ryan

ryan

    Hero Member

  • Administrators
  • 5,780 posts
  • 3124

  • LocationAtlanta, GA

Posted 01 May 2011 - 11:08 AM

It looks like you've got it right to me. The only thing I'm wondering is if in your example, does the first snippet get executed before the one that includes the styles/scripts? It would have to in order for those styles/scripts to be populated... i.e. you'd have to render those fields and store them in an output variable before rendering the styles/scripts in your document <head>. This is the way PW always works, in that the document wrapper (final output template) gets generated last, just so that all factors (styles, scripts, ajax, etc.) are known ahead of time and can be accounted for.

When it comes time to process the form, you can do this:

<?php
if($input->post->submit_save) {
    // replace 'submit_save' (above) with the name of your 'save' button field name.
    // tell the form to process input from the POST vars, $input->post:
    $form->processInput($input->post); 
}

If you want to check if there were any errors (after calling processInput above):

<?php
$errors = $form->getErrors();
if(count($errors)) {
    // $errors is an array of strings with error messages
    // each Inputfield also highlights it's own errors the next time you call render()
}

Here's how you can retrieve the values from any given field:

<?php
$inputfield = $form->get('your_field_name');
$value = $inputfield->value; 

// or a shorter version of the same thing: 
$value = $form->your_field_name->value;

You can also iterate the form:

<?php
foreach($form as $inputfield) {
    echo "<li>{$inputfield->name} = " . htmlentities($inputfield->value); 
}

The only thing to note about iterating the form is that some form fields contain more fields within them. You can identify them by the type:

<?php
if($inputfield instanceof InputfieldWrapper) {
    // field contains more fields
}

So to process a form for a page, you might want to use a recursive function like this:

<?php

function populatePage($page, $inputfield) {
    if($inputfield instanceof InputfieldWrapper) {
        foreach($inputfield as $i) populatePage($page, $i); 
    } else {
        // set the value to the page
        $page->set($inputfield->name, $inputfield->value); 
        // optionally perform additional validation on the $inputfield->value before setting it
    }
} 

$form->processInput($input->post);
populatePage($page, $form);

Another approach for the same thing is to iterate $input->post, which puts them in a flat (non-recursive) context. But in this strategy, you have to do more validation to make sure the submitted fields are ones you intend to populate:

<?php

$form->processInput($input->post);

foreach($input->post as $key => $unused) {
    $inputfield = $form->get($key); 
    if(!$inputfield || !$inputfield instanceof Inputfield) continue; 
    if(!$page->fields->has($inputfield->name)) continue; 
    // optionally perform additional validation here
    $page->set($inputfield->name, $inputfield->value); 
}

You can of course retrieve your values directly from $input->post (or $_POST), but the advantage of retrieving it from the $inputfield->value is that many inputfields perform some basic validation on the values you set to them.

In addition, every time you set a value to a page, it passes through the related Fieldtype's sanitize() method. That means that all values set to a page are converted to the right type for the Page. For example, a string submitted for a file in the POST vars is converted to a Pagefile object when you call $page->set($key, $value)... that in turn calls the Fieldtype's sanitize() method. This all happens behind the scenes.  

An important thing to note is that these Fieldtype sanitize() methods are there to sanitize for type, not security. That Fieldtype::sanitize method doesn't know if it's being provided submitted input, or something that you just set to the $page from the API, or something loaded from the DB. So the sanitize method for FieldtypePage (as an example) will ensure that whatever gets sent to it is a Page or something that it can convert to a Page (like a string or array of strings representing pages). Whereas something like FieldtypeText will accept any text you provide to it... after all, a <script> tag may be a very legitimate thing in your text field. But a <script> tag can also be a very dangerous thing in another situation. So if you are using Inputfields outside of an administrative context, or in a context where you want to enforce specific values, you may want to perform additional validation according to your need. The PW admin doesn't need additional validation, but your own forms may.

Here's an example of validating a field for specific needs before setting it to a page. In this case, we know that in our field 'message' we just want plain text and no markup, and we want to limit the length to 500 characters. In our 'message' field settings, we have the "encode entities" output filter turned on, so we are not encoding entities as part of the validation.
<?php

$inputfield = $form->get("message"); 
$value = strip_tags(trim($inputfield->value));
if(strlen($value) > 500) {
    $value = substr($value, 0, 500);
    $inputfield->error("Truncated length of this field to 500 characters");  
}
$page->set('message', $value); 






0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users