Jump to content
MatthewSchenker

Create Pages (with File-Upload Field) via API

Recommended Posts

Greetings Everyone,

*************************************************
*************************************************
EDIT NOTE: This post started as a work-in-progress discussion as I was working out the elements of a successful form. After contributions from participants in this discussion, the code below has been tested and works well.

You can use the code as shown below in your ProcessWire templates!

Feel free to follow up with additional quesations/comments!
*************************************************
*************************************************

I have successfully built front-end forms with ProcessWire to add pages via the API. It works great -- until I had to include image uploads along with the "regular" form fields.  Then it temporarily got a bit complicated.

In this discussion, I show how to handle front-end submissions in ProcessWire with the goal of allowing us to create pages from custom forms.  I then go a step further and show how to use the same form to upload files (images and other files).

I'm hoping this discussion can illustrate the whole process. I know a lot of people are interested in using ProcessWire to do front-end submissions, and my goal for this discussion is to benefit others as well as myself!

First, here's my original contact form (no file uploads):

<form action="/customer-service/contact/contact-success/" method="post">
<p><label for="contactname">Name:</label></p>
<p><input type="text" name="contactname"></p>
<p><label for="email">E-Mail:</label></p>
<p><input type="email" name="email"></p>
<p><label for="comments">Comments:</label></p>
<p><textarea name="comments" cols="25" rows="6"></textarea></p>
<button type="submit">Submit</button></form>

And here's the "contact-success" page that picks up the form entry to create ProcessWire pages:

<?php // First, confirm that a submission has been made
if ($input->post->contactname) {

// Save in the ProcessWire page tree; map submission to the template fields
$np = new Page(); // create new page object
$np->template = $templates->get("contact_submission"); 
$np->parent = $pages->get("/customer-service/contact-us/contact-submission-listing/");

// Send all form submissions through ProcessWire sanitization
$title = $sanitizer->text($input->post->contactname);
$name = $sanitizer->text($input->post->contactname);
$contactname = $sanitizer->text($input->post->contactname);
$email = $sanitizer->email($input->post->email);
$comments = $sanitizer->textarea($input->post->comments);

// Match up the sanitized inputs we just got with the template fields
$np->of(false);
$np->title = $contactname;
$np->name = $contactname;
$np->contactname = $contactname;
$np->email = $email;
$np->comments = $comments;

// Save/create the page
$np->save();							    										    		

} ?>

This works great! After submitting the form, we go to the "Success" page, and new submissions show up in the ProcessWire page tree right away. Excellent!

Now I need to add a photo field. I altered the above form so it looks like this:

<form action="/customer-service/contact/contact-success/" method="post" enctype="multipart/form-data">
<p><label for="contactname">Name:</label></p>
<p><input type="text" name="contactname"></p>
<p><label for="email">E-Mail:</label></p>
<p><input type="email" name="email"></p>
<p><label for="comments">Comments:</label></p>
<p><textarea name="comments" cols="25" rows="6"></textarea></p>
<p>Click the "Select Files" button below to upload your photo.</p>
<input type="file" name="contact_photo" />
<button type="submit">Submit</button>
</form>

And here's the updated "contact-success" page:

<?php // First, confirm that a submission has been made
if($input->post->contactname) {

// Set a temporary upload location where the submitted files are stored during form processing
$upload_path = $config->paths->assets . "files/contact_files/";

// New wire upload
$contact_photo = new WireUpload('contact_photo'); // References the name of the field in the HTML form that uploads the photo
$contact_photo->setMaxFiles(5);
$contact_photo->setOverwrite(false);
$contact_photo->setDestinationPath($upload_path);
$contact_photo->setValidExtensions(array('jpg', 'jpeg', 'png', 'gif'));

// execute upload and check for errors
$files = $contact_photo->execute();

// Run a count($files) test to make sure there are actually files; if so, proceed; if not, generate getErrors()
if(!count($files)) {
$contact_photo->error("Sorry, but you need to add a photo!");
return false;
}

// Do an initial save in the ProcessWire page tree; set the necessary information (template, parent, title, and name)
$np = new Page(); // create new page object
$np->template = $templates->get("contact_submission"); // set the template that applies to pages created from form submissions
$np->parent = $pages->get("/customer-service/contact-us/contact-submission-listing/"); // set the parent for the page being created here

// Send all the form's $_POST submissions through ProcessWire's sanitization and/or map to a variable with the same name as the template fields we'll be populating
$np->title = $sanitizer->text($input->post->contactname);
$np->name = $np->title;
$np->contactname = $sanitizer->text($input->post->contactname);
$np->email = $sanitizer->email($input->post->email);
$np->comments = $sanitizer->textarea($input->post->comments);
$np->save();

// Run photo upload
foreach($files as $filename) {
$pathname = $upload_path . $filename;
$np->contact_photo->add($pathname);
$np->message("Added file: $filename");
unlink($pathname);
}

// Save page again
$np->save(); ?>

<p>Thank you for your contact information.</p>

<?php return true;
}

else { ?>
<p> Sorry, your photo upload was not successful...</P>
<?php } ?>


Replace the field references with your own field names, make sure to change the various paths to match yours, and change the various messages to be what you need for your project.

Read the entire discussion to see how we worked through getting to the solution shown above.

Thanks,
Matthew

  • Like 9

Share this post


Link to post
Share on other sites

You have written contest_photo instead of contact_photo at the beginning. Not sure if this solves your issue. Sorry on mobile...

  • Like 2

Share this post


Link to post
Share on other sites

Hey Wanze,

Yes, that shows you have a good eye for sure!

Unfortunately, that was just an error from me transferring the text to the forum... I edited the forms in my first post and fixed that word.

In other words, it still doesn't work.

Any other ideas?

Thanks,

Matthew

Share this post


Link to post
Share on other sites

$contact_photo->setDestinationPath($upload_path);

I could be overlooking it (late here), but I don't see $upload_path defined anywhere.

Share this post


Link to post
Share on other sites

Hey renobird,

Late here too, but difficult to sleep when working on a ProcessWire site!

You are correct... I was missing that piece!  Definitely, that should be there.

I added it, and holding my breath I tried the form again.  But it still doesn't work.  Same result: no errors, and we see the "success" page, but the new page does not appear in the page tree.

Other suggestions or ideas?

Thanks,

Matthew

PS: I've edited my post above to keep track of changes.  That way, when this discussion is done, there will be a complete example for others to reference.

  • Like 1

Share this post


Link to post
Share on other sites
$upload_path = "/site/assets/files/temp/";

I think the path is not correct, it's more an "url".

The complete path usually starts with the folder your site's in (public_html/www).

Try this:

$upload_path = $config->paths->assets . 'files/temp/';

And have you debug mode turned on to see any errors?

Cheers

Share this post


Link to post
Share on other sites

Greetings,

To renobird: Yes, I am basing my work on Soma's code.  But because I have more fields than just a file upload, I have to re-work the original code.  In my code above, you can see that what I have looks like Soma's code, but with the other fields included.

$upload_path = "/site/assets/files/temp/";

I think the path is not correct, it's more an "url".

The complete path usually starts with the folder your site's in (public_html/www).

Try this:

$upload_path = $config->paths->assets . 'files/temp/';

And have you debug mode turned on to see any errors?

I fixed the path reference (updated my post above).  But still no luck.  The page is not being created.  And no errors are being reported.

Based on a comment from Ryan in a related discussion, I wonder if I am simply saving the page at the wrong stage?

Here's the related discussion: http://processwire.com/talk/topic/2997-uploading-a-new-photo-to-a-member-profile-avatar-field/#entry30621

I really want to have a full, public example of how to put file uploads in a form with other fields, so I am hoping this discussion can become a live working out of the details.

Thanks,

Matthew

Share this post


Link to post
Share on other sites

I have a working example - doing essentially what you are after. I'll compare to yours when I get back to the office (on mobile now) and see if can spot the issue.

Share this post


Link to post
Share on other sites

Not the ideal way to debug like this code in a forum. Nothing against anyone personally. Now the code lost intendation and no way to go through it.

Please post code to some snippet site like Gist or bitbucket.

In general, we should provide some more practice and best ways HOW TO DEBUG YOUR CODE. Good coding is learning how to debug. There's simple steps you can go through to test all vars and parts of code to see where the problem lays. Sometimes it's simple sometimes it can get hard, as you when you don't have errors.

Ok.

What I spotted too is the last bit, where is the $u->getErrors() coming from?

Also as you say the page isn't created at all? I would start there and get that right first and work you way up.

Thanks,

Soma

  • Like 2

Share this post


Link to post
Share on other sites

Hey Soma,

I know what you're saying, but I wanted to use the forum as a way to work out what the general idea should be.  Ideally, it would be great to have things like this all in Github, but I think we often need to use a forum this way.  It's a more direct way -- in the end -- to have code that everyone here can use for their sites.

I think having a full working example of a form with various fields as well as file-uploads would be great for the forum.

Good catch on the $u->getErrors()... Yes, that should be $contest_photo->getErrors()

However... It still doesn't work.

I'm sure we'll solve this!

Thanks,

Matthew

Share this post


Link to post
Share on other sites

Matthew,

have you debug mode enabled to see PHP errors/warnings?

Also can you do a print_r($_FILES); to check if the PHP $_FILES array isn't empty?

Maybe the file isn't sent at all to the server.

Share this post


Link to post
Share on other sites

I also know what you're saying, but I have to strongly disagree. I could've write this in any other similar thread.

The problem is the code posted in a wysiwyg is "horrible" to read and even copy it.

Then after this is working (as with 1000 other code here in the forum), there comes the next who takes this code but need another feature.. then the game start again with same story same code but different bugs issues.

What I would like to do is helping how to code and debug, not have 1 million snippets that are sometimes very bad coded and not complete best practice or not even working spooking around the forum. It already happened many times and it will get worse by time. I know what you're wanting to do and I understand, but in the long run what we are practicing here since 2 years is very bad for newcomers. We better this energy and time to help with little tutorials and snippets that are good to start with in a dedicated site/place.

  • Like 3

Share this post


Link to post
Share on other sites

Hey Soma,

I get you!  I really do...  As someone who is attempting to document how to do things, it is difficult when there are pieces here and there for larger routines.  For example, with file-uploads and front-end forms in general, there are isolated chunks throughout the forum, and putting it all together as a single file can be difficult.  My goal with this discussion is to consolidate a whole routine in one coherent file.

For sure, your points about better ways to discuss such code is worthy of a larger discussion!  And yet, forums like this often have bits of code (many times, they are more than just "bits").  In fact, if you remove the code from a forum like this, what's left to discuss?

If I were to move this to GitHub, would it be as valuable to the ProcessWire community?

Thanks,

Matthew

Share this post


Link to post
Share on other sites

I didn't say move this discussion to github, but the snippet maybe?

Share this post


Link to post
Share on other sites

Im saying gist.github.com not github itself :-)

Share this post


Link to post
Share on other sites

Greetings,

OK, Soma, here's my gist for this subject: https://gist.github.com/MatthewSchenker/5205927

I hope this can make it easier and more productive to work this out -- and better for everyone to use.

Also, maybe this can be a spark that gets more of us to Github to work out our code?

Thanks,

Matthew

  • Like 1

Share this post


Link to post
Share on other sites

Much better... and just now I spoted (starting from top)  an error

if ($input->get->contactname){

Your form is method post and not get!

So you would have easily spoted this already if you add an pseudo echo "Hello" after the if() to see it gets to after the check. As I said it's really simple if you go step by step and output vars to test and see if it really get's there. 

Share this post


Link to post
Share on other sites

Hi Soma,

I used "GET" for the original form (the one with no file uploads).  I then switched to "POST" for the one with file uploads.  But to make it easier, I will switch all of them to the "POST" method.

Thanks,

Matthew

Share this post


Link to post
Share on other sites

Dont want to be mean but have to say that I already built multiple working front end form examples that work. The one Reno linked would be what you do. So this is not so much about making a working example but to help you debug your code you messed up. :-P #hides behind stone#

Of course yours has some more fields, but there's no validation happen on those, which I think should be the lesson here. To make it easy on this subject I would suggest looking at my other front end forms using PW forms and field that will make it more streamlined and easy. Its also in my gists examples.

Share this post


Link to post
Share on other sites

Hey Soma,

Yes, I admit the code I posted has problems.  I just can't seem to pinpoint the problem because building these kinds of forms gets into a lot of PW-specific steps.  I haven't seen any examples to follow for forms with image-upload fields along with "regular" fields.

I actually based my code above on your very helpful gist for uploading images to newly created pages (https://gist.github.com/somatonic/4150974).  But somewhere in the process of mixing in "regular" fields with the image upload requirements, something is off.

I realize I must be doing something wrong, but I can't tell what.

I'm hoping in the end we'll have a working example of how to do this that can be beneficial to others, since I know other need (or will need) to do this.

Thanks,

Matthew

Share this post


Link to post
Share on other sites

Hi Matthew,

have you tried my debugging steps? =)

Matthew,


have you debug mode enabled to see PHP errors/warnings?


Also can you do a print_r($_FILES); to check if the PHP $_FILES array isn't empty?


Maybe the file isn't sent at all to the server at all.

Share this post


Link to post
Share on other sites

Greetings Wanze,

Sorry, I meant to respond to that earlier.

Yes, I am seeing a result from that.  For example, I uploaded a photo called "cloudphoto.jpg.  Here's the message I'm seeing:

Array ( [contact_photo] => Array ( [name] => cloudphoto.jpg [type] => image/jpeg [tmp_name] => /tmp/phpdRhJLN [error] => 0 [size] => 1864092 ) ) 

Thanks,

Matthew

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By Peter Knight
      I have a few web forms which require testing on a weekly basis and I don't want the recipients (administrators) to receive these test emails.
      What would be a good way to test approx 15 forms from the front end and have the test delivered a list of secondary administrator recipients?
      I'm thinking that I could have some kind of config file which watches for a trigger word or email and then understands that it's a test and to bypass the normal admins?
      All of the forms ask for an email address so I could setup an email such as 'testform@email.not' etc which my config file (hook?) would watch for.
      Or is there a better way to do this?
      Additionally, I have a few extra requirements...
      Forms should goto an alternative success page. This is because I don't want my test to skew my Google Analytics conversion tracking Forms would need to be tested from the front-end and not the PW admin area Any advice appreciated.
      BTW I realise this should be posted in the proper FormBuilder support forum. I am in the process of renewing my license for access to that support forum.
       
    • By VeiJari
      Hello forum,
      This is really a weird one, because front end editing works in a earlier website we did to a customer. 
      When I check the source code for current website it does initiate front end edit: 
      <span id=pw-edit-1 class='pw-edit pw-edit-InputfieldPageTitle' data-name=title data-page=1021 data-lang='1017' style='position:relative'><span class=pw-edit-orig>Tekijät</span><span class=pw-edit-copy id=pw-editor-title-1021 style='display:none;-webkit-user-select:text;user- select:text;' contenteditable>Tekijät</span></span>  But when I double click nothing happens (yes I'm 100% sure I'm superuser and logged in)
      I also tried to apply the front end with other methods than:
      $page->edit('title'); But didn't work either.
      We are using jquery 2.2.4, so it should not be a problem.
      Is this a bug related to current master or something else?
      Someone else having this problem as well?
    • By jom
      Hi everyone
      It seems that I don't fully understand the wireTempPath() function and I need some help.
      I use wireTempPath() to create a new location in assets/cache/WireTempDir and than copy a pdf from the assets/files/page folder to the new folder. I want the file to be accessible only for a limited time, that's why I use wireTempPath.
      The file seems to be copied to the right location, but gets deleted right afterwards, according to 
      As mentioned in the topic above, 
      $wireTempDir->setRemove(false); prevents the file to be deleted. But I like the file to be automatically deleted after a few days. So, how can I do that?
      My code so far (everything works, but the automatic removal of the tempDir folder):
      //generate and show download link $folder = time(); // timestamp as temporary folder $maxAge = (int) $settings->options_downloadlink_valid_hours * 3600; //tempDir wants maxAge as seconds $options = array( 'maxAge' => $maxAge ); $wireTempDir = wireTempDir($folder, $options); $wireTempDir->setRemove(false); $src_file = $page->ebook_download->filename; // Create a new directory in ProcessWire's cache dir if(wire('files')->mkdir($wireTempDir, $recursive = true)) { if(wire('files')->copy($src_file, $wireTempDir)){ //get subdirs from tempDir: $pos = strpos($wireTempDir, "WireTempDir"); $subdir = substr($wireTempDir, $pos, 100); $out .= "<p><a href='" . wire('pages')->get('template=passthrough')->httpUrl . "?file=" . $subdir . $page->ebook_download->basename . "' target='_blank'>$page->title</a></p>"; } } I appreciate any ideas - thanks!
      Oliver
    • By VeiJari
      Hello forum, this is my first security related post, so I'm a bit of a newbie.
      I understand that when I have direct front-input from user I should sanitize the input, but how about when I use a secret key for showing a API for a third-party supplier? Should I sanitize the input->get() key?
      I've tested this issue and I tried ?key=<?php echo $page->field; ?> And without adding any sanitization it comes back: /?key=<?php%20echo%20$page->field;%20?>
      So can I rely on this, or should I still use $sanitizer just in case?
       
      Thanks for the help!
    • By EyeDentify
      I have been experimenting with the new $page->meta() method and find it useful.

      Once i figured out that the data i "save" with it is tied to the page where i called the method from.

      So this is not obvious at least not for me in the documentation:
      https://processwire.com/api/ref/page/meta/
       
      So i just wanted to share that revelation with the community so you don´t get as confused as i was.

      Happy Coding Everyone.
×
×
  • Create New...