Upload With Preview

Allikas: Juhised

Preview of an image[muuda]

It is very simple to show a preview of a text variable. For instance, one changes the caption of an offer and wants to see the preview how will the changed caption look like before he or she submits the change. But how can we handle an image preview? It is obvious that every image must bu uploaded before we can show it. Can this happen automatically? We have to submit a form in order to upload an image file. But submitting a form causes at least a refresh of the page. That means the form submitting must happen somewhere else not at the page where is the form with other data. „Somewhere else“ can be in a different frame. We use „iframe“ in order to show an image preview. Shortly, the image to show will be uploaded into the „previews“' folder. The file will be renamed so that the name contains the information about the current user and object. Every previous file from the same user and object will be deleted. The preview will be shown on the parent page. Submitting the form with other object's data causes the latest previewed image to be moved into the object's images' folder. If there is no preview available, nothing will be done with the object's image information.

As follows, I explain the things that need to be done in order to implement the image preview functionality.

Template[muuda]

The template where we enter the object data must contain an iframe with the following information in the source address:

  1. the images-module takes care of image uploading (line 18);
  2. there is a special function for this (line 18);
  3. the ID-s of the placeholders where the preview must be shown must be given. One of them is the placeholder close to the upload iframe and another one can be a placeholder in the object's preview (line 18);
  4. for the validation, the path to the final folder of the image must be given (line 18);
  5. the type of the object (line 18);
  6. the ID of the object (line 18);
  7. the ID of the current system user (line 18).
  1. 							<a
  2. 					href="?module=schoolcards&amp;function=showFile&amp;idSchoolCard={ID-OF-SCHOOL-CARD}&amp;type=doc"
  3. 									target="doc">
  4.  
  5. 								<img id="imgODocInForm"
  6. 										src="{SRC-OF-IMAGE-OF-DOCUMENT-IN-FORM}"
  7. 										alt="{ALT-OF-IMAGE-OF-DOCUMENT-IN-FORM}"
  8. 										title="{ALT-OF-IMAGE-OF-DOCUMENT-IN-FORM}"
  9. 										{SIZES-OF-IMAGE-OF-DOCUMENT-IN-FORM}/>
  10.  
  11. 							</a>
  12.  
  13. 							<input id="imgODocInFormHidden"
  14. 									name="imgODocInFormHidden" type="hidden"
  15. 									value="{NAME-OF-IMAGE-OF-DOCUMENT-IN-FORM}"/>
  16.  
  17. 							<iframe
  18. src="?module=images&amp;function=buildFormForUploadingImage&amp;idsOImgFields[]=imgODocInForm&amp;idsOImgFields[]=iconForRemovingImgFromSchoolCard&amp;pathToFinalFolder={PATH-TO-FINAL-FOLDER}&amp;obj=document_of_school_card&amp;idObj={ID-OF-SCHOOL-CARD}&amp;pathToImages={PATH-TO-IMAGES}{CONDITIONS}"
  19. 									frameborder="0"></iframe>
  20.  
  21. 							<img id="iconForRemovingImgFromSchoolCard"
  22. 									alt="Eemalda dokumendipilt!"
  23. 									title="Eemalda dokumendipilt!"
  24. 									class="DrasticImg"
  25. 									onclick="delImgFromSystem('{URL}', ['imgODocInForm'], this)"
  26. 									onmouseover="this.src = '{PATH-TO-IMAGES}del2.gif';"
  27. 									onmouseout="this.src = '{PATH-TO-IMAGES}del.gif';"
  28. 									src="{PATH-TO-IMAGES}del.gif"
  29. 									style="visibility: {VISIBILITY-OF-ICON-FOR-REMOVING-IMAGE-FROM-SCHOOL-CARD};"/>

The icon for removing must be displayed when there is an image set that can be removed. If there is no image bound to the object and no image loaded for the preview then the icon for removing must be hidden. That icon must have an ID (line 21) in order to handle its visibility. The alternative text (lines 22-23) is object-specific. Clicking the icon must call a specific method (line 25) that removes the image from everywhere in the system. The visibility will be handled via the style (line 29) attribute of the icon.

View[muuda]

We set the conditions the file must fulfil:

		$conditions = array (
			VALIDATING_TYPE_FILE,
			VALIDATING_TYPE_UNIQUE_FILE
		);

We get the values either from validated fields or the database:

		$nameOImgODocInForm = '';
		$pathToImgODocInForm = '';
 
		if (!isset ($validatedElements) && isset ($schoolCard) && isset ($schoolCard->doc))
		{
			$nameOImgODocInForm = $schoolCard->doc;
 
			$pathToImgODocInForm = Support::getPathToSchoolCards(TRUE);
		}
		else if (isset ($validatedElements) && isset ($validatedElements['imgODocInFormHidden']->value))
		{
			$nameOImgODocInForm = $validatedElements['imgODocInFormHidden']->value;
 
			if (strpos($nameOImgODocInForm, 'preview') === FALSE)
			{
				$pathToImgODocInForm = Support::getPathToSchoolCards(TRUE);
			}
			else
			{
				$pathToImgODocInForm = Support::getPathToPreviews(TRUE);
			}
 
		}

We create an image object:

		$isImgSet = ($nameOImgODocInForm != '');
 
		require_once dirname(__FILE__) . '/../pildid/Pic.php';
 
		$imgOSchoolCard = new Pic(
			$isImgSet,
			$pathToImgODocInForm,
			$isImgSet ? $nameOImgODocInForm : NULL,
			100
		);

For the form, we set like this:

			'ALT-OF-IMAGE-OF-DOCUMENT-IN-FORM'                       => $imgOSchoolCard->alt,
			'CONDITIONS'                                             =>
					'&amp;conditions[]=' . implode('&conditions[]=', $conditions),
			'HREF-OF-IMAGE-OF-DOCUMENT-IN-FORM'                      => $imgOSchoolCard->href,
			'HREF-OF-IMAGE-OF-USER-IN-FORM'                          => $userImgOnSchoolCard->href,
			'ID-OF-HUMAN'                                            => $_SESSION['User_ID'],
			'ID-OF-SCHOOL-CARD'                                      =>
					isset ($schoolCard) ? $schoolCard->idSchoolCard : 0,
			'NAME-OF-IMAGE-OF-DOCUMENT-IN-FORM'                      => $nameOImgODocInForm,
			'NAME-OF-IMAGE-OF-USER-IN-FORM'                          => $nameOImgOUserInForm,
			'PATH-TO-FINAL-FOLDER'                                   => Support::getPathToSchoolCards(FALSE),
			'PATH-TO-IMAGES'                                         => $this->pathToImages,
			'SIZES-OF-IMAGE-OF-DOCUMENT-IN-FORM'                     => $imgOSchoolCard->sizesOImg,
			'SRC-OF-IMAGE-OF-DOCUMENT-IN-FORM'                       => $imgOSchoolCard->src,
			'URL'                                                    => sprintf(
				'?module=schoolcards&function=removeFile&type=doc&idSchoolCard=%1$u',
				isset ($schoolCard) ? $schoolCard->idSchoolCard : 0 // 1
			),
			'VISIBILITY-OF-ICON-FOR-REMOVING-IMAGE-FROM-SCHOOL-CARD' =>
					(isset ($schoolCard) && isset ($schoolCard->doc)) ? 'visible' : 'hidden'

Controller[muuda]

In the controller, one has to ensure that after the removing, no redirection will happen:

  1. 					$schoolCard->removeFile($_GET['type']);
  2.  
  3. 					exit;

That is all! The core takes care of everything else.

After we are satisfied with the preview image, we may want to confirm the form changes. In the constructor, we do not pass the value of $_FILES because we have uploaded the image already. Instead we pass the value of the hidden field:

						isset ($_POST['imgODocInFormHidden']) ? $_POST['imgODocInFormHidden'] : NULL,

Model[muuda]

But it is only all for the previewing. We have to submit the form as well and the system has to copy the actual preview image into the object's images' folder. Therefore, we had the hidden input field whose ID and name have the same value that the main placeholder but with Hidden at the end.

The class must have a variable for the file:

	/**
	 * @var string the name of the file of the document that shows the student's status
	 */
	var $doc;

The model has to have an array of the image sizes we need later on:

	/**
	 * @var array the array with the possible image sizes
	 */
	var $imageSizes = array (
		array (
			'width'  => 100,
			'height' => 100
		)
	);

The constructor assigns the posted hidden value to the object's variable:

				$this->doc = func_get_arg(3);

Please change the setter method, too!

We have to remember the last value:

			new ElementToValidate('picOOfferInFormHidden', $this->pic),

Some changes must be done in the so-called uploading part as well:

  1. 				$previousValues = new SchoolCard($this->idSchoolCard);
  2. 				$uploaded = FALSE;
  3.  
  4. 				if (isset ($this->pic) && (stripos($this->pic, 'preview') > 0))
  5. 				{
  6.  
  7. 					$fileNameParts = preg_split(
  8. 						'/preview/i',
  9. 						$this->pic,
  10. 						-1,
  11. 						PREG_SPLIT_OFFSET_CAPTURE
  12. 					);
  13.  
  14. 					$decodedFileNameOUserImg = urldecode($fileNamePartsOPic[1][0]);
  15.  
  16. 					$uploadfile = Support::getPathToSchoolCards(TRUE) . $decodedFileNameOUserImg;
  17.  
  18. 					$result = $previousValues->removeFile('pic');
  19.  
  20. 					$picUploaded = copy(
  21. 						Support::getPathToPreviews(TRUE) . $this->pic,
  22. 						$uploadfile
  23. 					);
  24.  
  25. 					$imgInfo = getimagesize($uploadfile);
  26.  
  27. 					if (!empty ($imgInfo))
  28. 					{
  29. 						$pathToTransformed = Support::getPathToSchoolCards(FALSE) . $decodedFileNameOUserImg;
  30.  
  31. 						require_once dirname(__FILE__) . '/../pildid/Pic.php';
  32.  
  33. 						Pic::smart_resize_image(
  34. 							$uploadfile,
  35. 							$this->imageSizes,
  36. 							TRUE,
  37. 							'file',
  38. 							FALSE,
  39. 							FALSE,
  40. 							$pathToTransformed
  41. 						);
  42.  
  43. 					}
  44.  
  45. 				}
  46.  
  47. 				if ((isset ($this->pic) && ($this->pic != '') && $picUploaded ||
  48. 						!isset ($this->pic) || ($this->pic == '') || (stripos($this->pic, 'preview') == 0)) &&
  49. 						(isset ($this->doc) && ($this->doc != '') && $docUploaded ||
  50. 						!isset ($this->doc) || ($this->doc == '') || (stripos($this->doc, 'preview') == 0)))
  51. 				{
  52. 					return SUCCESS;
  53. 				}
  54. 				else
  55. 				{
  56. 					require_once dirname(__FILE__) . '/../Error.php';
  57.  
  58. 					new Error(
  59. 						ERROR_TYPE_USUAL_ROLLBACK_UNLOCK,
  60. 						'Pilti ei saa üles laadida.'
  61. 					);
  62.  
  63. 					exit;
  64. 				}

As you see, there is no real upload anymore but moving the file from the previews-folder into the object's folder. Next, we do the query a little bit different in compare to the usual uploading process (lines 53-55):

  1. 					$queryStringForUpdatingUser = sprintf(
  2. 						'UPDATE Human SET Human.validUntil = "%1$s"%2$s%3$s
  3. 						WHERE Human.idHuman = %4$s',
  4. 						$this->tsWhenExpiring, // 1
  5. 						(isset ($this->doc) && ($this->doc != '') && (stripos($this->doc, 'preview') > 0)) ?
  6. 								sprintf(
  7. 									', Human.doc = "%1$s"',
  8. 									$decodedFileNameODocImg // 1
  9. 								) : '', // 2
  10. 						(isset ($this->pic) && ($this->pic != '') && (stripos($this->pic, 'preview') > 0)) ?
  11. 								sprintf(
  12. 									', Human.pic = "%1$s"',
  13. 									$decodedFileNameOUserImg // 1
  14. 								) : '', // 3
  15. 						$this->idHuman // 4
  16. 					);

The method for removing the file:

	/**
	 * This function removes a file of the school card.
	 * 
	 * @uses Connection for the database connection
	 * @uses Error to handle errors
	 * @uses ERROR_TYPE_SIMPLY_ROLLBACK_UNLOCK to handle errors
	 * @uses Support to get the path to files
	 */
	function removeFile($type)
	{
		$connection = new Connection();
 
		$queryStringOfRemovingFile = sprintf(
			'UPDATE Human SET Human.%1$s = NULL
					WHERE Human.idHuman = %2$u',
			$type, // 1
			$this->idHuman // 2
		);
 
		$queryResultsOfRemovingFile = $connection->db_object->query(
			$queryStringOfRemovingFile
		);
 
		if (MDB2::isError($queryResultsOfRemovingFile))
		{
			require_once dirname(__FILE__) . '/../Error.php';
 
			new Error(
				ERROR_TYPE_SEND_MAIL,
				'Faili eemaldamine ebaõnnestus.',
				$queryResultsOfRemovingFile,
				$connection
			);
 
			exit;
		}
 
		$connection->disconnect();
 
		$decodedFileName = urldecode($this->$type);
 
		if ($decodedFileName != '')
		{
 
			foreach (glob(sprintf(
				'%1$s%2$s*',
				Support::getPathToSchoolCards(FALSE), // 1
				$decodedFileName // 2
			)) as $filename)
			{
 
				if (!unlink($filename))
				{
					require_once dirname(__FILE__) . '/../Error.php';
 
					new Error(
						ERROR_TYPE_SEND_MAIL,
						sprintf(
							'Töödeldud faili <i>%1$s</i> eemaldamine ebaõnnestus.',
							$filename // 1
						)
					);
 
					exit;
				}
 
			}
 
			$filename = sprintf(
				'%1$s%2$s',
				Support::getPathToSchoolCards(TRUE), // 1
				$decodedFileName // 2
			);
 
			if (!unlink($filename))
			{
				require_once dirname(__FILE__) . '/../Error.php';
 
				new Error(
					ERROR_TYPE_SEND_MAIL,
					sprintf(
						'Algupärase faili <i>%1$s</i> eemaldamine ebaõnnestus.',
						$filename // 1
					)
				);
 
				exit;
			}
 
		}
 
	}