From 9068dba187b011290bd8bb947d3695fa90e71e27 Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Fri, 27 Dec 2013 15:25:21 +0100 Subject: [PATCH] Major update of the webapp. Migrate to PostgreSQL. Remove all the singletons. A bit of cleanup and functionality enhancements. --- .gitignore | 1 + manager.py | 20 +- static/css/style.css | 2 +- templates/downloads-index.html | 2 +- templates/geoip/index.html | 50 + templates/index.html | 2 +- templates/mirrors-item.html | 131 +- templates/mirrors.html | 11 +- templates/modules/map.html | 20 + templates/modules/mirrors-table.html | 30 +- templates/modules/news-item.html | 4 +- templates/modules/news-table.html | 2 +- templates/netboot/menu-config.cfg | 11 + templates/netboot/menu-header.cfg | 11 + templates/netboot/menu-separator.cfg | 1 + templates/netboot/menu.cfg | 8 +- templates/wishlist/modules/wish.html | 11 +- templates/wishlist/wish.html | 10 +- translations/de_DE/LC_MESSAGES/webapp.po | 3212 +++++++++++++++------- translations/webapp.pot | 2707 ++++++++++++------ webapp.conf.example | 7 + webapp.py | 2 +- webapp/__init__.py | 65 +- webapp/backend/__init__.py | 16 - webapp/backend/accounts.py | 9 +- webapp/backend/ads.py | 33 +- webapp/backend/banners.py | 28 - webapp/backend/base.py | 62 +- webapp/backend/countries.py | 274 ++ webapp/backend/database.py | 189 ++ webapp/backend/databases.py | 80 - webapp/backend/geoip.py | 115 +- webapp/backend/iuse.py | 31 +- webapp/backend/memcached.py | 36 +- webapp/backend/mirrors.py | 345 ++- webapp/backend/misc.py | 42 +- webapp/backend/netboot.py | 12 +- webapp/backend/news.py | 65 +- webapp/backend/planet.py | 43 +- webapp/backend/releases.py | 168 +- webapp/backend/settings.py | 66 +- webapp/backend/stasy.py | 15 +- webapp/backend/tracker.py | 240 +- webapp/backend/wishlist.py | 87 +- webapp/handlers.py | 19 + webapp/handlers_admin.py | 14 +- webapp/handlers_base.py | 59 +- webapp/handlers_boot.py | 67 +- webapp/handlers_download.py | 55 +- webapp/handlers_iuse.py | 10 +- webapp/handlers_mirrors.py | 41 +- webapp/handlers_tracker.py | 174 +- webapp/ui_modules.py | 57 +- 53 files changed, 5838 insertions(+), 2934 deletions(-) create mode 100644 templates/geoip/index.html create mode 100644 templates/modules/map.html create mode 100644 templates/netboot/menu-config.cfg create mode 100644 templates/netboot/menu-header.cfg create mode 100644 templates/netboot/menu-separator.cfg create mode 100644 webapp.conf.example delete mode 100644 webapp/backend/banners.py create mode 100644 webapp/backend/countries.py create mode 100644 webapp/backend/database.py delete mode 100644 webapp/backend/databases.py diff --git a/.gitignore b/.gitignore index c732e42..cd79799 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +/webapp.conf *.mo *.py[co] diff --git a/manager.py b/manager.py index 63fac1f..f921aaa 100644 --- a/manager.py +++ b/manager.py @@ -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() diff --git a/static/css/style.css b/static/css/style.css index a15ea4a..82ee4a5 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -11,7 +11,7 @@ body { padding-bottom: 10px; } -.ac { +.ac, td.ac { text-align: center; } diff --git a/templates/downloads-index.html b/templates/downloads-index.html index 0ed63b4..b4aff97 100644 --- a/templates/downloads-index.html +++ b/templates/downloads-index.html @@ -41,7 +41,7 @@ {% for release in releases %} - {{ release.name }} + {{ release.name }} {% if release.stable %} diff --git a/templates/geoip/index.html b/templates/geoip/index.html new file mode 100644 index 0000000..63a4a09 --- /dev/null +++ b/templates/geoip/index.html @@ -0,0 +1,50 @@ +{% extends "../base.html" %} + +{% block title %}{{ _("GeoIP for %s") % addr }}{% end block %} + +{% block body %} + + + {% if peer %} +
+
+
+ {% if peer.asn %} +
{{ _("Autonomous System") }}
+
{{ peer.asn }}
+ {% end %} + +
{{ _("Country") }}
+
+ {% if peer.country_name %} + {{ peer.country_name }} ({{ peer.country }}) + {% else %} + {{ peer.country_name }} + {% end %} +
+ + {% if peer.city %} +
{{ _("City") }}
+
{{ peer.city }}
+ + {% if peer.postal_code %} +
{{ _("Postal Code") }}
+
{{ peer.postal_code }}
+ {% end %} + {% end %} +
+
+ +
+ {% module Map(peer.latitude, peer.longitude) %} +
+
+ {% else %} +
+ {{ _("No GeoIP information could be found for the IP address '%s'.") % addr }} +
+ {% end %} + +{% end block %} diff --git a/templates/index.html b/templates/index.html index 6d644f0..2b3e5bc 100644 --- a/templates/index.html +++ b/templates/index.html @@ -139,7 +139,7 @@ {% for item in latest_news %} - {{ locale.format_date(item.date, relative=True, shorter=True) }} ‐ + {{ locale.format_date(item.published, relative=True, shorter=True) }} ‐ {{ item.title }} diff --git a/templates/mirrors-item.html b/templates/mirrors-item.html index cc375a2..e6c730f 100644 --- a/templates/mirrors-item.html +++ b/templates/mirrors-item.html @@ -17,52 +17,87 @@

{{ item.hostname }}

- - - - - - - - - - {% if item.prefer_for_countries %} - - - - - {% end %} - {% if client_distance %} - - - - - {% end %} -
{{ _("Last update") }}{{ locale.format_date(item.last_update, full_format=True) }}
{{ _("Owner") }}{{ item.owner }}
{{ _("Preferred for") }}{{ locale.list(item.prefer_for_countries_names) }}
{{ _("Your distance to this mirror") }}{{ "%.1f km" % client_distance }}
- - {{ _("Go to mirror") }} - -
-
- -

{{ _("Mirror location") }}

-

- {{ _("The mirror %s is located in %s.") % (item.hostname, item.location_str) }} -

- - {% if item.longitude and item.latitude %} - -

- {{ _("View larger map") }} - - - © OpenStreetMap contributors, CC-BY-SA -

-

- {{ _("The location of the mirror server is estimated by the IP address.") }} -

- {% else %} - {{ _("The location of the mirror server could not be estimated.") }} - {% end %} +
+
+ {% if item.owner %} +
{{ _("Owner") }}
+
{{ item.owner }}
+ {% end %} + + {% if item.location_str %} +
+ +
{{ _("Location") }}
+
{{ item.location_str }}
+ {% elif item.location %} +
+ + {% if item.country_name %} +
{{ _("Country") }}
+
{{ item.country_name }}
+ {% end %} + + {% if item.location.city %} +
{{ _("City") }}
+
{{ item.location.city }}
+ {% end %} + {% end %} + + {% if item.prefer_for_countries %} +
{{ _("Preferred for") }}
+
+ {{ locale.list(item.prefer_for_countries_names) }} +
+ {% end %} + + {% if client_distance %} +
{{ _("Estimated distance to you") }}
+
{{ "%.0fkm" % client_distance }}
+ {% end %} + +
+ + {% if item.asn %} +
{{ _("Autonomous System") }}
+
{{ item.asn }}
+ {% end %} + +
{{ _("IP Addresses") }}
+
+ {% for addr in item.addresses6 + item.addresses4 %} + {{ addr }}
+ {% end %} +
+ + {% if item.enabled %} +
+ +
{{ _("Last updated") }}
+
+ + {{ locale.format_date(item.last_update, relative=True) }} + +
+ {% end %} + +
+ +

+ {{ _("Go to mirror") }} +

+
+ +
+ {% if item.location %} + {% module Map(item.latitude, item.longitude) %} +

+ {{ _("The location of the mirror server is estimated by the IP address.") }} +

+ {% else %} +

+ {{ _("The location of this mirror server could not be estimated.") }} +

+ {% end %} +
+
{% end block %} diff --git a/templates/mirrors.html b/templates/mirrors.html index 59f0f47..1c5da56 100644 --- a/templates/mirrors.html +++ b/templates/mirrors.html @@ -21,14 +21,7 @@

{% end %} - {% if preferred_mirrors %} -

{{ _("Mirror servers nearby") }}

- {% module MirrorsTable(preferred_mirrors) %} +
-

{{ _("Worldwide mirror servers") }}

- {% module MirrorsTable(other_mirrors) %} - {% else %} -

{{ _("Worldwide mirror servers") }}

- {% 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 index 0000000..8849451 --- /dev/null +++ b/templates/modules/map.html @@ -0,0 +1,20 @@ + +

+ {{ _("View larger map") }} + - + © OpenStreetMap contributors, CC-BY-SA + + {% if latitude >= 0 %} + {{ latitude }} N, + {% else %} + {{ -latitude }} S, + {% end %} + {% if longitude >= 0 %} + {{ longitude }} E + {% else %} + {{ -longitude }} W + {% end %} + +

diff --git a/templates/modules/mirrors-table.html b/templates/modules/mirrors-table.html index e595020..e46c570 100644 --- a/templates/modules/mirrors-table.html +++ b/templates/modules/mirrors-table.html @@ -1,6 +1,19 @@ {% for mirror in mirrors %} + + {% if mirror.state == "UP" %} {% else %} {% end %} - {% end %} diff --git a/templates/modules/news-item.html b/templates/modules/news-item.html index 9eb061e..1716fa5 100644 --- a/templates/modules/news-item.html +++ b/templates/modules/news-item.html @@ -5,7 +5,7 @@ {% if announcement %} {{ _("Announcement") }}: {% end %} - {{ escape(item.title) }} + {{ item.title }} {% end %} @@ -16,7 +16,7 @@

{{ item.author }} - - {{ locale.format_date(item.date, full_format=True) }} + {{ locale.format_date(item.published, full_format=True) }}

diff --git a/templates/modules/news-table.html b/templates/modules/news-table.html index e1c3677..e431719 100644 --- a/templates/modules/news-table.html +++ b/templates/modules/news-table.html @@ -3,7 +3,7 @@
  • {{ n.title }}
    -   {{ locale.format_date(n.date, shorter=True) }} +   {{ locale.format_date(n.published, shorter=True) }}
     
  • {% end %} diff --git a/templates/netboot/menu-config.cfg b/templates/netboot/menu-config.cfg new file mode 100644 index 0000000..200bae1 --- /dev/null +++ b/templates/netboot/menu-config.cfg @@ -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 index 0000000..192fe6f --- /dev/null +++ b/templates/netboot/menu-header.cfg @@ -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 index 0000000..2af538f --- /dev/null +++ b/templates/netboot/menu-separator.cfg @@ -0,0 +1 @@ +menu separator diff --git a/templates/netboot/menu.cfg b/templates/netboot/menu.cfg index 83325f8..65bfd0f 100644 --- a/templates/netboot/menu.cfg +++ b/templates/netboot/menu.cfg @@ -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) %} diff --git a/templates/wishlist/modules/wish.html b/templates/wishlist/modules/wish.html index 7b8d247..e998e37 100644 --- a/templates/wishlist/modules/wish.html +++ b/templates/wishlist/modules/wish.html @@ -42,9 +42,14 @@
    - {% if wish.remaining_days >= 0 %} -

    {{ wish.remaining_days }}

    -

    {{ _("day to go", "days to go", wish.remaining_days) }}

    + {% if wish.status == "running" %} + {% if wish.remaining_days %} +

    {{ wish.remaining_days }}

    +

    {{ _("day to go", "days to go", wish.remaining_days) }}

    + {% else %} +

    {% raw _("%s €") % (wish.goal - wish.donated) %}

    +

    {{ _("to go") }}

    + {% end %} {% elif wish.status == "in_progress" %}

    {{ _("In progress") }}

    {% elif wish.status == "finished" %} diff --git a/templates/wishlist/wish.html b/templates/wishlist/wish.html index 5060734..15fa623 100644 --- a/templates/wishlist/wish.html +++ b/templates/wishlist/wish.html @@ -15,11 +15,15 @@

    - {{ _("Launched: %s") % wish.date_start }} + {{ _("Launched: %s") % locale.format_date(wish.date_start, full_format=True) }} • - - {{ _("Funding ends: %s") % wish.date_end }} + {% if wish.date_end %} + + {{ _("Funding ends: %s") % locale.format_date(wish.date_end, full_format=True) }} + {% else %} + {{ _("This funding runs until the goal is reached.") }} + {% end %}

    {% end block %} diff --git a/translations/de_DE/LC_MESSAGES/webapp.po b/translations/de_DE/LC_MESSAGES/webapp.po index 8423c65..653c825 100644 --- a/translations/de_DE/LC_MESSAGES/webapp.po +++ b/translations/de_DE/LC_MESSAGES/webapp.po @@ -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 \n" "Language-Team: LANGUAGE \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 €" -msgstr "%s €" +#: 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 latest version." +#: webapp/backend/countries.py:75 +msgid "Ethiopia" msgstr "" -"Achtung! Diese Releases könnten potentielle Sicherheitsprobleme aufweisen. " -"Daher wird empfohlen die neueste 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 %s is located in %s." -msgstr "Der Mirrorserver %s 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: %.2f MHz." +#: webapp/backend/countries.py:180 +msgid "Peru" msgstr "" -"Die durchschnittliche Geschwindigkeit aller System ist: %.2f MHz." -#: templates/fireinfo/stats-cpus.html:40 -#, python-format -msgid "All together, there are %s bogomips out there." -msgstr "Zusammengenomen sind es %s Bogomips." +#: 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: %.2f " -"MB." +#: webapp/backend/countries.py:189 +msgid "Palestinian Territory, occupied" msgstr "" -"Die durchschnittliche Menge an Arbeitsspeicher aller System ist: " -"%.2f MB." -#: 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: %.2f " +"MB." +msgstr "" +"Die durchschnittliche Menge an Arbeitsspeicher aller System ist: " +"%.2f MB." + +#: 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: %.2f MHz." +msgstr "" +"Die durchschnittliche Geschwindigkeit aller System ist: %.2f MHz." + +#: templates/fireinfo/stats-cpus.html:40 +#, python-format +msgid "All together, there are %s bogomips out there." +msgstr "Zusammengenomen sind es %s Bogomips." + +#: 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 latest version." +msgstr "" +"Achtung! Diese Releases könnten potentielle Sicherheitsprobleme aufweisen. " +"Daher wird empfohlen die neueste 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 €" +msgstr "%s €" -#: 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 %s is located in %s." +#~ msgstr "Der Mirrorserver %s 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" diff --git a/translations/webapp.pot b/translations/webapp.pot index d712f05..4caba93 100644 --- a/translations/webapp.pot +++ b/translations/webapp.pot @@ -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 \n" "Language-Team: LANGUAGE \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 €" +#: 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 latest 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 %s 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: %.2f MHz." +#: webapp/backend/countries.py:180 +msgid "Peru" msgstr "" -#: templates/fireinfo/stats-cpus.html:40 -#, python-format -msgid "All together, there are %s bogomips 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: %.2f " -"MB." +#: 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: %.2f " +"MB." +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: %.2f MHz." +msgstr "" + +#: templates/fireinfo/stats-cpus.html:40 +#, python-format +msgid "All together, there are %s bogomips 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 latest 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 €" 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 index 0000000..d2c5bbf --- /dev/null +++ b/webapp.conf.example @@ -0,0 +1,7 @@ +[global] + +[database] +server = pgsql-master.ipfire.org +database = webapp +username = webapp +password = ... diff --git a/webapp.py b/webapp.py index 0bbe14b..0217543 100755 --- 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(), diff --git a/webapp/__init__.py b/webapp/__init__.py index 54bd5b7..36bd58e 100644 --- a/webapp/__init__.py +++ b/webapp/__init__.py @@ -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 diff --git a/webapp/backend/__init__.py b/webapp/backend/__init__.py index 17f42d4..a6aa46b 100644 --- a/webapp/backend/__init__.py +++ b/webapp/backend/__init__.py @@ -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 diff --git a/webapp/backend/accounts.py b/webapp/backend/accounts.py index 1097661..d254816 100644 --- a/webapp/backend/accounts.py +++ b/webapp/backend/accounts.py @@ -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) diff --git a/webapp/backend/ads.py b/webapp/backend/ads.py index eadf10a..f7abe58 100644 --- a/webapp/backend/ads.py +++ b/webapp/backend/ads.py @@ -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 index 08db020..0000000 --- a/webapp/backend/banners.py +++ /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() diff --git a/webapp/backend/base.py b/webapp/backend/base.py index 58ee5ca..21a31c5 100644 --- a/webapp/backend/base.py +++ b/webapp/backend/base.py @@ -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 index 0000000..9c42119 --- /dev/null +++ b/webapp/backend/countries.py @@ -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 index 0000000..d2964d3 --- /dev/null +++ b/webapp/backend/database.py @@ -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 index f09cead..0000000 --- a/webapp/backend/databases.py +++ /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) diff --git a/webapp/backend/geoip.py b/webapp/backend/geoip.py index 43f92da..0fe4310 100644 --- a/webapp/backend/geoip.py +++ b/webapp/backend/geoip.py @@ -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 diff --git a/webapp/backend/iuse.py b/webapp/backend/iuse.py index 7b447c9..8c330ec 100644 --- a/webapp/backend/iuse.py +++ b/webapp/backend/iuse.py @@ -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) diff --git a/webapp/backend/memcached.py b/webapp/backend/memcached.py index a56b80e..105f1f5 100644 --- a/webapp/backend/memcached.py +++ b/webapp/backend/memcached.py @@ -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) diff --git a/webapp/backend/mirrors.py b/webapp/backend/mirrors.py index 260cff0..265e516 100644 --- a/webapp/backend/mirrors.py +++ b/webapp/backend/mirrors.py @@ -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 "" % ", ".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] diff --git a/webapp/backend/misc.py b/webapp/backend/misc.py index 22dce10..3cc129f 100644 --- a/webapp/backend/misc.py +++ b/webapp/backend/misc.py @@ -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 diff --git a/webapp/backend/netboot.py b/webapp/backend/netboot.py index eabd4dc..9d112f2 100644 --- a/webapp/backend/netboot.py +++ b/webapp/backend/netboot.py @@ -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 = [] diff --git a/webapp/backend/news.py b/webapp/backend/news.py index eddd036..51dfd44 100644 --- a/webapp/backend/news.py +++ b/webapp/backend/news.py @@ -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] diff --git a/webapp/backend/planet.py b/webapp/backend/planet.py index c34898f..0c75656 100644 --- a/webapp/backend/planet.py +++ b/webapp/backend/planet.py @@ -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 diff --git a/webapp/backend/releases.py b/webapp/backend/releases.py index 6c907f4..64bed28 100644 --- a/webapp/backend/releases.py +++ b/webapp/backend/releases.py @@ -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() diff --git a/webapp/backend/settings.py b/webapp/backend/settings.py index 076ea65..6355485 100644 --- a/webapp/backend/settings.py +++ b/webapp/backend/settings.py @@ -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() diff --git a/webapp/backend/stasy.py b/webapp/backend/stasy.py index 00c595b..87fe2fc 100644 --- a/webapp/backend/stasy.py +++ b/webapp/backend/stasy.py @@ -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() - diff --git a/webapp/backend/tracker.py b/webapp/backend/tracker.py index 4880bee..24a763b 100644 --- a/webapp/backend/tracker.py +++ b/webapp/backend/tracker.py @@ -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 ##### diff --git a/webapp/backend/wishlist.py b/webapp/backend/wishlist.py index e94634a..4cdb55e 100644 --- a/webapp/backend/wishlist.py +++ b/webapp/backend/wishlist.py @@ -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 diff --git a/webapp/handlers.py b/webapp/handlers.py index 33804ac..20c21ae 100644 --- a/webapp/handlers.py +++ b/webapp/handlers.py @@ -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) diff --git a/webapp/handlers_admin.py b/webapp/handlers_admin.py index c8a68dd..e7c7217 100644 --- a/webapp/handlers_admin.py +++ b/webapp/handlers_admin.py @@ -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) diff --git a/webapp/handlers_base.py b/webapp/handlers_base.py index e44d203..29d212b 100644 --- a/webapp/handlers_base.py +++ b/webapp/handlers_base.py @@ -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 diff --git a/webapp/handlers_boot.py b/webapp/handlers_boot.py index c1c4aed..becaa4a 100644 --- a/webapp/handlers_boot.py +++ b/webapp/handlers_boot.py @@ -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) diff --git a/webapp/handlers_download.py b/webapp/handlers_download.py index 0dc0526..cfe46f8 100644 --- a/webapp/handlers_download.py +++ b/webapp/handlers_download.py @@ -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") diff --git a/webapp/handlers_iuse.py b/webapp/handlers_iuse.py index 5d3c83c..4428afd 100644 --- a/webapp/handlers_iuse.py +++ b/webapp/handlers_iuse.py @@ -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) diff --git a/webapp/handlers_mirrors.py b/webapp/handlers_mirrors.py index 70973d6..1d7d212 100644 --- a/webapp/handlers_mirrors.py +++ b/webapp/handlers_mirrors.py @@ -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) diff --git a/webapp/handlers_tracker.py b/webapp/handlers_tracker.py index 6a91116..ec6e626 100644 --- a/webapp/handlers_tracker.py +++ b/webapp/handlers_tracker.py @@ -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)) diff --git a/webapp/ui_modules.py b/webapp/ui_modules.py index 931a79f..da0f25b 100644 --- a/webapp/ui_modules.py +++ b/webapp/ui_modules.py @@ -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", -- 2.39.2
    + {% if mirror.country_code %} + {{ mirror.country_code }} +
    + {% if mirror.country_name %} + {{ mirror.country_code }} + {% else %} + {{ mirror.country_code }} + {% end %} + {% if mirror in preferred_mirrors %}*{% end %} + {% end %} +
      @@ -15,17 +28,18 @@ -   +   {{ mirror.state }} - {{ mirror.hostname }} - - {{ mirror.owner }} -
    - {{ mirror.country_code }} - {{ mirror.location_str }} + {% if mirror.state in ("OUTOFSYNC", "DOWN") %} + + {{ _("Last update: %s") % locale.format_date(mirror.last_update, relative=True) }} + + {% end %} + + {{ mirror.hostname }} +
    {{ mirror.owner }}