summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRomain d'Alverny <rda@hupstream.com>2012-06-23 22:31:35 +0200
committerRomain d'Alverny <rda@hupstream.com>2012-06-23 22:31:35 +0200
commit1af18af0150f51215f20cb3cd4c51c36e86145cd (patch)
tree25fdbb8fa72576381a216d1e25daf40ce2b07086
parentd928ffae2e6d463336758d8a183b2ead87e0d433 (diff)
downloadplanet-1af18af0150f51215f20cb3cd4c51c36e86145cd.tar
planet-1af18af0150f51215f20cb3cd4c51c36e86145cd.tar.gz
planet-1af18af0150f51215f20cb3cd4c51c36e86145cd.tar.bz2
planet-1af18af0150f51215f20cb3cd4c51c36e86145cd.tar.xz
planet-1af18af0150f51215f20cb3cd4c51c36e86145cd.zip
Option to show only posts that have a specific tag or category.
- add configuration description in README.markdown - add unit test with testmore.php for this very feature
-rw-r--r--README.markdown22
-rw-r--r--app/classes/Planet.class.php41
-rw-r--r--app/classes/PlanetConfig.php17
-rw-r--r--app/lib/testmore.php343
-rw-r--r--t/test_categories.t55
5 files changed, 478 insertions, 0 deletions
diff --git a/README.markdown b/README.markdown
index 1427f69..84d5c74 100644
--- a/README.markdown
+++ b/README.markdown
@@ -17,3 +17,25 @@ Web hosting with PHP5 (PHP4 will not work).
License
-------
Moonmoon is free software and is released under BSD license.
+
+
+Configuration options
+---------------------
+After installation, configuration is kept in a YAML formatted ```custom/config.yml```:
+
+```%yaml
+url: http://planet.example.net # your planet base URL
+name: My Planet # your planet front page name
+locale: en # front page locale
+items: 10 # how many items to show
+refresh: 240 # feeds cache timeout (in seconds)
+cache: 10 # front page cache timeout (in seconds)
+cachedir: ./cache # where is cache stored
+postmaxlength: 0 # deprecated
+shuffle: 0 # deprecated
+nohtml: 0 # deprecated
+categories: # only list posts that have one
+ # of these (tag or category)
+```
+
+--- \ No newline at end of file
diff --git a/app/classes/Planet.class.php b/app/classes/Planet.class.php
index 4dcfbba..81a71fe 100644
--- a/app/classes/Planet.class.php
+++ b/app/classes/Planet.class.php
@@ -58,6 +58,10 @@ class Planet
*/
public function getItems()
{
+ $this->items = $this->_filterItemsByCategory(
+ $this->items,
+ $this->config->getCategories());
+
return $this->items;
}
@@ -152,4 +156,41 @@ class Planet
{
usort($this->items, array('PlanetItem','compare'));
}
+
+ /**
+ * Filter out items that do not match at least one
+ * of the defined categories.
+ *
+ * If there's no category, return all items.
+ *
+ * @param array $items to filter
+ * @param string $categories to filter against; may be a single word
+ * or a comma-separated list of words.
+ *
+ * @return array resulting list of items
+ */
+ public function _filterItemsByCategory($items, $categories = null)
+ {
+ $categories = trim($categories);
+
+ if (empty($categories))
+ return $items;
+
+ $categories = array_map('trim', explode(',', strtolower($categories)));
+ $cb_category_filter =
+ function ($item) use ($categories)
+ {
+ if (!is_array($item_categories = $item->get_categories()))
+ return false;
+
+ $item_categories = array_map(
+ function ($i) { return strtolower($i->get_label()); },
+ $item_categories
+ );
+
+ return array_intersect($categories, $item_categories);
+ };
+
+ return array_values(array_filter($items, $cb_category_filter));
+ }
}
diff --git a/app/classes/PlanetConfig.php b/app/classes/PlanetConfig.php
index a31938e..709bc2e 100644
--- a/app/classes/PlanetConfig.php
+++ b/app/classes/PlanetConfig.php
@@ -81,8 +81,25 @@ class PlanetConfig
return $this->conf['postmaxlength'];
}
+ public function getCategories()
+ {
+ return $this->conf['categories'];
+ }
+
public function toYaml()
{
return Spyc::YAMLDump($this->conf,4);
}
+
+ /**
+ * Generic accessor for config.
+ */
+ public function __get($key)
+ {
+ $key = strtolower($key);
+
+ return array_key_exists($key, $this->conf) ?
+ $this->conf[$key] :
+ null;
+ }
}
diff --git a/app/lib/testmore.php b/app/lib/testmore.php
new file mode 100644
index 0000000..aa06161
--- /dev/null
+++ b/app/lib/testmore.php
@@ -0,0 +1,343 @@
+<?php
+
+/*******************************************************************\
+* PROJECT INFORMATION *
+* *
+* Project: Apache-Test *
+* URL: http://perl.apache.org/Apache-Test/ *
+* Notice: Copyright (c) 2006 The Apache Software Foundation *
+* *
+*********************************************************************
+* LICENSE INFORMATION *
+* *
+* Licensed under the Apache License, Version 2.0 (the "License"); *
+* you may not use this file except in compliance with the *
+* License. You may obtain a copy of the License at: *
+* *
+* http://www.apache.org/licenses/LICENSE-2.0 *
+* *
+* Unless required by applicable law or agreed to in writing, *
+* software distributed under the License is distributed on an "AS *
+* IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either *
+* express or implied. See the License for the specific language *
+* governing permissions and limitations under the License. *
+* *
+*********************************************************************
+* MODULE INFORMATION *
+* *
+* This is a PHP implementation of Test::More: *
+* *
+* http://search.cpan.org/dist/Test-Simple/lib/Test/More.pm *
+* *
+*********************************************************************
+* CREDITS *
+* *
+* Originally inspired by work from Andy Lester. Written and *
+* maintained by Chris Shiflett. For contact information, see: *
+* *
+* http://shiflett.org/contact *
+* *
+\*******************************************************************/
+
+header('Content-Type: text/plain');
+register_shutdown_function('_test_end');
+
+$_no_plan = FALSE;
+$_num_failures = 0;
+$_num_skips = 0;
+$_test_num = 0;
+
+function plan($plan)
+{
+ /*
+ plan('no_plan');
+ plan('skip_all');
+ plan(array('skip_all' => 'My reason is...'));
+ plan(23);
+ */
+
+ global $_no_plan;
+ global $_skip_all;
+
+ switch ($plan) {
+ case 'no_plan':
+ $_no_plan = TRUE;
+ break;
+
+ case 'skip_all':
+ echo "1..0\n";
+ break;
+
+ default:
+ if (is_array($plan)) {
+ echo "1..0 # Skip {$plan['skip_all']}\n";
+ exit;
+ }
+
+ echo "1..$plan\n";
+ break;
+ }
+}
+
+function ok($pass, $test_name = '')
+{
+ global $_test_num;
+ global $_num_failures;
+ global $_num_skips;
+
+ $_test_num++;
+
+ if ($_num_skips) {
+ $_num_skips--;
+ return TRUE;
+ }
+
+ if (!empty($test_name) && $test_name[0] != '#') {
+ $test_name = "- $test_name";
+ }
+
+ if ($pass) {
+ echo "ok $_test_num $test_name\n";
+ } else {
+ echo "not ok $_test_num $test_name\n";
+
+ $_num_failures++;
+ $caller = debug_backtrace();
+
+ if (strstr($caller['0']['file'], $_SERVER['PHP_SELF'])) {
+ $file = $caller['0']['file'];
+ $line = $caller['0']['line'];
+ } else {
+ $file = $caller['1']['file'];
+ $line = $caller['1']['line'];
+ }
+
+ if (isset($_SERVER['SERVER_ROOT'])){
+ $file = str_replace($_SERVER['SERVER_ROOT'], 't', $file);
+ }
+
+ diag(" Failed test ($file at line $line)");
+ }
+
+ return $pass;
+}
+
+function is($this, $that, $test_name = '')
+{
+ $pass = ($this == $that);
+
+ ok($pass, $test_name);
+
+ if (!$pass) {
+ diag(" got: '$this'");
+ diag(" expected: '$that'");
+ }
+
+ return $pass;
+}
+
+function isnt($this, $that, $test_name = '')
+{
+ $pass = ($this != $that);
+
+ ok($pass, $test_name);
+
+ if (!$pass) {
+ diag(" '$this'");
+ diag(' !=');
+ diag(" '$that'");
+ }
+
+ return $pass;
+}
+
+function like($string, $pattern, $test_name = '')
+{
+ $pass = preg_match($pattern, $string);
+
+ ok($pass, $test_name);
+
+ if (!$pass) {
+ diag(" '$string'");
+ diag(" doesn't match '$pattern'");
+ }
+
+ return $pass;
+}
+
+function unlike($string, $pattern, $test_name = '')
+{
+ $pass = !preg_match($pattern, $string);
+
+ ok($pass, $test_name);
+
+ if (!$pass) {
+ diag(" '$string'");
+ diag(" matches '$pattern'");
+ }
+
+ return $pass;
+}
+
+function cmp_ok($this, $operator, $that, $test_name = '')
+{
+ eval("\$pass = (\$this $operator \$that);");
+
+ ob_start();
+ var_dump($this);
+ $_this = trim(ob_get_clean());
+
+ ob_start();
+ var_dump($that);
+ $_that = trim(ob_get_clean());
+
+ ok($pass, $test_name);
+
+ if (!$pass) {
+ diag(" got: $_this");
+ diag(" expected: $_that");
+ }
+
+ return $pass;
+}
+
+function can_ok($object, $methods)
+{
+ $pass = TRUE;
+ $errors = array();
+
+ foreach ($methods as $method) {
+ if (!method_exists($object, $method)) {
+ $pass = FALSE;
+ $errors[] = " method_exists(\$object, $method) failed";
+ }
+ }
+
+ if ($pass) {
+ ok(TRUE, "method_exists(\$object, ...)");
+ } else {
+ ok(FALSE, "method_exists(\$object, ...)");
+ diag($errors);
+ }
+
+ return $pass;
+}
+
+function isa_ok($object, $expected_class, $object_name = 'The object')
+{
+ $got_class = get_class($object);
+
+ if (version_compare(phpversion(), '5', '>=')) {
+ $pass = ($got_class == $expected_class);
+ } else {
+ $pass = ($got_class == strtolower($expected_class));
+ }
+
+ if ($pass) {
+ ok(TRUE, "$object_name isa $expected_class");
+ } else {
+ ok(FALSE, "$object_name isn't a '$expected_class' it's a '$got_class'");
+ }
+
+ return $pass;
+}
+
+function pass($test_name = '')
+{
+ return ok(TRUE, $test_name);
+}
+
+function fail($test_name = '')
+{
+ return ok(FALSE, $test_name);
+}
+
+function diag($message)
+{
+ if (is_array($message)) {
+ foreach($message as $current) {
+ echo "# $current\n";
+ }
+ } else {
+ echo "# $message\n";
+ }
+}
+
+function include_ok($module)
+{
+ $pass = ((include $module) == 1);
+ return ok($pass);
+}
+
+function require_ok($module)
+{
+ $pass = ((require $module) == 1);
+ return ok($pass);
+}
+
+function skip($message, $num)
+{
+ global $_num_skips;
+
+ if ($num < 0) {
+ $num = 0;
+ }
+
+ for ($i = 0; $i < $num; $i++) {
+ pass("# SKIP $message");
+ }
+
+ $_num_skips = $num;
+}
+
+function is_deeply($got_struct, $expected_struct, $test_name = '')
+{
+ $got_flat = var_export($got_struct, true);
+ $expected_flat = var_export($expected_struct, true);
+
+ is($got_flat, $expected_flat, $test_name);
+}
+
+
+/*
+
+TODO:
+
+function todo()
+{
+}
+
+function todo_skip()
+{
+}
+
+function eq_array()
+{
+}
+
+function eq_hash()
+{
+}
+
+function eq_set()
+{
+}
+
+*/
+
+function _test_end()
+{
+ global $_no_plan;
+ global $_num_failures;
+ global $_test_num;
+
+ if ($_no_plan) {
+ echo "1..$_test_num\n";
+ }
+
+ if ($_num_failures) {
+ diag("Looks like you failed $_num_failures tests of $_test_num.");
+ }
+}
+
+?>
diff --git a/t/test_categories.t b/t/test_categories.t
new file mode 100644
index 0000000..c41e03b
--- /dev/null
+++ b/t/test_categories.t
@@ -0,0 +1,55 @@
+<?php
+
+require realpath(__DIR__ . '/../app/lib/testmore.php');
+require realpath(__DIR__ . '/../app/classes/Planet.class.php');
+require realpath(__DIR__ . '/../app/classes/PlanetConfig.php');
+
+plan(6);
+
+class FoolCategory {
+ var $name = null;
+ function __construct($name) { $this->name = $name; }
+ function get_label() { return $this->name; }
+}
+
+class FoolItem
+{
+ function __construct($categories)
+ {
+ foreach ($categories as $c)
+ $this->categories[] = new FoolCategory($c);
+ }
+
+ function get_categories() { return $this->categories; }
+}
+
+$items = array(
+ new FoolItem(array('catA', 'catB', 'catC')),
+ new FoolItem(array('catB')),
+ new FoolItem(array('catA')),
+ new FoolItem(array('catC'))
+);
+
+$categories = 'catA';
+
+$p = new Planet();
+
+$new_items = $p->_filterItemsByCategory($items, null);
+is(count($new_items), count($items), 'Filter with null category.');
+
+$new_items = $p->_filterItemsByCategory($items, ' ');
+is(count($new_items), count($items), 'Filter with empty category.');
+
+$new_items = $p->_filterItemsByCategory($items, 'catA');
+is(count($new_items), 2, 'Filter with one category.');
+
+$new_items = $p->_filterItemsByCategory($items, 'catC');
+is(count($new_items), 2, 'Filter with one category.');
+
+$new_items = $p->_filterItemsByCategory($items, 'catB,catC');
+is(count($new_items), 3, 'Filter with two categories.');
+
+$new_items = $p->_filterItemsByCategory($items, 'catD');
+is(count($new_items), 0, 'Filter with a non-used category.');
+
+