Index of Upload Image Share Com Pimpandhost 40
17 May 2019: This commodity and the code 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 but useful content management system with PHP and MySQL. I as well showed how to extend the CMS to allow commodity categories.
In this tutorial, you'll expect at another way to extend the CMS. You'll take the original CMS code, and modify information technology then that the ambassador can upload an image for each article. Then, when a visitor views an commodity folio, the CMS will brandish the prototype at the showtime of the commodity. In addition, our CMS will generate a smaller thumbnail version of each article image, and brandish this thumbnail next to each article headline in the homepage and article archive pages.
You can see the finished result past clicking the View Demo link to a higher place. Observe the thumbnail images next to each article headline. Click a headline or thumbnail to view the corresponding commodity, along with the full-size article paradigm.
The programme
We'll first with the original CMS code from Build a CMS in an Afternoon with PHP and MySQL, and modify information technology to include the prototype upload characteristic. Hither are the steps we'll need to behave out:
- Create a couple of folders to store the article images
- Change diverse image-related settings to the CMS config file
- Modify the database to store the paradigm filename extensions
- Modify the
Article
class to handle images - Modify
admin.php
to handle epitome upload and deletion - Alter the front end-terminate templates to display the article images and thumbnails
- Alter the back-end article edit grade to let the administrator upload an image, view the article image, and delete the article image
- Tweak the CMS stylesheet to style the article images and the new elements in the commodity edit form
Ready? Let'due south go started!
Step i: Create the image folders
The get-go thing to do is create a couple of folders in your website to store the total-size and thumbnail commodity images.
Open up the existing cms
folder and yous'll see an images
folder containing the sample logo.jpg
prototype. Within this images
folder, create an manufactures
folder. And then, inside the manufactures
folder, create two more folders:
-
fullsize
to store the total-size article images -
pollex
to store the smaller thumbnail versions of the article images
Next you lot demand to give your spider web server user permission to create files in these two folders. Typically on a Linux or Mac system, you demand to modify the permissions to 777, similar this:
$ cd images/articles/ $ chmod 777 fullsize $ chmod 777 thumb
If your CMS is on a remote web server and so you tin unremarkably 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 up the config.php
file in the tiptop-level cms
binder, and add the new lines highlighted in the code below:
<?php ini_set( "display_errors", true ); date_default_timezone_set( "Australia/Sydney" ); // http://www.php.internet/manual/en/timezones.php ascertain( "DB_DSN", "mysql:host=localhost;dbname=cms" ); ascertain( "DB_USERNAME", "username" ); define( "DB_PASSWORD", "password" ); ascertain( "CLASS_PATH", "classes" ); define( "TEMPLATE_PATH", "templates" ); ascertain( "HOMEPAGE_NUM_ARTICLES", v ); define( "ADMIN_USERNAME", "admin" ); define( "ADMIN_PASSWORD", "mypass" ); define( "ARTICLE_IMAGE_PATH", "images/manufactures" ); define( "IMG_TYPE_FULLSIZE", "fullsize" ); ascertain( "IMG_TYPE_THUMB", "thumb" ); ascertain( "ARTICLE_THUMB_WIDTH", 120 ); define( "JPEG_QUALITY", 85 ); crave( CLASS_PATH . "/Commodity.php" ); function handleException( $exception ) { echo "Distressing, a problem occurred. Please try later."; error_log( $exception->getMessage() ); } set_exception_handler( 'handleException' ); ?>
You've added the following constants:
-
ARTICLE_IMAGE_PATH
defines the path to the commodity images folder, relative to the top-level CMS binder. (If you lot want to shop your article images somewhere else, change this constant appropriately.) -
IMG_TYPE_FULLSIZE
defines a constant to represent the "full-size" paradigm blazon. We'll apply this in the code whenever we want to indicate a total-size image. This value ("fullsize"
) is also used to locate the full-size images folder (images/articles/fullsize
), and then if you apply a unlike binder name, you'll want to update this constant too. -
IMG_TYPE_THUMB
does a similar job toIMG_TYPE_FULLSIZE
, but represents the "thumbnail" image type instead. -
ARTICLE_THUMB_WIDTH
defines the width to utilise 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 all-time quality (merely largest file size). 85 is a good compromise.
Step three: Modify the database
Yous need to make one small change to the articles
table in the CMS database. Open up up the tables.sql
file from the original CMS, and add together the line highlighted in the code below:
DROP TABLE IF EXISTS manufactures; CREATE Tabular array articles ( id smallint unsigned Non NULL auto_increment, publicationDate date Not NULL, # When the article was published title varchar(255) NOT Naught, # Full championship of the article summary text Not Aught, # A curt summary of the article content mediumtext NOT NULL, # The HTML content of the commodity imageExtension varchar(255) Not NULL, # The filename extension of the article's total-size and thumbnail images PRIMARY Central (id) );
This line adds a new field called imageExtension
to the manufactures
table. This field stores the filename extension of each commodity's uploaded image. For instance, if the ambassador uploads a PNG paradigm and then we'll shop the value ".png"
in the imageExtension
field.
What if you lot already take articles in your CMS?
If you load the above tables.sql
file into MySQL and then information technology will delete any existing articles
table in your cms
database, and recreate the articles
table from scratch. This will delete whatsoever articles already in your CMS, which is obviously not what you want.
So if y'all already accept articles in your CMS database, you desire to modify the articles
table while retaining the existing data in the tabular array. To do this, alter your tables.sql
file to the following:
Change TABLE articles ADD imageExtension varchar(255) NOT Zippo AFTER content;
Applying the changes
To actually create your manufactures
table (or add together the new imageExtension
field to your existing articles
table, equally appropriate), you lot demand to load the tables.sql
file into MySQL. To practice this, follow the procedure described in Applying the changes in my last tutorial.
To check that your changes have been made, get-go login to MySQL:
mysql -u username -p cms
And then use the SHOW TABLES
and Explicate
commands to check your table schemas in MySQL:
mysql> show tables; +---------------+ | Tables_in_cms | +---------------+ | articles | +---------------+ one row in set (0.00 sec) mysql> explain manufactures; +-----------------+----------------------+------+-----+---------+----------------+ | Field | Blazon | Null | Key | Default | Extra | +-----------------+----------------------+------+-----+---------+----------------+ | id | smallint(5) unsigned | NO | PRI | Nothing | auto_increment | | publicationDate | engagement | NO | | Cipher | | | championship | varchar(255) | NO | | Goose egg | | | summary | text | NO | | Cypher | | | content | mediumtext | NO | | NULL | | | imageExtension | varchar(255) | NO | | NULL | | +-----------------+----------------------+------+-----+---------+----------------+ 6 rows in fix (0.00 sec) mysql>
Detect the new imageExtension
field within the manufactures
table.
You've ready up your CMS database so that it'south ready to handle image uploads. Now you tin can start modifying the CMS code.
Footstep 4: Modify the Article
class
Adjacent, we need to change the Article
form to handle article images. Hither's the updated Article.php
grade file. I've highlighted the lines of code so you tin can run across what's been added and changed. Replace the lawmaking in your existing cms/classes/Article.php
file with this new code:
<?php /** * Class to handle articles */ class Article { // Properties /** * @var int The article ID from the database */ public $id = null; /** * @var int When the article is to be / was commencement published */ public $publicationDate = null; /** * @var string Full title of the article */ public $title = cipher; /** * @var string A short summary of the article */ public $summary = zip; /** * @var string The HTML content of the article */ public $content = aught; /** * @var string The filename extension of the article'south full-size and thumbnail images (empty string means the article has no image) */ public $imageExtension = ""; /** * Sets the object'south properties using the values in the supplied assortment * * @param assoc The property values */ public function __construct( $data=array() ) { if ( isset( $information['id'] ) ) $this->id = (int) $information['id']; if ( isset( $information['publicationDate'] ) ) $this->publicationDate = (int) $information['publicationDate']; if ( isset( $data['championship'] ) ) $this->title = preg_replace ( "/[^\.\,\-\_\'\"\@\?\!\:\$ a-zA-Z0-9()]/", "", $data['title'] ); if ( isset( $data['summary'] ) ) $this->summary = preg_replace ( "/[^\.\,\-\_\'\"\@\?\!\:\$ a-zA-Z0-ix()]/", "", $data['summary'] ); if ( isset( $data['content'] ) ) $this->content = $data['content']; if ( isset( $data['imageExtension'] ) ) $this->imageExtension = preg_replace ( "/[^\.\,\-\_\'\"\@\?\!\$ a-zA-Z0-ix()]/", "", $information['imageExtension'] ); } /** * Sets the object's backdrop using the edit form post values in the supplied array * * @param assoc The course post values */ public function storeFormValues( $params ) { // Store all the parameters $this->__construct( $params ); // Parse and store the publication engagement if ( isset($params['publicationDate']) ) { $publicationDate = explode ( '-', $params['publicationDate'] ); if ( count($publicationDate) == iii ) { list ( $y, $k, $d ) = $publicationDate; $this->publicationDate = mktime ( 0, 0, 0, $thousand, $d, $y ); } } } /** * Stores any paradigm uploaded from the edit class * * @param assoc The 'image' element from the $_FILES array containing the file upload information */ public role storeUploadedImage( $image ) { if ( $image['fault'] == UPLOAD_ERR_OK ) { // Does the Article object take an ID? if ( is_null( $this->id ) ) trigger_error( "Article::storeUploadedImage(): Attempt to upload an prototype for an Article object that does non have its ID property set.", E_USER_ERROR ); // Delete any previous image(s) for this article $this->deleteImages(); // Go and store the image filename extension $this->imageExtension = strtolower( strrchr( $image['proper name'], '.' ) ); // Store the image $tempFilename = trim( $paradigm['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 up 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 paradigm into memory switch ( $imageType ) { case IMAGETYPE_GIF: $imageResource = imagecreatefromgif ( $this->getImagePath() ); break; instance IMAGETYPE_JPEG: $imageResource = imagecreatefromjpeg ( $this->getImagePath() ); intermission; case IMAGETYPE_PNG: $imageResource = imagecreatefrompng ( $this->getImagePath() ); break; default: trigger_error ( "Article::storeUploadedImage(): Unhandled or unknown image type ($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 ); // Relieve 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 ); break; case IMAGETYPE_PNG: imagepng ( $thumbResource, $this->getImagePath( IMG_TYPE_THUMB ) ); break; default: trigger_error ( "Article::storeUploadedImage(): Unhandled or unknown image type ($imageType)", E_USER_ERROR ); } $this->update(); } } /** * Deletes any images and/or thumbnails associated with the article */ public function deleteImages() { // Delete all fullsize images for this commodity foreach (glob( ARTICLE_IMAGE_PATH . "/" . IMG_TYPE_FULLSIZE . "/" . $this->id . ".*") equally $filename) { if ( !unlink( $filename ) ) trigger_error( "Article::deleteImages(): Couldn't delete epitome file.", E_USER_ERROR ); } // Delete all thumbnail images for this article foreach (glob( ARTICLE_IMAGE_PATH . "/" . IMG_TYPE_THUMB . "/" . $this->id . ".*") as $filename) { if ( !unlink( $filename ) ) trigger_error( "Article::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 commodity's full-size or thumbnail image * * @param string The type of epitome path to retrieve (IMG_TYPE_FULLSIZE or IMG_TYPE_THUMB). Defaults to IMG_TYPE_FULLSIZE. * @return string|faux The image'southward path, or fake if an image hasn't been uploaded */ public function getImagePath( $type=IMG_TYPE_FULLSIZE ) { return ( $this->id && $this->imageExtension ) ? ( ARTICLE_IMAGE_PATH . "/$type/" . $this->id . $this->imageExtension ) : false; } /** * Returns an Article object matching the given article ID * * @param int The article ID * @return Article|simulated The article object, or simulated if the tape was not institute or there was a problem */ public static function getById( $id ) { $conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); $sql = "SELECT *, UNIX_TIMESTAMP(publicationDate) Equally publicationDate FROM articles WHERE id = :id"; $st = $conn->prepare( $sql ); $st->bindValue( ":id", $id, PDO::PARAM_INT ); $st->execute(); $row = $st->fetch(); $conn = nix; if ( $row ) render 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) * @render Array|simulated A two-element assortment : results => array, a list of Article objects; totalRows => Full number of articles */ public static function getList( $numRows=meg ) { $conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); $sql = "SELECT SQL_CALC_FOUND_ROWS *, UNIX_TIMESTAMP(publicationDate) AS publicationDate FROM articles ORDER BY publicationDate DESC LIMIT :numRows"; $st = $conn->prepare( $sql ); $st->bindValue( ":numRows", $numRows, PDO::PARAM_INT ); $st->execute(); $listing = assortment(); while ( $row = $st->fetch() ) { $article = new Article( $row ); $list[] = $commodity; } // Now become the full number of articles that matched the criteria $sql = "SELECT FOUND_ROWS() Equally totalRows"; $totalRows = $conn->query( $sql )->fetch(); $conn = null; render ( array ( "results" => $list, "totalRows" => $totalRows[0] ) ); } /** * Inserts the current Commodity object into the database, and sets its ID property. */ public office insert() { // Does the Article object already accept an ID? if ( !is_null( $this->id ) ) trigger_error ( "Article::insert(): Endeavour to insert an Article object that already has its ID belongings gear up (to $this->id).", E_USER_ERROR ); // Insert the Commodity $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->prepare ( $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->execute(); $this->id = $conn->lastInsertId(); $conn = null; } /** * Updates the current Article object in the database. */ public function update() { // Does the Article object have an ID? if ( is_null( $this->id ) ) trigger_error ( "Article::update(): Effort to update an Article object that does not have its ID property ready.", E_USER_ERROR ); // Update the Article $conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); $sql = "UPDATE articles Set up publicationDate=FROM_UNIXTIME(:publicationDate), title=:championship, summary=:summary, content=:content, imageExtension=:imageExtension WHERE id = :id"; $st = $conn->prepare ( $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->bindValue( ":id", $this->id, PDO::PARAM_INT ); $st->execute(); $conn = null; } /** * Deletes the current Article object from the database. */ public function delete() { // Does the Article object take an ID? if ( is_null( $this->id ) ) trigger_error ( "Article::delete(): Attempt to delete an Article object that does non have its ID holding set.", E_USER_ERROR ); // Delete the Article $conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); $st = $conn->gear up ( "DELETE FROM manufactures WHERE id = :id LIMIT 1" ); $st->bindValue( ":id", $this->id, PDO::PARAM_INT ); $st->execute(); $conn = null; } } ?>
Here's a list of the changes we've made to the Commodity
grade:
A new $imageExtension
belongings
This corresponds to the imageExtension
field you added to the manufactures
tabular array in Step 3. This property is used to store the filename extension for the article's total-size and thumbnail images — for instance, ".jpg"
or ".png"
.
We besides 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 article image using the article edit class. Its job is to move the uploaded prototype to the fullsize
images binder we created in Step 1, equally well every bit generate a thumbnail version of the paradigm and store it in the thumb
folder.
The method accepts a single $image
parameter. This should be the element in the PHP $_FILES
superglobal assortment that contains all the data about the uploaded image file.
Here'southward how the storeUploadedImage()
method works:
- Bank check for an upload error
The start affair the method does is check that the'error'
element in the$image
array equals the constantUPLOAD_ERR_OK
. This indicates that the user uploaded an image, and that the upload was successful. If the upload went OK then the method starts to process the uploaded image; otherwise it does nothing. - Does the
Article
object take an ID?
Bold the file was uploaded OK, the method and then makes sure that theCommodity
object has an ID; in other words, that information technology has been saved to the database. This is important, because we're going to rename the image file using the article'due south ID in a moment, so that nosotros can easily acquaintance the epitome with the article. If the article doesn't have an ID then the method callstrigger_error()
to display an error message and exit. - Delete any previous image(south) for this commodity
Next the method calls theArticle::deleteImages()
method to delete any existing total-size and thumbnail image files associated with the article. (We'll write this method in a moment.) We do this in club to keep the image folders clean, without whatsoever old, unused article images lying about. For example, if the article currently has a.png
image uploaded, and the user uploads a new.jpg
image, we desire to brand certain that the at present-unused.png
images are deleted from the folders. - Get and shop the image filename extension
As you saw earlier, the$imageExtension
property needs to store the filename extension of the commodity image. Here the method uses thestrrchr()
office to excerpt the filename extension — that is, everything after (and including) the final dot in the filename — and stores the result in$imageExtension
, converted to lowercase withstrtolower()
for consistency. - Store the image
Now the method moves the bodily uploaded image into theimages/articles/fullsize
binder. To do this, it first 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's temporary folder, such as/tmp/
.Then the method calls
is_uploaded_file()
to check that the file in the temporary folder is indeed a file uploaded by PHP. This is a good security precaution to prevent sensitive system files accidentally being made public.Finally, the method calls the
move_uploaded_file()
function to move the uploaded image from the temporary folder to theimages/articles/fullsize
folder. This function takes ii arguments: the path to the file in the temporary folder, and the path to move the file to. Information technology'south a good thought to usemove_uploaded_file()
to move uploaded files, since the function performs additional security checks on the file before moving information technology.Once the file'due south in place, the method calls the
chmod()
function to ready the file'south permissions to 0666. This ensures that the file can be read and written by anyone, including the web server user and whatsoever FTP user that may demand to alter or delete the article images. (As ever, if you're on a shared web server then you might want to use more than restrictive permissions than this.) - Get the image size and blazon
The next job forstoreUploadedImage()
is to create the smaller thumbnail version of the uploaded image. Outset it callsgetimagesize()
, paassing in the path to the uploaded image, in club to go the image's width, height and format (GIF, JPEG or PNG), which information technology and then stores in$imageWidth
,$imageHeight
and$imageType
respectively. - Load the image into retentivity
Now that the method knows the type of epitome it's dealing with, it callsimagecreatefromgif()
,imagecreatefromjpeg()
orimagecreatefrompng()
every bit appropriate to load the image into an epitome resource variable,$imageResource
, for processing. - Re-create and resize the image to create the thumbnail
At present it's time to create the thumbnail image. To do this, the method first computes the thumbnail height,$thumbHeight
, based on the full-size image height ($imageHeight
), the full-size image width ($imageWidth
), and the desired thumbnail width (ARTICLE_THUMB_WIDTH
).Next information technology calls
imagecreatetruecolor()
to create a bare image resource for storing the thumbnail image information, passing in the width and height of the image to create. It stores this resources in a$thumbResource
variable.Finally, it calls
imagecopyresampled()
to create the smaller version of the uploaded prototype and store the result in the$thumbResource
variable. Information technology passes the following arguments toimagecopyresampled()
:- The prototype resource to store the resized image in (
$thumbResource
) - The uploaded epitome resource (
$imageResource
) - The (10,y) coordinates of top-left corner of the rectangle in
$thumbResource
to copy the image data to (0,0
— that is, the top left corner of the thumbnail) - The (x,y) coordinates of top-left corner of the rectangle in
$imageResource
to copy the paradigm data from (0,0
— that is, the top left corner of the uploaded image) - The width and summit 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 acme of the rectangle in
$imageResource
to re-create the paradigm data from ($imageWidth
and$imageHeight
— that is, the entire width and meridian of the uploaded image)
- The prototype resource to store the resized image in (
- Save the thumbnail
Now that the method has created the thumbnail image information and stored it in$thumbResource
, it needs to write the new thumbnail paradigm to disk. To exercise this, it callsimagegif()
,imagejpeg()
orimagepng()
, depending on the image type. It passes in both$thumbResource
and the path to use for the thumbnail image. To get the path, information technology calls thegetImagePath()
method (which we'll look at in a moment), passing in ourIMG_TYPE_THUMB
constant to bespeak that we want the path to the thumbnail. - Update the article record
Finally, since theArticle
object's$imageExtension
belongings may well have changed as a result of uploading the image, the method calls$this->update()
to update the commodity tape in the database.
A new deleteImages()
method
The deleteImages()
method is responsible for clearing out any image files associated with the electric current article. It's called by storeUploadedImage()
earlier uploading a new paradigm (every bit you lot saw in the previous section). In addition, it'south called if the administrator specifically asks to delete the article'south image and thumbnail via the Edit Article form. Finally, deleteImages()
is too called when the article itself needs to be deleted.
deleteImages()
calls PHP'south glob()
function to retrieve a listing of all prototype files in both the images/articles/fullsize
and images/articles/thumb
folders that are named subsequently the article's ID. For example, if the article's ID is 3, the call to glob()
volition return any prototype files called "3.gif"
, "iii.jpg"
or "three.png"
.
For each filename in the assortment returned past glob()
, the method attempts to delete the file by calling PHP'southward unlink()
function. If in that location's a trouble deleting the file then it raises an mistake and exits.
One time all the image files have been deleted, deleteImages()
sets the Article
object's $imageExtension
property to an empty string (""
) to indicate that the commodity no longer has an uploaded epitome.
A new getImagePath()
method
The final new method we've added to the Commodity
class is getImagePath()
, which returns the path to one of the two commodity images.
The method takes a unmarried, optional statement, $type
, that indicates whether it should return the path to the total-size image (IMG_TYPE_FULLSIZE
, the default), or the thumbnail (IMG_TYPE_THUMB
). It then uses the commodity's ID, along with the value stored in the article's $imageExtension
holding, to compute the path to the image file within the images/manufactures/fullsize
or images/articles/thumb
folder.
For example, if getImagePath()
is passed IMG_TYPE_THUMB
as an argument, the article's ID is 3, and its $imageExtension
belongings contains ".jpg"
, and then the method will return the value "images/articles/thumb/3.jpg"
.
Changes to the insert()
and update()
methods
The final modifications to Commodity.php
are inside the insert()
and update()
methods toward the stop of the file. Equally you tin see, nosotros've modified the SQL INSERT
and UPDATE
statements to conform the new $imageExtension
belongings so that the epitome extension is stored in the articles
table. We've also added extra bindValue()
calls to laissez passer the property'due south value to the SQL statements.
Step 5: Modify the admin.php
script
We at present need to brand some changes to admin.php
, the back-end admin script, so that it can handle image uploads. Fortunately, we've already done nigh of the hard work in our Commodity
class, so there aren't many changes that we need to make to this script.
Here's the modified admin.php
file with the changes highlighted. Replace the code in your existing cms/admin.php
file with this code:
<?php require( "config.php" ); session_start(); $action = isset( $_GET['action'] ) ? $_GET['activity'] : ""; $username = isset( $_SESSION['username'] ) ? $_SESSION['username'] : ""; if ( $action != "login" && $action != "logout" && !$username ) { login(); get out; } switch ( $action ) { case 'login': login(); pause; case 'logout': logout(); break; case 'newArticle': newArticle(); pause; case 'editArticle': editArticle(); interruption; instance 'deleteArticle': deleteArticle(); break; default: listArticles(); } office login() { $results = array(); $results['pageTitle'] = "Admin Login | Widget News"; if ( isset( $_POST['login'] ) ) { // User has posted the login form: attempt 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 message to the user $results['errorMessage'] = "Wrong username or password. Please endeavor again."; require( TEMPLATE_PATH . "/admin/loginForm.php" ); } } else { // User has not posted the login form withal: brandish the form crave( TEMPLATE_PATH . "/admin/loginForm.php" ); } } role logout() { unset( $_SESSION['username'] ); header( "Location: admin.php" ); } function newArticle() { $results = array(); $results['pageTitle'] = "New Commodity"; $results['formAction'] = "newArticle"; if ( isset( $_POST['saveChanges'] ) ) { // User has posted the article edit class: save the new article $article = new Article; $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: return to the article list header( "Location: admin.php" ); } else { // User has not posted the commodity edit grade yet: display the form $results['article'] = new Commodity; require( TEMPLATE_PATH . "/admin/editArticle.php" ); } } function editArticle() { $results = array(); $results['pageTitle'] = "Edit Article"; $results['formAction'] = "editArticle"; if ( isset( $_POST['saveChanges'] ) ) { // User has posted the commodity edit form: salve the article changes if ( !$commodity = Article::getById( (int)$_POST['articleId'] ) ) { header( "Location: admin.php?error=articleNotFound" ); render; } $commodity->storeFormValues( $_POST ); if ( isset($_POST['deleteImage']) && $_POST['deleteImage'] == "aye" ) $article->deleteImages(); $article->update(); if ( isset( $_FILES['epitome'] ) ) $article->storeUploadedImage( $_FILES['paradigm'] ); header( "Location: admin.php?status=changesSaved" ); } elseif ( isset( $_POST['cancel'] ) ) { // User has cancelled their edits: return to the article list header( "Location: admin.php" ); } else { // User has non posted the article edit grade yet: brandish the form $results['article'] = Article::getById( (int)$_GET['articleId'] ); require( TEMPLATE_PATH . "/admin/editArticle.php" ); } } role deleteArticle() { if ( !$commodity = Article::getById( (int)$_GET['articleId'] ) ) { header( "Location: admin.php?error=articleNotFound" ); return; } $article->deleteImages(); $article->delete(); header( "Location: admin.php?condition=articleDeleted" ); } office listArticles() { $results = array(); $data = Article::getList(); $results['articles'] = $data['results']; $results['totalRows'] = $data['totalRows']; $results['pageTitle'] = "All Manufactures"; if ( isset( $_GET['fault'] ) ) { if ( $_GET['error'] == "articleNotFound" ) $results['errorMessage'] = "Error: Commodity not establish."; } if ( isset( $_GET['status'] ) ) { if ( $_GET['status'] == "changesSaved" ) $results['statusMessage'] = "Your changes have been saved."; if ( $_GET['status'] == "articleDeleted" ) $results['statusMessage'] = "Article deleted."; } crave( TEMPLATE_PATH . "/admin/listArticles.php" ); } ?>
Let'south piece of work through these changes to admin.php
:
- Changes to
newArticle()
Nosotros've added a single line of code to thenewArticle()
function to handle image uploads. It checks that the'prototype'
chemical element exists in the$_FILES
assortment and, if it does exist, information technology calls theCommodity
object'sstoreUploadedImage()
method, passing in the$_FILES['image']
element, to shop the image and create the thumbnail. - Changes to
editArticle()
As withnewArticle()
, we've added a line of lawmaking 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 Article form. If they did and then we call$article->deleteImages()
to delete any existing images associated with the article. - Changes to
deleteArticle()
Finally, we've added a single line of lawmaking to thedeleteArticle()
function that calls$article->deleteImages()
. This ensures that any images associated with the article also go deleted.
Pace 6: Modify the forepart-end templates
Our database and PHP code can now handle article images, but we need to make some changes both to the front-stop templates that visitors see, and the dorsum-end admin templates.
Let'south start by altering the front end-end templates to brandish the article images.
1. homepage.php
The homepage.php
template displays the site's home folio, including a listing of recent articles. Nosotros'll modify this template to display each commodity's thumbnail next to the article in the list.
Here's the modified file with the new lines highlighted — supercede your onetime cms/templates/homepage.php
file with this lawmaking:
<?php include "templates/include/header.php" ?> <ul id="headlines"> <?php foreach ( $results['articles'] as $article ) { ?> <li> <h2> <span grade="pubDate"><?php echo appointment('j F', $commodity->publicationDate)?></span><a href=".?action=viewArticle&articleId=<?php echo $article->id?>"><?php echo htmlspecialchars( $article->title )?></a> </h2> <p class="summary"> <?php if ( $imagePath = $article->getImagePath( IMG_TYPE_THUMB ) ) { ?> <a href=".?action=viewArticle&articleId=<?php echo $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="./?action=annal">Commodity Annal</a></p> <?php include "templates/include/footer.php" ?>
Hither we've added some code inside the "summary" paragraph for each article. The code calls the article's getImagePath()
method, passing in IMG_TYPE_THUMB
to betoken that nosotros want the path to the article'southward thumbnail. It so stores the path in the $imagePath
variable. If this path is a non-simulated
value then the article has a thumbnail epitome, so the code then constructs a link to view the article, wrapped around an img
element that contains the thumbnail'due south path. Nosotros've added an articleImageThumb
form to the thumbnail paradigm then that we tin manner information technology in the stylesheet.
If $imagePath
'southward value is fake
and so the article doesn't have a thumbnail, so no markup is constructed.
2. archive.php
archive.php
displays the article archive page — that is, a list of all the articles in the database. We need to modify it in the same way as homepage.php
, then that it displays thumbnails next to the article summaries.
Here'due south the modified archive.php
file — supercede your quondam 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 $commodity ) { ?> <li> <h2> <bridge course="pubDate"><?php echo engagement('j F Y', $article->publicationDate)?></span><a href=".?action=viewArticle&articleId=<?php echo $article->id?>"><?php echo htmlspecialchars( $article->title )?></a> </h2> <p class="summary"> <?php if ( $imagePath = $commodity->getImagePath( IMG_TYPE_THUMB ) ) { ?> <a href=".?action=viewArticle&articleId=<?php repeat $article->id?>"><img class="articleImageThumb" src="<?php repeat $imagePath?>" alt="Article Thumbnail" /></a> <?php } ?> <?php echo htmlspecialchars( $article->summary )?> </p> </li> <?php } ?> </ul> <p><?php echo $results['totalRows']?> commodity<?php echo ( $results['totalRows'] != 1 ) ? 's' : '' ?> in total.</p> <p><a href="./">Render to Homepage</a></p> <?php include "templates/include/footer.php" ?>
As you can see, this is the same alter that nosotros fabricated to homepage.php
. If an article has an epitome, its thumbnail will now appear next to the article summary in the annal page.
3. viewArticle.php
The viewArticle.php
template displays an individual commodity page, containing the commodity'southward headline, summary and content. Just as we modified homepage.php
and archive.php
to display thumbnails next to the article summaries, we as well need to raise viewArticle.php
so that it displays the full-size article images in the commodity pages.
Here's the changed template. As ever, I've highlighted the new code. Save this code over your erstwhile cms/templates/viewArticle.php
file:
<?php include "templates/include/header.php" ?> <h1 fashion="width: 75%;"><?php echo htmlspecialchars( $results['article']->title )?></h1> <div style="width: 75%; font-style: italic;"><?php echo htmlspecialchars( $results['article']->summary )?></div> <div style="width: 75%; min-height: 300px;"> <?php if ( $imagePath = $results['article']->getImagePath() ) { ?> <img id="articleImageFullsize" src="<?php echo $imagePath?>" alt="Commodity Image" /> <?php } ?> <?php echo $results['article']->content?> </div> <p class="pubDate">Published on <?php echo 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 essentially the same style every bit the code added to the homepage and annal templates. Information technology calls the article's getImagePath()
method to get the path to the total-size article image. If the path isn't false
then the commodity has an image, and the code inserts the appropriate img
element into the markup. The img
element is given an id
of articleImageFullsize
so that we tin can style it using CSS.
Footstep 7: Alter the back-stop templates
There's actually only ane dorsum-end admin template that we need to change, and that'due south the editArticle.php
article edit course. Hither'southward the new template with changes highlighted — save 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 by http://airbladesoftware.com/notes/note-to-self-prevent-uploads-hanging-in-safari part closeKeepAlive() { if ( /AppleWebKit|MSIE/.test( navigator.userAgent) ) { var xhr = new XMLHttpRequest(); xhr.open( "GET", "/ping/close", false ); xhr.send(); } } </script> <div id="adminHeader"> <h2>Widget News Admin</h2> <p>You lot 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> <form action="admin.php?activity=<?php repeat $results['formAction']?>" method="post" enctype="multipart/form-data" onsubmit="closeKeepAlive()"> <input type="hidden" name="articleId" value="<?php repeat $results['article']->id ?>"/> <?php if ( isset( $results['errorMessage'] ) ) { ?> <div grade="errorMessage"><?php repeat $results['errorMessage'] ?></div> <?php } ?> <ul> <li> <label for="title">Commodity Title</label> <input type="text" name="title" id="title" placeholder="Name of the article" required autofocus maxlength="255" value="<?php echo htmlspecialchars( $results['article']->championship )?>" /> </li> <li> <characterization for="summary">Commodity Summary</label> <textarea proper name="summary" id="summary" placeholder="Brief clarification of the article" required maxlength="1000" style="superlative: 5em;"><?php echo htmlspecialchars( $results['commodity']->summary )?></textarea> </li> <li> <characterization 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['commodity']->content )?></textarea> </li> <li> <characterization for="publicationDate">Publication Date</characterization> <input type="appointment" name="publicationDate" id="publicationDate" placeholder="YYYY-MM-DD" required maxlength="10" value="<?php echo $results['article']->publicationDate ? date( "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 echo $imagePath ?>" alt="Article Image" /> </li> <li> <label></label> <input type="checkbox" name="deleteImage" id="deleteImage" value="yes"/ > <label for="deleteImage">Delete</label> </li> <?php } ?> <li> <label for="epitome">New Image</label> <input type="file" name="paradigm" id="image" placeholder="Choose an epitome to upload" maxlength="255" /> </li> </ul> <div form="buttons"> <input type="submit" name="saveChanges" value="Save Changes" /> <input type="submit" formnovalidate name="cancel" value="Cancel" /> </div> </course> <?php if ( $results['article']->id ) { ?> <p><a href="admin.php?activeness=deleteArticle&articleId=<?php repeat $results['commodity']->id ?>" onclick="return confirm('Delete This Article?')">Delete This Article</a></p> <?php } ?> <?php include "templates/include/footer.php" ?>
Let's take a look at each of these changes in turn:
- The
closeKeepAlive()
JavaScript role
For some reason, Safari on the Mac has suffered from a long-continuing issue whereby file uploads hang occasionally. (Observe out more about this issue here and here.) Since this is quite annoying if y'all use Safari (as I practise), I've added this little function that makes an Ajax request to a not-existent URL on the server, forcing Safari to shut its connection to the server. This seems to set the problem. My part is similar to this script, except that my office doesn't crave the Prototype.js library. - Changes to the
<form>
tag
We've added the attributeenctype="multipart/form-data"
to thegrade
element. This attribute is required whenever you create a form containing a file upload field. It 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 paradigm data).We've likewise fastened the
closeKeepAlive()
function equally asubmit
event handler to the form, so that the function runs when the form is submitted. - The article image and "delete" checkbox
The adjacent addition to the form displays the full-size image currently associated with the article (if whatsoever). As with the forepart-terminate templates, the PHP code calls the article'sgetImagePath()
method to retrieve the epitome's path. If an prototype path was returned, we add anli
chemical element to the folio containing a field label ("Current Epitome"), along with animg
chemical element linking to the image. We also include anotherli
element containing adeleteImage
checkbox. This lets the administrator delete any image(s) currently associated with the article. - The image upload field
Concluding, but by no means least, we add together the<input type="file">
upload field that allows the administrator to upload an image for this commodity. We give information technology aproper noun
attribute of"image"
, which ways that we're able to admission the uploaded file from our PHP lawmaking using$_FILES['image']
(see Stride four before in the tutorial).
Pace eight: Change the stylesheet
Next we'll make some additions and changes to our CMS's stylesheet, fashion.css
, in gild to mode the article images and thumbnails on the front cease, besides as the new elements in the Edit Article form.
Here's the new style.css
file with the changes highlighted. Supersede your existing style.css
file in your cms
folder with this file:
/* Style the body and outer container */ trunk { margin: 0; color: #333; background-colour: #00a0b0; font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; line-height: 1.5em; } #container { width: 960px; background: #fff; margin: 20px auto; padding: 20px; -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; } /* The logo and footer */ #logo { brandish: cake; width: 300px; padding: 0 660px 20px 0; edge: none; edge-bottom: 1px solid #00a0b0; margin-lesser: 40px; } #footer { border-height: 1px solid #00a0b0; margin-height: 40px; padding: 20px 0 0 0; font-size: .8em; } /* Headings */ h1 { colour: #eb6841; margin-lesser: 30px; line-superlative: one.2em; } h2, h2 a { color: #edc951; } h2 a { text-ornament: none; } /* Article headlines */ #headlines { list-style: none; padding-left: 0; width: 75%; } #headlines li { margin-bottom: 2em; overflow: subconscious; } .pubDate { font-size: .8em; color: #eb6841; text-transform: majuscule; } #headlines .pubDate { display: inline-block; width: 100px; font-size: .5em; vertical-align: middle; } #headlines.archive .pubDate { width: 130px; } #headlines .articleImageThumb { width: 120px; float: left; margin: 4px 20px 0 0; border: 1px solid #00a0b0; } .summary { padding-left: 100px; } #headlines.archive .summary { padding-left: 130px; } /* Article pages */ #articleImageFullsize { width: 250px; float: left; margin: 4px 20px 10px 0; border: 1px solid #00a0b0; } /* "Yous are logged in..." header on admin pages */ #adminHeader { width: 940px; padding: 0 10px; border-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 drib shadow */ form { margin: 20px motorcar; padding: 40px 20px; overflow: car; background: #fff4cf; edge: 1px solid #666; -moz-border-radius: 5px; -webkit-edge-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, .8); box-shadow: 0 0 .5em rgba(0, 0, 0, .viii); } /* Give form elements consistent margin, padding and line tiptop */ form ul { listing-style: none; margin: 0; padding: 0; overflow: hidden; } form ul li { margin: .9em 0 0 0; padding: 0; } grade * { line-height: 1em; } /* The field labels */ label { display: block; float: left; clear: left; text-align: correct; width: xv%; 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; background: #fff; } input { font-size: .9em; } input[type="checkbox"] { display: inline-block; padding: 0; margin: 0 0 .8em 0; width: car; } select { padding: 0; margin-bottom: 2.5em; position: relative; top: .7em; } textarea { font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; font-size: .9em; pinnacle: 5em; line-height: 1.5em; } textarea#content { font-family: "Courier New", courier, stock-still; } #articleImage { edge: 2px solid #666; } #deleteImage { clear: both; } label[for="deleteImage"] { float: none; brandish: inline; } input[type="file"] { bladder: left; } /* Identify a border around focused fields */ form *:focus { border: 2px solid #7c412b; outline: none; } /* Display correctly filled-in fields with a light-green background */ input:valid, textarea:valid { background: #efe; } /* Submit buttons */ .buttons { text-align: center; margin: 40px 0 0 0; } input[type="submit"] { brandish: 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, .viii); box-shadow: 0 0 .5em rgba(0, 0, 0, .eight); color: #fff; background: #ef7d50; font-weight: bold; -webkit-appearance: none; } input[type="submit"]:hover, input[blazon="submit"]:active { cursor: pointer; background: #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, .8) inset; box-shadow: 0 0 .5em rgba(0, 0, 0, .8) inset; } /* Tables */ table { width: 100%; border-plummet: plummet; } tr, thursday, td { padding: 10px; margin: 0; text-align: left; } tabular array, th { border: 1px solid #00a0b0; } th { border-left: none; edge-correct: 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; } /* Status and error boxes */ .statusMessage, .errorMessage { font-size: .8em; padding: .5em; margin: 2em 0; -moz-border-radius: 5px; -webkit-edge-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, .8); -box-shadow: 0 0 .5em rgba(0, 0, 0, .8); } .statusMessage { groundwork-color: #2b2; border: 1px solid #080; color: #fff; } .errorMessage { background-colour: #f22; border: 1px solid #800; color: #fff; }
Every bit you lot can see, nosotros've added various rulesets to style the thumbnails in the homepage and archive pages; the full-size images in article pages; and the "delete image" checkbox, article image and file upload field inside the "Edit Article" course (which yous added in Footstep 7).
On line 89, we've ready the thumbnail width to 120 pixels to friction match the ARTICLE_THUMB_WIDTH
setting in config.php
. Similarly, on line 107 we've fix the width of the total-size images in the commodity pages to 250 pixels. If you want to apply dissimilar widths for the thumbnails and images then you demand to change these two values, every bit well as the ARTICLE_THUMB_WIDTH
setting.
Try information technology out!
Great stuff! You now take a CMS that can handle image uploads. To test your new CMS, follow these steps:
- Log in
Open your browser and visit the base of operations URL of your CMS — for instance,http://localhost/cms/
. Click the Site Admin link in the footer, and log in. - Upload some images
Click an article in the All Manufactures listing, or add a new article past clicking the Add a New Commodity link at the bottom of the page. In the New Article / Edit Article form, click the button next to the New Image characterization at the bottom of the form. Choose a file to upload, and then click Relieve Changes to salve your article edits and upload the image. - View your images
Click the Widget News logo at the peak of the page to view the site. You should meet thumbnail images adjacent to the articles in the list. If you click the Article Annal link at the lesser of the folio then you should also run into the thumbnails at that place. Click a thumbnail (or commodity headline) to view the full article page, along with the full-size article image. - Changing and deleting images
Just to make sure everything works, try editing an article with an image and uploading a new paradigm. This should then supplant the previous article image and thumbnail. (Depending on your server and browser setup, you may demand to clear your browser's cache and reload the folio to see the new image.) You can also effort clicking the Delete checkbox to remove an existing image from an article.
You can also try out the demo on our server too! The demo is read-simply, and so you can't upload or delete images, but yous tin can see how the images await on the front end-cease, too as in the back-cease commodity edit form.
Summary
In this tutorial we've added an paradigm upload characteristic to the content management system from my original tutorial. Here's what we did:
- Created some folders within your CMS to store the article images and thumbnails
- Added some constants to the config file to specify the path to the images folder, the width to utilize for article 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
belongings, also as methods to handle prototype uploads, paradigm deletion, and retrieving image paths - Extended the
admin.php
admin script to permit uploading and deleting of article images - Altered the forepart-end templates,
homepage.php
,annal.php
andviewArticle.php
, to brandish the article thumbnails and full-size images every bit appropriate - Enhanced the commodity edit grade,
editArticle.php
, to include the image upload field, as well as the currently-uploaded image and a "delete" image" checkbox, and - Tweaked the stylesheet,
style.css
, to style the article images and thumbnails, as well every bit the article edit grade elements.
You tin now employ your CMS to publish articles illustrated with images. Now all you need to do is create some beautiful images! Enjoy. 🙂
[Flickr photo credits for sample images: Businessman, Cogs, Willy Wonka, Newspapers, Dollars.]
johnsonhistogives.blogspot.com
Source: https://www.elated.com/add-image-uploading-to-your-cms/
0 Response to "Index of Upload Image Share Com Pimpandhost 40"
Publicar un comentario