aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--.tx/config8
-rw-r--r--COPYING60
-rw-r--r--README12
-rw-r--r--babel.ini2
-rwxr-xr-xdestroy_environment.py22
-rw-r--r--docs/Makefile29
-rw-r--r--docs/designdecisions.rst329
-rw-r--r--docs/hackinghowto.rst345
-rw-r--r--docs/source/_static/placeholder (renamed from docs/_static/placeholder)0
-rw-r--r--docs/source/_templates/mg_theme/layout.html39
-rw-r--r--docs/source/_templates/mg_theme/static/default.css_t299
-rw-r--r--docs/source/_templates/mg_theme/theme.conf31
-rw-r--r--docs/source/codebase.rst (renamed from docs/codebase.rst)2
-rw-r--r--docs/source/conf.py (renamed from docs/conf.py)29
-rw-r--r--docs/source/contributinghowto.rst (renamed from docs/contributinghowto.rst)6
-rw-r--r--docs/source/deploymenthowto.rst (renamed from docs/deploymenthowto.rst)4
-rw-r--r--docs/source/foreword.rst (renamed from docs/foreword.rst)0
-rw-r--r--docs/source/git.rst (renamed from docs/git.rst)2
-rw-r--r--docs/source/goblin.png (renamed from docs/goblin.png)bin47763 -> 47763 bytes
-rw-r--r--docs/source/index.rst (renamed from docs/index.rst)2
-rw-r--r--docs/source/mediagoblin.rst (renamed from docs/mediagoblin.rst)6
-rw-r--r--docs/source/mgext/__init__.py (renamed from docs/mgext/__init__.py)0
-rw-r--r--docs/source/mgext/youcanhelp.py (renamed from docs/mgext/youcanhelp.py)0
-rw-r--r--docs/source/snugglygoblin.png (renamed from docs/snugglygoblin.png)bin163754 -> 163754 bytes
-rw-r--r--docs/source/theminghowto.rst (renamed from docs/theminghowto.rst)0
-rw-r--r--docs/source/vision.rst (renamed from docs/vision.rst)0
-rw-r--r--extlib/960.gs/960_16_col.css (renamed from mediagoblin/contrib/960_16_col.css)0
-rwxr-xr-xextlib/960.gs/README.txt54
-rw-r--r--extlib/960.gs/reset.css (renamed from mediagoblin/contrib/reset.css)0
-rw-r--r--extlib/960.gs/text.css (renamed from mediagoblin/contrib/text.css)0
-rw-r--r--extlib/README71
-rwxr-xr-xlazyserver.sh14
-rw-r--r--licenses/AGPLv3.txt (renamed from AGPLv3.txt)0
-rw-r--r--licenses/CC0_1.0.txt (renamed from CC0_1.0.txt)0
-rw-r--r--licenses/LGPLv3.txt165
-rwxr-xr-xmaketarball.sh179
-rw-r--r--mediagoblin/app.py17
-rw-r--r--mediagoblin/auth/forms.py16
-rw-r--r--mediagoblin/auth/routing.py6
-rw-r--r--mediagoblin/auth/views.py57
-rw-r--r--mediagoblin/config_spec.ini4
-rw-r--r--mediagoblin/db/indexes.py15
-rw-r--r--mediagoblin/db/models.py2
-rw-r--r--mediagoblin/decorators.py5
-rw-r--r--mediagoblin/edit/__init__.py17
-rw-r--r--mediagoblin/edit/forms.py19
-rw-r--r--mediagoblin/edit/views.py19
-rw-r--r--mediagoblin/gmg_commands/__init__.py4
-rw-r--r--mediagoblin/gmg_commands/users.py16
-rw-r--r--mediagoblin/gmg_commands/wipealldata.py51
-rw-r--r--mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.mobin0 -> 5641 bytes
-rw-r--r--mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po317
-rw-r--r--mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po292
-rw-r--r--mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.mobin0 -> 5708 bytes
-rw-r--r--mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.po312
-rw-r--r--mediagoblin/i18n/fr/LC_MESSAGES/mediagoblin.mobin0 -> 5406 bytes
-rw-r--r--mediagoblin/i18n/fr/LC_MESSAGES/mediagoblin.po305
-rw-r--r--mediagoblin/i18n/nn_NO/LC_MESSAGES/mediagoblin.mobin0 -> 5217 bytes
-rw-r--r--mediagoblin/i18n/nn_NO/LC_MESSAGES/mediagoblin.po308
-rw-r--r--mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.mobin0 -> 5414 bytes
-rw-r--r--mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.po310
-rw-r--r--mediagoblin/i18n/ro/LC_MESSAGES/mediagoblin.mobin0 -> 5629 bytes
-rw-r--r--mediagoblin/i18n/ro/LC_MESSAGES/mediagoblin.po314
-rw-r--r--mediagoblin/i18n/sl/LC_MESSAGES/mediagoblin.mobin0 -> 5487 bytes
-rw-r--r--mediagoblin/i18n/sl/LC_MESSAGES/mediagoblin.po309
-rw-r--r--mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.mobin0 -> 5401 bytes
-rw-r--r--mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.po294
-rw-r--r--mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.mobin0 -> 5461 bytes
-rw-r--r--mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.po311
-rw-r--r--mediagoblin/init/__init__.py27
-rw-r--r--mediagoblin/listings/__init__.py (renamed from mediagoblin/templates/mediagoblin/auth/register_success.html)12
-rw-r--r--mediagoblin/listings/routing.py28
-rw-r--r--mediagoblin/listings/views.py91
-rw-r--r--mediagoblin/mg_globals.py15
-rw-r--r--mediagoblin/process_media/__init__.py13
-rw-r--r--mediagoblin/routing.py3
-rw-r--r--mediagoblin/static/css/base.css243
l---------mediagoblin/static/css/contrib/960_16_col.css1
l---------mediagoblin/static/css/contrib/reset.css1
l---------mediagoblin/static/css/contrib/text.css1
l---------mediagoblin/static/css/extlib/960_16_col.css1
l---------mediagoblin/static/css/extlib/reset.css1
l---------mediagoblin/static/css/extlib/text.css1
-rw-r--r--mediagoblin/static/images/background.pngbin0 -> 6336 bytes
-rw-r--r--mediagoblin/storage.py212
-rw-r--r--mediagoblin/submit/__init__.py17
-rw-r--r--mediagoblin/submit/forms.py10
-rw-r--r--mediagoblin/submit/views.py15
-rw-r--r--mediagoblin/templates/mediagoblin/auth/login.html15
-rw-r--r--mediagoblin/templates/mediagoblin/auth/register.html5
-rw-r--r--mediagoblin/templates/mediagoblin/auth/resent_verification_email.html2
-rw-r--r--mediagoblin/templates/mediagoblin/auth/verification_email.txt10
-rw-r--r--mediagoblin/templates/mediagoblin/base.html66
-rw-r--r--mediagoblin/templates/mediagoblin/edit/edit.html6
-rw-r--r--mediagoblin/templates/mediagoblin/edit/edit_profile.html8
-rw-r--r--mediagoblin/templates/mediagoblin/listings/tag.html43
-rw-r--r--mediagoblin/templates/mediagoblin/root.html16
-rw-r--r--mediagoblin/templates/mediagoblin/submit/start.html5
-rw-r--r--mediagoblin/templates/mediagoblin/user_pages/gallery.html22
-rw-r--r--mediagoblin/templates/mediagoblin/user_pages/media.html42
-rw-r--r--mediagoblin/templates/mediagoblin/user_pages/user.html95
-rw-r--r--mediagoblin/templates/mediagoblin/utils/object_gallery.html7
-rw-r--r--mediagoblin/templates/mediagoblin/utils/tags.html (renamed from mediagoblin/templates/mediagoblin/auth/verification_needed.html)20
-rw-r--r--mediagoblin/templates/mediagoblin/utils/wtforms.html10
-rw-r--r--mediagoblin/tests/test_auth.py21
-rw-r--r--mediagoblin/tests/test_mgoblin_app.ini4
-rw-r--r--mediagoblin/tests/test_submission.py39
-rw-r--r--mediagoblin/tests/test_tags.py50
-rw-r--r--mediagoblin/tests/tools.py3
-rw-r--r--mediagoblin/translations/en/LC_MESSAGES/mediagoblin.mobin502 -> 0 bytes
-rw-r--r--mediagoblin/translations/en/LC_MESSAGES/mediagoblin.po23
-rw-r--r--mediagoblin/user_pages/__init__.py17
-rw-r--r--mediagoblin/user_pages/forms.py9
-rw-r--r--mediagoblin/user_pages/views.py16
-rw-r--r--mediagoblin/util.py125
-rw-r--r--mediagoblin/views.py16
-rw-r--r--setup.py41
-rw-r--r--user_profile-fix_wrong_nesting.patch17
119 files changed, 5317 insertions, 1161 deletions
diff --git a/.gitignore b/.gitignore
index f5c2ba31..9da56bab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,7 +10,8 @@ mediagoblin.egg-info
*.pyo
docs/_build/
user_dev/
-mediagoblin_user.ini
+paste_local.ini
+mediagoblin_local.ini
server-log.txt
*~
*.swp
diff --git a/.tx/config b/.tx/config
new file mode 100644
index 00000000..711b5d94
--- /dev/null
+++ b/.tx/config
@@ -0,0 +1,8 @@
+[mediagoblin.mediagoblin]
+file_filter = mediagoblin/i18n/<lang>/LC_MESSAGES/mediagoblin.po
+source_file = mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po
+source_lang = en
+
+[main]
+host = https://www.transifex.net
+
diff --git a/COPYING b/COPYING
index fc930ccb..6895d3b8 100644
--- a/COPYING
+++ b/COPYING
@@ -2,30 +2,56 @@
COPYING
=========
-GNU MediaGoblin is composed of the following kinds of files:
+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.
-* software files: Python, JavaScript and HTML templates
-* non-software data: CSS, images, and video
-* documentation
+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, in the file ``licenses/AGPLv3.txt``.
+If not, see <http://www.gnu.org/licenses/>.
-Software files
-==============
-Python, JavaScript, and template files files are released under the
-AGPL v3. The text of this license is located in ``AGPLv3.txt``.
+JavaScript files located in the ``mediagoblin/`` directory tree of
+are free software: you can redistribute and/or modify them under the
+terms of the GNU Lesser General Public License as published by the
+Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+You should have received a copy of the GNU Lesser General Public
+License along with this program, in the file ``licenses/LGPLv3.txt``.
+If not, see <http://www.gnu.org/licenses/>.
-Non-software data
-=================
-CSS, images and video are all released under a CC0 license. The text
-of this license is located in ``CC0_1.0.txt``.
+Documentation files located in the ``docs/`` directory tree are released
+under a CC0 license. To the extent possible under law, the author(s)
+have dedicated all copyright and related and neighboring rights to these
+files to the public domain worldwide. These files are distributed
+without any warranty.
+You should have received a copy of the CC0 license in the file
+``licenses/CC0_1.0.txt``. If not, see
+<http://creativecommons.org/publicdomain/zero/1.0/>.
-Documentation
-=============
-All documentation is under the ``docs/`` directory. These materials
-are all released under a CC0 license. The text of this license is
-located in ``CC0_1.0.txt``.
+CSS, images and video located in the ``mediagoblin/`` directory tree are
+released under a CC0 license. To the extent possible under law, the author(s)
+have dedicated all copyright and related and neighboring rights to these
+files to the public domain worldwide. These files are distributed without
+any warranty.
+
+You should have received a copy of the CC0 license in the file
+``licenses/CC0_1.0.txt``. If not, see
+<http://creativecommons.org/publicdomain/zero/1.0/>.
+
+
+Additional library software has been made available in the ``extlib/``
+directory. All of it is Free Software and can be distributed under
+liberal terms, but those terms may differ in detail from the AGPL's
+particulars. See each package's license file in the extlib directory
+for additional terms.
diff --git a/README b/README
index 2ef6f78e..0aba179b 100644
--- a/README
+++ b/README
@@ -12,7 +12,7 @@ What is GNU MediaGoblin?
* Federated with OStatus!
* Customizable!
* A place for people to collaborate and show off original and derived
- creations. Free, as in freedom. We’re a GNU project in the making,
+ creations. Free, as in freedom. We’re a GNU project in the making,
afterall.
@@ -33,6 +33,10 @@ hang out, see `our Join page <http://mediagoblin.org/join/>`_
Where is the documentation?
===========================
-Documentation is located in the ``docs/`` directory in a "raw"
-restructured-text form. It is also mirrored at
-http://docs.mediagoblin.org/ in HTML form.
+The beginnings of a user manual is located in the ``docs/`` directory
+in HTML, Texinfo, and source (Restructured Text) forms. It's also
+available online at http://docs.mediagoblin.org/ in HTML form.
+
+Contributor/developer documentation as well as documentation on the
+project processes and infrastructure is located on
+`the wiki <http://wiki.mediagoblin.org/>`_.
diff --git a/babel.ini b/babel.ini
index 666270df..a4e3267a 100644
--- a/babel.ini
+++ b/babel.ini
@@ -10,4 +10,4 @@ encoding = utf-8
# # Extraction from JavaScript files
# [javascript: mediagoblin/static/js/**.js]
-# extract_messages = $._, jQuery._ \ No newline at end of file
+# extract_messages = $._, jQuery._
diff --git a/destroy_environment.py b/destroy_environment.py
deleted file mode 100755
index bbdeffe9..00000000
--- a/destroy_environment.py
+++ /dev/null
@@ -1,22 +0,0 @@
-#!./bin/python
-
-import pymongo
-import sys, os
-
-print "*** WARNING! ***"
-print " Running this will destroy your mediagoblin database,"
-print " remove all your media files in user_dev/, etc."
-
-drop_it = raw_input(
- 'Are you SURE you want to destroy your environment? (if so, type "yes")> ')
-
-if not drop_it == 'yes':
- sys.exit(1)
-
-conn = pymongo.Connection()
-conn.drop_database('mediagoblin')
-
-os.popen('rm -rf user_dev/media')
-os.popen('rm -rf user_dev/beaker')
-
-print "removed all your stuff! okay, now re-run ./bin/buildout"
diff --git a/docs/Makefile b/docs/Makefile
index 81fc3d13..0b97bf7c 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -5,14 +5,16 @@
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
-BUILDDIR = _build
+BUILDDIR = build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
-.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@@ -32,6 +34,9 @@ help:
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
clean:
-rm -rf $(BUILDDIR)/*
@@ -113,6 +118,24 @@ man:
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
diff --git a/docs/designdecisions.rst b/docs/designdecisions.rst
deleted file mode 100644
index afa1e26b..00000000
--- a/docs/designdecisions.rst
+++ /dev/null
@@ -1,329 +0,0 @@
-.. _design-decisions-chapter:
-
-==================
- Design Decisions
-==================
-
-.. contents:: Sections
- :local:
-
-
-This chapter talks a bit about design decisions.
-
-
-Why GNU MediaGoblin?
-====================
-
-Chris and Will on "Why GNU MediaGoblin":
-
- Chris came up with the name MediaGoblin. The name is pretty fun.
- It merges the idea that this is a Media hosting project with
- Goblin which sort of sounds like gobbling. Here's a piece of
- software that gobbles up your media for all to see.
-
- `According to Wikipedia <http://en.wikipedia.org/wiki/Goblin>`_, a
- goblin is:
-
- a legendary evil or mischievous illiterate creature, described
- as grotesquely evil or evil-like phantom
-
- So are we evil? No. Are we mischievous or illiterate? Not
- really. So what kind of goblin are we thinking about? We're
- thinking about these goblins:
-
- .. figure:: goblin.png
- :alt: Cute goblin with a beret.
-
- *Figure 1: Cute goblin with a beret. llustrated by Chris
- Webber*
-
- .. figure:: snugglygoblin.png
- :scale: 50%
- :alt: Snuggly goblin with a beret.
-
- *Figure 2: Snuggly goblin. Illustrated by Karen Rustad*
-
- Those are pretty cute goblins. Those are the kinds of goblins
- we're thinking about.
-
- Chris started doing work on the project after thinking about it
- for a year. Then, after talking with Matt and Rob, it became an
- official GNU project. Thus we now call it GNU MediaGoblin.
-
- That's a lot of letters, though, so in the interest of brevity and
- facilitating easier casual conversation and balancing that with
- what's important to us, we have the following rules:
-
- 1. "GNU MediaGoblin" is the name we're going to use in all official
- capacities: web site, documentation, press releases, ...
-
- 2. In casual conversation, it's ok to use more casual names.
-
- 3. If you're writing about the project, we ask that you call it GNU
- MediaGoblin.
-
- 4. If you don't like the name, we kindly ask you to take a deep
- breath, think a happy thought about cute little goblins playing
- on a playground and taking cute pictures of themselves, and let
- it go. (Will added this one.)
-
-
-Why Python
-==========
-
-Chris Webber on "Why Python":
-
- Because I know Python, love Python, am capable of actually making
- this thing happen in Python (I've worked on a lot of large free
- software web applications before in Python, including `Miro
- Community`_, the `Miro Guide`_, a large portion of `Creative
- Commons`_, and a whole bunch of things while working at `Imaginary
- Landscape`_). Me starting a project like this makes sense if it's
- done in Python.
-
- You might say that PHP is way more deployable, that Rails has way
- more cool developers riding around on fixie bikes---and all of
- those things are true. But I know Python, like Python, and think
- that Python is pretty great. I do think that deployment in Python
- is not as good as with PHP, but I think the days of shared hosting
- are (thankfully) coming to an end, and will probably be replaced
- by cheap virtual machines spun up on the fly for people who want
- that sort of stuff, and Python will be a huge part of that future,
- maybe even more than PHP will. The deployment tools are getting
- better. Maybe we can use something like Silver Lining. Maybe we
- can just distribute as ``.debs`` or ``.rpms``. We'll figure it
- out when we get there.
-
- Regardless, if I'm starting this project, which I am, it's gonna
- be in Python.
-
-.. _Miro Community: http://mirocommunity.org/
-.. _Miro Guide: http://miroguide.org/
-.. _Creative Commons: http://creativecommons.org/
-.. _Imaginary Landscape: http://www.imagescape.com/
-
-
-Why WSGI Minimalism
-===================
-
-Chris Webber on "Why WSGI Minimalism":
-
- If you notice in the technology list I list a lot of components
- that are very "django-like", but not actually `Django`_
- components. What can I say, I really like a lot of the ideas in
- Django! Which leads to the question: why not just use Django?
-
- While I really like Django's ideas and a lot of its components, I
- also feel that most of the best ideas in Django I want have been
- implemented as good or even better outside of Django. I could
- just use Django and replace the templating system with Jinja2, and
- the form system with wtforms, and the database with MongoDB and
- MongoKit, but at that point, how much of Django is really left?
-
- I also am sometimes saddened and irritated by how coupled all of
- Django's components are. Loosely coupled yes, but still coupled.
- WSGI has done a good job of providing a base layer for running
- applications on and if you know how to do it yourself [1]_, it's
- not hard or many lines of code at all to bind them together
- without any framework at all (not even say `Pylons`_, `Pyramid`_
- or `Flask`_ which I think are still great projects, especially for
- people who want this sort of thing but have no idea how to get
- started). And even at this already really early stage of writing
- MediaGoblin, that glue work is mostly done.
-
- Not to say I don't think Django isn't great for a lot of things.
- For a lot of stuff, it's still the best, but not for MediaGoblin,
- I think.
-
- One thing that Django does super well though is documentation. It
- still has some faults, but even with those considered I can hardly
- think of any other project in Python that has as nice of
- documentation as Django. It may be worth learning some lessons on
- documentation from Django [2]_, on that note.
-
- I'd really like to have a good, thorough hacking-howto and
- deployment-howto, especially in the former making some notes on
- how to make it easier for Django hackers to get started.
-
-.. _Django: http://www.djangoproject.com/
-.. _Pylons: http://pylonshq.com/
-.. _Pyramid: http://docs.pylonsproject.org/projects/pyramid/dev/
-.. _Flask: http://flask.pocoo.org/
-
-.. [1] http://pythonpaste.org/webob/do-it-yourself.html
-.. [2] http://pycon.blip.tv/file/4881071/
-
-
-Why MongoDB
-===========
-
-Chris Webber on "Why MongoDB":
-
- In case you were wondering, I am not a NOSQL fanboy, I do not go
- around telling people that MongoDB is web scale. Actually my
- choice for MongoDB isn't scalability, though scaling up really
- nicely is a pretty good feature and sets us up well in case large
- volume sites eventually do use MediaGoblin. But there's another
- side of scalability, and that's scaling down, which is important
- for federation, maybe even more important than scaling up in an
- ideal universe where everyone ran servers out of their own
- housing. As a memory-mapped database, MongoDB is pretty hungry,
- so actually I spent a lot of time debating whether the inability
- to scale down as nicely as something like SQL has with sqlite
- meant that it was out.
-
- But I decided in the end that I really want MongoDB, not for
- scalability, but for flexibility. Schema evolution pains in SQL
- are almost enough reason for me to want MongoDB, but not quite.
- The real reason is because I want the ability to eventually handle
- multiple media types through MediaGoblin, and also allow for
- plugins, without the rigidity of tables making that difficult. In
- other words, something like::
-
- {"title": "Me talking until you are bored",
- "description": "blah blah blah",
- "media_type": "audio",
- "media_data": {
- "length": "2:30",
- "codec": "OGG Vorbis"},
- "plugin_data": {
- "licensing": {
- "license": "http://creativecommons.org/licenses/by-sa/3.0/"}}}
-
-
- Being able to just dump media-specific information in a media_data
- hashtable is pretty great, and even better is having a plugin
- system where you can just let plugins have their own entire
- key-value space cleanly inside the document that doesn't interfere
- with anyone else's stuff. If we were to let plugins to deposit
- their own information inside the database, either we'd let plugins
- create their own tables which makes SQL migrations even harder
- than they already are, or we'd probably end up creating a table
- with a column for key, a column for value, and a column for type
- in one huge table called "plugin_data" or something similar. (Yo
- dawg, I heard you liked plugins, so I put a database in your
- database so you can query while you query.) Gross.
-
- I also don't want things to be too loose so that we forget or lose
- the structure of things, and that's one reason why I want to use
- MongoKit, because we can cleanly define a much structure as we
- want and verify that documents match that structure generally
- without adding too much bloat or overhead (MongoKit is a pretty
- lightweight wrapper and doesn't inject extra MongoKit-specific
- stuff into the database, which is nice and nicer than many other
- ORMs in that way).
-
-
-Why Sphinx for documentation
-============================
-
-Will Kahn-Greene on "Why Sphinx":
-
- `Sphinx`_ is a fantastic tool for organizing documentation for a
- Python-based project that makes it pretty easy to write docs that
- are readable in source form and can be "compiled" into HTML, LaTeX
- and other formats.
-
- There are other doc systems out there, but given that GNU
- MediaGoblin is being written in Python and I've done a ton of
- documentation using Sphinx, it makes sense to use Sphinx for now.
-
-.. _Sphinx: http://sphinx.pocoo.org/
-
-
-Why AGPLv3 and CC0?
-===================
-
-Chris, Brett, Will, Rob, Matt, et al curated into a story where
-everyone is the hero by Will on "Why AGPLv3 and CC0":
-
- The `AGPL v3`_ preserves the freedoms guaranteed by the GPL v3 in
- the context of software as a service. Using this license ensures
- that users of the service have the ability to examine the source,
- deploy their own instance, and implement their own version. This
- is really important to us and a core mission component of this
- project. Thus we decided that the software parts should be under
- this license.
-
- However, the project is made up of more than just software:
- there's CSS, images, and other output-related things. We wanted
- the templates/images/css side of the project all permissive and
- permissive in the same absolutely permissive way. We're waiving
- our copyrights to non-software things under the CC0 waiver.
-
- That brings us to the templates where there's some code and some
- output. The template engine we're using is called Jinja2. It
- mixes HTML markup with Python code to render the output of the
- software. We decided the templates are part of the output of the
- software and not the software itself. We wanted the output of the
- software to be licensed in a hassle-free way so that when someone
- deploys their own GNU MediaGoblin instance with their own
- templates, they don't have to deal with the copyleft aspects of
- the AGPLv3 and we'd be fine with that because the changes they're
- making are identity-related. So at first we decided to waive our
- copyrights to the templates with a CC0 waiver and then add an
- exception to the AGPLv3 for the software such that the templates
- can make calls into the software and yet be a separately licensed
- work. However, Brett brought up the question of whether this
- allows some unscrupulous person to make changes to the software
- through the templates in such a way that they're not bound by the
- AGPLv3: i.e. a loophole. We thought about this loophole and
- between this and the extra legalese involved in the exception to
- the AGPLv3, we decided that it's just way simpler if the templates
- were also licensed under the AGPLv3.
-
- Then we have the licensing for the documentation. Given that the
- documentation is tied to the software content-wise, we don't feel
- like we have to worry about ensuring freedom of the documentation
- or worry about attribution concerns. Thus we're waiving our
- copyrights to the documentation under CC0 as well.
-
- Lastly, we have branding. This covers logos and other things that
- are distinctive to GNU MediaGoblin that we feel represents this
- project. Since we don't currently have any branding, this is an
- open issue, but we're thinking we'll go with a CC BY-SA license.
-
- By licensing in this way, we make sure that users of the software
- receive the freedoms that the AGPLv3 ensures regardless of what
- fate befalls this project.
-
- So to summarize:
-
- * software (Python, JavaScript, HTML templates): licensed
- under AGPLv3
- * non-software things (CSS, images, video): copyrights waived
- under CC0 because this is output of the software
- * documentation: copyrights waived under CC0 because it's not part
- of the software
- * branding assets: we're kicking this can down the road, but
- probably CC BY-SA
-
- This is all codified in the ``COPYING`` file.
-
-.. _AGPL v3: http://www.gnu.org/licenses/agpl.html
-.. _CC0 v1: http://creativecommons.org/publicdomain/zero/1.0/
-
-
-Why (non-mandatory) copyright assignment?
-=========================================
-
-Chris Webber on "Why copyright assignment?":
-
- GNU MediaGoblin is a GNU project with non-mandatory but heavily
- encouraged copyright assignment to the FSF. Most, if not all, of
- the core contributors to GNU MediaGoblin will have done a
- copyright assignment, but unlike some other GNU projects, it isn't
- required here. We think this is the best choice for GNU
- MediaGoblin: it ensures that the Free Software Foundation may
- protect the software by enforcing the AGPL if the FSF sees fit,
- but it also means that we can immediately merge in changes from a
- new contributor. It also means that some significant non-FSF
- contributors might also be able to enforce the AGPL if seen fit.
-
- Again, assignment is not mandatory, but it is heavily encouraged,
- even incentivized: significant contributors who do a copyright
- assignment to the FSF are eligible to have a unique goblin drawing
- produced for them by the project's main founder, Christopher Allan
- Webber. See :ref:`contributing-howto-chapter` for details.
-
-
diff --git a/docs/hackinghowto.rst b/docs/hackinghowto.rst
deleted file mode 100644
index caafba53..00000000
--- a/docs/hackinghowto.rst
+++ /dev/null
@@ -1,345 +0,0 @@
-.. _hacking-howto:
-
-===============
- Hacking HOWTO
-===============
-
-.. contents:: Sections
- :local:
-
-
-So you want to hack on GNU MediaGoblin?
-=======================================
-
-First thing to do is check out the `Web site
-<http://mediagoblin.org/join/>`_ where we list all the project
-infrastructure including:
-
-* the IRC channel
-* the mailing list
-* the issue tracker
-
-Additionally, we have information on how to get involved, who to talk
-to, what needs to be worked on, and other things besides!
-
-Second thing to do is take a look at :ref:`codebase-chapter` where
-we've started documenting how GNU MediaGoblin is built and how to add
-new things.
-
-Third you'll need to :ref:`get the requirements
-<get-requirements-section>`.
-
-Fourth, you'll need to build a development environment. We use buildout,
-but if you want to use virtualenv, there's a set of mediocre not-very-supported
-steps in the `wiki <https://gitorious.org/mediagoblin/pages/Home>`_.
-
-
-.. _get-requirements-section:
-
-Getting requirements
-====================
-
-First, you need to have the following installed before you can build
-an environment for hacking on GNU MediaGoblin:
-
-* Python 2.6 or 2.7 - http://www.python.org/
-
- You'll need Python as well as the dev files for building modules.
-
-* python-lxml - http://lxml.de/
-* git - http://git-scm.com/
-* MongoDB - http://www.mongodb.org/
-
-If you're running Debian GNU/Linux or a Debian-derived distribution
-such as Mint or Ubuntu, running the following should install these
-requirements::
-
- sudo apt-get install mongodb git-core python python-dev \
- python-lxml
-
-On Fedora::
-
- yum install mongodb-server python-paste-deploy python-paste-script \
- git-core python python-devel python-lxml
-
-.. YouCanHelp::
-
- If you have instructions for other GNU/Linux distributions to set
- up requirements, let us know!
-
-
-.. _hacking-with-buildout:
-
-
-How to set up and maintain an environment for hacking with buildout
-===================================================================
-
-**Requirements**
-
-No additional requirements.
-
-
-**Create a development environment**
-
-After installing the requirements, follow these steps:
-
-1. Clone the repository::
-
- git clone git://gitorious.org/mediagoblin/mediagoblin.git
-
-2. Bootstrap and run buildout::
-
- cd mediagoblin
- python bootstrap.py && ./bin/buildout
-
-
-That's it! Using this method, buildout should create a ``user_dev``
-directory, in which certain things will be stored (media, beaker
-session stuff, etc). You can change this, but for development
-purposes this default should be fine.
-
-
-**Updating for dependency changes**
-
-While hacking on GNU MediaGoblin over time, you'll eventually have to
-update your development environment because the dependencies have
-changed. To do that, run::
-
- ./bin/buildout && ./bin/gmg migrate
-
-
-**Updating for code changes**
-
-You don't need to do anything---code changes are automatically
-available.
-
-
-**Deleting your buildout**
-
-At some point, you may want to delete your buildout. Perhaps it's to
-start over. Perhaps it's to test building development environments
-with buildout.
-
-To do this, do::
-
- rm -rf bin develop-eggs eggs mediagoblin.egg-info parts user_dev
-
-
-Running the server
-==================
-
-If you want to get things running quickly and without hassle, just
-run::
-
- ./lazyserver.sh
-
-This will start up a python server where you can begin playing with
-mediagoblin. It will also run celery in "always eager" mode so you
-don't have to start a separate process for it.
-
-This is fine in development, but if you want to actually run celery
-separately for testing (or deployment purposes), you'll want to run
-the server independently::
-
- ./bin/paster serve paste.ini --reload
-
-
-Running celeryd
-===============
-
-If you aren't using ./lazyserver.sh or otherwise aren't running celery
-in always eager mode, you'll need to do this if you want your media to
-process and actually show up. It's probably a good idea in
-development to have the web server (above) running in one terminal and
-celeryd in another window.
-
-Run::
-
- CELERY_CONFIG_MODULE=mediagoblin.init.celery.from_celery ./bin/celeryd
-
-
-Running the test suite
-======================
-
-Run::
-
- ./runtests.sh
-
-
-Running a shell
-===============
-
-If you want a shell with your database pre-setup and an instantiated
-application ready and at your fingertips....
-
-Run::
-
- ./bin/gmg shell
-
-
-Troubleshooting
-===============
-
-pymongo.errors.AutoReconnect: could not find master/primary
------------------------------------------------------------
-
-If you see this::
-
- pymongo.errors.AutoReconnect: could not find master/primary
-
-then make sure mongodb is installed and running.
-
-If it's installed, check the mongodb log. On my machine, that's
-``/var/log/mongodb/mongodb.log``. If you see something like::
-
- old lock file: /var/lib/mongodb/mongod.lock. probably means...
-
-in that case you might have had an unclean shutdown. Try::
-
- sudo mongod --repair
-
-If that didn't work, just delete the lock file and relaunch mongodb.
-
-Anyway, then start the mongodb server in whatever way is appropriate
-for your distro / OS.
-
-
-pkg_resources.DistributionNotFound: distribute
-----------------------------------------------
-
-If you get this while running buildout::
-
- pkg_resources.DistributionNotFound: distribute
-
-Try this commmand instead::
-
- python bootstrap.py --distribute && ./bin/buildout
-
-
-Wiping your user data
-=====================
-
-.. Note::
-
- Unless you're doing development and working on and testing creating
- a new instance, you will probably never have to do this. Will
- plans to do this work and thus he documented it.
-
-.. YouCanHelp::
-
- If you're familiar with MongoDB, we'd love to get a `script that
- removes all the GNU MediaGoblin data from an existing instance
- <http://bugs.foocorp.net/issues/296>`_. Let us know!
-
-
-Quickstart for Django programmers
-=================================
-
-We're not using Django, but the codebase is very Django-like in its
-structure.
-
-* ``routing.py`` is like ``urls.py`` in Django
-* ``models.py`` has mongokit ORM definitions
-* ``views.py`` is where the views go
-
-We're using MongoDB. Basically, instead of a relational database with
-tables, you have a big JSON structure which acts a lot like a Python
-dict.
-
-
-.. YouCanHelp::
-
- If there are other things that you think would help orient someone
- new to GNU MediaGoblin but coming from Django, let us know!
-
-
-Bite-sized bugs to start with
-=============================
-
-**May 3rd, 2011**: We don't have a list of bite-sized bugs, yet, but
-this is important to us. If you're interested in things to work on,
-let us know on `the mailing list <http://mediagoblin.org/join/>`_ or
-on the `IRC channel <http://mediagoblin.org/join/>`_.
-
-
-Tips for people new to coding
-=============================
-
-Learning Python
----------------
-
-GNU MediaGoblin is written using a programming language called `Python
-<http://python.org/>`_.
-
-There are two different incompatible iterations of Python which I'll
-refer to as Python 2 and Python 3. GNU MediaGoblin is written in
-Python 2 and requires Python 2.6 or 2.7. At some point, we might
-switch to Python 3, but that's a future thing.
-
-You can learn how to code in Python 2 from several excellent books
-that are freely available on the Internet:
-
-* `Learn Python the Hard Way <http://learnpythonthehardway.org/>`_
-* `Dive Into Pyton <http://diveintopython.org/>`_
-* `Python for Software Design <http://www.greenteapress.com/thinkpython/>`_
-* `A Byte of Python <http://www.swaroopch.com/notes/Python>`_
-
-These are all excellent texts.
-
-.. YouCanHelp::
-
- If you know of other good quality Python tutorials and Python
- tutorial videos, let us know!
-
-
-Learning Libraries GNU MediaGoblin uses
----------------------------------------
-
-GNU MediaGoblin uses a variety of libraries in order to do what it
-does. These libraries are listed in the :ref:`codebase-chapter`
-along with links to the project Web sites and documentation for the
-libraries.
-
-There are a variety of Python-related conferences every year that have
-sessions covering many aspects of these libraries. You can find them
-at `Python Miro Community <http://python.mirocommunity.org>`_ [0]_.
-
-.. [0] This is a shameless plug. Will Kahn-Greene runs Python Miro
- Community.
-
-If you have questions or need help, find us on the mailing list and on
-IRC.
-
-
-.. _hacking-howto-git:
-
-Learning git
-------------
-
-git is an interesting and very powerful tool. Like all powerful
-tools, it has a learning curve.
-
-If you're new to git, we highly recommend the following resources for
-getting the hang of it:
-
-* `Learn Git <http://learn.github.com/p/intro.html>`_ --- the GitHub
- intro to git
-* `Pro Git <http://progit.org/book/>`_ --- fantastic book
-* `Git casts <http://gitcasts.com/>`_ --- screencast covering git
- usage
-* `Git Reference <http://gitref.org/>`_ --- Git reference that makes
- it easier to get the hang of git if you're coming from other version
- control systems
-
-There's also a git mission at `OpenHatch <http://openhatch.org/>`_.
-
-
-Learning other utilities
-------------------------
-
-The `OpenHatch <http://openhatch.org/>`_ site has a series of
-`training missions <http://openhatch.org/missions/>`_ which are
-designed to help you learn how to use these tools.
-
-If you're new to tar, diff, patch and git, we highly recommend you sign
-up with OpenHatch and do the missions.
diff --git a/docs/_static/placeholder b/docs/source/_static/placeholder
index e69de29b..e69de29b 100644
--- a/docs/_static/placeholder
+++ b/docs/source/_static/placeholder
diff --git a/docs/source/_templates/mg_theme/layout.html b/docs/source/_templates/mg_theme/layout.html
new file mode 100644
index 00000000..eccda14b
--- /dev/null
+++ b/docs/source/_templates/mg_theme/layout.html
@@ -0,0 +1,39 @@
+{#
+ default/layout.html
+ ~~~~~~~~~~~~~~~~~~~
+
+ Sphinx layout template for the default theme.
+
+ :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+#}
+{% extends "basic/layout.html" %}
+
+{% if theme_collapsiblesidebar|tobool %}
+{% set script_files = script_files + ['_static/sidebar.js'] %}
+{% endif %}
+
+{%- block footer %}
+ <div class="footer">
+ <div>
+ {% trans path=pathto('copyright'), copyright=copyright|e %}&copy; <a href="{{ path }}">Copyright</a> {{ copyright }}.{% endtrans %}
+ {%- if last_updated %}
+ {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}
+ {%- endif %}
+ {% trans sphinx_version=sphinx_version|e %}Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> {{ sphinx_version }}.{% endtrans %}
+ </div>
+<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by-sa/3.0/88x31.png" /></a><br /><span xmlns:dc="http://purl.org/dc/elements/1.1/" href="http://purl.org/dc/dcmitype/Text" property="dc:title" rel="dc:type">{{ shorttitle|e }}</span> by <a xmlns:cc="http://creativecommons.org/ns#" href="http://bluesock.org/~willg/" property="cc:attributionName" rel="cc:attributionURL">Will Kahn-Greene</a> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-ShareAlike 3.0 Unported License</a>.<br />
+ </div>
+
+<script type="text/javascript">
+ var _gaq = _gaq || [];
+ _gaq.push(['_setAccount', 'UA-163840-8']);
+ _gaq.push(['_trackPageview']);
+
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+ })();
+</script>
+{%- endblock %}
diff --git a/docs/source/_templates/mg_theme/static/default.css_t b/docs/source/_templates/mg_theme/static/default.css_t
new file mode 100644
index 00000000..f200a0fe
--- /dev/null
+++ b/docs/source/_templates/mg_theme/static/default.css_t
@@ -0,0 +1,299 @@
+/*
+ * default.css_t
+ * ~~~~~~~~~~~~~
+ *
+ * Sphinx stylesheet -- default theme.
+ *
+ * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+@import url("basic.css");
+
+/* -- page layout ----------------------------------------------------------- */
+
+body {
+ font-family: {{ theme_bodyfont }};
+ font-size: 100%;
+ background-color: {{ theme_footerbgcolor }};
+ color: #000;
+ margin: 0;
+ padding: 0;
+}
+
+div.document {
+ background-color: {{ theme_sidebarbgcolor }};
+}
+
+div.documentwrapper {
+ float: left;
+ width: 100%;
+}
+
+div.bodywrapper {
+ margin: 0 0 0 230px;
+}
+
+div.body {
+ background-color: {{ theme_bgcolor }};
+ color: {{ theme_textcolor }};
+ padding: 0 20px 30px 20px;
+}
+
+{%- if theme_rightsidebar|tobool %}
+div.bodywrapper {
+ margin: 0 230px 0 0;
+}
+{%- endif %}
+
+div.footer {
+ color: {{ theme_footertextcolor }};
+ width: 100%;
+ padding: 9px 0 9px 0;
+ text-align: center;
+ font-size: 75%;
+}
+
+div.footer a {
+ color: {{ theme_footertextcolor }};
+ text-decoration: underline;
+}
+
+div.related {
+ background-color: {{ theme_relbarbgcolor }};
+ line-height: 30px;
+ color: {{ theme_relbartextcolor }};
+}
+
+div.related a {
+ color: {{ theme_relbarlinkcolor }};
+}
+
+div.sphinxsidebar {
+ {%- if theme_stickysidebar|tobool %}
+ top: 30px;
+ bottom: 0;
+ margin: 0;
+ position: fixed;
+ overflow: auto;
+ height: auto;
+ {%- endif %}
+ {%- if theme_rightsidebar|tobool %}
+ float: right;
+ {%- if theme_stickysidebar|tobool %}
+ right: 0;
+ {%- endif %}
+ {%- endif %}
+}
+
+{%- if theme_stickysidebar|tobool %}
+/* this is nice, but it it leads to hidden headings when jumping
+ to an anchor */
+/*
+div.related {
+ position: fixed;
+}
+
+div.documentwrapper {
+ margin-top: 30px;
+}
+*/
+{%- endif %}
+
+div.sphinxsidebar h3 {
+ font-family: {{ theme_headfont }};
+ color: {{ theme_sidebartextcolor }};
+ font-size: 1.4em;
+ font-weight: normal;
+ margin: 0;
+ padding: 0;
+}
+
+div.sphinxsidebar h3 a {
+ color: {{ theme_sidebartextcolor }};
+}
+
+div.sphinxsidebar h4 {
+ font-family: {{ theme_headfont }};
+ color: {{ theme_sidebartextcolor }};
+ font-size: 1.3em;
+ font-weight: normal;
+ margin: 5px 0 0 0;
+ padding: 0;
+}
+
+div.sphinxsidebar p {
+ color: {{ theme_sidebartextcolor }};
+}
+
+div.sphinxsidebar p.topless {
+ margin: 5px 10px 10px 10px;
+}
+
+div.sphinxsidebar ul {
+ margin: 10px;
+ padding: 0;
+ color: {{ theme_sidebartextcolor }};
+}
+
+div.sphinxsidebar a {
+ color: {{ theme_sidebarlinkcolor }};
+}
+
+div.sphinxsidebar input {
+ border: 1px solid {{ theme_sidebarlinkcolor }};
+ font-family: sans-serif;
+ font-size: 1em;
+}
+
+
+/* -- hyperlink styles ------------------------------------------------------ */
+
+a {
+ color: {{ theme_linkcolor }};
+ text-decoration: none;
+}
+
+a:visited {
+ color: {{ theme_visitedlinkcolor }};
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+{% if theme_externalrefs|tobool %}
+a.external {
+ text-decoration: none;
+ border-bottom: 1px dashed {{ theme_linkcolor }};
+}
+
+a.external:hover {
+ text-decoration: none;
+ border-bottom: none;
+}
+{% endif %}
+
+/* -- body styles ----------------------------------------------------------- */
+
+div.body h1,
+div.body h2,
+div.body h3,
+div.body h4,
+div.body h5,
+div.body h6 {
+ font-family: {{ theme_headfont }};
+ background-color: {{ theme_headbgcolor }};
+ font-weight: normal;
+ color: {{ theme_headtextcolor }};
+ border-bottom: 1px solid #ccc;
+ margin: 20px -20px 10px -20px;
+ padding: 3px 0 3px 10px;
+}
+
+div.body h1 { margin-top: 0; font-size: 200%; }
+div.body h2 { font-size: 160%; }
+div.body h3 { font-size: 140%; }
+div.body h4 { font-size: 120%; }
+div.body h5 { font-size: 110%; }
+div.body h6 { font-size: 100%; }
+
+a.headerlink {
+ color: {{ theme_headlinkcolor }};
+ font-size: 0.8em;
+ padding: 0 4px 0 4px;
+ text-decoration: none;
+}
+
+a.headerlink:hover {
+ background-color: {{ theme_headlinkcolor }};
+ color: white;
+}
+
+div.body p, div.body dd, div.body li {
+ text-align: justify;
+ line-height: 130%;
+}
+
+div.admonition p.admonition-title + p {
+ display: inline;
+}
+
+div.admonition p {
+ margin-bottom: 5px;
+}
+
+div.admonition pre {
+ margin-bottom: 5px;
+}
+
+div.admonition ul, div.admonition ol {
+ margin-bottom: 5px;
+}
+
+div.note {
+ background-color: #eee;
+ border: 1px solid #ccc;
+}
+
+div.seealso {
+ background-color: #ffc;
+ border: 1px solid #ff6;
+}
+
+div.topic {
+ background-color: #eee;
+}
+
+div.warning {
+ background-color: #ffe4e4;
+ border: 1px solid #f66;
+}
+
+p.admonition-title {
+ display: inline;
+}
+
+p.admonition-title:after {
+ content: ":";
+}
+
+pre {
+ padding: 5px;
+ background-color: {{ theme_codebgcolor }};
+ color: {{ theme_codetextcolor }};
+ line-height: 120%;
+ border: 1px solid #ac9;
+ border-left: none;
+ border-right: none;
+}
+
+tt {
+ background-color: #ecf0f3;
+ padding: 0 1px 0 1px;
+ font-size: 0.95em;
+}
+
+th {
+ background-color: #ede;
+}
+
+.warning tt {
+ background: #efc2c2;
+}
+
+.note tt {
+ background: #d6d6d6;
+}
+
+.viewcode-back {
+ font-family: {{ theme_bodyfont }};
+}
+
+div.viewcode-block:target {
+ background-color: #f4debf;
+ border-top: 1px solid #ac9;
+ border-bottom: 1px solid #ac9;
+}
diff --git a/docs/source/_templates/mg_theme/theme.conf b/docs/source/_templates/mg_theme/theme.conf
new file mode 100644
index 00000000..49442e3b
--- /dev/null
+++ b/docs/source/_templates/mg_theme/theme.conf
@@ -0,0 +1,31 @@
+[theme]
+inherit = basic
+stylesheet = default.css
+pygments_style = sphinx
+
+[options]
+rightsidebar = false
+stickysidebar = false
+collapsiblesidebar = false
+externalrefs = false
+
+footerbgcolor = #b11818
+footertextcolor = #ffffff
+sidebarbgcolor = #6a0000
+sidebartextcolor = #ffffff
+sidebarlinkcolor = #98dbcc
+relbarbgcolor = #b11818
+relbartextcolor = #ffffff
+relbarlinkcolor = #ffffff
+bgcolor = #ffffff
+textcolor = #000000
+headbgcolor = #fdeded
+headtextcolor = #20435c
+headlinkcolor = #c60f0f
+linkcolor = #355f7c
+visitedlinkcolor = #355f7c
+codebgcolor = #eeffcc
+codetextcolor = #333333
+
+bodyfont = sans-serif
+headfont = 'Trebuchet MS', sans-serif
diff --git a/docs/codebase.rst b/docs/source/codebase.rst
index 898eadfe..ba5f1e46 100644
--- a/docs/codebase.rst
+++ b/docs/source/codebase.rst
@@ -21,7 +21,7 @@ various recipes for getting things done.
for where we hang out.
For more information on how to get started hacking on GNU MediaGoblin,
-see :ref:`hacking-howto`.
+see `the wiki <http://wiki.mediagoblin.org/>`_.
Software Stack
diff --git a/docs/conf.py b/docs/source/conf.py
index 6c64cdda..e2f327c9 100644
--- a/docs/conf.py
+++ b/docs/source/conf.py
@@ -48,9 +48,9 @@ copyright = u'2011, Free Software Foundation, Inc and contributors'
# built documents.
#
# The short X.Y version.
-version = '0.0.3'
+version = '0.0.4'
# The full version, including alpha/beta/rc tags.
-release = '0.0.3'
+release = '0.0.4'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -64,7 +64,7 @@ release = '0.0.3'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
-exclude_patterns = ['_build']
+exclude_patterns = ['_build', 'mgext', '_templates', '_static']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
@@ -214,3 +214,26 @@ man_pages = [
('index', 'gnumediagoblin', u'GNU MediaGoblin Documentation',
[u'Chris Webber, et al'], 1)
]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output ------------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'gnumediagoblin', u'GNU MediaGoblin Documentation', u'gnumediagoblin',
+ 'GNU MediaGoblin', 'Media sharing web application.', 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
diff --git a/docs/contributinghowto.rst b/docs/source/contributinghowto.rst
index 06d2814e..a4f5771a 100644
--- a/docs/contributinghowto.rst
+++ b/docs/source/contributinghowto.rst
@@ -46,8 +46,8 @@ Here are some things you can do today:
**Write/Fix some code**
If you are a coder and you're looking to code, check out the
- :ref:`hacking-howto`. We even have tips on *becoming* a coder
- and we're willing to help you!
+ `wiki <http://wiki.mediagoblin.org/`_. We even have tips on
+ *becoming* a coder and we're willing to help you!
**Send encouragement**
@@ -135,7 +135,7 @@ the project, Chris Webber, will make a custom drawing of a goblin
dedicated specifically to you.
For why we're doing copyright assignment, see the
-:ref:`design-decisions-chapter`.
+`wiki <http://wiki.mediagoblin.org/>`_.
.. _filing-bugs:
diff --git a/docs/deploymenthowto.rst b/docs/source/deploymenthowto.rst
index f50edfb6..f3093a60 100644
--- a/docs/deploymenthowto.rst
+++ b/docs/source/deploymenthowto.rst
@@ -12,5 +12,5 @@ Step 3: Write the deployment guide and profit!
But seriously, this is a stub since we're not quite there (yet) but if
you want to see where we are now, you can try to run the latest
-development version by following the instructions at
-:ref:`hacking-howto`.
+development version by following the instructions on
+`the wiki <http://wiki.mediagoblin.org/>`_.
diff --git a/docs/foreword.rst b/docs/source/foreword.rst
index 1d423f08..1d423f08 100644
--- a/docs/foreword.rst
+++ b/docs/source/foreword.rst
diff --git a/docs/git.rst b/docs/source/git.rst
index bd0f9d52..ab3206b6 100644
--- a/docs/git.rst
+++ b/docs/source/git.rst
@@ -221,4 +221,4 @@ because he doesn't need it anymore.
How to learn git
================
-Check out :ref:`hacking-howto-git`!
+Check out `the wiki <http://wiki.mediagoblin.org/>`_.
diff --git a/docs/goblin.png b/docs/source/goblin.png
index e20265e6..e20265e6 100644
--- a/docs/goblin.png
+++ b/docs/source/goblin.png
Binary files differ
diff --git a/docs/index.rst b/docs/source/index.rst
index 2f84d6a6..8c00869a 100644
--- a/docs/index.rst
+++ b/docs/source/index.rst
@@ -15,11 +15,9 @@ Table of Contents:
mediagoblin
contributinghowto
deploymenthowto
- hackinghowto
theminghowto
git
codebase
- designdecisions
vision
diff --git a/docs/mediagoblin.rst b/docs/source/mediagoblin.rst
index c437ecc3..af6658f3 100644
--- a/docs/mediagoblin.rst
+++ b/docs/source/mediagoblin.rst
@@ -30,9 +30,9 @@ Why are we doing this?
Centralization and proprietization of media on the internet is a
serious problem and makes the web go from a system of extreme
resilience to a system of frightening fragility. We believe people
-should be able to own their data and that means someone has to build
-the tools to make it possible. We decided that in this case, that
-someone would be us!
+should be able to free their data from proprietary control and that
+means someone has to build the tools to make it possible. We decided
+that in this case, that someone would be us!
Who are you?
diff --git a/docs/mgext/__init__.py b/docs/source/mgext/__init__.py
index e69de29b..e69de29b 100644
--- a/docs/mgext/__init__.py
+++ b/docs/source/mgext/__init__.py
diff --git a/docs/mgext/youcanhelp.py b/docs/source/mgext/youcanhelp.py
index a99d0e4d..a99d0e4d 100644
--- a/docs/mgext/youcanhelp.py
+++ b/docs/source/mgext/youcanhelp.py
diff --git a/docs/snugglygoblin.png b/docs/source/snugglygoblin.png
index f325ae4b..f325ae4b 100644
--- a/docs/snugglygoblin.png
+++ b/docs/source/snugglygoblin.png
Binary files differ
diff --git a/docs/theminghowto.rst b/docs/source/theminghowto.rst
index 7b40685f..7b40685f 100644
--- a/docs/theminghowto.rst
+++ b/docs/source/theminghowto.rst
diff --git a/docs/vision.rst b/docs/source/vision.rst
index fad248df..fad248df 100644
--- a/docs/vision.rst
+++ b/docs/source/vision.rst
diff --git a/mediagoblin/contrib/960_16_col.css b/extlib/960.gs/960_16_col.css
index faa6d8b2..faa6d8b2 100644
--- a/mediagoblin/contrib/960_16_col.css
+++ b/extlib/960.gs/960_16_col.css
diff --git a/extlib/960.gs/README.txt b/extlib/960.gs/README.txt
new file mode 100755
index 00000000..da0ea86f
--- /dev/null
+++ b/extlib/960.gs/README.txt
@@ -0,0 +1,54 @@
+===============
+960 GRID SYSTEM
+===============
+
+Created by Nathan Smith. See the official site for more info: http://960.gs/
+
+============================================================================
+
+To install the Adobe Fireworks extension, simply double-click the *.mxp file
+included in the /fireworks_extension directory. If you are running Windows 7
+you will need admin permissions in order to install this extension properly.
+
+============================================================================
+
+Thank you for downloading the 960 Grid System. I hope it helps to streamline
+web development workflow. Enclosed in the bundle are printable sketch sheets
+and template files for Adobe Fireworks and Photoshop, OmniGraffle and Visio.
+
+Also included is a lightweight CSS file, which contains the grid dimensions.
+To use this file, simply include the 960.css in the <head> of the HTML page.
+You may also use the reset.css and text.css files, or opt to leave them out.
+Here is an example of the XHTML code necessary to incorporate the CSS files:
+
+<head>
+<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" />
+<link rel="stylesheet" type="text/css" media="all" href="css/text.css" />
+<link rel="stylesheet" type="text/css" media="all" href="css/960.css" />
+</head>
+
+It is worth noting that these styles do not automatically make up a finished
+site design. They are simply a starting point, ideally for rapid prototyping
+or as a basis for creating your own designs. You should not feel constrained
+by the way I have built the initial code. If you disagree with how something
+has been done, feel free to revise it for the needs of your particular site.
+
+The files in the 960 Grid System are free of charge, licensed under MIT/GPL.
+
+============================================================================
+
+Note that if you are building a site in a language which reads from right to
+left, use the CSS files that end in "_rtl.css" instead. Denote the language:
+
+<html lang="..." dir="rtl">
+
+Be sure to replace "..." with the appropriate two-letter abbreviation of the
+language you are using. Example: lang="he" for Hebrew, lang="ar" for Arabic.
+
+============================================================================
+
+GPL license:
+http://www.gnu.org/licenses/gpl.html
+
+MIT license:
+http://www.opensource.org/licenses/mit-license.php \ No newline at end of file
diff --git a/mediagoblin/contrib/reset.css b/extlib/960.gs/reset.css
index 87b7f368..87b7f368 100644
--- a/mediagoblin/contrib/reset.css
+++ b/extlib/960.gs/reset.css
diff --git a/mediagoblin/contrib/text.css b/extlib/960.gs/text.css
index 1a6b302f..1a6b302f 100644
--- a/mediagoblin/contrib/text.css
+++ b/extlib/960.gs/text.css
diff --git a/extlib/README b/extlib/README
new file mode 100644
index 00000000..c690beac
--- /dev/null
+++ b/extlib/README
@@ -0,0 +1,71 @@
+=========================
+ External Library README
+=========================
+
+DO NOT "FIX" CODE IN THIS DIRECTORY.
+
+ONLY UPSTREAM VERSIONS OF SOFTWARE GO IN THIS DIRECTORY.
+
+This directory is provided as a courtesy to our users who might be
+unable or unwilling to find and install libraries we depend on.
+
+If we "fix" software in this directory, we hamstring users who do the
+right thing and keep a single version of upstream libraries in a
+system-wide library. We introduce subtle and maddening bugs where
+our code is "accidentally" using the "wrong" library version. We may
+unwittingly interfere with other software that depends on the
+canonical release versions of those same libraries!
+
+Forking upstream software for trivial reasons makes us bad citizens in
+the Open Source community and adds unnecessary heartache for our
+users. Don't make us "that" project.
+
+
+FAQ
+===
+
+:Q: What should we do when we find a bug in upstream software?
+
+:A: First and foremost, REPORT THE BUG, and if possible send in a patch.
+
+ Watch for a release of the upstream software and integrate with it
+ when it's released.
+
+ In the meantime, work around the bug, if at all possible. Usually,
+ it's quite possible, if slightly harder or less efficient.
+
+:Q: What if the bug can't be worked around?
+
+:A: If the upstream developers have accepted a bug patch, it's
+ undesirable but acceptable to apply that patch to the library in
+ the ``extlib/`` dir. Ideally, use a release version for upstream or a
+ version control system snapshot.
+
+ Note that this is a last resort.
+
+:Q: What if upstream is unresponsive or won't accept a patch?
+
+:A: Try again.
+
+:Q: I tried again, and upstream is still unresponsive and nobody's
+ checked on my patch. Now what?
+
+:A: If the upstream project is moribund and there's a way to adopt it,
+ propose having the MediaGoblin dev team adopt the project. Or, adopt
+ it yourself.
+
+:Q: What if there's no upstream authority and it can't be adopted?
+
+:A: Then we fork it. Make a new name and a new version. Include it in
+ ``lib/`` instead of ``extlib/``, and use the GMG_* prefix to change
+ the namespace to avoid collisions (or something like that).
+
+ This is a last resort; consult with the rest of the dev group
+ before taking this radical step.
+
+
+Thanks
+======
+
+This policy originally copied from Status.net. Many many thanks to them
+for working out such a nice system for doing things.
diff --git a/lazyserver.sh b/lazyserver.sh
index 4f10f771..e4afdaa5 100755
--- a/lazyserver.sh
+++ b/lazyserver.sh
@@ -18,18 +18,18 @@
if [ "$1" = "-h" ]
then
- echo "$0 [-h] [-c paste.ini] ARGS_to_paster"
- echo " For example:"
- echo " $0 -c fcgi.ini port_number=23371"
- exit 1
+ echo "$0 [-h] [-c paste.ini] ARGS_to_paster"
+ echo " For example:"
+ echo " $0 -c fcgi.ini port_number=23371"
+ exit 1
fi
PASTE_INI=paste.ini
if [ "$1" = "-c" ]
then
- PASTE_INI="$2"
- shift
- shift
+ PASTE_INI="$2"
+ shift
+ shift
fi
if [ -f ./bin/paster ]; then
diff --git a/AGPLv3.txt b/licenses/AGPLv3.txt
index dba13ed2..dba13ed2 100644
--- a/AGPLv3.txt
+++ b/licenses/AGPLv3.txt
diff --git a/CC0_1.0.txt b/licenses/CC0_1.0.txt
index 0e259d42..0e259d42 100644
--- a/CC0_1.0.txt
+++ b/licenses/CC0_1.0.txt
diff --git a/licenses/LGPLv3.txt b/licenses/LGPLv3.txt
new file mode 100644
index 00000000..65c5ca88
--- /dev/null
+++ b/licenses/LGPLv3.txt
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/maketarball.sh b/maketarball.sh
index 2ee78016..5f17e578 100755
--- a/maketarball.sh
+++ b/maketarball.sh
@@ -1,57 +1,178 @@
#!/bin/bash
-# usage: maketarball
-# maketarball <tag>
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011 Free Software Foundation, Inc
#
-# With no arguments, this creates a source tarball from git master with a
-# filename based on today's date.
+# 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.
#
-# With a <tag> argument, this creates a tarball of the tag.
+# 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/>.
+
+
+# usage: maketarball [-drh] REVISH
+#
+# Creates a tarball of the repository at rev REVISH.
+
+# If -d is passed in, then it adds the date to the directory name.
+#
+# If -r is passed in, then it does some additional things required
+# for a release-ready tarball.
+#
+# If -h is passed in, shows help and exits.
#
# Examples:
#
-# ./maketarball
# ./maketarball v0.0.2
+# ./maketarball -d master
+# ./maketarball -r v0.0.2
-NOWDATE=`date "+%Y-%m-%d"`
-if [ -z "$1" ]
-then
- REVISH=master
- PREFIX="$NOWDATE-$REVISH"
-else
- REVISH=$1
- PREFIX="$REVISH"
+USAGE="Usage: $0 -h | [-dr] REVISH"
+
+REVISH="none"
+PREFIX="none"
+NOWDATE=""
+RELEASE="no"
+
+while getopts ":dhr" opt;
+do
+ case "$opt" in
+ h)
+ echo "$USAGE"
+ echo ""
+ echo "Creates a tarball of the repository at rev REVISH."
+ echo ""
+ echo " -h Shows this help message"
+ echo " -d Includes date in tar file name and directory"
+ echo " -r Performs other release-related actions"
+ exit 0
+ ;;
+ d)
+ NOWDATE=`date "+%Y-%m-%d-"`
+ shift $((OPTIND-1))
+ ;;
+ r)
+ RELEASE="yes"
+ shift $((OPTIND-1))
+ ;;
+ \?)
+ echo "Invalid option: -$OPTARG" >&2
+ echo "$USAGE" >&2
+ ;;
+ esac
+done
+
+if [[ -z "$1" ]]; then
+ echo "$USAGE";
+ exit 1;
fi
-# convert PREFIX to all lowercase.
-# nix the v from tag names.
+REVISH=$1
+PREFIX="$NOWDATE$REVISH"
+
+# convert PREFIX to all lowercase and nix the v from tag names.
PREFIX=`echo "$PREFIX" | tr '[A-Z]' '[a-z]' | sed s/v//`
-echo "== REVISH $REVISH"
-echo "== PREFIX $PREFIX"
+# build the filename base minus the .tar.gz stuff--this is also
+# the directory in the tarball.
+FNBASE="mediagoblin-$PREFIX"
+
+STARTDIR=`pwd`
+
+function cleanup {
+ pushd $STARTDIR
+
+ if [[ -e tmp ]]
+ then
+ echo "+ cleaning up tmp/"
+ rm -rf tmp
+ fi
+ popd
+}
+
+echo "+ Building tarball from: $REVISH"
+echo "+ Using prefix: $PREFIX"
+echo "+ Release?: $RELEASE"
echo ""
-echo "generating archive...."
+if [[ -e tmp ]]
+then
+ echo "+ there's an existing tmp/. please remove it."
+ exit 1
+fi
+
+mkdir $STARTDIR/tmp
+echo "+ generating archive...."
git archive \
--format=tar \
- --prefix=mediagoblin-$PREFIX/ \
- $REVISH > mediagoblin-$PREFIX.tar
+ --prefix=$FNBASE/ \
+ $REVISH > tmp/$FNBASE.tar
if [[ $? -ne 0 ]]
then
- echo "git archive command failed. See above text for reason."
- if [[ -e mediagoblin-$PREFIX.tar ]]
+ echo "+ git archive command failed. See above text for reason."
+ cleanup
+ exit 1
+fi
+
+
+if [[ $RELEASE = "yes" ]]
+then
+ pushd tmp/
+ tar -xvf $FNBASE.tar
+
+ pushd $FNBASE
+ pushd docs
+
+ echo "+ generating html docs"
+ make html
+ if [[ $? -ne 0 ]]
then
- rm mediagoblin-$PREFIX.tar
+ echo "+ sphinx docs generation failed. See above text for reason."
+ cleanup
+ exit 1
fi
- exit 1;
+
+ # NOTE: this doesn't work for gmg prior to v0.0.4.
+ echo "+ generating texinfo docs (doesn't work prior to v0.0.4)"
+ make info
+ popd
+
+ echo "+ moving docs to the right place"
+ if [[ -e docs/build/html/ ]]
+ then
+ mv docs/build/html/ docs/html/
+ mv docs/build/texinfo/ docs/texinfo/
+
+ rm -rf docs/build/
+ rm -rf docs/source/mgext/*.pyc
+ else
+ # this is the old directory structure pre-0.0.4
+ mv docs/_build/html/ docs/html/
+
+ rm -rf docs/_build/
+ rm -rf docs/mgext/*.pyc
+ fi
+
+ popd
+
+ tar -cvf $FNBASE.tar $FNBASE
+ popd
fi
-echo "compressing...."
-gzip mediagoblin-$PREFIX.tar
-echo "archive at mediagoblin-$PREFIX.tar.gz"
+echo "+ compressing...."
+gzip tmp/$FNBASE.tar
+
+echo "+ archive at tmp/$FNBASE.tar.gz"
-echo "done." \ No newline at end of file
+echo "+ done."
diff --git a/mediagoblin/app.py b/mediagoblin/app.py
index 85c3c0c7..c1ee3d77 100644
--- a/mediagoblin/app.py
+++ b/mediagoblin/app.py
@@ -20,11 +20,12 @@ import urllib
import routes
from webob import Request, exc
-from mediagoblin import routing, util, storage
+from mediagoblin import routing, util
from mediagoblin.mg_globals import setup_globals
from mediagoblin.init.celery import setup_celery_from_config
-from mediagoblin.init import get_jinja_loader, get_staticdirector, \
- setup_global_and_app_config, setup_workbench, setup_database
+from mediagoblin.init import (get_jinja_loader, get_staticdirector,
+ setup_global_and_app_config, setup_workbench, setup_database,
+ setup_storage)
class MediaGoblinApp(object):
@@ -62,10 +63,7 @@ class MediaGoblinApp(object):
app_config.get('user_template_path'))
# Set up storage systems
- self.public_store = storage.storage_system_from_config(
- app_config, 'publicstore')
- self.queue_store = storage.storage_system_from_config(
- app_config, 'queuestore')
+ self.public_store, self.queue_store = setup_storage()
# set up routing
self.routing = routing.get_mapper()
@@ -90,10 +88,7 @@ class MediaGoblinApp(object):
# object.
#######################################################
- setup_globals(
- app=self,
- public_store=self.public_store,
- queue_store=self.queue_store)
+ setup_globals(app = self)
# Workbench *currently* only used by celery, so this only
# matters in always eager mode :)
diff --git a/mediagoblin/auth/forms.py b/mediagoblin/auth/forms.py
index 7bc0aeb1..917909c5 100644
--- a/mediagoblin/auth/forms.py
+++ b/mediagoblin/auth/forms.py
@@ -16,34 +16,36 @@
import wtforms
+from mediagoblin.util import fake_ugettext_passthrough as _
+
class RegistrationForm(wtforms.Form):
username = wtforms.TextField(
- 'Username',
+ _('Username'),
[wtforms.validators.Required(),
wtforms.validators.Length(min=3, max=30),
wtforms.validators.Regexp(r'^\w+$')])
password = wtforms.PasswordField(
- 'Password',
+ _('Password'),
[wtforms.validators.Required(),
wtforms.validators.Length(min=6, max=30),
wtforms.validators.EqualTo(
'confirm_password',
- 'Passwords must match.')])
+ _('Passwords must match.'))])
confirm_password = wtforms.PasswordField(
- 'Confirm password',
+ _('Confirm password'),
[wtforms.validators.Required()])
email = wtforms.TextField(
- 'Email address',
+ _('Email address'),
[wtforms.validators.Required(),
wtforms.validators.Email()])
class LoginForm(wtforms.Form):
username = wtforms.TextField(
- 'Username',
+ _('Username'),
[wtforms.validators.Required(),
wtforms.validators.Regexp(r'^\w+$')])
password = wtforms.PasswordField(
- 'Password',
+ _('Password'),
[wtforms.validators.Required()])
diff --git a/mediagoblin/auth/routing.py b/mediagoblin/auth/routing.py
index 46c585d2..9547b3ea 100644
--- a/mediagoblin/auth/routing.py
+++ b/mediagoblin/auth/routing.py
@@ -19,18 +19,12 @@ from routes.route import Route
auth_routes = [
Route('mediagoblin.auth.register', '/register/',
controller='mediagoblin.auth.views:register'),
- Route('mediagoblin.auth.register_success', '/register/success/',
- template='mediagoblin/auth/register_success.html',
- controller='mediagoblin.views:simple_template_render'),
Route('mediagoblin.auth.login', '/login/',
controller='mediagoblin.auth.views:login'),
Route('mediagoblin.auth.logout', '/logout/',
controller='mediagoblin.auth.views:logout'),
Route('mediagoblin.auth.verify_email', '/verify_email/',
controller='mediagoblin.auth.views:verify_email'),
- Route('mediagoblin.auth.verify_email_notice', '/verification_required/',
- template='mediagoblin/auth/verification_needed.html',
- controller='mediagoblin.views:simple_template_render'),
Route('mediagoblin.auth.resend_verification', '/resend_verification/',
controller='mediagoblin.auth.views:resend_activation'),
Route('mediagoblin.auth.resend_verification_success',
diff --git a/mediagoblin/auth/views.py b/mediagoblin/auth/views.py
index 7fe507b1..121a8c8e 100644
--- a/mediagoblin/auth/views.py
+++ b/mediagoblin/auth/views.py
@@ -21,6 +21,7 @@ from webob import exc
from mediagoblin import messages
from mediagoblin import mg_globals
from mediagoblin.util import render_to_response, redirect
+from mediagoblin.util import pass_to_ugettext as _
from mediagoblin.db.util import ObjectId
from mediagoblin.auth import lib as auth_lib
from mediagoblin.auth import forms as auth_forms
@@ -36,7 +37,7 @@ def register(request):
messages.add_message(
request,
messages.WARNING,
- ('Sorry, registration is disabled on this instance.'))
+ _('Sorry, registration is disabled on this instance.'))
return redirect(request, "index")
register_form = auth_forms.RegistrationForm(request.POST)
@@ -51,20 +52,29 @@ def register(request):
if users_with_username:
register_form.username.errors.append(
- u'Sorry, a user with that name already exists.')
+ _(u'Sorry, a user with that name already exists.'))
else:
# Create the user
- entry = request.db.User()
- entry['username'] = request.POST['username'].lower()
- entry['email'] = request.POST['email']
- entry['pw_hash'] = auth_lib.bcrypt_gen_password_hash(
+ user = request.db.User()
+ user['username'] = request.POST['username'].lower()
+ user['email'] = request.POST['email']
+ user['pw_hash'] = auth_lib.bcrypt_gen_password_hash(
request.POST['password'])
- entry.save(validate=True)
+ user.save(validate=True)
- send_verification_email(entry, request)
+ # log the user in
+ request.session['user_id'] = unicode(user['_id'])
+ request.session.save()
- return redirect(request, "mediagoblin.auth.register_success")
+ # send verification email
+ send_verification_email(user, request)
+
+ # redirect the user to their homepage... there will be a
+ # message waiting for them to verify their email
+ return redirect(
+ request, 'mediagoblin.user_pages.user_home',
+ user=user['username'])
return render_to_response(
request,
@@ -136,23 +146,20 @@ def verify_email(request):
user['status'] = u'active'
user['email_verified'] = True
user.save()
- verification_successful = True
messages.add_message(
request,
messages.SUCCESS,
- ('Your email address has been verified. '
- 'You may now login, edit your profile, and submit images!'))
+ _("Your email address has been verified. "
+ "You may now login, edit your profile, and submit images!"))
else:
- verification_successful = False
- messages.add_message(request,
- messages.ERROR,
- 'The verification key or user id is incorrect')
+ messages.add_message(
+ request,
+ messages.ERROR,
+ _('The verification key or user id is incorrect'))
- return render_to_response(
- request,
- 'mediagoblin/user_pages/user.html',
- {'user': user,
- 'verification_successful' : verification_successful})
+ return redirect(
+ request, 'mediagoblin.user_pages.user_home',
+ user=request.user['username'])
def resend_activation(request):
@@ -166,4 +173,10 @@ def resend_activation(request):
send_verification_email(request.user, request)
- return redirect(request, 'mediagoblin.auth.resend_verification_success')
+ messages.add_message(
+ request,
+ messages.INFO,
+ _('Resent your verification email.'))
+ return redirect(
+ request, 'mediagoblin.user_pages.user_home',
+ user=request.user['username'])
diff --git a/mediagoblin/config_spec.ini b/mediagoblin/config_spec.ini
index 28be5f34..bbc1f7d6 100644
--- a/mediagoblin/config_spec.ini
+++ b/mediagoblin/config_spec.ini
@@ -24,6 +24,10 @@ email_sender_address = string(default="notice@mediagoblin.example.org")
# Set to false to disable registrations
allow_registration = boolean(default=True)
+# tag parsing
+tags_delimiter = string(default=",")
+tags_max_length = integer(default=50)
+
# By default not set, but you might want something like:
# "%(here)s/user_dev/templates/"
local_templates = string()
diff --git a/mediagoblin/db/indexes.py b/mediagoblin/db/indexes.py
index a832e013..30d43c98 100644
--- a/mediagoblin/db/indexes.py
+++ b/mediagoblin/db/indexes.py
@@ -90,6 +90,21 @@ MEDIAENTRY_INDEXES = {
# Indexing on uploaders and when media entries are created.
# Used for showing a user gallery, etc.
'index': [('uploader', ASCENDING),
+ ('created', DESCENDING)]},
+
+ 'state_uploader_tags_created': {
+ # Indexing on processed?, media uploader, associated tags, and timestamp
+ # Used for showing media items matching a tag search, most recent first.
+ 'index': [('state', ASCENDING),
+ ('uploader', ASCENDING),
+ ('tags.slug', DESCENDING),
+ ('created', DESCENDING)]},
+
+ 'state_tags_created': {
+ # Indexing on processed?, media tags, and timestamp (across all users)
+ # This is used for a front page tag search.
+ 'index': [('state', ASCENDING),
+ ('tags.slug', DESCENDING),
('created', DESCENDING)]}}
diff --git a/mediagoblin/db/models.py b/mediagoblin/db/models.py
index e97dc537..23527e84 100644
--- a/mediagoblin/db/models.py
+++ b/mediagoblin/db/models.py
@@ -186,7 +186,7 @@ class MediaEntry(Document):
'media_type': unicode,
'media_data': dict, # extra data relevant to this media_type
'plugin_data': dict, # plugins can dump stuff here.
- 'tags': [unicode],
+ 'tags': [dict],
'state': unicode,
# For now let's assume there can only be one main file queued
diff --git a/mediagoblin/decorators.py b/mediagoblin/decorators.py
index 081eda62..2e90274e 100644
--- a/mediagoblin/decorators.py
+++ b/mediagoblin/decorators.py
@@ -38,8 +38,9 @@ def require_active_login(controller):
def new_controller_func(request, *args, **kwargs):
if request.user and \
request.user.get('status') == u'needs_email_verification':
- return redirect(request,
- 'mediagoblin.auth.verify_email_notice')
+ return redirect(
+ request, 'mediagoblin.user_pages.user_home',
+ user=request.user['username'])
elif not request.user or request.user.get('status') != u'active':
return exc.HTTPFound(
location="%s?next=%s" % (
diff --git a/mediagoblin/edit/__init__.py b/mediagoblin/edit/__init__.py
index e69de29b..a8eeb5ed 100644
--- a/mediagoblin/edit/__init__.py
+++ b/mediagoblin/edit/__init__.py
@@ -0,0 +1,17 @@
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011 Free Software Foundation, Inc
+#
+# 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/>.
+
+
diff --git a/mediagoblin/edit/forms.py b/mediagoblin/edit/forms.py
index 0ed52af1..2f3ed203 100644
--- a/mediagoblin/edit/forms.py
+++ b/mediagoblin/edit/forms.py
@@ -17,20 +17,27 @@
import wtforms
+from mediagoblin.util import tag_length_validator, TOO_LONG_TAG_WARNING
+from mediagoblin.util import fake_ugettext_passthrough as _
+
class EditForm(wtforms.Form):
title = wtforms.TextField(
- 'Title',
+ _('Title'),
[wtforms.validators.Length(min=0, max=500)])
slug = wtforms.TextField(
- 'Slug',
- [wtforms.validators.Required(message="The slug can't be empty")])
+ _('Slug'),
+ [wtforms.validators.Required(message=_("The slug can't be empty"))])
description = wtforms.TextAreaField('Description of this work')
+ tags = wtforms.TextField(
+ _('Tags'),
+ [tag_length_validator])
class EditProfileForm(wtforms.Form):
- bio = wtforms.TextAreaField('Bio',
+ bio = wtforms.TextAreaField(
+ _('Bio'),
[wtforms.validators.Length(min=0, max=500)])
url = wtforms.TextField(
- 'Website',
+ _('Website'),
[wtforms.validators.Optional(),
- wtforms.validators.URL(message='Improperly formed URL')])
+ wtforms.validators.URL(message=_('Improperly formed URL'))])
diff --git a/mediagoblin/edit/views.py b/mediagoblin/edit/views.py
index f372fbb9..0b1a98f1 100644
--- a/mediagoblin/edit/views.py
+++ b/mediagoblin/edit/views.py
@@ -16,10 +16,14 @@
from webob import exc
+from string import split
from mediagoblin import messages
+from mediagoblin import mg_globals
from mediagoblin.util import (
- render_to_response, redirect, cleaned_markdown_conversion)
+ render_to_response, redirect, clean_html, convert_to_tag_list_of_dicts,
+ media_tags_as_string, cleaned_markdown_conversion)
+from mediagoblin.util import pass_to_ugettext as _
from mediagoblin.edit import forms
from mediagoblin.edit.lib import may_edit_media
from mediagoblin.decorators import require_active_login, get_user_media_entry
@@ -34,7 +38,8 @@ def edit_media(request, media):
form = forms.EditForm(request.POST,
title = media['title'],
slug = media['slug'],
- description = media['description'])
+ description = media['description'],
+ tags = media_tags_as_string(media['tags']))
if request.method == 'POST' and form.validate():
# Make sure there isn't already a MediaEntry with such a slug
@@ -46,11 +51,13 @@ def edit_media(request, media):
if existing_user_slug_entries:
form.slug.errors.append(
- u'An entry with that slug already exists for this user.')
+ _(u'An entry with that slug already exists for this user.'))
else:
media['title'] = request.POST['title']
media['description'] = request.POST.get('description')
-
+ media['tags'] = convert_to_tag_list_of_dicts(
+ request.POST.get('tags'))
+
media['description_html'] = cleaned_markdown_conversion(
media['description'])
@@ -65,7 +72,7 @@ def edit_media(request, media):
and request.method != 'POST':
messages.add_message(
request, messages.WARNING,
- "You are editing another user's media. Proceed with caution.")
+ _("You are editing another user's media. Proceed with caution."))
return render_to_response(
@@ -86,7 +93,7 @@ def edit_profile(request):
if request.method != 'POST':
messages.add_message(
request, messages.WARNING,
- "You are editing a user's profile. Proceed with caution.")
+ _("You are editing a user's profile. Proceed with caution."))
else:
user = request.user
diff --git a/mediagoblin/gmg_commands/__init__.py b/mediagoblin/gmg_commands/__init__.py
index 0cb4d3a2..921f0430 100644
--- a/mediagoblin/gmg_commands/__init__.py
+++ b/mediagoblin/gmg_commands/__init__.py
@@ -40,6 +40,10 @@ SUBCOMMAND_MAP = {
'setup': 'mediagoblin.gmg_commands.users:changepw_parser_setup',
'func': 'mediagoblin.gmg_commands.users:changepw',
'help': 'Makes admin an user'},
+ 'wipealldata': {
+ 'setup': 'mediagoblin.gmg_commands.wipealldata:wipe_parser_setup',
+ 'func': 'mediagoblin.gmg_commands.wipealldata:wipe',
+ 'help': 'Wipes **all** the data for this MediaGoblin instance'},
}
diff --git a/mediagoblin/gmg_commands/users.py b/mediagoblin/gmg_commands/users.py
index b4a6bbc1..14b6875d 100644
--- a/mediagoblin/gmg_commands/users.py
+++ b/mediagoblin/gmg_commands/users.py
@@ -1,3 +1,19 @@
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011 Free Software Foundation, Inc
+#
+# 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.gmg_commands import util as commands_util
from mediagoblin.auth import lib as auth_lib
from mediagoblin import mg_globals
diff --git a/mediagoblin/gmg_commands/wipealldata.py b/mediagoblin/gmg_commands/wipealldata.py
new file mode 100644
index 00000000..9ad32051
--- /dev/null
+++ b/mediagoblin/gmg_commands/wipealldata.py
@@ -0,0 +1,51 @@
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011 Free Software Foundation, Inc
+#
+# 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/>.
+
+import sys
+import pymongo
+import sys
+import os
+import shutil
+
+
+def wipe_parser_setup(subparser):
+ pass
+
+
+def wipe(args):
+ print "*** WARNING! ***"
+ print ""
+ print "Running this will destroy your mediagoblin database,"
+ print "remove all your media files in user_dev/, etc."
+
+ drop_it = raw_input(
+ 'Are you **SURE** you want to destroy your environment? '
+ '(if so, type "yes")> ')
+
+ if not drop_it == 'yes':
+ return
+
+ print "nixing data in mongodb...."
+ conn = pymongo.Connection()
+ conn.drop_database('mediagoblin')
+
+ for directory in [os.path.join(os.getcwd(), "user_dev", "media"),
+ os.path.join(os.getcwd(), "user_dev", "beaker")]:
+ if os.path.exists(directory):
+ print "nixing %s...." % directory
+ shutil.rmtree(directory)
+
+ print "removed all your stuff! okay, now re-run ./bin/buildout"
diff --git a/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.mo
new file mode 100644
index 00000000..6bb69ce9
--- /dev/null
+++ b/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.mo
Binary files differ
diff --git a/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po
new file mode 100644
index 00000000..30c55e21
--- /dev/null
+++ b/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po
@@ -0,0 +1,317 @@
+# Translations template for PROJECT.
+# Copyright (C) 2011 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Rafael Maguiña <rafael.maguina@gmail.com>, 2011.
+# <mediagoblin.org@samba-tng.org>, 2011.
+# <cwebber@dustycloud.org>, 2011.
+# Jan-Christoph Borchardt <JanCBorchardt@fsfe.org>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: GNU MediaGoblin\n"
+"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n"
+"POT-Creation-Date: 2011-08-08 22:53-0500\n"
+"PO-Revision-Date: 2011-08-10 23:20+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: German (http://www.transifex.net/projects/p/mediagoblin/team/de/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+"Language: de\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: mediagoblin/auth/forms.py:24 mediagoblin/auth/forms.py:46
+msgid "Username"
+msgstr "Benutzername"
+
+#: mediagoblin/auth/forms.py:29 mediagoblin/auth/forms.py:50
+msgid "Password"
+msgstr "Passwort"
+
+#: mediagoblin/auth/forms.py:34
+msgid "Passwords must match."
+msgstr "Passwörter müssen übereinstimmen."
+
+#: mediagoblin/auth/forms.py:36
+msgid "Confirm password"
+msgstr "Passwort wiederholen"
+
+#: mediagoblin/auth/forms.py:39
+msgid "Email address"
+msgstr "Email-Adresse"
+
+#: mediagoblin/auth/views.py:40
+msgid "Sorry, registration is disabled on this instance."
+msgstr "Registrierung ist auf dieser Instanz leider deaktiviert."
+
+#: mediagoblin/auth/views.py:55
+msgid "Sorry, a user with that name already exists."
+msgstr "Leider gibt es bereits einen Benutzer mit diesem Namen."
+
+#: mediagoblin/auth/views.py:152
+msgid ""
+"Your email address has been verified. You may now login, edit your profile, "
+"and submit images!"
+msgstr ""
+"Deine Email-Adresse wurde bestätigt. Du kannst dich nun anmelden, dein "
+"Profil bearbeiten und Bilder hochladen!"
+
+#: mediagoblin/auth/views.py:158
+msgid "The verification key or user id is incorrect"
+msgstr "Der Bestätigungssschlüssel oder die Nutzernummer ist falsch."
+
+#: mediagoblin/auth/views.py:179
+#: mediagoblin/templates/mediagoblin/auth/resent_verification_email.html:22
+msgid "Resent your verification email."
+msgstr "Bestätigungs-Email noch Mal senden."
+
+#: mediagoblin/edit/forms.py:26 mediagoblin/submit/forms.py:26
+msgid "Title"
+msgstr "Titel"
+
+#: mediagoblin/edit/forms.py:29
+msgid "Slug"
+msgstr "Kurztitel"
+
+#: mediagoblin/edit/forms.py:30
+msgid "The slug can't be empty"
+msgstr "Bitte gib einen Kurztitel ein"
+
+#: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:31
+msgid "Tags"
+msgstr "Markierungen"
+
+#: mediagoblin/edit/forms.py:38
+msgid "Bio"
+msgstr "Biographie"
+
+#: mediagoblin/edit/forms.py:41
+msgid "Website"
+msgstr "Webseite"
+
+#: mediagoblin/edit/forms.py:43
+msgid "Improperly formed URL"
+msgstr "Adresse fehlerhaft"
+
+#: mediagoblin/edit/views.py:54
+msgid "An entry with that slug already exists for this user."
+msgstr "Diesen Kurztitel hast du bereits vergeben."
+
+#: mediagoblin/edit/views.py:75
+msgid "You are editing another user's media. Proceed with caution."
+msgstr "Du bearbeitest die Medien eines Anderen. Bitte sei vorsichtig."
+
+#: mediagoblin/edit/views.py:96
+msgid "You are editing a user's profile. Proceed with caution."
+msgstr "Du bearbeitest das Profil eines Anderen. Bitte sei vorsichtig."
+
+#: mediagoblin/submit/forms.py:29
+msgid "File"
+msgstr "Datei"
+
+#: mediagoblin/submit/views.py:45
+msgid "You must provide a file."
+msgstr "Du musst eine Datei angeben."
+
+#: mediagoblin/submit/views.py:48
+msgid "The file doesn't seem to be an image!"
+msgstr "Diese Datei scheint kein Bild zu sein!"
+
+#: mediagoblin/submit/views.py:96
+msgid "Woohoo! Submitted!"
+msgstr "Yeeeaaah! Geschafft!"
+
+#: mediagoblin/templates/mediagoblin/base.html:22
+msgid "GNU MediaGoblin"
+msgstr "GNU MediaGoblin"
+
+#: mediagoblin/templates/mediagoblin/base.html:45
+msgid "Mediagoblin logo"
+msgstr "Mediagoblin-Logo"
+
+#: mediagoblin/templates/mediagoblin/base.html:51
+msgid "Submit media"
+msgstr "Medien hochladen"
+
+#: mediagoblin/templates/mediagoblin/base.html:62
+msgid "verify your email!"
+msgstr "Bitte bestätige deine Email-Adresse!"
+
+#: mediagoblin/templates/mediagoblin/base.html:72
+msgid "Login"
+msgstr "Anmelden"
+
+#: mediagoblin/templates/mediagoblin/base.html:88
+msgid ""
+"Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a "
+"href=\"http://gnu.org/\">GNU project</a>"
+msgstr ""
+"Läüft mit <a href=\"http://mediagoblin.org\">MediaGoblin</a>, einem <a "
+"href=\"http://gnu.org/\">GNU-Projekt</a>"
+
+#: mediagoblin/templates/mediagoblin/root.html:21
+msgid "Welcome to GNU MediaGoblin!"
+msgstr "Willkommen bei GNU MediaGoblin!"
+
+#: mediagoblin/templates/mediagoblin/root.html:26
+msgid "Submit an item"
+msgstr "Eintrag hochladen"
+
+#: mediagoblin/templates/mediagoblin/root.html:31
+#, python-format
+msgid "If you have an account, you can <a href=\"%(login_url)s\">Login</a>."
+msgstr ""
+"Falls du ein Konto hast, kannst du dich <a "
+"href=\"%(login_url)s\">anmelden</a>."
+
+#: mediagoblin/templates/mediagoblin/root.html:37
+#, python-format
+msgid ""
+"If you don't have an account, please <a "
+"href=\"%(register_url)s\">Register</a>."
+msgstr ""
+"Wenn du noch kein Konto hast, <a href=\"%(register_url)s\">registriere "
+"dich</a>."
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:26
+msgid "Log in"
+msgstr "Anmelden"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:29
+msgid "Login failed!"
+msgstr "Anmeldung fehlgeschlagen!"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:34
+#: mediagoblin/templates/mediagoblin/auth/register.html:30
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:35
+#: mediagoblin/templates/mediagoblin/submit/start.html:32
+msgid "Submit"
+msgstr "Bestätigen"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:42
+msgid "Don't have an account yet?"
+msgstr "Hast du noch kein Konto?"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:45
+msgid "Create one here!"
+msgstr "Registriere dich!"
+
+#: mediagoblin/templates/mediagoblin/auth/register.html:27
+msgid "Create an account!"
+msgstr "Neues Konto registrieren!"
+
+#: 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 ""
+"Hi %(username)s,\n"
+"\n"
+"um dein Konto bei GNU MediaGoblin zu aktivieren, musst du folgende Adresse in einem Webbrowser öffnen:\n"
+"\n"
+"%(verification_url)s"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:29
+#, python-format
+msgid "Editing %(media_title)s"
+msgstr "%(media_title)s bearbeiten"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:36
+msgid "Cancel"
+msgstr "Abbrechen"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:37
+msgid "Save changes"
+msgstr "Änderungen speichern"
+
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:29
+#, python-format
+msgid "Editing %(username)s's profile"
+msgstr "%(username)s’s Profil barbeiten"
+
+#: mediagoblin/templates/mediagoblin/listings/tag.html:29
+msgid "Media tagged with:"
+msgstr "Medien markiert mit:"
+
+#: mediagoblin/templates/mediagoblin/listings/tag.html:40
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:46
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:101
+msgid "atom feed"
+msgstr "Atom-Feed"
+
+#: mediagoblin/templates/mediagoblin/submit/start.html:26
+msgid "Submit yer media"
+msgstr "Medien hochladen"
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:30
+#, python-format
+msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media"
+msgstr "<a href=\"%(user_url)s\">%(username)s</a>’s Medien"
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:51
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:30
+msgid "Sorry, no such user found."
+msgstr "Dieser Benutzer wurde leider nicht gefunden."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:37
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:57
+msgid "Verification needed"
+msgstr "Überprüfung notwendig"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:40
+msgid "Almost done! Your account still needs to be verified."
+msgstr "Fast geschafft! Dein Konto muss nur noch bestätigt werden."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:45
+msgid ""
+"An email should arrive in a few moments with instructions on how to do so."
+msgstr ""
+"Gleich solltest du eine Email bekommen, die dir sagt was du noch machen "
+"musst."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:49
+msgid "In case it doesn't:"
+msgstr "Wenn sie nicht ankommt:"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:52
+msgid "Resend verification email"
+msgstr "Bestätigung noch Mal senden"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:60
+msgid ""
+"Someone has registered an account with this username, but it still has to be"
+" verified."
+msgstr ""
+"Jemand hat schon ein Konto mit diesem Nutzernamen registriert, aber es muss "
+"noch bestätigt werden."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:66
+#, 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 ""
+"Wenn dir dieses Konto gehört und die Bestätigungsmail weg ist, kannst du "
+"dich <a href=\"%(login_url)s\">anmelden</a> und sie erneut senden."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:76
+#, python-format
+msgid "%(username)s's profile"
+msgstr "%(username)s’s Profil"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:84
+msgid "Edit profile"
+msgstr "Profil bearbeiten"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:95
+#, python-format
+msgid "View all of %(username)s's media"
+msgstr "Alle Medien von %(username)s anschauen"
+
+
diff --git a/mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po
new file mode 100644
index 00000000..0de5ca62
--- /dev/null
+++ b/mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po
@@ -0,0 +1,292 @@
+# Translations template for PROJECT.
+# Copyright (C) 2011 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2011-08-08 22:53-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"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: mediagoblin/auth/forms.py:24 mediagoblin/auth/forms.py:46
+msgid "Username"
+msgstr ""
+
+#: mediagoblin/auth/forms.py:29 mediagoblin/auth/forms.py:50
+msgid "Password"
+msgstr ""
+
+#: mediagoblin/auth/forms.py:34
+msgid "Passwords must match."
+msgstr ""
+
+#: mediagoblin/auth/forms.py:36
+msgid "Confirm password"
+msgstr ""
+
+#: mediagoblin/auth/forms.py:39
+msgid "Email address"
+msgstr ""
+
+#: mediagoblin/auth/views.py:40
+msgid "Sorry, registration is disabled on this instance."
+msgstr ""
+
+#: mediagoblin/auth/views.py:55
+msgid "Sorry, a user with that name already exists."
+msgstr ""
+
+#: mediagoblin/auth/views.py:152
+msgid ""
+"Your email address has been verified. You may now login, edit your "
+"profile, and submit images!"
+msgstr ""
+
+#: mediagoblin/auth/views.py:158
+msgid "The verification key or user id is incorrect"
+msgstr ""
+
+#: mediagoblin/auth/views.py:179
+#: mediagoblin/templates/mediagoblin/auth/resent_verification_email.html:22
+msgid "Resent your verification email."
+msgstr ""
+
+#: mediagoblin/edit/forms.py:26 mediagoblin/submit/forms.py:26
+msgid "Title"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:29
+msgid "Slug"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:30
+msgid "The slug can't be empty"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:31
+msgid "Tags"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:38
+msgid "Bio"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:41
+msgid "Website"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:43
+msgid "Improperly formed URL"
+msgstr ""
+
+#: mediagoblin/edit/views.py:54
+msgid "An entry with that slug already exists for this user."
+msgstr ""
+
+#: mediagoblin/edit/views.py:75
+msgid "You are editing another user's media. Proceed with caution."
+msgstr ""
+
+#: mediagoblin/edit/views.py:96
+msgid "You are editing a user's profile. Proceed with caution."
+msgstr ""
+
+#: mediagoblin/submit/forms.py:29
+msgid "File"
+msgstr ""
+
+#: mediagoblin/submit/views.py:45
+msgid "You must provide a file."
+msgstr ""
+
+#: mediagoblin/submit/views.py:48
+msgid "The file doesn't seem to be an image!"
+msgstr ""
+
+#: mediagoblin/submit/views.py:96
+msgid "Woohoo! Submitted!"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:22
+msgid "GNU MediaGoblin"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:45
+msgid "Mediagoblin logo"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:51
+msgid "Submit media"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:62
+msgid "verify your email!"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:72
+msgid "Login"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:88
+msgid ""
+"Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a "
+"href=\"http://gnu.org/\">GNU project</a>"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/root.html:21
+msgid "Welcome to GNU MediaGoblin!"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/root.html:26
+msgid "Submit an item"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/root.html:31
+#, python-format
+msgid "If you have an account, you can <a href=\"%(login_url)s\">Login</a>."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/root.html:37
+#, python-format
+msgid ""
+"If you don't have an account, please <a "
+"href=\"%(register_url)s\">Register</a>."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:26
+msgid "Log in"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:29
+msgid "Login failed!"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:34
+#: mediagoblin/templates/mediagoblin/auth/register.html:30
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:35
+#: mediagoblin/templates/mediagoblin/submit/start.html:32
+msgid "Submit"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:42
+msgid "Don't have an account yet?"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:45
+msgid "Create one here!"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/auth/register.html:27
+msgid "Create an account!"
+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/edit/edit.html:29
+#, python-format
+msgid "Editing %(media_title)s"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:36
+msgid "Cancel"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:37
+msgid "Save changes"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:29
+#, python-format
+msgid "Editing %(username)s's profile"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/listings/tag.html:29
+msgid "Media tagged with:"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/listings/tag.html:40
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:46
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:101
+msgid "atom feed"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/submit/start.html:26
+msgid "Submit yer media"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:30
+#, python-format
+msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:51
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:30
+msgid "Sorry, no such user found."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:37
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:57
+msgid "Verification needed"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:40
+msgid "Almost done! Your account still needs to be verified."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:45
+msgid "An email should arrive in a few moments with instructions on how to do so."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:49
+msgid "In case it doesn't:"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:52
+msgid "Resend verification email"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:60
+msgid ""
+"Someone has registered an account with this username, but it still has to"
+" be verified."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:66
+#, 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/user_pages/user.html:76
+#, python-format
+msgid "%(username)s's profile"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:84
+msgid "Edit profile"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:95
+#, python-format
+msgid "View all of %(username)s's media"
+msgstr ""
+
diff --git a/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.mo
new file mode 100644
index 00000000..dfd3a1bc
--- /dev/null
+++ 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
new file mode 100644
index 00000000..0a12586c
--- /dev/null
+++ b/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.po
@@ -0,0 +1,312 @@
+# Translations template for PROJECT.
+# Copyright (C) 2011 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# <jacobo@gnu.org>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: GNU MediaGoblin\n"
+"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n"
+"POT-Creation-Date: 2011-08-08 22:53-0500\n"
+"PO-Revision-Date: 2011-08-10 20:30+0000\n"
+"Last-Translator: nvjacobo <jacobo@gnu.org>\n"
+"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/mediagoblin/team/es/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+"Language: es\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: mediagoblin/auth/forms.py:24 mediagoblin/auth/forms.py:46
+msgid "Username"
+msgstr "Nombre de Usuario"
+
+#: mediagoblin/auth/forms.py:29 mediagoblin/auth/forms.py:50
+msgid "Password"
+msgstr "Contraseña"
+
+#: mediagoblin/auth/forms.py:34
+msgid "Passwords must match."
+msgstr "Las contraseñas deben coincidir."
+
+#: mediagoblin/auth/forms.py:36
+msgid "Confirm password"
+msgstr "Confirme su contraseña"
+
+#: mediagoblin/auth/forms.py:39
+msgid "Email address"
+msgstr "Dirección de correo electrónico"
+
+#: mediagoblin/auth/views.py:40
+msgid "Sorry, registration is disabled on this instance."
+msgstr "Lo sentimos, el registro está deshabilitado en este momento."
+
+#: mediagoblin/auth/views.py:55
+msgid "Sorry, a user with that name already exists."
+msgstr "Lo sentimos, un usuario con ese nombre ya existe."
+
+#: mediagoblin/auth/views.py:152
+msgid ""
+"Your email address has been verified. You may now login, edit your profile, "
+"and submit images!"
+msgstr ""
+"Su dirección de correo electrónico ha sido verificada. Ahora puede ingresar,"
+" editar su perfil, y enviar las imágenes!"
+
+#: mediagoblin/auth/views.py:158
+msgid "The verification key or user id is incorrect"
+msgstr ""
+"La clave de la verificación o la identificación del usuario es incorrecta"
+
+#: mediagoblin/auth/views.py:179
+#: mediagoblin/templates/mediagoblin/auth/resent_verification_email.html:22
+msgid "Resent your verification email."
+msgstr "Reenvíe su correo electrónico de verificación"
+
+#: mediagoblin/edit/forms.py:26 mediagoblin/submit/forms.py:26
+msgid "Title"
+msgstr "Título"
+
+#: mediagoblin/edit/forms.py:29
+msgid "Slug"
+msgstr "Ficha"
+
+#: mediagoblin/edit/forms.py:30
+msgid "The slug can't be empty"
+msgstr "La ficha no puede estar vacia"
+
+#: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:31
+msgid "Tags"
+msgstr "Etiquetas"
+
+#: mediagoblin/edit/forms.py:38
+msgid "Bio"
+msgstr "Bio"
+
+#: mediagoblin/edit/forms.py:41
+msgid "Website"
+msgstr "Sitio web"
+
+#: mediagoblin/edit/forms.py:43
+msgid "Improperly formed URL"
+msgstr "URL de forma incorrecta"
+
+#: mediagoblin/edit/views.py:54
+msgid "An entry with that slug already exists for this user."
+msgstr "Una entrada con esa ficha ya existe para este usuario."
+
+#: mediagoblin/edit/views.py:75
+msgid "You are editing another user's media. Proceed with caution."
+msgstr ""
+"Usted está editando el contenido de otro usuario. Proceder con precaución."
+
+#: mediagoblin/edit/views.py:96
+msgid "You are editing a user's profile. Proceed with caution."
+msgstr "Usted está editando un perfil de usuario. Proceder con precaucións."
+
+#: mediagoblin/submit/forms.py:29
+msgid "File"
+msgstr "Archivo"
+
+#: mediagoblin/submit/views.py:45
+msgid "You must provide a file."
+msgstr "Usted debe proporcionar un archivo."
+
+#: mediagoblin/submit/views.py:48
+msgid "The file doesn't seem to be an image!"
+msgstr "El archivo no parece ser una imagen!"
+
+#: mediagoblin/submit/views.py:96
+msgid "Woohoo! Submitted!"
+msgstr "Woohoo! Enviado!"
+
+#: mediagoblin/templates/mediagoblin/base.html:22
+msgid "GNU MediaGoblin"
+msgstr "GNU MediaGoblin"
+
+#: mediagoblin/templates/mediagoblin/base.html:45
+msgid "Mediagoblin logo"
+msgstr "Mediagoblin logo"
+
+#: mediagoblin/templates/mediagoblin/base.html:51
+msgid "Submit media"
+msgstr "Enviar contenido"
+
+#: mediagoblin/templates/mediagoblin/base.html:62
+msgid "verify your email!"
+msgstr "Verifique su correo electrónico"
+
+#: mediagoblin/templates/mediagoblin/base.html:72
+msgid "Login"
+msgstr "Conectarse"
+
+#: mediagoblin/templates/mediagoblin/base.html:88
+msgid ""
+"Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a "
+"href=\"http://gnu.org/\">GNU project</a>"
+msgstr ""
+"Potenciado por <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a "
+"href=\"http://gnu.org/\">GNU project</a>"
+
+#: mediagoblin/templates/mediagoblin/root.html:21
+msgid "Welcome to GNU MediaGoblin!"
+msgstr "¡Bienvenido a GNU MediaGoblin!"
+
+#: mediagoblin/templates/mediagoblin/root.html:26
+msgid "Submit an item"
+msgstr "Enviar un item"
+
+#: mediagoblin/templates/mediagoblin/root.html:31
+#, python-format
+msgid "If you have an account, you can <a href=\"%(login_url)s\">Login</a>."
+msgstr ""
+"Si tiene una cuenta, puede iniciar sesión <a "
+"href=\"%(login_url)s\">Login</a>."
+
+#: mediagoblin/templates/mediagoblin/root.html:37
+#, python-format
+msgid ""
+"If you don't have an account, please <a "
+"href=\"%(register_url)s\">Register</a>."
+msgstr ""
+"Si no tienes una cuenta, por favor, <a "
+"href=\"%(register_url)s\">Regístrese</a> ."
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:26
+msgid "Log in"
+msgstr "Conectarse"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:29
+msgid "Login failed!"
+msgstr "El inicio de sesión fallo"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:34
+#: mediagoblin/templates/mediagoblin/auth/register.html:30
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:35
+#: mediagoblin/templates/mediagoblin/submit/start.html:32
+msgid "Submit"
+msgstr "Enviar"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:42
+msgid "Don't have an account yet?"
+msgstr "¿No tienes una cuenta?"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:45
+msgid "Create one here!"
+msgstr "Crea una aquí"
+
+#: mediagoblin/templates/mediagoblin/auth/register.html:27
+msgid "Create an account!"
+msgstr "Crea una cuenta!"
+
+#: 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 ""
+"Hola %(username)s , para activar su cuenta MediaGoblin GNU, abra ls "
+"siguiente URL en su navegador: %(verification_url)s "
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:29
+#, python-format
+msgid "Editing %(media_title)s"
+msgstr "Edición %(media_title)s "
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:36
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:37
+msgid "Save changes"
+msgstr "Salvar cambios"
+
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:29
+#, python-format
+msgid "Editing %(username)s's profile"
+msgstr "Edición %(username)s de perfil"
+
+#: mediagoblin/templates/mediagoblin/listings/tag.html:29
+msgid "Media tagged with:"
+msgstr "El contenido con la etiqueta:"
+
+#: mediagoblin/templates/mediagoblin/listings/tag.html:40
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:46
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:101
+msgid "atom feed"
+msgstr "feed Atom"
+
+#: mediagoblin/templates/mediagoblin/submit/start.html:26
+msgid "Submit yer media"
+msgstr "Envíe su contenido"
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:30
+#, python-format
+msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media"
+msgstr "Contenido de <a href=\"%(user_url)s\">%(username)s</a>'s"
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:51
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:30
+msgid "Sorry, no such user found."
+msgstr "Lo sentimos, no se ha encontrado el usuario."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:37
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:57
+msgid "Verification needed"
+msgstr "Verificación necesaria"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:40
+msgid "Almost done! Your account still needs to be verified."
+msgstr "Ya está casi hecho! Su cuenta tiene que ser verificada."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:45
+msgid ""
+"An email should arrive in a few moments with instructions on how to do so."
+msgstr ""
+"Un e-mail debe llegar en unos momentos con las instrucciones para hacerlo."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:49
+msgid "In case it doesn't:"
+msgstr "En caso de que no:"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:52
+msgid "Resend verification email"
+msgstr "Reenviar correo electrónico de verificación"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:60
+msgid ""
+"Someone has registered an account with this username, but it still has to be"
+" verified."
+msgstr ""
+"Alguien ha registrado una cuenta con este nombre de usuario, pero todavía "
+"tiene que ser verificado."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:66
+#, 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 ""
+"Si usted es esa persona, pero usted ha perdido su correo electrónico de "
+"verificación, usted puede reenviarlo <a href=\"%(login_url)s\">acceder</a>."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:76
+#, python-format
+msgid "%(username)s's profile"
+msgstr "Perfil de %(username)s's"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:84
+msgid "Edit profile"
+msgstr "Editar perfil"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:95
+#, python-format
+msgid "View all of %(username)s's media"
+msgstr "Ver todo el contenido de %(username)s's "
+
+
diff --git a/mediagoblin/i18n/fr/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/fr/LC_MESSAGES/mediagoblin.mo
new file mode 100644
index 00000000..a7daf2c9
--- /dev/null
+++ b/mediagoblin/i18n/fr/LC_MESSAGES/mediagoblin.mo
Binary files differ
diff --git a/mediagoblin/i18n/fr/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/fr/LC_MESSAGES/mediagoblin.po
new file mode 100644
index 00000000..4606bf47
--- /dev/null
+++ b/mediagoblin/i18n/fr/LC_MESSAGES/mediagoblin.po
@@ -0,0 +1,305 @@
+# Translations template for PROJECT.
+# Copyright (C) 2011 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# <marktraceur@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: GNU MediaGoblin\n"
+"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n"
+"POT-Creation-Date: 2011-08-08 22:53-0500\n"
+"PO-Revision-Date: 2011-08-10 21:24+0000\n"
+"Last-Translator: MarkTraceur <marktraceur@gmail.com>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+"Language: fr\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+
+#: mediagoblin/auth/forms.py:24 mediagoblin/auth/forms.py:46
+msgid "Username"
+msgstr ""
+
+#: mediagoblin/auth/forms.py:29 mediagoblin/auth/forms.py:50
+msgid "Password"
+msgstr ""
+
+#: mediagoblin/auth/forms.py:34
+msgid "Passwords must match."
+msgstr ""
+
+#: mediagoblin/auth/forms.py:36
+msgid "Confirm password"
+msgstr ""
+
+#: mediagoblin/auth/forms.py:39
+msgid "Email address"
+msgstr ""
+
+#: mediagoblin/auth/views.py:40
+msgid "Sorry, registration is disabled on this instance."
+msgstr ""
+
+#: mediagoblin/auth/views.py:55
+msgid "Sorry, a user with that name already exists."
+msgstr ""
+
+#: mediagoblin/auth/views.py:152
+msgid ""
+"Your email address has been verified. You may now login, edit your profile, "
+"and submit images!"
+msgstr ""
+
+#: mediagoblin/auth/views.py:158
+msgid "The verification key or user id is incorrect"
+msgstr ""
+
+#: mediagoblin/auth/views.py:179
+#: mediagoblin/templates/mediagoblin/auth/resent_verification_email.html:22
+msgid "Resent your verification email."
+msgstr "Nous avons renvoyé votre e-mail de vérification."
+
+#: mediagoblin/edit/forms.py:26 mediagoblin/submit/forms.py:26
+msgid "Title"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:29
+msgid "Slug"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:30
+msgid "The slug can't be empty"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:31
+msgid "Tags"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:38
+msgid "Bio"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:41
+msgid "Website"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:43
+msgid "Improperly formed URL"
+msgstr ""
+
+#: mediagoblin/edit/views.py:54
+msgid "An entry with that slug already exists for this user."
+msgstr ""
+
+#: mediagoblin/edit/views.py:75
+msgid "You are editing another user's media. Proceed with caution."
+msgstr ""
+
+#: mediagoblin/edit/views.py:96
+msgid "You are editing a user's profile. Proceed with caution."
+msgstr ""
+
+#: mediagoblin/submit/forms.py:29
+msgid "File"
+msgstr ""
+
+#: mediagoblin/submit/views.py:45
+msgid "You must provide a file."
+msgstr ""
+
+#: mediagoblin/submit/views.py:48
+msgid "The file doesn't seem to be an image!"
+msgstr ""
+
+#: mediagoblin/submit/views.py:96
+msgid "Woohoo! Submitted!"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:22
+msgid "GNU MediaGoblin"
+msgstr "GNU MediaGoblin"
+
+#: mediagoblin/templates/mediagoblin/base.html:45
+msgid "Mediagoblin logo"
+msgstr "logo de MediaGoblin"
+
+#: mediagoblin/templates/mediagoblin/base.html:51
+msgid "Submit media"
+msgstr "Soumettez des médias"
+
+#: mediagoblin/templates/mediagoblin/base.html:62
+msgid "verify your email!"
+msgstr "vérifiez votre addresse e-mail"
+
+#: mediagoblin/templates/mediagoblin/base.html:72
+msgid "Login"
+msgstr "Connexion"
+
+#: mediagoblin/templates/mediagoblin/base.html:88
+msgid ""
+"Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a "
+"href=\"http://gnu.org/\">GNU project</a>"
+msgstr ""
+"Alimenté par <a href=\"http://mediagoblin.org\">MediaGoblin</a>, un <a "
+"href=\"http://gnu.org/\">projet GNU</a>"
+
+#: mediagoblin/templates/mediagoblin/root.html:21
+msgid "Welcome to GNU MediaGoblin!"
+msgstr "Bienvenue à GNU MediaGoblin!"
+
+#: mediagoblin/templates/mediagoblin/root.html:26
+msgid "Submit an item"
+msgstr "Soumettez un fichier"
+
+#: mediagoblin/templates/mediagoblin/root.html:31
+#, python-format
+msgid "If you have an account, you can <a href=\"%(login_url)s\">Login</a>."
+msgstr ""
+"Si vous avez un compte, vous pouvez <a href=\"%(login_url)s\">Connecter</a>."
+
+#: mediagoblin/templates/mediagoblin/root.html:37
+#, python-format
+msgid ""
+"If you don't have an account, please <a "
+"href=\"%(register_url)s\">Register</a>."
+msgstr ""
+"Si vous n'avez pas un compte, s'il vous plaît, <a "
+"href=\"%(register_url)s\">vous inscrivez</a>."
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:26
+msgid "Log in"
+msgstr "Connexion"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:29
+msgid "Login failed!"
+msgstr "Connexion manqué"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:34
+#: mediagoblin/templates/mediagoblin/auth/register.html:30
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:35
+#: mediagoblin/templates/mediagoblin/submit/start.html:32
+msgid "Submit"
+msgstr "Soumettez"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:42
+msgid "Don't have an account yet?"
+msgstr "N'avez-vous toujours un compte?"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:45
+msgid "Create one here!"
+msgstr "En créez un ici!"
+
+#: mediagoblin/templates/mediagoblin/auth/register.html:27
+msgid "Create an account!"
+msgstr "Créez un compte!"
+
+#: 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 ""
+"Bonjour, %(username)s,\n"
+"\n"
+"pour activer votre compte de GNU MediaGoblin, ouvrez l'URL suite avec votre navigateur web:\n"
+"\n"
+"%(verification_url)s"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:29
+#, python-format
+msgid "Editing %(media_title)s"
+msgstr "On édit %(media_title)s"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:36
+msgid "Cancel"
+msgstr "Annulez"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:37
+msgid "Save changes"
+msgstr "Enregistrez les modifications"
+
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:29
+#, python-format
+msgid "Editing %(username)s's profile"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/listings/tag.html:29
+msgid "Media tagged with:"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/listings/tag.html:40
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:46
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:101
+msgid "atom feed"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/submit/start.html:26
+msgid "Submit yer media"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:30
+#, python-format
+msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:51
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:30
+msgid "Sorry, no such user found."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:37
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:57
+msgid "Verification needed"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:40
+msgid "Almost done! Your account still needs to be verified."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:45
+msgid ""
+"An email should arrive in a few moments with instructions on how to do so."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:49
+msgid "In case it doesn't:"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:52
+msgid "Resend verification email"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:60
+msgid ""
+"Someone has registered an account with this username, but it still has to be"
+" verified."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:66
+#, 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/user_pages/user.html:76
+#, python-format
+msgid "%(username)s's profile"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:84
+msgid "Edit profile"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:95
+#, python-format
+msgid "View all of %(username)s's media"
+msgstr ""
+
+
diff --git a/mediagoblin/i18n/nn_NO/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/nn_NO/LC_MESSAGES/mediagoblin.mo
new file mode 100644
index 00000000..8911785f
--- /dev/null
+++ b/mediagoblin/i18n/nn_NO/LC_MESSAGES/mediagoblin.mo
Binary files differ
diff --git a/mediagoblin/i18n/nn_NO/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/nn_NO/LC_MESSAGES/mediagoblin.po
new file mode 100644
index 00000000..bd00fd1f
--- /dev/null
+++ b/mediagoblin/i18n/nn_NO/LC_MESSAGES/mediagoblin.po
@@ -0,0 +1,308 @@
+# Translations template for PROJECT.
+# Copyright (C) 2011 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# <odin.omdal@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: GNU MediaGoblin\n"
+"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n"
+"POT-Creation-Date: 2011-08-08 22:53-0500\n"
+"PO-Revision-Date: 2011-08-10 21:23+0000\n"
+"Last-Translator: velmont <odin.omdal@gmail.com>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+"Language: nn_NO\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: mediagoblin/auth/forms.py:24 mediagoblin/auth/forms.py:46
+msgid "Username"
+msgstr "Brukarnamn"
+
+#: mediagoblin/auth/forms.py:29 mediagoblin/auth/forms.py:50
+msgid "Password"
+msgstr "Passord"
+
+#: mediagoblin/auth/forms.py:34
+msgid "Passwords must match."
+msgstr "Passorda må vera like."
+
+#: mediagoblin/auth/forms.py:36
+msgid "Confirm password"
+msgstr "Gjenta passord"
+
+#: mediagoblin/auth/forms.py:39
+msgid "Email address"
+msgstr "E-postadresse"
+
+#: mediagoblin/auth/views.py:40
+msgid "Sorry, registration is disabled on this instance."
+msgstr "Registrering er slege av. Orsak."
+
+#: mediagoblin/auth/views.py:55
+msgid "Sorry, a user with that name already exists."
+msgstr "Ein konto med dette brukarnamnet finst allereide."
+
+#: mediagoblin/auth/views.py:152
+msgid ""
+"Your email address has been verified. You may now login, edit your profile, "
+"and submit images!"
+msgstr ""
+"E-postadressa di, og dimed kontoen din er stadfesta. Du kan no logga inn, "
+"endra profilen din og lasta opp filer."
+
+#: mediagoblin/auth/views.py:158
+msgid "The verification key or user id is incorrect"
+msgstr "Stadfestingsnykelen eller brukar-ID-en din er feil."
+
+#: mediagoblin/auth/views.py:179
+#: mediagoblin/templates/mediagoblin/auth/resent_verification_email.html:22
+msgid "Resent your verification email."
+msgstr "Send ein ny stadfestingsepost."
+
+#: mediagoblin/edit/forms.py:26 mediagoblin/submit/forms.py:26
+msgid "Title"
+msgstr "Tittel"
+
+#: mediagoblin/edit/forms.py:29
+msgid "Slug"
+msgstr "Adressetittel"
+
+#: mediagoblin/edit/forms.py:30
+msgid "The slug can't be empty"
+msgstr "Adressetittelen kan ikkje vera tom"
+
+#: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:31
+msgid "Tags"
+msgstr "Merkelappar"
+
+#: mediagoblin/edit/forms.py:38
+msgid "Bio"
+msgstr "Presentasjon"
+
+#: mediagoblin/edit/forms.py:41
+msgid "Website"
+msgstr "Heimeside"
+
+#: mediagoblin/edit/forms.py:43
+msgid "Improperly formed URL"
+msgstr "Ugyldeg URL"
+
+#: mediagoblin/edit/views.py:54
+msgid "An entry with that slug already exists for this user."
+msgstr "Eit innlegg med denne adressetittelen finst allereie."
+
+#: mediagoblin/edit/views.py:75
+msgid "You are editing another user's media. Proceed with caution."
+msgstr "Ver forsiktig, du redigerer ein annan konto sitt innlegg."
+
+#: mediagoblin/edit/views.py:96
+msgid "You are editing a user's profile. Proceed with caution."
+msgstr "Ver forsiktig, du redigerer ein annan konto sin profil."
+
+#: mediagoblin/submit/forms.py:29
+msgid "File"
+msgstr "Fil"
+
+#: mediagoblin/submit/views.py:45
+msgid "You must provide a file."
+msgstr "Du må velja ei fil."
+
+#: mediagoblin/submit/views.py:48
+msgid "The file doesn't seem to be an image!"
+msgstr "Fila verkar ikkje å vera ei gyldig biletefil."
+
+#: mediagoblin/submit/views.py:96
+msgid "Woohoo! Submitted!"
+msgstr "Johoo! Opplasta!"
+
+#: mediagoblin/templates/mediagoblin/base.html:22
+msgid "GNU MediaGoblin"
+msgstr "GNU MediaGoblin"
+
+#: mediagoblin/templates/mediagoblin/base.html:45
+msgid "Mediagoblin logo"
+msgstr "MediaGoblin-logo"
+
+#: mediagoblin/templates/mediagoblin/base.html:51
+msgid "Submit media"
+msgstr "Last opp"
+
+#: mediagoblin/templates/mediagoblin/base.html:62
+msgid "verify your email!"
+msgstr "Stadfest epostadressa di"
+
+#: mediagoblin/templates/mediagoblin/base.html:72
+msgid "Login"
+msgstr "Logg inn"
+
+#: mediagoblin/templates/mediagoblin/base.html:88
+msgid ""
+"Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a "
+"href=\"http://gnu.org/\">GNU project</a>"
+msgstr ""
+"Driven av <a href=\"http://mediagoblin.org\">MediaGoblin</a>, eit <a "
+"href=\"http://gnu.org/\">GNU-prosjekt</a>"
+
+#: mediagoblin/templates/mediagoblin/root.html:21
+msgid "Welcome to GNU MediaGoblin!"
+msgstr "Velkomen til GNU MediaGoblin!"
+
+#: mediagoblin/templates/mediagoblin/root.html:26
+msgid "Submit an item"
+msgstr "Last opp"
+
+#: mediagoblin/templates/mediagoblin/root.html:31
+#, python-format
+msgid "If you have an account, you can <a href=\"%(login_url)s\">Login</a>."
+msgstr "Har du ein konto? <a href=\"%(login_url)s\">Logg inn</a>."
+
+#: mediagoblin/templates/mediagoblin/root.html:37
+#, python-format
+msgid ""
+"If you don't have an account, please <a "
+"href=\"%(register_url)s\">Register</a>."
+msgstr "Har du ingen konto? <a href=\"%(register_url)s\">Registrer deg</a>."
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:26
+msgid "Log in"
+msgstr "Logg inn"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:29
+msgid "Login failed!"
+msgstr "Innlogging feila!"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:34
+#: mediagoblin/templates/mediagoblin/auth/register.html:30
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:35
+#: mediagoblin/templates/mediagoblin/submit/start.html:32
+msgid "Submit"
+msgstr "Send"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:42
+msgid "Don't have an account yet?"
+msgstr "Har du ingen konto?"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:45
+msgid "Create one here!"
+msgstr "Lag ein!"
+
+#: mediagoblin/templates/mediagoblin/auth/register.html:27
+msgid "Create an account!"
+msgstr "Lag ein konto."
+
+#: 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 ""
+"Hei %(username)s,\n"
+"\n"
+"opna den følgjande adressa i netlesaren din for å aktivera kontoen din:\n"
+"\n"
+"%(verification_url)s"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:29
+#, python-format
+msgid "Editing %(media_title)s"
+msgstr "Redigerer %(media_title)s"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:36
+msgid "Cancel"
+msgstr "Avbryt"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:37
+msgid "Save changes"
+msgstr "Lagra"
+
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:29
+#, python-format
+msgid "Editing %(username)s's profile"
+msgstr "Redigerar profilen til %(username)s"
+
+#: mediagoblin/templates/mediagoblin/listings/tag.html:29
+msgid "Media tagged with:"
+msgstr "Merkelappar:"
+
+#: mediagoblin/templates/mediagoblin/listings/tag.html:40
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:46
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:101
+msgid "atom feed"
+msgstr "atom-feed"
+
+#: mediagoblin/templates/mediagoblin/submit/start.html:26
+msgid "Submit yer media"
+msgstr "Last opp"
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:30
+#, python-format
+msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media"
+msgstr "<a href=\"%(user_url)s\">%(username)s</a> sin mediafiler"
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:51
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:30
+msgid "Sorry, no such user found."
+msgstr "Fann ingen slik brukar"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:37
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:57
+msgid "Verification needed"
+msgstr "Treng stadfesting"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:40
+msgid "Almost done! Your account still needs to be verified."
+msgstr "Nesten klart. Du treng berre stadfesta kontoen din."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:45
+msgid ""
+"An email should arrive in a few moments with instructions on how to do so."
+msgstr "Ein epost med instruksjonar kjem straks."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:49
+msgid "In case it doesn't:"
+msgstr "I tilfelle det ikkje skjer:"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:52
+msgid "Resend verification email"
+msgstr "Send ein ny epost"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:60
+msgid ""
+"Someone has registered an account with this username, but it still has to be"
+" verified."
+msgstr ""
+"Det finst allereie ein konto med det brukarnamnet, men den kontoen treng "
+"stadfesting."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:66
+#, 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 ""
+"Viss dette er deg, kan du <a href=\"%(login_url)s\">logga inn</a> for å få "
+"tilsendt ny epost med stadfestingslenkje."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:76
+#, python-format
+msgid "%(username)s's profile"
+msgstr "%(username)s sin profil"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:84
+msgid "Edit profile"
+msgstr "Endra profil"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:95
+#, python-format
+msgid "View all of %(username)s's media"
+msgstr "Sjå all media frå %(username)s"
+
+
diff --git a/mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.mo
new file mode 100644
index 00000000..4dc4ab5f
--- /dev/null
+++ b/mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.mo
Binary files differ
diff --git a/mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.po
new file mode 100644
index 00000000..43f65af6
--- /dev/null
+++ b/mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.po
@@ -0,0 +1,310 @@
+# Translations template for PROJECT.
+# Copyright (C) 2011 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# <snd.noise@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: GNU MediaGoblin\n"
+"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n"
+"POT-Creation-Date: 2011-08-08 22:53-0500\n"
+"PO-Revision-Date: 2011-08-10 23:16+0000\n"
+"Last-Translator: osc <snd.noise@gmail.com>\n"
+"Language-Team: Portuguese (Brazilian) (http://www.transifex.net/projects/p/mediagoblin/team/pt_BR/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+"Language: pt_BR\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+
+#: mediagoblin/auth/forms.py:24 mediagoblin/auth/forms.py:46
+msgid "Username"
+msgstr "Nome de Usuário"
+
+#: mediagoblin/auth/forms.py:29 mediagoblin/auth/forms.py:50
+msgid "Password"
+msgstr "Senha"
+
+#: mediagoblin/auth/forms.py:34
+msgid "Passwords must match."
+msgstr "Senhas devem ser iguais."
+
+#: mediagoblin/auth/forms.py:36
+msgid "Confirm password"
+msgstr "Confirmar senha"
+
+#: mediagoblin/auth/forms.py:39
+msgid "Email address"
+msgstr "Endereço de email"
+
+#: mediagoblin/auth/views.py:40
+msgid "Sorry, registration is disabled on this instance."
+msgstr "Desculpa, o registro está desativado neste momento."
+
+#: mediagoblin/auth/views.py:55
+msgid "Sorry, a user with that name already exists."
+msgstr "Desculpe, um usuário com este nome já existe."
+
+#: mediagoblin/auth/views.py:152
+msgid ""
+"Your email address has been verified. You may now login, edit your profile, "
+"and submit images!"
+msgstr ""
+"O seu endereço de e-mail foi verificado. Você pode agora fazer login, editar"
+" seu perfil, e enviar imagens!"
+
+#: mediagoblin/auth/views.py:158
+msgid "The verification key or user id is incorrect"
+msgstr "A chave de verificação ou nome usuário estão incorretos."
+
+#: mediagoblin/auth/views.py:179
+#: mediagoblin/templates/mediagoblin/auth/resent_verification_email.html:22
+msgid "Resent your verification email."
+msgstr "O email de verificação foi reenviado."
+
+#: mediagoblin/edit/forms.py:26 mediagoblin/submit/forms.py:26
+msgid "Title"
+msgstr "Título"
+
+#: mediagoblin/edit/forms.py:29
+msgid "Slug"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:30
+msgid "The slug can't be empty"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:31
+msgid "Tags"
+msgstr "Tags"
+
+#: mediagoblin/edit/forms.py:38
+msgid "Bio"
+msgstr "Biográfia"
+
+#: mediagoblin/edit/forms.py:41
+msgid "Website"
+msgstr "Website"
+
+#: mediagoblin/edit/forms.py:43
+msgid "Improperly formed URL"
+msgstr ""
+
+#: mediagoblin/edit/views.py:54
+msgid "An entry with that slug already exists for this user."
+msgstr ""
+
+#: mediagoblin/edit/views.py:75
+msgid "You are editing another user's media. Proceed with caution."
+msgstr ""
+
+#: mediagoblin/edit/views.py:96
+msgid "You are editing a user's profile. Proceed with caution."
+msgstr ""
+
+#: mediagoblin/submit/forms.py:29
+msgid "File"
+msgstr "Arquivo"
+
+#: mediagoblin/submit/views.py:45
+msgid "You must provide a file."
+msgstr "Você deve fornecer um arquivo."
+
+#: mediagoblin/submit/views.py:48
+msgid "The file doesn't seem to be an image!"
+msgstr "O arquivo não parece ser uma imagem!"
+
+#: mediagoblin/submit/views.py:96
+msgid "Woohoo! Submitted!"
+msgstr "Eba! Enviado!"
+
+#: mediagoblin/templates/mediagoblin/base.html:22
+msgid "GNU MediaGoblin"
+msgstr "GNU MediaGoblin"
+
+#: mediagoblin/templates/mediagoblin/base.html:45
+msgid "Mediagoblin logo"
+msgstr "Logo de Mediagoblin"
+
+#: mediagoblin/templates/mediagoblin/base.html:51
+msgid "Submit media"
+msgstr "Enviar mídia"
+
+#: mediagoblin/templates/mediagoblin/base.html:62
+msgid "verify your email!"
+msgstr "Verifique seu email!"
+
+#: mediagoblin/templates/mediagoblin/base.html:72
+msgid "Login"
+msgstr "Login"
+
+#: mediagoblin/templates/mediagoblin/base.html:88
+msgid ""
+"Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a "
+"href=\"http://gnu.org/\">GNU project</a>"
+msgstr ""
+"Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a "
+"href=\"http://gnu.org/\">GNU project</a>"
+
+#: mediagoblin/templates/mediagoblin/root.html:21
+msgid "Welcome to GNU MediaGoblin!"
+msgstr "Bemvindo a GNU Mediagoblin!"
+
+#: mediagoblin/templates/mediagoblin/root.html:26
+msgid "Submit an item"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/root.html:31
+#, python-format
+msgid "If you have an account, you can <a href=\"%(login_url)s\">Login</a>."
+msgstr "Se você tem conta, você pode <a href=\"%(login_url)s\">Entrar</a> ."
+
+#: mediagoblin/templates/mediagoblin/root.html:37
+#, python-format
+msgid ""
+"If you don't have an account, please <a "
+"href=\"%(register_url)s\">Register</a>."
+msgstr ""
+"Se você não tem conta, por favor <a href=\"%(register_url)s\">Registrar</a> "
+"."
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:26
+msgid "Log in"
+msgstr "Entrar"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:29
+msgid "Login failed!"
+msgstr "Login falhou!"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:34
+#: mediagoblin/templates/mediagoblin/auth/register.html:30
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:35
+#: mediagoblin/templates/mediagoblin/submit/start.html:32
+msgid "Submit"
+msgstr "Enviar"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:42
+msgid "Don't have an account yet?"
+msgstr "Ainda não tem conta?"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:45
+msgid "Create one here!"
+msgstr "Crie uma aqui!"
+
+#: mediagoblin/templates/mediagoblin/auth/register.html:27
+msgid "Create an account!"
+msgstr "Criar uma conta!"
+
+#: 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 ""
+"Olá %(username)s,\n"
+"\n"
+"Para ativar sua conta GNU MediaGoblin, visite este endereço no seu navegador:\n"
+"\n"
+"%(verification_url)s"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:29
+#, python-format
+msgid "Editing %(media_title)s"
+msgstr "Editando %(media_title)s"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:36
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:37
+msgid "Save changes"
+msgstr "Salvar mudanças"
+
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:29
+#, python-format
+msgid "Editing %(username)s's profile"
+msgstr "Editando perfil de %(username)s"
+
+#: mediagoblin/templates/mediagoblin/listings/tag.html:29
+msgid "Media tagged with:"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/listings/tag.html:40
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:46
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:101
+msgid "atom feed"
+msgstr "atom feed"
+
+#: mediagoblin/templates/mediagoblin/submit/start.html:26
+msgid "Submit yer media"
+msgstr "Envie sua mídia"
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:30
+#, python-format
+msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:51
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:30
+msgid "Sorry, no such user found."
+msgstr "Desculpe, tal usuário não encontrado."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:37
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:57
+msgid "Verification needed"
+msgstr "Verificação necessária"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:40
+msgid "Almost done! Your account still needs to be verified."
+msgstr "Quase pronto! Sua conta precisa de verificação."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:45
+msgid ""
+"An email should arrive in a few moments with instructions on how to do so."
+msgstr "Receberá um email com instruções de como fazer."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:49
+msgid "In case it doesn't:"
+msgstr "Caso contrário:"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:52
+msgid "Resend verification email"
+msgstr "Reenviar email de verificação"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:60
+msgid ""
+"Someone has registered an account with this username, but it still has to be"
+" verified."
+msgstr ""
+"Alguém já registrou uma conta com este nome, mas ainda tem que ser "
+"verificada."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:66
+#, 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 ""
+"Se você é essa pessoa, mas você perdeu seu e-mail de verificação, você pode "
+"<a href=\"%(login_url)s\">efetuar login</a> e reenviá-la."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:76
+#, python-format
+msgid "%(username)s's profile"
+msgstr "Perfil de %(username)s"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:84
+msgid "Edit profile"
+msgstr "Editar perfil"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:95
+#, python-format
+msgid "View all of %(username)s's media"
+msgstr ""
+
+
diff --git a/mediagoblin/i18n/ro/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/ro/LC_MESSAGES/mediagoblin.mo
new file mode 100644
index 00000000..361846d4
--- /dev/null
+++ b/mediagoblin/i18n/ro/LC_MESSAGES/mediagoblin.mo
Binary files differ
diff --git a/mediagoblin/i18n/ro/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/ro/LC_MESSAGES/mediagoblin.po
new file mode 100644
index 00000000..feb261c8
--- /dev/null
+++ b/mediagoblin/i18n/ro/LC_MESSAGES/mediagoblin.po
@@ -0,0 +1,314 @@
+# Translations template for PROJECT.
+# Copyright (C) 2011 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# <gapop@hotmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: GNU MediaGoblin\n"
+"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n"
+"POT-Creation-Date: 2011-08-08 22:53-0500\n"
+"PO-Revision-Date: 2011-08-10 21:52+0000\n"
+"Last-Translator: gap <gapop@hotmail.com>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+"Language: ro\n"
+"Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1))\n"
+
+#: mediagoblin/auth/forms.py:24 mediagoblin/auth/forms.py:46
+msgid "Username"
+msgstr "Nume de utilizator"
+
+#: mediagoblin/auth/forms.py:29 mediagoblin/auth/forms.py:50
+msgid "Password"
+msgstr "Parolă"
+
+#: mediagoblin/auth/forms.py:34
+msgid "Passwords must match."
+msgstr "Parolele trebuie să fie identice."
+
+#: mediagoblin/auth/forms.py:36
+msgid "Confirm password"
+msgstr "Reintroduceți parola"
+
+#: mediagoblin/auth/forms.py:39
+msgid "Email address"
+msgstr "Adresa de e-mail"
+
+#: mediagoblin/auth/views.py:40
+msgid "Sorry, registration is disabled on this instance."
+msgstr "Ne pare rău, dar înscrierile sunt dezactivate pe această instanță."
+
+#: mediagoblin/auth/views.py:55
+msgid "Sorry, a user with that name already exists."
+msgstr "Ne pare rău, există deja un utilizator cu același nume."
+
+#: mediagoblin/auth/views.py:152
+msgid ""
+"Your email address has been verified. You may now login, edit your profile, "
+"and submit images!"
+msgstr ""
+"Adresa dvs. de e-mail a fost confirmată. Puteți să vă autentificați, să vă "
+"modificați profilul și să trimiteți imagini!"
+
+#: mediagoblin/auth/views.py:158
+msgid "The verification key or user id is incorrect"
+msgstr "Cheie de verificare sau user ID incorect."
+
+#: mediagoblin/auth/views.py:179
+#: mediagoblin/templates/mediagoblin/auth/resent_verification_email.html:22
+msgid "Resent your verification email."
+msgstr "E-mail-ul de verificare a fost retrimis."
+
+#: mediagoblin/edit/forms.py:26 mediagoblin/submit/forms.py:26
+msgid "Title"
+msgstr "Titlu"
+
+#: mediagoblin/edit/forms.py:29
+msgid "Slug"
+msgstr "Identificator"
+
+#: mediagoblin/edit/forms.py:30
+msgid "The slug can't be empty"
+msgstr "Identificatorul nu poate să lipsească"
+
+#: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:31
+msgid "Tags"
+msgstr "Etichete"
+
+#: mediagoblin/edit/forms.py:38
+msgid "Bio"
+msgstr "Biografie"
+
+#: mediagoblin/edit/forms.py:41
+msgid "Website"
+msgstr "Sit Web"
+
+#: mediagoblin/edit/forms.py:43
+msgid "Improperly formed URL"
+msgstr "Adresă URL incorectă"
+
+#: mediagoblin/edit/views.py:54
+msgid "An entry with that slug already exists for this user."
+msgstr ""
+"Există deja un entry cu același identificator pentru acest utilizator."
+
+#: mediagoblin/edit/views.py:75
+msgid "You are editing another user's media. Proceed with caution."
+msgstr "Editați fișierul unui alt utilizator. Se recomandă prudență."
+
+#: mediagoblin/edit/views.py:96
+msgid "You are editing a user's profile. Proceed with caution."
+msgstr "Editați profilul unui utilizator. Se recomandă prudență."
+
+#: mediagoblin/submit/forms.py:29
+msgid "File"
+msgstr "Fișier"
+
+#: mediagoblin/submit/views.py:45
+msgid "You must provide a file."
+msgstr "Trebuie să selectați un fișier."
+
+#: mediagoblin/submit/views.py:48
+msgid "The file doesn't seem to be an image!"
+msgstr "Fișierul nu pare a fi o imagine!"
+
+#: mediagoblin/submit/views.py:96
+msgid "Woohoo! Submitted!"
+msgstr "Gata, trimis!"
+
+#: mediagoblin/templates/mediagoblin/base.html:22
+msgid "GNU MediaGoblin"
+msgstr "GNU MediaGoblin"
+
+#: mediagoblin/templates/mediagoblin/base.html:45
+msgid "Mediagoblin logo"
+msgstr "Logo MediaGoblin"
+
+#: mediagoblin/templates/mediagoblin/base.html:51
+msgid "Submit media"
+msgstr "Transmiteți fișier"
+
+#: mediagoblin/templates/mediagoblin/base.html:62
+msgid "verify your email!"
+msgstr "verificați e-mail-ul!"
+
+#: mediagoblin/templates/mediagoblin/base.html:72
+msgid "Login"
+msgstr "Autentificare"
+
+#: mediagoblin/templates/mediagoblin/base.html:88
+msgid ""
+"Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a "
+"href=\"http://gnu.org/\">GNU project</a>"
+msgstr ""
+"Construit cu <a href=\"http://mediagoblin.org\">MediaGoblin</a>, un <a "
+"href=\"http://gnu.org/\">proiect GNU</a>"
+
+#: mediagoblin/templates/mediagoblin/root.html:21
+msgid "Welcome to GNU MediaGoblin!"
+msgstr "Bun venit la GNU MediaGoblin!"
+
+#: mediagoblin/templates/mediagoblin/root.html:26
+msgid "Submit an item"
+msgstr "Trimite un fișier"
+
+#: mediagoblin/templates/mediagoblin/root.html:31
+#, python-format
+msgid "If you have an account, you can <a href=\"%(login_url)s\">Login</a>."
+msgstr ""
+"Dacă aveți deja un cont, vă puteți <a "
+"href=\"%(login_url)s\">autentifica</a>."
+
+#: mediagoblin/templates/mediagoblin/root.html:37
+#, python-format
+msgid ""
+"If you don't have an account, please <a "
+"href=\"%(register_url)s\">Register</a>."
+msgstr ""
+"Dacă nu aveți cont, vă rugăm să vă <a "
+"href=\"%(register_url)s\">înregistrați</a>."
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:26
+msgid "Log in"
+msgstr "Autentificare"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:29
+msgid "Login failed!"
+msgstr "Autentificare nereușită!"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:34
+#: mediagoblin/templates/mediagoblin/auth/register.html:30
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:35
+#: mediagoblin/templates/mediagoblin/submit/start.html:32
+msgid "Submit"
+msgstr "Trimite"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:42
+msgid "Don't have an account yet?"
+msgstr "Nu aveți un cont?"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:45
+msgid "Create one here!"
+msgstr "Creați-l aici!"
+
+#: mediagoblin/templates/mediagoblin/auth/register.html:27
+msgid "Create an account!"
+msgstr "Creați un cont!"
+
+#: 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 ""
+"Bună, %(username)s,\n"
+"\n"
+"pentru activarea contului tău GNU MediaGoblin, accesează adresa următoare:\n"
+"\n"
+"%(verification_url)s"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:29
+#, python-format
+msgid "Editing %(media_title)s"
+msgstr "Editare %(media_title)s"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:36
+msgid "Cancel"
+msgstr "Anulare"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:37
+msgid "Save changes"
+msgstr "Salvează modificările"
+
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:29
+#, python-format
+msgid "Editing %(username)s's profile"
+msgstr "Editare profil %(username)s"
+
+#: mediagoblin/templates/mediagoblin/listings/tag.html:29
+msgid "Media tagged with:"
+msgstr "Etichete:"
+
+#: mediagoblin/templates/mediagoblin/listings/tag.html:40
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:46
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:101
+msgid "atom feed"
+msgstr "flux atom"
+
+#: mediagoblin/templates/mediagoblin/submit/start.html:26
+msgid "Submit yer media"
+msgstr "Trimite fișierele tale"
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:30
+#, python-format
+msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media"
+msgstr "Fișierele lui <a href=\"%(user_url)s\">%(username)s</a>"
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:51
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:30
+msgid "Sorry, no such user found."
+msgstr "Ne pare rău, nu am găsit utilizatorul căutat."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:37
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:57
+msgid "Verification needed"
+msgstr "Confirmare necesară"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:40
+msgid "Almost done! Your account still needs to be verified."
+msgstr "Aproape gata! Este necesară confirmarea contului dvs."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:45
+msgid ""
+"An email should arrive in a few moments with instructions on how to do so."
+msgstr "Veți primi în scurt timp un mesaj prin e-mail cu instrucțiuni."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:49
+msgid "In case it doesn't:"
+msgstr "Dacă nu primiți mesajul:"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:52
+msgid "Resend verification email"
+msgstr "Retrimite mesajul de verificare"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:60
+msgid ""
+"Someone has registered an account with this username, but it still has to be"
+" verified."
+msgstr ""
+"Cineva s-a înscris pe site cu acest nume de utilizator, dar nu a fost "
+"confirmat încă."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:66
+#, 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 ""
+"Dacă dvs. sunteți persoana respectivă și nu mai aveți e-mail-ul de "
+"verificare, puteți să vă <a href=\"%(login_url)s\">autentificați</a> pentru "
+"a-l retrimite."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:76
+#, python-format
+msgid "%(username)s's profile"
+msgstr "Profil %(username)s"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:84
+msgid "Edit profile"
+msgstr "Editare profil"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:95
+#, python-format
+msgid "View all of %(username)s's media"
+msgstr "Toate fișierele lui %(username)s"
+
+
diff --git a/mediagoblin/i18n/sl/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/sl/LC_MESSAGES/mediagoblin.mo
new file mode 100644
index 00000000..a70b1fef
--- /dev/null
+++ b/mediagoblin/i18n/sl/LC_MESSAGES/mediagoblin.mo
Binary files differ
diff --git a/mediagoblin/i18n/sl/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/sl/LC_MESSAGES/mediagoblin.po
new file mode 100644
index 00000000..bf2dd4fa
--- /dev/null
+++ b/mediagoblin/i18n/sl/LC_MESSAGES/mediagoblin.po
@@ -0,0 +1,309 @@
+# Translations template for PROJECT.
+# Copyright (C) 2011 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Jure Repinc <jlp@holodeck1.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: GNU MediaGoblin\n"
+"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n"
+"POT-Creation-Date: 2011-08-08 22:53-0500\n"
+"PO-Revision-Date: 2011-08-10 21:28+0000\n"
+"Last-Translator: JLP <jlp@holodeck1.com>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+"Language: sl\n"
+"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3)\n"
+
+#: mediagoblin/auth/forms.py:24 mediagoblin/auth/forms.py:46
+msgid "Username"
+msgstr "Uporabniško ime"
+
+#: mediagoblin/auth/forms.py:29 mediagoblin/auth/forms.py:50
+msgid "Password"
+msgstr "Geslo"
+
+#: mediagoblin/auth/forms.py:34
+msgid "Passwords must match."
+msgstr "Gesli morata biti enaki."
+
+#: mediagoblin/auth/forms.py:36
+msgid "Confirm password"
+msgstr "Potrdite geslo"
+
+#: mediagoblin/auth/forms.py:39
+msgid "Email address"
+msgstr "E-poštni naslov"
+
+#: mediagoblin/auth/views.py:40
+msgid "Sorry, registration is disabled on this instance."
+msgstr "Oprostite, prijava za ta izvod ni omogočena."
+
+#: mediagoblin/auth/views.py:55
+msgid "Sorry, a user with that name already exists."
+msgstr "Oprostite, uporabnik s tem imenom že obstaja."
+
+#: mediagoblin/auth/views.py:152
+msgid ""
+"Your email address has been verified. You may now login, edit your profile, "
+"and submit images!"
+msgstr ""
+"Vaš e-poštni naslov je bil potrjen. Sedaj se lahko prijavite, uredite svoj "
+"profil in pošljete slike."
+
+#: mediagoblin/auth/views.py:158
+msgid "The verification key or user id is incorrect"
+msgstr "Potrditveni ključ ali uporabniška identifikacija je napačna"
+
+#: mediagoblin/auth/views.py:179
+#: mediagoblin/templates/mediagoblin/auth/resent_verification_email.html:22
+msgid "Resent your verification email."
+msgstr "Ponovno pošiljanje potrditvene e-pošte."
+
+#: mediagoblin/edit/forms.py:26 mediagoblin/submit/forms.py:26
+msgid "Title"
+msgstr "Naslov"
+
+#: mediagoblin/edit/forms.py:29
+msgid "Slug"
+msgstr "Oznaka"
+
+#: mediagoblin/edit/forms.py:30
+msgid "The slug can't be empty"
+msgstr "Oznaka ne sme biti prazna"
+
+#: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:31
+msgid "Tags"
+msgstr "Oznake"
+
+#: mediagoblin/edit/forms.py:38
+msgid "Bio"
+msgstr "Biografija"
+
+#: mediagoblin/edit/forms.py:41
+msgid "Website"
+msgstr "Spletna stran"
+
+#: mediagoblin/edit/forms.py:43
+msgid "Improperly formed URL"
+msgstr "Napačno oblikovan URL"
+
+#: mediagoblin/edit/views.py:54
+msgid "An entry with that slug already exists for this user."
+msgstr "Vnos s to oznako za tega uporabnika že obstaja."
+
+#: mediagoblin/edit/views.py:75
+msgid "You are editing another user's media. Proceed with caution."
+msgstr "Urejate vsebino drugega uporabnika. Nadaljujte pazljivo."
+
+#: mediagoblin/edit/views.py:96
+msgid "You are editing a user's profile. Proceed with caution."
+msgstr "Urejate uporabniški profil. Nadaljujte pazljivo."
+
+#: mediagoblin/submit/forms.py:29
+msgid "File"
+msgstr "Datoteka"
+
+#: mediagoblin/submit/views.py:45
+msgid "You must provide a file."
+msgstr "Podati morate datoteko."
+
+#: mediagoblin/submit/views.py:48
+msgid "The file doesn't seem to be an image!"
+msgstr "Kot kaže datoteka ni slika."
+
+#: mediagoblin/submit/views.py:96
+msgid "Woohoo! Submitted!"
+msgstr "Juhej! Poslano."
+
+#: mediagoblin/templates/mediagoblin/base.html:22
+msgid "GNU MediaGoblin"
+msgstr "GNU MediaGoblin"
+
+#: mediagoblin/templates/mediagoblin/base.html:45
+msgid "Mediagoblin logo"
+msgstr "Logotip MediaGoblin"
+
+#: mediagoblin/templates/mediagoblin/base.html:51
+msgid "Submit media"
+msgstr "Pošlji vsebino"
+
+#: mediagoblin/templates/mediagoblin/base.html:62
+msgid "verify your email!"
+msgstr "Preverite svojo e-pošto."
+
+#: mediagoblin/templates/mediagoblin/base.html:72
+msgid "Login"
+msgstr "Prijava"
+
+#: mediagoblin/templates/mediagoblin/base.html:88
+msgid ""
+"Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a "
+"href=\"http://gnu.org/\">GNU project</a>"
+msgstr ""
+"Stran poganja <a href=\"http://mediagoblin.org\">MediaGoblin</a>, del <a "
+"href=\"http://gnu.org/\">projekta GNU</a>"
+
+#: mediagoblin/templates/mediagoblin/root.html:21
+msgid "Welcome to GNU MediaGoblin!"
+msgstr "Dobrodošli v GNU MediaGoblin!"
+
+#: mediagoblin/templates/mediagoblin/root.html:26
+msgid "Submit an item"
+msgstr "Pošljite datoteko"
+
+#: mediagoblin/templates/mediagoblin/root.html:31
+#, python-format
+msgid "If you have an account, you can <a href=\"%(login_url)s\">Login</a>."
+msgstr "Če imate račun, se lahko <a href=\"%(login_url)s\">Prijavite</a>."
+
+#: mediagoblin/templates/mediagoblin/root.html:37
+#, python-format
+msgid ""
+"If you don't have an account, please <a "
+"href=\"%(register_url)s\">Register</a>."
+msgstr "Če računa še nimate, se <a href=\"%(register_url)s\">Registrirajte</a>."
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:26
+msgid "Log in"
+msgstr "Prijava"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:29
+msgid "Login failed!"
+msgstr "Neuspešna prijava."
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:34
+#: mediagoblin/templates/mediagoblin/auth/register.html:30
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:35
+#: mediagoblin/templates/mediagoblin/submit/start.html:32
+msgid "Submit"
+msgstr "Pošlji"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:42
+msgid "Don't have an account yet?"
+msgstr "Še nimate računa?"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:45
+msgid "Create one here!"
+msgstr "Ustvarite si ga."
+
+#: mediagoblin/templates/mediagoblin/auth/register.html:27
+msgid "Create an account!"
+msgstr "Ustvarite račun."
+
+#: 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 ""
+"Pozdravljeni, %(username)s\n"
+"\n"
+"Za aktivacijo svojega računa GNU MediaGoblin odprite\n"
+"naslednji URL v svojem spletnem brskalniku:\n"
+"\n"
+"%(verification_url)s"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:29
+#, python-format
+msgid "Editing %(media_title)s"
+msgstr "Urejanje %(media_title)s"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:36
+msgid "Cancel"
+msgstr "Prekliči"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:37
+msgid "Save changes"
+msgstr "Shrani spremembe"
+
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:29
+#, python-format
+msgid "Editing %(username)s's profile"
+msgstr "Urejanje profila – %(username)s"
+
+#: mediagoblin/templates/mediagoblin/listings/tag.html:29
+msgid "Media tagged with:"
+msgstr "Vsebina označena z:"
+
+#: mediagoblin/templates/mediagoblin/listings/tag.html:40
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:46
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:101
+msgid "atom feed"
+msgstr "Vir Atom"
+
+#: mediagoblin/templates/mediagoblin/submit/start.html:26
+msgid "Submit yer media"
+msgstr "Pošljite svojo vsebino"
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:30
+#, python-format
+msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media"
+msgstr "Vsebina uporabnika <a href=\"%(user_url)s\">%(username)s</a>"
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:51
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:30
+msgid "Sorry, no such user found."
+msgstr "Oprostite, tega uporabnika ni bilo moč najti."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:37
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:57
+msgid "Verification needed"
+msgstr "Potrebna je potrditev"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:40
+msgid "Almost done! Your account still needs to be verified."
+msgstr "Skoraj ste zaključili. Račun je potrebno le še potrditi."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:45
+msgid ""
+"An email should arrive in a few moments with instructions on how to do so."
+msgstr "V kratkem bi morali prejeti e-pošto z navodili, kako to storiti."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:49
+msgid "In case it doesn't:"
+msgstr "Če je ne prejmete:"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:52
+msgid "Resend verification email"
+msgstr "Ponovno pošlji potrditveno e-pošto"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:60
+msgid ""
+"Someone has registered an account with this username, but it still has to be"
+" verified."
+msgstr ""
+"Nekdo je s tem uporabniškim imenom že registriral račun, vendar mora biti še"
+" potrjen."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:66
+#, 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 ""
+"Če ste ta oseba vi, a ste izgubili potrditveno e-pošto, se lahko <a "
+"href=\"%(login_url)s\">prijavite</a> in jo ponovno pošljete."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:76
+#, python-format
+msgid "%(username)s's profile"
+msgstr "Profil – %(username)s"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:84
+msgid "Edit profile"
+msgstr "Uredi profil"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:95
+#, python-format
+msgid "View all of %(username)s's media"
+msgstr "Prikaži vso vsebino uporabnika %(username)s"
+
+
diff --git a/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.mo
new file mode 100644
index 00000000..153584d3
--- /dev/null
+++ b/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.mo
Binary files differ
diff --git a/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.po
new file mode 100644
index 00000000..ec8611ee
--- /dev/null
+++ b/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.po
@@ -0,0 +1,294 @@
+# Translations template for PROJECT.
+# Copyright (C) 2011 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: GNU MediaGoblin\n"
+"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n"
+"POT-Creation-Date: 2011-08-08 22:53-0500\n"
+"PO-Revision-Date: 2011-08-09 03:57+0000\n"
+"Last-Translator: cwebber <cwebber@dustycloud.org>\n"
+"Language-Team: Serbian (http://www.transifex.net/projects/p/mediagoblin/team/sr/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+"Language: sr\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+
+#: mediagoblin/auth/forms.py:24 mediagoblin/auth/forms.py:46
+msgid "Username"
+msgstr ""
+
+#: mediagoblin/auth/forms.py:29 mediagoblin/auth/forms.py:50
+msgid "Password"
+msgstr ""
+
+#: mediagoblin/auth/forms.py:34
+msgid "Passwords must match."
+msgstr ""
+
+#: mediagoblin/auth/forms.py:36
+msgid "Confirm password"
+msgstr ""
+
+#: mediagoblin/auth/forms.py:39
+msgid "Email address"
+msgstr ""
+
+#: mediagoblin/auth/views.py:40
+msgid "Sorry, registration is disabled on this instance."
+msgstr ""
+
+#: mediagoblin/auth/views.py:55
+msgid "Sorry, a user with that name already exists."
+msgstr ""
+
+#: mediagoblin/auth/views.py:152
+msgid ""
+"Your email address has been verified. You may now login, edit your profile, "
+"and submit images!"
+msgstr ""
+
+#: mediagoblin/auth/views.py:158
+msgid "The verification key or user id is incorrect"
+msgstr ""
+
+#: mediagoblin/auth/views.py:179
+#: mediagoblin/templates/mediagoblin/auth/resent_verification_email.html:22
+msgid "Resent your verification email."
+msgstr ""
+
+#: mediagoblin/edit/forms.py:26 mediagoblin/submit/forms.py:26
+msgid "Title"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:29
+msgid "Slug"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:30
+msgid "The slug can't be empty"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:31
+msgid "Tags"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:38
+msgid "Bio"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:41
+msgid "Website"
+msgstr ""
+
+#: mediagoblin/edit/forms.py:43
+msgid "Improperly formed URL"
+msgstr ""
+
+#: mediagoblin/edit/views.py:54
+msgid "An entry with that slug already exists for this user."
+msgstr ""
+
+#: mediagoblin/edit/views.py:75
+msgid "You are editing another user's media. Proceed with caution."
+msgstr ""
+
+#: mediagoblin/edit/views.py:96
+msgid "You are editing a user's profile. Proceed with caution."
+msgstr ""
+
+#: mediagoblin/submit/forms.py:29
+msgid "File"
+msgstr ""
+
+#: mediagoblin/submit/views.py:45
+msgid "You must provide a file."
+msgstr ""
+
+#: mediagoblin/submit/views.py:48
+msgid "The file doesn't seem to be an image!"
+msgstr ""
+
+#: mediagoblin/submit/views.py:96
+msgid "Woohoo! Submitted!"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:22
+msgid "GNU MediaGoblin"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:45
+msgid "Mediagoblin logo"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:51
+msgid "Submit media"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:62
+msgid "verify your email!"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:72
+msgid "Login"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/base.html:88
+msgid ""
+"Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a "
+"href=\"http://gnu.org/\">GNU project</a>"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/root.html:21
+msgid "Welcome to GNU MediaGoblin!"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/root.html:26
+msgid "Submit an item"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/root.html:31
+#, python-format
+msgid "If you have an account, you can <a href=\"%(login_url)s\">Login</a>."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/root.html:37
+#, python-format
+msgid ""
+"If you don't have an account, please <a "
+"href=\"%(register_url)s\">Register</a>."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:26
+msgid "Log in"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:29
+msgid "Login failed!"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:34
+#: mediagoblin/templates/mediagoblin/auth/register.html:30
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:35
+#: mediagoblin/templates/mediagoblin/submit/start.html:32
+msgid "Submit"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:42
+msgid "Don't have an account yet?"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:45
+msgid "Create one here!"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/auth/register.html:27
+msgid "Create an account!"
+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/edit/edit.html:29
+#, python-format
+msgid "Editing %(media_title)s"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:36
+msgid "Cancel"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:37
+msgid "Save changes"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:29
+#, python-format
+msgid "Editing %(username)s's profile"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/listings/tag.html:29
+msgid "Media tagged with:"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/listings/tag.html:40
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:46
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:101
+msgid "atom feed"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/submit/start.html:26
+msgid "Submit yer media"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:30
+#, python-format
+msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:51
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:30
+msgid "Sorry, no such user found."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:37
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:57
+msgid "Verification needed"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:40
+msgid "Almost done! Your account still needs to be verified."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:45
+msgid ""
+"An email should arrive in a few moments with instructions on how to do so."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:49
+msgid "In case it doesn't:"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:52
+msgid "Resend verification email"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:60
+msgid ""
+"Someone has registered an account with this username, but it still has to be"
+" verified."
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:66
+#, 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/user_pages/user.html:76
+#, python-format
+msgid "%(username)s's profile"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:84
+msgid "Edit profile"
+msgstr ""
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:95
+#, python-format
+msgid "View all of %(username)s's media"
+msgstr ""
+
+
diff --git a/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.mo
new file mode 100644
index 00000000..04fe0b6f
--- /dev/null
+++ b/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.mo
Binary files differ
diff --git a/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.po
new file mode 100644
index 00000000..311c5656
--- /dev/null
+++ b/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.po
@@ -0,0 +1,311 @@
+# Translations template for PROJECT.
+# Copyright (C) 2011 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# <transifex@wandborg.se>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: GNU MediaGoblin\n"
+"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n"
+"POT-Creation-Date: 2011-08-08 22:53-0500\n"
+"PO-Revision-Date: 2011-08-09 15:00+0000\n"
+"Last-Translator: joar <transifex@wandborg.se>\n"
+"Language-Team: Swedish (http://www.transifex.net/projects/p/mediagoblin/team/sv/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+"Language: sv\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: mediagoblin/auth/forms.py:24 mediagoblin/auth/forms.py:46
+msgid "Username"
+msgstr "Användarnamn"
+
+#: mediagoblin/auth/forms.py:29 mediagoblin/auth/forms.py:50
+msgid "Password"
+msgstr "Lösenord"
+
+#: mediagoblin/auth/forms.py:34
+msgid "Passwords must match."
+msgstr "Lösenorden måste vara identiska."
+
+#: mediagoblin/auth/forms.py:36
+msgid "Confirm password"
+msgstr "Bekräfta lösenord"
+
+#: mediagoblin/auth/forms.py:39
+msgid "Email address"
+msgstr "E-postadress"
+
+#: mediagoblin/auth/views.py:40
+msgid "Sorry, registration is disabled on this instance."
+msgstr "Vi beklagar, registreringen är avtängd på den här instansen."
+
+#: mediagoblin/auth/views.py:55
+msgid "Sorry, a user with that name already exists."
+msgstr "En användare med det användarnamnet finns redan."
+
+#: mediagoblin/auth/views.py:152
+msgid ""
+"Your email address has been verified. You may now login, edit your profile, "
+"and submit images!"
+msgstr ""
+"Din e-postadress är verifierad. Du kan nu logga in, redigera din profil och "
+"ladda upp filer!"
+
+#: mediagoblin/auth/views.py:158
+msgid "The verification key or user id is incorrect"
+msgstr "Verifieringsnyckeln eller användar-IDt är fel."
+
+#: mediagoblin/auth/views.py:179
+#: mediagoblin/templates/mediagoblin/auth/resent_verification_email.html:22
+msgid "Resent your verification email."
+msgstr "Skickade ett nytt verifierings-email."
+
+#: mediagoblin/edit/forms.py:26 mediagoblin/submit/forms.py:26
+msgid "Title"
+msgstr "Titel"
+
+#: mediagoblin/edit/forms.py:29
+msgid "Slug"
+msgstr "Sökvägsnamn"
+
+#: mediagoblin/edit/forms.py:30
+msgid "The slug can't be empty"
+msgstr "Sökvägsnamnet kan inte vara tomt"
+
+#: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:31
+msgid "Tags"
+msgstr "Taggar"
+
+#: mediagoblin/edit/forms.py:38
+msgid "Bio"
+msgstr "Presentation"
+
+#: mediagoblin/edit/forms.py:41
+msgid "Website"
+msgstr "Hemsida"
+
+#: mediagoblin/edit/forms.py:43
+msgid "Improperly formed URL"
+msgstr "Ogiltig URL"
+
+#: mediagoblin/edit/views.py:54
+msgid "An entry with that slug already exists for this user."
+msgstr "Ett inlägg med det sökvägsnamnet existerar redan."
+
+#: mediagoblin/edit/views.py:75
+msgid "You are editing another user's media. Proceed with caution."
+msgstr "Var försiktig, du redigerar någon annans inlägg."
+
+#: mediagoblin/edit/views.py:96
+msgid "You are editing a user's profile. Proceed with caution."
+msgstr "Var försiktig, du redigerar en annan användares profil."
+
+#: mediagoblin/submit/forms.py:29
+msgid "File"
+msgstr "Fil"
+
+#: mediagoblin/submit/views.py:45
+msgid "You must provide a file."
+msgstr "Du måste ange en fil"
+
+#: mediagoblin/submit/views.py:48
+msgid "The file doesn't seem to be an image!"
+msgstr "Filen verkar inte vara en giltig bildfil!"
+
+#: mediagoblin/submit/views.py:96
+msgid "Woohoo! Submitted!"
+msgstr "Tjohoo! Upladdat!"
+
+#: mediagoblin/templates/mediagoblin/base.html:22
+msgid "GNU MediaGoblin"
+msgstr "GNU MediaGoblin"
+
+#: mediagoblin/templates/mediagoblin/base.html:45
+msgid "Mediagoblin logo"
+msgstr "MediaGoblin logo"
+
+#: mediagoblin/templates/mediagoblin/base.html:51
+msgid "Submit media"
+msgstr "Ladda upp"
+
+#: mediagoblin/templates/mediagoblin/base.html:62
+msgid "verify your email!"
+msgstr "Verifiera din e-postadress!"
+
+#: mediagoblin/templates/mediagoblin/base.html:72
+msgid "Login"
+msgstr "Logga in"
+
+#: mediagoblin/templates/mediagoblin/base.html:88
+msgid ""
+"Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a "
+"href=\"http://gnu.org/\">GNU project</a>"
+msgstr ""
+"Drivs av <a href=\"http://mediagoblin.org\">MediaGoblin</a>, ett <a "
+"href=\"http://gnu.org/\">GNU</a>-projekt"
+
+#: mediagoblin/templates/mediagoblin/root.html:21
+msgid "Welcome to GNU MediaGoblin!"
+msgstr "Välkommen till GNU MediaGoblin!"
+
+#: mediagoblin/templates/mediagoblin/root.html:26
+msgid "Submit an item"
+msgstr "Ladda upp"
+
+#: mediagoblin/templates/mediagoblin/root.html:31
+#, python-format
+msgid "If you have an account, you can <a href=\"%(login_url)s\">Login</a>."
+msgstr "Har du ett konto? <a href=\"%(login_url)s\">Logga in</a>."
+
+#: mediagoblin/templates/mediagoblin/root.html:37
+#, python-format
+msgid ""
+"If you don't have an account, please <a "
+"href=\"%(register_url)s\">Register</a>."
+msgstr ""
+"Har du inget konto? <a href=\"%(register_url)s\">Registrera ett konto</a>."
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:26
+msgid "Log in"
+msgstr "Logga in"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:29
+msgid "Login failed!"
+msgstr "Inloggning misslyckades!"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:34
+#: mediagoblin/templates/mediagoblin/auth/register.html:30
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:35
+#: mediagoblin/templates/mediagoblin/submit/start.html:32
+msgid "Submit"
+msgstr "Skicka"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:42
+msgid "Don't have an account yet?"
+msgstr "Har du inget konto?"
+
+#: mediagoblin/templates/mediagoblin/auth/login.html:45
+msgid "Create one here!"
+msgstr "Skapa ett!"
+
+#: mediagoblin/templates/mediagoblin/auth/register.html:27
+msgid "Create an account!"
+msgstr "Skapa ett konto!"
+
+#: 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 ""
+"Hej %(username)s,\n"
+"\n"
+"oppna den följande URLen i din webbläsare för att aktivera ditt konto på GNU MediaGoblin:\n"
+"\n"
+"%(verification_url)s"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:29
+#, python-format
+msgid "Editing %(media_title)s"
+msgstr "Redigerar %(media_title)s"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:36
+msgid "Cancel"
+msgstr "Avbryt"
+
+#: mediagoblin/templates/mediagoblin/edit/edit.html:37
+msgid "Save changes"
+msgstr "Spara"
+
+#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:29
+#, python-format
+msgid "Editing %(username)s's profile"
+msgstr "Redigerar %(username)ss profil"
+
+#: mediagoblin/templates/mediagoblin/listings/tag.html:29
+msgid "Media tagged with:"
+msgstr "Taggat med:"
+
+#: mediagoblin/templates/mediagoblin/listings/tag.html:40
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:46
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:101
+msgid "atom feed"
+msgstr "atom-feed"
+
+#: mediagoblin/templates/mediagoblin/submit/start.html:26
+msgid "Submit yer media"
+msgstr "Ladda upp"
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:30
+#, python-format
+msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media"
+msgstr "<a href=\"%(user_url)s\">%(username)s</a>s media"
+
+#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:51
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:30
+msgid "Sorry, no such user found."
+msgstr "Finns ingen sådan användare ännu."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:37
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:57
+msgid "Verification needed"
+msgstr "Verifiering krävs"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:40
+msgid "Almost done! Your account still needs to be verified."
+msgstr "Nästan klart! Nu behöver du bara verifiera ditt konto."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:45
+msgid ""
+"An email should arrive in a few moments with instructions on how to do so."
+msgstr ""
+"Ett e-postmeddelande med instruktioner kommer att hamna hos dig inom kort."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:49
+msgid "In case it doesn't:"
+msgstr "Om det inte skulle göra det:"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:52
+msgid "Resend verification email"
+msgstr "Skicka ett nytt e-postmeddelande"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:60
+msgid ""
+"Someone has registered an account with this username, but it still has to be"
+" verified."
+msgstr ""
+"Det finns redan ett konto med det här användarnamnet, men det behöver "
+"verifieras."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:66
+#, 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 ""
+"Om det är du som är den personen och har förlorat ditt e-postmeddelande med "
+"detaljer om hur du verifierar ditt konto så kan du <a "
+"href=\"%(login_url)s\">logga in</a> och begära ett nytt."
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:76
+#, python-format
+msgid "%(username)s's profile"
+msgstr "%(username)ss profil"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:84
+msgid "Edit profile"
+msgstr "Redigera profil"
+
+#: mediagoblin/templates/mediagoblin/user_pages/user.html:95
+#, python-format
+msgid "View all of %(username)s's media"
+msgstr "Se all media från %(username)s"
+
+
diff --git a/mediagoblin/init/__init__.py b/mediagoblin/init/__init__.py
index 1e519cc9..ff005703 100644
--- a/mediagoblin/init/__init__.py
+++ b/mediagoblin/init/__init__.py
@@ -23,6 +23,7 @@ from mediagoblin.mg_globals import setup_globals
from mediagoblin.db.open import setup_connection_and_db_from_config
from mediagoblin.db.util import MigrationManager
from mediagoblin.workbench import WorkbenchManager
+from mediagoblin.storage import storage_system_from_config
class Error(Exception): pass
@@ -60,9 +61,16 @@ def setup_database():
# Tiny hack to warn user if our migration is out of date
if not migration_manager.database_at_latest_migration():
- print (
- "*WARNING:* Your migrations are out of date, "
- "maybe run ./bin/gmg migrate?")
+ db_migration_num = migration_manager.database_current_migration()
+ latest_migration_num = migration_manager.latest_migration()
+ if db_migration_num < latest_migration_num:
+ print (
+ "*WARNING:* Your migrations are out of date, "
+ "maybe run ./bin/gmg migrate?")
+ elif db_migration_num > latest_migration_num:
+ print (
+ "*WARNING:* Your migrations are out of date... "
+ "in fact they appear to be from the future?!")
setup_globals(
db_connection = connection,
@@ -103,6 +111,19 @@ def get_staticdirector(app_config):
"direct_remote_paths must be provided")
+def setup_storage():
+ app_config = mg_globals.app_config
+
+ public_store = storage_system_from_config(app_config, 'publicstore')
+ queue_store = storage_system_from_config(app_config, 'queuestore')
+
+ setup_globals(
+ public_store = public_store,
+ queue_store = queue_store)
+
+ return public_store, queue_store
+
+
def setup_workbench():
app_config = mg_globals.app_config
diff --git a/mediagoblin/templates/mediagoblin/auth/register_success.html b/mediagoblin/listings/__init__.py
index cd82a0b9..fbccb9b8 100644
--- a/mediagoblin/templates/mediagoblin/auth/register_success.html
+++ b/mediagoblin/listings/__init__.py
@@ -1,4 +1,3 @@
-{#
# GNU MediaGoblin -- federated, autonomous media hosting
# Copyright (C) 2011 Free Software Foundation, Inc
#
@@ -14,12 +13,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/>.
-#}
-{% extends "mediagoblin/base.html" %}
-{% block mediagoblin_content %}
- <p>
- Register successful! :D <br />
- You should get a confirmation email soon.
- </p>
-{% endblock %}
+"""
+Non-user listing views and routing should go in this submodule.
+"""
diff --git a/mediagoblin/listings/routing.py b/mediagoblin/listings/routing.py
new file mode 100644
index 00000000..61dd5210
--- /dev/null
+++ b/mediagoblin/listings/routing.py
@@ -0,0 +1,28 @@
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011 Free Software Foundation, Inc
+#
+# 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 routes.route import Route
+
+tag_routes = [
+ # Route('mediagoblin.listings.tags_home', "/",
+ # controller="mediagoblin.listings.views:tags_home"),
+ Route('mediagoblin.listings.tags_listing', "/{tag}/",
+ controller="mediagoblin.listings.views:tag_listing"),
+ Route('mediagoblin.listings.tag_atom_feed', "/{tag}/atom/",
+ controller="mediagoblin.listings.views:tag_atom_feed"),
+ ]
+
diff --git a/mediagoblin/listings/views.py b/mediagoblin/listings/views.py
new file mode 100644
index 00000000..aade7e64
--- /dev/null
+++ b/mediagoblin/listings/views.py
@@ -0,0 +1,91 @@
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011 Free Software Foundation, Inc
+#
+# 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.util import DESCENDING
+
+from mediagoblin.util import Pagination, render_to_response
+from mediagoblin.decorators import uses_pagination
+
+from werkzeug.contrib.atom import AtomFeed
+
+
+def _get_tag_name_from_entries(media_entries, tag_slug):
+ """
+ Get a tag name from the first entry by looking it up via its slug.
+ """
+ # ... this is slightly hacky looking :\
+ tag_name = tag_slug
+ if media_entries.count():
+ for tag in media_entries[0]['tags']:
+ if tag['slug'] == tag_slug:
+ tag_name == tag['name']
+ break
+
+ return tag_name
+
+
+@uses_pagination
+def tag_listing(request, page):
+ """'Gallery'/listing for this tag slug"""
+ tag_slug = request.matchdict[u'tag']
+
+ cursor = request.db.MediaEntry.find(
+ {u'state': u'processed',
+ u'tags.slug': tag_slug})
+ cursor = cursor.sort('created', DESCENDING)
+
+ pagination = Pagination(page, cursor)
+ media_entries = pagination()
+
+ tag_name = _get_tag_name_from_entries(media_entries, tag_slug)
+
+ return render_to_response(
+ request,
+ 'mediagoblin/listings/tag.html',
+ {'tag_slug': tag_slug,
+ 'tag_name': tag_name,
+ 'media_entries': media_entries,
+ 'pagination': pagination})
+
+
+ATOM_DEFAULT_NR_OF_UPDATED_ITEMS = 15
+
+def tag_atom_feed(request):
+ """
+ generates the atom feed with the tag images
+ """
+ tag_slug = request.matchdict[u'tag']
+
+ cursor = request.db.MediaEntry.find(
+ {u'state': u'processed',
+ u'tags.slug': tag_slug})
+ cursor = cursor.sort('created', DESCENDING)
+ cursor = cursor.limit(ATOM_DEFAULT_NR_OF_UPDATED_ITEMS)
+
+ feed = AtomFeed(
+ "MediaGoblin: Feed for tag '%s'" % tag_slug,
+ feed_url=request.url,
+ url=request.host_url)
+
+ for entry in cursor:
+ feed.add(entry.get('title'),
+ entry.get('description_html'),
+ content_type='html',
+ author=entry.uploader()['username'],
+ updated=entry.get('created'),
+ url=entry.url_for_self(request.urlgen))
+
+ return feed.get_response()
diff --git a/mediagoblin/mg_globals.py b/mediagoblin/mg_globals.py
index 12a0e016..80ff5ead 100644
--- a/mediagoblin/mg_globals.py
+++ b/mediagoblin/mg_globals.py
@@ -1,3 +1,18 @@
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011 Free Software Foundation, Inc
+#
+# 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/>.
"""
In some places, we need to access the database, public_store, queue_store
"""
diff --git a/mediagoblin/process_media/__init__.py b/mediagoblin/process_media/__init__.py
index 125b24e0..8e12ca4d 100644
--- a/mediagoblin/process_media/__init__.py
+++ b/mediagoblin/process_media/__init__.py
@@ -19,6 +19,7 @@ from mediagoblin.db.util import ObjectId
from celery.task import task
from mediagoblin import mg_globals as mgg
+from contextlib import contextmanager
THUMB_SIZE = 180, 180
@@ -31,6 +32,12 @@ def create_pub_filepath(entry, filename):
unicode(entry['_id']),
filename])
+@contextmanager
+def closing(callback):
+ try:
+ yield callback
+ finally:
+ pass
@task
def process_media_initial(media_id):
@@ -53,7 +60,7 @@ def process_media_initial(media_id):
thumb_filepath = create_pub_filepath(entry, 'thumbnail.jpg')
thumb_file = mgg.public_store.get_file(thumb_filepath, 'w')
- with thumb_file:
+ with closing(thumb_file):
thumb.save(thumb_file, "JPEG", quality=90)
"""
@@ -73,7 +80,7 @@ def process_media_initial(media_id):
medium_filepath = create_pub_filepath(entry, 'medium.jpg')
medium_file = mgg.public_store.get_file(medium_filepath, 'w')
- with medium_file:
+ with closing(medium_file):
medium.save(medium_file, "JPEG", quality=90)
medium_processed = True
@@ -84,7 +91,7 @@ def process_media_initial(media_id):
with queued_file:
original_filepath = create_pub_filepath(entry, queued_filepath[-1])
- with mgg.public_store.get_file(original_filepath, 'wb') as original_file:
+ with closing(mgg.public_store.get_file(original_filepath, 'wb')) as original_file:
original_file.write(queued_file.read())
mgg.queue_store.delete_file(queued_filepath)
diff --git a/mediagoblin/routing.py b/mediagoblin/routing.py
index b854c85a..1340da60 100644
--- a/mediagoblin/routing.py
+++ b/mediagoblin/routing.py
@@ -20,6 +20,8 @@ from mediagoblin.auth.routing import auth_routes
from mediagoblin.submit.routing import submit_routes
from mediagoblin.user_pages.routing import user_routes
from mediagoblin.edit.routing import edit_routes
+from mediagoblin.listings.routing import tag_routes
+
def get_mapper():
mapping = Mapper()
@@ -33,5 +35,6 @@ def get_mapper():
mapping.extend(submit_routes, '/submit')
mapping.extend(user_routes, '/u')
mapping.extend(edit_routes, '/edit')
+ mapping.extend(tag_routes, '/tag')
return mapping
diff --git a/mediagoblin/static/css/base.css b/mediagoblin/static/css/base.css
index 31b8ebc2..59c2f49d 100644
--- a/mediagoblin/static/css/base.css
+++ b/mediagoblin/static/css/base.css
@@ -1,15 +1,17 @@
body {
- background-color: #1F1F1F;
- color: #aaa;
+ background-color: #111;
+ background-image: url("../images/background.png");
+ color: #999;
font-family: sans-serif;
- padding:none;
- margin:0px;
- height:100%;
+ padding: none;
+ margin: 0px;
+ height: 100%;
+ font: 16px "HelveticaNeue-Light","Helvetica Neue Light","Helvetica Neue",Helvetica,Arial,sans-serif;
}
form {
- margin:0px;
- padding:0px;
+ margin: 0px;
+ padding: 0px;
}
/* Carter One font */
@@ -18,22 +20,34 @@ form {
font-family: 'Carter One';
font-style: normal;
font-weight: normal;
- src: local('CarterOne'), url('http://themes.googleusercontent.com/font?kit=FWNn6ITYqL6or7ZTmBxRhq3fkYX5z1QtDUdIWoaaD_k') format('woff');
+ src: local('CarterOne'), url('http://themes.googleusercontent.com/font?kit=VjW2qt1pkqVtO22ObxgEBRsxEYwM7FgeyaSgU71cLG0') format('woff');
}
/* text styles */
h1{
- font-family: 'Carter One', arial, serif;
+ font-family: 'Carter One',arial,serif;
margin-bottom: 15px;
- margin-top:15px;
+ margin-top: 15px;
+ color: #fff;
+ font-size: 30px;
}
h2{
- margin-top:20px;
+ margin-top: 20px;
+ color: #fff;
+}
+
+h3{
+ border-bottom: 1px solid #222;
+ font-size: 18px;
}
a {
+ color: #999;
+}
+
+a.highlight {
color: #fff;
}
@@ -44,202 +58,207 @@ label {
/* website structure */
.mediagoblin_body {
- position:relative;
- min-height:100%;
+ position: relative;
+ min-height: 100%;
}
.mediagoblin_header {
- width:100%;
- height:36px;
- background-color:#2F2F2F;
- padding-top:14px;
- margin-bottom:40px;
+ height: 36px;
+ padding-top: 14px;
+ margin-bottom: 20px;
+ border-bottom: 1px solid #222222;
}
.header_submit{
- color:#272727;
- background-color:#aaa;
+ color: #272727;
+ background-color: #aaa;
background-image: -webkit-gradient(linear, left top, left bottom, from(##D2D2D2), to(#aaa));
background-image: -webkit-linear-gradient(top, #D2D2D2, #aaa);
background-image: -moz-linear-gradient(top, #D2D2D2, #aaa);
background-image: -ms-linear-gradient(top, #D2D2D2, #aaa);
background-image: -o-linear-gradient(top, #D2D2D2, #aaa);
background-image: linear-gradient(top, #D2D2D2, #aaa);
- box-shadow:0px 0px 4px #000;
- border-radius:5px 5px 5px 5px;
- margin:8px;
- padding:3px 8px;
- text-decoration:none;
- border:medium none;
- font-family:'Carter One',arial,serif;
+ box-shadow: 0px 0px 4px #000;
+ border-radius: 5px 5px 5px 5px;
+ margin: 8px;
+ padding: 3px 8px;
+ text-decoration: none;
+ border: medium none;
+ font-family: 'Carter One',arial,serif;
}
.mediagoblin_footer {
- width:100%;
- height:30px;
- background-color:#2F2F2F;
- bottom:0px;
- padding-top:8px;
- position:absolute;
- text-align:center;
- font-size:14px;
- color:#999;
+ height: 30px;
+ border-top: 1px solid #222222;
+ bottom: 0px;
+ padding-top: 8px;
+ text-align: center;
+ font-size: 14px;
+ color: #999;
}
.mediagoblin_content {
- padding-bottom:74px;
+ padding-bottom: 74px;
}
.mediagoblin_header_right {
- float:right;
+ float: right;
}
/* common website elements */
.button {
- font-family:'Carter One', arial, serif;
- height:32px;
- min-width:99px;
- background-color:#86d4b1;
+ font-family: 'Carter One', arial, serif;
+ height: 32px;
+ min-width: 99px;
+ background-color: #86d4b1;
background-image: -webkit-gradient(linear, left top, left bottom, from(#86d4b1), to(#62caa2));
background-image: -webkit-linear-gradient(top, #86d4b1, #62caa2);
background-image: -moz-linear-gradient(top, #86d4b1, #62caa2);
background-image: -ms-linear-gradient(top, #86d4b1, #62caa2);
background-image: -o-linear-gradient(top, #86d4b1, #62caa2);
background-image: linear-gradient(top, #86d4b1, #62caa2);
- box-shadow:0px 0px 4px #000;
- border-radius:5px;
- border:none;
- color:#272727;
- margin:10px 0px 10px 15px;
- font-size:1em;
- text-align:center;
- padding-left:11px;
- padding-right:11px;
+ box-shadow: 0px 0px 4px #000;
+ border-radius: 5px;
+ border: none;
+ color: #272727;
+ margin: 10px 0px 10px 15px;
+ font-size: 1em;
+ text-align: center;
+ padding-left: 11px;
+ padding-right: 11px;
+ text-decoration: none;
}
.pagination{
-text-align:center;
+text-align: center;
}
.pagination_arrow{
- margin:5px;
+ margin: 5px;
}
/* forms */
.form_box {
- background-color:#393939;
- background-image:url("../images/background_lines.png");
- background-repeat:repeat-x;
- font-size:18px;
- padding-bottom:30px;
- padding-top:30px;
- margin-left:auto;
- margin-right:auto;
- display:block;
- float:none;
+ background-color: #222;
+ background-image: url("../images/background_lines.png");
+ background-repeat: repeat-x;
+ font-size: 18px;
+ padding-bottom: 30px;
+ padding-top: 30px;
+ margin-left: auto;
+ margin-right: auto;
+ display: block;
+ float: none;
}
.edit_box {
- background-image:url("../images/background_edit.png");
+ background-image: url("../images/background_edit.png");
}
.form_box h1 {
- font-size:28px;
+ font-size: 28px;
}
.form_field_input input, .form_field_input textarea {
- width:100%;
- font-size:18px;
+ width: 100%;
+ font-size: 18px;
}
.form_field_box {
- margin-bottom:24px;
+ margin-bottom: 24px;
}
.form_field_label,.form_field_input {
- margin-bottom:4px;
+ margin-bottom: 4px;
}
.form_field_error {
- background-color:#87453b;
- color:#fff;
- border:none;
- font-size:16px;
- padding:9px;
- margin-top:8px;
- margin-bottom:8px;
+ background-color: #87453b;
+ color: #fff;
+ border: none;
+ font-size: 16px;
+ padding: 9px;
+ margin-top: 8px;
+ margin-bottom: 8px;
}
.form_submit_buttons {
- text-align:right;
+ text-align: right;
}
/* comments */
.comment_author {
- margin-bottom:40px;
- padding-top:4px;
+ margin-bottom: 40px;
+ padding-top: 4px;
+ font-size: 14px;
}
.comment_content p {
- margin-bottom:4px;
+ margin-bottom: 4px;
}
/* media galleries */
.media_thumbnail {
- padding:0px;
- width:180px;
- height:180px;
- overflow:hidden;
- float:left;
- margin:0px 4px 10px 4px;
- text-align:center;
+ padding: 0px;
+ width: 180px;
+ height: 180px;
+ overflow: hidden;
+ float: left;
+ margin: 0px 4px 10px 4px;
+ text-align: center;
+}
+
+/* media detail */
+
+.media_image_container {
+ text-align: center;
}
/* icons */
img.media_icon{
- margin:0 4px;
- vertical-align:sub;
+ margin: 0 4px;
+ vertical-align: sub;
}
/* navigation */
.navigation_button{
- width:139px;
- display:block;
- float:left;
- text-align:center;
- background-color:#393939;
- text-decoration:none;
- padding:12px 0pt;
- font-family:'Carter One', arial, serif;
- font-size:2em;
- margin:0 0 20px
+ width: 139px;
+ display: block;
+ float: left;
+ text-align: center;
+ background-color: #222;
+ text-decoration: none;
+ padding: 12px 0pt;
+ font-family: 'Carter One', arial, serif;
+ font-size: 2em;
+ margin: 0 0 20px
}
p.navigation_button{
- color:#272727;
+ color: #272727;
}
.navigation_left{
- margin-right:2px;
+ margin-right: 2px;
}
/* messages */
ul.mediagoblin_messages {
- list-style:none inside;
- color:#f7f7f7;
+ list-style: none inside;
+ color: #f7f7f7;
}
.mediagoblin_messages li {
- margin:5px 0;
- padding:8px;
- text-align:center;
+ margin: 5px 0;
+ padding: 8px;
+ text-align: center;
}
.message_success {
@@ -260,5 +279,15 @@ ul.mediagoblin_messages {
.message_debug {
background-color: #f7f7f7;
- color:#272727;
+ color: #272727;
+}
+
+ul.mediaentry_tags {
+ list-style-type: none;
+}
+
+ul.mediaentry_tags li {
+ display: inline;
+ margin: 0px 5px 0px 0px;
+ padding: 0px;
}
diff --git a/mediagoblin/static/css/contrib/960_16_col.css b/mediagoblin/static/css/contrib/960_16_col.css
deleted file mode 120000
index bc1a430c..00000000
--- a/mediagoblin/static/css/contrib/960_16_col.css
+++ /dev/null
@@ -1 +0,0 @@
-../../../contrib/960_16_col.css \ No newline at end of file
diff --git a/mediagoblin/static/css/contrib/reset.css b/mediagoblin/static/css/contrib/reset.css
deleted file mode 120000
index 87ae5592..00000000
--- a/mediagoblin/static/css/contrib/reset.css
+++ /dev/null
@@ -1 +0,0 @@
-../../../contrib/reset.css \ No newline at end of file
diff --git a/mediagoblin/static/css/contrib/text.css b/mediagoblin/static/css/contrib/text.css
deleted file mode 120000
index d75ce48b..00000000
--- a/mediagoblin/static/css/contrib/text.css
+++ /dev/null
@@ -1 +0,0 @@
-../../../contrib/text.css \ No newline at end of file
diff --git a/mediagoblin/static/css/extlib/960_16_col.css b/mediagoblin/static/css/extlib/960_16_col.css
new file mode 120000
index 00000000..d4e43898
--- /dev/null
+++ b/mediagoblin/static/css/extlib/960_16_col.css
@@ -0,0 +1 @@
+../../../../extlib/960.gs/960_16_col.css \ No newline at end of file
diff --git a/mediagoblin/static/css/extlib/reset.css b/mediagoblin/static/css/extlib/reset.css
new file mode 120000
index 00000000..65d06d34
--- /dev/null
+++ b/mediagoblin/static/css/extlib/reset.css
@@ -0,0 +1 @@
+../../../../extlib/960.gs/reset.css \ No newline at end of file
diff --git a/mediagoblin/static/css/extlib/text.css b/mediagoblin/static/css/extlib/text.css
new file mode 120000
index 00000000..2d864de4
--- /dev/null
+++ b/mediagoblin/static/css/extlib/text.css
@@ -0,0 +1 @@
+../../../../extlib/960.gs/text.css \ No newline at end of file
diff --git a/mediagoblin/static/images/background.png b/mediagoblin/static/images/background.png
new file mode 100644
index 00000000..aa101308
--- /dev/null
+++ b/mediagoblin/static/images/background.png
Binary files differ
diff --git a/mediagoblin/storage.py b/mediagoblin/storage.py
index 5d6faa4c..88c748ce 100644
--- a/mediagoblin/storage.py
+++ b/mediagoblin/storage.py
@@ -19,6 +19,7 @@ import re
import shutil
import urlparse
import uuid
+import cloudfiles
from werkzeug.utils import secure_filename
@@ -28,11 +29,21 @@ from mediagoblin import util
# Errors
########
-class Error(Exception): pass
-class InvalidFilepath(Error): pass
-class NoWebServing(Error): pass
-class NotImplementedError(Error): pass
+class Error(Exception):
+ pass
+
+
+class InvalidFilepath(Error):
+ pass
+
+
+class NoWebServing(Error):
+ pass
+
+
+class NotImplementedError(Error):
+ pass
###############################################
@@ -117,7 +128,7 @@ class StorageInterface(object):
Eg, if the filename doesn't exist:
>>> storage_handler.get_unique_filename(['dir1', 'dir2', 'fname.jpg'])
[u'dir1', u'dir2', u'fname.jpg']
-
+
But if a file does exist, let's get one back with at uuid tacked on:
>>> storage_handler.get_unique_filename(['dir1', 'dir2', 'fname.jpg'])
[u'dir1', u'dir2', u'd02c3571-dd62-4479-9d62-9e3012dada29-fname.jpg']
@@ -184,7 +195,7 @@ class BasicFileStorage(StorageInterface):
"""
return os.path.join(
self.base_dir, *clean_listy_filepath(filepath))
-
+
def file_exists(self, filepath):
return os.path.exists(self._resolve_filepath(filepath))
@@ -216,6 +227,191 @@ class BasicFileStorage(StorageInterface):
return self._resolve_filepath(filepath)
+class CloudFilesStorage(StorageInterface):
+ def __init__(self, **kwargs):
+ self.param_container = kwargs.get('cloudfiles_container')
+ self.param_user = kwargs.get('cloudfiles_user')
+ self.param_api_key = kwargs.get('cloudfiles_api_key')
+ self.param_host = kwargs.get('cloudfiles_host')
+ self.param_use_servicenet = kwargs.get('cloudfiles_use_servicenet')
+
+ if not self.param_host:
+ print('No CloudFiles host URL specified, '
+ 'defaulting to Rackspace US')
+
+ self.connection = cloudfiles.get_connection(
+ username=self.param_user,
+ api_key=self.param_api_key,
+ servicenet=True if self.param_use_servicenet == 'true' or \
+ self.param_use_servicenet == True else False)
+
+ if not self.param_container == \
+ self.connection.get_container(self.param_container):
+ self.container = self.connection.create_container(
+ self.param_container)
+ self.container.make_public(
+ ttl=60 * 60 * 2)
+ else:
+ self.container = self.connection.get_container(
+ self.param_container)
+
+ def _resolve_filepath(self, filepath):
+ return '/'.join(
+ clean_listy_filepath(filepath))
+
+ def file_exists(self, filepath):
+ try:
+ object = self.container.get_object(
+ self._resolve_filepath(filepath))
+ return True
+ except cloudfiles.errors.NoSuchObject:
+ return False
+
+ def get_file(self, filepath, mode='r'):
+ try:
+ obj = self.container.get_object(
+ self._resolve_filepath(filepath))
+ except cloudfiles.errors.NoSuchObject:
+ obj = self.container.create_object(
+ self._resolve_filepath(filepath))
+
+ return obj
+
+ def delete_file(self, filepath):
+ # TODO: Also delete unused directories if empty (safely, with
+ # checks to avoid race conditions).
+ self.container.delete_object(filepath)
+
+ def file_url(self, filepath):
+ return self.get_file(filepath).public_uri()
+
+
+class MountStorage(StorageInterface):
+ """
+ Experimental "Mount" virtual Storage Interface
+
+ This isn't an interface to some real storage, instead
+ it's a redirecting interface, that redirects requests
+ to other "StorageInterface"s.
+ For example, requests for ["store1", "a"] to first
+ storage with the path ["a"], etc.
+
+ To set this up, you currently need to call the mount()
+ method with the target path and a backend, that shall
+ be available under that target path.
+ You have to mount things in a sensible order,
+ especially you can't mount ["a", "b"] before ["a"].
+ """
+ def __init__(self, **kwargs):
+ self.mounttab = {}
+
+ def mount(self, dirpath, backend):
+ """
+ Mount a new backend under dirpath
+ """
+ new_ent = clean_listy_filepath(dirpath)
+
+ print "Mounting:", repr(new_ent)
+ already, rem_1, table, rem_2 = self._resolve_to_backend(new_ent, True)
+ print "===", repr(already), repr(rem_1), repr(rem_2), len(table)
+
+ assert (len(rem_2) > 0) or (None not in table), \
+ "That path is already mounted"
+ assert (len(rem_2) > 0) or (len(table)==0), \
+ "A longer path is already mounted here"
+
+ for part in rem_2:
+ table[part] = {}
+ table = table[part]
+ table[None] = backend
+
+ def _resolve_to_backend(self, filepath, extra_info = False):
+ """
+ extra_info = True is for internal use!
+
+ Normally, returns the backend and the filepath inside that backend.
+
+ With extra_info = True it returns the last directory node and the
+ remaining filepath from there in addition.
+ """
+ table = self.mounttab
+ filepath = filepath[:]
+ res_fp = None
+ while True:
+ new_be = table.get(None)
+ if (new_be is not None) or res_fp is None:
+ res_be = new_be
+ res_fp = filepath[:]
+ res_extra = (table, filepath[:])
+ # print "... New res: %r, %r, %r" % (res_be, res_fp, res_extra)
+ if len(filepath) == 0:
+ break
+ query = filepath.pop(0)
+ entry = table.get(query)
+ if entry is not None:
+ table = entry
+ res_extra = (table, filepath[:])
+ else:
+ break
+ if extra_info:
+ return (res_be, res_fp) + res_extra
+ else:
+ return (res_be, res_fp)
+
+ def resolve_to_backend(self, filepath):
+ backend, filepath = self._resolve_to_backend(filepath)
+ if backend is None:
+ raise Error("Path not mounted")
+ return backend, filepath
+
+ def __repr__(self, table = None, indent = []):
+ res = []
+ if table is None:
+ res.append("MountStorage<")
+ table = self.mounttab
+ v = table.get(None)
+ if v:
+ res.append(" " * len(indent) + repr(indent) + ": " + repr(v))
+ for k, v in table.iteritems():
+ if k == None:
+ continue
+ res.append(" " * len(indent) + repr(k) + ":")
+ res += self.__repr__(v, indent + [k])
+ if table is self.mounttab:
+ res.append(">")
+ return "\n".join(res)
+ else:
+ return res
+
+ def file_exists(self, filepath):
+ backend, filepath = self.resolve_to_backend(filepath)
+ return backend.file_exists(filepath)
+
+ def get_file(self, filepath, mode='r'):
+ backend, filepath = self.resolve_to_backend(filepath)
+ return backend.get_file(filepath, mode)
+
+ def delete_file(self, filepath):
+ backend, filepath = self.resolve_to_backend(filepath)
+ return backend.delete_file(filepath)
+
+ def file_url(self, filepath):
+ backend, filepath = self.resolve_to_backend(filepath)
+ return backend.file_url(filepath)
+
+ def get_local_path(self, filepath):
+ backend, filepath = self.resolve_to_backend(filepath)
+ return backend.get_local_path(filepath)
+
+ def copy_locally(self, filepath, dest_path):
+ """
+ Need to override copy_locally, because the local_storage
+ attribute is not correct.
+ """
+ backend, filepath = self.resolve_to_backend(filepath)
+ backend.copy_locally(filepath, dest_path)
+
+
###########
# Utilities
###########
@@ -283,7 +479,7 @@ def storage_system_from_config(paste_config, storage_prefix):
for key, value in paste_config.iteritems()
if prefix_re.match(key)])
- if config_params.has_key('storage_class'):
+ if 'storage_class' in config_params:
storage_class = config_params['storage_class']
config_params.pop('storage_class')
else:
@@ -291,5 +487,3 @@ def storage_system_from_config(paste_config, storage_prefix):
storage_class = util.import_component(storage_class)
return storage_class(**config_params)
-
-
diff --git a/mediagoblin/submit/__init__.py b/mediagoblin/submit/__init__.py
index e69de29b..a8eeb5ed 100644
--- a/mediagoblin/submit/__init__.py
+++ b/mediagoblin/submit/__init__.py
@@ -0,0 +1,17 @@
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011 Free Software Foundation, Inc
+#
+# 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/>.
+
+
diff --git a/mediagoblin/submit/forms.py b/mediagoblin/submit/forms.py
index 3fd9ea49..241d32dc 100644
--- a/mediagoblin/submit/forms.py
+++ b/mediagoblin/submit/forms.py
@@ -17,10 +17,16 @@
import wtforms
+from mediagoblin.util import tag_length_validator
+from mediagoblin.util import fake_ugettext_passthrough as _
+
class SubmitStartForm(wtforms.Form):
title = wtforms.TextField(
- 'Title',
+ _('Title'),
[wtforms.validators.Length(min=0, max=500)])
description = wtforms.TextAreaField('Description of this work')
- file = wtforms.FileField('File')
+ file = wtforms.FileField(_('File'))
+ tags = wtforms.TextField(
+ _('Tags'),
+ [tag_length_validator])
diff --git a/mediagoblin/submit/views.py b/mediagoblin/submit/views.py
index f19bf22e..59d8fe3f 100644
--- a/mediagoblin/submit/views.py
+++ b/mediagoblin/submit/views.py
@@ -16,11 +16,14 @@
from os.path import splitext
from cgi import FieldStorage
+from string import split
from werkzeug.utils import secure_filename
from mediagoblin.util import (
- render_to_response, redirect, cleaned_markdown_conversion)
+ render_to_response, redirect, cleaned_markdown_conversion, \
+ convert_to_tag_list_of_dicts)
+from mediagoblin.util import pass_to_ugettext as _
from mediagoblin.decorators import require_active_login
from mediagoblin.submit import forms as submit_forms, security
from mediagoblin.process_media import process_media_initial
@@ -39,10 +42,10 @@ def submit_start(request):
and isinstance(request.POST['file'], FieldStorage)
and request.POST['file'].file):
submit_form.file.errors.append(
- u'You must provide a file.')
+ _(u'You must provide a file.'))
elif not security.check_filetype(request.POST['file']):
submit_form.file.errors.append(
- u'The file doesn\'t seem to be an image!')
+ _(u"The file doesn't seem to be an image!"))
else:
filename = request.POST['file'].filename
@@ -59,6 +62,10 @@ def submit_start(request):
entry['media_type'] = u'image' # heh
entry['uploader'] = request.user['_id']
+ # Process the user's folksonomy "tags"
+ entry['tags'] = convert_to_tag_list_of_dicts(
+ request.POST.get('tags'))
+
# Save, just so we can get the entry id for the sake of using
# it to generate the file path
entry.save(validate=False)
@@ -87,7 +94,7 @@ def submit_start(request):
result = process_media_initial.delay(unicode(entry['_id']))
entry['queued_task_id'] = result.task_id
- add_message(request, SUCCESS, 'Woohoo! Submitted!')
+ add_message(request, SUCCESS, _('Woohoo! Submitted!'))
return redirect(request, "mediagoblin.user_pages.user_home",
user = request.user['username'])
diff --git a/mediagoblin/templates/mediagoblin/auth/login.html b/mediagoblin/templates/mediagoblin/auth/login.html
index e25783ea..5750feb0 100644
--- a/mediagoblin/templates/mediagoblin/auth/login.html
+++ b/mediagoblin/templates/mediagoblin/auth/login.html
@@ -23,20 +23,27 @@
<form action="{{ request.urlgen('mediagoblin.auth.login') }}"
method="POST" enctype="multipart/form-data">
<div class="grid_6 prefix_1 suffix_1 form_box">
- <h1>Log in</h1>
+ <h1>{% trans %}Log in{% endtrans %}</h1>
{% if login_failed %}
- <div class="form_field_error">Login failed!</div>
+ <div class="form_field_error">
+ {% trans %}Login failed!{% endtrans %}
+ </div>
{% endif %}
{{ wtforms_util.render_divs(login_form) }}
<div class="form_submit_buttons">
- <input type="submit" value="submit" class="button"/>
+ <input type="submit" value="{% trans %}Submit{% endtrans %}" class="button"/>
</div>
{% if next %}
<input type="hidden" name="next" value="{{ next }}" class="button"
style="display: none;"/>
{% endif %}
{% if allow_registration %}
- <p>Don't have an account yet?<br /><a href="{{ request.urlgen('mediagoblin.auth.register') }}">Create one here!</a></p>
+ <p>
+ {% trans %}Don't have an account yet?{% endtrans %}
+ <br />
+ <a href="{{ request.urlgen('mediagoblin.auth.register') }}">
+ {%- trans %}Create one here!{% endtrans %}</a>
+ </p>
{% endif %}
</div>
</form>
diff --git a/mediagoblin/templates/mediagoblin/auth/register.html b/mediagoblin/templates/mediagoblin/auth/register.html
index f77b3782..623cbdfd 100644
--- a/mediagoblin/templates/mediagoblin/auth/register.html
+++ b/mediagoblin/templates/mediagoblin/auth/register.html
@@ -24,10 +24,11 @@
<form action="{{ request.urlgen('mediagoblin.auth.register') }}"
method="POST" enctype="multipart/form-data">
<div class="grid_6 prefix_1 suffix_1 form_box">
- <h1>Create an account!</h1>
+ <h1>{% trans %}Create an account!{% endtrans %}</h1>
{{ wtforms_util.render_divs(register_form) }}
<div class="form_submit_buttons">
- <input type="submit" value="submit" class="button" />
+ <input type="submit" value="{% trans %}Submit{% endtrans %}"
+ class="button" />
</div>
</div>
</form>
diff --git a/mediagoblin/templates/mediagoblin/auth/resent_verification_email.html b/mediagoblin/templates/mediagoblin/auth/resent_verification_email.html
index da3a9e99..8fd80ee9 100644
--- a/mediagoblin/templates/mediagoblin/auth/resent_verification_email.html
+++ b/mediagoblin/templates/mediagoblin/auth/resent_verification_email.html
@@ -19,6 +19,6 @@
{% block mediagoblin_content %}
<p>
- Resent your verification email.
+ {% trans %}Resent your verification email.{% endtrans %}
</p>
{% endblock %}
diff --git a/mediagoblin/templates/mediagoblin/auth/verification_email.txt b/mediagoblin/templates/mediagoblin/auth/verification_email.txt
index 7863374d..32053101 100644
--- a/mediagoblin/templates/mediagoblin/auth/verification_email.txt
+++ b/mediagoblin/templates/mediagoblin/auth/verification_email.txt
@@ -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/>.
-#}
+-#}
+
+{% trans username=username, verification_url=verification_url|safe -%}
Hi {{ username }},
-to activate your GNU MediaGoblin account, open the following URL in your web browser
+to activate your GNU MediaGoblin account, open the following URL in
+your web browser:
-{{ verification_url|safe }}
+{{ verification_url }}
+{%- endtrans %}
diff --git a/mediagoblin/templates/mediagoblin/base.html b/mediagoblin/templates/mediagoblin/base.html
index 12f188de..986e0995 100644
--- a/mediagoblin/templates/mediagoblin/base.html
+++ b/mediagoblin/templates/mediagoblin/base.html
@@ -19,13 +19,13 @@
<html>
<head>
<meta charset="utf-8">
- <title>{% block title %}GNU MediaGoblin{% endblock title %}</title>
+ <title>{% block title %}{% trans %}GNU MediaGoblin{% endtrans %}{% endblock title %}</title>
<link rel="stylesheet" type="text/css"
- href="{{ request.staticdirect('/css/contrib/reset.css') }}"/>
+ href="{{ request.staticdirect('/css/extlib/reset.css') }}"/>
<link rel="stylesheet" type="text/css"
- href="{{ request.staticdirect('/css/contrib/text.css') }}"/>
+ href="{{ request.staticdirect('/css/extlib/text.css') }}"/>
<link rel="stylesheet" type="text/css"
- href="{{ request.staticdirect('/css/contrib/960_16_col.css') }}"/>
+ href="{{ request.staticdirect('/css/extlib/960_16_col.css') }}"/>
<link rel="stylesheet" type="text/css"
href="{{ request.staticdirect('/css/base.css') }}"/>
{% block mediagoblin_head %}
@@ -36,27 +36,41 @@
{% block mediagoblin_body %}
<div class="mediagoblin_body">
{% block mediagoblin_header %}
- <div class="mediagoblin_header">
- <div class="container_16">
- <div class="grid_16">
- {% block mediagoblin_logo %}
- <a class="mediagoblin_logo" href="{{ request.urlgen('index') }}"><img src="{{ request.staticdirect('/images/logo.png') }}" alt="Mediagoblin logo" /></a>
- {% endblock %}
+ <div class="container_16">
+ <div class="grid_16 mediagoblin_header">
+ {% block mediagoblin_logo %}
+ <a class="mediagoblin_logo"
+ href="{{ request.urlgen('index') }}">
+ <img src="{{ request.staticdirect('/images/logo.png') }}"
+ alt="{% trans %}Mediagoblin logo{% endtrans %}" />
+ </a>
+ {% endblock %}
+ {% if request.user and request.user['status'] == 'active' %}
+ <a class="header_submit"
+ href="{{ request.urlgen('mediagoblin.submit.start') }}">
+ {% trans %}Submit media{% endtrans %}
+ </a>
+ {% endif %}
+ {% block mediagoblin_header_title %}{% endblock %}
+ <div class="mediagoblin_header_right">
{% if request.user %}
- <a class="header_submit" href="{{ request.urlgen('mediagoblin.submit.start') }}">Submit media</a>
- {% endif %}
- {% block mediagoblin_header_title %}{% endblock %}
- <div class="mediagoblin_header_right">
- {% if request.user %}
+ {# the following link should only appear when verification is needed #}
+ {% if request.user.status == "needs_email_verification" %}
<a href="{{ request.urlgen('mediagoblin.user_pages.user_home',
- user= request.user['username']) }}">
- {{ request.user['username'] }}</a>'s account
- (<a href="{{ request.urlgen('mediagoblin.auth.logout') }}">logout</a>)
- {% else %}
- <a href="{{ request.urlgen('mediagoblin.auth.login') }}">
- Login</a>
+ user=request.user['username']) }}"
+ class="header_submit">
+ {% trans %}verify your email!{% endtrans %}</a>
{% endif %}
- </div>
+
+ <a href="{{ request.urlgen('mediagoblin.user_pages.user_home',
+ user= request.user['username']) }}">
+ {{ request.user['username'] }}</a>
+
+ (<a href="{{ request.urlgen('mediagoblin.auth.logout') }}">logout</a>)
+ {% else %}
+ <a href="{{ request.urlgen('mediagoblin.auth.login') }}">
+ {% trans %}Login{% endtrans %}</a>
+ {% endif %}
</div>
</div>
</div>
@@ -69,11 +83,11 @@
</div>
</div>
{% block mediagoblin_footer %}
- <div class="mediagoblin_footer">
- <div class="container_16">
- <div class="grid_16">
+ <div class="container_16">
+ <div class="grid_16 mediagoblin_footer">
+ {% trans -%}
Powered by <a href="http://mediagoblin.org">MediaGoblin</a>, a <a href="http://gnu.org/">GNU project</a>
- </div>
+ {%- endtrans %}
</div>
</div>
{% endblock %}
diff --git a/mediagoblin/templates/mediagoblin/edit/edit.html b/mediagoblin/templates/mediagoblin/edit/edit.html
index d19034cb..3dc170c8 100644
--- a/mediagoblin/templates/mediagoblin/edit/edit.html
+++ b/mediagoblin/templates/mediagoblin/edit/edit.html
@@ -26,15 +26,15 @@
media= media._id) }}"
method="POST" enctype="multipart/form-data">
<div class="grid_8 prefix_1 suffix_1 edit_box form_box">
- <h1>Editing {{ media.title }}</h1>
+ <h1>{% trans media_title=media.title %}Editing {{ media_title }}{% endtrans %}</h1>
<div style="text-align: center;" >
<img src="{{ request.app.public_store.file_url(
media['media_files']['thumb']) }}" />
</div>
{{ wtforms_util.render_divs(form) }}
<div class="form_submit_buttons">
- <a href="{{ media.url_for_self(request.urlgen) }}">Cancel</a>
- <input type="submit" value="Save changes" class="button" />
+ <a href="{{ media.url_for_self(request.urlgen) }}">{% trans %}Cancel{% endtrans %}</a>
+ <input type="submit" value="{% trans %}Save changes{% endtrans %}" class="button" />
</div>
</div>
</form>
diff --git a/mediagoblin/templates/mediagoblin/edit/edit_profile.html b/mediagoblin/templates/mediagoblin/edit/edit_profile.html
index a11b86d7..534e5f20 100644
--- a/mediagoblin/templates/mediagoblin/edit/edit_profile.html
+++ b/mediagoblin/templates/mediagoblin/edit/edit_profile.html
@@ -25,10 +25,14 @@
user['username'] }}"
method="POST" enctype="multipart/form-data">
<div class="grid_8 prefix_1 suffix_1 edit_box form_box">
- <h1>Editing {{ user['username'] }}'s profile</h1>
+ <h1>
+ {%- trans username=user['username'] -%}
+ Editing {{ username }}'s profile
+ {%- endtrans %}
+ </h1>
{{ wtforms_util.render_divs(form) }}
<div class="form_submit_buttons">
- <input type="submit" value="submit" class="button" />
+ <input type="submit" value="{% trans %}Submit{% endtrans %}" class="button" />
</div>
</div>
</form>
diff --git a/mediagoblin/templates/mediagoblin/listings/tag.html b/mediagoblin/templates/mediagoblin/listings/tag.html
new file mode 100644
index 00000000..a013797f
--- /dev/null
+++ b/mediagoblin/templates/mediagoblin/listings/tag.html
@@ -0,0 +1,43 @@
+{#
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011 Free Software Foundation, Inc
+#
+# 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 %}
+ <link rel="alternate" type="application/atom+xml"
+ href="{{ request.urlgen(
+ 'mediagoblin.listings.tag_atom_feed',
+ tag=tag_slug) }}">
+{% endblock mediagoblin_head %}
+
+{% block mediagoblin_content -%}
+ <h1>
+ {% trans %}Media tagged with:{% endtrans %} {{ tag_name }}
+ </h1>
+
+ <div class="container_16 media_gallery">
+ {% include "mediagoblin/utils/object_gallery.html" %}
+ </div>
+
+ <div class="grid_16">
+ <a href="{{ request.urlgen(
+ 'mediagoblin.listings.tag_atom_feed',
+ tag=tag_slug) }}">
+ {%- trans %}atom feed{% endtrans -%}
+ </a>
+ </div>
+{% endblock %}
diff --git a/mediagoblin/templates/mediagoblin/root.html b/mediagoblin/templates/mediagoblin/root.html
index bae033c4..a4e19984 100644
--- a/mediagoblin/templates/mediagoblin/root.html
+++ b/mediagoblin/templates/mediagoblin/root.html
@@ -22,22 +22,26 @@
{% if request.user %}
<p>
- <a href="{{ request.urlgen('mediagoblin.submit.start') }}">Submit an item</a>
+ <a href="{{ request.urlgen('mediagoblin.submit.start') }}">
+ {%- trans %}Submit an item{% endtrans -%}
+ </a>
</p>
{% else %}
<p>
- If you have an account, you can
- <a href="{{ request.urlgen('mediagoblin.auth.login') }}">Login</a>.
+ {% trans login_url=request.urlgen('mediagoblin.auth.login') -%}
+ If you have an account, you can <a href="{{ login_url }}">Login</a>.
+ {%- endtrans %}
</p>
{% if allow_registration %}
<p>
- If you don't have an account, please
- <a href="{{ request.urlgen('mediagoblin.auth.register') }}">Register</a>.
+ {% trans register_url=request.urlgen('mediagoblin.auth.register') -%}
+ If you don't have an account, please <a href="{{ register_url }}">Register</a>.
+ {%- endtrans %}
</p>
{% endif %}
{% endif %}
{# temporarily, an "image gallery" that isn't one really ;) #}
- {% include "mediagoblin/utils/object_gallery.html" %}
+ {% include "mediagoblin/utils/object_gallery.html" %}
{% endblock %}
diff --git a/mediagoblin/templates/mediagoblin/submit/start.html b/mediagoblin/templates/mediagoblin/submit/start.html
index 50c86afe..eb34c2e2 100644
--- a/mediagoblin/templates/mediagoblin/submit/start.html
+++ b/mediagoblin/templates/mediagoblin/submit/start.html
@@ -23,12 +23,13 @@
<form action="{{ request.urlgen('mediagoblin.submit.start') }}"
method="POST" enctype="multipart/form-data">
<div class="grid_8 prefix_1 suffix_1 form_box">
- <h1>Submit yer media</h1>
+ <h1>{% trans %}Submit yer media{% endtrans %}</h1>
{{ wtforms_util.render_field_div(submit_form.file) }}
{{ wtforms_util.render_field_div(submit_form.title) }}
{{ wtforms_util.render_textarea_div(submit_form.description) }}
+ {{ wtforms_util.render_field_div(submit_form.tags) }}
<div class="form_submit_buttons">
- <input type="submit" value="Submit" class="button" />
+ <input type="submit" value="{% trans %}Submit{% endtrans %}" class="button" />
</div>
</div>
</form>
diff --git a/mediagoblin/templates/mediagoblin/user_pages/gallery.html b/mediagoblin/templates/mediagoblin/user_pages/gallery.html
index a434ff15..a66a547e 100644
--- a/mediagoblin/templates/mediagoblin/user_pages/gallery.html
+++ b/mediagoblin/templates/mediagoblin/user_pages/gallery.html
@@ -27,21 +27,27 @@
{% block mediagoblin_content -%}
{% if user %}
<h1>
- <a href="{{ request.urlgen(
- 'mediagoblin.user_pages.user_home',
- user=user.username) }}">{{ user.username }}</a>'s media</h1>
+ {%- trans username=user.username,
+ user_url=request.urlgen(
+ 'mediagoblin.user_pages.user_home',
+ user=user.username) -%}
+ <a href="{{ user_url }}">{{ username }}</a>'s media
+ {%- endtrans %}
+ </h1>
</div>
<div class="container_16 media_gallery">
{% include "mediagoblin/utils/object_gallery.html" %}
</div>
<div class="grid_16">
-
- <a href={{ request.urlgen(
- 'mediagoblin.user_pages.atom_feed',
- user=user.username) }}> atom feed</a>
+ <a href="{{ request.urlgen(
+ 'mediagoblin.user_pages.atom_feed',
+ user=user.username) }}">
+ {%- trans %}atom feed{% endtrans -%}
+ </a>
+ </div>
{% else %}
{# This *should* not occur as the view makes sure we pass in a user. #}
- <p>Sorry, no such user found.<p/>
+ <p>{% trans %}Sorry, no such user found.{% endtrans %}<p/>
{% endif %}
{% endblock %}
diff --git a/mediagoblin/templates/mediagoblin/user_pages/media.html b/mediagoblin/templates/mediagoblin/user_pages/media.html
index dc0b6210..afc0d903 100644
--- a/mediagoblin/templates/mediagoblin/user_pages/media.html
+++ b/mediagoblin/templates/mediagoblin/user_pages/media.html
@@ -23,8 +23,11 @@
{% block mediagoblin_content %}
{% if media %}
<div class="grid_11 alpha">
- <img class="media_image" src="{{ request.app.public_store.file_url(
- media.get_display_media(media.media_files)) }}" />
+ <div class="media_image_container">
+ <img class="media_image"
+ src="{{ request.app.public_store.file_url(
+ media.get_display_media(media.media_files)) }}" />
+ </div>
<h2>
{{media.title}}
@@ -35,24 +38,26 @@
{% endautoescape %}
<p>
- &mdash;&nbsp;uploaded on
- {{ "%4d-%02d-%02d"|format(media.created.year,
- media.created.month, media.created.day) }}
- by
- <a href="{{ request.urlgen('mediagoblin.user_pages.user_home',
- user= media.uploader().username) }}">
- {{- media.uploader().username }}</a>
+ {% trans date="%4d-%02d-%02d"|format(
+ media.created.year,
+ media.created.month, media.created.day),
+ user_url=request.urlgen(
+ 'mediagoblin.user_pages.user_home',
+ user=media.uploader().username),
+ username=media.uploader().username -%}
+ &mdash;&nbsp;uploaded on {{ date }} by <a href="{{ user_url }}">{{ username }}</a>
+ {%- endtrans %}
</p>
<br />
- <h3>Comments</h3>
+ <h3>{% trans %}Comments{% endtrans %}</h3>
{% if request.user %}
<form action="{{ request.urlgen('mediagoblin.user_pages.media_post_comment',
user= media.uploader().username,
media=media._id) }}" method="POST">
{{ wtforms_util.render_field_div(comment_form.comment_content) }}
<div class="form_submit_buttons">
- <input type="submit" value="Post comment!" class="button" />
+ <input type="submit" value="{% trans %}Post comment!{% endtrans %}" class="button" />
</div>
</form>
{% endif %}
@@ -74,7 +79,7 @@
<div class="comment_author">&mdash;
<a href="{{ request.urlgen('mediagoblin.user_pages.user_home',
user = comment_author['username']) }}">
- {{ comment_author['username'] }}</a> at
+ {{ comment_author['username'] }}</a> {% trans %}at{% endtrans %}
<!--</div>
<div class="comment_datetime">-->
<a href="{{ request.urlgen('mediagoblin.user_pages.media_home.view_comment',
@@ -97,12 +102,12 @@
media = media._id)) }}
</div>
{% endif %}
+
<div class="grid_5 omega">
{% include "mediagoblin/utils/prev_next.html" %}
- <h3>Sidebar content here!</h3>
- <p>
{% if media['uploader'] == request.user['_id'] or
request.user['is_admin'] %}
+ <h3>Temporary button holder</h3>
<p>
<a href="{{ request.urlgen('mediagoblin.edit.edit_media',
user= media.uploader().username,
@@ -112,12 +117,15 @@
</p>
<p>
<img src="{{ request.staticdirect('/images/icon_delete.png') }}"
- class="media_icon" />delete
+ class="media_icon" />{% trans %}delete{% endtrans %}
</p>
{% endif %}
- </p>
+
+ {% if media.tags %}
+ {% include "mediagoblin/utils/tags.html" %}
+ {% endif %}
</div>
{% else %}
- <p>Sorry, no such media found.<p/>
+ <p>{% trans %}Sorry, no such media found.{% endtrans %}<p/>
{% endif %}
{% endblock %}
diff --git a/mediagoblin/templates/mediagoblin/user_pages/user.html b/mediagoblin/templates/mediagoblin/user_pages/user.html
index 9d99ac53..1115fc56 100644
--- a/mediagoblin/templates/mediagoblin/user_pages/user.html
+++ b/mediagoblin/templates/mediagoblin/user_pages/user.html
@@ -25,26 +25,83 @@
{% endblock mediagoblin_head %}
{% block mediagoblin_content -%}
- {% if user %}
- <h1>{{ user.username }}'s profile</h1>
- <div class="grid_6 alpha">
- {% include "mediagoblin/utils/profile.html" %}
- {% if request.user['_id'] == user['_id'] or request.user['is_admin'] %}
+ {# If no user... #}
+ {% if not user %}
+ <p>{% trans %}Sorry, no such user found.{% endtrans %}<p/>
+
+ {# User exists, but needs verification #}
+ {% elif user.status == "needs_email_verification" %}
+ {% if user == request.user %}
+ {# this should only be visible when you are this user #}
+ <div class="grid_6 prefix_1 suffix_1 form_box">
+ <h1>{% trans %}Verification needed{% endtrans %}</h1>
+
+ <p>
+ {% trans -%}
+ Almost done! Your account still needs to be verified.
+ {%- endtrans %}
+ </p>
+ <p>
+ {% trans -%}
+ An email should arrive in a few moments with instructions on how to do so.
+ {%- endtrans %}
+ </p>
+ <p>{% trans %}In case it doesn't:{% endtrans %}</p>
+
+ <a href="{{ request.urlgen('mediagoblin.auth.resend_verification') }}"
+ class="button">{% trans %}Resend verification email{% endtrans %}</a>
+ </div>
+ {% else %}
+ {# if the user is not you, but still needs to verify their email #}
+ <div class="grid_6 prefix_1 suffix_1 form_box">
+ <h1>{% trans %}Verification needed{% endtrans %}</h1>
+
+ <p>
+ {% trans -%}
+ Someone has registered an account with this username, but it still has to be verified.
+ {%- endtrans %}
+ </p>
+
+ <p>
+ {% trans login_url=request.urlgen('mediagoblin.auth.login') -%}
+ If you are that person but you've lost your verification email, you can <a href="{{ login_url }}">log in</a> and resend it.
+ {%- endtrans %}
+ </p>
+ </div>
+ {% endif %}
+
+ {# Active(?) (or at least verified at some point) user, horray! #}
+ {% else %}
+ <h1>
+ {%- trans username=user.username %}{{ username }}'s profile{% endtrans -%}
+ </h1>
+
+ <div class="grid_6 alpha">
+ {% include "mediagoblin/utils/profile.html" %}
+ {% if request.user['_id'] == user['_id'] or request.user['is_admin'] %}
<a href="{{ request.urlgen('mediagoblin.edit.profile') }}?username={{
- user.username }}">Edit profile</a>
- {% endif %}
- </div>
- <div class="grid_10 omega">
- {% set pagination_base_url = user_gallery_url %}
- {% include "mediagoblin/utils/object_gallery.html" %}
- <div class="clear"></div>
- <p><a href="{{ user_gallery_url }}">View all of {{ user.username }}'s media</a></p>
- <a href={{ request.urlgen(
- 'mediagoblin.user_pages.atom_feed',
- user=user.username) }}>atom feed</a>
- {% else %}
- {# This *should* not occur as the view makes sure we pass in a user. #}
- <p>Sorry, no such user found.<p/>
+ user.username }}">
+ {%- trans %}Edit profile{% endtrans -%}
+ </a>
+ {% endif %}
+ </div>
+
+ <div class="grid_10 omega">
+ {% set pagination_base_url = user_gallery_url %}
+ {% include "mediagoblin/utils/object_gallery.html" %}
+ <div class="clear"></div>
+ <p>
+ <a href="{{ user_gallery_url }}">
+ {% trans username=user.username -%}
+ View all of {{ username }}'s media{% endtrans -%}
+ </a>
+ </p>
+ <a href="{{ request.urlgen( 'mediagoblin.user_pages.atom_feed',
+ user=user.username) }}">
+ {%- trans %}atom feed{% endtrans -%}
+ </a>
</div>
+
+ <div class="clear"></div>
{% endif %}
{% endblock %}
diff --git a/mediagoblin/templates/mediagoblin/utils/object_gallery.html b/mediagoblin/templates/mediagoblin/utils/object_gallery.html
index 2c7a7129..03b85b17 100644
--- a/mediagoblin/templates/mediagoblin/utils/object_gallery.html
+++ b/mediagoblin/templates/mediagoblin/utils/object_gallery.html
@@ -19,7 +19,7 @@
{% from "mediagoblin/utils/pagination.html" import render_pagination %}
{% block object_gallery_content -%}
- {% if media_entries %}
+ {% if media_entries and media_entries.count() %}
{% for entry in media_entries %}
<div class="media_thumbnail">
<a href="{{ entry.url_for_self(request.urlgen) }}">
@@ -27,11 +27,16 @@
entry['media_files']['thumb']) }}" /></a>
</div>
{% endfor %}
+ <div class="clear"></div>
{% if pagination_base_url %}
{# different url, so set that and don't keep the get params #}
{{ render_pagination(request, pagination, pagination_base_url, False) }}
{% else %}
{{ render_pagination(request, pagination) }}
{% endif %}
+ {% else %}
+ <p>
+ <i>There doesn't seem to be any media here yet...</i>
+ </p>
{% endif %}
{% endblock %}
diff --git a/mediagoblin/templates/mediagoblin/auth/verification_needed.html b/mediagoblin/templates/mediagoblin/utils/tags.html
index 4104da19..32db6e31 100644
--- a/mediagoblin/templates/mediagoblin/auth/verification_needed.html
+++ b/mediagoblin/templates/mediagoblin/utils/tags.html
@@ -15,15 +15,15 @@
# 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_content %}
- <p>
- Verfication needed!<br />
- Please check your email to verify your account.
- </p>
-
- <p>
- Still haven't received an email? <a href="{{ request.urlgen('mediagoblin.auth.resend_verification') }}">Click here to resend it.</a>
- </p>
+{% block tags_content -%}
+ <h3>Tags</h3>
+ <ul class="mediaentry_tags">
+ {% for tag in media.tags %}
+ <li class="tag">
+ <a href="{{ request.urlgen(
+ 'mediagoblin.listings.tags_listing',
+ tag=tag['slug']) }}">{{ tag['name'] }}</li>
+ {% endfor %}
+ </ul>
{% endblock %}
diff --git a/mediagoblin/templates/mediagoblin/utils/wtforms.html b/mediagoblin/templates/mediagoblin/utils/wtforms.html
index 1d2f8619..e3d8e137 100644
--- a/mediagoblin/templates/mediagoblin/utils/wtforms.html
+++ b/mediagoblin/templates/mediagoblin/utils/wtforms.html
@@ -19,9 +19,9 @@
{# Generically render a field #}
{% macro render_field_div(field) %}
<div class="form_field_box">
- <div class="form_field_label">{{ field.label }}</div>
+ <div class="form_field_label">{{ _(field.label.text) }}</div>
{% if field.description -%}
- <div class="form_field_description">{{ field.description }}</div>
+ <div class="form_field_description">{{ _(field.description) }}</div>
{%- endif %}
<div class="form_field_input">{{ field }}</div>
{%- if field.errors -%}
@@ -38,9 +38,9 @@
# ... mostly the same thing except it includes rows and cols #}
{% macro render_textarea_div(field, rows=8, cols=20) %}
<div class="form_field_box">
- <div class="form_field_label">{{ field.label }}</div>
+ <div class="form_field_label">{{ _(field.label.text) }}</div>
{% if field.description -%}
- <div class="form_field_description">{{ field.description }}</div>
+ <div class="form_field_description">{{ _(field.description) }}</div>
{%- endif %}
<div class="form_field_input">{{ field(rows=rows, cols=cols) }}</div>
{%- if field.errors -%}
@@ -64,7 +64,7 @@
{% macro render_table(form) -%}
{% for field in form %}
<tr>
- <th>{{field.label}}</th>
+ <th>{{ _(field.label.text) }}</th>
<td>
{{field}}
{% if field.errors %}
diff --git a/mediagoblin/tests/test_auth.py b/mediagoblin/tests/test_auth.py
index ad9dd35b..4781dd9b 100644
--- a/mediagoblin/tests/test_auth.py
+++ b/mediagoblin/tests/test_auth.py
@@ -153,9 +153,9 @@ def test_register_views(test_app):
## Did we redirect to the proper page? Use the right template?
assert_equal(
urlparse.urlsplit(response.location)[2],
- '/auth/register/success/')
+ '/u/happygirl/')
assert util.TEMPLATE_TEST_CONTEXT.has_key(
- 'mediagoblin/auth/register_success.html')
+ 'mediagoblin/user_pages/user.html')
## Make sure user is in place
new_user = mg_globals.database.User.find_one(
@@ -164,6 +164,11 @@ def test_register_views(test_app):
assert new_user['status'] == u'needs_email_verification'
assert new_user['email_verified'] == False
+ ## Make sure user is logged in
+ request = util.TEMPLATE_TEST_CONTEXT[
+ 'mediagoblin/user_pages/user.html']['request']
+ assert request.session['user_id'] == unicode(new_user['_id'])
+
## Make sure we get email confirmation, and try verifying
assert len(util.EMAIL_TEST_INBOX) == 1
message = util.EMAIL_TEST_INBOX.pop()
@@ -185,12 +190,14 @@ def test_register_views(test_app):
## Try verifying with bs verification key, shouldn't work
util.clear_test_template_context()
- test_app.get(
+ response = test_app.get(
"/auth/verify_email/?userid=%s&token=total_bs" % unicode(
new_user['_id']))
+ response.follow()
context = util.TEMPLATE_TEST_CONTEXT[
'mediagoblin/user_pages/user.html']
- assert context['verification_successful'] == False
+ # assert context['verification_successful'] == True
+ # TODO: Would be good to test messages here when we can do so...
new_user = mg_globals.database.User.find_one(
{'username': 'happygirl'})
assert new_user
@@ -199,10 +206,12 @@ def test_register_views(test_app):
## Verify the email activation works
util.clear_test_template_context()
- test_app.get("%s?%s" % (path, get_params))
+ response = test_app.get("%s?%s" % (path, get_params))
+ response.follow()
context = util.TEMPLATE_TEST_CONTEXT[
'mediagoblin/user_pages/user.html']
- assert context['verification_successful'] == True
+ # assert context['verification_successful'] == True
+ # TODO: Would be good to test messages here when we can do so...
new_user = mg_globals.database.User.find_one(
{'username': 'happygirl'})
assert new_user
diff --git a/mediagoblin/tests/test_mgoblin_app.ini b/mediagoblin/tests/test_mgoblin_app.ini
index fd0f87a4..7716e9ca 100644
--- a/mediagoblin/tests/test_mgoblin_app.ini
+++ b/mediagoblin/tests/test_mgoblin_app.ini
@@ -7,6 +7,10 @@ email_sender_address = "notice@mediagoblin.example.org"
email_debug_mode = true
db_name = __mediagoblin_tests__
+# tag parsing
+tags_delimiter = ","
+tags_max_length = 50
+
# Celery shouldn't be set up by the application as it's setup via
# mediagoblin.init.celery.from_celery
celery_setup_elsewhere = true
diff --git a/mediagoblin/tests/test_submission.py b/mediagoblin/tests/test_submission.py
index 22b6117c..a7248255 100644
--- a/mediagoblin/tests/test_submission.py
+++ b/mediagoblin/tests/test_submission.py
@@ -35,6 +35,9 @@ EVIL_JPG = pkg_resources.resource_filename(
EVIL_PNG = pkg_resources.resource_filename(
'mediagoblin.tests', 'test_submission/evil.png')
+GOOD_TAG_STRING = 'yin,yang'
+BAD_TAG_STRING = 'rage,' + 'f' * 26 + 'u' * 26
+
class TestSubmission:
def setUp(self):
@@ -110,6 +113,42 @@ class TestSubmission:
assert util.TEMPLATE_TEST_CONTEXT.has_key(
'mediagoblin/user_pages/user.html')
+ def test_tags(self):
+ # Good tag string
+ # --------
+ util.clear_test_template_context()
+ response = self.test_app.post(
+ '/submit/', {
+ 'title': 'Balanced Goblin',
+ 'tags': GOOD_TAG_STRING
+ }, upload_files=[(
+ 'file', GOOD_JPG)])
+
+ # New media entry with correct tags should be created
+ response.follow()
+ context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/user_pages/user.html']
+ request = context['request']
+ media = request.db.MediaEntry.find({'title': 'Balanced Goblin'})[0]
+ assert_equal(media['tags'],
+ [{'name': u'yin', 'slug': u'yin'},
+ {'name': u'yang', 'slug': u'yang'}])
+
+ # Test tags that are too long
+ # ---------------
+ util.clear_test_template_context()
+ response = self.test_app.post(
+ '/submit/', {
+ 'title': 'Balanced Goblin',
+ 'tags': BAD_TAG_STRING
+ }, upload_files=[(
+ 'file', GOOD_JPG)])
+
+ # Too long error should be raised
+ context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html']
+ form = context['submit_form']
+ assert form.tags.errors == [
+ u'Tags must be shorter than 50 characters. Tags that are too long'\
+ ': ffffffffffffffffffffffffffuuuuuuuuuuuuuuuuuuuuuuuuuu']
def test_malicious_uploads(self):
# Test non-suppoerted file with non-supported extension
diff --git a/mediagoblin/tests/test_tags.py b/mediagoblin/tests/test_tags.py
new file mode 100644
index 00000000..c2e9fa2b
--- /dev/null
+++ b/mediagoblin/tests/test_tags.py
@@ -0,0 +1,50 @@
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011 Free Software Foundation, Inc
+#
+# 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.tests.tools import setup_fresh_app
+from mediagoblin import util
+from mediagoblin import mg_globals
+
+
+@setup_fresh_app
+def test_list_of_dicts_conversion(test_app):
+ """
+ When the user adds tags to a media entry, the string from the form is
+ converted into a list of tags, where each tag is stored in the database
+ as a dict. Each tag dict should contain the tag's name and slug. Another
+ function performs the reverse operation when populating a form to edit tags.
+ """
+ # Leading, trailing, and internal whitespace should be removed and slugified
+ assert util.convert_to_tag_list_of_dicts('sleep , 6 AM, chainsaw! ') == [
+ {'name': u'sleep', 'slug': u'sleep'},
+ {'name': u'6 AM', 'slug': u'6-am'},
+ {'name': u'chainsaw!', 'slug': u'chainsaw'}]
+
+ # If the user enters two identical tags, record only one of them
+ assert util.convert_to_tag_list_of_dicts('echo,echo') == [{'name': u'echo',
+ 'slug': u'echo'}]
+
+ # Make sure converting the list of dicts to a string works
+ assert util.media_tags_as_string([{'name': u'yin', 'slug': u'yin'},
+ {'name': u'yang', 'slug': u'yang'}]) == \
+ u'yin,yang'
+
+ # If the tag delimiter is a space then we expect different results
+ mg_globals.app_config['tags_delimiter'] = u' '
+ assert util.convert_to_tag_list_of_dicts('unicorn ceramic nazi') == [
+ {'name': u'unicorn', 'slug': u'unicorn'},
+ {'name': u'ceramic', 'slug': u'ceramic'},
+ {'name': u'nazi', 'slug': u'nazi'}]
diff --git a/mediagoblin/tests/tools.py b/mediagoblin/tests/tools.py
index 4b61f259..ab14c21e 100644
--- a/mediagoblin/tests/tools.py
+++ b/mediagoblin/tests/tools.py
@@ -58,6 +58,9 @@ def suicide_if_bad_celery_environ():
def get_test_app(dump_old_app=True):
suicide_if_bad_celery_environ()
+ # Make sure we've turned on testing
+ util._activate_testing()
+
# Leave this imported as it sets up celery.
from mediagoblin.init.celery import from_tests
diff --git a/mediagoblin/translations/en/LC_MESSAGES/mediagoblin.mo b/mediagoblin/translations/en/LC_MESSAGES/mediagoblin.mo
deleted file mode 100644
index fb7046cd..00000000
--- a/mediagoblin/translations/en/LC_MESSAGES/mediagoblin.mo
+++ /dev/null
Binary files differ
diff --git a/mediagoblin/translations/en/LC_MESSAGES/mediagoblin.po b/mediagoblin/translations/en/LC_MESSAGES/mediagoblin.po
deleted file mode 100644
index 3bec204e..00000000
--- a/mediagoblin/translations/en/LC_MESSAGES/mediagoblin.po
+++ /dev/null
@@ -1,23 +0,0 @@
-# Translations template for PROJECT.
-# Copyright (C) 2011 ORGANIZATION
-# This file is distributed under the same license as the PROJECT project.
-# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: PROJECT VERSION\n"
-"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2011-05-12 22:28-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"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Generated-By: Babel 0.9.6\n"
-
-#: mediagoblin/templates/mediagoblin/root.html:22
-msgid "Welcome to GNU MediaGoblin!"
-msgstr ""
-
diff --git a/mediagoblin/user_pages/__init__.py b/mediagoblin/user_pages/__init__.py
index e69de29b..a8eeb5ed 100644
--- a/mediagoblin/user_pages/__init__.py
+++ b/mediagoblin/user_pages/__init__.py
@@ -0,0 +1,17 @@
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011 Free Software Foundation, Inc
+#
+# 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/>.
+
+
diff --git a/mediagoblin/user_pages/forms.py b/mediagoblin/user_pages/forms.py
index 8829b674..25001019 100644
--- a/mediagoblin/user_pages/forms.py
+++ b/mediagoblin/user_pages/forms.py
@@ -16,7 +16,10 @@
import wtforms
+from mediagoblin.util import fake_ugettext_passthrough as _
+
+
class MediaCommentForm(wtforms.Form):
- comment_content = wtforms.TextAreaField(
- 'Comment',
- [wtforms.validators.Required()])
+ comment_content = wtforms.TextAreaField(
+ _('Comment'),
+ [wtforms.validators.Required()])
diff --git a/mediagoblin/user_pages/views.py b/mediagoblin/user_pages/views.py
index a3172ebd..fb72a421 100644
--- a/mediagoblin/user_pages/views.py
+++ b/mediagoblin/user_pages/views.py
@@ -22,8 +22,8 @@ from mediagoblin.util import (
Pagination, render_to_response, redirect, cleaned_markdown_conversion)
from mediagoblin.user_pages import forms as user_forms
-from mediagoblin.decorators import uses_pagination, get_user_media_entry, \
- require_active_login
+from mediagoblin.decorators import (uses_pagination, get_user_media_entry,
+ require_active_login)
from werkzeug.contrib.atom import AtomFeed
@@ -32,10 +32,14 @@ from werkzeug.contrib.atom import AtomFeed
def user_home(request, page):
"""'Homepage' of a User()"""
user = request.db.User.find_one({
- 'username': request.matchdict['user'],
- 'status': 'active'})
+ 'username': request.matchdict['user']})
if not user:
return exc.HTTPNotFound()
+ elif user['status'] != u'active':
+ return render_to_response(
+ request,
+ 'mediagoblin/user_pages/user.html',
+ {'user': user})
cursor = request.db.MediaEntry.find(
{'uploader': user['_id'],
@@ -139,7 +143,7 @@ def media_post_comment(request):
user = request.matchdict['user'])
-ATOM_DEFAULT_NR_OF_UPDATED_ITEMS = 5
+ATOM_DEFAULT_NR_OF_UPDATED_ITEMS = 15
def atom_feed(request):
"""
@@ -150,7 +154,7 @@ def atom_feed(request):
'username': request.matchdict['user'],
'status': 'active'})
if not user:
- return exc.HTTPNotFound()
+ return exc.HTTPNotFound()
cursor = request.db.MediaEntry.find({
'uploader': user['_id'],
diff --git a/mediagoblin/util.py b/mediagoblin/util.py
index 1892378c..b46c65d9 100644
--- a/mediagoblin/util.py
+++ b/mediagoblin/util.py
@@ -25,13 +25,16 @@ import re
import urllib
from math import ceil, floor
import copy
+import wtforms
from babel.localedata import exists
+from babel.support import LazyProxy
import jinja2
import translitcodec
from webob import Response, exc
from lxml.html.clean import Cleaner
import markdown
+from wtforms.form import Form
from mediagoblin import mg_globals
from mediagoblin import messages
@@ -92,8 +95,8 @@ def get_jinja_env(template_loader, locale):
extensions=['jinja2.ext.i18n', 'jinja2.ext.autoescape'])
template_env.install_gettext_callables(
- mg_globals.translations.gettext,
- mg_globals.translations.ngettext)
+ mg_globals.translations.ugettext,
+ mg_globals.translations.ungettext)
# All templates will know how to ...
# ... fetch all waiting messages and remove them from the queue
@@ -299,7 +302,7 @@ def send_email(from_addr, to_addrs, subject, message_body):
TRANSLATIONS_PATH = pkg_resources.resource_filename(
- 'mediagoblin', 'translations')
+ 'mediagoblin', 'i18n')
def locale_to_lower_upper(locale):
@@ -384,9 +387,63 @@ def clean_html(html):
return HTML_CLEANER.clean_html(html)
-MARKDOWN_INSTANCE = markdown.Markdown(safe_mode='escape')
+def convert_to_tag_list_of_dicts(tag_string):
+ """
+ Filter input from incoming string containing user tags,
+
+ Strips trailing, leading, and internal whitespace, and also converts
+ the "tags" text into an array of tags
+ """
+ taglist = []
+ if tag_string:
+
+ # Strip out internal, trailing, and leading whitespace
+ stripped_tag_string = u' '.join(tag_string.strip().split())
+
+ # Split the tag string into a list of tags
+ for tag in stripped_tag_string.split(
+ mg_globals.app_config['tags_delimiter']):
+
+ # Ignore empty or duplicate tags
+ if tag.strip() and tag.strip() not in [t['name'] for t in taglist]:
+
+ taglist.append({'name': tag.strip(),
+ 'slug': slugify(tag.strip())})
+ return taglist
+
+
+def media_tags_as_string(media_entry_tags):
+ """
+ Generate a string from a media item's tags, stored as a list of dicts
+
+ This is the opposite of convert_to_tag_list_of_dicts
+ """
+ media_tag_string = ''
+ if media_entry_tags:
+ media_tag_string = mg_globals.app_config['tags_delimiter'].join(
+ [tag['name'] for tag in media_entry_tags])
+ return media_tag_string
+
+TOO_LONG_TAG_WARNING = \
+ u'Tags must be shorter than %s characters. Tags that are too long: %s'
+
+def tag_length_validator(form, field):
+ """
+ Make sure tags do not exceed the maximum tag length.
+ """
+ tags = convert_to_tag_list_of_dicts(field.data)
+ too_long_tags = [
+ tag['name'] for tag in tags
+ if len(tag['name']) > mg_globals.app_config['tags_max_length']]
+
+ if too_long_tags:
+ raise wtforms.ValidationError(
+ TOO_LONG_TAG_WARNING % (mg_globals.app_config['tags_max_length'], \
+ ', '.join(too_long_tags)))
+MARKDOWN_INSTANCE = markdown.Markdown(safe_mode='escape')
+
def cleaned_markdown_conversion(text):
"""
Take a block of text, run it through MarkDown, and clean its HTML.
@@ -423,6 +480,66 @@ def setup_gettext(locale):
translations=this_gettext)
+# Force en to be setup before anything else so that
+# mg_globals.translations is never None
+setup_gettext('en')
+
+
+def pass_to_ugettext(*args, **kwargs):
+ """
+ Pass a translation on to the appropriate ugettext method.
+
+ 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.translations.ugettext(
+ *args, **kwargs)
+
+
+def lazy_pass_to_ugettext(*args, **kwargs):
+ """
+ Lazily pass to ugettext.
+
+ This is useful if you have to define a translation on a module
+ level but you need it to not translate until the time that it's
+ used as a string.
+ """
+ return LazyProxy(pass_to_ugettext, *args, **kwargs)
+
+
+def pass_to_ngettext(*args, **kwargs):
+ """
+ Pass a translation on to the appropriate ngettext method.
+
+ The reason we can't have a global ngettext method is because
+ mg_globals gets swapped out by the application per-request.
+ """
+ return mg_globals.translations.ngettext(
+ *args, **kwargs)
+
+
+def lazy_pass_to_ngettext(*args, **kwargs):
+ """
+ Lazily pass to ngettext.
+
+ This is useful if you have to define a translation on a module
+ level but you need it to not translate until the time that it's
+ used as a string.
+ """
+ return LazyProxy(pass_to_ngettext, *args, **kwargs)
+
+
+def fake_ugettext_passthrough(string):
+ """
+ Fake a ugettext call for extraction's sake ;)
+
+ In wtforms there's a separate way to define a method to translate
+ things... so we just need to mark up the text so that it can be
+ extracted, not so that it's actually run through gettext.
+ """
+ return string
+
+
PAGINATION_DEFAULT_PER_PAGE = 30
class Pagination(object):
diff --git a/mediagoblin/views.py b/mediagoblin/views.py
index e7d9dbdd..ccd7a2df 100644
--- a/mediagoblin/views.py
+++ b/mediagoblin/views.py
@@ -15,17 +15,23 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from mediagoblin import mg_globals
-from mediagoblin.util import render_to_response
+from mediagoblin.util import render_to_response, Pagination
from mediagoblin.db.util import DESCENDING
+from mediagoblin.decorators import uses_pagination
-def root_view(request):
- media_entries = request.db.MediaEntry.find(
+@uses_pagination
+def root_view(request, page):
+ cursor = request.db.MediaEntry.find(
{u'state': u'processed'}).sort('created', DESCENDING)
-
+
+ pagination = Pagination(page, cursor)
+ media_entries = pagination()
+
return render_to_response(
request, 'mediagoblin/root.html',
{'media_entries': media_entries,
- 'allow_registration': mg_globals.app_config["allow_registration"]})
+ 'allow_registration': mg_globals.app_config["allow_registration"],
+ 'pagination': pagination})
def simple_template_render(request):
diff --git a/setup.py b/setup.py
index 799f00d8..40715dd0 100644
--- a/setup.py
+++ b/setup.py
@@ -18,7 +18,7 @@ from setuptools import setup, find_packages
setup(
name = "mediagoblin",
- version = "0.0.3",
+ version = "0.0.4",
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
zip_safe=False,
# scripts and dependencies
@@ -44,26 +44,39 @@ setup(
'webtest',
'ConfigObj',
'Markdown',
+ 'python-cloudfiles',
## For now we're expecting that users will install this from
## their package managers.
# 'lxml',
],
test_suite='nose.collector',
-
- license = 'AGPLv3',
- author = 'Free Software Foundation and contributors',
- author_email = 'cwebber@gnu.org',
entry_points = """\
- [console_scripts]
- gmg = mediagoblin.gmg_commands:main_cli
+ [console_scripts]
+ gmg = mediagoblin.gmg_commands:main_cli
+ pybabel = mediagoblin.babel.messages.frontend:main
- [paste.app_factory]
- app = mediagoblin.app:paste_app_factory
+ [paste.app_factory]
+ app = mediagoblin.app:paste_app_factory
- [zc.buildout]
- make_user_dev_dirs = mediagoblin.buildout_recipes:MakeUserDevDirs
+ [zc.buildout]
+ make_user_dev_dirs = mediagoblin.buildout_recipes:MakeUserDevDirs
- [babel.extractors]
- jinja2 = jinja2.ext:babel_extract
- """,
+ [babel.extractors]
+ jinja2 = jinja2.ext:babel_extract
+ """,
+
+ license='AGPLv3',
+ author='Free Software Foundation and contributors',
+ author_email='cwebber@gnu.org',
+ url="http://mediagoblin.org/",
+ download_url="http://mediagoblin.org/download/",
+ long_description=open('README').read(),
+ classifiers=[
+ "Development Status :: 3 - Alpha",
+ "Environment :: Web Environment",
+ "License :: OSI Approved :: GNU Affero General Public License",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python",
+ "Topic :: Internet :: WWW/HTTP :: Dynamic Content"
+ ],
)
diff --git a/user_profile-fix_wrong_nesting.patch b/user_profile-fix_wrong_nesting.patch
deleted file mode 100644
index 590f87ff..00000000
--- a/user_profile-fix_wrong_nesting.patch
+++ /dev/null
@@ -1,17 +0,0 @@
-diff --git a/mediagoblin/templates/mediagoblin/user_pages/user.html b/mediagoblin/templates/mediagoblin/user_pages/user.html
-index 9d99ac5..a2b85af 100644
---- a/mediagoblin/templates/mediagoblin/user_pages/user.html
-+++ b/mediagoblin/templates/mediagoblin/user_pages/user.html
-@@ -42,9 +42,9 @@
- <a href={{ request.urlgen(
- 'mediagoblin.user_pages.atom_feed',
- user=user.username) }}>atom feed</a>
-- {% else %}
-- {# This *should* not occur as the view makes sure we pass in a user. #}
-- <p>Sorry, no such user found.<p/>
- </div>
-+ {% else %}
-+ {# This *should* not occur as the view makes sure we pass in a user. #}
-+ <p>Sorry, no such user found.<p/>
- {% endif %}
- {% endblock %}