Merge branch 'master' of git@github.com:johnbintz/hubblesite-daily-image-wordpress

This commit is contained in:
John Bintz 2009-07-15 08:06:43 -04:00
commit 5f4aec6b7a
3 changed files with 164 additions and 106 deletions

View File

@ -1,11 +1,18 @@
<?php <?php
/**
* Show a HubbleSite daily image as a widget.
*/
class DailyImageWidget { class DailyImageWidget {
/**
* Initialize the widget.
* For unit testing purposes, you can disable remote data loading by passing true to this function.
* @param boolean $skip_load_data True if data from the remote server should not be loaded.
*/
function DailyImageWidget($skip_load_data = false) { function DailyImageWidget($skip_load_data = false) {
$this->default_display_options = array( $this->default_display_options = array(
'title', 'title',
'image', 'image'
'styles'
); );
$this->_cache_time = 86400; $this->_cache_time = 86400;
@ -14,13 +21,11 @@ class DailyImageWidget {
$this->has_simplexml = class_exists('SimpleXMLElement'); $this->has_simplexml = class_exists('SimpleXMLElement');
$this->_valid_column_names = array('title', 'caption', 'date', 'image_url', 'gallery_url', 'credits'); $this->_valid_column_names = array('title', 'date', 'image_url', 'gallery_url', 'credits');
$this->_valid_options = array( $this->_valid_options = array(
"image" => __("Daily Image", "hubblesite-daily-image-widget"), "image" => __("Daily Image", "hubblesite-daily-image-widget"),
"title" => __("Image Title", "hubblesite-daily-image-widget"), "title" => __("Image Title", "hubblesite-daily-image-widget"),
"caption" => __("Image Caption", "hubblesite-daily-image-widget"), "credits" => __("Credits", "hubblesite-daily-image-widget")
"credits" => __("Credits", "hubblesite-daily-image-widget"),
"styles" => __("HubbleSite Styles", "hubblesite-daily-image-widget"),
); );
add_action('init', array($this, "_init")); add_action('init', array($this, "_init"));
@ -34,6 +39,9 @@ class DailyImageWidget {
} }
} }
/**
* WordPress init hook.
*/
function _init() { function _init() {
register_sidebar_widget(__("HubbleSite Daily Image", "hubblesite-daily-image-widget"), array($this, "render")); register_sidebar_widget(__("HubbleSite Daily Image", "hubblesite-daily-image-widget"), array($this, "render"));
register_widget_control(__("HubbleSite Daily Image", "hubblesite-daily-image-widget"), array($this, "render_ui")); register_widget_control(__("HubbleSite Daily Image", "hubblesite-daily-image-widget"), array($this, "render_ui"));
@ -42,6 +50,9 @@ class DailyImageWidget {
$this->get_display_options(); $this->get_display_options();
} }
/**
* Display a warning if the connection failed.
*/
function _connection_warning() { function _connection_warning() {
echo "<div class=\"updated fade\">"; echo "<div class=\"updated fade\">";
_e("<strong>HubbleSite Daily Image Widget</strong> was unable to retrieve new data from HubbleSite.", "hubblesite-daily-image-widget"); _e("<strong>HubbleSite Daily Image Widget</strong> was unable to retrieve new data from HubbleSite.", "hubblesite-daily-image-widget");
@ -49,6 +60,10 @@ class DailyImageWidget {
echo "</div>"; echo "</div>";
} }
/**
* Wrapper around a remote data call for unit testing purposes.
* @return string The data from the remote source.
*/
function _get_from_data_source() { function _get_from_data_source() {
if (extension_loaded('curl')) { if (extension_loaded('curl')) {
$ch = curl_init($this->data_source); $ch = curl_init($this->data_source);
@ -61,6 +76,12 @@ class DailyImageWidget {
} }
} }
/**
* Load the remote data into the object.
* This will try to pull from cache and, if necessary, retrieve and parse the XML from the
* remote server. If any of this fails, returns false.
* @return boolean True if data could be loaded, false otherwise.
*/
function _load_data() { function _load_data() {
if (($result = $this->_get_cached_data()) === false) { if (($result = $this->_get_cached_data()) === false) {
if (($xml_text = $this->_get_from_data_source()) !== false) { if (($xml_text = $this->_get_from_data_source()) !== false) {
@ -75,6 +96,9 @@ class DailyImageWidget {
} }
} }
/**
* Handle updating the widget options.
*/
function handle_post() { function handle_post() {
if (isset($_POST['hubblesite']['_wpnonce'])) { if (isset($_POST['hubblesite']['_wpnonce'])) {
if (wp_verify_nonce($_POST['hubblesite']['_wpnonce'], 'hubble')) { if (wp_verify_nonce($_POST['hubblesite']['_wpnonce'], 'hubble')) {
@ -111,47 +135,41 @@ class DailyImageWidget {
/** /**
* Render the widget. * Render the widget.
* @param array $args The theme's widget layout arguments.
*/ */
function render() { function render($args) {
if (!empty($this->data) && is_array($this->data)) { if (!empty($this->data) && is_array($this->data)) {
extract($args);
$options = $this->get_display_options(); $options = $this->get_display_options();
echo '<div id="hubblesite-daily-image">'; echo $before_widget;
echo '<p id="hubblesite-daily-image-header">HubbleSite Daily Image</p>'; echo $before_title;
echo "HubbleSite Daily Image";
echo $after_title;
if (in_array("image", $options)) { if (in_array("image", $options)) {
echo '<a href="' . $this->data['gallery_url'] . '" title="' . $this->data['title'] . '">'; echo '<a href="' . $this->data['gallery_url'] . '" title="' . $this->data['title'] . '">';
echo '<img src="' . $this->data['image_url'] . '" alt="' . $this->data['title'] . '" />'; echo '<img src="' . $this->data['image_url'] . '" alt="' . $this->data['title'] . '" width="100%" />';
echo '</a>'; echo '</a>';
} }
if (in_array("title", $options)) { if (in_array("title", $options)) {
echo '<a id="hubblesite-daily-image-title" href="' . $this->data['gallery_url'] . '">'; echo '<a id="hubblesite-daily-image-title" href="' . $this->data['gallery_url'] . '">';
echo $this->data['title']; echo $this->_fix_widows($this->data['title']);
echo '</a>'; echo '</a>';
} }
if (in_array("caption", $options)) {
echo '<div id="hubblesite-daily-image-caption">';
echo $this->data['caption'];
echo '</div>';
}
if (in_array("credits", $options)) { if (in_array("credits", $options)) {
echo '<div id="hubblesite-daily-image-credits">'; echo '<div id="hubblesite-daily-image-credits">';
echo $this->data['credits']; echo $this->_fix_widows($this->data['credits']);
echo '</div>'; echo '</div>';
} }
echo '</div>'; echo $after_widget;
if (in_array("styles", $options)) {
echo "<style type=\"text/css\">";
include(dirname(__FILE__) . '/../hubblesite-styles.css');
echo "</style>";
}
} }
} }
/**
* Render the widget admin UI.
*/
function render_ui() { function render_ui() {
echo "<input type=\"hidden\" name=\"hubblesite[_wpnonce]\" value=\"" . wp_create_nonce('hubble') . "\" />"; echo "<input type=\"hidden\" name=\"hubblesite[_wpnonce]\" value=\"" . wp_create_nonce('hubble') . "\" />";
echo "<p>"; echo "<p>";
@ -169,6 +187,9 @@ class DailyImageWidget {
/** /**
* Parse a string of XML from the HubbleSite Daily Gallery Image feed. * Parse a string of XML from the HubbleSite Daily Gallery Image feed.
* This will try to use SimpleXML if vailable. If not, will fall back on Expat.
* @param string $xml_text The text to parse.
* @return array|boolean The retrieved data, or false on failure.
*/ */
function parse_xml($xml_text) { function parse_xml($xml_text) {
if ($this->has_simplexml) { if ($this->has_simplexml) {
@ -216,10 +237,16 @@ class DailyImageWidget {
return $this->data; return $this->data;
} }
/**
* Expat start element handler.
*/
function _start_element_handler($parser, $name, $attributes) { function _start_element_handler($parser, $name, $attributes) {
$this->_character_data = ""; $this->_character_data = "";
} }
/**
* Expat end element handler.
*/
function _end_element_handler($parser, $name) { function _end_element_handler($parser, $name) {
$name = strtolower($name); $name = strtolower($name);
if (in_array($name, $this->_valid_column_names)) { if (in_array($name, $this->_valid_column_names)) {
@ -229,32 +256,44 @@ class DailyImageWidget {
$this->_character_data = ""; $this->_character_data = "";
} }
/**
* Expat character data handler.
*/
function _character_data_handler($parser, $data) { function _character_data_handler($parser, $data) {
$this->_character_data .= $data; $this->_character_data .= $data;
} }
/**
* Retrieve the cached data from WP Options.
* @return array|boolean The cached data or false upon failure.
*/
function _get_cached_data() { function _get_cached_data() {
$result = get_option('hubblesite-daily-image-cache'); if (($result = get_option('hubblesite-daily-image-cache')) !== false) {
list($timestamp, $cached_data) = $result;
if (is_string($result)) {
if (($data = @unserialize($result)) !== false) { if (($timestamp + $this->_cache_time) > time()) {
list($timestamp, $cached_data) = $data; $is_valid = true;
foreach ($this->_valid_column_names as $field) {
if (!isset($cached_data[$field])) { $is_valid = false; break; }
}
if (($timestamp + $this->_cache_time) > time()) { if ($is_valid) {
$is_valid = true; $this->data = $cached_data;
foreach ($this->_valid_column_names as $field) { return $cached_data;
if (!isset($cached_data[$field])) { $is_valid = false; break; }
}
if ($is_valid) {
$this->data = $cached_data;
return $cached_data;
}
} }
} }
} }
return false; return false;
} }
/**
* Try to ensure that no words in a paragraph or link are widowed.
* @param string $text The text to process.
* @return string The processed text.
*/
function _fix_widows($text) {
return preg_replace("#([^\ ]+)\ ([^\ \>]+)($|</p>|</a>)#", '\1&nbsp;\2\3', $text);
}
} }
function the_hubblesite_daily_image_widget() { function the_hubblesite_daily_image_widget() {
@ -262,4 +301,4 @@ function the_hubblesite_daily_image_widget() {
$diw->render(); $diw->render();
} }
?> ?>

View File

@ -1,17 +1,21 @@
div#hubblesite-daily-image { div#hubblesite-daily-image {
text-align: center; text-align: center;
border: 1px solid #67201D; border: 1px solid #6B7595;
background-color: #F0EEDD; background-color: #191D28;
padding: 10px; padding: 10px;
} }
div#hubblesite-daily-image a {
color: #D1D184
}
div#hubblesite-daily-image a:hover { div#hubblesite-daily-image a:hover {
color: #00d2d3 color: #00d2d3
} }
div#hubblesite-daily-image img { div#hubblesite-daily-image img {
margin: 5px 0; margin: 5px 0;
border: 1px solid #67201D; border: 1px solid #6B7595;
} }
div#hubblesite-daily-image a:hover img { div#hubblesite-daily-image a:hover img {
@ -22,7 +26,11 @@ p#hubblesite-daily-image-header {
text-align: center; text-align: center;
font-size: 12px; font-size: 12px;
font-weight: bold; font-weight: bold;
color: #666; color: #9FA6BA;
margin: 0; margin: 0;
padding: 0; padding: 0;
}
div#hubblesite-daily-image-credits {
color: #CCCCCC
} }

View File

@ -15,7 +15,6 @@ class DailyImageWidgetTest extends PHPUnit_Framework_TestCase {
$this->sample_data = array( $this->sample_data = array(
'title' => 'title', 'title' => 'title',
'caption' => 'caption',
'date' => '12345', 'date' => '12345',
'image_url' => 'image_url', 'image_url' => 'image_url',
'gallery_url' => 'gallery_url', 'gallery_url' => 'gallery_url',
@ -60,33 +59,21 @@ class DailyImageWidgetTest extends PHPUnit_Framework_TestCase {
array( array(
"image", "image",
array( array(
'//div[@id="hubblesite-daily-image"]' => true, '//div[@id="hubblesite-daily-image"]' => false,
'//div/a[@href="gallery_url" and @title="title"]' => true, '//a[@href="gallery_url" and @title="title"]' => true,
'//div/a/img[@src="image_url" and @alt="title"]' => true, '//a/img[@src="image_url" and @alt="title"]' => true,
) )
), ),
array( array(
"title", "title",
array( array(
'//div/a[@href="gallery_url" and @id="hubblesite-daily-image-title"]' => "title" '//a[@href="gallery_url" and @id="hubblesite-daily-image-title"]' => "title"
)
),
array(
"styles",
array(
'//style[@type="text/css"]' => true
)
),
array(
"caption",
array(
'//div/div[@id="hubblesite-daily-image-caption"]' => 'caption'
) )
), ),
array( array(
"credits", "credits",
array( array(
'//div/div[@id="hubblesite-daily-image-credits"]' => 'credits' '//div[@id="hubblesite-daily-image-credits"]' => 'credits'
) )
) )
); );
@ -99,7 +86,12 @@ class DailyImageWidgetTest extends PHPUnit_Framework_TestCase {
update_option('hubblesite-daily-image-options', $option_string); update_option('hubblesite-daily-image-options', $option_string);
ob_start(); ob_start();
$this->diw->render(); $this->diw->render(array(
'before_widget' => "",
'after_widget' => "",
'before_title' => "",
'after_title' => ""
));
$result = ob_get_clean(); $result = ob_get_clean();
$this->assertTrue(!empty($result)); $this->assertTrue(!empty($result));
@ -112,8 +104,8 @@ class DailyImageWidgetTest extends PHPUnit_Framework_TestCase {
function providerTestGetDisplayOptions() { function providerTestGetDisplayOptions() {
return array( return array(
array("", array("title", "image", "styles")), array("", array("title", "image")),
array("meow", array("title", "image", "styles")), array("meow", array("title", "image")),
array("title", array("title")), array("title", array("title")),
array("title,image", array("title", "image")), array("title,image", array("title", "image")),
array("title,meow", array("title")) array("title,meow", array("title"))
@ -262,52 +254,71 @@ class DailyImageWidgetTest extends PHPUnit_Framework_TestCase {
} }
} }
function testGetCachedData() { function providerTestGetCachedData() {
$test_time = time() + 86500; return array(
update_option('hubblesite-daily-image-cache', serialize(array($test_time, $this->sample_data))); array(time() + 86500, true),
$this->assertEquals($this->sample_data, $this->diw->_get_cached_data()); array(time() - 86500, false),
array(null, false)
$test_time = time() - 86500; );
update_option('hubblesite-daily-image-cache', serialize(array($test_time, $this->sample_data)));
$this->assertEquals(false, $this->diw->_get_cached_data());
update_option('hubblesite-daily-image-cache', null);
$this->assertEquals(false, $this->diw->_get_cached_data());
} }
function testLoadData() { /**
$diw = $this->getMock('DailyImageWidget', array('_get_from_data_source', '_get_cached_data', 'parse_xml')); * @dataProvider providerTestGetCachedData
$diw->expects($this->once())->method('_get_cached_data')->will($this->returnValue(false)); */
$diw->expects($this->once())->method('_get_from_data_source')->will($this->returnValue(false)); function testGetCachedData($test_time, $has_sample_data) {
_reset_wp(); if (!is_null($test_time)) {
update_option('hubblesite-daily-image-cache', array($test_time, $this->sample_data));
} else {
update_option('hubblesite-daily-image-cache', null);
}
$this->assertFalse($diw->_load_data()); $this->assertEquals($has_sample_data ? $this->sample_data : false, $this->diw->_get_cached_data());
$this->assertFalse(is_array(get_option('hubblesite-daily-image-cache'))); }
function providerTestLoadData() {
return array(
array(true, null, null, true),
array(false, false, null, false),
array(false, true, false, false),
array(false, true, true, true)
);
}
/**
* @dataProvider providerTestLoadData
*/
function testLoadData($get_cached_data, $get_from_data_source, $parse_xml_result, $expected_return) {
$diw = $this->getMock('DailyImageWidget', array('_get_from_data_source', '_get_cached_data', 'parse_xml')); $diw = $this->getMock('DailyImageWidget', array('_get_from_data_source', '_get_cached_data', 'parse_xml'));
$diw->expects($this->once())->method('_get_cached_data')->will($this->returnValue(true));
_reset_wp();
$this->assertTrue($diw->_load_data()); $diw->expects($this->once())->method('_get_cached_data')->will($this->returnValue($get_cached_data));
$this->assertFalse(is_array(get_option('hubblesite-daily-image-cache'))); if ($get_cached_data == false) {
$diw->expects($this->once())->method('_get_from_data_source')->will($this->returnValue($get_from_data_source));
$diw = $this->getMock('DailyImageWidget', array('_get_from_data_source', '_get_cached_data', 'parse_xml')); if ($get_from_data_source) {
$diw->expects($this->once())->method('_get_cached_data')->will($this->returnValue(false)); $diw->expects($this->once())->method('parse_xml')->will($this->returnValue($parse_xml_result));
$diw->expects($this->once())->method('_get_from_data_source')->will($this->returnValue(true)); }
$diw->expects($this->once())->method('parse_xml')->will($this->returnValue(false)); }
_reset_wp();
$this->assertFalse($diw->_load_data()); $this->assertEquals($expected_return, $diw->_load_data());
$this->assertFalse(is_array(get_option('hubblesite-daily-image-cache')));
$diw = $this->getMock('DailyImageWidget', array('_get_from_data_source', '_get_cached_data', 'parse_xml'));
$diw->expects($this->once())->method('_get_cached_data')->will($this->returnValue(false));
$diw->expects($this->once())->method('_get_from_data_source')->will($this->returnValue(true));
$diw->expects($this->once())->method('parse_xml')->will($this->returnValue(true));
_reset_wp();
$this->assertTrue($diw->_load_data()); $this->assertEquals($parse_xml_result, is_array(get_option('hubblesite-daily-image-cache')));
$this->assertTrue(is_array(get_option('hubblesite-daily-image-cache'))); }
function providerTestWidowProtection() {
return array(
array("this is fixed", "this is&nbsp;fixed"),
array("<p>this is fixed</p>" ,"<p>this is&nbsp;fixed</p>"),
array("<a>this is fixed</a>", "<a>this is&nbsp;fixed</a>"),
array("<a href='meow'>word</a>", "<a href='meow'>word</a>"),
array("<p>this is fixed</p><p>Also fixed</p>", '<p>this is&nbsp;fixed</p><p>Also&nbsp;fixed</p>')
);
}
/**
* @dataProvider providerTestWidowProtection
*/
function testWidowProtection($source, $result) {
$this->assertEquals($result, $this->diw->_fix_widows($source));
} }
} }