Jump to content

Create Pages (with File-Upload Field) via API


MatthewSchenker

Recommended Posts

Greetings,

OK, I think we must be very close...  I've reworked my code so it looks the way Ryan posted above.

But there is one thing that continues to happen that seems odd...

I receive this error on the "success" page after submitting the form:

Destination path does not exist temp

I've tried making the reference in various ways, including...

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

The "temp" folder definitely exists in this location:

/site/assets/files/temp

It's got permissions set to 777.

I assume I am missing something really obvious, but can't see what.

Thanks,

Matthew

Link to comment
Share on other sites

Greetings Soma,

I really appreciate your patience.  I admit this has been a rather long discussion...

I replaced the reference with this:
 

$upload_path = '../assets/files/temp/';

And now it works...

Yes, you read that correctly. It works.  I mean the whole form -- file uploads and everything.

I'll post the complete code shortly.

Thanks for helping me through this.  What kind of beer do you like?

Thanks,

Matthew

Link to comment
Share on other sites

Thanks Matthew, well I like all good beers :D

Have you tried my suggestion to output the upload path to debug? However, to make it short, Ryan made an small error in that he uses "$config" in a function scope. That would leave you with a upload path "temp". 

It would have to be "wire("config")->paths->assets" to get it to work, as wire() is global and can be used everywhere even in functions.

Link to comment
Share on other sites

... to make it short, Ryan made an small error in that he uses "$config" in a function scope. That would leave you with a upload path "temp". 

Aha!  There's the thing... I was wondering about that.  Also, probably due to the same point, I was getting an error using simple ProcessWire "get" reference to a template.

I actually took the code out of a function, and then -- as you described -- used the config reference instead.

Everything works.

Thanks again,

Matthew

Link to comment
Share on other sites

I have to agree with Soma here. There are several working versions already.

Rather than introduce yet another example, it seems like we should just wrap up the discussion with links to the already provided working examples.

  • Like 1
Link to comment
Share on other sites

Greetings,

I just edited the first post so it now contains the updated code.

I have left two versions of the form and success page posted, so people can see what everything looks like without file uploads.  Then there are the form and success page with file uploads.  Of course, be careful to change the references to match your own fields, paths, and messages.

There are of course a few other steps that others may want to add to the forms I posted.  But they work.

And of course Soma has posted some great examples of how to do this.

I'm glad to have worked through this so we can arrive at some code that handles file uploads.

Thanks,

Matthew

Link to comment
Share on other sites

  • 3 months later...

Hi guys.

I just happened to need a method of allowing image uploads and what do you know? There's a whole thread dedicated to it. ^-^

Matthew, nice work. Thank you. I have only 2 things to add:

1. The code as is makes the assumption that each user will only ever upload one image or that there are no people with the same name. Since the title is derived from the contactname an error is thrown if a second upload is made with the same name. I added:

$n = 0;
$np->title = $sanitizer->text($input->post->contactname)  . "-" . (++$n);
 

It ensures each upload will get a unique ID.

2. I noticed the images uploaded to the .temp directory don't get deleted. With extensive use this will lead to lots of wasted server space as the .temp folder gets more and more bloated with duplicate images. Is there a way to solve this?

You made my job very easy anyway thanks again.

  • Like 3
Link to comment
Share on other sites

2. I noticed the images uploaded to the .temp directory don't get deleted. With extensive use this will lead to lots of wasted server space as the .temp folder gets more and more bloated with duplicate images. Is there a way to solve this?

Here is code from Ryan in a post of this thread:

 // Run photo upload
foreach($files as $filename) {
  $pathname = $upload_path . $filename;
  $np->contact_photo->add($pathname);
  $np->message("Added file: $filename");
  unlink($pathname);  // This deletes the uploaded file after it was added to the new page ($np)
}

unlink();

If you work with another code, you also add the files to the page. After that you have to unlink('the temp-file');

  • Like 1
Link to comment
Share on other sites

Greetings,

When I started this discussion, my goal was to pull together various renditions of code that were being discussed in forum discussions and create a single full example people can use to make front-end page submissions, and to also do this with photos.  This is a vital part of building the kinds of sites that are really exciting with ProcessWire.

I have now used the techniques detailed in this discussion for several projects, including one where I am building a completely custom front-end interface with page submission and page editing (more on that soon).

1. The code as is makes the assumption that each user will only ever upload one image...
2. I noticed the images uploaded to the .temp directory don't get deleted. With extensive use this will lead to lots of wasted server space as the .temp folder gets more and more bloated with duplicate images. Is there a way to solve this?

In my first post in this discussion, I have the "unlink($pathname)" included in the routine.  This removes the temporary image.  Horst makes reference to the same code posted by Ryan.

HANDLING MULTIPLE FILE-UPLOAD FIELDS IN THE SAME FORM

Since starting this discussion, I have also added multiple file uploads in the same form.  The steps are pretty easy.

Below is my original code from post #1, with additional code for a second file-upload field (indicated with "[NEW CODE]"):

<?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'); // Reference field name in HTML form that uploads photos
$contact_photo->setMaxFiles(5);
$contact_photo->setOverwrite(false);
$contact_photo->setDestinationPath($upload_path);
$contact_photo->setValidExtensions(array('jpg', 'jpeg', 'png', 'gif'));

// Second wire upload (other_photos) [NEW CODE]
$other_photos = new WireUpload('other_photos'); // Reference field name in HTML form that uploads photos
$other_photos->setMaxFiles(10);  // Allow 10 other photos
$other_photos->setOverwrite(false);  // Use the temporary location set above
$other_photos->setDestinationPath($upload_path);
$other_photos->setValidExtensions(array('jpg', 'jpeg', 'png', 'gif'));

// execute upload and check for errors
$files = $contact_photo->execute();
$other_files = $other_photos->execute(); // [NEW CODE]

// 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;
}

// Set up submissions in the ProcessWire page tree
$np = new Page(); // create new page object
$np->template = $templates->get("contact_submission"); // Set template for pages created from form submissions
$np->parent = $pages->get("/customer-service/contact-us/contact-submission-listing/"); // Set parent for pages created from form submissions

// Send form submissions through ProcessWire sanitization and apply each to a template field for the new page
$np->of(false);
$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 for "contact_photo"
foreach($files as $filename)
{
$pathname = $upload_path . $filename;
$np->contact_photo->add($pathname);
$np->message("Added file: $filename");
unlink($pathname);
}

// Run photo upload for "other_photos" [NEW CODE]
foreach($other_files as $other_file)
{
$pathname = $upload_path . $other_file;
$np->other_photos->add($pathname);
$np->message("Added file: $other_file");
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}
?>

And here is the updated submission form that would connect with the above code:

<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" />
<input type="file" name="other_photos[]" multiple />
<button type="submit">Submit</button>
</form>

Points to keep in mind:

1. Add a WireUpload action for each photo-upload field, setting the rules for each one.

2. Add a separate "execute" action for each photo-upload field.

3. Add a separate foreach loop for each photo field you need.

4. If the file-upload field is for multiple files, make sure that the setMaxFiles() action matches what you set in the admin panel for that field.

5. In your form, if the file-upload field is for multiple files, specify the name with square brackets, and add the "multiple" designation.

Thanks,

Matthew

  • Like 3
Link to comment
Share on other sites

Gretings,

As a test, I just removed the "unlink" from one of my foreach loops, but left it in the other one.  It seems to work as expected.  If I have "unlink" at the end of the loop, the files are removed; if I don't include it, the files are not removed.  It's strange that it isn't working for you.

I actually am wondering whether in some cases it might be a good idea to keep the temporary files?  In those situations, I'm considering building a new sub-folder in the temp directory for each page -- maybe auto-create it based on the page ID?

Thanks,

Matthew

Link to comment
Share on other sites

Gretings,

As a test, I just removed the "unlink" from one of my foreach loops, but left it in the other one.  It seems to work as expected.  If I have "unlink" at the end of the loop, the files are removed; if I don't include it, the files are not removed.  It's strange that it isn't working for you.

I actually am wondering whether in some cases it might be a good idea to keep the temporary files?  In those situations, I'm considering building a new sub-folder in the temp directory for each page -- maybe auto-create it based on the page ID?

Thanks,

Matthew

Yes. I didn't even see the unlink when I incorporated the code but after reading yours and Horst's post i double checked. It's all in order and works like a charm with the exception that it doesn't delete. It's not a big deal as the server space is more than enough to accommodate the images and I can manually remove them. In my case it is a photo contest and entrants will be uploading high res images so I will have to monitor it.

Since the submissions are open to the public now (it was a rush which is why I was so happy to see complete code ready to use out of the box) I won't trouble shoot much until the deadline expires.

EDIT: I did a new test with a low res image (about 82k total) and it did delete on completion. Maybe it is working just not so well with the large files I was testing with previously.

Link to comment
Share on other sites

Thanks for posting this Matthew. A couple things I found that I wanted to mention, since this is a front-end form: 

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

You want your temporary upload path to be a non web accessible path. Also, if you've got multiple users uploading at the same time, it seems like there is potential for filename collisions, though not sure how big of a concern that is in this case. But you can at least make the directory non web accessible by preceding it with a period, i.e. ".contact_files" rather than "contact_files". 

$other_photos->setValidExtensions(array('jpg', 'jpeg', 'png', 'gif'));

Keep in mind that WireUpload is only validating the extension. It is not telling you for sure that it's an image. It could very well be some kind of executable with a jpg extension. I don't necessarily know how such a thing could be exploited, other than that I'm not so comfortable with letting a front-end user upload files that end up untouched in a publicly accessible directory. What would probably be safer is to make a new copy of any uploaded images before adding them to the page, and validate the size is within allowed limits while you are at it. 

$error = '';
$imageInfo = getimagesize($pathname);
if($imageInfo) list($width, $height) = $imageInfo; 

if(!$imageInfo) {
  $error = "Image is not valid";
} else if($width < 105 || $height < 105) {
  $error = "Image is too small";
} else if($width > 1600 || $height > 1200) {
  $error = "Image is too big";
} else {
  // create your own, slightly smaller copy
  $sizer = new ImageSizer($pathname); 
  if(!$sizer->resize($width-10, $height-10)) $error = "Unable to resize image";
}

if($error) {
  unlink($pathname); 
  echo "<p>Error: $error</p>";
} else {
  // add image to page
}
  • Like 4
Link to comment
Share on other sites

  • 10 months later...

 Try altering the following:

$pathname = $upload_path . $filename; 

to

$pathname = $upload_path.$filename; 

 no space between _path and $filename

@videokid: Sorry, but this is nonsense.

And for better readability you should use the first one with spaces.

  • Like 1
Link to comment
Share on other sites

Maybe...

but could you explain why that did the trick for me?

As I have noticed more then once....

$upload_path . $filename

file is still in the folder

$upload_path.$filename

file is gone in the temp folder...

About readability, I guess that's personal, to be honest strings and dots I NEVER use spaces, I simply don't like that...

Grtz...

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...