Using same feed reader app I made for TCCVB for prWeb.
It will check for blog updates every 5 hours. (as needed)
If the feed fails it wil only update our check date and continue to
function. That way if their feed fails for any reason it won't bring
down the site.
--- /dev/null
+CREATE SCHEMA blogs;
+
+GRANT ALL ON SCHEMA blogs TO nobody;
+
+\i ./tables/blog.sql
+\i ./tables/item.sql
+
--- /dev/null
+DROP TABLE IF EXISTS blogs.blog;
+
+CREATE TABLE blogs.blog (
+ id SERIAL,
+ title TEXT,
+ url TEXT,
+ description TEXT,
+ build TIMESTAMP with time zone,
+ checkdate TIMESTAMP with time zone,
+ PRIMARY KEY (id)
+);
+
+GRANT ALL ON blogs.blog TO nobody;
+GRANT ALL ON blogs.blog_id_seq TO nobody;
--- /dev/null
+DROP TABLE IF EXISTS blogs.item;
+
+CREATE TABLE blogs.item (
+ id SERIAL,
+ title TEXT,
+ url TEXT,
+ guid TEXT,
+ description TEXT,
+ pubdate TIMESTAMP with time zone,
+ PRIMARY KEY (id)
+);
+
+GRANT ALL ON blogs.item TO nobody;
+GRANT ALL ON blogs.item_id_seq TO nobody;
--- /dev/null
+<?php
+
+/**
+ * FeedReader.php
+ *
+ * PHP version 5.3
+ *
+ * @category Toolkit
+ * @package PrWeb
+ * @author Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license Gaslight Media
+ * @version SVN: (0.1)
+ * @link <>
+ */
+
+/**
+ * Toolkit_PrWeb_FeedReader
+ *
+ * Check the RSS feed on a regular bases to see if there's updates.
+ * If an update is found it removes all items adn creates new ones from the
+ * Feed.
+ *
+ * @category Toolkit
+ * @package PrWeb
+ * @author Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license Gaslight Media
+ * @release Release: (0.1)
+ * @link <>
+ */
+class Toolkit_Blogs_FeedReader
+{
+
+ /**
+ * PDO Connection to database
+ *
+ * @var PDO
+ */
+ private $_dbh;
+
+ /**
+ *
+ * @var Toolkit_Blogs_Models_BlogMapper
+ */
+ private $_blogMapper;
+
+ /**
+ * PrWeb Record
+ *
+ * @var Toolkit_Blogs_Models_Blog
+ */
+ private $_blog;
+
+ /**
+ * Items from the PrWeb Feed Rss
+ *
+ * @var ArrayObject
+ */
+ private $_items;
+
+
+ /**
+ * time interval to check for new updates from feed
+ * time is in minutes.
+ */
+ const FEED_CHECK_INTERVAL = 5;
+ const FEED_CHECK_PART = 'hours';
+ const FEED_RSS_URL = 'http://michigantrailmaps.com/blog/?feed=rss2';
+ const HTML_TPL = 'feed.html';
+
+
+ /**
+ * Creates Object of FeedReader for PrWeb
+ *
+ * @param PDO $pdo Database Connection
+ * @param Toolkit_Blogs_Models_BlogMapper $blogMapper Mapper Object for prWeb
+ */
+ public function __construct(
+ PDO $pdo,
+ Toolkit_Blogs_Models_BlogMapper $blogMapper
+ ) {
+ $this->_dbh = $pdo;
+ $this->_blogMapper = $blogMapper;
+ $this->_blog = $this->_blogMapper->fetchBlog();
+ $this->_items = $this->_blogMapper->fetchAllItems();
+ $this->checkForUpdates();
+ }
+
+ /**
+ * Checks the last build date for prWeb record
+ *
+ * @return boolean
+ */
+ public function checkForUpdates()
+ {
+ if (!$this->_blog) {
+ $prWebRss = $this->fetchRssFeed();
+ if ($prWebRss) {
+ $this->updateFeedData($prWebRss);
+ // if the items are updated then refetch them
+ $this->_blog = $this->_blogMapper->fetchBlog();
+ $this->_items = $this->_blogMapper->fetchAllItems();
+ }
+ return true;
+ }
+ $currentTime = new DateTime();
+ $buildDate = new DateTime($this->_blog->getCheckdate());
+ $buildDate->modify('+' . self::FEED_CHECK_INTERVAL . ' ' . self::FEED_CHECK_PART);
+ if ($currentTime > $buildDate) {
+ $prWebRss = $this->fetchRssFeed();
+ if ($prWebRss) {
+ $this->updateFeedData($prWebRss);
+ // if the items are updated then refetch them
+ $this->_blog = $this->_blogMapper->fetchBlog();
+ $this->_items = $this->_blogMapper->fetchAllItems();
+ } else {
+ // update the check date
+ $this->_blog->setCheckdate(date('Y-m-d H:i:s'));
+ $this->_blogMapper->savePrWeb($this->_blog);
+ }
+ }
+ }
+
+ public function fetchRssFeed()
+ {
+ try {
+ $blogRss = Zend_Feed::import(self::FEED_RSS_URL);
+ } catch (Zend_Feed_Exception $e) {
+ Toolkit_Common::handleError($e);
+ return false;
+ }
+ return $blogRss;
+ }
+
+ public function updateFeedData($blogRss)
+ {
+ if (!$this->_blog) {
+ $this->_blog = Toolkit_Blogs_Models_Blog::createByValues(
+ array(
+ 'title' => trim($blogRss->title()),
+ 'url' => trim($blogRss->link()),
+ 'description' => trim($blogRss->description()),
+ 'build' => trim($blogRss->lastBuildDate()),
+ 'checkdate' => date('Y-m-d H:i:s')
+ )
+ );
+ } else {
+ $this->_blog->setBuild(trim($blogRss->lastBuildDate()));
+ $this->_blog->setCheckdate(date('Y-m-d H:i:s'));
+ $this->_blog->setDescription(trim($blogRss->description()));
+ $this->_blog->setTitle(trim($blogRss->title()));
+ $this->_blog->setUrl(trim($blogRss->link()));
+ }
+ $this->_blogMapper->savePrWeb($this->_blog);
+ $items = new ArrayObject();
+ // Loop over each channel item and store relevant data
+ $index = 0;
+ foreach ($blogRss as $item) {
+ $items->offsetSet($index, array(
+ 'title' => $item->title(),
+ 'link' => $item->link(),
+ 'guid' => $item->guid(),
+ 'pubdate' => $item->pubDate(),
+ 'description' => $item->description()
+ ));
+ ++$index;
+ }
+ $this->_blogMapper->saveAllItems($items);
+ }
+
+ public function toHtml(HTML_Template_Flexy $tpl)
+ {
+ $glmBaseUrl = ($_SERVER['HTTPS'] == 'on')
+ ? GLM_APP_BASE_SECURE_URL
+ : GLM_APP_BASE_URL;
+ $GLOBALS['bottomScripts'][] = $glmBaseUrl . 'libjs/external.js';
+ $tpl->compile(self::HTML_TPL);
+ $page = new stdClass();
+ $page->items = $this->_items;
+ $page->prWeb = $this->_blog;
+ return $tpl->bufferedOutputObject($page);
+ }
+
+ public function fetchMostRecent()
+ {
+ return array(
+ 'blog' => $this->_blog,
+ 'items' => $this->_items
+ );
+ }
+
+
+}
--- /dev/null
+<?php
+
+/**
+ * PrWeb.php
+ *
+ * PHP version 5.3
+ *
+ * @category Toolkit
+ * @package PrWeb
+ * @author Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license Gaslight Media
+ * @version SVN: (0.1)
+ * @link <>
+ */
+
+/**
+ * Toolkit_Blogs_Models_Blog
+ *
+ * Representation of the PrWeb table
+ *
+ * @category Toolkit
+ * @package PrWeb
+ * @author Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license Gaslight Media
+ * @release Release: (0.1)
+ * @link <>
+ */
+class Toolkit_Blogs_Models_Blog
+{
+ private $_id;
+ private $_title;
+ private $_url;
+ private $_description;
+ private $_build;
+ private $_checkdate;
+
+
+ const TABLE_NAME = 'blogs.blog';
+ const PRI_KEY = 'id';
+
+
+ private function __construct($values)
+ {
+ extract($values);
+ $this->setBuild($build)
+ ->setCheckdate($checkdate)
+ ->setDescription($description)
+ ->setTitle($title)
+ ->setUrl($url);
+ if ($id = filter_var($id, FILTER_VALIDATE_INT)) {
+ $this->setId($id);
+ }
+ }
+
+ static public function createByValues($values)
+ {
+ if (is_array($values) && !empty($values)) {
+ return new Toolkit_Blogs_Models_Blog($values);
+ } else {
+ return false;
+ }
+ }
+
+ public function getId()
+ {
+ return $this->_id;
+ }
+
+ public function setId($id)
+ {
+ $this->_id = $id;
+ return $this;
+ }
+
+ public function getTitle()
+ {
+ return $this->_title;
+ }
+
+ public function setTitle($title)
+ {
+ $this->_title = $title;
+ return $this;
+ }
+
+ public function getUrl()
+ {
+ return $this->_url;
+ }
+
+ public function setUrl($url)
+ {
+ $this->_url = $url;
+ return $this;
+ }
+
+ public function getDescription()
+ {
+ return $this->_description;
+ }
+
+ public function setDescription($description)
+ {
+ $this->_description = $description;
+ return $this;
+ }
+
+ public function getBuild()
+ {
+ return $this->_build;
+ }
+
+ public function setBuild($build)
+ {
+ $this->_build = $build;
+ return $this;
+ }
+
+ public function getCheckdate()
+ {
+ return $this->_checkdate;
+ }
+
+ public function setCheckdate($checkdate)
+ {
+ $this->_checkdate = $checkdate;
+ return $this;
+ }
+
+
+}
--- /dev/null
+<?php
+
+/**
+ * PrWebMapper.php
+ *
+ * PHP version 5.3
+ *
+ * @category Toolkit
+ * @package PrWeb
+ * @author Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license Gaslight Media
+ * @version SVN: (0.1)
+ * @link <>
+ */
+
+/**
+ * Toolkit_Blogs_Models_BlogMapper
+ *
+ * Mapper Object for dealing with all database queries for generating
+ * the object for prweb and items.
+ *
+ * @category Toolkit
+ * @package PrWeb
+ * @author Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license Gaslight Media
+ * @release Release: (0.1)
+ * @link <>
+ */
+class Toolkit_Blogs_Models_BlogMapper
+{
+ /**
+ * PDO Connection to database
+ *
+ * @var PDO
+ */
+ private $_dbh;
+
+ /**
+ * There's only one prWeb record
+ */
+ const PRWEB_ID = 1;
+
+ /**
+ * Creates an object of type PrWebMapper
+ *
+ * @param PDO $pdo Database Connection
+ */
+ public function __construct(PDO $pdo)
+ {
+ $this->_dbh = $pdo;
+ }
+
+ /**
+ * Return the prweb record or false if none exists
+ *
+ * @return boolean | Toolkit_Blogs_Models_Blog
+ */
+ public function fetchBlog()
+ {
+ try {
+ $sql = "
+ SELECT *
+ FROM " . Toolkit_Blogs_Models_Blog::TABLE_NAME ."
+ WHERE " . Toolkit_Blogs_Models_Blog::PRI_KEY . " = "
+ . self::PRWEB_ID;
+ $stmt = $this->_dbh->query($sql);
+ if ($stmt->rowCount() > 0) {
+ return Toolkit_Blogs_Models_Blog::createByValues(
+ $stmt->fetch(PDO::FETCH_ASSOC)
+ );
+ } else {
+ return false;
+ }
+
+ } catch (PDOException $e) {
+ Toolkit_Common::handleError($e);
+ }
+
+ }
+
+ /**
+ * Return the items record or false if none exists
+ *
+ * @return boolean | Toolkit_Blogs_Models_Blog
+ */
+ public function fetchAllItems()
+ {
+ $items = new ArrayObject();
+ try {
+ $sql = "
+ SELECT *
+ FROM " . Toolkit_Blogs_Models_Item::TABLE_NAME ."
+ ORDER BY " . Toolkit_Blogs_Models_Item::SORT;
+ $stmt = $this->_dbh->query($sql);
+ if ($stmt->rowCount() > 0) {
+ $index = 0;
+ while ($values = $stmt->fetch(PDO::FETCH_ASSOC)) {
+ $items->offsetSet(
+ $index,
+ Toolkit_Blogs_Models_Item::createByValues(
+ $stmt->fetch(PDO::FETCH_ASSOC)
+ )
+ );
+ ++$index;
+ }
+ return $items;
+ } else {
+ return false;
+ }
+
+ } catch (PDOException $e) {
+ Toolkit_Common::handleError($e);
+ }
+
+ }
+
+ public function savePrWeb(
+ Toolkit_Blogs_Models_Blog $prWeb
+ ) {
+ if ($prWeb->getId()) {
+ $sql = "
+ UPDATE " . Toolkit_Blogs_Models_Blog::TABLE_NAME . "
+ SET title = :title,
+ url = :url,
+ description = :description,
+ build = :build,
+ checkdate = :checkdate
+ WHERE " . Toolkit_Blogs_Models_Blog::PRI_KEY . " = :id";
+ } else {
+ $sql = "
+ INSERT INTO " . Toolkit_Blogs_Models_Blog::TABLE_NAME . "
+ (id, title, url, description, build, checkdate)
+ VALUES
+ (" . self::PRWEB_ID . ", :title, :url, :description, :build, :checkdate)
+ RETURNING " . Toolkit_Blogs_Models_Blog::PRI_KEY;
+ }
+ $stmt = $this->_dbh->prepare($sql);
+ $stmt->bindParam(':title', $prWeb->getTitle());
+ $stmt->bindParam(':url', $prWeb->getUrl());
+ $stmt->bindParam(':description', $prWeb->getDescription());
+ $stmt->bindParam(':build', $prWeb->getBuild());
+ $stmt->bindParam(':checkdate', $prWeb->getCheckdate());
+ if ($prWeb->getId()) {
+ $stmt->bindParam(':id', $prWeb->getId(), PDO::PARAM_INT);
+ }
+ $stmt->execute();
+ if (!$prWeb->getId()) {
+ $prWeb->setId($stmt->fetchColumn());
+ }
+ return $prWeb;
+ }
+
+ public function saveAllItems(ArrayObject $items)
+ {
+ $this->_dbh->beginTransaction();
+ $this->_dbh->query(
+ "DELETE FROM " . Toolkit_Blogs_Models_Item::TABLE_NAME
+ );
+ foreach ($items as $item) {
+ $this->saveItem(
+ Toolkit_Blogs_Models_Item::createByValues($item)
+ );
+ }
+ $this->_dbh->commit();
+ }
+
+ public function saveItem(
+ Toolkit_Blogs_Models_Item $item
+ ) {
+ if ($item->getId()) {
+ $sql = "
+ UPDATE " . Toolkit_Blogs_Models_Item::TABLE_NAME . "
+ SET title = :title,
+ url = :url,
+ guid = :guid,
+ description = :description,
+ pubdate = :pubdate
+ WHERE " . Toolkit_Blogs_Models_Item::PRI_KEY . " = :id";
+ } else {
+ $sql = "
+ INSERT INTO " . Toolkit_Blogs_Models_Item::TABLE_NAME . "
+ (title, url, guid, description, pubdate)
+ VALUES
+ (:title, :url, :guid, :description, :pubdate)
+ RETURNING " . Toolkit_Blogs_Models_Item::PRI_KEY;
+ }
+ $stmt = $this->_dbh->prepare($sql);
+ $stmt->bindParam(':title', $item->getTitle());
+ $stmt->bindParam(':url', $item->getUrl());
+ $stmt->bindParam(':guid', $item->getGuid());
+ $stmt->bindParam(':description', $item->getDescription());
+ $stmt->bindParam(':pubdate', $item->getPubdate());
+ if ($item->getId()) {
+ $stmt->bindParam(':id', $item->getId(), PDO::PARAM_INT);
+ }
+ $stmt->execute();
+ if (!$item->getId()) {
+ $item->setId($stmt->fetchColumn());
+ }
+ return $item;
+ }
+
+
+}
--- /dev/null
+<?php
+
+/**
+ * Item.php
+ *
+ * PHP version 5.3
+ *
+ * @category Toolkit
+ * @package PrWeb
+ * @author Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license Gaslight Media
+ * @version SVN: (0.1)
+ * @link <>
+ */
+
+/**
+ * Toolkit_Blogs_Models_Item
+ *
+ * Representation of the Item table
+ *
+ * @category Toolkit
+ * @package PrWeb
+ * @author Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license Gaslight Media
+ * @release Release: (0.1)
+ * @link <>
+ */
+class Toolkit_Blogs_Models_Item
+{
+ private $_id;
+ private $_title;
+ private $_url;
+ private $_guid;
+ private $_description;
+ private $_pubdate;
+
+ const TABLE_NAME = 'blogs.item';
+ const PRI_KEY = 'id';
+ const SORT = 'pubdate desc';
+
+ private function __construct($values)
+ {
+ extract($values);
+ $this->setPubdate($pubdate)
+ ->setDescription($description)
+ ->setTitle($title)
+ ->setUrl($url)
+ ->setGuid($guid);
+ if ($id = filter_var($id, FILTER_VALIDATE_INT)) {
+ $this->setId($id);
+ }
+ }
+
+ static public function createByValues($values)
+ {
+ if (is_array($values) && !empty($values)) {
+ return new Toolkit_Blogs_Models_Item($values);
+ } else {
+ return false;
+ }
+ }
+
+ public function getId()
+ {
+ return $this->_id;
+ }
+
+ public function setId($id)
+ {
+ $this->_id = $id;
+ return $this;
+ }
+
+ public function getTitle()
+ {
+ return $this->_title;
+ }
+
+ public function setTitle($title)
+ {
+ $this->_title = $title;
+ return $this;
+ }
+
+ public function getUrl()
+ {
+ return $this->_url;
+ }
+
+ public function setUrl($url)
+ {
+ $this->_url = $url;
+ return $this;
+ }
+
+ public function getGuid()
+ {
+ return $this->_guid;
+ }
+
+ public function setGuid($guid)
+ {
+ $this->_guid = $guid;
+ return $this;
+ }
+
+ public function getDescription()
+ {
+ return $this->_description;
+ }
+
+ public function setDescription($description)
+ {
+ $this->_description = $description;
+ return $this;
+ }
+
+ public function getPubdate()
+ {
+ return $this->_pubdate;
+ }
+
+ public function setPubdate($pubdate)
+ {
+ $this->_pubdate = $pubdate;
+ return $this;
+ }
+
+ public function getPubdateFormated($format)
+ {
+ $time = strtotime($this->getPubdate());
+ return date($format, $time);
+ }
+
+
+}
--- /dev/null
+<style>
+ #prWeb-feed-rss {
+ width: 600px;
+ }
+ #prWeb-feed-rss header {
+ position: relative;
+ margin: 5 0;
+ height: auto;
+ margin-bottom: 5px;
+ }
+ #prWeb-feed-rss h1 {
+ margin: 1em 0 0.4em;
+ color: black;
+ font-size: 2.0rem;
+ font-weight: normal;
+ }
+ #prWeb-feed-rss h2 {
+ font-size: smaller;
+ font-weight: normal;
+ }
+ #prWeb-feed-rss article {
+ width: 100%;
+ }
+ #prWeb-feed-rss article h1 {
+ font-size: medium;
+ font-weight: normal;
+ color: #00456C;
+ }
+
+</style>
+<section id="prWeb-feed-rss">
+ <header>
+ <hgroup>
+ <h1>{prWeb.getTitle()}</h1>
+ <h2>{prWeb.getDescription()}</h2>
+ </hgroup>
+ </header>
+
+ <article class="prWeb-item" flexy:foreach="items,item">
+ <header>
+ <hgroup>
+ <h1>{item.getTitle()}</h1>
+ </hgroup>
+ </header>
+ <time datetime="{item.getpubDate()}" pubdate>
+ {item.getPubdateFormated(#F j, Y#)}
+ </time>
+ <div>
+ {item.getDescription():h}
+ </div>
+ </article>
+</section>
--- /dev/null
+; production server configuration for application
+[production]
+flexyOptions.templateDir = BASE "Toolkit/Blogs/Views"
+flexyOptions.compileDir = BASE "Toolkit/Blogs/Views/compiled"
+flexyOptions.url_rewrite = "baseurl/::" BASE_URL ",basesecureurl/::" BASE_SECURE_URL
+flexyOptions.forceCompile = Off
+flexyOptions.locale = "en"
+flexyOptions.debug = Off
+flexyOptions.allowPHP = On
+flexyOptions.flexyIgnore = On
+flexyOptions.globals = On
+flexyOptions.globalfunctions = On
+flexyOptions.privates = On
+flexyOptions.compiler = "Flexy"
+
+; development server configuration inherits from production and
+; overrides values as needed
+[development : production]
+
+; Chuck's server configuration inherits from development and
+; overrides values as needed
+[chuck : development]
+
+; Steve's server configuration inherits from development and
+; overrides values as needed
+[steve : development]
+;flexyOptions.forceCompile = On
+;flexyOptions.strict = On
\ No newline at end of file
$this->_events($this->_toolboxPage, $this->_pageGateway);
}
+ if ($this->_catid == HOME_ID) {
+ $this->_blogReeder();
+ }
+
if ( defined("GLM_BLOCKS")
&& GLM_BLOCKS
&& !filter_input(INPUT_GET, 'sitemap')
$this->rotatingImages = $decorator->toHtml($is);
}
+ private function _blogReeder()
+ {
+ $dbh = Toolkit_Database::getInstance();
+ $blogMapper = new Toolkit_Blogs_Models_BlogMapper($dbh);
+ $feedReader = new Toolkit_Blogs_FeedReader($dbh, $blogMapper);
+
+ $feed = $feedReader->fetchMostRecent();
+ $this->feed = $feed['items'];
+ }
+
/**
* Add Weather module
*
<a id="enews" href="baseurl/newsletter-7/">Sign Up For Our Enewsletter</a>
<a id="tripplanner" href="baseurl/trip-planner-6/">Trip And Trail Planner (<span class="trip-list-count">{tripPlannerCount:h}</span>)</a>
</div>
-
+
<div id="toolbox">
<div id="addthis_wrapper">
<!-- AddThis Button BEGIN -->
<script type="text/javascript">var addthis_config = {"data_track_addressbar":false};</script>
<script type="text/javascript" src="//s7.addthis.com/js/300/addthis_widget.js#pubid=ra-5135fe4558d287a9"></script>
<!-- AddThis Button END -->
- </div><!--/#addthis_wrapper-->
+ </div><!--/#addthis_wrapper-->
{toolboxContent:h}
</div><!--/#toolbox-->
<div id="hHlines_wrapper">
</div><!--/#trailshop_wrapper-->
<div id="blog_wrapper">
<h2>Trail Talk Blog</h2>
- <p class="blog_date">Wed, March 6, 2013 - 5:16PM</p>
- <a class="blog_link" href="#">Sally Jewel: She's No James Watt</a>
- <p class="blog_desc">Sally Jewell, the CEO of REI, has been nominated to the the next Secretary of the Interior and that's great because she's one of us, somebody who maintains their sanity by escaping outdoors.</p>
+ <article flexy:foreach="feed,item">
+ <p class="blog_date">{item.getPubdateFormated(#D, F j, Y g:iA#)}</p>
+ <a class="blog_link" href="{item.getGuid():h}">{item.getTitle()}</a>
+ <p class="blog_desc">{item.getDescription():h}</p>
+ </article>
</div><!--/#blog_wrapper-->
</div><!--/#action2-->
</div><!--/hHlines_wrapper-->