diff options
119 files changed, 5317 insertions, 1161 deletions
@@ -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 + @@ -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. @@ -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/>`_. @@ -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 %}© <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 Binary files differindex e20265e6..e20265e6 100644 --- a/docs/goblin.png +++ b/docs/source/goblin.png 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 Binary files differindex f325ae4b..f325ae4b 100644 --- a/docs/snugglygoblin.png +++ b/docs/source/snugglygoblin.png 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 Binary files differnew file mode 100644 index 00000000..6bb69ce9 --- /dev/null +++ b/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.mo 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 Binary files differnew file mode 100644 index 00000000..dfd3a1bc --- /dev/null +++ b/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.mo 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 Binary files differnew file mode 100644 index 00000000..a7daf2c9 --- /dev/null +++ b/mediagoblin/i18n/fr/LC_MESSAGES/mediagoblin.mo 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 Binary files differnew file mode 100644 index 00000000..8911785f --- /dev/null +++ b/mediagoblin/i18n/nn_NO/LC_MESSAGES/mediagoblin.mo 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 Binary files differnew file mode 100644 index 00000000..4dc4ab5f --- /dev/null +++ b/mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.mo 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 Binary files differnew file mode 100644 index 00000000..361846d4 --- /dev/null +++ b/mediagoblin/i18n/ro/LC_MESSAGES/mediagoblin.mo 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 Binary files differnew file mode 100644 index 00000000..a70b1fef --- /dev/null +++ b/mediagoblin/i18n/sl/LC_MESSAGES/mediagoblin.mo 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 Binary files differnew file mode 100644 index 00000000..153584d3 --- /dev/null +++ b/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.mo 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 Binary files differnew file mode 100644 index 00000000..04fe0b6f --- /dev/null +++ b/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.mo 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 Binary files differnew file mode 100644 index 00000000..aa101308 --- /dev/null +++ b/mediagoblin/static/images/background.png 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> - — 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 -%} + — 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">— <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 Binary files differdeleted file mode 100644 index fb7046cd..00000000 --- a/mediagoblin/translations/en/LC_MESSAGES/mediagoblin.mo +++ /dev/null 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): @@ -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 %} |