├── TODO.md ├── feed.php ├── README.md └── mergedrss.php /TODO.md: -------------------------------------------------------------------------------- 1 | TODO 2 | ==== 3 | 4 | directory 5 | --------- 6 | - find a space to put the directory file 7 | - give a possibility to add new communities via web form (maybe ask the SpaceAPI developers) 8 | - provide a way to cache the json file, if the communities web server is weak 9 | 10 | generator 11 | --------- 12 | - build a validator to approve given json files (as of now, we can only generate a file from a web form) 13 | - use the directory to load and approve the json files 14 | 15 | api 16 | --- 17 | - use links instead of copying files between directories 18 | - create a cache mode for collectCommunities.py 19 | -------------------------------------------------------------------------------- /feed.php: -------------------------------------------------------------------------------- 1 | export(false, true, 50); 61 | 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Anwendungen 2 | =========== 3 | 4 | Freifunk Community Map 5 | ---------------------- 6 | 7 | Ein Beispiel gibt es hier: http://weimarnetz.de/ffmap/map.html 8 | 9 | Freifunk Community Feed Merger 10 | ------------------------------ 11 | 12 | Blogfeeds werden hier zusammengefast: http://weimarnetz.de/fffeed/feed.php 13 | 14 | 15 | Entstehung 16 | ========== 17 | 18 | Zum Wireless Community Weekend 2013 in Berlin fand ein ersten Treffen 19 | zum Relaunch unserer Website freifunk.net statt. Dabei kam auch die 20 | Frage auf, wie man die einzelnen Freifunkcommunities am besten 21 | präsentieren kann, ohne alle Daten zentral zu erfassen und zudem den 22 | Communities eine einfache Möglichkeit zu bieten, eigene Daten selbst 23 | aktuell zu halten. 24 | 25 | In Anlehnung an die Hackerspaces API (http://hackerspaces.nl/spaceapi/) 26 | wurde die Idee einer Freifunk API geboren: Jede Community stellt ihre 27 | Daten in einem definierten Format an einer ihr zugänglichen Stelle 28 | (Webspace, Wiki, Webserver) bereit und trägt sich in das Verzeichnis 29 | ein. Das Verzeichnis besteht lediglich aus einer Zuordnung von 30 | Communitynamen und URL zu den den bereitgestellten Daten. Die erste 31 | Anwendung soll eine Karte mit darin angezeigten Freifunkcommunities 32 | sein, um Besuchern und Interessierten einen Überblick zu geben und 33 | lokale Ansprechpartner zu vermitteln. 34 | 35 | Die Freifunk API soll die Metadaten der Communities dezentral sammeln und anderen Nutzern zur Verfügung stellen. Die API ist nicht zu verwechseln mit einer Datenbank für Freifunkknoten oder als Verzeichnis von Firmwareeintellungen einzelner Communities. 36 | 37 | Weitere Informationen zur API sind in einem Blogartikel unter http://blog.freifunk.net/2013/die-neue-freifunk-api-aufruf-zum-mitmachen zusammengefasst. 38 | 39 | History 40 | ======= 41 | 42 | At the Wireless Community Weekend 2013 in Berlin there was a first meeting to relaunch freifunk.net. To represent local communities without collecting and storing data centrally, a way had to be found. Another requirement was to enable local communities to keep their data up to date easily. 43 | 44 | Based on the Hackerspaces API (http://hackerspaces.nl/spaceapi/) the idea of the freifunk API was born: Each community provides its data in a well defined format, hosted on their places (web space, wiki, web servers) and contributes a link to the directory. This directory only consists of the name and an url per community. First services supported by our freifunk API are the global community map and a community feed aggregator. 45 | 46 | The freifunk API is designed to collect metadata of communities in a decentral way and make it available to other users. It's not designated to be a freifunk node database or a directory of individual community firmware settings. 47 | 48 | 49 | -------------------------------------------------------------------------------- /mergedrss.php: -------------------------------------------------------------------------------- 1 | myTitle = $channel_title; 15 | $this->myLink = $channel_link; 16 | $this->myDescription = $channel_description; 17 | $this->myPubDate = $channel_pubdate; 18 | $this->myCacheTime = $cache_time_in_seconds; 19 | $this->fetch_timeout = $fetch_timeout; 20 | 21 | // initialize feed variable 22 | $this->myFeeds = array(); 23 | 24 | if (isset($feeds)) { 25 | // check if it's an array. if so, merge it into our existing array. if it's a single feed, just push it into the array 26 | if (is_array($feeds)) { 27 | $this->myFeeds = array_merge($feeds); 28 | } else { 29 | $this->myFeeds[] = $feeds; 30 | } 31 | } 32 | } 33 | 34 | // exports the data as a returned value and/or outputted to the screen 35 | public function export($return_as_string = true, $output = false, $limit = null) { 36 | // initialize a combined item array for later 37 | $items = array(); 38 | 39 | // loop through each feed 40 | foreach ($this->myFeeds as $feed_array) { 41 | $feed_url = $feed_array[0]; 42 | // determine my cache file name. for now i assume they're all kept in a file called "cache" 43 | $cache_file = "cache/" . $this->__create_feed_key($feed_url); 44 | 45 | // determine whether or not I should use the cached version of the xml 46 | $use_cache = false; 47 | if (file_exists($cache_file)) { 48 | if (time() - filemtime($cache_file) < $this->myCacheTime) { 49 | $use_cache = true; 50 | } 51 | } 52 | 53 | if ($use_cache) { 54 | // retrieve cached version 55 | $sxe = $this->__fetch_rss_from_cache($cache_file); 56 | $results = $sxe->channel->item; 57 | } else { 58 | // retrieve updated rss feed 59 | $sxe = $this->__fetch_rss_from_url($feed_url); 60 | $results = $sxe->channel->item; 61 | 62 | if (!isset($results)) { 63 | // couldn't fetch from the url. grab a cached version if we can 64 | if (file_exists($cache_file)) { 65 | $sxe = $this->__fetch_rss_from_cache($cache_file); 66 | $results = $sxe->channel->item; 67 | } 68 | } else { 69 | // we need to update the cache file 70 | $sxe->asXML($cache_file); 71 | } 72 | } 73 | 74 | if (isset($results)) { 75 | // add each item to the master item list 76 | foreach ($results as $item) { 77 | if (trim($item->title) == '') { 78 | continue; 79 | } 80 | //convert title to utf-8 (i.e. from facebook feeds) 81 | $item->title = html_entity_decode($item->title, ENT_QUOTES, 'UTF-8'); 82 | $source = $item->addChild('source', '' . $feed_array[1]); 83 | $source->addAttribute('url', $feed_array[2]); 84 | $items[] = $item; 85 | } 86 | } 87 | } 88 | 89 | 90 | // set all the initial, necessary xml data 91 | $xml = "\n"; 92 | $xml .= "\n"; 93 | $xml .= "\n"; 94 | if (isset($this->myTitle)) { $xml .= "\t".$this->myTitle."\n"; } 95 | $xml .= "\t\n"; 96 | if (isset($this->myLink)) { $xml .= "\t".$this->myLink."\n"; } 97 | if (isset($this->myDescription)) { $xml .= "\t".$this->myDescription."\n"; } 98 | if (isset($this->myPubDate)) { $xml .= "\t".$this->myPubDate."\n"; } 99 | 100 | // if there are any items to add to the feed, let's do it 101 | if (sizeof($items) >0) { 102 | 103 | // sort items 104 | usort($items, array($this,"__compare_items")); 105 | 106 | // if desired, splice items into an array of the specified size 107 | if (isset($limit)) { array_splice($items, intval($limit)); } 108 | 109 | // now let's convert all of our items to XML 110 | for ($i=0; $iasXML() ."\n"; 112 | } 113 | 114 | 115 | } 116 | $xml .= "\n"; 117 | 118 | // if output is desired print to screen 119 | if ($output) { echo $xml; } 120 | 121 | // if user wants results returned as a string, do so 122 | if ($return_as_string) { return $xml; } 123 | 124 | } 125 | 126 | 127 | // compares two items based on "pubDate" 128 | private function __compare_items($a,$b) { 129 | return strtotime($b->pubDate) - strtotime($a->pubDate); 130 | } 131 | 132 | // retrieves contents from a cache file ; returns null on error 133 | private function __fetch_rss_from_cache($cache_file) { 134 | if (file_exists($cache_file)) { 135 | return simplexml_load_file($cache_file); 136 | } 137 | return null; 138 | } 139 | 140 | // retrieves contents of an external RSS feed ; implicitly returns null on error 141 | private function __fetch_rss_from_url($url) { 142 | // Create new SimpleXMLElement instance 143 | try { 144 | //set user agent, i.e. facebook.com doesn't deliver feeds to unknown browsers 145 | ini_set('user_agent', 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36'); 146 | $fp = fopen($url, 'r', false , stream_context_create(array('http' => array('timeout' => $this->fetch_timeout)))); 147 | 148 | if ($fp) { 149 | $sxe = simplexml_load_string(stream_get_contents($fp)); 150 | } else { 151 | $sxe = false; 152 | } 153 | return $sxe; 154 | } catch (Exception $e) { 155 | return null; 156 | } 157 | } 158 | 159 | // creates a key for a specific feed url (used for creating friendly file names) 160 | private function __create_feed_key($url) { 161 | return preg_replace('/[^a-zA-Z0-9\.]/', '_', $url) . 'cache'; 162 | } 163 | 164 | } 165 | 166 | --------------------------------------------------------------------------------