Metatag Warning: Be Sure to Upload Your Image to Your Cms or Host.

17 May 2019: This commodity and the lawmaking were updated for PHP7 compatibility.

In my tutorial Build a CMS in an Afternoon with PHP and MySQL, I showed how to build a simple simply useful content direction system with PHP and MySQL. I likewise showed how to extend the CMS to allow article categories.

In this tutorial, yous'll expect at another way to extend the CMS. You'll take the original CMS code, and change it so that the administrator can upload an epitome for each article. So, when a visitor views an article page, the CMS volition display the image at the start of the article. In addition, our CMS will generate a smaller thumbnail version of each commodity image, and display this thumbnail next to each commodity headline in the homepage and article archive pages.

Yous can see the finished result by clicking the View Demo link higher up. Discover the thumbnail images next to each article headline. Click a headline or thumbnail to view the respective article, along with the full-size article image.

The plan

Nosotros'll kickoff with the original CMS lawmaking from Build a CMS in an Afternoon with PHP and MySQL, and modify it to include the prototype upload characteristic. Here are the steps we'll demand to acquit out:

  1. Create a couple of folders to store the article images
  2. Alter various paradigm-related settings to the CMS config file
  3. Modify the database to store the image filename extensions
  4. Modify the Article class to handle images
  5. Change admin.php to handle image upload and deletion
  6. Modify the front-stop templates to display the article images and thumbnails
  7. Modify the back-end article edit form to let the administrator upload an image, view the article image, and delete the article image
  8. Tweak the CMS stylesheet to style the article images and the new elements in the article edit grade

Ready? Let's become started!

Stride 1: Create the paradigm folders

The kickoff matter to do is create a couple of folders in your website to store the full-size and thumbnail article images.

Open the existing cms folder and you'll run into an images folder containing the sample logo.jpg image. Inside this images folder, create an articles folder. And then, inside the articles folder, create two more folders:

  • fullsize to store the full-size article images
  • thumb to store the smaller thumbnail versions of the commodity images

Next you lot demand to give your spider web server user permission to create files in these 2 folders. Typically on a Linux or Mac organization, you need to change the permissions to 777, similar this:

$ cd images/articles/ $ chmod 777 fullsize $ chmod 777 thumb        

If your CMS is on a remote web server then you can normally prepare these permissions using your FTP software.

Step 2: Edit the config file

The next step is to add some image-related constants to the CMS config file. Open upwards the config.php file in the acme-level cms folder, and add together the new lines highlighted in the code below:

<?php ini_set( "display_errors", true ); date_default_timezone_set( "Commonwealth of australia/Sydney" );  // http://world wide web.php.net/manual/en/timezones.php ascertain( "DB_DSN", "mysql:host=localhost;dbname=cms" ); ascertain( "DB_USERNAME", "username" ); ascertain( "DB_PASSWORD", "password" ); define( "CLASS_PATH", "classes" ); define( "TEMPLATE_PATH", "templates" ); define( "HOMEPAGE_NUM_ARTICLES", five ); define( "ADMIN_USERNAME", "admin" ); define( "ADMIN_PASSWORD", "mypass" ); define( "ARTICLE_IMAGE_PATH", "images/manufactures" ); define( "IMG_TYPE_FULLSIZE", "fullsize" ); define( "IMG_TYPE_THUMB", "thumb" ); define( "ARTICLE_THUMB_WIDTH", 120 ); define( "JPEG_QUALITY", 85 ); require( CLASS_PATH . "/Article.php" );  function handleException( $exception ) {   echo "Deplorable, a problem occurred. Please try later.";   error_log( $exception->getMessage() ); }  set_exception_handler( 'handleException' ); ?>        

Y'all've added the following constants:

  • ARTICLE_IMAGE_PATH defines the path to the article images folder, relative to the height-level CMS binder. (If you desire to store your commodity images somewhere else, change this constant accordingly.)
  • IMG_TYPE_FULLSIZE defines a abiding to represent the "full-size" paradigm blazon. We'll use this in the code whenever we want to indicate a full-size image. This value ("fullsize") is as well used to locate the full-size images binder (images/articles/fullsize), so if you use a different binder name, you'll desire to update this constant likewise.
  • IMG_TYPE_THUMB does a similar job to IMG_TYPE_FULLSIZE, only represents the "thumbnail" image type instead.
  • ARTICLE_THUMB_WIDTH defines the width to use for the article thumbnail images, in pixels. Our image-handling code will use this value when generating the thumbnail versions of article images when they're uploaded.
  • JPEG_QUALITY defines the quality level to use when generating thumbnail versions of JPEG images. The value ranges from 0 to 100, where 100 is the best quality (but largest file size). 85 is a proficient compromise.

Step 3: Alter the database

Safe

You need to make one small change to the articles table in the CMS database. Open up the tables.sql file from the original CMS, and add the line highlighted in the code below:

Drib Tabular array IF EXISTS articles; CREATE TABLE articles (   id              smallint unsigned Not Nix auto_increment,   publicationDate date NOT Zippo,                              # When the article was published   championship           varchar(255) NOT NULL,                      # Full title of the article   summary         text NOT NULL,                              # A brusque summary of the commodity   content         mediumtext NOT Nil,                        # The HTML content of the article   imageExtension  varchar(255) NOT Nil,                      # The filename extension of the article's full-size and thumbnail images    PRIMARY KEY     (id) );        

This line adds a new field called imageExtension to the articles table. This field stores the filename extension of each commodity's uploaded prototype. For case, if the administrator uploads a PNG image so nosotros'll store the value ".png" in the imageExtension field.

What if you already have manufactures in your CMS?

If you load the above tables.sql file into MySQL then information technology will delete any existing articles table in your cms database, and recreate the articles table from scratch. This will delete whatever articles already in your CMS, which is obviously not what you want.

So if y'all already have articles in your CMS database, you want to alter the manufactures table while retaining the existing information in the tabular array. To exercise this, alter your tables.sql file to the following:

Modify TABLE manufactures Add together imageExtension varchar(255) Not Zippo After content;        

Applying the changes

To really create your articles table (or add the new imageExtension field to your existing articles tabular array, as appropriate), you demand to load the tables.sql file into MySQL. To do this, follow the procedure described in Applying the changes in my final tutorial.

To cheque that your changes have been made, first login to MySQL:

mysql -u username -p cms

And then utilise the SHOW TABLES and EXPLAIN commands to check your table schemas in MySQL:

                      mysql> prove tables; +---------------+ | Tables_in_cms | +---------------+ | manufactures      | +---------------+ i row in gear up (0.00 sec)   mysql> explain articles; +-----------------+----------------------+------+-----+---------+----------------+ | Field           | Blazon                 | Nil | Key | Default | Extra          | +-----------------+----------------------+------+-----+---------+----------------+ | id              | smallint(5) unsigned | NO   | PRI | NULL    | auto_increment | | publicationDate | date                 | NO   |     | Naught    |                | | title           | varchar(255)         | NO   |     | Cipher    |                | | summary         | text                 | NO   |     | NULL    |                | | content         | mediumtext           | NO   |     | Cipher    |                | | imageExtension  | varchar(255)         | NO   |     | Aught    |                | +-----------------+----------------------+------+-----+---------+----------------+ 6 rows in fix (0.00 sec)  mysql>                  

Notice the new imageExtension field within the articles table.

You've set up your CMS database so that it's prepare to handle image uploads. Now y'all can showtime modifying the CMS code.

Step 4: Modify the Commodity form

Cogs

Side by side, we need to change the Article class to handle article images. Here'south the updated Commodity.php class file. I've highlighted the lines of lawmaking and then you can encounter what's been added and inverse. Replace the code in your existing cms/classes/Article.php file with this new lawmaking:

<?php  /**  * Class to handle manufactures  */  class Commodity {   // Properties    /**   * @var int The article ID from the database   */   public $id = null;    /**   * @var int When the article is to be / was first published   */   public $publicationDate = null;    /**   * @var string Full title of the article   */   public $title = null;    /**   * @var string A short summary of the commodity   */   public $summary = null;    /**   * @var string The HTML content of the article   */   public $content = null;    /**   * @var string The filename extension of the article'south full-size and thumbnail images (empty string means the commodity has no prototype)   */   public $imageExtension = "";     /**   * Sets the object'south backdrop using the values in the supplied array   *   * @param assoc The holding values   */    public part __construct( $information=array() ) {     if ( isset( $data['id'] ) ) $this->id = (int) $data['id'];     if ( isset( $information['publicationDate'] ) ) $this->publicationDate = (int) $information['publicationDate'];     if ( isset( $information['title'] ) ) $this->title = preg_replace ( "/[^\.\,\-\_\'\"\@\?\!\:\$ a-zA-Z0-9()]/", "", $data['title'] );     if ( isset( $information['summary'] ) ) $this->summary = preg_replace ( "/[^\.\,\-\_\'\"\@\?\!\:\$ a-zA-Z0-9()]/", "", $data['summary'] );     if ( isset( $data['content'] ) ) $this->content = $data['content'];     if ( isset( $information['imageExtension'] ) ) $this->imageExtension = preg_replace ( "/[^\.\,\-\_\'\"\@\?\!\$ a-zA-Z0-9()]/", "", $information['imageExtension'] );   }     /**   * Sets the object's backdrop using the edit form post values in the supplied array   *   * @param assoc The course mail service values   */    public function storeFormValues( $params ) {      // Shop all the parameters     $this->__construct( $params );      // Parse and store the publication date     if ( isset($params['publicationDate']) ) {       $publicationDate = explode ( '-', $params['publicationDate'] );        if ( count($publicationDate) == three ) {         list ( $y, $m, $d ) = $publicationDate;         $this->publicationDate = mktime ( 0, 0, 0, $yard, $d, $y );       }     }   }     /**   * Stores any paradigm uploaded from the edit form   *   * @param assoc The 'paradigm' element from the $_FILES array containing the file upload data   */    public function storeUploadedImage( $prototype ) {      if ( $image['error'] == UPLOAD_ERR_OK )     {       // Does the Article object have an ID?       if ( is_null( $this->id ) ) trigger_error( "Article::storeUploadedImage(): Attempt to upload an image for an Article object that does non have its ID belongings gear up.", E_USER_ERROR );        // Delete any previous epitome(southward) for this commodity       $this->deleteImages();        // Get and store the image filename extension       $this->imageExtension = strtolower( strrchr( $image['proper noun'], '.' ) );        // Store the image        $tempFilename = trim( $image['tmp_name'] );         if ( is_uploaded_file ( $tempFilename ) ) {         if ( !( move_uploaded_file( $tempFilename, $this->getImagePath() ) ) ) trigger_error( "Article::storeUploadedImage(): Couldn't move uploaded file.", E_USER_ERROR );         if ( !( chmod( $this->getImagePath(), 0666 ) ) ) trigger_error( "Article::storeUploadedImage(): Couldn't set permissions on uploaded file.", E_USER_ERROR );       }        // Get the prototype size and blazon       $attrs = getimagesize ( $this->getImagePath() );       $imageWidth = $attrs[0];       $imageHeight = $attrs[1];       $imageType = $attrs[ii];        // Load the image into memory       switch ( $imageType ) {         case IMAGETYPE_GIF:           $imageResource = imagecreatefromgif ( $this->getImagePath() );           break;         case IMAGETYPE_JPEG:           $imageResource = imagecreatefromjpeg ( $this->getImagePath() );           break;         case IMAGETYPE_PNG:           $imageResource = imagecreatefrompng ( $this->getImagePath() );           break;         default:           trigger_error ( "Article::storeUploadedImage(): Unhandled or unknown image blazon ($imageType)", E_USER_ERROR );       }        // Copy and resize the image to create the thumbnail       $thumbHeight = intval ( $imageHeight / $imageWidth * ARTICLE_THUMB_WIDTH );       $thumbResource = imagecreatetruecolor ( ARTICLE_THUMB_WIDTH, $thumbHeight );       imagecopyresampled( $thumbResource, $imageResource, 0, 0, 0, 0, ARTICLE_THUMB_WIDTH, $thumbHeight, $imageWidth, $imageHeight );        // Save the thumbnail       switch ( $imageType ) {         case IMAGETYPE_GIF:           imagegif ( $thumbResource, $this->getImagePath( IMG_TYPE_THUMB ) );           break;         case IMAGETYPE_JPEG:           imagejpeg ( $thumbResource, $this->getImagePath( IMG_TYPE_THUMB ), JPEG_QUALITY );           pause;         case IMAGETYPE_PNG:           imagepng ( $thumbResource, $this->getImagePath( IMG_TYPE_THUMB ) );           suspension;         default:           trigger_error ( "Commodity::storeUploadedImage(): Unhandled or unknown image type ($imageType)", E_USER_ERROR );       }        $this->update();     }   }     /**   * Deletes any images and/or thumbnails associated with the article   */    public part deleteImages() {      // Delete all fullsize images for this article     foreach (glob( ARTICLE_IMAGE_PATH . "/" . IMG_TYPE_FULLSIZE . "/" . $this->id . ".*") every bit $filename) {       if ( !unlink( $filename ) ) trigger_error( "Article::deleteImages(): Couldn't delete image file.", E_USER_ERROR );     }          // Delete all thumbnail images for this article     foreach (glob( ARTICLE_IMAGE_PATH . "/" . IMG_TYPE_THUMB . "/" . $this->id . ".*") every bit $filename) {       if ( !unlink( $filename ) ) trigger_error( "Commodity::deleteImages(): Couldn't delete thumbnail file.", E_USER_ERROR );     }      // Remove the epitome filename extension from the object     $this->imageExtension = "";   }     /**   * Returns the relative path to the article's full-size or thumbnail image   *   * @param string The blazon of image path to recall (IMG_TYPE_FULLSIZE or IMG_TYPE_THUMB). Defaults to IMG_TYPE_FULLSIZE.   * @return string|false The epitome'southward path, or false if an epitome hasn't been uploaded   */    public function getImagePath( $type=IMG_TYPE_FULLSIZE ) {     return ( $this->id && $this->imageExtension ) ? ( ARTICLE_IMAGE_PATH . "/$blazon/" . $this->id . $this->imageExtension ) : false;   }     /**   * Returns an Article object matching the given article ID   *   * @param int The article ID   * @render Article|faux The article object, or false if the record was not found or there was a trouble   */    public static function getById( $id ) {     $conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD );     $sql = "SELECT *, UNIX_TIMESTAMP(publicationDate) AS publicationDate FROM articles WHERE id = :id";     $st = $conn->set( $sql );     $st->bindValue( ":id", $id, PDO::PARAM_INT );     $st->execute();     $row = $st->fetch();     $conn = null;     if ( $row ) return new Article( $row );   }     /**   * Returns all (or a range of) Article objects in the DB   *   * @param int Optional The number of rows to return (default=all)   * @return Array|faux A 2-element assortment : results => array, a list of Article objects; totalRows => Full number of manufactures   */    public static function getList( $numRows=1000000 ) {     $conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD );     $sql = "SELECT SQL_CALC_FOUND_ROWS *, UNIX_TIMESTAMP(publicationDate) AS publicationDate FROM manufactures             Club By publicationDate DESC LIMIT :numRows";      $st = $conn->prepare( $sql );     $st->bindValue( ":numRows", $numRows, PDO::PARAM_INT );     $st->execute();     $list = array();      while ( $row = $st->fetch() ) {       $article = new Article( $row );       $listing[] = $article;     }      // Now get the total number of articles that matched the criteria     $sql = "SELECT FOUND_ROWS() Every bit totalRows";     $totalRows = $conn->query( $sql )->fetch();     $conn = cipher;     return ( assortment ( "results" => $listing, "totalRows" => $totalRows[0] ) );   }     /**   * Inserts the current Article object into the database, and sets its ID property.   */    public office insert() {      // Does the Article object already have an ID?     if ( !is_null( $this->id ) ) trigger_error ( "Article::insert(): Attempt to insert an Article object that already has its ID holding ready (to $this->id).", E_USER_ERROR );      // Insert the Article     $conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD );     $sql = "INSERT INTO articles ( publicationDate, title, summary, content, imageExtension ) VALUES ( FROM_UNIXTIME(:publicationDate), :title, :summary, :content, :imageExtension )";     $st = $conn->gear up ( $sql );     $st->bindValue( ":publicationDate", $this->publicationDate, PDO::PARAM_INT );     $st->bindValue( ":title", $this->title, PDO::PARAM_STR );     $st->bindValue( ":summary", $this->summary, PDO::PARAM_STR );     $st->bindValue( ":content", $this->content, PDO::PARAM_STR );     $st->bindValue( ":imageExtension", $this->imageExtension, PDO::PARAM_STR );     $st->execute();     $this->id = $conn->lastInsertId();     $conn = null;   }     /**   * Updates the current Article object in the database.   */    public role update() {      // Does the Article object have an ID?     if ( is_null( $this->id ) ) trigger_error ( "Article::update(): Attempt to update an Article object that does not have its ID belongings set.", E_USER_ERROR );         // Update the Article     $conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD );     $sql = "UPDATE articles Prepare publicationDate=FROM_UNIXTIME(:publicationDate), championship=:title, summary=:summary, content=:content, imageExtension=:imageExtension WHERE id = :id";     $st = $conn->set ( $sql );     $st->bindValue( ":publicationDate", $this->publicationDate, PDO::PARAM_INT );     $st->bindValue( ":championship", $this->title, PDO::PARAM_STR );     $st->bindValue( ":summary", $this->summary, PDO::PARAM_STR );     $st->bindValue( ":content", $this->content, PDO::PARAM_STR );     $st->bindValue( ":imageExtension", $this->imageExtension, PDO::PARAM_STR );     $st->bindValue( ":id", $this->id, PDO::PARAM_INT );     $st->execute();     $conn = null;   }     /**   * Deletes the current Commodity object from the database.   */    public function delete() {      // Does the Commodity object accept an ID?     if ( is_null( $this->id ) ) trigger_error ( "Article::delete(): Attempt to delete an Article object that does not take its ID holding set.", E_USER_ERROR );      // Delete the Commodity     $conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD );     $st = $conn->prepare ( "DELETE FROM articles WHERE id = :id LIMIT 1" );     $st->bindValue( ":id", $this->id, PDO::PARAM_INT );     $st->execute();     $conn = zilch;   }  }  ?>        

Hither'south a list of the changes we've fabricated to the Article course:

A new $imageExtension holding

This corresponds to the imageExtension field you added to the articles table in Step three. This property is used to store the filename extension for the commodity'due south full-size and thumbnail images — for example, ".jpg" or ".png".

Nosotros also modified the constructor method, __construct(), to store the new $imageExtension property value in newly-created Article objects.

A new storeUploadedImage() method

Nosotros'll call this method from the admin.php script whenever the user uploads a new commodity image using the article edit form. Its task is to move the uploaded image to the fullsize images folder we created in Step 1, as well as generate a thumbnail version of the paradigm and shop information technology in the thumb binder.

The method accepts a single $image parameter. This should be the element in the PHP $_FILES superglobal array that contains all the information about the uploaded epitome file.

Here'southward how the storeUploadedImage() method works:

  1. Bank check for an upload error
    The starting time thing the method does is check that the 'error' element in the $epitome assortment equals the constant UPLOAD_ERR_OK. This indicates that the user uploaded an image, and that the upload was successful. If the upload went OK so the method starts to process the uploaded epitome; otherwise it does nothing. 
  2. Does the Article object accept an ID?
    Bold the file was uploaded OK, the method then makes sure that the Article object has an ID; in other words, that it has been saved to the database. This is of import, because we're going to rename the image file using the commodity's ID in a moment, so that we can easily associate the image with the article. If the article doesn't have an ID so the method calls trigger_error() to display an fault message and exit.
  3. Delete whatever previous prototype(s) for this commodity
    Next the method calls the Article::deleteImages() method to delete whatever existing full-size and thumbnail epitome files associated with the commodity. (Nosotros'll write this method in a moment.) We do this in society to keep the paradigm folders clean, without any sometime, unused commodity images lying almost. For example, if the article currently has a .png paradigm uploaded, and the user uploads a new .jpg image, we desire to make sure that the at present-unused .png images are deleted from the folders.
  4. Get and store the paradigm filename extension
    Equally you saw earlier, the $imageExtension property needs to store the filename extension of the article epitome. Here the method uses the strrchr() part to extract the filename extension — that is, everything after (and including) the concluding dot in the filename — and stores the result in $imageExtension, converted to lowercase with strtolower() for consistency. 
  5. Shop the image
    Now the method moves the actual uploaded image into the images/articles/fullsize folder. To do this, it get-go retrieves the path to the uploaded file from the $_FILES['fieldname']['tmp_name'] array element and stores it in $tempFilename. Typically this value is the path to the uploaded file in the server'south temporary folder, such equally /tmp/

    Then the method calls is_uploaded_file() to cheque that the file in the temporary folder is indeed a file uploaded by PHP. This is a good security precaution to forestall sensitive arrangement files accidentally being made public.

    Finally, the method calls the move_uploaded_file() function to move the uploaded epitome from the temporary folder to the images/articles/fullsize folder. This function takes two arguments: the path to the file in the temporary folder, and the path to motility the file to. Information technology's a good thought to use move_uploaded_file() to motility uploaded files, since the function performs boosted security checks on the file earlier moving it.

    In one case the file's in identify, the method calls the chmod() role to gear up the file's permissions to 0666. This ensures that the file can be read and written by anyone, including the spider web server user and any FTP user that may need to change or delete the article images. (As always, if you lot're on a shared web server and then you might want to use more than restrictive permissions than this.)

  6. Get the image size and type
    The next job for storeUploadedImage() is to create the smaller thumbnail version of the uploaded image. First it calls getimagesize(), paassing in the path to the uploaded paradigm, in order to become the image's width, summit and format (GIF, JPEG or PNG), which it then stores in $imageWidth, $imageHeight and $imageType respectively.
  7. Load the image into retentivity
    Now that the method knows the blazon of image information technology'southward dealing with, information technology calls imagecreatefromgif(), imagecreatefromjpeg() or imagecreatefrompng() as appropriate to load the prototype into an image resource variable, $imageResource, for processing.
  8. Copy and resize the paradigm to create the thumbnail
    Now it'south time to create the thumbnail image. To do this, the method get-go computes the thumbnail elevation, $thumbHeight, based on the full-size image height ($imageHeight), the total-size paradigm width ($imageWidth), and the desired thumbnail width (ARTICLE_THUMB_WIDTH). 

    Side by side it calls imagecreatetruecolor() to create a blank image resource for storing the thumbnail image information, passing in the width and meridian of the paradigm to create. It stores this resource in a $thumbResource variable.

    Finally, it calls imagecopyresampled() to create the smaller version of the uploaded prototype and store the issue in the $thumbResource variable. It passes the following arguments to imagecopyresampled():

    • The prototype resource to store the resized paradigm in ($thumbResource)
    • The uploaded image resource ($imageResource)
    • The (ten,y) coordinates of top-left corner of the rectangle in $thumbResource to re-create the paradigm data to (0,0 — that is, the summit left corner of the thumbnail)
    • The (ten,y) coordinates of pinnacle-left corner of the rectangle in $imageResource to copy the image data from (0,0 — that is, the summit left corner of the uploaded image)
    • The width and height of the rectangle in $thumbResource to copy the paradigm data to ( ARTICLE_THUMB_WIDTH and $thumbHeight — that is, the entire width and height of the thumbnail)
    • The width and height of the rectangle in $imageResource to copy the image information from ( $imageWidth and $imageHeight — that is, the entire width and height of the uploaded image)
  9. Save the thumbnail
    Now that the method has created the thumbnail image data and stored it in $thumbResource, it needs to write the new thumbnail epitome to disk. To practice this, information technology calls imagegif(), imagejpeg() or imagepng(), depending on the epitome type. It passes in both $thumbResource and the path to use for the thumbnail prototype. To become the path, it calls the getImagePath() method (which nosotros'll expect at in a moment), passing in our IMG_TYPE_THUMB abiding to point that we desire the path to the thumbnail. 
  10. Update the article record
    Finally, since the Commodity object'southward $imageExtension property may well take inverse as a result of uploading the prototype, the method calls $this->update() to update the commodity record in the database.

A new deleteImages() method

The deleteImages() method is responsible for immigration out any image files associated with the current article. It's called by storeUploadedImage() before uploading a new paradigm (as you saw in the previous section). In improver, it's called if the administrator specifically asks to delete the article'south paradigm and thumbnail via the Edit Article course. Finally, deleteImages() is also called when the article itself needs to exist deleted.

deleteImages() calls PHP's glob() part to retrieve a list of all image files in both the images/manufactures/fullsize and images/manufactures/thumb folders that are named after the article's ID. For case, if the article's ID is 3, the call to glob() will return whatsoever image files called "3.gif", "iii.jpg" or "3.png".

For each filename in the assortment returned past glob(), the method attempts to delete the file by calling PHP'south unlink() function. If there'southward a problem deleting the file then it raises an error and exits.

Once all the image files have been deleted, deleteImages() sets the Article object'south $imageExtension holding to an empty string ("") to signal that the article no longer has an uploaded epitome.

A new getImagePath() method

The last new method we've added to the Article class is getImagePath(), which returns the path to one of the two article images.

The method takes a single, optional statement, $type, that indicates whether it should return the path to the full-size epitome (IMG_TYPE_FULLSIZE, the default), or the thumbnail (IMG_TYPE_THUMB). Information technology then uses the article's ID, forth with the value stored in the commodity's $imageExtension holding, to compute the path to the image file inside the images/articles/fullsize or images/articles/thumb folder.

For example, if getImagePath() is passed IMG_TYPE_THUMB as an argument, the commodity's ID is three, and its $imageExtension property contains ".jpg", then the method will render the value "images/articles/thumb/3.jpg".

Changes to the insert() and update() methods

The final modifications to Article.php are inside the insert() and update() methods toward the stop of the file. As y'all tin meet, we've modified the SQL INSERT and UPDATE statements to accommodate the new $imageExtension property so that the epitome extension is stored in the articles table. Nosotros've also added extra bindValue() calls to laissez passer the belongings'south value to the SQL statements.

Step five: Change the admin.php script

Lock

Nosotros now demand to make some changes to admin.php, the back-end admin script, so that information technology can handle paradigm uploads. Fortunately, nosotros've already done most of the hard work in our Article form, and then there aren't many changes that we need to make to this script.

Here's the modified admin.php file with the changes highlighted. Supervene upon the code in your existing cms/admin.php file with this code:

<?php  require( "config.php" ); session_start(); $activeness = isset( $_GET['action'] ) ? $_GET['action'] : ""; $username = isset( $_SESSION['username'] ) ? $_SESSION['username'] : "";  if ( $activeness != "login" && $activity != "logout" && !$username ) {   login();   leave; }  switch ( $action ) {   case 'login':     login();     break;   case 'logout':     logout();     intermission;   case 'newArticle':     newArticle();     intermission;   case 'editArticle':     editArticle();     pause;   instance 'deleteArticle':     deleteArticle();     pause;   default:     listArticles(); }   function login() {    $results = assortment();   $results['pageTitle'] = "Admin Login | Widget News";    if ( isset( $_POST['login'] ) ) {      // User has posted the login form: endeavor to log the user in      if ( $_POST['username'] == ADMIN_USERNAME && $_POST['password'] == ADMIN_PASSWORD ) {        // Login successful: Create a session and redirect to the admin homepage       $_SESSION['username'] = ADMIN_USERNAME;       header( "Location: admin.php" );      } else {        // Login failed: display an error bulletin to the user       $results['errorMessage'] = "Incorrect username or password. Please endeavor once more.";       require( TEMPLATE_PATH . "/admin/loginForm.php" );     }    } else {      // User has not posted the login form yet: display the form     require( TEMPLATE_PATH . "/admin/loginForm.php" );   }  }   function logout() {   unset( $_SESSION['username'] );   header( "Location: admin.php" ); }   function newArticle() {    $results = array();   $results['pageTitle'] = "New Article";   $results['formAction'] = "newArticle";    if ( isset( $_POST['saveChanges'] ) ) {      // User has posted the article edit form: salve the new commodity     $article = new Commodity;     $article->storeFormValues( $_POST );     $article->insert();     if ( isset( $_FILES['image'] ) ) $commodity->storeUploadedImage( $_FILES['image'] );     header( "Location: admin.php?status=changesSaved" );    } elseif ( isset( $_POST['cancel'] ) ) {      // User has cancelled their edits: render to the article listing     header( "Location: admin.php" );   } else {      // User has not posted the commodity edit form yet: display the form     $results['article'] = new Article;     crave( TEMPLATE_PATH . "/admin/editArticle.php" );   }  }   office editArticle() {    $results = array();   $results['pageTitle'] = "Edit Article";   $results['formAction'] = "editArticle";    if ( isset( $_POST['saveChanges'] ) ) {      // User has posted the article edit form: save the commodity changes      if ( !$article = Article::getById( (int)$_POST['articleId'] ) ) {       header( "Location: admin.php?error=articleNotFound" );       return;     }      $article->storeFormValues( $_POST );     if ( isset($_POST['deleteImage']) && $_POST['deleteImage'] == "yes" ) $commodity->deleteImages();     $article->update();     if ( isset( $_FILES['image'] ) ) $article->storeUploadedImage( $_FILES['image'] );     header( "Location: admin.php?condition=changesSaved" );    } elseif ( isset( $_POST['cancel'] ) ) {      // User has cancelled their edits: render to the commodity list     header( "Location: admin.php" );   } else {      // User has non posted the article edit form yet: display the form     $results['article'] = Article::getById( (int)$_GET['articleId'] );     require( TEMPLATE_PATH . "/admin/editArticle.php" );   }  }   office deleteArticle() {    if ( !$article = Article::getById( (int)$_GET['articleId'] ) ) {     header( "Location: admin.php?error=articleNotFound" );     render;   }    $article->deleteImages();   $article->delete();   header( "Location: admin.php?condition=articleDeleted" ); }   office listArticles() {   $results = assortment();   $data = Article::getList();   $results['manufactures'] = $information['results'];   $results['totalRows'] = $information['totalRows'];   $results['pageTitle'] = "All Manufactures";    if ( isset( $_GET['error'] ) ) {     if ( $_GET['error'] == "articleNotFound" ) $results['errorMessage'] = "Error: Article non found.";   }    if ( isset( $_GET['status'] ) ) {     if ( $_GET['status'] == "changesSaved" ) $results['statusMessage'] = "Your changes have been saved.";     if ( $_GET['status'] == "articleDeleted" ) $results['statusMessage'] = "Commodity deleted.";   }    crave( TEMPLATE_PATH . "/admin/listArticles.php" ); }  ?>        

Allow's work through these changes to admin.php:

  • Changes to newArticle()
    We've added a single line of code to the newArticle() role to handle image uploads. It checks that the 'epitome' chemical element exists in the $_FILES array and, if it does be, it calls the Article object's storeUploadedImage() method, passing in the $_FILES['image'] element, to store the image and create the thumbnail. 
  • Changes to editArticle()
    As with newArticle(), we've added a line of code that checks for an uploaded image and calls $article->storeUploadedImage() to store it and create the thumbnail. We've also added a line of code that checks if the user selected the "delete image" checkbox in the Edit Commodity form. If they did then we call $article->deleteImages() to delete whatsoever existing images associated with the article.
  • Changes to deleteArticle()
    Finally, we've added a single line of lawmaking to the deleteArticle() function that calls $commodity->deleteImages(). This ensures that any images associated with the article besides go deleted.

Stride half dozen: Change the front-end templates

Article thumbnails screenshot

Our database and PHP lawmaking can now handle article images, only we demand to make some changes both to the forepart-end templates that visitors run across, and the back-end admin templates.

Let's outset past altering the front-end templates to brandish the article images.

i. homepage.php

The homepage.php template displays the site's home page, including a list of recent articles. We'll modify this template to display each article'south thumbnail next to the article in the listing.

Hither's the modified file with the new lines highlighted — supersede your sometime cms/templates/homepage.php file with this code:

<?php include "templates/include/header.php" ?>        <ul id="headlines">  <?php foreach ( $results['articles'] as $commodity ) { ?>          <li>           <h2>             <span class="pubDate"><?php echo date('j F', $article->publicationDate)?></span><a href=".?activity=viewArticle&amp;articleId=<?php echo $article->id?>"><?php repeat htmlspecialchars( $commodity->championship )?></a>           </h2>           <p class="summary">             <?php if ( $imagePath = $article->getImagePath( IMG_TYPE_THUMB ) ) { ?>               <a href=".?action=viewArticle&amp;articleId=<?php repeat $article->id?>"><img class="articleImageThumb" src="<?php echo $imagePath?>" alt="Article Thumbnail" /></a>             <?php } ?>           <?php echo htmlspecialchars( $article->summary )?>           </p>         </li>  <?php } ?>        </ul>        <p><a href="./?activeness=archive">Commodity Archive</a></p>  <?php include "templates/include/footer.php" ?>        

Here we've added some lawmaking inside the "summary" paragraph for each article. The lawmaking calls the commodity's getImagePath() method, passing in IMG_TYPE_THUMB to signal that nosotros desire the path to the commodity's thumbnail. It then stores the path in the $imagePath variable. If this path is a non-imitation value and then the article has a thumbnail image, so the lawmaking then constructs a link to view the article, wrapped around an img element that contains the thumbnail's path. Nosotros've added an articleImageThumb form to the thumbnail paradigm and so that we tin fashion it in the stylesheet.

If $imagePath's value is false then the article doesn't have a thumbnail, then no markup is constructed.

2. archive.php

archive.php displays the commodity archive page — that is, a list of all the articles in the database. We need to alter it in the same way as homepage.php, and then that it displays thumbnails next to the article summaries.

Here's the modified annal.php file — replace your old cms/templates/archive.php file with this one:

<?php include "templates/include/header.php" ?>        <h1>Article Archive</h1>        <ul id="headlines" class="archive">  <?php foreach ( $results['articles'] as $article ) { ?>          <li>           <h2>             <span class="pubDate"><?php echo date('j F Y', $commodity->publicationDate)?></span><a href=".?action=viewArticle&amp;articleId=<?php repeat $article->id?>"><?php echo htmlspecialchars( $article->title )?></a>           </h2>           <p class="summary">             <?php if ( $imagePath = $article->getImagePath( IMG_TYPE_THUMB ) ) { ?>               <a href=".?action=viewArticle&amp;articleId=<?php echo $article->id?>"><img class="articleImageThumb" src="<?php echo $imagePath?>" alt="Article Thumbnail" /></a>             <?php } ?>           <?php repeat htmlspecialchars( $commodity->summary )?>           </p>         </li>  <?php } ?>        </ul>        <p><?php echo $results['totalRows']?> article<?php echo ( $results['totalRows'] != 1 ) ? 's' : '' ?> in full.</p>        <p><a href="./">Return to Homepage</a></p>  <?php include "templates/include/footer.php" ?>        

As you tin can run across, this is the aforementioned change that we made to homepage.php. If an article has an image, its thumbnail will now appear next to the article summary in the archive page.

iii. viewArticle.php

The viewArticle.php template displays an private article page, containing the article'south headline, summary and content. But equally we modified homepage.php and archive.php to display thumbnails adjacent to the commodity summaries, we also demand to enhance viewArticle.php so that it displays the total-size article images in the article pages.

Hither's the changed template. As e'er, I've highlighted the new code. Relieve this code over your onetime cms/templates/viewArticle.php file:

<?php include "templates/include/header.php" ?>        <h1 style="width: 75%;"><?php repeat htmlspecialchars( $results['article']->title )?></h1>       <div style="width: 75%; font-way: italic;"><?php repeat htmlspecialchars( $results['article']->summary )?></div>       <div manner="width: 75%; min-elevation: 300px;">       <?php if ( $imagePath = $results['article']->getImagePath() ) { ?>         <img id="articleImageFullsize" src="<?php echo $imagePath?>" alt="Article Epitome" />       <?php } ?>       <?php echo $results['commodity']->content?>       </div>       <p form="pubDate">Published on <?php repeat date('j F Y', $results['article']->publicationDate)?></p>        <p><a href="./">Return to Homepage</a></p>  <?php include "templates/include/footer.php" ?>        

This new code works in substantially the same way as the code added to the homepage and archive templates. Information technology calls the commodity's getImagePath() method to get the path to the total-size article prototype. If the path isn't simulated then the article has an image, and the lawmaking inserts the appropriate img chemical element into the markup. The img chemical element is given an id of articleImageFullsize and so that we can style information technology using CSS.

Step 7: Modify the back-end templates

Edit Article screenshot

There's actually only one back-end admin template that we need to alter, and that's the editArticle.php article edit class. Hither'southward the new template with changes highlighted — salvage it over your existing cms/templates/admin/editArticle.php file:

<?php include "templates/include/header.php" ?>        <script>        // Prevents file upload hangs in Mac Safari       // Inspired past http://airbladesoftware.com/notes/note-to-self-prevent-uploads-hanging-in-safari        role closeKeepAlive() {         if ( /AppleWebKit|MSIE/.exam( navigator.userAgent) ) {           var xhr = new XMLHttpRequest();           xhr.open( "Become", "/ping/shut", false );           xhr.send();         }       }        </script>        <div id="adminHeader">         <h2>Widget News Admin</h2>         <p>Y'all are logged in equally <b><?php echo htmlspecialchars( $_SESSION['username']) ?></b>. <a href="admin.php?action=logout"?>Log out</a></p>       </div>        <h1><?php echo $results['pageTitle']?></h1>        <course action="admin.php?action=<?php echo $results['formAction']?>" method="postal service" enctype="multipart/form-data" onsubmit="closeKeepAlive()">         <input blazon="hidden" name="articleId" value="<?php echo $results['article']->id ?>"/>  <?php if ( isset( $results['errorMessage'] ) ) { ?>         <div class="errorMessage"><?php echo $results['errorMessage'] ?></div> <?php } ?>          <ul>            <li>             <label for="title">Commodity Championship</label>             <input type="text" proper noun="title" id="title" placeholder="Name of the article" required autofocus maxlength="255" value="<?php repeat htmlspecialchars( $results['article']->championship )?>" />           </li>            <li>             <label for="summary">Article Summary</label>             <textarea name="summary" id="summary" placeholder="Brief clarification of the article" required maxlength="m" style="pinnacle: 5em;"><?php repeat htmlspecialchars( $results['article']->summary )?></textarea>           </li>            <li>             <label for="content">Article Content</label>             <textarea name="content" id="content" placeholder="The HTML content of the article" required maxlength="100000" style="height: 30em;"><?php echo htmlspecialchars( $results['article']->content )?></textarea>           </li>            <li>             <label for="publicationDate">Publication Date</label>             <input type="date" proper noun="publicationDate" id="publicationDate" placeholder="YYYY-MM-DD" required maxlength="10" value="<?php echo $results['article']->publicationDate ? appointment( "Y-m-d", $results['article']->publicationDate ) : "" ?>" />           </li>            <?php if ( $results['article'] && $imagePath = $results['article']->getImagePath() ) { ?>           <li>             <label>Current Image</label>             <img id="articleImage" src="<?php repeat $imagePath ?>" alt="Article Image" />           </li>            <li>             <characterization></label>             <input blazon="checkbox" name="deleteImage" id="deleteImage" value="aye"/ > <label for="deleteImage">Delete</label>           </li>           <?php } ?>            <li>             <characterization for="image">New Image</label>             <input type="file" name="epitome" id="image" placeholder="Choose an prototype to upload" maxlength="255" />           </li>          </ul>          <div class="buttons">           <input blazon="submit" name="saveChanges" value="Save Changes" />           <input type="submit" formnovalidate proper name="cancel" value="Cancel" />         </div>        </course>  <?php if ( $results['commodity']->id ) { ?>       <p><a href="admin.php?action=deleteArticle&amp;articleId=<?php echo $results['article']->id ?>" onclick="return confirm('Delete This Article?')">Delete This Article</a></p> <?php } ?>  <?php include "templates/include/footer.php" ?>        

Let's take a wait at each of these changes in plow:

  • The closeKeepAlive() JavaScript role
    For some reason, Safari on the Mac has suffered from a long-standing consequence whereby file uploads hang occasionally. (Find out more about this outcome here and here.) Since this is quite annoying if y'all use Safari (equally I practise), I've added this little function that makes an Ajax asking to a non-existent URL on the server, forcing Safari to shut its connectedness to the server. This seems to fix the trouble. My function is similar to this script, except that my function doesn't crave the Prototype.js library.
  • Changes to the <course> tag
    We've added the attribute enctype="multipart/course-information" to the form chemical element. This attribute is required whenever you create a form containing a file upload field. Information technology lets the browser know that it needs to encode the form data as a multipart MIME stream containing different media types (in this case, text and image data). 

    Nosotros've also attached the closeKeepAlive() function as a submit effect handler to the class, and then that the function runs when the form is submitted.

  • The article image and "delete" checkbox
    The side by side add-on to the form displays the total-size paradigm currently associated with the article (if any). As with the front end-finish templates, the PHP code calls the article's getImagePath() method to call up the image'southward path. If an image path was returned, we add an li element to the folio containing a field characterization ("Current Epitome"), along with an img chemical element linking to the image. We too include another li element containing a deleteImage checkbox. This lets the administrator delete any image(s) currently associated with the article.
  • The image upload field
    Last, but by no means to the lowest degree, we add together the <input blazon="file"> upload field that allows the ambassador to upload an image for this article. Nosotros give information technology a name attribute of "image", which means that we're able to admission the uploaded file from our PHP code using $_FILES['epitome'] (encounter Step iv earlier in the tutorial).

Step 8: Modify the stylesheet

Adjacent we'll brand some additions and changes to our CMS's stylesheet, manner.css, in club to style the article images and thumbnails on the front end, equally well as the new elements in the Edit Article form.

Here's the new style.css file with the changes highlighted. Replace your existing style.css file in your cms folder with this file:

/* Fashion the body and outer container */  body {   margin: 0;   colour: #333;   background-colour: #00a0b0;   font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;   line-pinnacle: 1.5em; }  #container {   width: 960px;   background: #fff;   margin: 20px motorcar;   padding: 20px;   -moz-border-radius: 5px;   -webkit-border-radius: 5px;   border-radius: 5px; }   /* The logo and footer */  #logo {   display: block;   width: 300px;   padding: 0 660px 20px 0;   border: none;   edge-bottom: 1px solid #00a0b0;   margin-bottom: 40px; }  #footer {   border-top: 1px solid #00a0b0;   margin-top: 40px;   padding: 20px 0 0 0;   font-size: .8em; }   /* Headings */  h1 {   color: #eb6841;   margin-lesser: 30px;   line-superlative: one.2em; }  h2, h2 a {   color: #edc951; }  h2 a {   text-ornamentation: none; }   /* Article headlines */  #headlines {   list-way: none;   padding-left: 0;   width: 75%; }  #headlines li {   margin-bottom: 2em;   overflow: hidden; }  .pubDate {   font-size: .8em;   color: #eb6841;   text-transform: uppercase; }  #headlines .pubDate {   display: inline-block;   width: 100px;   font-size: .5em;   vertical-marshal: middle; }  #headlines.archive .pubDate {   width: 130px; }  #headlines .articleImageThumb {   width: 120px;   bladder: left;   margin: 4px 20px 0 0;   border: 1px solid #00a0b0; }  .summary {   padding-left: 100px; }  #headlines.archive .summary {   padding-left: 130px; }   /* Commodity pages */  #articleImageFullsize {   width: 250px;   float: left;   margin: 4px 20px 10px 0;   border: 1px solid #00a0b0; }   /* "You lot are logged in..." header on admin pages */  #adminHeader {   width: 940px;   padding: 0 10px;   edge-bottom: 1px solid #00a0b0;   margin: -30px 0 40px 0;   font-size: 0.8em; }   /* Style the form with a coloured background, forth with curved corners and a drop shadow */  form {   margin: 20px auto;   padding: 40px 20px;   overflow: automobile;   background: #fff4cf;   edge: 1px solid #666;   -moz-border-radius: 5px;   -webkit-border-radius: 5px;     border-radius: 5px;   -moz-box-shadow: 0 0 .5em rgba(0, 0, 0, .8);   -webkit-box-shadow: 0 0 .5em rgba(0, 0, 0, .viii);   box-shadow: 0 0 .5em rgba(0, 0, 0, .8); }   /* Give form elements consistent margin, padding and line height */  form ul {   list-style: none;   margin: 0;   padding: 0;   overflow: subconscious; }  form ul li {   margin: .9em 0 0 0;   padding: 0; }  course * {   line-tiptop: 1em; }   /* The field labels */  label {   display: block;   float: left;   clear: left;   text-align: correct;   width: fifteen%;   padding: .4em 0 0 0;   margin: .15em .5em 0 0; }   /* The fields */  input, select, textarea {   brandish: block;   margin: 0;   padding: .4em;   width: 80%; }  input, textarea, .date {   border: 2px solid #666;   -moz-border-radius: 5px;   -webkit-border-radius: 5px;       border-radius: 5px;   groundwork: #fff; }  input {   font-size: .9em; }  input[type="checkbox"] {   display: inline-block;   padding: 0;   margin: 0 0 .8em 0;   width: auto; }  select {   padding: 0;   margin-bottom: 2.5em;   position: relative;   acme: .7em; }  textarea {   font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;   font-size: .9em;   meridian: 5em;   line-height: 1.5em; }  textarea#content {   font-family: "Courier New", courier, fixed; }  #articleImage {   edge: 2px solid #666; }  #deleteImage {   clear: both; }  label[for="deleteImage"] {   float: none;   display: inline; }  input[type="file"] {   float: left; }     /* Place a border effectually focused fields */  course *:focus {   edge: 2px solid #7c412b;   outline: none; }   /* Display correctly filled-in fields with a green background */  input:valid, textarea:valid {   groundwork: #efe; }   /* Submit buttons */  .buttons {   text-align: center;   margin: 40px 0 0 0; }  input[type="submit"] {   display: inline;   margin: 0 20px;   width: 12em;   padding: 10px;   border: 2px solid #7c412b;   -moz-border-radius: 5px;   -webkit-border-radius: 5px;     border-radius: 5px;   -moz-box-shadow: 0 0 .5em rgba(0, 0, 0, .8);   -webkit-box-shadow: 0 0 .5em rgba(0, 0, 0, .eight);   box-shadow: 0 0 .5em rgba(0, 0, 0, .8);   color: #fff;   groundwork: #ef7d50;   font-weight: bold;   -webkit-appearance: none; }  input[type="submit"]:hover, input[blazon="submit"]:active {   cursor: pointer;   groundwork: #fff;   color: #ef7d50; }  input[type="submit"]:active {   background: #eee;   -moz-box-shadow: 0 0 .5em rgba(0, 0, 0, .8) inset;   -webkit-box-shadow: 0 0 .5em rgba(0, 0, 0, .viii) inset;   box-shadow: 0 0 .5em rgba(0, 0, 0, .8) inset; }   /* Tables */  table {   width: 100%;   border-plummet: collapse; }  tr, th, td {   padding: 10px;   margin: 0;   text-align: left; }  table, th {   edge: 1px solid #00a0b0; }  th {   edge-left: none;   border-right: none;   background: #ef7d50;   color: #fff;   cursor: default; }  tr:nth-kid(odd) {   background: #fff4cf; }  tr:nth-child(even) {   background: #fff; }  tr:hover {   background: #ddd;   cursor: arrow; }   /* Condition and error boxes */  .statusMessage, .errorMessage {   font-size: .8em;   padding: .5em;   margin: 2em 0;   -moz-border-radius: 5px;   -webkit-border-radius: 5px;   edge-radius: 5px;    -moz-box-shadow: 0 0 .5em rgba(0, 0, 0, .eight);   -webkit-box-shadow: 0 0 .5em rgba(0, 0, 0, .viii);   -box-shadow: 0 0 .5em rgba(0, 0, 0, .eight); }  .statusMessage {   background-color: #2b2;   border: 1px solid #080;   color: #fff; }  .errorMessage {   groundwork-color: #f22;   edge: 1px solid #800;   colour: #fff; }        

Every bit you can encounter, we've added various rulesets to manner the thumbnails in the homepage and archive pages; the full-size images in article pages; and the "delete epitome" checkbox, article image and file upload field inside the "Edit Article" course (which you added in Pace 7).

On line 89, we've set the thumbnail width to 120 pixels to match the ARTICLE_THUMB_WIDTH setting in config.php. Similarly, on line 107 we've set the width of the full-size images in the article pages to 250 pixels. If y'all want to use dissimilar widths for the thumbnails and images then you need to change these two values, as well equally the ARTICLE_THUMB_WIDTH setting.

Try it out!

Not bad stuff! You now have a CMS that can handle image uploads. To examination your new CMS, follow these steps:

  1. Log in
    Open up your browser and visit the base URL of your CMS — for example, http://localhost/cms/. Click the Site Admin link in the footer, and log in.
  2. Upload some images
    Click an article in the All Articles list, or add together a new article by clicking the Add a New Article link at the lesser of the folio. In the New Article / Edit Article form, click the button next to the New Prototype characterization at the bottom of the class. Cull a file to upload, then click Salvage Changes to save your article edits and upload the epitome.
  3. View your images
    Click the Widget News logo at the top of the folio to view the site. Yous should see thumbnail images next to the articles in the listing. If you click the Article Archive link at the lesser of the page then yous should also meet the thumbnails there. Click a thumbnail (or article headline) to view the total article page, along with the total-size article image.
  4. Changing and deleting images
    Just to make certain everything works, try editing an article with an image and uploading a new prototype. This should then replace the previous commodity image and thumbnail. (Depending on your server and browser setup, you may need to articulate your browser'southward cache and reload the folio to meet the new image.) You can also try clicking the Delete checkbox to remove an existing image from an article.

Yous can as well try out the demo on our server too! The demo is read-only, so yous tin can't upload or delete images, only you tin see how the images expect on the forepart-end, as well as in the back-cease article edit form.

Summary

In this tutorial we've added an epitome upload feature to the content management organization from my original tutorial. Hither's what nosotros did:

  • Created some folders within your CMS to store the commodity images and thumbnails
  • Added some constants to the config file to specify the path to the images folder, the width to use for commodity thumbnails, and other useful settings
  • Modified the MySQL database to add an imageExtension field, which tracks the filename extension of the image uploaded for each article
  • Modified the Commodity class to add the $imageExtension property, as well as methods to handle epitome uploads, prototype deletion, and retrieving image paths
  • Extended the admin.php admin script to let uploading and deleting of article images
  • Altered the front-end templates, homepage.php, archive.php and viewArticle.php, to display the article thumbnails and full-size images equally appropriate
  • Enhanced the commodity edit form, editArticle.php, to include the epitome upload field, also as the currently-uploaded epitome and a "delete" image" checkbox, and
  • Tweaked the stylesheet, way.css, to manner the article images and thumbnails, as well equally the commodity edit form elements.

You tin now use your CMS to publish articles illustrated with images. Now all y'all need to practise is create some cute images! Enjoy. 🙂

[Flickr photograph credits for sample images: Businessman, Cogs, Willy Wonka, Newspapers, Dollars.]

woodcley1939.blogspot.com

Source: https://www.elated.com/add-image-uploading-to-your-cms/

Related Posts

0 Response to "Metatag Warning: Be Sure to Upload Your Image to Your Cms or Host."

Enviar um comentário

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel