Jump to content
MatthewSchenker

Create Pages (with File-Upload Field) via API

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

Share this post


Link to post
Share on other sites

And what do you think it is? What have you done to debug the upload path, now that you know it's something wrong there? The answer is even in the error itself.

Share this post


Link to post
Share on other sites

Greetings,

OK, I am willing to take the risk here of making an obvious error.

Just confirming that this code:

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

Corresponds to this directory:

/site/assets/files/temp

Thanks,

Matthew

Share this post


Link to post
Share on other sites

Confirming means you tested it? Echo it out in your code and add line "exit;" after it.

Share this post


Link to post
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

Share this post


Link to post
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.

Share this post


Link to post
Share on other sites

Edit: Just wanted to add that hardcoding the paths isn't exactly a good solution. Better use the config vars.

Share this post


Link to post
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

Share this post


Link to post
Share on other sites

Glad to hear you got it working.

Share this post


Link to post
Share on other sites

I'll post the complete code shortly.

 

 

Thanks,

Matthew

That would be great!

Share this post


Link to post
Share on other sites

I posted 3 or was it 4 examples codes that work already, so what is the deal ? :P

Spent lots of hours btw...

  • Like 1

Share this post


Link to post
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

Share this post


Link to post
Share on other sites

Maybe remove the code in the first post and link to a working code at least. :)

  • Like 1

Share this post


Link to post
Share on other sites

Maybe remove the code in the first post and link to a working code at least. :)

Why not :)

Anyway I'm finding already my way to go through this topics thanks to you.

Share this post


Link to post
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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
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

Share this post


Link to post
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

Share this post


Link to post
Share on other sites

I have the unlink ($pathname) in the code I'm using. The uploaded files are not being deleted though. Funny. I'll have to reexamine it. Everything works except for the file removal at the end.

Share this post


Link to post
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

Share this post


Link to post
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.

Share this post


Link to post
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

Share this post


Link to post
Share on other sites

@Digitex

Try altering the following:

$pathname = $upload_path . $filename; 

to

$pathname = $upload_path.$filename; 

no space between _path and $filename

Greetings.

Share this post


Link to post
Share on other sites

 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

Share this post


Link to post
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...

Share this post


Link to post
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

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By quickjeff
      Hi Guys, 
      I have been debugging a site for the last 2 hours and cannot solve the issue. 
      I have a site running on 3.0.148. 
      I installed the Kongondo Blog module and was updating the templates to include the website style. 
      Once everything was set and done, I checked the page tree to see an error appear. 
      Template must be assigned a name before 'filename' can be accessed
      The same error appears in templates. 
      Debugging Steps
      I checked the templates in the server to ensure I didnt accidentally delete the namespace.  Deleted cache in browser and server under assets Still no go. 
      Any help is appreciated. 
      Thanks! 
    • By benbyf
      I have a a form in my site footer that can be accessed anywhere on site, I've added the form in the _inc.php file and added the render in the pages footer.php. However, this works well on the homepage e.g. you can submit said form and get a thank you on reload, doesnt work at all on other pages... Just lots like a fresh reload. Any thing im doing wrong here or ways to diagnose as there isn't an error log for formbuilder etc...?
    • By Spyros
      Hello
      I'm having a strange issue with the $page->find(), for some reason I'm missing some of the pages from the results. I found then that I was missing all the pages with the same "PAGE NAME". Is it a bug or am I missing something?
      PS 
      If I change the "PAGE NAME" of one of the missing ones then I'm retrieving the page without any problem.
      Thank you
    • By Guy Incognito
      This short script loops through some images from an XML feed and pushes new ones to an image field. It all works perfectly, except for some reason the last image (only) in the loop each time doesn't receive the image description... can everyone spot why? TIA! 🙂 
      foreach ($propertyImages as $img) { $fileName = trim($img[0]); if ( !empty($fileName) ) { $imgPath = '../property_data/'.$fileName; if(file_exists($imgPath) && !in_array(strtolower($fileName),$currentImages)) { $p->property_images->add($imgPath); $p->save(); $newImg = $p->property_images->last(); $newImg->description = $img[1]; $p->save(); } } }  
    • By MateThemes
      Hello everyone!
      I am working with Processwire since some time. But some topics are quite hard for me.
      I have a Portfolio (Gallery) Page.
      I am build a template with Portfolio Index and pages with portfolio entries.
      Structure:
      Portfolio Index
      -- Portfolio Entry
      -- Portfolio Entry
      and so on.
      Portfolio Entry has an Image field with max 12 images and are accessible Templates. 
      Now I want to display the single Portfolio Entry on the Portfolio Index and Paginate them. In the index page all images of a single Entry page should be displayed (I should not be organized as albums, where a random image of the portfolio entry should be displayed). 
      I have no clue to achieve this. May someone could give me an advice.
      Thank you in advance!
×
×
  • Create New...