]> git.ipfire.org Git - ipfire.org.git/commitdiff
Major update of the webapp.
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 27 Dec 2013 14:25:21 +0000 (15:25 +0100)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 27 Dec 2013 14:25:21 +0000 (15:25 +0100)
Migrate to PostgreSQL. Remove all the singletons.
A bit of cleanup and functionality enhancements.

53 files changed:
.gitignore
manager.py
static/css/style.css
templates/downloads-index.html
templates/geoip/index.html [new file with mode: 0644]
templates/index.html
templates/mirrors-item.html
templates/mirrors.html
templates/modules/map.html [new file with mode: 0644]
templates/modules/mirrors-table.html
templates/modules/news-item.html
templates/modules/news-table.html
templates/netboot/menu-config.cfg [new file with mode: 0644]
templates/netboot/menu-header.cfg [new file with mode: 0644]
templates/netboot/menu-separator.cfg [new file with mode: 0644]
templates/netboot/menu.cfg
templates/wishlist/modules/wish.html
templates/wishlist/wish.html
translations/de_DE/LC_MESSAGES/webapp.po
translations/webapp.pot
webapp.conf.example [new file with mode: 0644]
webapp.py
webapp/__init__.py
webapp/backend/__init__.py
webapp/backend/accounts.py
webapp/backend/ads.py
webapp/backend/banners.py [deleted file]
webapp/backend/base.py
webapp/backend/countries.py [new file with mode: 0644]
webapp/backend/database.py [new file with mode: 0644]
webapp/backend/databases.py [deleted file]
webapp/backend/geoip.py
webapp/backend/iuse.py
webapp/backend/memcached.py
webapp/backend/mirrors.py
webapp/backend/misc.py
webapp/backend/netboot.py
webapp/backend/news.py
webapp/backend/planet.py
webapp/backend/releases.py
webapp/backend/settings.py
webapp/backend/stasy.py
webapp/backend/tracker.py
webapp/backend/wishlist.py
webapp/handlers.py
webapp/handlers_admin.py
webapp/handlers_base.py
webapp/handlers_boot.py
webapp/handlers_download.py
webapp/handlers_iuse.py
webapp/handlers_mirrors.py
webapp/handlers_tracker.py
webapp/ui_modules.py

index c732e42301e79b6dbe88bf69756705d8c91b9417..cd79799f96ae3297b42ab4a27098e5d32539449d 100644 (file)
@@ -1,2 +1,3 @@
+/webapp.conf
 *.mo
 *.py[co]
index 63fac1f6855e7f6578b0de0a56029bc8f9874d2f..f921aaa7ce0e4fd23a0664099b150ef08d55f938 100644 (file)
@@ -12,6 +12,8 @@ class Daemon(object):
                
                self.ioloop.set_blocking_log_threshold(900)
 
+               self.backend = backend.Backend("webapp.conf")
+
        @property
        def ioloop(self):
                return tornado.ioloop.IOLoop.instance()
@@ -53,6 +55,14 @@ class Manager(object):
                logging.debug("Next call will be in %.2f seconds." % \
                        (self.pc.callback_time / 1000))
 
+       @property
+       def backend(self):
+               return self.daemon.backend
+
+       @property
+       def settings(self):
+               return self.backend.settings
+
        @property
        def timeout(self):
                """
@@ -68,11 +78,11 @@ class Manager(object):
 class MirrorManager(Manager):
        @property
        def mirrors(self):
-               return backend.Mirrors()
+               return self.backend.mirrors
 
        @property
        def timeout(self):
-               return backend.Config().get_int("mirror_check_interval")
+               return self.backend.settings.get_int("mirror_check_interval", 3600)
 
        def do(self):
                # Check status of all mirror servers.
@@ -82,14 +92,14 @@ class MirrorManager(Manager):
 class ReleaseFilesManager(Manager):
        @property
        def releases(self):
-               return backend.Releases()
+               return self.backend.releases
 
        @property
        def timeout(self):
-               return backend.Config().get_int("releasefiles_check_interval")
+               return self.settings.get_int("releasefiles_check_interval", 3600)
 
        def do(self):
-               for release in self.releases.list():
+               for release in self.releases.get_all():
                        release.scan_files()
 
 
index a15ea4a4dfa3e80f3a4a00c4d29cfc3f24214395..82ee4a50b699a424761bddc31bed2f7666285e28 100644 (file)
@@ -11,7 +11,7 @@ body {
        padding-bottom: 10px;
 }
 
-.ac {
+.ac, td.ac {
        text-align: center;
 }
 
index 0ed63b443a04240b6b0266527b7358ee8529fcee..b4aff9713c2ef476b375ccb614e4f2d06b8ccd2d 100644 (file)
@@ -41,7 +41,7 @@
                        {% for release in releases %}
                                <tr>
                                        <td>
-                                               <a href="/release/{{ release.id }}">{{ release.name }}</a>
+                                               <a href="/release/{{ release.sname }}">{{ release.name }}</a>
                                        </td>
                                        <td>
                                                {% if release.stable %}
diff --git a/templates/geoip/index.html b/templates/geoip/index.html
new file mode 100644 (file)
index 0000000..63a4a09
--- /dev/null
@@ -0,0 +1,50 @@
+{% extends "../base.html" %}
+
+{% block title %}{{ _("GeoIP for %s") % addr }}{% end block %}
+
+{% block body %}
+       <div class="page-header">
+               <h3>{{ _("GeoIP for %s") % addr }}</h3>
+       </div>
+
+       {% if peer %}
+               <div class="row">
+                       <div class="span4">
+                               <dl>
+                                       {% if peer.asn %}
+                                               <dt>{{ _("Autonomous System") }}</dt>
+                                               <dd>{{ peer.asn }}</dd>
+                                       {% end %}
+
+                                       <dt>{{ _("Country") }}</dt>
+                                       <dd>
+                                               {% if peer.country_name %}
+                                                       {{ peer.country_name }} ({{ peer.country }})
+                                               {% else %}
+                                                       {{ peer.country_name }}
+                                               {% end %}
+                                       </dd>
+
+                                       {% if peer.city %}
+                                               <dt>{{ _("City") }}</dt>
+                                               <dd>{{ peer.city }}</dd>
+
+                                               {% if peer.postal_code %}
+                                                       <dt>{{ _("Postal Code") }}</dt>
+                                                       <dd>{{ peer.postal_code }}</dd>
+                                               {% end %}
+                                       {% end %}
+                               </dl>
+                       </div>
+
+                       <div class="span8">
+                               {% module Map(peer.latitude, peer.longitude) %}
+                       </div>
+               </div>
+       {% else %}
+               <div class="alert alert-info">
+                       {{ _("No GeoIP information could be found for the IP address '%s'.") % addr }}
+               </div>
+       {% end %}
+
+{% end block %}
index 6d644f0441490226e969731f0e0335d074de46f8..2b3e5bca108bbf121bcdaaf8d9ed3f7bae07d8c4 100644 (file)
                                        {% for item in latest_news %}
                                                <tr>
                                                        <td class="date">
-                                                               {{ locale.format_date(item.date, relative=True, shorter=True) }} &dash;
+                                                               {{ locale.format_date(item.published, relative=True, shorter=True) }} &dash;
                                                        </td>
                                                        <td class="link">
                                                                <a href="/news/{{ item.slug }}">{{ item.title }}</a>
index cc375a2d7de6a6df774a283f190b4fc811ab0b90..e6c730fbb95444c969cb6286af25abbd9c647913 100644 (file)
                <h1>{{ item.hostname }}</h1>
        </div>
 
-       <table class="table">
-               <tr>
-                       <td>{{ _("Last update") }}</td>
-                       <td>{{ locale.format_date(item.last_update, full_format=True) }}</td>
-               </tr>
-               <tr>
-                       <td>{{ _("Owner") }}</td>
-                       <td>{{ item.owner }}</td>
-               </tr>
-               {% if item.prefer_for_countries %}
-                       <tr>
-                               <td>{{ _("Preferred for") }}</td>
-                               <td>{{ locale.list(item.prefer_for_countries_names) }}</td>
-                       </tr>
-               {% end %}
-               {% if client_distance %}
-                       <tr>
-                               <td>{{ _("Your distance to this mirror") }}</td>
-                               <td>{{ "%.1f km" % client_distance }}</td>
-                       </tr>
-               {% end %}
-       </table>
-
-       <a class="btn pull-right" href="{{ item.url }}">{{ _("Go to mirror") }}</a>
-
-       <br style="clear: both;">
-       <hr>
-
-       <h3>{{ _("Mirror location") }}</h3>
-       <p>
-               {{ _("The mirror <em>%s</em> is located in %s.") % (item.hostname, item.location_str) }}
-       </p>
-
-       {% if item.longitude and item.latitude %}
-               <iframe width="100%" height="350" frameborder="0" scrolling="no" marginheight="0" marginwidth="0"
-                       src="http://www.openstreetmap.org/export/embed.html?bbox={{ item.longitude - 4 }},{{ item.latitude - 4 }},{{ item.longitude + 4 }},{{ item.latitude + 4 }}&amp;layer=mapquest&amp;marker={{ item.latitude }},{{ item.longitude }}" style="border: 1px solid black">
-               </iframe>
-               <p>
-                       <a href="http://www.openstreetmap.org/?lat={{ item.latitude }}&amp;lon={{ item.longitude }}&amp;zoom=8&amp;layers=M&amp;mlat={{ item.latitude }}&amp;mlon={{ item.longitude }}" target="_blank">{{ _("View larger map") }}</a>
-                       -
-                       &copy; <a href="http://www.openstreetmap.org/" target="_blank">OpenStreetMap</a> contributors, CC-BY-SA
-               </p>
-               <p class="muted ac">
-                       {{ _("The location of the mirror server is estimated by the IP address.") }}
-               </p>
-       {% else %}
-               {{ _("The location of the mirror server could not be estimated.") }}
-       {% end %}
+       <div class="row">
+               <div class="span4">
+                       {% if item.owner %}
+                               <dt>{{ _("Owner") }}</dt>
+                               <dd>{{ item.owner }}</dd>
+                       {% end %}
+
+                       {% if item.location_str %}
+                               <hr>
+
+                               <dt>{{ _("Location") }}</dt>
+                               <dd>{{ item.location_str }}</dd>
+                       {% elif item.location %}
+                               <hr>
+
+                               {% if item.country_name %}
+                                       <dt>{{ _("Country") }}</dt>
+                                       <dd>{{ item.country_name }}</dd>
+                               {% end %}
+
+                               {% if item.location.city %}
+                                       <dt>{{ _("City") }}</dt>
+                                       <dd>{{ item.location.city }}</dd>
+                               {% end %}
+                       {% end %}
+
+                       {% if item.prefer_for_countries %}
+                               <dt>{{ _("Preferred for") }}</dt>
+                               <dd>
+                                       {{ locale.list(item.prefer_for_countries_names) }}
+                               </dd>
+                       {% end %}
+
+                       {% if client_distance %}
+                               <dt>{{ _("Estimated distance to you") }}</dt>
+                               <dd>{{ "%.0fkm" % client_distance }}</dd>
+                       {% end %}
+
+                       <hr>
+
+                       {% if item.asn %}
+                               <dt>{{ _("Autonomous System") }}</dt>
+                               <dd>{{ item.asn }}</dd>
+                       {% end %}
+
+                       <dt>{{ _("IP Addresses") }}</dt>
+                       <dd>
+                               {% for addr in item.addresses6 + item.addresses4 %}
+                                       {{ addr }}<br>
+                               {% end %}
+                       </dd>
+
+                       {% if item.enabled %}
+                               <hr>
+
+                               <dt>{{ _("Last updated") }}</dt>
+                               <dd>
+                                       <abbr title="{{ locale.format_date(item.last_update, full_format=True) }} UTC">
+                                               {{ locale.format_date(item.last_update, relative=True) }}
+                                       </abbr>
+                               </dd>
+                       {% end %}
+
+                       <hr>
+
+                       <p class="ac">
+                               <a class="btn" href="{{ item.url }}">{{ _("Go to mirror") }}</a>
+                       </p>
+               </div>
+
+               <div class="span8">
+                       {% if item.location %}
+                               {% module Map(item.latitude, item.longitude) %}
+                               <p class="muted ac">
+                                       {{ _("The location of the mirror server is estimated by the IP address.") }}
+                               </p>
+                       {% else %}
+                               <p class="ac muted">
+                                       {{ _("The location of this mirror server could not be estimated.") }}
+                               </p>
+                       {% end %}
+               </div>
+       </div>
 {% end block %}
index 59f0f470cc980e355e57586e961300a5e5cea6c8..1c5da56b252354ccc26ef87629c65e787ba54692 100644 (file)
                </p>
        {% end %}
 
-       {% if preferred_mirrors %}
-               <h2>{{ _("Mirror servers nearby") }}</h2>
-               {% module MirrorsTable(preferred_mirrors) %}
+       <hr>
 
-               <h2>{{ _("Worldwide mirror servers") }}</h2>
-               {% module MirrorsTable(other_mirrors) %}
-       {% else %}
-               <h2>{{ _("Worldwide mirror servers") }}</h2>
-               {% module MirrorsTable(other_mirrors) %}
-       {% end %}
+       {% module MirrorsTable(mirrors, preferred_mirrors) %}
 {% end block %}
diff --git a/templates/modules/map.html b/templates/modules/map.html
new file mode 100644 (file)
index 0000000..8849451
--- /dev/null
@@ -0,0 +1,20 @@
+<iframe width="100%" height="500" frameborder="0" scrolling="no" marginheight="0" marginwidth="0"
+       src="http://www.openstreetmap.org/export/embed.html?bbox={{ longitude - 4 }},{{ latitude - 4 }},{{ longitude + 4 }},{{ latitude + 4 }}&amp;layer=mapquest&amp;marker={{ latitude }},{{ longitude }}" style="border: 1px solid black">
+</iframe>
+<p>
+       <a href="http://www.openstreetmap.org/?lat={{ latitude }}&amp;lon={{ longitude }}&amp;zoom=8&amp;layers=M&amp;mlat={{ latitude }}&amp;mlon={{ longitude }}" target="_blank">{{ _("View larger map") }}</a>
+       -
+       &copy; <a href="http://www.openstreetmap.org/" target="_blank">OpenStreetMap</a> contributors, CC-BY-SA
+       <span class="pull-right">
+               {% if latitude >= 0 %}
+                       {{ latitude }} N,
+               {% else %}
+                       {{ -latitude }} S,
+               {% end %}
+               {% if longitude >= 0 %}
+                       {{ longitude }} E
+               {% else %}
+                       {{ -longitude }} W
+               {% end %}
+       </span>
+</p>
index e59502036d71d33649a5fec02fb68902ebf10b31..e46c5708ba88d1c3d091cbbb3f47671821f98497 100644 (file)
@@ -1,6 +1,19 @@
 <table class="table table-striped">
        {% for mirror in mirrors %}
                <tr>
+                       <td class="ac">
+                               {% if mirror.country_code %}
+                                       <img src="{{ static_url("images/flags/%s.png" % mirror.country_code.lower()) }}" alt="{{ mirror.country_code }}" />
+                                       <br>
+                                       {% if mirror.country_name %}
+                                               <abbr title="{{ mirror.country_name }}">{{ mirror.country_code }}</abbr>
+                                       {% else %}
+                                               {{ mirror.country_code }}
+                                       {% end %}
+                                       {% if mirror in preferred_mirrors %}*{% end %}
+                               {% end %}
+                       </td>
+
                        {% if mirror.state == "UP" %}
                                <td style="background-color: green;">
                                        &nbsp;
                                </td>
                        {% else %}
                                <td style="background-color: grey;">
-                                       &nbsp;
+                                       &nbsp; {{ mirror.state }}
                                </td>
                        {% end %}
                        <td>
-                               <a href="/mirror/{{ mirror.id }}">{{ mirror.hostname }}</a>
-                       </td>
-                       <td>
-                               {{ mirror.owner }}
-                               <br>
-                               <img src="{{ static_url("images/flags/%s.png" % mirror.country_code) }}" alt="{{ mirror.country_code }}" />
-                               {{ mirror.location_str }}
+                               {% if mirror.state in ("OUTOFSYNC", "DOWN") %}
+                                       <span class="text-error pull-right">
+                                               {{ _("Last update: %s") % locale.format_date(mirror.last_update, relative=True) }}
+                                       </span>
+                               {% end %}
+
+                               <a href="/mirror/{{ mirror.hostname }}">{{ mirror.hostname }}</a>
+                               <br>{{ mirror.owner }}
                        </td>
                </tr>
        {% end %}
index 9eb061e020b54c4080e1e2be9240a73347efc545..1716fa5bd5cf4a82773f0d5da9772982f256b60d 100644 (file)
@@ -5,7 +5,7 @@
                                {% if announcement %}
                                        {{ _("Announcement") }}:
                                {% end %}
-                               <a href="/news/{{ escape(item.slug) }}">{{ escape(item.title) }}</a>
+                               <a href="/news/{{ item.slug }}">{{ item.title }}</a>
                        </h2>
                {% end %}
 
@@ -16,7 +16,7 @@
        <div class="span12">
                <p class="pull-right">
                        <a href="/author/{{ item.author_id }}">{{ item.author }}</a> -
-                       {{ locale.format_date(item.date, full_format=True) }}
+                       {{ locale.format_date(item.published, full_format=True) }}
                </p>
        </div>
 </div>
index e1c3677a409895ba5fa722adec5015bf728ab7d2..e4317193beb2deb51e5f802bedb694130eae45a2 100644 (file)
@@ -3,7 +3,7 @@
                <li>
                        <a href="/news/{{ n.slug }}"><strong>{{ n.title }}</strong></a>
                        <br>
-                       &nbsp;&nbsp;{{ locale.format_date(n.date, shorter=True) }}
+                       &nbsp;&nbsp;{{ locale.format_date(n.published, shorter=True) }}
                        <br>&nbsp;
                </li>
        {% end %}
diff --git a/templates/netboot/menu-config.cfg b/templates/netboot/menu-config.cfg
new file mode 100644 (file)
index 0000000..200bae1
--- /dev/null
@@ -0,0 +1,11 @@
+label {{ release.sname }}
+       menu label {{ release.name }}
+
+       text help
+{{ _("Install %s...") % release.name }}
+       endtext
+
+       kernel {{ release.netboot_kernel }}
+       initrd {{ release.netboot_initrd }}
+       append {{ release.netboot_append }}
+
diff --git a/templates/netboot/menu-header.cfg b/templates/netboot/menu-header.cfg
new file mode 100644 (file)
index 0000000..192fe6f
--- /dev/null
@@ -0,0 +1,11 @@
+menu begin {{ id }}
+       menu title {{ title }}
+
+       label {{ id }}.back
+               menu label {{ _("Back...") }}
+               menu exit
+
+       {% module NetBootMenuSeparator() %}
+
+       {% for r in [r for r in releases if r.is_netboot_capable()] %}{% module NetBootMenuConfig(r) %}{% end %}
+menu end
diff --git a/templates/netboot/menu-separator.cfg b/templates/netboot/menu-separator.cfg
new file mode 100644 (file)
index 0000000..2af538f
--- /dev/null
@@ -0,0 +1 @@
+menu separator
index 83325f8e3f39a16f73220ab69cbee66843fe835c..65bfd0f4e872f5781b20e68dfac87a856202d13f 100644 (file)
@@ -13,7 +13,11 @@ menu background boot.png
 prompt 0
 allowoptions 0
 
-menu title IPFire boot menu
+menu title {{ _("IPFire boot menu") }}
 
-{{ menu }}
+{% module NetBootMenuConfig(latest_release) %}
 
+{% module NetBootMenuSeparator() %}
+
+{% module NetBootMenuHeader(_("Stable releases"), stable_releases) %}
+{% module NetBootMenuHeader(_("Development releases"), development_releases) %}
index 7b8d247ad21c979f9cffc9f5e42ce79e758b82b8..e998e37a46f5d10f2a64a8eac20794b4070369ab 100644 (file)
                                                </div>
 
                                                <div class="span4 ac">
-                                                       {% if wish.remaining_days >= 0 %}
-                                                               <p class="lead">{{ wish.remaining_days }}</p>
-                                                               <p>{{ _("day to go", "days to go", wish.remaining_days) }}</p>
+                                                       {% if wish.status == "running" %}
+                                                               {% if wish.remaining_days %}
+                                                                       <p class="lead">{{ wish.remaining_days }}</p>
+                                                                       <p>{{ _("day to go", "days to go", wish.remaining_days) }}</p>
+                                                               {% else %}
+                                                                       <p class="lead">{% raw _("%s &euro;") % (wish.goal - wish.donated) %}</p>
+                                                                       <p>{{ _("to go") }}</p>
+                                                               {% end %}
                                                        {% elif wish.status == "in_progress" %}
                                                                <p class="lead">{{ _("In progress") }}</p>
                                                        {% elif wish.status == "finished" %}
index 506073439f76eee901030fd021aeed73da13bd21..15fa6233778fc6d8e1a4f54aa2c2bdfec289de87 100644 (file)
 
        <p class="ac">
                <i class="icon-calendar"></i>
-               {{ _("Launched: %s") % wish.date_start }}
+               {{ _("Launched: %s") % locale.format_date(wish.date_start, full_format=True) }}
 
                &bull;
 
-               <i class="icon-time"></i>
-               {{ _("Funding ends: %s") % wish.date_end }}
+               {% if wish.date_end %}
+                       <i class="icon-time"></i>
+                       {{ _("Funding ends: %s") % locale.format_date(wish.date_end, full_format=True) }}
+               {% else %}
+                       {{ _("This funding runs until the goal is reached.") }}
+               {% end %}
        </p>
 {% end block %}
index 8423c65db7b17cde6e4e58f0db2a4e529533acdf..653c825c0b685749dc28bf247c0991c4fe13853d 100644 (file)
@@ -7,959 +7,1618 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-07-22 11:43+0200\n"
+"POT-Creation-Date: 2013-12-27 11:53+0100\n"
 "PO-Revision-Date: 2013-07-22 11:44+0100\n"
 "Last-Translator: Michael Tremer <michael.tremer@ipfire.org>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
 "MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
+"Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
 "X-Generator: Poedit 1.5.4\n"
 
-#: webapp/handlers_news.py:43 webapp/ui_modules.py:76
-msgid "Unknown author"
-msgstr "Unbekannter Autor"
+#: webapp/backend/iuse.py:162
+#, python-format
+msgid "Mem: %s"
+msgstr "RAM: %s"
 
-#: webapp/__init__.py:285
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:225
-msgid "January"
-msgstr "Januar"
+#: webapp/backend/iuse.py:165
+#, python-format
+msgid "Disk: %s"
+msgstr "HDD: %s"
 
-#: webapp/__init__.py:287
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:225
-msgid "February"
-msgstr "Februar"
+#: webapp/backend/iuse.py:177
+#, python-format
+msgid "Networks: %s"
+msgstr "Netzwerke: %s"
 
-#: webapp/__init__.py:289
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:225
-msgid "March"
-msgstr "März"
+#: webapp/backend/countries.py:7
+#, fuzzy
+msgid "Andorra"
+msgstr "Hersteller"
 
-#: webapp/__init__.py:291
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:225
-msgid "April"
-msgstr "April"
+#: webapp/backend/countries.py:8
+msgid "United Arab Emirates"
+msgstr ""
 
-#: webapp/__init__.py:293
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:226
-msgid "May"
-msgstr "Mai"
+#: webapp/backend/countries.py:9
+msgid "Afghanistan"
+msgstr ""
 
-#: webapp/__init__.py:295
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:226
-msgid "June"
-msgstr "Juni"
+#: webapp/backend/countries.py:10
+msgid "Antigua and Barbuda"
+msgstr ""
 
-#: webapp/__init__.py:297
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:226
-msgid "July"
-msgstr "Juli"
+#: webapp/backend/countries.py:11
+msgid "Aanguilla"
+msgstr ""
 
-#: webapp/__init__.py:299
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:226
-msgid "August"
-msgstr "August"
+#: webapp/backend/countries.py:12
+msgid "Albania"
+msgstr ""
 
-#: webapp/__init__.py:301
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:227
-msgid "September"
-msgstr "September"
+#: webapp/backend/countries.py:13
+msgid "Armenia"
+msgstr ""
 
-#: webapp/__init__.py:303
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:227
-msgid "October"
-msgstr "Oktober"
+#: webapp/backend/countries.py:14
+msgid "Angola"
+msgstr ""
 
-#: webapp/__init__.py:305
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:227
-msgid "November"
-msgstr "November"
+#: webapp/backend/countries.py:15
+msgid "Antarctica"
+msgstr ""
 
-#: webapp/__init__.py:307
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:227
-msgid "December"
-msgstr "Dezember"
+#: webapp/backend/countries.py:16
+msgid "Argentina"
+msgstr ""
 
-#: webapp/ui_modules.py:239
-#, python-format
-msgid "%s to %s"
-msgstr "%s nach %s"
+#: webapp/backend/countries.py:17
+msgid "American Samoa"
+msgstr ""
 
-#: webapp/backend/wishlist.py:187
-msgid "Checkout this crowdfunding wish from #ipfire:"
+#: webapp/backend/countries.py:18
+msgid "Austria"
 msgstr ""
 
-#: webapp/backend/releases.py:96
-msgid "Image for the armv5tel architecture"
-msgstr "Image für die armv5tel-Architektur"
+#: webapp/backend/countries.py:19
+msgid "Australia"
+msgstr ""
 
-#: webapp/backend/releases.py:97
-msgid "armv5tel image for boards with serial console"
-msgstr "armv5tel-Image für Boards mit serieller Konsole"
+#: webapp/backend/countries.py:20
+msgid "Aruba"
+msgstr ""
 
-#: webapp/backend/releases.py:98
-msgid "Installable CD image"
-msgstr "Bootbares CD-Image"
+#: webapp/backend/countries.py:21
+msgid "Ã…land Islands"
+msgstr ""
 
-#: webapp/backend/releases.py:99
-msgid "Torrent file"
-msgstr "Torrentdatei"
+#: webapp/backend/countries.py:22
+msgid "Azerbaijan"
+msgstr ""
 
-#: webapp/backend/releases.py:100
-msgid "Flash image"
-msgstr "Flash-Image"
+#: webapp/backend/countries.py:23
+msgid "Bosnia and Herzegovina"
+msgstr ""
 
-#: webapp/backend/releases.py:101
-msgid "Alix image"
-msgstr "Alix-Image"
+#: webapp/backend/countries.py:24
+msgid "Barbados"
+msgstr ""
 
-#: webapp/backend/releases.py:102
-msgid "USB FDD Image"
-msgstr "USB-FDD-Image"
+#: webapp/backend/countries.py:25
+msgid "Bangladesh"
+msgstr ""
 
-#: webapp/backend/releases.py:103
-msgid "USB HDD Image"
-msgstr "USB-HDD-Image"
+#: webapp/backend/countries.py:26
+msgid "Belgium"
+msgstr ""
 
-#: webapp/backend/releases.py:104
-msgid "Pregenerated Xen image"
-msgstr "Vorgefertigtes Xen-Image"
+#: webapp/backend/countries.py:27
+msgid "Burkina Faso"
+msgstr ""
 
-#: webapp/backend/releases.py:110 webapp/backend/releases.py:150
-msgid "Unknown image type"
-msgstr "Unbekanntes Imageformat"
+#: webapp/backend/countries.py:28
+msgid "Bulgaria"
+msgstr ""
 
-#: webapp/backend/releases.py:136
-msgid "This image runs on many ARM-based boards"
-msgstr "Dieses Image läuft auf vielen ARM-basierten Boards"
+#: webapp/backend/countries.py:29
+msgid "Bahrain"
+msgstr ""
 
-#: webapp/backend/releases.py:137
-msgid "This image runs on ARM boards with a serial console"
-msgstr "Dieses Image läuft auf ARM-Boards mit serieller Konsole"
+#: webapp/backend/countries.py:30
+msgid "Burundi"
+msgstr ""
 
-#: webapp/backend/releases.py:138
-msgid "Use this image to burn a CD and install IPFire from it."
+#: webapp/backend/countries.py:31
+msgid "Benin"
 msgstr ""
-"Nutze dieses Image um eine CD zu erstellen und IPFire von dieser zu "
-"installieren."
 
-#: webapp/backend/releases.py:139
-msgid "Download the CD image from the torrent network."
-msgstr "CD-Image mit aus dem Torrentnetzwerk laden"
+#: webapp/backend/countries.py:32
+msgid "Saint Barthélemy"
+msgstr ""
 
-#: webapp/backend/releases.py:140
-msgid "An image that is meant to run on embedded devices."
-msgstr "Ein Image, das für eingebettete Systeme optimiert ist."
+#: webapp/backend/countries.py:33
+msgid "Bermuda"
+msgstr ""
 
-#: webapp/backend/releases.py:141
-msgid "Flash image where a serial console is enabled by default."
-msgstr "Image, bei welchem die serielle Konsole eingeschaltet ist."
+#: webapp/backend/countries.py:34
+msgid "Brunei Darussalam"
+msgstr ""
 
-#: webapp/backend/releases.py:142
-msgid "Install IPFire from a floppy-formated USB key."
-msgstr "IPFire von einem Floppy-formatiertem USB-Stick installieren."
+#: webapp/backend/countries.py:35
+msgid "Plurinational State of Bolivia"
+msgstr ""
 
-#: webapp/backend/releases.py:143
-msgid "If the floppy image doesn't work, use this image instead."
-msgstr "Wenn das Floppy-Image nicht funktioniert, dieses verwenden."
+#: webapp/backend/countries.py:36
+msgid "Sint Eustatius and Saba Bonaire"
+msgstr ""
 
-#: webapp/backend/releases.py:144
-msgid "A ready-to-run image for Xen."
-msgstr "Ein fertiges Image für Xen."
+#: webapp/backend/countries.py:37
+msgid "Brazil"
+msgstr ""
 
-#: webapp/backend/iuse.py:165
-#, python-format
-msgid "Mem: %s"
-msgstr "RAM: %s"
+#: webapp/backend/countries.py:38
+msgid "Bahamas"
+msgstr ""
 
-#: webapp/backend/iuse.py:168
-#, python-format
-msgid "Disk: %s"
-msgstr "HDD: %s"
+#: webapp/backend/countries.py:39
+msgid "Bhutan"
+msgstr ""
 
-#: webapp/backend/iuse.py:180
-#, python-format
-msgid "Networks: %s"
-msgstr "Netzwerke: %s"
+#: webapp/backend/countries.py:40
+msgid "Bouvet Island"
+msgstr ""
 
-#: templates/wishlist/wish.html:3
-msgid "Wish"
-msgstr "Wunsch"
+#: webapp/backend/countries.py:41
+msgid "Botswana"
+msgstr ""
 
-#: templates/wishlist/wish.html:18
-#, python-format
-msgid "Launched: %s"
-msgstr "Gestarted: %s"
+#: webapp/backend/countries.py:42
+msgid "Belarus"
+msgstr ""
 
-#: templates/wishlist/wish.html:23
-#, python-format
-msgid "Funding ends: %s"
-msgstr "Sammlung endet: %s"
+#: webapp/backend/countries.py:43
+msgid "Belize"
+msgstr ""
 
-#: templates/wishlist/closed.html:3 templates/wishlist/closed.html:13
-msgid "Closed wishes"
-msgstr "Beendete Wünsche"
+#: webapp/backend/countries.py:44
+msgid "Canada"
+msgstr ""
 
-#: templates/wishlist/closed.html:9
-msgid "Wishes open for donation"
-msgstr "Noch laufende Wünsche"
+#: webapp/backend/countries.py:45
+msgid "Cocos (Keeling) Islands"
+msgstr ""
 
-#: templates/wishlist/closed.html:36 templates/wishlist/closed.html:40
-msgid "Older"
-msgstr "Älter"
+#: webapp/backend/countries.py:46
+msgid "The Democratic Republic of the Congo"
+msgstr ""
 
-#: templates/wishlist/closed.html:46 templates/wishlist/closed.html:50
-msgid "Newer"
-msgstr "Neuer"
+#: webapp/backend/countries.py:47
+msgid "Central African Republic"
+msgstr ""
 
-#: templates/wishlist/base.html:6 templates/index.html:175
-msgid "IPFire Wishlist"
-msgstr "IPFire Wunschliste"
+#: webapp/backend/countries.py:48
+msgid "Congo"
+msgstr ""
 
-#: templates/wishlist/base.html:8
-msgid "Crowd funding for the IPFire project"
-msgstr "Crowdfunding für das IPFire-Projekt"
+#: webapp/backend/countries.py:49
+msgid "Switzerland"
+msgstr ""
 
-#: templates/wishlist/modules/wish.html:6
-msgid "Only a few days left!"
-msgstr "Nur noch wenige Tage!"
+#: webapp/backend/countries.py:50
+msgid "Côte d'Ivoire"
+msgstr ""
 
-#: templates/wishlist/modules/wish.html:26 templates/wishlist/donate.html:3
-#: templates/download-splash.html:37 templates/modules/menu.html:47
-#: templates/static/getinvolved.html:120 templates/static/donation.html:3
-#: templates/static/donation.html:8
-msgid "Donate"
-msgstr "Spenden"
+#: webapp/backend/countries.py:51
+msgid "Cook Islands"
+msgstr ""
 
-#: templates/wishlist/modules/wish.html:36
-msgid "funded"
-msgstr "gesammelt"
+#: webapp/backend/countries.py:52
+msgid "Chile"
+msgstr ""
 
-#: templates/wishlist/modules/wish.html:40
-#, python-format
-msgid "%s &euro;"
-msgstr "%s &euro;"
+#: webapp/backend/countries.py:53
+msgid "Cameroon"
+msgstr ""
 
-#: templates/wishlist/modules/wish.html:41
-msgid "donated"
-msgstr "gespendet"
+#: webapp/backend/countries.py:54
+msgid "China"
+msgstr ""
 
-#: templates/wishlist/modules/wish.html:47
-msgid "day to go"
-msgid_plural "days to go"
-msgstr[0] "Tag Ã¼brig"
-msgstr[1] "Tage Ã¼brig"
+#: webapp/backend/countries.py:55
+msgid "Colombia"
+msgstr ""
 
-#: templates/wishlist/modules/wish.html:49
-msgid "In progress"
-msgstr "In Entwicklung"
+#: webapp/backend/countries.py:56
+msgid "Costa Rica"
+msgstr ""
 
-#: templates/wishlist/modules/wish.html:51
-msgid "Finished"
-msgstr "Beendet"
+#: webapp/backend/countries.py:57
+msgid "Cuba"
+msgstr ""
 
-#: templates/wishlist/modules/wish.html:53
-msgid "Funding ended"
-msgstr "Sammlung beendet"
+#: webapp/backend/countries.py:58
+msgid "Cape Verde"
+msgstr ""
 
-#: templates/wishlist/modules/wish.html:63
-msgid "Share this wish with your friends and help us promote it!"
+#: webapp/backend/countries.py:59
+msgid "Curaçao"
 msgstr ""
-"Teile diesen Wunsch mit deinen Freunden und hilf diesen bekannter zu machen!"
 
-#: templates/wishlist/index.html:3 templates/index.html:3
-msgid "Home"
-msgstr "Startseite"
+#: webapp/backend/countries.py:60
+msgid "Chrismas Islands"
+msgstr ""
 
-#: templates/wishlist/donate.html:13 templates/wishlist/donate.html:23
-#: templates/wishlist/terms.html:3 templates/wishlist/terms.html:7
-#: templates/base.html:78
-msgid "Terms & Conditions"
-msgstr "Allgemeine Bedingungen"
+#: webapp/backend/countries.py:61
+msgid "Cyprus"
+msgstr ""
 
-#: templates/downloads-index.html:3
-msgid "Download Center"
-msgstr "Downloadcenter"
+#: webapp/backend/countries.py:62
+msgid "Czech Republic"
+msgstr ""
 
-#: templates/downloads-index.html:7
-msgid "IPFire Download Center"
-msgstr "IPFire Download-Center"
+#: webapp/backend/countries.py:63
+msgid "Germany"
+msgstr ""
 
-#: templates/downloads-index.html:30
-msgid "Available releases"
-msgstr "Verfügbare Releases"
+#: webapp/backend/countries.py:64
+#, fuzzy
+msgid "Djibouti"
+msgstr "Ãœber"
 
-#: templates/downloads-index.html:35 templates/downloads-older.html:25
-#: templates/tracker-torrents.html:32
-msgid "Release"
-msgstr "Release"
+#: webapp/backend/countries.py:65
+msgid "Denmark"
+msgstr ""
 
-#: templates/downloads-index.html:36
-msgid "Release type"
-msgstr "Release-Typ"
+#: webapp/backend/countries.py:66
+msgid "Dominica"
+msgstr ""
 
-#: templates/downloads-index.html:37
-msgid "Release date"
-msgstr "Veröffentlichungsdatum"
+#: webapp/backend/countries.py:67
+msgid "Dominican Republic"
+msgstr ""
 
-#: templates/downloads-index.html:48
-msgid "Stable"
-msgstr "Stabil"
+#: webapp/backend/countries.py:68
+msgid "Algeria"
+msgstr ""
 
-#: templates/downloads-index.html:50 templates/modules/menu.html:65
-#: templates/static/development.html:3 templates/static/development.html:8
-#: templates/static/getinvolved.html:153
-msgid "Development"
-msgstr "Entwicklung"
+#: webapp/backend/countries.py:69
+msgid "Ecuador"
+msgstr ""
 
-#: templates/admin-downloads-base.html:5
-msgid "Back to home"
-msgstr "Zurück zur Startseite"
+#: webapp/backend/countries.py:70
+msgid "Estonia"
+msgstr ""
 
-#: templates/admin-downloads-base.html:8 templates/admin-base.html:6
-msgid "Options"
-msgstr "Optionen"
+#: webapp/backend/countries.py:71
+msgid "Egypt"
+msgstr ""
 
-#: templates/admin-downloads-base.html:11
-msgid "Mirror stats"
-msgstr "Mirrorstatistiken"
+#: webapp/backend/countries.py:72
+msgid "Western Sahara"
+msgstr ""
 
-#: templates/downloads-older.html:3 templates/downloads-older.html:7
-msgid "Ancient downloads"
-msgstr "Veraltete Downloads"
+#: webapp/backend/countries.py:73
+msgid "Eritrea"
+msgstr ""
 
-#: templates/downloads-older.html:9 templates/downloads-all.html:9
-msgid ""
-"These are the ancient downloads of IPFire. They are just saved for "
-"historical reasons and should not be used in a productive environment."
+#: webapp/backend/countries.py:74
+msgid "Spain"
 msgstr ""
-"Dieses sind veraltete Downloads von IPFire. Sie wurden hier archiviert und "
-"sollten nicht in produktiven Umgebungen eingesetzt werden."
 
-#: templates/downloads-older.html:13
-msgid ""
-"Beware that these releases could lack possible security-fixes\tand so it is "
-"recommended to use the <em>latest</em> version."
+#: webapp/backend/countries.py:75
+msgid "Ethiopia"
 msgstr ""
-"Achtung! Diese Releases könnten potentielle Sicherheitsprobleme aufweisen. "
-"Daher wird empfohlen die <em>neueste</em> Version einzusetzen."
 
-#: templates/downloads-older.html:17 templates/downloads-all.html:13
-msgid "Go back to latest stable downloads."
-msgstr "Zurück zu den neuesten stabilen Downloads."
+#: webapp/backend/countries.py:76
+msgid "Finland"
+msgstr ""
 
-#: templates/downloads-older.html:26
-msgid "Published on"
-msgstr "Veröffentlicht am"
+#: webapp/backend/countries.py:77
+msgid "Fiji"
+msgstr ""
 
-#: templates/admin-login.html:3
-msgid "Please login"
-msgstr "Bitte einloggen"
+#: webapp/backend/countries.py:78
+msgid "Falkland Islands (Malvinas)"
+msgstr ""
 
-#: templates/mirrors-item.html:3
-#, python-format
-msgid "Mirror %s"
-msgstr "Mirror %s"
+#: webapp/backend/countries.py:79
+msgid "Federated States of Micronesia"
+msgstr ""
 
-#: templates/mirrors-item.html:8
-msgid "Up"
-msgstr "Verfügbar"
+#: webapp/backend/countries.py:80
+msgid "Faroe Islands"
+msgstr ""
 
-#: templates/mirrors-item.html:10
-msgid "Down"
-msgstr "Nicht verfügbar"
+#: webapp/backend/countries.py:81
+#, fuzzy
+msgid "France"
+msgstr "Abbrechen"
 
-#: templates/mirrors-item.html:12
-msgid "Out of sync"
-msgstr "Nicht synchronisiert"
+#: webapp/backend/countries.py:82
+msgid "Gabon"
+msgstr ""
 
-#: templates/mirrors-item.html:14 templates/fireinfo/profile-detail.html:74
-#: templates/fireinfo/profile-detail.html:82
-msgid "Unknown"
-msgstr "Unbekannt"
+#: webapp/backend/countries.py:83
+msgid "United Kingdom"
+msgstr ""
 
-#: templates/mirrors-item.html:22 templates/downloads-mirrors.html:50
-#: templates/fireinfo/profile-detail.html:231
-#: templates/download-mirror-detail.html:20 templates/admin-mirrors.html:15
-msgid "Last update"
-msgstr "Letztes Update"
+#: webapp/backend/countries.py:84
+msgid "Grenada"
+msgstr ""
 
-#: templates/mirrors-item.html:26 templates/downloads-mirrors.html:47
-#: templates/admin-mirrors-create.html:20
-#: templates/download-mirror-detail.html:16
-#: templates/admin-mirrors-details.html:21
-msgid "Owner"
-msgstr "Eigentümer"
+#: webapp/backend/countries.py:85
+msgid "Georgia"
+msgstr ""
 
-#: templates/mirrors-item.html:31
-msgid "Preferred for"
-msgstr "Bevorzugt für"
+#: webapp/backend/countries.py:86
+msgid "French Guiana"
+msgstr ""
 
-#: templates/mirrors-item.html:37
-msgid "Your distance to this mirror"
-msgstr "Deine Entfernung zu diesem Mirror"
+#: webapp/backend/countries.py:87
+msgid "Guersey"
+msgstr ""
 
-#: templates/mirrors-item.html:43
-msgid "Go to mirror"
-msgstr "Zum Mirror"
+#: webapp/backend/countries.py:88
+msgid "Ghana"
+msgstr ""
 
-#: templates/mirrors-item.html:48
-msgid "Mirror location"
-msgstr "Mirrorort"
+#: webapp/backend/countries.py:89
+msgid "Gibraltar"
+msgstr ""
 
-#: templates/mirrors-item.html:50
-#, python-format
-msgid "The mirror <em>%s</em> is located in %s."
-msgstr "Der Mirrorserver <em>%s</em> befindet sich in %s."
+#: webapp/backend/countries.py:90
+msgid "Greenland"
+msgstr ""
 
-#: templates/mirrors-item.html:58
-msgid "View larger map"
-msgstr "Größere Karte ansehen"
+#: webapp/backend/countries.py:91
+msgid "Gambia"
+msgstr ""
 
-#: templates/mirrors-item.html:63
-msgid "The location of the mirror server is estimated by the IP address."
+#: webapp/backend/countries.py:92
+msgid "Guinea"
 msgstr ""
-"Der Standort dieses Mirrorservers wurde anhand der IP-Adresse geschätzt."
 
-#: templates/mirrors-item.html:66
-msgid "The location of the mirror server could not be estimated."
-msgstr "Der Standort dieses Mirrorservers konnte nicht ermittelt werden."
+#: webapp/backend/countries.py:93
+msgid "Guadeloupe"
+msgstr ""
 
-#: templates/admin-downloads-mirrors.html:5 templates/admin-downloads.html:5
-msgid "Download statistics"
-msgstr "Downloadstatistiken"
+#: webapp/backend/countries.py:94
+msgid "Equatorial Guinea"
+msgstr ""
 
-#: templates/admin-downloads-mirrors.html:7
-msgid "Mirror load from today"
-msgstr "Mirrorauslastung von heute"
+#: webapp/backend/countries.py:95
+msgid "Greece"
+msgstr ""
 
-#: templates/admin-downloads-mirrors.html:10
-msgid "Mirror load"
-msgstr "Mirrorauslastung"
+#: webapp/backend/countries.py:96
+msgid "South Georgia and the South Sandwich Islands"
+msgstr ""
 
-#: templates/news-year.html:3 templates/news.html:3
-msgid "News"
-msgstr "Neuigkeiten"
+#: webapp/backend/countries.py:97
+msgid "Guatemala"
+msgstr ""
 
-#: templates/news-year.html:7
-#, python-format
-msgid "News from %(year)s"
-msgstr "Neuigkeiten aus %(year)s"
+#: webapp/backend/countries.py:98
+msgid "Guam"
+msgstr ""
 
-#: templates/tracker-torrent-detail.html:3
-#: templates/tracker-torrent-detail.html:18
-#: templates/modules/release-item.html:115
-msgid "Torrent download"
-msgstr "Torrent-Download"
+#: webapp/backend/countries.py:99
+msgid "Guinea-Bissau"
+msgstr ""
 
-#: templates/tracker-torrent-detail.html:15
-#: templates/modules/release-item.html:114
-msgid "Magnet link"
-msgstr "Magnet-Link"
+#: webapp/backend/countries.py:100
+msgid "Guyana"
+msgstr ""
 
-#: templates/tracker-torrent-detail.html:23 templates/tracker-torrents.html:34
-msgid "Peers"
-msgstr "Peers"
+#: webapp/backend/countries.py:101
+msgid "Hong Kong"
+msgstr ""
 
-#: templates/tracker-torrent-detail.html:28
-msgid "Seeds"
-msgstr "Seeds"
+#: webapp/backend/countries.py:102
+msgid "Heard Island and McDonald Islands"
+msgstr ""
 
-#: templates/sources.html:3
-msgid "Sources"
-msgstr "Quellen"
+#: webapp/backend/countries.py:103
+msgid "Honduras"
+msgstr ""
 
-#: templates/sources.html:6 templates/static/development.html:263
-msgid "Source Code"
-msgstr "Quellcode"
+#: webapp/backend/countries.py:104
+msgid "Croatia"
+msgstr ""
 
-#: templates/sources.html:9
-#, python-format
-msgid "There are %s source files on the server."
-msgstr "Es befinden sich %s Quelldateien auf dem Server."
+#: webapp/backend/countries.py:105
+msgid "Haiti"
+msgstr ""
 
-#: templates/news-author.html:7
-#, python-format
-msgid "%s's announcements"
-msgstr "%ss Ankündigungen"
+#: webapp/backend/countries.py:106
+msgid "Hungary"
+msgstr ""
 
-#: templates/admin-index.html:5
-msgid "Admin Area"
-msgstr "Adminbereich"
+#: webapp/backend/countries.py:107
+msgid "Indonesia"
+msgstr ""
 
-#: templates/admin-base.html:3
-msgid "IPFire Admin Area"
-msgstr "IPFire Adminbereich"
+#: webapp/backend/countries.py:108
+msgid "Ireland"
+msgstr ""
 
-#: templates/admin-base.html:9
-msgid "Accounts"
-msgstr "Accounts"
+#: webapp/backend/countries.py:109
+msgid "Israel"
+msgstr ""
 
-#: templates/admin-base.html:10 templates/modules/menu.html:110
-msgid "Mirrors"
-msgstr "Mirrors"
+#: webapp/backend/countries.py:110
+msgid "Isle of Man"
+msgstr ""
 
-#: templates/admin-base.html:11 templates/modules/menu.html:92
-msgid "Planet"
-msgstr "Planet"
+#: webapp/backend/countries.py:111
+msgid "India"
+msgstr ""
 
-#: templates/admin-base.html:12
-msgid "Downloads"
-msgstr "Downloads"
+#: webapp/backend/countries.py:112
+msgid "British Indian Ocean Territory"
+msgstr ""
 
-#: templates/admin-planet-compose.html:5 templates/admin-planet.html:8
-msgid "Compose new entry"
-msgstr "Neuen Beitrag erstellen"
+#: webapp/backend/countries.py:113
+msgid "Iraq"
+msgstr ""
 
-#: templates/admin-planet-compose.html:16 templates/admin-planet.html:14
-msgid "Title"
-msgstr "Titel"
+#: webapp/backend/countries.py:114
+msgid "Islamic Republic of Iran"
+msgstr ""
 
-#: templates/admin-planet-compose.html:33 templates/planet/posting.html:19
-msgid "Tags"
-msgstr "Tags"
+#: webapp/backend/countries.py:115
+msgid "Iceland"
+msgstr ""
 
-#: templates/admin-planet-compose.html:41
-msgid "Save"
-msgstr "Speichern"
+#: webapp/backend/countries.py:116
+msgid "Italy"
+msgstr ""
 
-#: templates/admin-planet-compose.html:42
-msgid "Preview"
-msgstr "Vorschau"
+#: webapp/backend/countries.py:117
+msgid "Jersey"
+msgstr ""
 
-#: templates/admin-planet-compose.html:43
-msgid "Cancel"
-msgstr "Abbrechen"
+#: webapp/backend/countries.py:118
+msgid "Jamaica"
+msgstr ""
 
-#: templates/download-splash.html:3 templates/download-splash.html:7
-msgid "Thanks for downloading IPFire!"
-msgstr "Vielen Dank, für das Herunterladen von IPFire! "
+#: webapp/backend/countries.py:119
+msgid "Jordan"
+msgstr ""
 
-#: templates/download-splash.html:61
-msgid "Next steps"
-msgstr "Nächste Schritte"
+#: webapp/backend/countries.py:120
+msgid "Japan"
+msgstr ""
 
-#: templates/download-splash.html:66
-msgid "Install IPFire"
-msgstr "IPFire installieren"
+#: webapp/backend/countries.py:121
+msgid "Kenya"
+msgstr ""
 
-#: templates/download-splash.html:82
-msgid "Access documentation"
-msgstr "Dokumentation lesen"
+#: webapp/backend/countries.py:122
+msgid "Kyrgyzstan"
+msgstr ""
 
-#: templates/download-splash.html:100
-msgid "Join the community"
-msgstr "Der Community beitreten"
+#: webapp/backend/countries.py:123
+msgid "Cambodia"
+msgstr ""
 
-#: templates/downloads-development.html:3
-#: templates/downloads-development.html:6
-msgid "Development Downloads"
-msgstr "Development Downloads"
+#: webapp/backend/countries.py:124
+msgid "Kiribati"
+msgstr ""
 
-#: templates/error-500.html:6
-msgid "Detailed information"
-msgstr "Mehr Informationen"
+#: webapp/backend/countries.py:125
+msgid "Comoros"
+msgstr ""
 
-#: templates/admin-planet.html:5
-msgid "Planet Administrator"
-msgstr "Planet Administrator"
+#: webapp/backend/countries.py:126
+msgid "Saint Kitts and Nevis"
+msgstr ""
 
-#: templates/admin-planet.html:13
-msgid "Author"
-msgstr "Autor"
+#: webapp/backend/countries.py:127
+msgid "Democratic People's Republic of Korea"
+msgstr ""
 
-#: templates/admin-planet.html:21 templates/admin-mirrors.html:24
-#: templates/admin-accounts.html:20
-msgid "Edit"
-msgstr "Bearbeiten"
+#: webapp/backend/countries.py:128
+msgid "Republic of Korea"
+msgstr ""
 
-#: templates/downloads-mirrors.html:3 templates/download-mirror-detail.html:3
-msgid "Mirror-Server"
-msgstr "Mirrorserver"
+#: webapp/backend/countries.py:129
+msgid "Kuwait"
+msgstr ""
 
-#: templates/downloads-mirrors.html:8 templates/download-mirror-detail.html:8
-msgid "IPFire Mirrors"
-msgstr "IPFire Mirrors"
+#: webapp/backend/countries.py:130
+msgid "Cayman Islands"
+msgstr ""
 
-#: templates/downloads-mirrors.html:48 templates/admin-mirrors-create.html:12
-#: templates/download-mirror-detail.html:12 templates/admin-mirrors.html:14
-#: templates/admin-mirrors-details.html:13
-msgid "Hostname"
-msgstr "Hostname"
+#: webapp/backend/countries.py:131
+msgid "Kazakhstan"
+msgstr ""
 
-#: templates/downloads-mirrors.html:49 templates/admin-mirrors-create.html:24
-#: templates/admin-mirrors-details.html:25
-msgid "Location"
-msgstr "Standort"
+#: webapp/backend/countries.py:132
+msgid "Lao People's Democratic Public"
+msgstr ""
 
-#: templates/downloads-mirrors.html:63
-msgid "details"
-msgstr "Details"
+#: webapp/backend/countries.py:133
+msgid "Lebanon"
+msgstr ""
 
-#: templates/fireinfo/stats.html:3 templates/modules/menu.html:136
-msgid "Statistics"
-msgstr "Statistiken"
+#: webapp/backend/countries.py:134
+msgid "Saint Lucia"
+msgstr ""
 
-#: templates/fireinfo/stats.html:7
-msgid "fireinfo statistics"
-msgstr "Fireinfo Statistiken"
+#: webapp/backend/countries.py:135
+msgid "Liechtenstein"
+msgstr ""
 
-#: templates/fireinfo/stats.html:17
-msgid "IPFire versions"
-msgstr "IPFire-Versionen"
+#: webapp/backend/countries.py:136
+msgid "Sri Lanka"
+msgstr ""
 
-#: templates/fireinfo/stats.html:38 templates/fireinfo/stats-geo.html:3
-#: templates/fireinfo/stats-geo.html:7
-msgid "Geo locations"
-msgstr "Geographischer Standort"
+#: webapp/backend/countries.py:137
+msgid "Liberia"
+msgstr ""
 
-#: templates/fireinfo/stats.html:59 templates/fireinfo/stats-cpus.html:7
-msgid "Hardware: CPUs"
-msgstr "Hardware: CPUs"
+#: webapp/backend/countries.py:138
+msgid "Lesotho"
+msgstr ""
 
-#: templates/fireinfo/stats.html:73
-msgid "CPU features"
-msgstr "CPU-Funktionen"
+#: webapp/backend/countries.py:139
+msgid "Lithuania"
+msgstr ""
 
-#: templates/fireinfo/stats.html:85
-msgid "Hardware: Memory"
-msgstr "Hardware: Arbeitsspeicher"
+#: webapp/backend/countries.py:140
+msgid "Luxembourg"
+msgstr ""
 
-#: templates/fireinfo/stats.html:106 templates/fireinfo/stats-network.html:3
-#: templates/fireinfo/profile-detail.html:95
-msgid "Network"
-msgstr "Netzwerk"
+#: webapp/backend/countries.py:141
+msgid "Latvia"
+msgstr ""
 
-#: templates/fireinfo/stats.html:127 templates/fireinfo/stats-virtual.html:3
-#: templates/base-feature.html:65 templates/static/features.html:1179
-#: templates/static/features.html:1404
-msgid "Virtualization"
-msgstr "Virtualisierung"
+#: webapp/backend/countries.py:142
+msgid "Libya"
+msgstr ""
 
-#: templates/fireinfo/stats-network.html:7
-msgid "Network configuration"
-msgstr "Netzwerkkonfiguration"
+#: webapp/backend/countries.py:143
+msgid "Morocco"
+msgstr ""
 
-#: templates/fireinfo/stats-oses.html:3
-#: templates/fireinfo/profile-detail.html:12
-msgid "Operating system"
-msgstr "Betriebssystem"
+#: webapp/backend/countries.py:144
+msgid "Monaco"
+msgstr ""
 
-#: templates/fireinfo/stats-oses.html:7
-msgid "Releases"
-msgstr "Releases"
+#: webapp/backend/countries.py:145
+msgid "Republic of Moldova"
+msgstr ""
 
-#: templates/fireinfo/stats-oses.html:18
-msgid "Architectures"
-msgstr "Architekturen"
+#: webapp/backend/countries.py:146
+msgid "Montenegro"
+msgstr ""
 
-#: templates/fireinfo/stats-oses.html:25
-msgid "Kernels"
-msgstr "Kernel"
+#: webapp/backend/countries.py:147
+msgid "Saint Martin (French Part)"
+msgstr ""
 
-#: templates/fireinfo/profile-notfound.html:3
-#: templates/fireinfo/profile-notfound.html:8
-msgid "Profile not found"
-msgstr "Profil nicht gefunden"
+#: webapp/backend/countries.py:148
+msgid "Madagascar"
+msgstr ""
 
-#: templates/fireinfo/stats-virtual.html:7
-msgid "Virtualization support"
-msgstr "Virtualisierungsunterstützung"
+#: webapp/backend/countries.py:149
+msgid "Marshall Islands"
+msgstr ""
 
-#: templates/fireinfo/stats-virtual.html:35
-msgid "Hypervisors"
-msgstr "Hypervisoren"
+#: webapp/backend/countries.py:150
+msgid "The former Yugoslav Republic of Macedonia"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:3
-#: templates/fireinfo/profile-detail.html:7
-msgid "Profile"
-msgstr "Profil"
+#: webapp/backend/countries.py:151
+msgid "Mali"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:16
-msgid "Version"
-msgstr "Version"
+#: webapp/backend/countries.py:152
+msgid "Myanmar"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:24
-msgid "Architecture"
-msgstr "Architektur"
+#: webapp/backend/countries.py:153
+msgid "Mongolia"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:32
-msgid "Kernel version"
-msgstr "Kernelversion"
+#: webapp/backend/countries.py:154
+msgid "Macao"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:43
-msgid "Hypervisor"
-msgstr "Hypervisor"
+#: webapp/backend/countries.py:155
+msgid "Northern Mariana Islands"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:45
-msgid "This machine is running in a virtual environment."
-msgstr "Diese Maschine läuft in einer virtuellen Umgebung."
+#: webapp/backend/countries.py:156
+msgid "Martinique"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:51
-#: templates/fireinfo/profile-detail.html:71
-#: templates/fireinfo/profile-detail.html:130
-msgid "Vendor"
-msgstr "Hersteller"
+#: webapp/backend/countries.py:157
+msgid "Mauritania"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:59
-msgid "Type"
-msgstr "Typ"
+#: webapp/backend/countries.py:158
+msgid "Montserrat"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:67
-msgid "Hardware vendor"
-msgstr "Hardwarehersteller"
+#: webapp/backend/countries.py:159
+msgid "Malta"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:79
-#: templates/fireinfo/profile-detail.html:138
-msgid "Model"
-msgstr "Modell"
+#: webapp/backend/countries.py:160
+msgid "Mauritius"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:97
-msgid "Enabled network zones"
-msgstr "Eingeschaltete Netzwerkzonen"
+#: webapp/backend/countries.py:161
+msgid "Maldives"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:119 templates/base-feature.html:62
-#: templates/static/features.html:1104 templates/static/features.html:1399
-msgid "Hardware"
-msgstr "Hardware"
+#: webapp/backend/countries.py:162
+msgid "Malawi"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:125
-msgid "CPU"
-msgstr "CPU"
+#: webapp/backend/countries.py:163
+msgid "Mexico"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:141
-msgid "Not available"
-msgstr "Nicht verfügbar"
+#: webapp/backend/countries.py:164
+msgid "Malaysia"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:146
-msgid "Cores"
-msgstr "Kerne"
+#: webapp/backend/countries.py:165
+msgid "Mozambique"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:154
-#: templates/fireinfo/stats-cpus.html:36
-msgid "Speed"
-msgstr "Geschwindigkeit"
+#: webapp/backend/countries.py:166
+msgid "Namibia"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:166
-msgid "Supported features"
-msgstr "Unterstützte Funktionen"
+#: webapp/backend/countries.py:167
+msgid "New Caledonia"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:170
-msgid "64 bit capable"
-msgstr "64-Bit fähig"
+#: webapp/backend/countries.py:168
+msgid "Niger"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:174
-msgid "PAE capable"
-msgstr "PAE fähig"
+#: webapp/backend/countries.py:169
+msgid "Norfolk Island"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:178
-msgid "VT-x/AMD-V"
-msgstr "VT-x/AMD-V"
+#: webapp/backend/countries.py:170
+msgid "Nigeria"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:190
-msgid "Memory size"
-msgstr "Arbeitsspeichergröße"
+#: webapp/backend/countries.py:171
+msgid "Nicaragua"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:198
-msgid "System disk size"
-msgstr "Systemplattengröße"
+#: webapp/backend/countries.py:172
+msgid "Netherlands"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:211
-msgid "Peripherial devices"
-msgstr "Peripheriegeräte"
+#: webapp/backend/countries.py:173
+msgid "Norway"
+msgstr ""
 
-#: templates/fireinfo/profile-detail.html:216
-msgid "Signature images"
-msgstr "Signaturbilder"
+#: webapp/backend/countries.py:174
+msgid "Nepal"
+msgstr ""
 
-#: templates/fireinfo/base.html:6 templates/modules/menu.html:98
-msgid "Fireinfo"
-msgstr "Fireinfo"
+#: webapp/backend/countries.py:175
+msgid "Nauru"
+msgstr ""
 
-#: templates/fireinfo/base.html:7
-msgid "A hardware data collection tool for IPFire"
-msgstr "Ein Hardwaredatensammeltool für IPFire"
+#: webapp/backend/countries.py:176
+msgid "Niue"
+msgstr ""
 
-#: templates/fireinfo/stats-cpus.html:3
-msgid "Processors"
-msgstr "Prozessoren"
+#: webapp/backend/countries.py:177
+msgid "New Zealand"
+msgstr ""
 
-#: templates/fireinfo/stats-cpus.html:12
-msgid "See statistics about common CPU flags"
-msgstr "Statistiken Ã¼ber CPU-Funktionen"
+#: webapp/backend/countries.py:178
+msgid "Oman"
+msgstr ""
 
-#: templates/fireinfo/stats-cpus.html:19
-msgid "Vendors"
-msgstr "Hersteller"
+#: webapp/backend/countries.py:179
+msgid "Panama"
+msgstr ""
 
-#: templates/fireinfo/stats-cpus.html:38
-#, python-format
-msgid ""
-"The average speed of all systems in the database is: <strong>%.2f MHz</"
-"strong>."
+#: webapp/backend/countries.py:180
+msgid "Peru"
 msgstr ""
-"Die durchschnittliche Geschwindigkeit aller System ist: <strong>%.2f MHz</"
-"strong>."
 
-#: templates/fireinfo/stats-cpus.html:40
-#, python-format
-msgid "All together, there are <strong>%s bogomips</strong> out there."
-msgstr "Zusammengenomen sind es <strong>%s Bogomips</strong>."
+#: webapp/backend/countries.py:181
+msgid "French Polynesia"
+msgstr ""
 
-#: templates/fireinfo/stats-cpus.html:48
-msgid "CPU core counter"
-msgstr "CPU-Kerne"
+#: webapp/backend/countries.py:182
+msgid "Papua New Guinea"
+msgstr ""
 
-#: templates/fireinfo/stats-cpus.html:50
-msgid ""
-"See a breakdown of the CPU cores that are installed on the IPFire systems."
-msgstr "Eine Ãœbersicht Ã¼ber die CPU Kerne, der IPFire Systeme."
+#: webapp/backend/countries.py:183
+msgid "Philipplines"
+msgstr ""
 
-#: templates/fireinfo/index.html:52
-msgid "Show"
-msgstr "Anzeigen"
+#: webapp/backend/countries.py:184
+msgid "Pakistan"
+msgstr ""
 
-#: templates/fireinfo/model-detail.html:11
-#, python-format
-msgid "This device is installed on approximately %.2f%% of all systems."
-msgstr "Dieses Gerät ist in etwa %.2f%% aller Systeme installiert."
+#: webapp/backend/countries.py:185
+msgid "Poland"
+msgstr ""
 
-#: templates/fireinfo/model-detail.html:14
-msgid "Feedback"
-msgstr "Feedback"
+#: webapp/backend/countries.py:186
+msgid "Saint Pierre and Miquelon"
+msgstr ""
 
-#: templates/fireinfo/model-detail.html:28
-msgid "Go to the wiki"
-msgstr "Zum Wiki"
+#: webapp/backend/countries.py:187
+msgid "Pitcairn"
+msgstr ""
 
-#: templates/fireinfo/stats-memory.html:3
-#: templates/fireinfo/stats-memory.html:7
-msgid "Memory"
-msgstr "Arbeitsspeicher"
+#: webapp/backend/countries.py:188
+msgid "Puerto Rico"
+msgstr ""
 
-#: templates/fireinfo/stats-memory.html:13
-#, python-format
-msgid ""
-"The average amount of memory of all systems in the database is: <strong>%.2f "
-"MB</strong>."
+#: webapp/backend/countries.py:189
+msgid "Palestinian Territory, occupied"
 msgstr ""
-"Die durchschnittliche Menge an Arbeitsspeicher aller System ist: <strong>"
-"%.2f MB</strong>."
 
-#: templates/fireinfo/stats-geo.html:28
-msgid "Language selection"
-msgstr "Sprachauswahl"
+#: webapp/backend/countries.py:190
+msgid "Portugal"
+msgstr ""
 
-#: templates/fireinfo/stats-cpu-flags.html:3
-msgid "CPU flags"
-msgstr "CPU Flags"
+#: webapp/backend/countries.py:191
+msgid "Palau"
+msgstr ""
 
-#: templates/fireinfo/stats-cpu-flags.html:7
-msgid "Processor flags"
-msgstr "Prozessor-Flags"
+#: webapp/backend/countries.py:192
+msgid "Paraguay"
+msgstr ""
 
-#: templates/fireinfo/stats-cpu-flags.html:30
-msgid "CPUs that support 64 bits"
-msgstr "CPUs, die 64 Bit unterstützen"
+#: webapp/backend/countries.py:193
+msgid "Qatar"
+msgstr ""
 
-#: templates/fireinfo/stats-cpu-flags.html:46
-msgid "CPUs with PAE"
-msgstr "CPUs mit PAE"
+#: webapp/backend/countries.py:194
+msgid "Réunion"
+msgstr ""
 
-#: templates/fireinfo/stats-cpu-flags.html:62
-msgid "CPUs that support virtualization"
-msgstr "CPUs, die Virtualisierung unterstützen"
+#: webapp/backend/countries.py:195
+msgid "Romania"
+msgstr ""
 
-#: templates/fireinfo/stats-admin.html:3
-msgid "Admin"
-msgstr "Administrator"
+#: webapp/backend/countries.py:196
+msgid "Serbia"
+msgstr ""
 
-#: templates/fireinfo/stats-admin.html:11
-msgid "Sending profiles"
-msgstr "Senden Profile"
+#: webapp/backend/countries.py:197
+msgid "Russian Federation"
+msgstr ""
 
-#: templates/fireinfo/stats-admin.html:19
-msgid "Archive size"
-msgstr "Archivgröße"
+#: webapp/backend/countries.py:198
+msgid "Rwanda"
+msgstr ""
 
-#: templates/downloads.html:3 templates/downloads.html:8
-msgid "Get IPFire"
-msgstr "IPFire herunterladen"
+#: webapp/backend/countries.py:199
+msgid "Saudi Arabia"
+msgstr ""
 
-#: templates/downloads.html:10
-msgid "IPFire is completely free to download and use"
+#: webapp/backend/countries.py:200
+msgid "Solomon Islands"
+msgstr ""
+
+#: webapp/backend/countries.py:201
+msgid "Seychelles"
+msgstr ""
+
+#: webapp/backend/countries.py:202
+msgid "Sudan"
+msgstr ""
+
+#: webapp/backend/countries.py:203
+msgid "Sweden"
+msgstr ""
+
+#: webapp/backend/countries.py:204
+msgid "Singapore"
+msgstr ""
+
+#: webapp/backend/countries.py:205
+msgid "Saint Helena, Ascension and Tristan Da Cunha"
+msgstr ""
+
+#: webapp/backend/countries.py:206
+msgid "Slovenia"
+msgstr ""
+
+#: webapp/backend/countries.py:207
+msgid "Svalbard and Jan Mayen"
+msgstr ""
+
+#: webapp/backend/countries.py:208
+msgid "Slovakia"
+msgstr ""
+
+#: webapp/backend/countries.py:209
+msgid "Sierra Leone"
+msgstr ""
+
+#: webapp/backend/countries.py:210
+msgid "San Marino"
+msgstr ""
+
+#: webapp/backend/countries.py:211
+msgid "Senegal"
+msgstr ""
+
+#: webapp/backend/countries.py:212
+msgid "Somalia"
+msgstr ""
+
+#: webapp/backend/countries.py:213
+msgid "Suriname"
+msgstr ""
+
+#: webapp/backend/countries.py:214
+msgid "South Sudan"
+msgstr ""
+
+#: webapp/backend/countries.py:215
+msgid "Sao Tome and Principe"
+msgstr ""
+
+#: webapp/backend/countries.py:216
+msgid "El Salvador"
+msgstr ""
+
+#: webapp/backend/countries.py:217
+msgid "Sint Maarten (Dutch Part)"
+msgstr ""
+
+#: webapp/backend/countries.py:218
+msgid "Syrian Arab Republic"
+msgstr ""
+
+#: webapp/backend/countries.py:219
+msgid "Swaziland"
+msgstr ""
+
+#: webapp/backend/countries.py:220
+msgid "Turks and Caicos Islands"
+msgstr ""
+
+#: webapp/backend/countries.py:221
+msgid "Chad"
+msgstr ""
+
+#: webapp/backend/countries.py:222
+msgid "French Southern Territories"
+msgstr ""
+
+#: webapp/backend/countries.py:223
+msgid "Togo"
+msgstr ""
+
+#: webapp/backend/countries.py:224
+msgid "Thailand"
+msgstr ""
+
+#: webapp/backend/countries.py:225
+msgid "Tajikistan"
+msgstr ""
+
+#: webapp/backend/countries.py:226
+msgid "Tokelau"
+msgstr ""
+
+#: webapp/backend/countries.py:227
+msgid "Timor-Leste"
+msgstr ""
+
+#: webapp/backend/countries.py:228
+msgid "Turkmenistan"
+msgstr ""
+
+#: webapp/backend/countries.py:229
+msgid "Tunisia"
+msgstr ""
+
+#: webapp/backend/countries.py:230
+msgid "Tonga"
+msgstr ""
+
+#: webapp/backend/countries.py:231
+msgid "Turkey"
+msgstr ""
+
+#: webapp/backend/countries.py:232
+msgid "Trinidad and Tobago"
+msgstr ""
+
+#: webapp/backend/countries.py:233
+msgid "Tuvalu"
+msgstr ""
+
+#: webapp/backend/countries.py:234
+msgid "Taiwan, Province of China"
+msgstr ""
+
+#: webapp/backend/countries.py:235
+msgid "United Republic of Tanzania"
+msgstr ""
+
+#: webapp/backend/countries.py:236
+msgid "Ukraine"
+msgstr ""
+
+#: webapp/backend/countries.py:237
+msgid "Uganda"
+msgstr ""
+
+#: webapp/backend/countries.py:238
+msgid "Unites States minor outlying islands"
+msgstr ""
+
+#: webapp/backend/countries.py:239
+msgid "Unites States"
+msgstr ""
+
+#: webapp/backend/countries.py:240
+msgid "Uruguay"
+msgstr ""
+
+#: webapp/backend/countries.py:241
+msgid "Uzbekistan"
+msgstr ""
+
+#: webapp/backend/countries.py:242
+msgid "Vatican City State"
+msgstr ""
+
+#: webapp/backend/countries.py:243
+msgid "Saint Vincent and the Grenadines"
+msgstr ""
+
+#: webapp/backend/countries.py:244
+msgid "Bolivarian Republic of Venezuela"
+msgstr ""
+
+#: webapp/backend/countries.py:245
+msgid "Virgin Islands, British"
+msgstr ""
+
+#: webapp/backend/countries.py:246
+msgid "Virgin Islands, U.S."
+msgstr ""
+
+#: webapp/backend/countries.py:247
+msgid "Viet Nam"
+msgstr ""
+
+#: webapp/backend/countries.py:248
+msgid "Vanuatu"
+msgstr ""
+
+#: webapp/backend/countries.py:249
+msgid "Wallis and Futuna"
+msgstr ""
+
+#: webapp/backend/countries.py:250
+msgid "Samoa"
+msgstr ""
+
+#: webapp/backend/countries.py:251
+msgid "Yemen"
+msgstr ""
+
+#: webapp/backend/countries.py:252
+msgid "Mayotte"
+msgstr ""
+
+#: webapp/backend/countries.py:253
+msgid "South Africa"
+msgstr ""
+
+#: webapp/backend/countries.py:254
+msgid "Zambia"
+msgstr ""
+
+#: webapp/backend/countries.py:255
+msgid "Zimbabwe"
+msgstr ""
+
+#: webapp/backend/wishlist.py:207
+msgid "Checkout this crowdfunding wish from #ipfire:"
+msgstr ""
+
+#: webapp/backend/releases.py:89
+msgid "Image for the armv5tel architecture"
+msgstr "Image für die armv5tel-Architektur"
+
+#: webapp/backend/releases.py:90
+msgid "armv5tel image for boards with serial console"
+msgstr "armv5tel-Image für Boards mit serieller Konsole"
+
+#: webapp/backend/releases.py:91
+msgid "Installable CD image"
+msgstr "Bootbares CD-Image"
+
+#: webapp/backend/releases.py:92
+msgid "Torrent file"
+msgstr "Torrentdatei"
+
+#: webapp/backend/releases.py:93
+msgid "Flash image"
+msgstr "Flash-Image"
+
+#: webapp/backend/releases.py:94
+msgid "Alix image"
+msgstr "Alix-Image"
+
+#: webapp/backend/releases.py:95
+msgid "USB FDD Image"
+msgstr "USB-FDD-Image"
+
+#: webapp/backend/releases.py:96
+msgid "USB HDD Image"
+msgstr "USB-HDD-Image"
+
+#: webapp/backend/releases.py:97
+msgid "Pregenerated Xen image"
+msgstr "Vorgefertigtes Xen-Image"
+
+#: webapp/backend/releases.py:103 webapp/backend/releases.py:143
+msgid "Unknown image type"
+msgstr "Unbekanntes Imageformat"
+
+#: webapp/backend/releases.py:129
+msgid "This image runs on many ARM-based boards"
+msgstr "Dieses Image läuft auf vielen ARM-basierten Boards"
+
+#: webapp/backend/releases.py:130
+msgid "This image runs on ARM boards with a serial console"
+msgstr "Dieses Image läuft auf ARM-Boards mit serieller Konsole"
+
+#: webapp/backend/releases.py:131
+msgid "Use this image to burn a CD and install IPFire from it."
+msgstr ""
+"Nutze dieses Image um eine CD zu erstellen und IPFire von dieser zu "
+"installieren."
+
+#: webapp/backend/releases.py:132
+msgid "Download the CD image from the torrent network."
+msgstr "CD-Image mit aus dem Torrentnetzwerk laden"
+
+#: webapp/backend/releases.py:133
+msgid "An image that is meant to run on embedded devices."
+msgstr "Ein Image, das für eingebettete Systeme optimiert ist."
+
+#: webapp/backend/releases.py:134
+msgid "Flash image where a serial console is enabled by default."
+msgstr "Image, bei welchem die serielle Konsole eingeschaltet ist."
+
+#: webapp/backend/releases.py:135
+msgid "Install IPFire from a floppy-formated USB key."
+msgstr "IPFire von einem Floppy-formatiertem USB-Stick installieren."
+
+#: webapp/backend/releases.py:136
+msgid "If the floppy image doesn't work, use this image instead."
+msgstr "Wenn das Floppy-Image nicht funktioniert, dieses verwenden."
+
+#: webapp/backend/releases.py:137
+msgid "A ready-to-run image for Xen."
+msgstr "Ein fertiges Image für Xen."
+
+#: webapp/__init__.py:311
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:235
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:235
+msgid "January"
+msgstr "Januar"
+
+#: webapp/__init__.py:313
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:235
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:235
+msgid "February"
+msgstr "Februar"
+
+#: webapp/__init__.py:315
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:235
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:235
+msgid "March"
+msgstr "März"
+
+#: webapp/__init__.py:317
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:235
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:235
+msgid "April"
+msgstr "April"
+
+#: webapp/__init__.py:319
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:236
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:236
+msgid "May"
+msgstr "Mai"
+
+#: webapp/__init__.py:321
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:236
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:236
+msgid "June"
+msgstr "Juni"
+
+#: webapp/__init__.py:323
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:236
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:236
+msgid "July"
+msgstr "Juli"
+
+#: webapp/__init__.py:325
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:236
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:236
+msgid "August"
+msgstr "August"
+
+#: webapp/__init__.py:327
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:237
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:237
+msgid "September"
+msgstr "September"
+
+#: webapp/__init__.py:329
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:237
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:237
+msgid "October"
+msgstr "Oktober"
+
+#: webapp/__init__.py:331
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:237
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:237
+msgid "November"
+msgstr "November"
+
+#: webapp/__init__.py:333
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:237
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:237
+msgid "December"
+msgstr "Dezember"
+
+#: webapp/ui_modules.py:114 webapp/handlers_news.py:43
+msgid "Unknown author"
+msgstr "Unbekannter Autor"
+
+#: webapp/ui_modules.py:265
+#, python-format
+msgid "%s to %s"
+msgstr "%s nach %s"
+
+#: templates/downloads.html:3 templates/downloads.html:8
+msgid "Get IPFire"
+msgstr "IPFire herunterladen"
+
+#: templates/downloads.html:10
+msgid "IPFire is completely free to download and use"
 msgstr "IPFire darf kostenlos heruntergeladen und genutzt werden"
 
-#: templates/downloads.html:23
-msgid "More download options"
-msgstr "Weitere Downloadoptionen"
+#: templates/downloads.html:23
+msgid "More download options"
+msgstr "Weitere Downloadoptionen"
+
+#: templates/downloads.html:55
+msgid "Get yourself involved"
+msgstr "Selbst mitmachen"
+
+#: templates/news-year.html:3 templates/news.html:3
+msgid "News"
+msgstr "Neuigkeiten"
+
+#: templates/news-year.html:7
+#, python-format
+msgid "News from %(year)s"
+msgstr "Neuigkeiten aus %(year)s"
+
+#: templates/admin-index.html:5
+msgid "Admin Area"
+msgstr "Adminbereich"
+
+#: templates/fireinfo/stats.html:3 templates/modules/menu.html:136
+msgid "Statistics"
+msgstr "Statistiken"
+
+#: templates/fireinfo/stats.html:7
+msgid "fireinfo statistics"
+msgstr "Fireinfo Statistiken"
+
+#: templates/fireinfo/stats.html:17
+msgid "IPFire versions"
+msgstr "IPFire-Versionen"
+
+#: templates/fireinfo/stats.html:38 templates/fireinfo/stats-geo.html:3
+#: templates/fireinfo/stats-geo.html:7
+msgid "Geo locations"
+msgstr "Geographischer Standort"
+
+#: templates/fireinfo/stats.html:59 templates/fireinfo/stats-cpus.html:7
+msgid "Hardware: CPUs"
+msgstr "Hardware: CPUs"
+
+#: templates/fireinfo/stats.html:73
+msgid "CPU features"
+msgstr "CPU-Funktionen"
+
+#: templates/fireinfo/stats.html:85
+msgid "Hardware: Memory"
+msgstr "Hardware: Arbeitsspeicher"
+
+#: templates/fireinfo/stats.html:106 templates/fireinfo/stats-network.html:3
+#: templates/fireinfo/profile-detail.html:95
+msgid "Network"
+msgstr "Netzwerk"
+
+#: templates/fireinfo/stats.html:127 templates/fireinfo/stats-virtual.html:3
+#: templates/base-feature.html:65 templates/static/features.html:1179
+#: templates/static/features.html:1404
+msgid "Virtualization"
+msgstr "Virtualisierung"
+
+#: templates/fireinfo/stats-memory.html:3
+#: templates/fireinfo/stats-memory.html:7
+msgid "Memory"
+msgstr "Arbeitsspeicher"
+
+#: templates/fireinfo/stats-memory.html:13
+#, python-format
+msgid ""
+"The average amount of memory of all systems in the database is: <strong>%.2f "
+"MB</strong>."
+msgstr ""
+"Die durchschnittliche Menge an Arbeitsspeicher aller System ist: <strong>"
+"%.2f MB</strong>."
+
+#: templates/fireinfo/model-detail.html:11
+#, python-format
+msgid "This device is installed on approximately %.2f%% of all systems."
+msgstr "Dieses Gerät ist in etwa %.2f%% aller Systeme installiert."
+
+#: templates/fireinfo/model-detail.html:14
+msgid "Feedback"
+msgstr "Feedback"
+
+#: templates/fireinfo/model-detail.html:28
+msgid "Go to the wiki"
+msgstr "Zum Wiki"
+
+#: templates/fireinfo/stats-admin.html:3
+msgid "Admin"
+msgstr "Administrator"
+
+#: templates/fireinfo/stats-admin.html:11
+msgid "Sending profiles"
+msgstr "Senden Profile"
+
+#: templates/fireinfo/stats-admin.html:19
+msgid "Archive size"
+msgstr "Archivgröße"
+
+#: templates/fireinfo/profile-notfound.html:3
+#: templates/fireinfo/profile-notfound.html:8
+msgid "Profile not found"
+msgstr "Profil nicht gefunden"
+
+#: templates/fireinfo/stats-cpu-flags.html:3
+msgid "CPU flags"
+msgstr "CPU Flags"
+
+#: templates/fireinfo/stats-cpu-flags.html:7
+msgid "Processor flags"
+msgstr "Prozessor-Flags"
+
+#: templates/fireinfo/stats-cpu-flags.html:30
+msgid "CPUs that support 64 bits"
+msgstr "CPUs, die 64 Bit unterstützen"
+
+#: templates/fireinfo/stats-cpu-flags.html:46
+msgid "CPUs with PAE"
+msgstr "CPUs mit PAE"
+
+#: templates/fireinfo/stats-cpu-flags.html:62
+msgid "CPUs that support virtualization"
+msgstr "CPUs, die Virtualisierung unterstützen"
+
+#: templates/fireinfo/stats-virtual.html:7
+msgid "Virtualization support"
+msgstr "Virtualisierungsunterstützung"
+
+#: templates/fireinfo/stats-virtual.html:35
+msgid "Hypervisors"
+msgstr "Hypervisoren"
+
+#: templates/fireinfo/stats-oses.html:3
+#: templates/fireinfo/profile-detail.html:12
+msgid "Operating system"
+msgstr "Betriebssystem"
+
+#: templates/fireinfo/stats-oses.html:7
+msgid "Releases"
+msgstr "Releases"
+
+#: templates/fireinfo/stats-oses.html:18
+msgid "Architectures"
+msgstr "Architekturen"
+
+#: templates/fireinfo/stats-oses.html:25
+msgid "Kernels"
+msgstr "Kernel"
+
+#: templates/fireinfo/stats-cpus.html:3
+msgid "Processors"
+msgstr "Prozessoren"
+
+#: templates/fireinfo/stats-cpus.html:12
+msgid "See statistics about common CPU flags"
+msgstr "Statistiken Ã¼ber CPU-Funktionen"
+
+#: templates/fireinfo/stats-cpus.html:19
+msgid "Vendors"
+msgstr "Hersteller"
+
+#: templates/fireinfo/stats-cpus.html:36
+#: templates/fireinfo/profile-detail.html:154
+msgid "Speed"
+msgstr "Geschwindigkeit"
+
+#: templates/fireinfo/stats-cpus.html:38
+#, python-format
+msgid ""
+"The average speed of all systems in the database is: <strong>%.2f MHz</"
+"strong>."
+msgstr ""
+"Die durchschnittliche Geschwindigkeit aller System ist: <strong>%.2f MHz</"
+"strong>."
+
+#: templates/fireinfo/stats-cpus.html:40
+#, python-format
+msgid "All together, there are <strong>%s bogomips</strong> out there."
+msgstr "Zusammengenomen sind es <strong>%s Bogomips</strong>."
+
+#: templates/fireinfo/stats-cpus.html:48
+msgid "CPU core counter"
+msgstr "CPU-Kerne"
+
+#: templates/fireinfo/stats-cpus.html:50
+msgid ""
+"See a breakdown of the CPU cores that are installed on the IPFire systems."
+msgstr "Eine Ãœbersicht Ã¼ber die CPU Kerne, der IPFire Systeme."
+
+#: templates/fireinfo/stats-network.html:7
+msgid "Network configuration"
+msgstr "Netzwerkkonfiguration"
+
+#: templates/fireinfo/index.html:52
+msgid "Show"
+msgstr "Anzeigen"
+
+#: templates/fireinfo/stats-geo.html:28
+msgid "Language selection"
+msgstr "Sprachauswahl"
+
+#: templates/fireinfo/profile-detail.html:3
+#: templates/fireinfo/profile-detail.html:7
+msgid "Profile"
+msgstr "Profil"
+
+#: templates/fireinfo/profile-detail.html:16
+msgid "Version"
+msgstr "Version"
+
+#: templates/fireinfo/profile-detail.html:24
+msgid "Architecture"
+msgstr "Architektur"
+
+#: templates/fireinfo/profile-detail.html:32
+msgid "Kernel version"
+msgstr "Kernelversion"
+
+#: templates/fireinfo/profile-detail.html:43
+msgid "Hypervisor"
+msgstr "Hypervisor"
+
+#: templates/fireinfo/profile-detail.html:45
+msgid "This machine is running in a virtual environment."
+msgstr "Diese Maschine läuft in einer virtuellen Umgebung."
+
+#: templates/fireinfo/profile-detail.html:51
+#: templates/fireinfo/profile-detail.html:71
+#: templates/fireinfo/profile-detail.html:130
+msgid "Vendor"
+msgstr "Hersteller"
+
+#: templates/fireinfo/profile-detail.html:59
+msgid "Type"
+msgstr "Typ"
+
+#: templates/fireinfo/profile-detail.html:67
+msgid "Hardware vendor"
+msgstr "Hardwarehersteller"
+
+#: templates/fireinfo/profile-detail.html:74
+#: templates/fireinfo/profile-detail.html:82 templates/mirrors-item.html:14
+msgid "Unknown"
+msgstr "Unbekannt"
+
+#: templates/fireinfo/profile-detail.html:79
+#: templates/fireinfo/profile-detail.html:138
+msgid "Model"
+msgstr "Modell"
+
+#: templates/fireinfo/profile-detail.html:97
+msgid "Enabled network zones"
+msgstr "Eingeschaltete Netzwerkzonen"
+
+#: templates/fireinfo/profile-detail.html:119 templates/base-feature.html:62
+#: templates/static/features.html:1104 templates/static/features.html:1399
+msgid "Hardware"
+msgstr "Hardware"
+
+#: templates/fireinfo/profile-detail.html:125
+msgid "CPU"
+msgstr "CPU"
+
+#: templates/fireinfo/profile-detail.html:141
+msgid "Not available"
+msgstr "Nicht verfügbar"
+
+#: templates/fireinfo/profile-detail.html:146
+msgid "Cores"
+msgstr "Kerne"
+
+#: templates/fireinfo/profile-detail.html:166
+msgid "Supported features"
+msgstr "Unterstützte Funktionen"
+
+#: templates/fireinfo/profile-detail.html:170
+msgid "64 bit capable"
+msgstr "64-Bit fähig"
+
+#: templates/fireinfo/profile-detail.html:174
+msgid "PAE capable"
+msgstr "PAE fähig"
+
+#: templates/fireinfo/profile-detail.html:178
+msgid "VT-x/AMD-V"
+msgstr "VT-x/AMD-V"
+
+#: templates/fireinfo/profile-detail.html:190
+msgid "Memory size"
+msgstr "Arbeitsspeichergröße"
+
+#: templates/fireinfo/profile-detail.html:198
+msgid "System disk size"
+msgstr "Systemplattengröße"
+
+#: templates/fireinfo/profile-detail.html:211
+msgid "Peripherial devices"
+msgstr "Peripheriegeräte"
+
+#: templates/fireinfo/profile-detail.html:216
+msgid "Signature images"
+msgstr "Signaturbilder"
+
+#: templates/fireinfo/profile-detail.html:231 templates/admin-mirrors.html:15
+#: templates/download-mirror-detail.html:20
+#: templates/downloads-mirrors.html:50
+msgid "Last update"
+msgstr "Letztes Update"
+
+#: templates/fireinfo/base.html:6 templates/modules/menu.html:98
+msgid "Fireinfo"
+msgstr "Fireinfo"
+
+#: templates/fireinfo/base.html:7
+msgid "A hardware data collection tool for IPFire"
+msgstr "Ein Hardwaredatensammeltool für IPFire"
+
+#: templates/sources.html:3
+msgid "Sources"
+msgstr "Quellen"
+
+#: templates/sources.html:6 templates/static/development.html:263
+msgid "Source Code"
+msgstr "Quellcode"
+
+#: templates/sources.html:9
+#, python-format
+msgid "There are %s source files on the server."
+msgstr "Es befinden sich %s Quelldateien auf dem Server."
 
-#: templates/downloads.html:55
-msgid "Get yourself involved"
-msgstr "Selbst mitmachen"
+#: templates/downloads-all.html:3
+msgid "Ancient Downloads"
+msgstr "Veraltete Downloads"
+
+#: templates/downloads-all.html:7 templates/index.html:18
+msgid "Download IPFire"
+msgstr "IPFire herunterladen"
+
+#: templates/downloads-all.html:9 templates/downloads-older.html:9
+msgid ""
+"These are the ancient downloads of IPFire. They are just saved for "
+"historical reasons and should not be used in a productive environment."
+msgstr ""
+"Dieses sind veraltete Downloads von IPFire. Sie wurden hier archiviert und "
+"sollten nicht in produktiven Umgebungen eingesetzt werden."
+
+#: templates/downloads-all.html:13 templates/downloads-older.html:17
+msgid "Go back to latest stable downloads."
+msgstr "Zurück zu den neuesten stabilen Downloads."
+
+#: templates/admin-accounts-edit.html:5 templates/admin-accounts.html:5
+msgid "Account Administrator"
+msgstr "Accountadministrator"
+
+#: templates/news-author.html:7
+#, python-format
+msgid "%s's announcements"
+msgstr "%ss Ankündigungen"
+
+#: templates/admin-mirrors.html:5
+msgid "Mirror Administrator"
+msgstr "Mirroradministrator"
 
-#: templates/admin-mirrors-create.html:5 templates/admin-mirrors.html:8
+#: templates/admin-mirrors.html:8 templates/admin-mirrors-create.html:5
 msgid "Create new mirror"
 msgstr "Neuen Mirror erstellen"
 
-#: templates/admin-mirrors-create.html:16
-msgid "Path"
-msgstr "Pfad"
-
-#: templates/admin-mirrors-create.html:28
-#: templates/admin-mirrors-details.html:33
-msgid "File mirror"
-msgstr "Dateimirror"
+#: templates/admin-mirrors.html:9
+msgid "Re-check now"
+msgstr "Jetzt neu prüfen"
 
-#: templates/admin-mirrors-create.html:31
-#: templates/admin-mirrors-create.html:40
-#: templates/admin-mirrors-create.html:49
-#: templates/admin-mirrors-create.html:58
-msgid "yes"
-msgstr "Ja"
+#: templates/admin-mirrors.html:14 templates/download-mirror-detail.html:12
+#: templates/admin-mirrors-create.html:12 templates/downloads-mirrors.html:48
+#: templates/admin-mirrors-details.html:13
+msgid "Hostname"
+msgstr "Hostname"
 
-#: templates/admin-mirrors-create.html:32
-#: templates/admin-mirrors-create.html:41
-#: templates/admin-mirrors-create.html:50
-#: templates/admin-mirrors-create.html:59
-msgid "no"
-msgstr "Nein"
+#: templates/admin-mirrors.html:23 templates/admin-mirrors-details.html:5
+msgid "Details"
+msgstr "Details"
 
-#: templates/admin-mirrors-create.html:37
-#: templates/admin-mirrors-details.html:37
-msgid "Pakfire 2 mirror"
-msgstr "Pakfire 2 Mirror"
+#: templates/admin-mirrors.html:24 templates/admin-planet.html:29
+#: templates/admin-accounts.html:20
+msgid "Edit"
+msgstr "Bearbeiten"
 
-#: templates/admin-mirrors-create.html:46
-#: templates/admin-mirrors-details.html:41
-msgid "Pakfire 3 mirror"
-msgstr "Pakfire 3 Mirror"
+#: templates/admin-mirrors.html:25 templates/admin-accounts.html:21
+msgid "Delete"
+msgstr "Löschen"
 
-#: templates/admin-mirrors-create.html:55
-#: templates/admin-mirrors-details.html:45
-msgid "Disabled?"
-msgstr "Ausgeschaltet?"
+#: templates/error-500.html:6
+msgid "Detailed information"
+msgstr "Mehr Informationen"
 
-#: templates/admin-downloads.html:7
-msgid "Download counters"
-msgstr "Downloadzähler"
+#: templates/download-mirror-detail.html:3 templates/downloads-mirrors.html:3
+msgid "Mirror-Server"
+msgstr "Mirrorserver"
 
-#: templates/admin-downloads.html:10
-msgid "Today"
-msgstr "Heute"
+#: templates/download-mirror-detail.html:8 templates/downloads-mirrors.html:8
+msgid "IPFire Mirrors"
+msgstr "IPFire Mirrors"
 
-#: templates/admin-downloads.html:14
-msgid "Yesterday"
-msgstr "Gestern"
+#: templates/download-mirror-detail.html:16 templates/mirrors-item.html:23
+#: templates/admin-mirrors-create.html:20 templates/downloads-mirrors.html:47
+#: templates/admin-mirrors-details.html:21
+msgid "Owner"
+msgstr "Eigentümer"
 
-#: templates/admin-downloads.html:18
-msgid "Total"
-msgstr "Insgesamt"
+#: templates/download-mirror-detail.html:24
+msgid "Number of files"
+msgstr "Anzahl von Dateien"
 
-#: templates/admin-downloads.html:23
-msgid "Downloads by country"
-msgstr "Downloads pro Land"
+#: templates/download-mirror-detail.html:32
+msgid "View list of all mirror servers."
+msgstr "Liste aller Mirrorserver."
 
 #: templates/news.html:7
 msgid "What is new on the IPFire project?"
@@ -969,10 +1628,10 @@ msgstr "Was gibt es neues im IPFire Projekt?"
 msgid "Stay up to date"
 msgstr "Informiere dich"
 
-#: templates/news.html:61 templates/planet/year.html:3
-#: templates/planet/user.html:3 templates/planet/search.html:3
-#: templates/planet/base.html:6 templates/planet/index.html:3
-#: templates/planet/posting.html:3 templates/index.html:154
+#: templates/news.html:61 templates/planet/search.html:3
+#: templates/planet/year.html:3 templates/planet/user.html:3
+#: templates/planet/posting.html:3 templates/planet/index.html:3
+#: templates/planet/base.html:6 templates/index.html:154
 msgid "IPFire Planet"
 msgstr "IPFire Planet"
 
@@ -980,9 +1639,185 @@ msgstr "IPFire Planet"
 msgid "Latest news"
 msgstr "Aktuelle Neuigkeiten"
 
-#: templates/base-feature.html:6 templates/modules/menu.html:7
-#: templates/static/features.html:3 templates/static/features.html:8
-#: templates/static/features.html:1349
+#: templates/admin-planet-compose.html:5 templates/admin-planet.html:8
+msgid "Compose new entry"
+msgstr "Neuen Beitrag erstellen"
+
+#: templates/admin-planet-compose.html:16 templates/admin-planet.html:14
+msgid "Title"
+msgstr "Titel"
+
+#: templates/admin-planet-compose.html:33 templates/planet/posting.html:26
+msgid "Tags"
+msgstr "Tags"
+
+#: templates/admin-planet-compose.html:44
+#: templates/admin-mirrors-details.html:17
+msgid "Status"
+msgstr "Status"
+
+#: templates/admin-planet-compose.html:48 templates/admin-planet.html:25
+msgid "Draft"
+msgstr ""
+
+#: templates/admin-planet-compose.html:52
+#, fuzzy
+msgid "Published"
+msgstr "Veröffentlicht am"
+
+#: templates/admin-planet-compose.html:58
+msgid "Save"
+msgstr "Speichern"
+
+#: templates/admin-planet-compose.html:59
+msgid "Preview"
+msgstr "Vorschau"
+
+#: templates/admin-planet-compose.html:60
+msgid "Cancel"
+msgstr "Abbrechen"
+
+#: templates/mirrors-item.html:3
+#, python-format
+msgid "Mirror %s"
+msgstr "Mirror %s"
+
+#: templates/mirrors-item.html:8
+msgid "Up"
+msgstr "Verfügbar"
+
+#: templates/mirrors-item.html:10
+msgid "Down"
+msgstr "Nicht verfügbar"
+
+#: templates/mirrors-item.html:12
+msgid "Out of sync"
+msgstr "Nicht synchronisiert"
+
+#: templates/mirrors-item.html:30 templates/admin-mirrors-create.html:24
+#: templates/downloads-mirrors.html:49 templates/admin-mirrors-details.html:25
+msgid "Location"
+msgstr "Standort"
+
+#: templates/mirrors-item.html:36 templates/geoip/index.html:19
+msgid "Country"
+msgstr ""
+
+#: templates/mirrors-item.html:41 templates/geoip/index.html:29
+msgid "City"
+msgstr ""
+
+#: templates/mirrors-item.html:47
+msgid "Preferred for"
+msgstr "Bevorzugt für"
+
+#: templates/mirrors-item.html:54
+msgid "Estimated distance to you"
+msgstr ""
+
+#: templates/mirrors-item.html:61 templates/geoip/index.html:15
+msgid "Autonomous System"
+msgstr ""
+
+#: templates/mirrors-item.html:65
+msgid "IP Addresses"
+msgstr ""
+
+#: templates/mirrors-item.html:75
+#, fuzzy
+msgid "Last updated"
+msgstr "Letztes Update"
+
+#: templates/mirrors-item.html:86
+msgid "Go to mirror"
+msgstr "Zum Mirror"
+
+#: templates/mirrors-item.html:94
+msgid "The location of the mirror server is estimated by the IP address."
+msgstr ""
+"Der Standort dieses Mirrorservers wurde anhand der IP-Adresse geschätzt."
+
+#: templates/mirrors-item.html:98
+#, fuzzy
+msgid "The location of this mirror server could not be estimated."
+msgstr "Der Standort dieses Mirrorservers konnte nicht ermittelt werden."
+
+#: templates/tracker-torrents.html:3
+msgid "Torrent Downloads"
+msgstr "Torrent-Downloads"
+
+#: templates/tracker-torrents.html:7
+msgid "IPFire Torrent Tracker"
+msgstr "IPFire Torrent-Tracker"
+
+#: templates/tracker-torrents.html:32 templates/downloads-older.html:25
+#: templates/downloads-index.html:35
+msgid "Release"
+msgstr "Release"
+
+#: templates/tracker-torrents.html:33
+msgid "Seeders"
+msgstr "Seeders"
+
+#: templates/tracker-torrents.html:34 templates/tracker-torrent-detail.html:23
+msgid "Peers"
+msgstr "Peers"
+
+#: templates/downloads-older.html:3 templates/downloads-older.html:7
+msgid "Ancient downloads"
+msgstr "Veraltete Downloads"
+
+#: templates/downloads-older.html:13
+msgid ""
+"Beware that these releases could lack possible security-fixes\tand so it is "
+"recommended to use the <em>latest</em> version."
+msgstr ""
+"Achtung! Diese Releases könnten potentielle Sicherheitsprobleme aufweisen. "
+"Daher wird empfohlen die <em>neueste</em> Version einzusetzen."
+
+#: templates/downloads-older.html:26
+msgid "Published on"
+msgstr "Veröffentlicht am"
+
+#: templates/admin-downloads-mirrors.html:5 templates/admin-downloads.html:5
+msgid "Download statistics"
+msgstr "Downloadstatistiken"
+
+#: templates/admin-downloads-mirrors.html:7
+msgid "Mirror load from today"
+msgstr "Mirrorauslastung von heute"
+
+#: templates/admin-downloads-mirrors.html:10
+msgid "Mirror load"
+msgstr "Mirrorauslastung"
+
+#: templates/admin-base.html:3
+msgid "IPFire Admin Area"
+msgstr "IPFire Adminbereich"
+
+#: templates/admin-base.html:6 templates/admin-downloads-base.html:8
+msgid "Options"
+msgstr "Optionen"
+
+#: templates/admin-base.html:9
+msgid "Accounts"
+msgstr "Accounts"
+
+#: templates/admin-base.html:10 templates/modules/menu.html:110
+msgid "Mirrors"
+msgstr "Mirrors"
+
+#: templates/admin-base.html:11 templates/modules/menu.html:92
+msgid "Planet"
+msgstr "Planet"
+
+#: templates/admin-base.html:12
+msgid "Downloads"
+msgstr "Downloads"
+
+#: templates/base-feature.html:6 templates/static/features.html:3
+#: templates/static/features.html:8 templates/static/features.html:1349
+#: templates/modules/menu.html:7
 msgid "About IPFire"
 msgstr "Ãœber IPFire"
 
@@ -1060,125 +1895,390 @@ msgstr "Quality of Service"
 msgid "Wireless AP"
 msgstr "WLAN-Access-Point"
 
-#: templates/download-mirror-detail.html:24
-msgid "Number of files"
-msgstr "Anzahl von Dateien"
+#: templates/downloads-development.html:3
+#: templates/downloads-development.html:6
+msgid "Development Downloads"
+msgstr "Development Downloads"
 
-#: templates/download-mirror-detail.html:32
-msgid "View list of all mirror servers."
-msgstr "Liste aller Mirrorserver."
+#: templates/admin-mirrors-create.html:16
+msgid "Path"
+msgstr "Pfad"
+
+#: templates/admin-mirrors-create.html:28
+#: templates/admin-mirrors-details.html:33
+msgid "File mirror"
+msgstr "Dateimirror"
+
+#: templates/admin-mirrors-create.html:31
+#: templates/admin-mirrors-create.html:40
+#: templates/admin-mirrors-create.html:49
+#: templates/admin-mirrors-create.html:58
+msgid "yes"
+msgstr "Ja"
+
+#: templates/admin-mirrors-create.html:32
+#: templates/admin-mirrors-create.html:41
+#: templates/admin-mirrors-create.html:50
+#: templates/admin-mirrors-create.html:59
+msgid "no"
+msgstr "Nein"
+
+#: templates/admin-mirrors-create.html:37
+#: templates/admin-mirrors-details.html:37
+msgid "Pakfire 2 mirror"
+msgstr "Pakfire 2 Mirror"
+
+#: templates/admin-mirrors-create.html:46
+#: templates/admin-mirrors-details.html:41
+msgid "Pakfire 3 mirror"
+msgstr "Pakfire 3 Mirror"
+
+#: templates/admin-mirrors-create.html:55
+#: templates/admin-mirrors-details.html:45
+msgid "Disabled?"
+msgstr "Ausgeschaltet?"
+
+#: templates/geoip/index.html:3 templates/geoip/index.html:7
+#, python-format
+msgid "GeoIP for %s"
+msgstr ""
+
+#: templates/geoip/index.html:33
+msgid "Postal Code"
+msgstr ""
+
+#: templates/geoip/index.html:46
+#, python-format
+msgid "No GeoIP information could be found for the IP address '%s'."
+msgstr ""
+
+#: templates/downloads-index.html:3
+msgid "Download Center"
+msgstr "Downloadcenter"
+
+#: templates/downloads-index.html:7
+msgid "IPFire Download Center"
+msgstr "IPFire Download-Center"
+
+#: templates/downloads-index.html:30
+msgid "Available releases"
+msgstr "Verfügbare Releases"
+
+#: templates/downloads-index.html:36
+msgid "Release type"
+msgstr "Release-Typ"
+
+#: templates/downloads-index.html:37
+msgid "Release date"
+msgstr "Veröffentlichungsdatum"
+
+#: templates/downloads-index.html:48
+msgid "Stable"
+msgstr "Stabil"
+
+#: templates/downloads-index.html:50 templates/static/getinvolved.html:153
+#: templates/static/development.html:3 templates/static/development.html:8
+#: templates/modules/menu.html:65
+msgid "Development"
+msgstr "Entwicklung"
+
+#: templates/downloads-mirrors.html:63
+msgid "details"
+msgstr "Details"
+
+#: templates/admin-planet.html:5
+msgid "Planet Administrator"
+msgstr "Planet Administrator"
+
+#: templates/admin-planet.html:13
+msgid "Author"
+msgstr "Autor"
+
+#: templates/admin-planet.html:31
+#, fuzzy
+msgid "Publish"
+msgstr "Veröffentlicht am"
+
+#: templates/error.html:3
+msgid "Error"
+msgstr "Error"
+
+#: templates/admin-downloads-base.html:5
+msgid "Back to home"
+msgstr "Zurück zur Startseite"
+
+#: templates/admin-downloads-base.html:11
+msgid "Mirror stats"
+msgstr "Mirrorstatistiken"
+
+#: templates/static/getinvolved.html:3 templates/static/getinvolved.html:8
+msgid "Get involved"
+msgstr "Mitmachen"
+
+#: templates/static/getinvolved.html:10
+msgid "Because making a difference is easy"
+msgstr "Weil es so leicht ist etwas Gutes zu tun"
+
+#: templates/static/getinvolved.html:47
+msgid "First steps"
+msgstr "Erste Schritte"
+
+#: templates/static/getinvolved.html:97
+msgid "Donations"
+msgstr "Spenden"
+
+#: templates/static/getinvolved.html:120 templates/static/donation.html:3
+#: templates/static/donation.html:8 templates/modules/menu.html:47
+#: templates/wishlist/donate.html:3 templates/wishlist/modules/wish.html:26
+#: templates/download-splash.html:37
+msgid "Donate"
+msgstr "Spenden"
+
+#: templates/static/getinvolved.html:126 templates/modules/menu.html:53
+msgid "Wishlist"
+msgstr "Wunschliste"
+
+#: templates/static/getinvolved.html:142
+msgid "Make a wish"
+msgstr "Wünsch dir was"
+
+#: templates/static/getinvolved.html:149
+msgid "Contribute yourself"
+msgstr "Bring dich selbst ein"
+
+#: templates/static/getinvolved.html:175
+msgid "Translation"
+msgstr "Ãœbersetzung"
+
+#: templates/static/getinvolved.html:190
+msgid "Translation team"
+msgstr "Ãœbersetzungsteam"
+
+#: templates/static/getinvolved.html:196
+msgid "Promotion"
+msgstr "Promotion"
+
+#: templates/static/features.html:10
+msgid "Because IPFire is more than just a firewall"
+msgstr "Weil IPFire mehr als nur eine Firewall ist"
+
+#: templates/static/features.html:22
+msgid "IPFire"
+msgstr "IPFire"
+
+#: templates/static/features.html:22
+msgid "An Open Source Firewall Distribution"
+msgstr "An Open Source Firewall Solution"
+
+#: templates/static/features.html:315
+msgid "The IPFire package management system"
+msgstr "Das IPFire Paketmanagementsystem"
+
+#: templates/static/features.html:791
+msgid "Transparent virus scanner"
+msgstr "Transparenter Virenscanner"
+
+#: templates/static/features.html:818
+msgid "Virtual Private Networks"
+msgstr "Virtuelle private Netzwerke"
+
+#: templates/static/features.html:974
+msgid "Intrusion detection system"
+msgstr "Einbruchsdetektierung"
+
+#: templates/static/features.html:1311 templates/static/features.html:1409
+msgid "Wireless Access Point"
+msgstr "WLAN-Access-Point"
+
+#: templates/static/features.html:1379
+msgid "Web Proxy"
+msgstr "Webproxy"
+
+#: templates/static/features.html:1389
+msgid "Intrusion Detection"
+msgstr "Einbruchsdetektierung"
+
+#: templates/static/artwork.html:3 templates/static/artwork.html:8
+#: templates/modules/menu.html:116
+msgid "Artwork"
+msgstr "Artwork"
+
+#: templates/static/artwork.html:36
+msgid "The IPFire Logo"
+msgstr "Das IPFire-Logo"
+
+#: templates/static/artwork.html:50
+msgid "Flyers, Rollups, CDs and more"
+msgstr "Flyer, Rollups, CDs und mehr"
+
+#: templates/static/development.html:37 templates/static/development.html:258
+msgid "Development tools"
+msgstr "Entwicklungstools"
+
+#: templates/static/development.html:55 templates/modules/menu.html:82
+msgid "Bugtracker"
+msgstr "Bugtracker"
+
+#: templates/static/development.html:77
+msgid "Development Mailing List"
+msgstr "Entwicklungsmailingliste"
+
+#: templates/static/development.html:96
+msgid "Source code"
+msgstr "Quellcode"
+
+#: templates/static/development.html:99
+msgid "Git"
+msgstr "Git"
+
+#: templates/static/development.html:118
+msgid "IPFire Git repositories"
+msgstr "IPFire Git-Repositorien"
+
+#: templates/static/development.html:124
+msgid "GitHub"
+msgstr "GitHub"
+
+#: templates/static/development.html:143
+msgid "ipfire on GitHub"
+msgstr "ipfire auf GitHub"
+
+#: templates/static/development.html:150 templates/static/development.html:268
+msgid "How to build IPFire?"
+msgstr "Wie kompiliert man IPFire?"
+
+#: templates/static/development.html:186
+msgid "How to submit patches?"
+msgstr "Wie sendet man Patches?"
+
+#: templates/static/development.html:214
+msgid "How to translate IPFire?"
+msgstr "Wie Ã¼bersetzt man IPFire?"
+
+#: templates/static/press.html:3 templates/static/press.html:7
+#: templates/base.html:69
+msgid "Press"
+msgstr "Presse"
+
+#: templates/static/press.html:31 templates/static/imprint.html:29
+#: templates/static/imprint.html:41 templates/static/imprint.html:53
+#: templates/static/imprint.html:65 templates/planet/user.html:48
+msgid "Mail"
+msgstr "Mail"
+
+#: templates/static/press.html:35
+msgid "Logo"
+msgstr "Logo"
 
-#: templates/downloads-all.html:3
-msgid "Ancient Downloads"
-msgstr "Veraltete Downloads"
+#: templates/static/imprint.html:3 templates/static/imprint.html:8
+#: templates/base.html:73
+msgid "Imprint"
+msgstr "Impressum"
 
-#: templates/downloads-all.html:7 templates/index.html:18
-msgid "Download IPFire"
-msgstr "IPFire herunterladen"
+#: templates/static/chat.html:3 templates/static/chat.html:8
+#: templates/modules/menu.html:39
+msgid "Chat"
+msgstr "Chat"
 
-#: templates/admin-accounts-edit.html:5 templates/admin-accounts.html:5
-msgid "Account Administrator"
-msgstr "Accountadministrator"
+#: templates/static/chat.html:33
+msgid "Server"
+msgstr "Server"
 
-#: templates/planet/year.html:6
-#, python-format
-msgid "Year %s"
-msgstr "Jahr %s"
+#: templates/static/chat.html:36
+msgid "Channel"
+msgstr "Channel"
 
-#: templates/planet/user.html:22 templates/planet/index.html:45
-msgid "Older posts"
-msgstr "Ältere Beiträge"
+#: templates/static/chat.html:41
+msgid "Use the web client"
+msgstr "Web-Client benutzen"
 
-#: templates/planet/user.html:26 templates/planet/index.html:49
-msgid "Newer posts"
-msgstr "Neuere Beiträge"
+#: templates/static/donation.html:10
+msgid "We need your help!"
+msgstr "Wir brauchen Deine Hilfe!"
 
-#: templates/planet/user.html:33
-#, python-format
-msgid "%s did not write any posts, yet."
-msgstr "%s hat bisher noch keine Beiträge verfasst."
+#: templates/static/donation.html:68
+msgid "Did you know...?"
+msgstr "Schon gewusst?"
 
-#: templates/planet/user.html:48 templates/static/press.html:31
-#: templates/static/imprint.html:29 templates/static/imprint.html:41
-#: templates/static/imprint.html:53 templates/static/imprint.html:65
-msgid "Mail"
-msgstr "Mail"
+#: templates/static/donation.html:84
+msgid "How do we use financial support?"
+msgstr "Wofür werden Spenden eingesetzt?"
 
-#: templates/planet/search.html:6
-#, python-format
-msgid "Search results for '%s'"
-msgstr "Suchergebnisse für '%s'"
+#: templates/static/donation.html:132
+msgid "Research & Development"
+msgstr "Forschung & Entwicklung"
 
-#: templates/planet/search.html:16
-#, python-format
-msgid "No results found for '%s'"
-msgstr "Keine Resultate gefunden für '%s'"
+#: templates/static/cebit.html:3
+msgid "CeBIT special"
+msgstr "CeBIT-Spezial"
 
-#: templates/planet/base.html:9
-msgid "The official blog of the IPFire team"
-msgstr "Das offizielle Blog des IPFire-Team"
+#: templates/static/cebit.html:8
+msgid "IPFire at CeBIT 2010"
+msgstr "IPFire auf der CeBIT 2010"
 
-#: templates/planet/index.html:24
-msgid "All posts from"
-msgstr "Alle Posts aus"
+#: templates/admin-accounts.html:8
+msgid "Create new account"
+msgstr "Neuen Account erstellen"
 
-#: templates/planet/posting.html:24 templates/modules/planet-entry.html:14
-msgid "Posted by"
-msgstr "Erstellt von"
+#: templates/admin-accounts.html:13
+msgid "Name (Nickname)"
+msgstr "Name (Nickname)"
 
-#: templates/planet/posting.html:25 templates/modules/planet-entry.html:15
-msgid "on"
-msgstr "am"
+#: templates/modules/sidebar-release.html:4
+#: templates/modules/release-item.html:2 templates/index.html:24
+msgid "Latest release"
+msgstr "Neuestes Release"
 
-#: templates/base.html:4
-msgid "No title given"
-msgstr "Kein Titel"
+#: templates/modules/sidebar-release.html:9
+msgid "Download now"
+msgstr "Jetzt herunterladen"
 
-#: templates/base.html:63
-msgid "IPFire is free software"
-msgstr "IPFire ist freie Software"
+#: templates/modules/release-item-short.html:4
+msgid "Here you will find the downloads for the version"
+msgstr "Hier gibt es alle Downloads für diese Version"
 
-#: templates/base.html:69 templates/static/press.html:3
-#: templates/static/press.html:7
-msgid "Press"
-msgstr "Presse"
+#: templates/modules/release-item-short.html:13
+#: templates/modules/release-item.html:123
+msgid "There are no downloads available for this release."
+msgstr "Es gibt keine Downloads für dieses Release."
 
-#: templates/base.html:73 templates/static/imprint.html:3
-#: templates/static/imprint.html:8
-msgid "Imprint"
-msgstr "Impressum"
+#: templates/modules/download-button.html:2
+#, python-format
+msgid "Download %s"
+msgstr "%s herunterladen"
 
-#: templates/admin-mirrors.html:5
-msgid "Mirror Administrator"
-msgstr "Mirroradministrator"
+#: templates/modules/map.html:5
+msgid "View larger map"
+msgstr "Größere Karte ansehen"
 
-#: templates/admin-mirrors.html:9
-msgid "Re-check now"
-msgstr "Jetzt neu prüfen"
+#: templates/modules/ads/download-splash.html:4
+msgid "Advertisement"
+msgstr "Werbung"
 
-#: templates/admin-mirrors.html:23 templates/admin-mirrors-details.html:5
-msgid "Details"
-msgstr "Details"
+#: templates/modules/ads/download-splash.html:5
+msgid "This download is sponsored by:"
+msgstr "Dieser Download wurde gesponsort von:"
 
-#: templates/admin-mirrors.html:25 templates/admin-accounts.html:21
-msgid "Delete"
-msgstr "Löschen"
+#: templates/modules/news-preview.html:6
+msgid "by"
+msgstr "von"
 
-#: templates/tracker-torrents.html:3
-msgid "Torrent Downloads"
-msgstr "Torrent-Downloads"
+#: templates/modules/mirrors-table.html:37
+#, fuzzy, python-format
+msgid "Last update: %s"
+msgstr "Letztes Update"
 
-#: templates/tracker-torrents.html:7
-msgid "IPFire Torrent Tracker"
-msgstr "IPFire Torrent-Tracker"
+#: templates/modules/news-year-nav.html:3
+msgid "Jump to"
+msgstr "Springe zu"
 
-#: templates/tracker-torrents.html:33
-msgid "Seeders"
-msgstr "Seeders"
+#: templates/modules/news-year-nav.html:6
+msgid "Most recent"
+msgstr "Aktuellste"
 
-#: templates/error.html:3
-msgid "Error"
-msgstr "Error"
+#: templates/modules/stasy-table-devices.html:6
+msgid "Kernel module"
+msgstr "Kernelmodul"
 
 #: templates/modules/donation-box.html:4
 msgid "Donate with PayPal or Credit Card"
@@ -1217,10 +2317,17 @@ msgstr "Kontonummer"
 msgid "Bank code"
 msgstr "BLZ"
 
-#: templates/modules/release-item.html:2
-#: templates/modules/sidebar-release.html:4 templates/index.html:24
-msgid "Latest release"
-msgstr "Neuestes Release"
+#: templates/modules/planet-entry.html:14 templates/planet/posting.html:31
+msgid "Posted by"
+msgstr "Erstellt von"
+
+#: templates/modules/planet-entry.html:15 templates/planet/posting.html:32
+msgid "on"
+msgstr "am"
+
+#: templates/modules/news-item.html:6
+msgid "Announcement"
+msgstr "Ankündigung"
 
 #: templates/modules/release-item.html:10
 msgid "Choose an architecture:"
@@ -1246,47 +2353,16 @@ msgstr "SHA1-Prüfsumme"
 msgid "Legend:"
 msgstr "Legende:"
 
-#: templates/modules/release-item.html:123
-#: templates/modules/release-item-short.html:13
-msgid "There are no downloads available for this release."
-msgstr "Es gibt keine Downloads für dieses Release."
-
-#: templates/modules/sidebar-release.html:9
-msgid "Download now"
-msgstr "Jetzt herunterladen"
-
-#: templates/modules/release-item-short.html:4
-msgid "Here you will find the downloads for the version"
-msgstr "Hier gibt es alle Downloads für diese Version"
-
-#: templates/modules/news-preview.html:6
-msgid "by"
-msgstr "von"
-
-#: templates/modules/download-button.html:2
-#, python-format
-msgid "Download %s"
-msgstr "%s herunterladen"
-
-#: templates/modules/stasy-table-devices.html:6
-msgid "Kernel module"
-msgstr "Kernelmodul"
-
-#: templates/modules/news-year-nav.html:3
-msgid "Jump to"
-msgstr "Springe zu"
-
-#: templates/modules/news-year-nav.html:6
-msgid "Most recent"
-msgstr "Aktuellste"
-
-#: templates/modules/ads/download-splash.html:4
-msgid "Advertisement"
-msgstr "Werbung"
+#: templates/modules/release-item.html:114
+#: templates/tracker-torrent-detail.html:15
+msgid "Magnet link"
+msgstr "Magnet-Link"
 
-#: templates/modules/ads/download-splash.html:5
-msgid "This download is sponsored by:"
-msgstr "Dieser Download wurde gesponsort von:"
+#: templates/modules/release-item.html:115
+#: templates/tracker-torrent-detail.html:3
+#: templates/tracker-torrent-detail.html:18
+msgid "Torrent download"
+msgstr "Torrent-Download"
 
 #: templates/modules/menu.html:4
 msgid "Download"
@@ -1312,15 +2388,6 @@ msgstr "Wiki"
 msgid "Forum"
 msgstr "Forum"
 
-#: templates/modules/menu.html:39 templates/static/chat.html:3
-#: templates/static/chat.html:8
-msgid "Chat"
-msgstr "Chat"
-
-#: templates/modules/menu.html:53 templates/static/getinvolved.html:126
-msgid "Wishlist"
-msgstr "Wunschliste"
-
 #: templates/modules/menu.html:61
 msgid "More"
 msgstr "Mehr"
@@ -1333,10 +2400,6 @@ msgstr "Ãœbersicht"
 msgid "Pakfire Build Service"
 msgstr "Pakfire Buildsystem"
 
-#: templates/modules/menu.html:82 templates/static/development.html:55
-msgid "Bugtracker"
-msgstr "Bugtracker"
-
 #: templates/modules/menu.html:87
 msgid "Miscellaneous"
 msgstr "Verschiedenes"
@@ -1345,11 +2408,6 @@ msgstr "Verschiedenes"
 msgid "Mailing lists"
 msgstr "Mailinglisten"
 
-#: templates/modules/menu.html:116 templates/static/artwork.html:3
-#: templates/static/artwork.html:8
-msgid "Artwork"
-msgstr "Artwork"
-
 #: templates/modules/menu.html:122
 msgid "CeBIT"
 msgstr "CeBIT"
@@ -1362,173 +2420,175 @@ msgstr "Tracker"
 msgid "Professional support available!"
 msgstr "Professioneller Support verfügbar!"
 
-#: templates/modules/news-item.html:6
-msgid "Announcement"
-msgstr "Ankündigung"
-
-#: templates/static/press.html:35
-msgid "Logo"
-msgstr "Logo"
-
-#: templates/static/features.html:10
-msgid "Because IPFire is more than just a firewall"
-msgstr "Weil IPFire mehr als nur eine Firewall ist"
-
-#: templates/static/features.html:22
-msgid "IPFire"
-msgstr "IPFire"
+#: templates/admin-login.html:3
+msgid "Please login"
+msgstr "Bitte einloggen"
 
-#: templates/static/features.html:22
-msgid "An Open Source Firewall Distribution"
-msgstr "An Open Source Firewall Solution"
+#: templates/planet/search.html:6
+#, python-format
+msgid "Search results for '%s'"
+msgstr "Suchergebnisse für '%s'"
 
-#: templates/static/features.html:315
-msgid "The IPFire package management system"
-msgstr "Das IPFire Paketmanagementsystem"
+#: templates/planet/search.html:16
+#, python-format
+msgid "No results found for '%s'"
+msgstr "Keine Resultate gefunden für '%s'"
 
-#: templates/static/features.html:791
-msgid "Transparent virus scanner"
-msgstr "Transparenter Virenscanner"
+#: templates/planet/year.html:6
+#, python-format
+msgid "Year %s"
+msgstr "Jahr %s"
 
-#: templates/static/features.html:818
-msgid "Virtual Private Networks"
-msgstr "Virtuelle private Netzwerke"
+#: templates/planet/user.html:22 templates/planet/index.html:45
+msgid "Older posts"
+msgstr "Ältere Beiträge"
 
-#: templates/static/features.html:974
-msgid "Intrusion detection system"
-msgstr "Einbruchsdetektierung"
+#: templates/planet/user.html:26 templates/planet/index.html:49
+msgid "Newer posts"
+msgstr "Neuere Beiträge"
 
-#: templates/static/features.html:1311 templates/static/features.html:1409
-msgid "Wireless Access Point"
-msgstr "WLAN-Access-Point"
+#: templates/planet/user.html:33
+#, python-format
+msgid "%s did not write any posts, yet."
+msgstr "%s hat bisher noch keine Beiträge verfasst."
 
-#: templates/static/features.html:1379
-msgid "Web Proxy"
-msgstr "Webproxy"
+#: templates/planet/posting.html:17
+msgid "Heads up!"
+msgstr ""
 
-#: templates/static/features.html:1389
-msgid "Intrusion Detection"
-msgstr "Einbruchsdetektierung"
+#: templates/planet/posting.html:17
+msgid "This post is a draft and has not been published, yet."
+msgstr ""
 
-#: templates/static/development.html:37 templates/static/development.html:258
-msgid "Development tools"
-msgstr "Entwicklungstools"
+#: templates/planet/index.html:24
+msgid "All posts from"
+msgstr "Alle Posts aus"
 
-#: templates/static/development.html:77
-msgid "Development Mailing List"
-msgstr "Entwicklungsmailingliste"
+#: templates/planet/base.html:9
+msgid "The official blog of the IPFire team"
+msgstr "Das offizielle Blog des IPFire-Team"
 
-#: templates/static/development.html:96
-msgid "Source code"
-msgstr "Quellcode"
+#: templates/admin-downloads.html:7
+msgid "Download counters"
+msgstr "Downloadzähler"
 
-#: templates/static/development.html:99
-msgid "Git"
-msgstr "Git"
+#: templates/admin-downloads.html:10
+msgid "Today"
+msgstr "Heute"
 
-#: templates/static/development.html:118
-msgid "IPFire Git repositories"
-msgstr "IPFire Git-Repositorien"
+#: templates/admin-downloads.html:14
+msgid "Yesterday"
+msgstr "Gestern"
 
-#: templates/static/development.html:124
-msgid "GitHub"
-msgstr "GitHub"
+#: templates/admin-downloads.html:18
+msgid "Total"
+msgstr "Insgesamt"
 
-#: templates/static/development.html:143
-msgid "ipfire on GitHub"
-msgstr "ipfire auf GitHub"
+#: templates/admin-downloads.html:23
+msgid "Downloads by country"
+msgstr "Downloads pro Land"
 
-#: templates/static/development.html:150 templates/static/development.html:268
-msgid "How to build IPFire?"
-msgstr "Wie kompiliert man IPFire?"
+#: templates/tracker-torrent-detail.html:28
+msgid "Seeds"
+msgstr "Seeds"
 
-#: templates/static/development.html:186
-msgid "How to submit patches?"
-msgstr "Wie sendet man Patches?"
+#: templates/wishlist/closed.html:3 templates/wishlist/closed.html:13
+msgid "Closed wishes"
+msgstr "Beendete Wünsche"
 
-#: templates/static/development.html:214
-msgid "How to translate IPFire?"
-msgstr "Wie Ã¼bersetzt man IPFire?"
+#: templates/wishlist/closed.html:9
+msgid "Wishes open for donation"
+msgstr "Noch laufende Wünsche"
 
-#: templates/static/chat.html:33
-msgid "Server"
-msgstr "Server"
+#: templates/wishlist/closed.html:36 templates/wishlist/closed.html:40
+msgid "Older"
+msgstr "Älter"
 
-#: templates/static/chat.html:36
-msgid "Channel"
-msgstr "Channel"
+#: templates/wishlist/closed.html:46 templates/wishlist/closed.html:50
+msgid "Newer"
+msgstr "Neuer"
 
-#: templates/static/chat.html:41
-msgid "Use the web client"
-msgstr "Web-Client benutzen"
+#: templates/wishlist/donate.html:13 templates/wishlist/donate.html:23
+#: templates/wishlist/terms.html:3 templates/wishlist/terms.html:7
+#: templates/base.html:78
+msgid "Terms & Conditions"
+msgstr "Allgemeine Bedingungen"
 
-#: templates/static/cebit.html:3
-msgid "CeBIT special"
-msgstr "CeBIT-Spezial"
+#: templates/wishlist/modules/wish.html:6
+msgid "Only a few days left!"
+msgstr "Nur noch wenige Tage!"
 
-#: templates/static/cebit.html:8
-msgid "IPFire at CeBIT 2010"
-msgstr "IPFire auf der CeBIT 2010"
+#: templates/wishlist/modules/wish.html:36
+msgid "funded"
+msgstr "gesammelt"
 
-#: templates/static/getinvolved.html:3 templates/static/getinvolved.html:8
-msgid "Get involved"
-msgstr "Mitmachen"
+#: templates/wishlist/modules/wish.html:40
+#: templates/wishlist/modules/wish.html:50
+#, python-format
+msgid "%s &euro;"
+msgstr "%s &euro;"
 
-#: templates/static/getinvolved.html:10
-msgid "Because making a difference is easy"
-msgstr "Weil es so leicht ist etwas Gutes zu tun"
+#: templates/wishlist/modules/wish.html:41
+msgid "donated"
+msgstr "gespendet"
 
-#: templates/static/getinvolved.html:47
-msgid "First steps"
-msgstr "Erste Schritte"
+#: templates/wishlist/modules/wish.html:48
+msgid "day to go"
+msgid_plural "days to go"
+msgstr[0] "Tag Ã¼brig"
+msgstr[1] "Tage Ã¼brig"
 
-#: templates/static/getinvolved.html:97
-msgid "Donations"
-msgstr "Spenden"
+#: templates/wishlist/modules/wish.html:51
+#, fuzzy
+msgid "to go"
+msgstr "Tag Ã¼brig"
 
-#: templates/static/getinvolved.html:142
-msgid "Make a wish"
-msgstr "Wünsch dir was"
+#: templates/wishlist/modules/wish.html:54
+msgid "In progress"
+msgstr "In Entwicklung"
 
-#: templates/static/getinvolved.html:149
-msgid "Contribute yourself"
-msgstr "Bring dich selbst ein"
+#: templates/wishlist/modules/wish.html:56
+msgid "Finished"
+msgstr "Beendet"
 
-#: templates/static/getinvolved.html:175
-msgid "Translation"
-msgstr "Ãœbersetzung"
+#: templates/wishlist/modules/wish.html:58
+msgid "Funding ended"
+msgstr "Sammlung beendet"
 
-#: templates/static/getinvolved.html:190
-msgid "Translation team"
-msgstr "Ãœbersetzungsteam"
+#: templates/wishlist/modules/wish.html:68
+msgid "Share this wish with your friends and help us promote it!"
+msgstr ""
+"Teile diesen Wunsch mit deinen Freunden und hilf diesen bekannter zu machen!"
 
-#: templates/static/getinvolved.html:196
-msgid "Promotion"
-msgstr "Promotion"
+#: templates/wishlist/index.html:3 templates/index.html:3
+msgid "Home"
+msgstr "Startseite"
 
-#: templates/static/artwork.html:36
-msgid "The IPFire Logo"
-msgstr "Das IPFire-Logo"
+#: templates/wishlist/wish.html:3
+msgid "Wish"
+msgstr "Wunsch"
 
-#: templates/static/artwork.html:50
-msgid "Flyers, Rollups, CDs and more"
-msgstr "Flyer, Rollups, CDs und mehr"
+#: templates/wishlist/wish.html:18
+#, python-format
+msgid "Launched: %s"
+msgstr "Gestarted: %s"
 
-#: templates/static/donation.html:10
-msgid "We need your help!"
-msgstr "Wir brauchen Deine Hilfe!"
+#: templates/wishlist/wish.html:24
+#, python-format
+msgid "Funding ends: %s"
+msgstr "Sammlung endet: %s"
 
-#: templates/static/donation.html:68
-msgid "Did you know...?"
-msgstr "Schon gewusst?"
+#: templates/wishlist/wish.html:26
+msgid "This funding runs until the goal is reached."
+msgstr ""
 
-#: templates/static/donation.html:84
-msgid "How do we use financial support?"
-msgstr "Wofür werden Spenden eingesetzt?"
+#: templates/wishlist/base.html:6 templates/index.html:175
+msgid "IPFire Wishlist"
+msgstr "IPFire Wunschliste"
 
-#: templates/static/donation.html:132
-msgid "Research & Development"
-msgstr "Forschung & Entwicklung"
+#: templates/wishlist/base.html:8
+msgid "Crowd funding for the IPFire project"
+msgstr "Crowdfunding für das IPFire-Projekt"
 
 #: templates/index.html:92
 msgid "Flexibility"
@@ -1582,10 +2642,6 @@ msgstr "von Lightning Wire Labs"
 msgid "All mirrors"
 msgstr "Alle Mirrors"
 
-#: templates/admin-mirrors-details.html:17
-msgid "Status"
-msgstr "Status"
-
 #: templates/admin-mirrors-details.html:29
 msgid "GeoIP Location"
 msgstr "GeoIP Standort"
@@ -1594,126 +2650,175 @@ msgstr "GeoIP Standort"
 msgid "Filelist"
 msgstr "Dateiliste"
 
-#: templates/admin-accounts.html:8
-msgid "Create new account"
-msgstr "Neuen Account erstellen"
+#: templates/base.html:4
+msgid "No title given"
+msgstr "Kein Titel"
 
-#: templates/admin-accounts.html:13
-msgid "Name (Nickname)"
-msgstr "Name (Nickname)"
+#: templates/base.html:63
+msgid "IPFire is free software"
+msgstr "IPFire ist freie Software"
+
+#: templates/download-splash.html:3 templates/download-splash.html:7
+msgid "Thanks for downloading IPFire!"
+msgstr "Vielen Dank, für das Herunterladen von IPFire! "
+
+#: templates/download-splash.html:61
+msgid "Next steps"
+msgstr "Nächste Schritte"
+
+#: templates/download-splash.html:66
+msgid "Install IPFire"
+msgstr "IPFire installieren"
 
-#: templates/mirrors.html:25
-msgid "Mirror servers nearby"
-msgstr "Nahgelegene Mirrorserver"
+#: templates/download-splash.html:82
+msgid "Access documentation"
+msgstr "Dokumentation lesen"
 
-#: templates/mirrors.html:28 templates/mirrors.html:31
-msgid "Worldwide mirror servers"
-msgstr "Weltweite Mirrorserver"
+#: templates/download-splash.html:100
+msgid "Join the community"
+msgstr "Der Community beitreten"
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:229
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:239
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:239
 msgid "Monday"
 msgstr "Montag"
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:229
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:239
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:239
 msgid "Tuesday"
 msgstr "Dienstag"
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:229
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:239
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:239
 msgid "Wednesday"
 msgstr "Mittwoch"
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:229
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:239
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:239
 msgid "Thursday"
 msgstr "Donnerstag"
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:230
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:240
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:240
 msgid "Friday"
 msgstr "Freitag"
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:230
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:240
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:240
 msgid "Saturday"
 msgstr "Samstag"
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:230
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:240
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:240
 msgid "Sunday"
 msgstr "Sonntag"
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:274
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:290
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:290
 #, python-format
 msgid "1 second ago"
 msgid_plural "%(seconds)d seconds ago"
 msgstr[0] "vor einer Sekunde"
 msgstr[1] "vor %(seconds)d Sekunden"
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:279
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:295
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:295
 #, python-format
 msgid "1 minute ago"
 msgid_plural "%(minutes)d minutes ago"
 msgstr[0] "vor einer Minute"
 msgstr[1] "vor %(minutes)d Minuten"
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:283
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:299
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:299
 #, python-format
 msgid "1 hour ago"
 msgid_plural "%(hours)d hours ago"
 msgstr[0] "vor einer Stunde"
 msgstr[1] "vor %(hours)d Stunden"
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:287
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:303
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:303
 #, python-format
 msgid "%(time)s"
 msgstr "%(time)s"
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:290
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:306
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:306
 msgid "yesterday"
 msgstr "gestern"
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:291
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:307
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:307
 #, python-format
 msgid "yesterday at %(time)s"
 msgstr "gestern um %(time)s"
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:293
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:309
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:309
 #, python-format
 msgid "%(weekday)s"
 msgstr "%(weekday)s"
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:294
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:310
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:310
 #, python-format
 msgid "%(weekday)s at %(time)s"
 msgstr "%(weekday)s um %(time)s"
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:296
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:338
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:312
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:354
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:312
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:354
 #, python-format
 msgid "%(month_name)s %(day)s"
 msgstr "%(day)s. %(month_name)s"
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:297
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:313
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:313
 #, python-format
 msgid "%(month_name)s %(day)s at %(time)s"
 msgstr "%(day)s. %(month_name)s um %(time)s"
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:300
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:316
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:316
 #, python-format
 msgid "%(month_name)s %(day)s, %(year)s"
 msgstr "%(day)s %(month_name)s %(year)s"
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:301
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:317
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:317
 #, python-format
 msgid "%(month_name)s %(day)s, %(year)s at %(time)s"
 msgstr "%(day)s. %(month_name)s %(year)s um %(time)s"
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:332
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:348
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:348
 #, python-format
 msgid "%(weekday)s, %(month_name)s %(day)s"
 msgstr "%(weekday)s, %(day)s. %(month_name)s"
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:353
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:369
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:369
 #, python-format
 msgid "%(commas)s and %(last)s"
 msgstr "%(commas)s und %(last)s"
 
+#~ msgid "Your distance to this mirror"
+#~ msgstr "Deine Entfernung zu diesem Mirror"
+
+#~ msgid "Mirror location"
+#~ msgstr "Mirrorort"
+
+#~ msgid "The mirror <em>%s</em> is located in %s."
+#~ msgstr "Der Mirrorserver <em>%s</em> befindet sich in %s."
+
+#~ msgid "Mirror servers nearby"
+#~ msgstr "Nahgelegene Mirrorserver"
+
+#~ msgid "Worldwide mirror servers"
+#~ msgstr "Weltweite Mirrorserver"
+
 #~ msgid "Please donate!"
 #~ msgstr "Spenden!"
 
@@ -1867,9 +2972,6 @@ msgstr "%(commas)s und %(last)s"
 #~ msgid "Join the IRC channel"
 #~ msgstr "Dem IRC-Channel beitreten"
 
-#~ msgid "About"
-#~ msgstr "Ãœber"
-
 #~ msgid "Updates in the last 24 hours"
 #~ msgstr "Updates innerhalb der vergangenen 24 Stunden"
 
index d712f0589ff1601f3eea199d29896b52b62c3bd6..4caba936c615f66002936eebdbff7abbe2a7c87e 100644 (file)
@@ -8,1165 +8,2258 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-07-22 11:43+0200\n"
+"POT-Creation-Date: 2013-12-27 11:53+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 "Language: \n"
 "MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
 
-#: webapp/handlers_news.py:43 webapp/ui_modules.py:76
-msgid "Unknown author"
+#: webapp/backend/iuse.py:162
+#, python-format
+msgid "Mem: %s"
 msgstr ""
 
-#: webapp/__init__.py:285
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:225
-msgid "January"
+#: webapp/backend/iuse.py:165
+#, python-format
+msgid "Disk: %s"
 msgstr ""
 
-#: webapp/__init__.py:287
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:225
-msgid "February"
+#: webapp/backend/iuse.py:177
+#, python-format
+msgid "Networks: %s"
 msgstr ""
 
-#: webapp/__init__.py:289
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:225
-msgid "March"
+#: webapp/backend/countries.py:7
+msgid "Andorra"
 msgstr ""
 
-#: webapp/__init__.py:291
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:225
-msgid "April"
+#: webapp/backend/countries.py:8
+msgid "United Arab Emirates"
 msgstr ""
 
-#: webapp/__init__.py:293
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:226
-msgid "May"
+#: webapp/backend/countries.py:9
+msgid "Afghanistan"
 msgstr ""
 
-#: webapp/__init__.py:295
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:226
-msgid "June"
+#: webapp/backend/countries.py:10
+msgid "Antigua and Barbuda"
 msgstr ""
 
-#: webapp/__init__.py:297
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:226
-msgid "July"
+#: webapp/backend/countries.py:11
+msgid "Aanguilla"
 msgstr ""
 
-#: webapp/__init__.py:299
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:226
-msgid "August"
+#: webapp/backend/countries.py:12
+msgid "Albania"
 msgstr ""
 
-#: webapp/__init__.py:301
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:227
-msgid "September"
+#: webapp/backend/countries.py:13
+msgid "Armenia"
 msgstr ""
 
-#: webapp/__init__.py:303
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:227
-msgid "October"
+#: webapp/backend/countries.py:14
+msgid "Angola"
 msgstr ""
 
-#: webapp/__init__.py:305
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:227
-msgid "November"
+#: webapp/backend/countries.py:15
+msgid "Antarctica"
 msgstr ""
 
-#: webapp/__init__.py:307
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:227
-msgid "December"
+#: webapp/backend/countries.py:16
+msgid "Argentina"
 msgstr ""
 
-#: webapp/ui_modules.py:239
-#, python-format
-msgid "%s to %s"
+#: webapp/backend/countries.py:17
+msgid "American Samoa"
 msgstr ""
 
-#: webapp/backend/wishlist.py:187
-msgid "Checkout this crowdfunding wish from #ipfire:"
+#: webapp/backend/countries.py:18
+msgid "Austria"
 msgstr ""
 
-#: webapp/backend/releases.py:96
-msgid "Image for the armv5tel architecture"
+#: webapp/backend/countries.py:19
+msgid "Australia"
 msgstr ""
 
-#: webapp/backend/releases.py:97
-msgid "armv5tel image for boards with serial console"
+#: webapp/backend/countries.py:20
+msgid "Aruba"
 msgstr ""
 
-#: webapp/backend/releases.py:98
-msgid "Installable CD image"
+#: webapp/backend/countries.py:21
+msgid "Ã…land Islands"
 msgstr ""
 
-#: webapp/backend/releases.py:99
-msgid "Torrent file"
+#: webapp/backend/countries.py:22
+msgid "Azerbaijan"
 msgstr ""
 
-#: webapp/backend/releases.py:100
-msgid "Flash image"
+#: webapp/backend/countries.py:23
+msgid "Bosnia and Herzegovina"
 msgstr ""
 
-#: webapp/backend/releases.py:101
-msgid "Alix image"
+#: webapp/backend/countries.py:24
+msgid "Barbados"
 msgstr ""
 
-#: webapp/backend/releases.py:102
-msgid "USB FDD Image"
+#: webapp/backend/countries.py:25
+msgid "Bangladesh"
 msgstr ""
 
-#: webapp/backend/releases.py:103
-msgid "USB HDD Image"
+#: webapp/backend/countries.py:26
+msgid "Belgium"
 msgstr ""
 
-#: webapp/backend/releases.py:104
-msgid "Pregenerated Xen image"
+#: webapp/backend/countries.py:27
+msgid "Burkina Faso"
 msgstr ""
 
-#: webapp/backend/releases.py:110 webapp/backend/releases.py:150
-msgid "Unknown image type"
+#: webapp/backend/countries.py:28
+msgid "Bulgaria"
 msgstr ""
 
-#: webapp/backend/releases.py:136
-msgid "This image runs on many ARM-based boards"
+#: webapp/backend/countries.py:29
+msgid "Bahrain"
 msgstr ""
 
-#: webapp/backend/releases.py:137
-msgid "This image runs on ARM boards with a serial console"
+#: webapp/backend/countries.py:30
+msgid "Burundi"
 msgstr ""
 
-#: webapp/backend/releases.py:138
-msgid "Use this image to burn a CD and install IPFire from it."
+#: webapp/backend/countries.py:31
+msgid "Benin"
 msgstr ""
 
-#: webapp/backend/releases.py:139
-msgid "Download the CD image from the torrent network."
+#: webapp/backend/countries.py:32
+msgid "Saint Barthélemy"
 msgstr ""
 
-#: webapp/backend/releases.py:140
-msgid "An image that is meant to run on embedded devices."
+#: webapp/backend/countries.py:33
+msgid "Bermuda"
 msgstr ""
 
-#: webapp/backend/releases.py:141
-msgid "Flash image where a serial console is enabled by default."
+#: webapp/backend/countries.py:34
+msgid "Brunei Darussalam"
 msgstr ""
 
-#: webapp/backend/releases.py:142
-msgid "Install IPFire from a floppy-formated USB key."
+#: webapp/backend/countries.py:35
+msgid "Plurinational State of Bolivia"
 msgstr ""
 
-#: webapp/backend/releases.py:143
-msgid "If the floppy image doesn't work, use this image instead."
+#: webapp/backend/countries.py:36
+msgid "Sint Eustatius and Saba Bonaire"
 msgstr ""
 
-#: webapp/backend/releases.py:144
-msgid "A ready-to-run image for Xen."
+#: webapp/backend/countries.py:37
+msgid "Brazil"
 msgstr ""
 
-#: webapp/backend/iuse.py:165
-#, python-format
-msgid "Mem: %s"
+#: webapp/backend/countries.py:38
+msgid "Bahamas"
 msgstr ""
 
-#: webapp/backend/iuse.py:168
-#, python-format
-msgid "Disk: %s"
+#: webapp/backend/countries.py:39
+msgid "Bhutan"
 msgstr ""
 
-#: webapp/backend/iuse.py:180
-#, python-format
-msgid "Networks: %s"
+#: webapp/backend/countries.py:40
+msgid "Bouvet Island"
 msgstr ""
 
-#: templates/wishlist/wish.html:3
-msgid "Wish"
+#: webapp/backend/countries.py:41
+msgid "Botswana"
 msgstr ""
 
-#: templates/wishlist/wish.html:18
-#, python-format
-msgid "Launched: %s"
+#: webapp/backend/countries.py:42
+msgid "Belarus"
 msgstr ""
 
-#: templates/wishlist/wish.html:23
-#, python-format
-msgid "Funding ends: %s"
+#: webapp/backend/countries.py:43
+msgid "Belize"
 msgstr ""
 
-#: templates/wishlist/closed.html:3 templates/wishlist/closed.html:13
-msgid "Closed wishes"
+#: webapp/backend/countries.py:44
+msgid "Canada"
 msgstr ""
 
-#: templates/wishlist/closed.html:9
-msgid "Wishes open for donation"
+#: webapp/backend/countries.py:45
+msgid "Cocos (Keeling) Islands"
 msgstr ""
 
-#: templates/wishlist/closed.html:36 templates/wishlist/closed.html:40
-msgid "Older"
+#: webapp/backend/countries.py:46
+msgid "The Democratic Republic of the Congo"
 msgstr ""
 
-#: templates/wishlist/closed.html:46 templates/wishlist/closed.html:50
-msgid "Newer"
+#: webapp/backend/countries.py:47
+msgid "Central African Republic"
 msgstr ""
 
-#: templates/wishlist/base.html:6 templates/index.html:175
-msgid "IPFire Wishlist"
+#: webapp/backend/countries.py:48
+msgid "Congo"
 msgstr ""
 
-#: templates/wishlist/base.html:8
-msgid "Crowd funding for the IPFire project"
+#: webapp/backend/countries.py:49
+msgid "Switzerland"
 msgstr ""
 
-#: templates/wishlist/modules/wish.html:6
-msgid "Only a few days left!"
+#: webapp/backend/countries.py:50
+msgid "Côte d'Ivoire"
 msgstr ""
 
-#: templates/wishlist/modules/wish.html:26 templates/wishlist/donate.html:3
-#: templates/download-splash.html:37 templates/modules/menu.html:47
-#: templates/static/getinvolved.html:120 templates/static/donation.html:3
-#: templates/static/donation.html:8
-msgid "Donate"
+#: webapp/backend/countries.py:51
+msgid "Cook Islands"
 msgstr ""
 
-#: templates/wishlist/modules/wish.html:36
-msgid "funded"
+#: webapp/backend/countries.py:52
+msgid "Chile"
 msgstr ""
 
-#: templates/wishlist/modules/wish.html:40
-#, python-format
-msgid "%s &euro;"
+#: webapp/backend/countries.py:53
+msgid "Cameroon"
 msgstr ""
 
-#: templates/wishlist/modules/wish.html:41
-msgid "donated"
+#: webapp/backend/countries.py:54
+msgid "China"
 msgstr ""
 
-#: templates/wishlist/modules/wish.html:47
-msgid "day to go"
-msgid_plural "days to go"
-msgstr[0] ""
-msgstr[1] ""
+#: webapp/backend/countries.py:55
+msgid "Colombia"
+msgstr ""
 
-#: templates/wishlist/modules/wish.html:49
-msgid "In progress"
+#: webapp/backend/countries.py:56
+msgid "Costa Rica"
 msgstr ""
 
-#: templates/wishlist/modules/wish.html:51
-msgid "Finished"
+#: webapp/backend/countries.py:57
+msgid "Cuba"
 msgstr ""
 
-#: templates/wishlist/modules/wish.html:53
-msgid "Funding ended"
+#: webapp/backend/countries.py:58
+msgid "Cape Verde"
 msgstr ""
 
-#: templates/wishlist/modules/wish.html:63
-msgid "Share this wish with your friends and help us promote it!"
+#: webapp/backend/countries.py:59
+msgid "Curaçao"
 msgstr ""
 
-#: templates/wishlist/index.html:3 templates/index.html:3
-msgid "Home"
+#: webapp/backend/countries.py:60
+msgid "Chrismas Islands"
 msgstr ""
 
-#: templates/wishlist/donate.html:13 templates/wishlist/donate.html:23
-#: templates/wishlist/terms.html:3 templates/wishlist/terms.html:7
-#: templates/base.html:78
-msgid "Terms & Conditions"
+#: webapp/backend/countries.py:61
+msgid "Cyprus"
 msgstr ""
 
-#: templates/downloads-index.html:3
-msgid "Download Center"
+#: webapp/backend/countries.py:62
+msgid "Czech Republic"
 msgstr ""
 
-#: templates/downloads-index.html:7
-msgid "IPFire Download Center"
+#: webapp/backend/countries.py:63
+msgid "Germany"
 msgstr ""
 
-#: templates/downloads-index.html:30
-msgid "Available releases"
+#: webapp/backend/countries.py:64
+msgid "Djibouti"
 msgstr ""
 
-#: templates/downloads-index.html:35 templates/downloads-older.html:25
-#: templates/tracker-torrents.html:32
-msgid "Release"
+#: webapp/backend/countries.py:65
+msgid "Denmark"
 msgstr ""
 
-#: templates/downloads-index.html:36
-msgid "Release type"
+#: webapp/backend/countries.py:66
+msgid "Dominica"
 msgstr ""
 
-#: templates/downloads-index.html:37
-msgid "Release date"
+#: webapp/backend/countries.py:67
+msgid "Dominican Republic"
 msgstr ""
 
-#: templates/downloads-index.html:48
-msgid "Stable"
+#: webapp/backend/countries.py:68
+msgid "Algeria"
 msgstr ""
 
-#: templates/downloads-index.html:50 templates/modules/menu.html:65
-#: templates/static/development.html:3 templates/static/development.html:8
-#: templates/static/getinvolved.html:153
-msgid "Development"
+#: webapp/backend/countries.py:69
+msgid "Ecuador"
 msgstr ""
 
-#: templates/admin-downloads-base.html:5
-msgid "Back to home"
+#: webapp/backend/countries.py:70
+msgid "Estonia"
 msgstr ""
 
-#: templates/admin-downloads-base.html:8 templates/admin-base.html:6
-msgid "Options"
+#: webapp/backend/countries.py:71
+msgid "Egypt"
 msgstr ""
 
-#: templates/admin-downloads-base.html:11
-msgid "Mirror stats"
+#: webapp/backend/countries.py:72
+msgid "Western Sahara"
 msgstr ""
 
-#: templates/downloads-older.html:3 templates/downloads-older.html:7
-msgid "Ancient downloads"
+#: webapp/backend/countries.py:73
+msgid "Eritrea"
 msgstr ""
 
-#: templates/downloads-older.html:9 templates/downloads-all.html:9
-msgid ""
-"These are the ancient downloads of IPFire. They are just saved for "
-"historical reasons and should not be used in a productive environment."
+#: webapp/backend/countries.py:74
+msgid "Spain"
 msgstr ""
 
-#: templates/downloads-older.html:13
-msgid ""
-"Beware that these releases could lack possible security-fixes\tand so it is "
-"recommended to use the <em>latest</em> version."
+#: webapp/backend/countries.py:75
+msgid "Ethiopia"
 msgstr ""
 
-#: templates/downloads-older.html:17 templates/downloads-all.html:13
-msgid "Go back to latest stable downloads."
+#: webapp/backend/countries.py:76
+msgid "Finland"
 msgstr ""
 
-#: templates/downloads-older.html:26
-msgid "Published on"
+#: webapp/backend/countries.py:77
+msgid "Fiji"
 msgstr ""
 
-#: templates/admin-login.html:3
-msgid "Please login"
+#: webapp/backend/countries.py:78
+msgid "Falkland Islands (Malvinas)"
 msgstr ""
 
-#: templates/mirrors-item.html:3
-#, python-format
-msgid "Mirror %s"
+#: webapp/backend/countries.py:79
+msgid "Federated States of Micronesia"
 msgstr ""
 
-#: templates/mirrors-item.html:8
-msgid "Up"
+#: webapp/backend/countries.py:80
+msgid "Faroe Islands"
 msgstr ""
 
-#: templates/mirrors-item.html:10
-msgid "Down"
+#: webapp/backend/countries.py:81
+msgid "France"
 msgstr ""
 
-#: templates/mirrors-item.html:12
-msgid "Out of sync"
+#: webapp/backend/countries.py:82
+msgid "Gabon"
 msgstr ""
 
-#: templates/mirrors-item.html:14 templates/fireinfo/profile-detail.html:74
-#: templates/fireinfo/profile-detail.html:82
-msgid "Unknown"
+#: webapp/backend/countries.py:83
+msgid "United Kingdom"
 msgstr ""
 
-#: templates/mirrors-item.html:22 templates/downloads-mirrors.html:50
-#: templates/fireinfo/profile-detail.html:231
-#: templates/download-mirror-detail.html:20 templates/admin-mirrors.html:15
-msgid "Last update"
+#: webapp/backend/countries.py:84
+msgid "Grenada"
 msgstr ""
 
-#: templates/mirrors-item.html:26 templates/downloads-mirrors.html:47
-#: templates/admin-mirrors-create.html:20
-#: templates/download-mirror-detail.html:16
-#: templates/admin-mirrors-details.html:21
-msgid "Owner"
+#: webapp/backend/countries.py:85
+msgid "Georgia"
 msgstr ""
 
-#: templates/mirrors-item.html:31
-msgid "Preferred for"
+#: webapp/backend/countries.py:86
+msgid "French Guiana"
 msgstr ""
 
-#: templates/mirrors-item.html:37
-msgid "Your distance to this mirror"
+#: webapp/backend/countries.py:87
+msgid "Guersey"
 msgstr ""
 
-#: templates/mirrors-item.html:43
-msgid "Go to mirror"
+#: webapp/backend/countries.py:88
+msgid "Ghana"
 msgstr ""
 
-#: templates/mirrors-item.html:48
-msgid "Mirror location"
+#: webapp/backend/countries.py:89
+msgid "Gibraltar"
 msgstr ""
 
-#: templates/mirrors-item.html:50
-#, python-format
-msgid "The mirror <em>%s</em> is located in %s."
+#: webapp/backend/countries.py:90
+msgid "Greenland"
 msgstr ""
 
-#: templates/mirrors-item.html:58
-msgid "View larger map"
+#: webapp/backend/countries.py:91
+msgid "Gambia"
 msgstr ""
 
-#: templates/mirrors-item.html:63
-msgid "The location of the mirror server is estimated by the IP address."
+#: webapp/backend/countries.py:92
+msgid "Guinea"
 msgstr ""
 
-#: templates/mirrors-item.html:66
-msgid "The location of the mirror server could not be estimated."
+#: webapp/backend/countries.py:93
+msgid "Guadeloupe"
 msgstr ""
 
-#: templates/admin-downloads-mirrors.html:5 templates/admin-downloads.html:5
-msgid "Download statistics"
+#: webapp/backend/countries.py:94
+msgid "Equatorial Guinea"
 msgstr ""
 
-#: templates/admin-downloads-mirrors.html:7
-msgid "Mirror load from today"
+#: webapp/backend/countries.py:95
+msgid "Greece"
 msgstr ""
 
-#: templates/admin-downloads-mirrors.html:10
-msgid "Mirror load"
+#: webapp/backend/countries.py:96
+msgid "South Georgia and the South Sandwich Islands"
 msgstr ""
 
-#: templates/news-year.html:3 templates/news.html:3
-msgid "News"
+#: webapp/backend/countries.py:97
+msgid "Guatemala"
 msgstr ""
 
-#: templates/news-year.html:7
-#, python-format
-msgid "News from %(year)s"
+#: webapp/backend/countries.py:98
+msgid "Guam"
 msgstr ""
 
-#: templates/tracker-torrent-detail.html:3
-#: templates/tracker-torrent-detail.html:18
-#: templates/modules/release-item.html:115
-msgid "Torrent download"
+#: webapp/backend/countries.py:99
+msgid "Guinea-Bissau"
 msgstr ""
 
-#: templates/tracker-torrent-detail.html:15
-#: templates/modules/release-item.html:114
-msgid "Magnet link"
+#: webapp/backend/countries.py:100
+msgid "Guyana"
 msgstr ""
 
-#: templates/tracker-torrent-detail.html:23 templates/tracker-torrents.html:34
-msgid "Peers"
+#: webapp/backend/countries.py:101
+msgid "Hong Kong"
 msgstr ""
 
-#: templates/tracker-torrent-detail.html:28
-msgid "Seeds"
+#: webapp/backend/countries.py:102
+msgid "Heard Island and McDonald Islands"
 msgstr ""
 
-#: templates/sources.html:3
-msgid "Sources"
+#: webapp/backend/countries.py:103
+msgid "Honduras"
 msgstr ""
 
-#: templates/sources.html:6 templates/static/development.html:263
-msgid "Source Code"
+#: webapp/backend/countries.py:104
+msgid "Croatia"
 msgstr ""
 
-#: templates/sources.html:9
-#, python-format
-msgid "There are %s source files on the server."
+#: webapp/backend/countries.py:105
+msgid "Haiti"
 msgstr ""
 
-#: templates/news-author.html:7
-#, python-format
-msgid "%s's announcements"
+#: webapp/backend/countries.py:106
+msgid "Hungary"
 msgstr ""
 
-#: templates/admin-index.html:5
-msgid "Admin Area"
+#: webapp/backend/countries.py:107
+msgid "Indonesia"
 msgstr ""
 
-#: templates/admin-base.html:3
-msgid "IPFire Admin Area"
+#: webapp/backend/countries.py:108
+msgid "Ireland"
 msgstr ""
 
-#: templates/admin-base.html:9
-msgid "Accounts"
+#: webapp/backend/countries.py:109
+msgid "Israel"
 msgstr ""
 
-#: templates/admin-base.html:10 templates/modules/menu.html:110
-msgid "Mirrors"
+#: webapp/backend/countries.py:110
+msgid "Isle of Man"
 msgstr ""
 
-#: templates/admin-base.html:11 templates/modules/menu.html:92
-msgid "Planet"
+#: webapp/backend/countries.py:111
+msgid "India"
 msgstr ""
 
-#: templates/admin-base.html:12
-msgid "Downloads"
+#: webapp/backend/countries.py:112
+msgid "British Indian Ocean Territory"
 msgstr ""
 
-#: templates/admin-planet-compose.html:5 templates/admin-planet.html:8
-msgid "Compose new entry"
+#: webapp/backend/countries.py:113
+msgid "Iraq"
 msgstr ""
 
-#: templates/admin-planet-compose.html:16 templates/admin-planet.html:14
-msgid "Title"
+#: webapp/backend/countries.py:114
+msgid "Islamic Republic of Iran"
 msgstr ""
 
-#: templates/admin-planet-compose.html:33 templates/planet/posting.html:19
-msgid "Tags"
+#: webapp/backend/countries.py:115
+msgid "Iceland"
 msgstr ""
 
-#: templates/admin-planet-compose.html:41
-msgid "Save"
+#: webapp/backend/countries.py:116
+msgid "Italy"
 msgstr ""
 
-#: templates/admin-planet-compose.html:42
-msgid "Preview"
+#: webapp/backend/countries.py:117
+msgid "Jersey"
 msgstr ""
 
-#: templates/admin-planet-compose.html:43
-msgid "Cancel"
+#: webapp/backend/countries.py:118
+msgid "Jamaica"
 msgstr ""
 
-#: templates/download-splash.html:3 templates/download-splash.html:7
-msgid "Thanks for downloading IPFire!"
+#: webapp/backend/countries.py:119
+msgid "Jordan"
 msgstr ""
 
-#: templates/download-splash.html:61
-msgid "Next steps"
+#: webapp/backend/countries.py:120
+msgid "Japan"
 msgstr ""
 
-#: templates/download-splash.html:66
-msgid "Install IPFire"
+#: webapp/backend/countries.py:121
+msgid "Kenya"
 msgstr ""
 
-#: templates/download-splash.html:82
-msgid "Access documentation"
+#: webapp/backend/countries.py:122
+msgid "Kyrgyzstan"
 msgstr ""
 
-#: templates/download-splash.html:100
-msgid "Join the community"
+#: webapp/backend/countries.py:123
+msgid "Cambodia"
 msgstr ""
 
-#: templates/downloads-development.html:3
-#: templates/downloads-development.html:6
-msgid "Development Downloads"
+#: webapp/backend/countries.py:124
+msgid "Kiribati"
 msgstr ""
 
-#: templates/error-500.html:6
-msgid "Detailed information"
+#: webapp/backend/countries.py:125
+msgid "Comoros"
 msgstr ""
 
-#: templates/admin-planet.html:5
-msgid "Planet Administrator"
+#: webapp/backend/countries.py:126
+msgid "Saint Kitts and Nevis"
 msgstr ""
 
-#: templates/admin-planet.html:13
-msgid "Author"
+#: webapp/backend/countries.py:127
+msgid "Democratic People's Republic of Korea"
 msgstr ""
 
-#: templates/admin-planet.html:21 templates/admin-mirrors.html:24
-#: templates/admin-accounts.html:20
-msgid "Edit"
+#: webapp/backend/countries.py:128
+msgid "Republic of Korea"
 msgstr ""
 
-#: templates/downloads-mirrors.html:3 templates/download-mirror-detail.html:3
-msgid "Mirror-Server"
+#: webapp/backend/countries.py:129
+msgid "Kuwait"
 msgstr ""
 
-#: templates/downloads-mirrors.html:8 templates/download-mirror-detail.html:8
-msgid "IPFire Mirrors"
+#: webapp/backend/countries.py:130
+msgid "Cayman Islands"
 msgstr ""
 
-#: templates/downloads-mirrors.html:48 templates/admin-mirrors-create.html:12
-#: templates/download-mirror-detail.html:12 templates/admin-mirrors.html:14
-#: templates/admin-mirrors-details.html:13
-msgid "Hostname"
+#: webapp/backend/countries.py:131
+msgid "Kazakhstan"
 msgstr ""
 
-#: templates/downloads-mirrors.html:49 templates/admin-mirrors-create.html:24
-#: templates/admin-mirrors-details.html:25
-msgid "Location"
+#: webapp/backend/countries.py:132
+msgid "Lao People's Democratic Public"
 msgstr ""
 
-#: templates/downloads-mirrors.html:63
-msgid "details"
+#: webapp/backend/countries.py:133
+msgid "Lebanon"
 msgstr ""
 
-#: templates/fireinfo/stats.html:3 templates/modules/menu.html:136
-msgid "Statistics"
+#: webapp/backend/countries.py:134
+msgid "Saint Lucia"
 msgstr ""
 
-#: templates/fireinfo/stats.html:7
-msgid "fireinfo statistics"
+#: webapp/backend/countries.py:135
+msgid "Liechtenstein"
 msgstr ""
 
-#: templates/fireinfo/stats.html:17
-msgid "IPFire versions"
+#: webapp/backend/countries.py:136
+msgid "Sri Lanka"
 msgstr ""
 
-#: templates/fireinfo/stats.html:38 templates/fireinfo/stats-geo.html:3
-#: templates/fireinfo/stats-geo.html:7
-msgid "Geo locations"
+#: webapp/backend/countries.py:137
+msgid "Liberia"
 msgstr ""
 
-#: templates/fireinfo/stats.html:59 templates/fireinfo/stats-cpus.html:7
-msgid "Hardware: CPUs"
+#: webapp/backend/countries.py:138
+msgid "Lesotho"
 msgstr ""
 
-#: templates/fireinfo/stats.html:73
-msgid "CPU features"
+#: webapp/backend/countries.py:139
+msgid "Lithuania"
 msgstr ""
 
-#: templates/fireinfo/stats.html:85
-msgid "Hardware: Memory"
+#: webapp/backend/countries.py:140
+msgid "Luxembourg"
 msgstr ""
 
-#: templates/fireinfo/stats.html:106 templates/fireinfo/stats-network.html:3
-#: templates/fireinfo/profile-detail.html:95
-msgid "Network"
+#: webapp/backend/countries.py:141
+msgid "Latvia"
 msgstr ""
 
-#: templates/fireinfo/stats.html:127 templates/fireinfo/stats-virtual.html:3
-#: templates/base-feature.html:65 templates/static/features.html:1179
-#: templates/static/features.html:1404
-msgid "Virtualization"
+#: webapp/backend/countries.py:142
+msgid "Libya"
 msgstr ""
 
-#: templates/fireinfo/stats-network.html:7
-msgid "Network configuration"
+#: webapp/backend/countries.py:143
+msgid "Morocco"
 msgstr ""
 
-#: templates/fireinfo/stats-oses.html:3
-#: templates/fireinfo/profile-detail.html:12
-msgid "Operating system"
+#: webapp/backend/countries.py:144
+msgid "Monaco"
 msgstr ""
 
-#: templates/fireinfo/stats-oses.html:7
-msgid "Releases"
+#: webapp/backend/countries.py:145
+msgid "Republic of Moldova"
 msgstr ""
 
-#: templates/fireinfo/stats-oses.html:18
-msgid "Architectures"
+#: webapp/backend/countries.py:146
+msgid "Montenegro"
 msgstr ""
 
-#: templates/fireinfo/stats-oses.html:25
-msgid "Kernels"
+#: webapp/backend/countries.py:147
+msgid "Saint Martin (French Part)"
 msgstr ""
 
-#: templates/fireinfo/profile-notfound.html:3
-#: templates/fireinfo/profile-notfound.html:8
-msgid "Profile not found"
+#: webapp/backend/countries.py:148
+msgid "Madagascar"
 msgstr ""
 
-#: templates/fireinfo/stats-virtual.html:7
-msgid "Virtualization support"
+#: webapp/backend/countries.py:149
+msgid "Marshall Islands"
 msgstr ""
 
-#: templates/fireinfo/stats-virtual.html:35
-msgid "Hypervisors"
+#: webapp/backend/countries.py:150
+msgid "The former Yugoslav Republic of Macedonia"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:3
-#: templates/fireinfo/profile-detail.html:7
-msgid "Profile"
+#: webapp/backend/countries.py:151
+msgid "Mali"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:16
-msgid "Version"
+#: webapp/backend/countries.py:152
+msgid "Myanmar"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:24
-msgid "Architecture"
+#: webapp/backend/countries.py:153
+msgid "Mongolia"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:32
-msgid "Kernel version"
+#: webapp/backend/countries.py:154
+msgid "Macao"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:43
-msgid "Hypervisor"
+#: webapp/backend/countries.py:155
+msgid "Northern Mariana Islands"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:45
-msgid "This machine is running in a virtual environment."
+#: webapp/backend/countries.py:156
+msgid "Martinique"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:51
-#: templates/fireinfo/profile-detail.html:71
-#: templates/fireinfo/profile-detail.html:130
-msgid "Vendor"
+#: webapp/backend/countries.py:157
+msgid "Mauritania"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:59
-msgid "Type"
+#: webapp/backend/countries.py:158
+msgid "Montserrat"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:67
-msgid "Hardware vendor"
+#: webapp/backend/countries.py:159
+msgid "Malta"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:79
-#: templates/fireinfo/profile-detail.html:138
-msgid "Model"
+#: webapp/backend/countries.py:160
+msgid "Mauritius"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:97
-msgid "Enabled network zones"
+#: webapp/backend/countries.py:161
+msgid "Maldives"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:119 templates/base-feature.html:62
-#: templates/static/features.html:1104 templates/static/features.html:1399
-msgid "Hardware"
+#: webapp/backend/countries.py:162
+msgid "Malawi"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:125
-msgid "CPU"
+#: webapp/backend/countries.py:163
+msgid "Mexico"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:141
-msgid "Not available"
+#: webapp/backend/countries.py:164
+msgid "Malaysia"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:146
-msgid "Cores"
+#: webapp/backend/countries.py:165
+msgid "Mozambique"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:154
-#: templates/fireinfo/stats-cpus.html:36
-msgid "Speed"
+#: webapp/backend/countries.py:166
+msgid "Namibia"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:166
-msgid "Supported features"
+#: webapp/backend/countries.py:167
+msgid "New Caledonia"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:170
-msgid "64 bit capable"
+#: webapp/backend/countries.py:168
+msgid "Niger"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:174
-msgid "PAE capable"
+#: webapp/backend/countries.py:169
+msgid "Norfolk Island"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:178
-msgid "VT-x/AMD-V"
+#: webapp/backend/countries.py:170
+msgid "Nigeria"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:190
-msgid "Memory size"
+#: webapp/backend/countries.py:171
+msgid "Nicaragua"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:198
-msgid "System disk size"
+#: webapp/backend/countries.py:172
+msgid "Netherlands"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:211
-msgid "Peripherial devices"
+#: webapp/backend/countries.py:173
+msgid "Norway"
 msgstr ""
 
-#: templates/fireinfo/profile-detail.html:216
-msgid "Signature images"
+#: webapp/backend/countries.py:174
+msgid "Nepal"
 msgstr ""
 
-#: templates/fireinfo/base.html:6 templates/modules/menu.html:98
-msgid "Fireinfo"
+#: webapp/backend/countries.py:175
+msgid "Nauru"
 msgstr ""
 
-#: templates/fireinfo/base.html:7
-msgid "A hardware data collection tool for IPFire"
+#: webapp/backend/countries.py:176
+msgid "Niue"
 msgstr ""
 
-#: templates/fireinfo/stats-cpus.html:3
-msgid "Processors"
+#: webapp/backend/countries.py:177
+msgid "New Zealand"
 msgstr ""
 
-#: templates/fireinfo/stats-cpus.html:12
-msgid "See statistics about common CPU flags"
+#: webapp/backend/countries.py:178
+msgid "Oman"
 msgstr ""
 
-#: templates/fireinfo/stats-cpus.html:19
-msgid "Vendors"
+#: webapp/backend/countries.py:179
+msgid "Panama"
 msgstr ""
 
-#: templates/fireinfo/stats-cpus.html:38
-#, python-format
-msgid ""
-"The average speed of all systems in the database is: <strong>%.2f MHz</"
-"strong>."
+#: webapp/backend/countries.py:180
+msgid "Peru"
 msgstr ""
 
-#: templates/fireinfo/stats-cpus.html:40
-#, python-format
-msgid "All together, there are <strong>%s bogomips</strong> out there."
+#: webapp/backend/countries.py:181
+msgid "French Polynesia"
 msgstr ""
 
-#: templates/fireinfo/stats-cpus.html:48
-msgid "CPU core counter"
+#: webapp/backend/countries.py:182
+msgid "Papua New Guinea"
 msgstr ""
 
-#: templates/fireinfo/stats-cpus.html:50
-msgid ""
-"See a breakdown of the CPU cores that are installed on the IPFire systems."
+#: webapp/backend/countries.py:183
+msgid "Philipplines"
 msgstr ""
 
-#: templates/fireinfo/index.html:52
-msgid "Show"
+#: webapp/backend/countries.py:184
+msgid "Pakistan"
 msgstr ""
 
-#: templates/fireinfo/model-detail.html:11
-#, python-format
-msgid "This device is installed on approximately %.2f%% of all systems."
+#: webapp/backend/countries.py:185
+msgid "Poland"
 msgstr ""
 
-#: templates/fireinfo/model-detail.html:14
-msgid "Feedback"
+#: webapp/backend/countries.py:186
+msgid "Saint Pierre and Miquelon"
 msgstr ""
 
-#: templates/fireinfo/model-detail.html:28
-msgid "Go to the wiki"
+#: webapp/backend/countries.py:187
+msgid "Pitcairn"
 msgstr ""
 
-#: templates/fireinfo/stats-memory.html:3
-#: templates/fireinfo/stats-memory.html:7
-msgid "Memory"
+#: webapp/backend/countries.py:188
+msgid "Puerto Rico"
 msgstr ""
 
-#: templates/fireinfo/stats-memory.html:13
-#, python-format
-msgid ""
-"The average amount of memory of all systems in the database is: <strong>%.2f "
-"MB</strong>."
+#: webapp/backend/countries.py:189
+msgid "Palestinian Territory, occupied"
 msgstr ""
 
-#: templates/fireinfo/stats-geo.html:28
-msgid "Language selection"
+#: webapp/backend/countries.py:190
+msgid "Portugal"
 msgstr ""
 
-#: templates/fireinfo/stats-cpu-flags.html:3
-msgid "CPU flags"
+#: webapp/backend/countries.py:191
+msgid "Palau"
 msgstr ""
 
-#: templates/fireinfo/stats-cpu-flags.html:7
-msgid "Processor flags"
+#: webapp/backend/countries.py:192
+msgid "Paraguay"
 msgstr ""
 
-#: templates/fireinfo/stats-cpu-flags.html:30
-msgid "CPUs that support 64 bits"
+#: webapp/backend/countries.py:193
+msgid "Qatar"
 msgstr ""
 
-#: templates/fireinfo/stats-cpu-flags.html:46
-msgid "CPUs with PAE"
+#: webapp/backend/countries.py:194
+msgid "Réunion"
 msgstr ""
 
-#: templates/fireinfo/stats-cpu-flags.html:62
-msgid "CPUs that support virtualization"
+#: webapp/backend/countries.py:195
+msgid "Romania"
 msgstr ""
 
-#: templates/fireinfo/stats-admin.html:3
-msgid "Admin"
+#: webapp/backend/countries.py:196
+msgid "Serbia"
 msgstr ""
 
-#: templates/fireinfo/stats-admin.html:11
-msgid "Sending profiles"
+#: webapp/backend/countries.py:197
+msgid "Russian Federation"
 msgstr ""
 
-#: templates/fireinfo/stats-admin.html:19
-msgid "Archive size"
+#: webapp/backend/countries.py:198
+msgid "Rwanda"
 msgstr ""
 
-#: templates/downloads.html:3 templates/downloads.html:8
-msgid "Get IPFire"
+#: webapp/backend/countries.py:199
+msgid "Saudi Arabia"
 msgstr ""
 
-#: templates/downloads.html:10
-msgid "IPFire is completely free to download and use"
+#: webapp/backend/countries.py:200
+msgid "Solomon Islands"
 msgstr ""
 
-#: templates/downloads.html:23
-msgid "More download options"
+#: webapp/backend/countries.py:201
+msgid "Seychelles"
 msgstr ""
 
-#: templates/downloads.html:55
-msgid "Get yourself involved"
+#: webapp/backend/countries.py:202
+msgid "Sudan"
 msgstr ""
 
-#: templates/admin-mirrors-create.html:5 templates/admin-mirrors.html:8
-msgid "Create new mirror"
+#: webapp/backend/countries.py:203
+msgid "Sweden"
 msgstr ""
 
-#: templates/admin-mirrors-create.html:16
-msgid "Path"
+#: webapp/backend/countries.py:204
+msgid "Singapore"
 msgstr ""
 
-#: templates/admin-mirrors-create.html:28
-#: templates/admin-mirrors-details.html:33
-msgid "File mirror"
+#: webapp/backend/countries.py:205
+msgid "Saint Helena, Ascension and Tristan Da Cunha"
 msgstr ""
 
-#: templates/admin-mirrors-create.html:31
-#: templates/admin-mirrors-create.html:40
-#: templates/admin-mirrors-create.html:49
-#: templates/admin-mirrors-create.html:58
-msgid "yes"
+#: webapp/backend/countries.py:206
+msgid "Slovenia"
 msgstr ""
 
-#: templates/admin-mirrors-create.html:32
-#: templates/admin-mirrors-create.html:41
-#: templates/admin-mirrors-create.html:50
-#: templates/admin-mirrors-create.html:59
-msgid "no"
+#: webapp/backend/countries.py:207
+msgid "Svalbard and Jan Mayen"
 msgstr ""
 
-#: templates/admin-mirrors-create.html:37
-#: templates/admin-mirrors-details.html:37
-msgid "Pakfire 2 mirror"
+#: webapp/backend/countries.py:208
+msgid "Slovakia"
 msgstr ""
 
-#: templates/admin-mirrors-create.html:46
-#: templates/admin-mirrors-details.html:41
-msgid "Pakfire 3 mirror"
+#: webapp/backend/countries.py:209
+msgid "Sierra Leone"
 msgstr ""
 
-#: templates/admin-mirrors-create.html:55
-#: templates/admin-mirrors-details.html:45
-msgid "Disabled?"
+#: webapp/backend/countries.py:210
+msgid "San Marino"
 msgstr ""
 
-#: templates/admin-downloads.html:7
-msgid "Download counters"
+#: webapp/backend/countries.py:211
+msgid "Senegal"
 msgstr ""
 
-#: templates/admin-downloads.html:10
-msgid "Today"
+#: webapp/backend/countries.py:212
+msgid "Somalia"
 msgstr ""
 
-#: templates/admin-downloads.html:14
-msgid "Yesterday"
+#: webapp/backend/countries.py:213
+msgid "Suriname"
 msgstr ""
 
-#: templates/admin-downloads.html:18
-msgid "Total"
+#: webapp/backend/countries.py:214
+msgid "South Sudan"
 msgstr ""
 
-#: templates/admin-downloads.html:23
-msgid "Downloads by country"
+#: webapp/backend/countries.py:215
+msgid "Sao Tome and Principe"
 msgstr ""
 
-#: templates/news.html:7
-msgid "What is new on the IPFire project?"
+#: webapp/backend/countries.py:216
+msgid "El Salvador"
 msgstr ""
 
-#: templates/news.html:34
-msgid "Stay up to date"
+#: webapp/backend/countries.py:217
+msgid "Sint Maarten (Dutch Part)"
 msgstr ""
 
-#: templates/news.html:61 templates/planet/year.html:3
-#: templates/planet/user.html:3 templates/planet/search.html:3
-#: templates/planet/base.html:6 templates/planet/index.html:3
-#: templates/planet/posting.html:3 templates/index.html:154
-msgid "IPFire Planet"
+#: webapp/backend/countries.py:218
+msgid "Syrian Arab Republic"
 msgstr ""
 
-#: templates/news.html:85
-msgid "Latest news"
+#: webapp/backend/countries.py:219
+msgid "Swaziland"
 msgstr ""
 
-#: templates/base-feature.html:6 templates/modules/menu.html:7
-#: templates/static/features.html:3 templates/static/features.html:8
-#: templates/static/features.html:1349
-msgid "About IPFire"
+#: webapp/backend/countries.py:220
+msgid "Turks and Caicos Islands"
 msgstr ""
 
-#: templates/base-feature.html:9 templates/static/features.html:88
-#: templates/static/features.html:1354 templates/index.html:75
-msgid "Security"
+#: webapp/backend/countries.py:221
+msgid "Chad"
 msgstr ""
 
-#: templates/base-feature.html:12 templates/static/features.html:139
-#: templates/static/features.html:1359
-msgid "Firewall"
+#: webapp/backend/countries.py:222
+msgid "French Southern Territories"
 msgstr ""
 
-#: templates/base-feature.html:15 templates/static/features.html:1364
-msgid "Pakfire"
+#: webapp/backend/countries.py:223
+msgid "Togo"
 msgstr ""
 
-#: templates/base-feature.html:18 templates/static/features.html:432
-#: templates/static/features.html:1369
-msgid "Updates"
+#: webapp/backend/countries.py:224
+msgid "Thailand"
 msgstr ""
 
-#: templates/base-feature.html:21
-msgid "It's free"
+#: webapp/backend/countries.py:225
+msgid "Tajikistan"
 msgstr ""
 
-#: templates/base-feature.html:25
-msgid "Features"
+#: webapp/backend/countries.py:226
+msgid "Tokelau"
 msgstr ""
 
-#: templates/base-feature.html:28 templates/static/features.html:502
-#: templates/static/features.html:1374
-msgid "Dialup"
+#: webapp/backend/countries.py:227
+msgid "Timor-Leste"
 msgstr ""
 
-#: templates/base-feature.html:31 templates/static/features.html:616
-msgid "Web proxy"
+#: webapp/backend/countries.py:228
+msgid "Turkmenistan"
 msgstr ""
 
-#: templates/base-feature.html:35 templates/static/features.html:705
-msgid "Content filter"
+#: webapp/backend/countries.py:229
+msgid "Tunisia"
 msgstr ""
 
-#: templates/base-feature.html:38 templates/static/features.html:766
-msgid "Update accelerator"
+#: webapp/backend/countries.py:230
+msgid "Tonga"
 msgstr ""
 
-#: templates/base-feature.html:41
-msgid "Virus scan"
+#: webapp/backend/countries.py:231
+msgid "Turkey"
 msgstr ""
 
-#: templates/base-feature.html:45 templates/static/features.html:818
-#: templates/static/features.html:1384
-msgid "VPN"
+#: webapp/backend/countries.py:232
+msgid "Trinidad and Tobago"
 msgstr ""
 
-#: templates/base-feature.html:49 templates/static/features.html:854
-msgid "IPsec"
+#: webapp/backend/countries.py:233
+msgid "Tuvalu"
 msgstr ""
 
-#: templates/base-feature.html:52 templates/static/features.html:915
-msgid "OpenVPN"
+#: webapp/backend/countries.py:234
+msgid "Taiwan, Province of China"
 msgstr ""
 
-#: templates/base-feature.html:56
-msgid "Intrusion detection"
+#: webapp/backend/countries.py:235
+msgid "United Republic of Tanzania"
 msgstr ""
 
-#: templates/base-feature.html:59 templates/static/features.html:1031
-#: templates/static/features.html:1394
-msgid "Quality of Service"
+#: webapp/backend/countries.py:236
+msgid "Ukraine"
 msgstr ""
 
-#: templates/base-feature.html:68
-msgid "Wireless AP"
+#: webapp/backend/countries.py:237
+msgid "Uganda"
 msgstr ""
 
-#: templates/download-mirror-detail.html:24
-msgid "Number of files"
+#: webapp/backend/countries.py:238
+msgid "Unites States minor outlying islands"
 msgstr ""
 
-#: templates/download-mirror-detail.html:32
-msgid "View list of all mirror servers."
+#: webapp/backend/countries.py:239
+msgid "Unites States"
 msgstr ""
 
-#: templates/downloads-all.html:3
-msgid "Ancient Downloads"
+#: webapp/backend/countries.py:240
+msgid "Uruguay"
 msgstr ""
 
-#: templates/downloads-all.html:7 templates/index.html:18
-msgid "Download IPFire"
+#: webapp/backend/countries.py:241
+msgid "Uzbekistan"
 msgstr ""
 
-#: templates/admin-accounts-edit.html:5 templates/admin-accounts.html:5
-msgid "Account Administrator"
+#: webapp/backend/countries.py:242
+msgid "Vatican City State"
 msgstr ""
 
-#: templates/planet/year.html:6
-#, python-format
-msgid "Year %s"
+#: webapp/backend/countries.py:243
+msgid "Saint Vincent and the Grenadines"
 msgstr ""
 
-#: templates/planet/user.html:22 templates/planet/index.html:45
-msgid "Older posts"
+#: webapp/backend/countries.py:244
+msgid "Bolivarian Republic of Venezuela"
 msgstr ""
 
-#: templates/planet/user.html:26 templates/planet/index.html:49
-msgid "Newer posts"
+#: webapp/backend/countries.py:245
+msgid "Virgin Islands, British"
 msgstr ""
 
-#: templates/planet/user.html:33
-#, python-format
-msgid "%s did not write any posts, yet."
+#: webapp/backend/countries.py:246
+msgid "Virgin Islands, U.S."
 msgstr ""
 
-#: templates/planet/user.html:48 templates/static/press.html:31
-#: templates/static/imprint.html:29 templates/static/imprint.html:41
-#: templates/static/imprint.html:53 templates/static/imprint.html:65
-msgid "Mail"
+#: webapp/backend/countries.py:247
+msgid "Viet Nam"
 msgstr ""
 
-#: templates/planet/search.html:6
-#, python-format
-msgid "Search results for '%s'"
+#: webapp/backend/countries.py:248
+msgid "Vanuatu"
 msgstr ""
 
-#: templates/planet/search.html:16
-#, python-format
-msgid "No results found for '%s'"
+#: webapp/backend/countries.py:249
+msgid "Wallis and Futuna"
 msgstr ""
 
-#: templates/planet/base.html:9
-msgid "The official blog of the IPFire team"
+#: webapp/backend/countries.py:250
+msgid "Samoa"
 msgstr ""
 
-#: templates/planet/index.html:24
-msgid "All posts from"
+#: webapp/backend/countries.py:251
+msgid "Yemen"
 msgstr ""
 
-#: templates/planet/posting.html:24 templates/modules/planet-entry.html:14
-msgid "Posted by"
+#: webapp/backend/countries.py:252
+msgid "Mayotte"
 msgstr ""
 
-#: templates/planet/posting.html:25 templates/modules/planet-entry.html:15
-msgid "on"
+#: webapp/backend/countries.py:253
+msgid "South Africa"
 msgstr ""
 
-#: templates/base.html:4
-msgid "No title given"
+#: webapp/backend/countries.py:254
+msgid "Zambia"
 msgstr ""
 
-#: templates/base.html:63
-msgid "IPFire is free software"
+#: webapp/backend/countries.py:255
+msgid "Zimbabwe"
 msgstr ""
 
-#: templates/base.html:69 templates/static/press.html:3
-#: templates/static/press.html:7
-msgid "Press"
+#: webapp/backend/wishlist.py:207
+msgid "Checkout this crowdfunding wish from #ipfire:"
 msgstr ""
 
-#: templates/base.html:73 templates/static/imprint.html:3
-#: templates/static/imprint.html:8
+#: webapp/backend/releases.py:89
+msgid "Image for the armv5tel architecture"
+msgstr ""
+
+#: webapp/backend/releases.py:90
+msgid "armv5tel image for boards with serial console"
+msgstr ""
+
+#: webapp/backend/releases.py:91
+msgid "Installable CD image"
+msgstr ""
+
+#: webapp/backend/releases.py:92
+msgid "Torrent file"
+msgstr ""
+
+#: webapp/backend/releases.py:93
+msgid "Flash image"
+msgstr ""
+
+#: webapp/backend/releases.py:94
+msgid "Alix image"
+msgstr ""
+
+#: webapp/backend/releases.py:95
+msgid "USB FDD Image"
+msgstr ""
+
+#: webapp/backend/releases.py:96
+msgid "USB HDD Image"
+msgstr ""
+
+#: webapp/backend/releases.py:97
+msgid "Pregenerated Xen image"
+msgstr ""
+
+#: webapp/backend/releases.py:103 webapp/backend/releases.py:143
+msgid "Unknown image type"
+msgstr ""
+
+#: webapp/backend/releases.py:129
+msgid "This image runs on many ARM-based boards"
+msgstr ""
+
+#: webapp/backend/releases.py:130
+msgid "This image runs on ARM boards with a serial console"
+msgstr ""
+
+#: webapp/backend/releases.py:131
+msgid "Use this image to burn a CD and install IPFire from it."
+msgstr ""
+
+#: webapp/backend/releases.py:132
+msgid "Download the CD image from the torrent network."
+msgstr ""
+
+#: webapp/backend/releases.py:133
+msgid "An image that is meant to run on embedded devices."
+msgstr ""
+
+#: webapp/backend/releases.py:134
+msgid "Flash image where a serial console is enabled by default."
+msgstr ""
+
+#: webapp/backend/releases.py:135
+msgid "Install IPFire from a floppy-formated USB key."
+msgstr ""
+
+#: webapp/backend/releases.py:136
+msgid "If the floppy image doesn't work, use this image instead."
+msgstr ""
+
+#: webapp/backend/releases.py:137
+msgid "A ready-to-run image for Xen."
+msgstr ""
+
+#: webapp/__init__.py:311
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:235
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:235
+msgid "January"
+msgstr ""
+
+#: webapp/__init__.py:313
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:235
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:235
+msgid "February"
+msgstr ""
+
+#: webapp/__init__.py:315
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:235
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:235
+msgid "March"
+msgstr ""
+
+#: webapp/__init__.py:317
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:235
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:235
+msgid "April"
+msgstr ""
+
+#: webapp/__init__.py:319
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:236
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:236
+msgid "May"
+msgstr ""
+
+#: webapp/__init__.py:321
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:236
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:236
+msgid "June"
+msgstr ""
+
+#: webapp/__init__.py:323
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:236
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:236
+msgid "July"
+msgstr ""
+
+#: webapp/__init__.py:325
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:236
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:236
+msgid "August"
+msgstr ""
+
+#: webapp/__init__.py:327
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:237
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:237
+msgid "September"
+msgstr ""
+
+#: webapp/__init__.py:329
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:237
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:237
+msgid "October"
+msgstr ""
+
+#: webapp/__init__.py:331
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:237
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:237
+msgid "November"
+msgstr ""
+
+#: webapp/__init__.py:333
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:237
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:237
+msgid "December"
+msgstr ""
+
+#: webapp/ui_modules.py:114 webapp/handlers_news.py:43
+msgid "Unknown author"
+msgstr ""
+
+#: webapp/ui_modules.py:265
+#, python-format
+msgid "%s to %s"
+msgstr ""
+
+#: templates/downloads.html:3 templates/downloads.html:8
+msgid "Get IPFire"
+msgstr ""
+
+#: templates/downloads.html:10
+msgid "IPFire is completely free to download and use"
+msgstr ""
+
+#: templates/downloads.html:23
+msgid "More download options"
+msgstr ""
+
+#: templates/downloads.html:55
+msgid "Get yourself involved"
+msgstr ""
+
+#: templates/news-year.html:3 templates/news.html:3
+msgid "News"
+msgstr ""
+
+#: templates/news-year.html:7
+#, python-format
+msgid "News from %(year)s"
+msgstr ""
+
+#: templates/admin-index.html:5
+msgid "Admin Area"
+msgstr ""
+
+#: templates/fireinfo/stats.html:3 templates/modules/menu.html:136
+msgid "Statistics"
+msgstr ""
+
+#: templates/fireinfo/stats.html:7
+msgid "fireinfo statistics"
+msgstr ""
+
+#: templates/fireinfo/stats.html:17
+msgid "IPFire versions"
+msgstr ""
+
+#: templates/fireinfo/stats.html:38 templates/fireinfo/stats-geo.html:3
+#: templates/fireinfo/stats-geo.html:7
+msgid "Geo locations"
+msgstr ""
+
+#: templates/fireinfo/stats.html:59 templates/fireinfo/stats-cpus.html:7
+msgid "Hardware: CPUs"
+msgstr ""
+
+#: templates/fireinfo/stats.html:73
+msgid "CPU features"
+msgstr ""
+
+#: templates/fireinfo/stats.html:85
+msgid "Hardware: Memory"
+msgstr ""
+
+#: templates/fireinfo/stats.html:106 templates/fireinfo/stats-network.html:3
+#: templates/fireinfo/profile-detail.html:95
+msgid "Network"
+msgstr ""
+
+#: templates/fireinfo/stats.html:127 templates/fireinfo/stats-virtual.html:3
+#: templates/base-feature.html:65 templates/static/features.html:1179
+#: templates/static/features.html:1404
+msgid "Virtualization"
+msgstr ""
+
+#: templates/fireinfo/stats-memory.html:3
+#: templates/fireinfo/stats-memory.html:7
+msgid "Memory"
+msgstr ""
+
+#: templates/fireinfo/stats-memory.html:13
+#, python-format
+msgid ""
+"The average amount of memory of all systems in the database is: <strong>%.2f "
+"MB</strong>."
+msgstr ""
+
+#: templates/fireinfo/model-detail.html:11
+#, python-format
+msgid "This device is installed on approximately %.2f%% of all systems."
+msgstr ""
+
+#: templates/fireinfo/model-detail.html:14
+msgid "Feedback"
+msgstr ""
+
+#: templates/fireinfo/model-detail.html:28
+msgid "Go to the wiki"
+msgstr ""
+
+#: templates/fireinfo/stats-admin.html:3
+msgid "Admin"
+msgstr ""
+
+#: templates/fireinfo/stats-admin.html:11
+msgid "Sending profiles"
+msgstr ""
+
+#: templates/fireinfo/stats-admin.html:19
+msgid "Archive size"
+msgstr ""
+
+#: templates/fireinfo/profile-notfound.html:3
+#: templates/fireinfo/profile-notfound.html:8
+msgid "Profile not found"
+msgstr ""
+
+#: templates/fireinfo/stats-cpu-flags.html:3
+msgid "CPU flags"
+msgstr ""
+
+#: templates/fireinfo/stats-cpu-flags.html:7
+msgid "Processor flags"
+msgstr ""
+
+#: templates/fireinfo/stats-cpu-flags.html:30
+msgid "CPUs that support 64 bits"
+msgstr ""
+
+#: templates/fireinfo/stats-cpu-flags.html:46
+msgid "CPUs with PAE"
+msgstr ""
+
+#: templates/fireinfo/stats-cpu-flags.html:62
+msgid "CPUs that support virtualization"
+msgstr ""
+
+#: templates/fireinfo/stats-virtual.html:7
+msgid "Virtualization support"
+msgstr ""
+
+#: templates/fireinfo/stats-virtual.html:35
+msgid "Hypervisors"
+msgstr ""
+
+#: templates/fireinfo/stats-oses.html:3
+#: templates/fireinfo/profile-detail.html:12
+msgid "Operating system"
+msgstr ""
+
+#: templates/fireinfo/stats-oses.html:7
+msgid "Releases"
+msgstr ""
+
+#: templates/fireinfo/stats-oses.html:18
+msgid "Architectures"
+msgstr ""
+
+#: templates/fireinfo/stats-oses.html:25
+msgid "Kernels"
+msgstr ""
+
+#: templates/fireinfo/stats-cpus.html:3
+msgid "Processors"
+msgstr ""
+
+#: templates/fireinfo/stats-cpus.html:12
+msgid "See statistics about common CPU flags"
+msgstr ""
+
+#: templates/fireinfo/stats-cpus.html:19
+msgid "Vendors"
+msgstr ""
+
+#: templates/fireinfo/stats-cpus.html:36
+#: templates/fireinfo/profile-detail.html:154
+msgid "Speed"
+msgstr ""
+
+#: templates/fireinfo/stats-cpus.html:38
+#, python-format
+msgid ""
+"The average speed of all systems in the database is: <strong>%.2f MHz</"
+"strong>."
+msgstr ""
+
+#: templates/fireinfo/stats-cpus.html:40
+#, python-format
+msgid "All together, there are <strong>%s bogomips</strong> out there."
+msgstr ""
+
+#: templates/fireinfo/stats-cpus.html:48
+msgid "CPU core counter"
+msgstr ""
+
+#: templates/fireinfo/stats-cpus.html:50
+msgid ""
+"See a breakdown of the CPU cores that are installed on the IPFire systems."
+msgstr ""
+
+#: templates/fireinfo/stats-network.html:7
+msgid "Network configuration"
+msgstr ""
+
+#: templates/fireinfo/index.html:52
+msgid "Show"
+msgstr ""
+
+#: templates/fireinfo/stats-geo.html:28
+msgid "Language selection"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:3
+#: templates/fireinfo/profile-detail.html:7
+msgid "Profile"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:16
+msgid "Version"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:24
+msgid "Architecture"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:32
+msgid "Kernel version"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:43
+msgid "Hypervisor"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:45
+msgid "This machine is running in a virtual environment."
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:51
+#: templates/fireinfo/profile-detail.html:71
+#: templates/fireinfo/profile-detail.html:130
+msgid "Vendor"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:59
+msgid "Type"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:67
+msgid "Hardware vendor"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:74
+#: templates/fireinfo/profile-detail.html:82 templates/mirrors-item.html:14
+msgid "Unknown"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:79
+#: templates/fireinfo/profile-detail.html:138
+msgid "Model"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:97
+msgid "Enabled network zones"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:119 templates/base-feature.html:62
+#: templates/static/features.html:1104 templates/static/features.html:1399
+msgid "Hardware"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:125
+msgid "CPU"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:141
+msgid "Not available"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:146
+msgid "Cores"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:166
+msgid "Supported features"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:170
+msgid "64 bit capable"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:174
+msgid "PAE capable"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:178
+msgid "VT-x/AMD-V"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:190
+msgid "Memory size"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:198
+msgid "System disk size"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:211
+msgid "Peripherial devices"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:216
+msgid "Signature images"
+msgstr ""
+
+#: templates/fireinfo/profile-detail.html:231 templates/admin-mirrors.html:15
+#: templates/download-mirror-detail.html:20
+#: templates/downloads-mirrors.html:50
+msgid "Last update"
+msgstr ""
+
+#: templates/fireinfo/base.html:6 templates/modules/menu.html:98
+msgid "Fireinfo"
+msgstr ""
+
+#: templates/fireinfo/base.html:7
+msgid "A hardware data collection tool for IPFire"
+msgstr ""
+
+#: templates/sources.html:3
+msgid "Sources"
+msgstr ""
+
+#: templates/sources.html:6 templates/static/development.html:263
+msgid "Source Code"
+msgstr ""
+
+#: templates/sources.html:9
+#, python-format
+msgid "There are %s source files on the server."
+msgstr ""
+
+#: templates/downloads-all.html:3
+msgid "Ancient Downloads"
+msgstr ""
+
+#: templates/downloads-all.html:7 templates/index.html:18
+msgid "Download IPFire"
+msgstr ""
+
+#: templates/downloads-all.html:9 templates/downloads-older.html:9
+msgid ""
+"These are the ancient downloads of IPFire. They are just saved for "
+"historical reasons and should not be used in a productive environment."
+msgstr ""
+
+#: templates/downloads-all.html:13 templates/downloads-older.html:17
+msgid "Go back to latest stable downloads."
+msgstr ""
+
+#: templates/admin-accounts-edit.html:5 templates/admin-accounts.html:5
+msgid "Account Administrator"
+msgstr ""
+
+#: templates/news-author.html:7
+#, python-format
+msgid "%s's announcements"
+msgstr ""
+
+#: templates/admin-mirrors.html:5
+msgid "Mirror Administrator"
+msgstr ""
+
+#: templates/admin-mirrors.html:8 templates/admin-mirrors-create.html:5
+msgid "Create new mirror"
+msgstr ""
+
+#: templates/admin-mirrors.html:9
+msgid "Re-check now"
+msgstr ""
+
+#: templates/admin-mirrors.html:14 templates/download-mirror-detail.html:12
+#: templates/admin-mirrors-create.html:12 templates/downloads-mirrors.html:48
+#: templates/admin-mirrors-details.html:13
+msgid "Hostname"
+msgstr ""
+
+#: templates/admin-mirrors.html:23 templates/admin-mirrors-details.html:5
+msgid "Details"
+msgstr ""
+
+#: templates/admin-mirrors.html:24 templates/admin-planet.html:29
+#: templates/admin-accounts.html:20
+msgid "Edit"
+msgstr ""
+
+#: templates/admin-mirrors.html:25 templates/admin-accounts.html:21
+msgid "Delete"
+msgstr ""
+
+#: templates/error-500.html:6
+msgid "Detailed information"
+msgstr ""
+
+#: templates/download-mirror-detail.html:3 templates/downloads-mirrors.html:3
+msgid "Mirror-Server"
+msgstr ""
+
+#: templates/download-mirror-detail.html:8 templates/downloads-mirrors.html:8
+msgid "IPFire Mirrors"
+msgstr ""
+
+#: templates/download-mirror-detail.html:16 templates/mirrors-item.html:23
+#: templates/admin-mirrors-create.html:20 templates/downloads-mirrors.html:47
+#: templates/admin-mirrors-details.html:21
+msgid "Owner"
+msgstr ""
+
+#: templates/download-mirror-detail.html:24
+msgid "Number of files"
+msgstr ""
+
+#: templates/download-mirror-detail.html:32
+msgid "View list of all mirror servers."
+msgstr ""
+
+#: templates/news.html:7
+msgid "What is new on the IPFire project?"
+msgstr ""
+
+#: templates/news.html:34
+msgid "Stay up to date"
+msgstr ""
+
+#: templates/news.html:61 templates/planet/search.html:3
+#: templates/planet/year.html:3 templates/planet/user.html:3
+#: templates/planet/posting.html:3 templates/planet/index.html:3
+#: templates/planet/base.html:6 templates/index.html:154
+msgid "IPFire Planet"
+msgstr ""
+
+#: templates/news.html:85
+msgid "Latest news"
+msgstr ""
+
+#: templates/admin-planet-compose.html:5 templates/admin-planet.html:8
+msgid "Compose new entry"
+msgstr ""
+
+#: templates/admin-planet-compose.html:16 templates/admin-planet.html:14
+msgid "Title"
+msgstr ""
+
+#: templates/admin-planet-compose.html:33 templates/planet/posting.html:26
+msgid "Tags"
+msgstr ""
+
+#: templates/admin-planet-compose.html:44
+#: templates/admin-mirrors-details.html:17
+msgid "Status"
+msgstr ""
+
+#: templates/admin-planet-compose.html:48 templates/admin-planet.html:25
+msgid "Draft"
+msgstr ""
+
+#: templates/admin-planet-compose.html:52
+msgid "Published"
+msgstr ""
+
+#: templates/admin-planet-compose.html:58
+msgid "Save"
+msgstr ""
+
+#: templates/admin-planet-compose.html:59
+msgid "Preview"
+msgstr ""
+
+#: templates/admin-planet-compose.html:60
+msgid "Cancel"
+msgstr ""
+
+#: templates/mirrors-item.html:3
+#, python-format
+msgid "Mirror %s"
+msgstr ""
+
+#: templates/mirrors-item.html:8
+msgid "Up"
+msgstr ""
+
+#: templates/mirrors-item.html:10
+msgid "Down"
+msgstr ""
+
+#: templates/mirrors-item.html:12
+msgid "Out of sync"
+msgstr ""
+
+#: templates/mirrors-item.html:30 templates/admin-mirrors-create.html:24
+#: templates/downloads-mirrors.html:49 templates/admin-mirrors-details.html:25
+msgid "Location"
+msgstr ""
+
+#: templates/mirrors-item.html:36 templates/geoip/index.html:19
+msgid "Country"
+msgstr ""
+
+#: templates/mirrors-item.html:41 templates/geoip/index.html:29
+msgid "City"
+msgstr ""
+
+#: templates/mirrors-item.html:47
+msgid "Preferred for"
+msgstr ""
+
+#: templates/mirrors-item.html:54
+msgid "Estimated distance to you"
+msgstr ""
+
+#: templates/mirrors-item.html:61 templates/geoip/index.html:15
+msgid "Autonomous System"
+msgstr ""
+
+#: templates/mirrors-item.html:65
+msgid "IP Addresses"
+msgstr ""
+
+#: templates/mirrors-item.html:75
+msgid "Last updated"
+msgstr ""
+
+#: templates/mirrors-item.html:86
+msgid "Go to mirror"
+msgstr ""
+
+#: templates/mirrors-item.html:94
+msgid "The location of the mirror server is estimated by the IP address."
+msgstr ""
+
+#: templates/mirrors-item.html:98
+msgid "The location of this mirror server could not be estimated."
+msgstr ""
+
+#: templates/tracker-torrents.html:3
+msgid "Torrent Downloads"
+msgstr ""
+
+#: templates/tracker-torrents.html:7
+msgid "IPFire Torrent Tracker"
+msgstr ""
+
+#: templates/tracker-torrents.html:32 templates/downloads-older.html:25
+#: templates/downloads-index.html:35
+msgid "Release"
+msgstr ""
+
+#: templates/tracker-torrents.html:33
+msgid "Seeders"
+msgstr ""
+
+#: templates/tracker-torrents.html:34 templates/tracker-torrent-detail.html:23
+msgid "Peers"
+msgstr ""
+
+#: templates/downloads-older.html:3 templates/downloads-older.html:7
+msgid "Ancient downloads"
+msgstr ""
+
+#: templates/downloads-older.html:13
+msgid ""
+"Beware that these releases could lack possible security-fixes\tand so it is "
+"recommended to use the <em>latest</em> version."
+msgstr ""
+
+#: templates/downloads-older.html:26
+msgid "Published on"
+msgstr ""
+
+#: templates/admin-downloads-mirrors.html:5 templates/admin-downloads.html:5
+msgid "Download statistics"
+msgstr ""
+
+#: templates/admin-downloads-mirrors.html:7
+msgid "Mirror load from today"
+msgstr ""
+
+#: templates/admin-downloads-mirrors.html:10
+msgid "Mirror load"
+msgstr ""
+
+#: templates/admin-base.html:3
+msgid "IPFire Admin Area"
+msgstr ""
+
+#: templates/admin-base.html:6 templates/admin-downloads-base.html:8
+msgid "Options"
+msgstr ""
+
+#: templates/admin-base.html:9
+msgid "Accounts"
+msgstr ""
+
+#: templates/admin-base.html:10 templates/modules/menu.html:110
+msgid "Mirrors"
+msgstr ""
+
+#: templates/admin-base.html:11 templates/modules/menu.html:92
+msgid "Planet"
+msgstr ""
+
+#: templates/admin-base.html:12
+msgid "Downloads"
+msgstr ""
+
+#: templates/base-feature.html:6 templates/static/features.html:3
+#: templates/static/features.html:8 templates/static/features.html:1349
+#: templates/modules/menu.html:7
+msgid "About IPFire"
+msgstr ""
+
+#: templates/base-feature.html:9 templates/static/features.html:88
+#: templates/static/features.html:1354 templates/index.html:75
+msgid "Security"
+msgstr ""
+
+#: templates/base-feature.html:12 templates/static/features.html:139
+#: templates/static/features.html:1359
+msgid "Firewall"
+msgstr ""
+
+#: templates/base-feature.html:15 templates/static/features.html:1364
+msgid "Pakfire"
+msgstr ""
+
+#: templates/base-feature.html:18 templates/static/features.html:432
+#: templates/static/features.html:1369
+msgid "Updates"
+msgstr ""
+
+#: templates/base-feature.html:21
+msgid "It's free"
+msgstr ""
+
+#: templates/base-feature.html:25
+msgid "Features"
+msgstr ""
+
+#: templates/base-feature.html:28 templates/static/features.html:502
+#: templates/static/features.html:1374
+msgid "Dialup"
+msgstr ""
+
+#: templates/base-feature.html:31 templates/static/features.html:616
+msgid "Web proxy"
+msgstr ""
+
+#: templates/base-feature.html:35 templates/static/features.html:705
+msgid "Content filter"
+msgstr ""
+
+#: templates/base-feature.html:38 templates/static/features.html:766
+msgid "Update accelerator"
+msgstr ""
+
+#: templates/base-feature.html:41
+msgid "Virus scan"
+msgstr ""
+
+#: templates/base-feature.html:45 templates/static/features.html:818
+#: templates/static/features.html:1384
+msgid "VPN"
+msgstr ""
+
+#: templates/base-feature.html:49 templates/static/features.html:854
+msgid "IPsec"
+msgstr ""
+
+#: templates/base-feature.html:52 templates/static/features.html:915
+msgid "OpenVPN"
+msgstr ""
+
+#: templates/base-feature.html:56
+msgid "Intrusion detection"
+msgstr ""
+
+#: templates/base-feature.html:59 templates/static/features.html:1031
+#: templates/static/features.html:1394
+msgid "Quality of Service"
+msgstr ""
+
+#: templates/base-feature.html:68
+msgid "Wireless AP"
+msgstr ""
+
+#: templates/downloads-development.html:3
+#: templates/downloads-development.html:6
+msgid "Development Downloads"
+msgstr ""
+
+#: templates/admin-mirrors-create.html:16
+msgid "Path"
+msgstr ""
+
+#: templates/admin-mirrors-create.html:28
+#: templates/admin-mirrors-details.html:33
+msgid "File mirror"
+msgstr ""
+
+#: templates/admin-mirrors-create.html:31
+#: templates/admin-mirrors-create.html:40
+#: templates/admin-mirrors-create.html:49
+#: templates/admin-mirrors-create.html:58
+msgid "yes"
+msgstr ""
+
+#: templates/admin-mirrors-create.html:32
+#: templates/admin-mirrors-create.html:41
+#: templates/admin-mirrors-create.html:50
+#: templates/admin-mirrors-create.html:59
+msgid "no"
+msgstr ""
+
+#: templates/admin-mirrors-create.html:37
+#: templates/admin-mirrors-details.html:37
+msgid "Pakfire 2 mirror"
+msgstr ""
+
+#: templates/admin-mirrors-create.html:46
+#: templates/admin-mirrors-details.html:41
+msgid "Pakfire 3 mirror"
+msgstr ""
+
+#: templates/admin-mirrors-create.html:55
+#: templates/admin-mirrors-details.html:45
+msgid "Disabled?"
+msgstr ""
+
+#: templates/geoip/index.html:3 templates/geoip/index.html:7
+#, python-format
+msgid "GeoIP for %s"
+msgstr ""
+
+#: templates/geoip/index.html:33
+msgid "Postal Code"
+msgstr ""
+
+#: templates/geoip/index.html:46
+#, python-format
+msgid "No GeoIP information could be found for the IP address '%s'."
+msgstr ""
+
+#: templates/downloads-index.html:3
+msgid "Download Center"
+msgstr ""
+
+#: templates/downloads-index.html:7
+msgid "IPFire Download Center"
+msgstr ""
+
+#: templates/downloads-index.html:30
+msgid "Available releases"
+msgstr ""
+
+#: templates/downloads-index.html:36
+msgid "Release type"
+msgstr ""
+
+#: templates/downloads-index.html:37
+msgid "Release date"
+msgstr ""
+
+#: templates/downloads-index.html:48
+msgid "Stable"
+msgstr ""
+
+#: templates/downloads-index.html:50 templates/static/getinvolved.html:153
+#: templates/static/development.html:3 templates/static/development.html:8
+#: templates/modules/menu.html:65
+msgid "Development"
+msgstr ""
+
+#: templates/downloads-mirrors.html:63
+msgid "details"
+msgstr ""
+
+#: templates/admin-planet.html:5
+msgid "Planet Administrator"
+msgstr ""
+
+#: templates/admin-planet.html:13
+msgid "Author"
+msgstr ""
+
+#: templates/admin-planet.html:31
+msgid "Publish"
+msgstr ""
+
+#: templates/error.html:3
+msgid "Error"
+msgstr ""
+
+#: templates/admin-downloads-base.html:5
+msgid "Back to home"
+msgstr ""
+
+#: templates/admin-downloads-base.html:11
+msgid "Mirror stats"
+msgstr ""
+
+#: templates/static/getinvolved.html:3 templates/static/getinvolved.html:8
+msgid "Get involved"
+msgstr ""
+
+#: templates/static/getinvolved.html:10
+msgid "Because making a difference is easy"
+msgstr ""
+
+#: templates/static/getinvolved.html:47
+msgid "First steps"
+msgstr ""
+
+#: templates/static/getinvolved.html:97
+msgid "Donations"
+msgstr ""
+
+#: templates/static/getinvolved.html:120 templates/static/donation.html:3
+#: templates/static/donation.html:8 templates/modules/menu.html:47
+#: templates/wishlist/donate.html:3 templates/wishlist/modules/wish.html:26
+#: templates/download-splash.html:37
+msgid "Donate"
+msgstr ""
+
+#: templates/static/getinvolved.html:126 templates/modules/menu.html:53
+msgid "Wishlist"
+msgstr ""
+
+#: templates/static/getinvolved.html:142
+msgid "Make a wish"
+msgstr ""
+
+#: templates/static/getinvolved.html:149
+msgid "Contribute yourself"
+msgstr ""
+
+#: templates/static/getinvolved.html:175
+msgid "Translation"
+msgstr ""
+
+#: templates/static/getinvolved.html:190
+msgid "Translation team"
+msgstr ""
+
+#: templates/static/getinvolved.html:196
+msgid "Promotion"
+msgstr ""
+
+#: templates/static/features.html:10
+msgid "Because IPFire is more than just a firewall"
+msgstr ""
+
+#: templates/static/features.html:22
+msgid "IPFire"
+msgstr ""
+
+#: templates/static/features.html:22
+msgid "An Open Source Firewall Distribution"
+msgstr ""
+
+#: templates/static/features.html:315
+msgid "The IPFire package management system"
+msgstr ""
+
+#: templates/static/features.html:791
+msgid "Transparent virus scanner"
+msgstr ""
+
+#: templates/static/features.html:818
+msgid "Virtual Private Networks"
+msgstr ""
+
+#: templates/static/features.html:974
+msgid "Intrusion detection system"
+msgstr ""
+
+#: templates/static/features.html:1311 templates/static/features.html:1409
+msgid "Wireless Access Point"
+msgstr ""
+
+#: templates/static/features.html:1379
+msgid "Web Proxy"
+msgstr ""
+
+#: templates/static/features.html:1389
+msgid "Intrusion Detection"
+msgstr ""
+
+#: templates/static/artwork.html:3 templates/static/artwork.html:8
+#: templates/modules/menu.html:116
+msgid "Artwork"
+msgstr ""
+
+#: templates/static/artwork.html:36
+msgid "The IPFire Logo"
+msgstr ""
+
+#: templates/static/artwork.html:50
+msgid "Flyers, Rollups, CDs and more"
+msgstr ""
+
+#: templates/static/development.html:37 templates/static/development.html:258
+msgid "Development tools"
+msgstr ""
+
+#: templates/static/development.html:55 templates/modules/menu.html:82
+msgid "Bugtracker"
+msgstr ""
+
+#: templates/static/development.html:77
+msgid "Development Mailing List"
+msgstr ""
+
+#: templates/static/development.html:96
+msgid "Source code"
+msgstr ""
+
+#: templates/static/development.html:99
+msgid "Git"
+msgstr ""
+
+#: templates/static/development.html:118
+msgid "IPFire Git repositories"
+msgstr ""
+
+#: templates/static/development.html:124
+msgid "GitHub"
+msgstr ""
+
+#: templates/static/development.html:143
+msgid "ipfire on GitHub"
+msgstr ""
+
+#: templates/static/development.html:150 templates/static/development.html:268
+msgid "How to build IPFire?"
+msgstr ""
+
+#: templates/static/development.html:186
+msgid "How to submit patches?"
+msgstr ""
+
+#: templates/static/development.html:214
+msgid "How to translate IPFire?"
+msgstr ""
+
+#: templates/static/press.html:3 templates/static/press.html:7
+#: templates/base.html:69
+msgid "Press"
+msgstr ""
+
+#: templates/static/press.html:31 templates/static/imprint.html:29
+#: templates/static/imprint.html:41 templates/static/imprint.html:53
+#: templates/static/imprint.html:65 templates/planet/user.html:48
+msgid "Mail"
+msgstr ""
+
+#: templates/static/press.html:35
+msgid "Logo"
+msgstr ""
+
+#: templates/static/imprint.html:3 templates/static/imprint.html:8
+#: templates/base.html:73
 msgid "Imprint"
 msgstr ""
 
-#: templates/admin-mirrors.html:5
-msgid "Mirror Administrator"
+#: templates/static/chat.html:3 templates/static/chat.html:8
+#: templates/modules/menu.html:39
+msgid "Chat"
 msgstr ""
 
-#: templates/admin-mirrors.html:9
-msgid "Re-check now"
+#: templates/static/chat.html:33
+msgid "Server"
 msgstr ""
 
-#: templates/admin-mirrors.html:23 templates/admin-mirrors-details.html:5
-msgid "Details"
+#: templates/static/chat.html:36
+msgid "Channel"
 msgstr ""
 
-#: templates/admin-mirrors.html:25 templates/admin-accounts.html:21
-msgid "Delete"
+#: templates/static/chat.html:41
+msgid "Use the web client"
 msgstr ""
 
-#: templates/tracker-torrents.html:3
-msgid "Torrent Downloads"
+#: templates/static/donation.html:10
+msgid "We need your help!"
 msgstr ""
 
-#: templates/tracker-torrents.html:7
-msgid "IPFire Torrent Tracker"
+#: templates/static/donation.html:68
+msgid "Did you know...?"
 msgstr ""
 
-#: templates/tracker-torrents.html:33
-msgid "Seeders"
+#: templates/static/donation.html:84
+msgid "How do we use financial support?"
 msgstr ""
 
-#: templates/error.html:3
-msgid "Error"
+#: templates/static/donation.html:132
+msgid "Research & Development"
+msgstr ""
+
+#: templates/static/cebit.html:3
+msgid "CeBIT special"
+msgstr ""
+
+#: templates/static/cebit.html:8
+msgid "IPFire at CeBIT 2010"
+msgstr ""
+
+#: templates/admin-accounts.html:8
+msgid "Create new account"
+msgstr ""
+
+#: templates/admin-accounts.html:13
+msgid "Name (Nickname)"
+msgstr ""
+
+#: templates/modules/sidebar-release.html:4
+#: templates/modules/release-item.html:2 templates/index.html:24
+msgid "Latest release"
+msgstr ""
+
+#: templates/modules/sidebar-release.html:9
+msgid "Download now"
+msgstr ""
+
+#: templates/modules/release-item-short.html:4
+msgid "Here you will find the downloads for the version"
+msgstr ""
+
+#: templates/modules/release-item-short.html:13
+#: templates/modules/release-item.html:123
+msgid "There are no downloads available for this release."
+msgstr ""
+
+#: templates/modules/download-button.html:2
+#, python-format
+msgid "Download %s"
+msgstr ""
+
+#: templates/modules/map.html:5
+msgid "View larger map"
+msgstr ""
+
+#: templates/modules/ads/download-splash.html:4
+msgid "Advertisement"
+msgstr ""
+
+#: templates/modules/ads/download-splash.html:5
+msgid "This download is sponsored by:"
+msgstr ""
+
+#: templates/modules/news-preview.html:6
+msgid "by"
+msgstr ""
+
+#: templates/modules/mirrors-table.html:37
+#, python-format
+msgid "Last update: %s"
+msgstr ""
+
+#: templates/modules/news-year-nav.html:3
+msgid "Jump to"
+msgstr ""
+
+#: templates/modules/news-year-nav.html:6
+msgid "Most recent"
+msgstr ""
+
+#: templates/modules/stasy-table-devices.html:6
+msgid "Kernel module"
 msgstr ""
 
 #: templates/modules/donation-box.html:4
@@ -1206,9 +2299,16 @@ msgstr ""
 msgid "Bank code"
 msgstr ""
 
-#: templates/modules/release-item.html:2
-#: templates/modules/sidebar-release.html:4 templates/index.html:24
-msgid "Latest release"
+#: templates/modules/planet-entry.html:14 templates/planet/posting.html:31
+msgid "Posted by"
+msgstr ""
+
+#: templates/modules/planet-entry.html:15 templates/planet/posting.html:32
+msgid "on"
+msgstr ""
+
+#: templates/modules/news-item.html:6
+msgid "Announcement"
 msgstr ""
 
 #: templates/modules/release-item.html:10
@@ -1235,46 +2335,15 @@ msgstr ""
 msgid "Legend:"
 msgstr ""
 
-#: templates/modules/release-item.html:123
-#: templates/modules/release-item-short.html:13
-msgid "There are no downloads available for this release."
-msgstr ""
-
-#: templates/modules/sidebar-release.html:9
-msgid "Download now"
-msgstr ""
-
-#: templates/modules/release-item-short.html:4
-msgid "Here you will find the downloads for the version"
-msgstr ""
-
-#: templates/modules/news-preview.html:6
-msgid "by"
-msgstr ""
-
-#: templates/modules/download-button.html:2
-#, python-format
-msgid "Download %s"
-msgstr ""
-
-#: templates/modules/stasy-table-devices.html:6
-msgid "Kernel module"
-msgstr ""
-
-#: templates/modules/news-year-nav.html:3
-msgid "Jump to"
-msgstr ""
-
-#: templates/modules/news-year-nav.html:6
-msgid "Most recent"
-msgstr ""
-
-#: templates/modules/ads/download-splash.html:4
-msgid "Advertisement"
+#: templates/modules/release-item.html:114
+#: templates/tracker-torrent-detail.html:15
+msgid "Magnet link"
 msgstr ""
 
-#: templates/modules/ads/download-splash.html:5
-msgid "This download is sponsored by:"
+#: templates/modules/release-item.html:115
+#: templates/tracker-torrent-detail.html:3
+#: templates/tracker-torrent-detail.html:18
+msgid "Torrent download"
 msgstr ""
 
 #: templates/modules/menu.html:4
@@ -1301,15 +2370,6 @@ msgstr ""
 msgid "Forum"
 msgstr ""
 
-#: templates/modules/menu.html:39 templates/static/chat.html:3
-#: templates/static/chat.html:8
-msgid "Chat"
-msgstr ""
-
-#: templates/modules/menu.html:53 templates/static/getinvolved.html:126
-msgid "Wishlist"
-msgstr ""
-
 #: templates/modules/menu.html:61
 msgid "More"
 msgstr ""
@@ -1322,10 +2382,6 @@ msgstr ""
 msgid "Pakfire Build Service"
 msgstr ""
 
-#: templates/modules/menu.html:82 templates/static/development.html:55
-msgid "Bugtracker"
-msgstr ""
-
 #: templates/modules/menu.html:87
 msgid "Miscellaneous"
 msgstr ""
@@ -1334,11 +2390,6 @@ msgstr ""
 msgid "Mailing lists"
 msgstr ""
 
-#: templates/modules/menu.html:116 templates/static/artwork.html:3
-#: templates/static/artwork.html:8
-msgid "Artwork"
-msgstr ""
-
 #: templates/modules/menu.html:122
 msgid "CeBIT"
 msgstr ""
@@ -1351,172 +2402,172 @@ msgstr ""
 msgid "Professional support available!"
 msgstr ""
 
-#: templates/modules/news-item.html:6
-msgid "Announcement"
-msgstr ""
-
-#: templates/static/press.html:35
-msgid "Logo"
-msgstr ""
-
-#: templates/static/features.html:10
-msgid "Because IPFire is more than just a firewall"
-msgstr ""
-
-#: templates/static/features.html:22
-msgid "IPFire"
+#: templates/admin-login.html:3
+msgid "Please login"
 msgstr ""
 
-#: templates/static/features.html:22
-msgid "An Open Source Firewall Distribution"
+#: templates/planet/search.html:6
+#, python-format
+msgid "Search results for '%s'"
 msgstr ""
 
-#: templates/static/features.html:315
-msgid "The IPFire package management system"
+#: templates/planet/search.html:16
+#, python-format
+msgid "No results found for '%s'"
 msgstr ""
 
-#: templates/static/features.html:791
-msgid "Transparent virus scanner"
+#: templates/planet/year.html:6
+#, python-format
+msgid "Year %s"
 msgstr ""
 
-#: templates/static/features.html:818
-msgid "Virtual Private Networks"
+#: templates/planet/user.html:22 templates/planet/index.html:45
+msgid "Older posts"
 msgstr ""
 
-#: templates/static/features.html:974
-msgid "Intrusion detection system"
+#: templates/planet/user.html:26 templates/planet/index.html:49
+msgid "Newer posts"
 msgstr ""
 
-#: templates/static/features.html:1311 templates/static/features.html:1409
-msgid "Wireless Access Point"
+#: templates/planet/user.html:33
+#, python-format
+msgid "%s did not write any posts, yet."
 msgstr ""
 
-#: templates/static/features.html:1379
-msgid "Web Proxy"
+#: templates/planet/posting.html:17
+msgid "Heads up!"
 msgstr ""
 
-#: templates/static/features.html:1389
-msgid "Intrusion Detection"
+#: templates/planet/posting.html:17
+msgid "This post is a draft and has not been published, yet."
 msgstr ""
 
-#: templates/static/development.html:37 templates/static/development.html:258
-msgid "Development tools"
+#: templates/planet/index.html:24
+msgid "All posts from"
 msgstr ""
 
-#: templates/static/development.html:77
-msgid "Development Mailing List"
+#: templates/planet/base.html:9
+msgid "The official blog of the IPFire team"
 msgstr ""
 
-#: templates/static/development.html:96
-msgid "Source code"
+#: templates/admin-downloads.html:7
+msgid "Download counters"
 msgstr ""
 
-#: templates/static/development.html:99
-msgid "Git"
+#: templates/admin-downloads.html:10
+msgid "Today"
 msgstr ""
 
-#: templates/static/development.html:118
-msgid "IPFire Git repositories"
+#: templates/admin-downloads.html:14
+msgid "Yesterday"
 msgstr ""
 
-#: templates/static/development.html:124
-msgid "GitHub"
+#: templates/admin-downloads.html:18
+msgid "Total"
 msgstr ""
 
-#: templates/static/development.html:143
-msgid "ipfire on GitHub"
+#: templates/admin-downloads.html:23
+msgid "Downloads by country"
 msgstr ""
 
-#: templates/static/development.html:150 templates/static/development.html:268
-msgid "How to build IPFire?"
+#: templates/tracker-torrent-detail.html:28
+msgid "Seeds"
 msgstr ""
 
-#: templates/static/development.html:186
-msgid "How to submit patches?"
+#: templates/wishlist/closed.html:3 templates/wishlist/closed.html:13
+msgid "Closed wishes"
 msgstr ""
 
-#: templates/static/development.html:214
-msgid "How to translate IPFire?"
+#: templates/wishlist/closed.html:9
+msgid "Wishes open for donation"
 msgstr ""
 
-#: templates/static/chat.html:33
-msgid "Server"
+#: templates/wishlist/closed.html:36 templates/wishlist/closed.html:40
+msgid "Older"
 msgstr ""
 
-#: templates/static/chat.html:36
-msgid "Channel"
+#: templates/wishlist/closed.html:46 templates/wishlist/closed.html:50
+msgid "Newer"
 msgstr ""
 
-#: templates/static/chat.html:41
-msgid "Use the web client"
+#: templates/wishlist/donate.html:13 templates/wishlist/donate.html:23
+#: templates/wishlist/terms.html:3 templates/wishlist/terms.html:7
+#: templates/base.html:78
+msgid "Terms & Conditions"
 msgstr ""
 
-#: templates/static/cebit.html:3
-msgid "CeBIT special"
+#: templates/wishlist/modules/wish.html:6
+msgid "Only a few days left!"
 msgstr ""
 
-#: templates/static/cebit.html:8
-msgid "IPFire at CeBIT 2010"
+#: templates/wishlist/modules/wish.html:36
+msgid "funded"
 msgstr ""
 
-#: templates/static/getinvolved.html:3 templates/static/getinvolved.html:8
-msgid "Get involved"
+#: templates/wishlist/modules/wish.html:40
+#: templates/wishlist/modules/wish.html:50
+#, python-format
+msgid "%s &euro;"
 msgstr ""
 
-#: templates/static/getinvolved.html:10
-msgid "Because making a difference is easy"
+#: templates/wishlist/modules/wish.html:41
+msgid "donated"
 msgstr ""
 
-#: templates/static/getinvolved.html:47
-msgid "First steps"
-msgstr ""
+#: templates/wishlist/modules/wish.html:48
+msgid "day to go"
+msgid_plural "days to go"
+msgstr[0] ""
+msgstr[1] ""
 
-#: templates/static/getinvolved.html:97
-msgid "Donations"
+#: templates/wishlist/modules/wish.html:51
+msgid "to go"
 msgstr ""
 
-#: templates/static/getinvolved.html:142
-msgid "Make a wish"
+#: templates/wishlist/modules/wish.html:54
+msgid "In progress"
 msgstr ""
 
-#: templates/static/getinvolved.html:149
-msgid "Contribute yourself"
+#: templates/wishlist/modules/wish.html:56
+msgid "Finished"
 msgstr ""
 
-#: templates/static/getinvolved.html:175
-msgid "Translation"
+#: templates/wishlist/modules/wish.html:58
+msgid "Funding ended"
 msgstr ""
 
-#: templates/static/getinvolved.html:190
-msgid "Translation team"
+#: templates/wishlist/modules/wish.html:68
+msgid "Share this wish with your friends and help us promote it!"
 msgstr ""
 
-#: templates/static/getinvolved.html:196
-msgid "Promotion"
+#: templates/wishlist/index.html:3 templates/index.html:3
+msgid "Home"
 msgstr ""
 
-#: templates/static/artwork.html:36
-msgid "The IPFire Logo"
+#: templates/wishlist/wish.html:3
+msgid "Wish"
 msgstr ""
 
-#: templates/static/artwork.html:50
-msgid "Flyers, Rollups, CDs and more"
+#: templates/wishlist/wish.html:18
+#, python-format
+msgid "Launched: %s"
 msgstr ""
 
-#: templates/static/donation.html:10
-msgid "We need your help!"
+#: templates/wishlist/wish.html:24
+#, python-format
+msgid "Funding ends: %s"
 msgstr ""
 
-#: templates/static/donation.html:68
-msgid "Did you know...?"
+#: templates/wishlist/wish.html:26
+msgid "This funding runs until the goal is reached."
 msgstr ""
 
-#: templates/static/donation.html:84
-msgid "How do we use financial support?"
+#: templates/wishlist/base.html:6 templates/index.html:175
+msgid "IPFire Wishlist"
 msgstr ""
 
-#: templates/static/donation.html:132
-msgid "Research & Development"
+#: templates/wishlist/base.html:8
+msgid "Crowd funding for the IPFire project"
 msgstr ""
 
 #: templates/index.html:92
@@ -1571,10 +2622,6 @@ msgstr ""
 msgid "All mirrors"
 msgstr ""
 
-#: templates/admin-mirrors-details.html:17
-msgid "Status"
-msgstr ""
-
 #: templates/admin-mirrors-details.html:29
 msgid "GeoIP Location"
 msgstr ""
@@ -1583,122 +2630,156 @@ msgstr ""
 msgid "Filelist"
 msgstr ""
 
-#: templates/admin-accounts.html:8
-msgid "Create new account"
+#: templates/base.html:4
+msgid "No title given"
 msgstr ""
 
-#: templates/admin-accounts.html:13
-msgid "Name (Nickname)"
+#: templates/base.html:63
+msgid "IPFire is free software"
+msgstr ""
+
+#: templates/download-splash.html:3 templates/download-splash.html:7
+msgid "Thanks for downloading IPFire!"
+msgstr ""
+
+#: templates/download-splash.html:61
+msgid "Next steps"
+msgstr ""
+
+#: templates/download-splash.html:66
+msgid "Install IPFire"
 msgstr ""
 
-#: templates/mirrors.html:25
-msgid "Mirror servers nearby"
+#: templates/download-splash.html:82
+msgid "Access documentation"
 msgstr ""
 
-#: templates/mirrors.html:28 templates/mirrors.html:31
-msgid "Worldwide mirror servers"
+#: templates/download-splash.html:100
+msgid "Join the community"
 msgstr ""
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:229
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:239
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:239
 msgid "Monday"
 msgstr ""
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:229
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:239
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:239
 msgid "Tuesday"
 msgstr ""
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:229
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:239
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:239
 msgid "Wednesday"
 msgstr ""
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:229
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:239
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:239
 msgid "Thursday"
 msgstr ""
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:230
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:240
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:240
 msgid "Friday"
 msgstr ""
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:230
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:240
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:240
 msgid "Saturday"
 msgstr ""
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:230
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:240
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:240
 msgid "Sunday"
 msgstr ""
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:274
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:290
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:290
 #, python-format
 msgid "1 second ago"
 msgid_plural "%(seconds)d seconds ago"
 msgstr[0] ""
 msgstr[1] ""
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:279
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:295
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:295
 #, python-format
 msgid "1 minute ago"
 msgid_plural "%(minutes)d minutes ago"
 msgstr[0] ""
 msgstr[1] ""
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:283
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:299
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:299
 #, python-format
 msgid "1 hour ago"
 msgid_plural "%(hours)d hours ago"
 msgstr[0] ""
 msgstr[1] ""
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:287
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:303
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:303
 #, python-format
 msgid "%(time)s"
 msgstr ""
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:290
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:306
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:306
 msgid "yesterday"
 msgstr ""
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:291
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:307
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:307
 #, python-format
 msgid "yesterday at %(time)s"
 msgstr ""
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:293
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:309
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:309
 #, python-format
 msgid "%(weekday)s"
 msgstr ""
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:294
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:310
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:310
 #, python-format
 msgid "%(weekday)s at %(time)s"
 msgstr ""
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:296
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:338
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:312
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:354
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:312
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:354
 #, python-format
 msgid "%(month_name)s %(day)s"
 msgstr ""
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:297
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:313
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:313
 #, python-format
 msgid "%(month_name)s %(day)s at %(time)s"
 msgstr ""
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:300
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:316
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:316
 #, python-format
 msgid "%(month_name)s %(day)s, %(year)s"
 msgstr ""
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:301
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:317
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:317
 #, python-format
 msgid "%(month_name)s %(day)s, %(year)s at %(time)s"
 msgstr ""
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:332
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:348
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:348
 #, python-format
 msgid "%(weekday)s, %(month_name)s %(day)s"
 msgstr ""
 
-#: /usr/lib/python2.7/site-packages/tornado/locale.py:353
+#: /usr/lib/python2.7/site-packages/tornado/locale.py:369
+#: /usr/lib/python3.3/site-packages/tornado/locale.py:369
 #, python-format
 msgid "%(commas)s and %(last)s"
 msgstr ""
diff --git a/webapp.conf.example b/webapp.conf.example
new file mode 100644 (file)
index 0000000..d2c5bbf
--- /dev/null
@@ -0,0 +1,7 @@
+[global]
+
+[database]
+server     = pgsql-master.ipfire.org
+database   = webapp
+username   = webapp
+password   = ...
index 0bbe14b9055fd7ac37bbd75638de1b2cb0fb568e..0217543a60ca09b572e45314d9cadafd0bf37fbd 100755 (executable)
--- a/webapp.py
+++ b/webapp.py
@@ -14,7 +14,7 @@ import tornado.options
 from webapp import Application
 
 if __name__ == "__main__":
-       app = Application()
+       app = Application(configfile="webapp.conf")
 
        context = daemon.DaemonContext(
                working_directory=os.getcwd(),
index 54bd5b7e312b04dae1319e6beeb85110b67eea84..36bd58ee7ce7ab32cff3976475da89edf23e556d 100644 (file)
@@ -21,7 +21,7 @@ BASEDIR = os.path.join(os.path.dirname(__file__), "..")
 tornado.locale.load_gettext_translations(os.path.join(BASEDIR, "translations"), "webapp")
 
 class Application(tornado.web.Application):
-       def __init__(self):
+       def __init__(self, **kwargs):
                self.__backend = None
 
                settings = dict(
@@ -34,30 +34,35 @@ class Application(tornado.web.Application):
                                "format_month_name" : self.format_month_name,
                        },
                        ui_modules = {
-                               "Advertisement"  : AdvertisementModule,
-                               "DonationBox"    : DonationBoxModule,
-                               "DownloadButton" : DownloadButtonModule,
-                               "Menu"           : MenuModule,
-                               "MirrorItem"     : MirrorItemModule,
-                               "MirrorsTable"   : MirrorsTableModule,
-                               "NewsItem"       : NewsItemModule,
-                               "NewsLine"       : NewsLineModule,
-                               "NewsTable"      : NewsTableModule,
-                               "NewsYearNavigation": NewsYearNavigationModule,
-                               "PlanetEntry"    : PlanetEntryModule,
-                               "ReleaseItem"    : ReleaseItemModule,
-                               "SidebarBanner"  : SidebarBannerModule,
-                               "SidebarRelease" : SidebarReleaseModule,
-                               "StasyTable"     : StasyTableModule,
-                               "StasyCPUCoreTable" : StasyCPUCoreTableModule,
-                               "StasyDeviceTable" : StasyDeviceTableModule,
-                               "StasyGeoTable"  : StasyGeoTableModule,
-                               "TrackerPeerList": TrackerPeerListModule,
-                               "Wish"           : WishModule,
-                               "Wishlist"       : WishlistModule,
+                               "Advertisement"        : AdvertisementModule,
+                               "DonationBox"          : DonationBoxModule,
+                               "DownloadButton"       : DownloadButtonModule,
+                               "Map"                  : MapModule,
+                               "Menu"                 : MenuModule,
+                               "MirrorItem"           : MirrorItemModule,
+                               "MirrorsTable"         : MirrorsTableModule,
+                               "NetBootMenuConfig"    : NetBootMenuConfigModule,
+                               "NetBootMenuHeader"    : NetBootMenuHeaderModule,
+                               "NetBootMenuSeparator" : NetBootMenuSeparatorModule,
+                               "NewsItem"             : NewsItemModule,
+                               "NewsLine"             : NewsLineModule,
+                               "NewsTable"            : NewsTableModule,
+                               "NewsYearNavigation"   : NewsYearNavigationModule,
+                               "PlanetEntry"          : PlanetEntryModule,
+                               "ReleaseItem"          : ReleaseItemModule,
+                               "SidebarBanner"        : SidebarBannerModule,
+                               "SidebarRelease"       : SidebarReleaseModule,
+                               "StasyTable"           : StasyTableModule,
+                               "StasyCPUCoreTable"    : StasyCPUCoreTableModule,
+                               "StasyDeviceTable"     : StasyDeviceTableModule,
+                               "StasyGeoTable"        : StasyGeoTableModule,
+                               "TrackerPeerList"      : TrackerPeerListModule,
+                               "Wish"                 : WishModule,
+                               "Wishlist"             : WishlistModule,
                        },
                        xsrf_cookies = True,
                )
+               settings.update(kwargs)
 
                tornado.web.Application.__init__(self, **settings)
 
@@ -106,7 +111,8 @@ class Application(tornado.web.Application):
                self.add_handlers(r"downloads?\.ipfire\.org", [
                        (r"/", DownloadsIndexHandler),
                        (r"/latest", DownloadsLatestHandler),
-                       (r"/release/([0-9]+)", DownloadsReleaseHandler),
+                       (r"/release/(\d)", DownloadsReleaseHandler),
+                       (r"/release/([\w\.\-]*)", DownloadsReleaseHandler),
                        (r"/older", DownloadsOlderHandler),
                        (r"/development", DownloadsDevelopmentHandler),
                        (r"/mirrors", tornado.web.RedirectHandler, { "url" : "http://mirrors.ipfire.org/" }),
@@ -120,7 +126,7 @@ class Application(tornado.web.Application):
                # mirrors.ipfire.org
                self.add_handlers(r"mirrors\.ipfire\.org", [
                        (r"/", MirrorIndexHandler),
-                       (r"/mirror/([0-9]+)", MirrorItemHandler),
+                       (r"/mirror/(.*)", MirrorItemHandler),
                        (r"/lists/pakfire2", MirrorListPakfire2Handler),
                ] + static_handlers)
 
@@ -206,6 +212,11 @@ class Application(tornado.web.Application):
                        (r"/terms", WishlistTermsHandler),
                ] + static_handlers)
 
+               # geoip.ipfire.org
+               self.add_handlers(r"geoip\.ipfire\.org", [
+                       (r"/", GeoIPHandler),
+               ] + static_handlers)
+
                # admin.ipfire.org
                self.add_handlers(r"admin\.ipfire\.org", [
                        (r"/", AdminIndexHandler),
@@ -252,7 +263,11 @@ class Application(tornado.web.Application):
        @property
        def backend(self):
                if self.__backend is None:
-                       self.__backend = backend.Backend()
+                       configfile = self.settings.get("configfile", None)
+                       if not configfile:
+                               raise RuntimeException("Could not find configuration file")
+
+                       self.__backend = backend.Backend(configfile=configfile)
 
                return self.__backend
 
index 17f42d430a8f22d05d345466fb341885401fc156..a6aa46b32d7b6fd1886e6a5c37b692b7f1f7993c 100644 (file)
@@ -7,19 +7,3 @@ define("debug", default=False, help="Run in debug mode", type=bool)
 parse_command_line()
 
 from base import Backend
-
-from ads       import Advertisements
-from accounts  import Accounts
-from banners   import Banners
-from geoip             import GeoIP
-from iuse              import IUse
-from memcached import Memcached
-from mirrors   import Downloads, Mirrors
-from netboot   import NetBoot
-from news              import News
-from planet            import Planet, PlanetEntry
-from releases  import Releases
-from settings  import Settings as Config
-from stasy             import Stasy
-from tracker   import Tracker
-from wishlist  import Wishlist
index 10976615ab6cc0c459b30ce5f12d9530cfff69ba..d2548168b2a1cce640d0270099216f23f95adfcb 100644 (file)
@@ -7,13 +7,8 @@ import logging
 import urllib
 
 from misc import Object
-from settings import Settings
 
 class Accounts(Object):
-       @property
-       def settings(self):
-               return Settings()
-
        def __init__(self, backend):
                Object.__init__(self, backend)
                self.__db = None
@@ -22,7 +17,7 @@ class Accounts(Object):
 
        @property
        def search_base(self):
-               return Settings().get("ldap_search_base")
+               return self.settings.get("ldap_search_base")
 
        @property
        def db(self):
@@ -34,7 +29,7 @@ class Accounts(Object):
                        bind_dn = self.settings.get("ldap_bind_dn")
 
                        if bind_dn:
-                               bind_pw = self.settings.get("ldap_bind_pw")
+                               bind_pw = self.settings.get("ldap_bind_pw", "")
 
                                self.__db.simple_bind(bind_dn, bind_pw)
 
index eadf10acfdf9f698c40e509be637b8cc5c394028..f7abe5885488c6194e6c93d6825080fb5e8eaf82 100644 (file)
@@ -5,43 +5,32 @@ from __future__ import division
 import datetime
 import textile
 
-from databases import Databases
-from misc import Singleton
-
-class Advertisements(object):
-       __metaclass__ = Singleton
-
-       @property
-       def db(self):
-               return Databases().webapp
+from misc import Object
 
+class Advertisements(Object):
        def get(self, where=None):
-               args = []
                query = "SELECT * FROM advertisements \
-                       WHERE DATE(NOW()) >= date_start AND DATE(NOW()) <= date_end AND published = 'Y'"
+                       WHERE NOW() BETWEEN date_start AND date_end AND published = %s"
+               args = [True]
 
                if where:
-                       query += " AND `where` = %s"
+                       query += " AND location = %s"
                        args.append(where)
 
-               query += " ORDER BY RAND() LIMIT 1"
+               query += " ORDER BY RANDOM() LIMIT 1"
 
                ad = self.db.get(query, *args)
                if ad:
-                       return Advert(self, ad.id, ad)
+                       return Advert(self.backend, ad.id, ad)
 
 
-class Advert(object):
-       def __init__(self, advertisements, id, data=None):
-               self.advertisements = advertisements
-               self.id = id
+class Advert(Object):
+       def __init__(self, backend, id, data=None):
+               Object.__init__(self, backend)
 
+               self.id = id
                self.__data = data
 
-       @property
-       def db(self):
-               return self.advertisements.db
-
        @property
        def data(self):
                if self.__data is None:
diff --git a/webapp/backend/banners.py b/webapp/backend/banners.py
deleted file mode 100644 (file)
index 08db020..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/python
-
-from databases import Databases
-from misc import Singleton
-
-class Banners(object):
-       __metaclass__ = Singleton
-
-       @property
-       def db(self):
-               return Databases().webapp
-
-       def list(self):
-               return self.db.query("SELECT * FROM banners")
-
-       def get_random(self):
-               return self.db.get("SELECT * FROM banners ORDER BY RAND() LIMIT 1")
-
-
-if __name__ == "__main__":
-       b = Banners()
-
-       print b.list()
-
-       print "--- RANDOM ---"
-
-       for i in range(5):
-               print i, b.get_random()
index 58ee5ca8c0ce3b21ac6116680cf4d1a5ae5b41b4..21a31c58742976cf3295a84aabe690dcb6986002 100644 (file)
@@ -1,17 +1,65 @@
 #!/usr/bin/python
 
-import tornado.database
+import ConfigParser as configparser
 
 import accounts
+import ads
+import database
+import geoip
+import iuse
+import memcached
+import mirrors
+import netboot
+import news
 import planet
-
-MYSQL_SERVER = "mysql-master.ipfire.org"
-MYSQL_DB     = "webapp"
-MYSQL_USER   = "webapp"
+import releases
+import settings
+import stasy
+import tracker
+import wishlist
 
 class Backend(object):
-       def __init__(self):
-               self.db = tornado.database.Connection(MYSQL_SERVER, MYSQL_DB, user=MYSQL_USER)
+       def __init__(self, configfile):
+               # Read configuration file.
+               self.config = self.read_config(configfile)
+
+               # Setup database.
+               self.setup_database()
 
+               # Initialize settings first.
+               self.settings = settings.Settings(self)
+               self.memcache = memcached.Memcached(self)
+
+               # Initialize backend modules.
                self.accounts = accounts.Accounts(self)
+               self.advertisements = ads.Advertisements(self)
+               self.downloads = mirrors.Downloads(self)
+               self.geoip = geoip.GeoIP(self)
+               self.iuse = iuse.IUse(self)
+               self.mirrors = mirrors.Mirrors(self)
+               self.netboot = netboot.NetBoot(self)
+               self.news = news.News(self)
                self.planet = planet.Planet(self)
+               self.releases = releases.Releases(self)
+               self.stasy = stasy.Stasy(self)
+               self.tracker = tracker.Tracker(self)
+               self.wishlist = wishlist.Wishlist(self)
+
+       def read_config(self, configfile):
+               cp = configparser.ConfigParser()
+               cp.read(configfile)
+
+               return cp
+
+       def setup_database(self):
+               """
+                       Sets up the database connection.
+               """
+               credentials = {
+                       "host"     : self.config.get("database", "server"),
+                       "database" : self.config.get("database", "database"),
+                       "user"     : self.config.get("database", "username"),
+                       "password" : self.config.get("database", "password"),
+               }
+
+               self.db = database.Connection(**credentials)
diff --git a/webapp/backend/countries.py b/webapp/backend/countries.py
new file mode 100644 (file)
index 0000000..9c42119
--- /dev/null
@@ -0,0 +1,274 @@
+#!/usr/bin/python
+# encoding: utf-8
+
+_ = lambda x: x
+
+country_list = {
+       "AD" : _("Andorra"),
+       "AE" : _("United Arab Emirates"),
+       "AF" : _("Afghanistan"),
+       "AG" : _("Antigua and Barbuda"),
+       "AI" : _("Aanguilla"),
+       "AL" : _("Albania"),
+       "AM" : _("Armenia"),
+       "AO" : _("Angola"),
+       "AQ" : _("Antarctica"),
+       "AR" : _("Argentina"),
+       "AS" : _("American Samoa"),
+       "AT" : _("Austria"),
+       "AU" : _("Australia"),
+       "AW" : _("Aruba"),
+       "AX" : _("Ã…land Islands"),
+       "AZ" : _("Azerbaijan"),
+       "BA" : _("Bosnia and Herzegovina"),
+       "BB" : _("Barbados"),
+       "BD" : _("Bangladesh"),
+       "BE" : _("Belgium"),
+       "BF" : _("Burkina Faso"),
+       "BG" : _("Bulgaria"),
+       "BH" : _("Bahrain"),
+       "BI" : _("Burundi"),
+       "BJ" : _("Benin"),
+       "BL" : _("Saint Barthélemy"),
+       "BM" : _("Bermuda"),
+       "BN" : _("Brunei Darussalam"),
+       "BO" : _("Plurinational State of Bolivia"),
+       "BQ" : _("Sint Eustatius and Saba Bonaire"),
+       "BR" : _("Brazil"),
+       "BS" : _("Bahamas"),
+       "BT" : _("Bhutan"),
+       "BV" : _("Bouvet Island"),
+       "BW" : _("Botswana"),
+       "BY" : _("Belarus"),
+       "BZ" : _("Belize"),
+       "CA" : _("Canada"),
+       "CC" : _("Cocos (Keeling) Islands"),
+       "CD" : _("The Democratic Republic of the Congo"),
+       "CF" : _("Central African Republic"),
+       "CG" : _("Congo"),
+       "CH" : _("Switzerland"),
+       "CI" : _("Côte d'Ivoire"),
+       "CK" : _("Cook Islands"),
+       "CL" : _("Chile"),
+       "CM" : _("Cameroon"),
+       "CN" : _("China"),
+       "CO" : _("Colombia"),
+       "CR" : _("Costa Rica"),
+       "CU" : _("Cuba"),
+       "CV" : _("Cape Verde"),
+       "CW" : _("Curaçao"),
+       "CX" : _("Chrismas Islands"),
+       "CY" : _("Cyprus"),
+       "CZ" : _("Czech Republic"),
+       "DE" : _("Germany"),
+       "DJ" : _("Djibouti"),
+       "DK" : _("Denmark"),
+       "DM" : _("Dominica"),
+       "DO" : _("Dominican Republic"),
+       "DZ" : _("Algeria"),
+       "EC" : _("Ecuador"),
+       "EE" : _("Estonia"),
+       "EG" : _("Egypt"),
+       "EH" : _("Western Sahara"),
+       "ER" : _("Eritrea"),
+       "ES" : _("Spain"),
+       "ET" : _("Ethiopia"),
+       "FI" : _("Finland"),
+       "FJ" : _("Fiji"),
+       "FK" : _("Falkland Islands (Malvinas)"),
+       "FM" : _("Federated States of Micronesia"),
+       "FO" : _("Faroe Islands"),
+       "FR" : _("France"),
+       "GA" : _("Gabon"),
+       "GB" : _("United Kingdom"),
+       "GD" : _("Grenada"),
+       "GE" : _("Georgia"),
+       "GF" : _("French Guiana"),
+       "GG" : _("Guersey"),
+       "GH" : _("Ghana"),
+       "GI" : _("Gibraltar"),
+       "GL" : _("Greenland"),
+       "GM" : _("Gambia"),
+       "GN" : _("Guinea"),
+       "GP" : _("Guadeloupe"),
+       "GQ" : _("Equatorial Guinea"),
+       "GR" : _("Greece"),
+       "GS" : _("South Georgia and the South Sandwich Islands"),
+       "GT" : _("Guatemala"),
+       "GU" : _("Guam"),
+       "GW" : _("Guinea-Bissau"),
+       "GY" : _("Guyana"),
+       "HK" : _("Hong Kong"),
+       "HM" : _("Heard Island and McDonald Islands"),
+       "HN" : _("Honduras"),
+       "HR" : _("Croatia"),
+       "HT" : _("Haiti"),
+       "HU" : _("Hungary"),
+       "ID" : _("Indonesia"),
+       "IE" : _("Ireland"),
+       "IL" : _("Israel"),
+       "IM" : _("Isle of Man"),
+       "IN" : _("India"),
+       "IO" : _("British Indian Ocean Territory"),
+       "IQ" : _("Iraq"),
+       "IR" : _("Islamic Republic of Iran"),
+       "IS" : _("Iceland"),
+       "IT" : _("Italy"),
+       "JE" : _("Jersey"),
+       "JM" : _("Jamaica"),
+       "JO" : _("Jordan"),
+       "JP" : _("Japan"),
+       "KE" : _("Kenya"),
+       "KG" : _("Kyrgyzstan"),
+       "KH" : _("Cambodia"),
+       "KI" : _("Kiribati"),
+       "KM" : _("Comoros"),
+       "KN" : _("Saint Kitts and Nevis"),
+       "KP" : _("Democratic People's Republic of Korea"),
+       "KR" : _("Republic of Korea"),
+       "KW" : _("Kuwait"),
+       "KY" : _("Cayman Islands"),
+       "KZ" : _("Kazakhstan"),
+       "LA" : _("Lao People's Democratic Public"),
+       "LB" : _("Lebanon"),
+       "LC" : _("Saint Lucia"),
+       "LI" : _("Liechtenstein"),
+       "LK" : _("Sri Lanka"),
+       "LR" : _("Liberia"),
+       "LS" : _("Lesotho"),
+       "LT" : _("Lithuania"),
+       "LU" : _("Luxembourg"),
+       "LV" : _("Latvia"),
+       "LY" : _("Libya"),
+       "MA" : _("Morocco"),
+       "MC" : _("Monaco"),
+       "MD" : _("Republic of Moldova"),
+       "ME" : _("Montenegro"),
+       "MF" : _("Saint Martin (French Part)"),
+       "MG" : _("Madagascar"),
+       "MH" : _("Marshall Islands"),
+       "MK" : _("The former Yugoslav Republic of Macedonia"),
+       "ML" : _("Mali"),
+       "MM" : _("Myanmar"),
+       "MN" : _("Mongolia"),
+       "MO" : _("Macao"),
+       "MP" : _("Northern Mariana Islands"),
+       "MQ" : _("Martinique"),
+       "MR" : _("Mauritania"),
+       "MS" : _("Montserrat"),
+       "MT" : _("Malta"),
+       "MU" : _("Mauritius"),
+       "MV" : _("Maldives"),
+       "MW" : _("Malawi"),
+       "MX" : _("Mexico"),
+       "MY" : _("Malaysia"),
+       "MZ" : _("Mozambique"),
+       "NA" : _("Namibia"),
+       "NC" : _("New Caledonia"),
+       "NE" : _("Niger"),
+       "NF" : _("Norfolk Island"),
+       "NG" : _("Nigeria"),
+       "NI" : _("Nicaragua"),
+       "NL" : _("Netherlands"),
+       "NO" : _("Norway"),
+       "NP" : _("Nepal"),
+       "NR" : _("Nauru"),
+       "NU" : _("Niue"),
+       "NZ" : _("New Zealand"),
+       "OM" : _("Oman"),
+       "PA" : _("Panama"),
+       "PE" : _("Peru"),
+       "PF" : _("French Polynesia"),
+       "PG" : _("Papua New Guinea"),
+       "PH" : _("Philipplines"),
+       "PK" : _("Pakistan"),
+       "PL" : _("Poland"),
+       "PM" : _("Saint Pierre and Miquelon"),
+       "PN" : _("Pitcairn"),
+       "PR" : _("Puerto Rico"),
+       "PS" : _("Palestinian Territory, occupied"),
+       "PT" : _("Portugal"),
+       "PW" : _("Palau"),
+       "PY" : _("Paraguay"),
+       "QA" : _("Qatar"),
+       "RE" : _("Réunion"),
+       "RO" : _("Romania"),
+       "RS" : _("Serbia"),
+       "RU" : _("Russian Federation"),
+       "RW" : _("Rwanda"),
+       "SA" : _("Saudi Arabia"),
+       "SB" : _("Solomon Islands"),
+       "SC" : _("Seychelles"),
+       "SD" : _("Sudan"),
+       "SE" : _("Sweden"),
+       "SG" : _("Singapore"),
+       "SH" : _("Saint Helena, Ascension and Tristan Da Cunha"),
+       "SI" : _("Slovenia"),
+       "SJ" : _("Svalbard and Jan Mayen"),
+       "SK" : _("Slovakia"),
+       "SL" : _("Sierra Leone"),
+       "SM" : _("San Marino"),
+       "SN" : _("Senegal"),
+       "SO" : _("Somalia"),
+       "SR" : _("Suriname"),
+       "SS" : _("South Sudan"),
+       "ST" : _("Sao Tome and Principe"),
+       "SV" : _("El Salvador"),
+       "SX" : _("Sint Maarten (Dutch Part)"),
+       "SY" : _("Syrian Arab Republic"),
+       "SZ" : _("Swaziland"),
+       "TC" : _("Turks and Caicos Islands"),
+       "TD" : _("Chad"),
+       "TF" : _("French Southern Territories"),
+       "TG" : _("Togo"),
+       "TH" : _("Thailand"),
+       "TJ" : _("Tajikistan"),
+       "TK" : _("Tokelau"),
+       "TL" : _("Timor-Leste"),
+       "TM" : _("Turkmenistan"),
+       "TN" : _("Tunisia"),
+       "TO" : _("Tonga"),
+       "TR" : _("Turkey"),
+       "TT" : _("Trinidad and Tobago"),
+       "TV" : _("Tuvalu"),
+       "TW" : _("Taiwan, Province of China"),
+       "TZ" : _("United Republic of Tanzania"),
+       "UA" : _("Ukraine"),
+       "UG" : _("Uganda"),
+       "UM" : _("Unites States minor outlying islands"),
+       "US" : _("Unites States"),
+       "UY" : _("Uruguay"),
+       "UZ" : _("Uzbekistan"),
+       "VA" : _("Vatican City State"),
+       "VC" : _("Saint Vincent and the Grenadines"),
+       "VE" : _("Bolivarian Republic of Venezuela"),
+       "VG" : _("Virgin Islands, British"),
+       "VI" : _("Virgin Islands, U.S."),
+       "VN" : _("Viet Nam"),
+       "VU" : _("Vanuatu"),
+       "WF" : _("Wallis and Futuna"),
+       "WS" : _("Samoa"),
+       "YE" : _("Yemen"),
+       "YT" : _("Mayotte"),
+       "ZA" : _("South Africa"),
+       "ZM" : _("Zambia"),
+       "ZW" : _("Zimbabwe"),
+}
+
+def get_by_code(code):
+       return country_list.get(code, None)
+
+def get_all(locale=None):
+       if locale is None:
+               l = country_list.keys()
+       else:
+               l = []
+
+               for c in country_list.keys():
+                       c = locale.translate(c)
+                       l.append(c)
+
+       # Sort list in place.
+       l.sort()
+
+       return l
diff --git a/webapp/backend/database.py b/webapp/backend/database.py
new file mode 100644 (file)
index 0000000..d2964d3
--- /dev/null
@@ -0,0 +1,189 @@
+#!/usr/bin/env python
+
+"""
+       A lightweight wrapper around psycopg2.
+
+       Originally part of the Tornado framework.  The tornado.database module
+       is slated for removal in Tornado 3.0, and it is now available separately
+       as torndb.
+"""
+
+from __future__ import absolute_import, division, with_statement
+
+import copy
+import itertools
+import logging
+import os
+import psycopg2
+import time
+
+class Connection(object):
+       """
+               A lightweight wrapper around MySQLdb DB-API connections.
+
+               The main value we provide is wrapping rows in a dict/object so that
+               columns can be accessed by name. Typical usage::
+
+                       db = torndb.Connection("localhost", "mydatabase")
+                       for article in db.query("SELECT * FROM articles"):
+                               print article.title
+
+               Cursors are hidden by the implementation, but other than that, the methods
+               are very similar to the DB-API.
+
+               We explicitly set the timezone to UTC and the character encoding to
+               UTF-8 on all connections to avoid time zone and encoding errors.
+       """
+       def __init__(self, host, database, user=None, password=None):
+               self.host = host
+               self.database = database
+
+               self._db = None
+               self._db_args = {
+                       "host"     : host,
+                       "database" : database,
+                       "user"     : user,
+                       "password" : password,
+               }
+
+               try:
+                       self.reconnect()
+               except Exception:
+                       logging.error("Cannot connect to database on %s", self.host, exc_info=True)
+
+       def __del__(self):
+               self.close()
+
+       def close(self):
+               """
+                       Closes this database connection.
+               """
+               if getattr(self, "_db", None) is not None:
+                       self._db.close()
+                       self._db = None
+
+       def reconnect(self):
+               """
+                       Closes the existing database connection and re-opens it.
+               """
+               self.close()
+
+               self._db = psycopg2.connect(**self._db_args)
+               self._db.autocommit = True
+
+       def query(self, query, *parameters, **kwparameters):
+               """
+                       Returns a row list for the given query and parameters.
+               """
+               cursor = self._cursor()
+               try:
+                       self._execute(cursor, query, parameters, kwparameters)
+                       column_names = [d[0] for d in cursor.description]
+                       return [Row(itertools.izip(column_names, row)) for row in cursor]
+               finally:
+                       cursor.close()
+
+       def get(self, query, *parameters, **kwparameters):
+               """
+                       Returns the first row returned for the given query.
+               """
+               rows = self.query(query, *parameters, **kwparameters)
+               if not rows:
+                       return None
+               elif len(rows) > 1:
+                       raise Exception("Multiple rows returned for Database.get() query")
+               else:
+                       return rows[0]
+
+       def execute(self, query, *parameters, **kwparameters):
+               """
+                       Executes the given query, returning the lastrowid from the query.
+               """
+               return self.execute_lastrowid(query, *parameters, **kwparameters)
+
+       def execute_lastrowid(self, query, *parameters, **kwparameters):
+               """
+                       Executes the given query, returning the lastrowid from the query.
+               """
+               cursor = self._cursor()
+               try:
+                       self._execute(cursor, query, parameters, kwparameters)
+                       return cursor.lastrowid
+               finally:
+                       cursor.close()
+
+       def execute_rowcount(self, query, *parameters, **kwparameters):
+               """
+                       Executes the given query, returning the rowcount from the query.
+               """
+               cursor = self._cursor()
+               try:
+                       self._execute(cursor, query, parameters, kwparameters)
+                       return cursor.rowcount
+               finally:
+                       cursor.close()
+
+       def executemany(self, query, parameters):
+               """
+                       Executes the given query against all the given param sequences.
+
+                       We return the lastrowid from the query.
+               """
+               return self.executemany_lastrowid(query, parameters)
+
+       def executemany_lastrowid(self, query, parameters):
+               """
+                       Executes the given query against all the given param sequences.
+
+                       We return the lastrowid from the query.
+               """
+               cursor = self._cursor()
+               try:
+                       cursor.executemany(query, parameters)
+                       return cursor.lastrowid
+               finally:
+                       cursor.close()
+
+       def executemany_rowcount(self, query, parameters):
+               """
+                       Executes the given query against all the given param sequences.
+
+                       We return the rowcount from the query.
+               """
+               cursor = self._cursor()
+
+               try:
+                       cursor.executemany(query, parameters)
+                       return cursor.rowcount
+               finally:
+                       cursor.close()
+
+       def _ensure_connected(self):
+               if self._db is None:
+                       self.reconnect()
+
+       def _cursor(self):
+               self._ensure_connected()
+               return self._db.cursor()
+
+       def _execute(self, cursor, query, parameters, kwparameters):
+               try:
+                       return cursor.execute(query, kwparameters or parameters)
+               except OperationalError:
+                       logging.error("Error connecting to database on %s", self.host)
+                       self.close()
+                       raise
+
+
+class Row(dict):
+       """A dict that allows for object-like property access syntax."""
+       def __getattr__(self, name):
+               try:
+                       return self[name]
+               except KeyError:
+                       raise AttributeError(name)
+
+
+# Alias some common exceptions
+IntegrityError = psycopg2.IntegrityError
+OperationalError = psycopg2.OperationalError
diff --git a/webapp/backend/databases.py b/webapp/backend/databases.py
deleted file mode 100644 (file)
index f09cead..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/usr/bin/python
-
-import logging
-import tornado.database
-
-from misc import Singleton
-
-class Row(tornado.database.Row):
-       pass
-
-MYSQL_SERVER = "mysql-master.ipfire.org"
-
-class Databases(object):
-       __metaclass__ = Singleton
-
-       def __init__(self):
-               self._connections = {}
-
-       @property
-       def webapp(self):
-               if not self._connections.has_key("webapp"):
-                       self._connections["webapp"] = \
-                               Connection(MYSQL_SERVER, "webapp", user="webapp")
-
-               return self._connections["webapp"]
-
-       @property
-       def geoip(self):
-               if not self._connections.has_key("geoip"):
-                       self._connections["geoip"] = \
-                               Connection(MYSQL_SERVER, "geoip", user="webapp")
-
-               return self._connections["geoip"]
-
-
-class Connection(tornado.database.Connection):
-       def __init__(self, *args, **kwargs):
-               logging.debug("Creating new database connection: %s" % args[1])
-
-               tornado.database.Connection.__init__(self, *args, **kwargs)
-
-       def update(self, table, item_id, **items):
-               query = "UPDATE %s SET " % table
-
-               keys = []
-               for k, v in items.items():
-                       # Never update id
-                       if k == "id":
-                               continue
-
-                       keys.append("%s='%s'" % (k, v))
-
-               query += ", ".join(keys)
-               query += " WHERE id=%s" % item_id
-
-               return self.execute(query)
-
-       def insert(self, table, **items):
-               query = "INSERT INTO %s" % table
-
-               keys = []
-               vals = []
-
-               for k, v in items.items():
-                       # Never insert id
-                       if k == "id":
-                               continue
-
-                       keys.append(k)
-                       vals.append("'%s'" % v)
-
-               query += "(%s)"% ", ".join(keys)
-               query += " VALUES(%s)" % ", ".join(vals)
-
-               return self.execute(query)
-
-       def _execute(self, cursor, query, parameters):
-               logging.debug("Executing query: %s" % (query % parameters))
-
-               return tornado.database.Connection._execute(self, cursor, query, parameters)
index 43f92da3acf57d59dff1b65eaa7ca489f3973327..0fe4310face8e05a3431c37a1917c5e0d3ec8bb3 100644 (file)
@@ -1,93 +1,76 @@
 #!/usr/bin/python
 
+import IPy
 import re
 
-from databases import Databases
-from memcached import Memcached
-from misc import Singleton
+import countries
 
-class GeoIP(object):
-       __metaclass__ = Singleton
+from misc import Object
 
-       @property
-       def db(self):
-               return Databases().geoip
+class GeoIP(Object):
+       def guess_address_family(self, addr):
+               if ":" in addr:
+                       return 6
 
-       def _encode_ip(self, addr):
-               # We get a tuple if there were proxy headers.
-               addr = addr.split(", ")
-               if addr:
-                       addr = addr[-1]
+               return 4
 
-               # ip is calculated as described in http://dev.maxmind.com/geoip/csv
-               a1, a2, a3, a4 = addr.split(".")
+       def get_country(self, addr):
+               ret = self.get_all(addr)
 
-               try:
-                       a1 = int(a1)
-                       a2 = int(a2)
-                       a3 = int(a3)
-                       a4 = int(a4)
-               except ValueError:
-                       return 0
+               if ret:
+                       return ret.country
 
-               return (16777216 * a1) + (65536 * a2) + (256 * a3) + a4
+       def get_location(self, addr):
+               family = self.guess_address_family(addr)
 
-       def get_country(self, addr):
-               addr = self._encode_ip(addr)
+               if family == 6:
+                       query = "SELECT *, NULL AS city, NULL AS postal_code FROM geoip_ipv6 WHERE %s \
+                               BETWEEN start_ip AND end_ip LIMIT 1"
+               elif family == 4:
+                       query = "SELECT * FROM geoip_ipv4 WHERE inet_to_bigint(%s) \
+                               BETWEEN start_ip AND end_ip LIMIT 1"
 
-               ret = self.db.get("SELECT locations.country_code AS country_code FROM addresses \
-                       JOIN locations ON locations.id = addresses.location \
-                       WHERE %s BETWEEN start_ip_num AND end_ip_num LIMIT 1", addr)
+               return self.db.get(query, addr)
 
-               if ret:
-                       return ret.country_code
+       def get_asn(self, addr):
+               family = self.guess_address_family(addr)
 
-       def get_all(self, addr):
-               addr = self._encode_ip(addr)
-               if not addr:
-                       return
+               if family == 6:
+                       query = "SELECT asn FROM geoip_asnv6 WHERE %s \
+                               BETWEEN start_ip AND end_ip LIMIT 1"
+               elif family == 4:
+                       query = "SELECT asn FROM geoip_asnv4 WHERE inet_to_bigint(%s) \
+                               BETWEEN start_ip AND end_ip LIMIT 1"
 
-               ret = self.db.get("SELECT locations.* FROM addresses \
-                       JOIN locations ON locations.id = addresses.location \
-                       WHERE %s BETWEEN start_ip_num AND end_ip_num LIMIT 1", addr)
+               ret = self.db.get(query, addr)
 
-               if not ret:
-                       return
+               if ret:
+                       return ret.asn
 
-               # If location was not determinable
-               if ret.latitude == 0 and ret.longitude == 0:
-                       return None
+       def get_all(self, addr):
+               location = self.get_location(addr)
 
-               return ret
+               if location:
+                       location["asn"] = self.get_asn(addr)
 
-       def get_country_name(self, code):
-               name = "Unkown"
+               return location
 
-               codes = {
-                       "A1" : "Anonymous Proxy",
-                       "A2" : "Satellite Provider",
-                       "EU" : "Europe",
-                       "AP" : "Asia/Pacific Region",
-               }
+       _countries = {
+               "A1" : "Anonymous Proxy",
+               "A2" : "Satellite Provider",
+               "AP" : "Asia/Pacific Region",
+               "EU" : "Europe",
+       }
 
+       def get_country_name(self, code):
                # Return description of some exceptional codes.
                try:
-                       return codes[code]
+                       return self._countries[code]
                except KeyError:
                        pass
 
-               ret = self.db.get("SELECT name FROM iso3166_countries WHERE code = %s LIMIT 1", code)
-               if ret:
-                       name = ret.name
-
-               # Fix some weird strings
-               name = re.sub(r"(.*) (.* Republic of)", r"\2 \1", name)
-
-               return name
-
-
-if __name__ == "__main__":
-       g = GeoIP()
+               country = countries.get_by_code(code)
+               if not country:
+                       return code
 
-       print g.get_country("123.123.123.123")
-       print g.get_all("123.123.123.123")
+               return country
index 7b447c9c5cbaf0d3dff196a26c491378231c92d1..8c330ecc10f5225c43585eaaa26b49488275b5fb 100644 (file)
@@ -8,25 +8,20 @@ import os.path
 
 from PIL import Image, ImageDraw, ImageFont, PngImagePlugin
 
-from misc import Singleton
+from misc import Object
 
-class IUse(object):
-       __metaclass__ = Singleton
-
-       images = []
-
-       def add_imagetype(self, type):
-               self.images.append(type)
+image_types = []
 
+class IUse(Object):
        def get_imagetype(self, id):
                id = int(id)
 
-               for image in self.images:
-                       if image.id == id:
-                               return image
+               for image_type in image_types:
+                       if image_type.id == id:
+                               return image_type
 
 
-class ImageObject(object):
+class ImageObject(Object):
        default_mode = "RGBA"
        default_size = 100, 100
 
@@ -34,7 +29,9 @@ class ImageObject(object):
        _font = "DejaVuSans.ttf"
        _font_size = 10
 
-       def __init__(self, request, profile):
+       def __init__(self, backend, request, profile):
+               Object.__init__(self, backend)
+
                self.request = request
                self.profile = profile
 
@@ -183,10 +180,4 @@ class Image1(ImageObject):
                self.draw_text((225, 32), "%s" % " - ".join(line3))
 
 
-IUse().add_imagetype(Image1)
-
-
-if __name__ == "__main__":
-       image = Image1("123")
-       image.save("picture.png")
-
+image_types.append(Image1)
index a56b80ee9e1b1a7f806019445a89259a45e704a8..105f1f54a3fbb4fba1fbd1ee36e19feee130303c 100644 (file)
@@ -1,30 +1,50 @@
 #!/usr/bin/python
 
+import logging
 import memcache
 
-from misc import Singleton
-from settings import Settings
+from misc import Object
 
-class Memcached(object):
-       __metaclass__ = Singleton
+class Memcached(Object):
+       def init(self):
+               self._connection = None
 
-       def __init__(self):
-               # Fetch hosts from database
-               hosts = Settings().get("memcached_servers").split(",")
+               servers = self.get_servers()
 
-               self._connection = memcache.Client(hosts, debug=0)
+               # Nothing to do, if no servers have been configured.
+               if not servers:
+                       logging.warning("No memcache servers defined")
+                       return
+
+               logging.info("Using memcache servers: %s" % ", ".join(servers))
+               self._connection = memcache.Client(servers, debug=0)
+
+       def get_servers(self):
+               servers = self.settings.get("memcached_servers")
+
+               if servers:
+                       return servers.split(" ")
 
        def get(self, key, *args, **kwargs):
+               if not self._connection:
+                       return
+
                key = str(key)
 
                return self._connection.get(key, *args, **kwargs)
 
        def set(self, key, *args, **kwargs):
+               if not self._connection:
+                       return
+
                key = str(key)
 
                return self._connection.set(key, *args, **kwargs)
 
        def delete(self, key, *args, **kwargs):
+               if not self._connection:
+                       return
+
                key = str(key)
 
                return self._connection.delete(key, *args, **kwargs)
index 260cff0d0a425340019b425c13a2b2edec31256a..265e5160b82994f1a18d7b12d037822a372611f5 100644 (file)
@@ -1,5 +1,8 @@
 #!/usr/bin/python
 
+from __future__ import division
+
+import datetime
 import logging
 import math
 import os.path
@@ -7,23 +10,12 @@ import random
 import socket
 import time
 import tornado.httpclient
+import tornado.netutil
+import urlparse
 
-from databases import Databases
-from geoip import GeoIP
-from memcached import Memcached
-from misc import Singleton
-
-class Downloads(object):
-       __metaclass__ = Singleton
-
-       @property
-       def db(self):
-               return Databases().webapp
-
-       @property
-       def mirrors(self):
-               return Mirrors()
+from misc import Object
 
+class Downloads(Object):
        @property
        def total(self):
                ret = self.db.get("SELECT COUNT(*) AS total FROM log_download")
@@ -32,20 +24,20 @@ class Downloads(object):
 
        @property
        def today(self):
-               ret = self.db.get("SELECT COUNT(*) AS today FROM log_download WHERE date >= NOW() - 1000000")
+               ret = self.db.get("SELECT COUNT(*) AS today FROM log_download WHERE date::date = NOW()::date")
 
                return ret.today
 
        @property
        def yesterday(self):
-               ret = self.db.get("SELECT COUNT(*) AS yesterday FROM log_download WHERE DATE(date) = DATE(NOW())-1")
+               ret = self.db.get("SELECT COUNT(*) AS yesterday FROM log_download WHERE date::date = (NOW() - INTERVAL '1 day')::date")
 
                return ret.yesterday
 
        @property
        def daily_map(self):
-               ret = self.db.query("SELECT DATE(date) AS date, COUNT(*) AS downloads FROM log_download"
-                       " WHERE DATE(date) BETWEEN DATE(NOW()) - 31 AND DATE(NOW()) GROUP BY DATE(date)")
+               ret = self.db.query("SELECT date::date AS date, COUNT(*) AS downloads FROM log_download"
+                       " WHERE date::date BETWEEN (NOW() - INTERVAL '30 days')::date AND NOW()::date GROUP BY date::date")
 
                return ret
 
@@ -53,7 +45,7 @@ class Downloads(object):
                query = "SELECT country_code, count(country_code) AS count FROM log_download"
 
                if duration == "today":
-                       query += " WHERE date >= NOW() - 1000000"
+                       query += " WHERE date::date = NOW()::date"
 
                query += " GROUP BY country_code ORDER BY count DESC"
 
@@ -61,8 +53,9 @@ class Downloads(object):
                ret = {}
 
                count = sum([o.count for o in results])
-               for res in results:
-                       ret[res.country_code] = float(res.count) / count
+               if count:
+                       for res in results:
+                               ret[res.country_code] = res.count / count
 
                return ret
 
@@ -70,7 +63,7 @@ class Downloads(object):
                query = "SELECT mirror, COUNT(mirror) AS count FROM log_download"
 
                if duration == "today":
-                       query += " WHERE date >= NOW() - 1000000"
+                       query += " WHERE date::date = NOW()::date"
 
                query += " GROUP BY mirror ORDER BY count DESC"
 
@@ -78,51 +71,47 @@ class Downloads(object):
                ret = {}
 
                count = sum([o.count for o in results])
-               for res in results:
-                       mirror = self.mirrors.get(res.mirror)
-                       ret[mirror.hostname] = float(res.count) / count
+               if count:
+                       for res in results:
+                               mirror = self.mirrors.get(res.mirror)
+                               ret[mirror.hostname] = res.count / count
 
                return ret
 
 
-class Mirrors(object):
-       __metaclass__ = Singleton
-
-       @property
-       def db(self):
-               return Databases().webapp
-
-       @property
-       def memcached(self):
-               return Memcached()
-
-       def list(self):
-               return [Mirror(m.id) for m in self.db.query("SELECT id FROM mirrors WHERE disabled = 'N' ORDER BY state,hostname")]
-
+class Mirrors(Object):
        def check_all(self):
-               for mirror in self.list():
+               for mirror in self.get_all():
                        mirror.check()
 
        def get(self, id):
-               return Mirror(id)
+               return Mirror(self.backend, id)
 
        def get_all(self):
-               return MirrorSet(self.list())
+               res = self.db.query("SELECT * FROM mirrors WHERE enabled = %s", True)
+
+               mirrors = []
+               for row in res:
+                       mirror = Mirror(self.backend, row.id, row)
+                       mirrors.append(mirror)
+
+               return MirrorSet(self.backend, sorted(mirrors))
 
        def get_all_up(self):
-               res = self.db.query("SELECT * FROM mirrors WHERE disabled = %s AND state = %s ORDER BY hostname", "N", "UP")
+               res = self.db.query("SELECT * FROM mirrors WHERE enabled = %s AND state = %s \
+                       ORDER BY hostname", True, "UP")
 
                mirrors = []
                for row in res:
-                       m = Mirror(row.id, row)
+                       m = Mirror(self.backend, row.id, row)
                        mirrors.append(m)
 
-               return MirrorSet(mirrors)
+               return MirrorSet(self.backend, mirrors)
 
        def get_by_hostname(self, hostname):
                mirror = self.db.get("SELECT id FROM mirrors WHERE hostname=%s", hostname)
 
-               return Mirror(mirror.id)
+               return Mirror(self.backend, mirror.id)
 
        def get_with_file(self, filename, country=None):
                # XXX quick and dirty solution - needs a performance boost
@@ -141,8 +130,6 @@ class Mirrors(object):
                                continue
                        mirrors.append(mirror)
 
-               logging.debug("%s" % mirrors)
-
                return mirrors
 
        def get_for_country(self, country):
@@ -152,19 +139,22 @@ class Mirrors(object):
                for mirror in mirrors:
                        yield self.get(mirror.id)
 
-       def get_for_location(self, addr):
-               distance = 10
-
-               mirrors = []
-               all_mirrors = self.list()
-
-               location = GeoIP().get_all(addr)
+       def get_for_location(self, location):
                if not location:
                        return None
 
-               while all_mirrors and len(mirrors) <= 2 and distance <= 270:
+               distance = 2500
+
+               mirrors = []
+               all_mirrors = self.get_all()
+
+               while all_mirrors and len(mirrors) <= 3 and distance <= 8000:
                        for mirror in all_mirrors:
-                               if mirror.distance_to(location) <= distance:
+                               mirror_distance = mirror.distance_to(location)
+                               if mirror_distance is None:
+                                       continue
+
+                               if mirror_distance<= distance:
                                        mirrors.append(mirror)
                                        all_mirrors.remove(mirror)
 
@@ -175,7 +165,7 @@ class Mirrors(object):
        def get_all_files(self):
                files = []
 
-               for mirror in self.list():
+               for mirror in self.get_all():
                        if not mirror.state == "UP":
                                continue
 
@@ -186,8 +176,10 @@ class Mirrors(object):
                return files
 
 
-class MirrorSet(object):
-       def __init__(self, mirrors):
+class MirrorSet(Object):
+       def __init__(self, backend, mirrors):
+               Object.__init__(self, backend)
+
                self._mirrors = mirrors
 
        def __add__(self, other):
@@ -199,7 +191,7 @@ class MirrorSet(object):
 
                        mirrors.append(mirror)
 
-               return MirrorSet(mirrors)
+               return MirrorSet(self.backend, mirrors)
 
        def __sub__(self, other):
                mirrors = self._mirrors[:]
@@ -208,7 +200,7 @@ class MirrorSet(object):
                        if mirror in mirrors:
                                mirrors.remove(mirror)
 
-               return MirrorSet(mirrors)
+               return MirrorSet(self.backend, mirrors)
 
        def __iter__(self):
                return iter(self._mirrors)
@@ -219,10 +211,6 @@ class MirrorSet(object):
        def __str__(self):
                return "<MirrorSet %s>" % ", ".join([m.hostname for m in self._mirrors])
 
-       @property
-       def db(self):
-               return Mirrors().db
-
        def get_with_file(self, filename):
                with_file = [m.mirror for m in self.db.query("SELECT mirror FROM mirror_files WHERE filename=%s", filename)]
 
@@ -231,7 +219,7 @@ class MirrorSet(object):
                        if mirror.id in with_file:
                                mirrors.append(mirror)
 
-               return MirrorSet(mirrors)
+               return MirrorSet(self.backend, mirrors)
 
        def get_random(self):
                mirrors = []
@@ -248,26 +236,28 @@ class MirrorSet(object):
                        if country in mirror.prefer_for_countries:
                                mirrors.append(mirror)
 
-               return MirrorSet(mirrors)
-
-       def get_for_location(self, addr):
-               distance = 10
+               return MirrorSet(self.backend, mirrors)
 
+       def get_for_location(self, location):
+               distance = 2500
                mirrors = []
 
-               location = GeoIP().get_all(addr)
                if location:
-                       while len(mirrors) <= 2 and distance <= 270:
+                       while len(mirrors) <= 3 and distance <= 8000:
                                for mirror in self._mirrors:
                                        if mirror in mirrors:
                                                continue
 
-                                       if mirror.distance_to(location) <= distance:
+                                       mirror_distance = mirror.distance_to(location)
+                                       if mirror_distance is None:
+                                               continue
+
+                                       if mirror_distance <= distance:
                                                mirrors.append(mirror)
 
                                distance *= 1.2
 
-               return MirrorSet(mirrors)
+               return MirrorSet(self.backend, mirrors)
 
        def get_with_state(self, state):
                mirrors = []
@@ -276,11 +266,13 @@ class MirrorSet(object):
                        if mirror.state == state:
                                mirrors.append(mirror)
 
-               return MirrorSet(mirrors)
+               return MirrorSet(self.backend, mirrors)
+
 
+class Mirror(Object):
+       def __init__(self, backend, id, data=None):
+               Object.__init__(self, backend)
 
-class Mirror(object):
-       def __init__(self, id, data=None):
                self.id = id
 
                if data:
@@ -296,11 +288,12 @@ class Mirror(object):
                return "<%s %s>" % (self.__class__.__name__, self.url)
 
        def __cmp__(self, other):
-               return cmp(self.id, other.id)
+               ret = cmp(self.country_code, other.country_code)
 
-       @property
-       def db(self):
-               return Databases().webapp
+               if not ret:
+                       ret = cmp(self.hostname, other.hostname)
+
+               return ret
 
        def generate_url(self):
                url = "http://%s" % self.hostname
@@ -311,30 +304,38 @@ class Mirror(object):
                        url += "/"
                return url
 
-       def __getattr__(self, key):
-               try:
-                       return self._info[key]
-               except KeyError:
-                       raise AttributeError(key)
+       @property
+       def hostname(self):
+               return self._info.hostname
+
+       @property
+       def path(self):
+               return self._info.path
 
        @property
        def address(self):
                return socket.gethostbyname(self.hostname)
 
+       @property
+       def owner(self):
+               return self._info.owner
+
        @property
        def location(self):
                if self.__location is None:
-                       self.__location = GeoIP().get_all(self.address)
+                       self.__location = self.geoip.get_location(self.address)
 
                return self.__location
 
        @property
        def latitude(self):
-               return self.location.latitude
+               if self.location:
+                       return self.location.latitude
 
        @property
        def longitude(self):
-               return self.location.longitude
+               if self.location:
+                       return self.location.longitude
 
        @property
        def coordinates(self):
@@ -351,29 +352,35 @@ class Mirror(object):
 
        @property
        def country_code(self):
-               return self.location.country_code.lower() or "unknown"
+               if self.location:
+                       return self.location.country
 
        @property
        def country_name(self):
                if self.__country_name is None:
-                       self.__country_name = GeoIP().get_country_name(self.country_code)
+                       self.__country_name = self.geoip.get_country_name(self.country_code)
 
                return self.__country_name
 
        @property
-       def city(self):
-               if self._info["city"]:
-                       return self._info["city"]
+       def location_str(self):
+               location = []
+
+               if self._info.location:
+                       location.append(self._info.location)
 
-               return self.location.city
+               elif self.location:
+                       location.append(self.location.city)
+                       location.append(self.country_name)
+
+               return ", ".join([s for s in location if s])
 
        @property
-       def location_str(self):
-               s = self.country_name
-               if self.city:
-                       s = "%s, %s" % (self.city, s)
+       def asn(self):
+               if not hasattr(self, "__asn"):
+                       self.__asn = self.geoip.get_asn(self.address)
 
-               return s
+               return self.__asn
 
        @property
        def filelist(self):
@@ -382,24 +389,43 @@ class Mirror(object):
 
        @property
        def prefix(self):
-               if self.type.startswith("pakfire"):
-                       return self.type
-
                return ""
 
+       @property
+       def url(self):
+               return self._info.url
+
+       def build_url(self, filename):
+               return urlparse.urljoin(self.url, filename)
+
+       @property
+       def last_update(self):
+               return self._info.last_update
+
+       @property
+       def state(self):
+               return self._info.state
+
        def set_state(self, state):
                logging.info("Setting state of %s to %s" % (self.hostname, state))
 
                if self.state == state:
                        return
 
-               self.db.execute("UPDATE mirrors SET state=%s WHERE id=%s",
-                       state, self.id)
+               self.db.execute("UPDATE mirrors SET state = %s WHERE id = %s", state, self.id)
 
                # Reload changed settings
                if hasattr(self, "_info"):
                        self._info["state"] = state
 
+       @property
+       def enabled(self):
+               return self._info.enabled
+
+       @property
+       def disabled(self):
+               return not self.enabled
+
        def check(self):
                logging.info("Running check for mirror %s" % self.hostname)
 
@@ -409,21 +435,28 @@ class Mirror(object):
        def check_state(self):
                logging.debug("Checking state of mirror %s" % self.id)
 
-               if self.disabled == "Y":
+               if not self.enabled:
                        self.set_state("DOWN")
+                       return
+
+               now = datetime.datetime.utcnow()
+
+               time_delta = now - self.last_update
+               time_diff = time_delta.total_seconds()
 
-               time_diff = time.time() - self.last_update
-               if time_diff > 3*24*60*60: # XXX get this into Settings
+               time_down = self.settings.get_int("mirrors_time_down", 3*24*60*60)
+               if time_diff >= time_down:
                        self.set_state("DOWN")
-               elif time_diff > 6*60*60:
-                       self.set_state("OUTOFSYNC")
-               else:
-                       self.set_state("UP")
+                       return
 
-       def check_timestamp(self):
-               if not self.type == "full":
+               time_outofsync = self.settings.get_int("mirrors_time_outofsync", 6*60*60)
+               if time_diff >= time_outofsync:
+                       self.set_state("OUTOFSYNC")
                        return
 
+               self.set_state("UP")
+
+       def check_timestamp(self):
                http = tornado.httpclient.AsyncHTTPClient()
 
                http.fetch(self.url + ".timestamp",
@@ -441,7 +474,9 @@ class Mirror(object):
                except ValueError:
                        timestamp = 0
 
-               self.db.execute("UPDATE mirrors SET last_update=%s WHERE id=%s",
+               timestamp = datetime.datetime.fromtimestamp(timestamp)
+
+               self.db.execute("UPDATE mirrors SET last_update = %s WHERE id = %s",
                        timestamp, self.id)
 
                # Reload changed settings
@@ -454,7 +489,7 @@ class Mirror(object):
 
        def check_filelist(self):
                # XXX need to remove data from disabled mirrors
-               if self.disabled == "Y" or self.type != "full":
+               if not self.enabled:
                        return
 
                http = tornado.httpclient.AsyncHTTPClient()
@@ -496,25 +531,45 @@ class Mirror(object):
 
        @property
        def prefer_for_countries_names(self):
-               return sorted([GeoIP().get_country_name(c) for c in self.prefer_for_countries])
+               countries = [self.geoip.get_country_name(c.upper()) for c in self.prefer_for_countries]
+
+               return sorted(countries)
 
        def distance_to(self, location, ignore_preference=False):
                if not location:
-                       return 0
+                       return None
 
-               if not ignore_preference and location.country_code.lower() in self.prefer_for_countries:
+               country_code = None
+               if location.country:
+                       country_code = location.country.lower()
+
+               if not ignore_preference and country_code in self.prefer_for_countries:
                        return 0
 
-               distance_vector = (
-                       self.latitude - location.latitude,
-                       self.longitude - location.longitude
-               )
+               # http://www.movable-type.co.uk/scripts/latlong.html
+
+               if self.latitude is None:
+                       return None
+
+               if self.longitude is None:
+                       return None
+
+               earth = 6371 # km
+               delta_lat = math.radians(self.latitude - location.latitude)
+               delta_lon = math.radians(self.longitude - location.longitude)
+
+               lat1 = math.radians(self.latitude)
+               lat2 = math.radians(location.latitude)
+
+               a = math.sin(delta_lat / 2) ** 2
+               a += math.cos(lat1) * math.cos(lat2) * (math.sin(delta_lon / 2) ** 2)
 
-               distance = 0
-               for i in distance_vector:
-                       distance += i**2
+               b1 = math.sqrt(a)
+               b2 = math.sqrt(1 - a)
 
-               return math.sqrt(distance)
+               c = 2 * math.atan2(b1, b2)
+
+               return c * earth
 
        def traffic(self, since):
                # XXX needs to be done better
@@ -537,13 +592,35 @@ class Mirror(object):
        def priority(self):
                return self._info.get("priority", 10)
 
-       def is_pakfire2(self):
-               return self.type in ("full", "pakfire2")
-
        @property
        def development(self):
-               return self._info.get("development", "N") == "Y"
+               return self._info.get("development", False)
 
        @property
        def mirrorlist(self):
-               return self._info.get("mirrorlist", "N") == "Y"
+               return self._info.get("mirrorlist", False)
+
+       @property
+       def addresses(self):
+               if not hasattr(self, "__addresses"):
+                       addrinfo = socket.getaddrinfo(self.hostname, 0, socket.AF_UNSPEC, socket.SOCK_STREAM)
+
+                       ret = []
+                       for family, socktype, proto, canonname, address in addrinfo:
+                               if family == socket.AF_INET:
+                                       address, port = address
+                               elif family == socket.AF_INET6:
+                                       address, port, flowid, scopeid = address
+                               ret.append((family, address))
+
+                       self.__addresses = ret
+
+               return self.__addresses
+
+       @property
+       def addresses6(self):
+               return [address for family, address in self.addresses if family == socket.AF_INET6]
+
+       @property
+       def addresses4(self):
+               return [address for family, address in self.addresses if family == socket.AF_INET]
index 22dce10b9b3ff3c25f988c09e2336f625cf2856d..3cc129f7b1896c058154b90683b95c15b45f0570 100644 (file)
@@ -4,6 +4,14 @@ class Object(object):
        def __init__(self, backend):
                self.backend = backend
 
+               self.init()
+
+       def init(self):
+               """
+                       Function for custom initialization.
+               """
+               pass
+
        @property
        def db(self):
                return self.backend.db
@@ -12,22 +20,26 @@ class Object(object):
        def accounts(self):
                return self.backend.accounts
 
+       @property
+       def downloads(self):
+               return self.backend.downloads
+
+       @property
+       def geoip(self):
+               return self.backend.geoip
+
+       @property
+       def iuse(self):
+               return self.backend.iuse
+
+       @property
+       def memcache(self):
+               return self.backend.memcache
+
        @property
        def planet(self):
                return self.backend.planet
 
-
-class Singleton(type):
-       """
-               A class for using the singleton pattern
-       """
-
-       def __init__(cls, name, bases, dict):
-               super(Singleton, cls).__init__(name, bases, dict)
-               cls.instance = None
-       def __call__(cls, *args, **kw):
-               if cls.instance is None:
-                       cls.instance = super(Singleton, cls).__call__(*args, **kw)
-               return cls.instance
+       @property
+       def settings(self):
+               return self.backend.settings
index eabd4dc8370407a60f404d83ceccfa5fbc5c93d1..9d112f228f82c4a7a9c247be49e4f02e673d66a7 100644 (file)
@@ -1,8 +1,6 @@
 #!/usr/bin/python
 
-from databases import Databases
-from misc import Singleton
-
+from misc import Object
 
 class MenuEntry(object):
        def __init__(self, _data):
@@ -36,13 +34,7 @@ class MenuEntry(object):
                        return int(self._data.get("item"))
 
 
-class NetBoot(object):
-       __metaclass__ = Singleton
-
-       @property
-       def db(self):
-               return Databases().webapp
-
+class NetBoot(Object):
        def get_menu(self, level=0):
                menu = []
 
index eddd036c016a1bb609d4ca9c715c931d65552144..51dfd4487fdcef6f8a06fcf3f463b1f844cfd714 100644 (file)
@@ -1,71 +1,56 @@
 #!/usr/bin/python
 
-from databases import Databases
-from misc import Singleton
-
-class News(object):
-       __metaclass__ = Singleton
-
-       @property
-       def db(self):
-               return Databases().webapp
-
-       def list(self):
-               return [i.uuid for i in self.db.query("SELECT DISTINCT uuid FROM news ORDER BY date")]
+from misc import Object
 
+class News(Object):
        def get(self, uuid, lang="en"):
-               return self.db.get("SELECT * FROM news WHERE uuid=%s AND lang=%s",
-                       uuid, lang)
+               return self.db.get("SELECT * FROM news WHERE uuid = %s AND lang = %s \
+                       AND published IS NOT NULL AND published <= NOW()", uuid, lang)
 
        def get_by_slug(self, slug):
-               return self.db.get("SELECT * FROM news WHERE slug=%s", slug)
+               return self.db.get("SELECT * FROM news WHERE slug = %s \
+                       AND published IS NOT NULL AND published <= NOW()", slug)
 
        def get_latest(self, author=None, locale=None, limit=1, offset=0):
-               query = "SELECT * FROM news WHERE published='Y'"
+               query = "SELECT * FROM news WHERE published IS NOT NULL AND published <= NOW()"
+               args = []
 
                if author:
-                       query += " AND author_id='%s'" % author
+                       query += " AND author_id = %s"
+                       args.append(author)
 
                if locale:
-                       query += " AND lang='%s'" % locale.code[:2]
+                       query += " AND lang = %s"
+                       args.append(locale.code[:2])
 
-               query += " ORDER BY date DESC"
+               query += " ORDER BY published DESC"
 
                if limit:
-                       if offset:
-                               query += " LIMIT %d,%d" % (offset, limit)
-                       else:
-                               query += " LIMIT %d" % limit
+                       query += " LIMIT %s"
+                       args.append(limit)
 
-               news = self.db.query(query)
+                       if offset:
+                               query += " OFFSET %s"
+                               args.append(offset)
 
-               return news
+               return self.db.query(query, *args)
 
        def get_by_year(self, year, locale=None):
-               query = "SELECT * FROM news WHERE published = 'Y' AND YEAR(date) = %s"
+               query = "SELECT * FROM news WHERE published IS NOT NULL AND published <= NOW() \
+                       AND EXTRACT(YEAR FROM published) = %s"
                args  = [year,]
 
                if locale:
                        query += " AND lang = %s"
                        args.append(locale.code[:2])
 
-               query += " ORDER BY date DESC"
+               query += " ORDER BY published DESC"
 
                return self.db.query(query, *args)
 
        @property
        def years(self):
-               years = []
-
-               for row in self.db.query("SELECT DISTINCT YEAR(date) AS year FROM news \
-                               WHERE published = 'Y' ORDER BY year DESC"):
-                       years.append(row.year)
-
-               return years
-
-
-if __name__ == "__main__":
-       n = News()
+               query = self.db.query("SELECT DISTINCT EXTRACT(YEAR FROM published)::integer AS year FROM news \
+                       WHERE published IS NOT NULL AND published <= NOW() ORDER BY year DESC")
 
-       print n.list()
-       print n.get_latest()
+               return [r.year for r in query]
index c34898f0fe097191217cafd4b3ff09a2ab5c03a7..0c7565684e6f77a2330f18e95ecac8b89a162c57 100644 (file)
@@ -3,12 +3,8 @@
 import datetime
 import re
 import textile
-import tornado.database
 import unicodedata
 
-from accounts import Accounts
-from databases import Databases
-
 from misc import Object
 
 class PlanetEntry(Object):
@@ -147,8 +143,11 @@ class PlanetEntry(Object):
 
 class Planet(Object):
        def get_authors(self):
+               query = self.db.query("SELECT DISTINCT author_id FROM planet WHERE status = %s \
+                       AND published IS NOT NULL AND published <= NOW()", "published")
+
                authors = []
-               for author in self.db.query("SELECT DISTINCT author_id FROM planet WHERE status = %s", "published"):
+               for author in query:
                        author = self.accounts.search(author.author_id)
                        if author:
                                authors.append(author)
@@ -156,32 +155,23 @@ class Planet(Object):
                return sorted(authors)
 
        def get_years(self):
-               res = self.db.query("SELECT DISTINCT YEAR(published) AS year \
+               res = self.db.query("SELECT DISTINCT EXTRACT(YEAR FROM published)::integer AS year \
                        FROM planet WHERE status = %s ORDER BY year DESC", "published")
 
                return [row.year for row in res]
 
        def get_entry_by_slug(self, slug):
                entry = self.db.get("SELECT * FROM planet WHERE slug = %s", slug)
+
                if entry:
                        return PlanetEntry(self.backend, entry)
 
        def get_entry_by_id(self, id):
                entry = self.db.get("SELECT * FROM planet WHERE id = %s", id)
+
                if entry:
                        return PlanetEntry(self.backend, entry)
 
-       def _limit_and_offset_query(self, limit=None, offset=None):
-               query = " "
-
-               if limit:
-                       if offset:
-                               query += "LIMIT %d,%d" % (offset, limit)
-                       else:
-                               query += "LIMIT %d" % limit
-
-               return query
-
        def get_entries(self, limit=3, offset=None, status="published", author_id=None):
                query = "SELECT * FROM planet"
                args, clauses = [], []
@@ -201,12 +191,12 @@ class Planet(Object):
 
                # Respect limit and offset
                if limit:
+                       query += " LIMIT %s"
+                       args.append(limit)
+
                        if offset:
-                               query += " LIMIT %s,%s"
-                               args += [offset, limit,]
-                       else:
-                               query += " LIMIT %s"
-                               args.append(limit)
+                               query += " OFFSET %s"
+                               args.append(offset)
 
                entries = []
                for entry in self.db.query(query, *args):
@@ -220,8 +210,8 @@ class Planet(Object):
 
        def get_entries_by_year(self, year):
                entries = self.db.query("SELECT * FROM planet \
-                       WHERE status = %s AND YEAR(published) = %s ORDER BY published DESC",
-                       "published", year)
+                       WHERE status = %s AND EXTRACT(YEAR FROM published) = %s \
+                       ORDER BY published DESC", "published", year)
 
                return [PlanetEntry(self.backend, e) for e in entries]
 
@@ -272,9 +262,8 @@ class Planet(Object):
        def save_entry(self, entry):
                slug = self._generate_slug(entry.title)
 
-               id = self.db.execute("INSERT INTO planet(author_id, title, slug, markdown, published) "
-                       "VALUES(%s, %s, %s, %s, UTC_TIMESTAMP())", entry.author.uid, entry.title,
-                       slug, entry.markdown)
+               id = self.db.execute("INSERT INTO planet(author_id, title, slug, markdown, published) \
+                       VALUES(%s, %s, %s, %s, NOW())", entry.author.uid, entry.title, slug, entry.markdown)
 
                return id
 
index 6c907f40afef4e19fb7387ef72d292599107d0ab..64bed28818d3f19f84d761df35eed85789ad95e9 100644 (file)
@@ -7,27 +7,22 @@ import re
 import urllib
 import urlparse
 
+import database
 import tracker
+from misc import Object
 
-from databases import Databases
-from misc import Singleton
-from settings import Settings
+class File(Object):
+       def __init__(self, backend, release, id, data=None):
+               Object.__init__(self, backend)
 
-class File(object):
-       def __init__(self, release, id):
                self.id = id
                self._release = release
 
                # get all data from database
-               self.__data = None
+               self.__data = data
 
-       @property
-       def db(self):
-               return Databases().webapp
-
-       @property
-       def tracker(self):
-               return self.release.tracker
+       def __cmp__(self, other):
+               return cmp(self.prio, other.prio)
 
        @property
        def data(self):
@@ -84,7 +79,7 @@ class File(object):
 
        @property
        def url(self):
-               baseurl = Settings().get("download_url")
+               baseurl = self.settings.get("download_url", "http://downloads.ipfire.org")
 
                return urlparse.urljoin(baseurl, self.filename)
 
@@ -209,29 +204,14 @@ class File(object):
 
                return self.tracker.get_peers(self.torrent_hash)
 
-       @property
-       def completed(self):
-               if not self.torrent_hash:
-                       return
-
-               return self.tracker.complete(self.torrent_hash)
-
 
-class Release(object):
-       @property
-       def db(self):
-               return Releases().db
-
-       @property
-       def tracker(self):
-               return tracker.Tracker()
-
-       def __init__(self, id):
+class Release(Object):
+       def __init__(self, backend, id, data=None):
+               Object.__init__(self, backend)
                self.id = id
 
                # get all data from database
-               self.__data = \
-                       self.db.get("SELECT * FROM releases WHERE id = %s", self.id)
+               self.__data = data or self.db.get("SELECT * FROM releases WHERE id = %s", self.id)
                assert self.__data
 
                self.__files = []
@@ -239,14 +219,17 @@ class Release(object):
        def __repr__(self):
                return "<%s %s>" % (self.__class__.__name__, self.name)
 
+       def __cmp__(self, other):
+               return cmp(self.id, other.id)
+
        @property
        def files(self):
                if not self.__files:
-                       files = self.db.query("SELECT id, filename FROM files WHERE releases = %s \
-                                       AND loadable = 'Y' AND NOT filename LIKE '%%.torrent'", self.id)
+                       files = self.db.query("SELECT * FROM files WHERE releases = %s \
+                               AND NOT filename LIKE '%%.torrent'", self.id)
 
-                       self.__files = [File(self, f.id) for f in files]
-                       self.__files.sort(lambda a, b: cmp(a.prio, b.prio))
+                       self.__files = [File(self.backend, self, f.id, f) for f in files]
+                       self.__files.sort()
 
                return self.__files
 
@@ -264,23 +247,25 @@ class Release(object):
 
        @property
        def name(self):
-               return self.__data.get("name")
+               return self.__data.name
+
+       @property
+       def sname(self):
+               return self.__data.sname
 
        @property
        def stable(self):
-               return self.__data.get("stable") == "Y"
+               return self.__data.stable
 
        @property
        def published(self):
-               return self.__data.get("published") == "Y"
+               return self.__data.published
 
-       @property
-       def date(self):
-               return self.__data.get("date")
+       date = published
 
        @property
        def path(self):
-               return self.__data.get("path")
+               return self.__data.path
 
        def get_file(self, type):
                for file in self.files:
@@ -329,6 +314,7 @@ class Release(object):
                        if filename.endswith(".md5"):
                                continue
 
+                       logging.info("Hashing %s..." % filename)
                        filehash = self.__file_hash(filename)
                        filesize = os.path.getsize(filename)
 
@@ -373,54 +359,77 @@ class Release(object):
                        if f:
                                f.close()
 
+       def is_netboot_capable(self):
+               return self.path and "ipfire-2.x" in self.path
+
+       @property
+       def netboot_kernel(self):
+               return "http://downloads.ipfire.org/%s/images/vmlinuz" % self.path
 
-class Releases(object):
-       __metaclass__ = Singleton
+       @property
+       def netboot_initrd(self):
+               return "http://downloads.ipfire.org/%s/images/instroot" % self.path
 
        @property
-       def db(self):
-               return Databases().webapp
+       def netboot_append(self):
+               return "ro"
 
-       def list(self):
-               return [Release(r.id) for r in self.db.query("SELECT id FROM releases ORDER BY date DESC")]
 
+class Releases(Object):
        def get_by_id(self, id):
-               id = int(id)
-               if id in [r.id for r in self.db.query("SELECT id FROM releases")]:
-                       return Release(id)
-
-       def get_latest(self, stable=1):
-               query = "SELECT id FROM releases WHERE published='Y' AND"
-               if stable:
-                       query += " stable='Y'"
-               else:
-                       query += " stable='N'"
+               ret = self.db.get("SELECT * FROM releases WHERE id = %s", id)
+
+               if ret:
+                       return Release(self.backend, ret.id, data=ret)
+
+       def get_by_sname(self, sname):
+               ret = self.db.get("SELECT * FROM releases WHERE sname = %s", sname)
+
+               if ret:
+                       return Release(self.backend, ret.id, data=ret)
 
-               query += " ORDER BY date DESC LIMIT 1"
+       def get_latest(self, stable=True):
+               ret = self.db.get("SELECT * FROM releases WHERE published IS NOT NULL AND published <= NOW() \
+                       AND stable = %s ORDER BY published DESC LIMIT 1", stable)
 
-               release = self.db.get(query)
-               if release:
-                       return Release(release.id)
+               if ret:
+                       return Release(self.backend, ret.id, data=ret)
 
        def get_stable(self):
-               releases = self.db.query("""SELECT id FROM releases
-                       WHERE published='Y' AND stable='Y'
-                       ORDER BY date DESC""")
+               query = self.db.query("SELECT * FROM releases \
+                       WHERE published IS NOT NULL AND published <= NOW() AND stable = TRUE \
+                       ORDER BY published DESC")
 
-               return [Release(r.id) for r in releases]
+               releases = []
+               for row in query:
+                       release = Release(self.backend, row.id, data=row)
+                       releases.append(release)
+
+               return releases
 
        def get_unstable(self):
-               releases = self.db.query("""SELECT id FROM releases
-                       WHERE published='Y' AND stable='N'
-                       ORDER BY date DESC""")
+               query = self.db.query("SELECT * FROM releases \
+                       WHERE published IS NOT NULL AND published <= NOW() AND stable = FALSE \
+                       ORDER BY published DESC")
+
+               releases = []
+               for row in query:
+                       release = Release(self.backend, row.id, data=row)
+                       releases.append(release)
 
-               return [Release(r.id) for r in releases]
+               return releases
 
        def get_all(self):
-               releases = self.db.query("""SELECT id FROM releases
-                       WHERE published='Y' ORDER BY date DESC""")
+               query = self.db.query("SELECT * FROM releases \
+                       WHERE published IS NOT NULL AND published <= NOW() \
+                       ORDER BY published DESC")
 
-               return [Release(r.id) for r in releases]
+               releases = []
+               for row in query:
+                       release = Release(self.backend, row.id, data=row)
+                       releases.append(release)
+
+               return releases
 
        def get_file_for_torrent_hash(self, torrent_hash):
                file = self.db.get("SELECT id, releases FROM files WHERE torrent_hash = %s LIMIT 1",
@@ -433,12 +442,3 @@ class Releases(object):
                file = File(release, file.id)
 
                return file
-
-
-if __name__ == "__main__":
-       r = Releases()
-
-       for release in r.get_all():
-               print release.name
-
-       print r.get_latest()
index 076ea65c7db335a57fc8fdf55f75572e1ea52277..6355485f0d76bbc36ad3b46ce973a9035d9028d4 100644 (file)
@@ -1,58 +1,58 @@
 #!/usr/bin/python
 
-from databases import Databases
-from misc import Singleton
+from misc import Object
 
-class Settings(object):
-       __metaclass__ = Singleton
+class Settings(Object):
+       def init(self):
+               """
+                       Initialize the settings dictionary by fetching the
+                       entire table from the database.
+               """
+               self.__settings = {}
 
-       @property
-       def db(self):
-               return Databases().webapp
+               query = self.db.query("SELECT k, v FROM settings")
+
+               for row in query:
+                       self.__settings[row.k] = row.v
 
        def query(self, key):
                return self.db.get("SELECT * FROM settings WHERE k=%s", key)
 
-       def get(self, key):
-               return "%s" % self.query(key)["v"]
-
-       def get_id(self, key):
-               return self.query(key)["id"]
+       def get(self, key, default=None):
+               return self.__settings.get(key, default)
 
-       def get_int(self, key):
+       def get_int(self, key, default=None):
                value = self.get(key)
 
                if value is None:
-                       return None
+                       return default
 
-               return int(value)
+               try:
+                       return int(value)
+               except (TypeError, ValueError):
+                       return default
 
        def get_float(self, key):
                value = self.get(key)
 
                if value is None:
-                       return None
+                       return default
 
-               return float(value)
+               try:
+                       return float(value)
+               except (TypeError, ValueError):
+                       return default
 
        def set(self, key, value):
-               id = self.get(key)
+               oldvalue = self.get(key)
 
-               if not id:
-                       self.db.execute("INSERT INTO settings(k, v) VALUES(%s, %s)", key, value)
+               if value == oldvalue:
+                       return
+
+               if oldvalue:
+                       self.db.execute("UPDATE settings SET v = %s WHERE k = %s", value, key)
                else:
-                       self.db.execute("UPDATE settings SET v=%s WHERE id=%s" % (value, id))
+                       self.db.execute("INSERT INTO settings(k, v) VALUES(%s, %s)", key, value)
 
        def get_all(self):
-               attrs = {}
-
-               for s in self.db.query("SELECT * FROM settings"):
-                       attrs[s.k] = s.v
-
-               return attrs
-
-
-if __name__ == "__main__":
-       s = Settings()
-
-       print s.get_all()
+               return self.__settings.copy()
index 00c595b827fed3428679ee83cd09640aa604f22b..87fe2fc72cd0b53c5b0fb70150f56cee522468dd 100644 (file)
@@ -8,7 +8,7 @@ import logging
 import pymongo
 import re
 
-from misc import Singleton
+from misc import Object
 
 DATABASE_HOST = ["wilhelmina.ipfire.org", "miranda.ipfire.org", "falco.ipfire.org",]
 DATABASE_NAME = "stasy"
@@ -27,9 +27,11 @@ CPU_VENDORS = {
        "Geode by NSC" : "NSC",
        "NexGenDriven" : "NexGen",
        "RiseRiseRise" : "Rise",
+       "SiS SiS SiS"  : "SiS",
        "SiS SiS SiS " : "SiS",
        "UMC UMC UMC " : "UMC",
        "VIA VIA VIA " : "VIA",
+       "Vortex86 SoC" : "Vortex86",
 }
 
 CPU_STRINGS = (
@@ -397,10 +399,8 @@ class Profile(ProfileDict):
                        return ProfileNetwork(network)
 
 
-class Stasy(object):
-       __metaclass__ = Singleton
-
-       def __init__(self):
+class Stasy(Object):
+       def init(self):
                # Initialize database connection
                self._conn = pymongo.Connection(DATABASE_HOST)
                self._db = self._conn[DATABASE_NAME]
@@ -809,8 +809,3 @@ class Stasy(object):
                                { "profile.system.release" : release }).count()
 
                return updates
-
-
-if __name__ == "__main__":
-       s = Stasy()
-
index 4880bee4398833462c34e8b88abfe87e66c413f2..24a763b22937a98de76a70584afbc3f2ea47bd48 100644 (file)
@@ -1,13 +1,10 @@
 #!/usr/bin/python
 
-import os
-import random
-import time
+from __future__ import division
 
-import releases
+import random
 
-from databases import Databases, Row
-from misc import Singleton
+from misc import Object
 
 def decode_hex(s):
        ret = []
@@ -20,150 +17,183 @@ def decode_hex(s):
 
        return "".join(ret)
 
-class Tracker(object):
-       id = "TheIPFireTorrentTracker"
-
-       # Intervals # XXX needs to be in Settings
-       _interval = 60*60
-       _min_interval = 30*60
+class Tracker(Object):
+       @property
+       def tracker_id(self):
+               return self.settings.get("tracker_id", "TheIPFireTorrentTracker")
 
-       random_interval = -60, 60
+       def _fuzzy_interval(self, interval, fuzz=60):
+               return interval + random.randint(-fuzz, fuzz)
 
-       numwant = 50
+       @property
+       def interval(self):
+               return self.settings.get_int("tracker_interval", 3600)
 
        @property
-       def db(self):
-               return Databases().webapp
+       def min_interval(self):
+               interval = self.settings.get_int("tracker_min_interval", self.interval // 2)
 
-       def _fetch(self, hash, limit=None, random=False, completed=False, no_peer_id=False):
-               query = "SELECT * FROM tracker_peers WHERE last_update >= %d" % self.since
+               return self._fuzzy_interval(interval)
+
+       @property
+       def numwant(self):
+               return self.settings.get_int("tracker_numwant", 50)
 
-               if hash:
-                       query += " AND hash = '%s'" % hash
+       def get_peers(self, info_hash, limit=None, random=True, no_peer_id=False, ipfamily=None):
+               query = "SELECT * FROM tracker WHERE last_update >= NOW() - INTERVAL '%ss'"
+               args = [self.interval,]
 
-               if completed:
-                       query += " AND left_data = 0"
-               else:
-                       query += " AND left_data != 0"
+               if info_hash:
+                       query += " AND hash = %s"
+                       args.append(info_hash)
 
                if random:
-                       query += " ORDER BY RAND()"
+                       query += " ORDER BY RANDOM()"
 
                if limit:
-                       query += " LIMIT %s" % limit
+                       query += " LIMIT %s"
+                       args.append(limit)
 
                peers = []
-               for peer in self.db.query(query):
-                       if not peer.ip or not peer.port:
-                               continue
-
-                       peer_dict = {
-                               "ip" : str(peer.ip),
-                               "port" : int(peer.port),
-                       }
+               for row in self.db.query(query, *args):
+                       peer6 = None
+                       peer4 = None
+
+                       if row.address6 and row.port6:
+                               peer6 = {
+                                       "ip" : row.address6,
+                                       "port" : row.port6,
+                               }
+
+                       if row.address4 and row.port4:
+                               peer4 = {
+                                       "ip" : row.address4,
+                                       "port" : row.port4,
+                               }
 
                        if not no_peer_id:
-                               peer_dict["peer id"] = str(peer.id),
+                               if peer6:
+                                       peer6["peer id"] = row.id
 
-                       peers.append(peer_dict)
+                               if peer4:
+                                       peer6["peer id"] = row.id
 
-               return peers
+                       if peer6:
+                               peers.append(peer6)
 
-       def get_peers(self, hash, **kwargs):
-               return self._fetch(hash, **kwargs)
+                       if peer4:
+                               peers.append(peer4)
 
-       def get_seeds(self, hash, **kwargs):
-               kwargs.update({"completed" : True})
-               return self._fetch(hash, **kwargs)
+               return peers
 
-       def complete(self, hash):
-               return len(self.get_seeds(hash))
+       def cleanup_peers(self):
+               """
+                       Remove all peers that have timed out.
+               """
+               self.db.execute("DELETE FROM tracker \
+                       WHERE last_update < NOW() - INTERVAL '%ss'", self.interval)
 
-       def incomplete(self, hash):
-               return len(self.get_peers(hash))
+       def update_peer(self, peer_id, info_hash, address6=None, port6=None,
+                       address4=None, port4=None, downloaded=None, uploaded=None, left_data=None):
+               if address4 and address4.startswith("172.28.1."):
+                       address = "178.63.73.246"
 
-       def event_started(self, hash, peer_id):
-               # Damn, mysql does not support INSERT IF NOT EXISTS...
-               if not self.db.query("SELECT id FROM tracker_peers WHERE hash = '%s' AND peer_id = '%s'" % (hash, peer_id)):
-                       self.db.execute("INSERT INTO tracker_peers(hash, peer_id) VALUES('%s', '%s')" % (hash, peer_id))
+               query = "UPDATE tracker SET last_update = NOW()"
+               args = []
 
-               if not hash in self.hashes:
-                       self.db.execute("INSERT INTO tracker_hashes(hash) VALUES('%s')" % hash)
+               if address6:
+                       query += ", address6 = %s"
+                       args.append(address6)
 
-       def event_stopped(self, hash, peer_id):
-               self.db.execute("DELETE FROM tracker_peers WHERE hash = '%s' AND peer_id = '%s'" % (hash, peer_id))
+               if port6:
+                       query += ", port6 = %s"
+                       args.append(port6)
 
-       def event_completed(self, hash, peer_id):
-               self.db.execute("UPDATE tracker_hashes SET completed=completed+1 WHERE hash = '%s'" % hash)
+               if address4:
+                       query += ", address4 = %s"
+                       args.append(address4)
 
-       def scrape(self, hashes=[]):
-               ret = {}
-               for hash in self.db.query("SELECT hash, completed FROM tracker_hashes"):
-                       hash, completed = hash.hash, hash.completed
+               if port4:
+                       query += ", port4 = %s"
+                       args.append(port4)
 
-                       if hashes and hash not in hashes:
-                               continue
+               if downloaded:
+                       query += ", downloaded = %s"
+                       args.append(downloaded)
 
-                       ret[hash] = {
-                               "complete" : self.complete(hash),
-                               "downloaded" : completed or 0,
-                               "incomplete" : self.incomplete(hash),
-                       }
+               if uploaded:
+                       query += ", uploaded = %s"
+                       args.append(uploaded)
 
-               return ret
+               if left_data:
+                       query += ", left_data = %s"
+                       args.append(left_data)
 
-       def update(self, hash, id, ip=None, port=None, downloaded=None, uploaded=None, left=None):
-               args = [ "last_update = '%s'" % self.now ]
+               query += " WHERE id = %s AND hash = %s"
+               args += [peer_id, info_hash]
 
-               if ip:
-                       if ip.startswith("172.28.1."):
-                               ip = "178.63.73.246"
+               self.db.execute(query, *args)
 
-                       args.append("ip='%s'" % ip)
+       def complete(self, info_hash):
+               ret = self.db.get("SELECT COUNT(*) AS c FROM tracker \
+                       WHERE hash = %s AND left_data = 0", info_hash)
 
-               if port:
-                       args.append("port='%s'" % port)
+               if ret:
+                       return ret.c
 
-               if downloaded:
-                       args.append("downloaded='%s'" % downloaded)
+       def incomplete(self, info_hash):
+               ret = self.db.get("SELECT COUNT(*) AS c FROM tracker \
+                       WHERE hash = %s AND left_data > 0", info_hash)
 
-               if uploaded:
-                       args.append("uploaded='%s'" % uploaded)
+               if ret:
+                       return ret.c
 
-               if left:
-                       args.append("left_data='%s'" % left)
+       def handle_event(self, event, peer_id, info_hash, **kwargs):
+               # started
+               if event == "started":
+                       self.insert_peer(peer_id, info_hash, **kwargs)
 
-               if not args:
-                       return
+               # stopped
+               elif event == "stopped":
+                       self.remove_peer(peer_id, info_hash)
 
-               query = "UPDATE tracker_peers SET " + ", ".join(args) + \
-                       " WHERE hash = '%s' AND peer_id = '%s'" % (hash, id)
+       def peer_exists(self, peer_id, info_hash):
+               ret = self.db.get("SELECT COUNT(*) AS c FROM tracker \
+                       WHERE id = %s AND hash = %s", peer_id, info_hash)
 
-               self.db.execute(query)
+               if ret and ret.c > 0:
+                       return True
 
-       @property
-       def hashes(self):
-               hashes = []
-               for h in self.db.query("SELECT hash FROM tracker_hashes"):
-                       hashes.append(h["hash"].lower())
+               return False
 
-               return hashes
+       def insert_peer(self, peer_id, info_hash, address6=None, port6=None, address4=None, port4=None):
+               exists = self.peer_exists(peer_id, info_hash)
+               if exists:
+                       return
 
-       @property
-       def now(self):
-               return int(time.time())
+               self.db.execute("INSERT INTO tracker(id, hash, address6, port6, address4, port4) \
+                       VALUES(%s, %s, %s, %s, %s, %s)", peer_id, info_hash, address6, port6, address4, port4)
 
-       @property
-       def since(self):
-               return int(time.time() - self.interval)
+       def remove_peer(self, peer_id, info_hash):
+               self.db.execute("DELETE FROM tracker \
+                       WHERE id = %s AND hash = %s", peer_id, info_hash)
 
-       @property
-       def interval(self):
-               return self._interval + random.randint(*self.random_interval)
+       def scrape(self, info_hashes):
+               ret = {
+                       "files" : {},
+                       "flags" : {
+                               "min_request_interval" : self.interval,
+                       }
+               }
 
-       @property
-       def min_interval(self):
-               return self._min_interval + random.randint(*self.random_interval)
+               for info_hash in info_hashes:
+                       ret["files"][info_hash] = {
+                               "complete"   : self.complete(info_hash),
+                               "incomplete" : self.incomplete(info_hash),
+                               "downloaded" : 0,
+                       }
+
+               return ret
 
 
 ##### This is borrowed from the bittorrent client libary #####
index e94634adab3abf1281b758ac4a8216393bc5bf34..4cdb55ead754e6f3bf47386403ef45755bba96b1 100644 (file)
@@ -5,16 +5,9 @@ from __future__ import division
 import datetime
 import textile
 
-from databases import Databases
-from misc import Singleton
-
-class Wishlist(object):
-       __metaclass__ = Singleton
-
-       @property
-       def db(self):
-               return Databases().webapp
+from misc import Object
 
+class Wishlist(Object):
        def get(self, slug):
                wish = self.db.get("SELECT * FROM wishlist WHERE slug = %s", slug)
 
@@ -32,33 +25,59 @@ class Wishlist(object):
 
        def get_all_running(self):
                return self.get_all_by_query("SELECT * FROM wishlist \
-                       WHERE DATE(NOW()) >= date_start AND DATE(NOW()) <= date_end AND status = 'running' \
+                       WHERE (CASE \
+                               WHEN date_end IS NULL THEN \
+                                       NOW() >= date_start AND goal >= donated \
+                               ELSE \
+                                       NOW() BETWEEN date_start AND date_end \
+                               END) AND status = 'running' \
                        ORDER BY prio ASC, date_end ASC")
 
        def get_all_finished(self, limit=5, offset=None):
-               query = "SELECT * FROM wishlist WHERE DATE(NOW()) > date_end AND status IS NOT NULL \
+               query = "SELECT * FROM wishlist \
+                       WHERE (CASE \
+                               WHEN date_end IS NULL THEN \
+                                       donated >= goal \
+                               ELSE \
+                                       NOW() > date_end \
+                               END) AND status IS NOT NULL \
                        ORDER BY date_end DESC"
                args = []
 
                if limit:
+                       query += " LIMIT %s"
+                       args.append(limit)
+
                        if offset:
-                               query += " LIMIT %s,%s"
-                               args += [limit, offset]
-                       else:
-                               query += " LIMIT %s"
-                               args.append(limit)
+                               query += " OFFSET %s"
+                               args.append(offset)
 
                return self.get_all_by_query(query, *args)
 
        def get_hot_wishes(self, limit=3):
                query = "SELECT * FROM wishlist \
-                       WHERE status = %s AND DATE(NOW()) BETWEEN date_start AND date_end AND ( \
-                               ((UNIX_TIMESTAMP() - UNIX_TIMESTAMP(date_start)) <= 864000) \
-                               OR ((UNIX_TIMESTAMP(date_end) - UNIX_TIMESTAMP()) <= 1209600) \
-                               OR ((donated / goal) >= 0.9) \
-                               OR (goal >= 3000) \
-                               OR (prio <= 5) \
-                       ) ORDER BY prio ASC, date_end ASC LIMIT %s"
+                       WHERE \
+                               status = %s \
+                       AND \
+                               date_start <= NOW() \
+                       AND \
+                               (CASE WHEN date_end IS NOT NULL THEN \
+                                       NOW() BETWEEN date_start AND date_end \
+                               ELSE \
+                                       TRUE \
+                               END) \
+                       AND \
+                               (AGE(NOW(), date_start) <= INTERVAL '10 days' \
+                               OR \
+                                       AGE(date_end, NOW()) <= INTERVAL '14 days' \
+                               OR \
+                                       (donated / goal) >= 0.85 \
+                               OR \
+                                       goal >= 3000 \
+                               OR \
+                                       prio <= 5 \
+                               ) \
+                       ORDER BY prio ASC, date_end ASC LIMIT %s"
 
                return self.get_all_by_query(query, "running", limit)
 
@@ -149,8 +168,13 @@ class Wish(object):
 
        @property
        def running(self):
-               if self.remaining_days < 0:
-                       return False
+               if self.date_end:
+                       if self.remaining_days and self.remaining_days < 0:
+                               return False
+
+               else:
+                       if self.donated >= self.goal:
+                               return False
 
                return True
 
@@ -164,19 +188,14 @@ class Wish(object):
 
        @property
        def running_days(self):
-               today = datetime.datetime.today()
-               today = today.date()
-
-               running = today - self.date_start
+               running = datetime.datetime.today() - self.date_start
                return running.days
 
        @property
        def remaining_days(self):
-               today = datetime.datetime.today()
-               today = today.date()
-
-               remaining = self.date_end - today
-               return remaining.days
+               if self.date_end:
+                       remaining = self.date_end - datetime.datetime.today()
+                       return remaining.days
 
        def is_new(self):
                return self.running_days < 10
index 33804ac3ee708a9bb209c23e06e3baf3bcb7d330..20c21aecab399d207de5895a9554ae66fe858e9a 100644 (file)
@@ -97,3 +97,22 @@ class StaticHandler(BaseHandler):
                        raise tornado.web.HTTPError(404)
 
                self.render("static/%s" % name, lang=self.locale.code[:2])
+
+
+class GeoIPHandler(BaseHandler):
+       def get_address(self):
+               addr = self.get_argument("addr", None)
+
+               if not addr:
+                       addr = self.get_remote_ip()
+
+               return addr
+
+       def get(self):
+               addr = self.get_address()
+
+               peer = self.geoip.get_all(addr)
+               if peer:
+                       peer["country_name"] = self.geoip.get_country_name(peer.country)
+
+               self.render("geoip/index.html", addr=addr, peer=peer)
index c8a68dd57d9cde640747e519d274059119a7aacf..e7c72177ef0e2d9a03ff2a158b54205531ed7418 100644 (file)
@@ -8,10 +8,6 @@ from handlers_base import *
 import backend
 
 class AdminBaseHandler(BaseHandler):
-       @property
-       def downloads(self):
-               return backend.Downloads()
-
        def get_current_user(self):
                return self.get_secure_cookie("account")
 
@@ -156,15 +152,13 @@ class AdminAccountsDeleteHandler(AdminAccountsBaseHandler):
 
 
 class AdminMirrorsBaseHandler(AdminBaseHandler):
-       @property
-       def mirrors(self):
-               return backend.Mirrors()
+       pass
 
 
 class AdminMirrorsHandler(AdminMirrorsBaseHandler):
        @tornado.web.authenticated
        def get(self):
-               mirrors = self.mirrors.list()
+               mirrors = self.mirrors.get_all()
 
                self.render("admin-mirrors.html", mirrors=mirrors)
 
@@ -180,7 +174,7 @@ class AdminMirrorsCreateHandler(AdminMirrorsBaseHandler):
        @tornado.web.authenticated
        def get(self, id=None):
                if id:
-                       mirror = self.db.get("SELECT * FROM mirrors WHERE id = '%s'", int(id))
+                       mirror = self.db.get("SELECT * FROM mirrors WHERE id = %s", id)
                else:
                        mirror = tornado.database.Row(
                                id="",
@@ -259,7 +253,7 @@ class AdminNewsBaseHandler(AdminBaseHandler):
 class AdminNewsHandler(AdminNewsBaseHandler):
        @tornado.web.authenticated
        def get(self):
-               news = self.news.list()
+               news = self.news.get_all()
 
                self.render("admin-news.html", news=news)
 
index e44d203aa322ae7d6e522c671b9fdd2e67d8bbb3..29d212bc36952c38372afd9c1cff5f90b498969d 100644 (file)
@@ -8,8 +8,6 @@ import time
 import tornado.locale
 import tornado.web
 
-import backend
-
 def format_size(b):
        units = ["B", "k", "M", "G"]
        unit_pointer = 0
@@ -106,53 +104,82 @@ class BaseHandler(tornado.web.RequestHandler):
 
                return ret
 
+       def get_remote_ip(self):
+               # Fix for clients behind a proxy that sends "X-Forwarded-For".
+               ip_addr = self.request.remote_ip.split(", ")
+
+               if ip_addr:
+                       ip_addr = ip_addr[-1]
+
+               return ip_addr
+
+       def get_remote_location(self):
+               if not hasattr(self, "__remote_location"):
+                       remote_ip = self.get_remote_ip()
+
+                       self.__remote_location = self.geoip.get_location(remote_ip)
+
+               return self.__remote_location
+
        @property
        def backend(self):
                return self.application.backend
 
+       @property
+       def db(self):
+               return self.backend.db
+
        @property
        def advertisements(self):
-               return backend.Advertisements()
+               return self.backend.advertisements
 
        @property
        def accounts(self):
                return self.backend.accounts
 
        @property
-       def banners(self):
-               return backend.Banners()
+       def downloads(self):
+               return self.backend.downloads
+
+       @property
+       def iuse(self):
+               return self.backend.iuse
 
        @property
        def memcached(self):
-               return backend.Memcached()
+               return self.backend.memcache
 
        @property
        def mirrors(self):
-               return backend.Mirrors()
+               return self.backend.mirrors
+
+       @property
+       def netboot(self):
+               return self.backend.netboot
 
        @property
        def news(self):
-               return backend.News()
+               return self.backend.news
 
        @property
        def config(self):
-               return backend.Config()
+               return self.backend.settings
 
        @property
        def releases(self):
-               return backend.Releases()
+               return self.backend.releases
 
        @property
-       def banners(self):
-               return backend.Banners()
+       def geoip(self):
+               return self.backend.geoip
 
        @property
-       def geoip(self):
-               return backend.GeoIP()
+       def stasy(self):
+               return self.backend.stasy
 
        @property
        def tracker(self):
-               return backend.Tracker()
+               return self.backend.tracker
 
        @property
        def planet(self):
@@ -160,4 +187,4 @@ class BaseHandler(tornado.web.RequestHandler):
 
        @property
        def wishlist(self):
-               return backend.Wishlist()
+               return self.backend.wishlist
index c1c4aed1491fe943d3cedf8cea6e14f562bc319d..becaa4a40c358e0b7f852a25c5eda7955b693d20 100644 (file)
@@ -10,6 +10,8 @@ import tornado.web
 
 import backend
 
+from handlers_base import BaseHandler
+
 BASEDIR = os.path.dirname(__file__)
 
 def word_wrap(s, width=45):
@@ -25,10 +27,8 @@ def word_wrap(s, width=45):
                lines.append(paragraph.lstrip())
        return '\n'.join(lines)
 
-class BootBaseHandler(tornado.web.RequestHandler):
-       @property
-       def netboot(self):
-               return backend.NetBoot()
+class BootBaseHandler(BaseHandler):
+       pass
 
 
 class MenuGPXEHandler(BootBaseHandler):
@@ -74,56 +74,17 @@ class MenuGPXEHandler(BootBaseHandler):
 
 
 class MenuCfgHandler(BootBaseHandler):
-       def _menu_string(self, menu, level=0):
-               s = ""
-
-               for entry in menu:
-                       s += self._menu_entry(entry, level=level)
-
-               return s
-
-       def _menu_entry(self, entry, level=0):
-               lines = []
-
-               ident = "\t" * level
-
-               if entry.type == "seperator":
-                       lines.append(ident + "menu separator")
-
-               elif entry.type == "header":
-                       lines.append(ident + "menu begin %d" % entry.id)
-                       lines.append(ident + "\tmenu title %s" % entry.title)
-
-                       # Add "Back..." entry
-                       lines.append(ident + "\tlabel %d.back" % entry.id)
-                       lines.append(ident + "\t\tmenu label Back...")
-                       lines.append(ident + "\t\tmenu exit")
-                       lines.append(ident + "\tmenu separator")
-
-                       lines.append("%s" % self._menu_string(entry.submenu, level=level+1))
-                       lines.append(ident + "menu end")
-
-               elif entry.type == "config":
-                       config = self.netboot.get_config(entry.item)
-
-                       lines.append(ident + "label %d" % config.id)
-                       lines.append(ident + "\tmenu label %s" % config.title)
-                       if config.description:
-                               lines.append(ident + "\ttext help")
-                               lines.append(word_wrap(config.description))
-                               lines.append(ident + "\tendtext")
-
-                       lines.append(ident + "\tkernel %s" % config.image1)
-                       if config.image2:
-                               lines.append(ident + "\tinitrd %s" % config.image2)
-                       if config.args:
-                               lines.append(ident + "\tappend %s" % config.args)
-
-               return "\n".join(lines + [""])
-
        def get(self):
                self.set_header("Content-Type", "text/plain")
 
-               menu = self._menu_string(self.netboot.get_menu(1))
+               latest_release = self.releases.get_latest()
+               stable_releases = self.releases.get_stable()
+               try:
+                       stable_releases.remove(latest_release)
+               except ValueError:
+                       pass
+
+               development_releases = self.releases.get_unstable()
 
-               self.render("netboot/menu.cfg", menu=menu)
+               self.render("netboot/menu.cfg", latest_release=latest_release,
+                       stable_releases=stable_releases, development_releases=development_releases)
index 0dc0526658a6adef19c4b0ed2a718b6a92e2e937..cfe46f8b63133fafcb4435cbf5908d6c3afb07ac 100644 (file)
@@ -17,7 +17,11 @@ class DownloadsIndexHandler(BaseHandler):
 
 class DownloadsReleaseHandler(BaseHandler):
        def get(self, release):
-               release = self.releases.get_by_id(release)
+               release = self.releases.get_by_sname(release)
+
+               if not release:
+                       release = self.releases.get_by_id(release)
+
                if not release:
                        raise tornado.web.HTTPError(404)
 
@@ -69,9 +73,16 @@ class DownloadDevelopmentHandler(BaseHandler):
 
 
 class DownloadFileHandler(BaseHandler):
-       def head(self, filename):
+       def prepare(self):
                self.set_header("Pragma", "no-cache")
 
+       def head(self, filename):
+               self.redirect_to_mirror(filename)
+
+       def get(self, filename):
+               self.redirect_to_mirror(filename, log_download=True)
+
+       def find_mirror(self, filename):
                # Get all mirrors...
                mirrors = self.mirrors.get_all()
                mirrors = mirrors.get_with_file(filename)
@@ -82,22 +93,37 @@ class DownloadFileHandler(BaseHandler):
 
                # Find mirrors located near to the user.
                # If we have not found any, we use all.
-               mirrors_nearby = mirrors.get_for_location(self.request.remote_ip)
-               if mirrors_nearby:
-                       mirrors = mirrors_nearby
+               remote_location = self.get_remote_location()
 
-               mirror = mirrors.get_random()
+               if remote_location:
+                       mirrors_nearby = mirrors.get_for_location(remote_location)
 
-               self.redirect(mirror.url + filename[len(mirror.prefix):])
-               return mirror
+                       if mirrors_nearby:
+                               mirrors = mirrors_nearby
 
-       def get(self, filename):
-               mirror = self.head(filename)
+               return mirrors.get_random()
+
+       def redirect_to_mirror(self, filename, log_download=False):
+               # Find a random mirror.
+               mirror = self.find_mirror(filename)
+
+               # Construct the redirection URL.
+               download_url = mirror.build_url(filename)
+
+               # Redirect the request.
+               self.redirect(download_url)
 
-               # Record the download.
-               country_code = self.geoip.get_country(self.request.remote_ip)
-               self.mirrors.db.execute("INSERT INTO log_download(filename, mirror, country_code) VALUES(%s, %s, %s)",
-                       filename, mirror.id, country_code)
+               if not log_download:
+                       return
+
+               remote_location = self.get_remote_location()
+               if remote_location:
+                       country_code = remote_location.country
+               else:
+                       country_code = None
+
+               self.db.execute("INSERT INTO log_download(filename, mirror, country_code) \
+                       VALUES(%s, %s, %s)", filename, mirror.id, country_code)
 
 
 class DownloadCompatHandler(BaseHandler):
@@ -114,6 +140,7 @@ class DownloadCompatHandler(BaseHandler):
 
                self.redirect("/%s" % _filename)
 
+
 class DownloadSplashHandler(BaseHandler):
        def get(self):
                self.render("download-splash.html")
index 5d3c83c0cfbea5f3d913fe5308634ba3af914bc0..4428afd8e19c2d7361ea7d99d022d20bea09342d 100644 (file)
@@ -9,14 +9,6 @@ from handlers_base import *
 import backend
 
 class IUseImage(BaseHandler):
-       @property
-       def iuse(self):
-               return backend.IUse()
-
-       @property
-       def stasy(self):
-               return backend.Stasy()
-
        def get_error_html(self, status_code, **kwargs):
                """
                        Select a random image from the errors directory
@@ -59,7 +51,7 @@ class IUseImage(BaseHandler):
                                raise tornado.web.HTTPError(404, "Profile '%s' was not found." % profile_id)
 
                        # Render the image
-                       image = image_cls(self, profile).to_string()
+                       image = image_cls(self.backend, self, profile).to_string()
 
                        # Save the image to the memcache for 15 minutes
                        self.memcached.set(mem_id, image, 15*60)
index 70973d643d5ca3f3854d49279ffdebd0f3303607..1d7d212c4939b08e09e8a59defbd7702e03f3de9 100644 (file)
@@ -7,35 +7,39 @@ from handlers_base import *
 
 class MirrorIndexHandler(BaseHandler):
        def get(self):
-               ip_addr = self.get_argument("addr", self.request.remote_ip)
+               ip_addr = self.get_argument("addr", None)
+               if not ip_addr:
+                       ip_addr = self.get_remote_ip()
+
+               location = self.geoip.get_location(ip_addr)
 
                # Get a list of all mirrors.
-               all_mirrors = self.mirrors.get_all()
+               mirrors = self.mirrors.get_all()
 
                # Choose the preferred ones by their location.
-               preferred_mirrors = all_mirrors.get_for_location(ip_addr)
-
-               # Remove the preferred ones from the list of the rest.
-               other_mirrors = all_mirrors - preferred_mirrors
+               preferred_mirrors = mirrors.get_for_location(location)
 
                self.render("mirrors.html",
-                       preferred_mirrors=preferred_mirrors, other_mirrors=other_mirrors)
+                       preferred_mirrors=preferred_mirrors, mirrors=mirrors)
 
 
 class MirrorItemHandler(BaseHandler):
-       def get(self, id):
-               mirror = self.mirrors.get(id)
+       def get(self, what):
+               mirror = self.mirrors.get_by_hostname(what)
                if not mirror:
-                       raise tornado.web.HTTPError(404)
+                       mirror = self.mirrors.get(id)
 
-               ip_addr = self.get_argument("addr", self.request.remote_ip)
-               client_location = self.geoip.get_all(ip_addr)
+               if not mirror:
+                       raise tornado.web.HTTPError(404, what)
+
+               ip_addr = self.get_argument("addr", None)
+               if not ip_addr:
+                       ip_addr = self.get_remote_ip()
+               client_location = self.geoip.get_location(ip_addr)
 
                client_distance = mirror.distance_to(client_location, ignore_preference=True)
-               client_distance *= 111.32 # to km
 
-               self.render("mirrors-item.html", item=mirror,
-                       client_distance=client_distance)
+               self.render("mirrors-item.html", item=mirror, client_distance=client_distance)
 
 
 class MirrorHandler(BaseHandler):
@@ -65,7 +69,7 @@ class MirrorListPakfire2Handler(BaseHandler):
 
                lines = []
                for m in mirrors:
-                       if not m.mirrorlist or not m.is_pakfire2():
+                       if not m.mirrorlist:
                                continue
 
                        # Skip all non-development mirrors
@@ -73,10 +77,7 @@ class MirrorListPakfire2Handler(BaseHandler):
                        if development and not m.development:
                                continue
 
-                       path = [m.path,]
-
-                       if m.type == "full":
-                               path.append("pakfire2")
+                       path = [m.path, "pakfire2"]
 
                        if suffix:
                                path.append(suffix)
index 6a91116608d986265e9d901638fc6c5c338a40f7..ec6e626f1ed633bda4de363fd9b70b607f70af1d 100644 (file)
@@ -1,5 +1,6 @@
 #!/usr/bin/python
 
+import re
 import tornado.web
 
 from backend.tracker import bencode, bdecode, decode_hex
@@ -41,33 +42,7 @@ class TrackerDownloadHandler(BaseHandler):
                self.redirect("http://downloads.ipfire.org/%s.torrent" % file.filename)
 
 
-#class TrackerTorrentsHandler(BaseHandler):
-#      @property
-#      def tracker(self):
-#              return self.tracker
-#
-#      def get(self):          
-#              releases = []
-#
-#              for release in self.releases.get_all():
-#                      if not release.torrent_hash:
-#                              continue
-#
-#                      release.torrent_hash = release.torrent_hash.lower()
-#
-#                      release.torrent_peers = self.tracker.incomplete(release.torrent_hash)
-#                      release.torrent_seeds = self.tracker.complete(release.torrent_hash)
-#
-#                      releases.append(release)
-#
-#              self.render("tracker-torrents.html", releases=releases)
-
-
-class TrackerBaseHandler(tornado.web.RequestHandler):
-       @property
-       def tracker(self):
-               return backend.Tracker()
-
+class TrackerBaseHandler(BaseHandler):
        def get_hexencoded_argument(self, name, all=False):
                try:
                        arguments = self.request.arguments[name]
@@ -86,74 +61,133 @@ class TrackerBaseHandler(tornado.web.RequestHandler):
                return arguments[0]
 
        def send_tracker_error(self, error_message):
-               self.write(bencode({"failure reason" : error_message }))
-               self.finish()
+               msg = bencode({"failure reason" : error_message })
+               self.finish(msg)
 
 
 class TrackerAnnounceHandler(TrackerBaseHandler):
-       def get(self):
+       def prepare(self):
                self.set_header("Content-Type", "text/plain")
 
+       def get_ipv6_address(self, default_port):
+               # Get the external IP address of the client.
+               addr = self.get_remote_ip()
+
+               if ":" in addr:
+                       return addr, default_port
+
+               # IPv6
+               ipv6 = self.get_argument("ipv6", None)
+               if ipv6:
+                       port = default_port
+
+                       m = re.match("^\[(.*)\]\:(\d)$", ipv6)
+                       if m:
+                               ipv6, port = (m.group(1), m.group(2))
+
+                       return ipv6, port
+
+               return None, None
+
+       def get_ipv4_address(self, default_port):
+               # Get the external IP address of the client.
+               addr = self.get_remote_ip()
+
+               if not ":" in addr:
+                       return addr, default_port
+
+               # IPv4
+               ipv4 = self.get_argument("ipv4", None)
+               if ipv4:
+                       return ipv4, default_port
+
+               ip = self.get_argument("ip", None)
+               if ip:
+                       return ip, default_port
+
+               return None, None
+
+       def get_port(self):
+               # Get the port and check it for sanity
+               port = self.get_argument("port", None)
+
+               try:
+                       port = int(port)
+
+                       if port < 0 or port > 65535:
+                               raise ValueError
+               except (TypeError, ValueError):
+                       port = None
+
+               return port
+
+       def get(self):
+               # Get the info hash
                info_hash = self.get_hexencoded_argument("info_hash")
                if not info_hash:
-                       self.send_tracker_error("Your client forgot to send your torrent's info_hash.")
+                       self.send_tracker_error("Your client forgot to send your torrent's info_hash")
                        return
 
-               # Fix for clients behind a proxy that sends "X-Forwarded-For".
-               ip_addr = self.request.remote_ip.split(", ")
-               if ip_addr:
-                       ip_addr = ip_addr[-1]
+               # Get the peer id
+               peer_id = self.get_hexencoded_argument("peer_id")
 
-               peer = {
-                       "id" : self.get_hexencoded_argument("peer_id"),
-                       "ip" : ip_addr,
-                       "port" : self.get_argument("port", None),
-                       "downloaded" : self.get_argument("downloaded", 0),
-                       "uploaded" : self.get_argument("uploaded", 0),
-                       "left" : self.get_argument("left", 0),
-               }
-
-               event = self.get_argument("event", "")
-               if not event in ("started", "stopped", "completed", ""):
-                       self.send_tracker_error("Got unknown event")
+               # Get the port and check it for sanity
+               port = self.get_port()
+               if not port:
+                       self.send_tracker_error("Invalid port number or port number missing")
                        return
 
-               if peer["port"]:
-                       peer["port"] = int(peer["port"])
+               addr_ipv6, port_ipv6 = self.get_ipv6_address(port)
+               addr_ipv4, port_ipv4 = self.get_ipv4_address(port)
 
-                       if peer["port"] < 0 or peer["port"] > 65535:
-                               self.send_tracker_error("Port number is not in valid range")
+               # Handle events
+               event = self.get_argument("event", None)
+               if event:
+                       if not event in ("started", "stopped", "completed"):
+                               self.send_tracker_error("Got unknown event")
                                return
 
-               eventhandlers = {
-                       "started" : self.tracker.event_started,
-                       "stopped" : self.tracker.event_stopped,
-                       "completed" : self.tracker.event_completed,
-               }
+                       self.tracker.handle_event(event, peer_id, info_hash,
+                               address6=addr_ipv6, port6=port_ipv6, address4=addr_ipv4, port4=port_ipv4)
 
-               if event:
-                       eventhandlers[event](info_hash, peer["id"])
+               peer_info = {
+                       "address6"   : addr_ipv6,
+                       "port6"      : port_ipv6,
+                       "address4"   : addr_ipv4,
+                       "port4"      : port_ipv4,
+                       "downloaded" : self.get_argument("downloaded", 0),
+                       "uploaded"   : self.get_argument("uploaded", 0),
+                       "left_data"  : self.get_argument("left", 0),
+               }
 
-               self.tracker.update(hash=info_hash, **peer)
+               self.tracker.update_peer(peer_id, info_hash, **peer_info)
 
                no_peer_id = self.get_argument("no_peer_id", False)
                numwant = self.get_argument("numwant", self.tracker.numwant)
 
-               self.write(bencode({
-                       "tracker id" : self.tracker.id,
-                       "interval" : self.tracker.interval,
+               peers = self.tracker.get_peers(info_hash, limit=numwant, no_peer_id=no_peer_id)
+
+               response = bencode({
+                       "tracker id"   : self.tracker.tracker_id,
+                       "interval"     : self.tracker.interval,
                        "min interval" : self.tracker.min_interval,
-                       "peers" : self.tracker.get_peers(info_hash, limit=numwant,
-                               random=True, no_peer_id=no_peer_id),
-                       "complete" : self.tracker.complete(info_hash),
-                       "incomplete" : self.tracker.incomplete(info_hash),
-               }))
-               self.finish()
+                       "peers"        : peers,
+                       "complete"     : self.tracker.complete(info_hash),
+                       "incomplete"   : self.tracker.incomplete(info_hash),
+               })
+               self.finish(response)
+
+       def on_finish(self):
+               """
+                       Cleanup after every request.
+               """
+               self.tracker.cleanup_peers()
 
 
 class TrackerScrapeHandler(TrackerBaseHandler):
        def get(self):
                info_hashes = self.get_hexencoded_argument("info_hash", all=True)
 
-               self.write(bencode(self.tracker.scrape(hashes=info_hashes)))
-               self.finish()
+               response = self.tracker.scrape(info_hashes)
+
+               self.finish(bencode(response))
index 931a79facf32172d6eb0f3731351b1adba5e85f4..da0f25b2b1ccd84b5332c88ac8fa303192314eeb 100644 (file)
@@ -5,11 +5,13 @@ from __future__ import division
 import hashlib
 import logging
 import operator
+import re
 import socket
 import textile
 import tornado.escape
 import tornado.locale
 import tornado.web
+import unicodedata
 
 from tornado.database import Row
 
@@ -60,11 +62,47 @@ class AdvertisementModule(UIModule):
                return self.render_string("modules/ads/%s.html" % where, ad=ad)
 
 
+class MapModule(UIModule):
+       def render(self, latitude, longitude):
+               return self.render_string("modules/map.html", latitude=latitude, longitude=longitude)
+
+
 class MenuModule(UIModule):
        def render(self):
                return self.render_string("modules/menu.html")
 
 
+class MirrorItemModule(UIModule):
+       def render(self, item):
+               return self.render_string("modules/mirror-item.html", item=item)
+
+
+class MirrorsTableModule(UIModule):
+       def render(self, mirrors, preferred_mirrors=[]):
+               return self.render_string("modules/mirrors-table.html",
+                       mirrors=mirrors, preferred_mirrors=preferred_mirrors)
+
+
+class NetBootMenuConfigModule(UIModule):
+       def render(self, release):
+               return self.render_string("netboot/menu-config.cfg", release=release)
+
+
+class NetBootMenuHeaderModule(UIModule):
+       def render(self, title, releases):
+               id = unicodedata.normalize("NFKD", unicode(title)).encode("ascii", "ignore")
+               id = re.sub(r"[^\w]+", " ", id)
+               id = "-".join(id.lower().strip().split())
+
+               return self.render_string("netboot/menu-header.cfg", id=id,
+                       title=title, releases=releases)
+
+
+class NetBootMenuSeparatorModule(UIModule):
+       def render(self):
+               return self.render_string("netboot/menu-separator.cfg")
+
+
 class NewsItemModule(UIModule):
        def get_author(self, author):
                # Get name of author
@@ -83,14 +121,7 @@ class NewsItemModule(UIModule):
                        item.text = item.text[:400] + "..."
 
                # Render text
-               text_id = "news-%s" % hashlib.md5(item.text.encode("utf-8")).hexdigest()
-
-               text = self.memcached.get(text_id)
-               if not text:
-                       text = textile.textile(item.text)
-                       self.memcached.set(text_id, text, 60)
-
-               item.text = text
+               item.text = textile.textile(item.text)
 
                return self.render_string("modules/news-item.html", item=item,
                        uncut=uncut, announcement=announcement, show_heading=show_heading)
@@ -117,11 +148,6 @@ class NewsYearNavigationModule(UIModule):
                        active=active, years=self.news.years)
 
 
-class MirrorItemModule(UIModule):
-       def render(self, item):
-               return self.render_string("modules/mirror-item.html", item=item)
-
-
 class SidebarItemModule(UIModule):
        def render(self):
                return self.render_string("modules/sidebar-item.html")
@@ -300,11 +326,6 @@ class StasyGeoTableModule(UIModule):
                return self.render_string("modules/stasy-table-geo.html", countries=countries)
 
 
-class MirrorsTableModule(UIModule):
-       def render(self, mirrors):
-               return self.render_string("modules/mirrors-table.html", mirrors=mirrors)
-
-
 class WishlistModule(UIModule):
        def render(self, wishes, short=False):
                return self.render_string("wishlist/modules/wishlist.html",