Compare commits
39 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
766f7772a5 | ||
![]() |
9bf0a5fad8 | ||
![]() |
47d64c8cce | ||
![]() |
b134202524 | ||
![]() |
44ce9ee4b9 | ||
![]() |
19f565e7eb | ||
![]() |
92823fe3eb | ||
![]() |
4dc739713e | ||
![]() |
f2a26967ad | ||
![]() |
56e67a1745 | ||
![]() |
c967ec7188 | ||
![]() |
7ef4f2d1b8 | ||
![]() |
0c2d3b550e | ||
![]() |
d9926d1fbd | ||
![]() |
87270d8096 | ||
![]() |
88405438e4 | ||
![]() |
cac797e331 | ||
![]() |
c6e663e9fa | ||
![]() |
d538e719db | ||
![]() |
45a37680ee | ||
![]() |
66d9c966e7 | ||
![]() |
2ee9fabd7f | ||
![]() |
753b512f7e | ||
![]() |
7dc5f0d712 | ||
![]() |
d3a41d172e | ||
![]() |
d5d7ecdc0e | ||
![]() |
365b483f03 | ||
![]() |
55a3637adf | ||
![]() |
a68f6e4563 | ||
![]() |
f730d349bd | ||
![]() |
634c8b3365 | ||
![]() |
8f0fb1ef7e | ||
![]() |
ec84f3d91b | ||
![]() |
5d5f3d07eb | ||
![]() |
94c6027fa3 | ||
![]() |
14d592f2ec | ||
![]() |
dcda5cb9da | ||
![]() |
14d4a80b56 | ||
![]() |
8f89abc9ed |
20 changed files with 1491 additions and 189 deletions
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
bin
|
||||||
|
vendor
|
||||||
|
composer.lock
|
||||||
|
*.sublime-project
|
||||||
|
*.sublime-workspace
|
||||||
|
nbproject/
|
19
.travis.yml
Normal file
19
.travis.yml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
language: php
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
php:
|
||||||
|
- 5.3
|
||||||
|
- 5.4
|
||||||
|
- 5.5
|
||||||
|
- 5.6
|
||||||
|
- hhvm
|
||||||
|
|
||||||
|
env:
|
||||||
|
- WP_VERSION=3.8
|
||||||
|
- WP_VERSION=latest
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION
|
||||||
|
- fgrep wp_version /tmp/wordpress/wp-includes/version.php
|
||||||
|
|
||||||
|
script: phpunit
|
13
README.md
13
README.md
|
@ -1,15 +1,23 @@
|
||||||
# Freifunk Metadata Wordpress Plugin
|
# Freifunk Metadata Wordpress Plugin
|
||||||
|
|
||||||
|
[](https://travis-ci.org/mschuett/freifunkmeta)
|
||||||
|
|
||||||
A small Wordpress plugin to render Freifunk metadata according to the [api.freifunk.net](https://github.com/freifunk/api.freifunk.net) specification ([german description](http://freifunk.net/blog/2013/12/die-freifunk-api/)).
|
A small Wordpress plugin to render Freifunk metadata according to the [api.freifunk.net](https://github.com/freifunk/api.freifunk.net) specification ([german description](http://freifunk.net/blog/2013/12/die-freifunk-api/)).
|
||||||
|
|
||||||
It reads (and caches) JSON input from a configured URL and provides shortcodes to output the data.
|
It reads (and caches) JSON input from a configured URL and provides shortcodes to output the data.
|
||||||
|
|
||||||
Currently implemented are `[ff_services]` and `[ff_contact]`.
|
Currently implemented are `[ff_services]`, `[ff_contact]`, and `[ff_state]`.
|
||||||
|
|
||||||
|
An `[ff_location]` is also usable, but needs more work.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
Text:
|
Text:
|
||||||
|
|
||||||
|
Location:
|
||||||
|
|
||||||
|
[ff_location]
|
||||||
|
|
||||||
Services:
|
Services:
|
||||||
|
|
||||||
[ff_services]
|
[ff_services]
|
||||||
|
@ -24,4 +32,7 @@ Text:
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
16
behat.yml
Normal file
16
behat.yml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
default:
|
||||||
|
context:
|
||||||
|
parameters:
|
||||||
|
base_url: http://localhost:8080/
|
||||||
|
role_map:
|
||||||
|
ender: subscriber
|
||||||
|
starter: editor
|
||||||
|
extensions:
|
||||||
|
Behat\MinkExtension\Extension:
|
||||||
|
base_url: http://localhost:8080/
|
||||||
|
goutte: ~
|
||||||
|
selenium2:
|
||||||
|
wd_host: http://localhost:8910/wd/hub
|
||||||
|
browser_name: phantomjs
|
||||||
|
default_session: selenium2
|
||||||
|
javascript_session: selenium2
|
112
bin/install-wp-tests.sh
Normal file
112
bin/install-wp-tests.sh
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if [ $# -lt 3 ]; then
|
||||||
|
echo "usage: $0 <db-name> <db-user> <db-pass> [db-host] [wp-version]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
DB_NAME=$1
|
||||||
|
DB_USER=$2
|
||||||
|
DB_PASS=$3
|
||||||
|
DB_HOST=${4-localhost}
|
||||||
|
WP_VERSION=${5-latest}
|
||||||
|
|
||||||
|
WP_TESTS_DIR=${WP_TESTS_DIR-/tmp/wordpress-tests-lib}
|
||||||
|
WP_CORE_DIR=${WP_CORE_DIR-/tmp/wordpress/}
|
||||||
|
|
||||||
|
download() {
|
||||||
|
if [ `which curl` ]; then
|
||||||
|
curl -s "$1" > "$2";
|
||||||
|
elif [ `which wget` ]; then
|
||||||
|
wget -nv -O "$2" "$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ $WP_VERSION =~ [0-9]+\.[0-9]+(\.[0-9]+)? ]]; then
|
||||||
|
WP_TESTS_TAG="tags/$WP_VERSION"
|
||||||
|
else
|
||||||
|
# http serves a single offer, whereas https serves multiple. we only want one
|
||||||
|
download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json
|
||||||
|
grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json
|
||||||
|
LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//')
|
||||||
|
if [[ -z "$LATEST_VERSION" ]]; then
|
||||||
|
echo "Latest WordPress version could not be found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
WP_TESTS_TAG="tags/$LATEST_VERSION"
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
install_wp() {
|
||||||
|
|
||||||
|
if [ -d $WP_CORE_DIR ]; then
|
||||||
|
return;
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p $WP_CORE_DIR
|
||||||
|
|
||||||
|
if [ $WP_VERSION == 'latest' ]; then
|
||||||
|
local ARCHIVE_NAME='latest'
|
||||||
|
else
|
||||||
|
local ARCHIVE_NAME="wordpress-$WP_VERSION"
|
||||||
|
fi
|
||||||
|
|
||||||
|
download https://wordpress.org/${ARCHIVE_NAME}.tar.gz /tmp/wordpress.tar.gz
|
||||||
|
tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C $WP_CORE_DIR
|
||||||
|
|
||||||
|
download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php
|
||||||
|
}
|
||||||
|
|
||||||
|
install_test_suite() {
|
||||||
|
# portable in-place argument for both GNU sed and Mac OSX sed
|
||||||
|
if [[ $(uname -s) == 'Darwin' ]]; then
|
||||||
|
local ioption='-i .bak'
|
||||||
|
else
|
||||||
|
local ioption='-i'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# set up testing suite if it doesn't yet exist
|
||||||
|
if [ ! -d $WP_TESTS_DIR ]; then
|
||||||
|
# set up testing suite
|
||||||
|
mkdir -p $WP_TESTS_DIR
|
||||||
|
svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd $WP_TESTS_DIR
|
||||||
|
|
||||||
|
if [ ! -f wp-tests-config.php ]; then
|
||||||
|
download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php
|
||||||
|
sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR':" "$WP_TESTS_DIR"/wp-tests-config.php
|
||||||
|
sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php
|
||||||
|
sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php
|
||||||
|
sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php
|
||||||
|
sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
install_db() {
|
||||||
|
# parse DB_HOST for port or socket references
|
||||||
|
local PARTS=(${DB_HOST//\:/ })
|
||||||
|
local DB_HOSTNAME=${PARTS[0]};
|
||||||
|
local DB_SOCK_OR_PORT=${PARTS[1]};
|
||||||
|
local EXTRA=""
|
||||||
|
|
||||||
|
if ! [ -z $DB_HOSTNAME ] ; then
|
||||||
|
if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then
|
||||||
|
EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp"
|
||||||
|
elif ! [ -z $DB_SOCK_OR_PORT ] ; then
|
||||||
|
EXTRA=" --socket=$DB_SOCK_OR_PORT"
|
||||||
|
elif ! [ -z $DB_HOSTNAME ] ; then
|
||||||
|
EXTRA=" --host=$DB_HOSTNAME --protocol=tcp"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# create database
|
||||||
|
mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA
|
||||||
|
}
|
||||||
|
|
||||||
|
install_wp
|
||||||
|
install_test_suite
|
||||||
|
install_db
|
16
composer.json
Normal file
16
composer.json
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"config": {
|
||||||
|
"bin-dir": "bin",
|
||||||
|
"vendor-dir": "vendor"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "4.1.*",
|
||||||
|
"behat/behat": "2.5.3",
|
||||||
|
"behat/behat": "2.5.3",
|
||||||
|
"behat/mink": "1.5",
|
||||||
|
"behat/mink-extension": "*",
|
||||||
|
"behat/mink-goutte-driver": "*",
|
||||||
|
"behat/mink-selenium2-driver": "*"
|
||||||
|
}
|
||||||
|
}
|
67
features/bootstrap/FeatureContext.php
Normal file
67
features/bootstrap/FeatureContext.php
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Behat\Behat\Context\ClosuredContextInterface,
|
||||||
|
Behat\Behat\Context\TranslatedContextInterface,
|
||||||
|
Behat\Behat\Context\BehatContext,
|
||||||
|
Behat\Behat\Context\Step\When,
|
||||||
|
Behat\Behat\Exception\PendingException;
|
||||||
|
use Behat\Gherkin\Node\PyStringNode,
|
||||||
|
Behat\Gherkin\Node\TableNode;
|
||||||
|
|
||||||
|
require "WordPressContext.php";
|
||||||
|
use \WordPress\Mink\Context as WP_Context;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Require 3rd-party libraries here:
|
||||||
|
//
|
||||||
|
// require_once 'PHPUnit/Autoload.php';
|
||||||
|
// require_once 'PHPUnit/Framework/Assert/Functions.php';
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Features context.
|
||||||
|
*/
|
||||||
|
class FeatureContext extends WP_Context\WordPress_Context
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @Given /^I am logged in as "([^"]*)" with "([^"]*)"$/
|
||||||
|
*/
|
||||||
|
public function iAmLoggedInAsWith($username, $password) {
|
||||||
|
// Works out of the box (with a base_url of course)
|
||||||
|
// And makes sure the current user is logged out first!
|
||||||
|
$this->login($username, $password);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @When /^I write a post with title "([^"]*)" and content "([^"]*)"$/
|
||||||
|
*/
|
||||||
|
public function iWriteAPostWithTitleAndContent($post_title, $content)
|
||||||
|
{
|
||||||
|
$this->fill_in_post('post', $post_title, 'publish', $content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Given /^the plugin "([^"]*)" is "([^"]*)"$/
|
||||||
|
*/
|
||||||
|
public function thePluginIs($plugin, $state)
|
||||||
|
{
|
||||||
|
if ($state == "active") {
|
||||||
|
$action = "activate";
|
||||||
|
} else {
|
||||||
|
$action = "deactivate";
|
||||||
|
}
|
||||||
|
shell_exec(escapeshellcmd("wp plugin $action $plugin"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @When /^I search for "([^"]*)"$/
|
||||||
|
*/
|
||||||
|
public function iSearchFor($term)
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
new When("I fill in \"s\" with \"$term\""),
|
||||||
|
new When("I press \"searchsubmit\""),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
248
features/bootstrap/WordPressContext.php
Normal file
248
features/bootstrap/WordPressContext.php
Normal file
|
@ -0,0 +1,248 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
# copied and adapted from
|
||||||
|
# https://github.com/maartenJacobs/WordPress-Behat-Context/blob/master/WordPressContext.php
|
||||||
|
namespace WordPress\Mink\Context;
|
||||||
|
use Behat\MinkExtension\Context\MinkContext as BehatContext;
|
||||||
|
|
||||||
|
class WordPress_Context extends BehatContext {
|
||||||
|
|
||||||
|
protected $base_url;
|
||||||
|
protected $role_map;
|
||||||
|
protected $session;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes context.
|
||||||
|
* Every scenario gets it's own context object.
|
||||||
|
*
|
||||||
|
* @param array $parameters context parameters (set them up through behat.yml)
|
||||||
|
*/
|
||||||
|
public function __construct(array $params) {
|
||||||
|
$this->base_url = $params['base_url'];
|
||||||
|
$this->role_map = $params['role_map'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a list of usernames (user_login field), checks for every username
|
||||||
|
* if they exist. Returns a list of the users that do not exist.
|
||||||
|
*
|
||||||
|
* @param array $users
|
||||||
|
* @return array
|
||||||
|
* @author Maarten Jacobs
|
||||||
|
**/
|
||||||
|
protected function check_users_exist(array $users) {
|
||||||
|
$session = $this->getSession();
|
||||||
|
|
||||||
|
// Check if the users exist, saving the inexistent users
|
||||||
|
$inexistent_users = array();
|
||||||
|
$this->visit( 'wp-admin/users.php' );
|
||||||
|
$current_page = $session->getPage();
|
||||||
|
foreach ($users as $username) {
|
||||||
|
if (!$current_page->hasContent($username)) {
|
||||||
|
$inexistent_users[] = $username;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $inexistent_users;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a user for every username given (user_login field).
|
||||||
|
* The inner values can also maps of the following type:
|
||||||
|
* array(
|
||||||
|
* 'username' =>
|
||||||
|
* 'password' => (default: pass)
|
||||||
|
* 'email' => (default: username@test.dev)
|
||||||
|
* 'role' => (default: checks rolemap, or 'subscriber')
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* @param array $users
|
||||||
|
* @author Maarten Jacobs
|
||||||
|
**/
|
||||||
|
protected function create_users(array $users) {
|
||||||
|
$session = $this->getSession();
|
||||||
|
|
||||||
|
foreach ($users as $username) {
|
||||||
|
if (is_array($username)) {
|
||||||
|
$name = $username['username'];
|
||||||
|
$password = array_key_exists('password', $username) ? $username['password'] : 'pass';
|
||||||
|
$email = array_key_exists('email', $username) ? $username['email'] : str_replace(' ', '_', $name) . '@test.dev';
|
||||||
|
} else {
|
||||||
|
$name = $username;
|
||||||
|
$password = 'pass';
|
||||||
|
$email = str_replace(' ', '_', $name) . '@test.dev';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->visit( 'wp-admin/user-new.php' );
|
||||||
|
$current_page = $session->getPage();
|
||||||
|
|
||||||
|
// Fill in the form
|
||||||
|
$current_page->findField('user_login')->setValue($name);
|
||||||
|
$current_page->findField('email')->setValue($email);
|
||||||
|
$current_page->findField('pass1')->setValue($password);
|
||||||
|
$current_page->findField('pass2')->setValue($password);
|
||||||
|
|
||||||
|
// Set role
|
||||||
|
$role = ucfirst( strtolower( $this->role_map[$name] ) );
|
||||||
|
$current_page->findField('role')->selectOption($role);
|
||||||
|
|
||||||
|
// Submit form
|
||||||
|
$current_page->findButton('Add New User')->click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fills in the form of a generic post.
|
||||||
|
* Given the status, will either publish or save as draft.
|
||||||
|
*
|
||||||
|
* @param string $post_type
|
||||||
|
* @param string $post_title
|
||||||
|
* @param string $status Either 'draft' or anything else for 'publish'
|
||||||
|
* @author Maarten Jacobs
|
||||||
|
**/
|
||||||
|
protected function fill_in_post($post_type, $post_title, $status = 'publish', $content = '<p>Testing all the things. All the time.</p>') {
|
||||||
|
// The post type, if not post, will be appended.
|
||||||
|
// Rather than a separate page per type, this is how WP works with forms for separate post types.
|
||||||
|
$uri_suffix = $post_type !== 'post' ? '?post_type=' . $post_type : '';
|
||||||
|
$this->visit( 'wp-admin/post-new.php' . $uri_suffix );
|
||||||
|
$session = $this->session = $this->getSession();
|
||||||
|
$current_page = $session->getPage();
|
||||||
|
|
||||||
|
// Fill in the title
|
||||||
|
$current_page->findField( 'post_title' )->setValue( $post_title );
|
||||||
|
// Fill in some nonsencical data for the body
|
||||||
|
// clickLink and setValue seem to be failing for TinyMCE (same for Cucumber unfortunately)
|
||||||
|
$session->executeScript( 'jQuery( "#content-html" ).click()' );
|
||||||
|
$session->executeScript( 'jQuery( "#content" ).val( "' . $content . '" )' );
|
||||||
|
|
||||||
|
// Click the appropriate button depending on the given status
|
||||||
|
$state_button = 'Save Draft';
|
||||||
|
switch ($status) {
|
||||||
|
case 'draft':
|
||||||
|
// We're good.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'publish':
|
||||||
|
default:
|
||||||
|
// Save as draft first
|
||||||
|
$current_page->findButton($state_button)->click();
|
||||||
|
$state_button = 'Publish';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$current_page->findButton($state_button)->click();
|
||||||
|
// go to view of new post
|
||||||
|
$session->getPage()->clickLink("View $post_type");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes sure the current user is logged out, and then logs in with
|
||||||
|
* the given username and password.
|
||||||
|
*
|
||||||
|
* @param string $username
|
||||||
|
* @param string $password
|
||||||
|
* @author Maarten Jacobs
|
||||||
|
**/
|
||||||
|
protected function login($username, $password = 'pass') {
|
||||||
|
$session = $this->session = $this->getSession();
|
||||||
|
$current_page = $session->getPage();
|
||||||
|
|
||||||
|
// Check if logged in as that user
|
||||||
|
$this->visit( 'wp-admin' );
|
||||||
|
if ($current_page->hasContent( "Howdy, {$username}" )) {
|
||||||
|
// We're already logged in as this user.
|
||||||
|
// Double-check
|
||||||
|
$this->assertPageContainsText('Dashboard');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logout
|
||||||
|
$this->visit( 'wp-login.php?action=logout' );
|
||||||
|
if ($session->getPage()->hasLink('log out')) {
|
||||||
|
$session->getPage()->clickLink('log out');
|
||||||
|
$current_page = $session->getPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// And login
|
||||||
|
$current_page->fillField('user_login', $username);
|
||||||
|
$current_page->fillField('user_pass', $password);
|
||||||
|
$current_page->findButton('wp-submit')->click();
|
||||||
|
|
||||||
|
// Assert that we are on the dashboard
|
||||||
|
$this->assertPageContainsText('Dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given the current page is post list page, we enter the title in the searchbox
|
||||||
|
* and search for that post.
|
||||||
|
*
|
||||||
|
* @param string $post_title The title of the post as it would appear in the WP backend
|
||||||
|
* @param boolean $do_assert If set to anything but false, will assert for the existence of the post title after the search
|
||||||
|
* @return void
|
||||||
|
* @author Maarten Jacobs
|
||||||
|
**/
|
||||||
|
protected function searchForPost( $post_title, $do_assert = FALSE ) {
|
||||||
|
|
||||||
|
$current_page = $this->getSession()->getPage();
|
||||||
|
|
||||||
|
// Search for the post
|
||||||
|
$search_field = $current_page->findField( 'post-search-input' ); // Searching on #id
|
||||||
|
// When there is no content, then the searchbox is not shown
|
||||||
|
// So we skip search in that case
|
||||||
|
if ($search_field) {
|
||||||
|
$search_field->setValue( $post_title );
|
||||||
|
|
||||||
|
$current_page->findField( 'Search Posts' ) // Searching on value
|
||||||
|
->click();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't stop tests even if the searchbox does not exist.
|
||||||
|
// That would prevent the dev from knowing what the hell's going on.
|
||||||
|
// Can I assert all the things?
|
||||||
|
if ( $do_assert ) {
|
||||||
|
$this->assertPageContainsText($post_title);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Given /^I trash the "([^"]*)" titled "([^"]*)"$/
|
||||||
|
*/
|
||||||
|
public function iTrashThePostTitled( $post_type, $post_title ) {
|
||||||
|
|
||||||
|
$session = $this->session = $this->getSession();
|
||||||
|
|
||||||
|
// Visit the posts page
|
||||||
|
$uri_suffix = $post_type !== 'post' ? '?post_type=' . $post_type : '';
|
||||||
|
$postlist_uri = 'wp-admin/edit.php' . $uri_suffix;
|
||||||
|
$this->visit( $postlist_uri );
|
||||||
|
$current_page = $session->getPage();
|
||||||
|
|
||||||
|
// Check if the post with that title is on the current page
|
||||||
|
if (!$current_page->hasContent( $post_title )) {
|
||||||
|
// If not, search for the post
|
||||||
|
$this->searchForPost( $post_title );
|
||||||
|
}
|
||||||
|
$this->assertPageContainsText($post_title);
|
||||||
|
|
||||||
|
// Select the post in the checkbox column
|
||||||
|
// This is tricky: the checkbox has a non-unique name (of course, that's the way to do it)
|
||||||
|
// So we need to check the box in a different way
|
||||||
|
// The easiest: jQuery
|
||||||
|
$session->executeScript( "jQuery( \"tr:contains('$post_title') :checkbox\" ).click()" );
|
||||||
|
|
||||||
|
// Trash it
|
||||||
|
// - Select the 'Move to Trash' option
|
||||||
|
$current_page->selectFieldOption( 'action', 'Move to Trash' );
|
||||||
|
// - Click to Apply
|
||||||
|
$current_page->findButton( 'doaction' )->click();
|
||||||
|
|
||||||
|
// Check if the post is no longer visible on the posts page
|
||||||
|
$this->visit( $postlist_uri );
|
||||||
|
$this->assertPageNotContainsText( $post_title );
|
||||||
|
// Make a search, because it could be on another page
|
||||||
|
$this->searchForPost( $post_title );
|
||||||
|
$this->assertPageNotContainsText( $post_title );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
21
features/shortcodes.feature
Normal file
21
features/shortcodes.feature
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
Feature: Use Shortcodes
|
||||||
|
In order to use my Plugin
|
||||||
|
As a website author
|
||||||
|
I need to write posts with shortcodes
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given I am logged in as "admin" with "vagrant"
|
||||||
|
|
||||||
|
Scenario: Without the plugin
|
||||||
|
Given the plugin "freifunkmeta" is "inactive"
|
||||||
|
When I write a post with title "test" and content "[ff_contact]"
|
||||||
|
#Then print current URL
|
||||||
|
Then I should see "ff_contact"
|
||||||
|
|
||||||
|
Scenario: With the plugin
|
||||||
|
Given the plugin "freifunkmeta" is "active"
|
||||||
|
When I write a post with title "test" and content "[ff_contact]"
|
||||||
|
#Then print current URL
|
||||||
|
Then I should see "Twitter" in the ".ff_contact" element
|
||||||
|
And I should not see "ff_contact"
|
||||||
|
|
10
features/wp-search.feature
Normal file
10
features/wp-search.feature
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
Feature: Search
|
||||||
|
In order to find older articles
|
||||||
|
As a website user
|
||||||
|
I need to be able to search for a word
|
||||||
|
|
||||||
|
Scenario: Searching for a post
|
||||||
|
Given I am on the homepage
|
||||||
|
When I search for "Welcome"
|
||||||
|
Then I should see "Hello world!"
|
||||||
|
And I should see "Welcome to WordPress. This is your first post."
|
BIN
freifunk_marker.png
Normal file
BIN
freifunk_marker.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 522 B |
559
freifunkmeta.php
559
freifunkmeta.php
|
@ -1,142 +1,449 @@
|
||||||
<?php
|
<?php
|
||||||
/*
|
/**
|
||||||
Plugin Name: Freifunk Metadata Shortcodes
|
Plugin Name: Freifunk Metadata Shortcodes
|
||||||
Plugin URI: http://mschuette.name/
|
Plugin URI: http://mschuette.name/
|
||||||
Description: Defines shortcodes to display Freifunk metadata
|
Description: Defines shortcodes to display Freifunk metadata
|
||||||
Version: 0.2
|
Version: 0.4
|
||||||
Author: Martin Schuette
|
Author: Martin Schuette
|
||||||
Author URI: http://mschuette.name/
|
Author URI: http://mschuette.name/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define('FF_META_DEFAULT_CACHETIME', 15);
|
define( 'FF_META_DEFAULT_CACHETIME', 15 );
|
||||||
define('FF_META_DEFAULT_DIR', 'https://raw.githubusercontent.com/freifunk/directory.api.freifunk.net/master/directory.json');
|
define( 'FF_META_DEFAULT_DIR', 'https://raw.githubusercontent.com/freifunk/directory.api.freifunk.net/master/directory.json' );
|
||||||
define('FF_META_DEFAULT_CITY', 'hamburg');
|
define( 'FF_META_DEFAULT_CITY', 'hamburg' );
|
||||||
|
|
||||||
/* gets metadata from URL, handles caching */
|
/**
|
||||||
function ff_meta_getmetadata ($url) {
|
* class to fetch and cache data from external URLs
|
||||||
$url_hash = hash('crc32', $url);
|
* returns either an array from decoded JSON data, or WP_Error
|
||||||
|
*/
|
||||||
|
class FF_Meta_Externaldata
|
||||||
|
{
|
||||||
|
public function get( $url ) {
|
||||||
|
//error_log( "FF_Meta_Externaldata::get( $url )" );
|
||||||
|
/* gets metadata from URL, handles caching,
|
||||||
|
* hashed because cache keys should be <= 40 chars */
|
||||||
|
$cachekey = 'ff_metadata_'.hash( 'crc32', $url );
|
||||||
|
$cachetime = get_option(
|
||||||
|
'ff_meta_cachetime', FF_META_DEFAULT_CACHETIME
|
||||||
|
) * MINUTE_IN_SECONDS;
|
||||||
|
|
||||||
// Caching
|
// Caching
|
||||||
if ( false === ( $metajson = get_transient( "ff_metadata_${url_hash}" ) ) ) {
|
if ( WP_DEBUG || ( false === ( $data = get_transient( $cachekey ) ) ) ) {
|
||||||
$metajson = wp_remote_retrieve_body( wp_remote_get($url) );
|
$args = array( 'sslverify' => false );
|
||||||
$cachetime = get_option( 'ff_meta_cachetime', FF_META_DEFAULT_CACHETIME) * MINUTE_IN_SECONDS;
|
$http_response = wp_remote_get( $url, $args );
|
||||||
set_transient( "ff_metadata_${url_hash}", $metajson, $cachetime );
|
if ( is_wp_error( $http_response ) ) {
|
||||||
|
$error_msg = sprintf(
|
||||||
|
'Unable to retrieve URL %s, error: %s',
|
||||||
|
$url, $http_response->get_error_message()
|
||||||
|
);
|
||||||
|
error_log( $error_msg, 4 );
|
||||||
|
return $http_response;
|
||||||
|
} else {
|
||||||
|
$json = wp_remote_retrieve_body( $http_response );
|
||||||
|
$data = json_decode( $json, $assoc = true );
|
||||||
|
set_transient( $cachekey, $data, $cachetime );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
}
|
}
|
||||||
$metadata = json_decode ( $metajson, $assoc = true );
|
|
||||||
return $metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! shortcode_exists( 'ff_state' ) ) {
|
/**
|
||||||
add_shortcode( 'ff_state', 'ff_meta_shortcode_handler');
|
* holds the community directory
|
||||||
|
*/
|
||||||
|
class FF_Directory
|
||||||
|
{
|
||||||
|
private $directory;
|
||||||
|
private $ed;
|
||||||
|
|
||||||
|
function __construct( $ext_data_service = null ) {
|
||||||
|
if ( is_null( $ext_data_service ) ) {
|
||||||
|
$this->ed = new FF_Meta_Externaldata();
|
||||||
|
} else {
|
||||||
|
$this->ed = $ext_data_service;
|
||||||
|
}
|
||||||
|
$data = $this->ed->get( FF_META_DEFAULT_DIR );
|
||||||
|
if ( is_wp_error( $data ) ) {
|
||||||
|
$this->directory = array();
|
||||||
|
} else {
|
||||||
|
$this->directory = $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_url_by_city( $city ) {
|
||||||
|
if ( array_key_exists( $city, $this->directory ) ) {
|
||||||
|
return $this->directory[$city];
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get one big array of all known community data
|
||||||
|
function get_all_data() {
|
||||||
|
$all_locs = array();
|
||||||
|
foreach ( $this->directory as $tmp_city => $url ) {
|
||||||
|
$tmp_meta = $this->ed->get( $url );
|
||||||
|
if ( ! is_wp_error( $tmp_meta ) ) {
|
||||||
|
$all_locs[$tmp_city] = $tmp_meta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $all_locs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ( ! shortcode_exists( 'ff_services' ) ) {
|
|
||||||
add_shortcode( 'ff_services', 'ff_meta_shortcode_handler');
|
/**
|
||||||
|
* OO interface to handle a single community/city
|
||||||
|
*/
|
||||||
|
class FF_Community
|
||||||
|
{
|
||||||
|
public $name;
|
||||||
|
public $street;
|
||||||
|
public $zip;
|
||||||
|
public $city;
|
||||||
|
public $lon;
|
||||||
|
public $lat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor from metadata
|
||||||
|
*/
|
||||||
|
function __construct( $metadata ) {
|
||||||
|
$loc = $metadata['location'];
|
||||||
|
$this->name = ( isset( $loc['address'] ) && isset( $loc['address']['Name'] ) )
|
||||||
|
? $loc['address']['Name'] : '';
|
||||||
|
$this->street = ( isset( $loc['address'] ) && isset( $loc['address']['Street'] ) )
|
||||||
|
? $loc['address']['Street'] : '';
|
||||||
|
$this->zip = ( isset( $loc['address'] ) && isset( $loc['address']['Zipcode'] ) )
|
||||||
|
? $loc['address']['Zipcode'] : '';
|
||||||
|
$this->city = isset( $loc['city'] ) ? $loc['city'] : '';
|
||||||
|
$this->lon = isset( $loc['lon'] ) ? $loc['lon'] : '';
|
||||||
|
$this->lat = isset( $loc['lat'] ) ? $loc['lat'] : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alternative constructor from city name
|
||||||
|
*/
|
||||||
|
static function make_from_city( $city, $ext_data_service = null ) {
|
||||||
|
if ( is_null( $ext_data_service ) ) {
|
||||||
|
$ed = new FF_Meta_Externaldata();
|
||||||
|
} else {
|
||||||
|
$ed = $ext_data_service;
|
||||||
|
}
|
||||||
|
$directory = new FF_Directory( $ed );
|
||||||
|
|
||||||
|
if ( false === ( $url = $directory->get_url_by_city( $city ) ) ) {
|
||||||
|
return '<!-- FF Meta Error: cannot get directory.json, '.
|
||||||
|
" or no URL for '$city' -->\n";
|
||||||
|
}
|
||||||
|
if ( false === ( $metadata = $ed->get( $url ) ) ) {
|
||||||
|
return "<!-- FF Meta Error: cannot get metadata from $url -->\n";
|
||||||
|
}
|
||||||
|
return new FF_Community( $metadata );
|
||||||
|
}
|
||||||
|
|
||||||
|
function format_address() {
|
||||||
|
if ( empty( $this->name ) || empty( $this->street ) || empty( $this->zip ) ) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
// TODO: style address + map as single box
|
||||||
|
// TODO: once it is "ready" package openlayers.js into the plugin
|
||||||
|
// ( cf. http://docs.openlayers.org/library/deploying.html )
|
||||||
|
// TODO: handle missing values ( i.e. only name & city )
|
||||||
|
return sprintf(
|
||||||
|
'<p>%s<br/>%s<br/>%s %s</p>',
|
||||||
|
$this->name, $this->street, $this->zip, $this->city
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ( ! shortcode_exists( 'ff_contact' ) ) {
|
|
||||||
add_shortcode( 'ff_contact', 'ff_meta_shortcode_handler');
|
/**
|
||||||
}
|
* main class for whole plugin
|
||||||
// Example:
|
*/
|
||||||
// [ff_state]
|
class FF_Meta
|
||||||
// [ff_state hamburg]
|
{
|
||||||
function ff_meta_shortcode_handler( $atts, $content, $name ) {
|
private $dir;
|
||||||
|
private $ed;
|
||||||
|
|
||||||
|
function reinit_external_data_service( $ext_data_service = null ) {
|
||||||
|
if ( is_null( $ext_data_service ) ) {
|
||||||
|
$this->ed = new FF_Meta_Externaldata();
|
||||||
|
} else {
|
||||||
|
$this->ed = $ext_data_service;
|
||||||
|
}
|
||||||
|
$this->dir = new FF_Directory( $this->ed );
|
||||||
|
}
|
||||||
|
|
||||||
|
function __construct( $ext_data_service = null ) {
|
||||||
|
if ( is_null( $ext_data_service ) ) {
|
||||||
|
$this->ed = new FF_Meta_Externaldata();
|
||||||
|
} else {
|
||||||
|
$this->ed = $ext_data_service;
|
||||||
|
}
|
||||||
|
$this->dir = new FF_Directory( $this->ed );
|
||||||
|
}
|
||||||
|
|
||||||
|
function register_stuff() {
|
||||||
|
if ( ! shortcode_exists( 'ff_state' ) ) {
|
||||||
|
add_shortcode( 'ff_state', array( $this, 'shortcode_handler' ) );
|
||||||
|
}
|
||||||
|
if ( ! shortcode_exists( 'ff_services' ) ) {
|
||||||
|
add_shortcode( 'ff_services', array( $this, 'shortcode_handler' ) );
|
||||||
|
}
|
||||||
|
if ( ! shortcode_exists( 'ff_contact' ) ) {
|
||||||
|
add_shortcode( 'ff_contact', array( $this, 'shortcode_handler' ) );
|
||||||
|
}
|
||||||
|
if ( ! shortcode_exists( 'ff_location' ) ) {
|
||||||
|
add_shortcode( 'ff_location', array( $this, 'shortcode_handler' ) );
|
||||||
|
}
|
||||||
|
if ( ! shortcode_exists( 'ff_list' ) ) {
|
||||||
|
add_shortcode( 'ff_list', array( $this, 'shortcode_handler' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
add_action( 'admin_menu', array( $this, 'admin_menu' ) );
|
||||||
|
add_action( 'admin_init', array( $this, 'admin_init' ) );
|
||||||
|
register_uninstall_hook( __FILE__, array( 'ff_meta', 'uninstall_hook' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private function aux_get_all_locations_json() {
|
||||||
|
if ( WP_DEBUG || ( false === ( $json_locs = get_transient( 'FF_metadata_json_locs' ) ) ) ) {
|
||||||
|
$all_locs = array();
|
||||||
|
$comm_list = $this->dir->get_all_data();
|
||||||
|
foreach ( $comm_list as $entry ) {
|
||||||
|
if ( isset( $entry['location'] )
|
||||||
|
&& isset( $entry['location']['lat'] )
|
||||||
|
&& isset( $entry['location']['lon'] )
|
||||||
|
) {
|
||||||
|
$all_locs[$entry['location']['city']] = array(
|
||||||
|
'lat' => $entry['location']['lat'],
|
||||||
|
'lon' => $entry['location']['lon'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$json_locs = json_encode( $all_locs );
|
||||||
|
$cachetime = get_option( 'FF_meta_cachetime', FF_META_DEFAULT_CACHETIME ) * MINUTE_IN_SECONDS;
|
||||||
|
set_transient( 'FF_metadata_json_locs', $json_locs, $cachetime );
|
||||||
|
}
|
||||||
|
return $json_locs;
|
||||||
|
}
|
||||||
|
|
||||||
|
function output_ff_state( $citydata ) {
|
||||||
|
if ( isset( $citydata['state'] ) && isset( $citydata['state']['nodes'] ) ) {
|
||||||
|
return sprintf( '%s', $citydata['state']['nodes'] );
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function output_ff_location( $citydata ) {
|
||||||
|
// normal per-city code
|
||||||
|
$loc = new FF_Community( $citydata );
|
||||||
|
$outstr = $loc->format_address();
|
||||||
|
$json_locs = $this->aux_get_all_locations_json();
|
||||||
|
|
||||||
|
if ( ! empty( $loc->name ) && ! empty( $loc->name ) ) {
|
||||||
|
$icon_url = plugin_dir_url( __FILE__ ) . 'freifunk_marker.png';
|
||||||
|
$loccity = $loc->city;
|
||||||
|
$outstr .= <<<EOT
|
||||||
|
<div id="mapdiv_${loccity}" style="width: 75%; height: 15em;"></div>
|
||||||
|
|
||||||
|
<style type="text/css"> <!--
|
||||||
|
/* There seems to be a bug in OpenLayers' style.css ( ? ).
|
||||||
|
* Original bottom:4.5em is far too high. */
|
||||||
|
#OpenLayers_Control_Attribution_7 { bottom: 3px; }
|
||||||
|
--></style>
|
||||||
|
|
||||||
|
<script src="http://www.openlayers.org/api/OpenLayers.js"></script>
|
||||||
|
<script>
|
||||||
|
map = new OpenLayers.Map( "mapdiv_${loccity}" );
|
||||||
|
map.addLayer( new OpenLayers.Layer.OSM() );
|
||||||
|
|
||||||
|
var lonLat = new OpenLayers.LonLat( $loc->lon, $loc->lat )
|
||||||
|
.transform(
|
||||||
|
new OpenLayers.Projection( "EPSG:4326" ), // transform from WGS 1984
|
||||||
|
map.getProjectionObject() // to Spherical Mercator Projection
|
||||||
|
);
|
||||||
|
|
||||||
|
var markers = new OpenLayers.Layer.Markers( "Markers" );
|
||||||
|
map.addLayer( markers );
|
||||||
|
|
||||||
|
markers.addMarker( new OpenLayers.Marker( lonLat ) );
|
||||||
|
|
||||||
|
var size = new OpenLayers.Size( 20,16 );
|
||||||
|
var offset = new OpenLayers.Pixel( 0, -( size.h/2 ) );
|
||||||
|
var icon = new OpenLayers.Icon( '$icon_url',size,offset );
|
||||||
|
|
||||||
|
var ff_loc = $json_locs;
|
||||||
|
delete ff_loc["$loccity"];
|
||||||
|
for ( key in ff_loc ) {
|
||||||
|
markers.addMarker( new OpenLayers.Marker(
|
||||||
|
new OpenLayers.LonLat( ff_loc[key]['lon'], ff_loc[key]['lat'] )
|
||||||
|
.transform( new OpenLayers.Projection( "EPSG:4326" ),map.getProjectionObject() ),
|
||||||
|
icon.clone()
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
|
||||||
|
var zoom=12;
|
||||||
|
map.setCenter ( lonLat, zoom );
|
||||||
|
</script>
|
||||||
|
EOT;
|
||||||
|
}
|
||||||
|
return $outstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function output_ff_services( $citydata ) {
|
||||||
|
if ( ! isset( $citydata['services'] ) ) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
$services = $citydata['services'];
|
||||||
|
$outstr = '<table><th>Dienst</th><th>Beschreibung</th><th>Freifunk URI</th><th>Internet URI</th>';
|
||||||
|
foreach ( $services as $service ) {
|
||||||
|
$internalUri = isset($service['internalUri']) ? $service['internalUri'] : '';
|
||||||
|
$externalUri = isset($service['externalUri']) ? $service['externalUri'] : '';
|
||||||
|
$outstr .= sprintf(
|
||||||
|
'<tr><td>%s</td><td>%s</td><td><a href="%s">%s</a></td><td><a href="%s">%s</a></td></tr>',
|
||||||
|
$service['serviceName'], $service['serviceDescription'],
|
||||||
|
$internalUri, $internalUri,
|
||||||
|
$externalUri, $externalUri
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$outstr .= '</table>';
|
||||||
|
return $outstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function output_ff_contact( $citydata ) {
|
||||||
|
if ( ! isset( $citydata['contact'] ) ) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
$contact = $citydata['contact'];
|
||||||
|
$outstr = '<p>';
|
||||||
|
// Output -- rather ugly but the data is not uniform,
|
||||||
|
// some fields are URIs, some are usernames, ...
|
||||||
|
if ( ! empty( $contact['email'] ) ) {
|
||||||
|
$outstr .= sprintf(
|
||||||
|
'E-Mail: <a href=\"mailto:%s\">%s</a><br />',
|
||||||
|
$contact['email'], $contact['email']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ( ! empty( $contact['ml'] ) ) {
|
||||||
|
$outstr .= sprintf(
|
||||||
|
'Mailingliste: <a href=\"mailto:%s\">%s</a><br />',
|
||||||
|
$contact['ml'], $contact['ml']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ( ! empty( $contact['irc'] ) ) {
|
||||||
|
$outstr .= sprintf(
|
||||||
|
'IRC: <a href=\"%s\">%s</a><br />',
|
||||||
|
$contact['irc'], $contact['irc']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ( ! empty( $contact['twitter'] ) ) {
|
||||||
|
// catch username instead of URI
|
||||||
|
if ( $contact['twitter'][0] === '@' ) {
|
||||||
|
$twitter_url = 'http://twitter.com/' . ltrim( $contact['twitter'], '@' );
|
||||||
|
$twitter_handle = $contact['twitter'];
|
||||||
|
} else {
|
||||||
|
$twitter_url = $contact['twitter'];
|
||||||
|
$twitter_handle = '@' . substr(
|
||||||
|
$contact['twitter'], strrpos( $contact['twitter'], '/' ) + 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$outstr .= sprintf(
|
||||||
|
'Twitter: <a href=\"%s\">%s</a><br />',
|
||||||
|
$twitter_url, $twitter_handle
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ( ! empty( $contact['facebook'] ) ) {
|
||||||
|
$outstr .= sprintf(
|
||||||
|
'Facebook: <a href=\"%s\">%s</a><br />',
|
||||||
|
$contact['facebook'], $contact['facebook']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ( ! empty( $contact['googleplus'] ) ) {
|
||||||
|
$outstr .= sprintf(
|
||||||
|
'G+: <a href=\"%s\">%s</a><br />',
|
||||||
|
$contact['googleplus'], $contact['googleplus']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ( ! empty( $contact['jabber'] ) ) {
|
||||||
|
$outstr .= sprintf(
|
||||||
|
'XMPP: <a href=\"xmpp:%s\">%s</a><br />',
|
||||||
|
$contact['jabber'], $contact['jabber']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$outstr .= '</p>';
|
||||||
|
return $outstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function output_ff_list() {
|
||||||
|
$comm_list = $this->dir->get_all_data();
|
||||||
|
$outstr = '<table>';
|
||||||
|
$outstr .= '<tr><th>Name</th><th>Stadt</th><th>Knoten</th></tr>';
|
||||||
|
foreach ( $comm_list as $handle => $entry ) {
|
||||||
|
$outstr .= sprintf(
|
||||||
|
'<tr><td><a href="%s">%s</a></td><td>%s</td><td>%s</td></tr>',
|
||||||
|
esc_url( $entry['url'] ),
|
||||||
|
isset( $entry['name'] ) ? esc_html( $entry['name'] ) : esc_html($handle),
|
||||||
|
isset( $entry['location']['city'] ) ? esc_html( $entry['location']['city'] ) : 'n/a',
|
||||||
|
isset( $entry['state']['nodes'] ) ? esc_html( $entry['state']['nodes'] ) : 'n/a'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$outstr .= '</table>';
|
||||||
|
return $outstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function shortcode_handler( $atts, $content, $shortcode ) {
|
||||||
// $atts[0] holds the city name, if given
|
// $atts[0] holds the city name, if given
|
||||||
if (empty($atts[0])) {
|
if ( empty( $atts[0] ) ) {
|
||||||
$city = get_option( 'ff_meta_city', FF_META_DEFAULT_CITY );
|
$city = get_option( 'FF_meta_city', FF_META_DEFAULT_CITY );
|
||||||
} else {
|
} else {
|
||||||
$city = $atts[0];
|
$city = $atts[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (false === ($directory = ff_meta_getmetadata ( FF_META_DEFAULT_DIR ))
|
if ( false === ( $cityurl = $this->dir->get_url_by_city( $city ) ) ) {
|
||||||
|| empty($directory[$city])) {
|
|
||||||
return "<!-- FF Meta Error: cannot get directory.json, or no URL for '$city' -->\n";
|
return "<!-- FF Meta Error: cannot get directory.json, or no URL for '$city' -->\n";
|
||||||
}
|
}
|
||||||
$url = $directory[$city];
|
|
||||||
|
|
||||||
if (false === ($metadata = ff_meta_getmetadata ($url))) {
|
$ed = new FF_Meta_Externaldata();
|
||||||
return "<!-- FF Meta Error: cannot get metadata from $url -->\n";
|
if ( false === ( $metadata = $this->ed->get( $cityurl ) ) ) {
|
||||||
|
return "<!-- FF Meta Error: cannot get metadata from $cityurl -->\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
$outstr = "<div class=\"ff $name\">";
|
$outstr = "<div class=\"ff $shortcode\">";
|
||||||
switch ($name) {
|
switch ( $shortcode ) {
|
||||||
case 'ff_state':
|
case 'ff_state':
|
||||||
$state = $metadata['state'];
|
$outstr .= $this->output_ff_state( $metadata );
|
||||||
$outstr .= sprintf('%s', $state['nodes']);
|
break;
|
||||||
|
case 'ff_location':
|
||||||
|
$outstr .= $this->output_ff_location( $metadata );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'ff_services':
|
case 'ff_services':
|
||||||
$outstr .= '<ul>';
|
$outstr .= $this->output_ff_services( $metadata );
|
||||||
if (isset($metadata['services'])) {
|
|
||||||
$services = $metadata['services'];
|
|
||||||
foreach ($services as $service) {
|
|
||||||
$outstr .= sprintf('<li>%s (%s): <a href="%s">%s</a></li>',
|
|
||||||
$service['serviceName'], $service['serviceDescription'],
|
|
||||||
$service['internalUri'], $service['internalUri']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$outstr .= '</ul>';
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'ff_contact':
|
case 'ff_contact':
|
||||||
$outstr .= '<p>';
|
$outstr .= $this->output_ff_contact( $metadata );
|
||||||
$contact = $metadata['contact'];
|
break;
|
||||||
// Output -- rather ugly but the data is not uniform, some fields are URIs, some are usernames, ...
|
case 'ff_list':
|
||||||
if (!empty($contact['email'])) {
|
$outstr .= $this->output_ff_list();
|
||||||
$outstr .= sprintf("E-Mail: <a href=\"mailto:%s\">%s</a><br />\n", $contact['email'], $contact['email']);
|
|
||||||
}
|
|
||||||
if (!empty($contact['ml'])) {
|
|
||||||
$outstr .= sprintf("Mailingliste: <a href=\"mailto:%s\">%s</a><br />\n", $contact['ml'], $contact['ml']);
|
|
||||||
}
|
|
||||||
if (!empty($contact['irc'])) {
|
|
||||||
$outstr .= sprintf("IRC: <a href=\"%s\">%s</a><br />\n", $contact['irc'], $contact['irc']);
|
|
||||||
}
|
|
||||||
if (!empty($contact['twitter'])) {
|
|
||||||
// catch username instead of URI
|
|
||||||
if ($contact['twitter'][0] === "@") {
|
|
||||||
$twitter_url = 'http://twitter.com/'.ltrim($contact['twitter'], "@");
|
|
||||||
$twitter_handle = $contact['twitter'];
|
|
||||||
} else {
|
|
||||||
$twitter_url = $contact['twitter'];
|
|
||||||
$twitter_handle = '@' . substr($contact['twitter'], strrpos($contact['twitter'], '/') + 1);
|
|
||||||
}
|
|
||||||
$outstr .= sprintf("Twitter: <a href=\"%s\">%s</a><br />\n", $twitter_url, $twitter_handle);
|
|
||||||
}
|
|
||||||
if (!empty($contact['facebook'])) {
|
|
||||||
$outstr .= sprintf("Facebook: <a href=\"%s\">%s</a><br />\n", $contact['facebook'], $contact['facebook']);
|
|
||||||
}
|
|
||||||
if (!empty($contact['googleplus'])) {
|
|
||||||
$outstr .= sprintf("G+: <a href=\"%s\">%s</a><br />\n", $contact['googleplus'], $contact['googleplus']);
|
|
||||||
}
|
|
||||||
if (!empty($contact['jabber'])) {
|
|
||||||
$outstr .= sprintf("XMPP: <a href=\"xmpp:%s\">%s</a><br />\n", $contact['jabber'], $contact['jabber']);
|
|
||||||
}
|
|
||||||
$outstr .= '</p>';
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return "";
|
$outstr .= '';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
$outstr .= '</div>';
|
||||||
// Output
|
|
||||||
$outstr .= "</div>";
|
|
||||||
return $outstr;
|
return $outstr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function admin_menu() {
|
||||||
// Options Page:
|
// Options Page:
|
||||||
add_action( 'admin_menu', 'ff_meta_admin_menu' );
|
|
||||||
function ff_meta_admin_menu() {
|
|
||||||
add_options_page(
|
add_options_page(
|
||||||
'FF Meta Plugin', // page title
|
'FF Meta Plugin', // page title
|
||||||
'FF Meta', // menu title
|
'FF Meta', // menu title
|
||||||
'manage_options', // req'd capability
|
'manage_options', // req'd capability
|
||||||
'ff_meta_plugin', // menu slug
|
'ff_meta_plugin', // menu slug
|
||||||
'ff_meta_options_page' // callback function
|
array( 'FF_meta', 'options_page' ) // callback function
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
add_action( 'admin_init', 'ff_meta_admin_init' );
|
function admin_init() {
|
||||||
function ff_meta_admin_init() {
|
|
||||||
register_setting(
|
register_setting(
|
||||||
'ff_meta_settings-group', // group name
|
'ff_meta_settings-group', // group name
|
||||||
'ff_meta_cachetime' // option name
|
'ff_meta_cachetime' // option name
|
||||||
|
@ -148,55 +455,63 @@ function ff_meta_admin_init() {
|
||||||
add_settings_section(
|
add_settings_section(
|
||||||
'ff_meta_section-one', // ID
|
'ff_meta_section-one', // ID
|
||||||
'Section One', // Title
|
'Section One', // Title
|
||||||
'ff_meta_section_one_callback', // callback to fill
|
array( 'FF_Meta', 'section_one_callback' ), // callback to fill
|
||||||
'ff_meta_plugin' // page to display on
|
'ff_meta_plugin' // page to display on
|
||||||
);
|
);
|
||||||
add_settings_field(
|
add_settings_field(
|
||||||
'ff_meta_city', // ID
|
'ff_meta_city', // ID
|
||||||
'Default community', // Title
|
'Default community', // Title
|
||||||
'ff_meta_city_callback', // callback to fill field
|
array( 'FF_Meta', 'city_callback' ), // callback to fill field
|
||||||
'ff_meta_plugin', // menu page=slug to display field on
|
'ff_meta_plugin', // menu page=slug to display field on
|
||||||
'ff_meta_section-one', // section to display the field in
|
'ff_meta_section-one', // section to display the field in
|
||||||
array('label_for' => 'ff_meta_city_id') // ID of input element
|
array( 'label_for' => 'ff_meta_city_id' ) // ID of input element
|
||||||
);
|
);
|
||||||
add_settings_field(
|
add_settings_field(
|
||||||
'ff_meta_cachetime', // ID
|
'ff_meta_cachetime', // ID
|
||||||
'Cache time', // Title
|
'Cache time', // Title
|
||||||
'ff_meta_cachetime_callback', // callback to fill field
|
array( 'FF_Meta', 'cachetime_callback' ), // callback to fill field
|
||||||
'ff_meta_plugin', // menu page=slug to display field on
|
'ff_meta_plugin', // menu page=slug to display field on
|
||||||
'ff_meta_section-one', // section to display the field in
|
'ff_meta_section-one', // section to display the field in
|
||||||
array('label_for' => 'ff_meta_cachetime_id') // ID of input element
|
array( 'label_for' => 'ff_meta_cachetime_id' ) // ID of input element
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ff_meta_section_one_callback() {
|
function section_one_callback() {
|
||||||
echo 'This Plugin provides shortcodes to display information from the Freifunk meta.json.';
|
echo 'This Plugin provides shortcodes to display information'
|
||||||
}
|
.' from the Freifunk meta.json.';
|
||||||
|
}
|
||||||
|
|
||||||
function ff_meta_cachetime_callback() {
|
function cachetime_callback() {
|
||||||
$time = get_option( 'ff_meta_cachetime', FF_META_DEFAULT_CACHETIME );
|
$time = get_option( 'ff_meta_cachetime', FF_META_DEFAULT_CACHETIME );
|
||||||
echo "<input type='number' name='ff_meta_cachetime' id='ff_meta_cachetime_id' class='small-text code' value='$time' /> minutes"
|
echo '<input type="number" name="ff_meta_cachetime" '
|
||||||
."<p class='description'>Data from external URLs is cached for this number of minutes.</p>";
|
.'id="ff_meta_cachetime_id" class="small-text code" value="'
|
||||||
}
|
. esc_attr( $time ) . ' /> minutes'
|
||||||
|
.'<p class="description">Data from external URLs is cached'
|
||||||
|
.' for this number of minutes.</p>';
|
||||||
|
}
|
||||||
|
|
||||||
function ff_meta_city_callback() {
|
function city_callback() {
|
||||||
if (false === ($directory = ff_meta_getmetadata ( FF_META_DEFAULT_DIR ))) {
|
$ed = new FF_Meta_Externaldata();
|
||||||
|
if ( false === ( $directory = $this->ed->get( FF_META_DEFAULT_DIR ) ) ) {
|
||||||
// TODO: error handling
|
// TODO: error handling
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$default_city = get_option( 'ff_meta_city', FF_META_DEFAULT_CITY );
|
$default_city = get_option( 'ff_meta_city', FF_META_DEFAULT_CITY );
|
||||||
|
|
||||||
echo "<select name='ff_meta_city' id='ff_meta_city_id' size='1'>";
|
echo "<select name='ff_meta_city' id='ff_meta_city_id' size='1'>";
|
||||||
foreach (array_keys($directory) as $city) {
|
foreach ( array_keys( $directory ) as $city ) {
|
||||||
$prettycity = ucwords(str_replace(array('_', '-'), ' ', $city));
|
$prettycity = ucwords( str_replace( array( '_', '-' ), ' ', $city ) );
|
||||||
$selected = selected( $default_city, $city );
|
$selected = selected( $default_city, $city );
|
||||||
echo "<option value='$city' $selected>$prettycity</option>";
|
printf(
|
||||||
|
'<option value="%s" %s>%s</option>',
|
||||||
|
esc_attr( $city ), $selected, esc_str( $prettycity )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
echo '</select>';
|
||||||
|
echo '<p class="description">This is the default city parameter.</p>';
|
||||||
}
|
}
|
||||||
echo "</select>";
|
|
||||||
echo "<p class='description'>This is the default city parameter.</p>";
|
|
||||||
}
|
|
||||||
|
|
||||||
function ff_meta_options_page() {
|
function options_page() {
|
||||||
?>
|
?>
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<h2>Freifunk Meta Plugin Options</h2>
|
<h2>Freifunk Meta Plugin Options</h2>
|
||||||
|
@ -207,5 +522,15 @@ function ff_meta_options_page() {
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
function uninstall_hook() {
|
||||||
|
delete_option( 'ff_meta_city' );
|
||||||
|
delete_option( 'ff_meta_cachetime' );
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$ffmeta = new FF_Meta();
|
||||||
|
$ffmeta->register_stuff();
|
||||||
|
$GLOBALS['wp-plugin-ffmeta'] = $ffmeta;
|
||||||
|
|
14
phpunit.xml
Normal file
14
phpunit.xml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<phpunit
|
||||||
|
bootstrap="tests/bootstrap.php"
|
||||||
|
backupGlobals="false"
|
||||||
|
colors="true"
|
||||||
|
convertErrorsToExceptions="true"
|
||||||
|
convertNoticesToExceptions="true"
|
||||||
|
convertWarningsToExceptions="true"
|
||||||
|
>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite>
|
||||||
|
<directory prefix="test-" suffix=".php">./tests/</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
</phpunit>
|
15
tests/bootstrap.php
Normal file
15
tests/bootstrap.php
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$_tests_dir = getenv('WP_TESTS_DIR');
|
||||||
|
if ( !$_tests_dir ) $_tests_dir = '/tmp/wordpress-tests-lib';
|
||||||
|
|
||||||
|
require_once $_tests_dir . '/includes/functions.php';
|
||||||
|
|
||||||
|
function _manually_load_plugin() {
|
||||||
|
require dirname( __FILE__ ) . '/../freifunkmeta.php';
|
||||||
|
}
|
||||||
|
tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );
|
||||||
|
|
||||||
|
require $_tests_dir . '/includes/bootstrap.php';
|
||||||
|
|
||||||
|
require 'mock_ext_dataservice.php';
|
4
tests/example_directory.json
Normal file
4
tests/example_directory.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"hamburg" : "http://example.org/ffhh.json",
|
||||||
|
"ffm" : "http://example.org/ffffm.json"
|
||||||
|
}
|
41
tests/example_ffffm.json
Normal file
41
tests/example_ffffm.json
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"name": "Freifunk Frankfurt am Main - ffffm",
|
||||||
|
"url": "http://www.wifi-frankfurt.de/",
|
||||||
|
"state": {
|
||||||
|
"nodes": 4,
|
||||||
|
"lastchange": 0,
|
||||||
|
"message": "open for public"
|
||||||
|
},
|
||||||
|
"location": {
|
||||||
|
"city": "Frankfurt am Main",
|
||||||
|
"lat": 50.110556,
|
||||||
|
"lon": 8.682222
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"email": "info@wifi-frankfurt.de",
|
||||||
|
"irc": "#freifunk on freenode",
|
||||||
|
"ml": "user@wifi-frankfurt.de",
|
||||||
|
"twitter": "https://twitter.com/FreiFunkFFM"
|
||||||
|
},
|
||||||
|
"techDetails": {
|
||||||
|
"stoererhaftung": "VPN",
|
||||||
|
"firmware": {
|
||||||
|
"url": "http://wiki.freifunk.net/Firmware_Flashen"
|
||||||
|
},
|
||||||
|
"networks": {
|
||||||
|
"ipv4": [
|
||||||
|
{
|
||||||
|
"netmask": 16,
|
||||||
|
"network": "10.126.0.0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"routing": "BATMAN",
|
||||||
|
"splashpage": "none"
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"lastchange": 1391267102
|
||||||
|
},
|
||||||
|
"api": "0.1"
|
||||||
|
}
|
||||||
|
|
107
tests/example_ffhh.json
Normal file
107
tests/example_ffhh.json
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
{
|
||||||
|
"name": "Freifunk Hamburg",
|
||||||
|
"api": "0.3.0",
|
||||||
|
"url": "https://hamburg.freifunk.net",
|
||||||
|
"techDetails": {
|
||||||
|
"updatemode": "autoupdater",
|
||||||
|
"bootstrap": "http://formular.hamburg.freifunk.net",
|
||||||
|
"splashpage": "none",
|
||||||
|
"firmware": {
|
||||||
|
"url": "https://hamburg.freifunk.net/anleitung",
|
||||||
|
"name": "gluon"
|
||||||
|
},
|
||||||
|
"routing": "batman-adv",
|
||||||
|
"tld": {
|
||||||
|
"nameserver": [
|
||||||
|
"10.112.1.1",
|
||||||
|
"10.112.14.1"
|
||||||
|
],
|
||||||
|
"domainname": "ffhh"
|
||||||
|
},
|
||||||
|
"vpn": "fastd",
|
||||||
|
"stoererhaftung": "VPN in die Niederlande",
|
||||||
|
"networks": {
|
||||||
|
"ipv4": [
|
||||||
|
{
|
||||||
|
"network": "10.112.0.0/18"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ipv6": [
|
||||||
|
{
|
||||||
|
"network": "fd51:2bb2:fd0d::/48"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"nodes": 429,
|
||||||
|
"lastchange": "2014-04-27T12:50:01.842182",
|
||||||
|
"message": "open for public"
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"ml": "freifunk@hamburg.ccc.de",
|
||||||
|
"twitter": "@FreifunkHH",
|
||||||
|
"irc": "irc://irc.hackint.net/ffhh",
|
||||||
|
"facebook": "https://www.facebook.com/FreifunkHamburg",
|
||||||
|
"email": "kontakt@hamburg.freifunk.net"
|
||||||
|
},
|
||||||
|
"location": {
|
||||||
|
"lat": 53.574267,
|
||||||
|
"city": "Hamburg",
|
||||||
|
"lon": 10.024418,
|
||||||
|
"address": {
|
||||||
|
"Street": "Humboldtstr. 53",
|
||||||
|
"Name": "Chaos Computer Club Hansestadt Hamburg",
|
||||||
|
"Zipcode": "22083"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"services": [
|
||||||
|
{
|
||||||
|
"internalUri": "xmpp://jabber.ffhh",
|
||||||
|
"serviceName": "jabber",
|
||||||
|
"serviceDescription": "Chat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalUri": "nntp://news.services.ffhh",
|
||||||
|
"serviceName": "news",
|
||||||
|
"serviceDescription": "Newsserver"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalUri": "http://radio.ffhh",
|
||||||
|
"serviceName": "radio",
|
||||||
|
"serviceDescription": "CC Musik und freisprech podcast"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nodeMaps": [
|
||||||
|
{
|
||||||
|
"url": "http://knotenkarte.de",
|
||||||
|
"interval": "1 min",
|
||||||
|
"technicalType": "ffmap",
|
||||||
|
"mapType": "geographical"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://knotengraph.de",
|
||||||
|
"interval": "1 min",
|
||||||
|
"technicalType": "ffmap",
|
||||||
|
"mapType": "structural"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://graph.hamburg.freifunk.net/list.html",
|
||||||
|
"interval": "1 min",
|
||||||
|
"technicalType": "ffmap",
|
||||||
|
"mapType": "list/status"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"feeds": [
|
||||||
|
{
|
||||||
|
"category": "blog",
|
||||||
|
"url": "https://hamburg.freifunk.net/?feed=rss2",
|
||||||
|
"type": "rss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "others",
|
||||||
|
"url": "https://hamburg.freifunk.net/feed/freisprech-mp3/?feed=rss2",
|
||||||
|
"type": "rss"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
22
tests/mock_ext_dataservice.php
Normal file
22
tests/mock_ext_dataservice.php
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mock for FF_Meta_Externaldata
|
||||||
|
* in order to run all unit tests offline with predefined content
|
||||||
|
*/
|
||||||
|
class MockDataService {
|
||||||
|
function get($url) {
|
||||||
|
// translate file "http://example.org/%s.json" to "./example_%s.json"
|
||||||
|
$url_filename = basename(parse_url($url, PHP_URL_PATH));
|
||||||
|
$local_filename = __DIR__.'/example_'.$url_filename;
|
||||||
|
if (file_exists($local_filename)) {
|
||||||
|
$json = file_get_contents($local_filename);
|
||||||
|
$stubdata = json_decode($json, $assoc = true);
|
||||||
|
//error_log("MockDataService: fetch $url from $local_filename", 4);
|
||||||
|
return $stubdata;
|
||||||
|
} else {
|
||||||
|
//error_log("MockDataService: cannot fetch $url", 4);
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
178
tests/test-LowLevelTests.php
Normal file
178
tests/test-LowLevelTests.php
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
# low level test of PHP functions & methods w/o WP integration
|
||||||
|
class LowLevelTests extends PHPUnit_Framework_TestCase {
|
||||||
|
function setUp() {
|
||||||
|
$this->FFM = new FF_Meta(new MockDataService());
|
||||||
|
$this->FFM->reinit_external_data_service(new MockDataService());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* some very basic things */
|
||||||
|
function test_basic_json_parsing() {
|
||||||
|
$json = file_get_contents(__DIR__.'/example_ffhh.json');
|
||||||
|
$data = json_decode($json, $assoc = true);
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('name', $data);
|
||||||
|
$this->assertArrayHasKey('state', $data);
|
||||||
|
$this->assertArrayHasKey('location', $data);
|
||||||
|
$this->assertArrayHasKey('services', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_externaldata_mock() {
|
||||||
|
$ed = new MockDataService();
|
||||||
|
$url_dir = 'https://raw.githubusercontent.com/freifunk/directory.api.freifunk.net/master/directory.json';
|
||||||
|
$url_ff = 'http://meta.hamburg.freifunk.net/ffhh.json';
|
||||||
|
$url_inv = 'http://meta.hamburg.freifunk.net/invalid.txt';
|
||||||
|
|
||||||
|
// verify that $ed->get does not read the URLs above, but local example_*.json files
|
||||||
|
$data_ff = $ed->get($url_ff);
|
||||||
|
$this->assertArrayHasKey('name', $data_ff);
|
||||||
|
$this->assertArrayHasKey('state', $data_ff);
|
||||||
|
$this->assertArrayHasKey('location', $data_ff);
|
||||||
|
$this->assertArrayHasKey('services', $data_ff);
|
||||||
|
|
||||||
|
$data_dir = $ed->get($url_dir);
|
||||||
|
$this->assertArrayHasKey('hamburg', $data_dir);
|
||||||
|
$this->assertEquals(2, count($data_dir));
|
||||||
|
|
||||||
|
$data_inv = $ed->get($url_inv);
|
||||||
|
$this->assertEquals(0, count($data_inv));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the aux. classes */
|
||||||
|
function test_ff_directory() {
|
||||||
|
$dir = new FF_Directory(new MockDataService());
|
||||||
|
$valid = $dir->get_url_by_city('hamburg');
|
||||||
|
$invalid = $dir->get_url_by_city('jena');
|
||||||
|
|
||||||
|
$this->assertTrue(!!$valid);
|
||||||
|
$this->assertTrue(!$invalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException PHPUnit_Framework_Error
|
||||||
|
*/
|
||||||
|
function test_ff_community_invalid() {
|
||||||
|
$data = array();
|
||||||
|
$comm = new FF_Community($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_ff_community_empty() {
|
||||||
|
$data = array('location' => array());
|
||||||
|
$comm = new FF_Community($data);
|
||||||
|
$this->assertEmpty($comm->street);
|
||||||
|
$this->assertEmpty($comm->name);
|
||||||
|
|
||||||
|
$string = $comm->format_address();
|
||||||
|
$this->assertEquals('', $string);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_ff_community_filled() {
|
||||||
|
$data = array('location' => array(
|
||||||
|
'address' => array(
|
||||||
|
'Name' => 'some_name',
|
||||||
|
'Street' => 'some_street',
|
||||||
|
'Zipcode' => 'some_zip'
|
||||||
|
),
|
||||||
|
'city' => 'some_city',
|
||||||
|
'lon' => 'some_lon',
|
||||||
|
'lat' => 'some_lat',
|
||||||
|
));
|
||||||
|
$comm = new FF_Community($data);
|
||||||
|
$this->assertEquals('some_name', $comm->name);
|
||||||
|
$this->assertEquals('some_street', $comm->street);
|
||||||
|
$this->assertEquals('some_zip', $comm->zip);
|
||||||
|
$this->assertEquals('some_city', $comm->city);
|
||||||
|
$this->assertEquals('some_lon', $comm->lon);
|
||||||
|
$this->assertEquals('some_lat', $comm->lat);
|
||||||
|
|
||||||
|
$string = $comm->format_address();
|
||||||
|
$this->assertEquals('<p>some_name<br/>some_street<br/>some_zip some_city</p>', $string);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_ff_community_make_from_city() {
|
||||||
|
$comm = FF_Community::make_from_city('hamburg', new MockDataService());
|
||||||
|
$this->assertEquals('Chaos Computer Club Hansestadt Hamburg', $comm->name);
|
||||||
|
$this->assertEquals('Humboldtstr. 53', $comm->street);
|
||||||
|
$this->assertEquals('22083', $comm->zip);
|
||||||
|
$this->assertEquals('Hamburg', $comm->city);
|
||||||
|
$this->assertEquals(10.024418, $comm->lon);
|
||||||
|
$this->assertEquals(53.574267, $comm->lat);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the output methods */
|
||||||
|
function test_output_ff_state_null() {
|
||||||
|
$data = array("state" => array("nodes" => null));
|
||||||
|
$ret = $this->FFM->output_ff_state($data);
|
||||||
|
$this->assertEmpty($ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_output_ff_state() {
|
||||||
|
$data = array("state" => array("nodes" => 429));
|
||||||
|
$ret = $this->FFM->output_ff_state($data);
|
||||||
|
$this->assertRegExp('/429/', $ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_output_ff_services_null() {
|
||||||
|
$data = array();
|
||||||
|
$ret = $this->FFM->output_ff_services($data);
|
||||||
|
$this->assertEmpty($ret);
|
||||||
|
$this->assertEquals('', $ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_output_ff_services() {
|
||||||
|
$data = array(
|
||||||
|
'services' => array(array(
|
||||||
|
'serviceName' => 'jabber',
|
||||||
|
'serviceDescription' => 'chat',
|
||||||
|
'internalUri' => 'xmpp://jabber.local',
|
||||||
|
'externalUri' => 'xmpp://jabber.example.org',
|
||||||
|
)));
|
||||||
|
$ret = $this->FFM->output_ff_services($data);
|
||||||
|
$this->assertEquals('<table><th>Dienst</th><th>Beschreibung</th><th>Freifunk URI</th><th>Internet URI</th>'.
|
||||||
|
'<tr><td>jabber</td><td>chat</td><td><a href="xmpp://jabber.local">xmpp://jabber.local</a></td>'.
|
||||||
|
'<td><a href="xmpp://jabber.example.org">xmpp://jabber.example.org</a></td></tr>'.
|
||||||
|
'</table>', $ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_output_ff_contact_null() {
|
||||||
|
$data = array();
|
||||||
|
$ret = $this->FFM->output_ff_contact($data);
|
||||||
|
$this->assertEquals('', $ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_output_ff_contact_filled() {
|
||||||
|
$data = array('contact' => array(
|
||||||
|
'email' => 'mail@example.com',
|
||||||
|
'jabber' => 'example@freifunk.net'
|
||||||
|
));
|
||||||
|
$ret = $this->FFM->output_ff_contact($data);
|
||||||
|
$this->assertRegExp('/E-Mail/', $ret);
|
||||||
|
$this->assertRegExp('/mailto:mail@example\.com/', $ret);
|
||||||
|
$this->assertRegExp('/XMPP/', $ret);
|
||||||
|
$this->assertRegExp('/xmpp:example/', $ret);
|
||||||
|
|
||||||
|
$data = array('contact' => array(
|
||||||
|
'twitter' => 'http://twitter.com/freifunk'
|
||||||
|
));
|
||||||
|
$ret = $this->FFM->output_ff_contact($data);
|
||||||
|
$this->assertRegExp('/twitter\.com\/freifunk/', $ret);
|
||||||
|
|
||||||
|
$data = array('contact' => array(
|
||||||
|
'twitter' => '@freifunk'
|
||||||
|
));
|
||||||
|
$ret = $this->FFM->output_ff_contact($data);
|
||||||
|
$this->assertRegExp('/Twitter/', $ret);
|
||||||
|
$this->assertRegExp('/twitter\.com\/freifunk/', $ret);
|
||||||
|
|
||||||
|
$data = array('contact' => array(
|
||||||
|
'ml' => 'mail@example.com',
|
||||||
|
'irc' => 'irc://irc.hackint.net/example',
|
||||||
|
'facebook' => 'freifunk',
|
||||||
|
));
|
||||||
|
$ret = $this->FFM->output_ff_contact($data);
|
||||||
|
$this->assertRegExp('/mailto:mail@example\.com/', $ret);
|
||||||
|
$this->assertRegExp('/irc\.hackint\.net\/example/', $ret);
|
||||||
|
$this->assertRegExp('/Facebook:/', $ret);
|
||||||
|
}
|
||||||
|
}
|
70
tests/test-WpIntegrationTests.php
Normal file
70
tests/test-WpIntegrationTests.php
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
# tests with WP integration, using the loaded plugin
|
||||||
|
class WpIntegrationTests extends WP_UnitTestCase {
|
||||||
|
function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
// access to plugin instance
|
||||||
|
$this->plugin = $GLOBALS['wp-plugin-ffmeta'];
|
||||||
|
$this->plugin->reinit_external_data_service(new MockDataService());
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_post_ff_state() {
|
||||||
|
$post_content = '[ff_state]';
|
||||||
|
$post_attribs = array( 'post_title' => 'Test', 'post_content' => $post_content );
|
||||||
|
$post = $this->factory->post->create_and_get( $post_attribs );
|
||||||
|
|
||||||
|
// w/o filter:
|
||||||
|
$this->assertEquals($post_content, $post->post_content);
|
||||||
|
|
||||||
|
// with filter:
|
||||||
|
$output = apply_filters( 'the_content', $post->post_content );
|
||||||
|
$this->assertEquals("<div class=\"ff ff_state\">429</div>\n", $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_post_ff_state_othercity() {
|
||||||
|
$post_content = '[ff_state ffm]';
|
||||||
|
$post_attribs = array( 'post_title' => 'Test', 'post_content' => $post_content );
|
||||||
|
$post = $this->factory->post->create_and_get( $post_attribs );
|
||||||
|
$output = apply_filters( 'the_content', $post->post_content );
|
||||||
|
|
||||||
|
$this->assertEquals("<div class=\"ff ff_state\"></div>\n", $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_post_ff_state_inv_city() {
|
||||||
|
$post_content = '[ff_state jena]';
|
||||||
|
$post_attribs = array( 'post_title' => 'Test', 'post_content' => $post_content );
|
||||||
|
$post = $this->factory->post->create_and_get( $post_attribs );
|
||||||
|
$output = apply_filters( 'the_content', $post->post_content );
|
||||||
|
|
||||||
|
$this->assertRegExp('/<!-- FF Meta Error:/', $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_post_ff_services() {
|
||||||
|
$post_content = '[ff_services]';
|
||||||
|
$post_attribs = array( 'post_title' => 'Test', 'post_content' => $post_content );
|
||||||
|
$post = $this->factory->post->create_and_get( $post_attribs );
|
||||||
|
|
||||||
|
// w/o filter:
|
||||||
|
$this->assertEquals($post_content, $post->post_content);
|
||||||
|
|
||||||
|
// with filter:
|
||||||
|
$output = apply_filters( 'the_content', $post->post_content );
|
||||||
|
$this->assertRegExp('/radio\.ffhh/', $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_post_ff_list() {
|
||||||
|
$post_content = '[ff_list]';
|
||||||
|
$post_attribs = array( 'post_title' => 'Test', 'post_content' => $post_content );
|
||||||
|
$post = $this->factory->post->create_and_get( $post_attribs );
|
||||||
|
|
||||||
|
// w/o filter:
|
||||||
|
$this->assertEquals($post_content, $post->post_content);
|
||||||
|
|
||||||
|
// with filter:
|
||||||
|
$output = apply_filters( 'the_content', $post->post_content );
|
||||||
|
$this->assertRegExp('/Hamburg/', $output);
|
||||||
|
$this->assertRegExp('/Frankfurt/', $output);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue