Browse files

first commit

  • Loading branch information...
0 parents commit 72f9ef80e9aca59730e4f1c906cad2e76c22dbf4 PHPMaster.com committed Oct 11, 2011
Showing with 329 additions and 0 deletions.
  1. +2 −0 README
  2. +113 −0 cron/crontask.php
  3. +116 −0 include/POP3.php
  4. +12 −0 include/config.php
  5. +28 −0 public/approve.php
  6. +32 −0 public/index.php
  7. +26 −0 sql/schema.sql
2 README
@@ -0,0 +1,2 @@
+This is the source code for the article Creating a Mobile Photo Blog by Martin
+Psinas, available at http://phpmaster.com/creating-a-mobile-photo-blog-part-1
113 cron/crontask.php
@@ -0,0 +1,113 @@
+#! /usr/bin/php
+<?php
+chdir(pathinfo(__FILE__, PATHINFO_DIRNAME));
+require_once "../include/POP3.php";
+require_once "../include/config.php";
+
+// connect to database
+$db = new PDO("mysql:host=" . DB_HOST . ";dbname=" . DB_NAME, DB_USER, DB_PASSWORD);
+
+// prepared SQL statements
+$sqlSelectValid = "SELECT is_valid FROM pending WHERE message_id = :msgno";
+$stmSelectValid = $db->prepare($sqlSelectValid);
+
+$sqlInsertToken = "INSERT INTO pending (message_id, token) VALUES (:msgno, :token)";
+$stmInsertToken = $db->prepare($sqlInsertToken);
+
+$sqlInsertPost = "INSERT INTO blog_posts (title, body, create_ts) VALUES (:title, :body, FROM_UNIXTIME(:timestamp))";
+$stmInsertPost = $db->prepare($sqlInsertPost);
+
+$sqlInsertImage = "INSERT INTO images (post_id, image_path) VALUES (:post_id, :img_path)";
+$stmInsertImage = $db->prepare($sqlInsertImage);
+
+$sqlDeletePending = "DELETE FROM pending WHERE message_id = :msgno";
+$stmDeletePending = $db->prepare($sqlDeletePending);
+
+// retrieve messages
+$pop3 = new POP3(EMAIL_HOST, EMAIL_USER, EMAIL_PASSWORD);
+$msgList = $pop3->listMessages();
+
+if (!empty($msgList)) {
+ foreach ($msgList as $value) {
+ // see if a token exists
+ $stmSelectValid->execute(array(":msgno" => $value["msgno"]));
+ $isValid = $stmSelectValid->fetchColumn();
+
+ // message has been approved
+ if ($isValid == "Y") {
+ // get message contents
+ $msg = $pop3->mimeToArray($value["msgno"], true);
+ // convert date to timestamp
+ $timestamp = strtotime($value["date"]);
+ if ($timestamp === false) {
+ $timestamp = null;
+ }
+ $title = $value["subject"];
+ if (sizeof($msg) > 1) {
+ $body = (isset($msg["1.1"])) ? $msg["1.1"]["data"] : $msg[1]["data"];
+ }
+ else {
+ $body = $pop3->fetchBody($value["msgno"]);
+ }
+ // copy images to server
+ $files = array();
+ foreach ($msg as $parts) {
+ if (isset($parts["filename"])) {
+ $dir = ROOT_PATH . IMG_PATH;
+ $ext = strtolower(pathinfo($parts["filename"], PATHINFO_EXTENSION));
+ // only accept jpg or png
+ if (in_array($ext, array("jpg","png"))) {
+ // give the file a unique name
+ $hash = sha1($parts["data"]);
+ $file = $hash . "." . $ext;
+ $thumb = $hash . "_t." . $ext;
+
+ if (!file_exists($dir . $file)) {
+ // copy image and make thumbnails
+ $img = new Imagick();
+ $img->readimageblob($parts["data"]);
+ $img->writeImage($dir . $file);
+ $img->thumbnailImage(MAX_WIDTH, 0);
+ $img->writeImage($dir . $thumb);
+ $img->clear();
+ $img->destroy();
+
+ $files[] = IMG_PATH . $file;
+ }
+ }
+ }
+ }
+
+ // update database
+ if (isset($timestamp, $title, $body)) {
+ // insert post
+ $stmInsertPost->execute(array(":title" => $title, ":body" => $body, ":timestamp" => $timestamp));
+ $postID = $db->lastInsertId();
+
+ // insert images
+ $stmInsertImage->bindParam(":post_id", $postID);
+ $stmInsertImage->bindParam(":img_path", $path);
+ foreach($files as $path) {
+ $stmInsertImage->execute();
+ }
+
+ // delete token
+ $stmDeletePending->execute(array(":msgno" => $value["msgno"]));
+ }
+ // mark e-mail for deletion
+ $pop3->deleteMessage($value["msgno"]);
+ }
+ // message has no approval token
+ else {
+ // create a unique token
+ $token = md5(uniqid(mt_rand(), true));
+ $stmInsertToken->execute(array(":msgno" => $value["msgno"], ":token" => $token));
+
+ // send email for approval
+ $title = htmlentities($value["subject"], ENT_QUOTES);
+ $subject = "Pending Post Notification: " . $title;
+ $message = '<a href="http://www.example.com/approve.php?token=' . $token . '">Click Here To Approve</a>';
+ mail(PUBLIC_EMAIL, $subject, $message);
+ }
+ }
+}
116 include/POP3.php
@@ -0,0 +1,116 @@
+<?php
+class POP3
+{
+ private $conn;
+
+ // make connection
+ public function __construct($host, $user, $pass, $folder = "INBOX", $port = 110, $useSSL = false) {
+ $ssl = ($useSSL) ? "" : "/novalidate-cert";
+ $mailbox = sprintf("{%s:%d/pop3%s}%s", $host, $port, $ssl, $folder);
+ $this->conn = imap_open($mailbox, $user, $pass);
+ }
+
+ // close connection and trigger expunge
+ public function __destruct() {
+ imap_close($this->conn, CL_EXPUNGE);
+ }
+
+ // fetch the body of an email with no attachments
+ public function fetchBody($msgNum = "") {
+ return imap_fetchbody($this->conn, $msgNum, 1);
+ }
+
+ // retrieve a list of messages
+ public function listMessages($msgNum = "") {
+ $msgList = array();
+ if ($msgNum) {
+ $range = $msgNum;
+ }
+ else {
+ $info = imap_check($this->conn);
+ $range = "1:" . $info->Nmsgs;
+ }
+ $response = imap_fetch_overview($this->conn, $range);
+ foreach ($response as $msg) {
+ $msgList[$msg->msgno] = (array)$msg;
+ }
+ return $msgList;
+ }
+
+ // delete a message
+ public function deleteMessage($msgNum) {
+ return imap_delete($this->conn, $msgNum);
+ }
+
+ // parse headers into usable code
+ public function parseHeaders($headers) {
+ $headers = preg_replace('/\r\n\s+/m', "", $headers);
+ preg_match_all('/([^: ]+): (.+?(?:\r\n\s(?:.+?))*)?\r\n/m',
+ $headers, $matches);
+ foreach ($matches[1] as $key => $value) {
+ $result[$value] = $matches[2][$key];
+ }
+ return $result;
+ }
+
+ // separate MIME types
+ public function mimeToArray($msgNum, $parseHeaders = false) {
+ $mail = imap_fetchstructure($this->conn, $msgNum);
+ $mail = $this->getParts($msgNum, $mail, 0);
+ if ($parseHeaders) {
+ $mail[0]["parsed"] = $this->parseHeaders($mail[0]["data"]);
+ }
+ return $mail;
+ }
+
+ // separate mail parts
+ public function getParts($msgNum, $part, $prefix) {
+ $attachments = array();
+ $attachments[$prefix] = $this->decodePart($msgNum, $part, $prefix);
+
+ // multi-part
+ if (isset($part->parts)) {
+ $prefix = ($prefix) ? $prefix . "." : "";
+ foreach ($part->parts as $number => $subpart) {
+ $attachments = array_merge($attachments, $this->getParts($msgNum, $subpart, $prefix . ($number + 1)));
+ }
+ }
+ return $attachments;
+ }
+
+ // decode attachments
+ public function decodePart($msgNum, $part, $prefix) {
+ $attachment = array();
+
+ if ($part->ifdparameters) {
+ foreach ($part->dparameters as $obj) {
+ $attachment[strtolower($obj->attribute)] = $obj->value;
+ if(strtolower($obj->attribute) == "filename") {
+ $attachment["is_attachment"] = true;
+ $attachment["filename"] = $obj->value;
+ }
+ }
+ }
+
+ if ($part->ifparameters) {
+ foreach ($part->parameters as $obj) {
+ $attachment[strtolower($obj->attribute)] = $obj->value;
+ if (strtolower($obj->attribute) == "name") {
+ $attachment["is_attachment"] = true;
+ $attachment["name"] = $obj->value;
+ }
+ }
+ }
+
+ $attachment["data"] = imap_fetchbody($this->conn, $msgNum, $prefix);
+ // 3 is base64
+ if ($part->encoding == 3) {
+ $attachment["data"] = base64_decode($attachment["data"]);
+ }
+ // 4 is quoted-printable
+ else if ($part->encoding == 4) {
+ $attachment["data"] = quoted_printable_decode($attachment["data"]);
+ }
+ return($attachment);
+ }
+}
12 include/config.php
@@ -0,0 +1,12 @@
+<?php
+// database connection
+define("DB_HOST", "localhost");
+define("DB_USER", "dbuser");
+define("DB_PASSWORD", "********");
+define("DB_NAME", "test");
+
+// miscellaneous
+define("PUBLIC_EMAIL", "mobile@example.com");
+define("MAX_WIDTH", 240);
+define("ROOT_PATH", "/var/www/");
+define("IMG_PATH", "photoblog/");
28 public/approve.php
@@ -0,0 +1,28 @@
+<?php
+require_once "../include/config.php";
+
+// connect to database
+$db = new PDO("mysql:host=" . DB_HOST . ";dbname=" . DB_NAME, DB_USER, DB_PASSWORD);
+
+// receive incoming token
+$token = isset($_GET["token"]) && ctype_alnum($_GET["token"]) ? $_GET["token"] : null;
+
+if (!empty($token)) {
+ // verify token
+ $sql = "SELECT pending_id FROM pending WHERE token = :token AND is_valid = 'N'";
+ $stm = $db->prepare($sql);
+ $stm->execute(array(":token" => $token));
+ $pendID = $stm->fetchColumn();
+ $stm->closeCursor();
+
+ if (!empty($pendID)) {
+ // set the entry to be published
+ $sql = "UPDATE pending SET is_valid = 'Y' WHERE pending_id = :pend_id";
+ $stm = $db->prepare($sql);
+ $stm->execute(array(":pend_id" => $pendID));
+ echo "<p>Publishing...</p>";
+ }
+ else {
+ echo "<p>Invalid token.</p>";
+ }
+}
32 public/index.php
@@ -0,0 +1,32 @@
+<?php
+require_once "../include/config.php";
+
+// connect to database
+$db = new PDO("mysql:host=" . DB_HOST . ";dbname=" . DB_NAME, DB_USER, DB_PASSWORD);
+
+// prepared SQL statements
+$sqlSelectImages = "SELECT * FROM images WHERE post_id = :post_id";
+$stmSelectImages = $db->prepare($sqlSelectImages);
+
+// output each blog post
+$sqlSelectPosts = "SELECT * FROM blog_posts ORDER BY create_ts DESC";
+$result = $db->query($sqlSelectPosts);
+while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
+ $stmSelectImages->execute(array(":post_id" => $row["post_id"]));
+ $images = $stmSelectImages->fetchAll();
+
+ echo "<div>";
+ echo "<h2>" . htmlspecialchars($row["title"]) . "</h2>";
+ echo "<p>" . htmlspecialchars($row["body"]) . "</p>";
+ if (!empty($images)) {
+ // output thumbnail and link for each image
+ foreach ($images as $img) {
+ $ext = "." . pathinfo($img["image_path"], PATHINFO_EXTENSION);
+ $thmb = str_replace($ext, "_t" . $ext, $img["image_path"]);
+ echo '<a href="' . $img["image_path"] . '">';
+ echo '<img src="' . $thmb . '" alt="' . basename($img["image_path"]) . '"/>';
+ echo "</a>";
+ }
+ }
+ echo "</div>";
+}
26 sql/schema.sql
@@ -0,0 +1,26 @@
+CREATE TABLE blog_posts (
+ post_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
+ title VARCHAR(100) NOT NULL,
+ body TEXT NOT NULL,
+ create_ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+
+ PRIMARY KEY (post_id)
+);
+
+CREATE TABLE images (
+ image_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
+ post_id INTEGER UNSIGNED NOT NULL,
+ image_path VARCHAR(200) NOT NULL,
+
+ PRIMARY KEY (image_id),
+ FOREIGN KEY (post_id) REFERENCES blog_posts(post_id)
+);
+
+CREATE TABLE pending (
+ pending_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
+ message_id INTEGER UNSIGNED NOT NULL,
+ token CHAR(32) NOT NULL,
+ is_valid ENUM('Y','N') DEFAULT 'N',
+
+ PRIMARY KEY (pending_id)
+);

0 comments on commit 72f9ef8

Please sign in to comment.