aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--AUTHORS8
-rw-r--r--alembic.ini59
-rw-r--r--docs/source/api/client_register.rst11
-rw-r--r--docs/source/siteadmin/configuration.rst28
-rw-r--r--docs/source/siteadmin/media-types.rst10
-rw-r--r--docs/source/siteadmin/relnotes.rst126
m---------extlib/sandyseventiesspeedboat0
-rwxr-xr-xlazystarter.sh9
-rw-r--r--mediagoblin.ini11
-rw-r--r--mediagoblin/_compat.py32
-rw-r--r--mediagoblin/_version.py2
-rw-r--r--mediagoblin/app.py8
-rw-r--r--mediagoblin/auth/tools.py4
-rw-r--r--mediagoblin/auth/views.py4
-rw-r--r--mediagoblin/config_spec.ini23
-rw-r--r--mediagoblin/db/migration_tools.py58
-rw-r--r--mediagoblin/db/migrations.py208
-rw-r--r--mediagoblin/db/migrations/README57
-rw-r--r--mediagoblin/db/migrations/env.py71
-rw-r--r--mediagoblin/db/migrations/script.py.mako22
-rw-r--r--mediagoblin/db/migrations/versions/.gitkeep0
-rw-r--r--mediagoblin/db/mixin.py122
-rw-r--r--mediagoblin/db/models.py243
-rw-r--r--mediagoblin/db/open.py4
-rw-r--r--mediagoblin/decorators.py3
-rw-r--r--mediagoblin/edit/routing.py2
-rw-r--r--mediagoblin/edit/views.py48
-rw-r--r--mediagoblin/federation/routing.py16
-rw-r--r--mediagoblin/federation/views.py216
-rw-r--r--mediagoblin/gmg_commands/__init__.py8
-rw-r--r--mediagoblin/gmg_commands/addmedia.py18
-rw-r--r--mediagoblin/gmg_commands/batchaddmedia.py46
-rw-r--r--mediagoblin/gmg_commands/dbupdate.py19
-rw-r--r--mediagoblin/gmg_commands/deletemedia.py9
-rw-r--r--mediagoblin/gmg_commands/reprocess.py43
-rw-r--r--mediagoblin/gmg_commands/serve.py66
-rw-r--r--mediagoblin/gmg_commands/users.py30
-rw-r--r--mediagoblin/i18n/cs/LC_MESSAGES/mediagoblin.mobin52489 -> 52472 bytes
-rw-r--r--mediagoblin/i18n/cs/LC_MESSAGES/mediagoblin.po250
-rw-r--r--mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po22
-rw-r--r--mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.mobin54231 -> 54187 bytes
-rw-r--r--mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.po51
-rw-r--r--mediagoblin/i18n/fa_IR/LC_MESSAGES/mediagoblin.mobin0 -> 51077 bytes
-rw-r--r--mediagoblin/i18n/fa_IR/LC_MESSAGES/mediagoblin.po2489
-rw-r--r--mediagoblin/i18n/he/LC_MESSAGES/mediagoblin.mobin56728 -> 56801 bytes
-rw-r--r--mediagoblin/i18n/he/LC_MESSAGES/mediagoblin.po28
-rw-r--r--mediagoblin/i18n/nl/LC_MESSAGES/mediagoblin.mobin51522 -> 52424 bytes
-rw-r--r--mediagoblin/i18n/nl/LC_MESSAGES/mediagoblin.po737
-rw-r--r--mediagoblin/init/celery/__init__.py8
-rw-r--r--mediagoblin/init/config.py47
-rw-r--r--mediagoblin/media_types/ascii/processing.py12
-rw-r--r--mediagoblin/media_types/audio/processing.py2
-rw-r--r--mediagoblin/media_types/blog/views.py22
-rw-r--r--mediagoblin/media_types/image/processing.py15
-rw-r--r--mediagoblin/media_types/pdf/processing.py7
-rw-r--r--mediagoblin/media_types/raw_image/processing.py10
-rw-r--r--mediagoblin/media_types/stl/model_loader.py2
-rw-r--r--mediagoblin/media_types/stl/processing.py2
-rw-r--r--mediagoblin/media_types/video/processing.py4
-rw-r--r--mediagoblin/mg_globals.py3
-rw-r--r--mediagoblin/moderation/tools.py7
-rw-r--r--mediagoblin/oauth/views.py19
-rw-r--r--mediagoblin/plugins/api/tools.py4
-rw-r--r--mediagoblin/plugins/api/views.py16
-rw-r--r--mediagoblin/plugins/basic_auth/tools.py4
-rw-r--r--mediagoblin/plugins/httpapiauth/__init__.py4
-rw-r--r--mediagoblin/plugins/ldap/tools.py4
-rw-r--r--mediagoblin/plugins/ldap/views.py5
-rw-r--r--mediagoblin/plugins/oauth/forms.py2
-rw-r--r--mediagoblin/plugins/oauth/models.py4
-rw-r--r--mediagoblin/plugins/oauth/tools.py6
-rw-r--r--mediagoblin/plugins/oauth/views.py12
-rw-r--r--mediagoblin/plugins/openid/store.py6
-rw-r--r--mediagoblin/plugins/openid/views.py5
-rw-r--r--mediagoblin/plugins/persona/views.py4
-rw-r--r--mediagoblin/plugins/piwigo/tools.py2
-rw-r--r--mediagoblin/plugins/piwigo/views.py6
-rw-r--r--mediagoblin/processing/__init__.py6
-rw-r--r--mediagoblin/processing/task.py12
-rw-r--r--mediagoblin/static/css/base.css37
-rw-r--r--mediagoblin/static/images/small-gavroche.pngbin0 -> 4599 bytes
-rw-r--r--mediagoblin/static/images/small-gavroche.xcfbin0 -> 392752 bytes
-rw-r--r--mediagoblin/storage/__init__.py14
-rw-r--r--mediagoblin/storage/cloudfiles.py4
-rw-r--r--mediagoblin/storage/filestorage.py9
-rw-r--r--mediagoblin/storage/mountstorage.py4
-rw-r--r--mediagoblin/submit/lib.py20
-rw-r--r--mediagoblin/submit/views.py12
-rw-r--r--mediagoblin/templates/mediagoblin/base.html244
-rw-r--r--mediagoblin/templates/mediagoblin/edit/deauthorize_applications.html69
-rw-r--r--mediagoblin/templates/mediagoblin/edit/edit_account.html5
-rw-r--r--mediagoblin/templates/mediagoblin/federation/activity.html42
-rw-r--r--mediagoblin/templates/mediagoblin/federation/host-meta.xml22
-rw-r--r--mediagoblin/tests/test_api.py39
-rw-r--r--mediagoblin/tests/test_auth.py14
-rw-r--r--mediagoblin/tests/test_basic_auth.py3
-rw-r--r--mediagoblin/tests/test_edit.py17
-rw-r--r--mediagoblin/tests/test_exif.py18
-rw-r--r--mediagoblin/tests/test_http_callback.py8
-rw-r--r--mediagoblin/tests/test_ldap.py12
-rw-r--r--mediagoblin/tests/test_legacy_api.py11
-rw-r--r--mediagoblin/tests/test_metadata.py4
-rw-r--r--mediagoblin/tests/test_modelmethods.py9
-rw-r--r--mediagoblin/tests/test_notifications.py6
-rw-r--r--mediagoblin/tests/test_oauth1.py15
-rw-r--r--mediagoblin/tests/test_oauth2.py14
-rw-r--r--mediagoblin/tests/test_openid.py10
-rw-r--r--mediagoblin/tests/test_paste.ini34
-rw-r--r--mediagoblin/tests/test_pdf.py29
-rw-r--r--mediagoblin/tests/test_persona.py12
-rw-r--r--mediagoblin/tests/test_piwigo.py17
-rw-r--r--mediagoblin/tests/test_pluginapi.py15
-rw-r--r--mediagoblin/tests/test_privileges.py42
-rw-r--r--mediagoblin/tests/test_reporting.py5
-rw-r--r--mediagoblin/tests/test_sql_migrations.py10
-rw-r--r--mediagoblin/tests/test_storage.py20
-rw-r--r--mediagoblin/tests/test_submission.py16
l---------[-rw-r--r--]mediagoblin/tests/test_submission/good.pdfbin194007 -> 44 bytes
-rw-r--r--mediagoblin/tests/test_util.py15
-rw-r--r--mediagoblin/tests/test_workbench.py2
-rw-r--r--mediagoblin/tests/tools.py5
-rw-r--r--mediagoblin/tools/crypto.py6
-rw-r--r--mediagoblin/tools/exif.py15
-rw-r--r--mediagoblin/tools/federation.py63
-rw-r--r--mediagoblin/tools/mail.py20
-rw-r--r--mediagoblin/tools/metadata.py3
-rw-r--r--mediagoblin/tools/pagination.py6
-rw-r--r--mediagoblin/tools/processing.py11
-rw-r--r--mediagoblin/tools/response.py10
-rw-r--r--mediagoblin/tools/staticdirect.py4
-rw-r--r--mediagoblin/tools/template.py11
-rw-r--r--mediagoblin/tools/translate.py17
-rw-r--r--mediagoblin/tools/url.py4
-rw-r--r--mediagoblin/tools/workbench.py10
-rw-r--r--mediagoblin/user_pages/views.py12
-rw-r--r--paste.ini43
-rw-r--r--setup.py146
-rw-r--r--tox.ini13
139 files changed, 5458 insertions, 1309 deletions
diff --git a/.gitignore b/.gitignore
index 2524739f..2b21a4bd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,7 +23,9 @@
/mediagoblin.db
/celery.db
/kombu.db
+/celerybeat-schedule
/server-log.txt
+*.egg/
# pyconfigure/automake generated files
/Makefile
@@ -44,3 +46,7 @@
# The legacy of buildout
.installed.cfg
+
+# Virtualenv, tox
+venv*
+.tox/
diff --git a/AUTHORS b/AUTHORS
index 9429f5ae..86360e09 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -15,11 +15,14 @@ Thank you!
* Aleksej Serdjukov
* Alon Levy
* Alex Camelio
+* Amirouche Boubekki
* András Veres-Szentkirályi
* Asheesh Laroia
+* Andrew Browning
* Bassam Kurdali
* Bernhard Keller
* Berker Peksag
+* Beuc
* Boris Bobrov
* Brandon Invergo
* Brett Smith
@@ -44,6 +47,7 @@ Thank you!
* Jef van Schendel
* Jeremy Pope
* Jessica Tallon
+* Jiyda Mint Moussa
* Jim Campbell
* Joar Wandborg
* Jorge Araya Navarro
@@ -56,12 +60,14 @@ Thank you!
* Larisa Hoffenbecker
* Lenna Peterson
* Loïc Le Ninan
+* Low Kian Seong
* Luke Slater
* Manuel Urbano Santos
* Marcel van der Boom
* Mark Holmquist
* Mats Sjöberg
* Matt Lee
+* Matt Molyneaux
* Michele Azzolari
* Mike Linksvayer
* Natalie Foust-Pilcher
@@ -78,7 +84,9 @@ Thank you!
* Sam Clegg
* Sam Kleinman
* Sam Tuke
+* Sebastian Hugentobler
* Sebastian Spaeth
+* Sergio Durigan Junior
* Shawn Khan
* Simon Fondrie-Teitler
* Stefano Zacchiroli
diff --git a/alembic.ini b/alembic.ini
new file mode 100644
index 00000000..2fa08099
--- /dev/null
+++ b/alembic.ini
@@ -0,0 +1,59 @@
+# A generic, single database configuration.
+
+[alembic]
+# path to migration scripts
+script_location = %(here)s/mediagoblin/db/migrations
+
+# template used to generate migration files
+# file_template = %%(rev)s_%%(slug)s
+
+# max length of characters to apply to the
+# "slug" field
+#truncate_slug_length = 40
+
+# set to 'true' to run the environment during
+# the 'revision' command, regardless of autogenerate
+# revision_environment = false
+
+# set to 'true' to allow .pyc and .pyo files without
+# a source .py file to be detected as revisions in the
+# versions/ directory
+# sourceless = false
+
+sqlalchemy.url = sqlite:///mediagoblin.db
+
+
+# Logging configuration
+[loggers]
+keys = root,sqlalchemy,alembic
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = WARN
+handlers = console
+qualname =
+
+[logger_sqlalchemy]
+level = WARN
+handlers =
+qualname = sqlalchemy.engine
+
+[logger_alembic]
+level = INFO
+handlers =
+qualname = alembic
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S
diff --git a/docs/source/api/client_register.rst b/docs/source/api/client_register.rst
index 08f92c47..9c29bafc 100644
--- a/docs/source/api/client_register.rst
+++ b/docs/source/api/client_register.rst
@@ -15,7 +15,7 @@
Registering a Client
====================
-To use the GNU MediaGoblin API you need to use the dynamic client registration. This has been adapted from the `OpenID specification <https://openid.net/specs/openid-connect-registration-1_0.html>`_, this is the only part of OpenID that is being used to serve the purpose to provide the client registration which is used in OAuth.
+To use the GNU MediaGoblin API you need to use the dynamic client registration. This has been adapted from the `OpenID specification <https://openid.net/specs/openid-connect-registration-1_0.html>`_, this is the only part of OpenID that is being used to serve the purpose to provide the client registration which is used in OAuth.
The endpoint is ``/api/client/register``
@@ -39,8 +39,8 @@ application_type
application_name
**optional** - This is the name of your client
-logo_url
- **optional** - This is a URL of the logo image for your client
+logo_uri
+ **optional** - This is a URI of the logo image for your client
redirect_uri
**optional** - This is a space seporated list of pre-registered URLs for use at the Authorization Server
@@ -93,8 +93,8 @@ Using the response we got above we can update the information and add new inform
"client_id": "vwljdhUMhhNbdKizpjZlxv",
"client_secret": "hJtfhaQzgKerlLVdaeRAgmbcstSOBLRfgOinMxBCHcb",
"application_type": "web",
- "application_name": "MyClient!",
- "logo_url": "https://myclient.org/images/my_logo.png",
+ "application_name": "MyClient!",
+ "logo_uri": "https://myclient.org/images/my_logo.png",
"contacts": "myemail@someprovider.com another_developer@provider.net",
}
@@ -155,4 +155,3 @@ redirect_uris must be space-separated URLs.
URI <URI> is not a valid URI
This is when your URI is invalid.
-
diff --git a/docs/source/siteadmin/configuration.rst b/docs/source/siteadmin/configuration.rst
index 3da5cdd9..dd0d6cd9 100644
--- a/docs/source/siteadmin/configuration.rst
+++ b/docs/source/siteadmin/configuration.rst
@@ -109,6 +109,34 @@ they sound like.
- email_smtp_user
- email_smtp_pass
+Changing data directory
+-----------------------
+
+MediaGoblin by default stores your data in wherever ``data_basedir``.
+This can be changed by changing the value in your ``mediagoblin.ini`` file
+for example::
+
+ [DEFAULT]
+ data_basedir = "/var/mediagoblin/user_data"
+
+For efficiency reasons MediaGoblin doesn't serve these files itself and
+instead leaves that to the webserver. You will have to alter the location
+to match the path in ``data_basedir``.
+
+If you use ``lazyserver.sh`` you need to change the ``paste.ini`` file::
+
+ [app:mediagoblin]
+ /mgoblin_media = /var/mediagoblin/user_data
+
+If you use nginx you need to change the config::
+
+ # Instance specific media:
+ location /mgoblin_media/ {
+ alias /var/mediagoblin/user_data;
+ }
+
+Once you have done this you will need to move any existing media you had in the
+old directory to the new directory so existing media still can be displayed.
All other configuration changes
-------------------------------
diff --git a/docs/source/siteadmin/media-types.rst b/docs/source/siteadmin/media-types.rst
index 44ad02bb..f8030081 100644
--- a/docs/source/siteadmin/media-types.rst
+++ b/docs/source/siteadmin/media-types.rst
@@ -264,3 +264,13 @@ Run
./bin/gmg dbupdate
+Blog (HIGHLY EXPERIMENTAL)
+==========================
+
+MediaGoblin has a blog media type, which you might notice by looking
+through the docs! However, it is *highly experimental*. We have not
+security reviewed this, and it acts in a way that is not like normal
+blogs (the blogposts are themselves media types!).
+
+So you can play with this, but it is not necessarily recommended yet
+for production use! :)
diff --git a/docs/source/siteadmin/relnotes.rst b/docs/source/siteadmin/relnotes.rst
index 3542bdcb..16899a0b 100644
--- a/docs/source/siteadmin/relnotes.rst
+++ b/docs/source/siteadmin/relnotes.rst
@@ -21,6 +21,132 @@ This chapter has important information for releases in it.
If you're upgrading from a previous release, please read it
carefully, or at least skim over it.
+0.7.1
+=====
+
+This is a purely bugfix release. Important changes happened since
+0.7.0; if running MediaGoblin 0.7.0, an upgrade is highly recommended;
+see below. This release is especially useful if you have been running
+postgres and have been receiving seemingly random database transaction
+errors.
+
+**Do this to upgrade**
+
+1. Update to the latest release. If checked out from git, run:
+ ``git fetch && git checkout -q v0.7.1 && git submodule init && git submodule update``
+2. Make sure to run
+ ``./bin/python setup.py develop --upgrade && ./bin/gmg dbupdate``
+
+That's it, probably! If you run into problems, don't hesitate to
+`contact us <http://mediagoblin.org/pages/join.html>`_
+(IRC is often best).
+
+**Bugfixes/improvements:**
+
+- The *MOST IMPORTANT* change in this release:
+ Disabling a couple of non-critical features that were causing
+ database transaction issues. (These should be back by 0.8.0.)
+
+ + Disabled the "checking if the database is up to date at
+ mediagoblin startup" feature
+ + Disabled the garbage collection stuff by default for now
+ (You can set garbage_collection under the config mediagoblin
+ header to something other than 0 to turn it back on for now, but
+ it's potentially risky for the moment.)
+
+- Some fixes to the 0.7.0 docs
+- Fixed Sandy 70s speedboat navbar by updating git submodule
+- Added support for cr2 files in raw_image media type
+- Added a description to setup.py
+- Collection and CollectionItem objects now have nicer in-python representations
+- Fixed unicode error with raw image mediatype logging
+- Fixed #945 "Host metadata does not confirm to spec (/.well-known/meta-data)"
+
+ + Add XRD+XML formatting for /.well-known/host-meta
+ + Add /.well-known/webfinger API to lookup user hrefs
+
+- deleteuser gmg subcommand now fails gracefully
+- Removed a false download link from setup.py
+
+0.7.0
+====
+
+**Do this to upgrade**
+
+1. Update to the latest release. If checked out from git, run:
+ ``git fetch && git checkout -q v0.7.0 && git submodule init && git submodule update``
+2. Make sure to run
+ ``./bin/python setup.py develop --upgrade && ./bin/gmg dbupdate``
+
+(NOTE: earlier versions of the 0.7.0 release instructions left out the
+``git submodule init`` step! If you did an upgrade earlier based on
+these instructions and your theme looks weirdly aligned, try running
+the following:)
+
+ ``git submodule init && git submodule update``
+
+That's it, probably! If you run into problems, don't hesitate to
+`contact us <http://mediagoblin.org/pages/join.html>`_
+(IRC is often best).
+
+**New features:**
+
+- New mobile upload API making use of the
+ `Pump API <https://github.com/e14n/pump.io/blob/master/API.md>`_
+ (which will be the foundation for MediaGoblin's federation)
+- New theme: Sandy 70s Speedboat!
+
+- Metadata features! We also now have a json-ld context.
+
+- Many improvements for archival institutions, including metadata
+ support and featuring items on the homepage. With the (new!)
+ archivalook plugin enabled, featuring media is possible.
+ Additionally, metadata about the particular media item will show up
+ in the sidebar.
+
+ In the future these plugins may be separated, but for now they have
+ come together as part of the same plugin.
+
+- There is a new gmg subcommand called batchaddmedia that allows for
+ uploading many files at once. This is aimed to be useful for
+ archival institutions and groups where there is an already existing
+ and large set of available media that needs to be included.
+- Speaking of, the call to postgres in the makefile is fixed.
+- We have a new, generic media-page context hook that allows for
+ adding context depending on the type of media.
+- Tired of video thumbnails breaking during processing all the time?
+ Good news, everyone! Video thumbnail generation should not fail
+ frequently anymore. (We think...)
+- You can now set default permissions for new users in the config.
+
+- bootstrap.sh / gnu configuration stuff still exists, but moves to be
+ experimental-bootstrap.sh so as to not confuse newcomers. There are
+ some problems currently with the autoconf stuff that we need to work
+ out... we still have interest in supporting it, though help is
+ welcome.
+
+- MediaGoblin now checks whether or not the database is up to date
+ when starting.
+- Switched to `Skeleton <http://www.getskeleton.com/>`_ as a system for
+ graphic design.
+- New gmg subcommands for administrators:
+ - A "deletemedia" command
+ - A "deleteuser" command
+- We now have a blogging media type... it's very experimental,
+ however. Use with caution!
+- We have switched to exifread as an external library for reading EXIF
+ data. It's basically the same thing as before, but packaged
+ separately from MediaGoblin.
+- Many improvements to internationalization. Also (still rudimentary,
+ but existant!) RTL language support!
+
+**Known issues:**
+ - The host-meta is now json by default; in the spec it should be xml by
+ default. We have done this because of compatibility with the pump
+ API. We are checking with upstream to see if there is a way to
+ resolve this discrepancy.
+
+
0.6.1
=====
diff --git a/extlib/sandyseventiesspeedboat b/extlib/sandyseventiesspeedboat
-Subproject 994294e59a5248e29af4418903ce4fb40bd67be
+Subproject 8873d9b559a4c5b3bb90997227d5455f8730fd4
diff --git a/lazystarter.sh b/lazystarter.sh
index 41994015..3a4efd25 100755
--- a/lazystarter.sh
+++ b/lazystarter.sh
@@ -20,7 +20,7 @@ selfname=$(basename "$0")
local_bin="./bin"
case "$selfname" in
lazyserver.sh)
- starter_cmd=paster
+ starter_cmd=gunicorn
ini_prefix=paste
;;
lazycelery.sh)
@@ -36,9 +36,8 @@ esac
if [ "$1" = "-h" ]; then
echo "$0 [-h] [-c filename.ini] [ARGS_to_${starter_cmd} ...]"
echo ""
- echo " For example:"
- echo " $0 -c fcgi.ini port_number=23371"
- echo " or: $0 --server-name=fcgi --log-file=paste.log"
+ echo " For Gunicorn settings, see at:"
+ echo " http://docs.gunicorn.org/en/19.0/settings.html"
echo ""
echo " The configfile defaults to ${ini_prefix}_local.ini,"
echo " if that is readable, otherwise ${ini_prefix}.ini."
@@ -71,7 +70,7 @@ set -x
export CELERY_ALWAYS_EAGER=true
case "$selfname" in
lazyserver.sh)
- $starter serve "$ini_file" "$@" --reload
+ $starter --paste "$ini_file" --log-file=- $@
;;
lazycelery.sh)
MEDIAGOBLIN_CONFIG="${ini_file}" \
diff --git a/mediagoblin.ini b/mediagoblin.ini
index 5e2477a4..7899d7ca 100644
--- a/mediagoblin.ini
+++ b/mediagoblin.ini
@@ -5,6 +5,11 @@
# It defines types and defaults so it’s a good place to look for documentation
# or to find hidden options that we didn’t tell you about. :)
+# To chnange the directory you should make sure you change the
+# directory in paste.ini and/or your webserver configuration.
+#[DEFAULT]
+# data_basedir = "/path/to/data/directory"
+
[mediagoblin]
direct_remote_path = /mgoblin_static/
email_sender_address = "notice@mediagoblin.example.org"
@@ -24,7 +29,7 @@ allow_registration = true
allow_reporting = true
## Uncomment this to put some user-overriding templates here
-# local_templates = %(here)s/user_dev/templates/
+# local_templates = %(data_basedir)s/templates/
## You can set your theme by specifying this (not specifying it will
## use the default theme). Run `gmg assetlink` to apply the change.
@@ -37,10 +42,10 @@ allow_reporting = true
user_privilege_scheme = "uploader,commenter,reporter"
[storage:queuestore]
-base_dir = %(here)s/user_dev/media/queue
+base_dir = %(data_basedir)s/media/queue
[storage:publicstore]
-base_dir = %(here)s/user_dev/media/public
+base_dir = %(data_basedir)s/media/public
base_url = /mgoblin_media/
[celery]
diff --git a/mediagoblin/_compat.py b/mediagoblin/_compat.py
new file mode 100644
index 00000000..9164d5fc
--- /dev/null
+++ b/mediagoblin/_compat.py
@@ -0,0 +1,32 @@
+import functools
+import warnings
+
+import six
+
+if six.PY3:
+ from email.mime.text import MIMEText
+else:
+ from email.MIMEText import MIMEText
+
+
+def encode_to_utf8(method):
+ def wrapper(self):
+ if six.PY2 and isinstance(method(self), six.text_type):
+ return method(self).encode('utf-8')
+ return method(self)
+ functools.update_wrapper(wrapper, method, ['__name__', '__doc__'])
+ return wrapper
+
+
+# based on django.utils.encoding.python_2_unicode_compatible
+def py2_unicode(klass):
+ if six.PY2:
+ if '__str__' not in klass.__dict__:
+ warnings.warn("@py2_unicode cannot be applied "
+ "to %s because it doesn't define __str__()." %
+ klass.__name__)
+ klass.__unicode__ = klass.__str__
+ klass.__str__ = encode_to_utf8(klass.__unicode__)
+ if '__repr__' in klass.__dict__:
+ klass.__repr__ = encode_to_utf8(klass.__repr__)
+ return klass
diff --git a/mediagoblin/_version.py b/mediagoblin/_version.py
index 9ff2a374..b82f3f1f 100644
--- a/mediagoblin/_version.py
+++ b/mediagoblin/_version.py
@@ -23,4 +23,4 @@
# see http://www.python.org/dev/peps/pep-0386/
-__version__ = "0.6.2.dev"
+__version__ = "0.7.2.dev"
diff --git a/mediagoblin/app.py b/mediagoblin/app.py
index 6923e198..b3e41835 100644
--- a/mediagoblin/app.py
+++ b/mediagoblin/app.py
@@ -23,6 +23,7 @@ from mediagoblin.tools.routing import endpoint_to_controller
from werkzeug.wrappers import Request
from werkzeug.exceptions import HTTPException
from werkzeug.routing import RequestRedirect
+from werkzeug.wsgi import SharedDataMiddleware
from mediagoblin import meddleware, __version__
from mediagoblin.db.util import check_db_up_to_date
@@ -93,7 +94,9 @@ class MediaGoblinApp(object):
self.db = setup_database(app_config['run_migrations'])
# Quit app if need to run dbupdate
- check_db_up_to_date()
+ ## NOTE: This is currently commented out due to session errors..
+ ## We'd like to re-enable!
+ # check_db_up_to_date()
# Register themes
self.theme_registry, self.current_theme = register_themes(app_config)
@@ -279,8 +282,11 @@ def paste_app_factory(global_config, **app_config):
if not mediagoblin_config:
raise IOError("Usable mediagoblin config not found.")
+ del app_config['config']
mgoblin_app = MediaGoblinApp(mediagoblin_config)
+ mgoblin_app.call_backend = SharedDataMiddleware(mgoblin_app.call_backend,
+ exports=app_config)
mgoblin_app = hook_transform('wrap_wsgi', mgoblin_app)
return mgoblin_app
diff --git a/mediagoblin/auth/tools.py b/mediagoblin/auth/tools.py
index 39df85af..f153737b 100644
--- a/mediagoblin/auth/tools.py
+++ b/mediagoblin/auth/tools.py
@@ -16,6 +16,8 @@
import logging
+
+import six
import wtforms
from sqlalchemy import or_
@@ -136,7 +138,7 @@ def register_user(request, register_form):
user.save()
# log the user in
- request.session['user_id'] = unicode(user.id)
+ request.session['user_id'] = six.text_type(user.id)
request.session.save()
# send verification email
diff --git a/mediagoblin/auth/views.py b/mediagoblin/auth/views.py
index 3d132f84..a90db0ea 100644
--- a/mediagoblin/auth/views.py
+++ b/mediagoblin/auth/views.py
@@ -14,6 +14,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import six
+
from itsdangerous import BadSignature
from mediagoblin import messages, mg_globals
@@ -93,7 +95,7 @@ def login(request):
# set up login in session
if login_form.stay_logged_in.data:
request.session['stay_logged_in'] = True
- request.session['user_id'] = unicode(user.id)
+ request.session['user_id'] = six.text_type(user.id)
request.session.save()
if request.form.get('next'):
diff --git a/mediagoblin/config_spec.ini b/mediagoblin/config_spec.ini
index b5c957c8..dade8420 100644
--- a/mediagoblin/config_spec.ini
+++ b/mediagoblin/config_spec.ini
@@ -1,3 +1,6 @@
+[DEFAULT]
+data_basedir = %(here)s/user_dev
+
[mediagoblin]
# HTML title of the pages
html_title = string(default="GNU MediaGoblin")
@@ -13,10 +16,10 @@ sql_engine = string(default="sqlite:///%(here)s/mediagoblin.db")
run_migrations = boolean(default=False)
# Where temporary files used in processing and etc are kept
-workbench_path = string(default="%(here)s/user_dev/media/workbench")
+workbench_path = string(default="%(data_basedir)s/media/workbench")
# Where to store cryptographic sensible data
-crypto_path = string(default="%(here)s/user_dev/crypto")
+crypto_path = string(default="%(data_basedir)s/crypto")
# Where mediagoblin-builtin static assets are kept
direct_remote_path = string(default="/mgoblin_static/")
@@ -67,7 +70,7 @@ allow_reporting = boolean(default=True)
show_tos = boolean(default=False)
# By default not set, but you might want something like:
-# "%(here)s/user_dev/templates/"
+# "%(data_basedir)s/templates/"
local_templates = string()
# Whether or not celery is set up via an environment variable or
@@ -90,14 +93,14 @@ exif_visible = boolean(default=False)
original_date_visible = boolean(default=False)
# Theming stuff
-theme_install_dir = string(default="%(here)s/user_dev/themes/")
+theme_install_dir = string(default="%(data_basedir)s/themes/")
theme_web_path = string(default="/theme_static/")
-theme_linked_assets_dir = string(default="%(here)s/user_dev/theme_static/")
+theme_linked_assets_dir = string(default="%(data_basedir)s/theme_static/")
theme = string()
# plugin default assets directory
plugin_web_path = string(default="/plugin_static/")
-plugin_linked_assets_dir = string(default="%(here)s/user_dev/plugin_static/")
+plugin_linked_assets_dir = string(default="%(data_basedir)s/plugin_static/")
# Default user upload limit (in Mb)
upload_limit = integer(default=None)
@@ -110,7 +113,9 @@ user_privilege_scheme = string(default="uploader,commenter,reporter")
# Frequency garbage collection will run (setting to 0 or false to disable)
# Setting units are minutes.
-garbage_collection = integer(default=60)
+## NOTE: This is temporarily disabled, but we want to set it back:
+## garbage_collection = integer(default=60)
+garbage_collection = integer(default=0)
[jinja2]
# Jinja2 supports more directives than the minimum required by mediagoblin.
@@ -121,12 +126,12 @@ extensions = string_list(default=list())
[storage:publicstore]
storage_class = string(default="mediagoblin.storage.filestorage:BasicFileStorage")
-base_dir = string(default="%(here)s/user_dev/media/public")
+base_dir = string(default="%(data_basedir)s/media/public")
base_url = string(default="/mgoblin_media/")
[storage:queuestore]
storage_class = string(default="mediagoblin.storage.filestorage:BasicFileStorage")
-base_dir = string(default="%(here)s/user_dev/media/queue")
+base_dir = string(default="%(data_basedir)s/media/queue")
[media:medium]
# Dimensions used when creating media display images.
diff --git a/mediagoblin/db/migration_tools.py b/mediagoblin/db/migration_tools.py
index e39070c3..fae98643 100644
--- a/mediagoblin/db/migration_tools.py
+++ b/mediagoblin/db/migration_tools.py
@@ -14,14 +14,68 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from __future__ import unicode_literals
+
+import logging
+import os
+
+from alembic import command
+from alembic.config import Config
+from alembic.migration import MigrationContext
+
+from mediagoblin.db.base import Base
from mediagoblin.tools.common import simple_printer
from sqlalchemy import Table
from sqlalchemy.sql import select
+log = logging.getLogger(__name__)
+
+
class TableAlreadyExists(Exception):
pass
+class AlembicMigrationManager(object):
+
+ def __init__(self, session):
+ root_dir = os.path.abspath(os.path.dirname(os.path.dirname(
+ os.path.dirname(__file__))))
+ alembic_cfg_path = os.path.join(root_dir, 'alembic.ini')
+ self.alembic_cfg = Config(alembic_cfg_path)
+ self.session = session
+
+ def get_current_revision(self):
+ context = MigrationContext.configure(self.session.bind)
+ return context.get_current_revision()
+
+ def upgrade(self, version):
+ return command.upgrade(self.alembic_cfg, version or 'head')
+
+ def downgrade(self, version):
+ if isinstance(version, int) or version is None or version.isdigit():
+ version = 'base'
+ return command.downgrade(self.alembic_cfg, version)
+
+ def stamp(self, revision):
+ return command.stamp(self.alembic_cfg, revision=revision)
+
+ def init_tables(self):
+ Base.metadata.create_all(self.session.bind)
+ # load the Alembic configuration and generate the
+ # version table, "stamping" it with the most recent rev:
+ # XXX: we need to find a better way to detect current installations
+ # using sqlalchemy-migrate because we don't have to create all table
+ # for them
+ command.stamp(self.alembic_cfg, 'head')
+
+ def init_or_migrate(self, version=None):
+ # XXX: we need to call this method when we ditch
+ # sqlalchemy-migrate entirely
+ # if self.get_current_revision() is None:
+ # self.init_tables()
+ self.upgrade(version)
+
+
class MigrationManager(object):
"""
Migration handling tool.
@@ -39,7 +93,7 @@ class MigrationManager(object):
- migration_registry: where we should find all migrations to
run
"""
- self.name = unicode(name)
+ self.name = name
self.models = models
self.foundations = foundations
self.session = session
@@ -230,7 +284,7 @@ class MigrationManager(object):
for migration_number, migration_func in migrations_to_run:
self.printer(
u' + Running migration %s, "%s"... ' % (
- migration_number, migration_func.func_name))
+ migration_number, migration_func.__name__))
migration_func(self.session)
self.set_current_migration(migration_number)
self.printer('done.\n')
diff --git a/mediagoblin/db/migrations.py b/mediagoblin/db/migrations.py
index be509a75..0e0ee6be 100644
--- a/mediagoblin/db/migrations.py
+++ b/mediagoblin/db/migrations.py
@@ -17,19 +17,24 @@
import datetime
import uuid
+import six
+
+if six.PY2:
+ import migrate
+
from sqlalchemy import (MetaData, Table, Column, Boolean, SmallInteger,
Integer, Unicode, UnicodeText, DateTime,
ForeignKey, Date, Index)
from sqlalchemy.exc import ProgrammingError
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql import and_
-from migrate.changeset.constraint import UniqueConstraint
+from sqlalchemy.schema import UniqueConstraint
from mediagoblin.db.extratypes import JSONEncoded, MutationDict
from mediagoblin.db.migration_tools import (
RegisterMigration, inspect_table, replace_table_hack)
from mediagoblin.db.models import (MediaEntry, Collection, MediaComment, User,
- Privilege)
+ Privilege, Generator)
from mediagoblin.db.extratypes import JSONEncoded, MutationDict
@@ -249,7 +254,7 @@ def mediaentry_new_slug_era(db):
for row in db.execute(media_table.select()):
# no slug, try setting to an id
if not row.slug:
- append_garbage_till_unique(row, unicode(row.id))
+ append_garbage_till_unique(row, six.text_type(row.id))
# has "=" or ":" in it... we're getting rid of those
elif u"=" in row.slug or u":" in row.slug:
append_garbage_till_unique(
@@ -278,7 +283,7 @@ def unique_collections_slug(db):
existing_slugs[row.creator].append(row.slug)
for row_id in slugs_to_change:
- new_slug = unicode(uuid.uuid4())
+ new_slug = six.text_type(uuid.uuid4())
db.execute(collection_table.update().
where(collection_table.c.id == row_id).
values(slug=new_slug))
@@ -578,7 +583,6 @@ PRIVILEGE_FOUNDATIONS_v0 = [{'privilege_name':u'admin'},
{'privilege_name':u'commenter'},
{'privilege_name':u'active'}]
-
# vR1 stands for "version Rename 1". This only exists because we need
# to deal with dropping some booleans and it's otherwise impossible
# with sqlite.
@@ -891,6 +895,198 @@ def revert_username_index(db):
db.commit()
+class Generator_R0(declarative_base()):
+ __tablename__ = "core__generators"
+ id = Column(Integer, primary_key=True)
+ name = Column(Unicode, nullable=False)
+ published = Column(DateTime, nullable=False, default=datetime.datetime.now)
+ updated = Column(DateTime, nullable=False, default=datetime.datetime.now)
+ object_type = Column(Unicode, nullable=False)
+
+class ActivityIntermediator_R0(declarative_base()):
+ __tablename__ = "core__activity_intermediators"
+ id = Column(Integer, primary_key=True)
+ type = Column(Unicode, nullable=False)
+
+class Activity_R0(declarative_base()):
+ __tablename__ = "core__activities"
+ id = Column(Integer, primary_key=True)
+ actor = Column(Integer, ForeignKey(User.id), nullable=False)
+ published = Column(DateTime, nullable=False, default=datetime.datetime.now)
+ updated = Column(DateTime, nullable=False, default=datetime.datetime.now)
+ verb = Column(Unicode, nullable=False)
+ content = Column(Unicode, nullable=True)
+ title = Column(Unicode, nullable=True)
+ generator = Column(Integer, ForeignKey(Generator_R0.id), nullable=True)
+ object = Column(Integer,
+ ForeignKey(ActivityIntermediator_R0.id),
+ nullable=False)
+ target = Column(Integer,
+ ForeignKey(ActivityIntermediator_R0.id),
+ nullable=True)
+
+@RegisterMigration(24, MIGRATIONS)
+def activity_migration(db):
+ """
+ Creates everything to create activities in GMG
+ - Adds Activity, ActivityIntermediator and Generator table
+ - Creates GMG service generator for activities produced by the server
+ - Adds the activity_as_object and activity_as_target to objects/targets
+ - Retroactively adds activities for what we can acurately work out
+ """
+ # Set constants we'll use later
+ FOREIGN_KEY = "core__activity_intermediators.id"
+ ACTIVITY_COLUMN = "activity"
+
+ # Create the new tables.
+ ActivityIntermediator_R0.__table__.create(db.bind)
+ Generator_R0.__table__.create(db.bind)
+ Activity_R0.__table__.create(db.bind)
+ db.commit()
+
+ # Initiate the tables we want to use later
+ metadata = MetaData(bind=db.bind)
+ user_table = inspect_table(metadata, "core__users")
+ activity_table = inspect_table(metadata, "core__activities")
+ generator_table = inspect_table(metadata, "core__generators")
+ collection_table = inspect_table(metadata, "core__collections")
+ media_entry_table = inspect_table(metadata, "core__media_entries")
+ media_comments_table = inspect_table(metadata, "core__media_comments")
+ ai_table = inspect_table(metadata, "core__activity_intermediators")
+
+
+ # Create the foundations for Generator
+ db.execute(generator_table.insert().values(
+ name="GNU Mediagoblin",
+ object_type="service",
+ published=datetime.datetime.now(),
+ updated=datetime.datetime.now()
+ ))
+ db.commit()
+
+ # Get the ID of that generator
+ gmg_generator = db.execute(generator_table.select(
+ generator_table.c.name==u"GNU Mediagoblin")).first()
+
+
+ # Now we want to modify the tables which MAY have an activity at some point
+ media_col = Column(ACTIVITY_COLUMN, Integer, ForeignKey(FOREIGN_KEY))
+ media_col.create(media_entry_table)
+
+ user_col = Column(ACTIVITY_COLUMN, Integer, ForeignKey(FOREIGN_KEY))
+ user_col.create(user_table)
+
+ comments_col = Column(ACTIVITY_COLUMN, Integer, ForeignKey(FOREIGN_KEY))
+ comments_col.create(media_comments_table)
+
+ collection_col = Column(ACTIVITY_COLUMN, Integer, ForeignKey(FOREIGN_KEY))
+ collection_col.create(collection_table)
+ db.commit()
+
+
+ # Now we want to retroactively add what activities we can
+ # first we'll add activities when people uploaded media.
+ # these can't have content as it's not fesible to get the
+ # correct content strings.
+ for media in db.execute(media_entry_table.select()):
+ # Now we want to create the intermedaitory
+ db_ai = db.execute(ai_table.insert().values(
+ type="media",
+ ))
+ db_ai = db.execute(ai_table.select(
+ ai_table.c.id==db_ai.inserted_primary_key[0]
+ )).first()
+
+ # Add the activity
+ activity = {
+ "verb": "create",
+ "actor": media.uploader,
+ "published": media.created,
+ "updated": media.created,
+ "generator": gmg_generator.id,
+ "object": db_ai.id
+ }
+ db.execute(activity_table.insert().values(**activity))
+
+ # Add the AI to the media.
+ db.execute(media_entry_table.update().values(
+ activity=db_ai.id
+ ).where(media_entry_table.c.id==media.id))
+
+ # Now we want to add all the comments people made
+ for comment in db.execute(media_comments_table.select()):
+ # Get the MediaEntry for the comment
+ media_entry = db.execute(
+ media_entry_table.select(
+ media_entry_table.c.id==comment.media_entry
+ )).first()
+
+ # Create an AI for target
+ db_ai_media = db.execute(ai_table.select(
+ ai_table.c.id==media_entry.activity
+ )).first().id
+
+ db.execute(
+ media_comments_table.update().values(
+ activity=db_ai_media
+ ).where(media_comments_table.c.id==media_entry.id))
+
+ # Now create the AI for the comment
+ db_ai_comment = db.execute(ai_table.insert().values(
+ type="comment"
+ )).inserted_primary_key[0]
+
+ activity = {
+ "verb": "comment",
+ "actor": comment.author,
+ "published": comment.created,
+ "updated": comment.created,
+ "generator": gmg_generator.id,
+ "object": db_ai_comment,
+ "target": db_ai_media,
+ }
+
+ # Now add the comment object
+ db.execute(activity_table.insert().values(**activity))
+
+ # Now add activity to comment
+ db.execute(media_comments_table.update().values(
+ activity=db_ai_comment
+ ).where(media_comments_table.c.id==comment.id))
+
+ # Create 'create' activities for all collections
+ for collection in db.execute(collection_table.select()):
+ # create AI
+ db_ai = db.execute(ai_table.insert().values(
+ type="collection"
+ ))
+ db_ai = db.execute(ai_table.select(
+ ai_table.c.id==db_ai.inserted_primary_key[0]
+ )).first()
+
+ # Now add link the collection to the AI
+ db.execute(collection_table.update().values(
+ activity=db_ai.id
+ ).where(collection_table.c.id==collection.id))
+
+ activity = {
+ "verb": "create",
+ "actor": collection.creator,
+ "published": collection.created,
+ "updated": collection.created,
+ "generator": gmg_generator.id,
+ "object": db_ai.id,
+ }
+
+ db.execute(activity_table.insert().values(**activity))
+
+ # Now add the activity to the collection
+ db.execute(collection_table.update().values(
+ activity=db_ai.id
+ ).where(collection_table.c.id==collection.id))
+
+ db.commit()
+
class Location_V0(declarative_base()):
__tablename__ = "core__locations"
id = Column(Integer, primary_key=True)
@@ -898,7 +1094,7 @@ class Location_V0(declarative_base()):
position = Column(MutationDict.as_mutable(JSONEncoded))
address = Column(MutationDict.as_mutable(JSONEncoded))
-@RegisterMigration(24, MIGRATIONS)
+@RegisterMigration(25, MIGRATIONS)
def add_location_model(db):
""" Add location model """
metadata = MetaData(bind=db.bind)
diff --git a/mediagoblin/db/migrations/README b/mediagoblin/db/migrations/README
new file mode 100644
index 00000000..93d85eff
--- /dev/null
+++ b/mediagoblin/db/migrations/README
@@ -0,0 +1,57 @@
+Migration Guide
+---------------
+
+Alembic comes with a CLI called ``alembic``.
+
+Create a Migration
+^^^^^^^^^^^^^^^^^^
+
+Lets create our first migration::
+
+ $ alembic revision -m "add favourite_band field"
+ Generating
+ /your/gmg/path/mediagoblin/db/migrations/versions/1e3793de36a_add_favourite_band_field.py ... done
+
+By default, migration files have two methods: ``upgrade`` and ``downgrade``.
+Alembic will invoke these methods to apply the migrations to your current
+database.
+
+Now, we need to edit our newly created migration file
+``1e3793de36a_add_favourite_band_field.py`` to add a new column ``favourite_band``
+to ``core__users`` table::
+
+ def upgrade():
+ op.add_column('core__users', sa.Column('favourite_band', sa.Unicode(100)))
+
+
+ def downgrade():
+ op.drop_column('core__users', 'favourite_band')
+
+.. note::
+
+ Alembic can also generate `automatic migrations <http://alembic.readthedocs.org/en/latest/tutorial.html#auto-generating-migrations>`__.
+
+Then we can run ``gmg dbupdate`` to apply the new migration::
+
+ $ gmg dbupdate
+ INFO [alembic.migration] Context impl SQLiteImpl.
+ INFO [alembic.migration] Will assume non-transactional DDL.
+ INFO [alembic.migration] Running upgrade None -> 1e3793de36a, add favourite band field
+
+If you want to revert that migration, simply run::
+
+ $ alembic downgrade -1
+
+.. warning::
+
+ Currently, Alembic cannot do ``DROP COLUMN``, ``ALTER COLUMN`` etc.
+ operations in SQLite. Please see https://bitbucket.org/zzzeek/alembic/issue/21/column-renames-not-supported-on-sqlite
+ for detailed information.
+
+Glossary
+^^^^^^^^
+
+* ``alembic.ini``: The Alembic configuration file. The ``alembic`` CLI will
+ look that file everytime it invaked.
+* ``mediagoblin/db/migrations/versions/``: Alembic will add new migration files
+ to this directory.
diff --git a/mediagoblin/db/migrations/env.py b/mediagoblin/db/migrations/env.py
new file mode 100644
index 00000000..712b6164
--- /dev/null
+++ b/mediagoblin/db/migrations/env.py
@@ -0,0 +1,71 @@
+from __future__ import with_statement
+from alembic import context
+from sqlalchemy import engine_from_config, pool
+from logging.config import fileConfig
+
+# this is the Alembic Config object, which provides
+# access to the values within the .ini file in use.
+config = context.config
+
+# Interpret the config file for Python logging.
+# This line sets up loggers basically.
+fileConfig(config.config_file_name)
+
+# add your model's MetaData object here
+# for 'autogenerate' support
+# from myapp import mymodel
+# target_metadata = mymodel.Base.metadata
+target_metadata = None
+
+# other values from the config, defined by the needs of env.py,
+# can be acquired:
+# my_important_option = config.get_main_option("my_important_option")
+# ... etc.
+
+def run_migrations_offline():
+ """Run migrations in 'offline' mode.
+
+ This configures the context with just a URL
+ and not an Engine, though an Engine is acceptable
+ here as well. By skipping the Engine creation
+ we don't even need a DBAPI to be available.
+
+ Calls to context.execute() here emit the given string to the
+ script output.
+
+ """
+ url = config.get_main_option("sqlalchemy.url")
+ context.configure(url=url, target_metadata=target_metadata)
+
+ with context.begin_transaction():
+ context.run_migrations()
+
+def run_migrations_online():
+ """Run migrations in 'online' mode.
+
+ In this scenario we need to create an Engine
+ and associate a connection with the context.
+
+ """
+ engine = engine_from_config(
+ config.get_section(config.config_ini_section),
+ prefix='sqlalchemy.',
+ poolclass=pool.NullPool)
+
+ connection = engine.connect()
+ context.configure(
+ connection=connection,
+ target_metadata=target_metadata
+ )
+
+ try:
+ with context.begin_transaction():
+ context.run_migrations()
+ finally:
+ connection.close()
+
+if context.is_offline_mode():
+ run_migrations_offline()
+else:
+ run_migrations_online()
+
diff --git a/mediagoblin/db/migrations/script.py.mako b/mediagoblin/db/migrations/script.py.mako
new file mode 100644
index 00000000..95702017
--- /dev/null
+++ b/mediagoblin/db/migrations/script.py.mako
@@ -0,0 +1,22 @@
+"""${message}
+
+Revision ID: ${up_revision}
+Revises: ${down_revision}
+Create Date: ${create_date}
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = ${repr(up_revision)}
+down_revision = ${repr(down_revision)}
+
+from alembic import op
+import sqlalchemy as sa
+${imports if imports else ""}
+
+def upgrade():
+ ${upgrades if upgrades else "pass"}
+
+
+def downgrade():
+ ${downgrades if downgrades else "pass"}
diff --git a/mediagoblin/db/migrations/versions/.gitkeep b/mediagoblin/db/migrations/versions/.gitkeep
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/mediagoblin/db/migrations/versions/.gitkeep
diff --git a/mediagoblin/db/mixin.py b/mediagoblin/db/mixin.py
index 1f2e7ec3..39690cfc 100644
--- a/mediagoblin/db/mixin.py
+++ b/mediagoblin/db/mixin.py
@@ -39,9 +39,12 @@ from mediagoblin.tools import common, licenses
from mediagoblin.tools.pluginapi import hook_handle
from mediagoblin.tools.text import cleaned_markdown_conversion
from mediagoblin.tools.url import slugify
+from mediagoblin.tools.translate import pass_to_ugettext as _
class UserMixin(object):
+ object_type = "person"
+
@property
def bio_html(self):
return cleaned_markdown_conversion(self.bio)
@@ -131,6 +134,11 @@ class MediaEntryMixin(GenerateSlugMixin):
return check_media_slug_used(self.uploader, slug, self.id)
@property
+ def object_type(self):
+ """ Converts media_type to pump-like type - don't use internally """
+ return self.media_type.split(".")[-1]
+
+ @property
def description_html(self):
"""
Rendered version of the description, run through
@@ -208,7 +216,7 @@ class MediaEntryMixin(GenerateSlugMixin):
will return self.thumb_url if original url doesn't exist"""
if u"original" not in self.media_files:
return self.thumb_url
-
+
return mg_globals.app.public_store.file_url(
self.media_files[u"original"]
)
@@ -297,6 +305,8 @@ class MediaEntryMixin(GenerateSlugMixin):
class MediaCommentMixin(object):
+ object_type = "comment"
+
@property
def content_html(self):
"""
@@ -321,6 +331,8 @@ class MediaCommentMixin(object):
class CollectionMixin(GenerateSlugMixin):
+ object_type = "collection"
+
def check_slug_used(self, slug):
# import this here due to a cyclic import issue
# (db.models -> db.mixin -> db.util -> db.models)
@@ -363,3 +375,111 @@ class CollectionItemMixin(object):
Run through Markdown and the HTML cleaner.
"""
return cleaned_markdown_conversion(self.note)
+
+class ActivityMixin(object):
+ object_type = "activity"
+
+ VALID_VERBS = ["add", "author", "create", "delete", "dislike", "favorite",
+ "follow", "like", "post", "share", "unfavorite", "unfollow",
+ "unlike", "unshare", "update", "tag"]
+
+ def get_url(self, request):
+ return request.urlgen(
+ "mediagoblin.federation.activity_view",
+ username=self.get_actor.username,
+ id=self.id,
+ qualified=True
+ )
+
+ def generate_content(self):
+ """ Produces a HTML content for object """
+ # some of these have simple and targetted. If self.target it set
+ # it will pick the targetted. If they DON'T have a targetted version
+ # the information in targetted won't be added to the content.
+ verb_to_content = {
+ "add": {
+ "simple" : _("{username} added {object}"),
+ "targetted": _("{username} added {object} to {target}"),
+ },
+ "author": {"simple": _("{username} authored {object}")},
+ "create": {"simple": _("{username} created {object}")},
+ "delete": {"simple": _("{username} deleted {object}")},
+ "dislike": {"simple": _("{username} disliked {object}")},
+ "favorite": {"simple": _("{username} favorited {object}")},
+ "follow": {"simple": _("{username} followed {object}")},
+ "like": {"simple": _("{username} liked {object}")},
+ "post": {
+ "simple": _("{username} posted {object}"),
+ "targetted": _("{username} posted {object} to {target}"),
+ },
+ "share": {"simple": _("{username} shared {object}")},
+ "unfavorite": {"simple": _("{username} unfavorited {object}")},
+ "unfollow": {"simple": _("{username} stopped following {object}")},
+ "unlike": {"simple": _("{username} unliked {object}")},
+ "unshare": {"simple": _("{username} unshared {object}")},
+ "update": {"simple": _("{username} updated {object}")},
+ "tag": {"simple": _("{username} tagged {object}")},
+ }
+
+ obj = self.get_object
+ target = self.get_target
+ actor = self.get_actor
+ content = verb_to_content.get(self.verb, None)
+
+ if content is None or obj is None:
+ return
+
+ if target is None or "targetted" not in content:
+ self.content = content["simple"].format(
+ username=actor.username,
+ object=obj.object_type
+ )
+ else:
+ self.content = content["targetted"].format(
+ username=actor.username,
+ object=obj.object_type,
+ target=target.object_type,
+ )
+
+ return self.content
+
+ def serialize(self, request):
+ obj = {
+ "id": self.id,
+ "actor": self.get_actor.serialize(request),
+ "verb": self.verb,
+ "published": self.published.isoformat(),
+ "updated": self.updated.isoformat(),
+ "content": self.content,
+ "url": self.get_url(request),
+ "object": self.get_object.serialize(request),
+ "objectType": self.object_type,
+ }
+
+ if self.generator:
+ obj["generator"] = self.get_generator.serialize(request)
+
+ if self.title:
+ obj["title"] = self.title
+
+ target = self.get_target
+ if target is not None:
+ obj["target"] = target.serialize(request)
+
+ return obj
+
+ def unseralize(self, data):
+ """
+ Takes data given and set it on this activity.
+
+ Several pieces of data are not written on because of security
+ reasons. For example changing the author or id of an activity.
+ """
+ if "verb" in data:
+ self.verb = data["verb"]
+
+ if "title" in data:
+ self.title = data["title"]
+
+ if "content" in data:
+ self.content = data["content"]
diff --git a/mediagoblin/db/models.py b/mediagoblin/db/models.py
index 20eccdae..b1bdba88 100644
--- a/mediagoblin/db/models.py
+++ b/mediagoblin/db/models.py
@@ -18,9 +18,10 @@
TODO: indexes on foreignkeys, where useful.
"""
+from __future__ import print_function
+
import logging
import datetime
-import base64
from sqlalchemy import Column, Integer, Unicode, UnicodeText, DateTime, \
Boolean, ForeignKey, UniqueConstraint, PrimaryKeyConstraint, \
@@ -35,16 +36,12 @@ from mediagoblin.db.extratypes import (PathTupleWithSlashes, JSONEncoded,
MutationDict)
from mediagoblin.db.base import Base, DictReadAttrProxy
from mediagoblin.db.mixin import UserMixin, MediaEntryMixin, \
- MediaCommentMixin, CollectionMixin, CollectionItemMixin
+ MediaCommentMixin, CollectionMixin, CollectionItemMixin, \
+ ActivityMixin
from mediagoblin.tools.files import delete_media_files
from mediagoblin.tools.common import import_component
-# It's actually kind of annoying how sqlalchemy-migrate does this, if
-# I understand it right, but whatever. Anyway, don't remove this :P
-#
-# We could do migration calls more manually instead of relying on
-# this import-based meddling...
-from migrate import changeset
+import six
_log = logging.getLogger(__name__)
@@ -122,7 +119,6 @@ class Location(Base):
if "country" in data["address"]:
self.address["country"] = data["address"]["country"]
-
class User(Base, UserMixin):
"""
TODO: We should consider moving some rarely used fields
@@ -151,6 +147,8 @@ class User(Base, UserMixin):
location = Column(Integer, ForeignKey("core__locations.id"))
get_location = relationship("Location", lazy="joined")
+ activity = Column(Integer, ForeignKey("core__activity_intermediators.id"))
+
## TODO
# plugin data would be in a separate model
@@ -218,7 +216,7 @@ class User(Base, UserMixin):
"id": "acct:{0}@{1}".format(self.username, request.host),
"preferredUsername": self.username,
"displayName": "{0}@{1}".format(self.username, request.host),
- "objectType": "person",
+ "objectType": self.object_type,
"pump_io": {
"shared": False,
"followed": False,
@@ -306,6 +304,8 @@ class RequestToken(Base):
created = Column(DateTime, nullable=False, default=datetime.datetime.now)
updated = Column(DateTime, nullable=False, default=datetime.datetime.now)
+ get_client = relationship(Client)
+
class AccessToken(Base):
"""
Model for representing the access tokens
@@ -319,6 +319,8 @@ class AccessToken(Base):
created = Column(DateTime, nullable=False, default=datetime.datetime.now)
updated = Column(DateTime, nullable=False, default=datetime.datetime.now)
+ get_requesttoken = relationship(RequestToken)
+
class NonceTimestamp(Base):
"""
@@ -396,6 +398,8 @@ class MediaEntry(Base, MediaEntryMixin):
media_metadata = Column(MutationDict.as_mutable(JSONEncoded),
default=MutationDict())
+ activity = Column(Integer, ForeignKey("core__activity_intermediators.id"))
+
## TODO
# fail_error
@@ -431,7 +435,7 @@ class MediaEntry(Base, MediaEntryMixin):
return the value of the key.
"""
media_file = MediaFile.query.filter_by(media_entry=self.id,
- name=unicode(file_key)).first()
+ name=six.text_type(file_key)).first()
if media_file:
if metadata_key:
@@ -444,11 +448,11 @@ class MediaEntry(Base, MediaEntryMixin):
Update the file_metadata of a MediaFile.
"""
media_file = MediaFile.query.filter_by(media_entry=self.id,
- name=unicode(file_key)).first()
+ name=six.text_type(file_key)).first()
file_metadata = media_file.file_metadata or {}
- for key, value in kwargs.iteritems():
+ for key, value in six.iteritems(kwargs):
file_metadata[key] = value
media_file.file_metadata = file_metadata
@@ -473,7 +477,7 @@ class MediaEntry(Base, MediaEntryMixin):
media_data.get_media_entry = self
else:
# Update old media data
- for field, value in kwargs.iteritems():
+ for field, value in six.iteritems(kwargs):
setattr(media_data, field, value)
@memoized_property
@@ -502,7 +506,7 @@ class MediaEntry(Base, MediaEntryMixin):
# Delete all related files/attachments
try:
delete_media_files(self)
- except OSError, error:
+ except OSError as error:
# Returns list of files we failed to delete
_log.error('No such files from the user "{1}" to delete: '
'{0}'.format(str(error), self.get_uploader))
@@ -517,18 +521,13 @@ class MediaEntry(Base, MediaEntryMixin):
# pass through commit=False/True in kwargs
super(MediaEntry, self).delete(**kwargs)
- @property
- def objectType(self):
- """ Converts media_type to pump-like type - don't use internally """
- return self.media_type.split(".")[-1]
-
def serialize(self, request, show_comments=True):
""" Unserialize MediaEntry to object """
author = self.get_uploader
context = {
"id": self.id,
"author": author.serialize(request),
- "objectType": self.objectType,
+ "objectType": self.object_type,
"url": self.url_for_self(request.urlgen),
"image": {
"url": request.host_url + self.thumb_url[1:],
@@ -545,7 +544,7 @@ class MediaEntry(Base, MediaEntryMixin):
"self": {
"href": request.urlgen(
"mediagoblin.federation.object",
- objectType=self.objectType,
+ object_type=self.object_type,
id=self.id,
qualified=True
),
@@ -567,14 +566,15 @@ class MediaEntry(Base, MediaEntryMixin):
context["location"] = self.get_location.serialize(request)
if show_comments:
- comments = [comment.serialize(request) for comment in self.get_comments()]
+ comments = [
+ comment.serialize(request) for comment in self.get_comments()]
total = len(comments)
context["replies"] = {
"totalItems": total,
"items": comments,
"url": request.urlgen(
"mediagoblin.federation.object.comments",
- objectType=self.objectType,
+ object_type=self.object_type,
id=self.id,
qualified=True
),
@@ -745,13 +745,16 @@ class MediaComment(Base, MediaCommentMixin):
lazy="dynamic",
cascade="all, delete-orphan"))
+
+ activity = Column(Integer, ForeignKey("core__activity_intermediators.id"))
+
def serialize(self, request):
""" Unserialize to python dictionary for API """
media = MediaEntry.query.filter_by(id=self.media_entry).first()
author = self.get_author
context = {
"id": self.id,
- "objectType": "comment",
+ "objectType": self.object_type,
"content": self.content,
"inReplyTo": media.serialize(request, show_comments=False),
"author": author.serialize(request)
@@ -819,6 +822,8 @@ class Collection(Base, CollectionMixin):
backref=backref("collections",
cascade="all, delete-orphan"))
+ activity = Column(Integer, ForeignKey("core__activity_intermediators.id"))
+
__table_args__ = (
UniqueConstraint('creator', 'slug'),
{})
@@ -831,6 +836,14 @@ class Collection(Base, CollectionMixin):
return CollectionItem.query.filter_by(
collection=self.id).order_by(order_col)
+ def __repr__(self):
+ safe_title = self.title.encode('ascii', 'replace')
+ return '<{classname} #{id}: {title} by {creator}>'.format(
+ id=self.id,
+ classname=self.__class__.__name__,
+ creator=self.creator,
+ title=safe_title)
+
class CollectionItem(Base, CollectionItemMixin):
__tablename__ = "core__collection_items"
@@ -860,6 +873,13 @@ class CollectionItem(Base, CollectionItemMixin):
"""A dict like view on this object"""
return DictReadAttrProxy(self)
+ def __repr__(self):
+ return '<{classname} #{id}: Entry {entry} in {collection}>'.format(
+ id=self.id,
+ classname=self.__class__.__name__,
+ collection=self.collection,
+ entry=self.media_entry)
+
class ProcessingMetaData(Base):
__tablename__ = 'core__processing_metadata'
@@ -1157,6 +1177,174 @@ class PrivilegeUserAssociation(Base):
ForeignKey(Privilege.id),
primary_key=True)
+class Generator(Base):
+ """ Information about what created an activity """
+ __tablename__ = "core__generators"
+
+ id = Column(Integer, primary_key=True)
+ name = Column(Unicode, nullable=False)
+ published = Column(DateTime, default=datetime.datetime.now)
+ updated = Column(DateTime, default=datetime.datetime.now)
+ object_type = Column(Unicode, nullable=False)
+
+ def __repr__(self):
+ return "<{klass} {name}>".format(
+ klass=self.__class__.__name__,
+ name=self.name
+ )
+
+ def serialize(self, request):
+ return {
+ "id": self.id,
+ "displayName": self.name,
+ "published": self.published.isoformat(),
+ "updated": self.updated.isoformat(),
+ "objectType": self.object_type,
+ }
+
+ def unserialize(self, data):
+ if "displayName" in data:
+ self.name = data["displayName"]
+
+
+class ActivityIntermediator(Base):
+ """
+ This is used so that objects/targets can have a foreign key back to this
+ object and activities can a foreign key to this object. This objects to be
+ used multiple times for the activity object or target and also allows for
+ different types of objects to be used as an Activity.
+ """
+ __tablename__ = "core__activity_intermediators"
+
+ id = Column(Integer, primary_key=True)
+ type = Column(Unicode, nullable=False)
+
+ TYPES = {
+ "user": User,
+ "media": MediaEntry,
+ "comment": MediaComment,
+ "collection": Collection,
+ }
+
+ def _find_model(self, obj):
+ """ Finds the model for a given object """
+ for key, model in self.TYPES.items():
+ if isinstance(obj, model):
+ return key, model
+
+ return None, None
+
+ def set(self, obj):
+ """ This sets itself as the activity """
+ key, model = self._find_model(obj)
+ if key is None:
+ raise ValueError("Invalid type of object given")
+
+ # We need to save so that self.id is populated
+ self.type = key
+ self.save()
+
+ # First set self as activity
+ obj.activity = self.id
+ obj.save()
+
+ def get(self):
+ """ Finds the object for an activity """
+ if self.type is None:
+ return None
+
+ model = self.TYPES[self.type]
+ return model.query.filter_by(activity=self.id).first()
+
+ def save(self, *args, **kwargs):
+ if self.type not in self.TYPES.keys():
+ raise ValueError("Invalid type set")
+ Base.save(self, *args, **kwargs)
+
+class Activity(Base, ActivityMixin):
+ """
+ This holds all the metadata about an activity such as uploading an image,
+ posting a comment, etc.
+ """
+ __tablename__ = "core__activities"
+
+ id = Column(Integer, primary_key=True)
+ actor = Column(Integer,
+ ForeignKey("core__users.id"),
+ nullable=False)
+ published = Column(DateTime, nullable=False, default=datetime.datetime.now)
+ updated = Column(DateTime, nullable=False, default=datetime.datetime.now)
+ verb = Column(Unicode, nullable=False)
+ content = Column(Unicode, nullable=True)
+ title = Column(Unicode, nullable=True)
+ generator = Column(Integer,
+ ForeignKey("core__generators.id"),
+ nullable=True)
+ object = Column(Integer,
+ ForeignKey("core__activity_intermediators.id"),
+ nullable=False)
+ target = Column(Integer,
+ ForeignKey("core__activity_intermediators.id"),
+ nullable=True)
+
+ get_actor = relationship(User,
+ foreign_keys="Activity.actor", post_update=True)
+ get_generator = relationship(Generator)
+
+ def __repr__(self):
+ if self.content is None:
+ return "<{klass} verb:{verb}>".format(
+ klass=self.__class__.__name__,
+ verb=self.verb
+ )
+ else:
+ return "<{klass} {content}>".format(
+ klass=self.__class__.__name__,
+ content=self.content
+ )
+
+ @property
+ def get_object(self):
+ if self.object is None:
+ return None
+
+ ai = ActivityIntermediator.query.filter_by(id=self.object).first()
+ return ai.get()
+
+ def set_object(self, obj):
+ self.object = self._set_model(obj)
+
+ @property
+ def get_target(self):
+ if self.target is None:
+ return None
+
+ ai = ActivityIntermediator.query.filter_by(id=self.target).first()
+ return ai.get()
+
+ def set_target(self, obj):
+ self.target = self._set_model(obj)
+
+ def _set_model(self, obj):
+ # Firstly can we set obj
+ if not hasattr(obj, "activity"):
+ raise ValueError(
+ "{0!r} is unable to be set on activity".format(obj))
+
+ if obj.activity is None:
+ # We need to create a new AI
+ ai = ActivityIntermediator()
+ ai.set(obj)
+ ai.save()
+ return ai.id
+
+ # Okay we should have an existing AI
+ return ActivityIntermediator.query.filter_by(id=obj.activity).first().id
+
+ def save(self, set_updated=True, *args, **kwargs):
+ if set_updated:
+ self.updated = datetime.datetime.now()
+ super(Activity, self).save(*args, **kwargs)
with_polymorphic(
Notification,
@@ -1167,8 +1355,9 @@ MODELS = [
MediaFile, FileKeynames, MediaAttachmentFile, ProcessingMetaData,
Notification, CommentNotification, ProcessingNotification, Client,
CommentSubscription, ReportBase, CommentReport, MediaReport, UserBan,
- Privilege, PrivilegeUserAssociation,
+ Privilege, PrivilegeUserAssociation,
RequestToken, AccessToken, NonceTimestamp,
+ Activity, ActivityIntermediator, Generator,
Location]
"""
@@ -1220,7 +1409,7 @@ def show_table_init(engine_uri):
if __name__ == '__main__':
from sys import argv
- print repr(argv)
+ print(repr(argv))
if len(argv) == 2:
uri = argv[1]
else:
diff --git a/mediagoblin/db/open.py b/mediagoblin/db/open.py
index 4ff0945f..34f0bffa 100644
--- a/mediagoblin/db/open.py
+++ b/mediagoblin/db/open.py
@@ -18,6 +18,8 @@
from sqlalchemy import create_engine, event
import logging
+import six
+
from mediagoblin.db.base import Base, Session
from mediagoblin import mg_globals
@@ -28,7 +30,7 @@ class DatabaseMaster(object):
def __init__(self, engine):
self.engine = engine
- for k, v in Base._decl_class_registry.iteritems():
+ for k, v in six.iteritems(Base._decl_class_registry):
setattr(self, k, v)
def commit(self):
diff --git a/mediagoblin/decorators.py b/mediagoblin/decorators.py
index 5bf60048..f3be679d 100644
--- a/mediagoblin/decorators.py
+++ b/mediagoblin/decorators.py
@@ -16,10 +16,11 @@
from functools import wraps
-from urlparse import urljoin
from werkzeug.exceptions import Forbidden, NotFound
from oauthlib.oauth1 import ResourceEndpoint
+from six.moves.urllib.parse import urljoin
+
from mediagoblin import mg_globals as mgg
from mediagoblin import messages
from mediagoblin.db.models import MediaEntry, User, MediaComment, AccessToken
diff --git a/mediagoblin/edit/routing.py b/mediagoblin/edit/routing.py
index a2d03d26..b349975d 100644
--- a/mediagoblin/edit/routing.py
+++ b/mediagoblin/edit/routing.py
@@ -28,3 +28,5 @@ add_route('mediagoblin.edit.verify_email', '/edit/verify_email/',
'mediagoblin.edit.views:verify_email')
add_route('mediagoblin.edit.email', '/edit/email/',
'mediagoblin.edit.views:change_email')
+add_route('mediagoblin.edit.deauthorize_applications', '/edit/deauthorize/',
+ 'mediagoblin.edit.views:deauthorize_applications')
diff --git a/mediagoblin/edit/views.py b/mediagoblin/edit/views.py
index 17c83d46..30a32a7e 100644
--- a/mediagoblin/edit/views.py
+++ b/mediagoblin/edit/views.py
@@ -14,6 +14,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import six
+
from datetime import datetime
from itsdangerous import BadSignature
@@ -45,7 +47,7 @@ from mediagoblin.tools.text import (
convert_to_tag_list_of_dicts, media_tags_as_string)
from mediagoblin.tools.url import slugify
from mediagoblin.db.util import check_media_slug_used, check_collection_slug_used
-from mediagoblin.db.models import User, Location
+from mediagoblin.db.models import User, Client, AccessToken, Location
import mimetypes
@@ -82,7 +84,7 @@ def edit_media(request, media):
media.tags = convert_to_tag_list_of_dicts(
form.tags.data)
- media.license = unicode(form.license.data) or None
+ media.license = six.text_type(form.license.data) or None
media.slug = slug
media.save()
@@ -140,7 +142,7 @@ def edit_attachments(request, media):
attachment_public_filepath \
= mg_globals.public_store.get_unique_filepath(
- ['media_entries', unicode(media.id), 'attachment',
+ ['media_entries', six.text_type(media.id), 'attachment',
public_filename])
attachment_public_file = mg_globals.public_store.get_file(
@@ -212,8 +214,8 @@ def edit_profile(request, url_user=None):
location=location)
if request.method == 'POST' and form.validate():
- user.url = unicode(form.url.data)
- user.bio = unicode(form.bio.data)
+ user.url = six.text_type(form.url.data)
+ user.bio = six.text_type(form.bio.data)
# Save location
if form.location.data and user.location is None:
@@ -271,6 +273,34 @@ def edit_account(request):
{'user': user,
'form': form})
+@require_active_login
+def deauthorize_applications(request):
+ """ Deauthroize OAuth applications """
+ if request.method == 'POST' and "application" in request.form:
+ token = request.form["application"]
+ access_token = AccessToken.query.filter_by(token=token).first()
+ if access_token is None:
+ messages.add_message(
+ request,
+ messages.ERROR,
+ _("Unknown application, not able to deauthorize")
+ )
+ else:
+ access_token.delete()
+ messages.add_message(
+ request,
+ messages.SUCCESS,
+ _("Application has been deauthorized")
+ )
+
+ access_tokens = AccessToken.query.filter_by(user=request.user.id)
+ applications = [(a.get_requesttoken, a) for a in access_tokens]
+
+ return render_to_response(
+ request,
+ 'mediagoblin/edit/deauthorize_applications.html',
+ {'applications': applications}
+ )
@require_active_login
def delete_account(request):
@@ -336,9 +366,9 @@ def edit_collection(request, collection):
form.slug.errors.append(
_(u'A collection with that slug already exists for this user.'))
else:
- collection.title = unicode(form.title.data)
- collection.description = unicode(form.description.data)
- collection.slug = unicode(form.slug.data)
+ collection.title = six.text_type(form.title.data)
+ collection.description = six.text_type(form.description.data)
+ collection.slug = six.text_type(form.slug.data)
collection.save()
@@ -468,7 +498,7 @@ def edit_metadata(request, media):
return redirect_obj(request, media)
if len(form.media_metadata) == 0:
- for identifier, value in media.media_metadata.iteritems():
+ for identifier, value in six.iteritems(media.media_metadata):
if identifier == "@context": continue
form.media_metadata.append_entry({
'identifier':identifier,
diff --git a/mediagoblin/federation/routing.py b/mediagoblin/federation/routing.py
index c1c5a264..2f8ed799 100644
--- a/mediagoblin/federation/routing.py
+++ b/mediagoblin/federation/routing.py
@@ -51,12 +51,12 @@ add_route(
# object endpoints
add_route(
"mediagoblin.federation.object",
- "/api/<string:objectType>/<string:id>",
+ "/api/<string:object_type>/<string:id>",
"mediagoblin.federation.views:object_endpoint"
)
add_route(
"mediagoblin.federation.object.comments",
- "/api/<string:objectType>/<string:id>/comments",
+ "/api/<string:object_type>/<string:id>/comments",
"mediagoblin.federation.views:object_comments"
)
@@ -73,7 +73,19 @@ add_route(
)
add_route(
+ "mediagoblin.webfinger.well-known.webfinger",
+ "/.well-known/webfinger",
+ "mediagoblin.federation.views:lrdd_lookup"
+)
+
+add_route(
"mediagoblin.webfinger.whoami",
"/api/whoami",
"mediagoblin.federation.views:whoami"
)
+
+add_route(
+ "mediagoblin.federation.activity_view",
+ "/<string:username>/activity/<string:id>",
+ "mediagoblin.federation.views:activity_view"
+) \ No newline at end of file
diff --git a/mediagoblin/federation/views.py b/mediagoblin/federation/views.py
index 5b27144d..4c0593fc 100644
--- a/mediagoblin/federation/views.py
+++ b/mediagoblin/federation/views.py
@@ -20,10 +20,11 @@ import mimetypes
from werkzeug.datastructures import FileStorage
-from mediagoblin.decorators import oauth_required
+from mediagoblin.decorators import oauth_required, require_active_login
from mediagoblin.federation.decorators import user_has_privilege
-from mediagoblin.db.models import User, MediaEntry, MediaComment
-from mediagoblin.tools.response import redirect, json_response, json_error
+from mediagoblin.db.models import User, MediaEntry, MediaComment, Activity
+from mediagoblin.tools.response import redirect, json_response, json_error, \
+ render_404, render_to_response
from mediagoblin.meddleware.csrf import csrf_exempt
from mediagoblin.submit.lib import new_upload_entry, api_upload_request, \
api_add_to_feed
@@ -139,7 +140,7 @@ def feed_endpoint(request):
return json_error("No such 'user' with id '{0}'".format(username), 404)
if request.data:
- data = json.loads(request.data)
+ data = json.loads(request.data.decode())
else:
data = {"verb": None, "object": {}}
@@ -354,21 +355,8 @@ def feed_endpoint(request):
"items": [],
}
-
- # Look up all the media to put in the feed (this will be changed
- # when we get real feeds/inboxes/outboxes/activites)
- for media in MediaEntry.query.all():
- item = {
- "verb": "post",
- "object": media.serialize(request),
- "actor": media.get_uploader.serialize(request),
- "content": "{0} posted a picture".format(request.user.username),
- "id": media.id,
- }
- item["updated"] = item["object"]["updated"]
- item["published"] = item["object"]["published"]
- item["url"] = item["object"]["url"]
- feed["items"].append(item)
+ for activity in Activity.query.filter_by(actor=request.user.id):
+ feed["items"].append(activity.serialize(request))
feed["totalItems"] = len(feed["items"])
return json_response(feed)
@@ -376,7 +364,7 @@ def feed_endpoint(request):
@oauth_required
def object_endpoint(request):
""" Lookup for a object type """
- object_type = request.matchdict["objectType"]
+ object_type = request.matchdict["object_type"]
try:
object_id = int(request.matchdict["id"])
except ValueError:
@@ -408,17 +396,17 @@ def object_comments(request):
media = MediaEntry.query.filter_by(id=request.matchdict["id"]).first()
if media is None:
return json_error("Can't find '{0}' with ID '{1}'".format(
- request.matchdict["objectType"],
+ request.matchdict["object_type"],
request.matchdict["id"]
), 404)
- comments = response.serialize(request)
+ comments = media.serialize(request)
comments = comments.get("replies", {
"totalItems": 0,
"items": [],
"url": request.urlgen(
"mediagoblin.federation.object.comments",
- objectType=media.objectType,
+ object_type=media.object_type,
id=media.id,
qualified=True
)
@@ -432,42 +420,129 @@ def object_comments(request):
return json_response(comments)
##
-# Well known
+# RFC6415 - Web Host Metadata
##
def host_meta(request):
- """ /.well-known/host-meta - provide URLs to resources """
- links = []
+ """
+ This provides the host-meta URL information that is outlined
+ in RFC6415. By default this should provide XRD+XML however
+ if the client accepts JSON we will provide that over XRD+XML.
+ The 'Accept' header is used to decude this.
- links.append({
- "ref": "registration_endpoint",
- "href": request.urlgen(
- "mediagoblin.oauth.client_register",
- qualified=True
- ),
- })
- links.append({
- "ref": "http://apinamespace.org/oauth/request_token",
- "href": request.urlgen(
- "mediagoblin.oauth.request_token",
- qualified=True
- ),
- })
- links.append({
- "ref": "http://apinamespace.org/oauth/authorize",
- "href": request.urlgen(
- "mediagoblin.oauth.authorize",
- qualified=True
- ),
- })
- links.append({
- "ref": "http://apinamespace.org/oauth/access_token",
- "href": request.urlgen(
- "mediagoblin.oauth.access_token",
- qualified=True
- ),
- })
+ A client should use this endpoint to determine what URLs to
+ use for OAuth endpoints.
+ """
+
+ links = [
+ {
+ "rel": "lrdd",
+ "type": "application/json",
+ "href": request.urlgen(
+ "mediagoblin.webfinger.well-known.webfinger",
+ qualified=True
+ )
+ },
+ {
+ "rel": "registration_endpoint",
+ "href": request.urlgen(
+ "mediagoblin.oauth.client_register",
+ qualified=True
+ ),
+ },
+ {
+ "rel": "http://apinamespace.org/oauth/request_token",
+ "href": request.urlgen(
+ "mediagoblin.oauth.request_token",
+ qualified=True
+ ),
+ },
+ {
+ "rel": "http://apinamespace.org/oauth/authorize",
+ "href": request.urlgen(
+ "mediagoblin.oauth.authorize",
+ qualified=True
+ ),
+ },
+ {
+ "rel": "http://apinamespace.org/oauth/access_token",
+ "href": request.urlgen(
+ "mediagoblin.oauth.access_token",
+ qualified=True
+ ),
+ },
+ {
+ "rel": "http://apinamespace.org/activitypub/whoami",
+ "href": request.urlgen(
+ "mediagoblin.webfinger.whoami",
+ qualified=True
+ ),
+ },
+ ]
+
+ if "application/json" in request.accept_mimetypes:
+ return json_response({"links": links})
+
+ # provide XML+XRD
+ return render_to_response(
+ request,
+ "mediagoblin/federation/host-meta.xml",
+ {"links": links},
+ mimetype="application/xrd+xml"
+ )
+
+def lrdd_lookup(request):
+ """
+ This is the lrdd endpoint which can lookup a user (or
+ other things such as activities). This is as specified by
+ RFC6415.
+
+ The cleint must provide a 'resource' as a GET parameter which
+ should be the query to be looked up.
+ """
+
+ if "resource" not in request.args:
+ return json_error("No resource parameter", status=400)
+
+ resource = request.args["resource"]
+
+ if "@" in resource:
+ # Lets pull out the username
+ resource = resource[5:] if resource.startswith("acct:") else resource
+ username, host = resource.split("@", 1)
+
+ # Now lookup the user
+ user = User.query.filter_by(username=username).first()
+
+ if user is None:
+ return json_error(
+ "Can't find 'user' with username '{0}'".format(username))
+
+ return json_response([
+ {
+ "rel": "http://webfinger.net/rel/profile-page",
+ "href": user.url_for_self(request.urlgen),
+ "type": "text/html"
+ },
+ {
+ "rel": "self",
+ "href": request.urlgen(
+ "mediagoblin.federation.user",
+ username=user.username,
+ qualified=True
+ )
+ },
+ {
+ "rel": "activity-outbox",
+ "href": request.urlgen(
+ "mediagoblin.federation.feed",
+ username=user.username,
+ qualified=True
+ )
+ }
+ ])
+ else:
+ return json_error("Unrecognized resource parameter", status=404)
- return json_response({"links": links})
def whoami(request):
""" /api/whoami - HTTP redirect to API profile """
@@ -481,3 +556,34 @@ def whoami(request):
)
return redirect(request, location=profile)
+
+@require_active_login
+def activity_view(request):
+ """ /<username>/activity/<id> - Display activity
+
+ This should display a HTML presentation of the activity
+ this is NOT an API endpoint.
+ """
+ # Get the user object.
+ username = request.matchdict["username"]
+ user = User.query.filter_by(username=username).first()
+
+ activity_id = request.matchdict["id"]
+
+ if request.user is None:
+ return render_404(request)
+
+ activity = Activity.query.filter_by(
+ id=activity_id,
+ author=user.id
+ ).first()
+ if activity is None:
+ return render_404(request)
+
+ return render_to_response(
+ request,
+ "mediagoblin/federation/activity.html",
+ {"activity": activity}
+ )
+
+
diff --git a/mediagoblin/gmg_commands/__init__.py b/mediagoblin/gmg_commands/__init__.py
index 0cb239a2..22fef91c 100644
--- a/mediagoblin/gmg_commands/__init__.py
+++ b/mediagoblin/gmg_commands/__init__.py
@@ -17,6 +17,8 @@
import argparse
import os
+import six
+
from mediagoblin.tools.common import import_component
@@ -61,6 +63,10 @@ SUBCOMMAND_MAP = {
'setup': 'mediagoblin.gmg_commands.deletemedia:parser_setup',
'func': 'mediagoblin.gmg_commands.deletemedia:deletemedia',
'help': 'Delete media entries'},
+ 'serve': {
+ 'setup': 'mediagoblin.gmg_commands.serve:parser_setup',
+ 'func': 'mediagoblin.gmg_commands.serve:serve',
+ 'help': 'PasteScript replacement'},
'batchaddmedia': {
'setup': 'mediagoblin.gmg_commands.batchaddmedia:parser_setup',
'func': 'mediagoblin.gmg_commands.batchaddmedia:batchaddmedia',
@@ -98,7 +104,7 @@ def main_cli():
"otherwise mediagoblin.ini"))
subparsers = parser.add_subparsers(help='sub-command help')
- for command_name, command_struct in SUBCOMMAND_MAP.iteritems():
+ for command_name, command_struct in six.iteritems(SUBCOMMAND_MAP):
if 'help' in command_struct:
subparser = subparsers.add_parser(
command_name, help=command_struct['help'])
diff --git a/mediagoblin/gmg_commands/addmedia.py b/mediagoblin/gmg_commands/addmedia.py
index c33a8c56..b741b96f 100644
--- a/mediagoblin/gmg_commands/addmedia.py
+++ b/mediagoblin/gmg_commands/addmedia.py
@@ -14,8 +14,12 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from __future__ import print_function
+
import os
+import six
+
from mediagoblin.gmg_commands import util as commands_util
from mediagoblin.submit.lib import (
submit_media, get_upload_file_limits,
@@ -68,14 +72,14 @@ def addmedia(args):
# get the user
user = app.db.User.query.filter_by(username=args.username.lower()).first()
if user is None:
- print "Sorry, no user by username '%s'" % args.username
+ print("Sorry, no user by username '%s'" % args.username)
return
# check for the file, if it exists...
filename = os.path.split(args.filename)[-1]
abs_filename = os.path.abspath(args.filename)
if not os.path.exists(abs_filename):
- print "Can't find a file with filename '%s'" % args.filename
+ print("Can't find a file with filename '%s'" % args.filename)
return
upload_limit, max_file_size = get_upload_file_limits(user)
@@ -85,21 +89,21 @@ def addmedia(args):
if some_string is None:
return None
else:
- return unicode(some_string)
+ return six.text_type(some_string)
try:
submit_media(
mg_app=app,
user=user,
- submitted_file=file(abs_filename, 'r'), filename=filename,
+ submitted_file=open(abs_filename, 'r'), filename=filename,
title=maybe_unicodeify(args.title),
description=maybe_unicodeify(args.description),
license=maybe_unicodeify(args.license),
tags_string=maybe_unicodeify(args.tags) or u"",
upload_limit=upload_limit, max_file_size=max_file_size)
except FileUploadLimit:
- print "This file is larger than the upload limits for this site."
+ print("This file is larger than the upload limits for this site.")
except UserUploadLimit:
- print "This file will put this user past their upload limits."
+ print("This file will put this user past their upload limits.")
except UserPastUploadLimit:
- print "This user is already past their upload limits."
+ print("This user is already past their upload limits.")
diff --git a/mediagoblin/gmg_commands/batchaddmedia.py b/mediagoblin/gmg_commands/batchaddmedia.py
index 4931bda2..4137b55c 100644
--- a/mediagoblin/gmg_commands/batchaddmedia.py
+++ b/mediagoblin/gmg_commands/batchaddmedia.py
@@ -14,10 +14,16 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import os
-import requests, codecs
+from __future__ import print_function
+
+import codecs
import csv
-from urlparse import urlparse
+import os
+
+import requests
+import six
+
+from six.moves.urllib.parse import urlparse
from mediagoblin.gmg_commands import util as commands_util
from mediagoblin.submit.lib import (
@@ -60,8 +66,8 @@ def batchaddmedia(args):
# get the user
user = app.db.User.query.filter_by(username=args.username.lower()).first()
if user is None:
- print _(u"Sorry, no user by username '{username}' exists".format(
- username=args.username))
+ print(_(u"Sorry, no user by username '{username}' exists".format(
+ username=args.username)))
return
upload_limit, max_file_size = get_upload_file_limits(user)
@@ -73,7 +79,7 @@ def batchaddmedia(args):
else:
error = _(u'File at {path} not found, use -h flag for help'.format(
path=args.metadata_path))
- print error
+ print(error)
return
abs_metadata_filename = os.path.abspath(metadata_path)
@@ -85,7 +91,7 @@ def batchaddmedia(args):
if some_string is None:
return None
else:
- return unicode(some_string)
+ return six.text_type(some_string)
with codecs.open(
abs_metadata_filename, 'r', encoding='utf-8') as all_metadata:
@@ -110,13 +116,13 @@ def batchaddmedia(args):
license = file_metadata.get('license')
try:
json_ld_metadata = compact_and_validate(file_metadata)
- except ValidationError, exc:
+ except ValidationError as exc:
error = _(u"""Error with media '{media_id}' value '{error_path}': {error_msg}
Metadata was not uploaded.""".format(
media_id=media_id,
error_path=exc.path[0],
error_msg=exc.message))
- print error
+ print(error)
continue
url = urlparse(original_location)
@@ -136,9 +142,9 @@ Metadata was not uploaded.""".format(
try:
media_file = file(file_abs_path, 'r')
except IOError:
- print _(u"""\
+ print(_(u"""\
FAIL: Local file {filename} could not be accessed.
-{filename} will not be uploaded.""".format(filename=filename))
+{filename} will not be uploaded.""".format(filename=filename)))
continue
try:
submit_media(
@@ -152,22 +158,22 @@ FAIL: Local file {filename} could not be accessed.
metadata=json_ld_metadata,
tags_string=u"",
upload_limit=upload_limit, max_file_size=max_file_size)
- print _(u"""Successfully submitted {filename}!
+ print(_(u"""Successfully submitted {filename}!
Be sure to look at the Media Processing Panel on your website to be sure it
-uploaded successfully.""".format(filename=filename))
+uploaded successfully.""".format(filename=filename)))
files_uploaded += 1
except FileUploadLimit:
- print _(
-u"FAIL: This file is larger than the upload limits for this site.")
+ print(_(
+u"FAIL: This file is larger than the upload limits for this site."))
except UserUploadLimit:
- print _(
-"FAIL: This file will put this user past their upload limits.")
+ print(_(
+"FAIL: This file will put this user past their upload limits."))
except UserPastUploadLimit:
- print _("FAIL: This user is already past their upload limits.")
- print _(
+ print(_("FAIL: This user is already past their upload limits."))
+ print(_(
"{files_uploaded} out of {files_attempted} files successfully submitted".format(
files_uploaded=files_uploaded,
- files_attempted=files_attempted))
+ files_attempted=files_attempted)))
def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs):
diff --git a/mediagoblin/gmg_commands/dbupdate.py b/mediagoblin/gmg_commands/dbupdate.py
index 27283a20..31827cd0 100644
--- a/mediagoblin/gmg_commands/dbupdate.py
+++ b/mediagoblin/gmg_commands/dbupdate.py
@@ -16,10 +16,11 @@
import logging
+import six
from sqlalchemy.orm import sessionmaker
from mediagoblin.db.open import setup_connection_and_db_from_config
-from mediagoblin.db.migration_tools import MigrationManager
+from mediagoblin.db.migration_tools import MigrationManager, AlembicMigrationManager
from mediagoblin.init import setup_global_and_app_config
from mediagoblin.tools.common import import_component
@@ -106,6 +107,13 @@ forgotten to add it? ({1})'.format(plugin, exc))
return managed_dbdata
+def run_alembic_migrations(db, app_config, global_config):
+ """Initializes a database and runs all Alembic migrations."""
+ Session = sessionmaker(bind=db.engine)
+ manager = AlembicMigrationManager(Session())
+ manager.init_or_migrate()
+
+
def run_dbupdate(app_config, global_config):
"""
Initialize or migrate the database as specified by the config file.
@@ -116,9 +124,14 @@ def run_dbupdate(app_config, global_config):
# Set up the database
db = setup_connection_and_db_from_config(app_config, migrations=True)
- #Run the migrations
+ # Run the migrations
run_all_migrations(db, app_config, global_config)
+ # TODO: Make this happen regardless of python 2 or 3 once ensured
+ # to be "safe"!
+ if six.PY3:
+ run_alembic_migrations(db, app_config, global_config)
+
def run_all_migrations(db, app_config, global_config):
"""
@@ -131,7 +144,7 @@ def run_all_migrations(db, app_config, global_config):
"""
# Gather information from all media managers / projects
dbdatas = gather_database_data(
- global_config.get('plugins', {}).keys())
+ list(global_config.get('plugins', {}).keys()))
Session = sessionmaker(bind=db.engine)
diff --git a/mediagoblin/gmg_commands/deletemedia.py b/mediagoblin/gmg_commands/deletemedia.py
index ab5a81f6..bf50abde 100644
--- a/mediagoblin/gmg_commands/deletemedia.py
+++ b/mediagoblin/gmg_commands/deletemedia.py
@@ -14,6 +14,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from __future__ import print_function
import sys
from mediagoblin.gmg_commands import util as commands_util
@@ -29,7 +30,7 @@ def deletemedia(args):
media_ids = set([int(mid) for mid in args.media_ids.split(',') if mid.isdigit()])
if not media_ids:
- print 'Can\'t find any valid media ID(s).'
+ print('Can\'t find any valid media ID(s).')
sys.exit(1)
found_medias = set()
filter_ids = app.db.MediaEntry.id.in_(media_ids)
@@ -37,8 +38,8 @@ def deletemedia(args):
for media in medias:
found_medias.add(media.id)
media.delete()
- print 'Media ID %d has been deleted.' % media.id
+ print('Media ID %d has been deleted.' % media.id)
for media in media_ids - found_medias:
- print 'Can\'t find a media with ID %d.' % media
- print 'Done.'
+ print('Can\'t find a media with ID %d.' % media)
+ print('Done.')
sys.exit(0)
diff --git a/mediagoblin/gmg_commands/reprocess.py b/mediagoblin/gmg_commands/reprocess.py
index e2f19ea3..85cae6df 100644
--- a/mediagoblin/gmg_commands/reprocess.py
+++ b/mediagoblin/gmg_commands/reprocess.py
@@ -13,6 +13,9 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import print_function
+
import argparse
import os
@@ -143,7 +146,7 @@ def available(args):
manager = get_processing_manager_for_type(media_type)
except ProcessingManagerDoesNotExist:
entry = MediaEntry.query.filter_by(id=args.id_or_type).first()
- print 'No such processing manager for {0}'.format(entry.media_type)
+ print('No such processing manager for {0}'.format(entry.media_type))
if args.state:
processors = manager.list_all_processors_by_state(args.state)
@@ -152,25 +155,25 @@ def available(args):
else:
processors = manager.list_eligible_processors(media_entry)
- print "Available processors:"
- print "====================="
- print ""
+ print("Available processors:")
+ print("=====================")
+ print("")
if args.action_help:
for processor in processors:
- print processor.name
- print "-" * len(processor.name)
+ print(processor.name)
+ print("-" * len(processor.name))
parser = processor.generate_parser()
parser.print_help()
- print ""
+ print("")
else:
for processor in processors:
if processor.description:
- print " - %s: %s" % (processor.name, processor.description)
+ print(" - %s: %s" % (processor.name, processor.description))
else:
- print " - %s" % processor.name
+ print(" - %s" % processor.name)
def run(args, media_id=None):
@@ -185,12 +188,12 @@ def run(args, media_id=None):
processor_class = manager.get_processor(
args.reprocess_command, media_entry)
except ProcessorDoesNotExist:
- print 'No such processor "%s" for media with id "%s"' % (
- args.reprocess_command, media_entry.id)
+ print('No such processor "%s" for media with id "%s"' % (
+ args.reprocess_command, media_entry.id))
return
except ProcessorNotEligible:
- print 'Processor "%s" exists but media "%s" is not eligible' % (
- args.reprocess_command, media_entry.id)
+ print('Processor "%s" exists but media "%s" is not eligible' % (
+ args.reprocess_command, media_entry.id))
return
reprocess_parser = processor_class.generate_parser()
@@ -203,7 +206,7 @@ def run(args, media_id=None):
except ProcessingManagerDoesNotExist:
entry = MediaEntry.query.filter_by(id=media_id).first()
- print 'No such processing manager for {0}'.format(entry.media_type)
+ print('No such processing manager for {0}'.format(entry.media_type))
def bulk_run(args):
@@ -233,12 +236,12 @@ def thumbs(args):
processor_class = manager.get_processor(
'resize', media_entry)
except ProcessorDoesNotExist:
- print 'No such processor "%s" for media with id "%s"' % (
- 'resize', media_entry.id)
+ print('No such processor "%s" for media with id "%s"' % (
+ 'resize', media_entry.id))
return
except ProcessorNotEligible:
- print 'Processor "%s" exists but media "%s" is not eligible' % (
- 'resize', media_entry.id)
+ print('Processor "%s" exists but media "%s" is not eligible' % (
+ 'resize', media_entry.id))
return
reprocess_parser = processor_class.generate_parser()
@@ -260,7 +263,7 @@ def thumbs(args):
reprocess_info=reprocess_request)
except ProcessingManagerDoesNotExist:
- print 'No such processing manager for {0}'.format(entry.media_type)
+ print('No such processing manager for {0}'.format(entry.media_type))
def initial(args):
@@ -276,7 +279,7 @@ def initial(args):
media_entry,
reprocess_action='initial')
except ProcessingManagerDoesNotExist:
- print 'No such processing manager for {0}'.format(entry.media_type)
+ print('No such processing manager for {0}'.format(entry.media_type))
def reprocess(args):
diff --git a/mediagoblin/gmg_commands/serve.py b/mediagoblin/gmg_commands/serve.py
new file mode 100644
index 00000000..64400fdd
--- /dev/null
+++ b/mediagoblin/gmg_commands/serve.py
@@ -0,0 +1,66 @@
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import print_function
+
+from paste.deploy import loadapp, loadserver
+
+
+class ServeCommand(object):
+
+ def loadserver(self, server_spec, name, relative_to, **kwargs):
+ return loadserver(server_spec, name=name, relative_to=relative_to,
+ **kwargs)
+
+ def loadapp(self, app_spec, name, relative_to, **kwargs):
+ return loadapp(app_spec, name=name, relative_to=relative_to, **kwargs)
+
+ def daemonize(self):
+ # TODO: pass to gunicorn if available
+ pass
+
+ def restart_with_reloader(self):
+ pass
+
+ def restart_with_monitor(self, reloader=False):
+ pass
+
+ def run(self):
+ print('Running...')
+
+
+def parser_setup(subparser):
+ subparser.add_argument('config', metavar='CONFIG_FILE')
+ subparser.add_argument('command',
+ choices=['start', 'stop', 'restart', 'status'],
+ nargs='?', default='start')
+ subparser.add_argument('-n', '--app-name',
+ dest='app_name',
+ metavar='NAME',
+ help="Load the named application (default main)")
+ subparser.add_argument('-s', '--server',
+ dest='server',
+ metavar='SERVER_TYPE',
+ help="Use the named server.")
+ subparser.add_argument('--reload',
+ dest='reload',
+ action='store_true',
+ help="Use auto-restart file monitor")
+
+
+def serve(args):
+ serve_cmd = ServeCommand() # TODO: pass args to it
+ serve_cmd.run()
diff --git a/mediagoblin/gmg_commands/users.py b/mediagoblin/gmg_commands/users.py
index 71149497..63c48690 100644
--- a/mediagoblin/gmg_commands/users.py
+++ b/mediagoblin/gmg_commands/users.py
@@ -14,6 +14,10 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from __future__ import print_function
+
+import six
+
from mediagoblin.gmg_commands import util as commands_util
from mediagoblin import auth
from mediagoblin import mg_globals
@@ -45,13 +49,13 @@ def adduser(args):
).count()
if users_with_username:
- print u'Sorry, a user with that name already exists.'
+ print(u'Sorry, a user with that name already exists.')
else:
# Create the user
entry = db.User()
- entry.username = args.username.lower()
- entry.email = unicode(args.email)
+ entry.username = six.text_type(args.username.lower())
+ entry.email = six.text_type(args.email)
entry.pw_hash = auth.gen_password_hash(args.password)
default_privileges = [
db.Privilege.query.filter(
@@ -66,7 +70,7 @@ def adduser(args):
entry.all_privileges = default_privileges
entry.save()
- print "User created (and email marked as verified)"
+ print(u"User created (and email marked as verified)")
def makeadmin_parser_setup(subparser):
@@ -81,16 +85,16 @@ def makeadmin(args):
db = mg_globals.database
user = db.User.query.filter_by(
- username=unicode(args.username.lower())).one()
+ username=six.text_type(args.username.lower())).one()
if user:
user.all_privileges.append(
db.Privilege.query.filter(
db.Privilege.privilege_name==u'admin').one()
)
user.save()
- print 'The user is now Admin'
+ print(u'The user is now Admin')
else:
- print 'The user doesn\'t exist'
+ print(u'The user doesn\'t exist')
def changepw_parser_setup(subparser):
@@ -108,13 +112,13 @@ def changepw(args):
db = mg_globals.database
user = db.User.query.filter_by(
- username=unicode(args.username.lower())).one()
+ username=six.text_type(args.username.lower())).one()
if user:
user.pw_hash = auth.gen_password_hash(args.password)
user.save()
- print 'Password successfully changed'
+ print(u'Password successfully changed')
else:
- print 'The user doesn\'t exist'
+ print(u'The user doesn\'t exist')
def deleteuser_parser_setup(subparser):
@@ -129,9 +133,9 @@ def deleteuser(args):
db = mg_globals.database
user = db.User.query.filter_by(
- username=unicode(args.username.lower())).one()
+ username=unicode(args.username.lower())).first()
if user:
user.delete()
- print 'The user %s has been deleted' % args.username
+ print('The user %s has been deleted' % args.username)
else:
- print 'The user %s doesn\'t exist' % args.username
+ print('The user %s doesn\'t exist' % args.username)
diff --git a/mediagoblin/i18n/cs/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/cs/LC_MESSAGES/mediagoblin.mo
index d3c83a26..ec94b753 100644
--- a/mediagoblin/i18n/cs/LC_MESSAGES/mediagoblin.mo
+++ b/mediagoblin/i18n/cs/LC_MESSAGES/mediagoblin.mo
Binary files differ
diff --git a/mediagoblin/i18n/cs/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/cs/LC_MESSAGES/mediagoblin.po
index b8b918bc..87af7b0b 100644
--- a/mediagoblin/i18n/cs/LC_MESSAGES/mediagoblin.po
+++ b/mediagoblin/i18n/cs/LC_MESSAGES/mediagoblin.po
@@ -10,8 +10,8 @@ msgstr ""
"Project-Id-Version: GNU MediaGoblin\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2014-08-04 13:45-0500\n"
-"PO-Revision-Date: 2014-08-04 18:45+0000\n"
-"Last-Translator: cwebber <cwebber@dustycloud.org>\n"
+"PO-Revision-Date: 2014-08-20 17:20+0000\n"
+"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
"Language-Team: Czech (http://www.transifex.com/projects/p/mediagoblin/language/cs/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -74,7 +74,7 @@ msgstr "Napřed se musíte přihlásit, abychom věděli komu máme email poslat
#: mediagoblin/auth/views.py:193
msgid "You've already verified your email address!"
-msgstr "Svojí emailovou adresu máte již ověřenou!"
+msgstr "Svojí emailovou adresu máte již ověřenu!"
#: mediagoblin/auth/views.py:203
msgid "Resent your verification email."
@@ -101,7 +101,7 @@ msgid ""
"You can use\n"
" <a href=\"http://daringfireball.net/projects/markdown/basics\">\n"
" Markdown</a> for formatting."
-msgstr "Můžete použít\n <a href=\"http://daringfireball.net/projects/markdown/basics\">\n Markdown</a> pro formátování."
+msgstr "Pro formátování můžete používat <a href=\"http://cs.wikipedia.org/wiki/Markdown\" target=\"_blank\">Markdown</a>."
#: mediagoblin/edit/forms.py:37 mediagoblin/media_types/blog/forms.py:27
#: mediagoblin/submit/forms.py:45
@@ -222,7 +222,7 @@ msgstr "Upravujete tvorbu jiného uživatele. Buďte opatrná/ý."
#: mediagoblin/edit/views.py:166
#, python-format
msgid "You added the attachment %s!"
-msgstr "Přidali jste přílohu %s!"
+msgstr "Přidal(a) jste přílohu %s!"
#: mediagoblin/edit/views.py:193
msgid "You can only edit your own profile."
@@ -299,62 +299,62 @@ msgid ""
"script (and how to format the metadata csv file), read the MediaGoblin\n"
"documentation page on command line uploading\n"
"<http://docs.mediagoblin.org/siteadmin/commandline-upload.html>"
-msgstr ""
+msgstr "Podrobnější instrukce jak správně používat tento\nskript (a jak formátovat soubor .csv s metadaty) najdete v dokumentaci\nMediaGoblinu na stránce o uploadování skrze příkazovou řádku\n<http://docs.mediagoblin.org/siteadmin/commandline-upload.html>"
#: mediagoblin/gmg_commands/batchaddmedia.py:40
msgid "Name of user these media entries belong to"
-msgstr ""
+msgstr "Jméno uživatele, kterému patří tyto tvorby"
#: mediagoblin/gmg_commands/batchaddmedia.py:43
msgid "Path to the csv file containing metadata information."
-msgstr ""
+msgstr "Cesta k souboru .csv obsahujícímu metadata."
#: mediagoblin/gmg_commands/batchaddmedia.py:48
msgid "Don't process eagerly, pass off to celery"
-msgstr ""
+msgstr "Nezpracovávat okamžitě, předat serveru Celery."
#: mediagoblin/gmg_commands/batchaddmedia.py:63
msgid "Sorry, no user by username '{username}' exists"
-msgstr ""
+msgstr "Promiňte, ale uživatel '{username}' neexistuje."
#: mediagoblin/gmg_commands/batchaddmedia.py:74
msgid "File at {path} not found, use -h flag for help"
-msgstr ""
+msgstr "Soubor {path} nebyl nalezen, použijte argument -h pro nápovědu."
#: mediagoblin/gmg_commands/batchaddmedia.py:115
msgid ""
"Error with media '{media_id}' value '{error_path}': {error_msg}\n"
"Metadata was not uploaded."
-msgstr ""
+msgstr "Chyba u tvorby '{media_id}' cesta '{error_path}': {error_msg}\n\nMetadata nebyla odeslána."
#: mediagoblin/gmg_commands/batchaddmedia.py:141
msgid ""
"FAIL: Local file {filename} could not be accessed.\n"
"{filename} will not be uploaded."
-msgstr ""
+msgstr "CHYBA: Lokální soubor {filename} není přístupný.\nSoubor {filename} nebude odeslán na server."
#: mediagoblin/gmg_commands/batchaddmedia.py:157
msgid ""
"Successfully submitted {filename}!\n"
"Be sure to look at the Media Processing Panel on your website to be sure it\n"
"uploaded successfully."
-msgstr ""
+msgstr "Soubor {filename} byl úspěšně odeslán!\nPro jistotu se můžete podívat na Panel zpracování tvoreb na vašich stránkách, kde uvidíte\nzda upload proběhl úspěšně."
#: mediagoblin/gmg_commands/batchaddmedia.py:160
msgid "FAIL: This file is larger than the upload limits for this site."
-msgstr ""
+msgstr "CHYBA: Velikost tohoto souboru překračuje maximální velikost povolenou na těchto stránkách."
#: mediagoblin/gmg_commands/batchaddmedia.py:163
msgid "FAIL: This file will put this user past their upload limits."
-msgstr ""
+msgstr "CHYBA: Odesláním tohoto souboru by současný uživatel překročil svůj limit pro nahrávání souborů."
#: mediagoblin/gmg_commands/batchaddmedia.py:166
msgid "FAIL: This user is already past their upload limits."
-msgstr ""
+msgstr "CHYBA: Tento uživatel již vyčerpal svůj limit pro nahrávání souborů."
#: mediagoblin/gmg_commands/batchaddmedia.py:168
msgid "{files_uploaded} out of {files_attempted} files successfully submitted"
-msgstr ""
+msgstr "{files_uploaded} z celkového počtu {files_attempted} souborů úspěšně odesláno."
#: mediagoblin/meddleware/csrf.py:134
msgid ""
@@ -384,28 +384,28 @@ msgstr "Jupí! Odesláno!"
#: mediagoblin/media_types/blog/views.py:198
msgid "Woohoo! edited blogpost is submitted"
-msgstr ""
+msgstr "Jupí! Upravený příspěvek byl odeslán!"
#: mediagoblin/media_types/blog/views.py:320
msgid "You deleted the Blog."
-msgstr "Smazal(a) jste Blog."
+msgstr "Smazal(a) jste blog."
#: mediagoblin/media_types/blog/views.py:326
#: mediagoblin/user_pages/views.py:329
msgid "The media was not deleted because you didn't check that you were sure."
-msgstr "Tvorba nebyla odstraněna, protože jste nezaškrtli, že jste si jistí."
+msgstr "Tvorba nebyla odstraněna, protože jste nezaškrtl(a), že jste si jist(a)."
#: mediagoblin/media_types/blog/views.py:333
msgid "You are about to delete another user's Blog. Proceed with caution."
-msgstr ""
+msgstr "Chystáte se smazat blog jiného uživatele. Buďte opatrná/ý"
#: mediagoblin/media_types/blog/views.py:344
msgid "The blog was not deleted because you have no rights."
-msgstr ""
+msgstr "Blog nebyl smazán, protože k tomu nemáte oprávnění."
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_admin_dashboard.html:43
msgid "Add Blog Post"
-msgstr ""
+msgstr "Přidat příspěvek"
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_admin_dashboard.html:50
msgid "Edit Blog"
@@ -435,11 +435,11 @@ msgstr "Smazat"
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_admin_dashboard.html:102
msgid "<em> Go to list view </em>"
-msgstr ""
+msgstr "<em> Zobrazit jako seznam </em>"
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_admin_dashboard.html:104
msgid " No blog post yet. "
-msgstr ""
+msgstr "Zatím zde nejsou žádné příspěvky."
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_confirm_delete.html:30
#: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30
@@ -481,16 +481,16 @@ msgstr "Přidat"
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_post_edit_create.html:23
msgid "Create/Edit a blog post."
-msgstr ""
+msgstr "Vytvořit/Upravit příspěvek."
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_post_edit_create.html:29
msgid "Create/Edit a Blog Post."
-msgstr ""
+msgstr "Vytvořit/Upravit příspěvek."
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_post_listing.html:24
#, python-format
msgid "%(blog_owner_name)s's Blog"
-msgstr ""
+msgstr "Blog uživatele %(blog_owner_name)s"
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/list_of_blogs.html:46
msgid "View"
@@ -502,7 +502,7 @@ msgstr "Vytvořit Blog"
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/url_to_dashboard.html:20
msgid " Blog Dashboard "
-msgstr ""
+msgstr "Ovládací panel blogů"
#: mediagoblin/media_types/pdf/processing.py:142
msgid "unoconv failing to run, check log file"
@@ -546,7 +546,7 @@ msgstr "Jaká práva odejmete?"
#: mediagoblin/moderation/forms.py:122
msgid "Why user was banned:"
-msgstr ""
+msgstr "Proč byl uživateli udělen zákaz:"
#: mediagoblin/moderation/forms.py:125
msgid "Message to user:"
@@ -554,23 +554,23 @@ msgstr "Zpráva pro uživatele:"
#: mediagoblin/moderation/forms.py:128
msgid "Resolution content:"
-msgstr ""
+msgstr "Obsah řešení:"
#: mediagoblin/moderation/tools.py:34
msgid ""
"\n"
"{mod} took away {user}'s {privilege} privileges."
-msgstr ""
+msgstr "\n{mod} odebral(a) uživateli {user} oprávnění {privilege}."
#: mediagoblin/moderation/tools.py:47
msgid ""
"\n"
"{mod} banned user {user} {expiration_date}."
-msgstr ""
+msgstr "\n{mod} udělil(a) uživateli {user} zákaz až do {expiration_date}."
#: mediagoblin/moderation/tools.py:51
msgid "until {date}"
-msgstr ""
+msgstr "do {date}"
#: mediagoblin/moderation/tools.py:53
#: mediagoblin/templates/mediagoblin/banned.html:30
@@ -581,19 +581,19 @@ msgstr "trvale"
msgid ""
"\n"
"{mod} sent a warning email to the {user}."
-msgstr ""
+msgstr "\n{mod} poslal(a) varovný email uživateli {user}."
#: mediagoblin/moderation/tools.py:71
msgid ""
"\n"
"{mod} deleted the comment."
-msgstr ""
+msgstr "\n{mod} smazal(a) komentář."
#: mediagoblin/moderation/tools.py:78
msgid ""
"\n"
"{mod} deleted the media entry."
-msgstr ""
+msgstr "\n{mod} smazal(a) tvorbu."
#: mediagoblin/moderation/tools.py:91
msgid "Warning from"
@@ -638,23 +638,23 @@ msgstr "Promiňte, již jste vyčerpali svůj datový limit."
#: mediagoblin/plugins/archivalook/forms.py:21
msgid "Enter the URL for the media to be featured"
-msgstr ""
+msgstr "Zadejte URL adresu tvorby, kterou chcete vystavit"
#: mediagoblin/plugins/archivalook/tools.py:132
msgid "Primary"
-msgstr ""
+msgstr "První"
#: mediagoblin/plugins/archivalook/tools.py:133
msgid "Secondary"
-msgstr ""
+msgstr "Druhá"
#: mediagoblin/plugins/archivalook/tools.py:134
msgid "Tertiary"
-msgstr ""
+msgstr "Třetí"
#: mediagoblin/plugins/archivalook/tools.py:135
msgid "-----------{display_type}-Features---------------------------\n"
-msgstr ""
+msgstr "-----------{display_type}-úroveň---------------------------\n"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:33
msgid "How does this work?"
@@ -662,7 +662,7 @@ msgstr "Jak to funguje?"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:34
msgid "How to feature media?"
-msgstr ""
+msgstr "Jak mohu vystavit tvorbu?"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:37
msgid ""
@@ -675,11 +675,11 @@ msgid ""
" inside the text box, click the Submit Query button, and your media should be\n"
" displayed on the front page.\n"
" "
-msgstr ""
+msgstr "\nJděte na stránku tvorby, kterou chcete vystavit. Zkopírujte její URL adresu, a pak ji vložte jako nový řádek to textového okénka výše. V každém řádku by mělo být pouze jedno URL. Vložte jej pod nadpis, který určuje důležitost (první, druhá, nebo třetí úroveň). Až do tohoto okénka vložíte adresy všech tvoreb, které si přejete vystavit, klikněte na tlačítko \"Submit Query\", a vaše tvorby se objeví na hlavní stránce."
#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:48
msgid "Is there another way to manage featured media?"
-msgstr ""
+msgstr "Je i jiný způsob, jak spravovat vystavené tvorby?"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:51
msgid ""
@@ -701,11 +701,11 @@ msgid ""
" prominent and Demote moves the featured media lower down and makes it\n"
" less prominent.\n"
" "
-msgstr ""
+msgstr "\nAno. Můžete také jít na stránku příslušné tvorby, a podívat se na panel, který najdete po straně. Pokud tvorba není vystavena, uvidíte tam tlačítko “Vystavit“. Klikněte na něj, a tvorba bude vystavena v první úrovni, nahoře na stránce. Všechny ostatní vystavené tvorby budou posunuty níže.<br /><br />\n\nOtevřete-li stránku tvorby, která je právě vystavena, uvidíte možnosti „Zrušit vystavení“, „Zvýšit důležitost“ a „Snížit důležitost“. Kliknete-li na „Zrušit vystavení“, tvorba se již nebude zobrazovat na hlavní stránce. Kdykoli ji můžete znovu vystavit. Zvýšení důležitosti posune tvorbu o úroveň výše, a snížení naopak o úroveň níže."
#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:70
msgid "What is a Primary Feature? What is a Secondary Feature?"
-msgstr ""
+msgstr "Co znamená První úroveň, Druhá úroveň?"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:74
msgid ""
@@ -718,13 +718,13 @@ msgid ""
" Primary Features also can display longer descriptions than Secondary\n"
" Features, and Secondary Features can display longer descriptions than\n"
" Tertiary Features."
-msgstr ""
+msgstr "\nTyto kategorie určují, jak výrazná bude vaše tvorba na hlavní stránce. Při použití první úrovně se zobrazí hned nahoře a bude mnohem větší. Druhá úroveň znamená, že bude o něco menší. To, co je vystavené pod třetí úrovní, se zobrazí v mřížce na konci stránky.<br /><br />\n\nPrvní úroveň také zobrazí u tvorby delší popis než druhá, a třetí bude mít popis ještě kratší."
#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:85
msgid ""
"How to decide what information is displayed when a media entry is\n"
" featured?"
-msgstr ""
+msgstr "Co rozhoduje o tom, jaké informace se budou zobrazovat u vystavené tvorby?"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:88
msgid ""
@@ -736,11 +736,11 @@ msgid ""
" Secondary Features display the first 256 characters of their description,\n"
" and Tertiary Features display the first 128 characters of their description.\n"
" "
-msgstr ""
+msgstr "\nKdyž je tvorba vystavena, její nadpis, obrázek a část jejího popisu budou zobrazeny na hlavní stránce.\nDélka zobrazeného popisu závisí na úrovni její důležitosti.\nPrvní úroveň ukáže prvních 512 znaků popisu. Druhá úroveň ukáže 256, a třetí jen 128 znaků."
#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:98
msgid "How to unfeature a piece of media?"
-msgstr ""
+msgstr "Jak zrušit vystavení tvorby?"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:102
msgid ""
@@ -748,7 +748,7 @@ msgid ""
" Unfeature a media by removing its line from the above textarea and then\n"
" pressing the Submit Query button.\n"
" "
-msgstr ""
+msgstr "\nVystavení tvorby lze zrušit tím, že smažete její řádku z textového okénka, které najdete výše na této stránce, a potvrdíte změnu kliknutím na tlačítko \"Submit Query\"."
#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:108
msgid "CAUTION:"
@@ -761,47 +761,47 @@ msgid ""
" you make a typo, once you press Submit Query, your media entry will NOT be\n"
" featured. Make sure that all your intended Media Entries are featured.\n"
" "
-msgstr ""
+msgstr "\nKdyž kopírujete a vkládáte URL adresy do textového okénka, mějte na paměti, že případný překlep nebo chyba v adrese způsobí, že vaše tvorba nebude vystavena. Zkontrolujte si proto, že všechny tvorby byly skutečně vystaveny."
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:26
msgid ""
"\n"
"Feature Media "
-msgstr ""
+msgstr "\nVystavit tvorbu"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:28
msgid "Feature"
-msgstr ""
+msgstr "Vystavit"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:34
msgid ""
"\n"
"Unfeature Media "
-msgstr ""
+msgstr "\nZrušit vystavení tvorby"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:36
msgid "Unfeature"
-msgstr ""
+msgstr "Zrušit vystavení"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:42
msgid ""
"\n"
"Promote Feature "
-msgstr ""
+msgstr "\nZvýšit úroveň důležitosti pro vystavení"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:44
msgid "Promote"
-msgstr ""
+msgstr "Zvýšit důležitost"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:50
msgid ""
"\n"
"Demote Feature "
-msgstr ""
+msgstr "\nSnížit úroveň důležitosti pro vystavení"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:52
msgid "Demote"
-msgstr ""
+msgstr "Snížit důležitost"
#: mediagoblin/plugins/archivalook/templates/archivalook/recent_media.html:30
#: mediagoblin/templates/mediagoblin/root.html:32
@@ -810,14 +810,14 @@ msgstr "Nejnovější tvorba"
#: mediagoblin/plugins/archivalook/templates/archivalook/root.html:61
msgid "Nothing is currently featured."
-msgstr ""
+msgstr "V tuto chvíli není nic vystaveno."
#: mediagoblin/plugins/archivalook/templates/archivalook/root.html:62
msgid ""
"If you would like to feature a\n"
" piece of media, go to that media entry's homepage and click the button\n"
" that says <a class=\"button_action\">Feature</a>."
-msgstr ""
+msgstr "Pokud zde chcete vystavit některou tvorbu, jděte na stránku příslušné tvorby a klikněte tam na tlačítko <a class=\"button_action\">Vystavit</a>."
#: mediagoblin/plugins/archivalook/templates/archivalook/root.html:67
#, python-format
@@ -827,29 +827,29 @@ msgid ""
" have media featured as long as your instance has the 'archivalook'\n"
" plugin enabled. A more advanced tool to manage features can be found\n"
" in the <a href=\"%(featured_media_url)s\">feature management panel.</a>"
-msgstr ""
+msgstr "Tuto stránku vidíte proto, že máte oprávnění vystavovat tvorby. Obyčejný uživatel by viděl jen prázdnou stránku, proto mějte vždy vystavenu nějakou tvorbu, pokud používáte na své instanci plugin 'archivalook'. Pokročilejším nástrojem k ovládání této funkce je <a href=\"%(featured_media_url)s\">Panel pro správu vystavených tvoreb</a>."
#: mediagoblin/plugins/archivalook/templates/archivalook/root.html:79
msgid "View most recent media"
-msgstr ""
+msgstr "Zobrazit nejnovější tvorby"
#: mediagoblin/plugins/archivalook/templates/archivalook/bits/feature_dropdown.html:22
msgid "Feature management panel"
-msgstr ""
+msgstr "Správa vystavených tvoreb"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/audio_primary.html:43
msgid ""
"Sorry, this audio will not work because\n"
"\tyour web browser does not support HTML5\n"
"\taudio."
-msgstr ""
+msgstr "Omlouváme se, ale tento zvukový soubor\n\tnelze přehrát, protože váš prohlížeč\n\tnepodporuje HTML5 audio."
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/audio_primary.html:46
msgid ""
"You can get a modern web browser that\n"
"\tcan play the audio at <a href=\"http://getfirefox.com\">\n"
"\t http://getfirefox.com</a>!"
-msgstr ""
+msgstr "Můžete si stáhnout moderní prohlížeč,\n\tkterý umí přehrávat tento typ zvuku,\n\tna <a href=\"http://getfirefox.com\">http://getfirefox.com</a>!"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/video_primary.html:43
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/video_secondary.html:43
@@ -857,7 +857,7 @@ msgid ""
"Sorry, this video will not work because\n"
" your web browser does not support HTML5 \n"
" video."
-msgstr ""
+msgstr "Omlouváme se, ale toto video nelze přehrát, protože\nváš prohlížeč nepodporuje HTML5\nvideo."
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/video_primary.html:46
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/video_secondary.html:46
@@ -865,7 +865,7 @@ msgid ""
"You can get a modern web browser that \n"
" can play this video at <a href=\"http://getfirefox.com\">\n"
" http://getfirefox.com</a>!"
-msgstr ""
+msgstr "Můžete si stáhnout moderní prohlížeč, který umí přehrát toto video, na <a href=\"http://getfirefox.com\"> http://getfirefox.com</a>!"
#: mediagoblin/plugins/basic_auth/forms.py:24
#: mediagoblin/plugins/ldap/forms.py:35 mediagoblin/plugins/openid/forms.py:27
@@ -1136,7 +1136,7 @@ msgstr "Přihlásit se"
#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login.html:39
#: mediagoblin/templates/mediagoblin/auth/login.html:39
msgid "Logging in failed!"
-msgstr "Přihlášení selhalo!"
+msgstr "Chybné přihlášení!"
#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login.html:44
msgid "Log in to create an account!"
@@ -1226,7 +1226,7 @@ msgid ""
"You can use\n"
" <a href=\"http://daringfireball.net/projects/markdown/basics\">\n"
" Markdown</a> for formatting."
-msgstr "Můžete používat\n<a href=\"http://daringfireball.net/projects/markdown/basics\">\nMarkdown</a> pro formátování."
+msgstr "Pro formátování můžete používat <a href=\"http://cs.wikipedia.org/wiki/Markdown\" target=\"_blank\">Markdown</a>."
#: mediagoblin/submit/views.py:55
msgid "You must provide a file."
@@ -1239,7 +1239,7 @@ msgstr "Sbírka „%s“ přidána!"
#: mediagoblin/templates/mediagoblin/banned.html:20
msgid "You are Banned."
-msgstr "Byl vám udělen Zákaz."
+msgstr "Byl vám udělen zákaz."
#: mediagoblin/templates/mediagoblin/banned.html:24
#: mediagoblin/templates/mediagoblin/error.html:24
@@ -1248,7 +1248,7 @@ msgstr "Obrázek vystresovaného goblina"
#: mediagoblin/templates/mediagoblin/banned.html:26
msgid "You have been banned"
-msgstr "Zákaz platí"
+msgstr "Byl vám udělen zákaz,"
#: mediagoblin/templates/mediagoblin/banned.html:28
#, python-format
@@ -1298,7 +1298,7 @@ msgstr "Vytvořit novou sbírku"
#: mediagoblin/templates/mediagoblin/base.html:163
msgid "Moderation powers:"
-msgstr ""
+msgstr "Moderátorské funkce:"
#: mediagoblin/templates/mediagoblin/base.html:169
msgid "User management panel"
@@ -1319,7 +1319,7 @@ msgstr "Autorizovat"
#: mediagoblin/templates/mediagoblin/api/authorize.html:29
msgid "You are logged in as"
-msgstr "Jste přihlášen jako"
+msgstr "Jste přihlášen(a) jako"
#: mediagoblin/templates/mediagoblin/api/authorize.html:33
msgid "Do you want to authorize "
@@ -1351,11 +1351,11 @@ msgstr "Měnit vaše informace"
#: mediagoblin/templates/mediagoblin/api/oob.html:21
msgid "Authorization Finished"
-msgstr "Autorizace Dokončena"
+msgstr "Autorizace dokončena"
#: mediagoblin/templates/mediagoblin/api/oob.html:26
msgid "Authorization Complete"
-msgstr "Autorizace Kompletní"
+msgstr "Autorizace kompletní"
#: mediagoblin/templates/mediagoblin/api/oob.html:28
msgid "Copy and paste this into your client:"
@@ -1406,7 +1406,7 @@ msgstr "Prozkoumat"
#: mediagoblin/templates/mediagoblin/bits/frontpage_welcome.html:24
msgid "Hi there, welcome to this MediaGoblin site!"
-msgstr "Zdravíme, vítejte na stránkách projektu MediaGoblin!"
+msgstr "Dobrý den, vítejte na MediaGoblinu!"
#: mediagoblin/templates/mediagoblin/bits/frontpage_welcome.html:25
msgid ""
@@ -1516,11 +1516,11 @@ msgstr "Upravujete profil %(username)s"
#: mediagoblin/templates/mediagoblin/edit/metadata.html:67
#, python-format
msgid "Metadata for \"%(media_name)s\""
-msgstr ""
+msgstr "Metadata pro \"%(media_name)s\""
#: mediagoblin/templates/mediagoblin/edit/metadata.html:72
msgid "MetaData"
-msgstr ""
+msgstr "Metadata"
#: mediagoblin/templates/mediagoblin/edit/metadata.html:80
msgid "Add new Row"
@@ -1528,11 +1528,11 @@ msgstr "Přidat nový řádek"
#: mediagoblin/templates/mediagoblin/edit/metadata.html:83
msgid "Update Metadata"
-msgstr ""
+msgstr "Aktualizovat metadata"
#: mediagoblin/templates/mediagoblin/edit/metadata.html:87
msgid "Clear empty Rows"
-msgstr ""
+msgstr "Odstranit prázdné řádky"
#: mediagoblin/templates/mediagoblin/edit/verification.txt:19
#, python-format
@@ -1546,7 +1546,7 @@ msgid ""
"\n"
"If you are not %(username)s or didn't request an email change, you can ignore\n"
"this email."
-msgstr "Ahoj,\n\nPotřebujeme ověřit, jestli jste %(username)s. Pokud ano,\nklikněte prosím na tento odkaz pro ověření vaší nové emailové adresy.\n\n%(verification_url)s\n\nPokud nejste %(username)s, nebo jste nežádali o změnu emailu, nemusíte\nsi tohoto emailu všímat."
+msgstr "Dobrý den,\n\nPotřebujeme ověřit, jestli jste %(username)s. Pokud ano,\nklikněte prosím na tento odkaz pro ověření vaší nové emailové adresy.\n\n%(verification_url)s\n\nPokud nejste %(username)s, nebo jste nežádali o změnu emailu, nemusíte\nsi tohoto emailu všímat."
#: mediagoblin/templates/mediagoblin/fragments/header_notifications.html:4
msgid "New comments"
@@ -1593,7 +1593,7 @@ msgid ""
"Sorry, this audio will not work because \n"
"\tyour web browser does not support HTML5 \n"
"\taudio."
-msgstr "Promiňte, tento zvukový soubor nelze přehrát\n\tprotože váš prohlížeč nepodporuje HTML5\n\taudio."
+msgstr "Promiňte, tento zvukový soubor nelze přehrát,\n\tprotože váš prohlížeč nepodporuje HTML5\n\taudio."
#: mediagoblin/templates/mediagoblin/media_displays/audio.html:47
msgid ""
@@ -1659,18 +1659,18 @@ msgstr "Stáhnout model"
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:145
msgid "File Format"
-msgstr "Formát Souboru"
+msgstr "Formát souboru"
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:147
msgid "Object Height"
-msgstr "Výška Objektu"
+msgstr "Výška objektu"
#: mediagoblin/templates/mediagoblin/media_displays/video.html:63
msgid ""
"Sorry, this video will not work because\n"
" your web browser does not support HTML5 \n"
" video."
-msgstr "Promiňte, toto video nelze přehrát protože\nváš prohlížeč nepodporuje HTML5\nvideo."
+msgstr "Omlouváme se, ale toto video nelze přehrát, protože\nváš prohlížeč nepodporuje HTML5\nvideo."
#: mediagoblin/templates/mediagoblin/media_displays/video.html:66
msgid ""
@@ -1709,15 +1709,15 @@ msgstr "Uživatel"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:41
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:70
msgid "When submitted"
-msgstr ""
+msgstr "Odesláno"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:42
msgid "Transcoding progress"
-msgstr ""
+msgstr "Stav transkódování"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:53
msgid "Unknown"
-msgstr ""
+msgstr "Neznámý"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:59
#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:56
@@ -1727,15 +1727,15 @@ msgstr "Žádné zpracovávané tvorby"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:62
#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:59
msgid "These uploads failed to process:"
-msgstr "Tuto tvorbu se nepovedlo zpracovat:"
+msgstr "Tyto tvorby se nepovedlo zpracovat:"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:71
msgid "Reason for failure"
-msgstr ""
+msgstr "Příčina selhání"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:72
msgid "Failure metadata"
-msgstr ""
+msgstr "Metadata o selhání"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:91
#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:86
@@ -1748,7 +1748,7 @@ msgstr "Posledních 10 úspěšných uploadů"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:101
msgid "Submitted"
-msgstr ""
+msgstr "Odesláno"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:113
#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:107
@@ -1792,7 +1792,7 @@ msgstr "\nOBSAH OD UŽIVATELE\n<a href=\"%(user_url)s\"> %(user_name)s</a>\nBYL
#: mediagoblin/templates/mediagoblin/moderation/report.html:102
msgid "Reason for report:"
-msgstr ""
+msgstr "Důvod hlášení:"
#: mediagoblin/templates/mediagoblin/moderation/report.html:133
#: mediagoblin/templates/mediagoblin/moderation/user.html:136
@@ -1898,15 +1898,15 @@ msgstr "Nebyla nalezena žádná uzavřená hlášení."
#: mediagoblin/templates/mediagoblin/moderation/user.html:23
#, python-format
msgid "User: %(username)s"
-msgstr ""
+msgstr "Uživatel: %(username)s"
#: mediagoblin/templates/mediagoblin/moderation/user.html:42
msgid "Return to Users Panel"
-msgstr ""
+msgstr "Návrat do Panelu Uživatelů"
#: mediagoblin/templates/mediagoblin/moderation/user.html:49
msgid "Sorry, no such user found."
-msgstr ""
+msgstr "Omlouváme se, ale požadovaný uživatel nebyl nalezen."
#: mediagoblin/templates/mediagoblin/moderation/user.html:53
#: mediagoblin/templates/mediagoblin/user_pages/user_nonactive.html:40
@@ -1918,7 +1918,7 @@ msgstr "Je třeba ověřit email"
msgid ""
"Someone has registered an account with this username, but it still has\n"
" to be activated."
-msgstr ""
+msgstr "Někdo si zaregistroval účet s tímto jménem, zatím ale nebyl aktivován."
#: mediagoblin/templates/mediagoblin/moderation/user.html:66
#: mediagoblin/templates/mediagoblin/user_pages/user.html:34
@@ -1931,11 +1931,11 @@ msgstr "Profil %(username)s"
#: mediagoblin/templates/mediagoblin/moderation/user.html:68
#, python-format
msgid "BANNED until %(expiration_date)s"
-msgstr ""
+msgstr "má ZÁKAZ až do %(expiration_date)s"
#: mediagoblin/templates/mediagoblin/moderation/user.html:72
msgid "Banned Indefinitely"
-msgstr ""
+msgstr "má trvalý zákaz"
#: mediagoblin/templates/mediagoblin/moderation/user.html:78
#: mediagoblin/templates/mediagoblin/user_pages/user.html:62
@@ -1956,60 +1956,60 @@ msgstr "Procházet sbírky"
#: mediagoblin/templates/mediagoblin/moderation/user.html:105
#, python-format
msgid "Active Reports on %(username)s"
-msgstr ""
+msgstr "Aktivní hlášení na uživatele %(username)s"
#: mediagoblin/templates/mediagoblin/moderation/user.html:112
msgid "Report ID"
-msgstr ""
+msgstr "ID hlášení"
#: mediagoblin/templates/mediagoblin/moderation/user.html:113
msgid "Reported Content"
-msgstr ""
+msgstr "Nahlášený obsah"
#: mediagoblin/templates/mediagoblin/moderation/user.html:114
msgid "Description of Report"
-msgstr ""
+msgstr "Popis hlášení"
#: mediagoblin/templates/mediagoblin/moderation/user.html:122
#, python-format
msgid "Report #%(report_number)s"
-msgstr ""
+msgstr "Hlášení #%(report_number)s"
#: mediagoblin/templates/mediagoblin/moderation/user.html:129
msgid "Reported Comment"
-msgstr ""
+msgstr "Nahlášený komentář"
#: mediagoblin/templates/mediagoblin/moderation/user.html:131
msgid "Reported Media Entry"
-msgstr ""
+msgstr "Nahlášená tvorba"
#: mediagoblin/templates/mediagoblin/moderation/user.html:142
#, python-format
msgid "No active reports filed on %(username)s"
-msgstr ""
+msgstr "Na %(username)s nebyla podána žádná hlášení"
#: mediagoblin/templates/mediagoblin/moderation/user.html:150
#, python-format
msgid "All reports on %(username)s"
-msgstr ""
+msgstr "Všechna hlášení na %(username)s"
#: mediagoblin/templates/mediagoblin/moderation/user.html:155
#, python-format
msgid "All reports that %(username)s has filed"
-msgstr ""
+msgstr "Všechna hlášení, která %(username)s podal(a)"
#: mediagoblin/templates/mediagoblin/moderation/user.html:164
#, python-format
msgid "%(username)s's Privileges"
-msgstr ""
+msgstr "Oprávnění uživatele %(username)s"
#: mediagoblin/templates/mediagoblin/moderation/user.html:172
msgid "Privilege"
-msgstr ""
+msgstr "Oprávnění"
#: mediagoblin/templates/mediagoblin/moderation/user.html:173
msgid "Granted"
-msgstr ""
+msgstr "Uděleno"
#: mediagoblin/templates/mediagoblin/moderation/user.html:180
msgid "Yes"
@@ -2021,11 +2021,11 @@ msgstr "Ne"
#: mediagoblin/templates/mediagoblin/moderation/user.html:213
msgid "Ban User"
-msgstr ""
+msgstr "Udělit zákaz"
#: mediagoblin/templates/mediagoblin/moderation/user.html:218
msgid "UnBan User"
-msgstr ""
+msgstr "Odvolat zákaz"
#: mediagoblin/templates/mediagoblin/moderation/user_panel.html:21
#: mediagoblin/templates/mediagoblin/moderation/user_panel.html:26
@@ -2067,7 +2067,7 @@ msgstr "Přidejte vaši tvorbu"
#: mediagoblin/templates/mediagoblin/user_pages/blog_media.html:38
#, python-format
msgid "❖ Blog post by <a href=\"%(user_url)s\">%(username)s</a>"
-msgstr ""
+msgstr "❖ Příspěvek od uživatele <a href=\"%(user_url)s\">%(username)s</a>"
#: mediagoblin/templates/mediagoblin/user_pages/blog_media.html:92
#: mediagoblin/templates/mediagoblin/user_pages/media.html:104
@@ -2097,17 +2097,17 @@ msgstr "%(collection_title)s od <a href=\"%(user_url)s\">%(username)s</a>"
#: mediagoblin/templates/mediagoblin/user_pages/collection_confirm_delete.html:23
#, python-format
msgid "Delete collection %(collection_title)s"
-msgstr ""
+msgstr "Smazat sbírku %(collection_title)s"
#: mediagoblin/templates/mediagoblin/user_pages/collection_confirm_delete.html:38
#, python-format
msgid "Really delete collection: %(title)s?"
-msgstr ""
+msgstr "Opravdu smazat sbírku %(title)s?"
#: mediagoblin/templates/mediagoblin/user_pages/collection_item_confirm_remove.html:23
#, python-format
msgid "Remove %(media_title)s from %(collection_title)s"
-msgstr ""
+msgstr "Odstranit %(media_title)s z %(collection_title)s"
#: mediagoblin/templates/mediagoblin/user_pages/collection_item_confirm_remove.html:39
#, python-format
@@ -2216,7 +2216,7 @@ msgstr "Zde můžete ostatním říct něco o sobě."
#: mediagoblin/templates/mediagoblin/user_pages/user.html:94
#, python-format
msgid "View all of %(username)s's media"
-msgstr "Zobrazit všechnu tvorbu %(username)s"
+msgstr "Zobrazit všechnu tvorbu uživatele %(username)s"
#: mediagoblin/templates/mediagoblin/user_pages/user.html:107
msgid ""
@@ -2394,7 +2394,7 @@ msgstr "Komentář"
msgid ""
"You can use <a href=\"http://daringfireball.net/projects/markdown/basics\" "
"target=\"_blank\">Markdown</a> for formatting."
-msgstr "Můžete používat <a href=\"http://daringfireball.net/projects/markdown/basics\" target=\"_blank\">Markdown</a> pro formátování."
+msgstr "Pro formátování můžete používat <a href=\"http://cs.wikipedia.org/wiki/Markdown\" target=\"_blank\">Markdown</a>."
#: mediagoblin/user_pages/forms.py:35
msgid "I am sure I want to remove this item from the collection"
@@ -2417,7 +2417,7 @@ msgid ""
"You can use\n"
" <a href=\"http://daringfireball.net/projects/markdown/basics\" target=\"_blank\">\n"
" Markdown</a> for formatting."
-msgstr "Můžete používat\n<a href=\"http://daringfireball.net/projects/markdown/basics\" target=\"_blank\">Markdown</a> pro formátování."
+msgstr "Pro formátování můžete používat <a href=\"http://cs.wikipedia.org/wiki/Markdown\" target=\"_blank\">Markdown</a>."
#: mediagoblin/user_pages/forms.py:55 mediagoblin/user_pages/forms.py:61
msgid "Reason for Reporting"
diff --git a/mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po
index 55f5e77f..d8237a6c 100644
--- a/mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po
+++ b/mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2014-08-04 13:45-0500\n"
+"POT-Creation-Date: 2014-09-21 11:08-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -193,7 +193,7 @@ msgstr ""
#: mediagoblin/plugins/basic_auth/forms.py:43
#: mediagoblin/plugins/ldap/forms.py:39
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:64
-#: mediagoblin/tests/test_util.py:116
+#: mediagoblin/tests/test_util.py:143
msgid "Password"
msgstr ""
@@ -612,11 +612,11 @@ msgstr ""
msgid "You will not receive notifications for comments on %s."
msgstr ""
-#: mediagoblin/oauth/views.py:242
+#: mediagoblin/oauth/views.py:241
msgid "Must provide an oauth_token."
msgstr ""
-#: mediagoblin/oauth/views.py:247 mediagoblin/oauth/views.py:298
+#: mediagoblin/oauth/views.py:246 mediagoblin/oauth/views.py:297
msgid "No request token found."
msgstr ""
@@ -2373,34 +2373,34 @@ msgstr ""
msgid "Could not read the image file."
msgstr ""
-#: mediagoblin/tools/response.py:38
+#: mediagoblin/tools/response.py:39
msgid "Oops!"
msgstr ""
-#: mediagoblin/tools/response.py:39
+#: mediagoblin/tools/response.py:40
msgid "An error occured"
msgstr ""
-#: mediagoblin/tools/response.py:53
+#: mediagoblin/tools/response.py:54
msgid "Bad Request"
msgstr ""
-#: mediagoblin/tools/response.py:55
+#: mediagoblin/tools/response.py:56
msgid "The request sent to the server is invalid, please double check it"
msgstr ""
-#: mediagoblin/tools/response.py:63
+#: mediagoblin/tools/response.py:64
msgid "Operation not allowed"
msgstr ""
-#: mediagoblin/tools/response.py:64
+#: mediagoblin/tools/response.py:65
msgid ""
"Sorry Dave, I can't let you do that!</p><p>You have tried to perform a "
"function that you are not allowed to. Have you been trying to delete all "
"user accounts again?"
msgstr ""
-#: mediagoblin/tools/response.py:72
+#: mediagoblin/tools/response.py:73
msgid ""
"There doesn't seem to be a page at this address. Sorry!</p><p>If you're "
"sure the address is correct, maybe the page you're looking for has been "
diff --git a/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.mo
index 36158a48..00c8cfee 100644
--- a/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.mo
+++ b/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.mo
Binary files differ
diff --git a/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.po
index 79ce63a7..b192514a 100644
--- a/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.po
+++ b/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.po
@@ -3,6 +3,7 @@
# This file is distributed under the same license as the PROJECT project.
#
# Translators:
+# Artopal <artopal@fastmail.fm>, 2014
# aleksejrs <deletesoftware@yandex.ru>, 2011, 2012
# ekenbrand <ekenbrand@hotmail.com>, 2011
# Jacobo Nájera <jacobo@gnu.org>, 2011-2012
@@ -21,8 +22,8 @@ msgstr ""
"Project-Id-Version: GNU MediaGoblin\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2014-08-04 13:45-0500\n"
-"PO-Revision-Date: 2014-08-04 18:45+0000\n"
-"Last-Translator: cwebber <cwebber@dustycloud.org>\n"
+"PO-Revision-Date: 2014-08-11 08:55+0000\n"
+"Last-Translator: Laura Arjona Reina <larjona99@gmail.com>\n"
"Language-Team: Spanish (http://www.transifex.com/projects/p/mediagoblin/language/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -557,31 +558,31 @@ msgstr "¿Qué permisos vas a retirar?"
#: mediagoblin/moderation/forms.py:122
msgid "Why user was banned:"
-msgstr ""
+msgstr "Por qué se inhabilitó al usuario:"
#: mediagoblin/moderation/forms.py:125
msgid "Message to user:"
-msgstr ""
+msgstr "Mensaje al usuario:"
#: mediagoblin/moderation/forms.py:128
msgid "Resolution content:"
-msgstr ""
+msgstr "Contenido de la resolución:"
#: mediagoblin/moderation/tools.py:34
msgid ""
"\n"
"{mod} took away {user}'s {privilege} privileges."
-msgstr ""
+msgstr "\n{mod} retiró al usuario {user} los permisos de: {privilege}."
#: mediagoblin/moderation/tools.py:47
msgid ""
"\n"
"{mod} banned user {user} {expiration_date}."
-msgstr ""
+msgstr "\n{mod} inhabilitó al usuario {user} hasta {expiration_date}."
#: mediagoblin/moderation/tools.py:51
msgid "until {date}"
-msgstr ""
+msgstr "hasta {date}"
#: mediagoblin/moderation/tools.py:53
#: mediagoblin/templates/mediagoblin/banned.html:30
@@ -592,19 +593,19 @@ msgstr "indefinidamente"
msgid ""
"\n"
"{mod} sent a warning email to the {user}."
-msgstr ""
+msgstr "\n{mod} envió un correo de advertencia al {user}."
#: mediagoblin/moderation/tools.py:71
msgid ""
"\n"
"{mod} deleted the comment."
-msgstr ""
+msgstr "\n{mod} eliminó el comentario."
#: mediagoblin/moderation/tools.py:78
msgid ""
"\n"
"{mod} deleted the media entry."
-msgstr ""
+msgstr "\n{mod} borró el contenido."
#: mediagoblin/moderation/tools.py:91
msgid "Warning from"
@@ -712,7 +713,7 @@ msgid ""
" prominent and Demote moves the featured media lower down and makes it\n"
" less prominent.\n"
" "
-msgstr ""
+msgstr "\nSí. Si lo prefieres, puedes ir a la página del contenido que te gustaría destacar\no quitar de destacados, y mirar en la barra lateral junto al contenido. Si el contenido\naún no se ha destacado, debería haber un botón que ponga \"Destacar\". Pulsa ese botón\ny el contenido se destacará como primario, al inicio de la portada.\nTodos los demás contenidos destacados seguirán destacados, pero se mostrarán debajo\nen la página de portada.<br /><br />\n\nSi vas a la página de un contenido que actualmente ya está destacado, verás las opciones\n\"Quitar de destacados\", \"Ascender\" y \"Descender\" donde antes se mostraba \"Destacar\".\nHaz clic en Quitar de destacados y ese contenido ya no se mostrará en la portada,\naunque puedes volver a destacarlo en cualquier momento. Ascender mueve el contenido destacado más arriba en la portada y lo hace más prominente, y Descender mueve el contenido más abajo y lo hace menos prominente."
#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:70
msgid "What is a Primary Feature? What is a Secondary Feature?"
@@ -828,7 +829,7 @@ msgid ""
"If you would like to feature a\n"
" piece of media, go to that media entry's homepage and click the button\n"
" that says <a class=\"button_action\">Feature</a>."
-msgstr ""
+msgstr "Si te gustaría destacar\nun contenido, ve a la página de ese contenido y pulsa el botón\nque dice <a class=\"button_action\">Destacar</a>."
#: mediagoblin/plugins/archivalook/templates/archivalook/root.html:67
#, python-format
@@ -838,7 +839,7 @@ msgid ""
" have media featured as long as your instance has the 'archivalook'\n"
" plugin enabled. A more advanced tool to manage features can be found\n"
" in the <a href=\"%(featured_media_url)s\">feature management panel.</a>"
-msgstr ""
+msgstr "Estás viendo esta página porque eres un usuario con permisos para\ndestacar contenido, un usuario normal vería una página en blanco, así que\nasegúrate de destacar contenido mientras tu sitio tenga el complemento\n'archivalook' activado. Se puede encontrar una herramienta más avanzada\npara gestionar los destacados \nen el <a href?\"%(featured_media_url)s\">panel de gestión de destacados</a>."
#: mediagoblin/plugins/archivalook/templates/archivalook/root.html:79
msgid "View most recent media"
@@ -1309,7 +1310,7 @@ msgstr "Crear nueva colección"
#: mediagoblin/templates/mediagoblin/base.html:163
msgid "Moderation powers:"
-msgstr ""
+msgstr "Poderes de moderación:"
#: mediagoblin/templates/mediagoblin/base.html:169
msgid "User management panel"
@@ -1715,20 +1716,20 @@ msgstr "ID"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:68
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:99
msgid "User"
-msgstr ""
+msgstr "Usuario"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:41
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:70
msgid "When submitted"
-msgstr ""
+msgstr "Cuándo se envió"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:42
msgid "Transcoding progress"
-msgstr ""
+msgstr "Proceso de transcodificación"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:53
msgid "Unknown"
-msgstr ""
+msgstr "Desconocido"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:59
#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:56
@@ -1742,11 +1743,11 @@ msgstr "Estos archivos no pudieron ser procesados:"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:71
msgid "Reason for failure"
-msgstr ""
+msgstr "Motivo del fallo"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:72
msgid "Failure metadata"
-msgstr ""
+msgstr "Metadatos del fallo"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:91
#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:86
@@ -1759,7 +1760,7 @@ msgstr "Últimos 10 envíos con éxito"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:101
msgid "Submitted"
-msgstr ""
+msgstr "Enviado"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:113
#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:107
@@ -1803,7 +1804,7 @@ msgstr "\nESTE CONTENIDO DE⏎\n<a href=\"%(user_url)s\"> %(user_name)s</a>⏎\n
#: mediagoblin/templates/mediagoblin/moderation/report.html:102
msgid "Reason for report:"
-msgstr ""
+msgstr "Razón del informe:"
#: mediagoblin/templates/mediagoblin/moderation/report.html:133
#: mediagoblin/templates/mediagoblin/moderation/user.html:136
@@ -2285,11 +2286,11 @@ msgstr "Añadir a una colección"
#: mediagoblin/templates/mediagoblin/utils/comment-subscription.html:24
msgid "Subscribe to comments"
-msgstr ""
+msgstr "Suscribirse a los comentarios"
#: mediagoblin/templates/mediagoblin/utils/comment-subscription.html:30
msgid "Silence comments"
-msgstr ""
+msgstr "Silenciar comentarios"
#: mediagoblin/templates/mediagoblin/utils/feed_link.html:21
#: mediagoblin/themes/airy/templates/mediagoblin/utils/feed_link.html:21
diff --git a/mediagoblin/i18n/fa_IR/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/fa_IR/LC_MESSAGES/mediagoblin.mo
new file mode 100644
index 00000000..2a55a957
--- /dev/null
+++ b/mediagoblin/i18n/fa_IR/LC_MESSAGES/mediagoblin.mo
Binary files differ
diff --git a/mediagoblin/i18n/fa_IR/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/fa_IR/LC_MESSAGES/mediagoblin.po
new file mode 100644
index 00000000..79aa6590
--- /dev/null
+++ b/mediagoblin/i18n/fa_IR/LC_MESSAGES/mediagoblin.po
@@ -0,0 +1,2489 @@
+# Translations template for PROJECT.
+# Copyright (C) 2014 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: GNU MediaGoblin\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2014-08-25 14:44-0500\n"
+"PO-Revision-Date: 2011-08-07 03:51+0000\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: Persian (Iran) (http://www.transifex.com/projects/p/mediagoblin/language/fa_IR/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 1.3\n"
+"Language: fa_IR\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: mediagoblin/decorators.py:303 mediagoblin/plugins/openid/views.py:202
+msgid "Sorry, registration is disabled on this instance."
+msgstr ""
+
+#: mediagoblin/decorators.py:318
+msgid "Sorry, reporting is disabled on this instance."
+msgstr ""
+
+#: mediagoblin/decorators.py:361 mediagoblin/plugins/ldap/views.py:55
+#: mediagoblin/plugins/persona/views.py:77
+msgid "Sorry, authentication is disabled on this instance."
+msgstr ""
+
+#: mediagoblin/auth/tools.py:43
+msgid "Invalid User name or email address."
+msgstr ""
+
+#: mediagoblin/auth/tools.py:44
+msgid "This field does not take email addresses."
+msgstr ""
+
+#: mediagoblin/auth/tools.py:45
+msgid "This field requires an email address."
+msgstr ""
+
+#: mediagoblin/auth/tools.py:116
+msgid "Sorry, a user with that name already exists."
+msgstr ""
+
+#: mediagoblin/auth/tools.py:120 mediagoblin/edit/views.py:407
+msgid "Sorry, a user with that email address already exists."
+msgstr ""
+
+#: mediagoblin/auth/views.py:142 mediagoblin/edit/views.py:363
+#: mediagoblin/edit/views.py:384 mediagoblin/plugins/basic_auth/views.py:110
+msgid "The verification key or user id is incorrect."
+msgstr ""
+
+#: mediagoblin/auth/views.py:161
+msgid ""
+"Your email address has been verified. You may now login, edit your profile, "
+"and submit images!"
+msgstr ""
+
+#: mediagoblin/auth/views.py:167
+msgid "The verification key or user id is incorrect"
+msgstr ""
+
+#: mediagoblin/auth/views.py:185
+msgid "You must be logged in so we know who to send the email to!"
+msgstr ""
+
+#: mediagoblin/auth/views.py:193
+msgid "You've already verified your email address!"
+msgstr ""
+
+#: mediagoblin/auth/views.py:203
+msgid "Resent your verification email."
+msgstr ""
+
+#: mediagoblin/edit/forms.py:29 mediagoblin/edit/forms.py:89
+#: mediagoblin/media_types/blog/forms.py:24
+#: mediagoblin/media_types/blog/forms.py:33 mediagoblin/submit/forms.py:37
+#: mediagoblin/submit/forms.py:61
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:40
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:69
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:100
+#: mediagoblin/user_pages/forms.py:45
+msgid "Title"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:32 mediagoblin/submit/forms.py:40
+msgid "Description of this work"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:33 mediagoblin/edit/forms.py:56
+#: mediagoblin/edit/forms.py:93 mediagoblin/submit/forms.py:65
+msgid ""
+"You can use\n"
+" <a href=\"http://daringfireball.net/projects/markdown/basics\">\n"
+" Markdown</a> for formatting."
+msgstr ""
+
+#: mediagoblin/edit/forms.py:37 mediagoblin/media_types/blog/forms.py:27
+#: mediagoblin/submit/forms.py:45
+msgid "Tags"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:39 mediagoblin/submit/forms.py:47
+msgid "Separate tags by commas."
+msgstr ""
+
+#: mediagoblin/edit/forms.py:42 mediagoblin/edit/forms.py:97
+msgid "Slug"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:43 mediagoblin/edit/forms.py:98
+msgid "The slug can't be empty"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:44
+msgid ""
+"The title part of this media's address. You usually don't need to change "
+"this."
+msgstr ""
+
+#: mediagoblin/edit/forms.py:48 mediagoblin/media_types/blog/forms.py:29
+#: mediagoblin/submit/forms.py:50
+#: mediagoblin/templates/mediagoblin/utils/license.html:20
+msgid "License"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:54
+msgid "Bio"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:60
+msgid "Website"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:62
+msgid "This address contains errors"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:67
+msgid "Email me when others comment on my media"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:69
+msgid "Enable insite notifications about events."
+msgstr ""
+
+#: mediagoblin/edit/forms.py:71
+msgid "License preference"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:77
+msgid "This will be your default license on upload forms."
+msgstr ""
+
+#: mediagoblin/edit/forms.py:90
+msgid "The title can't be empty"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:92 mediagoblin/submit/forms.py:64
+#: mediagoblin/user_pages/forms.py:48
+msgid "Description of this collection"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:99
+msgid ""
+"The title part of this collection's address. You usually don't need to "
+"change this."
+msgstr ""
+
+#: mediagoblin/edit/forms.py:106 mediagoblin/plugins/basic_auth/forms.py:68
+msgid "Old password"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:108 mediagoblin/plugins/basic_auth/forms.py:70
+msgid "Enter your old password to prove you own this account."
+msgstr ""
+
+#: mediagoblin/edit/forms.py:111 mediagoblin/plugins/basic_auth/forms.py:73
+msgid "New password"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:119
+msgid "New email address"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:123 mediagoblin/plugins/basic_auth/forms.py:28
+#: mediagoblin/plugins/basic_auth/forms.py:43
+#: mediagoblin/plugins/ldap/forms.py:39
+#: mediagoblin/templates/mediagoblin/edit/edit_account.html:64
+#: mediagoblin/tests/test_util.py:143
+msgid "Password"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:125
+msgid "Enter your password to prove you own this account."
+msgstr ""
+
+#: mediagoblin/edit/forms.py:155
+msgid "Identifier"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:156
+msgid "Value"
+msgstr ""
+
+#: mediagoblin/edit/views.py:78
+msgid "An entry with that slug already exists for this user."
+msgstr ""
+
+#: mediagoblin/edit/views.py:96
+msgid "You are editing another user's media. Proceed with caution."
+msgstr ""
+
+#: mediagoblin/edit/views.py:166
+#, python-format
+msgid "You added the attachment %s!"
+msgstr ""
+
+#: mediagoblin/edit/views.py:193
+msgid "You can only edit your own profile."
+msgstr ""
+
+#: mediagoblin/edit/views.py:199
+msgid "You are editing a user's profile. Proceed with caution."
+msgstr ""
+
+#: mediagoblin/edit/views.py:215
+msgid "Profile changes saved"
+msgstr ""
+
+#: mediagoblin/edit/views.py:248
+msgid "Account settings saved"
+msgstr ""
+
+#: mediagoblin/edit/views.py:282
+msgid "You need to confirm the deletion of your account."
+msgstr ""
+
+#: mediagoblin/edit/views.py:318 mediagoblin/submit/views.py:132
+#: mediagoblin/user_pages/views.py:252
+#, python-format
+msgid "You already have a collection called \"%s\"!"
+msgstr ""
+
+#: mediagoblin/edit/views.py:322
+msgid "A collection with that slug already exists for this user."
+msgstr ""
+
+#: mediagoblin/edit/views.py:337
+msgid "You are editing another user's collection. Proceed with caution."
+msgstr ""
+
+#: mediagoblin/edit/views.py:378
+msgid "Your email address has been verified."
+msgstr ""
+
+#: mediagoblin/edit/views.py:413 mediagoblin/plugins/basic_auth/views.py:200
+msgid "Wrong password"
+msgstr ""
+
+#: mediagoblin/gmg_commands/assetlink.py:60
+msgid "Cannot link theme... no theme set\n"
+msgstr ""
+
+#: mediagoblin/gmg_commands/assetlink.py:73
+msgid "No asset directory for this theme\n"
+msgstr ""
+
+#: mediagoblin/gmg_commands/assetlink.py:76
+msgid "However, old link directory symlink found; removed.\n"
+msgstr ""
+
+#: mediagoblin/gmg_commands/assetlink.py:112
+#, python-format
+msgid "Could not link \"%s\": %s exists and is not a symlink\n"
+msgstr ""
+
+#: mediagoblin/gmg_commands/assetlink.py:119
+#, python-format
+msgid "Skipping \"%s\"; already set up.\n"
+msgstr ""
+
+#: mediagoblin/gmg_commands/assetlink.py:124
+#, python-format
+msgid "Old link found for \"%s\"; removing.\n"
+msgstr ""
+
+#: mediagoblin/gmg_commands/batchaddmedia.py:34
+msgid ""
+"For more information about how to properly run this\n"
+"script (and how to format the metadata csv file), read the MediaGoblin\n"
+"documentation page on command line uploading\n"
+"<http://docs.mediagoblin.org/siteadmin/commandline-upload.html>"
+msgstr ""
+
+#: mediagoblin/gmg_commands/batchaddmedia.py:40
+msgid "Name of user these media entries belong to"
+msgstr ""
+
+#: mediagoblin/gmg_commands/batchaddmedia.py:43
+msgid "Path to the csv file containing metadata information."
+msgstr ""
+
+#: mediagoblin/gmg_commands/batchaddmedia.py:48
+msgid "Don't process eagerly, pass off to celery"
+msgstr ""
+
+#: mediagoblin/gmg_commands/batchaddmedia.py:63
+msgid "Sorry, no user by username '{username}' exists"
+msgstr ""
+
+#: mediagoblin/gmg_commands/batchaddmedia.py:74
+msgid "File at {path} not found, use -h flag for help"
+msgstr ""
+
+#: mediagoblin/gmg_commands/batchaddmedia.py:115
+msgid ""
+"Error with media '{media_id}' value '{error_path}': {error_msg}\n"
+"Metadata was not uploaded."
+msgstr ""
+
+#: mediagoblin/gmg_commands/batchaddmedia.py:141
+msgid ""
+"FAIL: Local file {filename} could not be accessed.\n"
+"{filename} will not be uploaded."
+msgstr ""
+
+#: mediagoblin/gmg_commands/batchaddmedia.py:157
+msgid ""
+"Successfully submitted {filename}!\n"
+"Be sure to look at the Media Processing Panel on your website to be sure it\n"
+"uploaded successfully."
+msgstr ""
+
+#: mediagoblin/gmg_commands/batchaddmedia.py:160
+msgid "FAIL: This file is larger than the upload limits for this site."
+msgstr ""
+
+#: mediagoblin/gmg_commands/batchaddmedia.py:163
+msgid "FAIL: This file will put this user past their upload limits."
+msgstr ""
+
+#: mediagoblin/gmg_commands/batchaddmedia.py:166
+msgid "FAIL: This user is already past their upload limits."
+msgstr ""
+
+#: mediagoblin/gmg_commands/batchaddmedia.py:168
+msgid "{files_uploaded} out of {files_attempted} files successfully submitted"
+msgstr ""
+
+#: mediagoblin/meddleware/csrf.py:134
+msgid ""
+"CSRF cookie not present. This is most likely the result of a cookie blocker "
+"or somesuch.<br/>Make sure to permit the settings of cookies for this "
+"domain."
+msgstr ""
+
+#: mediagoblin/media_types/__init__.py:79
+#: mediagoblin/media_types/__init__.py:101
+msgid "Sorry, I don't support that file type :("
+msgstr ""
+
+#: mediagoblin/media_types/blog/forms.py:26
+#: mediagoblin/media_types/blog/forms.py:35
+#: mediagoblin/plugins/oauth/forms.py:36
+msgid "Description"
+msgstr ""
+
+#: mediagoblin/media_types/blog/forms.py:40 mediagoblin/user_pages/forms.py:31
+msgid "I am sure I want to delete this"
+msgstr ""
+
+#: mediagoblin/media_types/blog/views.py:156 mediagoblin/submit/views.py:69
+msgid "Woohoo! Submitted!"
+msgstr ""
+
+#: mediagoblin/media_types/blog/views.py:198
+msgid "Woohoo! edited blogpost is submitted"
+msgstr ""
+
+#: mediagoblin/media_types/blog/views.py:320
+msgid "You deleted the Blog."
+msgstr ""
+
+#: mediagoblin/media_types/blog/views.py:326
+#: mediagoblin/user_pages/views.py:329
+msgid "The media was not deleted because you didn't check that you were sure."
+msgstr ""
+
+#: mediagoblin/media_types/blog/views.py:333
+msgid "You are about to delete another user's Blog. Proceed with caution."
+msgstr ""
+
+#: mediagoblin/media_types/blog/views.py:344
+msgid "The blog was not deleted because you have no rights."
+msgstr ""
+
+#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_admin_dashboard.html:43
+msgid "Add Blog Post"
+msgstr ""
+
+#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_admin_dashboard.html:50
+msgid "Edit Blog"
+msgstr ""
+
+#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_admin_dashboard.html:57
+msgid "Delete Blog"
+msgstr ""
+
+#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_admin_dashboard.html:92
+#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blogpost_draft_view.html:35
+#: mediagoblin/templates/mediagoblin/user_pages/blog_media.html:76
+#: mediagoblin/templates/mediagoblin/user_pages/collection.html:52
+#: mediagoblin/templates/mediagoblin/user_pages/media.html:84
+msgid "Edit"
+msgstr ""
+
+#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_admin_dashboard.html:93
+#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blogpost_draft_view.html:36
+#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/delete.html:39
+#: mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/edit.html:39
+#: mediagoblin/templates/mediagoblin/user_pages/blog_media.html:80
+#: mediagoblin/templates/mediagoblin/user_pages/collection.html:56
+#: mediagoblin/templates/mediagoblin/user_pages/media.html:88
+msgid "Delete"
+msgstr ""
+
+#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_admin_dashboard.html:102
+msgid "<em> Go to list view </em>"
+msgstr ""
+
+#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_admin_dashboard.html:104
+msgid " No blog post yet. "
+msgstr ""
+
+#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_confirm_delete.html:30
+#: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30
+#, python-format
+msgid "Really delete %(title)s?"
+msgstr ""
+
+#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_confirm_delete.html:47
+#: mediagoblin/templates/mediagoblin/edit/attachments.html:61
+#: mediagoblin/templates/mediagoblin/edit/delete_account.html:42
+#: mediagoblin/templates/mediagoblin/edit/edit.html:41
+#: mediagoblin/templates/mediagoblin/edit/edit_collection.html:32
+#: mediagoblin/templates/mediagoblin/user_pages/collection_confirm_delete.html:54
+#: mediagoblin/templates/mediagoblin/user_pages/collection_item_confirm_remove.html:60
+#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:67
+#: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:48
+msgid "Cancel"
+msgstr ""
+
+#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_confirm_delete.html:48
+#: mediagoblin/templates/mediagoblin/edit/delete_account.html:44
+#: mediagoblin/templates/mediagoblin/user_pages/collection_confirm_delete.html:56
+#: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:49
+msgid "Delete permanently"
+msgstr ""
+
+#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_edit_create.html:26
+msgid "Create/Edit a Blog"
+msgstr ""
+
+#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_edit_create.html:37
+#: mediagoblin/plugins/oauth/templates/oauth/client/register.html:29
+#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/add.html:39
+#: mediagoblin/templates/mediagoblin/submit/collection.html:30
+#: mediagoblin/templates/mediagoblin/submit/start.html:39
+#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:68
+msgid "Add"
+msgstr ""
+
+#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_post_edit_create.html:23
+msgid "Create/Edit a blog post."
+msgstr ""
+
+#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_post_edit_create.html:29
+msgid "Create/Edit a Blog Post."
+msgstr ""
+
+#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_post_listing.html:24
+#, python-format
+msgid "%(blog_owner_name)s's Blog"
+msgstr ""
+
+#: mediagoblin/media_types/blog/templates/mediagoblin/blog/list_of_blogs.html:46
+msgid "View"
+msgstr ""
+
+#: mediagoblin/media_types/blog/templates/mediagoblin/blog/list_of_blogs.html:65
+msgid "Create a Blog"
+msgstr ""
+
+#: mediagoblin/media_types/blog/templates/mediagoblin/blog/url_to_dashboard.html:20
+msgid " Blog Dashboard "
+msgstr ""
+
+#: mediagoblin/media_types/pdf/processing.py:142
+msgid "unoconv failing to run, check log file"
+msgstr ""
+
+#: mediagoblin/media_types/video/processing.py:44
+msgid "Video transcoding failed"
+msgstr ""
+
+#: mediagoblin/moderation/forms.py:21
+msgid "Take away privilege"
+msgstr ""
+
+#: mediagoblin/moderation/forms.py:22
+msgid "Ban the user"
+msgstr ""
+
+#: mediagoblin/moderation/forms.py:23
+msgid "Send the user a message"
+msgstr ""
+
+#: mediagoblin/moderation/forms.py:24
+msgid "Delete the content"
+msgstr ""
+
+#: mediagoblin/moderation/forms.py:53 mediagoblin/moderation/forms.py:118
+msgid "User will be banned until:"
+msgstr ""
+
+#: mediagoblin/moderation/forms.py:57
+msgid "Why are you banning this User?"
+msgstr ""
+
+#: mediagoblin/moderation/forms.py:109
+msgid "What action will you take to resolve the report?"
+msgstr ""
+
+#: mediagoblin/moderation/forms.py:115
+msgid "What privileges will you take away?"
+msgstr ""
+
+#: mediagoblin/moderation/forms.py:122
+msgid "Why user was banned:"
+msgstr ""
+
+#: mediagoblin/moderation/forms.py:125
+msgid "Message to user:"
+msgstr ""
+
+#: mediagoblin/moderation/forms.py:128
+msgid "Resolution content:"
+msgstr ""
+
+#: mediagoblin/moderation/tools.py:34
+msgid ""
+"\n"
+"{mod} took away {user}'s {privilege} privileges."
+msgstr ""
+
+#: mediagoblin/moderation/tools.py:47
+msgid ""
+"\n"
+"{mod} banned user {user} {expiration_date}."
+msgstr ""
+
+#: mediagoblin/moderation/tools.py:51
+msgid "until {date}"
+msgstr ""
+
+#: mediagoblin/moderation/tools.py:53
+#: mediagoblin/templates/mediagoblin/banned.html:30
+msgid "indefinitely"
+msgstr ""
+
+#: mediagoblin/moderation/tools.py:62
+msgid ""
+"\n"
+"{mod} sent a warning email to the {user}."
+msgstr ""
+
+#: mediagoblin/moderation/tools.py:71
+msgid ""
+"\n"
+"{mod} deleted the comment."
+msgstr ""
+
+#: mediagoblin/moderation/tools.py:78
+msgid ""
+"\n"
+"{mod} deleted the media entry."
+msgstr ""
+
+#: mediagoblin/moderation/tools.py:91
+msgid "Warning from"
+msgstr ""
+
+#: mediagoblin/notifications/tools.py:54 mediagoblin/user_pages/lib.py:60
+msgid "commented on your post"
+msgstr ""
+
+#: mediagoblin/notifications/views.py:35
+#, python-format
+msgid "Subscribed to comments on %s!"
+msgstr ""
+
+#: mediagoblin/notifications/views.py:48
+#, python-format
+msgid "You will not receive notifications for comments on %s."
+msgstr ""
+
+#: mediagoblin/oauth/views.py:241
+msgid "Must provide an oauth_token."
+msgstr ""
+
+#: mediagoblin/oauth/views.py:246 mediagoblin/oauth/views.py:297
+msgid "No request token found."
+msgstr ""
+
+#: mediagoblin/plugins/api/views.py:76 mediagoblin/plugins/piwigo/views.py:155
+#: mediagoblin/submit/views.py:78
+msgid "Sorry, the file size is too big."
+msgstr ""
+
+#: mediagoblin/plugins/api/views.py:79 mediagoblin/plugins/piwigo/views.py:158
+#: mediagoblin/submit/views.py:81
+msgid "Sorry, uploading this file will put you over your upload limit."
+msgstr ""
+
+#: mediagoblin/plugins/api/views.py:83 mediagoblin/plugins/piwigo/views.py:162
+#: mediagoblin/submit/views.py:87
+msgid "Sorry, you have reached your upload limit."
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/forms.py:21
+msgid "Enter the URL for the media to be featured"
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/tools.py:132
+msgid "Primary"
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/tools.py:133
+msgid "Secondary"
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/tools.py:134
+msgid "Tertiary"
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/tools.py:135
+msgid "-----------{display_type}-Features---------------------------\n"
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:33
+msgid "How does this work?"
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:34
+msgid "How to feature media?"
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:37
+msgid ""
+"\n"
+" Go to the page of the media entry you want to feature. Copy it's URL and\n"
+" then paste it into a new line in the text box above. There should be only\n"
+" one url per line. The url that you paste into the text box should be under\n"
+" the header describing how prominent a feature it will be (whether Primary,\n"
+" Secondary, or Tertiary). Once all of the media that you want to feature are\n"
+" inside the text box, click the Submit Query button, and your media should be\n"
+" displayed on the front page.\n"
+" "
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:48
+msgid "Is there another way to manage featured media?"
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:51
+msgid ""
+"\n"
+" Yes. If you would prefer, you may go to the media homepage of the piece\n"
+" of media you would like to feature or unfeature and look at the bar to\n"
+" the side of the media entry. If the piece of media has not been featured\n"
+" yet you should see a button that says \"Feature\". Press that button and\n"
+" the media will be featured as a Primary Feature at the top of the page.\n"
+" All other featured media entries will remain as features, but will be\n"
+" pushed further down the page.<br /><br />\n"
+"\n"
+" If you go to the media homepage of a piece of media that is currently\n"
+" featured, you will see the options \"Unfeature\", \"Promote\" and \"Demote\"\n"
+" where previously there was the button which said \"Feature\". Click\n"
+" Unfeature and that media entry will no longer be displayed on the\n"
+" front page, although you can feature it again at any point. Promote\n"
+" moves the featured media higher up on the page and makes it more\n"
+" prominent and Demote moves the featured media lower down and makes it\n"
+" less prominent.\n"
+" "
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:70
+msgid "What is a Primary Feature? What is a Secondary Feature?"
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:74
+msgid ""
+"\n"
+" These categories just describe how prominent a feature will be on your\n"
+" front page. Primary Features are placed at the top of the front page and are\n"
+" much larger. Next are Secondary Features, which are slightly smaller.\n"
+" Tertiary Features make up a grid at the bottom of the page.<br /><br />\n"
+"\n"
+" Primary Features also can display longer descriptions than Secondary\n"
+" Features, and Secondary Features can display longer descriptions than\n"
+" Tertiary Features."
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:85
+msgid ""
+"How to decide what information is displayed when a media entry is\n"
+" featured?"
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:88
+msgid ""
+"\n"
+" When a media entry is featured, the entry's title, it's thumbnail and a\n"
+" portion of its description will be displayed on your website's front page.\n"
+" The number of characters displayed varies on the prominence of the feature.\n"
+" Primary Features display the first 512 characters of their description,\n"
+" Secondary Features display the first 256 characters of their description,\n"
+" and Tertiary Features display the first 128 characters of their description.\n"
+" "
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:98
+msgid "How to unfeature a piece of media?"
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:102
+msgid ""
+"\n"
+" Unfeature a media by removing its line from the above textarea and then\n"
+" pressing the Submit Query button.\n"
+" "
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:108
+msgid "CAUTION:"
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:110
+msgid ""
+"\n"
+" When copying and pasting urls into the above text box, be aware that if\n"
+" you make a typo, once you press Submit Query, your media entry will NOT be\n"
+" featured. Make sure that all your intended Media Entries are featured.\n"
+" "
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:26
+msgid ""
+"\n"
+"Feature Media "
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:28
+msgid "Feature"
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:34
+msgid ""
+"\n"
+"Unfeature Media "
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:36
+msgid "Unfeature"
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:42
+msgid ""
+"\n"
+"Promote Feature "
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:44
+msgid "Promote"
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:50
+msgid ""
+"\n"
+"Demote Feature "
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:52
+msgid "Demote"
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/recent_media.html:30
+#: mediagoblin/templates/mediagoblin/root.html:32
+msgid "Most recent media"
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/root.html:61
+msgid "Nothing is currently featured."
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/root.html:62
+msgid ""
+"If you would like to feature a\n"
+" piece of media, go to that media entry's homepage and click the button\n"
+" that says <a class=\"button_action\">Feature</a>."
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/root.html:67
+#, python-format
+msgid ""
+"You're seeing this page because you are a user capable of\n"
+" featuring media, a regular user would see a blank page, so be sure to\n"
+" have media featured as long as your instance has the 'archivalook'\n"
+" plugin enabled. A more advanced tool to manage features can be found\n"
+" in the <a href=\"%(featured_media_url)s\">feature management panel.</a>"
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/root.html:79
+msgid "View most recent media"
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/bits/feature_dropdown.html:22
+msgid "Feature management panel"
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/audio_primary.html:43
+msgid ""
+"Sorry, this audio will not work because\n"
+"\tyour web browser does not support HTML5\n"
+"\taudio."
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/audio_primary.html:46
+msgid ""
+"You can get a modern web browser that\n"
+"\tcan play the audio at <a href=\"http://getfirefox.com\">\n"
+"\t http://getfirefox.com</a>!"
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/video_primary.html:43
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/video_secondary.html:43
+msgid ""
+"Sorry, this video will not work because\n"
+" your web browser does not support HTML5 \n"
+" video."
+msgstr ""
+
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/video_primary.html:46
+#: mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/video_secondary.html:46
+msgid ""
+"You can get a modern web browser that \n"
+" can play this video at <a href=\"http://getfirefox.com\">\n"
+" http://getfirefox.com</a>!"
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/forms.py:24
+#: mediagoblin/plugins/ldap/forms.py:35 mediagoblin/plugins/openid/forms.py:27
+#: mediagoblin/plugins/persona/forms.py:24
+#: mediagoblin/templates/mediagoblin/moderation/user_panel.html:76
+msgid "Username"
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/forms.py:32
+#: mediagoblin/plugins/ldap/forms.py:28 mediagoblin/plugins/openid/forms.py:31
+#: mediagoblin/plugins/persona/forms.py:28
+#: mediagoblin/plugins/persona/forms.py:39
+msgid "Email address"
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/forms.py:39
+msgid "Username or Email"
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/forms.py:46
+msgid "Stay logged in"
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/forms.py:51
+msgid "Username or email"
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/views.py:54
+msgid ""
+"If that email address (case sensitive!) is registered an email has been sent"
+" with instructions on how to change your password."
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/views.py:65
+msgid "Couldn't find someone with that username."
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/views.py:68
+msgid ""
+"An email has been sent with instructions on how to change your password."
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/views.py:75
+msgid ""
+"Could not send password recovery email as your username is inactive or your "
+"account's email address has not been verified."
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/views.py:123
+msgid "The user id is incorrect."
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/views.py:139
+msgid "You can now log in using your new password."
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/views.py:163
+msgid ""
+"You are no longer an active user. Please contact the system admin to "
+"reactivate your account."
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/views.py:215
+msgid "Your password was changed successfully"
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/change_fp.html:28
+#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/change_fp.html:36
+msgid "Set your new password"
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/change_fp.html:39
+msgid "Set password"
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/change_pass.html:28
+#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/change_pass.html:38
+#, python-format
+msgid "Changing %(username)s's password"
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/change_pass.html:45
+#: mediagoblin/templates/mediagoblin/edit/change_email.html:40
+msgid "Save"
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/create_account_link.html:22
+msgid "Don't have an account yet?"
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/create_account_link.html:24
+msgid "Create one here!"
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/edit_link.html:22
+msgid "Change your password."
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/forgot_password.html:23
+#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/forgot_password.html:31
+msgid "Recover password"
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/forgot_password.html:34
+msgid "Send instructions"
+msgstr ""
+
+#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/fp_link.html:22
+msgid "Forgot your password?"
+msgstr ""
+
+#: mediagoblin/plugins/geolocation/templates/mediagoblin/plugins/geolocation/map.html:24
+msgid "Location"
+msgstr ""
+
+#: mediagoblin/plugins/geolocation/templates/mediagoblin/plugins/geolocation/map.html:52
+#, python-format
+msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>"
+msgstr ""
+
+#: mediagoblin/plugins/ldap/templates/mediagoblin/plugins/ldap/create_account_link.html:22
+msgid "Sign in to create an account!"
+msgstr ""
+
+#: mediagoblin/plugins/metadata_display/templates/mediagoblin/plugins/metadata_display/metadata_table.html:22
+msgid "Metadata"
+msgstr ""
+
+#: mediagoblin/plugins/metadata_display/templates/mediagoblin/plugins/metadata_display/metadata_table.html:40
+msgid "Edit Metadata"
+msgstr ""
+
+#: mediagoblin/plugins/oauth/forms.py:29
+msgid "Allow"
+msgstr ""
+
+#: mediagoblin/plugins/oauth/forms.py:30
+msgid "Deny"
+msgstr ""
+
+#: mediagoblin/plugins/oauth/forms.py:34
+msgid "Name"
+msgstr ""
+
+#: mediagoblin/plugins/oauth/forms.py:35
+msgid "The name of the OAuth client"
+msgstr ""
+
+#: mediagoblin/plugins/oauth/forms.py:38
+msgid ""
+"This will be visible to users allowing your\n"
+" application to authenticate as them."
+msgstr ""
+
+#: mediagoblin/plugins/oauth/forms.py:40
+msgid "Type"
+msgstr ""
+
+#: mediagoblin/plugins/oauth/forms.py:45
+msgid ""
+"<strong>Confidential</strong> - The client can\n"
+" make requests to the GNU MediaGoblin instance that can not be\n"
+" intercepted by the user agent (e.g. server-side client).<br />\n"
+" <strong>Public</strong> - The client can't make confidential\n"
+" requests to the GNU MediaGoblin instance (e.g. client-side\n"
+" JavaScript client)."
+msgstr ""
+
+#: mediagoblin/plugins/oauth/forms.py:52
+msgid "Redirect URI"
+msgstr ""
+
+#: mediagoblin/plugins/oauth/forms.py:54
+msgid ""
+"The redirect URI for the applications, this field\n"
+" is <strong>required</strong> for public clients."
+msgstr ""
+
+#: mediagoblin/plugins/oauth/forms.py:66
+msgid "This field is required for public clients"
+msgstr ""
+
+#: mediagoblin/plugins/oauth/views.py:55
+msgid "The client {0} has been registered!"
+msgstr ""
+
+#: mediagoblin/plugins/oauth/templates/oauth/client/connections.html:22
+msgid "OAuth client connections"
+msgstr ""
+
+#: mediagoblin/plugins/oauth/templates/oauth/client/list.html:22
+msgid "Your OAuth clients"
+msgstr ""
+
+#: mediagoblin/plugins/openid/__init__.py:97
+#: mediagoblin/plugins/openid/views.py:268
+#: mediagoblin/plugins/openid/views.py:297
+msgid "Sorry, an account is already registered to that OpenID."
+msgstr ""
+
+#: mediagoblin/plugins/openid/forms.py:38
+msgid "OpenID"
+msgstr ""
+
+#: mediagoblin/plugins/openid/views.py:48
+msgid "Sorry, the OpenID server could not be found"
+msgstr ""
+
+#: mediagoblin/plugins/openid/views.py:61
+#, python-format
+msgid "No OpenID service was found for %s"
+msgstr ""
+
+#: mediagoblin/plugins/openid/views.py:106
+#, python-format
+msgid "Verification of %s failed: %s"
+msgstr ""
+
+#: mediagoblin/plugins/openid/views.py:117
+msgid "Verification cancelled"
+msgstr ""
+
+#: mediagoblin/plugins/openid/views.py:314
+msgid "Your OpenID url was saved successfully."
+msgstr ""
+
+#: mediagoblin/plugins/openid/views.py:338
+#: mediagoblin/plugins/openid/views.py:393
+msgid "You can't delete your only OpenID URL unless you have a password set"
+msgstr ""
+
+#: mediagoblin/plugins/openid/views.py:343
+#: mediagoblin/plugins/openid/views.py:402
+msgid "That OpenID is not registered to this account."
+msgstr ""
+
+#: mediagoblin/plugins/openid/views.py:385
+msgid "OpenID was successfully removed."
+msgstr ""
+
+#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/add.html:23
+#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/add.html:31
+#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/delete.html:34
+#: mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/edit.html:23
+msgid "Add an OpenID"
+msgstr ""
+
+#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/add.html:34
+#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/delete.html:23
+#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/delete.html:31
+msgid "Delete an OpenID"
+msgstr ""
+
+#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/edit_link.html:21
+msgid "OpenID's"
+msgstr ""
+
+#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login.html:28
+#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login.html:36
+#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login.html:57
+#: mediagoblin/templates/mediagoblin/base.html:122
+#: mediagoblin/templates/mediagoblin/auth/login.html:28
+#: mediagoblin/templates/mediagoblin/auth/login.html:36
+#: mediagoblin/templates/mediagoblin/auth/login.html:47
+msgid "Log in"
+msgstr ""
+
+#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login.html:39
+#: mediagoblin/templates/mediagoblin/auth/login.html:39
+msgid "Logging in failed!"
+msgstr ""
+
+#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login.html:44
+msgid "Log in to create an account!"
+msgstr ""
+
+#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login.html:51
+msgid "Or login with a password!"
+msgstr ""
+
+#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login_link.html:23
+msgid "Or login with OpenID!"
+msgstr ""
+
+#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/register_link.html:23
+msgid "Or register with OpenID!"
+msgstr ""
+
+#: mediagoblin/plugins/persona/__init__.py:90
+msgid "Sorry, an account is already registered to that Persona email."
+msgstr ""
+
+#: mediagoblin/plugins/persona/views.py:138
+msgid "The Persona email address was successfully removed."
+msgstr ""
+
+#: mediagoblin/plugins/persona/views.py:144
+msgid ""
+"You can't delete your only Persona email address unless you have a password "
+"set."
+msgstr ""
+
+#: mediagoblin/plugins/persona/views.py:149
+msgid "That Persona email address is not registered to this account."
+msgstr ""
+
+#: mediagoblin/plugins/persona/views.py:176
+msgid ""
+"Sorry, an account is already registered with that Persona email address."
+msgstr ""
+
+#: mediagoblin/plugins/persona/views.py:192
+msgid "Your Persona email address was saved successfully."
+msgstr ""
+
+#: mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/edit.html:31
+msgid "Delete a Persona email address"
+msgstr ""
+
+#: mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/edit.html:34
+msgid "Add a Persona email address"
+msgstr ""
+
+#: mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/edit_link.html:21
+msgid "Persona's"
+msgstr ""
+
+#: mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/login_link.html:22
+msgid "Or login with Persona!"
+msgstr ""
+
+#: mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/register_link.html:22
+msgid "Or register with Persona!"
+msgstr ""
+
+#: mediagoblin/processing/__init__.py:420
+msgid "Invalid file given for media type."
+msgstr ""
+
+#: mediagoblin/processing/__init__.py:427
+msgid "Copying to public storage failed."
+msgstr ""
+
+#: mediagoblin/processing/__init__.py:435
+msgid "An acceptable processing file was not found"
+msgstr ""
+
+#: mediagoblin/submit/forms.py:30
+msgid "Max file size: {0} mb"
+msgstr ""
+
+#: mediagoblin/submit/forms.py:34
+msgid "File"
+msgstr ""
+
+#: mediagoblin/submit/forms.py:41
+msgid ""
+"You can use\n"
+" <a href=\"http://daringfireball.net/projects/markdown/basics\">\n"
+" Markdown</a> for formatting."
+msgstr ""
+
+#: mediagoblin/submit/views.py:55
+msgid "You must provide a file."
+msgstr ""
+
+#: mediagoblin/submit/views.py:138
+#, python-format
+msgid "Collection \"%s\" added!"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/banned.html:20
+msgid "You are Banned."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/banned.html:24
+#: mediagoblin/templates/mediagoblin/error.html:24
+msgid "Image of goblin stressing out"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/banned.html:26
+msgid "You have been banned"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/banned.html:28
+#, python-format
+msgid "until %(until_when)s"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:97
+msgid "Verify your email!"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:104
+#: mediagoblin/templates/mediagoblin/base.html:112
+msgid "log out"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:131
+#, python-format
+msgid "<a href=\"%(user_url)s\">%(user_name)s</a>'s account"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:138
+msgid "Change account settings"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:142
+#: mediagoblin/templates/mediagoblin/base.html:165
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:21
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:27
+#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:21
+#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:26
+msgid "Media processing panel"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:152
+msgid "Log out"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:155
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:113
+msgid "Add media"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:158
+#: mediagoblin/templates/mediagoblin/user_pages/collection_list.html:41
+msgid "Create new collection"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:163
+msgid "Moderation powers:"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:169
+msgid "User management panel"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:173
+msgid "Report management panel"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/api/authorize.html:21
+msgid "Authorization"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/api/authorize.html:26
+#: mediagoblin/templates/mediagoblin/api/authorize.html:53
+msgid "Authorize"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/api/authorize.html:29
+msgid "You are logged in as"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/api/authorize.html:33
+msgid "Do you want to authorize "
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/api/authorize.html:37
+msgid "an unknown application"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/api/authorize.html:39
+msgid " to access your account? "
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/api/authorize.html:41
+msgid "Applications with access to your account can: "
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/api/authorize.html:43
+msgid "Post new media as you"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/api/authorize.html:44
+msgid "See your information (e.g profile, media, etc...)"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/api/authorize.html:45
+msgid "Change your information"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/api/oob.html:21
+msgid "Authorization Finished"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/api/oob.html:26
+msgid "Authorization Complete"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/api/oob.html:28
+msgid "Copy and paste this into your client:"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/auth/register.html:28
+#: mediagoblin/templates/mediagoblin/auth/register.html:36
+msgid "Create an account!"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/auth/register.html:41
+msgid "Create"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/auth/verification_email.txt:19
+#, python-format
+msgid ""
+"Hi %(username)s,\n"
+"\n"
+"to activate your GNU MediaGoblin account, open the following URL in\n"
+"your web browser:\n"
+"\n"
+"%(verification_url)s"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/bits/base_footer.html:21
+#, python-format
+msgid ""
+"Powered by <a href=\"http://mediagoblin.org/\" title='Version "
+"%(version)s'>MediaGoblin</a>, a <a href=\"http://gnu.org/\">GNU</a> project."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/bits/base_footer.html:24
+#, python-format
+msgid ""
+"Released under the <a "
+"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a "
+"href=\"%(source_link)s\">Source code</a> available."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/bits/base_footer.html:30
+msgid "Terms of Service"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/bits/frontpage_welcome.html:20
+msgid "Explore"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/bits/frontpage_welcome.html:24
+msgid "Hi there, welcome to this MediaGoblin site!"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/bits/frontpage_welcome.html:25
+msgid ""
+"This site is running <a href=\"http://mediagoblin.org\">MediaGoblin</a>, an "
+"extraordinarily great piece of media hosting software."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/bits/frontpage_welcome.html:27
+msgid ""
+"To add your own media, place comments, and more, you can log in with your "
+"MediaGoblin account."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/bits/frontpage_welcome.html:29
+msgid "Don't have one yet? It's easy!"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/bits/frontpage_welcome.html:36
+msgid ""
+"\n"
+" >Create an account at this site</a>\n"
+" or"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/bits/frontpage_welcome.html:42
+msgid ""
+"\n"
+" <a class=\"button_action\" href=\"http://mediagoblin.readthedocs.org/\">Set up MediaGoblin on your own server</a>"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/bits/logo.html:23
+#: mediagoblin/themes/airy/templates/mediagoblin/bits/logo.html:23
+msgid "MediaGoblin logo"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/attachments.html:23
+#: mediagoblin/templates/mediagoblin/edit/attachments.html:35
+#, python-format
+msgid "Editing attachments for %(media_title)s"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/attachments.html:44
+#: mediagoblin/templates/mediagoblin/user_pages/media.html:204
+#: mediagoblin/templates/mediagoblin/user_pages/media.html:220
+msgid "Attachments"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/attachments.html:57
+#: mediagoblin/templates/mediagoblin/user_pages/media.html:226
+msgid "Add attachment"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/attachments.html:63
+#: mediagoblin/templates/mediagoblin/edit/edit.html:42
+#: mediagoblin/templates/mediagoblin/edit/edit_account.html:47
+#: mediagoblin/templates/mediagoblin/edit/edit_collection.html:33
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:40
+msgid "Save changes"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/change_email.html:23
+#: mediagoblin/templates/mediagoblin/edit/change_email.html:33
+#, python-format
+msgid "Changing %(username)s's email"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/delete_account.html:28
+#, python-format
+msgid "Really delete user '%(user_name)s' and all related media/comments?"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/delete_account.html:35
+msgid "Yes, really delete my account"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:23
+#: mediagoblin/templates/mediagoblin/edit/edit.html:35
+#, python-format
+msgid "Editing %(media_title)s"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/edit_account.html:28
+#: mediagoblin/templates/mediagoblin/edit/edit_account.html:40
+#, python-format
+msgid "Changing %(username)s's account settings"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/edit_account.html:54
+msgid "Delete my account"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/edit_account.html:59
+msgid "Email"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/edit_collection.html:29
+#, python-format
+msgid "Editing %(collection_title)s"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:23
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:34
+#, python-format
+msgid "Editing %(username)s's profile"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/metadata.html:67
+#, python-format
+msgid "Metadata for \"%(media_name)s\""
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/metadata.html:72
+msgid "MetaData"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/metadata.html:80
+msgid "Add new Row"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/metadata.html:83
+msgid "Update Metadata"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/metadata.html:87
+msgid "Clear empty Rows"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/verification.txt:19
+#, python-format
+msgid ""
+"Hi,\n"
+"\n"
+"We wanted to verify that you are %(username)s. If this is the case, then \n"
+"please follow the link below to verify your new email address.\n"
+"\n"
+"%(verification_url)s\n"
+"\n"
+"If you are not %(username)s or didn't request an email change, you can ignore\n"
+"this email."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/fragments/header_notifications.html:4
+msgid "New comments"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/fragments/header_notifications.html:24
+#: mediagoblin/templates/mediagoblin/media_displays/image.html:39
+#: mediagoblin/templates/mediagoblin/moderation/report.html:57
+#: mediagoblin/templates/mediagoblin/moderation/report.html:120
+#: mediagoblin/templates/mediagoblin/user_pages/blog_media.html:131
+#: mediagoblin/templates/mediagoblin/user_pages/blog_media.html:151
+#: mediagoblin/templates/mediagoblin/user_pages/media.html:146
+#: mediagoblin/templates/mediagoblin/user_pages/media.html:181
+#: mediagoblin/templates/mediagoblin/user_pages/report.html:48
+#, python-format
+msgid "%(formatted_time)s ago"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/fragments/header_notifications.html:41
+msgid "Mark all read"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/listings/collection.html:30
+#: mediagoblin/templates/mediagoblin/listings/collection.html:35
+#: mediagoblin/templates/mediagoblin/listings/tag.html:30
+#: mediagoblin/templates/mediagoblin/listings/tag.html:35
+#, python-format
+msgid "Media tagged with: %(tag_name)s"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34
+#: mediagoblin/templates/mediagoblin/media_displays/audio.html:56
+#: mediagoblin/templates/mediagoblin/media_displays/pdf.html:67
+#: mediagoblin/templates/mediagoblin/media_displays/video.html:74
+msgid "Download"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/media_displays/ascii.html:38
+msgid "Original"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/media_displays/audio.html:44
+msgid ""
+"Sorry, this audio will not work because \n"
+"\tyour web browser does not support HTML5 \n"
+"\taudio."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/media_displays/audio.html:47
+msgid ""
+"You can get a modern web browser that \n"
+"\tcan play the audio at <a href=\"http://getfirefox.com\">\n"
+"\t http://getfirefox.com</a>!"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/media_displays/audio.html:60
+#: mediagoblin/templates/mediagoblin/media_displays/pdf.html:73
+#: mediagoblin/templates/mediagoblin/media_displays/video.html:80
+msgid "Original file"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/media_displays/audio.html:63
+msgid "WebM file (Vorbis codec)"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/media_displays/image.html:36
+msgid "Created"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/media_displays/pdf.html:59
+#: mediagoblin/templates/mediagoblin/media_displays/stl.html:90
+#: mediagoblin/templates/mediagoblin/media_displays/stl.html:96
+#: mediagoblin/templates/mediagoblin/media_displays/stl.html:102
+#: mediagoblin/templates/mediagoblin/media_displays/stl.html:108
+#: mediagoblin/templates/mediagoblin/user_pages/blog_media.html:59
+#: mediagoblin/templates/mediagoblin/user_pages/blog_media.html:65
+#: mediagoblin/templates/mediagoblin/user_pages/media.html:62
+#: mediagoblin/templates/mediagoblin/user_pages/media.html:68
+#, python-format
+msgid "Image for %(media_title)s"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/media_displays/pdf.html:81
+msgid "PDF file"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/media_displays/stl.html:116
+msgid "Perspective"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/media_displays/stl.html:119
+msgid "Front"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/media_displays/stl.html:122
+msgid "Top"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/media_displays/stl.html:125
+msgid "Side"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/media_displays/stl.html:130
+msgid "WebGL"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/media_displays/stl.html:136
+msgid "Download model"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/media_displays/stl.html:145
+msgid "File Format"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/media_displays/stl.html:147
+msgid "Object Height"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/media_displays/video.html:63
+msgid ""
+"Sorry, this video will not work because\n"
+" your web browser does not support HTML5 \n"
+" video."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/media_displays/video.html:66
+msgid ""
+"You can get a modern web browser that \n"
+" can play this video at <a href=\"http://getfirefox.com\">\n"
+" http://getfirefox.com</a>!"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/media_displays/video.html:88
+msgid "WebM file (VP8/Vorbis)"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:30
+msgid ""
+"Here you can track the state of media being processed on this instance."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:33
+#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:32
+msgid "Media in-processing"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:38
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:67
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:98
+#: mediagoblin/templates/mediagoblin/moderation/user_panel.html:75
+msgid "ID"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:39
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:68
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:99
+msgid "User"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:41
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:70
+msgid "When submitted"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:42
+msgid "Transcoding progress"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:53
+msgid "Unknown"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:59
+#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:56
+msgid "No media in-processing"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:62
+#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:59
+msgid "These uploads failed to process:"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:71
+msgid "Reason for failure"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:72
+msgid "Failure metadata"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:91
+#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:86
+msgid "No failed entries!"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:93
+msgid "Last 10 successful uploads"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:101
+msgid "Submitted"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:113
+#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:107
+msgid "No processed entries, yet!"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report.html:27
+msgid "Sorry, no such report found."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report.html:33
+msgid "Return to Reports Panel"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report.html:35
+#: mediagoblin/templates/mediagoblin/user_pages/media.html:162
+msgid "Report"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report.html:38
+msgid "Reported comment"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report.html:83
+#, python-format
+msgid ""
+"\n"
+" ❖ Reported media by <a href=\"%(user_url)s\">%(user_name)s</a>\n"
+" "
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report.html:92
+#, python-format
+msgid ""
+"\n"
+" CONTENT BY\n"
+" <a href=\"%(user_url)s\"> %(user_name)s</a>\n"
+" HAS BEEN DELETED\n"
+" "
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report.html:102
+msgid "Reason for report:"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report.html:133
+#: mediagoblin/templates/mediagoblin/moderation/user.html:136
+msgid "Resolve"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report.html:137
+#: mediagoblin/templates/mediagoblin/moderation/report.html:157
+msgid "Resolve This Report"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report.html:149
+msgid "Status"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report.html:151
+msgid "RESOLVED"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report.html:159
+msgid "You cannot take action against an administrator"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:22
+#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:27
+msgid "Report panel"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:30
+msgid ""
+"\n"
+" Here you can look up open reports that have been filed by users.\n"
+" "
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:35
+msgid "Active Reports Filed"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:77
+#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:173
+msgid "Offender"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:78
+msgid "When Reported"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:79
+#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:175
+msgid "Reported By"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:80
+#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:176
+msgid "Reason"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:95
+#, python-format
+msgid ""
+"\n"
+" Comment Report #%(report_id)s\n"
+" "
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:111
+#, python-format
+msgid ""
+"\n"
+" Media Report #%(report_id)s\n"
+" "
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:125
+msgid "No open reports found."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:127
+msgid "Closed Reports"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:172
+msgid "Resolved"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:174
+msgid "Action Taken"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:188
+#, python-format
+msgid ""
+"\n"
+" Closed Report #%(report_id)s\n"
+" "
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:202
+msgid "No closed reports found."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:23
+#, python-format
+msgid "User: %(username)s"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:42
+msgid "Return to Users Panel"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:49
+msgid "Sorry, no such user found."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:53
+#: mediagoblin/templates/mediagoblin/user_pages/user_nonactive.html:40
+#: mediagoblin/templates/mediagoblin/user_pages/user_nonactive.html:60
+msgid "Email verification needed"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:55
+msgid ""
+"Someone has registered an account with this username, but it still has\n"
+" to be activated."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:66
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:34
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:46
+#: mediagoblin/templates/mediagoblin/user_pages/user_nonactive.html:25
+#, python-format
+msgid "%(username)s's profile"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:68
+#, python-format
+msgid "BANNED until %(expiration_date)s"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:72
+msgid "Banned Indefinitely"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:78
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:62
+msgid "This user hasn't filled in their profile (yet)."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:89
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:57
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:74
+msgid "Edit profile"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:96
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:81
+msgid "Browse collections"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:105
+#, python-format
+msgid "Active Reports on %(username)s"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:112
+msgid "Report ID"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:113
+msgid "Reported Content"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:114
+msgid "Description of Report"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:122
+#, python-format
+msgid "Report #%(report_number)s"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:129
+msgid "Reported Comment"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:131
+msgid "Reported Media Entry"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:142
+#, python-format
+msgid "No active reports filed on %(username)s"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:150
+#, python-format
+msgid "All reports on %(username)s"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:155
+#, python-format
+msgid "All reports that %(username)s has filed"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:164
+#, python-format
+msgid "%(username)s's Privileges"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:172
+msgid "Privilege"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:173
+msgid "Granted"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:180
+msgid "Yes"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:182
+msgid "No"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:213
+msgid "Ban User"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user.html:218
+msgid "UnBan User"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user_panel.html:21
+#: mediagoblin/templates/mediagoblin/moderation/user_panel.html:26
+msgid "User panel"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user_panel.html:29
+msgid ""
+"\n"
+" Here you can look up users in order to take punitive actions on them.\n"
+" "
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user_panel.html:34
+msgid "Active Users"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user_panel.html:77
+msgid "When Joined"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user_panel.html:78
+msgid "# of Comments Posted"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/moderation/user_panel.html:95
+msgid "No users found."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/submit/collection.html:26
+msgid "Add a collection"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/submit/start.html:28
+#: mediagoblin/templates/mediagoblin/submit/start.html:35
+msgid "Add your media"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/blog_media.html:38
+#, python-format
+msgid "❖ Blog post by <a href=\"%(user_url)s\">%(username)s</a>"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/blog_media.html:92
+#: mediagoblin/templates/mediagoblin/user_pages/media.html:104
+msgid "Add a comment"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/blog_media.html:103
+#: mediagoblin/templates/mediagoblin/user_pages/media.html:115
+msgid "Add this comment"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/blog_media.html:149
+#: mediagoblin/templates/mediagoblin/user_pages/media.html:179
+msgid "Added"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/collection.html:30
+#, python-format
+msgid "%(collection_title)s (%(username)s's collection)"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/collection.html:39
+#, python-format
+msgid "%(collection_title)s by <a href=\"%(user_url)s\">%(username)s</a>"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/collection_confirm_delete.html:23
+#, python-format
+msgid "Delete collection %(collection_title)s"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/collection_confirm_delete.html:38
+#, python-format
+msgid "Really delete collection: %(title)s?"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/collection_item_confirm_remove.html:23
+#, python-format
+msgid "Remove %(media_title)s from %(collection_title)s"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/collection_item_confirm_remove.html:39
+#, python-format
+msgid "Really remove %(media_title)s from %(collection_title)s?"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/collection_item_confirm_remove.html:62
+msgid "Remove"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/collection_list.html:21
+#, python-format
+msgid "%(username)s's collections"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/collection_list.html:28
+#, python-format
+msgid "<a href=\"%(user_url)s\">%(username)s</a>'s collections"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/comment_email.txt:19
+#, python-format
+msgid ""
+"Hi %(username)s,\n"
+"%(comment_author)s commented on your post (%(comment_url)s) at %(instance_name)s\n"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:30
+#, python-format
+msgid "%(username)s's media"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:38
+#, python-format
+msgid ""
+"<a href=\"%(user_url)s\">%(username)s</a>'s media with tag <a "
+"href=\"%(tag_url)s\">%(tag)s</a>"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:48
+#, python-format
+msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/media.html:38
+#, python-format
+msgid "❖ Browsing media by <a href=\"%(user_url)s\">%(username)s</a>"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/media.html:119
+msgid "Comment Preview"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:28
+#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:40
+#, python-format
+msgid "Add “%(media_title)s” to a collection"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:54
+msgid "+"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:58
+msgid "Add a new collection"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:29
+msgid ""
+"You can track the state of media being processed for your gallery here."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:89
+msgid "Your last 10 successful uploads"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/report.html:21
+msgid "<h2>File a Report</h2>"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/report.html:24
+msgid "Reporting this Comment"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/report.html:60
+msgid "Reporting this Media Entry"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/report.html:72
+#, python-format
+msgid ""
+"\n"
+" ❖ Published by <a href=\"%(user_url)s\"\n"
+" class=\"comment_authorlink\">%(username)s</a>\n"
+" "
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/report.html:81
+msgid "File Report "
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:53
+msgid "Here's a spot to tell others about yourself."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:94
+#, python-format
+msgid "View all of %(username)s's media"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:107
+msgid ""
+"This is where your media will appear, but you don't seem to have added "
+"anything yet."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:119
+#: mediagoblin/templates/mediagoblin/utils/collection_gallery.html:84
+#: mediagoblin/templates/mediagoblin/utils/object_gallery.html:70
+msgid "There doesn't seem to be any media here yet..."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user_nonactive.html:43
+msgid "Almost done! Your account still needs to be activated."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user_nonactive.html:48
+msgid ""
+"An email should arrive in a few moments with instructions on how to do so."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user_nonactive.html:52
+msgid "In case it doesn't:"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user_nonactive.html:55
+msgid "Resend verification email"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user_nonactive.html:63
+msgid ""
+"Someone has registered an account with this username, but it still has to be"
+" activated."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user_nonactive.html:68
+#, python-format
+msgid ""
+"If you are that person but you've lost your verification email, you can <a "
+"href=\"%(login_url)s\">log in</a> and resend it."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/utils/collection_gallery.html:49
+msgid "(remove)"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/utils/collections.html:21
+msgid "Collected in"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/utils/collections.html:40
+msgid "Add to a collection"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/utils/comment-subscription.html:24
+msgid "Subscribe to comments"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/utils/comment-subscription.html:30
+msgid "Silence comments"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/utils/feed_link.html:21
+#: mediagoblin/themes/airy/templates/mediagoblin/utils/feed_link.html:21
+msgid "feed icon"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/utils/feed_link.html:23
+#: mediagoblin/themes/airy/templates/mediagoblin/utils/feed_link.html:23
+msgid "Atom feed"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/utils/license.html:25
+msgid "All rights reserved"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/utils/pagination.html:39
+msgid "← Newer"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/utils/pagination.html:45
+msgid "Older →"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/utils/pagination.html:48
+msgid "Go to page:"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28
+#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33
+msgid "newer"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39
+#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44
+msgid "older"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/utils/report.html:25
+msgid "Report media"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/utils/tags.html:20
+msgid "Tagged with"
+msgstr ""
+
+#: mediagoblin/tools/exif.py:81
+msgid "Could not read the image file."
+msgstr ""
+
+#: mediagoblin/tools/response.py:38
+msgid "Oops!"
+msgstr ""
+
+#: mediagoblin/tools/response.py:39
+msgid "An error occured"
+msgstr ""
+
+#: mediagoblin/tools/response.py:53
+msgid "Bad Request"
+msgstr ""
+
+#: mediagoblin/tools/response.py:55
+msgid "The request sent to the server is invalid, please double check it"
+msgstr ""
+
+#: mediagoblin/tools/response.py:63
+msgid "Operation not allowed"
+msgstr ""
+
+#: mediagoblin/tools/response.py:64
+msgid ""
+"Sorry Dave, I can't let you do that!</p><p>You have tried to perform a "
+"function that you are not allowed to. Have you been trying to delete all "
+"user accounts again?"
+msgstr ""
+
+#: mediagoblin/tools/response.py:72
+msgid ""
+"There doesn't seem to be a page at this address. Sorry!</p><p>If you're sure"
+" the address is correct, maybe the page you're looking for has been moved or"
+" deleted."
+msgstr ""
+
+#: mediagoblin/tools/timesince.py:62
+msgid "year"
+msgstr ""
+
+#: mediagoblin/tools/timesince.py:63
+msgid "month"
+msgstr ""
+
+#: mediagoblin/tools/timesince.py:64
+msgid "week"
+msgstr ""
+
+#: mediagoblin/tools/timesince.py:65
+msgid "day"
+msgstr ""
+
+#: mediagoblin/tools/timesince.py:66
+msgid "hour"
+msgstr ""
+
+#: mediagoblin/tools/timesince.py:67
+msgid "minute"
+msgstr ""
+
+#: mediagoblin/user_pages/forms.py:23
+msgid "Comment"
+msgstr ""
+
+#: mediagoblin/user_pages/forms.py:25
+msgid ""
+"You can use <a href=\"http://daringfireball.net/projects/markdown/basics\" "
+"target=\"_blank\">Markdown</a> for formatting."
+msgstr ""
+
+#: mediagoblin/user_pages/forms.py:35
+msgid "I am sure I want to remove this item from the collection"
+msgstr ""
+
+#: mediagoblin/user_pages/forms.py:39
+msgid "Collection"
+msgstr ""
+
+#: mediagoblin/user_pages/forms.py:40
+msgid "-- Select --"
+msgstr ""
+
+#: mediagoblin/user_pages/forms.py:42
+msgid "Include a note"
+msgstr ""
+
+#: mediagoblin/user_pages/forms.py:49
+msgid ""
+"You can use\n"
+" <a href=\"http://daringfireball.net/projects/markdown/basics\" target=\"_blank\">\n"
+" Markdown</a> for formatting."
+msgstr ""
+
+#: mediagoblin/user_pages/forms.py:55 mediagoblin/user_pages/forms.py:61
+msgid "Reason for Reporting"
+msgstr ""
+
+#: mediagoblin/user_pages/views.py:188
+msgid "Sorry, comments are disabled."
+msgstr ""
+
+#: mediagoblin/user_pages/views.py:193
+msgid "Oops, your comment was empty."
+msgstr ""
+
+#: mediagoblin/user_pages/views.py:199
+msgid "Your comment has been posted!"
+msgstr ""
+
+#: mediagoblin/user_pages/views.py:235
+msgid "Please check your entries and try again."
+msgstr ""
+
+#: mediagoblin/user_pages/views.py:275
+msgid "You have to select or add a collection"
+msgstr ""
+
+#: mediagoblin/user_pages/views.py:286
+#, python-format
+msgid "\"%s\" already in collection \"%s\""
+msgstr ""
+
+#: mediagoblin/user_pages/views.py:292
+#, python-format
+msgid "\"%s\" added to collection \"%s\""
+msgstr ""
+
+#: mediagoblin/user_pages/views.py:317
+msgid "You deleted the media."
+msgstr ""
+
+#: mediagoblin/user_pages/views.py:336
+msgid "You are about to delete another user's media. Proceed with caution."
+msgstr ""
+
+#: mediagoblin/user_pages/views.py:409
+msgid "You deleted the item from the collection."
+msgstr ""
+
+#: mediagoblin/user_pages/views.py:413
+msgid "The item was not removed because you didn't check that you were sure."
+msgstr ""
+
+#: mediagoblin/user_pages/views.py:421
+msgid ""
+"You are about to delete an item from another user's collection. Proceed with"
+" caution."
+msgstr ""
+
+#: mediagoblin/user_pages/views.py:453
+#, python-format
+msgid "You deleted the collection \"%s\""
+msgstr ""
+
+#: mediagoblin/user_pages/views.py:460
+msgid ""
+"The collection was not deleted because you didn't check that you were sure."
+msgstr ""
+
+#: mediagoblin/user_pages/views.py:468
+msgid ""
+"You are about to delete another user's collection. Proceed with caution."
+msgstr ""
diff --git a/mediagoblin/i18n/he/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/he/LC_MESSAGES/mediagoblin.mo
index d5675b70..3746f9fe 100644
--- a/mediagoblin/i18n/he/LC_MESSAGES/mediagoblin.mo
+++ b/mediagoblin/i18n/he/LC_MESSAGES/mediagoblin.mo
Binary files differ
diff --git a/mediagoblin/i18n/he/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/he/LC_MESSAGES/mediagoblin.po
index 16b414cc..e48cdabe 100644
--- a/mediagoblin/i18n/he/LC_MESSAGES/mediagoblin.po
+++ b/mediagoblin/i18n/he/LC_MESSAGES/mediagoblin.po
@@ -11,8 +11,8 @@ msgstr ""
"Project-Id-Version: GNU MediaGoblin\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2014-08-04 13:45-0500\n"
-"PO-Revision-Date: 2014-08-04 18:45+0000\n"
-"Last-Translator: cwebber <cwebber@dustycloud.org>\n"
+"PO-Revision-Date: 2014-08-23 12:23+0000\n"
+"Last-Translator: GenghisKhan <genghiskhan@gmx.ca>\n"
"Language-Team: Hebrew (http://www.transifex.com/projects/p/mediagoblin/language/he/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -832,7 +832,7 @@ msgstr ""
#: mediagoblin/plugins/archivalook/templates/archivalook/root.html:79
msgid "View most recent media"
-msgstr ""
+msgstr "תפיה במדיה האחרונה ביותר"
#: mediagoblin/plugins/archivalook/templates/archivalook/bits/feature_dropdown.html:22
msgid "Feature management panel"
@@ -1145,7 +1145,7 @@ msgstr "התחבר כדי ליצור חשבון!"
#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login.html:51
msgid "Or login with a password!"
-msgstr "או התחבר בעזרת סיסמה!"
+msgstr "אפשר להתחבר גם בעזרת סיסמה!"
#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login_link.html:23
msgid "Or login with OpenID!"
@@ -1153,7 +1153,7 @@ msgstr "או התחבר עם OpenID!"
#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/register_link.html:23
msgid "Or register with OpenID!"
-msgstr "או הירשם בעזרת OpenID!"
+msgstr "אפשר להירשם גם בעזרת OpenID!"
#: mediagoblin/plugins/persona/__init__.py:90
msgid "Sorry, an account is already registered to that Persona email."
@@ -1365,7 +1365,7 @@ msgstr "העתק והדבק את מידע זה אל תוך הלקוח שלך:"
#: mediagoblin/templates/mediagoblin/auth/register.html:28
#: mediagoblin/templates/mediagoblin/auth/register.html:36
msgid "Create an account!"
-msgstr "יצירת חשבון!"
+msgstr "צור חשבון!"
#: mediagoblin/templates/mediagoblin/auth/register.html:41
msgid "Create"
@@ -1387,7 +1387,7 @@ msgstr "שלום %(username)s,\n\nבכדי להפעיל את חשבונך בשי
msgid ""
"Powered by <a href=\"http://mediagoblin.org/\" title='Version "
"%(version)s'>MediaGoblin</a>, a <a href=\"http://gnu.org/\">GNU</a> project."
-msgstr "ממונע על ידי <a href=\"http://mediagoblin.org/\" title='גירסה %(version)s'>MediaGoblin</a>, פרויקט <a href=\"http://gnu.org/\">GNU</a>."
+msgstr "ממונע על ידי <a href=\"http://mediagoblin.org/\" title='גירסה %(version)s'>MediaGoblin</a>, פרויקט של <a href=\"http://gnu.org/\">GNU</a>."
#: mediagoblin/templates/mediagoblin/bits/base_footer.html:24
#, python-format
@@ -1710,7 +1710,7 @@ msgstr "משתמש"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:41
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:70
msgid "When submitted"
-msgstr ""
+msgstr "כאשר נשלח"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:42
msgid "Transcoding progress"
@@ -1749,7 +1749,7 @@ msgstr "10 העלאות מוצלחות אחרונות"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:101
msgid "Submitted"
-msgstr ""
+msgstr "נשלח"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:113
#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:107
@@ -1793,7 +1793,7 @@ msgstr "\n תוכן מאת\n <a href=\"%(user_url)s\"> %(use
#: mediagoblin/templates/mediagoblin/moderation/report.html:102
msgid "Reason for report:"
-msgstr ""
+msgstr "סיבת דיווח:"
#: mediagoblin/templates/mediagoblin/moderation/report.html:133
#: mediagoblin/templates/mediagoblin/moderation/user.html:136
@@ -2006,11 +2006,11 @@ msgstr "הפריבילגיות של %(username)s"
#: mediagoblin/templates/mediagoblin/moderation/user.html:172
msgid "Privilege"
-msgstr ""
+msgstr "פריבילגיה"
#: mediagoblin/templates/mediagoblin/moderation/user.html:173
msgid "Granted"
-msgstr ""
+msgstr "הוענקה"
#: mediagoblin/templates/mediagoblin/moderation/user.html:180
msgid "Yes"
@@ -2275,11 +2275,11 @@ msgstr "הוסף אל אוסף"
#: mediagoblin/templates/mediagoblin/utils/comment-subscription.html:24
msgid "Subscribe to comments"
-msgstr ""
+msgstr "הירשם לתגובות"
#: mediagoblin/templates/mediagoblin/utils/comment-subscription.html:30
msgid "Silence comments"
-msgstr ""
+msgstr "השתק תגובות"
#: mediagoblin/templates/mediagoblin/utils/feed_link.html:21
#: mediagoblin/themes/airy/templates/mediagoblin/utils/feed_link.html:21
diff --git a/mediagoblin/i18n/nl/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/nl/LC_MESSAGES/mediagoblin.mo
index 9713d2d6..4d400e6e 100644
--- a/mediagoblin/i18n/nl/LC_MESSAGES/mediagoblin.mo
+++ b/mediagoblin/i18n/nl/LC_MESSAGES/mediagoblin.mo
Binary files differ
diff --git a/mediagoblin/i18n/nl/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/nl/LC_MESSAGES/mediagoblin.po
index e0ad7104..d06a25a4 100644
--- a/mediagoblin/i18n/nl/LC_MESSAGES/mediagoblin.po
+++ b/mediagoblin/i18n/nl/LC_MESSAGES/mediagoblin.po
@@ -3,6 +3,7 @@
# This file is distributed under the same license as the PROJECT project.
#
# Translators:
+# André Koot <meneer@tken.net>, 2014
# schendje <mail@jefvanschendel.nl>, 2011, 2012
# mvanderboom <mvanderboom@gmail.com>, 2012
msgid ""
@@ -10,8 +11,8 @@ msgstr ""
"Project-Id-Version: GNU MediaGoblin\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2014-08-04 13:45-0500\n"
-"PO-Revision-Date: 2014-08-04 18:45+0000\n"
-"Last-Translator: cwebber <cwebber@dustycloud.org>\n"
+"PO-Revision-Date: 2014-08-24 16:42+0000\n"
+"Last-Translator: André Koot <meneer@tken.net>\n"
"Language-Team: Dutch (http://www.transifex.com/projects/p/mediagoblin/language/nl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -22,28 +23,28 @@ msgstr ""
#: mediagoblin/decorators.py:303 mediagoblin/plugins/openid/views.py:202
msgid "Sorry, registration is disabled on this instance."
-msgstr "Sorry, registratie is uitgeschakeld op deze instantie."
+msgstr "Sorry, registratie is uitgeschakeld op deze goblin."
#: mediagoblin/decorators.py:318
msgid "Sorry, reporting is disabled on this instance."
-msgstr ""
+msgstr "Sorry, rapportage is uitgeschakeld op deze goblin."
#: mediagoblin/decorators.py:361 mediagoblin/plugins/ldap/views.py:55
#: mediagoblin/plugins/persona/views.py:77
msgid "Sorry, authentication is disabled on this instance."
-msgstr ""
+msgstr "Sorry, authenticatie is uitgeschakeld op deze goblin."
#: mediagoblin/auth/tools.py:43
msgid "Invalid User name or email address."
-msgstr ""
+msgstr "Ongeldige gebruikersnaam of e-mailadres."
#: mediagoblin/auth/tools.py:44
msgid "This field does not take email addresses."
-msgstr ""
+msgstr "Dit veld accepteert geen e-mailadressen."
#: mediagoblin/auth/tools.py:45
msgid "This field requires an email address."
-msgstr ""
+msgstr "Dit veld vereist een e-mailadres."
#: mediagoblin/auth/tools.py:116
msgid "Sorry, a user with that name already exists."
@@ -56,7 +57,7 @@ msgstr "Sorry, een gebruiker met dat e-mailadres bestaat al."
#: mediagoblin/auth/views.py:142 mediagoblin/edit/views.py:363
#: mediagoblin/edit/views.py:384 mediagoblin/plugins/basic_auth/views.py:110
msgid "The verification key or user id is incorrect."
-msgstr ""
+msgstr "De verificatiecode of gebruikersnaam is onjuist."
#: mediagoblin/auth/views.py:161
msgid ""
@@ -66,7 +67,7 @@ msgstr "Uw e-mailadres is geverifieerd. U kunt nu inloggen, uw profiel bewerken,
#: mediagoblin/auth/views.py:167
msgid "The verification key or user id is incorrect"
-msgstr "De verificatie sleutel of gebruikers-ID is onjuist"
+msgstr "De verificatiecode of gebruikersnaam is onjuist"
#: mediagoblin/auth/views.py:185
msgid "You must be logged in so we know who to send the email to!"
@@ -106,7 +107,7 @@ msgstr "Voor opmaak kun je <a href=\"http://daringfireball.net/projects/markdown
#: mediagoblin/edit/forms.py:37 mediagoblin/media_types/blog/forms.py:27
#: mediagoblin/submit/forms.py:45
msgid "Tags"
-msgstr "Etiket"
+msgstr "Tags"
#: mediagoblin/edit/forms.py:39 mediagoblin/submit/forms.py:47
msgid "Separate tags by commas."
@@ -146,34 +147,34 @@ msgstr "Dit adres bevat fouten"
#: mediagoblin/edit/forms.py:67
msgid "Email me when others comment on my media"
-msgstr ""
+msgstr "Notificeer me per e-mail bij reacties op mijn media"
#: mediagoblin/edit/forms.py:69
msgid "Enable insite notifications about events."
-msgstr ""
+msgstr "Activeer notificaties over gebeurtenissen op de site."
#: mediagoblin/edit/forms.py:71
msgid "License preference"
-msgstr ""
+msgstr "Licentievoorkeuren"
#: mediagoblin/edit/forms.py:77
msgid "This will be your default license on upload forms."
-msgstr ""
+msgstr "Dit wordt de standaardlicentie op uploadformulieren."
#: mediagoblin/edit/forms.py:90
msgid "The title can't be empty"
-msgstr ""
+msgstr "De titel mag niet leeg zijn"
#: mediagoblin/edit/forms.py:92 mediagoblin/submit/forms.py:64
#: mediagoblin/user_pages/forms.py:48
msgid "Description of this collection"
-msgstr ""
+msgstr "Beschrijving van deze verzameling"
#: mediagoblin/edit/forms.py:99
msgid ""
"The title part of this collection's address. You usually don't need to "
"change this."
-msgstr ""
+msgstr "Het titelgedeelte van het adres van deze verzameling. Normaal gesproken hoef je deze niet te veranderen."
#: mediagoblin/edit/forms.py:106 mediagoblin/plugins/basic_auth/forms.py:68
msgid "Old password"
@@ -189,7 +190,7 @@ msgstr "Nieuw wachtwoord"
#: mediagoblin/edit/forms.py:119
msgid "New email address"
-msgstr ""
+msgstr "Nieuw e-mailadres"
#: mediagoblin/edit/forms.py:123 mediagoblin/plugins/basic_auth/forms.py:28
#: mediagoblin/plugins/basic_auth/forms.py:43
@@ -201,19 +202,19 @@ msgstr "Wachtwoord"
#: mediagoblin/edit/forms.py:125
msgid "Enter your password to prove you own this account."
-msgstr ""
+msgstr "Vul je wachtwoord in om te bewijzen dat dit jouw account is."
#: mediagoblin/edit/forms.py:155
msgid "Identifier"
-msgstr ""
+msgstr "Identificatie"
#: mediagoblin/edit/forms.py:156
msgid "Value"
-msgstr ""
+msgstr "Waarde"
#: mediagoblin/edit/views.py:78
msgid "An entry with that slug already exists for this user."
-msgstr "Er bestaat al een met die slug voor deze gebruiker."
+msgstr "Er bestaat al een onderwerp met die slug voor deze gebruiker."
#: mediagoblin/edit/views.py:96
msgid "You are editing another user's media. Proceed with caution."
@@ -222,11 +223,11 @@ msgstr "U bent de media van een andere gebruiker aan het aanpassen. Ga voorzicht
#: mediagoblin/edit/views.py:166
#, python-format
msgid "You added the attachment %s!"
-msgstr ""
+msgstr "Je hebt bijlage %s toegevoegd!"
#: mediagoblin/edit/views.py:193
msgid "You can only edit your own profile."
-msgstr ""
+msgstr "Je kunt alleen je eigen profiel bijwerken."
#: mediagoblin/edit/views.py:199
msgid "You are editing a user's profile. Proceed with caution."
@@ -242,25 +243,25 @@ msgstr "Accountinstellingen opgeslagen"
#: mediagoblin/edit/views.py:282
msgid "You need to confirm the deletion of your account."
-msgstr ""
+msgstr "Je moet het verwijderen van je account bevestigen."
#: mediagoblin/edit/views.py:318 mediagoblin/submit/views.py:132
#: mediagoblin/user_pages/views.py:252
#, python-format
msgid "You already have a collection called \"%s\"!"
-msgstr ""
+msgstr "Je hebt al een verzameling met de naam \"%s\"!"
#: mediagoblin/edit/views.py:322
msgid "A collection with that slug already exists for this user."
-msgstr ""
+msgstr "Er bestaat al een verzameling met die slug voor deze gebruiker."
#: mediagoblin/edit/views.py:337
msgid "You are editing another user's collection. Proceed with caution."
-msgstr ""
+msgstr "Je bewerkt de verzameling van een andere gebruiker. Ga voorzichtig te werk."
#: mediagoblin/edit/views.py:378
msgid "Your email address has been verified."
-msgstr ""
+msgstr "Je e-mailadres is geverifieerd."
#: mediagoblin/edit/views.py:413 mediagoblin/plugins/basic_auth/views.py:200
msgid "Wrong password"
@@ -268,30 +269,30 @@ msgstr "Verkeerd wachtwoord"
#: mediagoblin/gmg_commands/assetlink.py:60
msgid "Cannot link theme... no theme set\n"
-msgstr ""
+msgstr "Kan thema niet linken... nog geen thema ingesteld\n"
#: mediagoblin/gmg_commands/assetlink.py:73
msgid "No asset directory for this theme\n"
-msgstr ""
+msgstr "Geen objectendirectory voor dit thema\n"
#: mediagoblin/gmg_commands/assetlink.py:76
msgid "However, old link directory symlink found; removed.\n"
-msgstr ""
+msgstr "Maar, symlink naar oude link directory gevonden; verwijderd.\n"
#: mediagoblin/gmg_commands/assetlink.py:112
#, python-format
msgid "Could not link \"%s\": %s exists and is not a symlink\n"
-msgstr ""
+msgstr "Kon \"%s\" niet linken: %s bestaat al en is geen symlink\n"
#: mediagoblin/gmg_commands/assetlink.py:119
#, python-format
msgid "Skipping \"%s\"; already set up.\n"
-msgstr ""
+msgstr "Overslaan \"%s\"; is al ingesteld\n"
#: mediagoblin/gmg_commands/assetlink.py:124
#, python-format
msgid "Old link found for \"%s\"; removing.\n"
-msgstr ""
+msgstr "Oude link gevonden voor \"%s\"; verwijderen.\n"
#: mediagoblin/gmg_commands/batchaddmedia.py:34
msgid ""
@@ -303,35 +304,35 @@ msgstr ""
#: mediagoblin/gmg_commands/batchaddmedia.py:40
msgid "Name of user these media entries belong to"
-msgstr ""
+msgstr "Gebruikersnaam van de eigenaar van deze nummers"
#: mediagoblin/gmg_commands/batchaddmedia.py:43
msgid "Path to the csv file containing metadata information."
-msgstr ""
+msgstr "Pad naar het csv-bestand met metadata informatie."
#: mediagoblin/gmg_commands/batchaddmedia.py:48
msgid "Don't process eagerly, pass off to celery"
-msgstr ""
+msgstr "Geen haast, rustig, dan breekt het lijntje niet"
#: mediagoblin/gmg_commands/batchaddmedia.py:63
msgid "Sorry, no user by username '{username}' exists"
-msgstr ""
+msgstr "Sorry, er bestaat geen gebruiker met de naam '{username}'"
#: mediagoblin/gmg_commands/batchaddmedia.py:74
msgid "File at {path} not found, use -h flag for help"
-msgstr ""
+msgstr "Bestand op {path} niet gevonden, gebruik de -h flag voor hulp"
#: mediagoblin/gmg_commands/batchaddmedia.py:115
msgid ""
"Error with media '{media_id}' value '{error_path}': {error_msg}\n"
"Metadata was not uploaded."
-msgstr ""
+msgstr "Fout met media '{media_id}' waarde '{error_path}': {error_msg}\nMetadata niet geüpload."
#: mediagoblin/gmg_commands/batchaddmedia.py:141
msgid ""
"FAIL: Local file {filename} could not be accessed.\n"
"{filename} will not be uploaded."
-msgstr ""
+msgstr "FOUT: Lokaal bestand {filename} kon niet worden benaderd.\n{filename} wordt niet geüpload."
#: mediagoblin/gmg_commands/batchaddmedia.py:157
msgid ""
@@ -342,19 +343,19 @@ msgstr ""
#: mediagoblin/gmg_commands/batchaddmedia.py:160
msgid "FAIL: This file is larger than the upload limits for this site."
-msgstr ""
+msgstr "FOUT: Dit bestand is groter dan de uploadlimiet voor deze site."
#: mediagoblin/gmg_commands/batchaddmedia.py:163
msgid "FAIL: This file will put this user past their upload limits."
-msgstr ""
+msgstr "FOUT: Dit bestand laat de gebruiker voorbij de uploadlimiet gaan."
#: mediagoblin/gmg_commands/batchaddmedia.py:166
msgid "FAIL: This user is already past their upload limits."
-msgstr ""
+msgstr "FOUT: Deze gebruiker is al voorbij de uploadlimiet."
#: mediagoblin/gmg_commands/batchaddmedia.py:168
msgid "{files_uploaded} out of {files_attempted} files successfully submitted"
-msgstr ""
+msgstr "{files_uploaded} van {files_attempted} bestanden succesvol aangemeld"
#: mediagoblin/meddleware/csrf.py:134
msgid ""
@@ -372,7 +373,7 @@ msgstr "Sorry, dat bestandstype wordt niet ondersteunt."
#: mediagoblin/media_types/blog/forms.py:35
#: mediagoblin/plugins/oauth/forms.py:36
msgid "Description"
-msgstr ""
+msgstr "Beschrijving"
#: mediagoblin/media_types/blog/forms.py:40 mediagoblin/user_pages/forms.py:31
msgid "I am sure I want to delete this"
@@ -384,11 +385,11 @@ msgstr "Mooizo! Toegevoegd!"
#: mediagoblin/media_types/blog/views.py:198
msgid "Woohoo! edited blogpost is submitted"
-msgstr ""
+msgstr "Zohee! bijgewerkt blogbericht geplaatst"
#: mediagoblin/media_types/blog/views.py:320
msgid "You deleted the Blog."
-msgstr ""
+msgstr "Je hebt het blogbericht verwijderd."
#: mediagoblin/media_types/blog/views.py:326
#: mediagoblin/user_pages/views.py:329
@@ -397,23 +398,23 @@ msgstr "Deze media was niet verwijderd omdat je niet hebt aangegeven dat je het
#: mediagoblin/media_types/blog/views.py:333
msgid "You are about to delete another user's Blog. Proceed with caution."
-msgstr ""
+msgstr "Je staat op het punt het Blog van iemand anders te verwijderen. Pas op."
#: mediagoblin/media_types/blog/views.py:344
msgid "The blog was not deleted because you have no rights."
-msgstr ""
+msgstr "Het blog werd niet verwijderd omdat je niet de juiste rechten hebt."
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_admin_dashboard.html:43
msgid "Add Blog Post"
-msgstr ""
+msgstr "Toevoegen Blogbericht"
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_admin_dashboard.html:50
msgid "Edit Blog"
-msgstr ""
+msgstr "Bewerk blog"
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_admin_dashboard.html:57
msgid "Delete Blog"
-msgstr ""
+msgstr "Verwijder Blog"
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_admin_dashboard.html:92
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blogpost_draft_view.html:35
@@ -435,11 +436,11 @@ msgstr "Verwijderen"
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_admin_dashboard.html:102
msgid "<em> Go to list view </em>"
-msgstr ""
+msgstr "<em> Ga naar lijstoverzicht </em>"
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_admin_dashboard.html:104
msgid " No blog post yet. "
-msgstr ""
+msgstr "Nog geen blogbericht"
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_confirm_delete.html:30
#: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30
@@ -468,7 +469,7 @@ msgstr "Permanent verwijderen"
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_edit_create.html:26
msgid "Create/Edit a Blog"
-msgstr ""
+msgstr "Maak/bewerk een Blog"
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_edit_create.html:37
#: mediagoblin/plugins/oauth/templates/oauth/client/register.html:29
@@ -481,160 +482,160 @@ msgstr "Voeg toe"
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_post_edit_create.html:23
msgid "Create/Edit a blog post."
-msgstr ""
+msgstr "Maak/bewerk een blogbericht"
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_post_edit_create.html:29
msgid "Create/Edit a Blog Post."
-msgstr ""
+msgstr "Maak/bewerk een blogbericht"
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/blog_post_listing.html:24
#, python-format
msgid "%(blog_owner_name)s's Blog"
-msgstr ""
+msgstr "%(blog_owner_name)s's Blog"
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/list_of_blogs.html:46
msgid "View"
-msgstr ""
+msgstr "Bekijk"
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/list_of_blogs.html:65
msgid "Create a Blog"
-msgstr ""
+msgstr "Maak een Blog"
#: mediagoblin/media_types/blog/templates/mediagoblin/blog/url_to_dashboard.html:20
msgid " Blog Dashboard "
-msgstr ""
+msgstr " Blog Dashboard "
#: mediagoblin/media_types/pdf/processing.py:142
msgid "unoconv failing to run, check log file"
-msgstr ""
+msgstr "unoconv kon niet starten, controleer het logbestand"
#: mediagoblin/media_types/video/processing.py:44
msgid "Video transcoding failed"
-msgstr ""
+msgstr "Video transcoderen mislukt"
#: mediagoblin/moderation/forms.py:21
msgid "Take away privilege"
-msgstr ""
+msgstr "Neem rechten af"
#: mediagoblin/moderation/forms.py:22
msgid "Ban the user"
-msgstr ""
+msgstr "Blokkeer deze gebruiker"
#: mediagoblin/moderation/forms.py:23
msgid "Send the user a message"
-msgstr ""
+msgstr "Stuur de gebruiker een bericht"
#: mediagoblin/moderation/forms.py:24
msgid "Delete the content"
-msgstr ""
+msgstr "Verwijder de inhoud"
#: mediagoblin/moderation/forms.py:53 mediagoblin/moderation/forms.py:118
msgid "User will be banned until:"
-msgstr ""
+msgstr "De gebruiker wordt geblokkeerd tot:"
#: mediagoblin/moderation/forms.py:57
msgid "Why are you banning this User?"
-msgstr ""
+msgstr "Waarom blokkeer je deze gebruiker?"
#: mediagoblin/moderation/forms.py:109
msgid "What action will you take to resolve the report?"
-msgstr ""
+msgstr "Welke actie voer je uit om de melding af te handelen?"
#: mediagoblin/moderation/forms.py:115
msgid "What privileges will you take away?"
-msgstr ""
+msgstr "Welke rechten neem je af?"
#: mediagoblin/moderation/forms.py:122
msgid "Why user was banned:"
-msgstr ""
+msgstr "Waarom de gebruiker werd geblokkeerd:"
#: mediagoblin/moderation/forms.py:125
msgid "Message to user:"
-msgstr ""
+msgstr "Bericht aan de gebruiker:"
#: mediagoblin/moderation/forms.py:128
msgid "Resolution content:"
-msgstr ""
+msgstr "Oplossing inhoud:"
#: mediagoblin/moderation/tools.py:34
msgid ""
"\n"
"{mod} took away {user}'s {privilege} privileges."
-msgstr ""
+msgstr "\n{mod} nam van {user} de {privilege} rechten af."
#: mediagoblin/moderation/tools.py:47
msgid ""
"\n"
"{mod} banned user {user} {expiration_date}."
-msgstr ""
+msgstr "\n{mod} blokkeerde de gebruiker {user} {expiration_date}."
#: mediagoblin/moderation/tools.py:51
msgid "until {date}"
-msgstr ""
+msgstr "tot {date}"
#: mediagoblin/moderation/tools.py:53
#: mediagoblin/templates/mediagoblin/banned.html:30
msgid "indefinitely"
-msgstr ""
+msgstr "onbepaalde tijd"
#: mediagoblin/moderation/tools.py:62
msgid ""
"\n"
"{mod} sent a warning email to the {user}."
-msgstr ""
+msgstr "\n{mod} stuurde een waarschuwingsbericht naar {user}."
#: mediagoblin/moderation/tools.py:71
msgid ""
"\n"
"{mod} deleted the comment."
-msgstr ""
+msgstr "\n{mod} verwijderde de reactie."
#: mediagoblin/moderation/tools.py:78
msgid ""
"\n"
"{mod} deleted the media entry."
-msgstr ""
+msgstr "\n{mod} verwijderde het mediaonderwerp."
#: mediagoblin/moderation/tools.py:91
msgid "Warning from"
-msgstr ""
+msgstr "Waarschuwing van"
#: mediagoblin/notifications/tools.py:54 mediagoblin/user_pages/lib.py:60
msgid "commented on your post"
-msgstr ""
+msgstr "reageerde op je bericht"
#: mediagoblin/notifications/views.py:35
#, python-format
msgid "Subscribed to comments on %s!"
-msgstr ""
+msgstr "Geabonneerd op reacties op %s!"
#: mediagoblin/notifications/views.py:48
#, python-format
msgid "You will not receive notifications for comments on %s."
-msgstr ""
+msgstr "Je ontvangt geen meldingen over reacties op %s."
#: mediagoblin/oauth/views.py:242
msgid "Must provide an oauth_token."
-msgstr ""
+msgstr "Moet een oauth_token leveren."
#: mediagoblin/oauth/views.py:247 mediagoblin/oauth/views.py:298
msgid "No request token found."
-msgstr ""
+msgstr "Geen aanvraagtoken gevonden."
#: mediagoblin/plugins/api/views.py:76 mediagoblin/plugins/piwigo/views.py:155
#: mediagoblin/submit/views.py:78
msgid "Sorry, the file size is too big."
-msgstr ""
+msgstr "Sorry, het bestand is te groot."
#: mediagoblin/plugins/api/views.py:79 mediagoblin/plugins/piwigo/views.py:158
#: mediagoblin/submit/views.py:81
msgid "Sorry, uploading this file will put you over your upload limit."
-msgstr ""
+msgstr "Sorry, door te uploaden zou je over je uploadlimiet gaan."
#: mediagoblin/plugins/api/views.py:83 mediagoblin/plugins/piwigo/views.py:162
#: mediagoblin/submit/views.py:87
msgid "Sorry, you have reached your upload limit."
-msgstr ""
+msgstr "Sorry, je hebt je uploadlimiet bereikt."
#: mediagoblin/plugins/archivalook/forms.py:21
msgid "Enter the URL for the media to be featured"
@@ -642,27 +643,27 @@ msgstr ""
#: mediagoblin/plugins/archivalook/tools.py:132
msgid "Primary"
-msgstr ""
+msgstr "Primair"
#: mediagoblin/plugins/archivalook/tools.py:133
msgid "Secondary"
-msgstr ""
+msgstr "Secundair"
#: mediagoblin/plugins/archivalook/tools.py:134
msgid "Tertiary"
-msgstr ""
+msgstr "Tertiair"
#: mediagoblin/plugins/archivalook/tools.py:135
msgid "-----------{display_type}-Features---------------------------\n"
-msgstr ""
+msgstr "-----------{display_type}-Mogelijkheden---------------------------\n"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:33
msgid "How does this work?"
-msgstr ""
+msgstr "Hoe werkt het?"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:34
msgid "How to feature media?"
-msgstr ""
+msgstr "Hoe publiceer je media?"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:37
msgid ""
@@ -752,7 +753,7 @@ msgstr ""
#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:108
msgid "CAUTION:"
-msgstr ""
+msgstr "WAARSCHUWING:"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature.html:110
msgid ""
@@ -767,41 +768,41 @@ msgstr ""
msgid ""
"\n"
"Feature Media "
-msgstr ""
+msgstr "\nMedia aanbevelen"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:28
msgid "Feature"
-msgstr ""
+msgstr "Aanbevolen"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:34
msgid ""
"\n"
"Unfeature Media "
-msgstr ""
+msgstr "\nMedia niet meer aanbevelen"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:36
msgid "Unfeature"
-msgstr ""
+msgstr "Niet aanbevolen"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:42
msgid ""
"\n"
"Promote Feature "
-msgstr ""
+msgstr "\nPromoveer aanbeveling "
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:44
msgid "Promote"
-msgstr ""
+msgstr "Promoveren"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:50
msgid ""
"\n"
"Demote Feature "
-msgstr ""
+msgstr "\nDemoveer aanbeveling"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_media_sidebar.html:52
msgid "Demote"
-msgstr ""
+msgstr "Demoveren"
#: mediagoblin/plugins/archivalook/templates/archivalook/recent_media.html:30
#: mediagoblin/templates/mediagoblin/root.html:32
@@ -810,7 +811,7 @@ msgstr "Nieuwste media"
#: mediagoblin/plugins/archivalook/templates/archivalook/root.html:61
msgid "Nothing is currently featured."
-msgstr ""
+msgstr "Momenteel is niets aanbevolen."
#: mediagoblin/plugins/archivalook/templates/archivalook/root.html:62
msgid ""
@@ -831,7 +832,7 @@ msgstr ""
#: mediagoblin/plugins/archivalook/templates/archivalook/root.html:79
msgid "View most recent media"
-msgstr ""
+msgstr "Bekijk de meest recente media"
#: mediagoblin/plugins/archivalook/templates/archivalook/bits/feature_dropdown.html:22
msgid "Feature management panel"
@@ -842,14 +843,14 @@ msgid ""
"Sorry, this audio will not work because\n"
"\tyour web browser does not support HTML5\n"
"\taudio."
-msgstr ""
+msgstr "Sorry, dit geluidsfragment zal niet werken omdat\n\tje web-browser geen HTML5 audio ondersteunt."
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/audio_primary.html:46
msgid ""
"You can get a modern web browser that\n"
"\tcan play the audio at <a href=\"http://getfirefox.com\">\n"
"\t http://getfirefox.com</a>!"
-msgstr ""
+msgstr "Je kunt een moderne web browser die\n\taudio kan afspelen krijgen op at <a href=\"http://getfirefox.com\">\n\t http://getfirefox.com</a>!"
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/video_primary.html:43
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/video_secondary.html:43
@@ -857,7 +858,7 @@ msgid ""
"Sorry, this video will not work because\n"
" your web browser does not support HTML5 \n"
" video."
-msgstr ""
+msgstr "Sorry, deze video werkt niet omdat je\nwebbrowser geen HTML5 video ondersteunt."
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/video_primary.html:46
#: mediagoblin/plugins/archivalook/templates/archivalook/feature_displays/video_secondary.html:46
@@ -865,7 +866,7 @@ msgid ""
"You can get a modern web browser that \n"
" can play this video at <a href=\"http://getfirefox.com\">\n"
" http://getfirefox.com</a>!"
-msgstr ""
+msgstr "Je kunt een moderne web browser die deze\n\tvideo kan afspelen krijgen op at <a href=\"http://getfirefox.com\">\n\t http://getfirefox.com</a>!"
#: mediagoblin/plugins/basic_auth/forms.py:24
#: mediagoblin/plugins/ldap/forms.py:35 mediagoblin/plugins/openid/forms.py:27
@@ -879,19 +880,19 @@ msgstr "Gebruikersnaam"
#: mediagoblin/plugins/persona/forms.py:28
#: mediagoblin/plugins/persona/forms.py:39
msgid "Email address"
-msgstr "E-mail adres"
+msgstr "E-mailadres"
#: mediagoblin/plugins/basic_auth/forms.py:39
msgid "Username or Email"
-msgstr ""
+msgstr "Gebruikersnaam of email-adres"
#: mediagoblin/plugins/basic_auth/forms.py:46
msgid "Stay logged in"
-msgstr ""
+msgstr "Blijf ingelogd"
#: mediagoblin/plugins/basic_auth/forms.py:51
msgid "Username or email"
-msgstr "Gebruikersnaam of email-adres"
+msgstr "Gebruikersnaam of e-mailadres"
#: mediagoblin/plugins/basic_auth/views.py:54
msgid ""
@@ -901,7 +902,7 @@ msgstr ""
#: mediagoblin/plugins/basic_auth/views.py:65
msgid "Couldn't find someone with that username."
-msgstr ""
+msgstr "Kon niemand vinden met die gebruikersnaam."
#: mediagoblin/plugins/basic_auth/views.py:68
msgid ""
@@ -912,11 +913,11 @@ msgstr "Een e-mail met instructies om je wachtwoord te veranderen is verstuurd."
msgid ""
"Could not send password recovery email as your username is inactive or your "
"account's email address has not been verified."
-msgstr "Email kon niet verstuurd worden omdat je gebruikersnaam inactief is of omdat je e-mailadres nog niet geverifieerd is."
+msgstr "E-mail kon niet verstuurd worden omdat je gebruikersnaam inactief is of omdat je e-mailadres nog niet geverifieerd is."
#: mediagoblin/plugins/basic_auth/views.py:123
msgid "The user id is incorrect."
-msgstr ""
+msgstr "De gebruikersID is onjuist."
#: mediagoblin/plugins/basic_auth/views.py:139
msgid "You can now log in using your new password."
@@ -930,7 +931,7 @@ msgstr ""
#: mediagoblin/plugins/basic_auth/views.py:215
msgid "Your password was changed successfully"
-msgstr ""
+msgstr "Je wachtwoord is succesvol gewijzigd"
#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/change_fp.html:28
#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/change_fp.html:36
@@ -945,16 +946,16 @@ msgstr "Wachtwoord opslaan"
#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/change_pass.html:38
#, python-format
msgid "Changing %(username)s's password"
-msgstr ""
+msgstr "Wijzigen %(username)s's wachtwoord"
#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/change_pass.html:45
#: mediagoblin/templates/mediagoblin/edit/change_email.html:40
msgid "Save"
-msgstr ""
+msgstr "Bewaren"
#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/create_account_link.html:22
msgid "Don't have an account yet?"
-msgstr "Heeft u nog geen account?"
+msgstr "Heb je nog geen account?"
#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/create_account_link.html:24
msgid "Create one here!"
@@ -962,7 +963,7 @@ msgstr "Maak er hier een!"
#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/edit_link.html:22
msgid "Change your password."
-msgstr ""
+msgstr "Wijzig je wachtwoord:"
#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/forgot_password.html:23
#: mediagoblin/plugins/basic_auth/templates/mediagoblin/plugins/basic_auth/forgot_password.html:31
@@ -988,31 +989,31 @@ msgstr "Bekijken op <a href=\"%(osm_url)s\">OpenStreetMap</a>"
#: mediagoblin/plugins/ldap/templates/mediagoblin/plugins/ldap/create_account_link.html:22
msgid "Sign in to create an account!"
-msgstr ""
+msgstr "Log in om een account te maken!"
#: mediagoblin/plugins/metadata_display/templates/mediagoblin/plugins/metadata_display/metadata_table.html:22
msgid "Metadata"
-msgstr ""
+msgstr "Metadata"
#: mediagoblin/plugins/metadata_display/templates/mediagoblin/plugins/metadata_display/metadata_table.html:40
msgid "Edit Metadata"
-msgstr ""
+msgstr "Bewerk Metadata"
#: mediagoblin/plugins/oauth/forms.py:29
msgid "Allow"
-msgstr ""
+msgstr "Toestaan"
#: mediagoblin/plugins/oauth/forms.py:30
msgid "Deny"
-msgstr ""
+msgstr "Verbieden"
#: mediagoblin/plugins/oauth/forms.py:34
msgid "Name"
-msgstr ""
+msgstr "Naam"
#: mediagoblin/plugins/oauth/forms.py:35
msgid "The name of the OAuth client"
-msgstr ""
+msgstr "De naam van de OAuth client"
#: mediagoblin/plugins/oauth/forms.py:38
msgid ""
@@ -1022,7 +1023,7 @@ msgstr ""
#: mediagoblin/plugins/oauth/forms.py:40
msgid "Type"
-msgstr ""
+msgstr "Type"
#: mediagoblin/plugins/oauth/forms.py:45
msgid ""
@@ -1036,7 +1037,7 @@ msgstr ""
#: mediagoblin/plugins/oauth/forms.py:52
msgid "Redirect URI"
-msgstr ""
+msgstr "Redirect URI"
#: mediagoblin/plugins/oauth/forms.py:54
msgid ""
@@ -1046,82 +1047,82 @@ msgstr ""
#: mediagoblin/plugins/oauth/forms.py:66
msgid "This field is required for public clients"
-msgstr ""
+msgstr "Dit is een verplicht veld voor openbare clients"
#: mediagoblin/plugins/oauth/views.py:55
msgid "The client {0} has been registered!"
-msgstr ""
+msgstr "De client {0} is geregistereerd!"
#: mediagoblin/plugins/oauth/templates/oauth/client/connections.html:22
msgid "OAuth client connections"
-msgstr ""
+msgstr "OAuth client verbindingen"
#: mediagoblin/plugins/oauth/templates/oauth/client/list.html:22
msgid "Your OAuth clients"
-msgstr ""
+msgstr "Je OAuth clients"
#: mediagoblin/plugins/openid/__init__.py:97
#: mediagoblin/plugins/openid/views.py:268
#: mediagoblin/plugins/openid/views.py:297
msgid "Sorry, an account is already registered to that OpenID."
-msgstr ""
+msgstr "Sorry, er is al een account geregistreerd met die OpenID"
#: mediagoblin/plugins/openid/forms.py:38
msgid "OpenID"
-msgstr ""
+msgstr "OpenID"
#: mediagoblin/plugins/openid/views.py:48
msgid "Sorry, the OpenID server could not be found"
-msgstr ""
+msgstr "Sorry, de OpenID server kon niet worden gevonden"
#: mediagoblin/plugins/openid/views.py:61
#, python-format
msgid "No OpenID service was found for %s"
-msgstr ""
+msgstr "Geen OpenID service gevonden voor %s"
#: mediagoblin/plugins/openid/views.py:106
#, python-format
msgid "Verification of %s failed: %s"
-msgstr ""
+msgstr "Verificatie van %s mislukt\"%s"
#: mediagoblin/plugins/openid/views.py:117
msgid "Verification cancelled"
-msgstr ""
+msgstr "Verificatie geannuleerd"
#: mediagoblin/plugins/openid/views.py:314
msgid "Your OpenID url was saved successfully."
-msgstr ""
+msgstr "Je OpenID URL is succesvol bewaard."
#: mediagoblin/plugins/openid/views.py:338
#: mediagoblin/plugins/openid/views.py:393
msgid "You can't delete your only OpenID URL unless you have a password set"
-msgstr ""
+msgstr "Je kunt je OpenID URL alleen verwijderen als je een wachtwoord hebt ingesteld."
#: mediagoblin/plugins/openid/views.py:343
#: mediagoblin/plugins/openid/views.py:402
msgid "That OpenID is not registered to this account."
-msgstr ""
+msgstr "Die OpenID is niet geregistreerd voor dit account."
#: mediagoblin/plugins/openid/views.py:385
msgid "OpenID was successfully removed."
-msgstr ""
+msgstr "OpenID succesvol verwijderd."
#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/add.html:23
#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/add.html:31
#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/delete.html:34
#: mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/edit.html:23
msgid "Add an OpenID"
-msgstr ""
+msgstr "OpenID toevoegen"
#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/add.html:34
#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/delete.html:23
#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/delete.html:31
msgid "Delete an OpenID"
-msgstr ""
+msgstr "OpenID verwijderen"
#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/edit_link.html:21
msgid "OpenID's"
-msgstr ""
+msgstr "OpenID's"
#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login.html:28
#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login.html:36
@@ -1140,66 +1141,66 @@ msgstr "Inloggen is mislukt!"
#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login.html:44
msgid "Log in to create an account!"
-msgstr ""
+msgstr "Log in om een account te maken!"
#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login.html:51
msgid "Or login with a password!"
-msgstr ""
+msgstr "Of login met een wachtwoord!"
#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login_link.html:23
msgid "Or login with OpenID!"
-msgstr ""
+msgstr "Of login met OpenID"
#: mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/register_link.html:23
msgid "Or register with OpenID!"
-msgstr ""
+msgstr "Of registreer met een OpenID!"
#: mediagoblin/plugins/persona/__init__.py:90
msgid "Sorry, an account is already registered to that Persona email."
-msgstr ""
+msgstr "Sorry, er is al een account geregistreerd met dat Persona e-mailadres."
#: mediagoblin/plugins/persona/views.py:138
msgid "The Persona email address was successfully removed."
-msgstr ""
+msgstr "Het Persona e-mailadres is succesvol verwijderd."
#: mediagoblin/plugins/persona/views.py:144
msgid ""
"You can't delete your only Persona email address unless you have a password "
"set."
-msgstr ""
+msgstr "Je kunt je Persona e-mailadres alleen verwijderen als je een wachtwoord hebt ingesteld."
#: mediagoblin/plugins/persona/views.py:149
msgid "That Persona email address is not registered to this account."
-msgstr ""
+msgstr "Dat Persona e-mailadres is niet geregistreerd voor dit account."
#: mediagoblin/plugins/persona/views.py:176
msgid ""
"Sorry, an account is already registered with that Persona email address."
-msgstr ""
+msgstr "Sorry, er is al een account geregistreerd met dat Persona e-mailadrers."
#: mediagoblin/plugins/persona/views.py:192
msgid "Your Persona email address was saved successfully."
-msgstr ""
+msgstr "Je Persona e-mailadres is succesvol bewaard."
#: mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/edit.html:31
msgid "Delete a Persona email address"
-msgstr ""
+msgstr "Verwijder een Persona e-mailadres"
#: mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/edit.html:34
msgid "Add a Persona email address"
-msgstr ""
+msgstr "Voeg een Persona e-mailadres toe"
#: mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/edit_link.html:21
msgid "Persona's"
-msgstr ""
+msgstr "Persona's"
#: mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/login_link.html:22
msgid "Or login with Persona!"
-msgstr ""
+msgstr "Of login met Persona!"
#: mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/register_link.html:22
msgid "Or register with Persona!"
-msgstr ""
+msgstr "Of registreer mer Persona!"
#: mediagoblin/processing/__init__.py:420
msgid "Invalid file given for media type."
@@ -1207,7 +1208,7 @@ msgstr "Verkeerd bestandsformaat voor mediatype opgegeven."
#: mediagoblin/processing/__init__.py:427
msgid "Copying to public storage failed."
-msgstr ""
+msgstr "Kopiëren naar openbare opslag mislukt."
#: mediagoblin/processing/__init__.py:435
msgid "An acceptable processing file was not found"
@@ -1215,7 +1216,7 @@ msgstr ""
#: mediagoblin/submit/forms.py:30
msgid "Max file size: {0} mb"
-msgstr ""
+msgstr "Max bestandsgrootte: {0} mb"
#: mediagoblin/submit/forms.py:34
msgid "File"
@@ -1226,20 +1227,20 @@ msgid ""
"You can use\n"
" <a href=\"http://daringfireball.net/projects/markdown/basics\">\n"
" Markdown</a> for formatting."
-msgstr ""
+msgstr "Je kunt\n <a href=\"http://daringfireball.net/projects/markdown/basics\">\n Markdown</a> gebruiken voor opmaak."
#: mediagoblin/submit/views.py:55
msgid "You must provide a file."
-msgstr "U moet een bestand aangeven."
+msgstr "Je moet een bestand opgeven."
#: mediagoblin/submit/views.py:138
#, python-format
msgid "Collection \"%s\" added!"
-msgstr ""
+msgstr "Verzameling \"%s\" toegevoegd!"
#: mediagoblin/templates/mediagoblin/banned.html:20
msgid "You are Banned."
-msgstr ""
+msgstr "Je bent geblokkeerd."
#: mediagoblin/templates/mediagoblin/banned.html:24
#: mediagoblin/templates/mediagoblin/error.html:24
@@ -1248,12 +1249,12 @@ msgstr ""
#: mediagoblin/templates/mediagoblin/banned.html:26
msgid "You have been banned"
-msgstr ""
+msgstr "Je bent geblokkeerd"
#: mediagoblin/templates/mediagoblin/banned.html:28
#, python-format
msgid "until %(until_when)s"
-msgstr ""
+msgstr "tot %(until_when)s"
#: mediagoblin/templates/mediagoblin/base.html:97
msgid "Verify your email!"
@@ -1262,12 +1263,12 @@ msgstr "Verifieer je e-mailadres!"
#: mediagoblin/templates/mediagoblin/base.html:104
#: mediagoblin/templates/mediagoblin/base.html:112
msgid "log out"
-msgstr ""
+msgstr "uitloggen"
#: mediagoblin/templates/mediagoblin/base.html:131
#, python-format
msgid "<a href=\"%(user_url)s\">%(user_name)s</a>'s account"
-msgstr ""
+msgstr "<a href=\"%(user_url)s\">%(user_name)s</a>'s account"
#: mediagoblin/templates/mediagoblin/base.html:138
msgid "Change account settings"
@@ -1284,7 +1285,7 @@ msgstr "Mediaverwerkingspaneel"
#: mediagoblin/templates/mediagoblin/base.html:152
msgid "Log out"
-msgstr ""
+msgstr "Afmelden"
#: mediagoblin/templates/mediagoblin/base.html:155
#: mediagoblin/templates/mediagoblin/user_pages/user.html:113
@@ -1294,72 +1295,72 @@ msgstr "Voeg media toe"
#: mediagoblin/templates/mediagoblin/base.html:158
#: mediagoblin/templates/mediagoblin/user_pages/collection_list.html:41
msgid "Create new collection"
-msgstr ""
+msgstr "Creëer een nieuwe verzameling"
#: mediagoblin/templates/mediagoblin/base.html:163
msgid "Moderation powers:"
-msgstr ""
+msgstr "Moderator mogelijkheden:"
#: mediagoblin/templates/mediagoblin/base.html:169
msgid "User management panel"
-msgstr ""
+msgstr "Gebruikersbeheer panel"
#: mediagoblin/templates/mediagoblin/base.html:173
msgid "Report management panel"
-msgstr ""
+msgstr "Meldingenbeheer panel"
#: mediagoblin/templates/mediagoblin/api/authorize.html:21
msgid "Authorization"
-msgstr ""
+msgstr "Autorisatie"
#: mediagoblin/templates/mediagoblin/api/authorize.html:26
#: mediagoblin/templates/mediagoblin/api/authorize.html:53
msgid "Authorize"
-msgstr ""
+msgstr "Autoriseer"
#: mediagoblin/templates/mediagoblin/api/authorize.html:29
msgid "You are logged in as"
-msgstr ""
+msgstr "Je bent ingelogd als"
#: mediagoblin/templates/mediagoblin/api/authorize.html:33
msgid "Do you want to authorize "
-msgstr ""
+msgstr "Sta je toegang toe"
#: mediagoblin/templates/mediagoblin/api/authorize.html:37
msgid "an unknown application"
-msgstr ""
+msgstr "voor een onbekende app"
#: mediagoblin/templates/mediagoblin/api/authorize.html:39
msgid " to access your account? "
-msgstr ""
+msgstr "om je account te gebruiken?"
#: mediagoblin/templates/mediagoblin/api/authorize.html:41
msgid "Applications with access to your account can: "
-msgstr ""
+msgstr "Apps met toegang tot jouw account kunnen:"
#: mediagoblin/templates/mediagoblin/api/authorize.html:43
msgid "Post new media as you"
-msgstr ""
+msgstr "Nieuwe media namens jou plaatsen"
#: mediagoblin/templates/mediagoblin/api/authorize.html:44
msgid "See your information (e.g profile, media, etc...)"
-msgstr ""
+msgstr "Je informatie zien (bv. profiel, media etc.)"
#: mediagoblin/templates/mediagoblin/api/authorize.html:45
msgid "Change your information"
-msgstr ""
+msgstr "Je informatie wijzigen"
#: mediagoblin/templates/mediagoblin/api/oob.html:21
msgid "Authorization Finished"
-msgstr ""
+msgstr "Autorisatie gereed"
#: mediagoblin/templates/mediagoblin/api/oob.html:26
msgid "Authorization Complete"
-msgstr ""
+msgstr "Autorisatie compleet"
#: mediagoblin/templates/mediagoblin/api/oob.html:28
msgid "Copy and paste this into your client:"
-msgstr ""
+msgstr "Kopieer en plak dit in je client:"
#: mediagoblin/templates/mediagoblin/auth/register.html:28
#: mediagoblin/templates/mediagoblin/auth/register.html:36
@@ -1379,14 +1380,14 @@ msgid ""
"your web browser:\n"
"\n"
"%(verification_url)s"
-msgstr "Hallo %(username)s , open de volgende URL in uw webbrowser om uw GNU MediaGoblin account te activeren: %(verification_url)s "
+msgstr "Hallo %(username)s , open de volgende URL in je webbrowser om je GNU MediaGoblin account te activeren: %(verification_url)s "
#: mediagoblin/templates/mediagoblin/bits/base_footer.html:21
#, python-format
msgid ""
"Powered by <a href=\"http://mediagoblin.org/\" title='Version "
"%(version)s'>MediaGoblin</a>, a <a href=\"http://gnu.org/\">GNU</a> project."
-msgstr ""
+msgstr "Powered by <a href=\"http://mediagoblin.org/\" title='Version %(version)s'>MediaGoblin</a>, een <a href=\"http://gnu.org/\">GNU</a> project."
#: mediagoblin/templates/mediagoblin/bits/base_footer.html:24
#, python-format
@@ -1398,7 +1399,7 @@ msgstr "Uitgegeven onder de <a href=\"http://www.fsf.org/licensing/licenses/agpl
#: mediagoblin/templates/mediagoblin/bits/base_footer.html:30
msgid "Terms of Service"
-msgstr ""
+msgstr "Gebruiksvoorwaarden"
#: mediagoblin/templates/mediagoblin/bits/frontpage_welcome.html:20
msgid "Explore"
@@ -1418,7 +1419,7 @@ msgstr "Deze website draait <a href=\"http://mediagoblin.org\">MediaGoblin</a>,
msgid ""
"To add your own media, place comments, and more, you can log in with your "
"MediaGoblin account."
-msgstr ""
+msgstr "Om je eigen media toe te voegen, te reageren en meer, kun je inloggen met je MediaGoblin account."
#: mediagoblin/templates/mediagoblin/bits/frontpage_welcome.html:29
msgid "Don't have one yet? It's easy!"
@@ -1429,13 +1430,13 @@ msgid ""
"\n"
" >Create an account at this site</a>\n"
" or"
-msgstr ""
+msgstr "\n >Creëer een account op deze site</a>\n of"
#: mediagoblin/templates/mediagoblin/bits/frontpage_welcome.html:42
msgid ""
"\n"
" <a class=\"button_action\" href=\"http://mediagoblin.readthedocs.org/\">Set up MediaGoblin on your own server</a>"
-msgstr ""
+msgstr "\n <a class=\"button_action\" href=\"http://mediagoblin.readthedocs.org/\">Installeer MediaGoblin op je eigen server</a>"
#: mediagoblin/templates/mediagoblin/bits/logo.html:23
#: mediagoblin/themes/airy/templates/mediagoblin/bits/logo.html:23
@@ -1446,18 +1447,18 @@ msgstr "MediaGoblin logo"
#: mediagoblin/templates/mediagoblin/edit/attachments.html:35
#, python-format
msgid "Editing attachments for %(media_title)s"
-msgstr ""
+msgstr "Aanpassen bijlagen voor %(media_title)s"
#: mediagoblin/templates/mediagoblin/edit/attachments.html:44
#: mediagoblin/templates/mediagoblin/user_pages/media.html:204
#: mediagoblin/templates/mediagoblin/user_pages/media.html:220
msgid "Attachments"
-msgstr ""
+msgstr "Bijlagen"
#: mediagoblin/templates/mediagoblin/edit/attachments.html:57
#: mediagoblin/templates/mediagoblin/user_pages/media.html:226
msgid "Add attachment"
-msgstr ""
+msgstr "Voeg een bijlage toe"
#: mediagoblin/templates/mediagoblin/edit/attachments.html:63
#: mediagoblin/templates/mediagoblin/edit/edit.html:42
@@ -1471,16 +1472,16 @@ msgstr "Wijzigingen opslaan"
#: mediagoblin/templates/mediagoblin/edit/change_email.html:33
#, python-format
msgid "Changing %(username)s's email"
-msgstr ""
+msgstr "Wijzigen %(username)s's e-mailadres"
#: mediagoblin/templates/mediagoblin/edit/delete_account.html:28
#, python-format
msgid "Really delete user '%(user_name)s' and all related media/comments?"
-msgstr ""
+msgstr "Echt gebruiker '%(user_name)s' en alle gerelateerde media/reacties verwijderen?"
#: mediagoblin/templates/mediagoblin/edit/delete_account.html:35
msgid "Yes, really delete my account"
-msgstr ""
+msgstr "Ja, verwijder mijn account"
#: mediagoblin/templates/mediagoblin/edit/edit.html:23
#: mediagoblin/templates/mediagoblin/edit/edit.html:35
@@ -1496,16 +1497,16 @@ msgstr "%(username)ss accountinstellingen aanpassen"
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:54
msgid "Delete my account"
-msgstr ""
+msgstr "Verwijder mijn account"
#: mediagoblin/templates/mediagoblin/edit/edit_account.html:59
msgid "Email"
-msgstr ""
+msgstr "E-mailadres"
#: mediagoblin/templates/mediagoblin/edit/edit_collection.html:29
#, python-format
msgid "Editing %(collection_title)s"
-msgstr ""
+msgstr "Bewerken %(collection_title)s"
#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:23
#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:34
@@ -1516,23 +1517,23 @@ msgstr "Het profiel aanpassen van %(username)s"
#: mediagoblin/templates/mediagoblin/edit/metadata.html:67
#, python-format
msgid "Metadata for \"%(media_name)s\""
-msgstr ""
+msgstr "Metadata voor \"%(media_name)s\""
#: mediagoblin/templates/mediagoblin/edit/metadata.html:72
msgid "MetaData"
-msgstr ""
+msgstr "MetaData"
#: mediagoblin/templates/mediagoblin/edit/metadata.html:80
msgid "Add new Row"
-msgstr ""
+msgstr "Voeg nieuwe rij toe"
#: mediagoblin/templates/mediagoblin/edit/metadata.html:83
msgid "Update Metadata"
-msgstr ""
+msgstr "Metadata bijwerken"
#: mediagoblin/templates/mediagoblin/edit/metadata.html:87
msgid "Clear empty Rows"
-msgstr ""
+msgstr "Lege rijen opschonen"
#: mediagoblin/templates/mediagoblin/edit/verification.txt:19
#, python-format
@@ -1546,11 +1547,11 @@ msgid ""
"\n"
"If you are not %(username)s or didn't request an email change, you can ignore\n"
"this email."
-msgstr ""
+msgstr "Hallo,\n\nWe willen verifiëren dat jij %(username)s bent. Als dat zo is, dan \nkun je de onderstaande link volgens om je e-mailadres te verifiëren.\n\n%(verification_url)s\n\nAls je niet %(username)s bent, of geen e-mailadreswijziging aanvroeg, dan kun je dit bericht negeren."
#: mediagoblin/templates/mediagoblin/fragments/header_notifications.html:4
msgid "New comments"
-msgstr ""
+msgstr "Nieuwe reacties"
#: mediagoblin/templates/mediagoblin/fragments/header_notifications.html:24
#: mediagoblin/templates/mediagoblin/media_displays/image.html:39
@@ -1563,11 +1564,11 @@ msgstr ""
#: mediagoblin/templates/mediagoblin/user_pages/report.html:48
#, python-format
msgid "%(formatted_time)s ago"
-msgstr ""
+msgstr "%(formatted_time)s geleden"
#: mediagoblin/templates/mediagoblin/fragments/header_notifications.html:41
msgid "Mark all read"
-msgstr ""
+msgstr "Alles markeren als gelezen"
#: mediagoblin/templates/mediagoblin/listings/collection.html:30
#: mediagoblin/templates/mediagoblin/listings/collection.html:35
@@ -1582,7 +1583,7 @@ msgstr "Media met het label: %(tag_name)s"
#: mediagoblin/templates/mediagoblin/media_displays/pdf.html:67
#: mediagoblin/templates/mediagoblin/media_displays/video.html:74
msgid "Download"
-msgstr ""
+msgstr "Download"
#: mediagoblin/templates/mediagoblin/media_displays/ascii.html:38
msgid "Original"
@@ -1593,28 +1594,28 @@ msgid ""
"Sorry, this audio will not work because \n"
"\tyour web browser does not support HTML5 \n"
"\taudio."
-msgstr "Sorry, dit geluidsfragment zal niet werken omdat\n»uw web-browser geen HTML5 ondersteunt⏎\n»audio."
+msgstr "Sorry, dit geluidsfragment zal niet werken omdat \n\tje webbrowser geen HTML5 audio\n\tondersteunt."
#: mediagoblin/templates/mediagoblin/media_displays/audio.html:47
msgid ""
"You can get a modern web browser that \n"
"\tcan play the audio at <a href=\"http://getfirefox.com\">\n"
"\t http://getfirefox.com</a>!"
-msgstr "U kunt een moderne web-browser die \n\taudio kan afspelen vinden op <a href=\"http://getfirefox.com\">\n\t http://getfirefox.com</a>!"
+msgstr "Je kunt een moderne web-browser die \n\taudio kan afspelen vinden op <a href=\"http://getfirefox.com\">\n\t http://getfirefox.com</a>!"
#: mediagoblin/templates/mediagoblin/media_displays/audio.html:60
#: mediagoblin/templates/mediagoblin/media_displays/pdf.html:73
#: mediagoblin/templates/mediagoblin/media_displays/video.html:80
msgid "Original file"
-msgstr ""
+msgstr "Origineel bestand"
#: mediagoblin/templates/mediagoblin/media_displays/audio.html:63
msgid "WebM file (Vorbis codec)"
-msgstr ""
+msgstr "WebM bestand (Vorbis codec)"
#: mediagoblin/templates/mediagoblin/media_displays/image.html:36
msgid "Created"
-msgstr ""
+msgstr "Aangemaakt"
#: mediagoblin/templates/mediagoblin/media_displays/pdf.html:59
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:90
@@ -1631,62 +1632,62 @@ msgstr "Afbeelding voor %(media_title)s"
#: mediagoblin/templates/mediagoblin/media_displays/pdf.html:81
msgid "PDF file"
-msgstr ""
+msgstr "PDF bestand"
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:116
msgid "Perspective"
-msgstr ""
+msgstr "Perspectief"
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:119
msgid "Front"
-msgstr ""
+msgstr "Voorkant"
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:122
msgid "Top"
-msgstr ""
+msgstr "Bovenkant"
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:125
msgid "Side"
-msgstr ""
+msgstr "Zijkant"
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:130
msgid "WebGL"
-msgstr ""
+msgstr "WebGL"
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:136
msgid "Download model"
-msgstr ""
+msgstr "Download model"
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:145
msgid "File Format"
-msgstr ""
+msgstr "Bestandsformaat"
#: mediagoblin/templates/mediagoblin/media_displays/stl.html:147
msgid "Object Height"
-msgstr ""
+msgstr "Object hoogte"
#: mediagoblin/templates/mediagoblin/media_displays/video.html:63
msgid ""
"Sorry, this video will not work because\n"
" your web browser does not support HTML5 \n"
" video."
-msgstr ""
+msgstr "Sorry, deze video werkt niet omdat\nje webbrowser geen HTML5 video ondersteunt."
#: mediagoblin/templates/mediagoblin/media_displays/video.html:66
msgid ""
"You can get a modern web browser that \n"
" can play this video at <a href=\"http://getfirefox.com\">\n"
" http://getfirefox.com</a>!"
-msgstr ""
+msgstr "Je kunt een moderne webbrowser die\ndeze video af kan spelen krijgen op\n<a href=\"http://getfirefox.com\">http://getfirefox.com</a>!"
#: mediagoblin/templates/mediagoblin/media_displays/video.html:88
msgid "WebM file (VP8/Vorbis)"
-msgstr ""
+msgstr "WebM bestand (VP8/Vorbis)"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:30
msgid ""
"Here you can track the state of media being processed on this instance."
-msgstr ""
+msgstr "Hier kun je de status zien van de media die verwerkt worden op deze Goblin."
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:33
#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:32
@@ -1698,26 +1699,26 @@ msgstr "Media te verwerken"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:98
#: mediagoblin/templates/mediagoblin/moderation/user_panel.html:75
msgid "ID"
-msgstr ""
+msgstr "ID"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:39
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:68
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:99
msgid "User"
-msgstr ""
+msgstr "Gebruiker"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:41
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:70
msgid "When submitted"
-msgstr ""
+msgstr "Wanneer toegevoegd"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:42
msgid "Transcoding progress"
-msgstr ""
+msgstr "Transcoderen voortgang"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:53
msgid "Unknown"
-msgstr ""
+msgstr "Onbekend"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:59
#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:56
@@ -1731,46 +1732,46 @@ msgstr "Deze toevoegingen konden niet verwerkt worden:"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:71
msgid "Reason for failure"
-msgstr ""
+msgstr "Reden voor mislukken"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:72
msgid "Failure metadata"
-msgstr ""
+msgstr "Mislukken metadata"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:91
#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:86
msgid "No failed entries!"
-msgstr ""
+msgstr "Geen fouten!"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:93
msgid "Last 10 successful uploads"
-msgstr ""
+msgstr "Laatste 10 geslaagde uploads"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:101
msgid "Submitted"
-msgstr ""
+msgstr "Aangemeld"
#: mediagoblin/templates/mediagoblin/moderation/media_panel.html:113
#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:107
msgid "No processed entries, yet!"
-msgstr ""
+msgstr "Niets verwerkt, nog niet!"
#: mediagoblin/templates/mediagoblin/moderation/report.html:27
msgid "Sorry, no such report found."
-msgstr ""
+msgstr "Sorry, geen melding gevonden."
#: mediagoblin/templates/mediagoblin/moderation/report.html:33
msgid "Return to Reports Panel"
-msgstr ""
+msgstr "Terug naar Meldingenpanel"
#: mediagoblin/templates/mediagoblin/moderation/report.html:35
#: mediagoblin/templates/mediagoblin/user_pages/media.html:162
msgid "Report"
-msgstr ""
+msgstr "Melden"
#: mediagoblin/templates/mediagoblin/moderation/report.html:38
msgid "Reported comment"
-msgstr ""
+msgstr "Gemelde reactie"
#: mediagoblin/templates/mediagoblin/moderation/report.html:83
#, python-format
@@ -1778,7 +1779,7 @@ msgid ""
"\n"
" ❖ Reported media by <a href=\"%(user_url)s\">%(user_name)s</a>\n"
" "
-msgstr ""
+msgstr "\n ❖ Gemelde media door <a href=\"%(user_url)s\">%(user_name)s</a>\n "
#: mediagoblin/templates/mediagoblin/moderation/report.html:92
#, python-format
@@ -1788,68 +1789,68 @@ msgid ""
" <a href=\"%(user_url)s\"> %(user_name)s</a>\n"
" HAS BEEN DELETED\n"
" "
-msgstr ""
+msgstr "\n INHOUD VAN\n <a href=\"%(user_url)s\"> %(user_name)s</a>\n IS VERWIJDERD\n "
#: mediagoblin/templates/mediagoblin/moderation/report.html:102
msgid "Reason for report:"
-msgstr ""
+msgstr "Reden voor melding:"
#: mediagoblin/templates/mediagoblin/moderation/report.html:133
#: mediagoblin/templates/mediagoblin/moderation/user.html:136
msgid "Resolve"
-msgstr ""
+msgstr "Oplossen"
#: mediagoblin/templates/mediagoblin/moderation/report.html:137
#: mediagoblin/templates/mediagoblin/moderation/report.html:157
msgid "Resolve This Report"
-msgstr ""
+msgstr "Los deze melding op"
#: mediagoblin/templates/mediagoblin/moderation/report.html:149
msgid "Status"
-msgstr ""
+msgstr "Status"
#: mediagoblin/templates/mediagoblin/moderation/report.html:151
msgid "RESOLVED"
-msgstr ""
+msgstr "OPGELOST"
#: mediagoblin/templates/mediagoblin/moderation/report.html:159
msgid "You cannot take action against an administrator"
-msgstr ""
+msgstr "Je kunt geen actie nemen tegenover een beheerder"
#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:22
#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:27
msgid "Report panel"
-msgstr ""
+msgstr "Meldingenpanel"
#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:30
msgid ""
"\n"
" Here you can look up open reports that have been filed by users.\n"
" "
-msgstr ""
+msgstr "\n Hier kun je de door gebruikers ingestuurde open meldingen zien.\n "
#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:35
msgid "Active Reports Filed"
-msgstr ""
+msgstr "Actieve ingestuurde meldingen"
#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:77
#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:173
msgid "Offender"
-msgstr ""
+msgstr "Overtreder"
#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:78
msgid "When Reported"
-msgstr ""
+msgstr "Wanneer gemeld"
#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:79
#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:175
msgid "Reported By"
-msgstr ""
+msgstr "Gemeld door"
#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:80
#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:176
msgid "Reason"
-msgstr ""
+msgstr "Reden"
#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:95
#, python-format
@@ -1857,7 +1858,7 @@ msgid ""
"\n"
" Comment Report #%(report_id)s\n"
" "
-msgstr ""
+msgstr "\n Gemelde Reactie #%(report_id)s\n "
#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:111
#, python-format
@@ -1865,23 +1866,23 @@ msgid ""
"\n"
" Media Report #%(report_id)s\n"
" "
-msgstr ""
+msgstr "\n Gemelde Media #%(report_id)s\n "
#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:125
msgid "No open reports found."
-msgstr ""
+msgstr "Geen open meldingen gevonden."
#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:127
msgid "Closed Reports"
-msgstr ""
+msgstr "Afgesloten meldingen"
#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:172
msgid "Resolved"
-msgstr ""
+msgstr "Opgelost"
#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:174
msgid "Action Taken"
-msgstr ""
+msgstr "Actie"
#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:188
#, python-format
@@ -1889,36 +1890,36 @@ msgid ""
"\n"
" Closed Report #%(report_id)s\n"
" "
-msgstr ""
+msgstr "\n Afgesloten melding #%(report_id)s\n "
#: mediagoblin/templates/mediagoblin/moderation/report_panel.html:202
msgid "No closed reports found."
-msgstr ""
+msgstr "Geen afgesloten meldingen gevonden."
#: mediagoblin/templates/mediagoblin/moderation/user.html:23
#, python-format
msgid "User: %(username)s"
-msgstr ""
+msgstr "Gebruiker: %(username)s"
#: mediagoblin/templates/mediagoblin/moderation/user.html:42
msgid "Return to Users Panel"
-msgstr ""
+msgstr "Terug naar gebruikerspanel"
#: mediagoblin/templates/mediagoblin/moderation/user.html:49
msgid "Sorry, no such user found."
-msgstr ""
+msgstr "Sorry, die gebruiker kon niet worden gevonden."
#: mediagoblin/templates/mediagoblin/moderation/user.html:53
#: mediagoblin/templates/mediagoblin/user_pages/user_nonactive.html:40
#: mediagoblin/templates/mediagoblin/user_pages/user_nonactive.html:60
msgid "Email verification needed"
-msgstr "Emailverificatie is nodig"
+msgstr "E-mailverificatie is nodig"
#: mediagoblin/templates/mediagoblin/moderation/user.html:55
msgid ""
"Someone has registered an account with this username, but it still has\n"
" to be activated."
-msgstr ""
+msgstr "Iemand heeft een account met deze gebruikersnaam gemaakt, maar die moet nog geactiveerd worden."
#: mediagoblin/templates/mediagoblin/moderation/user.html:66
#: mediagoblin/templates/mediagoblin/user_pages/user.html:34
@@ -1931,11 +1932,11 @@ msgstr "Profiel van %(username)s"
#: mediagoblin/templates/mediagoblin/moderation/user.html:68
#, python-format
msgid "BANNED until %(expiration_date)s"
-msgstr ""
+msgstr "GEBLOKKEERD tot %(expiration_date)s"
#: mediagoblin/templates/mediagoblin/moderation/user.html:72
msgid "Banned Indefinitely"
-msgstr ""
+msgstr "Geblokkeerd voor onbepaalde tijd"
#: mediagoblin/templates/mediagoblin/moderation/user.html:78
#: mediagoblin/templates/mediagoblin/user_pages/user.html:62
@@ -1951,113 +1952,113 @@ msgstr "Profiel aanpassen."
#: mediagoblin/templates/mediagoblin/moderation/user.html:96
#: mediagoblin/templates/mediagoblin/user_pages/user.html:81
msgid "Browse collections"
-msgstr ""
+msgstr "Blader door verzamelingen"
#: mediagoblin/templates/mediagoblin/moderation/user.html:105
#, python-format
msgid "Active Reports on %(username)s"
-msgstr ""
+msgstr "Actieve meldingen voor %(username)s"
#: mediagoblin/templates/mediagoblin/moderation/user.html:112
msgid "Report ID"
-msgstr ""
+msgstr "Melding ID"
#: mediagoblin/templates/mediagoblin/moderation/user.html:113
msgid "Reported Content"
-msgstr ""
+msgstr "Gemelde inhoud"
#: mediagoblin/templates/mediagoblin/moderation/user.html:114
msgid "Description of Report"
-msgstr ""
+msgstr "Beschrijving van melding"
#: mediagoblin/templates/mediagoblin/moderation/user.html:122
#, python-format
msgid "Report #%(report_number)s"
-msgstr ""
+msgstr "Melding #%(report_number)s"
#: mediagoblin/templates/mediagoblin/moderation/user.html:129
msgid "Reported Comment"
-msgstr ""
+msgstr "Gemelde reactie"
#: mediagoblin/templates/mediagoblin/moderation/user.html:131
msgid "Reported Media Entry"
-msgstr ""
+msgstr "Gemelde media"
#: mediagoblin/templates/mediagoblin/moderation/user.html:142
#, python-format
msgid "No active reports filed on %(username)s"
-msgstr ""
+msgstr "Geen actieve meldingen gedaan voor %(username)s"
#: mediagoblin/templates/mediagoblin/moderation/user.html:150
#, python-format
msgid "All reports on %(username)s"
-msgstr ""
+msgstr "Alle meldingen voor %(username)s"
#: mediagoblin/templates/mediagoblin/moderation/user.html:155
#, python-format
msgid "All reports that %(username)s has filed"
-msgstr ""
+msgstr "Alle meldingen die %(username)s heeft ingestuurd"
#: mediagoblin/templates/mediagoblin/moderation/user.html:164
#, python-format
msgid "%(username)s's Privileges"
-msgstr ""
+msgstr "%(username)s's rechten"
#: mediagoblin/templates/mediagoblin/moderation/user.html:172
msgid "Privilege"
-msgstr ""
+msgstr "Recht"
#: mediagoblin/templates/mediagoblin/moderation/user.html:173
msgid "Granted"
-msgstr ""
+msgstr "Toegekend"
#: mediagoblin/templates/mediagoblin/moderation/user.html:180
msgid "Yes"
-msgstr ""
+msgstr "Ja"
#: mediagoblin/templates/mediagoblin/moderation/user.html:182
msgid "No"
-msgstr ""
+msgstr "Nee"
#: mediagoblin/templates/mediagoblin/moderation/user.html:213
msgid "Ban User"
-msgstr ""
+msgstr "Blokkeer gebruiker"
#: mediagoblin/templates/mediagoblin/moderation/user.html:218
msgid "UnBan User"
-msgstr ""
+msgstr "Deblokkeer gebruiker"
#: mediagoblin/templates/mediagoblin/moderation/user_panel.html:21
#: mediagoblin/templates/mediagoblin/moderation/user_panel.html:26
msgid "User panel"
-msgstr ""
+msgstr "Gebruikerspanel"
#: mediagoblin/templates/mediagoblin/moderation/user_panel.html:29
msgid ""
"\n"
" Here you can look up users in order to take punitive actions on them.\n"
" "
-msgstr ""
+msgstr "\n Hier kun je je gebruikers opzoeken om eventueel acties te ondernemen.\n "
#: mediagoblin/templates/mediagoblin/moderation/user_panel.html:34
msgid "Active Users"
-msgstr ""
+msgstr "Actieve gebruikers"
#: mediagoblin/templates/mediagoblin/moderation/user_panel.html:77
msgid "When Joined"
-msgstr ""
+msgstr "Wanneer aangemeld"
#: mediagoblin/templates/mediagoblin/moderation/user_panel.html:78
msgid "# of Comments Posted"
-msgstr ""
+msgstr "# geplaatste reacties"
#: mediagoblin/templates/mediagoblin/moderation/user_panel.html:95
msgid "No users found."
-msgstr ""
+msgstr "Geen gebruikers gevonden."
#: mediagoblin/templates/mediagoblin/submit/collection.html:26
msgid "Add a collection"
-msgstr ""
+msgstr "Voeg een collectie toe"
#: mediagoblin/templates/mediagoblin/submit/start.html:28
#: mediagoblin/templates/mediagoblin/submit/start.html:35
@@ -2067,7 +2068,7 @@ msgstr "Voeg media toe"
#: mediagoblin/templates/mediagoblin/user_pages/blog_media.html:38
#, python-format
msgid "❖ Blog post by <a href=\"%(user_url)s\">%(username)s</a>"
-msgstr ""
+msgstr "❖ Blogbericht door <a href=\"%(user_url)s\">%(username)s</a>"
#: mediagoblin/templates/mediagoblin/user_pages/blog_media.html:92
#: mediagoblin/templates/mediagoblin/user_pages/media.html:104
@@ -2082,58 +2083,58 @@ msgstr "Voeg dit bericht toe"
#: mediagoblin/templates/mediagoblin/user_pages/blog_media.html:149
#: mediagoblin/templates/mediagoblin/user_pages/media.html:179
msgid "Added"
-msgstr ""
+msgstr "Toegevoegd"
#: mediagoblin/templates/mediagoblin/user_pages/collection.html:30
#, python-format
msgid "%(collection_title)s (%(username)s's collection)"
-msgstr ""
+msgstr "%(collection_title)s (%(username)s's collectie)"
#: mediagoblin/templates/mediagoblin/user_pages/collection.html:39
#, python-format
msgid "%(collection_title)s by <a href=\"%(user_url)s\">%(username)s</a>"
-msgstr ""
+msgstr "%(collection_title)s van <a href=\"%(user_url)s\">%(username)s</a>"
#: mediagoblin/templates/mediagoblin/user_pages/collection_confirm_delete.html:23
#, python-format
msgid "Delete collection %(collection_title)s"
-msgstr ""
+msgstr "Verwijder collectie %(collection_title)s"
#: mediagoblin/templates/mediagoblin/user_pages/collection_confirm_delete.html:38
#, python-format
msgid "Really delete collection: %(title)s?"
-msgstr ""
+msgstr "Collectie %(title)s echt verwijderen?"
#: mediagoblin/templates/mediagoblin/user_pages/collection_item_confirm_remove.html:23
#, python-format
msgid "Remove %(media_title)s from %(collection_title)s"
-msgstr ""
+msgstr "Verwijder %(media_title)s uit %(collection_title)s"
#: mediagoblin/templates/mediagoblin/user_pages/collection_item_confirm_remove.html:39
#, python-format
msgid "Really remove %(media_title)s from %(collection_title)s?"
-msgstr ""
+msgstr "Echt %(media_title)s verwijderen uit %(collection_title)s?"
#: mediagoblin/templates/mediagoblin/user_pages/collection_item_confirm_remove.html:62
msgid "Remove"
-msgstr ""
+msgstr "Verwijderen"
#: mediagoblin/templates/mediagoblin/user_pages/collection_list.html:21
#, python-format
msgid "%(username)s's collections"
-msgstr ""
+msgstr "%(username)s's verzamelingen"
#: mediagoblin/templates/mediagoblin/user_pages/collection_list.html:28
#, python-format
msgid "<a href=\"%(user_url)s\">%(username)s</a>'s collections"
-msgstr ""
+msgstr "<a href=\"%(user_url)s\">%(username)s</a>'s verzamelingen"
#: mediagoblin/templates/mediagoblin/user_pages/comment_email.txt:19
#, python-format
msgid ""
"Hi %(username)s,\n"
"%(comment_author)s commented on your post (%(comment_url)s) at %(instance_name)s\n"
-msgstr ""
+msgstr "Hallo %(username)s,\n%(comment_author)s reageerden op je bericht (%(comment_url)s) op %(instance_name)s\n"
#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:30
#, python-format
@@ -2145,7 +2146,7 @@ msgstr "Media van %(username)s"
msgid ""
"<a href=\"%(user_url)s\">%(username)s</a>'s media with tag <a "
"href=\"%(tag_url)s\">%(tag)s</a>"
-msgstr ""
+msgstr "<a href=\"%(user_url)s\">%(username)s</a>'s media met tag <a href=\"%(tag_url)s\">%(tag)s</a>"
#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:48
#, python-format
@@ -2159,21 +2160,21 @@ msgstr "❖ Blader door media van <a href=\"%(user_url)s\">%(username)s</a>"
#: mediagoblin/templates/mediagoblin/user_pages/media.html:119
msgid "Comment Preview"
-msgstr ""
+msgstr "Voorbeeldreactie"
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:28
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:40
#, python-format
msgid "Add “%(media_title)s” to a collection"
-msgstr ""
+msgstr "Voeg “%(media_title)s” toe aan collectie"
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:54
msgid "+"
-msgstr ""
+msgstr "+"
#: mediagoblin/templates/mediagoblin/user_pages/media_collect.html:58
msgid "Add a new collection"
-msgstr ""
+msgstr "Voeg een nieuwe collectie toe"
#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:29
msgid ""
@@ -2182,19 +2183,19 @@ msgstr "Hier kun je de status zien van de media die verwerkt worden."
#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:89
msgid "Your last 10 successful uploads"
-msgstr ""
+msgstr "Je 10 laatste geslaagde uploads"
#: mediagoblin/templates/mediagoblin/user_pages/report.html:21
msgid "<h2>File a Report</h2>"
-msgstr ""
+msgstr "<h2>Stuur een melding in</h2>"
#: mediagoblin/templates/mediagoblin/user_pages/report.html:24
msgid "Reporting this Comment"
-msgstr ""
+msgstr "Deze reactie wordt gemeld"
#: mediagoblin/templates/mediagoblin/user_pages/report.html:60
msgid "Reporting this Media Entry"
-msgstr ""
+msgstr "Dit mediabestand wordt gemeld"
#: mediagoblin/templates/mediagoblin/user_pages/report.html:72
#, python-format
@@ -2203,11 +2204,11 @@ msgid ""
" ❖ Published by <a href=\"%(user_url)s\"\n"
" class=\"comment_authorlink\">%(username)s</a>\n"
" "
-msgstr ""
+msgstr "\n ❖ gepubliceerd door <a href=\"%(user_url)s\"\n class=\"comment_authorlink\">%(username)s</a>\n "
#: mediagoblin/templates/mediagoblin/user_pages/report.html:81
msgid "File Report "
-msgstr ""
+msgstr "Bestandsmelding"
#: mediagoblin/templates/mediagoblin/user_pages/user.html:53
msgid "Here's a spot to tell others about yourself."
@@ -2258,32 +2259,32 @@ msgstr "Iemand heeft een account met deze gebruikersnaam gemaakt, maar hij moet
msgid ""
"If you are that person but you've lost your verification email, you can <a "
"href=\"%(login_url)s\">log in</a> and resend it."
-msgstr "Als u die persoon bent, maar de verificatie e-mail verloren hebt, kunt u <a href=\"%(login_url)s\">inloggen</a> en hem nogmaals verzenden."
+msgstr "Als jij die persoon bent, maar de verificatie e-mail verloren hebt, kunt je <a href=\"%(login_url)s\">inloggen</a> en het nogmaals verzenden."
#: mediagoblin/templates/mediagoblin/utils/collection_gallery.html:49
msgid "(remove)"
-msgstr ""
+msgstr "(verwijdered)"
#: mediagoblin/templates/mediagoblin/utils/collections.html:21
msgid "Collected in"
-msgstr ""
+msgstr "Opgenomen in"
#: mediagoblin/templates/mediagoblin/utils/collections.html:40
msgid "Add to a collection"
-msgstr ""
+msgstr "Voeg toe aan collectie"
#: mediagoblin/templates/mediagoblin/utils/comment-subscription.html:24
msgid "Subscribe to comments"
-msgstr ""
+msgstr "Abonneer op reacties"
#: mediagoblin/templates/mediagoblin/utils/comment-subscription.html:30
msgid "Silence comments"
-msgstr ""
+msgstr "Onderdruk reacties"
#: mediagoblin/templates/mediagoblin/utils/feed_link.html:21
#: mediagoblin/themes/airy/templates/mediagoblin/utils/feed_link.html:21
msgid "feed icon"
-msgstr "feed icoon"
+msgstr "feed pictogram"
#: mediagoblin/templates/mediagoblin/utils/feed_link.html:23
#: mediagoblin/themes/airy/templates/mediagoblin/utils/feed_link.html:23
@@ -2318,7 +2319,7 @@ msgstr "ouder"
#: mediagoblin/templates/mediagoblin/utils/report.html:25
msgid "Report media"
-msgstr ""
+msgstr "Meld media"
#: mediagoblin/templates/mediagoblin/utils/tags.html:20
msgid "Tagged with"
@@ -2334,19 +2335,19 @@ msgstr "Oeps!"
#: mediagoblin/tools/response.py:39
msgid "An error occured"
-msgstr ""
+msgstr "Er trad een fout op"
#: mediagoblin/tools/response.py:53
msgid "Bad Request"
-msgstr ""
+msgstr "Verkeerde aanvraag"
#: mediagoblin/tools/response.py:55
msgid "The request sent to the server is invalid, please double check it"
-msgstr ""
+msgstr "De aanvraag aan de server is ongeldig, controleer opnieuw"
#: mediagoblin/tools/response.py:63
msgid "Operation not allowed"
-msgstr ""
+msgstr "Actie niet toegestaan"
#: mediagoblin/tools/response.py:64
msgid ""
@@ -2364,68 +2365,68 @@ msgstr ""
#: mediagoblin/tools/timesince.py:62
msgid "year"
-msgstr ""
+msgstr "jaar"
#: mediagoblin/tools/timesince.py:63
msgid "month"
-msgstr ""
+msgstr "maand"
#: mediagoblin/tools/timesince.py:64
msgid "week"
-msgstr ""
+msgstr "week"
#: mediagoblin/tools/timesince.py:65
msgid "day"
-msgstr ""
+msgstr "dag"
#: mediagoblin/tools/timesince.py:66
msgid "hour"
-msgstr ""
+msgstr "uur"
#: mediagoblin/tools/timesince.py:67
msgid "minute"
-msgstr ""
+msgstr "minuut"
#: mediagoblin/user_pages/forms.py:23
msgid "Comment"
-msgstr ""
+msgstr "Reactie"
#: mediagoblin/user_pages/forms.py:25
msgid ""
"You can use <a href=\"http://daringfireball.net/projects/markdown/basics\" "
"target=\"_blank\">Markdown</a> for formatting."
-msgstr ""
+msgstr "Je kunt <a href=\"http://daringfireball.net/projects/markdown/basics\" target=\"_blank\">Markdown</a> gebruiken voor opmaak."
#: mediagoblin/user_pages/forms.py:35
msgid "I am sure I want to remove this item from the collection"
-msgstr ""
+msgstr "Ik weet zeker dat ik dit uit de verzameling wil verwijderen."
#: mediagoblin/user_pages/forms.py:39
msgid "Collection"
-msgstr ""
+msgstr "Verzameling"
#: mediagoblin/user_pages/forms.py:40
msgid "-- Select --"
-msgstr ""
+msgstr "-- Selecteer --"
#: mediagoblin/user_pages/forms.py:42
msgid "Include a note"
-msgstr ""
+msgstr "Voeg een notitie toe"
#: mediagoblin/user_pages/forms.py:49
msgid ""
"You can use\n"
" <a href=\"http://daringfireball.net/projects/markdown/basics\" target=\"_blank\">\n"
" Markdown</a> for formatting."
-msgstr ""
+msgstr "Je kunt\n <a href=\"http://daringfireball.net/projects/markdown/basics\" target=\"_blank\">\n Markdown</a> gebruiken voor opmaak."
#: mediagoblin/user_pages/forms.py:55 mediagoblin/user_pages/forms.py:61
msgid "Reason for Reporting"
-msgstr ""
+msgstr "Aanleiding voor melding"
#: mediagoblin/user_pages/views.py:188
msgid "Sorry, comments are disabled."
-msgstr ""
+msgstr "Sorry, reageren is uitgeschakeld."
#: mediagoblin/user_pages/views.py:193
msgid "Oops, your comment was empty."
@@ -2437,21 +2438,21 @@ msgstr "Je bericht is geplaatst!"
#: mediagoblin/user_pages/views.py:235
msgid "Please check your entries and try again."
-msgstr ""
+msgstr "Controleer de nummer en probeer het opnieuw."
#: mediagoblin/user_pages/views.py:275
msgid "You have to select or add a collection"
-msgstr ""
+msgstr "Je moet een collectie selecteren of aanmaken"
#: mediagoblin/user_pages/views.py:286
#, python-format
msgid "\"%s\" already in collection \"%s\""
-msgstr ""
+msgstr "\"%s\" zit al in collectie \"%s\""
#: mediagoblin/user_pages/views.py:292
#, python-format
msgid "\"%s\" added to collection \"%s\""
-msgstr ""
+msgstr "\"%s\" toegevoegd aan collectie \"%s\""
#: mediagoblin/user_pages/views.py:317
msgid "You deleted the media."
@@ -2463,29 +2464,29 @@ msgstr "Je staat op het punt de media van iemand anders te verwijderen. Pas op."
#: mediagoblin/user_pages/views.py:409
msgid "You deleted the item from the collection."
-msgstr ""
+msgstr "Je hebt het nummer uit de collectie verwijderd."
#: mediagoblin/user_pages/views.py:413
msgid "The item was not removed because you didn't check that you were sure."
-msgstr ""
+msgstr "Dit nummer is niet verwijderd omdat je niet hebt aangegeven dat je het zeker weet."
#: mediagoblin/user_pages/views.py:421
msgid ""
"You are about to delete an item from another user's collection. Proceed with"
" caution."
-msgstr ""
+msgstr "Je staat op het punt een nummer uit de collectie van iemand anders te verwijderen. Pas op."
#: mediagoblin/user_pages/views.py:453
#, python-format
msgid "You deleted the collection \"%s\""
-msgstr ""
+msgstr "Je verwijderde collectie \"%s\""
#: mediagoblin/user_pages/views.py:460
msgid ""
"The collection was not deleted because you didn't check that you were sure."
-msgstr ""
+msgstr "De collectie is niet verwijderd omdat je niet hebt aangegeven dat je het zeker weet."
#: mediagoblin/user_pages/views.py:468
msgid ""
"You are about to delete another user's collection. Proceed with caution."
-msgstr ""
+msgstr "Je staat op het punt om de collectie van iemand anders te verwijderen. Pas op."
diff --git a/mediagoblin/init/celery/__init__.py b/mediagoblin/init/celery/__init__.py
index 19c13f7d..780e0055 100644
--- a/mediagoblin/init/celery/__init__.py
+++ b/mediagoblin/init/celery/__init__.py
@@ -19,6 +19,8 @@ import sys
import datetime
import logging
+import six
+
from celery import Celery
from mediagoblin.tools.pluginapi import hook_runall
@@ -48,7 +50,7 @@ def get_celery_settings_dict(app_config, global_config,
celery_settings = {}
# Add all celery settings from config
- for key, value in celery_conf.iteritems():
+ for key, value in six.iteritems(celery_conf):
celery_settings[key] = value
# TODO: use default result stuff here if it exists
@@ -67,7 +69,7 @@ def get_celery_settings_dict(app_config, global_config,
frequency = int(frequency)
celery_settings['CELERYBEAT_SCHEDULE'] = {
'garbage-collection': {
- 'task': 'mediagoblin.submit.task.garbage_collection',
+ 'task': 'mediagoblin.submit.task.collect_garbage',
'schedule': datetime.timedelta(minutes=frequency),
}
}
@@ -113,7 +115,7 @@ def setup_celery_from_config(app_config, global_config,
__import__(settings_module)
this_module = sys.modules[settings_module]
- for key, value in celery_settings.iteritems():
+ for key, value in six.iteritems(celery_settings):
setattr(this_module, key, value)
if set_environ:
diff --git a/mediagoblin/init/config.py b/mediagoblin/init/config.py
index 11a91cff..a9189e8d 100644
--- a/mediagoblin/init/config.py
+++ b/mediagoblin/init/config.py
@@ -14,6 +14,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import copy
import logging
import os
import pkg_resources
@@ -29,16 +30,21 @@ CONFIG_SPEC_PATH = pkg_resources.resource_filename(
'mediagoblin', 'config_spec.ini')
-def _setup_defaults(config, config_path):
+def _setup_defaults(config, config_path, extra_defaults=None):
"""
Setup DEFAULTS in a config object from an (absolute) config_path.
"""
+ extra_defaults = extra_defaults or {}
+
config.setdefault('DEFAULT', {})
config['DEFAULT']['here'] = os.path.dirname(config_path)
config['DEFAULT']['__file__'] = config_path
+ for key, value in extra_defaults.items():
+ config['DEFAULT'].setdefault(key, value)
+
-def read_mediagoblin_config(config_path, config_spec=CONFIG_SPEC_PATH):
+def read_mediagoblin_config(config_path, config_spec_path=CONFIG_SPEC_PATH):
"""
Read a config object from config_path.
@@ -54,7 +60,7 @@ def read_mediagoblin_config(config_path, config_spec=CONFIG_SPEC_PATH):
Args:
- config_path: path to the config file
- - config_spec: config file that provides defaults and value types
+ - config_spec_path: config file that provides defaults and value types
for validation / conversion. Defaults to mediagoblin/config_spec.ini
Returns:
@@ -68,7 +74,20 @@ def read_mediagoblin_config(config_path, config_spec=CONFIG_SPEC_PATH):
# we can add their plugin specs to the general config_spec.
config = ConfigObj(
config_path,
- interpolation='ConfigParser')
+ interpolation="ConfigParser")
+
+ # temporary bootstrap, just setup here and __file__... we'll do this again
+ _setup_defaults(config, config_path)
+
+ # Now load the main config spec
+ config_spec = ConfigObj(
+ config_spec_path,
+ encoding="UTF8", list_values=False, _inspec=True)
+
+ # Set up extra defaults that will be pushed into the rest of the
+ # configs. This is a combined extrapolation of defaults based on
+ mainconfig_defaults = copy.copy(config_spec.get("DEFAULT", {}))
+ mainconfig_defaults.update(config["DEFAULT"])
plugins = config.get("plugins", {}).keys()
plugin_configs = {}
@@ -79,11 +98,12 @@ def read_mediagoblin_config(config_path, config_spec=CONFIG_SPEC_PATH):
plugin, "config_spec.ini")
if not os.path.exists(plugin_config_spec_path):
continue
-
+
plugin_config_spec = ConfigObj(
plugin_config_spec_path,
- encoding='UTF8', list_values=False, _inspec=True)
- _setup_defaults(plugin_config_spec, config_path)
+ encoding="UTF8", list_values=False, _inspec=True)
+ _setup_defaults(
+ plugin_config_spec, config_path, mainconfig_defaults)
if not "plugin_spec" in plugin_config_spec:
continue
@@ -94,23 +114,18 @@ def read_mediagoblin_config(config_path, config_spec=CONFIG_SPEC_PATH):
_log.warning(
"When setting up config section, could not import '%s'" %
plugin)
-
- # Now load the main config spec
- config_spec = ConfigObj(
- config_spec,
- encoding='UTF8', list_values=False, _inspec=True)
# append the plugin specific sections of the config spec
- config_spec['plugins'] = plugin_configs
+ config_spec["plugins"] = plugin_configs
- _setup_defaults(config_spec, config_path)
+ _setup_defaults(config_spec, config_path, mainconfig_defaults)
config = ConfigObj(
config_path,
configspec=config_spec,
- interpolation='ConfigParser')
+ interpolation="ConfigParser")
- _setup_defaults(config, config_path)
+ _setup_defaults(config, config_path, mainconfig_defaults)
# For now the validator just works with the default functions,
# but in the future if we want to add additional validation/configuration
diff --git a/mediagoblin/media_types/ascii/processing.py b/mediagoblin/media_types/ascii/processing.py
index 84030362..ed6600ee 100644
--- a/mediagoblin/media_types/ascii/processing.py
+++ b/mediagoblin/media_types/ascii/processing.py
@@ -22,6 +22,8 @@ except ImportError:
import Image
import logging
+import six
+
from mediagoblin import mg_globals as mgg
from mediagoblin.processing import (
create_pub_filepath, FilenameBuilder,
@@ -93,7 +95,7 @@ class CommonAsciiProcessor(MediaProcessor):
orig_file.seek(0)
def store_unicode_file(self):
- with file(self.process_filename, 'rb') as orig_file:
+ with open(self.process_filename, 'rb') as orig_file:
self._detect_charset(orig_file)
unicode_filepath = create_pub_filepath(self.entry,
'ascii-portable.txt')
@@ -104,7 +106,7 @@ class CommonAsciiProcessor(MediaProcessor):
# Encode the unicode instance to ASCII and replace any
# non-ASCII with an HTML entity (&#
unicode_file.write(
- unicode(orig_file.read().decode(
+ six.text_type(orig_file.read().decode(
self.charset)).encode(
'ascii',
'xmlcharrefreplace'))
@@ -112,7 +114,7 @@ class CommonAsciiProcessor(MediaProcessor):
self.entry.media_files['unicode'] = unicode_filepath
def generate_thumb(self, font=None, thumb_size=None):
- with file(self.process_filename, 'rb') as orig_file:
+ with open(self.process_filename, 'rb') as orig_file:
# If no font kwarg, check config
if not font:
font = self.ascii_config.get('thumbnail_font', None)
@@ -141,7 +143,7 @@ class CommonAsciiProcessor(MediaProcessor):
thumb = converter._create_image(
orig_file.read())
- with file(tmp_thumb, 'w') as thumb_file:
+ with open(tmp_thumb, 'w') as thumb_file:
thumb.thumbnail(
thumb_size,
Image.ANTIALIAS)
@@ -271,6 +273,6 @@ class Resizer(CommonAsciiProcessor):
class AsciiProcessingManager(ProcessingManager):
def __init__(self):
- super(self.__class__, self).__init__()
+ super(AsciiProcessingManager, self).__init__()
self.add_processor(InitialProcessor)
self.add_processor(Resizer)
diff --git a/mediagoblin/media_types/audio/processing.py b/mediagoblin/media_types/audio/processing.py
index c4ed4eca..de6fa9ca 100644
--- a/mediagoblin/media_types/audio/processing.py
+++ b/mediagoblin/media_types/audio/processing.py
@@ -370,7 +370,7 @@ class Transcoder(CommonAudioProcessor):
class AudioProcessingManager(ProcessingManager):
def __init__(self):
- super(self.__class__, self).__init__()
+ super(AudioProcessingManager, self).__init__()
self.add_processor(InitialProcessor)
self.add_processor(Resizer)
self.add_processor(Transcoder)
diff --git a/mediagoblin/media_types/blog/views.py b/mediagoblin/media_types/blog/views.py
index 088164b9..0b88037f 100644
--- a/mediagoblin/media_types/blog/views.py
+++ b/mediagoblin/media_types/blog/views.py
@@ -19,6 +19,8 @@ _log = logging.getLogger(__name__)
from datetime import datetime
+import six
+
from werkzeug.exceptions import Forbidden
from mediagoblin.tools import pluginapi
@@ -75,8 +77,8 @@ def blog_edit(request):
if request.method=='POST' and form.validate():
_log.info("Here")
blog = request.db.Blog()
- blog.title = unicode(form.title.data)
- blog.description = unicode(cleaned_markdown_conversion((form.description.data)))
+ blog.title = six.text_type(form.title.data)
+ blog.description = six.text_type(cleaned_markdown_conversion((form.description.data)))
blog.author = request.user.id
blog.generate_slug()
@@ -112,8 +114,8 @@ def blog_edit(request):
'app_config': mg_globals.app_config})
else:
if request.method == 'POST' and form.validate():
- blog.title = unicode(form.title.data)
- blog.description = unicode(cleaned_markdown_conversion((form.description.data)))
+ blog.title = six.text_type(form.title.data)
+ blog.description = six.text_type(cleaned_markdown_conversion((form.description.data)))
blog.author = request.user.id
blog.generate_slug()
@@ -137,10 +139,10 @@ def blogpost_create(request):
blogpost = request.db.MediaEntry()
blogpost.media_type = 'mediagoblin.media_types.blogpost'
- blogpost.title = unicode(form.title.data)
- blogpost.description = unicode(cleaned_markdown_conversion((form.description.data)))
+ blogpost.title = six.text_type(form.title.data)
+ blogpost.description = six.text_type(cleaned_markdown_conversion((form.description.data)))
blogpost.tags = convert_to_tag_list_of_dicts(form.tags.data)
- blogpost.license = unicode(form.license.data) or None
+ blogpost.license = six.text_type(form.license.data) or None
blogpost.uploader = request.user.id
blogpost.generate_slug()
@@ -187,10 +189,10 @@ def blogpost_edit(request):
form = blog_forms.BlogPostEditForm(request.form, **defaults)
if request.method == 'POST' and form.validate():
- blogpost.title = unicode(form.title.data)
- blogpost.description = unicode(cleaned_markdown_conversion((form.description.data)))
+ blogpost.title = six.text_type(form.title.data)
+ blogpost.description = six.text_type(cleaned_markdown_conversion((form.description.data)))
blogpost.tags = convert_to_tag_list_of_dicts(form.tags.data)
- blogpost.license = unicode(form.license.data)
+ blogpost.license = six.text_type(form.license.data)
set_blogpost_state(request, blogpost)
blogpost.generate_slug()
blogpost.save()
diff --git a/mediagoblin/media_types/image/processing.py b/mediagoblin/media_types/image/processing.py
index 6dd540e5..e0ddfe87 100644
--- a/mediagoblin/media_types/image/processing.py
+++ b/mediagoblin/media_types/image/processing.py
@@ -14,6 +14,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from __future__ import print_function
+
try:
from PIL import Image
except ImportError:
@@ -22,6 +24,8 @@ import os
import logging
import argparse
+import six
+
from mediagoblin import mg_globals as mgg
from mediagoblin.db.models import Location
from mediagoblin.processing import (
@@ -66,14 +70,14 @@ def resize_image(entry, resized, keyname, target_name, new_size,
resize_filter = PIL_FILTERS[filter.upper()]
except KeyError:
raise Exception('Filter "{0}" not found, choose one of {1}'.format(
- unicode(filter),
+ six.text_type(filter),
u', '.join(PIL_FILTERS.keys())))
resized.thumbnail(new_size, resize_filter)
# Copy the new file to the conversion subdir, then remotely.
tmp_resized_filename = os.path.join(workdir, target_name)
- with file(tmp_resized_filename, 'w') as resized_file:
+ with open(tmp_resized_filename, 'wb') as resized_file:
resized.save(resized_file, quality=quality)
store_public(entry, keyname, tmp_resized_filename, target_name)
@@ -115,7 +119,7 @@ def resize_tool(entry,
or im.size[1] > new_size[1]\
or exif_image_needs_rotation(exif_tags):
resize_image(
- entry, im, unicode(keyname), target_name,
+ entry, im, six.text_type(keyname), target_name,
tuple(new_size),
exif_tags, conversions_subdir,
quality, filter)
@@ -365,7 +369,7 @@ class Resizer(CommonImageProcessor):
class ImageProcessingManager(ProcessingManager):
def __init__(self):
- super(self.__class__, self).__init__()
+ super(ImageProcessingManager, self).__init__()
self.add_processor(InitialProcessor)
self.add_processor(Resizer)
@@ -381,5 +385,4 @@ if __name__ == '__main__':
clean = clean_exif(result)
useful = get_useful(clean)
- print pp.pprint(
- clean)
+ print(pp.pprint(clean))
diff --git a/mediagoblin/media_types/pdf/processing.py b/mediagoblin/media_types/pdf/processing.py
index a000007a..f6d10a5f 100644
--- a/mediagoblin/media_types/pdf/processing.py
+++ b/mediagoblin/media_types/pdf/processing.py
@@ -138,10 +138,10 @@ def is_unoconv_working():
try:
proc = Popen([unoconv, '--show'], stderr=PIPE)
output = proc.stderr.read()
- except OSError, e:
+ except OSError:
_log.warn(_('unoconv failing to run, check log file'))
return False
- if 'ERROR' in output:
+ if b'ERROR' in output:
return False
return True
@@ -207,6 +207,7 @@ def pdf_info(original):
_log.debug('pdfinfo could not read the pdf file.')
raise BadMediaFail()
+ lines = [l.decode() for l in lines]
info_dict = dict([[part.strip() for part in l.strip().split(':', 1)]
for l in lines if ':' in l])
@@ -466,6 +467,6 @@ class Resizer(CommonPdfProcessor):
class PdfProcessingManager(ProcessingManager):
def __init__(self):
- super(self.__class__, self).__init__()
+ super(PdfProcessingManager, self).__init__()
self.add_processor(InitialProcessor)
self.add_processor(Resizer)
diff --git a/mediagoblin/media_types/raw_image/processing.py b/mediagoblin/media_types/raw_image/processing.py
index 83b01559..740ba2dd 100644
--- a/mediagoblin/media_types/raw_image/processing.py
+++ b/mediagoblin/media_types/raw_image/processing.py
@@ -29,7 +29,7 @@ from mediagoblin.processing import (
_log = logging.getLogger(__name__)
MEDIA_TYPE = 'mediagoblin.media_types.raw_image'
-ACCEPTED_EXTENSIONS = ['nef',]
+ACCEPTED_EXTENSIONS = ['nef', 'cr2']
# The entire function have to be copied
@@ -54,7 +54,7 @@ class InitialRawProcessor(InitialProcessor):
"""
Pull out a full-size JPEG-preview
"""
- super(self.__class__, self).common_setup()
+ super(InitialRawProcessor, self).common_setup()
self._original_raw = self.process_filename
@@ -67,8 +67,8 @@ class InitialRawProcessor(InitialProcessor):
# Extract the biggest preview and write it as our working image
md.previews[-1].write_to_file(
self.process_filename.encode('utf-8'))
- self.process_filename += ".jpg"
- _log.debug('Wrote new file from {0} to preview (jpg) {1}'.format(
+ self.process_filename += '.jpg'
+ _log.debug(u'Wrote new file from {0} to preview (jpg) {1}'.format(
self._original_raw, self.process_filename))
# Override the namebuilder with our new jpg-based name
@@ -77,6 +77,6 @@ class InitialRawProcessor(InitialProcessor):
class RawImageProcessingManager(ProcessingManager):
def __init__(self):
- super(self.__class__, self).__init__()
+ super(RawImageProcessingManager, self).__init__()
self.add_processor(InitialRawProcessor)
self.add_processor(Resizer)
diff --git a/mediagoblin/media_types/stl/model_loader.py b/mediagoblin/media_types/stl/model_loader.py
index 88f19314..c1864613 100644
--- a/mediagoblin/media_types/stl/model_loader.py
+++ b/mediagoblin/media_types/stl/model_loader.py
@@ -22,7 +22,7 @@ class ThreeDeeParseError(Exception):
pass
-class ThreeDee():
+class ThreeDee(object):
"""
3D model parser base class. Derrived classes are used for basic
analysis of 3D models, and are not intended to be used for 3D
diff --git a/mediagoblin/media_types/stl/processing.py b/mediagoblin/media_types/stl/processing.py
index 65a86234..55764aeb 100644
--- a/mediagoblin/media_types/stl/processing.py
+++ b/mediagoblin/media_types/stl/processing.py
@@ -365,6 +365,6 @@ class Resizer(CommonStlProcessor):
class StlProcessingManager(ProcessingManager):
def __init__(self):
- super(self.__class__, self).__init__()
+ super(StlProcessingManager, self).__init__()
self.add_processor(InitialProcessor)
self.add_processor(Resizer)
diff --git a/mediagoblin/media_types/video/processing.py b/mediagoblin/media_types/video/processing.py
index c21c74b3..bbed4f12 100644
--- a/mediagoblin/media_types/video/processing.py
+++ b/mediagoblin/media_types/video/processing.py
@@ -44,7 +44,7 @@ class VideoTranscodingFail(BaseProcessingFail):
general_message = _(u'Video transcoding failed')
-EXCLUDED_EXTS = ["nef"]
+EXCLUDED_EXTS = ["nef", "cr2"]
def sniff_handler(media_file, filename):
name, ext = os.path.splitext(filename)
@@ -456,7 +456,7 @@ class Transcoder(CommonVideoProcessor):
class VideoProcessingManager(ProcessingManager):
def __init__(self):
- super(self.__class__, self).__init__()
+ super(VideoProcessingManager, self).__init__()
self.add_processor(InitialProcessor)
self.add_processor(Resizer)
self.add_processor(Transcoder)
diff --git a/mediagoblin/mg_globals.py b/mediagoblin/mg_globals.py
index 26ed66fa..7da31680 100644
--- a/mediagoblin/mg_globals.py
+++ b/mediagoblin/mg_globals.py
@@ -21,6 +21,7 @@ import gettext
import pkg_resources
import threading
+import six
#############################
# General mediagoblin globals
@@ -64,7 +65,7 @@ def setup_globals(**kwargs):
"""
from mediagoblin import mg_globals
- for key, value in kwargs.iteritems():
+ for key, value in six.iteritems(kwargs):
if not hasattr(mg_globals, key):
raise AssertionError("Global %s not known" % key)
setattr(mg_globals, key, value)
diff --git a/mediagoblin/moderation/tools.py b/mediagoblin/moderation/tools.py
index 8d758f18..0bcd8762 100644
--- a/mediagoblin/moderation/tools.py
+++ b/mediagoblin/moderation/tools.py
@@ -14,6 +14,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import six
+
from mediagoblin import mg_globals
from mediagoblin.db.models import User, Privilege, UserBan
from mediagoblin.db.base import Session
@@ -22,8 +24,9 @@ from mediagoblin.tools.response import redirect
from datetime import datetime
from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
+
def take_punitive_actions(request, form, report, user):
- message_body =''
+ message_body = ''
# The bulk of this action is running through all of the different
# punitive actions that a moderator could take.
@@ -212,6 +215,6 @@ def parse_report_panel_settings(form):
filters['reporter_id'] = form.reporter.data
filters = dict((k, v)
- for k, v in filters.iteritems() if v)
+ for k, v in six.iteritems(filters) if v)
return filters
diff --git a/mediagoblin/oauth/views.py b/mediagoblin/oauth/views.py
index 90ad5bbf..1b7c789a 100644
--- a/mediagoblin/oauth/views.py
+++ b/mediagoblin/oauth/views.py
@@ -15,7 +15,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import datetime
-import string
+
+import six
from oauthlib.oauth1.rfc5849.utils import UNICODE_ASCII_CHARACTER_SET
from oauthlib.oauth1 import (RequestTokenEndpoint, AuthorizationEndpoint,
@@ -124,21 +125,21 @@ def client_register(request):
error = "Invalid registration type"
return json_response({"error": error}, status=400)
- logo_url = data.get("logo_url", client.logo_url)
- if logo_url is not None and not validate_url(logo_url):
- error = "Logo URL {0} is not a valid URL.".format(logo_url)
+ logo_uri = data.get("logo_uri", client.logo_url)
+ if logo_uri is not None and not validate_url(logo_uri):
+ error = "Logo URI {0} is not a valid URI.".format(logo_uri)
return json_response(
{"error": error},
status=400
)
else:
- client.logo_url = logo_url
+ client.logo_url = logo_uri
client.application_name = data.get("application_name", None)
contacts = data.get("contacts", None)
if contacts is not None:
- if type(contacts) is not unicode:
+ if not isinstance(contacts, six.text_type):
error = "Contacts must be a string of space-seporated email addresses."
return json_response({"error": error}, status=400)
@@ -154,7 +155,7 @@ def client_register(request):
redirect_uris = data.get("redirect_uris", None)
if redirect_uris is not None:
- if type(redirect_uris) is not unicode:
+ if not isinstance(redirect_uris, six.text_type):
error = "redirect_uris must be space-seporated URLs."
return json_response({"error": error}, status=400)
@@ -189,10 +190,6 @@ def request_token(request):
error = "Could not decode data."
return json_response({"error": error}, status=400)
- if data == "":
- error = "Unknown Content-Type"
- return json_response({"error": error}, status=400)
-
if not data and request.headers:
data = request.headers
diff --git a/mediagoblin/plugins/api/tools.py b/mediagoblin/plugins/api/tools.py
index d1b3ebb1..56256236 100644
--- a/mediagoblin/plugins/api/tools.py
+++ b/mediagoblin/plugins/api/tools.py
@@ -18,9 +18,11 @@ import logging
import json
from functools import wraps
-from urlparse import urljoin
from werkzeug.exceptions import Forbidden
from werkzeug.wrappers import Response
+
+from six.moves.urllib.parse import urljoin
+
from mediagoblin import mg_globals
from mediagoblin.tools.pluginapi import PluginManager
from mediagoblin.storage.filestorage import BasicFileStorage
diff --git a/mediagoblin/plugins/api/views.py b/mediagoblin/plugins/api/views.py
index e8af7988..ef0b87e3 100644
--- a/mediagoblin/plugins/api/views.py
+++ b/mediagoblin/plugins/api/views.py
@@ -17,6 +17,8 @@
import json
import logging
+import six
+
from werkzeug.exceptions import BadRequest
from werkzeug.wrappers import Response
@@ -55,16 +57,16 @@ def post_entry(request):
callback_url = request.form.get('callback_url')
if callback_url:
- callback_url = unicode(callback_url)
+ callback_url = six.text_type(callback_url)
try:
entry = submit_media(
mg_app=request.app, user=request.user,
submitted_file=request.files['file'],
filename=request.files['file'].filename,
- title=unicode(request.form.get('title')),
- description=unicode(request.form.get('description')),
- license=unicode(request.form.get('license', '')),
- tags_string=unicode(request.form.get('tags', '')),
+ title=six.text_type(request.form.get('title')),
+ description=six.text_type(request.form.get('description')),
+ license=six.text_type(request.form.get('license', '')),
+ tags_string=six.text_type(request.form.get('tags', '')),
upload_limit=upload_limit, max_file_size=max_file_size,
callback_url=callback_url)
@@ -89,7 +91,7 @@ def post_entry(request):
'''
if isinstance(e, InvalidFileType) or \
isinstance(e, FileTypeNotSupported):
- raise BadRequest(unicode(e))
+ raise BadRequest(six.text_type(e))
else:
raise
@@ -103,7 +105,7 @@ def api_test(request):
# TODO: This is the *only* thing using Response() here, should that
# not simply use json_response()?
- return Response(json.dumps(user_data))
+ return Response(json.dumps(user_data, sort_keys=True))
def get_entries(request):
diff --git a/mediagoblin/plugins/basic_auth/tools.py b/mediagoblin/plugins/basic_auth/tools.py
index f943bf39..13f240b2 100644
--- a/mediagoblin/plugins/basic_auth/tools.py
+++ b/mediagoblin/plugins/basic_auth/tools.py
@@ -16,6 +16,8 @@
import bcrypt
import random
+import six
+
from mediagoblin import mg_globals
from mediagoblin.tools.crypto import get_timed_signer_url
from mediagoblin.tools.mail import send_email
@@ -66,7 +68,7 @@ def bcrypt_gen_password_hash(raw_pass, extra_salt=None):
if extra_salt:
raw_pass = u"%s:%s" % (extra_salt, raw_pass)
- return unicode(
+ return six.text_type(
bcrypt.hashpw(raw_pass.encode('utf-8'), bcrypt.gensalt()))
diff --git a/mediagoblin/plugins/httpapiauth/__init__.py b/mediagoblin/plugins/httpapiauth/__init__.py
index 2b2d593c..d7180463 100644
--- a/mediagoblin/plugins/httpapiauth/__init__.py
+++ b/mediagoblin/plugins/httpapiauth/__init__.py
@@ -16,6 +16,8 @@
import logging
+import six
+
from werkzeug.exceptions import Unauthorized
from mediagoblin.auth.tools import check_login_simple
@@ -40,7 +42,7 @@ class HTTPAuth(Auth):
if not request.authorization:
return False
- user = check_login_simple(unicode(request.authorization['username']),
+ user = check_login_simple(six.text_type(request.authorization['username']),
request.authorization['password'])
if user:
diff --git a/mediagoblin/plugins/ldap/tools.py b/mediagoblin/plugins/ldap/tools.py
index 1c436792..2be2dcd7 100644
--- a/mediagoblin/plugins/ldap/tools.py
+++ b/mediagoblin/plugins/ldap/tools.py
@@ -16,6 +16,8 @@
import ldap
import logging
+import six
+
from mediagoblin.tools import pluginapi
_log = logging.getLogger(__name__)
@@ -47,7 +49,7 @@ class LDAP(object):
return email
def login(self, username, password):
- for k, v in self.ldap_settings.iteritems():
+ for k, v in six.iteritems(self.ldap_settings):
try:
self._connect(v)
user_dn = v['LDAP_USER_DN_TEMPLATE'].format(username=username)
diff --git a/mediagoblin/plugins/ldap/views.py b/mediagoblin/plugins/ldap/views.py
index aef1bf56..be434daf 100644
--- a/mediagoblin/plugins/ldap/views.py
+++ b/mediagoblin/plugins/ldap/views.py
@@ -13,6 +13,9 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import six
+
from mediagoblin import mg_globals, messages
from mediagoblin.auth.tools import register_user
from mediagoblin.db.models import User
@@ -40,7 +43,7 @@ def login(request):
if user:
# set up login in session
- request.session['user_id'] = unicode(user.id)
+ request.session['user_id'] = six.text_type(user.id)
request.session.save()
if request.form.get('next'):
diff --git a/mediagoblin/plugins/oauth/forms.py b/mediagoblin/plugins/oauth/forms.py
index ddf4d390..4585c27c 100644
--- a/mediagoblin/plugins/oauth/forms.py
+++ b/mediagoblin/plugins/oauth/forms.py
@@ -16,7 +16,7 @@
import wtforms
-from urlparse import urlparse
+from six.moves.urllib.parse import urlparse
from mediagoblin.tools.extlib.wtf_html5 import URLField
from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
diff --git a/mediagoblin/plugins/oauth/models.py b/mediagoblin/plugins/oauth/models.py
index 439424d3..3fe562a2 100644
--- a/mediagoblin/plugins/oauth/models.py
+++ b/mediagoblin/plugins/oauth/models.py
@@ -26,10 +26,6 @@ from mediagoblin.db.models import User
from mediagoblin.plugins.oauth.tools import generate_identifier, \
generate_secret, generate_token, generate_code, generate_refresh_token
-# Don't remove this, I *think* it applies sqlalchemy-migrate functionality onto
-# the models.
-from migrate import changeset
-
class OAuthClient(Base):
__tablename__ = 'oauth__client'
diff --git a/mediagoblin/plugins/oauth/tools.py b/mediagoblin/plugins/oauth/tools.py
index af0a3305..2053d5d4 100644
--- a/mediagoblin/plugins/oauth/tools.py
+++ b/mediagoblin/plugins/oauth/tools.py
@@ -23,6 +23,8 @@ from datetime import datetime
from functools import wraps
+import six
+
from mediagoblin.tools.response import json_response
@@ -86,7 +88,7 @@ def create_token(client, user):
def generate_identifier():
''' Generates a ``uuid.uuid4()`` '''
- return unicode(uuid.uuid4())
+ return six.text_type(uuid.uuid4())
def generate_token():
@@ -110,5 +112,5 @@ def generate_secret():
'''
# XXX: We might not want it to use bcrypt, since bcrypt takes its time to
# generate the result.
- return unicode(getrandbits(192))
+ return six.text_type(getrandbits(192))
diff --git a/mediagoblin/plugins/oauth/views.py b/mediagoblin/plugins/oauth/views.py
index de637d6b..8ca73521 100644
--- a/mediagoblin/plugins/oauth/views.py
+++ b/mediagoblin/plugins/oauth/views.py
@@ -17,7 +17,9 @@
import logging
-from urllib import urlencode
+from six.moves.urllib.parse import urlencode
+
+import six
from werkzeug.exceptions import BadRequest
@@ -44,11 +46,11 @@ def register_client(request):
if request.method == 'POST' and form.validate():
client = OAuthClient()
- client.name = unicode(form.name.data)
- client.description = unicode(form.description.data)
- client.type = unicode(form.type.data)
+ client.name = six.text_type(form.name.data)
+ client.description = six.text_type(form.description.data)
+ client.type = six.text_type(form.type.data)
client.owner_id = request.user.id
- client.redirect_uri = unicode(form.redirect_uri.data)
+ client.redirect_uri = six.text_type(form.redirect_uri.data)
client.save()
diff --git a/mediagoblin/plugins/openid/store.py b/mediagoblin/plugins/openid/store.py
index 8f9a7012..24726814 100644
--- a/mediagoblin/plugins/openid/store.py
+++ b/mediagoblin/plugins/openid/store.py
@@ -16,6 +16,8 @@
import base64
import time
+import six
+
from openid.association import Association as OIDAssociation
from openid.store.interface import OpenIDStore
from openid.store import nonce
@@ -34,12 +36,12 @@ class SQLAlchemyOpenIDStore(OpenIDStore):
if not assoc:
assoc = Association()
- assoc.server_url = unicode(server_url)
+ assoc.server_url = six.text_type(server_url)
assoc.handle = association.handle
# django uses base64 encoding, python-openid uses a blob field for
# secret
- assoc.secret = unicode(base64.encodestring(association.secret))
+ assoc.secret = six.text_type(base64.encodestring(association.secret))
assoc.issued = association.issued
assoc.lifetime = association.lifetime
assoc.assoc_type = association.assoc_type
diff --git a/mediagoblin/plugins/openid/views.py b/mediagoblin/plugins/openid/views.py
index bb2de7ab..71f444fa 100644
--- a/mediagoblin/plugins/openid/views.py
+++ b/mediagoblin/plugins/openid/views.py
@@ -13,6 +13,9 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import six
+
from openid.consumer import consumer
from openid.consumer.discover import DiscoveryFailure
from openid.extensions.sreg import SRegRequest, SRegResponse
@@ -186,7 +189,7 @@ def finish_login(request):
if user:
# Set up login in session
- request.session['user_id'] = unicode(user.id)
+ request.session['user_id'] = six.text_type(user.id)
request.session.save()
if request.session.get('next'):
diff --git a/mediagoblin/plugins/persona/views.py b/mediagoblin/plugins/persona/views.py
index 1bba3b8c..41d38353 100644
--- a/mediagoblin/plugins/persona/views.py
+++ b/mediagoblin/plugins/persona/views.py
@@ -17,6 +17,8 @@ import json
import logging
import requests
+import six
+
from werkzeug.exceptions import BadRequest
from mediagoblin import messages, mg_globals
@@ -63,7 +65,7 @@ def login(request):
user = query.user if query else None
if user:
- request.session['user_id'] = unicode(user.id)
+ request.session['user_id'] = six.text_type(user.id)
request.session['persona_login_email'] = email
request.session.save()
diff --git a/mediagoblin/plugins/piwigo/tools.py b/mediagoblin/plugins/piwigo/tools.py
index 484ea531..7b9b7af3 100644
--- a/mediagoblin/plugins/piwigo/tools.py
+++ b/mediagoblin/plugins/piwigo/tools.py
@@ -47,7 +47,7 @@ class PwgNamedArray(list):
def _fill_element_dict(el, data, as_attr=()):
- for k, v in data.iteritems():
+ for k, v in six.iteritems(data):
if k in as_attr:
if not isinstance(v, six.string_types):
v = str(v)
diff --git a/mediagoblin/plugins/piwigo/views.py b/mediagoblin/plugins/piwigo/views.py
index f913a730..1fe1e576 100644
--- a/mediagoblin/plugins/piwigo/views.py
+++ b/mediagoblin/plugins/piwigo/views.py
@@ -17,6 +17,8 @@
import logging
import re
+import six
+
from werkzeug.exceptions import MethodNotAllowed, BadRequest, NotImplemented
from werkzeug.wrappers import BaseResponse
@@ -133,8 +135,8 @@ def pwg_images_addSimple(request):
mg_app=request.app, user=request.user,
submitted_file=request.files['image'],
filename=request.files['image'].filename,
- title=unicode(form.name.data),
- description=unicode(form.comment.data),
+ title=six.text_type(form.name.data),
+ description=six.text_type(form.comment.data),
upload_limit=upload_limit, max_file_size=max_file_size)
collection_id = form.category.data
diff --git a/mediagoblin/processing/__init__.py b/mediagoblin/processing/__init__.py
index 102fd5de..5a88ddea 100644
--- a/mediagoblin/processing/__init__.py
+++ b/mediagoblin/processing/__init__.py
@@ -24,6 +24,8 @@ except:
import logging
import os
+import six
+
from mediagoblin import mg_globals as mgg
from mediagoblin.db.util import atomic_update
from mediagoblin.db.models import MediaEntry
@@ -46,7 +48,7 @@ class ProgressCallback(object):
def create_pub_filepath(entry, filename):
return mgg.public_store.get_unique_filepath(
['media_entries',
- unicode(entry.id),
+ six.text_type(entry.id),
filename])
@@ -319,7 +321,7 @@ def mark_entry_failed(entry_id, exc):
atomic_update(mgg.database.MediaEntry,
{'id': entry_id},
{u'state': u'failed',
- u'fail_error': unicode(exc.exception_path),
+ u'fail_error': six.text_type(exc.exception_path),
u'fail_metadata': exc.metadata})
else:
_log.warn("No idea what happened here, but it failed: %r", exc)
diff --git a/mediagoblin/processing/task.py b/mediagoblin/processing/task.py
index 7f683485..1a21c6d2 100644
--- a/mediagoblin/processing/task.py
+++ b/mediagoblin/processing/task.py
@@ -15,8 +15,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
-import urllib
-import urllib2
+
+from six.moves.urllib import request, parse
import celery
from celery.registry import tasks
@@ -42,15 +42,15 @@ def handle_push_urls(feed_url):
hubparameters = {
'hub.mode': 'publish',
'hub.url': feed_url}
- hubdata = urllib.urlencode(hubparameters)
+ hubdata = parse.urlencode(hubparameters)
hubheaders = {
"Content-type": "application/x-www-form-urlencoded",
"Connection": "close"}
for huburl in mgg.app_config["push_urls"]:
- hubrequest = urllib2.Request(huburl, hubdata, hubheaders)
+ hubrequest = request.Request(huburl, hubdata, hubheaders)
try:
- hubresponse = urllib2.urlopen(hubrequest)
- except (urllib2.HTTPError, urllib2.URLError) as exc:
+ hubresponse = request.urlopen(hubrequest)
+ except (request.HTTPError, request.URLError) as exc:
# We retry by default 3 times before failing
_log.info("PuSH url %r gave error %r", huburl, exc)
try:
diff --git a/mediagoblin/static/css/base.css b/mediagoblin/static/css/base.css
index 0bd58738..c7e1496f 100644
--- a/mediagoblin/static/css/base.css
+++ b/mediagoblin/static/css/base.css
@@ -25,7 +25,7 @@
src: local('Lato Regular'), local('Lato-Regular'), url('../fonts/Lato-Regular.ttf') format('truetype');
}
-body {
+html, body {
background-color: #161616;
color: #C3C3C3;
padding: 0;
@@ -110,6 +110,23 @@ input, textarea {
/* website structure */
+#wrap {
+ min-height: 100%;
+ height: auto;
+
+ /* This must be equal to the footer height + 5px */
+ margin-bottom: -35px;
+}
+
+#wrap:after {
+ content: "";
+ display: block;
+}
+
+footer, #wrap:after {
+ height: 30px;
+}
+
header {
width: 100%;
padding: 0;
@@ -176,9 +193,7 @@ a.logo {
footer {
width: 100%;
- height: 30px;
border-top: 1px solid #333;
- bottom: 0px;
padding: 8px 0;
text-align: center;
font-size: 0.875em;
@@ -950,3 +965,19 @@ table.metadata_editor tr td {
table.metadata_editor tr td.form_field_input input {
width:350px;
}
+
+.application {
+ min-height: 30px;
+ margin-left: 70px;
+}
+
+.application-icon {
+ position: absolute;
+ left: 12px;
+ width: 50px;
+ height: 50px;
+}
+
+.application-button {
+ float: right;
+}
diff --git a/mediagoblin/static/images/small-gavroche.png b/mediagoblin/static/images/small-gavroche.png
new file mode 100644
index 00000000..13192d97
--- /dev/null
+++ b/mediagoblin/static/images/small-gavroche.png
Binary files differ
diff --git a/mediagoblin/static/images/small-gavroche.xcf b/mediagoblin/static/images/small-gavroche.xcf
new file mode 100644
index 00000000..0291d2e1
--- /dev/null
+++ b/mediagoblin/static/images/small-gavroche.xcf
Binary files differ
diff --git a/mediagoblin/storage/__init__.py b/mediagoblin/storage/__init__.py
index 51b46c07..14f13bd3 100644
--- a/mediagoblin/storage/__init__.py
+++ b/mediagoblin/storage/__init__.py
@@ -14,9 +14,13 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from __future__ import absolute_import
+
import shutil
import uuid
+import six
+
from werkzeug.utils import secure_filename
from mediagoblin.tools import common
@@ -174,7 +178,7 @@ class StorageInterface(object):
shutil.copy(self.get_local_path(filepath), dest_path)
else:
with self.get_file(filepath, 'rb') as source_file:
- with file(dest_path, 'wb') as dest_file:
+ with open(dest_path, 'wb') as dest_file:
# Copy from remote storage in 4M chunks
shutil.copyfileobj(source_file, dest_file, length=4*1048576)
@@ -187,7 +191,7 @@ class StorageInterface(object):
your storage system.
"""
with self.get_file(filepath, 'wb') as dest_file:
- with file(filename, 'rb') as source_file:
+ with open(filename, 'rb') as source_file:
# Copy to storage system in 4M chunks
shutil.copyfileobj(source_file, dest_file, length=4*1048576)
@@ -220,7 +224,7 @@ def clean_listy_filepath(listy_filepath):
A cleaned list of unicode objects.
"""
cleaned_filepath = [
- unicode(secure_filename(filepath))
+ six.text_type(secure_filename(filepath))
for filepath in listy_filepath]
if u'' in cleaned_filepath:
@@ -257,7 +261,7 @@ def storage_system_from_config(config_section):
"""
# This construct is needed, because dict(config) does
# not replace the variables in the config items.
- config_params = dict(config_section.iteritems())
+ config_params = dict(six.iteritems(config_section))
if 'storage_class' in config_params:
storage_class = config_params['storage_class']
@@ -268,4 +272,4 @@ def storage_system_from_config(config_section):
storage_class = common.import_component(storage_class)
return storage_class(**config_params)
-import filestorage
+from . import filestorage
diff --git a/mediagoblin/storage/cloudfiles.py b/mediagoblin/storage/cloudfiles.py
index 47c81ad6..532e5bac 100644
--- a/mediagoblin/storage/cloudfiles.py
+++ b/mediagoblin/storage/cloudfiles.py
@@ -143,7 +143,7 @@ class CloudFilesStorage(StorageInterface):
"""
# Override this method, using the "stream" iterator for efficient streaming
with self.get_file(filepath, 'rb') as source_file:
- with file(dest_path, 'wb') as dest_file:
+ with open(dest_path, 'wb') as dest_file:
for data in source_file:
dest_file.write(data)
@@ -164,7 +164,7 @@ class CloudFilesStorage(StorageInterface):
# TODO: Fixing write() still seems worthwhile though.
_log.debug('Sending {0} to cloudfiles...'.format(filepath))
with self.get_file(filepath, 'wb') as dest_file:
- with file(filename, 'rb') as source_file:
+ with open(filename, 'rb') as source_file:
# Copy to storage system in 4096 byte chunks
dest_file.send(source_file)
diff --git a/mediagoblin/storage/filestorage.py b/mediagoblin/storage/filestorage.py
index 29b8383b..f989539c 100644
--- a/mediagoblin/storage/filestorage.py
+++ b/mediagoblin/storage/filestorage.py
@@ -14,15 +14,16 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import os
+import shutil
+
+import six.moves.urllib.parse as urlparse
+
from mediagoblin.storage import (
StorageInterface,
clean_listy_filepath,
NoWebServing)
-import os
-import shutil
-import urlparse
-
class BasicFileStorage(StorageInterface):
"""
diff --git a/mediagoblin/storage/mountstorage.py b/mediagoblin/storage/mountstorage.py
index dffc619b..4125a88d 100644
--- a/mediagoblin/storage/mountstorage.py
+++ b/mediagoblin/storage/mountstorage.py
@@ -14,6 +14,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import six
+
from mediagoblin.storage import StorageInterface, clean_listy_filepath
@@ -120,7 +122,7 @@ class MountStorage(StorageInterface):
v = table.get(None)
if v:
res.append(" " * len(indent) + repr(indent) + ": " + repr(v))
- for k, v in table.iteritems():
+ for k, v in six.iteritems(table):
if k == None:
continue
res.append(" " * len(indent) + repr(k) + ":")
diff --git a/mediagoblin/submit/lib.py b/mediagoblin/submit/lib.py
index aaa90ea0..1813aa82 100644
--- a/mediagoblin/submit/lib.py
+++ b/mediagoblin/submit/lib.py
@@ -18,12 +18,15 @@ import logging
import uuid
from os.path import splitext
+import six
+
from werkzeug.utils import secure_filename
from werkzeug.datastructures import FileStorage
from mediagoblin import mg_globals
from mediagoblin.tools.response import json_response
from mediagoblin.tools.text import convert_to_tag_list_of_dicts
+from mediagoblin.tools.federation import create_activity
from mediagoblin.db.models import MediaEntry, ProcessingMetaData
from mediagoblin.processing import mark_entry_failed
from mediagoblin.processing.task import ProcessMedia
@@ -58,7 +61,7 @@ def get_upload_file_limits(user):
"""
Get the upload_limit and max_file_size for this user
"""
- if user.upload_limit >= 0:
+ if user.upload_limit is not None and user.upload_limit >= 0: # TODO: debug this
upload_limit = user.upload_limit
else:
upload_limit = mg_globals.app_config.get('upload_limit', None)
@@ -128,7 +131,7 @@ def submit_media(mg_app, user, submitted_file, filename,
# If the filename contains non ascii generate a unique name
if not all(ord(c) < 128 for c in filename):
- filename = unicode(uuid.uuid4()) + splitext(filename)[-1]
+ filename = six.text_type(uuid.uuid4()) + splitext(filename)[-1]
# Sniff the submitted media to determine which
# media plugin should handle processing
@@ -137,7 +140,7 @@ def submit_media(mg_app, user, submitted_file, filename,
# create entry and save in database
entry = new_upload_entry(user)
entry.media_type = media_type
- entry.title = (title or unicode(splitext(filename)[0]))
+ entry.title = (title or six.text_type(splitext(filename)[0]))
entry.description = description or u""
@@ -200,6 +203,10 @@ def submit_media(mg_app, user, submitted_file, filename,
add_comment_subscription(user, entry)
+ # Create activity
+ entry.activity = create_activity("post", entry, entry.uploader).id
+ entry.save()
+
return entry
@@ -213,7 +220,7 @@ def prepare_queue_task(app, entry, filename):
# (If we got it off the task's auto-generation, there'd be
# a risk of a race condition when we'd save after sending
# off the task)
- task_id = unicode(uuid.uuid4())
+ task_id = six.text_type(uuid.uuid4())
entry.queued_task_id = task_id
# Now store generate the queueing related filename
@@ -289,4 +296,9 @@ def api_add_to_feed(request, entry):
run_process_media(entry, feed_url)
add_comment_subscription(request.user, entry)
+
+ # Create activity
+ entry.activity = create_activity("post", entry, entry.uploader).id
+ entry.save()
+
return json_response(entry.serialize(request))
diff --git a/mediagoblin/submit/views.py b/mediagoblin/submit/views.py
index 42c378a8..b0588599 100644
--- a/mediagoblin/submit/views.py
+++ b/mediagoblin/submit/views.py
@@ -14,6 +14,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import six
+
from mediagoblin import messages
import mediagoblin.mg_globals as mg_globals
@@ -59,9 +61,9 @@ def submit_start(request):
mg_app=request.app, user=request.user,
submitted_file=request.files['file'],
filename=request.files['file'].filename,
- title=unicode(submit_form.title.data),
- description=unicode(submit_form.description.data),
- license=unicode(submit_form.license.data) or None,
+ title=six.text_type(submit_form.title.data),
+ description=six.text_type(submit_form.description.data),
+ license=six.text_type(submit_form.license.data) or None,
tags_string=submit_form.tags.data,
upload_limit=upload_limit, max_file_size=max_file_size,
urlgen=request.urlgen)
@@ -117,8 +119,8 @@ def add_collection(request, media=None):
if request.method == 'POST' and submit_form.validate():
collection = request.db.Collection()
- collection.title = unicode(submit_form.title.data)
- collection.description = unicode(submit_form.description.data)
+ collection.title = six.text_type(submit_form.title.data)
+ collection.description = six.text_type(submit_form.description.data)
collection.creator = request.user.id
collection.generate_slug()
diff --git a/mediagoblin/templates/mediagoblin/base.html b/mediagoblin/templates/mediagoblin/base.html
index 13cfb47b..16a0d4f6 100644
--- a/mediagoblin/templates/mediagoblin/base.html
+++ b/mediagoblin/templates/mediagoblin/base.html
@@ -62,138 +62,142 @@
<body>
{% include 'mediagoblin/bits/body_start.html' %}
{% block mediagoblin_body %}
- {% block mediagoblin_header %}
- <div class="container">
- <header>
- <div class="row foot">
- <div class="header_left">
- {%- include "mediagoblin/bits/logo.html" -%}
- {% block mediagoblin_header_title %}{% endblock %}
- </div>
- <div class="header_right">
- {%- if request.user %}
- {% if request.user and
- request.user.has_privilege('active') and
- not request.user.is_banned() %}
+ <div id="wrap">
+ {% block mediagoblin_header %}
+ <div class="container">
+ <header>
+ <div class="row foot">
+ <div class="header_left">
+ {%- include "mediagoblin/bits/logo.html" -%}
+ {% block mediagoblin_header_title %}{% endblock %}
+ </div>
+ <div class="header_right">
+ {%- if request.user %}
+ {% if request.user and
+ request.user.has_privilege('active') and
+ not request.user.is_banned() %}
- {% set notification_count = get_notification_count(request.user.id) %}
- {% if notification_count %}
+ {% set notification_count = get_notification_count(request.user.id) %}
+ {% if notification_count %}
+ <a href="javascript:;"
+ class="notification-gem button_action button_info"
+ title="Notifications">
+ {{ notification_count }}</a>
+ {% endif %}
<a href="javascript:;"
- class="notification-gem button_action button_info"
- title="Notifications">
- {{ notification_count }}</a>
+ class="button_action header_dropdown_down"
+ aria-controls="header_dropdown">&#9660;</a>
+ <a href="javascript:;"
+ class="button_action header_dropdown_up"
+ aria-controls="header_dropdown">&#9650;</a>
+ {% elif request.user and not request.user.has_privilege('active') %}
+ {# the following link should only appear when verification is needed #}
+ <a href="{{ request.urlgen('mediagoblin.user_pages.user_home',
+ user=request.user.username) }}"
+ class="button_action_highlight">
+ {% trans %}Verify your email!{% endtrans %}</a>
+ or <a id="logout" href=
+ {% if persona is not defined %}
+ "{{ request.urlgen('mediagoblin.auth.logout') }}"
+ {% else %}
+ "javascript:;"
+ {% endif %}
+ >{% trans %}log out{% endtrans %}</a>
+ {% elif request.user and request.user.is_banned() %}
+ <a id="logout" href=
+ {% if persona is not defined %}
+ "{{ request.urlgen('mediagoblin.auth.logout') }}"
+ {% else %}
+ "javascript:;"
+ {% endif %}
+ >{% trans %}log out{% endtrans %}</a>
+ {% endif %}
+ {%- elif auth %}
+ <a href=
+ {% if persona_auth is defined %}
+ "javascript:;" id="persona_login"
+ {% else %}
+ "{{ request.urlgen('mediagoblin.auth.login') }}"
{% endif %}
- <a href="javascript:;"
- class="button_action header_dropdown_down"
- aria-controls="header_dropdown">&#9660;</a>
- <a href="javascript:;"
- class="button_action header_dropdown_up"
- aria-controls="header_dropdown">&#9650;</a>
- {% elif request.user and not request.user.has_privilege('active') %}
- {# the following link should only appear when verification is needed #}
- <a href="{{ request.urlgen('mediagoblin.user_pages.user_home',
- user=request.user.username) }}"
- class="button_action_highlight">
- {% trans %}Verify your email!{% endtrans %}</a>
- or <a id="logout" href=
- {% if persona is not defined %}
- "{{ request.urlgen('mediagoblin.auth.logout') }}"
- {% else %}
- "javascript:;"
- {% endif %}
- >{% trans %}log out{% endtrans %}</a>
- {% elif request.user and request.user.is_banned() %}
- <a id="logout" href=
- {% if persona is not defined %}
- "{{ request.urlgen('mediagoblin.auth.logout') }}"
- {% else %}
- "javascript:;"
- {% endif %}
- >{% trans %}log out{% endtrans %}</a>
- {% endif %}
- {%- elif auth %}
- <a href=
- {% if persona_auth is defined %}
- "javascript:;" id="persona_login"
- {% else %}
- "{{ request.urlgen('mediagoblin.auth.login') }}"
- {% endif %}
- >
- {%- trans %}Log in{% endtrans -%}
- </a>
- {%- endif %}
- </div>
- <div class="clear"></div>
- {% if request.user and request.user.has_privilege('active') %}
- <div id="header_dropdown">
- <p>
- <span class="dropdown_title">
- {% trans user_url=request.urlgen('mediagoblin.user_pages.user_home',
- user=request.user.username),
- user_name=request.user.username -%}
- <a href="{{ user_url }}">{{ user_name }}</a>'s account
- {%- endtrans %}
- </span>
- &middot;
- <a href="{{ request.urlgen('mediagoblin.edit.account') }}">{%- trans %}Change account settings{% endtrans -%}</a>
- &middot;
- <a href="{{ request.urlgen('mediagoblin.user_pages.processing_panel',
- user=request.user.username) }}">
- {%- trans %}Media processing panel{% endtrans -%}
+ >
+ {%- trans %}Log in{% endtrans -%}
</a>
- &middot;
- {% template_hook("blog_dashboard_home") %}
- <a id="logout" href=
- {% if persona is not defined %}
- "{{ request.urlgen('mediagoblin.auth.logout') }}"
- {% else %}
- "javascript:;"
- {% endif %}
- >{% trans %}Log out{% endtrans %}</a>
- </p>
- <a class="button_action" href="{{ request.urlgen('mediagoblin.submit.start') }}">
- {%- trans %}Add media{% endtrans -%}
- </a>
- <a class="button_action" href="{{ request.urlgen('mediagoblin.submit.collection') }}">
- {%- trans %}Create new collection{% endtrans -%}
- </a>
- {% template_hook("header_dropdown_buttons") %}
- {% if request.user.has_privilege('moderator') %}
+ {%- endif %}
+ </div>
+ <div class="clear"></div>
+ {% if request.user and request.user.has_privilege('active') %}
+ <div id="header_dropdown">
<p>
- <span class="dropdown_title">{% trans %}Moderation powers:{% endtrans %}</span>
- <a href="{{ request.urlgen('mediagoblin.moderation.media_panel') }}">
- {%- trans %}Media processing panel{% endtrans -%}
- </a>
+ <span class="dropdown_title">
+ {% trans user_url=request.urlgen('mediagoblin.user_pages.user_home',
+ user=request.user.username),
+ user_name=request.user.username -%}
+ <a href="{{ user_url }}">{{ user_name }}</a>'s account
+ {%- endtrans %}
+ </span>
&middot;
- <a href="{{ request.urlgen('mediagoblin.moderation.users') }}">
- {%- trans %}User management panel{% endtrans -%}
- </a>
+ <a href="{{ request.urlgen('mediagoblin.edit.account') }}">{%- trans %}Change account settings{% endtrans -%}</a>
&middot;
- <a href="{{ request.urlgen('mediagoblin.moderation.reports') }}">
- {%- trans %}Report management panel{% endtrans -%}
+ <a href="{{ request.urlgen('mediagoblin.user_pages.processing_panel',
+ user=request.user.username) }}">
+ {%- trans %}Media processing panel{% endtrans -%}
</a>
- {% template_hook("moderation_powers") %}
+ &middot;
+ {% template_hook("blog_dashboard_home") %}
+ <a id="logout" href=
+ {% if persona is not defined %}
+ "{{ request.urlgen('mediagoblin.auth.logout') }}"
+ {% else %}
+ "javascript:;"
+ {% endif %}
+ >{% trans %}Log out{% endtrans %}</a>
</p>
- {% endif %}
- {% include 'mediagoblin/fragments/header_notifications.html' %}
- </div>
+ <a class="button_action" href="{{ request.urlgen('mediagoblin.submit.start') }}">
+ {%- trans %}Add media{% endtrans -%}
+ </a>
+ <a class="button_action" href="{{ request.urlgen('mediagoblin.submit.collection') }}">
+ {%- trans %}Create new collection{% endtrans -%}
+ </a>
+ {% template_hook("header_dropdown_buttons") %}
+ {% if request.user.has_privilege('moderator') %}
+ <p>
+ <span class="dropdown_title">{% trans %}Moderation powers:{% endtrans %}</span>
+ <a href="{{ request.urlgen('mediagoblin.moderation.media_panel') }}">
+ {%- trans %}Media processing panel{% endtrans -%}
+ </a>
+ &middot;
+ <a href="{{ request.urlgen('mediagoblin.moderation.users') }}">
+ {%- trans %}User management panel{% endtrans -%}
+ </a>
+ &middot;
+ <a href="{{ request.urlgen('mediagoblin.moderation.reports') }}">
+ {%- trans %}Report management panel{% endtrans -%}
+ </a>
+ {% template_hook("moderation_powers") %}
+ </p>
+ {% endif %}
+ {% include 'mediagoblin/fragments/header_notifications.html' %}
+ </div>
+ {% endif %}
+ </div><!-- end row -->
+ </header>
+ </div>
+ {% endblock %}
+ <div class="container">
+ {% include 'mediagoblin/bits/above_content.html' %}
+ <div class="mediagoblin_content">
+ {% include "mediagoblin/utils/messages.html" %}
+ {% block mediagoblin_content %}
+ {% endblock mediagoblin_content %}
+ {% if csrf_token is defined %}
+ {% template_hook("persona_form") %}
{% endif %}
- </div><!-- end row -->
- </header>
- </div>
- {% endblock %}
- <div class="container">
- {% include 'mediagoblin/bits/above_content.html' %}
- <div class="mediagoblin_content">
- {% include "mediagoblin/utils/messages.html" %}
- {% block mediagoblin_content %}
- {% endblock mediagoblin_content %}
- {% if csrf_token is defined %}
- {% template_hook("persona_form") %}
- {% endif %}
+ </div>
</div>
- {%- include "mediagoblin/bits/base_footer.html" %}
</div>
+ <div class="container">
+ {%- include "mediagoblin/bits/base_footer.html" %}
+ </div>
{%- endblock mediagoblin_body %}
{% include 'mediagoblin/bits/body_end.html' %}
</body>
diff --git a/mediagoblin/templates/mediagoblin/edit/deauthorize_applications.html b/mediagoblin/templates/mediagoblin/edit/deauthorize_applications.html
new file mode 100644
index 00000000..f3b83e4e
--- /dev/null
+++ b/mediagoblin/templates/mediagoblin/edit/deauthorize_applications.html
@@ -0,0 +1,69 @@
+{#
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#}
+{%- extends "mediagoblin/base.html" %}
+
+{% block title -%}
+ {% trans -%}
+ Deauthorize applications
+ {%- endtrans %} &mdash; {{ super() }}
+{%- endblock %}
+
+{% block mediagoblin_content %}
+ <h2>{% trans -%}Deauthorize Applications{%- endtrans %}</h2>
+ <p>{% trans -%}
+ These applications can access your GNU MediaGoblin account. Deauthorizing the
+ application will prevent the application from accessing your account.
+ {%- endtrans %}
+ </p>
+
+ <form method="POST" action="{{ request.urlgen('mediagoblin.edit.deauthorize_applications') }}">
+ {{ csrf_token }}
+ {% if not applications %}
+ <em>{% trans -%}There are no applications authorized.{%- endtrans %}</em>
+ {% endif %}
+ {% for application, access in applications %}
+ <div class="application">
+ <div class="application-button">
+ <button class="button_action" name="application" value="{{ access.token }}">Deauthorize</button>
+ </div>
+ {% if application.get_client.logo_url %}
+ <img class="application-icon" src="{{ application.get_client.logo_url }}">
+ {% else %}
+ <img class="application-icon" src="{{ request.staticdirect('/images/small-gavroche.png') }}">
+ {% endif %}
+ <div class="application-content">
+ <strong>{{ application.get_client.application_name }}</strong>
+ <p class="application-data">
+ <small>
+ {% trans -%}Type:{%- endtrans %}
+ &nbsp;
+ {{ application.get_client.application_type }}</small>
+ <br />
+ <small>
+ {% trans -%}Authorized:{%- endtrans %}
+ &nbsp;
+ {%- trans formatted_time=timesince(access.created) -%}
+ {{ formatted_time }} ago
+ {%- endtrans -%}
+ </small>
+ </p>
+ </div>
+ </div>
+ {% endfor %}
+ </form>
+{% endblock %} \ No newline at end of file
diff --git a/mediagoblin/templates/mediagoblin/edit/edit_account.html b/mediagoblin/templates/mediagoblin/edit/edit_account.html
index 574fe459..14a66482 100644
--- a/mediagoblin/templates/mediagoblin/edit/edit_account.html
+++ b/mediagoblin/templates/mediagoblin/edit/edit_account.html
@@ -53,7 +53,10 @@
<a href="{{ request.urlgen('mediagoblin.edit.delete_account') }}">
{%- trans %}Delete my account{% endtrans -%}
</a>
- &middot;
+ <br />
+ <a href="{{ request.urlgen('mediagoblin.edit.deauthorize_applications') }}">
+ {%- trans %}Deauthorize applications{% endtrans -%}
+ </a>
{% template_hook("edit_link") %}
<a href="{{ request.urlgen('mediagoblin.edit.email') }}">
{% trans %}Email{% endtrans %}
diff --git a/mediagoblin/templates/mediagoblin/federation/activity.html b/mediagoblin/templates/mediagoblin/federation/activity.html
new file mode 100644
index 00000000..14377a48
--- /dev/null
+++ b/mediagoblin/templates/mediagoblin/federation/activity.html
@@ -0,0 +1,42 @@
+{#
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2014 MediaGoblin contributors. See AUTHORS.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#}
+{%- extends "mediagoblin/base.html" %}
+
+{% block mediagoblin_head %}
+ {% template_hook("media_head") %}
+{% endblock mediagoblin_head %}
+
+{% block mediagoblin_content %}
+<div class="media_pane eleven columns">
+ <h2 class="media_title">
+ {% if activity.title %}{{ activity.title }}{% endif %}
+ </h2>
+ {% autoescape False %}
+ <p> {{ activity.content }} </p>
+ {% endautoescape %}
+
+ <div class="media_sidebar">
+ {% block mediagoblin_after_added_sidebar %}
+ <a href="{{ activity.url(request) }}"
+ class="button_action"
+ id="button_reportmedia">
+ View {{ activity.get_object.object_type }}
+ </a>
+ {% endblock %}
+ </div>
+{% endblock %} \ No newline at end of file
diff --git a/mediagoblin/templates/mediagoblin/federation/host-meta.xml b/mediagoblin/templates/mediagoblin/federation/host-meta.xml
new file mode 100644
index 00000000..7970a0d2
--- /dev/null
+++ b/mediagoblin/templates/mediagoblin/federation/host-meta.xml
@@ -0,0 +1,22 @@
+{# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2014 MediaGoblin contributors. See AUTHORS.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+-#}
+<?xml version='1.0' encoding='UTF-8'?>
+<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>
+ {% for link in links %}
+ <Link rel="{{ link.rel }}" href="{{ link.href }}" />
+ {% endfor %}
+</XRD> \ No newline at end of file
diff --git a/mediagoblin/tests/test_api.py b/mediagoblin/tests/test_api.py
index 93e82f18..6b0722aa 100644
--- a/mediagoblin/tests/test_api.py
+++ b/mediagoblin/tests/test_api.py
@@ -15,7 +15,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import json
-import mock
+try:
+ import mock
+except ImportError:
+ import unittest.mock as mock
import pytest
from webtest import AppError
@@ -55,7 +58,7 @@ class TestAPI(object):
headers=headers
)
- return response, json.loads(response.body)
+ return response, json.loads(response.body.decode())
def _upload_image(self, test_app, image):
""" Uploads and image to MediaGoblin via pump.io API """
@@ -72,7 +75,7 @@ class TestAPI(object):
data,
headers=headers
)
- image = json.loads(response.body)
+ image = json.loads(response.body.decode())
return response, image
@@ -142,7 +145,7 @@ class TestAPI(object):
headers=headers
)
- assert "403 FORBIDDEN" in excinfo.value.message
+ assert "403 FORBIDDEN" in excinfo.value.args[0]
def test_unable_to_post_feed_as_someone_else(self, test_app):
""" Tests that can't post an image to someone else's feed """
@@ -165,7 +168,7 @@ class TestAPI(object):
headers=headers
)
- assert "403 FORBIDDEN" in excinfo.value.message
+ assert "403 FORBIDDEN" in excinfo.value.args[0]
def test_only_able_to_update_own_image(self, test_app):
""" Test's that the uploader is the only person who can update an image """
@@ -197,7 +200,7 @@ class TestAPI(object):
headers=headers
)
- assert "403 FORBIDDEN" in excinfo.value.message
+ assert "403 FORBIDDEN" in excinfo.value.args[0]
def test_upload_image_with_filename(self, test_app):
""" Tests that you can upload an image with filename and description """
@@ -224,7 +227,7 @@ class TestAPI(object):
headers={"Content-Type": "application/json"}
)
- image = json.loads(response.body)["object"]
+ image = json.loads(response.body.decode())["object"]
# Check everything has been set on the media correctly
media = MediaEntry.query.filter_by(id=image["id"]).first()
@@ -260,7 +263,7 @@ class TestAPI(object):
)
# Assert that we've got a 403
- assert "403 FORBIDDEN" in excinfo.value.message
+ assert "403 FORBIDDEN" in excinfo.value.args[0]
def test_object_endpoint(self, test_app):
""" Tests that object can be looked up at endpoint """
@@ -281,7 +284,7 @@ class TestAPI(object):
with self.mock_oauth():
request = test_app.get(object_uri)
- image = json.loads(request.body)
+ image = json.loads(request.body.decode())
entry = MediaEntry.query.filter_by(id=image["id"]).first()
assert request.status_code == 200
@@ -351,7 +354,7 @@ class TestAPI(object):
headers=headers
)
- assert "403 FORBIDDEN" in excinfo.value.message
+ assert "403 FORBIDDEN" in excinfo.value.args[0]
def test_unable_to_update_someone_elses_comment(self, test_app):
""" Test that you're able to update someoen elses comment. """
@@ -396,14 +399,14 @@ class TestAPI(object):
headers=headers
)
- assert "403 FORBIDDEN" in excinfo.value.message
+ assert "403 FORBIDDEN" in excinfo.value.args[0]
def test_profile(self, test_app):
""" Tests profile endpoint """
uri = "/api/user/{0}/profile".format(self.user.username)
with self.mock_oauth():
response = test_app.get(uri)
- profile = json.loads(response.body)
+ profile = json.loads(response.body.decode())
assert response.status_code == 200
@@ -417,7 +420,7 @@ class TestAPI(object):
uri = "/api/user/{0}/".format(self.user.username)
with self.mock_oauth():
response = test_app.get(uri)
- user = json.loads(response.body)
+ user = json.loads(response.body.decode())
assert response.status_code == 200
@@ -433,7 +436,7 @@ class TestAPI(object):
with pytest.raises(AppError) as excinfo:
response = test_app.get("/api/whoami")
- assert "401 UNAUTHORIZED" in excinfo.value.message
+ assert "401 UNAUTHORIZED" in excinfo.value.args[0]
def test_read_feed(self, test_app):
""" Test able to read objects from the feed """
@@ -443,7 +446,7 @@ class TestAPI(object):
uri = "/api/user/{0}/feed".format(self.active_user.username)
with self.mock_oauth():
response = test_app.get(uri)
- feed = json.loads(response.body)
+ feed = json.loads(response.body.decode())
assert response.status_code == 200
@@ -468,9 +471,9 @@ class TestAPI(object):
with pytest.raises(AppError) as excinfo:
self._post_image_to_feed(test_app, data)
- assert "403 FORBIDDEN" in excinfo.value.message
+ assert "403 FORBIDDEN" in excinfo.value.args[0]
- def test_object_endpoint(self, test_app):
+ def test_object_endpoint_requestable(self, test_app):
""" Test that object endpoint can be requested """
response, data = self._upload_image(test_app, GOOD_JPG)
response, data = self._post_image_to_feed(test_app, data)
@@ -478,7 +481,7 @@ class TestAPI(object):
with self.mock_oauth():
response = test_app.get(data["object"]["links"]["self"]["href"])
- data = json.loads(response.body)
+ data = json.loads(response.body.decode())
assert response.status_code == 200
diff --git a/mediagoblin/tests/test_auth.py b/mediagoblin/tests/test_auth.py
index 1bbc3d01..7980953f 100644
--- a/mediagoblin/tests/test_auth.py
+++ b/mediagoblin/tests/test_auth.py
@@ -14,10 +14,14 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import urlparse
+
import pkg_resources
import pytest
+import six
+
+import six.moves.urllib.parse as urlparse
+
from mediagoblin import mg_globals
from mediagoblin.db.models import User
from mediagoblin.tests.tools import get_app, fixture_add_user
@@ -107,7 +111,7 @@ def test_register_views(test_app):
## Make sure user is logged in
request = template.TEMPLATE_TEST_CONTEXT[
'mediagoblin/user_pages/user_nonactive.html']['request']
- assert request.session['user_id'] == unicode(new_user.id)
+ assert request.session['user_id'] == six.text_type(new_user.id)
## Make sure we get email confirmation, and try verifying
assert len(mail.EMAIL_TEST_INBOX) == 1
@@ -115,7 +119,7 @@ def test_register_views(test_app):
assert message['To'] == 'angrygrrl@example.org'
email_context = template.TEMPLATE_TEST_CONTEXT[
'mediagoblin/auth/verification_email.txt']
- assert email_context['verification_url'] in message.get_payload(decode=True)
+ assert email_context['verification_url'].encode('ascii') in message.get_payload(decode=True)
path = urlparse.urlsplit(email_context['verification_url'])[2]
get_params = urlparse.urlsplit(email_context['verification_url'])[3]
@@ -186,7 +190,7 @@ def test_register_views(test_app):
email_context = template.TEMPLATE_TEST_CONTEXT[
'mediagoblin/plugins/basic_auth/fp_verification_email.txt']
#TODO - change the name of verification_url to something forgot-password-ish
- assert email_context['verification_url'] in message.get_payload(decode=True)
+ assert email_context['verification_url'].encode('ascii') in message.get_payload(decode=True)
path = urlparse.urlsplit(email_context['verification_url'])[2]
get_params = urlparse.urlsplit(email_context['verification_url'])[3]
@@ -305,7 +309,7 @@ def test_authentication_views(test_app):
# Make sure user is in the session
context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
session = context['request'].session
- assert session['user_id'] == unicode(test_user.id)
+ assert session['user_id'] == six.text_type(test_user.id)
# Successful logout
# -----------------
diff --git a/mediagoblin/tests/test_basic_auth.py b/mediagoblin/tests/test_basic_auth.py
index 828f0515..e7157bee 100644
--- a/mediagoblin/tests/test_basic_auth.py
+++ b/mediagoblin/tests/test_basic_auth.py
@@ -13,7 +13,8 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import urlparse
+
+import six.moves.urllib.parse as urlparse
from mediagoblin.db.models import User
from mediagoblin.plugins.basic_auth import tools as auth_tools
diff --git a/mediagoblin/tests/test_edit.py b/mediagoblin/tests/test_edit.py
index dc9c422f..384929cb 100644
--- a/mediagoblin/tests/test_edit.py
+++ b/mediagoblin/tests/test_edit.py
@@ -14,7 +14,9 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import urlparse, os, pytest
+import six
+import six.moves.urllib.parse as urlparse
+import pytest
from mediagoblin import mg_globals
from mediagoblin.db.models import User, MediaEntry
@@ -142,8 +144,7 @@ class TestUserEdit(object):
assert message['To'] == 'new@example.com'
email_context = template.TEMPLATE_TEST_CONTEXT[
'mediagoblin/edit/verification.txt']
- assert email_context['verification_url'] in \
- message.get_payload(decode=True)
+ assert email_context['verification_url'].encode('ascii') in message.get_payload(decode=True)
path = urlparse.urlsplit(email_context['verification_url'])[2]
assert path == u'/edit/verify_email/'
@@ -250,5 +251,11 @@ class TestMetaDataEdit:
old_metadata = new_metadata
new_metadata = media_entry.media_metadata
assert new_metadata == old_metadata
- assert ("u&#39;On the worst day&#39; is not a &#39;date-time&#39;" in
- response.body)
+ context = template.TEMPLATE_TEST_CONTEXT[
+ 'mediagoblin/edit/metadata.html']
+ if six.PY2:
+ expected = "u'On the worst day' is not a 'date-time'"
+ else:
+ expected = "'On the worst day' is not a 'date-time'"
+ assert context['form'].errors[
+ 'media_metadata'][0]['identifier'][0] == expected
diff --git a/mediagoblin/tests/test_exif.py b/mediagoblin/tests/test_exif.py
index af301818..ccc91d03 100644
--- a/mediagoblin/tests/test_exif.py
+++ b/mediagoblin/tests/test_exif.py
@@ -20,6 +20,8 @@ try:
except ImportError:
import Image
+from collections import OrderedDict
+
from mediagoblin.tools.exif import exif_fix_image_orientation, \
extract_exif, clean_exif, get_gps_data, get_useful
from .resources import GOOD_JPG, EMPTY_JPG, BAD_JPG, GPS_JPG
@@ -48,22 +50,23 @@ def test_exif_extraction():
assert gps == {}
# Do we have the "useful" tags?
- assert useful == {'EXIF CVAPattern': {'field_length': 8,
+
+ expected = OrderedDict({'EXIF CVAPattern': {'field_length': 8,
'field_offset': 26224,
'field_type': 7,
- 'printable': u'[0, 2, 0, 2, 1, 2, 0, 1]',
+ 'printable': '[0, 2, 0, 2, 1, 2, 0, 1]',
'tag': 41730,
'values': [0, 2, 0, 2, 1, 2, 0, 1]},
'EXIF ColorSpace': {'field_length': 2,
'field_offset': 476,
'field_type': 3,
- 'printable': u'sRGB',
+ 'printable': 'sRGB',
'tag': 40961,
'values': [1]},
'EXIF ComponentsConfiguration': {'field_length': 4,
'field_offset': 308,
'field_type': 7,
- 'printable': u'YCbCr',
+ 'printable': 'YCbCr',
'tag': 37121,
'values': [1, 2, 3, 0]},
'EXIF CompressedBitsPerPixel': {'field_length': 8,
@@ -365,7 +368,10 @@ def test_exif_extraction():
'field_type': 5,
'printable': u'300',
'tag': 283,
- 'values': [[300, 1]]}}
+ 'values': [[300, 1]]}})
+
+ for k, v in useful.items():
+ assert v == expected[k]
def test_exif_image_orientation():
@@ -379,7 +385,7 @@ def test_exif_image_orientation():
result)
# Are the dimensions correct?
- assert image.size == (428, 640)
+ assert image.size in ((428, 640), (640, 428))
# If this pixel looks right, the rest of the image probably will too.
assert_in(image.getdata()[10000],
diff --git a/mediagoblin/tests/test_http_callback.py b/mediagoblin/tests/test_http_callback.py
index 64b7ee8f..38f1cfaf 100644
--- a/mediagoblin/tests/test_http_callback.py
+++ b/mediagoblin/tests/test_http_callback.py
@@ -17,7 +17,9 @@
import json
import pytest
-from urlparse import urlparse, parse_qs
+import six
+
+from six.moves.urllib.parse import parse_qs, urlparse
from mediagoblin import mg_globals
from mediagoblin.tools import processing
@@ -49,7 +51,7 @@ class TestHTTPCallback(object):
'client_id': client_id,
'client_secret': client_secret})
- response_data = json.loads(response.body)
+ response_data = json.loads(response.body.decode())
return response_data['access_token']
@@ -63,7 +65,7 @@ class TestHTTPCallback(object):
code = parse_qs(urlparse(redirect.location).query)['code'][0]
client = self.db.OAuthClient.query.filter(
- self.db.OAuthClient.identifier == unicode(client_id)).first()
+ self.db.OAuthClient.identifier == six.text_type(client_id)).first()
client_secret = client.secret
diff --git a/mediagoblin/tests/test_ldap.py b/mediagoblin/tests/test_ldap.py
index 7e20d059..f251d150 100644
--- a/mediagoblin/tests/test_ldap.py
+++ b/mediagoblin/tests/test_ldap.py
@@ -13,10 +13,16 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import urlparse
+
import pkg_resources
import pytest
-import mock
+import six
+try:
+ import mock
+except ImportError:
+ import unittest.mock as mock
+
+import six.moves.urllib.parse as urlparse
from mediagoblin import mg_globals
from mediagoblin.db.base import Session
@@ -126,6 +132,6 @@ def test_ldap_plugin(ldap_plugin_app):
# Make sure user is in the session
context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
session = context['request'].session
- assert session['user_id'] == unicode(test_user.id)
+ assert session['user_id'] == six.text_type(test_user.id)
_test_authentication()
diff --git a/mediagoblin/tests/test_legacy_api.py b/mediagoblin/tests/test_legacy_api.py
index 4e0cbd8f..b3b2fcec 100644
--- a/mediagoblin/tests/test_legacy_api.py
+++ b/mediagoblin/tests/test_legacy_api.py
@@ -17,6 +17,7 @@
import logging
import base64
+import json
import pytest
@@ -48,10 +49,10 @@ class TestAPI(object):
return template.TEMPLATE_TEST_CONTEXT[template_name]
def http_auth_headers(self):
- return {'Authorization': 'Basic {0}'.format(
- base64.b64encode(':'.join([
+ return {'Authorization': ('Basic {0}'.format(
+ base64.b64encode((':'.join([
self.user.username,
- self.user_password])))}
+ self.user_password])).encode('ascii')).decode()))}
def do_post(self, data, test_app, **kwargs):
url = kwargs.pop('url', '/api/submit')
@@ -77,8 +78,8 @@ class TestAPI(object):
'/api/test',
headers=self.http_auth_headers())
- assert response.body == \
- '{"username": "joapi", "email": "joapi@example.com"}'
+ assert json.loads(response.body.decode()) == {
+ "username": "joapi", "email": "joapi@example.com"}
def test_2_test_submission(self, test_app):
self.login(test_app)
diff --git a/mediagoblin/tests/test_metadata.py b/mediagoblin/tests/test_metadata.py
index b4ea646e..a10e00ec 100644
--- a/mediagoblin/tests/test_metadata.py
+++ b/mediagoblin/tests/test_metadata.py
@@ -56,7 +56,7 @@ class TestMetadataFunctionality:
jsonld_fail_1 = None
try:
jsonld_fail_1 = compact_and_validate(metadata_fail_1)
- except ValidationError, e:
+ except ValidationError as e:
assert e.message == "'All Rights Reserved.' is not a 'uri'"
assert jsonld_fail_1 == None
#,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,
@@ -72,7 +72,7 @@ class TestMetadataFunctionality:
jsonld_fail_2 = None
try:
jsonld_fail_2 = compact_and_validate(metadata_fail_2)
- except ValidationError, e:
+ except ValidationError as e:
assert e.message == "'The other day' is not a 'date-time'"
assert jsonld_fail_2 == None
diff --git a/mediagoblin/tests/test_modelmethods.py b/mediagoblin/tests/test_modelmethods.py
index 32d5dce0..82cca855 100644
--- a/mediagoblin/tests/test_modelmethods.py
+++ b/mediagoblin/tests/test_modelmethods.py
@@ -17,13 +17,18 @@
# Maybe not every model needs a test, but some models have special
# methods, and so it makes sense to test them here.
+from __future__ import print_function
+
from mediagoblin.db.base import Session
from mediagoblin.db.models import MediaEntry, User, Privilege
from mediagoblin.tests import MGClientTestCase
from mediagoblin.tests.tools import fixture_add_user
-import mock
+try:
+ import mock
+except ImportError:
+ import unittest.mock as mock
import pytest
@@ -202,7 +207,7 @@ def test_media_data_init(test_app):
obj_in_session = 0
for obj in Session():
obj_in_session += 1
- print repr(obj)
+ print(repr(obj))
assert obj_in_session == 0
diff --git a/mediagoblin/tests/test_notifications.py b/mediagoblin/tests/test_notifications.py
index 3bf36f5f..385da569 100644
--- a/mediagoblin/tests/test_notifications.py
+++ b/mediagoblin/tests/test_notifications.py
@@ -16,7 +16,7 @@
import pytest
-import urlparse
+import six.moves.urllib.parse as urlparse
from mediagoblin.tools import template, mail
@@ -135,13 +135,13 @@ otherperson@example.com\n\nSGkgb3RoZXJwZXJzb24sCmNocmlzIGNvbW1lbnRlZCBvbiB5b3VyI
self.logout()
self.login('otherperson', 'nosreprehto')
- self.test_app.get(media_uri_slug + '/c/{0}/'.format(comment_id))
+ self.test_app.get(media_uri_slug + 'c/{0}/'.format(comment_id))
notification = Notification.query.filter_by(id=notification_id).first()
assert notification.seen == True
- self.test_app.get(media_uri_slug + '/notifications/silence/')
+ self.test_app.get(media_uri_slug + 'notifications/silence/')
subscription = CommentSubscription.query.filter_by(id=subscription_id)\
.first()
diff --git a/mediagoblin/tests/test_oauth1.py b/mediagoblin/tests/test_oauth1.py
index 568036e5..e41a68c7 100644
--- a/mediagoblin/tests/test_oauth1.py
+++ b/mediagoblin/tests/test_oauth1.py
@@ -14,10 +14,9 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import cgi
-
import pytest
-from urlparse import parse_qs, urlparse
+
+from six.moves.urllib.parse import parse_qs, urlparse
from oauthlib.oauth1 import Client
@@ -73,7 +72,7 @@ class TestOAuth(object):
"application_name": "Testificate MD",
"application_type": "web",
"contacts": "someone@someplace.com tuteo@tsengeo.lu",
- "logo_url": "http://ayrel.com/utral.png",
+ "logo_uri": "http://ayrel.com/utral.png",
"redirect_uris": "http://navi-kosman.lu http://gmg-yawne-oeru.lu",
}
@@ -86,7 +85,7 @@ class TestOAuth(object):
assert client.secret == client_info["client_secret"]
assert client.application_type == query["application_type"]
assert client.redirect_uri == query["redirect_uris"].split()
- assert client.logo_url == query["logo_url"]
+ assert client.logo_url == query["logo_uri"]
assert client.contacts == query["contacts"].split()
@@ -103,7 +102,7 @@ class TestOAuth(object):
"type": "client_update",
"application_name": "neytiri",
"contacts": "someone@someplace.com abc@cba.com",
- "logo_url": "http://place.com/picture.png",
+ "logo_uri": "http://place.com/picture.png",
"application_type": "web",
"redirect_uris": "http://blah.gmg/whatever https://inboxen.org/",
}
@@ -118,7 +117,7 @@ class TestOAuth(object):
assert client.application_type == update_query["application_type"]
assert client.application_name == update_query["application_name"]
assert client.contacts == update_query["contacts"].split()
- assert client.logo_url == update_query["logo_url"]
+ assert client.logo_url == update_query["logo_uri"]
assert client.redirect_uri == update_query["redirect_uris"].split()
def to_authorize_headers(self, data):
@@ -146,7 +145,7 @@ class TestOAuth(object):
headers["Content-Type"] = self.MIME_FORM
response = self.test_app.post(endpoint, headers=headers)
- response = cgi.parse_qs(response.body)
+ response = parse_qs(response.body.decode())
# each element is a list, reduce it to a string
for key, value in response.items():
diff --git a/mediagoblin/tests/test_oauth2.py b/mediagoblin/tests/test_oauth2.py
index 957f4e65..16372730 100644
--- a/mediagoblin/tests/test_oauth2.py
+++ b/mediagoblin/tests/test_oauth2.py
@@ -18,7 +18,9 @@ import json
import logging
import pytest
-from urlparse import parse_qs, urlparse
+import six
+
+from six.moves.urllib.parse import parse_qs, urlparse
from mediagoblin import mg_globals
from mediagoblin.tools import template, pluginapi
@@ -154,14 +156,14 @@ class TestOAuth(object):
code = self.get_code_from_redirect_uri(code_redirect.location)
client = self.db.OAuthClient.query.filter(
- self.db.OAuthClient.identifier == unicode(client_id)).first()
+ self.db.OAuthClient.identifier == six.text_type(client_id)).first()
token_res = self.test_app.get('/oauth-2/access_token?client_id={0}&\
code={1}&client_secret={2}'.format(client_id, code, client.secret))
assert token_res.status_int == 200
- token_data = json.loads(token_res.body)
+ token_data = json.loads(token_res.body.decode())
assert not 'error' in token_data
assert 'access_token' in token_data
@@ -182,14 +184,14 @@ code={1}&client_secret={2}'.format(client_id, code, client.secret))
code = self.get_code_from_redirect_uri(code_redirect.location)
client = self.db.OAuthClient.query.filter(
- self.db.OAuthClient.identifier == unicode(client_id)).first()
+ self.db.OAuthClient.identifier == six.text_type(client_id)).first()
token_res = self.test_app.get('/oauth-2/access_token?\
code={0}&client_secret={1}'.format(code, client.secret))
assert token_res.status_int == 200
- token_data = json.loads(token_res.body)
+ token_data = json.loads(token_res.body.decode())
assert 'error' in token_data
assert not 'access_token' in token_data
@@ -213,7 +215,7 @@ code={0}&client_secret={1}'.format(code, client.secret))
assert token_res.status_int == 200
- new_token_data = json.loads(token_res.body)
+ new_token_data = json.loads(token_res.body.decode())
assert not 'error' in new_token_data
assert 'access_token' in new_token_data
diff --git a/mediagoblin/tests/test_openid.py b/mediagoblin/tests/test_openid.py
index 0424fdda..a3ab176a 100644
--- a/mediagoblin/tests/test_openid.py
+++ b/mediagoblin/tests/test_openid.py
@@ -14,10 +14,14 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import urlparse
import pkg_resources
import pytest
-import mock
+import six
+import six.moves.urllib.parse as urlparse
+try:
+ import mock
+except ImportError:
+ import unittest.mock as mock
openid_consumer = pytest.importorskip(
"openid.consumer.consumer")
@@ -206,7 +210,7 @@ class TestOpenIDPlugin(object):
# Make sure user is in the session
context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
session = context['request'].session
- assert session['user_id'] == unicode(test_user.id)
+ assert session['user_id'] == six.text_type(test_user.id)
_test_new_user()
diff --git a/mediagoblin/tests/test_paste.ini b/mediagoblin/tests/test_paste.ini
index a9595432..8d75c3cb 100644
--- a/mediagoblin/tests/test_paste.ini
+++ b/mediagoblin/tests/test_paste.ini
@@ -1,40 +1,18 @@
[DEFAULT]
debug = true
-[composite:main]
-use = egg:Paste#urlmap
-/ = mediagoblin
-/mgoblin_media/ = publicstore_serve
-/test_static/ = mediagoblin_static
-/theme_static/ = theme_static
-/plugin_static/ = plugin_static
-
-[app:mediagoblin]
+[app:main]
use = egg:mediagoblin#app
config = %(here)s/mediagoblin.ini
-
-[app:publicstore_serve]
-use = egg:Paste#static
-document_root = %(here)s/user_dev/media/public
-
-[app:mediagoblin_static]
-use = egg:Paste#static
-document_root = %(here)s/mediagoblin/static/
-
-[app:theme_static]
-use = egg:Paste#static
-document_root = %(here)s/user_dev/theme_static/
-cache_max_age = 86400
-
-[app:plugin_static]
-use = egg:Paste#static
-document_root = %(here)s/user_dev/plugin_static/
-cache_max_age = 86400
+/mgoblin_media = %(here)s/user_dev/media/public
+/test_static = %(here)s/mediagoblin/static
+/theme_static = %(here)s/user_dev/theme_static
+/plugin_static = %(here)s/user_dev/plugin_static
[celery]
CELERY_ALWAYS_EAGER = true
[server:main]
-use = egg:Paste#http
+use = egg:gunicorn
host = 127.0.0.1
port = 6543
diff --git a/mediagoblin/tests/test_pdf.py b/mediagoblin/tests/test_pdf.py
index b4d1940a..7107dc9a 100644
--- a/mediagoblin/tests/test_pdf.py
+++ b/mediagoblin/tests/test_pdf.py
@@ -14,6 +14,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import collections
import tempfile
import shutil
import os
@@ -21,19 +22,25 @@ import pytest
from mediagoblin.media_types.pdf.processing import (
pdf_info, check_prerequisites, create_pdf_thumb)
-from .resources import GOOD_PDF as GOOD
+from .resources import GOOD_PDF
-@pytest.mark.skipif("not check_prerequisites()")
+@pytest.mark.skipif("not os.path.exists(GOOD_PDF) or not check_prerequisites()")
def test_pdf():
- good_dict = {'pdf_version_major': 1, 'pdf_title': '',
- 'pdf_page_size_width': 612, 'pdf_author': '',
- 'pdf_keywords': '', 'pdf_pages': 10,
- 'pdf_producer': 'dvips + GNU Ghostscript 7.05',
- 'pdf_version_minor': 3,
- 'pdf_creator': 'LaTeX with hyperref package',
- 'pdf_page_size_height': 792}
- assert pdf_info(GOOD) == good_dict
+ expected_dict = {'pdf_author': -1,
+ 'pdf_creator': -1,
+ 'pdf_keywords': -1,
+ 'pdf_page_size_height': -1,
+ 'pdf_page_size_width': -1,
+ 'pdf_pages': -1,
+ 'pdf_producer': -1,
+ 'pdf_title': -1,
+ 'pdf_version_major': 1,
+ 'pdf_version_minor': -1}
+ good_info = pdf_info(GOOD_PDF)
+ for k, v in expected_dict.items():
+ assert(k in good_info)
+ assert(v == -1 or v == good_info[k])
temp_dir = tempfile.mkdtemp()
- create_pdf_thumb(GOOD, os.path.join(temp_dir, 'good_256_256.png'), 256, 256)
+ create_pdf_thumb(GOOD_PDF, os.path.join(temp_dir, 'good_256_256.png'), 256, 256)
shutil.rmtree(temp_dir)
diff --git a/mediagoblin/tests/test_persona.py b/mediagoblin/tests/test_persona.py
index a1cd30eb..a8466b8a 100644
--- a/mediagoblin/tests/test_persona.py
+++ b/mediagoblin/tests/test_persona.py
@@ -13,10 +13,16 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import urlparse
+
import pkg_resources
import pytest
-import mock
+import six
+try:
+ import mock
+except ImportError:
+ import unittest.mock as mock
+
+import six.moves.urllib.parse as urlparse
pytest.importorskip("requests")
@@ -140,7 +146,7 @@ class TestPersonaPlugin(object):
# Make sure user is in the session
context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
session = context['request'].session
- assert session['user_id'] == unicode(test_user.id)
+ assert session['user_id'] == six.text_type(test_user.id)
_test_registration()
diff --git a/mediagoblin/tests/test_piwigo.py b/mediagoblin/tests/test_piwigo.py
index 16ad0111..33aea580 100644
--- a/mediagoblin/tests/test_piwigo.py
+++ b/mediagoblin/tests/test_piwigo.py
@@ -44,28 +44,23 @@ class Test_PWG(object):
def test_session(self):
resp = self.do_post("pwg.session.login",
{"username": u"nouser", "password": "wrong"})
- assert resp.body == XML_PREFIX \
- + '<rsp stat="fail"><err code="999" msg="Invalid username/password"/></rsp>'
+ assert resp.body == (XML_PREFIX + '<rsp stat="fail"><err code="999" msg="Invalid username/password"/></rsp>').encode('ascii')
resp = self.do_post("pwg.session.login",
{"username": self.username, "password": "wrong"})
- assert resp.body == XML_PREFIX \
- + '<rsp stat="fail"><err code="999" msg="Invalid username/password"/></rsp>'
+ assert resp.body == (XML_PREFIX + '<rsp stat="fail"><err code="999" msg="Invalid username/password"/></rsp>').encode('ascii')
resp = self.do_get("pwg.session.getStatus")
- assert resp.body == XML_PREFIX \
- + '<rsp stat="ok"><username>guest</username></rsp>'
+ assert resp.body == (XML_PREFIX + '<rsp stat="ok"><username>guest</username></rsp>').encode('ascii')
resp = self.do_post("pwg.session.login",
{"username": self.username, "password": self.password})
- assert resp.body == XML_PREFIX + '<rsp stat="ok">1</rsp>'
+ assert resp.body == (XML_PREFIX + '<rsp stat="ok">1</rsp>').encode('ascii')
resp = self.do_get("pwg.session.getStatus")
- assert resp.body == XML_PREFIX \
- + '<rsp stat="ok"><username>chris</username></rsp>'
+ assert resp.body == (XML_PREFIX + '<rsp stat="ok"><username>chris</username></rsp>').encode('ascii')
self.do_get("pwg.session.logout")
resp = self.do_get("pwg.session.getStatus")
- assert resp.body == XML_PREFIX \
- + '<rsp stat="ok"><username>guest</username></rsp>'
+ assert resp.body == (XML_PREFIX + '<rsp stat="ok"><username>guest</username></rsp>').encode('ascii')
diff --git a/mediagoblin/tests/test_pluginapi.py b/mediagoblin/tests/test_pluginapi.py
index eae0ce15..2fd6df39 100644
--- a/mediagoblin/tests/test_pluginapi.py
+++ b/mediagoblin/tests/test_pluginapi.py
@@ -224,7 +224,7 @@ def test_hook_handle():
assert pluginapi.hook_handle(
"nothing_handling", call_log, unhandled_okay=True) is None
assert call_log == []
-
+
# Multiple provided, go with the first!
call_log = []
assert pluginapi.hook_handle(
@@ -348,7 +348,7 @@ def test_modify_context(context_modified_app):
"""
# Specific thing passed into a page
result = context_modified_app.get("/modify_context/specific/")
- assert result.body.strip() == """Specific page!
+ assert result.body.strip() == b"""Specific page!
specific thing: in yer specificpage
global thing: globally appended!
@@ -357,7 +357,7 @@ doubleme: happyhappy"""
# General test, should have global context variable only
result = context_modified_app.get("/modify_context/")
- assert result.body.strip() == """General page!
+ assert result.body.strip() == b"""General page!
global thing: globally appended!
lol: cats
@@ -421,7 +421,7 @@ def test_plugin_assetlink(static_plugin_app):
junk_file_path = os.path.join(
linked_assets_dir.rstrip(os.path.sep),
'junk.txt')
- with file(junk_file_path, 'w') as junk_file:
+ with open(junk_file_path, 'w') as junk_file:
junk_file.write('barf')
os.unlink(plugin_link_dir)
@@ -440,14 +440,14 @@ to:
# link dir exists, but is a non-symlink
os.unlink(plugin_link_dir)
- with file(plugin_link_dir, 'w') as clobber_file:
+ with open(plugin_link_dir, 'w') as clobber_file:
clobber_file.write('clobbered!')
result = run_assetlink().collection[0]
assert result == 'Could not link "staticstuff": %s exists and is not a symlink\n' % (
plugin_link_dir)
- with file(plugin_link_dir, 'r') as clobber_file:
+ with open(plugin_link_dir, 'r') as clobber_file:
assert clobber_file.read() == 'clobbered!'
@@ -456,11 +456,10 @@ def test_plugin_staticdirect(static_plugin_app):
Test that the staticdirect utilities pull up the right things
"""
result = json.loads(
- static_plugin_app.get('/staticstuff/').body)
+ static_plugin_app.get('/staticstuff/').body.decode())
assert len(result) == 2
assert result['mgoblin_bunny_pic'] == '/test_static/images/bunny_pic.png'
assert result['plugin_bunny_css'] == \
'/plugin_static/staticstuff/css/bunnify.css'
-
diff --git a/mediagoblin/tests/test_privileges.py b/mediagoblin/tests/test_privileges.py
index 05829b34..8ea3d754 100644
--- a/mediagoblin/tests/test_privileges.py
+++ b/mediagoblin/tests/test_privileges.py
@@ -14,6 +14,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import six
import pytest
from datetime import date, timedelta
from webtest import AppError
@@ -79,7 +80,7 @@ class TestPrivilegeFunctionality:
response = self.test_app.get('/')
assert response.status == "200 OK"
- assert "You are Banned" in response.body
+ assert b"You are Banned" in response.body
# Then test what happens when that ban has an expiration date which
# hasn't happened yet
#----------------------------------------------------------------------
@@ -92,7 +93,7 @@ class TestPrivilegeFunctionality:
response = self.test_app.get('/')
assert response.status == "200 OK"
- assert "You are Banned" in response.body
+ assert b"You are Banned" in response.body
# Then test what happens when that ban has an expiration date which
# has already happened
@@ -107,7 +108,7 @@ class TestPrivilegeFunctionality:
response = self.test_app.get('/')
assert response.status == "302 FOUND"
- assert not "You are Banned" in response.body
+ assert not b"You are Banned" in response.body
def testVariousPrivileges(self):
# The various actions that require privileges (ex. reporting,
@@ -127,14 +128,16 @@ class TestPrivilegeFunctionality:
#----------------------------------------------------------------------
with pytest.raises(AppError) as excinfo:
response = self.test_app.get('/submit/')
- assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
+ excinfo = str(excinfo) if six.PY2 else str(excinfo).encode('ascii')
+ assert b'Bad response: 403 FORBIDDEN' in excinfo
with pytest.raises(AppError) as excinfo:
response = self.do_post({'upload_files':[('file',GOOD_JPG)],
'title':u'Normal Upload 1'},
url='/submit/')
- assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
+ excinfo = str(excinfo) if six.PY2 else str(excinfo).encode('ascii')
+ assert b'Bad response: 403 FORBIDDEN' in excinfo
# Test that a user cannot comment without the commenter privilege
#----------------------------------------------------------------------
@@ -149,50 +152,58 @@ class TestPrivilegeFunctionality:
media_uri_slug = '/u/{0}/m/{1}/'.format(self.admin_user.username,
media_entry.slug)
response = self.test_app.get(media_uri_slug)
- assert not "Add a comment" in response.body
+ assert not b"Add a comment" in response.body
self.query_for_users()
with pytest.raises(AppError) as excinfo:
response = self.test_app.post(
media_uri_id + 'comment/add/',
{'comment_content': u'Test comment #42'})
- assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
+ excinfo = str(excinfo) if six.PY2 else str(excinfo).encode('ascii')
+ assert b'Bad response: 403 FORBIDDEN' in excinfo
# Test that a user cannot report without the reporter privilege
#----------------------------------------------------------------------
with pytest.raises(AppError) as excinfo:
response = self.test_app.get(media_uri_slug+"report/")
- assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
+ excinfo = str(excinfo) if six.PY2 else str(excinfo).encode('ascii')
+ assert b'Bad response: 403 FORBIDDEN' in excinfo
with pytest.raises(AppError) as excinfo:
response = self.do_post(
{'report_reason':u'Testing Reports #1',
'reporter_id':u'3'},
url=(media_uri_slug+"report/"))
- assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
+ excinfo = str(excinfo) if six.PY2 else str(excinfo).encode('ascii')
+ assert b'Bad response: 403 FORBIDDEN' in excinfo
# Test that a user cannot access the moderation pages w/o moderator
# or admin privileges
#----------------------------------------------------------------------
with pytest.raises(AppError) as excinfo:
response = self.test_app.get("/mod/users/")
- assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
+ excinfo = str(excinfo) if six.PY2 else str(excinfo).encode('ascii')
+ assert b'Bad response: 403 FORBIDDEN' in excinfo
with pytest.raises(AppError) as excinfo:
response = self.test_app.get("/mod/reports/")
- assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
+ excinfo = str(excinfo) if six.PY2 else str(excinfo).encode('ascii')
+ assert b'Bad response: 403 FORBIDDEN' in excinfo
with pytest.raises(AppError) as excinfo:
response = self.test_app.get("/mod/media/")
- assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
+ excinfo = str(excinfo) if six.PY2 else str(excinfo).encode('ascii')
+ assert b'Bad response: 403 FORBIDDEN' in excinfo
with pytest.raises(AppError) as excinfo:
response = self.test_app.get("/mod/users/1/")
- assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
+ excinfo = str(excinfo) if six.PY2 else str(excinfo).encode('ascii')
+ assert b'Bad response: 403 FORBIDDEN' in excinfo
with pytest.raises(AppError) as excinfo:
response = self.test_app.get("/mod/reports/1/")
- assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
+ excinfo = str(excinfo) if six.PY2 else str(excinfo).encode('ascii')
+ assert b'Bad response: 403 FORBIDDEN' in excinfo
self.query_for_users()
@@ -202,4 +213,5 @@ class TestPrivilegeFunctionality:
'targeted_user':self.admin_user.id},
url='/mod/reports/1/')
self.query_for_users()
- assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
+ excinfo = str(excinfo) if six.PY2 else str(excinfo).encode('ascii')
+ assert b'Bad response: 403 FORBIDDEN' in excinfo
diff --git a/mediagoblin/tests/test_reporting.py b/mediagoblin/tests/test_reporting.py
index a154a061..6a9fe205 100644
--- a/mediagoblin/tests/test_reporting.py
+++ b/mediagoblin/tests/test_reporting.py
@@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest
+import six
from mediagoblin.tools import template
from mediagoblin.tests.tools import (fixture_add_user, fixture_media_entry,
@@ -75,7 +76,7 @@ class TestReportFiling:
response, context = self.do_post(
{'report_reason':u'Testing Media Report',
- 'reporter_id':unicode(allie_id)},url= media_uri_slug + "report/")
+ 'reporter_id':six.text_type(allie_id)},url= media_uri_slug + "report/")
assert response.status == "302 FOUND"
@@ -110,7 +111,7 @@ class TestReportFiling:
response, context = self.do_post({
'report_reason':u'Testing Comment Report',
- 'reporter_id':unicode(allie_id)},url= comment_uri_slug + "report/")
+ 'reporter_id':six.text_type(allie_id)},url= comment_uri_slug + "report/")
assert response.status == "302 FOUND"
diff --git a/mediagoblin/tests/test_sql_migrations.py b/mediagoblin/tests/test_sql_migrations.py
index 3d67fdf6..7e0569ad 100644
--- a/mediagoblin/tests/test_sql_migrations.py
+++ b/mediagoblin/tests/test_sql_migrations.py
@@ -14,6 +14,11 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import six
+import pytest
+
+pytestmark = pytest.mark.skipif(six.PY3, reason='needs sqlalchemy.migrate')
+
import copy
from sqlalchemy import (
@@ -23,7 +28,8 @@ from sqlalchemy import (
from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql import select, insert
-from migrate import changeset
+if six.PY2:
+ from migrate import changeset
from mediagoblin.db.base import GMGTableBase
from mediagoblin.db.migration_tools import MigrationManager, RegisterMigration
@@ -190,7 +196,7 @@ def level_exits_new_table(db_conn):
for level in result:
- for exit_name, to_level in level['exits'].iteritems():
+ for exit_name, to_level in six.iteritems(level['exits']):
# Insert the level exit
db_conn.execute(
level_exits.insert().values(
diff --git a/mediagoblin/tests/test_storage.py b/mediagoblin/tests/test_storage.py
index f6f1d18f..5cb1672b 100644
--- a/mediagoblin/tests/test_storage.py
+++ b/mediagoblin/tests/test_storage.py
@@ -19,6 +19,8 @@ import os
import tempfile
import pytest
+import six
+
from werkzeug.utils import secure_filename
from mediagoblin import storage
@@ -45,7 +47,7 @@ def test_clean_listy_filepath():
storage.clean_listy_filepath(['../../', 'linooks.jpg'])
-class FakeStorageSystem():
+class FakeStorageSystem(object):
def __init__(self, foobie, blech, **kwargs):
self.foobie = foobie
self.blech = blech
@@ -78,8 +80,8 @@ def test_storage_system_from_config():
'mediagoblin.tests.test_storage:FakeStorageSystem'})
assert this_storage.foobie == 'eiboof'
assert this_storage.blech == 'hcelb'
- assert unicode(this_storage.__class__) == \
- u'mediagoblin.tests.test_storage.FakeStorageSystem'
+ assert six.text_type(this_storage.__class__) == \
+ u"<class 'mediagoblin.tests.test_storage.FakeStorageSystem'>"
##########################
@@ -172,7 +174,7 @@ def test_basic_storage_get_file():
with this_storage.get_file(filepath, 'r') as our_file:
assert our_file.read() == 'First file'
assert os.path.exists(os.path.join(tmpdir, 'dir1/dir2/ourfile.txt'))
- with file(os.path.join(tmpdir, 'dir1/dir2/ourfile.txt'), 'r') as our_file:
+ with open(os.path.join(tmpdir, 'dir1/dir2/ourfile.txt'), 'r') as our_file:
assert our_file.read() == 'First file'
# Write to the same path but try to get a unique file.
@@ -184,13 +186,13 @@ def test_basic_storage_get_file():
with this_storage.get_file(new_filepath, 'r') as our_file:
assert our_file.read() == 'Second file'
assert os.path.exists(os.path.join(tmpdir, *new_filepath))
- with file(os.path.join(tmpdir, *new_filepath), 'r') as our_file:
+ with open(os.path.join(tmpdir, *new_filepath), 'r') as our_file:
assert our_file.read() == 'Second file'
# Read from an existing file
manually_written_file = os.makedirs(
os.path.join(tmpdir, 'testydir'))
- with file(os.path.join(tmpdir, 'testydir/testyfile.txt'), 'w') as testyfile:
+ with open(os.path.join(tmpdir, 'testydir/testyfile.txt'), 'w') as testyfile:
testyfile.write('testy file! so testy.')
with this_storage.get_file(['testydir', 'testyfile.txt']) as testyfile:
@@ -286,7 +288,7 @@ def test_basic_storage_copy_locally():
this_storage.copy_locally(filepath, new_file_dest)
this_storage.delete_file(filepath)
- assert file(new_file_dest).read() == 'Testing this file'
+ assert open(new_file_dest).read() == 'Testing this file'
os.remove(new_file_dest)
os.rmdir(dest_tmpdir)
@@ -295,7 +297,7 @@ def test_basic_storage_copy_locally():
def _test_copy_local_to_storage_works(tmpdir, this_storage):
local_filename = tempfile.mktemp()
- with file(local_filename, 'w') as tmpfile:
+ with open(local_filename, 'w') as tmpfile:
tmpfile.write('haha')
this_storage.copy_local_to_storage(
@@ -303,7 +305,7 @@ def _test_copy_local_to_storage_works(tmpdir, this_storage):
os.remove(local_filename)
- assert file(
+ assert open(
os.path.join(tmpdir, 'dir1/dir2/copiedto.txt'),
'r').read() == 'haha'
diff --git a/mediagoblin/tests/test_submission.py b/mediagoblin/tests/test_submission.py
index 36e57b8c..03d255fb 100644
--- a/mediagoblin/tests/test_submission.py
+++ b/mediagoblin/tests/test_submission.py
@@ -14,14 +14,18 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import sys
-reload(sys)
-sys.setdefaultencoding('utf-8')
+import six
+
+if six.PY2: # this hack only work in Python 2
+ import sys
+ reload(sys)
+ sys.setdefaultencoding('utf-8')
-import urlparse
import os
import pytest
+import six.moves.urllib.parse as urlparse
+
from mediagoblin.tests.tools import fixture_add_user
from mediagoblin import mg_globals
from mediagoblin.db.models import MediaEntry, User
@@ -34,7 +38,7 @@ from .resources import GOOD_JPG, GOOD_PNG, EVIL_FILE, EVIL_JPG, EVIL_PNG, \
BIG_BLUE, GOOD_PDF, GPS_JPG, MED_PNG, BIG_PNG
GOOD_TAG_STRING = u'yin,yang'
-BAD_TAG_STRING = unicode('rage,' + 'f' * 26 + 'u' * 26)
+BAD_TAG_STRING = six.text_type('rage,' + 'f' * 26 + 'u' * 26)
FORM_CONTEXT = ['mediagoblin/submit/start.html', 'submit_form']
REQUEST_CONTEXT = ['mediagoblin/user_pages/user.html', 'request']
@@ -145,7 +149,7 @@ class TestSubmission:
def test_normal_png(self):
self.check_normal_upload(u'Normal upload 2', GOOD_PNG)
- @pytest.mark.skipif("not pdf_check_prerequisites()")
+ @pytest.mark.skipif("not os.path.exists(GOOD_PDF) or not pdf_check_prerequisites()")
def test_normal_pdf(self):
response, context = self.do_post({'title': u'Normal upload 3 (pdf)'},
do_follow=True,
diff --git a/mediagoblin/tests/test_submission/good.pdf b/mediagoblin/tests/test_submission/good.pdf
index ab5db006..d7029f36 100644..120000
--- a/mediagoblin/tests/test_submission/good.pdf
+++ b/mediagoblin/tests/test_submission/good.pdf
Binary files differ
diff --git a/mediagoblin/tests/test_util.py b/mediagoblin/tests/test_util.py
index 36563e75..8193233f 100644
--- a/mediagoblin/tests/test_util.py
+++ b/mediagoblin/tests/test_util.py
@@ -14,12 +14,17 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import mock
+try:
+ import mock
+except ImportError:
+ import unittest.mock as mock
import email
import pytest
import smtplib
import pkg_resources
+import six
+
from mediagoblin.tests.tools import get_app
from mediagoblin.tools import common, url, translate, mail, text, testing
@@ -57,7 +62,7 @@ I hope you like unit tests JUST AS MUCH AS I DO!""")
assert message['From'] == "sender@mediagoblin.example.org"
assert message['To'] == "amanda@example.org, akila@example.org"
assert message['Subject'] == "Testing is so much fun!"
- assert message.get_payload(decode=True) == """HAYYY GUYS!
+ assert message.get_payload(decode=True) == b"""HAYYY GUYS!
I hope you like unit tests JUST AS MUCH AS I DO!"""
@@ -70,7 +75,7 @@ I hope you like unit tests JUST AS MUCH AS I DO!"""
assert mbox_message['From'] == "sender@mediagoblin.example.org"
assert mbox_message['To'] == "amanda@example.org, akila@example.org"
assert mbox_message['Subject'] == "Testing is so much fun!"
- assert mbox_message.get_payload(decode=True) == """HAYYY GUYS!
+ assert mbox_message.get_payload(decode=True) == b"""HAYYY GUYS!
I hope you like unit tests JUST AS MUCH AS I DO!"""
@@ -144,13 +149,13 @@ def test_gettext_lazy_proxy():
orig = u"Password"
set_thread_locale("es")
- p1 = unicode(proxy)
+ p1 = six.text_type(proxy)
p1_should = pass_to_ugettext(orig)
assert p1_should != orig, "Test useless, string not translated"
assert p1 == p1_should
set_thread_locale("sv")
- p2 = unicode(proxy)
+ p2 = six.text_type(proxy)
p2_should = pass_to_ugettext(orig)
assert p2_should != orig, "Test broken, string not translated"
assert p2 == p2_should
diff --git a/mediagoblin/tests/test_workbench.py b/mediagoblin/tests/test_workbench.py
index 6695618b..f3ff57ed 100644
--- a/mediagoblin/tests/test_workbench.py
+++ b/mediagoblin/tests/test_workbench.py
@@ -50,7 +50,7 @@ class TestWorkbench(object):
# kill a workbench
this_workbench = self.workbench_manager.create()
tmpfile_name = this_workbench.joinpath('temp.txt')
- tmpfile = file(tmpfile_name, 'w')
+ tmpfile = open(tmpfile_name, 'w')
with tmpfile:
tmpfile.write('lollerskates')
diff --git a/mediagoblin/tests/tools.py b/mediagoblin/tests/tools.py
index 34392bf1..7d29321b 100644
--- a/mediagoblin/tests/tools.py
+++ b/mediagoblin/tests/tools.py
@@ -19,6 +19,7 @@ import os
import pkg_resources
import shutil
+import six
from paste.deploy import loadapp
from webtest import TestApp
@@ -144,7 +145,7 @@ def install_fixtures_simple(db, fixtures):
"""
Very simply install fixtures in the database
"""
- for collection_name, collection_fixtures in fixtures.iteritems():
+ for collection_name, collection_fixtures in six.iteritems(fixtures):
collection = db[collection_name]
for fixture in collection_fixtures:
collection.insert(fixture)
@@ -164,7 +165,7 @@ def assert_db_meets_expected(db, expected):
{'id': 'foo',
'some_field': 'some_value'},]}
"""
- for collection_name, collection_data in expected.iteritems():
+ for collection_name, collection_data in six.iteritems(expected):
collection = db[collection_name]
for expected_document in collection_data:
document = collection.query.filter_by(id=expected_document['id']).first()
diff --git a/mediagoblin/tools/crypto.py b/mediagoblin/tools/crypto.py
index b219a484..c85ecd4a 100644
--- a/mediagoblin/tools/crypto.py
+++ b/mediagoblin/tools/crypto.py
@@ -51,7 +51,7 @@ def load_key(filename):
def create_key(key_dir, key_filepath):
global __itsda_secret
- old_umask = os.umask(077)
+ old_umask = os.umask(0o77)
key_file = None
try:
if not os.path.isdir(key_dir):
@@ -60,7 +60,7 @@ def create_key(key_dir, key_filepath):
key = str(getrandbits(192))
key_file = tempfile.NamedTemporaryFile(dir=key_dir, suffix='.bin',
delete=False)
- key_file.write(key)
+ key_file.write(key.encode('ascii'))
key_file.flush()
os.rename(key_file.name, key_filepath)
key_file.close()
@@ -79,7 +79,7 @@ def setup_crypto():
key_filepath = os.path.join(key_dir, 'itsdangeroussecret.bin')
try:
load_key(key_filepath)
- except IOError, error:
+ except IOError as error:
if error.errno != errno.ENOENT:
raise
create_key(key_dir, key_filepath)
diff --git a/mediagoblin/tools/exif.py b/mediagoblin/tools/exif.py
index 50f1aabf..ec83f43c 100644
--- a/mediagoblin/tools/exif.py
+++ b/mediagoblin/tools/exif.py
@@ -14,6 +14,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import six
+
from exifread import process_file
from exifread.utils import Ratio
@@ -75,7 +77,7 @@ def extract_exif(filename):
Returns EXIF tags found in file at ``filename``
"""
try:
- with file(filename) as image:
+ with open(filename, 'rb') as image:
return process_file(image, details=False)
except IOError:
raise BadMediaFail(_('Could not read the image file.'))
@@ -94,7 +96,7 @@ def clean_exif(exif):
'Thumbnail JPEGInterchangeFormat']
return dict((key, _ifd_tag_to_dict(value)) for (key, value)
- in exif.iteritems() if key not in disabled_tags)
+ in six.iteritems(exif) if key not in disabled_tags)
def _ifd_tag_to_dict(tag):
@@ -110,7 +112,7 @@ def _ifd_tag_to_dict(tag):
'field_length': tag.field_length,
'values': None}
- if isinstance(tag.printable, str):
+ if isinstance(tag.printable, six.binary_type):
# Force it to be decoded as UTF-8 so that it'll fit into the DB
data['printable'] = tag.printable.decode('utf8', 'replace')
@@ -118,7 +120,7 @@ def _ifd_tag_to_dict(tag):
data['values'] = [_ratio_to_list(val) if isinstance(val, Ratio) else val
for val in tag.values]
else:
- if isinstance(tag.values, str):
+ if isinstance(tag.values, six.binary_type):
# Force UTF-8, so that it fits into the DB
data['values'] = tag.values.decode('utf8', 'replace')
else:
@@ -132,7 +134,8 @@ def _ratio_to_list(ratio):
def get_useful(tags):
- return dict((key, tag) for (key, tag) in tags.iteritems())
+ from collections import OrderedDict
+ return OrderedDict((key, tag) for (key, tag) in six.iteritems(tags))
def get_gps_data(tags):
@@ -149,7 +152,7 @@ def get_gps_data(tags):
'latitude': tags['GPS GPSLatitude'],
'longitude': tags['GPS GPSLongitude']}
- for key, dat in dms_data.iteritems():
+ for key, dat in six.iteritems(dms_data):
gps_data[key] = (
lambda v:
float(v[0].num) / float(v[0].den) \
diff --git a/mediagoblin/tools/federation.py b/mediagoblin/tools/federation.py
new file mode 100644
index 00000000..890e8801
--- /dev/null
+++ b/mediagoblin/tools/federation.py
@@ -0,0 +1,63 @@
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2014 MediaGoblin contributors. See AUTHORS.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from mediagoblin.db.models import Activity, Generator, User
+
+def create_activity(verb, obj, actor, target=None):
+ """
+ This will create an Activity object which for the obj if possible
+ and save it. The verb should be one of the following:
+ add, author, create, delete, dislike, favorite, follow
+ like, post, share, unfollow, unfavorite, unlike, unshare,
+ update, tag.
+
+ If none of those fit you might not want/need to create an activity for
+ the object. The list is in mediagoblin.db.models.Activity.VALID_VERBS
+ """
+ # exception when we try and generate an activity with an unknow verb
+ # could change later to allow arbitrary verbs but at the moment we'll play
+ # it safe.
+
+ if verb not in Activity.VALID_VERBS:
+ raise ValueError("A invalid verb type has been supplied.")
+
+ # This should exist as we're creating it by the migration for Generator
+ generator = Generator.query.filter_by(name="GNU MediaGoblin").first()
+ if generator is None:
+ generator = Generator(
+ name="GNU MediaGoblin",
+ object_type="service"
+ )
+ generator.save()
+
+ activity = Activity(verb=verb)
+ activity.set_object(obj)
+
+ if target is not None:
+ activity.set_target(target)
+
+ # If they've set it override the actor from the obj.
+ activity.actor = actor.id if isinstance(actor, User) else actor
+
+ activity.generator = generator.id
+ activity.save()
+
+ # Sigh want to do this prior to save but I can't figure a way to get
+ # around relationship() not looking up object when model isn't saved.
+ if activity.generate_content():
+ activity.save()
+
+ return activity
diff --git a/mediagoblin/tools/mail.py b/mediagoblin/tools/mail.py
index ab355835..ab3e0eaa 100644
--- a/mediagoblin/tools/mail.py
+++ b/mediagoblin/tools/mail.py
@@ -14,11 +14,13 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from __future__ import print_function, unicode_literals
+
import six
import smtplib
import sys
-from email.MIMEText import MIMEText
from mediagoblin import mg_globals, messages
+from mediagoblin._compat import MIMEText
from mediagoblin.tools import common
### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -130,12 +132,12 @@ def send_email(from_addr, to_addrs, subject, message_body):
EMAIL_TEST_INBOX.append(message)
elif mg_globals.app_config['email_debug_mode']:
- print u"===== Email ====="
- print u"From address: %s" % message['From']
- print u"To addresses: %s" % message['To']
- print u"Subject: %s" % message['Subject']
- print u"-- Body: --"
- print message.get_payload(decode=True)
+ print("===== Email =====")
+ print("From address: %s" % message['From'])
+ print("To addresses: %s" % message['To'])
+ print("Subject: %s" % message['Subject'])
+ print("-- Body: --")
+ print(message.get_payload(decode=True))
return mhost.sendmail(from_addr, to_addrs, message.as_string())
@@ -162,5 +164,5 @@ def email_debug_message(request):
if mg_globals.app_config['email_debug_mode']:
# DEBUG message, no need to translate
messages.add_message(request, messages.DEBUG,
- u"This instance is running in email debug mode. "
- u"The email will be on the console of the server process.")
+ "This instance is running in email debug mode. "
+ "The email will be on the console of the server process.")
diff --git a/mediagoblin/tools/metadata.py b/mediagoblin/tools/metadata.py
index bfefcac9..aeb4f829 100644
--- a/mediagoblin/tools/metadata.py
+++ b/mediagoblin/tools/metadata.py
@@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from io import open
import os
import copy
import json
@@ -102,7 +103,7 @@ def load_resource(package, resource_path):
os.path.sep.
"""
filename = resource_filename(package, os.path.sep.join(resource_path))
- return file(filename).read()
+ return open(filename, encoding="utf-8").read()
def load_resource_json(package, resource_path):
"""
diff --git a/mediagoblin/tools/pagination.py b/mediagoblin/tools/pagination.py
index 855878e0..a525caf7 100644
--- a/mediagoblin/tools/pagination.py
+++ b/mediagoblin/tools/pagination.py
@@ -17,9 +17,11 @@
import urllib
import copy
from math import ceil, floor
-from itertools import izip, count
+from itertools import count
from werkzeug.datastructures import MultiDict
+from six.moves import zip
+
PAGINATION_DEFAULT_PER_PAGE = 30
@@ -52,7 +54,7 @@ class Pagination(object):
if jump_to_id:
cursor = copy.copy(self.cursor)
- for (doc, increment) in izip(cursor, count(0)):
+ for (doc, increment) in list(zip(cursor, count(0))):
if doc.id == jump_to_id:
self.page = 1 + int(floor(increment / self.per_page))
diff --git a/mediagoblin/tools/processing.py b/mediagoblin/tools/processing.py
index 2abe6452..39a329c5 100644
--- a/mediagoblin/tools/processing.py
+++ b/mediagoblin/tools/processing.py
@@ -18,8 +18,7 @@ import logging
import json
import traceback
-from urllib2 import urlopen, Request, HTTPError
-from urllib import urlencode
+from six.moves.urllib import request, parse
_log = logging.getLogger(__name__)
@@ -37,10 +36,10 @@ def create_post_request(url, data, **kw):
data_parser: The parser function that is used to parse the `data`
argument
'''
- data_parser = kw.get('data_parser', urlencode)
+ data_parser = kw.get('data_parser', parse.urlencode)
headers = kw.get('headers', {})
- return Request(url, data_parser(data), headers=headers)
+ return request.Request(url, data_parser(data), headers=headers)
def json_processing_callback(entry):
@@ -76,11 +75,11 @@ def json_processing_callback(entry):
data_parser=json.dumps)
try:
- urlopen(request)
+ request.urlopen(request)
_log.debug('Processing callback for {0} sent'.format(entry))
return True
- except HTTPError:
+ except request.HTTPError:
_log.error('Failed to send callback: {0}'.format(
traceback.format_exc()))
diff --git a/mediagoblin/tools/response.py b/mediagoblin/tools/response.py
index 57552963..889938a8 100644
--- a/mediagoblin/tools/response.py
+++ b/mediagoblin/tools/response.py
@@ -16,6 +16,7 @@
import json
+import six
import werkzeug.utils
from werkzeug.wrappers import Response as wz_Response
from mediagoblin.tools.template import render_template
@@ -29,11 +30,12 @@ class Response(wz_Response):
default_mimetype = u'text/html'
-def render_to_response(request, template, context, status=200):
+def render_to_response(request, template, context, status=200, mimetype=None):
"""Much like Django's shortcut.render()"""
return Response(
render_template(request, template, context),
- status=status)
+ status=status,
+ mimetype=mimetype)
def render_error(request, status=500, title=_('Oops!'),
err_msg=_('An error occured')):
@@ -152,7 +154,7 @@ def json_response(serializable, _disable_cors=False, *args, **kw):
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, X-Requested-With'}
- for key, value in cors_headers.iteritems():
+ for key, value in six.iteritems(cors_headers):
response.headers.set(key, value)
return response
@@ -164,7 +166,7 @@ def json_error(error_str, status=400, *args, **kwargs):
code to 400.
"""
return json_response({"error": error_str}, status=status, *args, **kwargs)
-
+
def form_response(data, *args, **kwargs):
"""
Responds using application/x-www-form-urlencoded and returns a werkzeug
diff --git a/mediagoblin/tools/staticdirect.py b/mediagoblin/tools/staticdirect.py
index 8381b8b6..881dd20e 100644
--- a/mediagoblin/tools/staticdirect.py
+++ b/mediagoblin/tools/staticdirect.py
@@ -24,6 +24,8 @@
import logging
+import six
+
_log = logging.getLogger(__name__)
@@ -48,7 +50,7 @@ class StaticDirect(object):
def __init__(self, domains):
self.domains = dict(
[(key, value.rstrip('/'))
- for key, value in domains.iteritems()])
+ for key, value in six.iteritems(domains)])
self.cache = {}
def __call__(self, filepath, domain=None):
diff --git a/mediagoblin/tools/template.py b/mediagoblin/tools/template.py
index e5acdf45..b01196fd 100644
--- a/mediagoblin/tools/template.py
+++ b/mediagoblin/tools/template.py
@@ -14,6 +14,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import six
import jinja2
from jinja2.ext import Extension
@@ -33,7 +34,6 @@ from mediagoblin.tools.pluginapi import get_hook_templates, hook_transform
from mediagoblin.tools.timesince import timesince
from mediagoblin.meddleware.csrf import render_csrf_form_token
-
SETUP_JINJA_ENVS = {}
@@ -66,9 +66,12 @@ def get_jinja_env(template_loader, locale):
'jinja2.ext.i18n', 'jinja2.ext.autoescape',
TemplateHookExtension] + local_exts)
- template_env.install_gettext_callables(
- mg_globals.thread_scope.translations.ugettext,
- mg_globals.thread_scope.translations.ungettext)
+ if six.PY2:
+ template_env.install_gettext_callables(mg_globals.thread_scope.translations.ugettext,
+ mg_globals.thread_scope.translations.ungettext)
+ else:
+ template_env.install_gettext_callables(mg_globals.thread_scope.translations.gettext,
+ mg_globals.thread_scope.translations.ngettext)
# All templates will know how to ...
# ... fetch all waiting messages and remove them from the queue
diff --git a/mediagoblin/tools/translate.py b/mediagoblin/tools/translate.py
index f77351b5..a5e56cfe 100644
--- a/mediagoblin/tools/translate.py
+++ b/mediagoblin/tools/translate.py
@@ -17,6 +17,7 @@
import gettext
import pkg_resources
+import six
from babel import localedata
from babel.support import LazyProxy
@@ -52,9 +53,9 @@ class ReallyLazyProxy(LazyProxy):
"""
Like LazyProxy, except that it doesn't cache the value ;)
"""
- @property
- def value(self):
- return self._func(*self._args, **self._kwargs)
+ def __init__(self, func, *args, **kwargs):
+ super(ReallyLazyProxy, self).__init__(func, *args, **kwargs)
+ object.__setattr__(self, '_is_cache_enabled', False)
def __repr__(self):
return "<%s for %s(%r, %r)>" % (
@@ -146,8 +147,9 @@ def pass_to_ugettext(*args, **kwargs):
The reason we can't have a global ugettext method is because
mg_globals gets swapped out by the application per-request.
"""
- return mg_globals.thread_scope.translations.ugettext(
- *args, **kwargs)
+ if six.PY2:
+ return mg_globals.thread_scope.translations.ugettext(*args, **kwargs)
+ return mg_globals.thread_scope.translations.gettext(*args, **kwargs)
def pass_to_ungettext(*args, **kwargs):
"""
@@ -156,8 +158,9 @@ def pass_to_ungettext(*args, **kwargs):
The reason we can't have a global ugettext method is because
mg_globals gets swapped out by the application per-request.
"""
- return mg_globals.thread_scope.translations.ungettext(
- *args, **kwargs)
+ if six.PY2:
+ return mg_globals.thread_scope.translations.ungettext(*args, **kwargs)
+ return mg_globals.thread_scope.translations.ngettext(*args, **kwargs)
def lazy_pass_to_ugettext(*args, **kwargs):
diff --git a/mediagoblin/tools/url.py b/mediagoblin/tools/url.py
index 657c0373..4d97247a 100644
--- a/mediagoblin/tools/url.py
+++ b/mediagoblin/tools/url.py
@@ -17,6 +17,8 @@
import re
from unidecode import unidecode
+import six
+
_punct_re = re.compile(r'[\t !"#:$%&\'()*\-/<=>?@\[\\\]^_`{|},.]+')
@@ -27,4 +29,4 @@ def slugify(text, delim=u'-'):
result = []
for word in _punct_re.split(text.lower()):
result.extend(unidecode(word).split())
- return unicode(delim.join(result))
+ return six.text_type(delim.join(result))
diff --git a/mediagoblin/tools/workbench.py b/mediagoblin/tools/workbench.py
index 0bd4096b..f1ad6414 100644
--- a/mediagoblin/tools/workbench.py
+++ b/mediagoblin/tools/workbench.py
@@ -18,10 +18,15 @@ import os
import shutil
import tempfile
+import six
+
+from mediagoblin._compat import py2_unicode
# Actual workbench stuff
# ----------------------
+
+@py2_unicode
class Workbench(object):
"""
Represent the directory for the workbench
@@ -36,11 +41,8 @@ class Workbench(object):
"""
self.dir = dir
- def __unicode__(self):
- return unicode(self.dir)
-
def __str__(self):
- return str(self.dir)
+ return six.text_type(self.dir)
def __repr__(self):
try:
diff --git a/mediagoblin/user_pages/views.py b/mediagoblin/user_pages/views.py
index 78751a28..b6cbcabd 100644
--- a/mediagoblin/user_pages/views.py
+++ b/mediagoblin/user_pages/views.py
@@ -18,6 +18,8 @@ import logging
import datetime
import json
+import six
+
from mediagoblin import messages, mg_globals
from mediagoblin.db.models import (MediaEntry, MediaTag, Collection,
CollectionItem, User)
@@ -26,6 +28,7 @@ from mediagoblin.tools.response import render_to_response, render_404, \
from mediagoblin.tools.text import cleaned_markdown_conversion
from mediagoblin.tools.translate import pass_to_ugettext as _
from mediagoblin.tools.pagination import Pagination
+from mediagoblin.tools.federation import create_activity
from mediagoblin.user_pages import forms as user_forms
from mediagoblin.user_pages.lib import (send_comment_email,
add_media_to_collection, build_report_object)
@@ -178,7 +181,7 @@ def media_post_comment(request, media):
comment = request.db.MediaComment()
comment.media_entry = media.id
comment.author = request.user.id
- comment.content = unicode(request.form['comment_content'])
+ comment.content = six.text_type(request.form['comment_content'])
# Show error message if commenting is disabled.
if not mg_globals.app_config['allow_comments']:
@@ -199,7 +202,7 @@ def media_post_comment(request, media):
_('Your comment has been posted!'))
trigger_notification(comment, media, request)
-
+ create_activity("post", comment, comment.author, target=media)
add_comment_subscription(request.user, media)
return redirect_obj(request, media)
@@ -212,7 +215,7 @@ def media_preview_comment(request):
if not request.is_xhr:
return render_404(request)
- comment = unicode(request.form['comment_content'])
+ comment = six.text_type(request.form['comment_content'])
cleancomment = { "content":cleaned_markdown_conversion(comment)}
return Response(json.dumps(cleancomment))
@@ -261,6 +264,7 @@ def media_collect(request, media):
collection.creator = request.user.id
collection.generate_slug()
collection.save()
+ create_activity("create", collection, collection.creator)
# Otherwise, use the collection selected from the drop-down
else:
@@ -287,7 +291,7 @@ def media_collect(request, media):
% (media.title, collection.title))
else: # Add item to collection
add_media_to_collection(collection, media, form.note.data)
-
+ create_activity("add", media, request.user, target=collection)
messages.add_message(request, messages.SUCCESS,
_('"%s" added to collection "%s"')
% (media.title, collection.title))
diff --git a/paste.ini b/paste.ini
index 3c7eb177..afd5982b 100644
--- a/paste.ini
+++ b/paste.ini
@@ -6,19 +6,17 @@
debug = false
[pipeline:main]
-pipeline = errors routing
-
-[composite:routing]
-use = egg:Paste#urlmap
-/ = mediagoblin
-/mgoblin_media/ = publicstore_serve
-/mgoblin_static/ = mediagoblin_static
-/theme_static/ = theme_static
-/plugin_static/ = plugin_static
+# pipeline = errors mediagoblin
+pipeline = mediagoblin
[app:mediagoblin]
use = egg:mediagoblin#app
config = %(here)s/mediagoblin_local.ini %(here)s/mediagoblin.ini
+# static paths
+/mgoblin_media = %(here)s/user_dev/media/public
+/mgoblin_static = %(here)s/mediagoblin/static
+/theme_static = %(here)s/user_dev/theme_static
+/plugin_static = %(here)s/user_dev/plugin_static
[loggers]
keys = root
@@ -42,26 +40,6 @@ formatter = generic
[formatter_generic]
format = %(asctime)s %(levelname)-7.7s [%(name)s] %(message)s
-[app:publicstore_serve]
-use = egg:Paste#static
-document_root = %(here)s/user_dev/media/public/
-cache_max_age = 604800
-
-[app:mediagoblin_static]
-use = egg:Paste#static
-document_root = %(here)s/mediagoblin/static/
-cache_max_age = 86400
-
-[app:theme_static]
-use = egg:Paste#static
-document_root = %(here)s/user_dev/theme_static/
-cache_max_age = 86400
-
-[app:plugin_static]
-use = egg:Paste#static
-document_root = %(here)s/user_dev/plugin_static/
-cache_max_age = 86400
-
[filter:errors]
use = egg:mediagoblin#errors
debug = false
@@ -74,9 +52,14 @@ debug = false
# The server that is run by default.
# By default, should only be accessable locally
[server:main]
-use = egg:Paste#http
+use = egg:gunicorn
host = 127.0.0.1
port = 6543
+# Gunicorn settings. See http://docs.gunicorn.org/en/19.0/settings.html
+# for more information about configuring Gunicorn
+proc_name = gmg
+reload = true
+accesslog = -
#######################
# Helper server configs
diff --git a/setup.py b/setup.py
index e2e84f2b..6773437e 100644
--- a/setup.py
+++ b/setup.py
@@ -14,17 +14,25 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from __future__ import print_function
+
from setuptools import setup, find_packages
+from io import open
import os
import re
+import sys
+
+PY2 = sys.version_info[0] == 2 # six is not installed yet
+
READMEFILE = "README"
VERSIONFILE = os.path.join("mediagoblin", "_version.py")
VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]"
def get_version():
- verstrline = open(VERSIONFILE, "rt").read()
+ with open(VERSIONFILE, "rt") as fobj:
+ verstrline = fobj.read()
mo = re.search(VSRE, verstrline, re.M)
if mo:
return mo.group(1)
@@ -32,6 +40,67 @@ def get_version():
raise RuntimeError("Unable to find version string in %s." %
VERSIONFILE)
+py2_only_install_requires = []
+if PY2:
+ py2_only_install_requires.append('argparse') # only for < 2.7
+ py2_only_install_requires.append('PasteScript')
+ # newer sqlalchemy-migrate requires pbr which BREAKS EVERYTHING AND IS
+ # TERRIBLE AND IS THE END OF ALL THINGS
+ # I'd love to remove this restriction.
+ py2_only_install_requires.append('sqlalchemy-migrate<0.8')
+ # # Annoying. Please remove once we can! We only indirectly
+ # # use pbr, and currently it breaks things, presumably till
+ # # their next release.
+ # py2_only_install_requires.append('pbr==0.5.22')
+ py2_only_install_requires.append('mock') # mock is in the stdlib for 3.3+
+ # PyPI version (1.4.2) does not have proper Python 3 support
+ py2_only_install_requires.append('ExifRead')
+
+install_requires = [
+ 'gunicorn',
+ 'alembic==0.6.6',
+ 'python-dateutil',
+ 'wtforms',
+ 'py-bcrypt',
+ 'pytest>=2.3.1',
+ 'pytest-xdist',
+ 'werkzeug>=0.7',
+ 'celery>=3.0',
+ 'kombu',
+ 'jinja2',
+ 'Babel>=1.3',
+ 'webtest<2',
+ 'ConfigObj',
+ 'Markdown',
+ 'sqlalchemy<0.9.0, >0.8.0',
+ 'itsdangerous',
+ 'pytz',
+ # PLEASE change this when we can; a dependency is forcing us to set this
+ # specific number and it is breaking setup.py develop
+ 'six==1.5.2',
+ 'oauthlib',
+ 'unidecode',
+ 'jsonschema',
+ 'PasteDeploy',
+ 'requests',
+ 'pyld',
+ # This is optional:
+ # 'translitcodec',
+ # For now we're expecting that users will install this from
+ # their package managers.
+ # 'lxml',
+ # 'Pillow',
+] + py2_only_install_requires
+
+dependency_links = []
+if not PY2:
+ # PyPI version (1.4.2) does not have proper Python 3 support
+ dependency_links.append('https://github.com/ianare/exif-py/zipball/develop#egg=ExifRead-2.0.0')
+ install_requires.append('ExifRead>=2.0.0')
+
+with open(READMEFILE, encoding="utf-8") as fobj:
+ long_description = fobj.read()
+
try:
setup(
name="mediagoblin",
@@ -40,57 +109,8 @@ try:
zip_safe=False,
include_package_data = True,
# scripts and dependencies
- install_requires=[
- 'setuptools',
- 'python-dateutil',
- 'PasteScript',
- 'wtforms',
- 'py-bcrypt',
- 'pytest>=2.3.1',
- 'pytest-xdist',
- 'werkzeug>=0.7',
- 'celery>=3.0',
- 'kombu',
- 'jinja2',
- 'sphinx',
- 'Babel>=1.0',
- 'argparse',
- 'webtest<2',
- 'ConfigObj',
- 'Markdown',
- 'sqlalchemy<0.9.0, >0.8.0',
- # newer sqlalchemy-migrate requires pbr which BREAKS EVERYTHING AND IS
- # TERRIBLE AND IS THE END OF ALL THINGS
- # I'd love to remove this restriction.
- 'sqlalchemy-migrate<0.8',
- 'mock',
- 'itsdangerous',
- 'pytz',
- 'six>=1.4.1',
- 'oauthlib',
- 'unidecode',
- 'jsonschema',
- 'requests',
- 'pyld',
- 'ExifRead',
-
- # PLEASE change this when we can; a dependency is forcing us to set this
- # specific number and it is breaking setup.py develop
- 'six==1.5.2'
-
- ## Annoying. Please remove once we can! We only indirectly
- ## use pbr, and currently it breaks things, presumably till
- ## their next release.
- # 'pbr==0.5.22',
-
- ## This is optional!
- # 'translitcodec',
- ## For now we're expecting that users will install this from
- ## their package managers.
- # 'lxml',
- # 'PIL',
- ],
- # requires=['gst'],
+ install_requires=install_requires,
+ dependency_links=dependency_links,
test_suite='nose.collector',
entry_points="""\
[console_scripts]
@@ -113,30 +133,34 @@ try:
author='Free Software Foundation and contributors',
author_email='cwebber@gnu.org',
url="http://mediagoblin.org/",
- download_url="http://mediagoblin.org/download/",
- long_description=open(READMEFILE).read(),
+ long_description=long_description,
+ description='MediaGoblin is a web application for publishing all kinds of media',
classifiers=[
"Development Status :: 3 - Alpha",
"Environment :: Web Environment",
"License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
"Operating System :: OS Independent",
"Programming Language :: Python",
+ 'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3.3',
+ 'Programming Language :: Python :: 3.4',
"Topic :: Internet :: WWW/HTTP :: Dynamic Content"
],
)
-except TypeError, e:
+except TypeError as e:
+ import sys
+
# Check if the problem is caused by the sqlalchemy/setuptools conflict
msg_as_str = str(e)
if not (msg_as_str == 'dist must be a Distribution instance'):
raise
# If so, tell the user it is OK to just run the script again.
- print "\n\n---------- NOTE ----------"
- print "The setup.py command you ran failed."
- print ""
- print ("It is a known possible failure. Just run it again. It works the "
- "second time.")
- import sys
+ print("\n\n---------- NOTE ----------", file=sys.stderr)
+ print("The setup.py command you ran failed.\n", file=sys.stderr)
+ print("It is a known possible failure. Just run it again. It works the "
+ "second time.", file=sys.stderr)
sys.exit(1)
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 00000000..023c53ed
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,13 @@
+[tox]
+envlist = py27, py33
+skipsdist = True
+sitepackages = False
+
+[testenv]
+usedevelop = True
+# for ExifRead 2.0.0
+install_command = pip install --process-dependency-links --pre {opts} {packages}
+commands = py.test ./mediagoblin/tests --boxed
+deps =
+ lxml
+ Pillow