From 04a95150646247abd13992b5c103a6d780d8861b Mon Sep 17 00:00:00 2001 From: Caleb Forbes Davis V Date: Fri, 8 Jul 2011 01:59:44 -0500 Subject: [PATCH 001/118] F360(tagging) - adds tag fields for submission, edit and display --- mediagoblin/edit/forms.py | 2 ++ mediagoblin/edit/views.py | 5 ++++- mediagoblin/submit/forms.py | 1 + mediagoblin/submit/views.py | 2 ++ mediagoblin/templates/mediagoblin/submit/start.html | 1 + mediagoblin/templates/mediagoblin/user_pages/media.html | 4 ++++ 6 files changed, 14 insertions(+), 1 deletion(-) diff --git a/mediagoblin/edit/forms.py b/mediagoblin/edit/forms.py index d5e7f0a9..b2d575cb 100644 --- a/mediagoblin/edit/forms.py +++ b/mediagoblin/edit/forms.py @@ -25,6 +25,8 @@ class EditForm(wtforms.Form): slug = wtforms.TextField( 'Slug') description = wtforms.TextAreaField('Description of this work') + tags = wtforms.TextField( + 'Tags') class EditProfileForm(wtforms.Form): bio = wtforms.TextAreaField('Bio', diff --git a/mediagoblin/edit/views.py b/mediagoblin/edit/views.py index a3326b2d..96cb4be3 100644 --- a/mediagoblin/edit/views.py +++ b/mediagoblin/edit/views.py @@ -16,6 +16,7 @@ from webob import exc +from string import split from mediagoblin import messages from mediagoblin.util import render_to_response, redirect, clean_html @@ -35,7 +36,8 @@ def edit_media(request, media): form = forms.EditForm(request.POST, title = media['title'], slug = media['slug'], - description = media['description']) + description = media['description'], + tags = ' '.join(media['tags'])) if request.method == 'POST' and form.validate(): # Make sure there isn't already a MediaEntry with such a slug @@ -59,6 +61,7 @@ def edit_media(request, media): media['description'])) media['slug'] = request.POST['slug'] + media['tags'] = split(request.POST['tags']) media.save() return redirect(request, "mediagoblin.user_pages.media_home", diff --git a/mediagoblin/submit/forms.py b/mediagoblin/submit/forms.py index 3fd9ea49..0e0fd086 100644 --- a/mediagoblin/submit/forms.py +++ b/mediagoblin/submit/forms.py @@ -24,3 +24,4 @@ class SubmitStartForm(wtforms.Form): [wtforms.validators.Length(min=0, max=500)]) description = wtforms.TextAreaField('Description of this work') file = wtforms.FileField('File') + tags = wtforms.TextField('Tags') diff --git a/mediagoblin/submit/views.py b/mediagoblin/submit/views.py index 4c7476b0..cdd58786 100644 --- a/mediagoblin/submit/views.py +++ b/mediagoblin/submit/views.py @@ -16,6 +16,7 @@ from os.path import splitext from cgi import FieldStorage +from string import split from werkzeug.utils import secure_filename @@ -58,6 +59,7 @@ def submit_start(request): entry['media_type'] = u'image' # heh entry['uploader'] = request.user['_id'] + entry['tags'] = split(request.POST.get('tags')) # Save, just so we can get the entry id for the sake of using # it to generate the file path diff --git a/mediagoblin/templates/mediagoblin/submit/start.html b/mediagoblin/templates/mediagoblin/submit/start.html index 50c86afe..7bacb552 100644 --- a/mediagoblin/templates/mediagoblin/submit/start.html +++ b/mediagoblin/templates/mediagoblin/submit/start.html @@ -25,6 +25,7 @@

Submit yer media

{{ wtforms_util.render_field_div(submit_form.file) }} + {{ wtforms_util.render_field_div(submit_form.tags) }} {{ wtforms_util.render_field_div(submit_form.title) }} {{ wtforms_util.render_textarea_div(submit_form.description) }}
diff --git a/mediagoblin/templates/mediagoblin/user_pages/media.html b/mediagoblin/templates/mediagoblin/user_pages/media.html index 1484cc73..1c263880 100644 --- a/mediagoblin/templates/mediagoblin/user_pages/media.html +++ b/mediagoblin/templates/mediagoblin/user_pages/media.html @@ -48,6 +48,10 @@ user= media.uploader().username) }}"> {{- media.uploader().username }}

+ +

+ {{ ' '.join(media.tags) }} +


Comments

From 272469daf5eb53f2302ae3948dde4e40eaf12497 Mon Sep 17 00:00:00 2001 From: Caleb Forbes Davis V Date: Sun, 10 Jul 2011 23:36:21 -0500 Subject: [PATCH 002/118] adds index for tag searches by an uploader --- mediagoblin/db/indexes.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mediagoblin/db/indexes.py b/mediagoblin/db/indexes.py index d379a52b..57bd33cd 100644 --- a/mediagoblin/db/indexes.py +++ b/mediagoblin/db/indexes.py @@ -88,6 +88,13 @@ MEDIAENTRY_INDEXES = { # Indexing on uploaders and when media entries are created. # Used for showing a user gallery, etc. 'index': [('uploader', ASCENDING), + ('created', DESCENDING)]}, + + 'uploader_tags_created': { + # Indexing on the media uploader, the associated tags, and timestamp + # Used for showing media items matching a tag search, most recent first. + 'index': [('uploader', ASCENDING), + ('tags', DESCENDING), ('created', DESCENDING)]}} From cdf538bd6163a47b4c4a6326c943b6deaf2c495a Mon Sep 17 00:00:00 2001 From: Caleb Forbes Davis V Date: Tue, 12 Jul 2011 20:06:17 -0500 Subject: [PATCH 003/118] adds filter function to parse and clean tags field input - for some reason the tags are showing up in the media edit form with u'..' and surrounded with []. I don't know why, grr --- mediagoblin/edit/forms.py | 5 +++-- mediagoblin/edit/views.py | 5 +++-- mediagoblin/submit/forms.py | 4 +++- mediagoblin/util.py | 19 ++++++++++++++++++- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/mediagoblin/edit/forms.py b/mediagoblin/edit/forms.py index b2d575cb..5e3aab96 100644 --- a/mediagoblin/edit/forms.py +++ b/mediagoblin/edit/forms.py @@ -17,6 +17,8 @@ import wtforms +from mediagoblin.util import convert_to_tag_list + class EditForm(wtforms.Form): title = wtforms.TextField( @@ -25,8 +27,7 @@ class EditForm(wtforms.Form): slug = wtforms.TextField( 'Slug') description = wtforms.TextAreaField('Description of this work') - tags = wtforms.TextField( - 'Tags') + tags = wtforms.TextField('Tags', filters=[convert_to_tag_list]) class EditProfileForm(wtforms.Form): bio = wtforms.TextAreaField('Bio', diff --git a/mediagoblin/edit/views.py b/mediagoblin/edit/views.py index 96cb4be3..f5e7f454 100644 --- a/mediagoblin/edit/views.py +++ b/mediagoblin/edit/views.py @@ -19,7 +19,8 @@ from webob import exc from string import split from mediagoblin import messages -from mediagoblin.util import render_to_response, redirect, clean_html +from mediagoblin.util import render_to_response, redirect, clean_html, \ + TAGS_DELIMITER from mediagoblin.edit import forms from mediagoblin.edit.lib import may_edit_media from mediagoblin.decorators import require_active_login, get_user_media_entry @@ -37,7 +38,7 @@ def edit_media(request, media): title = media['title'], slug = media['slug'], description = media['description'], - tags = ' '.join(media['tags'])) + tags = TAGS_DELIMITER.join(media['tags'])) if request.method == 'POST' and form.validate(): # Make sure there isn't already a MediaEntry with such a slug diff --git a/mediagoblin/submit/forms.py b/mediagoblin/submit/forms.py index 0e0fd086..e13d5425 100644 --- a/mediagoblin/submit/forms.py +++ b/mediagoblin/submit/forms.py @@ -17,6 +17,8 @@ import wtforms +from mediagoblin.util import convert_to_tag_list + class SubmitStartForm(wtforms.Form): title = wtforms.TextField( @@ -24,4 +26,4 @@ class SubmitStartForm(wtforms.Form): [wtforms.validators.Length(min=0, max=500)]) description = wtforms.TextAreaField('Description of this work') file = wtforms.FileField('File') - tags = wtforms.TextField('Tags') + tags = wtforms.TextField('Tags', filters=[convert_to_tag_list]) diff --git a/mediagoblin/util.py b/mediagoblin/util.py index ab219df0..7ee0a2d5 100644 --- a/mediagoblin/util.py +++ b/mediagoblin/util.py @@ -22,6 +22,7 @@ import sys import re import urllib from math import ceil +from string import strip import copy from babel.localedata import exists @@ -369,8 +370,24 @@ def clean_html(html): return HTML_CLEANER.clean_html(html) -MARKDOWN_INSTANCE = markdown.Markdown(safe_mode='escape') +TAGS_DELIMITER = ' ' +def convert_to_tag_list(tag_string): + """ + Filter input from a "tags" field, + + Strips trailing, leading, and internal whitespace, and also converts + the user input into an array of tags + """ + if tag_string: + taglist = [] + stripped_tag_string = ' '.join(tag_string.strip().split()) + for tag in stripped_tag_string.split(TAGS_DELIMITER): + if tag.strip(): taglist.append(tag.strip()) + return taglist + + +MARKDOWN_INSTANCE = markdown.Markdown(safe_mode='escape') def cleaned_markdown_conversion(text): """ From 93e3468a2af92a623a659628a20605025cea9ca7 Mon Sep 17 00:00:00 2001 From: Caleb Forbes Davis V Date: Tue, 12 Jul 2011 20:43:16 -0500 Subject: [PATCH 004/118] displays the tags on edit correctly now -before it was running the tags field through the submit filter. that was kind of dumb -removes the filter function from the edit form -adds unicode syntax in the filter function -uses split correctly when saving the edited tags to mongodb --- mediagoblin/edit/forms.py | 2 +- mediagoblin/edit/views.py | 2 +- mediagoblin/util.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mediagoblin/edit/forms.py b/mediagoblin/edit/forms.py index 5e3aab96..e13cfaa9 100644 --- a/mediagoblin/edit/forms.py +++ b/mediagoblin/edit/forms.py @@ -27,7 +27,7 @@ class EditForm(wtforms.Form): slug = wtforms.TextField( 'Slug') description = wtforms.TextAreaField('Description of this work') - tags = wtforms.TextField('Tags', filters=[convert_to_tag_list]) + tags = wtforms.TextField('Tags') class EditProfileForm(wtforms.Form): bio = wtforms.TextAreaField('Bio', diff --git a/mediagoblin/edit/views.py b/mediagoblin/edit/views.py index f5e7f454..0c4fd735 100644 --- a/mediagoblin/edit/views.py +++ b/mediagoblin/edit/views.py @@ -62,7 +62,7 @@ def edit_media(request, media): media['description'])) media['slug'] = request.POST['slug'] - media['tags'] = split(request.POST['tags']) + media['tags'] = request.POST['tags'].split(TAGS_DELIMITER) media.save() return redirect(request, "mediagoblin.user_pages.media_home", diff --git a/mediagoblin/util.py b/mediagoblin/util.py index 7ee0a2d5..4421bec4 100644 --- a/mediagoblin/util.py +++ b/mediagoblin/util.py @@ -370,7 +370,7 @@ def clean_html(html): return HTML_CLEANER.clean_html(html) -TAGS_DELIMITER = ' ' +TAGS_DELIMITER = u' ' def convert_to_tag_list(tag_string): """ @@ -381,7 +381,7 @@ def convert_to_tag_list(tag_string): """ if tag_string: taglist = [] - stripped_tag_string = ' '.join(tag_string.strip().split()) + stripped_tag_string = u' '.join(tag_string.strip().split()) for tag in stripped_tag_string.split(TAGS_DELIMITER): if tag.strip(): taglist.append(tag.strip()) return taglist From 6f2e4585cc7475362205a9ddb0e69d6da2b6dc85 Mon Sep 17 00:00:00 2001 From: Caleb Forbes Davis V Date: Tue, 12 Jul 2011 22:26:10 -0500 Subject: [PATCH 005/118] uses standard functions instead of form filters and fixes taglist default - seems simpler to use the same tag field processing procedures on media submit and edit, so now processing with a regular function instead of a form filter. Filters run on form load and post by default. - moved tags to sidebar - taglist defaults to [] instead of None - adds case sensitivity toggle --- mediagoblin/edit/forms.py | 2 -- mediagoblin/edit/views.py | 7 ++++--- mediagoblin/submit/forms.py | 4 +--- mediagoblin/submit/views.py | 5 +++-- .../templates/mediagoblin/user_pages/media.html | 9 +++++---- mediagoblin/util.py | 11 ++++++++--- 6 files changed, 21 insertions(+), 17 deletions(-) diff --git a/mediagoblin/edit/forms.py b/mediagoblin/edit/forms.py index e13cfaa9..21c8509a 100644 --- a/mediagoblin/edit/forms.py +++ b/mediagoblin/edit/forms.py @@ -17,8 +17,6 @@ import wtforms -from mediagoblin.util import convert_to_tag_list - class EditForm(wtforms.Form): title = wtforms.TextField( diff --git a/mediagoblin/edit/views.py b/mediagoblin/edit/views.py index 0c4fd735..0432024e 100644 --- a/mediagoblin/edit/views.py +++ b/mediagoblin/edit/views.py @@ -19,8 +19,9 @@ from webob import exc from string import split from mediagoblin import messages -from mediagoblin.util import render_to_response, redirect, clean_html, \ - TAGS_DELIMITER +from mediagoblin.util import ( + render_to_response, redirect, clean_html, TAGS_DELIMITER, \ + convert_to_tag_list) from mediagoblin.edit import forms from mediagoblin.edit.lib import may_edit_media from mediagoblin.decorators import require_active_login, get_user_media_entry @@ -62,7 +63,7 @@ def edit_media(request, media): media['description'])) media['slug'] = request.POST['slug'] - media['tags'] = request.POST['tags'].split(TAGS_DELIMITER) + media['tags'] = convert_to_tag_list(request.POST['tags']) media.save() return redirect(request, "mediagoblin.user_pages.media_home", diff --git a/mediagoblin/submit/forms.py b/mediagoblin/submit/forms.py index e13d5425..0e0fd086 100644 --- a/mediagoblin/submit/forms.py +++ b/mediagoblin/submit/forms.py @@ -17,8 +17,6 @@ import wtforms -from mediagoblin.util import convert_to_tag_list - class SubmitStartForm(wtforms.Form): title = wtforms.TextField( @@ -26,4 +24,4 @@ class SubmitStartForm(wtforms.Form): [wtforms.validators.Length(min=0, max=500)]) description = wtforms.TextAreaField('Description of this work') file = wtforms.FileField('File') - tags = wtforms.TextField('Tags', filters=[convert_to_tag_list]) + tags = wtforms.TextField('Tags') diff --git a/mediagoblin/submit/views.py b/mediagoblin/submit/views.py index cdd58786..46ec4cea 100644 --- a/mediagoblin/submit/views.py +++ b/mediagoblin/submit/views.py @@ -21,7 +21,8 @@ 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) from mediagoblin.decorators import require_active_login from mediagoblin.submit import forms as submit_forms, security from mediagoblin.process_media import process_media_initial @@ -59,7 +60,7 @@ def submit_start(request): entry['media_type'] = u'image' # heh entry['uploader'] = request.user['_id'] - entry['tags'] = split(request.POST.get('tags')) + entry['tags'] = convert_to_tag_list(request.POST.get('tags')) # Save, just so we can get the entry id for the sake of using # it to generate the file path diff --git a/mediagoblin/templates/mediagoblin/user_pages/media.html b/mediagoblin/templates/mediagoblin/user_pages/media.html index 1c263880..47d5db35 100644 --- a/mediagoblin/templates/mediagoblin/user_pages/media.html +++ b/mediagoblin/templates/mediagoblin/user_pages/media.html @@ -48,10 +48,6 @@ user= media.uploader().username) }}"> {{- media.uploader().username }}

- -

- {{ ' '.join(media.tags) }} -


Comments

@@ -114,6 +110,11 @@

{% endif %}

+ {% if media.tags %} +

+ {{ ' '.join(media.tags) }} +

+ {% endif %}
{% else %}

Sorry, no such media found.

diff --git a/mediagoblin/util.py b/mediagoblin/util.py index 4421bec4..f2a2793b 100644 --- a/mediagoblin/util.py +++ b/mediagoblin/util.py @@ -371,6 +371,7 @@ def clean_html(html): TAGS_DELIMITER = u' ' +TAGS_CASE_SENSITIVE = False def convert_to_tag_list(tag_string): """ @@ -379,12 +380,16 @@ def convert_to_tag_list(tag_string): Strips trailing, leading, and internal whitespace, and also converts the user input into an array of tags """ + taglist = [] if tag_string: - taglist = [] stripped_tag_string = u' '.join(tag_string.strip().split()) for tag in stripped_tag_string.split(TAGS_DELIMITER): - if tag.strip(): taglist.append(tag.strip()) - return taglist + if tag.strip(): + if TAGS_CASE_SENSITIVE: + taglist.append(tag.strip()) + else: + taglist.append(tag.strip().lower()) + return taglist MARKDOWN_INSTANCE = markdown.Markdown(safe_mode='escape') From 4451219560a4d991a8c4d04e9dffa99fb092bd5b Mon Sep 17 00:00:00 2001 From: Caleb Forbes Davis V Date: Tue, 12 Jul 2011 22:52:32 -0500 Subject: [PATCH 006/118] ensures no duplicate tags per media entry --- mediagoblin/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediagoblin/util.py b/mediagoblin/util.py index f2a2793b..951bdd51 100644 --- a/mediagoblin/util.py +++ b/mediagoblin/util.py @@ -384,7 +384,7 @@ def convert_to_tag_list(tag_string): if tag_string: stripped_tag_string = u' '.join(tag_string.strip().split()) for tag in stripped_tag_string.split(TAGS_DELIMITER): - if tag.strip(): + if tag.strip() and tag not in taglist: if TAGS_CASE_SENSITIVE: taglist.append(tag.strip()) else: From cc7ff3c50513ae169abab196f32de97af30e6744 Mon Sep 17 00:00:00 2001 From: Caleb Forbes Davis V Date: Tue, 12 Jul 2011 23:58:25 -0500 Subject: [PATCH 007/118] enforces maximum tag length with (in)appropriate messaging --- mediagoblin/edit/views.py | 4 +++- mediagoblin/submit/views.py | 4 +++- mediagoblin/util.py | 26 +++++++++++++++++++++++--- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/mediagoblin/edit/views.py b/mediagoblin/edit/views.py index 0432024e..df0a0d52 100644 --- a/mediagoblin/edit/views.py +++ b/mediagoblin/edit/views.py @@ -63,7 +63,9 @@ def edit_media(request, media): media['description'])) media['slug'] = request.POST['slug'] - media['tags'] = convert_to_tag_list(request.POST['tags']) + + # Process the user's folksonomy "tags" + media['tags'] = convert_to_tag_list(request) media.save() return redirect(request, "mediagoblin.user_pages.media_home", diff --git a/mediagoblin/submit/views.py b/mediagoblin/submit/views.py index 46ec4cea..bda77b1d 100644 --- a/mediagoblin/submit/views.py +++ b/mediagoblin/submit/views.py @@ -60,7 +60,9 @@ def submit_start(request): entry['media_type'] = u'image' # heh entry['uploader'] = request.user['_id'] - entry['tags'] = convert_to_tag_list(request.POST.get('tags')) + + # Process the user's folksonomy "tags" + entry['tags'] = convert_to_tag_list(request) # Save, just so we can get the entry id for the sake of using # it to generate the file path diff --git a/mediagoblin/util.py b/mediagoblin/util.py index 951bdd51..44e64258 100644 --- a/mediagoblin/util.py +++ b/mediagoblin/util.py @@ -372,19 +372,39 @@ def clean_html(html): TAGS_DELIMITER = u' ' TAGS_CASE_SENSITIVE = False +TAGS_MAX_LENGTH = 50 -def convert_to_tag_list(tag_string): +def convert_to_tag_list(request): """ - Filter input from a "tags" field, + Filter input from any "tags" field in the session, Strips trailing, leading, and internal whitespace, and also converts - the user input into an array of tags + the "tags" text into an array of tags """ + tag_string = request.POST.get('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(TAGS_DELIMITER): + + # Do not permit duplicate tags if tag.strip() and tag not in taglist: + + # Enforce maximum tag length + if len(tag) > TAGS_MAX_LENGTH: + tag = tag[:TAGS_MAX_LENGTH] + u'...' + messages.add_message( + request, messages.WARNING, \ + u'Tag truncated to ' + unicode(TAGS_MAX_LENGTH) + \ + u' characters.') + messages.add_message( + request, messages.INFO, \ + u'Why the long tag? Seriously.') + if TAGS_CASE_SENSITIVE: taglist.append(tag.strip()) else: From 1580c7c5ceb309134a7c4e0c8ecf89f95cb76273 Mon Sep 17 00:00:00 2001 From: Caleb Forbes Davis V Date: Wed, 13 Jul 2011 00:22:16 -0500 Subject: [PATCH 008/118] adds index for searching across all users' tagged images --- mediagoblin/db/indexes.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mediagoblin/db/indexes.py b/mediagoblin/db/indexes.py index 57bd33cd..1a2de55f 100644 --- a/mediagoblin/db/indexes.py +++ b/mediagoblin/db/indexes.py @@ -95,6 +95,12 @@ MEDIAENTRY_INDEXES = { # Used for showing media items matching a tag search, most recent first. 'index': [('uploader', ASCENDING), ('tags', DESCENDING), + ('created', DESCENDING)]}, + + 'tags_created': { + # Indexing media tags, and timestamp (across all users) + # This is used for a front page tag search. + 'index': [('tags', DESCENDING), ('created', DESCENDING)]}} From cea8f2b632de13ca9b423ea97ee8f6abf3cd6ecb Mon Sep 17 00:00:00 2001 From: Caleb Forbes Davis V Date: Wed, 13 Jul 2011 12:14:18 -0500 Subject: [PATCH 009/118] adds "state" to tags indices --- mediagoblin/db/indexes.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/mediagoblin/db/indexes.py b/mediagoblin/db/indexes.py index 1a2de55f..3440ac2a 100644 --- a/mediagoblin/db/indexes.py +++ b/mediagoblin/db/indexes.py @@ -90,17 +90,19 @@ MEDIAENTRY_INDEXES = { 'index': [('uploader', ASCENDING), ('created', DESCENDING)]}, - 'uploader_tags_created': { - # Indexing on the media uploader, the associated tags, and timestamp + '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': [('uploader', ASCENDING), + 'index': [('state', ASCENDING), + ('uploader', ASCENDING), ('tags', DESCENDING), ('created', DESCENDING)]}, - 'tags_created': { - # Indexing media tags, and timestamp (across all users) + 'state_tags_created': { + # Indexing on processed?, media tags, and timestamp (across all users) # This is used for a front page tag search. - 'index': [('tags', DESCENDING), + 'index': [('state', ASCENDING), + ('tags', DESCENDING), ('created', DESCENDING)]}} From dccef26263ba98c47fc5f8121a074a34b012ba89 Mon Sep 17 00:00:00 2001 From: Elrond Date: Sun, 17 Jul 2011 16:09:22 +0200 Subject: [PATCH 010/118] Move setting up of storage into init/__init__.py Factoring out this one should be the last one needed to rewrite the celery setup. The idea is to not setup the whole app, but just call a bunch of individual setup_* functions and be done. --- mediagoblin/app.py | 15 +++++---------- mediagoblin/init/__init__.py | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/mediagoblin/app.py b/mediagoblin/app.py index 85c3c0c7..58db4e8d 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 + 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/init/__init__.py b/mediagoblin/init/__init__.py index 1e519cc9..64fa9b92 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 @@ -103,6 +104,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 From 909371cdceace162af880c275b9e6e70488e3029 Mon Sep 17 00:00:00 2001 From: Caleb Forbes Davis V Date: Wed, 20 Jul 2011 23:54:32 -0500 Subject: [PATCH 011/118] raises tag length error in form context instead of in message queue --- mediagoblin/edit/forms.py | 5 ++++- mediagoblin/edit/views.py | 4 +--- mediagoblin/submit/forms.py | 5 ++++- mediagoblin/util.py | 35 +++++++++++++++++++++-------------- 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/mediagoblin/edit/forms.py b/mediagoblin/edit/forms.py index 21c8509a..e7a86bba 100644 --- a/mediagoblin/edit/forms.py +++ b/mediagoblin/edit/forms.py @@ -16,6 +16,7 @@ import wtforms +from mediagoblin.util import tag_length_validator, TOO_LONG_TAG_WARNING class EditForm(wtforms.Form): @@ -25,7 +26,9 @@ class EditForm(wtforms.Form): slug = wtforms.TextField( 'Slug') description = wtforms.TextAreaField('Description of this work') - tags = wtforms.TextField('Tags') + tags = wtforms.TextField( + 'Tags', + [tag_length_validator]) class EditProfileForm(wtforms.Form): bio = wtforms.TextAreaField('Bio', diff --git a/mediagoblin/edit/views.py b/mediagoblin/edit/views.py index df0a0d52..b3d239e1 100644 --- a/mediagoblin/edit/views.py +++ b/mediagoblin/edit/views.py @@ -55,6 +55,7 @@ def edit_media(request, media): else: media['title'] = request.POST['title'] media['description'] = request.POST.get('description') + media['tags'] = convert_to_tag_list(request.POST.get('tags')) md = markdown.Markdown( safe_mode = 'escape') @@ -63,9 +64,6 @@ def edit_media(request, media): media['description'])) media['slug'] = request.POST['slug'] - - # Process the user's folksonomy "tags" - media['tags'] = convert_to_tag_list(request) media.save() return redirect(request, "mediagoblin.user_pages.media_home", diff --git a/mediagoblin/submit/forms.py b/mediagoblin/submit/forms.py index 0e0fd086..1a5a7f4e 100644 --- a/mediagoblin/submit/forms.py +++ b/mediagoblin/submit/forms.py @@ -16,6 +16,7 @@ import wtforms +from mediagoblin.util import tag_length_validator, TOO_LONG_TAG_WARNING class SubmitStartForm(wtforms.Form): @@ -24,4 +25,6 @@ class SubmitStartForm(wtforms.Form): [wtforms.validators.Length(min=0, max=500)]) description = wtforms.TextAreaField('Description of this work') file = wtforms.FileField('File') - tags = wtforms.TextField('Tags') + tags = wtforms.TextField( + 'Tags', + [tag_length_validator]) diff --git a/mediagoblin/util.py b/mediagoblin/util.py index 44e64258..a84e07c4 100644 --- a/mediagoblin/util.py +++ b/mediagoblin/util.py @@ -24,6 +24,7 @@ import urllib from math import ceil from string import strip import copy +import wtforms from babel.localedata import exists import jinja2 @@ -374,14 +375,13 @@ TAGS_DELIMITER = u' ' TAGS_CASE_SENSITIVE = False TAGS_MAX_LENGTH = 50 -def convert_to_tag_list(request): +def convert_to_tag_list(tag_string): """ - Filter input from any "tags" field in the session, + 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 """ - tag_string = request.POST.get('tags') taglist = [] if tag_string: @@ -394,17 +394,6 @@ def convert_to_tag_list(request): # Do not permit duplicate tags if tag.strip() and tag not in taglist: - # Enforce maximum tag length - if len(tag) > TAGS_MAX_LENGTH: - tag = tag[:TAGS_MAX_LENGTH] + u'...' - messages.add_message( - request, messages.WARNING, \ - u'Tag truncated to ' + unicode(TAGS_MAX_LENGTH) + \ - u' characters.') - messages.add_message( - request, messages.INFO, \ - u'Why the long tag? Seriously.') - if TAGS_CASE_SENSITIVE: taglist.append(tag.strip()) else: @@ -412,6 +401,24 @@ def convert_to_tag_list(request): return taglist +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(field.data) + too_long_tags = [ + tag for tag in tags + if len(tag) > TAGS_MAX_LENGTH] + + if too_long_tags: + raise wtforms.ValidationError( + TOO_LONG_TAG_WARNING % ( + TAGS_MAX_LENGTH, ', '.join(too_long_tags))) + + MARKDOWN_INSTANCE = markdown.Markdown(safe_mode='escape') def cleaned_markdown_conversion(text): From 6272920cff0f5b5bf1343ee01a33f010d315fe83 Mon Sep 17 00:00:00 2001 From: Jef van Schendel Date: Mon, 25 Jul 2011 21:25:34 +0200 Subject: [PATCH 012/118] Assorted style changes, small base.html changes, added new background image --- mediagoblin/static/images/background.png | Bin 0 -> 6336 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 mediagoblin/static/images/background.png diff --git a/mediagoblin/static/images/background.png b/mediagoblin/static/images/background.png new file mode 100644 index 0000000000000000000000000000000000000000..aa101308dc8677b9e606361fdccd33ea4e98cde0 GIT binary patch literal 6336 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 Date: Mon, 25 Jul 2011 21:26:58 +0200 Subject: [PATCH 013/118] Ok, so here are the actual changes. Woops! --- mediagoblin/static/css/base.css | 34 ++++++++++------ mediagoblin/templates/mediagoblin/base.html | 44 ++++++++++----------- 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/mediagoblin/static/css/base.css b/mediagoblin/static/css/base.css index 31b8ebc2..2d099526 100644 --- a/mediagoblin/static/css/base.css +++ b/mediagoblin/static/css/base.css @@ -1,10 +1,12 @@ 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%; + font: 16px "HelveticaNeue-Light","Helvetica Neue Light","Helvetica Neue",Helvetica,Arial,sans-serif; } form { @@ -18,23 +20,31 @@ 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; + color:#fff; + font-size: 30px; } h2{ margin-top:20px; + color:#fff; +} + +h3{ + border-bottom:1px solid #222; + font-size:18px; } a { - color: #fff; + color: #999; } label { @@ -49,11 +59,10 @@ label { } .mediagoblin_header { - width:100%; height:36px; - background-color:#2F2F2F; padding-top:14px; - margin-bottom:40px; + margin-bottom:20px; + border-bottom:1px solid #222222; } .header_submit{ @@ -75,12 +84,10 @@ label { } .mediagoblin_footer { - width:100%; height:30px; - background-color:#2F2F2F; + border-top:1px solid #222222; bottom:0px; padding-top:8px; - position:absolute; text-align:center; font-size:14px; color:#999; @@ -129,7 +136,7 @@ text-align:center; /* forms */ .form_box { - background-color:#393939; + background-color:#222; background-image:url("../images/background_lines.png"); background-repeat:repeat-x; font-size:18px; @@ -181,6 +188,7 @@ text-align:center; .comment_author { margin-bottom:40px; padding-top:4px; + font-size:14px; } .comment_content p { @@ -213,7 +221,7 @@ img.media_icon{ display:block; float:left; text-align:center; - background-color:#393939; + background-color:#222; text-decoration:none; padding:12px 0pt; font-family:'Carter One', arial, serif; diff --git a/mediagoblin/templates/mediagoblin/base.html b/mediagoblin/templates/mediagoblin/base.html index 12f188de..b38ad674 100644 --- a/mediagoblin/templates/mediagoblin/base.html +++ b/mediagoblin/templates/mediagoblin/base.html @@ -36,27 +36,25 @@ {% block mediagoblin_body %}

{% block mediagoblin_header %} -
-
-
- {% block mediagoblin_logo %} - - {% endblock %} +
+
+ {% block mediagoblin_logo %} + + {% endblock %} + {% if request.user %} + Submit media + {% endif %} + {% block mediagoblin_header_title %}{% endblock %} +
{% if request.user %} - Submit media + + {{ request.user['username'] }}'s account + (logout) + {% else %} + + Login {% endif %} - {% block mediagoblin_header_title %}{% endblock %} -
- {% if request.user %} - - {{ request.user['username'] }}'s account - (logout) - {% else %} - - Login - {% endif %} -
@@ -69,11 +67,9 @@
{% block mediagoblin_footer %} - {% else %} diff --git a/mediagoblin/templates/mediagoblin/utils/tags.html b/mediagoblin/templates/mediagoblin/utils/tags.html new file mode 100644 index 00000000..94c4cf69 --- /dev/null +++ b/mediagoblin/templates/mediagoblin/utils/tags.html @@ -0,0 +1,25 @@ +{# +# 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 . +#} + +{% block tags_content -%} +
    + {% for tag in media.tags %} +
  • {{ tag['name'] }}
  • + {% endfor %} +
+{% endblock %} diff --git a/mediagoblin/util.py b/mediagoblin/util.py index 8bb90acf..ab72b5c8 100644 --- a/mediagoblin/util.py +++ b/mediagoblin/util.py @@ -371,7 +371,7 @@ def clean_html(html): return HTML_CLEANER.clean_html(html) -def convert_to_tag_list(tag_string): +def convert_to_tag_list_of_dicts(tag_string): """ Filter input from incoming string containing user tags, @@ -389,15 +389,29 @@ def convert_to_tag_list(tag_string): mg_globals.app_config['tags_delimiter']): # Do not permit duplicate tags - if tag.strip() and tag not in taglist: + if tag.strip() and tag.strip() not in taglist: if mg_globals.app_config['tags_case_sensitive']: - taglist.append(tag.strip()) + taglist.append({'name': tag.strip(), + 'slug': slugify(tag.strip())}) else: - taglist.append(tag.strip().lower()) + taglist.append({'name': tag.strip().lower(), + 'slug': slugify(tag.strip().lower())}) 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' @@ -405,10 +419,10 @@ def tag_length_validator(form, field): """ Make sure tags do not exceed the maximum tag length. """ - tags = convert_to_tag_list(field.data) + tags = convert_to_tag_list_of_dicts(field.data) too_long_tags = [ - tag for tag in tags - if len(tag) > mg_globals.app_config['tags_max_length']] + tag['name'] for tag in tags + if len(tag['name']) > mg_globals.app_config['tags_max_length']] if too_long_tags: raise wtforms.ValidationError( From 37be7b6da6aa765991fac55c5c2d0cdb37fd24fc Mon Sep 17 00:00:00 2001 From: Caleb Forbes Davis V Date: Wed, 27 Jul 2011 16:04:41 -0500 Subject: [PATCH 020/118] updates indices to index on the slug component of the tag list - uses dot notation to reach into the JSON-style MediaEntry tags field object to index on the slug property of each tag --- mediagoblin/db/indexes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mediagoblin/db/indexes.py b/mediagoblin/db/indexes.py index 3440ac2a..d0e11311 100644 --- a/mediagoblin/db/indexes.py +++ b/mediagoblin/db/indexes.py @@ -95,14 +95,14 @@ MEDIAENTRY_INDEXES = { # Used for showing media items matching a tag search, most recent first. 'index': [('state', ASCENDING), ('uploader', ASCENDING), - ('tags', DESCENDING), + ('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', DESCENDING), + ('tags.slug', DESCENDING), ('created', DESCENDING)]}} From f99b5caeb68cb60d768f0e049388a6f4a8b68ac0 Mon Sep 17 00:00:00 2001 From: Caleb Forbes Davis V Date: Wed, 27 Jul 2011 23:57:43 -0500 Subject: [PATCH 021/118] modifies duplicate tag check for list of dict tag type change --- mediagoblin/util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mediagoblin/util.py b/mediagoblin/util.py index ab72b5c8..f051dc50 100644 --- a/mediagoblin/util.py +++ b/mediagoblin/util.py @@ -388,8 +388,8 @@ def convert_to_tag_list_of_dicts(tag_string): for tag in stripped_tag_string.split( mg_globals.app_config['tags_delimiter']): - # Do not permit duplicate tags - if tag.strip() and tag.strip() not in taglist: + # Ignore empty or duplicate tags + if tag.strip() and tag.strip() not in [t['name'] for t in taglist]: if mg_globals.app_config['tags_case_sensitive']: taglist.append({'name': tag.strip(), From 4476adcdb02d8c36ae87efe1c00ef0972ecff64d Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Thu, 28 Jul 2011 21:20:32 -0500 Subject: [PATCH 022/118] Make "needs verification" link only appear if you need verification really. --- mediagoblin/templates/mediagoblin/base.html | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/mediagoblin/templates/mediagoblin/base.html b/mediagoblin/templates/mediagoblin/base.html index 22a691b5..8e3b0724 100644 --- a/mediagoblin/templates/mediagoblin/base.html +++ b/mediagoblin/templates/mediagoblin/base.html @@ -50,10 +50,15 @@ {{ request.user['username'] }}'s account - - - needs verification! + + {# the following link should only appear when verification is needed #} + {% if request.user.status == "needs_email_verification" %} + + needs verification! + {% endif %} + (logout) {% else %} From 990d3b6985041facf391f4ece0829a11c0fd317e Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Thu, 28 Jul 2011 22:12:24 -0500 Subject: [PATCH 023/118] Give useful verification information on users that need to verify their email --- .../mediagoblin/user_pages/user.html | 63 +++++++++++++------ mediagoblin/user_pages/views.py | 8 ++- 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/mediagoblin/templates/mediagoblin/user_pages/user.html b/mediagoblin/templates/mediagoblin/user_pages/user.html index b0c1027c..ae540bfe 100644 --- a/mediagoblin/templates/mediagoblin/user_pages/user.html +++ b/mediagoblin/templates/mediagoblin/user_pages/user.html @@ -25,22 +25,49 @@ {% endblock mediagoblin_head %} {% block mediagoblin_content -%} - {% if user %} - -
-

Verification needed

- -

Almost done! Your account still needs to be verified.

-

An email should arrive in a few moments with instructions on how to do so.

-

In case it doesn't:

- -
Resend verification email - -

Someone has registered an account with this username, but it still has to be verified.

-

If you are that person but you've lost your verification email, you can log in and resend it.

-
- {# + {# If no user... #} + {% if not user %} +

Sorry, no such user found.

+ + {# 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 #} +

+

Verification needed

+ +

Almost done! Your account still needs to be verified.

+

+ An email should arrive in a few moments with instructions + on how to do so. +

+

In case it doesn't:

+ + Resend verification email +
+ {% else %} + {# if the user is not you, but still needs to verify their email #} +
+

Verification needed

+ +

+ Someone has registered an account with this username, but it + still has to be verified. +

+ +

+ If you are that person but you've lost your verification + email, you can + log in + and resend it. +

+
+ {% endif %} + + {# Active(?) (or at least verified at some point) user, horray! #} + {% else %}

{{ user.username }}'s profile

+
{% include "mediagoblin/utils/profile.html" %} {% if request.user['_id'] == user['_id'] or request.user['is_admin'] %} @@ -48,6 +75,7 @@ user.username }}">Edit profile {% endif %}
+
{% set pagination_base_url = user_gallery_url %} {% include "mediagoblin/utils/object_gallery.html" %} @@ -56,10 +84,7 @@ 'mediagoblin.user_pages.atom_feed', user=user.username) }}>atom feed
+
- #} - {% else %} - {# This *should* not occur as the view makes sure we pass in a user. #} -

Sorry, no such user found.

{% endif %} {% endblock %} diff --git a/mediagoblin/user_pages/views.py b/mediagoblin/user_pages/views.py index a3172ebd..57dcb555 100644 --- a/mediagoblin/user_pages/views.py +++ b/mediagoblin/user_pages/views.py @@ -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'], From f22339d22983cb0fb4d0261c8296e3e27a3f5939 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Thu, 28 Jul 2011 22:14:20 -0500 Subject: [PATCH 024/118] A small amount of indenting in base.html --- mediagoblin/templates/mediagoblin/base.html | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/mediagoblin/templates/mediagoblin/base.html b/mediagoblin/templates/mediagoblin/base.html index 8e3b0724..9fcd1dcd 100644 --- a/mediagoblin/templates/mediagoblin/base.html +++ b/mediagoblin/templates/mediagoblin/base.html @@ -39,10 +39,17 @@

{% block mediagoblin_logo %} - + {% endblock %} {% if request.user %} - Submit media + + Submit media + {% endif %} {% block mediagoblin_header_title %}{% endblock %}
From 391861ac9feb8e31454f62ba7ca7e70fea2445ef Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Thu, 28 Jul 2011 22:15:18 -0500 Subject: [PATCH 025/118] I prefer it when "needs verification" looks like a button :) --- mediagoblin/templates/mediagoblin/base.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediagoblin/templates/mediagoblin/base.html b/mediagoblin/templates/mediagoblin/base.html index 9fcd1dcd..c1d3caed 100644 --- a/mediagoblin/templates/mediagoblin/base.html +++ b/mediagoblin/templates/mediagoblin/base.html @@ -62,7 +62,7 @@ {% if request.user.status == "needs_email_verification" %} + class="header_submit"> needs verification! {% endif %} From a435bcb313b1f15d522387a27625fa1ed1290bcb Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Thu, 28 Jul 2011 22:19:05 -0500 Subject: [PATCH 026/118] I think the "'s account" is a waste of space --- mediagoblin/templates/mediagoblin/base.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediagoblin/templates/mediagoblin/base.html b/mediagoblin/templates/mediagoblin/base.html index c1d3caed..0dbd6d6f 100644 --- a/mediagoblin/templates/mediagoblin/base.html +++ b/mediagoblin/templates/mediagoblin/base.html @@ -56,7 +56,7 @@ {% if request.user %} - {{ request.user['username'] }}'s account + {{ request.user['username'] }} {# the following link should only appear when verification is needed #} {% if request.user.status == "needs_email_verification" %} From d43b472a08b9debf966015ef2ecfd12e3bc5d9d1 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Fri, 29 Jul 2011 08:05:57 -0500 Subject: [PATCH 027/118] require_active_login now redirect's to user's homepage if not email verified --- mediagoblin/decorators.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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" % ( From 61927e6e802d5d138466a9f3c34c6faa91ec19b6 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Fri, 29 Jul 2011 08:09:13 -0500 Subject: [PATCH 028/118] resend_activation should give the user a message and redirect them to their user_home --- mediagoblin/auth/views.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mediagoblin/auth/views.py b/mediagoblin/auth/views.py index 7fe507b1..6f3c5165 100644 --- a/mediagoblin/auth/views.py +++ b/mediagoblin/auth/views.py @@ -166,4 +166,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']) From 3880c8f31b68cc19a868d738071ea6180100ef22 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Fri, 29 Jul 2011 08:10:45 -0500 Subject: [PATCH 029/118] "Resend verification email" button should actually resend the verification email --- mediagoblin/templates/mediagoblin/user_pages/user.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mediagoblin/templates/mediagoblin/user_pages/user.html b/mediagoblin/templates/mediagoblin/user_pages/user.html index ae540bfe..7769b8b3 100644 --- a/mediagoblin/templates/mediagoblin/user_pages/user.html +++ b/mediagoblin/templates/mediagoblin/user_pages/user.html @@ -43,7 +43,8 @@

In case it doesn't:

- Resend verification email + Resend verification email
{% else %} {# if the user is not you, but still needs to verify their email #} From 8adc3841a34ebd54d2542fe3ad3db394c6ccc19a Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Fri, 29 Jul 2011 08:11:50 -0500 Subject: [PATCH 030/118] Make the button CSS not underline its link text --- mediagoblin/static/css/base.css | 1 + 1 file changed, 1 insertion(+) diff --git a/mediagoblin/static/css/base.css b/mediagoblin/static/css/base.css index 2073ecb8..84eea9ca 100644 --- a/mediagoblin/static/css/base.css +++ b/mediagoblin/static/css/base.css @@ -127,6 +127,7 @@ label { text-align:center; padding-left:11px; padding-right:11px; + text-decoration:none; } .pagination{ From 7074304c91bfc19e2a6365f3d9567ec5a2636f9d Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Fri, 29 Jul 2011 08:14:02 -0500 Subject: [PATCH 031/118] Remove the verification needed page --- mediagoblin/auth/routing.py | 3 -- .../mediagoblin/auth/verification_needed.html | 29 ------------------- 2 files changed, 32 deletions(-) delete mode 100644 mediagoblin/templates/mediagoblin/auth/verification_needed.html diff --git a/mediagoblin/auth/routing.py b/mediagoblin/auth/routing.py index 46c585d2..a50afb48 100644 --- a/mediagoblin/auth/routing.py +++ b/mediagoblin/auth/routing.py @@ -28,9 +28,6 @@ auth_routes = [ 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/templates/mediagoblin/auth/verification_needed.html b/mediagoblin/templates/mediagoblin/auth/verification_needed.html deleted file mode 100644 index 4104da19..00000000 --- a/mediagoblin/templates/mediagoblin/auth/verification_needed.html +++ /dev/null @@ -1,29 +0,0 @@ -{# -# 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 . -#} -{% extends "mediagoblin/base.html" %} - -{% block mediagoblin_content %} -

- Verfication needed!
- Please check your email to verify your account. -

- -

- Still haven't received an email? Click here to resend it. -

-{% endblock %} From 0bc036209dab581ad60b97231473c832fca9e875 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Fri, 29 Jul 2011 08:22:26 -0500 Subject: [PATCH 032/118] Redirect to the user's profile after registration - Updated the view - Updated the tests - Fixed a weirdness in the registration view where the 'user' variable used to be called 'entry' --- mediagoblin/auth/routing.py | 3 --- mediagoblin/auth/views.py | 21 ++++++++++++++------- mediagoblin/tests/test_auth.py | 4 ++-- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/mediagoblin/auth/routing.py b/mediagoblin/auth/routing.py index a50afb48..9547b3ea 100644 --- a/mediagoblin/auth/routing.py +++ b/mediagoblin/auth/routing.py @@ -19,9 +19,6 @@ 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/', diff --git a/mediagoblin/auth/views.py b/mediagoblin/auth/views.py index 6f3c5165..cf07d668 100644 --- a/mediagoblin/auth/views.py +++ b/mediagoblin/auth/views.py @@ -55,16 +55,23 @@ def register(request): 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) + send_verification_email(user, request) - return redirect(request, "mediagoblin.auth.register_success") + messages.add_message( + request, + messages.INFO, + ('Registration successful! ' + 'You should get a registration email soon.')) + return redirect( + request, 'mediagoblin.user_pages.user_home', + user=user['username']) return render_to_response( request, diff --git a/mediagoblin/tests/test_auth.py b/mediagoblin/tests/test_auth.py index ad9dd35b..ccb9a536 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( From dc9cbb3888ea2e285fcf2d8368382a688f03268a Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Fri, 29 Jul 2011 08:23:56 -0500 Subject: [PATCH 033/118] Only show submit button if user is active --- mediagoblin/templates/mediagoblin/base.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediagoblin/templates/mediagoblin/base.html b/mediagoblin/templates/mediagoblin/base.html index 0dbd6d6f..0a037d26 100644 --- a/mediagoblin/templates/mediagoblin/base.html +++ b/mediagoblin/templates/mediagoblin/base.html @@ -45,7 +45,7 @@ alt="Mediagoblin logo" /> {% endblock %} - {% if request.user %} + {% if request.user and request.user['status'] == 'active' %} Submit media From 7b6836a362a2c4a42be1ae95e7a31e07c113d600 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Fri, 29 Jul 2011 08:25:22 -0500 Subject: [PATCH 034/118] "Needs verification!" button should go to the left of username Looks cleaner is all.. --- mediagoblin/templates/mediagoblin/base.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mediagoblin/templates/mediagoblin/base.html b/mediagoblin/templates/mediagoblin/base.html index 0a037d26..45c17e94 100644 --- a/mediagoblin/templates/mediagoblin/base.html +++ b/mediagoblin/templates/mediagoblin/base.html @@ -54,10 +54,6 @@ {% block mediagoblin_header_title %}{% endblock %}
From ebcd29d4f8e984f59d808360708f8fc38cfbda99 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Fri, 29 Jul 2011 08:27:32 -0500 Subject: [PATCH 036/118] Don't need the register success template anymore --- .../mediagoblin/auth/register_success.html | 25 ------------------- 1 file changed, 25 deletions(-) delete mode 100644 mediagoblin/templates/mediagoblin/auth/register_success.html diff --git a/mediagoblin/templates/mediagoblin/auth/register_success.html b/mediagoblin/templates/mediagoblin/auth/register_success.html deleted file mode 100644 index cd82a0b9..00000000 --- a/mediagoblin/templates/mediagoblin/auth/register_success.html +++ /dev/null @@ -1,25 +0,0 @@ -{# -# 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 . -#} -{% extends "mediagoblin/base.html" %} - -{% block mediagoblin_content %} -

- Register successful! :D
- You should get a confirmation email soon. -

-{% endblock %} From 7c9d1a2bca0479d83bff390621c38fac0ef4c144 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Fri, 29 Jul 2011 08:36:35 -0500 Subject: [PATCH 037/118] Adding spaces after the colon in our CSS file. Discussed this style change with Jef van Schendel and got his buy-in ;) --- mediagoblin/static/css/base.css | 216 ++++++++++++++++---------------- 1 file changed, 108 insertions(+), 108 deletions(-) diff --git a/mediagoblin/static/css/base.css b/mediagoblin/static/css/base.css index 84eea9ca..70db6da9 100644 --- a/mediagoblin/static/css/base.css +++ b/mediagoblin/static/css/base.css @@ -1,17 +1,17 @@ body { background-color: #111; - background-image:url("../images/background.png"); + 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 */ @@ -26,21 +26,21 @@ form { /* text styles */ h1{ - font-family:'Carter One',arial,serif; + font-family: 'Carter One',arial,serif; margin-bottom: 15px; - margin-top:15px; - color:#fff; + margin-top: 15px; + color: #fff; font-size: 30px; } h2{ - margin-top:20px; - color:#fff; + margin-top: 20px; + color: #fff; } h3{ - border-bottom:1px solid #222; - font-size:18px; + border-bottom: 1px solid #222; + font-size: 18px; } a { @@ -58,201 +58,201 @@ label { /* website structure */ .mediagoblin_body { - position:relative; - min-height:100%; + position: relative; + min-height: 100%; } .mediagoblin_header { - height:36px; - padding-top:14px; - margin-bottom:20px; - border-bottom:1px solid #222222; + 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 { - height:30px; - border-top:1px solid #222222; - bottom:0px; - padding-top:8px; - 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; - text-decoration:none; + 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:#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; + 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; - font-size:14px; + 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; } /* 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:#222; - 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 { @@ -273,5 +273,5 @@ ul.mediagoblin_messages { .message_debug { background-color: #f7f7f7; - color:#272727; + color: #272727; } From f73f4c4b8425fb49b95a453107e5717caeb38386 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Fri, 29 Jul 2011 08:43:59 -0500 Subject: [PATCH 038/118] Log in user after regitration --- mediagoblin/auth/views.py | 6 ++++++ mediagoblin/tests/test_auth.py | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/mediagoblin/auth/views.py b/mediagoblin/auth/views.py index cf07d668..e0d9c8f1 100644 --- a/mediagoblin/auth/views.py +++ b/mediagoblin/auth/views.py @@ -62,8 +62,14 @@ def register(request): request.POST['password']) user.save(validate=True) + # log the user in + request.session['user_id'] = unicode(user['_id']) + request.session.save() + + # send verification email send_verification_email(user, request) + # give the user a message and redirect messages.add_message( request, messages.INFO, diff --git a/mediagoblin/tests/test_auth.py b/mediagoblin/tests/test_auth.py index ccb9a536..f0bb183f 100644 --- a/mediagoblin/tests/test_auth.py +++ b/mediagoblin/tests/test_auth.py @@ -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() From dce5c9cb9b28154edef85f5c9031f6a67be503ea Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Fri, 29 Jul 2011 08:44:47 -0500 Subject: [PATCH 039/118] Remove this superfluous "registration successful" message --- mediagoblin/auth/views.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/mediagoblin/auth/views.py b/mediagoblin/auth/views.py index e0d9c8f1..fb5db870 100644 --- a/mediagoblin/auth/views.py +++ b/mediagoblin/auth/views.py @@ -69,12 +69,8 @@ def register(request): # send verification email send_verification_email(user, request) - # give the user a message and redirect - messages.add_message( - request, - messages.INFO, - ('Registration successful! ' - 'You should get a registration email soon.')) + # 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']) From 8ff4dec742d0b9f375afd9d1862a560e1be200d1 Mon Sep 17 00:00:00 2001 From: Caleb Forbes Davis V Date: Fri, 29 Jul 2011 13:56:40 -0500 Subject: [PATCH 040/118] Adds tag unit testing - overrides default tag parsing globals in test_mgoblin_app.ini - piggybacks on existing test_submission code to check correct tag parsing and storage in the database - verifies expected behavior given different delimiters, case sensitivities, tags that are too long, and extra whitespace - verifies list-of-dict database storage and tag slugification --- mediagoblin/tests/test_mgoblin_app.ini | 5 + mediagoblin/tests/test_submission.py | 198 +++++++++++++++++++++ mediagoblin/tests/test_submission/evil | Bin 0 -> 96284 bytes mediagoblin/tests/test_submission/evil.jpg | Bin 0 -> 96284 bytes mediagoblin/tests/test_submission/evil.png | Bin 0 -> 96284 bytes mediagoblin/tests/test_submission/good.jpg | Bin 0 -> 10059 bytes mediagoblin/tests/test_submission/good.png | Bin 0 -> 50598 bytes mediagoblin/tests/test_tags.py | 57 ++++++ 8 files changed, 260 insertions(+) create mode 100644 mediagoblin/tests/test_submission.py create mode 100755 mediagoblin/tests/test_submission/evil create mode 100755 mediagoblin/tests/test_submission/evil.jpg create mode 100755 mediagoblin/tests/test_submission/evil.png create mode 100644 mediagoblin/tests/test_submission/good.jpg create mode 100644 mediagoblin/tests/test_submission/good.png create mode 100644 mediagoblin/tests/test_tags.py diff --git a/mediagoblin/tests/test_mgoblin_app.ini b/mediagoblin/tests/test_mgoblin_app.ini index fd0f87a4..5395ca10 100644 --- a/mediagoblin/tests/test_mgoblin_app.ini +++ b/mediagoblin/tests/test_mgoblin_app.ini @@ -7,6 +7,11 @@ email_sender_address = "notice@mediagoblin.example.org" email_debug_mode = true db_name = __mediagoblin_tests__ +# tag parsing +tags_delimiter = "," +tags_case_sensitive = False +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 new file mode 100644 index 00000000..7c6b0f75 --- /dev/null +++ b/mediagoblin/tests/test_submission.py @@ -0,0 +1,198 @@ +# 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 . + +import urlparse +import pkg_resources + +from nose.tools import assert_equal + +from mediagoblin.auth import lib as auth_lib +from mediagoblin.tests.tools import setup_fresh_app, get_test_app +from mediagoblin import mg_globals +from mediagoblin import util + +GOOD_JPG = pkg_resources.resource_filename( + 'mediagoblin.tests', 'test_submission/good.jpg') +GOOD_PNG = pkg_resources.resource_filename( + 'mediagoblin.tests', 'test_submission/good.png') +EVIL_FILE = pkg_resources.resource_filename( + 'mediagoblin.tests', 'test_submission/evil') +EVIL_JPG = pkg_resources.resource_filename( + 'mediagoblin.tests', 'test_submission/evil.jpg') +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): + self.test_app = get_test_app() + + # TODO: Possibly abstract into a decorator like: + # @as_authenticated_user('chris') + test_user = mg_globals.database.User() + test_user['username'] = u'chris' + test_user['email'] = u'chris@example.com' + test_user['email_verified'] = True + test_user['status'] = u'active' + test_user['pw_hash'] = auth_lib.bcrypt_gen_password_hash('toast') + test_user.save() + + self.test_app.post( + '/auth/login/', { + 'username': u'chris', + 'password': 'toast'}) + + def test_missing_fields(self): + # Test blank form + # --------------- + util.clear_test_template_context() + response = self.test_app.post( + '/submit/', {}) + context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html'] + form = context['submit_form'] + assert form.file.errors == [u'You must provide a file.'] + + # Test blank file + # --------------- + util.clear_test_template_context() + response = self.test_app.post( + '/submit/', { + 'title': 'test title'}) + context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html'] + form = context['submit_form'] + assert form.file.errors == [u'You must provide a file.'] + + + def test_normal_uploads(self): + # Test JPG + # -------- + util.clear_test_template_context() + response = self.test_app.post( + '/submit/', { + 'title': 'Normal upload 1' + }, upload_files=[( + 'file', GOOD_JPG)]) + + # User should be redirected + response.follow() + assert_equal( + urlparse.urlsplit(response.location)[2], + '/u/chris/') + assert util.TEMPLATE_TEST_CONTEXT.has_key( + 'mediagoblin/user_pages/user.html') + + # Test PNG + # -------- + util.clear_test_template_context() + response = self.test_app.post( + '/submit/', { + 'title': 'Normal upload 2' + }, upload_files=[( + 'file', GOOD_PNG)]) + + response.follow() + assert_equal( + urlparse.urlsplit(response.location)[2], + '/u/chris/') + 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 + # ----------------------------------------------------- + util.clear_test_template_context() + response = self.test_app.post( + '/submit/', { + 'title': 'Malicious Upload 2' + }, upload_files=[( + 'file', EVIL_FILE)]) + + context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html'] + form = context['submit_form'] + assert form.file.errors == ['The file doesn\'t seem to be an image!'] + + # NOTE: The following 2 tests will fail. These can be uncommented + # after http://bugs.foocorp.net/issues/324 is resolved and + # bad files are handled properly. + + # Test non-supported file with .jpg extension + # ------------------------------------------- + #util.clear_test_template_context() + #response = self.test_app.post( + # '/submit/', { + # 'title': 'Malicious Upload 2' + # }, upload_files=[( + # 'file', EVIL_JPG)]) + + #context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html'] + #form = context['submit_form'] + #assert form.file.errors == ['The file doesn\'t seem to be an image!'] + + # Test non-supported file with .png extension + # ------------------------------------------- + #util.clear_test_template_context() + #response = self.test_app.post( + # '/submit/', { + # 'title': 'Malicious Upload 3' + # }, upload_files=[( + # 'file', EVIL_PNG)]) + + #context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html'] + #form = context['submit_form'] + #assert form.file.errors == ['The file doesn\'t seem to be an image!'] + diff --git a/mediagoblin/tests/test_submission/evil b/mediagoblin/tests/test_submission/evil new file mode 100755 index 0000000000000000000000000000000000000000..775da66498273a5de4e7b8518f2735fd854c3d89 GIT binary patch literal 96284 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 literal 0 HcmV?d00001 diff --git a/mediagoblin/tests/test_submission/good.png b/mediagoblin/tests/test_submission/good.png new file mode 100644 index 0000000000000000000000000000000000000000..c1eadf9c2cf5461b8078182c59afdd4c2b3e3ab9 GIT binary patch literal 50598 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. + +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'}] + + # Make sure case-sensitivity is retained when desired + mg_globals.app_config['tags_case_sensitive'] = True + 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'}] From a7e23c4863999a996ee56917755bef5d7ae8f69f Mon Sep 17 00:00:00 2001 From: Will Kahn-Greene Date: Wed, 27 Jul 2011 15:02:30 -0700 Subject: [PATCH 041/118] Minor tweaks to README. --- README | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README b/README index 2ef6f78e..082ab2a7 100644 --- a/README +++ b/README @@ -12,7 +12,7 @@ What is GNU MediaGoblin? * Federated with OStatus! * Customizable! * A place for people to collaborate and show off original and derived - creations. Free, as in freedom. We’re a GNU project in the making, + creations. Free, as in freedom. We’re a GNU project in the making, afterall. @@ -34,5 +34,5 @@ Where is the documentation? =========================== Documentation is located in the ``docs/`` directory in a "raw" -restructured-text form. It is also mirrored at +restructured-text form. It is also available at http://docs.mediagoblin.org/ in HTML form. From 24c5c586dfe2ae047ca116c54b256b7d70273c62 Mon Sep 17 00:00:00 2001 From: Will Kahn-Greene Date: Wed, 27 Jul 2011 15:16:42 -0700 Subject: [PATCH 042/118] Tweaks maketarball.sh This tweaks maketarball.sh so that it takes a -d argument which adds the date to the filename and parent directory. Also, this changes maketarball.sh so it requires a rev-ish--no more "by default, grabs master". --- maketarball.sh | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/maketarball.sh b/maketarball.sh index 2ee78016..635ba481 100755 --- a/maketarball.sh +++ b/maketarball.sh @@ -1,29 +1,31 @@ #!/bin/bash -# usage: maketarball -# maketarball +# usage: maketarball [-d] # -# With no arguments, this creates a source tarball from git master with a -# filename based on today's date. -# -# With a argument, this creates a tarball of the tag. +# Creates a tarball from a rev-ish. If -d is passed in, then it adds +# the date to the directory name. # # Examples: # -# ./maketarball +# ./maketarball -d master # ./maketarball v0.0.2 +if [[ -z "$1" ]]; then + echo "Usage: ./maketarball [-d] "; + exit 1; +fi + NOWDATE=`date "+%Y-%m-%d"` -if [ -z "$1" ] -then - REVISH=master +if [[ $@ == *-d* ]]; then + REVISH=$2 PREFIX="$NOWDATE-$REVISH" else REVISH=$1 PREFIX="$REVISH" fi + # convert PREFIX to all lowercase. # nix the v from tag names. PREFIX=`echo "$PREFIX" | tr '[A-Z]' '[a-z]' | sed s/v//` @@ -54,4 +56,4 @@ gzip mediagoblin-$PREFIX.tar echo "archive at mediagoblin-$PREFIX.tar.gz" -echo "done." \ No newline at end of file +echo "done." From 0c0fc054e64907e12996e3c47471893c19faddbd Mon Sep 17 00:00:00 2001 From: Will Kahn-Greene Date: Wed, 27 Jul 2011 15:19:51 -0700 Subject: [PATCH 043/118] Cosmetic. Tweaks spacing. --- lazyserver.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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 From 88bbab2789b91d2de1783f7f5a8670b4a0f01a4a Mon Sep 17 00:00:00 2001 From: Will Kahn-Greene Date: Wed, 27 Jul 2011 15:20:07 -0700 Subject: [PATCH 044/118] Adds license/copyright header to maketarball.sh --- maketarball.sh | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/maketarball.sh b/maketarball.sh index 635ba481..5ded9671 100755 --- a/maketarball.sh +++ b/maketarball.sh @@ -1,5 +1,22 @@ #!/bin/bash +# 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 . + + # usage: maketarball [-d] # # Creates a tarball from a rev-ish. If -d is passed in, then it adds @@ -11,7 +28,7 @@ # ./maketarball v0.0.2 if [[ -z "$1" ]]; then - echo "Usage: ./maketarball [-d] "; + echo "Usage: $0 [-d] "; exit 1; fi From 763cf83f641e03070f11304a8bdeee31eab1a726 Mon Sep 17 00:00:00 2001 From: Will Kahn-Greene Date: Wed, 27 Jul 2011 15:38:47 -0700 Subject: [PATCH 045/118] Moves destroy_environment to wipealldata command --- destroy_environment.py | 22 ----------- mediagoblin/gmg_commands/__init__.py | 4 ++ mediagoblin/gmg_commands/wipealldata.py | 51 +++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 22 deletions(-) delete mode 100755 destroy_environment.py create mode 100644 mediagoblin/gmg_commands/wipealldata.py 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/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/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 . + +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" From fc3dc2554179b9e08209ed27a750bb581fe2d85c Mon Sep 17 00:00:00 2001 From: Will Kahn-Greene Date: Wed, 27 Jul 2011 16:16:05 -0700 Subject: [PATCH 046/118] Adds license header --- mediagoblin/edit/__init__.py | 17 +++++++++++++++++ mediagoblin/gmg_commands/users.py | 16 ++++++++++++++++ mediagoblin/mg_globals.py | 15 +++++++++++++++ mediagoblin/submit/__init__.py | 17 +++++++++++++++++ mediagoblin/user_pages/__init__.py | 17 +++++++++++++++++ 5 files changed, 82 insertions(+) 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 . + + 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 . + 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/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 . """ In some places, we need to access the database, public_store, queue_store """ 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 . + + 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 . + + From 50854db05d130ef4b564379584d147880fe72a92 Mon Sep 17 00:00:00 2001 From: Will Kahn-Greene Date: Wed, 27 Jul 2011 16:21:21 -0700 Subject: [PATCH 047/118] Tweaks import lines switching \ for ( ). --- mediagoblin/app.py | 6 +++--- mediagoblin/user_pages/views.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mediagoblin/app.py b/mediagoblin/app.py index 58db4e8d..c1ee3d77 100644 --- a/mediagoblin/app.py +++ b/mediagoblin/app.py @@ -23,9 +23,9 @@ from webob import Request, exc 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, \ - setup_storage +from mediagoblin.init import (get_jinja_loader, get_staticdirector, + setup_global_and_app_config, setup_workbench, setup_database, + setup_storage) class MediaGoblinApp(object): diff --git a/mediagoblin/user_pages/views.py b/mediagoblin/user_pages/views.py index 57dcb555..dc71b059 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 From 482d53cd30229798ff58f9d6f3cbb3b86f94f218 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sat, 30 Jul 2011 12:33:57 -0500 Subject: [PATCH 048/118] Let users know when their migrations are from the future :O --- mediagoblin/init/__init__.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/mediagoblin/init/__init__.py b/mediagoblin/init/__init__.py index 64fa9b92..ff005703 100644 --- a/mediagoblin/init/__init__.py +++ b/mediagoblin/init/__init__.py @@ -61,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, From d4535719370ca3be4790070e4bbe572671ba6045 Mon Sep 17 00:00:00 2001 From: Will Kahn-Greene Date: Sat, 30 Jul 2011 16:25:44 -0400 Subject: [PATCH 049/118] 460. Adds texinfo output bits Building texinfo version of the manual only works with Sphinx from hg tip at present. Also, there are some minor issues and someone should go through the manual and read it to make sure there aren't issues. --- docs/Makefile | 21 +++++++++++++++++++++ docs/conf.py | 23 +++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/docs/Makefile b/docs/Makefile index 81fc3d13..9a4608de 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -12,6 +12,9 @@ PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @@ -113,6 +116,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/conf.py b/docs/conf.py index 6c64cdda..a7acd3e2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -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' From 2271286079397b8f9f1adc23e36c35a5d8ea35e2 Mon Sep 17 00:00:00 2001 From: Will Kahn-Greene Date: Sat, 30 Jul 2011 19:52:19 -0400 Subject: [PATCH 050/118] 270, 459. extlib policy, JS -> lgpl, ... * adds README to extlib/ * changes javascript to lgpl * also fixes the agplv3 text so that it says "agplv3 or later" * moves license files into licenses/ * adds lgplv3 license --- COPYING | 60 +++++++--- extlib/README | 71 ++++++++++++ AGPLv3.txt => licenses/AGPLv3.txt | 0 CC0_1.0.txt => licenses/CC0_1.0.txt | 0 licenses/LGPLv3.txt | 165 ++++++++++++++++++++++++++++ 5 files changed, 279 insertions(+), 17 deletions(-) create mode 100644 extlib/README rename AGPLv3.txt => licenses/AGPLv3.txt (100%) rename CC0_1.0.txt => licenses/CC0_1.0.txt (100%) create mode 100644 licenses/LGPLv3.txt diff --git a/COPYING b/COPYING index fc930ccb..6895d3b8 100644 --- a/COPYING +++ b/COPYING @@ -2,30 +2,56 @@ COPYING ========= -GNU MediaGoblin is composed of the following kinds of files: +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. -* software files: Python, JavaScript and HTML templates -* non-software data: CSS, images, and video -* documentation +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public +License along with this program, in the file ``licenses/AGPLv3.txt``. +If not, see . -Software files -============== +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. -Python, JavaScript, and template files files are released under the -AGPL v3. The text of this license is located in ``AGPLv3.txt``. +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 . -Non-software data -================= +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. -CSS, images and video are all released under a CC0 license. The text -of this license is located in ``CC0_1.0.txt``. +You should have received a copy of the CC0 license in the file +``licenses/CC0_1.0.txt``. If not, see +. -Documentation -============= +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. -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``. +You should have received a copy of the CC0 license in the file +``licenses/CC0_1.0.txt``. If not, see +. + + +Additional library software has been made available in the ``extlib/`` +directory. All of it is Free Software and can be distributed under +liberal terms, but those terms may differ in detail from the AGPL's +particulars. See each package's license file in the extlib directory +for additional terms. diff --git a/extlib/README b/extlib/README new file mode 100644 index 00000000..c23da6e6 --- /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 StatusNet 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/AGPLv3.txt b/licenses/AGPLv3.txt similarity index 100% rename from AGPLv3.txt rename to licenses/AGPLv3.txt diff --git a/CC0_1.0.txt b/licenses/CC0_1.0.txt similarity index 100% rename from CC0_1.0.txt rename to 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. + 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. From 2faec3672371db3e17bf6671306fcc243eefadf2 Mon Sep 17 00:00:00 2001 From: Will Kahn-Greene Date: Sat, 30 Jul 2011 22:27:46 -0400 Subject: [PATCH 051/118] 270. Moves 960.gs stuff to extlib/ per policy This adds the README.txt from the tarball for 960.gs and also moves the files and fixes the symlinks. --- .../contrib => extlib/960.gs}/960_16_col.css | 0 extlib/960.gs/README.txt | 54 +++++++++++++++++++ .../contrib => extlib/960.gs}/reset.css | 0 .../contrib => extlib/960.gs}/text.css | 0 mediagoblin/static/css/contrib/960_16_col.css | 1 - mediagoblin/static/css/contrib/reset.css | 1 - mediagoblin/static/css/contrib/text.css | 1 - mediagoblin/templates/mediagoblin/base.html | 6 +-- 8 files changed, 57 insertions(+), 6 deletions(-) rename {mediagoblin/contrib => extlib/960.gs}/960_16_col.css (100%) create mode 100755 extlib/960.gs/README.txt rename {mediagoblin/contrib => extlib/960.gs}/reset.css (100%) rename {mediagoblin/contrib => extlib/960.gs}/text.css (100%) delete mode 120000 mediagoblin/static/css/contrib/960_16_col.css delete mode 120000 mediagoblin/static/css/contrib/reset.css delete mode 120000 mediagoblin/static/css/contrib/text.css diff --git a/mediagoblin/contrib/960_16_col.css b/extlib/960.gs/960_16_col.css similarity index 100% rename from mediagoblin/contrib/960_16_col.css rename to 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 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: + + + + + + + +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: + + + +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 similarity index 100% rename from mediagoblin/contrib/reset.css rename to extlib/960.gs/reset.css diff --git a/mediagoblin/contrib/text.css b/extlib/960.gs/text.css similarity index 100% rename from mediagoblin/contrib/text.css rename to extlib/960.gs/text.css 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/templates/mediagoblin/base.html b/mediagoblin/templates/mediagoblin/base.html index 656fb46b..6af02c00 100644 --- a/mediagoblin/templates/mediagoblin/base.html +++ b/mediagoblin/templates/mediagoblin/base.html @@ -21,11 +21,11 @@ {% block title %}GNU MediaGoblin{% endblock title %} + href="{{ request.staticdirect('/css/extlib/reset.css') }}"/> + href="{{ request.staticdirect('/css/extlib/text.css') }}"/> + href="{{ request.staticdirect('/css/extlib/960_16_col.css') }}"/> {% block mediagoblin_head %} From 3539dc8fb6cb59c6a52cb116e8993d73cd26f0d5 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sat, 30 Jul 2011 21:44:36 -0500 Subject: [PATCH 052/118] TOO_LONG_TAG_WARNING isn't needed in this module --- mediagoblin/submit/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediagoblin/submit/forms.py b/mediagoblin/submit/forms.py index 1a5a7f4e..f02c95a6 100644 --- a/mediagoblin/submit/forms.py +++ b/mediagoblin/submit/forms.py @@ -16,7 +16,7 @@ import wtforms -from mediagoblin.util import tag_length_validator, TOO_LONG_TAG_WARNING +from mediagoblin.util import tag_length_validator class SubmitStartForm(wtforms.Form): From 34e4be6fb1c047ece5d3fc311e758468f2a72795 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sat, 30 Jul 2011 21:45:11 -0500 Subject: [PATCH 053/118] tags field should be last on the submission form --- mediagoblin/templates/mediagoblin/submit/start.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediagoblin/templates/mediagoblin/submit/start.html b/mediagoblin/templates/mediagoblin/submit/start.html index 7bacb552..6d00510c 100644 --- a/mediagoblin/templates/mediagoblin/submit/start.html +++ b/mediagoblin/templates/mediagoblin/submit/start.html @@ -25,9 +25,9 @@

Submit yer media

{{ wtforms_util.render_field_div(submit_form.file) }} - {{ wtforms_util.render_field_div(submit_form.tags) }} {{ wtforms_util.render_field_div(submit_form.title) }} {{ wtforms_util.render_textarea_div(submit_form.description) }} + {{ wtforms_util.render_field_div(submit_form.tags) }}
From 1b89b817e5bfdc29aa266bdad056252f55e49a00 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sat, 30 Jul 2011 21:54:18 -0500 Subject: [PATCH 054/118] Removing option to make tags lowercase ...that's basically handled by the slugification --- mediagoblin/config_spec.ini | 1 - mediagoblin/tests/test_mgoblin_app.ini | 1 - mediagoblin/util.py | 8 ++------ 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/mediagoblin/config_spec.ini b/mediagoblin/config_spec.ini index a296f0c1..bbc1f7d6 100644 --- a/mediagoblin/config_spec.ini +++ b/mediagoblin/config_spec.ini @@ -26,7 +26,6 @@ allow_registration = boolean(default=True) # tag parsing tags_delimiter = string(default=",") -tags_case_sensitive = boolean(default=False) tags_max_length = integer(default=50) # By default not set, but you might want something like: diff --git a/mediagoblin/tests/test_mgoblin_app.ini b/mediagoblin/tests/test_mgoblin_app.ini index 5395ca10..7716e9ca 100644 --- a/mediagoblin/tests/test_mgoblin_app.ini +++ b/mediagoblin/tests/test_mgoblin_app.ini @@ -9,7 +9,6 @@ db_name = __mediagoblin_tests__ # tag parsing tags_delimiter = "," -tags_case_sensitive = False tags_max_length = 50 # Celery shouldn't be set up by the application as it's setup via diff --git a/mediagoblin/util.py b/mediagoblin/util.py index bb9f6db4..5880f856 100644 --- a/mediagoblin/util.py +++ b/mediagoblin/util.py @@ -405,12 +405,8 @@ def convert_to_tag_list_of_dicts(tag_string): # Ignore empty or duplicate tags if tag.strip() and tag.strip() not in [t['name'] for t in taglist]: - if mg_globals.app_config['tags_case_sensitive']: - taglist.append({'name': tag.strip(), - 'slug': slugify(tag.strip())}) - else: - taglist.append({'name': tag.strip().lower(), - 'slug': slugify(tag.strip().lower())}) + taglist.append({'name': tag.strip(), + 'slug': slugify(tag.strip())}) return taglist From bc0b10d177253344852cbe4e0dee3048f4206831 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sat, 30 Jul 2011 22:15:54 -0500 Subject: [PATCH 055/118] Updating tests to reflect not having a 'tags_case_sensitive' option. I should probably update the tags of things when I change them. --- mediagoblin/tests/test_tags.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/mediagoblin/tests/test_tags.py b/mediagoblin/tests/test_tags.py index fa6beca9..c2e9fa2b 100644 --- a/mediagoblin/tests/test_tags.py +++ b/mediagoblin/tests/test_tags.py @@ -28,13 +28,6 @@ def test_list_of_dicts_conversion(test_app): 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'}] - - # Make sure case-sensitivity is retained when desired - mg_globals.app_config['tags_case_sensitive'] = True 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'}, From 4a74d54ef59ea3a4eff06d78f6c235fcb6631203 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 31 Jul 2011 15:00:42 -0500 Subject: [PATCH 056/118] Add a clear div between the object gallery and the "user's media" / atom feed --- mediagoblin/templates/mediagoblin/user_pages/user.html | 1 + 1 file changed, 1 insertion(+) diff --git a/mediagoblin/templates/mediagoblin/user_pages/user.html b/mediagoblin/templates/mediagoblin/user_pages/user.html index 7769b8b3..76cf36be 100644 --- a/mediagoblin/templates/mediagoblin/user_pages/user.html +++ b/mediagoblin/templates/mediagoblin/user_pages/user.html @@ -80,6 +80,7 @@
{% set pagination_base_url = user_gallery_url %} {% include "mediagoblin/utils/object_gallery.html" %} +

View all of {{ user.username }}'s media

Date: Sun, 31 Jul 2011 15:07:43 -0500 Subject: [PATCH 057/118] Give a more usful message if no media is available. --- mediagoblin/templates/mediagoblin/utils/object_gallery.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mediagoblin/templates/mediagoblin/utils/object_gallery.html b/mediagoblin/templates/mediagoblin/utils/object_gallery.html index 2c7a7129..a14d7d7b 100644 --- a/mediagoblin/templates/mediagoblin/utils/object_gallery.html +++ b/mediagoblin/templates/mediagoblin/utils/object_gallery.html @@ -33,5 +33,9 @@ {% else %} {{ render_pagination(request, pagination) }} {% endif %} + {% else %} +

+ There doesn't seem to be anything here... +

{% endif %} {% endblock %} From 269943a645d16c51af0388c764c76721336369bc Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 31 Jul 2011 15:09:52 -0500 Subject: [PATCH 058/118] We should redirect after verify_email to the user's homepage --- mediagoblin/auth/views.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/mediagoblin/auth/views.py b/mediagoblin/auth/views.py index fb5db870..df7e2a88 100644 --- a/mediagoblin/auth/views.py +++ b/mediagoblin/auth/views.py @@ -145,23 +145,19 @@ 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!')) else: - verification_successful = False 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): From 77bc1c286778f7b0618f64c1d7498e7f6735d3c6 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 31 Jul 2011 15:22:54 -0500 Subject: [PATCH 059/118] Show the message about nothing exiting either if media_entries object not there or empty --- mediagoblin/templates/mediagoblin/utils/object_gallery.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mediagoblin/templates/mediagoblin/utils/object_gallery.html b/mediagoblin/templates/mediagoblin/utils/object_gallery.html index a14d7d7b..1b1c69f6 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 %}
@@ -35,7 +35,7 @@ {% endif %} {% else %}

- There doesn't seem to be anything here... + There doesn't seem to be any media here yet...

{% endif %} {% endblock %} From 8a20ee349ffdc76b6de7cde8a43c4714fcc034af Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 31 Jul 2011 15:51:55 -0500 Subject: [PATCH 060/118] Renaming "StatusNet" -> MediaGoblin in the extlib policy --- extlib/README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extlib/README b/extlib/README index c23da6e6..c690beac 100644 --- a/extlib/README +++ b/extlib/README @@ -51,7 +51,7 @@ FAQ checked on my patch. Now what? :A: If the upstream project is moribund and there's a way to adopt it, - propose having the StatusNet dev team adopt the project. Or, adopt + 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? From 82b15ad93f6d9dbfb0b00e1fb0a966506be792a0 Mon Sep 17 00:00:00 2001 From: Will Kahn-Greene Date: Sun, 31 Jul 2011 17:09:09 -0400 Subject: [PATCH 061/118] 270. Adds symlinks for 960.gs stuff --- mediagoblin/static/css/extlib/960_16_col.css | 1 + mediagoblin/static/css/extlib/reset.css | 1 + mediagoblin/static/css/extlib/text.css | 1 + 3 files changed, 3 insertions(+) create mode 120000 mediagoblin/static/css/extlib/960_16_col.css create mode 120000 mediagoblin/static/css/extlib/reset.css create mode 120000 mediagoblin/static/css/extlib/text.css 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 From 25a7eb25bf8ba7ce977434fda0d18be441fe3d07 Mon Sep 17 00:00:00 2001 From: Will Kahn-Greene Date: Sun, 31 Jul 2011 17:54:54 -0400 Subject: [PATCH 062/118] Moves docs files around so we build from source/ directory --- docs/Makefile | 12 +- docs/{ => source}/_static/placeholder | 0 docs/source/_templates/mg_theme/layout.html | 39 +++ .../_templates/mg_theme/static/default.css_t | 299 ++++++++++++++++++ docs/source/_templates/mg_theme/theme.conf | 31 ++ docs/{ => source}/codebase.rst | 0 docs/{ => source}/conf.py | 2 +- docs/{ => source}/contributinghowto.rst | 0 docs/{ => source}/deploymenthowto.rst | 0 docs/{ => source}/designdecisions.rst | 0 docs/{ => source}/foreword.rst | 0 docs/{ => source}/git.rst | 0 docs/{ => source}/goblin.png | Bin docs/{ => source}/hackinghowto.rst | 0 docs/{ => source}/index.rst | 0 docs/{ => source}/mediagoblin.rst | 0 docs/{ => source}/mgext/__init__.py | 0 docs/{ => source}/mgext/youcanhelp.py | 0 docs/{ => source}/snugglygoblin.png | Bin docs/{ => source}/theminghowto.rst | 0 docs/{ => source}/vision.rst | 0 21 files changed, 377 insertions(+), 6 deletions(-) rename docs/{ => source}/_static/placeholder (100%) create mode 100644 docs/source/_templates/mg_theme/layout.html create mode 100644 docs/source/_templates/mg_theme/static/default.css_t create mode 100644 docs/source/_templates/mg_theme/theme.conf rename docs/{ => source}/codebase.rst (100%) rename docs/{ => source}/conf.py (99%) rename docs/{ => source}/contributinghowto.rst (100%) rename docs/{ => source}/deploymenthowto.rst (100%) rename docs/{ => source}/designdecisions.rst (100%) rename docs/{ => source}/foreword.rst (100%) rename docs/{ => source}/git.rst (100%) rename docs/{ => source}/goblin.png (100%) rename docs/{ => source}/hackinghowto.rst (100%) rename docs/{ => source}/index.rst (100%) rename docs/{ => source}/mediagoblin.rst (100%) rename docs/{ => source}/mgext/__init__.py (100%) rename docs/{ => source}/mgext/youcanhelp.py (100%) rename docs/{ => source}/snugglygoblin.png (100%) rename docs/{ => source}/theminghowto.rst (100%) rename docs/{ => source}/vision.rst (100%) diff --git a/docs/Makefile b/docs/Makefile index 9a4608de..0b97bf7c 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -5,17 +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) . +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 ' where is one of" @@ -35,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)/* diff --git a/docs/_static/placeholder b/docs/source/_static/placeholder similarity index 100% rename from docs/_static/placeholder rename to 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 %} +
+ + +{%- 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 similarity index 100% rename from docs/codebase.rst rename to docs/source/codebase.rst diff --git a/docs/conf.py b/docs/source/conf.py similarity index 99% rename from docs/conf.py rename to docs/source/conf.py index a7acd3e2..5861a463 100644 --- a/docs/conf.py +++ b/docs/source/conf.py @@ -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 diff --git a/docs/contributinghowto.rst b/docs/source/contributinghowto.rst similarity index 100% rename from docs/contributinghowto.rst rename to docs/source/contributinghowto.rst diff --git a/docs/deploymenthowto.rst b/docs/source/deploymenthowto.rst similarity index 100% rename from docs/deploymenthowto.rst rename to docs/source/deploymenthowto.rst diff --git a/docs/designdecisions.rst b/docs/source/designdecisions.rst similarity index 100% rename from docs/designdecisions.rst rename to docs/source/designdecisions.rst diff --git a/docs/foreword.rst b/docs/source/foreword.rst similarity index 100% rename from docs/foreword.rst rename to docs/source/foreword.rst diff --git a/docs/git.rst b/docs/source/git.rst similarity index 100% rename from docs/git.rst rename to docs/source/git.rst diff --git a/docs/goblin.png b/docs/source/goblin.png similarity index 100% rename from docs/goblin.png rename to docs/source/goblin.png diff --git a/docs/hackinghowto.rst b/docs/source/hackinghowto.rst similarity index 100% rename from docs/hackinghowto.rst rename to docs/source/hackinghowto.rst diff --git a/docs/index.rst b/docs/source/index.rst similarity index 100% rename from docs/index.rst rename to docs/source/index.rst diff --git a/docs/mediagoblin.rst b/docs/source/mediagoblin.rst similarity index 100% rename from docs/mediagoblin.rst rename to docs/source/mediagoblin.rst diff --git a/docs/mgext/__init__.py b/docs/source/mgext/__init__.py similarity index 100% rename from docs/mgext/__init__.py rename to docs/source/mgext/__init__.py diff --git a/docs/mgext/youcanhelp.py b/docs/source/mgext/youcanhelp.py similarity index 100% rename from docs/mgext/youcanhelp.py rename to docs/source/mgext/youcanhelp.py diff --git a/docs/snugglygoblin.png b/docs/source/snugglygoblin.png similarity index 100% rename from docs/snugglygoblin.png rename to docs/source/snugglygoblin.png diff --git a/docs/theminghowto.rst b/docs/source/theminghowto.rst similarity index 100% rename from docs/theminghowto.rst rename to docs/source/theminghowto.rst diff --git a/docs/vision.rst b/docs/source/vision.rst similarity index 100% rename from docs/vision.rst rename to docs/source/vision.rst From fd857e219fbf3cd1671ce4971e67d57207af18bc Mon Sep 17 00:00:00 2001 From: Will Kahn-Greene Date: Sun, 31 Jul 2011 21:03:01 -0400 Subject: [PATCH 063/118] Adds release-related bits; fixes arg handling * fixes arg handling * adds -h support * builds html and texinfo docs and puts them in the right place * puts the resulting tarball and any work done in a tmp/ directory * fixes messages so it tells you what it's doing --- maketarball.sh | 163 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 131 insertions(+), 32 deletions(-) diff --git a/maketarball.sh b/maketarball.sh index 5ded9671..6bfaf920 100755 --- a/maketarball.sh +++ b/maketarball.sh @@ -17,60 +17,159 @@ # along with this program. If not, see . -# usage: maketarball [-d] +# usage: maketarball [-drh] REVISH # -# Creates a tarball from a rev-ish. If -d is passed in, then it adds -# the date to the directory name. +# 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 -d master # ./maketarball v0.0.2 +# ./maketarball -d master +# ./maketarball -r v0.0.2 + + +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: $0 [-d] "; + echo "$USAGE"; exit 1; fi -NOWDATE=`date "+%Y-%m-%d"` +REVISH=$1 +PREFIX="$NOWDATE$REVISH" -if [[ $@ == *-d* ]]; then - REVISH=$2 - PREFIX="$NOWDATE-$REVISH" -else - REVISH=$1 - PREFIX="$REVISH" -fi - - -# convert PREFIX to all lowercase. -# nix the v from tag names. +# 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 ]] - then - rm mediagoblin-$PREFIX.tar - fi - exit 1; + echo "+ git archive command failed. See above text for reason." + cleanup + exit 1 fi -echo "compressing...." -gzip mediagoblin-$PREFIX.tar -echo "archive at mediagoblin-$PREFIX.tar.gz" +if [[ $RELEASE = "yes" ]] +then + pushd tmp/ + tar -xvf $FNBASE.tar -echo "done." + pushd $FNBASE + pushd docs + + echo "+ generating html docs" + make html + if [[ $? -ne 0 ]] + then + echo "+ sphinx docs generation failed. See above text for reason." + cleanup + exit 1 + fi + + # 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/ + else + mv docs/_build/html/ docs/html/ + + rm -rf docs/_build/ + fi + + popd + + tar -cvf $FNBASE.tar $FNBASE + popd +fi + + +echo "+ compressing...." +gzip tmp/$FNBASE.tar + +echo "+ archive at tmp/$FNBASE.tar.gz" + +echo "+ done." From 71454fd351bcc18662168bd89d6876ea498c3715 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 31 Jul 2011 20:45:49 -0500 Subject: [PATCH 064/118] Added tag listing views. Also added routing, added templates, etc. --- mediagoblin/listings/__init__.py | 19 +++++++ mediagoblin/listings/routing.py | 25 +++++++++ mediagoblin/listings/views.py | 53 +++++++++++++++++++ mediagoblin/routing.py | 3 ++ .../templates/mediagoblin/listings/tag.html | 28 ++++++++++ .../templates/mediagoblin/utils/tags.html | 6 ++- 6 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 mediagoblin/listings/__init__.py create mode 100644 mediagoblin/listings/routing.py create mode 100644 mediagoblin/listings/views.py create mode 100644 mediagoblin/templates/mediagoblin/listings/tag.html diff --git a/mediagoblin/listings/__init__.py b/mediagoblin/listings/__init__.py new file mode 100644 index 00000000..fbccb9b8 --- /dev/null +++ b/mediagoblin/listings/__init__.py @@ -0,0 +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 . + +""" +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..90580601 --- /dev/null +++ b/mediagoblin/listings/routing.py @@ -0,0 +1,25 @@ +# 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 . + + +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")] + diff --git a/mediagoblin/listings/views.py b/mediagoblin/listings/views.py new file mode 100644 index 00000000..cdb3db31 --- /dev/null +++ b/mediagoblin/listings/views.py @@ -0,0 +1,53 @@ +# 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 . + +from mediagoblin.db.util import DESCENDING + +from mediagoblin.util import Pagination, render_to_response +from mediagoblin.decorators import uses_pagination + + +@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() + + # Take the tag "name" from the first MediaEntry's non-normalized + # tag naming. + # ... 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 + else: + tag_name = tag_slug + + return render_to_response( + request, + 'mediagoblin/listings/tag.html', + {'tag_name': tag_name, + 'media_entries': media_entries, + 'pagination': pagination}) 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/templates/mediagoblin/listings/tag.html b/mediagoblin/templates/mediagoblin/listings/tag.html new file mode 100644 index 00000000..db3381d2 --- /dev/null +++ b/mediagoblin/templates/mediagoblin/listings/tag.html @@ -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 . +#} +{% extends "mediagoblin/base.html" %} + +{% block mediagoblin_content -%} +

+ Media tagged with: {{ tag_name }} +

+ + +{% endblock %} diff --git a/mediagoblin/templates/mediagoblin/utils/tags.html b/mediagoblin/templates/mediagoblin/utils/tags.html index 94c4cf69..ade41944 100644 --- a/mediagoblin/templates/mediagoblin/utils/tags.html +++ b/mediagoblin/templates/mediagoblin/utils/tags.html @@ -17,9 +17,13 @@ #} {% block tags_content -%} +

Tags

{% endblock %} From 5d9006479088ecadcc4dcef14a9d8ccb0e4227f3 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 31 Jul 2011 20:48:13 -0500 Subject: [PATCH 065/118] Updated media detail view to linkify the tags. Adjusted tag link styling. --- mediagoblin/static/css/base.css | 10 ++++++++++ .../templates/mediagoblin/user_pages/media.html | 3 +++ 2 files changed, 13 insertions(+) diff --git a/mediagoblin/static/css/base.css b/mediagoblin/static/css/base.css index 70db6da9..c84fe047 100644 --- a/mediagoblin/static/css/base.css +++ b/mediagoblin/static/css/base.css @@ -275,3 +275,13 @@ ul.mediagoblin_messages { background-color: #f7f7f7; 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/templates/mediagoblin/user_pages/media.html b/mediagoblin/templates/mediagoblin/user_pages/media.html index 7622d6e6..353cf91c 100644 --- a/mediagoblin/templates/mediagoblin/user_pages/media.html +++ b/mediagoblin/templates/mediagoblin/user_pages/media.html @@ -97,9 +97,11 @@ media = media._id)) }}
{% endif %} +
{% include "mediagoblin/utils/prev_next.html" %}

Sidebar content here!

+

{% if media['uploader'] == request.user['_id'] or request.user['is_admin'] %} @@ -116,6 +118,7 @@

{% endif %}

+ {% if media.tags %} {% include "mediagoblin/utils/tags.html" %} {% endif %} From 92ed289261fc8b091ef459757eb321db9be7d6b8 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 31 Jul 2011 20:54:21 -0500 Subject: [PATCH 066/118] Align image in media detail to center --- mediagoblin/static/css/base.css | 6 ++++++ mediagoblin/templates/mediagoblin/user_pages/media.html | 7 +++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/mediagoblin/static/css/base.css b/mediagoblin/static/css/base.css index c84fe047..59c2f49d 100644 --- a/mediagoblin/static/css/base.css +++ b/mediagoblin/static/css/base.css @@ -212,6 +212,12 @@ text-align: center; text-align: center; } +/* media detail */ + +.media_image_container { + text-align: center; +} + /* icons */ img.media_icon{ diff --git a/mediagoblin/templates/mediagoblin/user_pages/media.html b/mediagoblin/templates/mediagoblin/user_pages/media.html index 353cf91c..a1382518 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 %}
- +
+ +

{{media.title}} From a5303e4791236d83189c81b50fee782e294b231e Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 31 Jul 2011 21:05:35 -0500 Subject: [PATCH 067/118] For no good reason, I feel like 15 is a good number of default feed items. --- mediagoblin/user_pages/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediagoblin/user_pages/views.py b/mediagoblin/user_pages/views.py index dc71b059..85a84db6 100644 --- a/mediagoblin/user_pages/views.py +++ b/mediagoblin/user_pages/views.py @@ -143,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): """ From 1a897068725233b607d377b713b733c6d5084477 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 31 Jul 2011 21:24:33 -0500 Subject: [PATCH 068/118] Added tags atom feed and linked it in the appropriate places --- mediagoblin/listings/routing.py | 5 +- mediagoblin/listings/views.py | 62 +++++++++++++++---- .../templates/mediagoblin/listings/tag.html | 13 ++++ 3 files changed, 67 insertions(+), 13 deletions(-) diff --git a/mediagoblin/listings/routing.py b/mediagoblin/listings/routing.py index 90580601..61dd5210 100644 --- a/mediagoblin/listings/routing.py +++ b/mediagoblin/listings/routing.py @@ -21,5 +21,8 @@ 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")] + 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 index cdb3db31..aade7e64 100644 --- a/mediagoblin/listings/views.py +++ b/mediagoblin/listings/views.py @@ -19,6 +19,23 @@ 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): @@ -33,21 +50,42 @@ def tag_listing(request, page): pagination = Pagination(page, cursor) media_entries = pagination() - # Take the tag "name" from the first MediaEntry's non-normalized - # tag naming. - # ... 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 - else: - tag_name = tag_slug + tag_name = _get_tag_name_from_entries(media_entries, tag_slug) return render_to_response( request, 'mediagoblin/listings/tag.html', - {'tag_name': tag_name, + {'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/templates/mediagoblin/listings/tag.html b/mediagoblin/templates/mediagoblin/listings/tag.html index db3381d2..6f10ec8d 100644 --- a/mediagoblin/templates/mediagoblin/listings/tag.html +++ b/mediagoblin/templates/mediagoblin/listings/tag.html @@ -17,6 +17,13 @@ #} {% extends "mediagoblin/base.html" %} +{% block mediagoblin_head %} + +{% endblock mediagoblin_head %} + {% block mediagoblin_content -%}

Media tagged with: {{ tag_name }} @@ -25,4 +32,10 @@ + + {% endblock %} From b855b010d08eecb34108da6c8b9bed728fa0ff72 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 31 Jul 2011 21:24:54 -0500 Subject: [PATCH 069/118] user pages atom feed not enclosed properly in div, fixing. --- mediagoblin/templates/mediagoblin/user_pages/gallery.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mediagoblin/templates/mediagoblin/user_pages/gallery.html b/mediagoblin/templates/mediagoblin/user_pages/gallery.html index a434ff15..637c892d 100644 --- a/mediagoblin/templates/mediagoblin/user_pages/gallery.html +++ b/mediagoblin/templates/mediagoblin/user_pages/gallery.html @@ -36,10 +36,10 @@ {% include "mediagoblin/utils/object_gallery.html" %}

{% else %} {# This *should* not occur as the view makes sure we pass in a user. #}

Sorry, no such user found.

From f9372f6c8b2b9889a05d072c79abe96dee699cda Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 31 Jul 2011 21:33:01 -0500 Subject: [PATCH 070/118] "needs verification"->"verify your email" willkg says "needs verification" isn't a verb, so it's a weird link. Good point. --- mediagoblin/templates/mediagoblin/base.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediagoblin/templates/mediagoblin/base.html b/mediagoblin/templates/mediagoblin/base.html index 6af02c00..4da685f9 100644 --- a/mediagoblin/templates/mediagoblin/base.html +++ b/mediagoblin/templates/mediagoblin/base.html @@ -59,7 +59,7 @@ - needs verification! + verify your email! {% endif %} `_, 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/source/git.rst b/docs/source/git.rst index bd0f9d52..ab3206b6 100644 --- a/docs/source/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 `_. diff --git a/docs/source/hackinghowto.rst b/docs/source/hackinghowto.rst deleted file mode 100644 index caafba53..00000000 --- a/docs/source/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 -`_ 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 -`. - -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 `_. - - -.. _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 - `_. 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 `_ or -on the `IRC channel `_. - - -Tips for people new to coding -============================= - -Learning Python ---------------- - -GNU MediaGoblin is written using a programming language called `Python -`_. - -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 `_ -* `Dive Into Pyton `_ -* `Python for Software Design `_ -* `A Byte of 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 `_ [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 `_ --- the GitHub - intro to git -* `Pro Git `_ --- fantastic book -* `Git casts `_ --- screencast covering git - usage -* `Git Reference `_ --- 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 `_. - - -Learning other utilities ------------------------- - -The `OpenHatch `_ site has a series of -`training 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/source/index.rst b/docs/source/index.rst index 2f84d6a6..8c00869a 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -15,11 +15,9 @@ Table of Contents: mediagoblin contributinghowto deploymenthowto - hackinghowto theminghowto git codebase - designdecisions vision From a656ccd561a1ac735e3256748016646a7d5576c3 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Mon, 1 Aug 2011 08:34:50 -0500 Subject: [PATCH 072/118] Updating tests to reflect we redirect to the user's page after verification now. --- mediagoblin/tests/test_auth.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/mediagoblin/tests/test_auth.py b/mediagoblin/tests/test_auth.py index f0bb183f..4781dd9b 100644 --- a/mediagoblin/tests/test_auth.py +++ b/mediagoblin/tests/test_auth.py @@ -190,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 @@ -204,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 From 0419d0da24f44ce1fa1c1b8478067c76fc870085 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Mon, 1 Aug 2011 09:11:14 -0500 Subject: [PATCH 073/118] get_test_app() should turn on testing buckets --- mediagoblin/tests/tools.py | 3 +++ 1 file changed, 3 insertions(+) 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 From cd57611f95191ba9b1d58a11dd8eb42d3c9185e0 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Mon, 1 Aug 2011 09:54:09 -0500 Subject: [PATCH 074/118] Phrasing update: "own your data" -> "free your data from proprietary control" --- docs/source/mediagoblin.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/mediagoblin.rst b/docs/source/mediagoblin.rst index c437ecc3..af6658f3 100644 --- a/docs/source/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? From db2b07eeb7f162ec1d0d75ae20d2b19215020c7e Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Mon, 1 Aug 2011 10:46:44 -0500 Subject: [PATCH 075/118] Make sure there's a break between the object gallery and its pagination. --- mediagoblin/templates/mediagoblin/utils/object_gallery.html | 1 + 1 file changed, 1 insertion(+) diff --git a/mediagoblin/templates/mediagoblin/utils/object_gallery.html b/mediagoblin/templates/mediagoblin/utils/object_gallery.html index 1b1c69f6..03b85b17 100644 --- a/mediagoblin/templates/mediagoblin/utils/object_gallery.html +++ b/mediagoblin/templates/mediagoblin/utils/object_gallery.html @@ -27,6 +27,7 @@ entry['media_files']['thumb']) }}" />

{% endfor %} +
{% if pagination_base_url %} {# different url, so set that and don't keep the get params #} {{ render_pagination(request, pagination, pagination_base_url, False) }} From 2542aa30c0fcee783a12f9b8b610e4998871fdd9 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Mon, 1 Aug 2011 10:49:05 -0500 Subject: [PATCH 076/118] Make index page paginated --- mediagoblin/templates/mediagoblin/root.html | 2 +- mediagoblin/views.py | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/mediagoblin/templates/mediagoblin/root.html b/mediagoblin/templates/mediagoblin/root.html index bae033c4..ed7b931f 100644 --- a/mediagoblin/templates/mediagoblin/root.html +++ b/mediagoblin/templates/mediagoblin/root.html @@ -39,5 +39,5 @@ {# 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/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 . 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): From 21cdb646562452e45ea1ba5c93819836a1aaa9b3 Mon Sep 17 00:00:00 2001 From: Will Kahn-Greene Date: Mon, 1 Aug 2011 12:12:41 -0400 Subject: [PATCH 077/118] Updates documentation section in the README --- README | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README b/README index 082ab2a7..0aba179b 100644 --- a/README +++ b/README @@ -33,6 +33,10 @@ hang out, see `our Join page `_ Where is the documentation? =========================== -Documentation is located in the ``docs/`` directory in a "raw" -restructured-text form. It is also available 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 `_. From b7e57b1f76c5f2fba3527dfcd919f517d1c67cca Mon Sep 17 00:00:00 2001 From: Will Kahn-Greene Date: Mon, 1 Aug 2011 12:13:02 -0400 Subject: [PATCH 078/118] Adds additional metadata to setup.py * trove classifiers * long description * url and download_url * ... --- setup.py | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/setup.py b/setup.py index 799f00d8..400bd591 100644 --- a/setup.py +++ b/setup.py @@ -49,21 +49,32 @@ setup( # '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 - [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" + ], ) From c7f0b6fab08a52cc5a6e242ad3df2e674cb68fb9 Mon Sep 17 00:00:00 2001 From: Will Kahn-Greene Date: Mon, 1 Aug 2011 12:17:03 -0400 Subject: [PATCH 079/118] Updating version to 0.0.4. --- docs/source/conf.py | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 5861a463..e2f327c9 100644 --- a/docs/source/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. diff --git a/setup.py b/setup.py index 400bd591..3508f5f0 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ from setuptools import setup, find_packages setup( name = "mediagoblin", - version = "0.0.3", + version = "0.0.4", packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), zip_safe=False, # scripts and dependencies From 4d74812dfc5a671aa50f54951ffe9e0ee520f8f7 Mon Sep 17 00:00:00 2001 From: Will Kahn-Greene Date: Mon, 1 Aug 2011 12:20:31 -0400 Subject: [PATCH 080/118] Removes .pyc files from mgext directory after building --- maketarball.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/maketarball.sh b/maketarball.sh index 6bfaf920..5f17e578 100755 --- a/maketarball.sh +++ b/maketarball.sh @@ -154,10 +154,13 @@ then 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 From 68cf996c1df17ac1f53f5fd86b71bfdfcb9f2aba Mon Sep 17 00:00:00 2001 From: Elrond Date: Mon, 18 Jul 2011 14:07:03 +0200 Subject: [PATCH 081/118] First start at MountStorage. This includes the mounttab, a resolver and adding mount entries. --- mediagoblin/storage.py | 76 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/mediagoblin/storage.py b/mediagoblin/storage.py index 5d6faa4c..3d5ce9ab 100644 --- a/mediagoblin/storage.py +++ b/mediagoblin/storage.py @@ -216,6 +216,82 @@ class BasicFileStorage(StorageInterface): return self._resolve_filepath(filepath) +class MountStorage(StorageInterface): + def __init__(self, **kwargs): + self.mounttab = {} + + def mount(self, dirpath, backend): + """ + Mount a new backend under dirpath + """ + new_ent = clean_listy_filepath(dirpath) + new_ent.append(u'') + + 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) + + assert rem_1.pop(-1) == u'', "Internal Error 1" + assert rem_2.pop(-1) == u'', "Internal Error 2" + assert (already is None) or (len(rem_2) > 0), "Already mounted" + 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 __repr__(self, table = None, indent = ""): + res = [] + if table is None: + res.append("MountStorage<") + table = self.mounttab + v = table.get(None) + if v: + res.append(indent + "On this level: " + repr(v)) + for k, v in table.iteritems(): + if k == None: + continue + res.append(indent + repr(k) + ":") + res += self.__repr__(v, indent + " ") + if table is self.mounttab: + res.append(">") + return "\n".join(res) + else: + return res + + ########### # Utilities ########### From 93b2796c7e89e269d60db8e48bf84a0f2ca88d12 Mon Sep 17 00:00:00 2001 From: Elrond Date: Sat, 23 Jul 2011 15:27:02 +0200 Subject: [PATCH 082/118] MountStorage: Some small fixups/changes. 1) A bit more assert. 2) Change __repr__ to use lists for the recursion parameter. --- mediagoblin/storage.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mediagoblin/storage.py b/mediagoblin/storage.py index 3d5ce9ab..d994268b 100644 --- a/mediagoblin/storage.py +++ b/mediagoblin/storage.py @@ -237,6 +237,7 @@ class MountStorage(StorageInterface): for part in rem_2: table[part] = {} table = table[part] + assert not table.has_key(None), "Huh? Already mounted?!" table[None] = backend def resolve_to_backend(self, filepath, extra_info = False): @@ -272,19 +273,19 @@ class MountStorage(StorageInterface): else: return (res_be, res_fp) - def __repr__(self, table = None, indent = ""): + 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(indent + "On this level: " + repr(v)) + res.append(" " * len(indent) + repr(indent) + ": " + repr(v)) for k, v in table.iteritems(): if k == None: continue - res.append(indent + repr(k) + ":") - res += self.__repr__(v, indent + " ") + res.append(" " * len(indent) + repr(k) + ":") + res += self.__repr__(v, indent + [k]) if table is self.mounttab: res.append(">") return "\n".join(res) From 937e2c88112ee2f2ec71b9b38ccb1b473f32237e Mon Sep 17 00:00:00 2001 From: Elrond Date: Sat, 23 Jul 2011 15:29:22 +0200 Subject: [PATCH 083/118] MountStorage: Create all the wrappers All those methods just call the appropiate method of the relevant backend. --- mediagoblin/storage.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/mediagoblin/storage.py b/mediagoblin/storage.py index d994268b..e3d54a30 100644 --- a/mediagoblin/storage.py +++ b/mediagoblin/storage.py @@ -292,6 +292,34 @@ class MountStorage(StorageInterface): 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 From aaa46f5a156093c4295e4cb0d85eae72e28468ea Mon Sep 17 00:00:00 2001 From: Jef van Schendel Date: Tue, 2 Aug 2011 21:17:30 +0200 Subject: [PATCH 084/118] Sidebar changes: correct tags header to be

, show action buttons header only to owner --- mediagoblin/templates/mediagoblin/user_pages/media.html | 5 +---- mediagoblin/templates/mediagoblin/utils/tags.html | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/mediagoblin/templates/mediagoblin/user_pages/media.html b/mediagoblin/templates/mediagoblin/user_pages/media.html index a1382518..ddcf3ce9 100644 --- a/mediagoblin/templates/mediagoblin/user_pages/media.html +++ b/mediagoblin/templates/mediagoblin/user_pages/media.html @@ -103,11 +103,9 @@
{% include "mediagoblin/utils/prev_next.html" %} -

Sidebar content here!

- -

{% if media['uploader'] == request.user['_id'] or request.user['is_admin'] %} +

Temporary button holder

delete

{% endif %} -

{% if media.tags %} {% include "mediagoblin/utils/tags.html" %} diff --git a/mediagoblin/templates/mediagoblin/utils/tags.html b/mediagoblin/templates/mediagoblin/utils/tags.html index ade41944..32db6e31 100644 --- a/mediagoblin/templates/mediagoblin/utils/tags.html +++ b/mediagoblin/templates/mediagoblin/utils/tags.html @@ -17,7 +17,7 @@ #} {% block tags_content -%} -

Tags

+

Tags

@@ -85,7 +85,7 @@ {% block mediagoblin_footer %}
{% 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">
-

Editing {{ media.title }}

+

{% trans media_title=media.title %}Editing {{ media_title }}{% endtrans %}

{{ wtforms_util.render_divs(form) }}
diff --git a/mediagoblin/templates/mediagoblin/edit/edit_profile.html b/mediagoblin/templates/mediagoblin/edit/edit_profile.html index a11b86d7..4171cb4d 100644 --- a/mediagoblin/templates/mediagoblin/edit/edit_profile.html +++ b/mediagoblin/templates/mediagoblin/edit/edit_profile.html @@ -25,10 +25,10 @@ user['username'] }}" method="POST" enctype="multipart/form-data">
-

Editing {{ user['username'] }}'s profile

+

{% trans username=user['username'] %}Editing {{ username }}'s profile{% endtrans %}

{{ wtforms_util.render_divs(form) }}
- +
diff --git a/mediagoblin/templates/mediagoblin/listings/tag.html b/mediagoblin/templates/mediagoblin/listings/tag.html index 6f10ec8d..18123dbd 100644 --- a/mediagoblin/templates/mediagoblin/listings/tag.html +++ b/mediagoblin/templates/mediagoblin/listings/tag.html @@ -26,7 +26,7 @@ {% block mediagoblin_content -%}

- Media tagged with: {{ tag_name }} + {% trans %}Media tagged with:{% endtrans %} {{ tag_name }}

From 58b79b159e15335a0d8c82ba329543088cabfd49 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Mon, 8 Aug 2011 10:19:21 -0500 Subject: [PATCH 095/118] Just some indentation changes to the templates because I'm picky about such things :) --- .../templates/mediagoblin/auth/login.html | 19 ++++--- .../templates/mediagoblin/auth/register.html | 2 +- .../mediagoblin/auth/verification_email.txt | 8 +-- mediagoblin/templates/mediagoblin/base.html | 4 +- .../mediagoblin/edit/edit_profile.html | 6 ++- .../templates/mediagoblin/listings/tag.html | 4 +- mediagoblin/templates/mediagoblin/root.html | 15 +++--- .../mediagoblin/user_pages/gallery.html | 18 ++++--- .../mediagoblin/user_pages/media.html | 16 +++--- .../mediagoblin/user_pages/user.html | 50 +++++++++++-------- 10 files changed, 88 insertions(+), 54 deletions(-) diff --git a/mediagoblin/templates/mediagoblin/auth/login.html b/mediagoblin/templates/mediagoblin/auth/login.html index 8b1e2296..5750feb0 100644 --- a/mediagoblin/templates/mediagoblin/auth/login.html +++ b/mediagoblin/templates/mediagoblin/auth/login.html @@ -25,9 +25,11 @@

{% trans %}Log in{% endtrans %}

{% if login_failed %} -
{% trans %}Login failed!{% - endtrans %}
{% endif %} {{ - wtforms_util.render_divs(login_form) }} +
+ {% trans %}Login failed!{% endtrans %} +
+ {% endif %} + {{ wtforms_util.render_divs(login_form) }}
@@ -36,10 +38,13 @@ style="display: none;"/> {% endif %} {% if allow_registration %} -

{% trans %}Don't have an account yet?{% endtrans - %}
{% trans %}Create one here!{% endtrans %}

{% endif - %} +

+ {% trans %}Don't have an account yet?{% endtrans %} +
+ + {%- trans %}Create one here!{% endtrans %} +

+ {% endif %}
{% endblock %} diff --git a/mediagoblin/templates/mediagoblin/auth/register.html b/mediagoblin/templates/mediagoblin/auth/register.html index 5d512829..623cbdfd 100644 --- a/mediagoblin/templates/mediagoblin/auth/register.html +++ b/mediagoblin/templates/mediagoblin/auth/register.html @@ -28,7 +28,7 @@ {{ wtforms_util.render_divs(register_form) }}
+ class="button" />
diff --git a/mediagoblin/templates/mediagoblin/auth/verification_email.txt b/mediagoblin/templates/mediagoblin/auth/verification_email.txt index cb01600d..32053101 100644 --- a/mediagoblin/templates/mediagoblin/auth/verification_email.txt +++ b/mediagoblin/templates/mediagoblin/auth/verification_email.txt @@ -14,11 +14,13 @@ # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -#} +-#} -{% trans username=username, verification_url=verification_url %}Hi {{ username }}, +{% trans username=username, verification_url=verification_url|safe -%} +Hi {{ username }}, to activate your GNU MediaGoblin account, open the following URL in your web browser: -{{ verification_url|safe }}{% endtrans %} +{{ verification_url }} +{%- endtrans %} diff --git a/mediagoblin/templates/mediagoblin/base.html b/mediagoblin/templates/mediagoblin/base.html index b9c98ef6..986e0995 100644 --- a/mediagoblin/templates/mediagoblin/base.html +++ b/mediagoblin/templates/mediagoblin/base.html @@ -85,7 +85,9 @@ {% block mediagoblin_footer %}
{% endblock %} diff --git a/mediagoblin/templates/mediagoblin/edit/edit_profile.html b/mediagoblin/templates/mediagoblin/edit/edit_profile.html index 4171cb4d..534e5f20 100644 --- a/mediagoblin/templates/mediagoblin/edit/edit_profile.html +++ b/mediagoblin/templates/mediagoblin/edit/edit_profile.html @@ -25,7 +25,11 @@ user['username'] }}" method="POST" enctype="multipart/form-data">
-

{% trans username=user['username'] %}Editing {{ username }}'s profile{% endtrans %}

+

+ {%- trans username=user['username'] -%} + Editing {{ username }}'s profile + {%- endtrans %} +

{{ wtforms_util.render_divs(form) }}
diff --git a/mediagoblin/templates/mediagoblin/listings/tag.html b/mediagoblin/templates/mediagoblin/listings/tag.html index 18123dbd..a013797f 100644 --- a/mediagoblin/templates/mediagoblin/listings/tag.html +++ b/mediagoblin/templates/mediagoblin/listings/tag.html @@ -36,6 +36,8 @@
{% trans %}atom feed{% endtrans %} + tag=tag_slug) }}"> + {%- trans %}atom feed{% endtrans -%} +
{% endblock %} diff --git a/mediagoblin/templates/mediagoblin/root.html b/mediagoblin/templates/mediagoblin/root.html index 3570374e..a4e19984 100644 --- a/mediagoblin/templates/mediagoblin/root.html +++ b/mediagoblin/templates/mediagoblin/root.html @@ -22,18 +22,21 @@ {% if request.user %}

- {% trans %}Submit an item{% endtrans %} + + {%- trans %}Submit an item{% endtrans -%} +

{% else %}

- {% trans login_url=request.urlgen('mediagoblin.auth.login') %}If you have an account, you can - Login.{% endtrans %} + {% trans login_url=request.urlgen('mediagoblin.auth.login') -%} + If you have an account, you can Login. + {%- endtrans %}

{% if allow_registration %}

- {% trans - register_url=request.urlgen('mediagoblin.auth.register') %}If you don't have an account, please - Register.{% endtrans %} + {% trans register_url=request.urlgen('mediagoblin.auth.register') -%} + If you don't have an account, please Register. + {%- endtrans %}

{% endif %} {% endif %} diff --git a/mediagoblin/templates/mediagoblin/user_pages/gallery.html b/mediagoblin/templates/mediagoblin/user_pages/gallery.html index 71f14da4..a66a547e 100644 --- a/mediagoblin/templates/mediagoblin/user_pages/gallery.html +++ b/mediagoblin/templates/mediagoblin/user_pages/gallery.html @@ -27,20 +27,24 @@ {% block mediagoblin_content -%} {% if user %}

- {% trans - username=user.username %}{{ username }}'s - media{% endtrans %}

+ {%- trans username=user.username, + user_url=request.urlgen( + 'mediagoblin.user_pages.user_home', + user=user.username) -%} + {{ username }}'s media + {%- endtrans %} +
{% else %} {# This *should* not occur as the view makes sure we pass in a user. #} diff --git a/mediagoblin/templates/mediagoblin/user_pages/media.html b/mediagoblin/templates/mediagoblin/user_pages/media.html index 2acf7a3d..afc0d903 100644 --- a/mediagoblin/templates/mediagoblin/user_pages/media.html +++ b/mediagoblin/templates/mediagoblin/user_pages/media.html @@ -38,13 +38,15 @@ {% endautoescape %}

- {% 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 {{ username }}{% endtrans %} + {% 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 {{ username }} + {%- endtrans %}


diff --git a/mediagoblin/templates/mediagoblin/user_pages/user.html b/mediagoblin/templates/mediagoblin/user_pages/user.html index c3ad07db..1115fc56 100644 --- a/mediagoblin/templates/mediagoblin/user_pages/user.html +++ b/mediagoblin/templates/mediagoblin/user_pages/user.html @@ -36,11 +36,15 @@

{% trans %}Verification needed{% endtrans %}

-

{% trans %}Almost done! Your account still needs to be - verified.{% endtrans %}

- {% trans %}An email should arrive in a few moments with - instructions on how to do so.{% endtrans %} + {% trans -%} + Almost done! Your account still needs to be verified. + {%- endtrans %} +

+

+ {% trans -%} + An email should arrive in a few moments with instructions on how to do so. + {%- endtrans %}

{% trans %}In case it doesn't:{% endtrans %}

@@ -53,30 +57,32 @@

{% trans %}Verification needed{% endtrans %}

- {% trans %}Someone has registered an account with this - username, but it still has to be verified.{% endtrans %} + {% trans -%} + Someone has registered an account with this username, but it still has to be verified. + {%- endtrans %}

- {% trans login_url=request.urlgen('mediagoblin.auth.login') - %}If you are that person but you've lost your verification - email, you can - log in and resend it.{% - endtrans %} + {% trans login_url=request.urlgen('mediagoblin.auth.login') -%} + If you are that person but you've lost your verification email, you can log in and resend it. + {%- endtrans %}

{% endif %} {# Active(?) (or at least verified at some point) user, horray! #} {% else %} -

{% trans username=user.username %}{{ username }}'s profile{% - endtrans %}

+

+ {%- trans username=user.username %}{{ username }}'s profile{% endtrans -%} +

{% include "mediagoblin/utils/profile.html" %} {% if request.user['_id'] == user['_id'] or request.user['is_admin'] %} {% trans %}Edit profile{% endtrans %} + user.username }}"> + {%- trans %}Edit profile{% endtrans -%} + {% endif %}
@@ -84,12 +90,16 @@ {% set pagination_base_url = user_gallery_url %} {% include "mediagoblin/utils/object_gallery.html" %}
-

{% trans - username=user.username %}View all of {{ username }}'s media{% - endtrans %}

- {% trans %}atom feed1{% - endtrans %} +

+ + {% trans username=user.username -%} + View all of {{ username }}'s media{% endtrans -%} + +

+ + {%- trans %}atom feed{% endtrans -%} +
From 4100798b86106832373b5afca77960edec5cdcc8 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Mon, 8 Aug 2011 10:21:17 -0500 Subject: [PATCH 096/118] New extracted strings from the templates! --- .../i18n/en/LC_MESSAGES/mediagoblin.po | 200 ++++++++++++++++-- 1 file changed, 181 insertions(+), 19 deletions(-) diff --git a/mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po index 67830c2e..5e54390d 100644 --- a/mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po @@ -1,26 +1,14 @@ -# Translations template for GNU MediaGoblin. -# -# 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 . +# Translations template for PROJECT. +# Copyright (C) 2011 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2011. # #, fuzzy msgid "" msgstr "" -"Project-Id-Version: GNU MediaGoblin\n" -"POT-Creation-Date: 2011-08-06 23:01-0500\n" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2011-08-08 10:20-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -29,7 +17,181 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.6\n" +#: 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 MediaGoblin, a GNU project" +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 Login." +msgstr "" + +#: mediagoblin/templates/mediagoblin/root.html:37 +#, python-format +msgid "" +"If you don't have an account, please Register." +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/resent_verification_email.html:22 +msgid "Resent your verification email." +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 "%(username)s'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 " +"log in 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 "" + From 255f02c48623533f6e781d9d1eade12d18ee0745 Mon Sep 17 00:00:00 2001 From: Elrond Date: Mon, 8 Aug 2011 20:11:28 +0200 Subject: [PATCH 097/118] MountStorage: Add docs. --- mediagoblin/storage.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/mediagoblin/storage.py b/mediagoblin/storage.py index 25598c82..bbf1c034 100644 --- a/mediagoblin/storage.py +++ b/mediagoblin/storage.py @@ -287,6 +287,21 @@ class CloudFilesStorage(StorageInterface): 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 = {} From 620fca54727e37ba1b3e5ba745fdd7d7186dcafd Mon Sep 17 00:00:00 2001 From: Elrond Date: Mon, 8 Aug 2011 21:51:11 +0200 Subject: [PATCH 098/118] MountStorage: Improve mounting asserts The asserts now differentiate between mounting on the same path and mounting over a shorter path. --- mediagoblin/storage.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mediagoblin/storage.py b/mediagoblin/storage.py index bbf1c034..88c748ce 100644 --- a/mediagoblin/storage.py +++ b/mediagoblin/storage.py @@ -313,13 +313,16 @@ class MountStorage(StorageInterface): 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) + 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" - assert (already is None) or (len(rem_2) > 0), "Already mounted" for part in rem_2: table[part] = {} table = table[part] - assert not table.has_key(None), "Huh? Already mounted?!" table[None] = backend def _resolve_to_backend(self, filepath, extra_info = False): From c65ea50fbd09842f9928a4879063ed5c343a73a2 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Mon, 8 Aug 2011 19:52:14 -0500 Subject: [PATCH 099/118] Swedish translations updated to 100% --- .../i18n/de/LC_MESSAGES/mediagoblin.po | 179 +++++++++++++++++- 1 file changed, 177 insertions(+), 2 deletions(-) diff --git a/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po index 715757fb..0303ec12 100644 --- a/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po @@ -7,8 +7,8 @@ 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-06 23:01-0500\n" -"PO-Revision-Date: 2011-08-07 14:06+0000\n" +"POT-Creation-Date: 2011-08-08 10:20-0500\n" +"PO-Revision-Date: 2011-08-08 15:22+0000\n" "Last-Translator: cwebber \n" "Language-Team: German (http://www.transifex.net/projects/p/mediagoblin/team/de/)\n" "MIME-Version: 1.0\n" @@ -18,8 +18,183 @@ msgstr "" "Language: de\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" +#: 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 MediaGoblin, a GNU project" +msgstr "" + #: 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 "" + +#: mediagoblin/templates/mediagoblin/root.html:31 +#, python-format +msgid "If you have an account, you can Login." +msgstr "" + +#: mediagoblin/templates/mediagoblin/root.html:37 +#, python-format +msgid "" +"If you don't have an account, please Register." +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/resent_verification_email.html:22 +msgid "Resent your verification email." +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 "%(username)s'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 log in 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 "" + From 4d989b5eaff0490b13a9698a598b7bc678901eb1 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Mon, 8 Aug 2011 19:53:40 -0500 Subject: [PATCH 100/118] Compiled the new Swedish translations. I know Elrond wants 'no compiled crap in our repository' but until I have a better solution... --- mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.mo | Bin 602 -> 3688 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.mo index ba6256234c243d76d87ef64c1363068aff62f8a7..c403e3cc1e98f59c087bc732986a57d6b25cac1b 100644 GIT binary patch literal 3688 zcmeH}&u<$=6vwAfKwW;SNFaU)#zAQl)oVMEii+E$v}p*1q(n(t!~sR)-Di7XJ!8$x z#dfCFqjFpWWIszF$Fl{pe+#l+*Fe_m z2FQAS53*jjK-TNr;~+q(m@|Gz;AM(D)$PJo!Yco$^l)=w=?_C8Zrix|scRUY7JH72R3(7 zC5g>VkUr=vlZGhuGFaInxkFC3HKjFi&PeJhok`!dd6r7$oZca+ja0Ht1KG1ZGQrD% z4qe>!!R1NN)_7@+?AievOs({`CYF>*C=*MLR3NEfq4ZxF-$G=vOO7hyf;Gp3>?(NL z>nDd4&Whz!hg}W0p-o3l&lGdq4mxB|spwSRzpr#FmoCdS7G=e@GHt}$8W#3?R-*_} zE6ihuBOS6<`-Y5TuS(k|BSV*D+h&;^u-D5QtB@ip#to0a^qy%7`55>uDN~7_TT_lIogp4BNKLtHBK#g z#MKmdjQAPjt8619DXoZN5oJ)F4y8p3Hp^R`VqKwI$BK13Ku)EyHQbv?H-vt(UN4fS z>|3+0GqpI86dX?#NxrM^s7y_hH6VkA;F zH`wEwd8?W!O)M&rhi+^CvdDJn#n$3^L&R9w+02nwadw(r_cyCvv-$`Z_u zW81f%DnbR@Lq&m^Lcn2}??vsYi1kwD_+lEvvB%zN?za$aP~W-`ulbHnsBcqdA`Y^gG#{pFh#SCt6iL#9K}opJAG=DHD{17PLGw`txR*`!Gy0o2Q&y zix^=#x$027MXHCnlIvkzW=69a3_dt`S0P`fv^mW=>pX>p zJhbCed;K`HHo17&BsN7wY|2wD9ikO7?Q p7?qZy06h2zL8U)9;||Wa|HB!Fcj=F94{ybIk=FZ9sRx{M{{j-PON#&i delta 125 zcmaDMbBiVXo)F7a1|VPpVi_RT0dbIk4UjDj#I`{EfRTYA5=hGfu@y)|cxp~^er~El zNxp)+U#Nm_YD#9Jdwx<*X5QpF_8xXK1tViC1H;LJ9E$Ab3Wg?D24 Date: Mon, 8 Aug 2011 22:26:30 -0500 Subject: [PATCH 101/118] Actually I did *not* successfully check in our new languages (sr and sv); now fixed :) --- .../i18n/sr/LC_MESSAGES/mediagoblin.po | 199 ++++++++++++++++ .../i18n/sv/LC_MESSAGES/mediagoblin.po | 214 ++++++++++++++++++ 2 files changed, 413 insertions(+) create mode 100644 mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.po create mode 100644 mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.po diff --git a/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.po new file mode 100644 index 00000000..b5a6f2d3 --- /dev/null +++ b/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.po @@ -0,0 +1,199 @@ +# 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 10:20-0500\n" +"PO-Revision-Date: 2011-08-09 03:25+0000\n" +"Last-Translator: FULL NAME \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/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 MediaGoblin, a GNU project" +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 Login." +msgstr "" + +#: mediagoblin/templates/mediagoblin/root.html:37 +#, python-format +msgid "" +"If you don't have an account, please Register." +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/resent_verification_email.html:22 +msgid "Resent your verification email." +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 "%(username)s'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 log in 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.po b/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.po new file mode 100644 index 00000000..8899a3ea --- /dev/null +++ b/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.po @@ -0,0 +1,214 @@ +# Translations template for PROJECT. +# Copyright (C) 2011 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# +# , 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 10:20-0500\n" +"PO-Revision-Date: 2011-08-09 00:35+0000\n" +"Last-Translator: joar \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/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 MediaGoblin, a GNU project" +msgstr "" +"Drivs av MediaGoblin, ett project " +"från GNU" + +#: 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 Login." +msgstr "Har du ett konto? Logga in." + +#: mediagoblin/templates/mediagoblin/root.html:37 +#, python-format +msgid "" +"If you don't have an account, please Register." +msgstr "" +"Har du inget konto? Registrera ett konto." + +#: 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/resent_verification_email.html:22 +msgid "Resent your verification email." +msgstr "Skickade ett nytt verifierings-email." + +#: 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 "%(username)s's media" +msgstr "%(username)ss 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 log in 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 logga in 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" + + From 956ba354c9c79384250e85ad4d9f16900503921b Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Mon, 8 Aug 2011 22:27:26 -0500 Subject: [PATCH 102/118] Once again compiling .po to .mo --- mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.mo | Bin 0 -> 3752 bytes mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.mo | Bin 0 -> 3767 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.mo create mode 100644 mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.mo new file mode 100644 index 0000000000000000000000000000000000000000..cb07e1d0835f00a0fb97666fdbe3062b8227a5eb GIT binary patch literal 3752 zcmeH}&u<$=6vwAfT45-lB7uMkULx{KWbJiIQM-*((k2Z-PKc7Uhy#k&-tl_SddHfX zjcciuIDjhA6H+VnALt1V2(CR;f;$Hea4Hu#!4V0G3w+oNFMy;uBnsfuDgK z_cq9JzX03dUGODv3`Vbk=RnS53%m>_Am{fjh_ASh%QN7wAkX^)WPgW_7V(dRgz-4| z3b+J52{u8F-vM8T79WGG*Bv}$z3zgn*Y6rasNI{H+h*QX%s^*6|Rt-_e~`W)nS-2owm_!4Bjz6Lq&J&@!60J2`cfUMUF zCb3>Ih+E=1$a>ubIltdP_Im_kv0le;;dv)O_BRD``~W0y6=c0M$a>uXIsPqBj7?@% znl$XlDLY}QYjjH`Qp|?bHnLS4EuL>>CYiFMb9-mcR>C=0bY!GLF`sm_brk7Tj?f1> zGZZ$PI!hf{r;>zHDI-fxQ$y0WG^(X!R2K6o$xf&evTdCu5rxL6ZAmJnkXo`w9o>#1rNJ3 zr%NSd9bz5ZoN1r6+GjG%eVTMvrsUd^S~^K|kE32%uS1GbZr76yY8c%^1%py)Y_d?c z8L~!2$;5JtLqJ&xKOq(;jfHrP%<=sRXpnH>a(8>pi^5Fj#!bkyry&v@tAYE`ISh~v z_X5O>$Ve*z<;ugzXo-i_o~CXhLn~>oUn65>oixK{TOW+s`Hs7NWqWjjbM&HUPN+X9Lp$g!JxPm=XHf{u@-B(hn|Y4(cfK3+qrH~ zspL&l%q?pZkIwCB3eIO5O}?Y*z$Q*Jo?986`4W zpT*YO)Bz2u*1)*j(z>Zlx135>85GX$?SYE2Hj}or_THwKpHhh9E#L?UXvVKrJ^wVm zRP}=y-}6uTJ|=qWa$9je`{$h@|70+8>J1;CQa!ZJ+caTn6D-pNbn#NXPOI}Pi!{5q zGQV7ZdwyYIeQ{%B4pY-O3uEbR%CHmAhBOU@icAjH(d+ffjvc9%yz<~ex(_VH!MtKk zWvaB6WF}0!i`sN-KxsE0+Sx6Hkpo9{aUR`JH0!P7uk}=TTGie!l@e2>4tKpC7DWtlAy%>fX;^vnUG&TQ+}&-@SYsL g&kx>{gMVNK$5VcYJo+_+x5dGG^58xBe|}H?15eCV>i_@% literal 0 HcmV?d00001 diff --git a/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.mo new file mode 100644 index 0000000000000000000000000000000000000000..e300ae89d0b8fc62abab1aa603c44cbcc1bcaf4c GIT binary patch literal 3767 zcma);&u=706~`+Oeq{I&w4e}v=8c7SZN)t^-e_SnS$oN@on0gE#@LP{4#}!!x~985 z-Bqos>Y0p`l{g@f;DiLy3M3BTU8&k7pecYdoK+ z?&?>szVE$y{qBXwf6nlC5&vJs|L$XMbo%$^M;Lnw!~0+x{26!!{3G}s@W0^4!R?PS zb^+W4KL_3ezYON!r@^0qUj!e3Uk85=J_-IAybt~ZoPe)A&e&b>uiy>v;>Q^KJeYuP zo%<9Sz4ESTvt@{9U>;4vu!9Rds0+;d7GvIa5`Ed_C0#nfWHwE!w@8avT;J-jO z??0fk_t+=O^)G;g@kQ_};4Sb8un)TRGw|=w?)%^uFn%33Tc822g1-ixUVj7KdKVj| z{Q$&=ZQ{%Aa|d*~yaqb|GtlYvqw4zG;14l=2)+Yue2TH#;CtY=z%4lcHSkUFli)8Z z{sw#+= zBqbB8A`OXaos0w}k~j^-m@<_KX$_4N8{?|6y6D?PNkd9ftj5k@sK}@myUAr=r0k{^ z+zR5bP_EysFTpd4g%(Y=rQ{_`F~>@$^JpUMb+#QQcBX+FBr>Aq6~D)OHnAyG@{`Kz z=Sp|$?kjZ1iE>Nz(nPY`2`rq>Tx&GoHOD?i&eMt0YT=RE_cc{fkc ze-~0T{BvW`qn=h{M6lavEU%Qx_PNuj3>n)QxFw*hfE|v_32h)=udwd=C1{Ykz0v+#pt^-f(}e4t%ZII5AY`?3&LO zh*p+zj7Jv>`9NtsStE&BEc&s35-D3q)c8Ts%Mx3`EtFW1)kCjQJ(vhxohC>Agedda z9~~lOM+puDj-x~k&eYTF2zphdeeBNpwop$^#;wY5qQHz7a8l;GKYN;Nuar612?H!_ zP&?g43ypn|D{X^2CQ8EKMiH6dP<5$_S`XvaK&igcc}oi0t^#M~bD*6VQwY=8d-*W% zrxex+wr~V=>2ha%J?LD;ht@mY%blQerPIO0U|)<9=g;E2=c&`_K6mBW4*nXu+}Pky zb7@j+Q+MfDaZT5(yOIpV8{ZzIFnh?VmJyqnEu(@*A~+O0>(ap(CzXk5rP|7PJZ{aN zJiC|t%x)|mZ<|qjwXv5Lny0}_N@u1^GWSEXarp(*xVy8lBB{AS>#L1BJ9oBEC0=iJ z8aI`+IN^iCNe&&Xcmvv{JdrQBgw;majlvFstEcnarUpU>+p@1h9EV-H+DmNXmXJb2 zOT=cm>$rd-=5-7V0@;^{-=5{3B9 zHLsf*exYkv8e(JUO56A;f*dC-GCdS2A|YIkSIunb4r&+~sw@LIX2(2}=)KCojysu< zw?kwedgZ96C#a(#&pC;DiRS5rCcBNe!ym{ZDi5yXbbuXbQ+2i3SU=L%>Y46NUHYqL zCfPzwvD@PK%tZ7xmFMt1M9UgXAEw8CvuIZsC)kZ7w5whUDLI-EW|SM|5CKBY>qhzX zt;Gs_zy9`eh6OE6PyE&mJqk1q{XL%#hQc z7zsSYXck&BaB+x~z=XMELQgo;8Y7NqxbxdP(GwAzE2AeT7C}8HV)N$e6Z&jN)K0Bg z29&L>GBmq-ecsF&uh8jcUbR>yUxG3uGc3|nP=v_1D3;C9&vosw{IMfpERya;B*TH0 zPWnS03e#kVXp>MDxm|V1^6jVWl3RGKGsH)yPJQSe>2_2dr!>&hx8~kN&g+@JR~`%# zxaB2sGCxv*s2Mi_rRc2tDRq#sF%$T{fX Date: Mon, 8 Aug 2011 22:32:28 -0500 Subject: [PATCH 103/118] We should pass ugettext instead of gettext into the jinja template env Otherwise we might get UnicodeDecodeErrors :) --- mediagoblin/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediagoblin/util.py b/mediagoblin/util.py index c9f4a0ac..d26dc6dc 100644 --- a/mediagoblin/util.py +++ b/mediagoblin/util.py @@ -93,7 +93,7 @@ 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.ugettext, mg_globals.translations.ngettext) # All templates will know how to ... From 1eec9c88445ca73e4b237ce39190fda0775fc4f1 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Mon, 8 Aug 2011 22:49:35 -0500 Subject: [PATCH 104/118] mediagoblin/translations/ directory not used, mediagoblin/i18n/ is; removing former --- .../en/LC_MESSAGES/mediagoblin.mo | Bin 502 -> 0 bytes .../en/LC_MESSAGES/mediagoblin.po | 23 ------------------ 2 files changed, 23 deletions(-) delete mode 100644 mediagoblin/translations/en/LC_MESSAGES/mediagoblin.mo delete mode 100644 mediagoblin/translations/en/LC_MESSAGES/mediagoblin.po diff --git a/mediagoblin/translations/en/LC_MESSAGES/mediagoblin.mo b/mediagoblin/translations/en/LC_MESSAGES/mediagoblin.mo deleted file mode 100644 index fb7046cdd069d85b478d03ce838a08b13736f58b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 502 zcmaJ;%TB^T6s^Wpmu_`&*=gcn+eCu{CAC;T}Vev5;e zpc`&-l5>)i`#AUW^yJ+#b!>2MaJ3VEs$uwhMGT3jq}$+)FzeM!`M>x9ZqkDKrYR$98QF()g;c*9GIE4(St9C2D`8m!!7$?e<-w lB7sO{#zjt{w&2, 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 \n" -"Language-Team: LANGUAGE \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 "" - From 03e5bd6d352f9479eefd3c7ad1f39137bd9ba17d Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Mon, 8 Aug 2011 22:51:03 -0500 Subject: [PATCH 105/118] Provide a pass_to_ugettext method and set up gettext to default to English. --- mediagoblin/util.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/mediagoblin/util.py b/mediagoblin/util.py index d26dc6dc..ed7be841 100644 --- a/mediagoblin/util.py +++ b/mediagoblin/util.py @@ -478,6 +478,22 @@ 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) + + PAGINATION_DEFAULT_PER_PAGE = 30 class Pagination(object): From 4b1adc132cf45507e2f910122992230d428c6856 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Mon, 8 Aug 2011 22:53:39 -0500 Subject: [PATCH 106/118] Marked relevant strings in python views/forms for translation via ugettext --- mediagoblin/auth/forms.py | 16 +++++++++------- mediagoblin/auth/views.py | 18 ++++++++++-------- mediagoblin/edit/forms.py | 17 ++++++++++------- mediagoblin/edit/views.py | 7 ++++--- mediagoblin/submit/forms.py | 8 +++++--- mediagoblin/submit/views.py | 7 ++++--- 6 files changed, 42 insertions(+), 31 deletions(-) diff --git a/mediagoblin/auth/forms.py b/mediagoblin/auth/forms.py index 7bc0aeb1..1b6bc7c2 100644 --- a/mediagoblin/auth/forms.py +++ b/mediagoblin/auth/forms.py @@ -16,34 +16,36 @@ import wtforms +from mediagoblin.util import pass_to_ugettext 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/views.py b/mediagoblin/auth/views.py index df7e2a88..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,7 +52,7 @@ 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 @@ -148,12 +149,13 @@ def verify_email(request): 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: - 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 redirect( request, 'mediagoblin.user_pages.user_home', @@ -174,7 +176,7 @@ def resend_activation(request): messages.add_message( request, messages.INFO, - 'Resent your verification email.') + _('Resent your verification email.')) return redirect( request, 'mediagoblin.user_pages.user_home', user=request.user['username']) diff --git a/mediagoblin/edit/forms.py b/mediagoblin/edit/forms.py index a1783a72..8052f482 100644 --- a/mediagoblin/edit/forms.py +++ b/mediagoblin/edit/forms.py @@ -16,25 +16,28 @@ import wtforms + from mediagoblin.util import tag_length_validator, TOO_LONG_TAG_WARNING +from mediagoblin.util import pass_to_ugettext 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', + _('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 5cbaadb5..0b1a98f1 100644 --- a/mediagoblin/edit/views.py +++ b/mediagoblin/edit/views.py @@ -23,6 +23,7 @@ from mediagoblin import mg_globals from mediagoblin.util import ( 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 @@ -50,7 +51,7 @@ 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') @@ -71,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( @@ -92,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/submit/forms.py b/mediagoblin/submit/forms.py index f02c95a6..ccb62a03 100644 --- a/mediagoblin/submit/forms.py +++ b/mediagoblin/submit/forms.py @@ -16,15 +16,17 @@ import wtforms + from mediagoblin.util import tag_length_validator +from mediagoblin.util import pass_to_ugettext 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', + _('Tags'), [tag_length_validator]) diff --git a/mediagoblin/submit/views.py b/mediagoblin/submit/views.py index 87e57dda..ba13b755 100644 --- a/mediagoblin/submit/views.py +++ b/mediagoblin/submit/views.py @@ -23,6 +23,7 @@ from werkzeug.utils import secure_filename from mediagoblin.util import ( 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 @@ -41,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 @@ -92,7 +93,7 @@ def submit_start(request): # queue it for processing process_media_initial.delay(unicode(entry['_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']) From d1e67890da02f29a8fc4605c2f6601526fcacf35 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Mon, 8 Aug 2011 22:56:16 -0500 Subject: [PATCH 107/118] Extracted translatable strings from python files. --- .../i18n/en/LC_MESSAGES/mediagoblin.po | 105 +++++++++++++++++- 1 file changed, 100 insertions(+), 5 deletions(-) diff --git a/mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po index 5e54390d..0de5ca62 100644 --- a/mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2011-08-08 10:20-0500\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 \n" "Language-Team: LANGUAGE \n" @@ -17,6 +17,105 @@ msgstr "" "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 "" @@ -90,10 +189,6 @@ msgstr "" msgid "Create an account!" msgstr "" -#: mediagoblin/templates/mediagoblin/auth/resent_verification_email.html:22 -msgid "Resent your verification email." -msgstr "" - #: mediagoblin/templates/mediagoblin/auth/verification_email.txt:19 #, python-format msgid "" From 7bc2d2e313336759d77e4dbf9f4a6ced711a5bf1 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Tue, 9 Aug 2011 09:41:16 -0500 Subject: [PATCH 108/118] New translations including Swedish at 100% (again! now with python strings) --- .../i18n/de/LC_MESSAGES/mediagoblin.po | 107 ++++++++++++++++- .../i18n/sr/LC_MESSAGES/mediagoblin.po | 109 ++++++++++++++++-- .../i18n/sv/LC_MESSAGES/mediagoblin.po | 109 +++++++++++++++++- 3 files changed, 306 insertions(+), 19 deletions(-) diff --git a/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po index 0303ec12..065a28b8 100644 --- a/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po @@ -7,8 +7,8 @@ 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 10:20-0500\n" -"PO-Revision-Date: 2011-08-08 15:22+0000\n" +"POT-Creation-Date: 2011-08-08 22:53-0500\n" +"PO-Revision-Date: 2011-08-09 03:57+0000\n" "Last-Translator: cwebber \n" "Language-Team: German (http://www.transifex.net/projects/p/mediagoblin/team/de/)\n" "MIME-Version: 1.0\n" @@ -18,6 +18,105 @@ msgstr "" "Language: de\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 "" + +#: 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 "" @@ -91,10 +190,6 @@ msgstr "" msgid "Create an account!" msgstr "" -#: mediagoblin/templates/mediagoblin/auth/resent_verification_email.html:22 -msgid "Resent your verification email." -msgstr "" - #: mediagoblin/templates/mediagoblin/auth/verification_email.txt:19 #, python-format msgid "" diff --git a/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.po index b5a6f2d3..ec8611ee 100644 --- a/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.po @@ -6,9 +6,9 @@ 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 10:20-0500\n" -"PO-Revision-Date: 2011-08-09 03:25+0000\n" -"Last-Translator: FULL NAME \n" +"POT-Creation-Date: 2011-08-08 22:53-0500\n" +"PO-Revision-Date: 2011-08-09 03:57+0000\n" +"Last-Translator: cwebber \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" @@ -17,6 +17,105 @@ msgstr "" "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 "" @@ -90,10 +189,6 @@ msgstr "" msgid "Create an account!" msgstr "" -#: mediagoblin/templates/mediagoblin/auth/resent_verification_email.html:22 -msgid "Resent your verification email." -msgstr "" - #: mediagoblin/templates/mediagoblin/auth/verification_email.txt:19 #, python-format msgid "" diff --git a/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.po index 8899a3ea..ffcd17df 100644 --- a/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.po @@ -7,8 +7,8 @@ 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 10:20-0500\n" -"PO-Revision-Date: 2011-08-09 00:35+0000\n" +"POT-Creation-Date: 2011-08-08 22:53-0500\n" +"PO-Revision-Date: 2011-08-09 14:10+0000\n" "Last-Translator: joar \n" "Language-Team: Swedish (http://www.transifex.net/projects/p/mediagoblin/team/sv/)\n" "MIME-Version: 1.0\n" @@ -18,6 +18,107 @@ msgstr "" "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" @@ -94,10 +195,6 @@ msgstr "Skapa ett!" msgid "Create an account!" msgstr "Skapa ett konto!" -#: mediagoblin/templates/mediagoblin/auth/resent_verification_email.html:22 -msgid "Resent your verification email." -msgstr "Skickade ett nytt verifierings-email." - #: mediagoblin/templates/mediagoblin/auth/verification_email.txt:19 #, python-format msgid "" From b811346a2abfb6b0359b12cb68047f8fd98bf204 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Tue, 9 Aug 2011 09:48:06 -0500 Subject: [PATCH 109/118] Compiling the translations files again --- .../i18n/de/LC_MESSAGES/mediagoblin.mo | Bin 3688 -> 5330 bytes .../i18n/sr/LC_MESSAGES/mediagoblin.mo | Bin 3752 -> 5401 bytes .../i18n/sv/LC_MESSAGES/mediagoblin.mo | Bin 3767 -> 5467 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.mo index c403e3cc1e98f59c087bc732986a57d6b25cac1b..eaa79dfa4c8bed2a279beb08a59c418b57dc7ed6 100644 GIT binary patch literal 5330 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 CN`2J; delta 786 zcmYMwO-NKx6u|NG(l;NCO*W%7S>}_P7X{VSFj#^`pJQP|QMQ{zr-26ML(BrhVOmH* zt>P36Ni7Q6*g_bnjX_au+C(cCqC(&*TGb*9s{f@2J?^`|d+z%<_nd{@uR3c>k>-cO z<#KQ39CXomZU@Hz`2aaM3-os5;$89)^>qTnv ziA9x-Wt_llvq%Q#@ff=5QQ%vjdE}F!3G>K>MD$e0%jPp?!9zk=EA-se| z>>$6qW5JLmrZBFcdGQCDA6ou@2Z*3U?BW(oV-Ry_4p72Hx_1lPh%fVSFJ8lg_!LdY z7SQB#*Ybbz%N`aCIm~1#J&m+RvS=zOp=sG2Z~r;oCVq*pu`eXjkH4@7PjLE9oWuY= z@_dFd;upxP@&Rk6!p|(q_yrw2PhaCWhICQtcoCoA4*ZU$f*9?h80qrd<(Wj&kv=r7 zKZg#Ede85p>ELu&^lQ^c-|^vcE?-oiJ=J(@Pg|PtU(h%HS-l>}>YqTno(d-QXRs$$ z%vDNxXT0L1GlS0A{FTCRx-wEMlr4&|FgSE5OE+PXE>?*0YmF>B)h From ff0e4fd52b680a13ac75756ec3f8a883bed9f535 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Tue, 9 Aug 2011 10:12:05 -0500 Subject: [PATCH 110/118] Joar updating a typo in the Swedish translations :) --- .../i18n/sv/LC_MESSAGES/mediagoblin.mo | Bin 5467 -> 5461 bytes .../i18n/sv/LC_MESSAGES/mediagoblin.po | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.mo index f13bfb9e3e1679a162a01a2dba0ed37fd8cba734..04fe0b6ffde34b7eabe97b8ea9784f123e96795b 100644 GIT binary patch delta 299 zcmXZWF-rn*7{~F4j_VR^IIX1+OFFs}c4(-f(W`HWcLkBxp!EsTh;xW9lP!B&*JzO9+;TN^^IA@EMl8q2vsm)<~2qY&IH zkY0g<+z_s zxWFs?!9#SiB1c%>+t}MhmG{uYJ{IsPD-)?Oys=OZm#7{l$W2*e9ygf6)06+A7Sd5V z*u*~8aEdux;0-3I3e{PjVOxc^`#AQa;CASHV<-OA+_ZMtx`^%L7u^^|;k`e2u\n" "Language-Team: Swedish (http://www.transifex.net/projects/p/mediagoblin/team/sv/)\n" "MIME-Version: 1.0\n" @@ -144,8 +144,8 @@ msgid "" "Powered by MediaGoblin, a GNU project" msgstr "" -"Drivs av MediaGoblin, ett project " -"från GNU" +"Drivs av MediaGoblin, ett GNU-projekt" #: mediagoblin/templates/mediagoblin/root.html:21 msgid "Welcome to GNU MediaGoblin!" From 1c266dc328cd6fb1e0275d104a6e8fbceeb187fc Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 10 Aug 2011 10:48:02 -0500 Subject: [PATCH 111/118] Utilities to lazily translate strings and also fake a translation for extraction --- mediagoblin/util.py | 48 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/mediagoblin/util.py b/mediagoblin/util.py index ed7be841..b46c65d9 100644 --- a/mediagoblin/util.py +++ b/mediagoblin/util.py @@ -28,11 +28,13 @@ 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 @@ -94,7 +96,7 @@ def get_jinja_env(template_loader, locale): template_env.install_gettext_callables( mg_globals.translations.ugettext, - mg_globals.translations.ngettext) + mg_globals.translations.ungettext) # All templates will know how to ... # ... fetch all waiting messages and remove them from the queue @@ -494,6 +496,50 @@ def pass_to_ugettext(*args, **kwargs): *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): From db3e0583494f201c663c7d037410011da951666b Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 10 Aug 2011 10:50:42 -0500 Subject: [PATCH 112/118] Make it so that form fields and descriptions are actually translated --- mediagoblin/templates/mediagoblin/utils/wtforms.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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) %}
-
{{ field.label }}
+
{{ _(field.label.text) }}
{% if field.description -%} -
{{ field.description }}
+
{{ _(field.description) }}
{%- endif %}
{{ field }}
{%- 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) %}
-
{{ field.label }}
+
{{ _(field.label.text) }}
{% if field.description -%} -
{{ field.description }}
+
{{ _(field.description) }}
{%- endif %}
{{ field(rows=rows, cols=cols) }}
{%- if field.errors -%} @@ -64,7 +64,7 @@ {% macro render_table(form) -%} {% for field in form %} - {{field.label}} + {{ _(field.label.text) }} {{field}} {% if field.errors %} From 7960ac985f9b5a80063f21012d53793cb2d22dd9 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 10 Aug 2011 12:07:59 -0500 Subject: [PATCH 113/118] Converting all forms to use the "fake/null" gettext conversion function Gettext doesn't actually get run right in the form but we do need to wrap the strings in _() so stuff extracts :) --- mediagoblin/auth/forms.py | 2 +- mediagoblin/edit/forms.py | 2 +- mediagoblin/submit/forms.py | 2 +- mediagoblin/user_pages/forms.py | 9 ++++++--- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/mediagoblin/auth/forms.py b/mediagoblin/auth/forms.py index 1b6bc7c2..917909c5 100644 --- a/mediagoblin/auth/forms.py +++ b/mediagoblin/auth/forms.py @@ -16,7 +16,7 @@ import wtforms -from mediagoblin.util import pass_to_ugettext as _ +from mediagoblin.util import fake_ugettext_passthrough as _ class RegistrationForm(wtforms.Form): diff --git a/mediagoblin/edit/forms.py b/mediagoblin/edit/forms.py index 8052f482..2f3ed203 100644 --- a/mediagoblin/edit/forms.py +++ b/mediagoblin/edit/forms.py @@ -18,7 +18,7 @@ import wtforms from mediagoblin.util import tag_length_validator, TOO_LONG_TAG_WARNING -from mediagoblin.util import pass_to_ugettext as _ +from mediagoblin.util import fake_ugettext_passthrough as _ class EditForm(wtforms.Form): diff --git a/mediagoblin/submit/forms.py b/mediagoblin/submit/forms.py index ccb62a03..241d32dc 100644 --- a/mediagoblin/submit/forms.py +++ b/mediagoblin/submit/forms.py @@ -18,7 +18,7 @@ import wtforms from mediagoblin.util import tag_length_validator -from mediagoblin.util import pass_to_ugettext as _ +from mediagoblin.util import fake_ugettext_passthrough as _ class SubmitStartForm(wtforms.Form): diff --git a/mediagoblin/user_pages/forms.py b/mediagoblin/user_pages/forms.py index 8829b674..0489f76a 100644 --- a/mediagoblin/user_pages/forms.py +++ b/mediagoblin/user_pages/forms.py @@ -16,7 +16,10 @@ import wtforms +from mediagoblin.util import pass_to_ugettext as _ + + class MediaCommentForm(wtforms.Form): - comment_content = wtforms.TextAreaField( - 'Comment', - [wtforms.validators.Required()]) + comment_content = wtforms.TextAreaField( + _('Comment'), + [wtforms.validators.Required()]) From ad4aef3a67301cf941944981f50dc8734efb9b91 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 10 Aug 2011 12:08:14 -0500 Subject: [PATCH 114/118] Removing a tab. This is a tab-free zone! --- mediagoblin/user_pages/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediagoblin/user_pages/views.py b/mediagoblin/user_pages/views.py index 85a84db6..fb72a421 100644 --- a/mediagoblin/user_pages/views.py +++ b/mediagoblin/user_pages/views.py @@ -154,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'], From 369fd2f97214168eae0f8408c4006741b4de8d0a Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 10 Aug 2011 12:28:42 -0500 Subject: [PATCH 115/118] Ooops! We should do a fake ugettext passthrough here also. --- mediagoblin/user_pages/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediagoblin/user_pages/forms.py b/mediagoblin/user_pages/forms.py index 0489f76a..ce7bfaa4 100644 --- a/mediagoblin/user_pages/forms.py +++ b/mediagoblin/user_pages/forms.py @@ -16,7 +16,7 @@ import wtforms -from mediagoblin.util import pass_to_ugettext as _ +from mediagoblin.util import fake_ugettext_passthrough as _ class MediaCommentForm(wtforms.Form): From 9bc564ff53a7488ee3b6548dea73d5b43d234cda Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 10 Aug 2011 12:44:58 -0500 Subject: [PATCH 116/118] Minor change to indentation --- mediagoblin/user_pages/forms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mediagoblin/user_pages/forms.py b/mediagoblin/user_pages/forms.py index ce7bfaa4..25001019 100644 --- a/mediagoblin/user_pages/forms.py +++ b/mediagoblin/user_pages/forms.py @@ -21,5 +21,5 @@ from mediagoblin.util import fake_ugettext_passthrough as _ class MediaCommentForm(wtforms.Form): comment_content = wtforms.TextAreaField( - _('Comment'), - [wtforms.validators.Required()]) + _('Comment'), + [wtforms.validators.Required()]) From 5d2b00be871c09022446d863150bb77fe7b6e4b2 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 10 Aug 2011 13:07:09 -0500 Subject: [PATCH 117/118] Updating translations --- .../i18n/de/LC_MESSAGES/mediagoblin.mo | Bin 5330 -> 5453 bytes .../i18n/de/LC_MESSAGES/mediagoblin.po | 51 +-- .../i18n/es/LC_MESSAGES/mediagoblin.mo | Bin 0 -> 5331 bytes .../i18n/es/LC_MESSAGES/mediagoblin.po | 299 ++++++++++++++++++ .../i18n/pt_BR/LC_MESSAGES/mediagoblin.mo | Bin 0 -> 5337 bytes .../i18n/pt_BR/LC_MESSAGES/mediagoblin.po | 294 +++++++++++++++++ 6 files changed, 620 insertions(+), 24 deletions(-) create mode 100644 mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.mo create mode 100644 mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.po create mode 100644 mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.mo create mode 100644 mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.po diff --git a/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.mo index eaa79dfa4c8bed2a279beb08a59c418b57dc7ed6..d5024687f631db02c1be382230123cf93e96fc0b 100644 GIT binary patch delta 1067 zcmZ|MPe>F|90%~?|)GdOb@6)a9-!RPQ{pQc__j|v2m44CwsJg1K z93k>I6Ky4;(FUUZa1tJZci|vhti(H`L>myFfwgc9Zh;qJGt9##cpdJ5w_yyp$8N2A&kQXxCt&*`~bHjUV)~e8dS%U zlF+=DhOO`b%)xW9!rBKds&{;#p`Wu?+DEeS)T7 z6}mIVp~=$<+h7`QgQua%=T2f_CU6VZ!#fZsJ-~&*$Ix{C4w?o(Le!ud%>4*V!eej* zCgBaJ;0!d4C9#`!xCfSo8wNL)4%e+jj&~pJ={mTt3rF3M73$2WvSjF4I#4}~dn%9) z>-D)KEH|({Av*)*_Jx*jTNy3g()s!?k(PejR(w3iC5>2NI29<(rUZAm$^{;mMK{{l zF`|MA;W3+A%H~4j)waE>SubdzJ)RENJt{trrsA1lOL&=nNAWP^bi&eHl)gn5BJKUs z=bnQEe=PFSWfMH-xjY=td6tX9Z<|uxLxCIoY-?>%o>Q7D=9lM@TC(!I%@r3i)WY}i zRZDCpQNAY8K2qX&c$o`qPRZiS*z4XO0SySLmF2QrV9J?>hA}0)|Nk4srDk2rJ<4*K z*VDz%wS5s@ycvI5IGIzN4g9ucHkDTn$5cXHzORL=JK0&m*<^W6O4AftVwbp{*37C5 qq=O+1`)klFk3X0d7@7LjT8i+4eawqHloe^M*HuDxQ|TW)rRY1zngAC7 delta 597 zcmXZYODIH99LMqhFyk>8?`Pp!OpHfJni`aBlq3sAHfkPMvzUkOok>EoR+G&{_KFqB zV5JnR36ZiPalXX{^9Gtin*PUd1Nr6qe#X*5e7b;0@AJ-W`$@D~lgC%JCQ1(Lu3- zDV)LrZW+T_ti>Iy#zRy=F42ZrRE_UY1$st?d@>cGrSkv09YfSwWm2S(#ReO#xQil*&ZlVgfjr5Q+bYm8W&{8APfFrnoUM$8_RPSG)I6D%iZbW?%-DS3$+L94l zJhrwKx%&n@gYK?A9{Z%vjQ7kKzNi`Y#bbs?3oPq?ziw#5xy5KOVa8Vi;aDP=St$Ls RWIn9pw)9)|Mf%IW_6IX5Q>Fj_ diff --git a/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po index 065a28b8..14ef5b83 100644 --- a/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po @@ -2,14 +2,15 @@ # Copyright (C) 2011 ORGANIZATION # This file is distributed under the same license as the PROJECT project. # +# , 2011. # , 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 03:57+0000\n" -"Last-Translator: cwebber \n" +"PO-Revision-Date: 2011-08-09 19:03+0000\n" +"Last-Translator: elrond \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" @@ -20,23 +21,23 @@ msgstr "" #: mediagoblin/auth/forms.py:24 mediagoblin/auth/forms.py:46 msgid "Username" -msgstr "" +msgstr "Benutzername" #: mediagoblin/auth/forms.py:29 mediagoblin/auth/forms.py:50 msgid "Password" -msgstr "" +msgstr "Passwort" #: mediagoblin/auth/forms.py:34 msgid "Passwords must match." -msgstr "" +msgstr "Passwörter müssen übereinstimmen." #: mediagoblin/auth/forms.py:36 msgid "Confirm password" -msgstr "" +msgstr "Passwort wiederholen" #: mediagoblin/auth/forms.py:39 msgid "Email address" -msgstr "" +msgstr "E-Mail-Adresse" #: mediagoblin/auth/views.py:40 msgid "Sorry, registration is disabled on this instance." @@ -51,6 +52,8 @@ msgid "" "Your email address has been verified. You may now login, edit your profile, " "and submit images!" msgstr "" +"Ihre E-Mail-Adresse wurde bestätigt. Sie können sich jetzt anmelden, Ihr " +"Profil bearbeiten und Bilder hochladen!" #: mediagoblin/auth/views.py:158 msgid "The verification key or user id is incorrect" @@ -63,7 +66,7 @@ msgstr "" #: mediagoblin/edit/forms.py:26 mediagoblin/submit/forms.py:26 msgid "Title" -msgstr "" +msgstr "Titel" #: mediagoblin/edit/forms.py:29 msgid "Slug" @@ -83,7 +86,7 @@ msgstr "" #: mediagoblin/edit/forms.py:41 msgid "Website" -msgstr "" +msgstr "Webseite" #: mediagoblin/edit/forms.py:43 msgid "Improperly formed URL" @@ -103,7 +106,7 @@ msgstr "" #: mediagoblin/submit/forms.py:29 msgid "File" -msgstr "" +msgstr "Datei" #: mediagoblin/submit/views.py:45 msgid "You must provide a file." @@ -119,23 +122,23 @@ msgstr "" #: mediagoblin/templates/mediagoblin/base.html:22 msgid "GNU MediaGoblin" -msgstr "" +msgstr "GNU MediaGoblin" #: mediagoblin/templates/mediagoblin/base.html:45 msgid "Mediagoblin logo" -msgstr "" +msgstr "Mediagoblin Logo" #: mediagoblin/templates/mediagoblin/base.html:51 msgid "Submit media" -msgstr "" +msgstr "Medien hochladen" #: mediagoblin/templates/mediagoblin/base.html:62 msgid "verify your email!" -msgstr "" +msgstr "Bitte bestätigen Sie Ihre E-Mail-Adresse!" #: mediagoblin/templates/mediagoblin/base.html:72 msgid "Login" -msgstr "" +msgstr "Anmelden" #: mediagoblin/templates/mediagoblin/base.html:88 msgid "" @@ -149,7 +152,7 @@ msgstr "Willkommen bei GNU MediaGoblin!" #: mediagoblin/templates/mediagoblin/root.html:26 msgid "Submit an item" -msgstr "" +msgstr "Eintrag hochladen" #: mediagoblin/templates/mediagoblin/root.html:31 #, python-format @@ -165,18 +168,18 @@ msgstr "" #: mediagoblin/templates/mediagoblin/auth/login.html:26 msgid "Log in" -msgstr "" +msgstr "Anmelden" #: mediagoblin/templates/mediagoblin/auth/login.html:29 msgid "Login failed!" -msgstr "" +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 "" +msgstr "Speichern" #: mediagoblin/templates/mediagoblin/auth/login.html:42 msgid "Don't have an account yet?" @@ -204,15 +207,15 @@ msgstr "" #: mediagoblin/templates/mediagoblin/edit/edit.html:29 #, python-format msgid "Editing %(media_title)s" -msgstr "" +msgstr "%(media_title)s bearbeiten" #: mediagoblin/templates/mediagoblin/edit/edit.html:36 msgid "Cancel" -msgstr "" +msgstr "Abbrechen" #: mediagoblin/templates/mediagoblin/edit/edit.html:37 msgid "Save changes" -msgstr "" +msgstr "Änderungen speichern" #: mediagoblin/templates/mediagoblin/edit/edit_profile.html:29 #, python-format @@ -231,7 +234,7 @@ msgstr "" #: mediagoblin/templates/mediagoblin/submit/start.html:26 msgid "Submit yer media" -msgstr "" +msgstr "Medien hochladen" #: mediagoblin/templates/mediagoblin/user_pages/gallery.html:30 #, python-format @@ -285,7 +288,7 @@ msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/user.html:84 msgid "Edit profile" -msgstr "" +msgstr "Profil bearbeiten" #: mediagoblin/templates/mediagoblin/user_pages/user.html:95 #, python-format diff --git a/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.mo new file mode 100644 index 0000000000000000000000000000000000000000..043265a330a162bc23df295f3220a69d93155b07 GIT binary patch literal 5331 zcmeI0O^h5z6@V*XNEl3F;s8mApvGb4HQt$>wQ*v`-UY9-Hjcbz@$T9RNSK=LnwheD zy2f4IyB-NRapJ-O@e=_8q#(h;Na5!M5)vjSBqYQIE{KyQ1So(|lna9Ibs66epub8ruvEW%4rP8h!_=iOC|^EHwjrAnXr%nE9?ttU7LVKeOH0ebwie ziYz%0_RVddeSF%?v0`9jXVi&g;6tuspW3>9#uu43QRIs>*CBUFqEl<*pmVP~RO`vTz_>(*zAutkn=Er{R=ZRi-L>m_;0HF%gI;%epQj~-*!s5H+;)!e*q*f}YB+9a7Z@krZiE+DDvGoANc&|-B{%{#A7nl!SBI_cA{%LaOA zLRj}%tWL5px7wggWlDYH5k~61&1_vg<Yqg2VDsGXH2viB7&U9r9 z;%1S9%ZD}#gr-yE@_avTNhB>waod+yVV^1&M|f^qx+T8n#^U$QF+d&3H2fQD++cyr zn;ScmaJL6wN)Hojg4NsQX0}HL*lZ(}&yU9}^2Xwqg-h2{zt!ms4Qlx++7fbJOdwUedl@J?fRw#+yB_mNqhU;|~sD zftLp+kNQou-Bc?cao1qx5>7|9to!&Pm zd4!3VZIsJob-s$m?QyL>xntOyYFQ3U)Hi9*2DQvFRm+_8@>rQbl&eT#!?r_J)_~2U zD+Oz+%rhKYm6$$DcNUmspJgMlN>0eO(jd!slm@h_CZ`mXBI;L5y3{ZV)%c@Iv#dM; znj#XrU^>K2%2BDB)F>$@tSCAImyaQKxZDmlhGiT!1|u}99;}#NP%CmCNrsJd(ZSlm z*f0?@Fg*&7%qr^}r-uVTRked*J}RHz5|Ar)q^WtO{f?s($h-(c38Gq&Dx_A&^vDCz%YtoVb-_`F4F%kHZ>ckzh5w@M1WZ#GZ4wbfQFKSWsjG|L_r0&{$|t$x`7`6H zK_x2-V-=yT3#h4=G9PhiR6&bOAyub&`?}Oqz$*JTboys&%Y#A^ueq26C>>o2&Bhne zxYknA15TwpR?g(Il3R@adyG zd>U0mV%UD#r(Fnz4Yj2Yt~APfjeQzVaYMBAq1Mbyqji**K6I#kPq}5_#Dpxz`Ht7eE53hRKjxU4_6m}>v0}}j zt(S);b)l~(Pa?u4&ZGzQt_Kt7qBOm%%$&m5>No>^Sj^7Tx!RpaW<=`$KDqahB-?KPa37?&QC8kGf` z&lY*Nadc~*sMfVvV}YX-b8WWu(T>Y&r)_E}4wfK|+Irq}Y@%Du_couX{m-;ntEthr zDl@Us5mnCyIYhWgkN7}(EK3KK@wfa{gu-Nw?rf{iGTE!{v~-|^Iszdp}-T(haeiDqZ_vDLmUq-&DRb=BnGD)l`^2|7<24iY?~RUmB&zAjhZ TxQl%iofH3~aJ9HolB>T1MPD@2 literal 0 HcmV?d00001 diff --git a/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.po new file mode 100644 index 00000000..d2744d86 --- /dev/null +++ b/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.po @@ -0,0 +1,299 @@ +# Translations template for PROJECT. +# Copyright (C) 2011 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# +# , 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 03:38+0000\n" +"Last-Translator: nvjacobo \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 "" + +#: 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 "Reenvíe su correo electrónico de verificación" + +#: 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 "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 "." + +#: 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 "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/templates/mediagoblin/base.html:51 +msgid "Submit media" +msgstr "" + +#: 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 "" + +#: mediagoblin/templates/mediagoblin/base.html:88 +msgid "" +"Powered by MediaGoblin, a GNU project" +msgstr "" +"Potenciado por MediaGoblin, a GNU project" + +#: 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 "" + +#: mediagoblin/templates/mediagoblin/root.html:31 +#, python-format +msgid "If you have an account, you can Login." +msgstr "" +"Si tiene una cuenta, puede iniciar sesión Login." + +#: mediagoblin/templates/mediagoblin/root.html:37 +#, python-format +msgid "" +"If you don't have an account, please Register." +msgstr "" + +#: mediagoblin/templates/mediagoblin/auth/login.html:26 +msgid "Log in" +msgstr "" + +#: 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 "" + +#: 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 "Crea una aquí" + +#: 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 "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 "" + +#: 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 "%(username)s'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 "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 "" + +#: 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 log in 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/pt_BR/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.mo new file mode 100644 index 0000000000000000000000000000000000000000..ce967d59fec8ad8a2c8fa7d1c881116ea36eaf4b GIT binary patch literal 5337 zcmeH}O>87b6~_xOBpDXS2LTcglr62+)_TU{1+&ZS+F-A}iz9n6{@B&Bf~97rB#@9m3d#)*2*e=*1QOzc1OyZzp(qj;ey@AR_Imla zaq?(9KUa6v>-XOO)vNyf!xLXsd=~k84}TM{m(k(puWwN59Sr{l=itPIQVZ|{@Co?j zX#4}n6!kKE6MO|e2w#N{!~5P?jvt4QGd>GX!7K2ca0k8@J_X+bzXVyTz6Re0pMjr) z&%+@+%R{cg@4yxK5BMlNf1gs1z)dKA7|0UUg<>~_;?HNH`1fUaKYSL-`sZL5z6c+K z)7*R?T!G?m6N+CBUWWnB!koqtJA9h8J`|mwg-^g=jmDD%BRb!JlHXfU;%P#8z8{Uh2qhm+Ly7zQP~v+L ziq5}=m{hMq$-@L9i_Q=5BRW3@MdzpCHTVz@5uHB`MW=5;$;XeN`0;Zn`FaV8-QPm- z=TDHY`X@h<$5RiL>mP+(#%H1E{1qrVe;10szkuS`@1W@X2PnFn$Ed^=!Fl)vD9`^M zBoy^SDC>U;<+(3GvGYeLI{yVq9v;Mbu{RCH?>#8~-GY2o#E*t|py>Q{C^~-=VnY2G ziq5};qVxT4FLhppqVp%A===hd`9Fc`^mOiRmYSZO@n^j5XRaM5R$Vf>o7wi_RC)h) zo+UGWYU$|gOLJz46+IipMlB~j=Yx)1YA5wgmuK3vS}sq6_90FZomv}t9h`1jy>GL) z9owj;mQ!teCQh{Px;%-r$+CFg>NwR#x9vdpT+gP#>p>j4T=gN#TOoF-*DlpvHxPr7 z)83V)(vS_yxuI)#a*}tnNiu7qp|*EokEOQDbm+!jOVl;B5<7L-q%E7M%PwulSx@(k z_XC$j>M{$1)dpwEP$tKh2vT=#W+&CEOV0$|H8_1ZPY-SQuv&}aaF9+J#A!#Lo-WdL zJH#O&;l-r#`oC3Stw@fEqRe`)u81&KukGngN%>XROyX31G{)%R;@L`toEWmSFU}7| ziDOH~;mm5+w<+1MWF<-5KpffI-XPJHV%@+tbu)7VGS#S5PEVJ*wTviG7KB=F%OlXu zVuzIXZRU}tnFqOkhO{J77KOQ^%S+j3i^VN6cck1B*NI2wrNdN;Mw67=>Lots*cIwHg; zZ7axRwOy)ludd~t|0sJ+?Z}2{bxqo_UhS|=)ebwo*jC1)a;X$C9NAPw4>;Jm(y(TW zIwP@Vi|MlTc!Sw-SvHiYWQQDS4YK@LYrv~=a!NzVTitR=n;u4^8r>>A%ZeSKX(CbV zO%vUu9Tl!gkCJx6ioDs2!w3>b9BgkqEOZzThIm$9*fkxmc4a?O4C8Xq-rC+MFfr3J z9U70!D*79{hYdhiwY`2A7T50*Yen62ZfycU5XqS}=%r1nPrUtKTya=E{Y zhHWEtJ*EvC8n|oU)n^VG{|(#pgr;uTq=km!=&^n?sctyeb#79ZISEqdH%DEAPF6I= zQlY~GsHV3v*W%D9MQfQ{x=!uhb*ZC)mGO;z`fqECjY1Lc#}NfkB)Tw~jSiwwucf7X zl1h6llgVKvry5&qs8h}z_F^ z*Yg(|^@X!_K9vpQLv=SZsZR(qYv?O`8youC^5&Ypw6?jtzVVUe)z$5_ot-77rk$MP zt?pXWYv?UP$~!n^_4G<+J`*RgNoVx*D6GL?Pzz!=ZreL|$JM_pvVgU@ez?7|JvUR? zO7hGk)hqO0-_U8l82ZI?7da8+&f;{cm-PHhWpjOV?J(i\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 "" + +#: 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 MediaGoblin, a GNU project" +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 Login." +msgstr "" + +#: mediagoblin/templates/mediagoblin/root.html:37 +#, python-format +msgid "" +"If you don't have an account, please Register." +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 "%(username)s'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 log in 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 "" + + From 6d794f268bb1f5d3cc56c5f0dc902571d48ebeac Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 10 Aug 2011 18:31:29 -0500 Subject: [PATCH 118/118] Updating translations --- .../i18n/de/LC_MESSAGES/mediagoblin.mo | Bin 5453 -> 5641 bytes .../i18n/de/LC_MESSAGES/mediagoblin.po | 91 +++-- .../i18n/es/LC_MESSAGES/mediagoblin.mo | Bin 5331 -> 5708 bytes .../i18n/es/LC_MESSAGES/mediagoblin.po | 79 +++-- .../i18n/fr/LC_MESSAGES/mediagoblin.mo | Bin 0 -> 5406 bytes .../i18n/fr/LC_MESSAGES/mediagoblin.po | 305 +++++++++++++++++ .../i18n/nn_NO/LC_MESSAGES/mediagoblin.mo | Bin 0 -> 5217 bytes .../i18n/nn_NO/LC_MESSAGES/mediagoblin.po | 308 +++++++++++++++++ .../i18n/pt_BR/LC_MESSAGES/mediagoblin.mo | Bin 5337 -> 5414 bytes .../i18n/pt_BR/LC_MESSAGES/mediagoblin.po | 108 +++--- .../i18n/ro/LC_MESSAGES/mediagoblin.mo | Bin 0 -> 5629 bytes .../i18n/ro/LC_MESSAGES/mediagoblin.po | 314 ++++++++++++++++++ .../i18n/sl/LC_MESSAGES/mediagoblin.mo | Bin 0 -> 5487 bytes .../i18n/sl/LC_MESSAGES/mediagoblin.po | 309 +++++++++++++++++ 14 files changed, 1399 insertions(+), 115 deletions(-) create mode 100644 mediagoblin/i18n/fr/LC_MESSAGES/mediagoblin.mo create mode 100644 mediagoblin/i18n/fr/LC_MESSAGES/mediagoblin.po create mode 100644 mediagoblin/i18n/nn_NO/LC_MESSAGES/mediagoblin.mo create mode 100644 mediagoblin/i18n/nn_NO/LC_MESSAGES/mediagoblin.po create mode 100644 mediagoblin/i18n/ro/LC_MESSAGES/mediagoblin.mo create mode 100644 mediagoblin/i18n/ro/LC_MESSAGES/mediagoblin.po create mode 100644 mediagoblin/i18n/sl/LC_MESSAGES/mediagoblin.mo create mode 100644 mediagoblin/i18n/sl/LC_MESSAGES/mediagoblin.po diff --git a/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.mo index d5024687f631db02c1be382230123cf93e96fc0b..6bb69ce9bd9b38a4afc601b10d53fa76ef313738 100644 GIT binary patch delta 2265 zcmZ{l-)~e!6vsyh0_FD)qbT6nq(!LRQYng7B(xN&&^G>n5EG-!?#{hC?7efz%-lBi zWqt6)@Sw>)5lKu8Z-&H3LZabqO-xMGHxk2>`s9=N2N?aHxp#MqV$<%Yvv=mqIp6c+ z{xJH{*q58vv>z8joZlkE5ikK?20sHA!F%8+IJ#AcA@BmY5xfNM1}}qqz&5x8yaql2 zeo?N!1)s$FE?5Eo1YuI_+9t$0u(D0G1^yS$;NxTP7> z*$Mm%%}eL2=dCm@kv_9ibXJ;EGnuV7rEPdR{=o8NC(xoK|!?l{&$JR+gs9j1exvpCu>xuv~DwGZ;-}y{SyN$&ZU!tsGY8 za=YTSR|z%QIqa<|tF(8tq-?BeeD#dZVk@sSwd!2hN~ac8u3YK1Rk3#7YMe0@@#bI! zAL`ADc(pVd9BB2!?R>TM@bJjN+$n2hst&nTs!>ha8a#krr_;{bZAaUAN{#+e_@`-Z z@dB}?axSYS&~POu+46cR_%T+5$gxCO5-ZbCG~KYuIYrpZs-<*M)oM0dh6B{S9Ysc& zsiC2fg9utjx-PxW%zL>_4mojgQ5h2?g|x08d90e<+tyQV8bR}!UdnP4yO5J*6%)i6 zqn0$L(3VB%T;0BBTAEZP$m{XL@#TEk`{X_@C23ob z+*7Lkln4olLqe*Z;T@Bk=DRn#ccSpLBX>-$3(9x?TED+tMT1;cEzb^%)yfr>x*(HM zg$EwGYEkT=m7DHkcI}*LO6+@AY{I1(*J4Dd8Oe5#k%HsZ?BCwXhSo~1mmm}HQMEUD1Ee7=tMi% zvYeyi3GL423fW^7q0V^opv4d_mlw%ADgvNN%Wy{|I*s&jwm(e^@- zyr5d)1IF|9KiAC+TyO5?zT>CnfeMnthu#j5?YWgcql6SBX)}&+t=>o?Aw|d4+>$2 z4h3D}d8r5@i;f9OmktJ2w+`JZx)c;dm%0RX>ia^6$9#Uj;mz;&d+%2N+rFii*!)%@ z#KdMH_Te-R;e9-g@7D93twJ=CpT#6jVmp>_3(jB)Z{iNTi)mb0&);Ad`DbJa(I!OP z(kA9@EF50fuoq`>5l>+!Rg37OgU>L7uQ7oi*8PIp$$y|7C{A}mkwv>NhdsC-t9Tym z{+H=_A-V}Z^1(Lvi<>Y-l}U84AAS6PG8`aZL3fxPDc{q3>p^8SbZb*? zKGK!t3oWq*^QZB*DYbT+ruY%_sx@z><1&^%X`HqL%d7E64W=VqFxQ(pOf@}dR+|=` zwHvyuf*D;=T83AYcd@DhZFJ&&Y9kS2o{j>yV(w%*&4WzJ%qGL6@WVcyzazmW2CNC*f h7DX?Ll$=swwDvgi>`1oAMpf7I^GC~p3PZI^`~x9tmi7Pu diff --git a/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po index 14ef5b83..30c55e21 100644 --- a/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po @@ -2,15 +2,17 @@ # Copyright (C) 2011 ORGANIZATION # This file is distributed under the same license as the PROJECT project. # +# Rafael Maguiña , 2011. # , 2011. # , 2011. +# Jan-Christoph Borchardt , 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 19:03+0000\n" -"Last-Translator: elrond \n" +"PO-Revision-Date: 2011-08-10 23:20+0000\n" +"Last-Translator: JanCBorchardt \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" @@ -37,32 +39,32 @@ msgstr "Passwort wiederholen" #: mediagoblin/auth/forms.py:39 msgid "Email address" -msgstr "E-Mail-Adresse" +msgstr "Email-Adresse" #: mediagoblin/auth/views.py:40 msgid "Sorry, registration is disabled on this instance." -msgstr "" +msgstr "Registrierung ist auf dieser Instanz leider deaktiviert." #: mediagoblin/auth/views.py:55 msgid "Sorry, a user with that name already exists." -msgstr "" +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 "" -"Ihre E-Mail-Adresse wurde bestätigt. Sie können sich jetzt anmelden, Ihr " +"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 "" +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 "" +msgstr "Bestätigungs-Email noch Mal senden." #: mediagoblin/edit/forms.py:26 mediagoblin/submit/forms.py:26 msgid "Title" @@ -70,19 +72,19 @@ msgstr "Titel" #: mediagoblin/edit/forms.py:29 msgid "Slug" -msgstr "" +msgstr "Kurztitel" #: mediagoblin/edit/forms.py:30 msgid "The slug can't be empty" -msgstr "" +msgstr "Bitte gib einen Kurztitel ein" #: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:31 msgid "Tags" -msgstr "" +msgstr "Markierungen" #: mediagoblin/edit/forms.py:38 msgid "Bio" -msgstr "" +msgstr "Biographie" #: mediagoblin/edit/forms.py:41 msgid "Website" @@ -90,19 +92,19 @@ msgstr "Webseite" #: mediagoblin/edit/forms.py:43 msgid "Improperly formed URL" -msgstr "" +msgstr "Adresse fehlerhaft" #: mediagoblin/edit/views.py:54 msgid "An entry with that slug already exists for this user." -msgstr "" +msgstr "Diesen Kurztitel hast du bereits vergeben." #: mediagoblin/edit/views.py:75 msgid "You are editing another user's media. Proceed with caution." -msgstr "" +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 "" +msgstr "Du bearbeitest das Profil eines Anderen. Bitte sei vorsichtig." #: mediagoblin/submit/forms.py:29 msgid "File" @@ -110,15 +112,15 @@ msgstr "Datei" #: mediagoblin/submit/views.py:45 msgid "You must provide a file." -msgstr "" +msgstr "Du musst eine Datei angeben." #: mediagoblin/submit/views.py:48 msgid "The file doesn't seem to be an image!" -msgstr "" +msgstr "Diese Datei scheint kein Bild zu sein!" #: mediagoblin/submit/views.py:96 msgid "Woohoo! Submitted!" -msgstr "" +msgstr "Yeeeaaah! Geschafft!" #: mediagoblin/templates/mediagoblin/base.html:22 msgid "GNU MediaGoblin" @@ -126,7 +128,7 @@ msgstr "GNU MediaGoblin" #: mediagoblin/templates/mediagoblin/base.html:45 msgid "Mediagoblin logo" -msgstr "Mediagoblin Logo" +msgstr "Mediagoblin-Logo" #: mediagoblin/templates/mediagoblin/base.html:51 msgid "Submit media" @@ -134,7 +136,7 @@ msgstr "Medien hochladen" #: mediagoblin/templates/mediagoblin/base.html:62 msgid "verify your email!" -msgstr "Bitte bestätigen Sie Ihre E-Mail-Adresse!" +msgstr "Bitte bestätige deine Email-Adresse!" #: mediagoblin/templates/mediagoblin/base.html:72 msgid "Login" @@ -145,6 +147,8 @@ msgid "" "Powered by MediaGoblin, a GNU project" msgstr "" +"Läüft mit MediaGoblin, einem GNU-Projekt" #: mediagoblin/templates/mediagoblin/root.html:21 msgid "Welcome to GNU MediaGoblin!" @@ -158,6 +162,8 @@ msgstr "Eintrag hochladen" #, python-format msgid "If you have an account, you can Login." msgstr "" +"Falls du ein Konto hast, kannst du dich anmelden." #: mediagoblin/templates/mediagoblin/root.html:37 #, python-format @@ -165,6 +171,8 @@ msgid "" "If you don't have an account, please Register." msgstr "" +"Wenn du noch kein Konto hast, registriere " +"dich." #: mediagoblin/templates/mediagoblin/auth/login.html:26 msgid "Log in" @@ -179,19 +187,19 @@ msgstr "Anmeldung fehlgeschlagen!" #: mediagoblin/templates/mediagoblin/edit/edit_profile.html:35 #: mediagoblin/templates/mediagoblin/submit/start.html:32 msgid "Submit" -msgstr "Speichern" +msgstr "Bestätigen" #: mediagoblin/templates/mediagoblin/auth/login.html:42 msgid "Don't have an account yet?" -msgstr "" +msgstr "Hast du noch kein Konto?" #: mediagoblin/templates/mediagoblin/auth/login.html:45 msgid "Create one here!" -msgstr "" +msgstr "Registriere dich!" #: mediagoblin/templates/mediagoblin/auth/register.html:27 msgid "Create an account!" -msgstr "" +msgstr "Neues Konto registrieren!" #: mediagoblin/templates/mediagoblin/auth/verification_email.txt:19 #, python-format @@ -203,6 +211,11 @@ msgid "" "\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 @@ -220,17 +233,17 @@ msgstr "Änderungen speichern" #: mediagoblin/templates/mediagoblin/edit/edit_profile.html:29 #, python-format msgid "Editing %(username)s's profile" -msgstr "" +msgstr "%(username)s’s Profil barbeiten" #: mediagoblin/templates/mediagoblin/listings/tag.html:29 msgid "Media tagged with:" -msgstr "" +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 "" +msgstr "Atom-Feed" #: mediagoblin/templates/mediagoblin/submit/start.html:26 msgid "Submit yer media" @@ -239,40 +252,44 @@ msgstr "Medien hochladen" #: mediagoblin/templates/mediagoblin/user_pages/gallery.html:30 #, python-format msgid "%(username)s's media" -msgstr "" +msgstr "%(username)s’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 "" +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 "" +msgstr "Überprüfung notwendig" #: mediagoblin/templates/mediagoblin/user_pages/user.html:40 msgid "Almost done! Your account still needs to be verified." -msgstr "" +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 "" +msgstr "Wenn sie nicht ankommt:" #: mediagoblin/templates/mediagoblin/user_pages/user.html:52 msgid "Resend verification email" -msgstr "" +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 @@ -280,11 +297,13 @@ msgid "" "If you are that person but you've lost your verification email, you can log in and resend it." msgstr "" +"Wenn dir dieses Konto gehört und die Bestätigungsmail weg ist, kannst du " +"dich anmelden und sie erneut senden." #: mediagoblin/templates/mediagoblin/user_pages/user.html:76 #, python-format msgid "%(username)s's profile" -msgstr "" +msgstr "%(username)s’s Profil" #: mediagoblin/templates/mediagoblin/user_pages/user.html:84 msgid "Edit profile" @@ -293,6 +312,6 @@ msgstr "Profil bearbeiten" #: mediagoblin/templates/mediagoblin/user_pages/user.html:95 #, python-format msgid "View all of %(username)s's media" -msgstr "" +msgstr "Alle Medien von %(username)s anschauen" diff --git a/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.mo index 043265a330a162bc23df295f3220a69d93155b07..dfd3a1bcc4775ce5f49a4adbf1ba9c481103fe50 100644 GIT binary patch delta 2081 zcmaKs&ug4T7{|wk#HvlwO=4_HrL$J^12>!a16x8hhPnppHU!%UB2s4Oo$L_USO-r(O~@(m8t4`L3iSKRley$CPVPMzIDL5V<;A5} z7Wbx)R3DpO7j{!B8VUNsY~qB~tS#o;j3eiaj)j(y@$a&ahv!oA0i(jj-J6s%CuvLY zmMPPV3FV2M5v~jd;+Src= zZZuWQQK?;Q6V9d4!qF{e8Er8>Ags!!M%Gfq$C*gaGAFvX7&&JJLnu}xQvPRwZ71Db za{I?MlV|7p{FmHcvy$FhS&{Izl^FZcvjO+!gdSJyTwlrv`3d<1+ zoc&UoJyt1}w-SLHSRr&rGOJ8>e{^b*agJ4cc7eT7^}+Qct|cCtMJAlPP5gUis4J&dd!% zMJy37W=kaK*35NNrfGtUdp4nUc&pDh4z+4PLT;qyGLkl{m9~fSi)X)Dcc z-eP1$1=(RkNYj+K81(M*k`L>9!(Xc1u)@0_aS5~16r#r_Vx8{pe|CwflDa|T=vcOm zLp9JG2yg-sis1XPuc8gFAP13MA6rNd>=LS%AM6)eH4?7H8dAaO$QZW`Sr#ox&z2TDV>+#_;`A!%Lp0_yl zeT*UK?mIEmeSe%C9CbANWBhEoQ8bL7Z|J_5=}_UK!}|(arR&y&eh7uxLg%w29PKda z9u?)^$D25GVX#Lp+8A>6Ch7~9otl_VF<#-!{~s>imd;P=p01d=bfsl5GV)SL-9#rc qTgh)0CQUxMD)cH$;afeQyzTDICZ@P6lTCs7u`&>wc6MvxxxWE@Cv#N* delta 667 zcmXZXPe>GD9LDkAx^BB`+q&bdmL@feuGF^q2g#x!kPcCz2x)mLN8NDK-C<{Ti=bGD zE}ef+L`4uCx)|9;myRZMkRUvD>*OH_szaS5zb^^zyr1_Sc%S!qH}acD-z1vq9U?Nd zPh@B}`=i}(dcusbOd#!DE(E11GrY{LpNP`HfMOR;lw5 z%5)bG;B9<}6PV(balD2mXa#Lt$36HJBlr!?#an0!g{=Pt)3}#?7Ta+Mm+?Gy;ybG@ zGRWdHKPV;tuo;u2Tt*x7cw^T`IK=)VngV~&oZCk8;sovJAf0G(a@dOlXdWn?0*KsSgwH3`p%F$7OezADdYdnq2g!EbJx{gOPnzs(> zooKIf&MQ}aHz=&Fx-#ho!7a~U2%J(as5&LLI=`qdtcvDhT{_x!R=wB*?Y57r9beK7 tJF73_W7dRMn)3_JLcy61YOY`O^lyAb->1(s^^4wX@6%r`MLpJ$*#Ul;X@LL$ diff --git a/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.po index d2744d86..0a12586c 100644 --- a/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.po @@ -8,7 +8,7 @@ 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 03:38+0000\n" +"PO-Revision-Date: 2011-08-10 20:30+0000\n" "Last-Translator: nvjacobo \n" "Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/mediagoblin/team/es/)\n" "MIME-Version: 1.0\n" @@ -28,33 +28,36 @@ msgstr "Contraseña" #: mediagoblin/auth/forms.py:34 msgid "Passwords must match." -msgstr "" +msgstr "Las contraseñas deben coincidir." #: mediagoblin/auth/forms.py:36 msgid "Confirm password" -msgstr "" +msgstr "Confirme su contraseña" #: mediagoblin/auth/forms.py:39 msgid "Email address" -msgstr "" +msgstr "Dirección de correo electrónico" #: mediagoblin/auth/views.py:40 msgid "Sorry, registration is disabled on this instance." -msgstr "" +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 "" +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 @@ -63,19 +66,19 @@ msgstr "Reenvíe su correo electrónico de verificación" #: mediagoblin/edit/forms.py:26 mediagoblin/submit/forms.py:26 msgid "Title" -msgstr "" +msgstr "Título" #: mediagoblin/edit/forms.py:29 msgid "Slug" -msgstr "" +msgstr "Ficha" #: mediagoblin/edit/forms.py:30 msgid "The slug can't be empty" -msgstr "" +msgstr "La ficha no puede estar vacia" #: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:31 msgid "Tags" -msgstr "" +msgstr "Etiquetas" #: mediagoblin/edit/forms.py:38 msgid "Bio" @@ -95,11 +98,12 @@ 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 "." +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 "." +msgstr "Usted está editando un perfil de usuario. Proceder con precaucións." #: mediagoblin/submit/forms.py:29 msgid "File" @@ -123,11 +127,11 @@ msgstr "GNU MediaGoblin" #: mediagoblin/templates/mediagoblin/base.html:45 msgid "Mediagoblin logo" -msgstr "" +msgstr "Mediagoblin logo" #: mediagoblin/templates/mediagoblin/base.html:51 msgid "Submit media" -msgstr "" +msgstr "Enviar contenido" #: mediagoblin/templates/mediagoblin/base.html:62 msgid "verify your email!" @@ -135,7 +139,7 @@ msgstr "Verifique su correo electrónico" #: mediagoblin/templates/mediagoblin/base.html:72 msgid "Login" -msgstr "" +msgstr "Conectarse" #: mediagoblin/templates/mediagoblin/base.html:88 msgid "" @@ -151,7 +155,7 @@ msgstr "¡Bienvenido a GNU MediaGoblin!" #: mediagoblin/templates/mediagoblin/root.html:26 msgid "Submit an item" -msgstr "" +msgstr "Enviar un item" #: mediagoblin/templates/mediagoblin/root.html:31 #, python-format @@ -166,10 +170,12 @@ msgid "" "If you don't have an account, please Register." msgstr "" +"Si no tienes una cuenta, por favor, Regístrese ." #: mediagoblin/templates/mediagoblin/auth/login.html:26 msgid "Log in" -msgstr "" +msgstr "Conectarse" #: mediagoblin/templates/mediagoblin/auth/login.html:29 msgid "Login failed!" @@ -180,11 +186,11 @@ msgstr "El inicio de sesión fallo" #: mediagoblin/templates/mediagoblin/edit/edit_profile.html:35 #: mediagoblin/templates/mediagoblin/submit/start.html:32 msgid "Submit" -msgstr "" +msgstr "Enviar" #: mediagoblin/templates/mediagoblin/auth/login.html:42 msgid "Don't have an account yet?" -msgstr "" +msgstr "¿No tienes una cuenta?" #: mediagoblin/templates/mediagoblin/auth/login.html:45 msgid "Create one here!" @@ -192,7 +198,7 @@ msgstr "Crea una aquí" #: mediagoblin/templates/mediagoblin/auth/register.html:27 msgid "Create an account!" -msgstr "" +msgstr "Crea una cuenta!" #: mediagoblin/templates/mediagoblin/auth/verification_email.txt:19 #, python-format @@ -204,11 +210,13 @@ msgid "" "\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 "" +msgstr "Edición %(media_title)s " #: mediagoblin/templates/mediagoblin/edit/edit.html:36 msgid "Cancel" @@ -221,26 +229,26 @@ msgstr "Salvar cambios" #: mediagoblin/templates/mediagoblin/edit/edit_profile.html:29 #, python-format msgid "Editing %(username)s's profile" -msgstr "" +msgstr "Edición %(username)s de perfil" #: mediagoblin/templates/mediagoblin/listings/tag.html:29 msgid "Media tagged with:" -msgstr "" +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 "" +msgstr "feed Atom" #: mediagoblin/templates/mediagoblin/submit/start.html:26 msgid "Submit yer media" -msgstr "" +msgstr "Envíe su contenido" #: mediagoblin/templates/mediagoblin/user_pages/gallery.html:30 #, python-format msgid "%(username)s's media" -msgstr "" +msgstr "Contenido de %(username)s's" #: mediagoblin/templates/mediagoblin/user_pages/gallery.html:51 #: mediagoblin/templates/mediagoblin/user_pages/user.html:30 @@ -250,30 +258,33 @@ 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 "" +msgstr "Verificación necesaria" #: mediagoblin/templates/mediagoblin/user_pages/user.html:40 msgid "Almost done! Your account still needs to be verified." -msgstr "" +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 "" +msgstr "En caso de que no:" #: mediagoblin/templates/mediagoblin/user_pages/user.html:52 msgid "Resend verification email" -msgstr "" +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 @@ -281,19 +292,21 @@ msgid "" "If you are that person but you've lost your verification email, you can log in and resend it." msgstr "" +"Si usted es esa persona, pero usted ha perdido su correo electrónico de " +"verificación, usted puede reenviarlo acceder." #: mediagoblin/templates/mediagoblin/user_pages/user.html:76 #, python-format msgid "%(username)s's profile" -msgstr "" +msgstr "Perfil de %(username)s's" #: mediagoblin/templates/mediagoblin/user_pages/user.html:84 msgid "Edit profile" -msgstr "" +msgstr "Editar perfil" #: mediagoblin/templates/mediagoblin/user_pages/user.html:95 #, python-format msgid "View all of %(username)s's media" -msgstr "" +msgstr "Ver todo el contenido de %(username)s's " diff --git a/mediagoblin/i18n/fr/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/fr/LC_MESSAGES/mediagoblin.mo new file mode 100644 index 0000000000000000000000000000000000000000..a7daf2c91cd50bd2dea5da7b64a63ece9b1ac78f GIT binary patch literal 5406 zcmeH~TZ|i58GsKEXlu$X<=RksHqvT0itW9S(BLFXvgsxy+D(`2CQ&JBHTE3alZ@w> zIcL0a6nLiafIw8LDv&BJ68k`S;;j-wEAhfZc>uu^2m}%cP=%DDJ^|l9W3L@25RiC4 zRad+F`OM7u=f9udzuLR!ImOR${yxm#o;S;C^z-MpDD{4pe}Qvw&mN@?!$a^KyfRq- z0P=|X6MP$d1-={p4eo{ay|q|B3?F8F5IzV`!w_z`#&z5{*{vQ<3~-vz$`KL@`D z`|uzyIS;=Bm*Fk=KKR&uN<9SEpy&}mwx~7~xd{|~Za~rR%kX~qB9#5xunm6*KLlsF z`6yh5qHi6FUKU=09+u%xpxE^qT!0TfP`rNyzLWJTl>JXZd2btvoIad}pNHayZ$pvy z5){4v2ob4XfnwKdP{RjH_!L&)K6n_4U7v!F!4MvWUxy!rzl2Z2zd}T)^|u$#e;R)O zO-gm3#Q71NBym0jCC(u{2VWShe-94v6FAex(D0=)9ia&k} z<+)d(`0)X5%Kp9ZDx8H9=dZyL5Z`566e!UF}-&Y{N>IMF3coT}BUxuRBFCe1Tt5D*6 zKQEOypMc`u8&Lf992C87L5aiPp_-n}oJo_QV-C0(r@N_bMzK-H0^Lqc^Z5Sa{xew` zA8`AZ#-Bbm7c8-(W5OsFzLbDXtw5T_;x``TQK9Fm$g)xI4$Eml|6a%9!1AZeIbow7+YN;|q6 zIJaffP@Q6!j!4uC5+T6{uZhdg*m@i#>XQ*dN1Mx~5PK6>SG1Ius8Mi2IUd#95rF z&L+Acs*nt!H4bfSicMH082;Bys=$O(Di5vpL94}nN&8xHsg*|?Z?>#jA7<$A14WqO z-~}WZST9axw$;~3Q<*?w)QWLWs(?CrtWq_G1${eGI@4x|B~bp zCSEZOFAu8=MKtb=YjNjahP|pbq+lBDAZZz=HYiiIL8a$q6*!_?L<$?miYo5`yQM1) ztDNTRq=2_L&+NLVoQ@AMxz?sDl|*;3eYr> zFmgeixJf(8Rg)ei?Svg!y%YHX#E!_D&TyH>VYuj{S#e=AXgReh^++-d(?vUDIs?N* zOebj3c;uDHXP8vZS%H1v)lMDo4Ug^b=kxXVknAE^qVPl+1j?XQ@Zepmpp%R&^72} zd1EXhG&+E)dOfub4vivcjUbb*Q@wLvVrgK7eM6o8-QK)VNa9`;k^s4*bD`q zTDrrjw8z4k99D9wQDS|aaPCltQBu~TebL+yDM2IE=ua+>eGw`*r2=m|9A|hl7uGL! zc14CuA-^DU-uHlV1h?1kyY*J_Z@ys_XM@G+y;!TrQR;Y=P^RK3l)FWRy zV-iD^8@%1G>E)nqVm)8|c=cnYH|8aVwj_(q^}7vBJKN^E&N%Tb&4@0w!pC%YF=|9p z>ii6?|9YioGilb@73LFWvEh!1sp*MC?`%tn{qM)7Wt+&m%eOT(hNo^Zx-ve|RC?Ak z*c8TS`o%OYMueioA<1JRgYPI3 zW$k8|%k%q`(9TU?9!&<*v%@2LI85cwariZ4e!pJKLqqWr@NvO~OkBt1-C<*THgBmz zc@%O&4>Re1J6h!%uhF$6Jrjoh@lUkxtjM&jz|xa fWJd42IN8eHn9)WvT2U*MMo~qBiI4$1FDLa+wTxLT literal 0 HcmV?d00001 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. +# +# , 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 \n" +"Language-Team: LANGUAGE \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 MediaGoblin, a GNU project" +msgstr "" +"Alimenté par MediaGoblin, un projet GNU" + +#: 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 Login." +msgstr "" +"Si vous avez un compte, vous pouvez Connecter." + +#: mediagoblin/templates/mediagoblin/root.html:37 +#, python-format +msgid "" +"If you don't have an account, please Register." +msgstr "" +"Si vous n'avez pas un compte, s'il vous plaît, vous inscrivez." + +#: 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 "%(username)s'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 log in and resend it." +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:76 +#, python-format +msgid "%(username)s's profile" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:84 +msgid "Edit profile" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:95 +#, python-format +msgid "View all of %(username)s's media" +msgstr "" + + diff --git a/mediagoblin/i18n/nn_NO/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/nn_NO/LC_MESSAGES/mediagoblin.mo new file mode 100644 index 0000000000000000000000000000000000000000..8911785ff9b8003d2146b82adccd80693b97cafc GIT binary patch literal 5217 zcmb7{OKc=Z8Gs8|2uu-+c*cl0ObAE_-)8i^$PqH{2}}%`~@7t$2jB~ zd<`zcf56Yer|wYdKDZ7=4g*=DdQkRFp~&+MDDr(5ejNS?%KD$e9{er*Je=X>m*6rK zdD~Fr%HTFE;4J(d6usVq3-G>Al;^L)PcmMEvi_@3&h0_jXADomm!a6hU;)`e zy#gg}UWel6pTQjd8Wu3%;RX0(D0=-FBC2{ABZ}TLP|jPaxDM}Oyba~t9^_Yz_z-)2 z3yPlKfjjVZDD&^(L*nldD0W|lBHtBw6K=x=_;V=v@HW)&pHTdGKgJh19;*1|iUAaP zpM*`g45#7Oq1g9(Q0({?#5L*oL^9%3+h{)9*CoHjlfp4V&shs_nqU ziFUngl!V&kdAw_NoNA-Hc25toflUji_u`_*Q&;4pP7!CR(^;x}*`90|X4+-tR%wxs z%eh6*aB?z=v`KPn!m+k5#g3i2S+0v-?6hdzRLgOuE}FDs6Lm35yKz3yL*v|DmWS#h z3k$0a!j!5^)sIk8_iS#b)Jm2Q{qY8V&$=9EF4M*8H8H`j|rTqGFW zPb#0^)(flNI3^5p>zukIj=6m8rd}75U(VV|oT@7^n;tBlZ8Wfn!AiR#{8&73WJ#^g ztj>lu#Tpi?BuTa>g512ZhM^mN-JWghcAo8FsaB(La;EfK$A|~YfKXSvas+;}?1RaN zHg~wDJu2k+qnIU;u<+YWE-&3a>lb&h+(ft~Srk{>PcJb*9EmjS8*0p8L0mNJlL@%v z4w%)$#2RPyW+v) zFocx>4X2cMtu9Oyv0lQyRX&Qmx3OlFsf{{9>wieX5mp|UqSI@t zjckwD40U^apnk6?hON0dAAIFj%`A_mmnI@fl$a&&_FYk}iPDkGots{gI6}o8+bLwS zx>0)LxL?aR@9Xxa+LVOp^h_FAr#4AbwMnM?v@#Aam!3k0iKOy1U_W)GV9okC!?0zE z>9O=^f!WORd@Nc?hD?+OS$?E6AXPazrJ#&Dy>dyH8b+b2UX_yNJ_9sGB#fPD<2NZs zUNosuQchSgY7gR~g6I(!c2Ez!AL_vv$;t;?CUR;^@{wSu!$mu52bE&7&A>zy9+~Cq z8`(nwP*v?vEajzF^x>ZO4>h zLjljahw99J;lE|u4%O5xn{@D@2zsR6OsQL0*2}UfUD~9OIDfvX8dNf07)uWwTtH2| zk!Kw)jnZiyGm@&)Jiac?C}5?2wM-wh)+Y)rEfH$e+X@nZ}$$8(Gu5we#6xo353~Tz$#^nls@l7ezl!OYd zh5`0>DA_t{z{U+b%<>{wcTpS$%cIBzTUkq2`PpXN>}DBG9X3;2%pr>G=q9s$E_QBY zU1Q_=R^ab7&IwkyAzFHIeqkY)Kf|wHTx^|wJeWT{KhMPAhTV-to+I-X=Jn!2Yw_{N z=J{!?8CL{bxk+7unt2O*(Ft(vpUc8HZDxbeB+o{2jWr36CFZBmhzJh0tQoX)n!b4L zx<0qI_G}VMm04HA2-JHYlgE0e$14lA~4w(Ylgfx}oFE zkUiE7Wy92HG}qlX-$cKJhLX>SUh(ys11xQ+s>>hKpHZr;w& zlgHH;XnkVY>WJBquWVK-SM7W8naTBs#9c$&TH2<%8I2P*DO($nxNC_VF@TQy$fg}1 zxo>g&Drs`cf36aV{4Y&r*T#vFUMKI{#0-aq1?vYrZNR*;%aLG{CI=clz(n3FN!-T< zEAmx=-hB5!^8dNZv1G=+dv4d~1Vo|RvHh~q*_5VJOjSEZO+J})kawHUh@9_4lbNPg zFwx;$JIJ9bJ-Ns8QQye7F#>azrvwszb^4dZVdA{982H!q7R2G?CWnsxVjREwWK!YKERsN z-caJy^1e;gmanc822`6nNGAP_FUq=`Go1WBrLNd`U>)58?NQRF!zl~Xu^#8Svc=, 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 \n" +"Language-Team: LANGUAGE \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 MediaGoblin, a GNU project" +msgstr "" +"Driven av MediaGoblin, eit GNU-prosjekt" + +#: 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 Login." +msgstr "Har du ein konto? Logg inn." + +#: mediagoblin/templates/mediagoblin/root.html:37 +#, python-format +msgid "" +"If you don't have an account, please Register." +msgstr "Har du ingen konto? Registrer deg." + +#: 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 "%(username)s's media" +msgstr "%(username)s 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 log in and resend it." +msgstr "" +"Viss dette er deg, kan du logga inn 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 index ce967d59fec8ad8a2c8fa7d1c881116ea36eaf4b..4dc4ab5f772ebae42247eb44feec1c54b2d3d495 100644 GIT binary patch delta 2117 zcmZ{lPiS049LGm#lbR-JW0W+_p|e6YHObc0s*r|gTthDXlbS!E2lbctW?x3$o7v2J zZ&Q14>rK3PSSaX02#TRXQAj;{TD)lSPz4bb_2xwsy!7bzH*d391BUQE@6F8bH^2Y; z)0t17{G$KC#)uH&l>s5nf}7wpxDB2IzXzWIdj^H*182b_;39YoTmp}SZ-Dq0n|M45 z-T)ygZh|MkPry^)SK!0oFJLeD_n>HaMfb6A4GTx0^*!(%@DkV$?G^AzVIH%7yJeega5Smdxr``2EYxhKMt}%?+-PEcmgktQQS1BXG5^aMByRzQxxjSw#^@GdwC zegH~vw|(FTa17r+gM7h#@Hw#O5g{&sb0B+i9gM*b!H2+JgbS}?h{M+Wb#P?2HlTZ( z$vb)-U=z)H{swPn(6R4P+s900g zE+HpVWj3(kG~V;1Z9s_^l>es9T$jYWHX%s^6J(~6!pi5xLZ~x{)x3wrmkBN0OjM}p z^-O0mJQWKKKF2%7Nj9)M6k@@I%Bj|l18*wFu>)pbW zeSJ&hNK&Tj>`o_!U6KXx8)-9pmgc0APAOB@ zt?j9p%A#}ae*--!Yt|u~b?chn^gK51$a(25flg8mUV3!FWP9hE+s98dDwJBEKa_;i zb_T)c1L7hZRXlN3ykYZ_@PU^ZMUe{Smd-Y2_<+xn--?QyEc+7jPcFVX3It^?b(E7% z%VNoHA-kbme@o6%^KS2J4R#rED9ZO>*u<6p*^pJM+wnr4;5@~g@gxt)w00;J%W)ow z`8dj3w-Wg#>cpu?qc3c}U)kIljiLmMO;nLl^3}~T+{~4?eXr|6WaTB526B zBV8Ojv7E0bI+J81N;+w;%A|E0t>VAn2U?-MutHIaxKhznV?`s^2veqTmm6Ov6se>+ zIDE1(FM|!V19#(IApH1|h$KgC-zl~hGeN^G=Tv5q?v>W~glATU2>a8Re)~O_YAkeSh4c8lMzR|e! zP$Z63^Dx`PpLTj4X0@A^YiiF&JXThWGAv<~ eO!;#50W@hz;8!7$Ri(nV%SBOPPVks0iGKm-qK8lb delta 557 zcmXZY!7IaI9LMo58{70_8yiM32ZfQXtZ5ufo1$$8GtweI?cgA^?Z81lCx3xjZloNA zT_|$nL=KB^aZy^iy0~~h@$2b%J>Ory=lgt~-*Mkd@2OU8G>gc5y~rSLU=R;*3ZMS# zt_Bek^&Hk>1|7JJjhO$hcd?DSh#H<_3*KM{J|ivV8_j68iy?LvHl}U#;1(7!iB3)# z$0c;*5jNo^sv!4RgJo2WUr`16M27q@Sy6NT?RVn}wGTbG=QKn*S)A}fC4NAa>V@@O*gKJq$Gyo&BIX^9B_fmYu}CyJ9ZRK3Y0X?!`ZDcUegPrsLz4gi diff --git a/mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.po index 68b7a0b5..43f65af6 100644 --- a/mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.po @@ -2,13 +2,14 @@ # Copyright (C) 2011 ORGANIZATION # This file is distributed under the same license as the PROJECT project. # +# , 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 18:05+0000\n" -"Last-Translator: FULL NAME \n" +"PO-Revision-Date: 2011-08-10 23:16+0000\n" +"Last-Translator: osc \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" @@ -19,50 +20,52 @@ msgstr "" #: mediagoblin/auth/forms.py:24 mediagoblin/auth/forms.py:46 msgid "Username" -msgstr "" +msgstr "Nome de Usuário" #: mediagoblin/auth/forms.py:29 mediagoblin/auth/forms.py:50 msgid "Password" -msgstr "" +msgstr "Senha" #: mediagoblin/auth/forms.py:34 msgid "Passwords must match." -msgstr "" +msgstr "Senhas devem ser iguais." #: mediagoblin/auth/forms.py:36 msgid "Confirm password" -msgstr "" +msgstr "Confirmar senha" #: mediagoblin/auth/forms.py:39 msgid "Email address" -msgstr "" +msgstr "Endereço de email" #: mediagoblin/auth/views.py:40 msgid "Sorry, registration is disabled on this instance." -msgstr "" +msgstr "Desculpa, o registro está desativado neste momento." #: mediagoblin/auth/views.py:55 msgid "Sorry, a user with that name already exists." -msgstr "" +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 "" +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 "" +msgstr "O email de verificação foi reenviado." #: mediagoblin/edit/forms.py:26 mediagoblin/submit/forms.py:26 msgid "Title" -msgstr "" +msgstr "Título" #: mediagoblin/edit/forms.py:29 msgid "Slug" @@ -74,15 +77,15 @@ msgstr "" #: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:31 msgid "Tags" -msgstr "" +msgstr "Tags" #: mediagoblin/edit/forms.py:38 msgid "Bio" -msgstr "" +msgstr "Biográfia" #: mediagoblin/edit/forms.py:41 msgid "Website" -msgstr "" +msgstr "Website" #: mediagoblin/edit/forms.py:43 msgid "Improperly formed URL" @@ -102,49 +105,51 @@ msgstr "" #: mediagoblin/submit/forms.py:29 msgid "File" -msgstr "" +msgstr "Arquivo" #: mediagoblin/submit/views.py:45 msgid "You must provide a file." -msgstr "" +msgstr "Você deve fornecer um arquivo." #: mediagoblin/submit/views.py:48 msgid "The file doesn't seem to be an image!" -msgstr "" +msgstr "O arquivo não parece ser uma imagem!" #: mediagoblin/submit/views.py:96 msgid "Woohoo! Submitted!" -msgstr "" +msgstr "Eba! Enviado!" #: mediagoblin/templates/mediagoblin/base.html:22 msgid "GNU MediaGoblin" -msgstr "" +msgstr "GNU MediaGoblin" #: mediagoblin/templates/mediagoblin/base.html:45 msgid "Mediagoblin logo" -msgstr "" +msgstr "Logo de Mediagoblin" #: mediagoblin/templates/mediagoblin/base.html:51 msgid "Submit media" -msgstr "" +msgstr "Enviar mídia" #: mediagoblin/templates/mediagoblin/base.html:62 msgid "verify your email!" -msgstr "" +msgstr "Verifique seu email!" #: mediagoblin/templates/mediagoblin/base.html:72 msgid "Login" -msgstr "" +msgstr "Login" #: mediagoblin/templates/mediagoblin/base.html:88 msgid "" "Powered by MediaGoblin, a GNU project" msgstr "" +"Powered by MediaGoblin, a GNU project" #: mediagoblin/templates/mediagoblin/root.html:21 msgid "Welcome to GNU MediaGoblin!" -msgstr "" +msgstr "Bemvindo a GNU Mediagoblin!" #: mediagoblin/templates/mediagoblin/root.html:26 msgid "Submit an item" @@ -153,7 +158,7 @@ msgstr "" #: mediagoblin/templates/mediagoblin/root.html:31 #, python-format msgid "If you have an account, you can Login." -msgstr "" +msgstr "Se você tem conta, você pode Entrar ." #: mediagoblin/templates/mediagoblin/root.html:37 #, python-format @@ -161,33 +166,35 @@ msgid "" "If you don't have an account, please Register." msgstr "" +"Se você não tem conta, por favor Registrar " +"." #: mediagoblin/templates/mediagoblin/auth/login.html:26 msgid "Log in" -msgstr "" +msgstr "Entrar" #: mediagoblin/templates/mediagoblin/auth/login.html:29 msgid "Login failed!" -msgstr "" +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 "" +msgstr "Enviar" #: mediagoblin/templates/mediagoblin/auth/login.html:42 msgid "Don't have an account yet?" -msgstr "" +msgstr "Ainda não tem conta?" #: mediagoblin/templates/mediagoblin/auth/login.html:45 msgid "Create one here!" -msgstr "" +msgstr "Crie uma aqui!" #: mediagoblin/templates/mediagoblin/auth/register.html:27 msgid "Create an account!" -msgstr "" +msgstr "Criar uma conta!" #: mediagoblin/templates/mediagoblin/auth/verification_email.txt:19 #, python-format @@ -199,24 +206,29 @@ msgid "" "\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 "" +msgstr "Editando %(media_title)s" #: mediagoblin/templates/mediagoblin/edit/edit.html:36 msgid "Cancel" -msgstr "" +msgstr "Cancelar" #: mediagoblin/templates/mediagoblin/edit/edit.html:37 msgid "Save changes" -msgstr "" +msgstr "Salvar mudanças" #: mediagoblin/templates/mediagoblin/edit/edit_profile.html:29 #, python-format msgid "Editing %(username)s's profile" -msgstr "" +msgstr "Editando perfil de %(username)s" #: mediagoblin/templates/mediagoblin/listings/tag.html:29 msgid "Media tagged with:" @@ -226,11 +238,11 @@ msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/gallery.html:46 #: mediagoblin/templates/mediagoblin/user_pages/user.html:101 msgid "atom feed" -msgstr "" +msgstr "atom feed" #: mediagoblin/templates/mediagoblin/submit/start.html:26 msgid "Submit yer media" -msgstr "" +msgstr "Envie sua mídia" #: mediagoblin/templates/mediagoblin/user_pages/gallery.html:30 #, python-format @@ -240,35 +252,37 @@ msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/gallery.html:51 #: mediagoblin/templates/mediagoblin/user_pages/user.html:30 msgid "Sorry, no such user found." -msgstr "" +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 "" +msgstr "Verificação necessária" #: mediagoblin/templates/mediagoblin/user_pages/user.html:40 msgid "Almost done! Your account still needs to be verified." -msgstr "" +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 "" +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 "" +msgstr "Caso contrário:" #: mediagoblin/templates/mediagoblin/user_pages/user.html:52 msgid "Resend verification email" -msgstr "" +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 @@ -276,15 +290,17 @@ msgid "" "If you are that person but you've lost your verification email, you can log in and resend it." msgstr "" +"Se você é essa pessoa, mas você perdeu seu e-mail de verificação, você pode " +"efetuar login e reenviá-la." #: mediagoblin/templates/mediagoblin/user_pages/user.html:76 #, python-format msgid "%(username)s's profile" -msgstr "" +msgstr "Perfil de %(username)s" #: mediagoblin/templates/mediagoblin/user_pages/user.html:84 msgid "Edit profile" -msgstr "" +msgstr "Editar perfil" #: mediagoblin/templates/mediagoblin/user_pages/user.html:95 #, python-format diff --git a/mediagoblin/i18n/ro/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/ro/LC_MESSAGES/mediagoblin.mo new file mode 100644 index 0000000000000000000000000000000000000000..361846d4d930e4d83983757e96d7170cdb4863fc GIT binary patch literal 5629 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, 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 \n" +"Language-Team: LANGUAGE \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 MediaGoblin, a GNU project" +msgstr "" +"Construit cu MediaGoblin, un proiect GNU" + +#: 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 Login." +msgstr "" +"Dacă aveți deja un cont, vă puteți autentifica." + +#: mediagoblin/templates/mediagoblin/root.html:37 +#, python-format +msgid "" +"If you don't have an account, please Register." +msgstr "" +"Dacă nu aveți cont, vă rugăm să vă înregistrați." + +#: 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 "%(username)s's media" +msgstr "Fișierele lui %(username)s" + +#: 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 log in and resend it." +msgstr "" +"Dacă dvs. sunteți persoana respectivă și nu mai aveți e-mail-ul de " +"verificare, puteți să vă autentificați pentru " +"a-l retrimite." + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:76 +#, python-format +msgid "%(username)s's profile" +msgstr "Profil %(username)s" + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:84 +msgid "Edit profile" +msgstr "Editare profil" + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:95 +#, python-format +msgid "View all of %(username)s's media" +msgstr "Toate fișierele lui %(username)s" + + diff --git a/mediagoblin/i18n/sl/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/sl/LC_MESSAGES/mediagoblin.mo new file mode 100644 index 0000000000000000000000000000000000000000..a70b1fef8130a4406cfc4e637d76b718986663e6 GIT binary patch literal 5487 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, 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 \n" +"Language-Team: LANGUAGE \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 MediaGoblin, a GNU project" +msgstr "" +"Stran poganja MediaGoblin, del projekta GNU" + +#: 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 Login." +msgstr "Če imate račun, se lahko Prijavite." + +#: mediagoblin/templates/mediagoblin/root.html:37 +#, python-format +msgid "" +"If you don't have an account, please Register." +msgstr "Če računa še nimate, se Registrirajte." + +#: 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 "%(username)s's media" +msgstr "Vsebina uporabnika %(username)s" + +#: 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 log in and resend it." +msgstr "" +"Če ste ta oseba vi, a ste izgubili potrditveno e-pošto, se lahko prijavite 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" + +