Have you thought about own facebook-style photo gallry system with comments? I think – yes. Today I made up my mind to prepare it for you. Main idea – when we click at images – they popup (ajax) with bigger image at the left and comments section at the right. All images are in the database (mySQL). And, of course, we will use PHP to achieve our result. Also, our comment system will prevent accepting more than 1 comment per 10 mins (to avoid spam).
[sociallocker]
[/sociallocker]
Now – download the source files and lets start coding !
Step 1. SQL
For our gallery I prepared two SQL tables: first table keeps records of our images. It contains several fields: title, filename, description, time of adding and comments count. Another table keeps comments. So, execute next SQL instructions:
01 | CREATE TABLE IF NOT EXISTS `s281_photos` ( |
02 | `id` int(10) unsigned NOT NULL auto_increment, |
03 | `title` varchar(255) default '', |
04 | `filename` varchar(255) default '', |
05 | `description` text NOT NULL, |
06 | `when` int(11) NOT NULL default '0', |
07 | `comments_count` int(11) NOT NULL default '0', |
09 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; |
10 | INSERT INTO `s281_photos` (`title`, `filename`, `description`, `when`) VALUES |
11 | ('Item #1', 'photo1.jpg', 'Description of Item #1', UNIX_TIMESTAMP()), |
12 | ('Item #2', 'photo2.jpg', 'Description of Item #2', UNIX_TIMESTAMP()+1), |
13 | ('Item #3', 'photo3.jpg', 'Description of Item #3', UNIX_TIMESTAMP()+2), |
14 | ('Item #4', 'photo4.jpg', 'Description of Item #4', UNIX_TIMESTAMP()+3), |
15 | ('Item #5', 'photo5.jpg', 'Description of Item #5', UNIX_TIMESTAMP()+4), |
16 | ('Item #6', 'photo6.jpg', 'Description of Item #6', UNIX_TIMESTAMP()+5), |
17 | ('Item #7', 'photo7.jpg', 'Description of Item #7', UNIX_TIMESTAMP()+6), |
18 | ('Item #8', 'photo8.jpg', 'Description of Item #8', UNIX_TIMESTAMP()+7), |
19 | ('Item #9', 'photo9.jpg', 'Description of Item #9', UNIX_TIMESTAMP()+8), |
20 | ('Item #10', 'photo10.jpg', 'Description of Item #10', UNIX_TIMESTAMP()+9); |
21 | CREATE TABLE IF NOT EXISTS `s281_items_cmts` ( |
22 | `c_id` int(11) NOT NULL AUTO_INCREMENT , |
23 | `c_item_id` int(12) NOT NULL default '0', |
24 | `c_ip` varchar(20) default NULL, |
25 | `c_name` varchar(64) default '', |
26 | `c_text` text NOT NULL , |
27 | `c_when` int(11) NOT NULL default '0', |
29 | KEY `c_item_id` (`c_item_id`) |
30 | ) ENGINE=MYISAM DEFAULT CHARSET=utf8; |
Step 2. PHP
Now, please create empty index.php file and put next code:
index.php
03 | if (version_compare(phpversion(), "5.3.0", ">=") == 1) |
04 | error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED); |
06 | error_reporting(E_ALL & ~E_NOTICE); |
07 | require_once('classes/CMySQL.php'); |
08 | require_once('classes/CMyComments.php'); |
09 | if ($_POST['action'] == 'accept_comment') { |
10 | echo $GLOBALS['MyComments']->acceptComment(); |
15 | $aItems = $GLOBALS['MySQL']->getAll("SELECT * FROM `s281_photos` ORDER by `when` ASC"); |
16 | foreach ($aItems as $i => $aItemInfo) { |
17 | $sPhotos .= '<div class="photo"><img src="images/thumb_'.$aItemInfo['filename'].'" id="'.$aItemInfo['id'].'" /><p>'.$aItemInfo['title'].' item</p><i>'.$aItemInfo['description'].'</i></div>'; |
21 | <html lang="en"><head> |
22 | <meta charset="utf-8" /> |
23 | <title>Facebook like photo gallery with comments | Script Tutorials</title> |
25 | <link href="css/main.css" rel="stylesheet" type="text/css" /> |
29 | google.load("jquery", "1.7.1"); |
31 | <script src="js/script.js"></script> |
35 | <h2>Facebook like photo gallery with comments</h2> |
38 | <!-- Container with last photos --> |
39 | <div class="container"> |
43 | <!-- Hidden preview block --> |
44 | <div id="photo_preview" style="display:none"> |
45 | <div class="photo_wrp"> |
46 | <img class="close" src="images/close.gif" /> |
47 | <div style="clear:both"></div> |
48 | <div class="pleft">test1</div> |
49 | <div class="pright">test2</div> |
50 | <div style="clear:both"></div> |
We have just created main index file of our gallery. By default – script generates a list of images (with title and description), and it also generates an empty hidden object which we are going to use in order to accept custom content by ajax requests. Also, when we post comments, we forward this request (to accept new comment) into comments class. Now, lets review next important php file:
photos_ajx.php
03 | if (version_compare(phpversion(), "5.3.0", ">=") == 1) |
04 | error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED); |
06 | error_reporting(E_ALL & ~E_NOTICE); |
07 | if ($_POST['action'] == 'get_info' && (int)$_POST['id'] > 0) { |
08 | require_once('classes/CMySQL.php'); |
09 | require_once('classes/CMyComments.php'); |
11 | $iPid = (int)$_POST['id']; |
12 | $aImageInfo = $GLOBALS['MySQL']->getRow("SELECT * FROM `s281_photos` WHERE `id` = '{$iPid}'"); |
14 | $sCommentsBlock = $GLOBALS['MyComments']->getComments($iPid); |
15 | $aItems = $GLOBALS['MySQL']->getAll("SELECT * FROM `s281_photos` ORDER by `when` ASC"); |
18 | $iPrev = (int)$GLOBALS['MySQL']->getOne("SELECT `id` FROM `s281_photos` WHERE `id` < '{$iPid}' ORDER BY `id` DESC LIMIT 1"); |
19 | $iNext = (int)$GLOBALS['MySQL']->getOne("SELECT `id` FROM `s281_photos` WHERE `id` > '{$iPid}' ORDER BY `id` ASC LIMIT 1"); |
20 | $sPrevBtn = ($iPrev) ? '<div class="preview_prev" onclick="getPhotoPreviewAjx(\''.$iPrev.'\')"><img src="images/prev.png" alt="prev" /></div>' : ''; |
21 | $sNextBtn = ($iNext) ? '<div class="preview_next" onclick="getPhotoPreviewAjx(\''.$iNext.'\')"><img src="images/next.png" alt="next" /></div>' : ''; |
22 | require_once('classes/Services_JSON.php'); |
23 | $oJson = new Services_JSON(); |
24 | header('Content-Type:text/javascript'); |
25 | echo $oJson->encode(array( |
26 | 'data1' => '<img class="fileUnitSpacer" src="images/'. $aImageInfo['filename'] .'">' . $sPrevBtn . $sNextBtn, |
27 | 'data2' => $sCommentsBlock, |
This file sends back information about requested photo. This is an enlarged image, block with comments and navigation buttons (to open previous / next images ajaxy). As you can see – we use comments class, now, it’s time to look at it too:
04 | function CMyComments() { |
07 | function getComments($i) { |
10 | $aComments = $GLOBALS['MySQL']->getAll("SELECT * FROM `s281_items_cmts` WHERE `c_item_id` = '{$i}' ORDER BY `c_when` DESC LIMIT 10"); |
11 | foreach ($aComments as $i => $aCmtsInfo) { |
12 | $sWhen = date('F j, Y H:i', $aCmtsInfo['c_when']); |
14 | <div class="comment" id="{$aCmtsInfo['c_id']}"> |
15 | <p>Comment from {$aCmtsInfo['c_name']} <span>({$sWhen})</span>:</p> |
16 | <p>{$aCmtsInfo['c_text']}</p> |
21 | <div class="comments" id="comments"> |
23 | <div id="comments_warning1" style="display:none">Don`t forget to fill both fields (Name and Comment)</div> |
24 | <div id="comments_warning2" style="display:none">You can't post more than one comment per 10 minutes (spam protection)</div> |
25 | <form onsubmit="return false;"> |
27 | <tr><td class="label"><label>Your name: </label></td><td class="field"><input type="text" value="" title="Please enter your name" id="name" /></td></tr> |
28 | <tr><td class="label"><label>Comment: </label></td><td class="field"><textarea name="text" id="text"></textarea></td></tr> |
29 | <tr><td class="label"> </td><td class="field"><button onclick="submitComment({$i}); return false;">Post comment</button></td></tr> |
32 | <div id="comments_list">{$sComments}</div> |
36 | function acceptComment() { |
37 | $iItemId = (int)$_POST['id']; |
38 | $sIp = $this->getVisitorIP(); |
39 | $sName = $GLOBALS['MySQL']->escape(strip_tags($_POST['name'])); |
40 | $sText = $GLOBALS['MySQL']->escape(strip_tags($_POST['text'])); |
41 | if ($sName && $sText) { |
43 | $iOldId = $GLOBALS['MySQL']->getOne("SELECT `c_item_id` FROM `s281_items_cmts` WHERE `c_item_id` = '{$iItemId}' AND `c_ip` = '{$sIp}' AND `c_when` >= UNIX_TIMESTAMP() - 600 LIMIT 1"); |
46 | $GLOBALS['MySQL']->res("INSERT INTO `s281_items_cmts` SET `c_item_id` = '{$iItemId}', `c_ip` = '{$sIp}', `c_when` = UNIX_TIMESTAMP(), `c_name` = '{$sName}', `c_text` = '{$sText}'"); |
47 | $GLOBALS['MySQL']->res("UPDATE `s281_photos` SET `comments_count` = `comments_count` + 1 WHERE `id` = '{$iItemId}'"); |
50 | $aComments = $GLOBALS['MySQL']->getAll("SELECT * FROM `s281_items_cmts` WHERE `c_item_id` = '{$iItemId}' ORDER BY `c_when` DESC LIMIT 10"); |
51 | foreach ($aComments as $i => $aCmtsInfo) { |
52 | $sWhen = date('F j, Y H:i', $aCmtsInfo['c_when']); |
54 | <div class="comment" id="{$aCmtsInfo['c_id']}"> |
55 | <p>Comment from {$aCmtsInfo['c_name']} <span>({$sWhen})</span>:</p> |
56 | <p>{$aCmtsInfo['c_text']}</p> |
66 | function getVisitorIP() { |
68 | if( ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) && ( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) ) { |
69 | $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; |
70 | } elseif( ( isset( $_SERVER['HTTP_CLIENT_IP'])) && (!empty($_SERVER['HTTP_CLIENT_IP'] ) ) ) { |
71 | $ip = explode(".",$_SERVER['HTTP_CLIENT_IP']); |
72 | $ip = $ip[3].".".$ip[2].".".$ip[1].".".$ip[0]; |
73 | } elseif((!isset( $_SERVER['HTTP_X_FORWARDED_FOR'])) || (empty($_SERVER['HTTP_X_FORWARDED_FOR']))) { |
74 | if ((!isset( $_SERVER['HTTP_CLIENT_IP'])) && (empty($_SERVER['HTTP_CLIENT_IP']))) { |
75 | $ip = $_SERVER['REMOTE_ADDR']; |
81 | $GLOBALS['MyComments'] = new CMyComments(); |
This class performs two main functions – it can accept new comments and also it can give us a box with comments. There are two more service classes: CMySQL.php and Services_JSON.php. They are two known classes to work with database and json. You can adjust database settings in database class. Both classes available in our package.
Step 3. Javascript
Now we should prepare user interface behavior with using javascript, please prepare next file for the project:
js/script.js
02 | function closePhotoPreview() { |
03 | $('#photo_preview').hide(); |
04 | $('#photo_preview .pleft').html('empty'); |
05 | $('#photo_preview .pright').html('empty'); |
08 | function getPhotoPreviewAjx(id) { |
09 | $.post('photos_ajx.php', {action: 'get_info', id: id}, |
11 | $('#photo_preview .pleft').html(data.data1); |
12 | $('#photo_preview .pright').html(data.data2); |
13 | $('#photo_preview').show(); |
18 | function submitComment(id) { |
19 | var sName = $('#name').val(); |
20 | var sText = $('#text').val(); |
22 | $.post('index.php', { action: 'accept_comment', name: sName, text: sText, id: id }, |
25 | $('#comments_list').fadeOut(1000, function () { |
30 | $('#comments_warning2').fadeIn(1000, function () { |
31 | $(this).fadeOut(1000); |
37 | $('#comments_warning1').fadeIn(1000, function () { |
38 | $(this).fadeOut(1000); |
45 | $('#photo_preview .photo_wrp').click(function (event) { |
46 | event.preventDefault(); |
49 | $('#photo_preview').click(function (event) { |
52 | $('#photo_preview img.close').click(function (event) { |
56 | $('.container .photo img').click(function (event) { |
57 | if (event.preventDefault) event.preventDefault(); |
58 | getPhotoPreviewAjx($(this).attr('id')); |
Please note, we use jQuery instructions in our script (I hope that you haven’t forgot that we linked jQuery library in the header section through google service).
Step 4. CSS
In the long run, we should stylize our page elements (our container with photos, photo preview area with comments):
css/main.css
003 | border: 1px solid #111111; |
011 | -moz-border-radius: 5px; |
012 | -ms-border-radius: 5px; |
013 | -o-border-radius: 5px; |
014 | -webkit-border-radius: 5px; |
018 | border: 1px solid transparent; |
025 | -moz-box-sizing: border-box; |
026 | -webkit-box-sizing: border-box; |
027 | -o-box-sizing: border-box; |
028 | box-sizing: border-box; |
030 | -moz-transition: border 0.2s ease 0s; |
031 | -ms-transition: border 0.2s ease 0s; |
032 | -o-transition: border 0.2s ease 0s; |
033 | -webkit-transition: border 0.2s ease 0s; |
034 | transition: border 0.2s ease 0s; |
051 | background-color: rgba(0, 0, 0, 0.7); |
063 | background-color: #FAFAFA; |
065 | margin: 100px auto 0; |
069 | vertical-align: middle; |
071 | -moz-border-radius: 5px; |
072 | -ms-border-radius: 5px; |
073 | -o-border-radius: 5px; |
074 | -webkit-border-radius: 5px; |
092 | .preview_prev, .preview_next { |
098 | -moz-transition: opacity 0.2s ease 0s; |
099 | -ms-transition: opacity 0.2s ease 0s; |
100 | -o-transition: opacity 0.2s ease 0s; |
101 | -webkit-transition: opacity 0.2s ease 0s; |
102 | transition: opacity 0.2s ease 0s; |
104 | .preview_prev:hover, .preview_next:hover { |
118 | #comments table td.label { |
125 | #comments table label { |
129 | vertical-align: middle; |
131 | #comments table td.field input, #comments table td.field textarea { |
132 | border: 1px solid #96A6C5; |
133 | font-family: Verdana,Arial,sans-serif; |
143 | #comments_list .comment { |
144 | border-top: 1px solid #000; |
147 | #comments_list .comment:first-child { |
148 | border-top-width:0px; |
150 | #comments_list .comment span { |
Conclusion
And again, we have just prepared our next practically useful tutorial. Sure that this material will useful for your own projects. Good luck in your work!