diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4a880a9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+bin
+vendor
+composer.lock
+*.sublime-project
+*.sublime-workspace
+nbproject/
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..38b233c
--- /dev/null
+++ b/.travis.yml
@@ -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
diff --git a/README.md b/README.md
index d7a6d5b..1b7a199 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,23 @@
# 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/)).
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
Text:
+ Location:
+
+ [ff_location]
+
Services:
[ff_services]
@@ -24,4 +32,7 @@ Text:
Output:
+
+

+
diff --git a/behat.yml b/behat.yml
new file mode 100644
index 0000000..6b10c9c
--- /dev/null
+++ b/behat.yml
@@ -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
\ No newline at end of file
diff --git a/bin/install-wp-tests.sh b/bin/install-wp-tests.sh
new file mode 100644
index 0000000..0aaf09f
--- /dev/null
+++ b/bin/install-wp-tests.sh
@@ -0,0 +1,112 @@
+#!/usr/bin/env bash
+
+if [ $# -lt 3 ]; then
+ echo "usage: $0 [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
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..65d629a
--- /dev/null
+++ b/composer.json
@@ -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": "*"
+ }
+}
diff --git a/features/bootstrap/FeatureContext.php b/features/bootstrap/FeatureContext.php
new file mode 100644
index 0000000..d615d33
--- /dev/null
+++ b/features/bootstrap/FeatureContext.php
@@ -0,0 +1,67 @@
+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\""),
+ );
+ }
+
+}
diff --git a/features/bootstrap/WordPressContext.php b/features/bootstrap/WordPressContext.php
new file mode 100644
index 0000000..130cecc
--- /dev/null
+++ b/features/bootstrap/WordPressContext.php
@@ -0,0 +1,248 @@
+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 = '
Testing all the things. All the time.
') {
+ // 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 );
+
+ }
+
+}
diff --git a/features/shortcodes.feature b/features/shortcodes.feature
new file mode 100644
index 0000000..28df0fd
--- /dev/null
+++ b/features/shortcodes.feature
@@ -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"
+
diff --git a/features/wp-search.feature b/features/wp-search.feature
new file mode 100644
index 0000000..879e7d8
--- /dev/null
+++ b/features/wp-search.feature
@@ -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."
diff --git a/freifunk_marker.png b/freifunk_marker.png
new file mode 100644
index 0000000..279c962
Binary files /dev/null and b/freifunk_marker.png differ
diff --git a/freifunkmeta.php b/freifunkmeta.php
index fe65de6..ca39e8e 100644
--- a/freifunkmeta.php
+++ b/freifunkmeta.php
@@ -1,211 +1,536 @@
false );
+ $http_response = wp_remote_get( $url, $args );
+ 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;
+ }
}
-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 '\n";
+ }
+ if ( false === ( $metadata = $ed->get( $url ) ) ) {
+ return "\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(
+ '
';
+ return $outstr;
+ }
+
+ function admin_menu() {
+ // Options Page:
+ add_options_page(
+ 'FF Meta Plugin', // page title
+ 'FF Meta', // menu title
+ 'manage_options', // req'd capability
+ 'ff_meta_plugin', // menu slug
+ array( 'FF_meta', 'options_page' ) // callback function
+ );
+ }
+
+ function admin_init() {
+ register_setting(
+ 'ff_meta_settings-group', // group name
+ 'ff_meta_cachetime' // option name
+ );
+ register_setting(
+ 'ff_meta_settings-group', // group name
+ 'ff_meta_city' // option name
+ );
+ add_settings_section(
+ 'ff_meta_section-one', // ID
+ 'Section One', // Title
+ array( 'FF_Meta', 'section_one_callback' ), // callback to fill
+ 'ff_meta_plugin' // page to display on
+ );
+ add_settings_field(
+ 'ff_meta_city', // ID
+ 'Default community', // Title
+ array( 'FF_Meta', 'city_callback' ), // callback to fill field
+ 'ff_meta_plugin', // menu page=slug to display field on
+ 'ff_meta_section-one', // section to display the field in
+ array( 'label_for' => 'ff_meta_city_id' ) // ID of input element
+ );
+ add_settings_field(
+ 'ff_meta_cachetime', // ID
+ 'Cache time', // Title
+ array( 'FF_Meta', 'cachetime_callback' ), // callback to fill field
+ 'ff_meta_plugin', // menu page=slug to display field on
+ 'ff_meta_section-one', // section to display the field in
+ array( 'label_for' => 'ff_meta_cachetime_id' ) // ID of input element
+ );
+ }
+
+ function section_one_callback() {
+ echo 'This Plugin provides shortcodes to display information'
+ .' from the Freifunk meta.json.';
+ }
+
+ function cachetime_callback() {
+ $time = get_option( 'ff_meta_cachetime', FF_META_DEFAULT_CACHETIME );
+ echo 'Data from external URLs is cached'
+ .' for this number of minutes.
+ ";
- return $outstr;
-}
-
-
-// Options Page:
-add_action( 'admin_menu', 'ff_meta_admin_menu' );
-function ff_meta_admin_menu() {
- add_options_page(
- 'FF Meta Plugin', // page title
- 'FF Meta', // menu title
- 'manage_options', // req'd capability
- 'ff_meta_plugin', // menu slug
- 'ff_meta_options_page' // callback function
- );
-}
-
-add_action( 'admin_init', 'ff_meta_admin_init' );
-function ff_meta_admin_init() {
- register_setting(
- 'ff_meta_settings-group', // group name
- 'ff_meta_cachetime' // option name
- );
- register_setting(
- 'ff_meta_settings-group', // group name
- 'ff_meta_city' // option name
- );
- add_settings_section(
- 'ff_meta_section-one', // ID
- 'Section One', // Title
- 'ff_meta_section_one_callback', // callback to fill
- 'ff_meta_plugin' // page to display on
- );
- add_settings_field(
- 'ff_meta_city', // ID
- 'Default community', // Title
- 'ff_meta_city_callback', // callback to fill field
- 'ff_meta_plugin', // menu page=slug to display field on
- 'ff_meta_section-one', // section to display the field in
- array('label_for' => 'ff_meta_city_id') // ID of input element
- );
- add_settings_field(
- 'ff_meta_cachetime', // ID
- 'Cache time', // Title
- 'ff_meta_cachetime_callback', // callback to fill field
- 'ff_meta_plugin', // menu page=slug to display field on
- 'ff_meta_section-one', // section to display the field in
- array('label_for' => 'ff_meta_cachetime_id') // ID of input element
- );
-}
-
-function ff_meta_section_one_callback() {
- echo 'This Plugin provides shortcodes to display information from the Freifunk meta.json.';
-}
-
-function ff_meta_cachetime_callback() {
- $time = get_option( 'ff_meta_cachetime', FF_META_DEFAULT_CACHETIME );
- echo " minutes"
- ."
Data from external URLs is cached for this number of minutes.