Browse files

nearing completion

  • Loading branch information...
1 parent 1757436 commit 266104bec4899d5b91db995d6e3808cd9743ae97 @leggetter leggetter committed Feb 23, 2012
View
106 Persistence.php
@@ -0,0 +1,106 @@
+<?php
+date_default_timezone_set('UTC');
+
+class Persistence {
+
+ private $data = array();
+
+ function __construct() {
+ session_start();
+
+ if( isset($_SESSION['blog_comments']) == true ){
+ $this->data = $_SESSION['blog_comments'];
+ }
+ }
+
+ /**
+ * Get all comments for the given post.
+ */
+ function get_comments($comment_post_ID) {
+ $comments = array();
+ if( isset($this->data[$comment_post_ID]) == true ) {
+ $comments = $this->data[$comment_post_ID];
+ }
+ return $comments;
+ }
+
+ /**
+ * Get all comments.
+ */
+ function get_all_comments() {
+ return $this->data;
+ }
+
+ /**
+ * Store the comment.
+ */
+ function add_comment($vars) {
+
+ $added = false;
+
+ $comment_post_ID = $vars['comment_post_ID'];
+ $input = array(
+ 'comment_author' => $vars['comment_author'],
+ 'email' => $vars['email'],
+ 'comment' => $vars['comment'],
+ 'comment_post_ID' => $comment_post_ID,
+ 'date' => date('r'));
+
+ if($this->validate_input($input) == true) {
+ if( isset($this->data[$comment_post_ID]) == false ) {
+ $this->data[$comment_post_ID] = array();
+ }
+
+ $input['id'] = uniqid('comment_');
+
+ $this->data[$comment_post_ID][] = $input;
+
+ $this->sync();
+
+ $added = $input;
+ }
+ return $added;
+ }
+
+ function delete_all() {
+ $this->data = array();
+ $this->sync();
+ }
+
+ private function sync() {
+ $_SESSION['blog_comments'] = $this->data;
+ }
+
+ /**
+ * TODO: much more validation and sanitization. Use a library.
+ */
+ private function validate_input($input) {
+ $input['email'] = filter_var($input['email'], FILTER_SANITIZE_EMAIL);
+ if (filter_var($input['email'], FILTER_VALIDATE_EMAIL) == false) {
+ return false;
+ }
+
+ if($this->check_string($input['comment_author']) == false) {
+ return false;
+ }
+ $input['comment_author'] = htmlentities($input['comment_author']);
+
+ if($this->check_string($input['comment'], 5) == false) {
+ return false;
+ }
+ $input['comment'] = htmlentities($input['comment']);
+
+ $input['comment_post_ID'] = filter_var($input['comment_post_ID'], FILTER_VALIDATE_INT);
+ if (filter_var($input['comment_post_ID'], FILTER_VALIDATE_INT) == false) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private function check_string($string, $min_size = 1) {
+ return strlen(trim($string)) >= $min_size;
+ }
+}
+
+?>
View
8 clear.php
@@ -0,0 +1,8 @@
+<?php
+require('Persistence.php');
+
+$db = new Persistence();
+$db->delete_all();
+
+print_r($db->get_all_comments());
+?>
View
119 css/global-forms.css
@@ -0,0 +1,119 @@
+/*
+ Name: Global Form Styles
+ Description: Default styling for forms.
+ Message classes borrowed from
+ http://www.blueprintcss.org/
+ Coder: Enrique Ramirez
+ Coder URI: http://enrique-ramirez.com
+*/
+
+fieldset {
+ background: #f9f9f9;
+ margin: 1.5em;
+ padding: 2em;
+
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+}
+legend {font-size: 1.25em; margin-bottom: 0 !important; margin-bottom: 1.429em; padding: 0 .5em;}
+label {font-size: 1.1em; height: 25px; line-height: 25px;}
+
+ /* Input Types */
+ input[type='text'],
+ input[type='email'],
+ input[type='url'],
+ textarea {
+ background: #fff;
+ border: 1px solid #eee;
+ color: #999;
+ font-family: inherit;
+ font-size: inherit;
+ padding: 2px;
+ }
+ input[type='text']:hover,
+ input[type='email']:hover,
+ input[type='url']:hover,
+ textarea:hover {
+ background: #FFFBEF;
+ border-color: #ff0;
+ cursor: text;
+ }
+ input[type='text']:focus,
+ input[type='email']:focus,
+ input[type='url']:focus,
+ textarea:focus {
+ background: #ffC;
+ border-color: #ff1;
+ color: #0d0d0d;
+ }
+
+ input[type='checkbox'], input[type='radio'] {
+ display: block;
+ margin-top: 4px;
+ }
+
+ input[type='submit'] {
+ background: #C74350;
+ border: 0;
+ border-radius: 5px;
+ color: #fff;
+ cursor: pointer;
+ font-family: inherit;
+ font-size: inherit;
+ padding: .3em 2em;
+ text-shadow: 1px 1px 1px #000;
+ }
+
+ input:required, textarea:required {outline: 1px solid #C74350;}
+
+ /* Textarea */
+ textarea {width: 99%; margin-bottom: 7px;}
+
+ /* Alignments */
+ div.left {margin-left: 1em;}
+ div.right {margin-right: 1em;}
+
+ .labels-left label, div.left label {
+ clear: left;
+ float: left;
+ margin-right: .5em;
+ text-align: right;
+ }
+ .labels-left input, div.left input, .labels-left select, div.left select {float: left;}
+
+ .labels-right label, div.right label {
+ float: left;
+ margin-left: .5em;
+ text-align: right;
+ }
+ .labels-right input, div.right input, .labels-right select, div.right select {clear: left; float: left;}
+
+ .labels-top label, div.top label {display: block;}
+ .labels-top input, div.top input {margin-bottom: 0;}
+
+ /* Columns */
+ .columns-2 div.column1, .columns-2 div.column2 {float: left; width: 48%;}
+ .columns-2 input.text {width: 150px;}
+
+ .columns-3 div.column1, .columns-3 div.column2, .columns-3 div.column3 {float: left; width: 33%;}
+ .columns-3 input.text {width: 120px;}
+
+ .columns-2 div.left, .columns-2 div.right, .columns-2 div.top {width: 32%;}
+ .columns-3 div.left, .columns-3 div.right, .columns-3 div.top {width: 29%;}
+
+/* Messages classes */
+.req {color: #C74350;}
+.error,.notice, .success {
+ padding: .2em;
+ margin-bottom: 1em;
+ border: 2px solid #ddd;
+}
+
+.error {background: #FBE3E4; border-color: #FBC2C4; color: #8a1f11;}
+.notice {background: #FFF6BF; border-color: #FFD324; color: #514721;}
+.success {background: #E6EFC2; border-color: #C6D880; color: #264409;}
+
+.error a {color: #8a1f11;}
+.notice a {color: #514721;}
+.success a {color: #264409;}
View
457 css/main.css
@@ -0,0 +1,457 @@
+/*
+ Name: Smashing HTML5
+ Date: July 2009
+ Description: Sample layout for HTML5 and CSS3 goodness.
+ Version: 1.0
+ Author: Enrique Ramírez
+ Autor URI: http://enrique-ramirez.com
+*/
+
+/* Imports */
+@import url("reset.css");
+@import url("global-forms.css");
+
+/***** Global *****/
+/* Body */
+ body {
+ background: #F5F4EF url('../images/bg.png');
+ color: #000305;
+ font-size: 87.5%; /* Base font size: 14px */
+ font-family: 'Trebuchet MS', Trebuchet, 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
+ line-height: 1.429;
+ margin: 0;
+ padding: 0;
+ text-align: left;
+ }
+
+/* Headings */
+h2 {font-size: 1.571em} /* 22px */
+h3 {font-size: 1.429em} /* 20px */
+h4 {font-size: 1.286em} /* 18px */
+h5 {font-size: 1.143em} /* 16px */
+h6 {font-size: 1em} /* 14px */
+
+h2, h3, h4, h5, h6 {
+ font-weight: 400;
+ line-height: 1.1;
+ margin-bottom: .8em;
+}
+
+/* Anchors */
+a {outline: 0;}
+a img {border: 0px; text-decoration: none;}
+a:link, a:visited {
+ color: #C74350;
+ padding: 0 1px;
+ text-decoration: underline;
+}
+a:hover, a:active {
+ background-color: #C74350;
+ color: #fff;
+ text-decoration: none;
+ text-shadow: 1px 1px 1px #333;
+}
+
+/* Paragraphs */
+p {margin-bottom: 1.143em;}
+* p:last-child {margin-bottom: 0;}
+
+strong, b {font-weight: bold;}
+em, i {font-style: italic;}
+
+::-moz-selection {background: #F6CF74; color: #fff;}
+::selection {background: #F6CF74; color: #fff;}
+
+/* Lists */
+ul {
+ list-style: outside disc;
+ margin: 1em 0 1.5em 1.5em;
+}
+
+ol {
+ list-style: outside decimal;
+ margin: 1em 0 1.5em 1.5em;
+}
+
+dl {margin: 0 0 1.5em 0;}
+dt {font-weight: bold;}
+dd {margin-left: 1.5em;}
+
+/* Quotes */
+blockquote {font-style: italic;}
+cite {}
+
+q {}
+
+/* Tables */
+table {margin: .5em auto 1.5em auto; width: 98%;}
+
+ /* Thead */
+ thead th {padding: .5em .4em; text-align: left;}
+ thead td {}
+
+ /* Tbody */
+ tbody td {padding: .5em .4em;}
+ tbody th {}
+
+ tbody .alt td {}
+ tbody .alt th {}
+
+ /* Tfoot */
+ tfoot th {}
+ tfoot td {}
+
+/* HTML5 tags */
+header, section, footer,
+aside, nav, article, figure {
+ display: block;
+}
+
+/***** Layout *****/
+.body {clear: both; margin: 0 auto; width: 800px;}
+img.right figure.right {float: right; margin: 0 0 2em 2em;}
+img.left, figure.left {float: right; margin: 0 0 2em 2em;}
+
+/*
+ Header
+*****************/
+#banner {
+ margin: 0 auto;
+ padding: 2.5em 0 0 0;
+}
+
+ /* Banner */
+ #banner h1 {font-size: 3.571em; line-height: .6;}
+ #banner h1 a:link, #banner h1 a:visited {
+ color: #000305;
+ display: block;
+ font-weight: bold;
+ margin: 0 0 .6em .2em;
+ text-decoration: none;
+ width: 427px;
+ }
+ #banner h1 a:hover, #banner h1 a:active {
+ background: none;
+ color: #C74350;
+ text-shadow: none;
+ }
+
+ #banner h1 strong {font-size: 0.36em; font-weight: normal;}
+
+ /* Main Nav */
+ #banner nav {
+ background: #000305;
+ font-size: 1.143em;
+ height: 40px;
+ line-height: 30px;
+ margin: 0 auto 2em auto;
+ padding: 0;
+ text-align: center;
+ width: 800px;
+
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ }
+
+ #banner nav ul {list-style: none; margin: 0 auto; width: 800px;}
+ #banner nav li {float: left; display: inline; margin: 0;}
+
+ #banner nav a:link, #banner nav a:visited {
+ color: #fff;
+ display: inline-block;
+ height: 30px;
+ padding: 5px 1.5em;
+ text-decoration: none;
+ }
+ #banner nav a:hover, #banner nav a:active,
+ #banner nav .active a:link, #banner nav .active a:visited {
+ background: #C74451;
+ color: #fff;
+ text-shadow: none !important;
+ }
+
+ #banner nav li:first-child a {
+ border-top-left-radius: 5px;
+ -moz-border-radius-topleft: 5px;
+ -webkit-border-top-left-radius: 5px;
+
+ border-bottom-left-radius: 5px;
+ -moz-border-radius-bottomleft: 5px;
+ -webkit-border-bottom-left-radius: 5px;
+ }
+
+/*
+ Featured
+*****************/
+#featured {
+ background: #fff;
+ margin-bottom: 2em;
+ overflow: hidden;
+ padding: 20px;
+ width: 760px;
+
+ border-radius: 10px;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+}
+
+#featured figure {
+ border: 2px solid #eee;
+ float: right;
+ margin: 0.786em 2em 0 5em;
+ width: 248px;
+}
+#featured figure img {display: block; float: right;}
+
+#featured h2 {color: #C74451; font-size: 1.714em; margin-bottom: 0.333em;}
+#featured h3 {font-size: 1.429em; margin-bottom: .5em;}
+
+#featured h3 a:link, #featured h3 a:visited {color: #000305; text-decoration: none;}
+#featured h3 a:hover, #featured h3 a:active {color: #fff;}
+
+/*
+ Body
+*****************/
+#content {
+ background: #fff;
+ margin-bottom: 2em;
+ overflow: hidden;
+ padding: 20px 20px;
+ width: 760px;
+
+ border-radius: 10px;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+}
+
+/*
+ Extras
+*****************/
+#extras {margin: 0 auto 3em auto; overflow: hidden;}
+
+#extras ul {list-style: none; margin: 0;}
+#extras li {border-bottom: 1px solid #fff;}
+#extras h2 {
+ color: #C74350;
+ font-size: 1.429em;
+ margin-bottom: .25em;
+ padding: 0 3px;
+}
+
+#extras a:link, #extras a:visited {
+ color: #444;
+ display: block;
+ border-bottom: 1px solid #F4E3E3;
+ text-decoration: none;
+ padding: .3em .25em;
+}
+
+#extras li:last-child,
+#extras li:last-child a {border: 0}
+
+#extras .blogroll li:nth-last-child(2),
+#extras .blogroll li:nth-last-child(3),
+#extras .blogroll li:nth-last-child(2) a,
+#extras .blogroll li:nth-last-child(3) a {border: 0;}
+
+#extras a:hover, #extras a:active {color: #fff;}
+
+ /* Blogroll */
+ #extras .blogroll {
+ float: left;
+ width: 615px;
+ }
+
+ #extras .blogroll li {float: left; margin: 0 20px 0 0; width: 185px;}
+
+ /* Social */
+ #extras .social {
+ float: right;
+ width: 175px;
+ }
+
+ #extras div[class='social'] a {
+ background-repeat: no-repeat;
+ background-position: 3px 6px;
+ padding-left: 25px;
+ }
+
+ /* Icons */
+ .social a[href*='delicious.com'] {background-image: url('../images/icons/delicious.png');}
+ .social a[href*='digg.com'] {background-image: url('../images/icons/digg.png');}
+ .social a[href*='facebook.com'] {background-image: url('../images/icons/facebook.png');}
+ .social a[href*='last.fm'], .social a[href*='lastfm.'] {background-image: url('../images/icons/lastfm.png');}
+ .social a[href*='/feed/'] {background-image: url('../images/icons/rss.png');}
+ .social a[href*='twitter.com'] {background-image: url('../images/icons/twitter.png');}
+
+/*
+ About
+*****************/
+#about {
+ background: #fff;
+ font-style: normal;
+ margin-bottom: 2em;
+ overflow: hidden;
+ padding: 20px;
+ text-align: left;
+ width: 760px;
+
+ border-radius: 10px;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+}
+
+#about .primary {float: left; width: 165px;}
+#about .primary strong {color: #C64350; display: block; font-size: 1.286em;}
+#about .photo {float: left; margin: 5px 20px;}
+
+#about .url:link, #about .url:visited {text-decoration: none;}
+
+#about .bio {float: right; width: 500px;}
+
+/*
+ Footer
+*****************/
+#contentinfo {padding-bottom: 2em; text-align: right;}
+
+/***** Sections *****/
+/* Blog */
+.hentry {
+ border-bottom: 1px solid #eee;
+ padding: 1.5em 0;
+}
+li:last-child .hentry, #content > .hentry {border: 0; margin: 0;}
+#content > .hentry {padding: 1em 0;}
+
+.entry-title {font-size: 1.429em; margin-bottom: 0;}
+.entry-title a:link, .entry-title a:visited {text-decoration: none;}
+
+.hentry .post-info * {font-style: normal;}
+
+ /* Content */
+ .hentry footer {margin-bottom: 2em;}
+ .hentry footer address {display: inline;}
+ #posts-list footer address {display: block;}
+
+ /* Blog Index */
+ #posts-list {list-style: none; margin: 0;}
+ #posts-list .hentry {padding-left: 200px; position: relative;}
+ #posts-list .hentry:hover {
+ background: #C64350;
+ color: #fff;
+ }
+ #posts-list .hentry:hover a:link, #posts-list .hentry:hover a:visited {
+ color: #F6CF74;
+ text-shadow: 1px 1px 1px #333;
+ }
+
+ #posts-list footer {
+ left: 10px;
+ position: absolute;
+ top: 1.5em;
+ width: 190px;
+ }
+
+ #posts-list.has-comments li.no-comments {
+ display: none;
+ }
+
+ /* About the Author */
+ #about-author {
+ background: #f9f9f9;
+ clear: both;
+ font-style: normal;
+ margin: 2em 0;
+ padding: 10px 20px 15px 20px;
+
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ }
+
+ #about-author strong {
+ color: #C64350;
+ clear: both;
+ display: block;
+ font-size: 1.429em;
+ }
+
+ #about-author .photo {border: 1px solid #ddd; float: left; margin: 5px 1em 0 0;}
+
+/* Comments */
+
+#comments {
+ background: #fff;
+ margin-bottom: 2em;
+ overflow: hidden;
+ padding: 20px 20px;
+ width: 760px;
+
+ border-radius: 10px;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+}
+
+#comments label {
+ display: block;
+}
+
+#comments label:after {
+ content:" *";
+}
+
+#comments ol li:nth-child(2n+1) {
+ background-color: #F5F4EF;
+}
+
+#respond {
+ margin-top: 40px;
+}
+
+#respond input[type='text'],
+#respond input[type='email'],
+#respond textarea {
+ margin-bottom: 10px;
+ display: block;
+ width: 100%;
+
+ border: 1px solid rgba(0, 0, 0, 0.1);
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ -o-border-radius: 5px;
+ -ms-border-radius: 5px;
+ -khtml-border-radius: 5px;
+ border-radius: 5px;
+
+ line-height: 1.4em;
+}
+
+#comments-list {list-style: none; margin: 0 1em;}
+#comments-list blockquote {
+ background: #f8f8f8;
+ clear: both;
+ font-style: normal;
+ margin: 0;
+ padding: 15px 20px;
+
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+}
+#comments-list footer {color: #888; padding: .5em 1em 0 0; text-align: right;}
+
+#comments-list li:nth-child(2n) blockquote {background: #F5f5f5;}
+
+/* Add a Comment */
+#add-comment label {clear: left; float: left; text-align: left; width: 150px;}
+#add-comment input[type='text'],
+#add-comment input[type='email'],
+#add-comment input[type='url'] {float: left; width: 200px;}
+
+#add-comment textarea {float: left; height: 150px; width: 495px;}
+
+#add-comment p.req {clear: both; margin: 0 .5em 1em 0; text-align: right;}
+
+#add-comment input[type='submit'] {float: right; margin: 0 .5em;}
+#add-comment * {margin-bottom: .5em;}
View
52 css/reset.css
@@ -0,0 +1,52 @@
+/*
+ Name: Reset Stylesheet
+ Description: Resets browser's default CSS
+ Author: Eric Meyer
+ Author URI: http://meyerweb.com/eric/tools/css/reset/
+*/
+
+/* v1.0 | 20080212 */
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, font, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td {
+ background: transparent;
+ border: 0;
+ font-size: 100%;
+ margin: 0;
+ outline: 0;
+ padding: 0;
+ vertical-align: baseline;
+}
+
+body {line-height: 1;}
+
+ol, ul {list-style: none;}
+
+blockquote, q {quotes: none;}
+
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: '';
+ content: none;
+}
+
+/* remember to define focus styles! */
+:focus {
+ outline: 0;
+}
+
+/* remember to highlight inserts somehow! */
+ins {text-decoration: none;}
+del {text-decoration: line-through;}
+
+/* tables still need 'cellspacing="0"' in the markup */
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
View
BIN images/avatar.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN images/bg.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN images/icons/delicious.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN images/icons/digg.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN images/icons/facebook.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN images/icons/lastfm.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN images/icons/rss.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN images/icons/twitter.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN images/sm-logo.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
182 index.php
@@ -0,0 +1,182 @@
+<?php
+require('pusher_config.php');
+require('Persistence.php');
+$comment_post_ID = 1;
+$db = new Persistence();
+$comments = $db->get_comments($comment_post_ID);
+$has_comments = (count($comments) > 0);
+?>
+
+<!DOCTYPE html>
+<html lang="es">
+<head>
+ <title>Smashing HTML5!</title>
+ <meta charset="utf-8" />
+
+ <link rel="stylesheet" href="css/main.css" type="text/css" />
+
+ <!--[if IE]>
+ <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
+ <!--[if lte IE 7]>
+ <link rel="stylesheet" type="text/css" media="all" href="css/ie.css"/>
+ <script src="js/IE8.js" type="text/javascript"></script><![endif]-->
+ <!--[if lt IE 7]>
+ <link rel="stylesheet" type="text/css" media="all" href="css/ie6.css"/><![endif]-->
+
+</head>
+
+<body id="index" class="home">
+
+ <header id="banner" class="body">
+ <h1><a href="#">Smashing HTML5! <strong>HTML5 in the year <del>2022</del> <ins>2012</ins></strong></a></h1>
+ <nav><ul>
+ <li class="active"><a href="#">home</a></li>
+ <li><a href="#">portfolio</a></li>
+ <li><a href="#">blog</a></li>
+ <li><a href="#">contact</a></li>
+ </ul></nav>
+ </header><!-- /#banner -->
+
+ <section id="content" class="body">
+
+ <article class="hentry">
+ <header>
+ <h2 class="entry-title"><a href="#" rel="bookmark" title="Permalink to this Building a Pusher-powered Real-Time Commenting System">Building a Pusher-powered Real-Time Commenting System</a></h2>
+ </header>
+
+ <footer class="post-info">
+ <abbr class="published" title="2012-02-10T14:07:00-07:00">
+ 10th February 2012
+ </abbr>
+
+ <address class="vcard author">
+ By <a class="url fn" href="#">Phil Leggetter</a>
+ </address>
+ </footer><!-- /.post-info -->
+
+ <div class="entry-content">
+ <p>The web has become increasingly interactive over the years. This trend is set to continue with the next generation of applications driven by the <strong>real-time web</strong>. Adding real-time functionality to an application can result in a more interactive and engaging user experience. However, setting up and maintaining the server-side realtime components can be an unwanted distraction. But don't worry, there is a solution.</p>
+ </div><!-- /.entry-content -->
+ </article>
+
+ </section><!-- /#content -->
+
+ <section id="comments" class="body">
+
+ <header>
+ <h2>Comments</h2>
+ </header>
+
+ <ol id="posts-list" class="hfeed<?php echo($has_comments?' has-comments':''); ?>">
+ <li class="no-comments">Be the first to add a comment.</li>
+ <?php
+ foreach ($comments as &$comment) {
+ ?>
+ <li><article id="comment_<?php echo($comment['id']); ?>" class="hentry">
+ <footer class="post-info">
+ <abbr class="published" title="<?php echo($comment['date']); ?>">
+ <?php echo( date('d F Y', strtotime($comment['date']) ) ); ?>
+ </abbr>
+
+ <address class="vcard author">
+ By <a class="url fn" href="#"><?php echo($comment['comment_author']); ?></a>
+ </address>
+ </footer>
+
+ <div class="entry-content">
+ <p><?php echo($comment['comment']); ?></p>
+ </div>
+ </article></li>
+ <?php
+ }
+ ?>
+ </ol>
+
+ <div id="respond">
+
+ <h3>Leave a Comment</h3>
+
+ <form action="post_comment.php" method="post" id="commentform">
+
+ <label for="comment_author" class="required">Your name</label>
+ <input type="text" name="comment_author" id="comment_author" value="" tabindex="1" required="required">
+
+ <label for="email" class="required">Your email</label>
+ <input type="email" name="email" id="email" value="" tabindex="2" required="required">
+
+ <label for="comment" class="required">Your message</label>
+ <textarea name="comment" id="comment" rows="10" tabindex="4" required="required"></textarea>
+
+ <input type="hidden" name="comment_post_ID" value="<?php echo($comment_post_ID); ?>" id="comment_post_ID" />
+ <input name="submit" type="submit" value="Submit comment" />
+
+ </form>
+
+ </div>
+
+ </section>
+
+ <section id="extras" class="body">
+ <div class="blogroll">
+ <h2>blogroll</h2>
+ <ul>
+ <li><a href="#" rel="external">HTML5 Doctor</a></li>
+ <li><a href="#" rel="external">HTML5 Spec (working draft)</a></li>
+ <li><a href="#" rel="external">Smashing Magazine</a></li>
+ <li><a href="#" rel="external">W3C</a></li>
+ <li><a href="#" rel="external">Wordpress</a></li>
+ <li><a href="#" rel="external">Wikipedia</a></li>
+
+ <li><a href="#" rel="external">HTML5 Doctor</a></li>
+ <li><a href="#" rel="external">HTML5 Spec (working draft)</a></li>
+ <li><a href="#" rel="external">Smashing Magazine</a></li>
+ <li><a href="#" rel="external">W3C</a></li>
+ <li><a href="#" rel="external">Wordpress</a></li>
+ <li><a href="#" rel="external">Wikipedia</a></li>
+
+ <li><a href="#" rel="external">HTML5 Doctor</a></li>
+ <li><a href="#" rel="external">HTML5 Spec (working draft)</a></li>
+ <li><a href="#" rel="external">Smashing Magazine</a></li>
+ <li><a href="#" rel="external">W3C</a></li>
+ <li><a href="#" rel="external">Wordpress</a></li>
+ <li><a href="#" rel="external">Wikipedia</a></li><article class="hentry">
+ </ul>
+ </div><!-- /.blogroll -->
+
+ <div class="social">
+ <h2>social</h2>
+ <ul>
+ <li><a href="http://delicious.com/enrique_ramirez" rel="me">delicious</a></li>
+ <li><a href="http://digg.com/users/enriqueramirez" rel="me">digg</a></li>
+ <li><a href="http://facebook.com/enrique.ramirez.velez" rel="me">facebook</a></li>
+ <li><a href="http://www.lastfm.es/user/enrique-ramirez" rel="me">last.fm</a></li>
+ <li><a href="http://website.com/feed/" rel="alternate">rss</a></li>
+ <li><a href="http://twitter.com/enrique_ramirez" rel="me">twitter</a></li>
+ </ul>
+ </div><!-- /.social -->
+ </section><!-- /#extras -->
+
+ <footer id="contentinfo" class="body">
+ <address id="about" class="vcard body">
+ <span class="primary">
+ <strong><a href="#" class="fn url">Smashing Magazine</a></strong>
+ <span class="role">Amazing Magazine</span>
+ </span><!-- /.primary -->
+
+ <img src="images/avatar.gif" alt="Smashing Magazine Logo" class="photo" />
+
+ <span class="bio">Smashing Magazine is a website and blog that offers resources and advice to web developers and web designers. It was founded by Sven Lennartz and Vitaly Friedman.</span>
+
+ </address><!-- /#about -->
+
+ <p>2005-2012 <a href="http://smashingmagazine.com">Smashing Magazine</a>.</p>
+ </footer><!-- /#contentinfo -->
+
+<script>
+var APP_KEY = '<?php echo(APP_KEY); ?>';
+</script>
+<script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
+<script src="http://js.pusher.com/1.11/pusher.min.js"></script>
+<script src="js/app.js"></script>
+</body>
+</html>
View
117 js/app.js
@@ -0,0 +1,117 @@
+$(function() {
+ $('#commentform').submit(handleSubmit);
+});
+
+function handleSubmit() {
+ var form = $(this);
+ var data = {
+ "comment_author": form.find('#comment_author').val(),
+ "email": form.find('#email').val(),
+ "comment": form.find('#comment').val(),
+ "comment_post_ID": form.find('#comment_post_ID').val()
+ };
+
+ var socketId = getSocketId();
+ if(socketId !== null) {
+ data.socket_id = socketId;
+ }
+
+ postComment(data);
+
+ return false;
+}
+
+function postComment(data) {
+ $.ajax({
+ type: 'POST',
+ url: 'post_comment.php',
+ data: data,
+ headers: {
+ 'X-Requested-With': 'XMLHttpRequest'
+ },
+ success: postSuccess,
+ error: postError
+ });
+}
+
+function postSuccess(data, textStatus, jqXHR) {
+ $('#commentform').get(0).reset();
+ displayComment(data);
+}
+
+function postError(jqXHR, textStatus, errorThrown) {
+ // display error
+}
+
+function displayComment(data) {
+ var commentHtml = createComment(data);
+ var commentEl = $(commentHtml);
+ commentEl.hide();
+ var postsList = $('#posts-list');
+ postsList.addClass('has-comments');
+ postsList.prepend(commentEl);
+ commentEl.slideDown();
+}
+
+function createComment(data) {
+ var html = '' +
+ '<li><article id="' + data.id + '" class="hentry">' +
+ '<footer class="post-info">' +
+ '<abbr class="published" title="' + data.date + '">' +
+ parseDisplayDate(data.date) +
+ '</abbr>' +
+ '<address class="vcard author">' +
+ 'By <a class="url fn" href="#">' + data.comment_author + '</a>' +
+ '</address>' +
+ '</footer>' +
+ '<div class="entry-content">' +
+ '<p>' + data.comment + '</p>' +
+ '</div>' +
+ '</article></li>';
+
+ return html;
+}
+
+function parseDisplayDate(date) {
+ date = (date instanceof Date? date : new Date( Date.parse(date) ) );
+ var display = date.getDate() + ' ' +
+ ['January', 'February', 'March',
+ 'April', 'May', 'June', 'July',
+ 'August', 'September', 'October',
+ 'November', 'December'][date.getMonth()] + ' ' +
+ date.getFullYear();
+ return display;
+}
+
+$(function() {
+
+ $(document).keyup(function(e) {
+ e = e || window.event;
+ if(e.keyCode === 85){
+ displayComment({
+ "comment_id": 'comment_1',
+ "comment_post_ID": 1,
+ "date": "Tue, 21 Feb 2012 18:33:03 +0000",
+ "comment": "The realtime web rocks!",
+ "comment_author": "Phil Leggetter"
+ });
+ }
+ });
+
+});
+
+Pusher.log = function(msg) {
+ if(console && console.log) {
+ console.log(msg);
+ }
+};
+var pusher = new Pusher(APP_KEY);
+var channel = pusher.subscribe('comments-' + $('#comment_post_ID').val());
+channel.bind('new_comment', displayComment);
+
+function getSocketId() {
+ if(pusher && pusher.connection.state === 'connected') {
+ return pusher.connection.socket_id;
+ }
+ return null;
+}
View
46 post_comment.php
@@ -0,0 +1,46 @@
+<?php
+require('Persistence.php');
+require('squeeks-Pusher-PHP/lib/Pusher.php');
+require('pusher_config.php');
+
+$ajax = ($_SERVER[ 'HTTP_X_REQUESTED_WITH' ] === 'XMLHttpRequest');
+
+$db = new Persistence();
+$added = $db->add_comment($_POST);
+
+if($added) {
+ $channel_name = 'comments-' . $added['comment_post_ID'];
+ $event_name = 'new_comment';
+ $socket_id = (isset($_POST['socket_id'])?$_POST['socket_id']:null);
+
+ $pusher = new Pusher(APP_KEY, APP_SECRET, APP_ID);
+ $pusher->trigger($channel_name, $event_name, $added, $socket_id);
+}
+
+if($ajax) {
+ sendAjaxResponse($added);
+}
+else {
+ sendStandardResponse($added);
+}
+
+function sendAjaxResponse($added) {
+ header("Content-Type: application/json");
+ if($added) {
+ header( 'Status: 201' );
+ echo( json_encode($added) );
+ }
+ else {
+ header( 'Status: 400' );
+ }
+}
+
+function sendStandardResponse($added) {
+ if($added) {
+ header( 'Location: index.php' );
+ }
+ else {
+ header( 'Location: index.php?error=Your comment was not posted due to errors in your form submission' );
+ }
+}
+?>
View
5 pusher_config.example.php
@@ -0,0 +1,5 @@
+<?php
+define('APP_ID', '');
+define('APP_KEY', '');
+define('APP_SECRET', '');
+?>
View
102 squeeks-Pusher-PHP/README.md
@@ -0,0 +1,102 @@
+Pusher PHP Library
+==================
+
+This is a very simple PHP library to the Pusher API (http://pusherapp.com).
+Using it is easy as pie:
+
+ require('Pusher.php');
+
+ $pusher = new Pusher($key, $secret, $app_id);
+ $pusher->trigger('my-channel', 'my_event', 'hello world');
+
+
+If you prefer to use the Singleton pattern usage is similiar, but like this:
+
+ require('Pusher.php');
+ $pusher = PusherInstance::get_pusher();
+
+ $pusher->trigger('my-channel', 'my_event', 'hello world');
+
+Note: You need to set your API information in Pusher.php
+
+Arrays
+------
+Objects are automatically converted to JSON format:
+
+ $array['name'] = 'joe';
+ $array['message_count'] = 23;
+
+ $pusher->trigger('my_channel', 'my_event', $array);
+
+The output of this will be:
+
+ "{'name': 'joe', 'message_count': 23}"
+
+Socket id
+---------
+In order to avoid duplicates you can optionally specify the sender's socket id while triggering an event (http://pusherapp.com/docs/duplicates):
+
+ $pusher->trigger('my-channel','event','data','socket_id');
+
+Debugging
+---------
+You can either turn on debugging by setting the fifth argument to true, like so:
+
+ $pusher->trigger('my-channel', 'event', 'data', null, true)
+
+or with all requests:
+
+ $pusher = new Pusher($key, $secret, $app_id, true);
+
+On failed requests, this will return the server's response, instead of false.
+
+JSON format
+-----------
+
+If your data is already encoded in JSON format, you can avoid a second encoding step by setting the sixth argument true, like so:
+
+ $pusher->trigger('my-channel', 'event', 'data', null, false, true)
+
+Private channels
+----------------
+To authorise your users to access private channels on Pusher, you can use the socket_auth function:
+
+ $pusher->socket_auth('my-channel','socket_id');
+
+Presence channels
+-----------------
+Using presence channels is similar to private channels, but you can specify extra data to identify that particular user:
+
+ $pusher->presence_auth('my-channel','socket_id', 'user_id', 'user_info');
+
+Presence example
+----------------
+
+First set this variable in your JS app:
+
+ Pusher.auth_url = '/presence_auth.php';
+
+Next, create the following in presence_auth.php:
+
+ <?php
+ header('Content-Type: application/json');
+ if ($_SESSION['user_id']){
+ $sql = "SELECT * FROM `users` WHERE id='$_SESSION[user_id]'";
+ $result = mysql_query($sql,$mysql);
+ $user = mysql_fetch_assoc($result);
+ } else {
+ die('aaargh, no-one is logged in')
+ }
+
+ $pusher = new Pusher($key, $secret, $app_id);
+ $presence_data = array('name' => $user['name']);
+ echo $pusher->presence_auth($_POST['channel_name'], $_POST['socket_id'], $user['id'], $presence_data);
+ ?>
+
+Note: this assumes that you store your users in a table called `users` and that those users have a `name` column. It also assumes that you have a login mechanism that stores the `user_id` of the logged in user in the session.
+
+
+License
+-------
+Copyright 2010, Squeeks. Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
+
View
223 squeeks-Pusher-PHP/lib/Pusher.php
@@ -0,0 +1,223 @@
+<?php
+
+/*
+ Pusher PHP Library
+ /////////////////////////////////
+ This was a very simple PHP library to the Pusher API.
+
+ $pusher = new Pusher(APIKEY, SECRET, APP_ID, CHANNEL, [Debug: true/false, HOST, PORT]);
+ $pusher->trigger('my_event', 'test_channel', [socket_id, Debug: true/false]);
+ $pusher->socket_auth('socket_id');
+
+ Copyright 2011, Squeeks. Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
+
+ Contributors:
+ + Paul44 (http://github.com/Paul44)
+ + Ben Pickles (http://github.com/benpickles)
+ + Mastercoding (http://www.mastercoding.nl)
+ + Alias14 (mali0037@gmail.com)
+ + Max Williams (max@pusher.com)
+ + Zack Kitzmiller (delicious@zackisamazing.com)
+ + Andrew Bender (igothelp@gmail.com)
+*/
+
+class PusherInstance {
+
+ private static $instance = null;
+ private static $app_id = '';
+ private static $secret = '';
+ private static $api_key = '';
+
+ private function __construct() { }
+ private function __clone() { }
+
+ public static function get_pusher()
+ {
+ if (self::$instance !== null) return self::$instance;
+
+ self::$instance = new Pusher(
+ self::$api_key,
+ self::$secret,
+ self::$app_id
+ );
+
+ return self::$instance;
+ }
+}
+
+class Pusher
+{
+
+ private $settings = array ();
+
+ /**
+ * PHP5 Constructor.
+ *
+ * Initializes a new Pusher instance with key, secret , app ID and channel.
+ * You can optionally turn on debugging for all requests by setting debug to true.
+ *
+ * @param string $auth_key
+ * @param string $secret
+ * @param int $app_id
+ * @param bool $debug [optional]
+ * @param string $host [optional]
+ * @param int $port [optional]
+ * @param int $timeout [optional]
+ */
+ public function __construct( $auth_key, $secret, $app_id, $debug = false, $host = 'http://api.pusherapp.com', $port = '80', $timeout = 30 )
+ {
+
+ // Check compatibility, disable for speed improvement
+ $this->check_compatibility();
+
+ // Setup defaults
+ $this->settings['server'] = $host;
+ $this->settings['port'] = $port;
+ $this->settings['auth_key'] = $auth_key;
+ $this->settings['secret'] = $secret;
+ $this->settings['app_id'] = $app_id;
+ $this->settings['url'] = '/apps/' . $this->settings['app_id'];
+ $this->settings['debug'] = $debug;
+ $this->settings['timeout'] = $timeout;
+
+ }
+
+ /**
+ * Check if the current PHP setup is sufficient to run this class
+ */
+ private function check_compatibility()
+ {
+
+ // Check for dependent PHP extensions (JSON, cURL)
+ if ( ! extension_loaded( 'curl' ) || ! extension_loaded( 'json' ) )
+ {
+ die( 'There is missing dependant extensions - please ensure both cURL and JSON modules are installed' );
+ }
+
+ # Supports SHA256?
+ if ( ! in_array( 'sha256', hash_algos() ) )
+ {
+ die( 'SHA256 appears to be unsupported - make sure you have support for it, or upgrade your version of PHP.' );
+ }
+
+ }
+
+ /**
+ * Trigger an event by providing event name and payload.
+ * Optionally provide a socket ID to exclude a client (most likely the sender).
+ *
+ * @param string $event
+ * @param mixed $payload
+ * @param int $socket_id [optional]
+ * @param string $channel [optional]
+ * @param bool $debug [optional]
+ * @return bool|string
+ */
+ public function trigger( $channel, $event, $payload, $socket_id = null, $debug = false, $already_encoded = false )
+ {
+
+ # Check if we can initialize a cURL connection
+ $ch = curl_init();
+ if ( $ch === false )
+ {
+ die( 'Could not initialise cURL!' );
+ }
+
+ # Add channel to URL..
+ $s_url = $this->settings['url'] . '/channels/' . $channel . '/events';
+
+ # Build the request
+ $signature = "POST\n" . $s_url . "\n";
+ $payload_encoded = $already_encoded ? $payload : json_encode( $payload );
+ $query = "auth_key=" . $this->settings['auth_key'] . "&auth_timestamp=" . time() . "&auth_version=1.0&body_md5=" . md5( $payload_encoded ) . "&name=" . $event;
+
+ # Socket ID set?
+ if ( $socket_id !== null )
+ {
+ $query .= "&socket_id=" . $socket_id;
+ }
+
+ # Create the signed signature...
+ $auth_signature = hash_hmac( 'sha256', $signature . $query, $this->settings['secret'], false );
+ $signed_query = $query . "&auth_signature=" . $auth_signature;
+ $full_url = $this->settings['server'] . ':' . $this->settings['port'] . $s_url . '?' . $signed_query;
+
+ # Set cURL opts and execute request
+ curl_setopt( $ch, CURLOPT_URL, $full_url );
+ curl_setopt( $ch, CURLOPT_HTTPHEADER, array ( "Content-Type: application/json" ) );
+ curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
+ curl_setopt( $ch, CURLOPT_POST, 1 );
+ curl_setopt( $ch, CURLOPT_POSTFIELDS, $payload_encoded );
+ curl_setopt( $ch, CURLOPT_TIMEOUT, $this->settings['timeout'] );
+
+ $response = curl_exec( $ch );
+
+ curl_close( $ch );
+
+ if ( $response == "202 ACCEPTED\n" && $debug == false )
+ {
+ return true;
+ }
+ elseif ( $debug == true || $this->settings['debug'] == true )
+ {
+ return $response;
+ }
+ else
+ {
+ return false;
+ }
+
+ }
+
+ /**
+ * Creates a socket signature
+ *
+ * @param int $socket_id
+ * @param string $custom_data
+ * @return string
+ */
+ public function socket_auth( $channel, $socket_id, $custom_data = false )
+ {
+
+ if($custom_data == true)
+ {
+ $signature = hash_hmac( 'sha256', $socket_id . ':' . $channel . ':' . $custom_data, $this->settings['secret'], false );
+ }
+ else
+ {
+ $signature = hash_hmac( 'sha256', $socket_id . ':' . $channel, $this->settings['secret'], false );
+ }
+
+ $signature = array ( 'auth' => $this->settings['auth_key'] . ':' . $signature );
+ // add the custom data if it has been supplied
+ if($custom_data){
+ $signature['channel_data'] = $custom_data;
+ }
+ return json_encode( $signature );
+
+ }
+
+ /**
+ * Creates a presence signature (an extension of socket signing)
+ *
+ * @param int $socket_id
+ * @param string $user_id
+ * @param mixed $user_info
+ * @return string
+ */
+ public function presence_auth( $channel, $socket_id, $user_id, $user_info = false )
+ {
+
+ $user_data = array( 'user_id' => $user_id );
+ if($user_info == true)
+ {
+ $user_data['user_info'] = $user_info;
+ }
+
+ return $this->socket_auth($channel, $socket_id, json_encode($user_data) );
+ }
+
+
+}
+
+?>
View
44 squeeks-Pusher-PHP/test/push.php
@@ -0,0 +1,44 @@
+<?php
+
+ define('PUSHERAPP_AUTHKEY', getenv('PUSHERAPP_AUTHKEY'));
+ define('PUSHERAPP_SECRET' , getenv('PUSHERAPP_SECRET'));
+ define('PUSHERAPP_APPID' , getenv('PUSHERAPP_APPID'));
+
+ require_once('../lib/Pusher.php');
+
+ class PusherPushTest extends PHPUnit_Framework_TestCase
+ {
+
+ protected function setUp()
+ {
+ if(PUSHERAPP_AUTHKEY == '' || PUSHERAPP_SECRET == '' || PUSHERAPP_APPID == '' )
+ {
+ $this->markTestSkipped('Please set the
+ PUSHERAPP_AUTHKEY, PUSHERAPP_SECRET and
+ PUSHERAPP_APPID keys.');
+ }
+ else
+ {
+ $this->pusher = new Pusher(PUSHERAPP_AUTHKEY, PUSHERAPP_SECRET, PUSHERAPP_APPID, true);
+ }
+ }
+
+ public function testObjectConstruct()
+ {
+ $this->assertNotNull($this->pusher, 'Created new Pusher object');
+ }
+
+ public function testStringPush()
+ {
+ $string_trigger = $this->pusher->trigger('test_channel', 'my_event', 'Test string');
+ $this->assertTrue($string_trigger, 'Trigger with string payload');
+ }
+
+ public function testArrayPush()
+ {
+ $structure_trigger = $this->pusher->trigger('test_channel', 'my_event', array( test => 1 ));
+ $this->assertTrue($structure_trigger, 'Trigger with structured payload');
+ }
+ }
+
+?>
View
28 squeeks-Pusher-PHP/test/socket_auth.php
@@ -0,0 +1,28 @@
+<?php
+
+ require_once('../lib/Pusher.php');
+
+ class PusherPushTest extends PHPUnit_Framework_TestCase
+ {
+
+ protected function setUp()
+ {
+ $this->pusher = new Pusher('thisisaauthkey', 'thisisasecret', 1, true);
+ }
+
+ public function testObjectConstruct()
+ {
+ $this->assertNotNull($this->pusher, 'Created new Pusher object');
+ }
+
+ public function testSocketAuthKey()
+ {
+ $socket_auth = $this->pusher->socket_auth('testing_pusher-php', 'testing_socket_auth');
+ $this->assertEquals($socket_auth,
+ '{"auth":"thisisaauthkey:ee548cf60217ed18281da39a8eb23609105f1bde29372650cb67bd91c284aae1"}',
+ 'Socket auth key valid');
+ }
+
+ }
+
+?>

0 comments on commit 266104b

Please sign in to comment.