Jump to content

File Upload inside Custom Module


Recommended Posts

Creating a module to add records to an external database. All the "text" form fields work perfectly. Now I am at the point of trying to do file uploads for the listing. I want to upload and save the images into a directory inside the Files directory. In part I have:

		//Add pictures
		$f = $this->modules->get("InputfieldFile");
		$f->label = 'Property Picture #1 Import';
		$f->attr('name','prop_pix_1');
		$f->extensions = 'jpg jpeg png gif';
		$f->maxFiles = 1;
		$f->descriptionRows = 0;
		$f->columnWidth = 25;
		$f->overwrite = true;
		$f->required = false;
		$f->destinationPath = $dPath;
		$form->add($f);

And then in the processor section:

print_r($image);

and am only getting:

Array ( [0] => IMG_2752.JPG )

Shouldn't I get a lot more data? Obviously with only that little bit of info I can't save the file:

move_uploaded_file($_FILES["prop_pix_1"]["tmp_name"], $dPath.'test.jpg');

 

Link to comment
Share on other sites

I was struggling with that recently too. It doesn't seem to be too well documented. If I remember correctly you could do the following

<?php
....
$form->add($f);

if($this->input->requestMethod('POST')) {
  // validate and upload the file to /site/assets/files
  $form->processInput($this->input->post);
  
  // access file
  $uploadedFile = $form->get('prop_pix_1')->value;
}

 

  • Like 1
Link to comment
Share on other sites

Whenever you need something think "Where is it done in the core" and grab ideas from there ? You can see how it is done in ProcessModuleInstall.php

I've done it that way recently:

    $tmpDir = $this->files->tempDir('upload_csv');

    /** @var InputfieldFile $f */
    $f = $this->modules->get('InputfieldFile');
    $f->attr('id+name', "upload_csv");
    $f->label = 'foo';
    $f->icon = 'download';
    $f->extensions = "csv";
    $f->maxFiles = 1;
    $f->descriptionRows = 0;
    $f->destinationPath = $tmpDir;

// when the form is submitted:

    /** @var WireUpload $ul */
    $ul = $this->wire(new WireUpload($f->name));
    $ul->setValidExtensions(explode(" ", $f->extensions));
    $ul->setMaxFiles($f->maxFiles);
    $ul->setOverwrite($f->overwrite);
    $ul->setDestinationPath($f->destinationPath);
    $ul->setExtractArchives(false);
    $ul->setLowercase(false);

    $files = $ul->execute();
    if(count($files)) {
      $file = $f->destinationPath . reset($files);
      // do something with your file
    }
    else {
      $this->error('No uploads found');
    }

There is an issue with ajax loaded fields: https://github.com/processwire/processwire-issues/issues/885

And it seems that ZIP is always added to the allowed file endings... Can anybody confirm that?

PS: Don't you use TracyDebugger? It's a lot better than using print_r() ? 

  • Like 3
Link to comment
Share on other sites

12 hours ago, bernhard said:

PS: Don't you use TracyDebugger? It's a lot better than using print_r() ? 

Tried installing TracyDebugger and while it did give me some things to clean up it won't let my form process at all. Keeps throwing an error for missing vars, etc. I use the same form to edit and enter new listings. Earlier in the code I check for a value in a $_GET and if there I populate $row (which was previously defined as an array).

           //Add Property Street Address
100:            $f = $this->modules->get("InputfieldText");
101:            $f->label = 'Property Address';
102:            $f->attr('name','prop_add');
103:            $f->columnWidth = 40;
104:            $f->requiredLabel = '*';
105:            $f->required = true;
106:            $f->requiredAttr = true;
107:            $f->placeholder = 'Example: 123 Main Street';
108:            $f->value = $row['st_address'];    
109:            $form->add($f);

This block of code is throwing an error with Tracy turned on: Undefined index: st_address

I've always done things this way. Is a best practices kind of thing?

 

Figured it out! Too many iterations of testing and deleted one too many lines.

Edited by SoccerGuy3
Figured it out
Link to comment
Share on other sites

@bernhard @MrSnoozles

Thanks for your help. Your pointers along with a 7 year old post by @Soma got it for me. The piece that I was missing I believe is that I had it in my head that the building of the form and the processing of the form needed to be in separate functions. Once I combined the two functions into one, did some further debugging (yes with Tracy), I was able to get a photo uploaded, named and saved where I wanted it!! Whew! Waaaayyyyyyy too many hours spent on this, but finally got it. Thanks again.

Link to comment
Share on other sites

21 hours ago, bernhard said:

 

 

21 hours ago, bernhard said:

    $tmpDir = $this->files->tempDir('upload_csv');

    /** @var InputfieldFile $f */
    $f = $this->modules->get('InputfieldFile');
    $f->attr('id+name', "upload_csv");
    $f->label = 'foo';
    $f->icon = 'download';
    $f->extensions = "csv";
    $f->maxFiles = 1;
    $f->descriptionRows = 0;
    $f->destinationPath = $tmpDir;

// when the form is submitted:

    /** @var WireUpload $ul */
    $ul = $this->wire(new WireUpload($f->name));
    $ul->setValidExtensions(explode(" ", $f->extensions));
    $ul->setMaxFiles($f->maxFiles);
    $ul->setOverwrite($f->overwrite);
    $ul->setDestinationPath($f->destinationPath);
    $ul->setExtractArchives(false);
    $ul->setLowercase(false);

    $files = $ul->execute();
    if(count($files)) {
      $file = $f->destinationPath . reset($files);
      // do something with your file
    }
    else {
      $this->error('No uploads found');
    }

 

bernhard  isnt InputfieldFile already upload file when form submit, why you call wireUpload again ?

Link to comment
Share on other sites

Just try it without and see what happens: Nothing ? If you are not on a page edit screen PW doesn't know where to upload your file so you need do tell it manually. You could also do a move_uploaded_file() but that does not sanitize your data.

Link to comment
Share on other sites

Again, thanks for your help, the form is working great. I would like to do an upgrade of sorts and add to the image upload box. I would like to show the image if it exists. I can determine the file name, but how to insert it into the form builder? Here's my form builder code for the image field:

		$f = $this->modules->get("InputfieldFile");
		$f->label = 'Property Picture #1 Import';
		$f->attr('name','prop_pix_1');
		$f->extensions = 'jpg jpeg png gif';
		$f->maxFiles = 1;
		$f->descriptionRows = 0;
		$f->columnWidth = 25;
		$f->overwrite = true;
		$f->required = false;
		$f->destinationPath = $dPath;
		$form->add($f);

I tried putting an image tag into to the "description" tag, but all I got in return was the code displayed on screen. Any hints/ideas?

Link to comment
Share on other sites

  • 2 years later...
On 5/22/2019 at 5:40 PM, SoccerGuy3 said:

@bernhard @MrSnoozles

Thanks for your help. Your pointers along with a 7 year old post by @Soma got it for me. The piece that I was missing I believe is that I had it in my head that the building of the form and the processing of the form needed to be in separate functions. Once I combined the two functions into one, did some further debugging (yes with Tracy), I was able to get a photo uploaded, named and saved where I wanted it!! Whew! Waaaayyyyyyy too many hours spent on this, but finally got it. Thanks again.

@SoccerGuy3 Would you be able to post the relevant code that got this working for you? Been struggling for a couple days getting this to work. After I call for the WireUpload->execute(), the files array that it returns is always empty. 

Link to comment
Share on other sites

7 minutes ago, rastographics said:

@SoccerGuy3 Would you be able to post the relevant code that got this working for you? Been struggling for a couple days getting this to work. After I call for the WireUpload->execute(), the files array that it returns is always empty. 

		// form was submitted so we process the form
		if($this->input->requestMethod('POST')) {

			//Set directory for photos
			$target_dir = $this->wire('config')->paths->files . '_listings/';

			// user submitted the form, process it and check for errors
		    $form->processInput($this->input->post);

			// do with the form what you like
			if($_POST['prop_delete'] == 'N') {

				//create the listing_id or pull from hidden field on update
				if(isset($_POST['listing_id'])) {
					$propNum = mysqli_real_escape_string($dbCon, $_POST['listing_id']);
				} else {
					$propNum = 'CDR'.uniqid(true);
				}

				// Database Table Insert Stuff goes here

			} else {
				//Delete box checked, removed listing first...
			}

			//Process photos
			function convertImage($originalImage, $outputImage, $quality) {
			    // jpg, png, gif or bmp?
			    $exploded = explode('.',$originalImage);
			    $ext = $exploded[count($exploded) - 1];

			    if (preg_match('/jpg|jpeg/i',$ext)) {
			        $imageTmp=imagecreatefromjpeg($originalImage);
					$outputImage = str_ireplace('jpeg','jpg',$outputImage);
			    } else if (preg_match('/png/i',$ext)) {
			        $imageTmp=imagecreatefrompng($originalImage);
					$outputImage = str_ireplace('png','jpg',$outputImage);
			    } else if (preg_match('/gif/i',$ext)) {
			        $imageTmp=imagecreatefromgif($originalImage);
					$outputImage = str_ireplace('gif','jpg',$outputImage);
			    } else if (preg_match('/bmp/i',$ext)) {
			        $imageTmp=imagecreatefrombmp($originalImage);
					$outputImage = str_ireplace('bmp','jpg',$outputImage);
			    } else {
			        return 0;
				}

			    // quality is a value from 0 (worst) to 100 (best)
			    imagejpeg($imageTmp, $outputImage, $quality);
			    imagedestroy($imageTmp);
				unlink($originalImage);

			    return 1;
			}

			$oldumask = umask(0);
			mkdir($target_dir.$propNum, 0777); // or even 01777 so you get the sticky bit set
			umask($oldumask);

			foreach ($form->get('prop_pix')->value as $pix) {
				//ensure image is jpg image and delete original upload
				convertImage($target_dir.$pix,$target_dir.$propNum.'/'.$pix,70);
			}

			//Update Database Table
			if(mysqli_query($dbCon, $sql)){
				$this->wire('session')->redirect("/site-admin/setup/add-a-listing/");
				$success = "Record added successfully.";
			} else{
				$success = "ERROR: Not able to execute $sql. " . mysqli_error($link);
				echo "ERROR: Not able to execute $sql. " . mysqli_error($link); exit();
			}

			//echo $success; exit();

			// Close connection
			mysqli_close($dbCon);

			//Return to controller
			return $success;
		} else {
			return $form->render();
		}

I think this is what you are looking for. Holler if not. Note: I removed all the DB interaction stuff as it was 100 lines long and not on topic.

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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...